This commit is contained in:
2026-05-04 09:45:46 +03:00
commit 4149376f54
9 changed files with 882 additions and 0 deletions

363
src/main.cpp Normal file
View File

@@ -0,0 +1,363 @@
#include <TFT_eSPI.h>
#include <TinyGPSPlus.h>
#include <HardwareSerial.h>
#include <Arduino.h>
#include <SPI.h>
#include "Free_Fonts.h"
#define RX2_PIN 16
#define TX2_PIN 17
#define GPS_BAUD 9600
#define LED 2
#define KEY_X 3 // start X
#define KEY_Y 24
#define KEY_W 76 // Key width
#define KEY_H 36 // Key height
#define KEY_SPACING_X 3 // X gap
#define KEY_SPACING_Y 3 // Y gap
#define KEY_TEXTSIZE 1 // Font size multiplier
#define BUTTON_X_DELTA 22
#define NUM_KEYS 3
#define QUEUE_SIZE 5
TFT_eSPI tft = TFT_eSPI();
TinyGPSPlus gps;
HardwareSerial serialGPS(1);
TaskHandle_t taskBlinkHandle = NULL; // for task control: suspend / resume
TaskHandle_t taskGPSHandle = NULL;
TFT_eSPI_Button key[NUM_KEYS];
SemaphoreHandle_t spiMutex;
QueueHandle_t gpsQueue = NULL;
char label[] = "label";
String btnLabel[] = {"GPS", "DATE", "LOC"};
// uint16_t calData[5] = { 293, 3474, 445, 3470, 0 };
// uint16_t calData[5] = { 314, 3512, 424, 3489, 0 };
// uint16_t calData[5] = { 233, 3558, 390, 3514, 0 };
// uint16_t calData[5] = { 283, 3553, 416, 3514, 0 };
// Initial Free Heap: 274996 bytes
struct GpsData
{
float lat;
float lng;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint16_t year;
uint8_t month;
uint8_t day;
uint32_t sat;
float speed;
float alt;
bool isValid;
};
// struct GPSData1 {
// float lat = 0.0f;
// float lon = 0.0f;
// float alt = 0.0f;
// uint8_t satellites = 0;
// float speed = 0.0f;
// float course = 0.0f;
// bool valid = false;
// };
// packet.lat = gps.location.isValid() ? gps.location.lat() : 0.0f;
void smartDelay(unsigned long ms);
void initDisplay();
void displayGPSInfo();
void drawButtons();
void taskBlink(void *pvParameters);
void taskGPS(void *pvParameters);
void taskTFT(void *pvParameters);
void drawTitle(const char *title);
//------------------------------------------------------------------------------------------
void setup()
{
Serial.begin(115200);
spiMutex = xSemaphoreCreateMutex();
Serial.printf("Starting FreeRTOS: Memory Usage\nInitial Free Heap: %u bytes\n", xPortGetFreeHeapSize());
gpsQueue = xQueueCreate(QUEUE_SIZE, sizeof(GpsData));
xTaskCreatePinnedToCore(taskBlink, "taskBlink", 2000, NULL, 1, &taskBlinkHandle, 0);
xTaskCreatePinnedToCore(taskGPS, "taskGPS", 2000, NULL, 1, &taskGPSHandle, 0);
xTaskCreatePinnedToCore(taskTFT, "taskTFT", 2000, NULL, 1, NULL, 1);
}
void loop()
{
static uint32_t lastCheck = 0;
if (millis() - lastCheck > 5000)
{
Serial.printf("Free Heap: %u bytes\n", xPortGetFreeHeapSize());
lastCheck = millis();
}
}
void taskGPS(void *pvParameters)
{
serialGPS.begin(GPS_BAUD, SERIAL_8N1, RX2_PIN, TX2_PIN);
Serial.print("Task GPS running on core ");
Serial.println(xPortGetCoreID());
GpsData data;
while (true)
{
while (serialGPS.available() > 0)
{
gps.encode(serialGPS.read());
}
if (gps.location.isUpdated())
{
data.lat = gps.location.lat();
data.lng = gps.location.lng();
data.speed = gps.speed.kmph();
data.alt = gps.altitude.meters();
data.sat = gps.satellites.value();
Serial.printf("Lat: %.6f, Lon: %.6f\n", data.lat, data.lng);
Serial.printf("Speed: %.1f km/h\n", data.speed);
Serial.printf("Alt: %.1f m\n", data.alt);
Serial.printf("Satellites: %d\n", data.sat);
}
if (gps.date.isUpdated() && gps.time.isUpdated())
{
data.year = gps.date.year();
data.month = gps.date.month();
data.day = gps.date.day();
data.hour = gps.time.hour();
data.minute = gps.time.minute();
data.second = gps.time.second();
Serial.printf("Date: %04d-%02d-%02d\n", data.year, data.month, data.day);
Serial.printf("Time: %02d:%02d:%02d\n", data.hour, data.minute, data.second);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
// Serial.printf("TaskGPS Stack Free: %u bytes\n", uxTaskGetStackHighWaterMark(NULL));
}
}
void taskBlink(void *pvParameters)
{
pinMode(LED, OUTPUT);
for (;;)
{ // Infinite loop
digitalWrite(LED, HIGH);
// Serial.println("BlinkTask: LED ON");
vTaskDelay(1000 / portTICK_PERIOD_MS); // 1000ms
digitalWrite(LED, LOW);
// Serial.println("BlinkTask: LED OFF");
vTaskDelay(1000 / portTICK_PERIOD_MS);
// Serial.print("Task Blink running on core ");
// Serial.println(xPortGetCoreID());
// Serial.printf("TaskBlink Stack Free: %u bytes\n", uxTaskGetStackHighWaterMark(NULL));
}
}
void taskTFT(void *pvParameters)
{
initDisplay();
drawTitle("Hello world!");
drawButtons();
for (;;)
{
// Serial.printf("taskTFT Stack Free: %u bytes\n", uxTaskGetStackHighWaterMark(NULL));
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
void loop_1()
{
if (gps.location.isUpdated())
{
displayGPSInfo();
}
uint16_t t_x = 0, t_y = 0; // To store the touch coordinates
// Get current touch state and coordinates
bool pressed = tft.getTouch(&t_x, &t_y);
if (pressed)
{
Serial.println("pressed");
digitalWrite(LED, HIGH);
}
else
{
digitalWrite(LED, LOW);
}
// Adjust press state of each key appropriately
for (uint8_t b = 0; b < NUM_KEYS; b++)
{
if (pressed && key[b].contains(t_x, t_y))
{
key[b].press(true);
}
else
{
key[b].press(false);
}
}
// Check if any key has changed state
for (uint8_t b = 0; b < NUM_KEYS; b++)
{
// If button was just pressed, redraw inverted button
if (key[b].justPressed())
{
Serial.println("Button " + (String)b + " pressed");
tft.setFreeFont(FF5);
key[b].drawButton(true, btnLabel[b]);
}
// If button was just released, redraw normal color button
if (key[b].justReleased())
{
Serial.println("Button " + (String)b + " released");
tft.setFreeFont(FF5);
key[b].drawButton(false, btnLabel[b]);
}
}
smartDelay(100);
}
void drawButtons()
{
for (int i = 0; i < NUM_KEYS; i++)
{
key[i].initButtonUL(&tft,
KEY_X + i * (KEY_W + KEY_SPACING_X),
KEY_Y,
KEY_W,
KEY_H,
TFT_LIGHTGREY,
TFT_DARKGREY,
TFT_WHITE,
label,
KEY_TEXTSIZE);
key[i].setLabelDatum(0, 2, MC_DATUM);
key[i].drawButton(false, btnLabel[i]);
}
}
void initDisplay()
{
tft.init();
tft.setRotation(2);
uint16_t calData[5] = {283, 3553, 416, 3514, 0};
tft.setTouch(calData);
#ifndef TFT_BL
Serial.println("No TFT backlight pin defined");
#else
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
#endif
tft.fillScreen(TFT_BLACK);
}
void drawTitle(const char *title)
{
tft.setFreeFont(FF5);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.drawString(title, 0, 0, GFXFF);
}
void displayGPSInfo()
{
Serial.print(F("Location: "));
if (gps.location.isValid())
{
Serial.print(gps.location.lat(), 6);
Serial.print(F(","));
Serial.print(gps.location.lng(), 6);
}
else
{
Serial.print(F("INVALID"));
}
Serial.print(F(" Date/Time: "));
if (gps.date.isValid())
{
Serial.print(gps.date.month());
Serial.print(F("/"));
Serial.print(gps.date.day());
Serial.print(F("/"));
Serial.print(gps.date.year());
}
else
{
Serial.print(F("INVALID"));
}
Serial.print(F(" "));
if (gps.time.isValid())
{
if (gps.time.hour() < 10)
Serial.print(F("0"));
Serial.print(gps.time.hour());
Serial.print(F(":"));
if (gps.time.minute() < 10)
Serial.print(F("0"));
Serial.print(gps.time.minute());
Serial.print(F(":"));
if (gps.time.second() < 10)
Serial.print(F("0"));
Serial.print(gps.time.second());
Serial.print(F("."));
if (gps.time.centisecond() < 10)
Serial.print(F("0"));
Serial.print(gps.time.centisecond());
String a = "";
a += String(gps.time.hour());
a += ":";
a += String(gps.time.minute());
a += ":";
a += String(gps.time.second());
tft.setFreeFont(FF30);
tft.setTextColor(TFT_CYAN, TFT_BLACK);
tft.drawString(a, 5, 100, GFXFF);
}
else
{
Serial.print(F("INVALID"));
}
Serial.println();
}
void smartDelay(unsigned long ms)
{
unsigned long start = millis();
do
{
while (serialGPS.available())
gps.encode(serialGPS.read());
} while (millis() - start < ms);
}