diff --git a/firmware/nest_main.ino b/firmware/nest_main.ino new file mode 100644 index 0000000000000000000000000000000000000000..c1d00a0a898830e54625543fb7039dd003f77bd4 --- /dev/null +++ b/firmware/nest_main.ino @@ -0,0 +1,347 @@ +#include <Adafruit_TinyUSB.h> +#include <SPI.h> +#include "Ucglib.h" +#include <TouchScreen.h> +#include <bluefruit.h> +#include "algorithm_by_RF.h" +#include "max30102.h" + +#define TFT_CS 33 +#define TFT_RST 2 +#define TFT_DC 25 +#define TFT_MOSI 24 +#define TFT_SCLK 26 +#define LIGHT_PIN 10 +#define OXI_INT 18 + +Ucglib_ST7735_18x128x160_HWSPI ucg(TFT_DC, TFT_CS, TFT_RST); + +// Touchscreen pins +#define YP A6 +#define XM A2 +#define YM A0 +#define XP A7 +#define TS_MINX 150 +#define TS_MINY 120 +#define TS_MAXX 920 +#define TS_MAXY 940 +TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300); + +// Global variables for the screen state +enum Screen { MAIN_MENU, RESCUE_CONFIRM, SENDING_DATA, SURVEY_SENT, HEART_RATE }; +Screen currentScreen = MAIN_MENU; + +int currentBpm = -1; // Initialized to -1 to indicate no valid value yet +uint32_t elapsedTime, timeStart; + +uint32_t aun_ir_buffer[BUFFER_SIZE]; // Infrared LED sensor data +uint32_t aun_red_buffer[BUFFER_SIZE]; // Red LED sensor data +float old_n_spo2; // Previous SPO2 value +uint8_t uch_dummy, k; + +// Define UUIDs for the service and characteristic +#define SERVICE_UUID "12345678-1234-5678-1234-56789abcdef0" +#define CHARACTERISTIC_UUID "abcdef12-3456-7890-1234-56789abcdef1" +BLEService customService(SERVICE_UUID); +BLECharacteristic alertCharacteristic(CHARACTERISTIC_UUID); + +void setup(void) { + + pinMode(OXI_INT, INPUT); // pin 18 connects to the interrupt output pin of the MAX30102 + + Serial.begin(115200); + while (!Serial); // Wait for the serial port to be ready for debugging + + maxim_max30102_init(); // Initialize the MAX30102 + old_n_spo2 = 0.0; + + pinMode(LIGHT_PIN, OUTPUT); + analogWrite(LIGHT_PIN, 255); + ucg.begin(UCG_FONT_MODE_TRANSPARENT); + ucg.clearScreen(); + setupBLE(); // Initialize BLE configuration + drawInterface(); // Initially draw the main screen +} + +void loop(void) { + bool isTouched = checkTouch(); // This returns true if the screen was touched + + if (currentScreen == HEART_RATE && !isTouched) { + updateHeartRate(); // Only update if we are on the heart rate screen and no touch was detected + } + + delay(100); // Small delay to avoid too fast refreshing +} + +void setupBLE() { + Bluefruit.begin(); + Bluefruit.Periph.setConnectCallback(connect_callback); + Bluefruit.Periph.setDisconnectCallback(disconnect_callback); + Bluefruit.setName("NEST Device"); + + // Initialize the service + customService.begin(); + + // Initialize the characteristic + alertCharacteristic.setProperties(CHR_PROPS_NOTIFY); + alertCharacteristic.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); + alertCharacteristic.setFixedLen(20); + alertCharacteristic.begin(); + + startAdvertising(); +} + +void startAdvertising() { + Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + Bluefruit.Advertising.addTxPower(); + Bluefruit.Advertising.addService(customService); + Bluefruit.Advertising.addName(); + Bluefruit.Advertising.start(0); // 0 for continuous advertising +} + +void connect_callback(uint16_t conn_handle) { + Serial.println("Device Connected!"); +} + +void disconnect_callback(uint16_t conn_handle, uint8_t reason) { + Serial.println("Device Disconnected!"); +} + +void drawInterface() { + currentScreen = MAIN_MENU; + ucg.clearScreen(); + ucg.setColor(255, 255, 255); // White + ucg.setFont(ucg_font_ncenR12_tr); + ucg.setPrintPos(10, 15); + ucg.print("NEST - OS"); + + drawButton(5, 30, 118, 36, "Pain Diary", 0x2104); + drawButton(5, 75, 118, 36, "Rescue", 0x2104); + drawButton(5, 120, 118, 36, "Monitoring", 0x2104); +} + +void drawRescueConfirmationScreen() { + currentScreen = RESCUE_CONFIRM; + ucg.clearScreen(); + + ucg.setColor(255, 255, 255); + ucg.setFont(ucg_font_ncenR12_tr); + ucg.setPrintPos(10, 20); ucg.print("Are you sure"); + ucg.setPrintPos(10, 40); ucg.print("you want to"); + ucg.setPrintPos(10, 60); ucg.print("send data"); + + ucg.setColor(255, 0, 0); // Red for the NO button + ucg.drawDisc(40, 100, 20, UCG_DRAW_ALL); + ucg.setColor(255, 255, 255); ucg.setPrintPos(34, 107); ucg.print("X"); + + // Smaller YES button with a check mark + ucg.setColor(0, 0, 255); // Blue + ucg.drawDisc(90, 100, 20, UCG_DRAW_ALL); // Reduced diameter + ucg.setColor(255, 255, 255); // White + + // Draw a thicker check mark + int baseX = 85, baseY = 98; // Base point for the check + // Lines for the lower part of the check + ucg.drawLine(baseX, baseY, baseX + 4, baseY + 4); + ucg.drawLine(baseX, baseY - 1, baseX + 4, baseY + 3); + ucg.drawLine(baseX, baseY + 1, baseX + 4, baseY + 5); + // Lines for the upper part of the check + ucg.drawLine(baseX + 3, baseY + 3, baseX + 12, baseY - 6); + ucg.drawLine(baseX + 3, baseY + 4, baseX + 12, baseY - 5); + ucg.drawLine(baseX + 3, baseY + 5, baseX + 12, baseY - 4); + +} + +void drawSendingDataScreen() { + currentScreen = SENDING_DATA; + ucg.clearScreen(); + ucg.setColor(255, 255, 255); + ucg.setFont(ucg_font_ncenR14_hr); + ucg.setPrintPos(10, 60); ucg.print("Sending"); + ucg.setPrintPos(10, 80); ucg.print("data..."); + + const char* alertMsg = "Data Sent"; + if (Bluefruit.connected()) { + if (alertCharacteristic.notify(alertMsg, strlen(alertMsg))) { + Serial.println("Alert sent via BLE"); + } else { + Serial.println("Failed to send notification"); + } + } else { + Serial.println("No devices connected."); + } + + delay(3000); // Simulate some processing + drawSurveySentScreen(); // Call the screen for survey sent +} + +void drawSurveySentScreen() { + currentScreen = SURVEY_SENT; // Set the correct state + ucg.clearScreen(); + ucg.setColor(255, 255, 255); + ucg.setFont(ucg_font_ncenR14_hr); + ucg.setPrintPos(10, 60); + ucg.print("Survey"); + ucg.setPrintPos(10, 80); + ucg.print("Sent"); +} + +void drawHeartRateScreen() { + currentScreen = HEART_RATE; // Make sure to add this to the screen enum + ucg.clearScreen(); // Clear the screen before drawing the new view + + ucg.setColor(255, 255, 255); // White for the text + ucg.setFont(ucg_font_ncenR14_hr); // Large font for the title + ucg.setPrintPos(10, 20); // Adjust position as needed + ucg.print("Heart"); + ucg.setPrintPos(10, 35); // Adjust position as needed + ucg.print("Rate"); + + // Draw a button to go back + ucg.setColor(128, 0, 128); // Purple color for the button + ucg.drawDisc(20, 80, 15, UCG_DRAW_ALL); + ucg.setColor(255, 255, 255); // White for the button icon + ucg.setFont(ucg_font_ncenR12_tr); // Change to a smaller font for the icon + ucg.setPrintPos(15, 85); // Adjust to center the text in the circle + ucg.print("<"); + + // Draw the heart rate value + ucg.setFont(ucg_font_ncenR24_tr); // Large font for the numbers + ucg.setColor(255, 255, 255); // White + ucg.setPrintPos(60, 80); // Adjust as needed + + if (currentBpm >= 0) { + ucg.print(currentBpm); // Show the current BPM value + } else { + ucg.print(" -"); // Show a dash if BPM is invalid + } + + ucg.setFont(ucg_font_ncenR14_hr); // Smaller font for "BPM" + ucg.setPrintPos(60, 100); // Adjust as needed + ucg.print("BPM"); + + // Draw a small heart below "BPM" + drawHeart(80, 110); // Adjust coordinates as needed +} + +void drawHeart(int x, int y) { + ucg.setColor(255, 0, 0); // Red color for the heart + + // Draw the top of the heart as two red circles + // Make sure the circles slightly overlap the triangle + ucg.drawDisc(x - 2, y - 2, 4, UCG_DRAW_UPPER_LEFT); // Adjust position for better blending + ucg.drawDisc(x - 2, y - 2, 4, UCG_DRAW_UPPER_RIGHT); // Adjust position for better blending + ucg.drawDisc(x + 9, y - 2, 4, UCG_DRAW_UPPER_RIGHT); // Adjust position for better blending + ucg.drawDisc(x + 9, y - 2, 4, UCG_DRAW_UPPER_LEFT); // Adjust position for better blending + + // Draw the bottom part of the heart as a triangle + ucg.drawTriangle(x - 6, y - 2, x + 14, y - 2, x + 4, y + 8); +} + +bool checkTouch() { + TSPoint p = ts.getPoint(); + pinMode(XM, OUTPUT); + pinMode(YP, OUTPUT); + bool screenTouched = false; + + if (p.z > ts.pressureThreshhold) { + int x = map(p.x, TS_MINX, TS_MAXX, 0, 128); + int y = map(p.y, TS_MINY, TS_MAXY, 0, 160); + screenTouched = true; // The screen was touched + + switch(currentScreen) { + case MAIN_MENU: + if (y > 30 && y < 66) { // "Pain Diary" button + // Implement what should happen if "Pain Diary" is selected + } else if (y > 75 && y < 111) { // "Rescue" button + drawRescueConfirmationScreen(); + } else if (y > 120 && y < 156) { // "Monitoring" button + drawHeartRateScreen(); + } + break; + + case RESCUE_CONFIRM: + if (x > 20 && x < 60 && y > 80 && y < 120) { // NO button + drawInterface(); // Go back to the main menu + } else if (x > 70 && x < 110 && y > 80 && y < 120) { // YES button + drawSendingDataScreen(); // Go to "Sending data..." + } + break; + + case SENDING_DATA: + // Hold the screen until any part is touched, then go back to the menu + drawInterface(); + break; + + case SURVEY_SENT: + // Return to the main menu with any screen touch + drawInterface(); + break; + + case HEART_RATE: + if (x > 20 && x < 60 && y > 80 && y < 120) { // Back button area on the "Heart Rate" screen + drawInterface(); // Go back to the main menu + } + break; + + default: + // You can add more cases if there are more screens + break; + } + } + return screenTouched; +} + +void drawButton(int x, int y, int w, int h, const char* text, uint16_t color) { + ucg.setColor(255, 255, 255); + ucg.drawRBox(x, y, w, h, 5); + ucg.setColor(color >> 8, color & 0xFF, color & 0xFF); + ucg.drawRBox(x + 2, y + 2, w - 4, h - 4, 5); + ucg.setColor(255, 255, 255); + ucg.setFont(ucg_font_6x10_tr); + int textX = x + (w - ucg.getStrWidth(text)) / 2; + int textY = y + h / 2 + 3; + ucg.setPrintPos(textX, textY); + ucg.print(text); +} + +void updateHeartRate() { + static unsigned long lastUpdateTime = 0; // Save the last time the reading was updated + int32_t n_heart_rate; // Variable to store heart rate value + int8_t ch_hr_valid; // Indicator if heart rate reading is valid + float n_spo2, ratio, correl; // Additional variables used in calculation + int8_t ch_spo2_valid; // Validity indicator for oxygen saturation + int32_t i; + + for(i = 0; i < BUFFER_SIZE; i++) { + while (digitalRead(OXI_INT) == 1); // Wait until the interrupt pin asserts + maxim_max30102_read_fifo((aun_red_buffer + i), (aun_ir_buffer + i)); // Read from MAX30102 FIFO + } + + // Calculate heart rate and oxygen saturation + rf_heart_rate_and_oxygen_saturation(aun_ir_buffer, BUFFER_SIZE, aun_red_buffer, &n_spo2, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid, &ratio, &correl); + + if (ch_hr_valid) { + currentBpm = n_heart_rate; + } else { + currentBpm = -1; // If reading is invalid, set to -1 + } + + if (currentScreen == HEART_RATE) { + ucg.setColor(0, 0, 0); // Assuming the background is black + ucg.drawBox(40, 50, 80, 35); // Adjust dimensions to fully cover the number and text + + ucg.setColor(255, 255, 255); // White + ucg.setFont(ucg_font_ncenR24_tr); + ucg.setPrintPos(60, 80); // Adjust as needed + + if (currentBpm >= 0) { + ucg.print(currentBpm); + } else { + ucg.print(" -"); + } + } +} + + +