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