From b7f53270c93e88aefa95833c30d3f77393cd708d Mon Sep 17 00:00:00 2001 From: "Yetton, Abi (UG - Elec Electronic Eng)" <ay00177@surrey.ac.uk> Date: Mon, 3 Jan 2022 17:38:35 +0000 Subject: [PATCH] added embedded code --- Embedded Code/pillable.ino | 808 +++++++++++++++++++++++++++++++++++++ 1 file changed, 808 insertions(+) create mode 100644 Embedded Code/pillable.ino diff --git a/Embedded Code/pillable.ino b/Embedded Code/pillable.ino new file mode 100644 index 0000000..8917b8f --- /dev/null +++ b/Embedded Code/pillable.ino @@ -0,0 +1,808 @@ +/*#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h"*/ +#include "ESP32Time.h" +#include "HTTPClient.h" +#include "WiFi.h" + +/*********************************************** +* * +* Defines * +* * +***********************************************/ + +// Pin Defines: +#define PIN_BUZZER 32 +#define PIN_CAP1_LED 15 +#define PIN_CAP2_LED 33 +#define PIN_CAP3_LED 27 +#define PIN_CAP1_LDR 36 +#define PIN_CAP2_LDR 39 +#define PIN_CAP3_LDR 34 +#define PIN_BUTTON 4 + +// Comms Commands: +#define GET_REAL_TIME_WIFI 1 +#define GET_SCHEDULE_WIFI 2 +#define ALERT_MISSED_WIFI 3 +#define GET_REAL_TIME_BLE 4 +#define GET_SCHEDULE_BLE 5 +#define GET_PATIENT_ID_BLE 6 +#define GET_WIFI_BLE 7 + +/*********************************************** +* * +* Globals * +* * +***********************************************/ + +// State flags: +bool init_state_flg = false; +bool comms_state_flg = false; +bool operate_state_flg = false; + +// RTC: +ESP32Time rtc; + +// Comms: +int comms_to_do = 0; + +// WiFi: +const char* ssid = "ASUS_SkinnerFamily"; +const char* password = "5cbe3b4dfe"; + +// Bluetooth: +bool deviceConnected = false; +bool hasConnected = false; + +// Other: +String patient_id = "testp"; +String patient_password = "123"; +int schedule[20][4] = {0}; +bool schedule_known = false; +bool time_known = false; +bool cap1_open = false; +bool cap2_open = false; +bool cap3_open = false; + +/*********************************************** +* * +* Bluetooth setup * +* * +***********************************************/ + +// Define the UUID for our Custom Service +/*#define serviceID BLEUUID((uint16_t)0x1700) + +// Define our custom characteristic along with it's properties +BLECharacteristic customCharacteristic( + BLEUUID((uint16_t)0x1A00), + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY +); + +class ServerCallbacks: public BLEServerCallbacks +{ + void onConnect(BLEServer* MyServer) + { + deviceConnected = true; + }; + + void onDisconnect(BLEServer* MyServer) + { + deviceConnected = false; + } +};*/ + +/*********************************************** +* * +* Interrupts * +* * +***********************************************/ + +hw_timer_t * timer = NULL; + +void IRAM_ATTR onTimer() +{ + // Operate state is entered every 500ms + operate_state_flg = true; +} + +void button_isr() +{ + // Button pin has gone high + noInterrupts(); // Disable interrupts for 50ms + delay(50); // to allow for bounce + interrupts(); + if (digitalRead(PIN_BUTTON) == HIGH) + { + // If the button pin is still high after + // 50ms, button has been pressed + } +} + +/*********************************************** +* * +* Main Functions * +* * +***********************************************/ + +void setup() +{ + init_state_flg = true; +} + +void loop() +{ + if (init_state_flg) + { + initialise_state(); + } + if (comms_state_flg) + { + comms_state(); + } + else if (operate_state_flg) + { + operate_state(); + } + else + { + idle_state(); + } +} + +/*********************************************** +* * +* States * +* * +***********************************************/ + +void initialise_state() +{ + Serial.begin(9600); + + // Setup pin directions: + pinMode(PIN_BUZZER, OUTPUT); + pinMode(PIN_CAP1_LED, OUTPUT); + pinMode(PIN_CAP2_LED, OUTPUT); + pinMode(PIN_CAP3_LED, OUTPUT); + pinMode(PIN_CAP1_LDR, INPUT); + pinMode(PIN_CAP2_LDR, INPUT); + pinMode(PIN_CAP3_LDR, INPUT); + pinMode(PIN_BUTTON, INPUT); + + // Setup pin outputs: + digitalWrite(PIN_BUZZER, LOW); + digitalWrite(PIN_CAP1_LED, LOW); + digitalWrite(PIN_CAP2_LED, LOW); + digitalWrite(PIN_CAP3_LED, LOW); + + // Set up an interrupt to trigger on the button pin's rising edge + attachInterrupt(PIN_BUTTON, button_isr, FALLING); + + setup_wifi(); + setup_bluetooth(); + + // Set up timer interrupt: + timer = timerBegin(0, 80, true); // Set up timer 0 to interrupt every 1us + timerAttachInterrupt(timer, &onTimer, true); + timerAlarmWrite(timer, 500000, true); // onTimer is called every 500,000 interrupts (500ms) + timerAlarmEnable(timer); + + init_state_flg = false; +} + +void comms_state() +{ + switch (comms_to_do) + { + case GET_REAL_TIME_WIFI: + wifi_get_set_rtc(); + break; + case GET_SCHEDULE_WIFI: + wifi_get_schedule(); + break; + case ALERT_MISSED_WIFI: + wifi_alert_missed(); + break; + case GET_REAL_TIME_BLE: + ble_get_set_rtc(); + break; + case GET_SCHEDULE_BLE: + ble_get_schedule(); + break; + case GET_PATIENT_ID_BLE: + ble_get_patient_id(); + break; + case GET_WIFI_BLE: + ble_get_wifi(); + break; + default: + break; + } + + comms_to_do = 0; + comms_state_flg = false; +} + +void operate_state() +{ + check_capsules(); + if (time_known) + { + if (schedule_known) + { + check_schedule(); + } + else + { + comms_to_do = GET_SCHEDULE_WIFI; + comms_state_flg = true; + } + } + else + { + comms_to_do = GET_REAL_TIME_WIFI; + comms_state_flg = true; + } + operate_state_flg = false; +} + +void idle_state(void) +{ + // No executable code + // This state exists only to provide the FSM + // with a default state to enter if no state + // flags are set +} + +/*********************************************** +* * +* WiFi Functions * +* * +***********************************************/ + +void setup_wifi() +{ + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) + { + delay(500); + Serial.println("Connecting to WiFi.."); + } + if (WiFi.status() == WL_CONNECTED) + { + Serial.println("Connected!"); + } +} + +void wifi_get_set_rtc() +{ + HTTPClient http; + + http.begin("https://skinnerserver.ddns.net/eee3035/GetDateTime.php"); // Specify destination for HTTP request + + http.addHeader("Content-Type", "application/x-www-form-urlencoded"); + // Data to send with HTTP POST + String httpRequestData = ""; + // Send HTTP POST request + int httpResponseCode = http.POST(httpRequestData); + + + if(httpResponseCode>0) + { + String response = http.getString(); // Get the response to the request + + Serial.println(httpResponseCode); // Print return code + Serial.println(response); // Print request answer + + char str[4] = ""; + str[0] = response[2]; + str[1] = response[3]; + str[2] = response[4]; + str[3] = response[5]; + int year = atoi(str); + str[0] = response[7]; + str[1] = response[8]; + str[2] = 'X'; // Dummy, can't be converted to int + str[3] = 'X'; + int month = atoi(str); + str[0] = response[10]; + str[1] = response[11]; + str[2] = 'X'; + str[3] = 'X'; + int day = atoi(str); + str[0] = response[15]; + str[1] = response[16]; + str[2] = 'X'; + str[3] = 'X'; + int hour = atoi(str); + str[0] = response[20]; + str[1] = response[21]; + str[2] = 'X'; + str[3] = 'X'; + int minute = atoi(str); + str[0] = response[25]; + str[1] = response[26]; + str[2] = 'X'; + str[3] = 'X'; + int second = atoi(str); + Serial.println(year); + Serial.println(month); + Serial.println(day); + Serial.println(hour); + Serial.println(minute); + Serial.println(second); + + rtc.setTime(second, minute, hour, day, month, year); + + time_known = true; + } + else + { + Serial.print("Error on sending POST: "); + Serial.println(httpResponseCode); + } +} + +void wifi_get_schedule() +{ + HTTPClient http; + + // Specify destination for HTTP request + http.begin("https://skinnerserver.ddns.net/eee3035/GetSchedule.php"); + + http.addHeader("Content-Type", "application/x-www-form-urlencoded"); + // Data to send with HTTP POST + String httpRequestData = "username=" + patient_id; + // Send HTTP POST request + int httpResponseCode = http.POST(httpRequestData); + + + if(httpResponseCode>0) + { + String response = http.getString(); // Get request response + + Serial.println(httpResponseCode); // Print return code + Serial.println(response); // Print request answer + + load_schedule(response); + } + else + { + Serial.print("Error on sending POST: "); + Serial.println(httpResponseCode); + } +} + +void wifi_alert_missed() +{ + int hour_int = rtc.getHour(); + int minute_int = rtc.getMinute(); + if (minute_int < 15) + { + if (hour_int == 0) + { + hour_int = 24; + } + else + { + hour_int--; + } + minute_int += 45; + } + else + { + minute_int -= 15; + } + + String hour_str = (String)hour_int; + String minute_str = (String)minute_int; + + Serial.print("Hour: "); + Serial.println(hour_str); + Serial.print("Minute: "); + Serial.println(minute_str); + + HTTPClient http; + + http.begin("https://skinnerserver.ddns.net/eee3035/SetMissedDose.php"); // Specify destination for HTTP request + + http.addHeader("Content-Type", "application/x-www-form-urlencoded"); + // Data to send with HTTP POST + String httpRequestData = "username=" + patient_id + "&hour=" + hour_str + "&minute=" + minute_str; + // Send HTTP POST request + int httpResponseCode = http.POST(httpRequestData); + + + if(httpResponseCode>0) + { + String response = http.getString(); // Get the response to the request + + Serial.println(httpResponseCode); // Print return code + Serial.println(response); // Print request answer + } + else + { + Serial.print("Error on sending POST: "); + Serial.println(httpResponseCode); + } +} + +/*********************************************** +* * +* Bluetooth Functions * +* * +***********************************************/ + +void setup_bluetooth() +{ + /*BLEDevice::init("Pillable1"); // Create and name the BLE Device <- Causes a memory leak!! + + // Create the BLE Server + BLEServer *MyServer = BLEDevice::createServer(); + MyServer->setCallbacks(new ServerCallbacks()); // Set the function that handles Server Callbacks + + // Add a service to our server + BLEService *customService = MyServer->createService(BLEUUID((uint16_t)0x1700)); // A random ID has been selected + + // Add a characteristic to the service + customService->addCharacteristic(&customCharacteristic); //customCharacteristic was defined above + + // Add Descriptors to the Characteristic + customCharacteristic.addDescriptor(new BLE2902()); //Add this line only if the characteristic has the Notify property + + BLEDescriptor VariableDescriptor(BLEUUID((uint16_t)0x2901)); + VariableDescriptor.setValue("Temperature -40-60°C"); // Use this format to add a hint for the user. This is optional. + customCharacteristic.addDescriptor(&VariableDescriptor); + + // Configure Advertising with the Services to be advertised + MyServer->getAdvertising()->addServiceUUID(serviceID); + + // Start the service + customService->start(); + + // Start the Server/Advertising + MyServer->getAdvertising()->start(); + + Serial.println("Waiting for a Client to connect...");*/ +} + +void test_bluetooth_comms() +{ + /*static uint8_t value = 0; + Serial.println(value); + if (deviceConnected) + { + // Set the value + if (!hasConnected) + { + Serial.println("Device connected"); + } + hasConnected = true; + customCharacteristic.setValue(&value,1); // This is a value of a single byte + customCharacteristic.notify(); // Notify the client of a change + if (++value >= 256) + { + value = 0; + } + } + else + { + deviceConnected = false; + if (hasConnected) + { + Serial.println("Device disconnected"); + setup_bluetooth(); + hasConnected = false; + } + }*/ +} + +// The following functions have not yet been implemented +// at a low level, they currently just exist to show how +// they would be implemented in the future +void ble_get_set_rtc() {} +void ble_get_schedule() {} +void ble_get_patient_id() {} +void ble_get_wifi() {} + +/*********************************************** +* * +* Other Functions * +* * +***********************************************/ + +void load_schedule(String sched_str) +{ + bool done_sched = false; + int dose_cnt = 0; + char str[9] = ""; + int sched_pos = 3; + int str_pos = 0; + + while (!done_sched) + { + for (int i = 0; i < 9; i++) + { + str[i] = 'X'; + } + while (sched_str[sched_pos] != '"') + { + str[str_pos] = sched_str[sched_pos]; + str_pos++; + sched_pos++; + } + sched_pos += 2; + if (str[0] == 'M' && str[1] == 'o' && str[2] == 'n') + { + schedule[dose_cnt][0] = 1; + } + else if (str[0] == 'T' && str[1] == 'u' && str[2] == 'e') + { + schedule[dose_cnt][0] = 2; + } + else if (str[0] == 'W' && str[1] == 'e' && str[2] == 'd') + { + schedule[dose_cnt][0] = 3; + } + else if (str[0] == 'T' && str[1] == 'h' && str[2] == 'u') + { + schedule[dose_cnt][0] = 4; + } + else if (str[0] == 'F' && str[1] == 'r' && str[2] == 'i') + { + schedule[dose_cnt][0] = 5; + } + else if (str[0] == 'S' && str[1] == 'a' && str[2] == 't') + { + schedule[dose_cnt][0] = 6; + } + else if (str[0] == 'S' && str[1] == 'u' && str[2] == 'n') + { + schedule[dose_cnt][0] = 7; + } + else {} + + // Hour: + str[0] = sched_str[sched_pos]; + sched_pos++; + if (sched_str[sched_pos] != ',') // Could be one digit or two + { + str[1] = sched_str[sched_pos]; + sched_pos += 2; + } + else + { + sched_pos++; + str[1] = 'X'; // Dummy, can't be converted to int + } + for (int i = 2; i < 9; i++) + { + str[i] = 'X'; // Dummy, can't be converted to int + } + schedule[dose_cnt][1] = atoi(str); + + // Minute: + str[0] = sched_str[sched_pos]; + sched_pos++; + if (sched_str[sched_pos] != ',') + { + str[1] = sched_str[sched_pos]; + sched_pos += 2; + } + else + { + sched_pos++; + str[1] = 'X'; // Dummy, can't be converted to int + } + for (int i = 2; i < 9; i++) + { + str[i] = 'X'; // Dummy, can't be converted to int + } + schedule[dose_cnt][2] = atoi(str); + + // Capsule: + str[0] = sched_str[sched_pos]; + sched_pos++; + if (sched_str[sched_pos] != ']') + { + str[1] = sched_str[sched_pos]; + sched_pos ++; + } + else + { + str[1] = 'X'; // Dummy, can't be converted to int + } + for (int i = 2; i < 9; i++) + { + str[i] = 'X'; // Dummy, can't be converted to int + } + schedule[dose_cnt][3] = atoi(str); + + // Find end: + sched_pos ++; + if (sched_str[sched_pos] == ']') + { + done_sched = true; + } + else + { + sched_pos += 3; + } + dose_cnt++; + str_pos = 0; + } + schedule_known = true; + + for (int i = 0; i < 10; i++) + { + Serial.print(schedule[i][0]); + Serial.print(" "); + Serial.print(schedule[i][1]); + Serial.print(" "); + Serial.print(schedule[i][2]); + Serial.print(" "); + Serial.println(schedule[i][3]); + Serial.println(" "); + } +} + +void check_capsules() +{ + int high_threshold = 1200; // High hysteresis value is 1.2V + int low_threshold = 800; // Low hysteresis value is 800mV + + int adc = analogRead(PIN_CAP1_LDR); // Read in capsule 1 LDR voltage + + float voltage_1 = (float)adc; + voltage_1 /= 4095; // Divide by max number of adc values + voltage_1 *= 3300; // Multiply by +ve rail voltage + + adc = analogRead(PIN_CAP2_LDR); // Read in capsule 2 LDR voltage + + float voltage_2 = (float)adc; + voltage_2 /= 4095; + voltage_2 *= 3180; + + adc = analogRead(PIN_CAP3_LDR); // Read in capsule 3 LDR voltage + + float voltage_3 = (float)adc; + voltage_3 /= 4095; + voltage_3 *= 3180; + + if (cap1_open) // Check capsule 1's LDR + { + if (voltage_1 > high_threshold) // Check if voltage has gone above higher boundary + { + cap1_open = false; + } + } + else + { + if (voltage_1 < low_threshold) // Check if voltage has gone below lower boundary + { + cap1_open = true; + } + } + + if (cap2_open) // Check capsule 2's LDR + { + if (voltage_2 > high_threshold) + { + cap2_open = false; + } + } + else + { + if (voltage_2 < low_threshold) + { + cap2_open = true; + } + } + + if (cap3_open) // Check capsule 3's LDR + { + if (voltage_3 > high_threshold) + { + cap3_open = false; + } + } + else + { + if (voltage_3 < low_threshold) + { + cap3_open = true; + } + } +} + +void check_schedule() +{ + static int finish_time = 0; + static bool buzzer_on = false; + static int capsule = 0; + int day = rtc.getDayofWeek(); + int hour = rtc.getHour(true); // True for 24hr format + int minute = rtc.getMinute(); + int second = rtc.getSecond(); + + if (day == 0) // getDayofWeek() returns Sunday as 0 + { + day = 7; + } + + // Check if a dose is due + for (int dose = 0; dose < 20; dose++) + { + if (!buzzer_on && (day == schedule[dose][0]) && (hour == schedule[dose][1]) && (minute == schedule[dose][2])) + { + capsule = schedule[dose][3]; + switch(capsule) // Turn on the LED corresponding with the capsule to be opened + { + case 1: + digitalWrite(PIN_CAP1_LED, HIGH); + break; + case 2: + digitalWrite(PIN_CAP2_LED, HIGH); + break; + case 3: + digitalWrite(PIN_CAP3_LED, HIGH); + break; + default: + break; + } + digitalWrite(PIN_BUZZER, HIGH); + finish_time = minute + 15; + if (finish_time >= 60) + { // Make sure we loop back to the start of the hour + finish_time -= 60; + } + buzzer_on = true; + } + } + + // Control buzzer: + int second_digit = second % 10; + if (buzzer_on && (second_digit == 1 || second_digit == 3 || second_digit == 5)) + { + digitalWrite(PIN_BUZZER, HIGH); + } + else + { + digitalWrite(PIN_BUZZER, LOW); + } + + // Check if the relevant capsule has been opened + bool opened = false; + if ((cap1_open && capsule == 1) || (cap2_open && capsule == 2) || (cap3_open && capsule == 3)) + { + opened = true; + } + + // Control LED: + if (buzzer_on && (minute == finish_time || opened)) + { // If 15 mins is up or the capsule has been opened, turn off all LEDs + digitalWrite(PIN_CAP1_LED, LOW); + digitalWrite(PIN_CAP2_LED, LOW); + digitalWrite(PIN_CAP3_LED, LOW); + buzzer_on = false; // Buzzer can be turned off + capsule = 0; + if (!opened) + { // The capsule has not been opened within 15 minutes, alert server + Serial.println("Dose missed"); + comms_to_do = ALERT_MISSED_WIFI; + comms_state_flg = true; + } + } + + // Since we know the time here, it's a convenient time to check if it's + // midnight and update the schedule in case it's changed + if (hour == 0 && minute == 0 && second == 0) + { + schedule_known = false; // This flag will be examined in operate state + } +} -- GitLab