From c8f5c932b8d49b0a5b54260e1a638595f304dab6 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 13 May 2025 14:01:52 +1000 Subject: [PATCH 1/2] Create new folder for dev --- .../Complete_Code/dev_display_driver/main.cpp | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 IoT_Wearable/Codebase/Complete_Code/dev_display_driver/main.cpp diff --git a/IoT_Wearable/Codebase/Complete_Code/dev_display_driver/main.cpp b/IoT_Wearable/Codebase/Complete_Code/dev_display_driver/main.cpp new file mode 100644 index 00000000..a73793e5 --- /dev/null +++ b/IoT_Wearable/Codebase/Complete_Code/dev_display_driver/main.cpp @@ -0,0 +1,79 @@ +// This is used for: +// - Arduino Nano 33 IoT board, paired with +// - WaveShare ST7789v2 display module + +#include // Core graphics library +#include // Hardware-specific library for ST7789, used for initializing display object +#include +#include +#include +#include + +//Include user defined header files +#include +#include + +//Setting up the pins + #define TFT_CS 10 + #define TFT_RST 8 + #define TFT_DC 9 + +// We are using the HARDWARE SPI pins for MOSI and SCLK, which are unique +// to each board and not reassignable. For Arduino Nano 33 IoT: MOSI = pin 11 and +// SCLK = pin 13. This is done to improve the performance of the display. + +// Create a display object of the Adafruit_ST7789 class +Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST); + +//Declare rtc object for real time clock +RTCZero rtc; + + +void drawTemplate() { + tft.fillScreen(ST77XX_BLACK); //Clear screen before redrawing + + uint8_t padding = 10; // Padding for layout + + // Top Section: Battery level with battery icon + drawBatteryIcon(tft, padding + 10, padding + 10, ST77XX_WHITE); // Draw battery icon with 85% charge + + // First Section: Heart Rate and SpO2 + drawHeartIcon(tft, padding + 10, padding + 170, ST77XX_RED); // Heart icon + drawBubbleIcon(tft, padding + 10, padding + 240, ST77XX_CYAN); // Bubble icon with SpO2 percentage + + drawCircularStepsGauge(tft, 170, 220, 2000, 10000, ST77XX_GREEN); + + // Fourth Section: Body Temperature + drawThermometerIcon(tft, padding + 10, padding + 200); // Thermometer icon + + // Use the new RTC-aware version of drawTime + drawTime(tft, rtc, 20, 85, ST77XX_WHITE); + drawDate(tft, rtc, 120, 25, ST77XX_WHITE); +} + +void setup() { + // Initialize Serial Monitor + Serial.begin(9600); + Serial.println(F("Initializing ST7789 Display...")); + + // Initialize WiFi + WiFi_Setup(WiFi, rtc); + + // Initialize display (240x280 resolution) + tft.init(240, 280); + tft.setRotation(2); // Portrait orientation + tft.fillScreen(ST77XX_BLACK); + + // Set default text size and color + tft.setTextSize(2); + tft.setTextColor(ST77XX_WHITE); + + // Draw the template + drawTemplate(); +} + +void loop() { + // Continuously update the time every second + delay(3000); // Wait for 3 seconds + drawTemplate(); // Redraw the template to update the time +} From 71f6d2a6470488a5c7a883f001e6655cca6a41a5 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 13 May 2025 14:02:31 +1000 Subject: [PATCH 2/2] Add files via upload --- .../dev_display_driver/template.h | 169 ++++++++++++++++++ .../dev_display_driver/wifi_time.h | 77 ++++++++ 2 files changed, 246 insertions(+) create mode 100644 IoT_Wearable/Codebase/Complete_Code/dev_display_driver/template.h create mode 100644 IoT_Wearable/Codebase/Complete_Code/dev_display_driver/wifi_time.h diff --git a/IoT_Wearable/Codebase/Complete_Code/dev_display_driver/template.h b/IoT_Wearable/Codebase/Complete_Code/dev_display_driver/template.h new file mode 100644 index 00000000..826b8a35 --- /dev/null +++ b/IoT_Wearable/Codebase/Complete_Code/dev_display_driver/template.h @@ -0,0 +1,169 @@ +#ifndef template_h +#define template_h + +#include // Core graphics library for displays +#include // Specific driver for the ST7789 display +#include // SPI communication used for the display + +/** + * Get the name of the weekday for a given date using Zeller's Congruence. + * + * @param day Day of the month + * @param month Month (1 = January, 12 = December) + * @param year Full year (e.g., 2024) + * @return Abbreviated weekday name (e.g., "Mon", "Tue", ...) + */ +const char* weekdayName(int day, int month, int year) { + if (month < 3) { + month += 12; + year -= 1; + } + + int k = year % 100; + int j = year / 100; + int h = (day + (13 * (month + 1)) / 5 + k + k/4 + j/4 + 5*j) % 7; + + const char* days[] = {"Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"}; + return days[h]; +} + +/** + * Draws a battery icon on the display, representing battery percentage visually. + */ +void drawBatteryIcon(Adafruit_ST7789 tft, int x, int y, uint16_t color) { + tft.drawRect(x, y, 50, 25, color); // Battery body outline + tft.fillRect(x + 52, y + 7, 5, 12, color); // Battery terminal + tft.fillRect(x + 2, y + 2, 46, 21, ST77XX_BLACK); // Battery "empty" interior + + // Display the battery percentage as text + tft.setCursor(x + 8, y + 5); + tft.setTextColor(color); + tft.setTextSize(2); + tft.print("80%"); +} + +/** + * Draws a heart icon and displays a heart rate value. + */ +void drawHeartIcon(Adafruit_ST7789 tft, int x, int y, uint16_t color) { + tft.fillCircle(x + 4, y + 4, 4, color); // Left part of the heart + tft.fillCircle(x + 12, y + 4, 4, color); // Right part of the heart + tft.fillTriangle(x, y + 4, x + 8, y + 16, x + 16, y + 4, color); // Bottom point + + // Display heart rate + tft.setCursor(x + 25, y); + tft.setTextColor(ST77XX_WHITE); + tft.setTextSize(2); + tft.print("76"); +} + +/** + * Draws a bubble (oxygen) icon and displays an SpO2 percentage value. + */ +void drawBubbleIcon(Adafruit_ST7789 tft, int x, int y, uint16_t color) { + tft.drawCircle(x + 8, y + 8, 8, color); // Outer bubble outline + tft.fillCircle(x + 6, y + 6, 2, color); // Highlight dot inside + + // Display SpO2 percentage + tft.setCursor(x + 25, y); + tft.setTextColor(ST77XX_WHITE); + tft.setTextSize(2); + tft.print("98%"); +} + +/** + * Draws a thermometer icon and displays a body temperature reading. + */ +void drawThermometerIcon(Adafruit_ST7789 tft, int x, int y) { + tft.fillRect(x + 4, y + 2, 4, 12, ST77XX_WHITE); // Thermometer stem + tft.fillCircle(x + 6, y + 16, 3, ST77XX_WHITE); // Thermometer bulb (outer) + tft.fillRect(x + 5, y + 7, 2, 12, ST77XX_RED); // Mercury (stem) + tft.fillCircle(x + 6, y + 16, 2, ST77XX_RED); // Mercury (bulb) + + // Display body temperature + tft.setCursor(x + 25, y + 3); + tft.setTextColor(ST77XX_WHITE); + tft.setTextSize(2); + tft.print("36.4C"); +} + +/** + * Displays the current time in HH:MM format using RTCZero. + */ +void drawTime(Adafruit_ST7789 tft, RTCZero& rtc, int x, int y, uint16_t color) { + int hours = rtc.getHours(); + int minutes = rtc.getMinutes(); + + tft.setCursor(x, y); + tft.setTextColor(color); + tft.setTextSize(7); + if (hours < 10) tft.print("0"); + tft.print(hours); + tft.print(":"); + if (minutes < 10) tft.print("0"); + tft.print(minutes); +} + +/** + * Displays the current date in format: Weekday DD/MM using RTCZero. + */ +void drawDate(Adafruit_ST7789 tft, RTCZero& rtc, int x, int y, uint16_t color) { + int day = rtc.getDay(); + int month = rtc.getMonth(); + int year = rtc.getYear() + 2000; + + const char* weekday = weekdayName(day, month, year); + + tft.setCursor(x, y); + tft.setTextColor(color); + tft.setTextSize(2); + tft.print(weekday); + tft.print(" "); + + if (day < 10) tft.print("0"); + tft.print(day); + tft.print("/"); + + if (month < 10) tft.print("0"); + tft.print(month); +} + +/** + * Draws a circular step counter gauge showing progress toward a step goal. + */ +void drawCircularStepsGauge(Adafruit_ST7789 tft, int x, int y, int steps, int maxSteps, uint16_t color) { + // Draw outer circle for gauge + tft.fillCircle(x, y, 50, ST77XX_BLACK); // Background + tft.drawCircle(x, y, 50, ST77XX_WHITE); // Border + + // Convert step count to angle (0° to 360°) + int angle = map(steps, 0, maxSteps, 0, 360); + + // Draw each pixel along the arc + for (int i = 0; i < angle; i++) { + int x1 = x + 50 * cos(radians(i)); + int y1 = y - 50 * sin(radians(i)); + tft.drawPixel(x1, y1, color); + } + + // Label: "Steps" above gauge + tft.setCursor(x - 15, y - 25); + tft.setTextColor(ST77XX_WHITE); + tft.setTextSize(1); + tft.print("Steps"); + + // Display current step count in center + tft.setCursor(x - 22, y - 5); + tft.setTextColor(ST77XX_WHITE); + tft.setTextSize(2); + tft.print(steps); + + // Display goal step count below + tft.setCursor(x - 15, y + 25); + tft.setTextColor(ST77XX_WHITE); + tft.setTextSize(1); + tft.print("/"); + tft.print(maxSteps); +} + +#endif diff --git a/IoT_Wearable/Codebase/Complete_Code/dev_display_driver/wifi_time.h b/IoT_Wearable/Codebase/Complete_Code/dev_display_driver/wifi_time.h new file mode 100644 index 00000000..ea5b1005 --- /dev/null +++ b/IoT_Wearable/Codebase/Complete_Code/dev_display_driver/wifi_time.h @@ -0,0 +1,77 @@ +#ifndef wifi_time_h +#define wifi_time_h + +#include // WiFi library for Arduino boards using NINA module +#include // Required for time retrieval using NTP +#include // Real-time clock library + +// Debug toggle: Set to false to disable Serial prints +bool WIFI_DEBUG = true; + +// WiFi credentials +#define SECRET_SSID "REPLACE_WITH_YOUR_SSID" +#define SECRET_PASS "REPLACE_WITH_YOUR_PASSWORD" + +// WiFi connection status tracking variable +int status = WL_IDLE_STATUS; + +/** + * Function to connect to WiFi and synchronize RTC with NTP time. + * + * @param wifi Reference to the WiFi module instance (usually `WiFi`) + * @param rtc Reference to RTCZero instance used for tracking real time + * + * @return true if setup was successful and time was synced, false otherwise + */ +bool WiFi_Setup(WiFiClass& wifi, RTCZero& rtc) { + // Check if WiFi module is present and working + if (wifi.status() == WL_NO_SHIELD) { + if (WIFI_DEBUG) Serial.println("WiFi adapter not ready"); + return false; // Exit early if WiFi hardware is not detected + } + + // Attempt to connect to the WiFi network + while (status != WL_CONNECTED) { + if (WIFI_DEBUG) { + Serial.print("Connecting to SSID: "); + Serial.println(SECRET_SSID); + } + status = wifi.begin(SECRET_SSID, SECRET_PASS); // Start connection attempt + delay(3000); // Wait 3 seconds before retrying + } + + if (WIFI_DEBUG) Serial.println("WiFi connected!"); + + + // Clock logic to be fixed, currently not accurate + + // Initialize the real-time clock + rtc.begin(); + + // Try to get the current time from an NTP server + unsigned long epoch; // Stores the retrieved epoch time + int tries = 0, maxTries = 6; // Retry up to 6 times + + do { + epoch = wifi.getTime(); // Request time from NTP server + tries++; + delay(1000); // Wait 1 second between tries + } while ((epoch == 0) && (tries < maxTries)); + + // If time could not be retrieved after maxTries, return false + if (tries == maxTries) { + if (WIFI_DEBUG) Serial.println("NTP unreachable!!"); + return false; + } + +Serial.print("Epoch received: "); +Serial.println(epoch); + + // Set RTC time using the retrieved epoch + rtc.setEpoch(epoch); + if (WIFI_DEBUG) Serial.println("Time synced."); + + return true; +} + +#endif