#include "Adafruit_ThinkInk.h" #include #include #include #include #include "esp_sleep.h" //PIN LAYOUTS #define SRAM_CS 32 #define EPD_CS 15 #define EPD_DC 33 #define EPD_RESET -1 // can set to -1 and share with microcontroller Reset! #define EPD_BUSY 27 // can set to -1 to not use a pin (will wait a fixed delay) #define EPD_ENABLE 12 #define BATTERY_PIN A13 ThinkInk_290_Tricolor_Z10 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY); #define COLOR0 EPD_WHITE #define COLOR1 EPD_BLACK #define COLOR2 EPD_RED const char* ssid = "Breqtest"; const char* password = "aidsaids"; const char* nightscoutUrl = "http://nightscout.chrispr.org:8082/api/v1/entries?count=20"; // TIME CONSTANTS const long gmtOffset_sec = -18000; const int daylightOffset_sec = 3600; const char* ntpServer = "pool.ntp.org"; #define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */ #define TIME_TO_SLEEP 5 /* Time ESP32 will go to sleep (in seconds) */ void setup() { // put your setup code here, to run once: Serial.begin(115200); while (!Serial) { delay(10); } Serial.println("Nightscout CGM BS Monitor starting..."); //turn EPD on pinMode(EPD_ENABLE, OUTPUT); digitalWrite(EPD_ENABLE, HIGH); ConnectToWifi(); String nightscoutResponse = httpGETRequest(nightscoutUrl); Serial.println(nightscoutResponse); DynamicJsonDocument doc(8192); DeserializationError error = deserializeJson(doc, nightscoutResponse); if (error) { Serial.print(F("deserializeJson() failed: ")); Serial.println(error.f_str()); return; } JsonArray array = doc.as(); display.begin(); display.clearBuffer(); RenderGraph(array); RenderRightPane(array[0]); RenderBatteryPercentage(); display.display(); String dateString = array[0]["dateString"].as(); int lastReadInSecondsWithinTheHour = GetLastSyncTimeInSecondsWithinHour(dateString); Serial.print("lastReadInSecondsWithinTheHour: "); Serial.println(lastReadInSecondsWithinTheHour); randomSeed(analogRead(0)); //re-sync every 5th time or so if(random(5) == 0) SleepWithTimeSync(lastReadInSecondsWithinTheHour); else SleepFiveMins(); } int GetLastSyncTimeInSecondsWithinHour(String dateString) { //"dateString":"2022-02-11T19:59:42.481-0500" int idx = dateString.indexOf("T"); int minutesInTheHour = dateString.substring(idx+4, idx+6).toInt(); int secondsInTheHour = dateString.substring(idx+7, idx+9).toInt(); return (minutesInTheHour * 60) + secondsInTheHour; } void SleepWithTimeSync(int lastReadingInSeconds) { configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); struct tm timeinfo; if(!getLocalTime(&timeinfo)){ Serial.println("Failed to obtain time"); SleepFiveMins(); } //Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S"); int secondsInTheHour = (timeinfo.tm_min * 60) + timeinfo.tm_sec; Serial.print("currentSecondsWithinTheHour"); Serial.println(secondsInTheHour); int secondsToSleep = 300 - (secondsInTheHour - lastReadingInSeconds); Serial.print("Would like to sleep "); Serial.println(secondsToSleep); if(secondsToSleep > 0 && secondsToSleep < 300) { //buffer for new processing secondsToSleep = secondsToSleep + 7; esp_sleep_enable_timer_wakeup(secondsToSleep * uS_TO_S_FACTOR); Serial.flush(); TurnOffEPD(); esp_deep_sleep_start(); } else { SleepFiveMins(); } } void RenderBatteryPercentage() { int batteryPinInput = analogRead(BATTERY_PIN); Serial.printf("raw adc value %d", batteryPinInput); //Vin = 3.3(read/4098) float batteryVoltage = 3.3 * (float(batteryPinInput) / 4098.0); //ESP32 A13 returns half the voltage, so double this reading batteryVoltage = batteryVoltage * 2.0; Serial.print("calc. voltage "); Serial.println(batteryVoltage); //Render on screen display.setCursor(245, 110); display.setTextSize(2); if(batteryVoltage < 3.6) display.setTextColor(COLOR2); else display.setTextColor(COLOR1); display.print(batteryVoltage); display.print("v"); } void TurnOffEPD() { digitalWrite(EPD_ENABLE, LOW); delay(1000); gpio_hold_en(GPIO_NUM_12); gpio_deep_sleep_hold_en(); } void SleepFiveMins() { esp_sleep_enable_timer_wakeup(285 * uS_TO_S_FACTOR); Serial.flush(); TurnOffEPD(); esp_deep_sleep_start(); } void RenderGraph(JsonArray sgvRecords) { boolean redrawScreen = true; double x = 0; double y = 100; double cnt = 0; for(JsonVariant v : sgvRecords) { JsonObject obj = v.as(); Serial.println(obj["sgv"].as()); if(obj["device"].as().indexOf("xDrip") == -1) continue; if(cnt > 45) continue; Graph(display, cnt, obj["sgv"].as(), 20, 110, 210, 105, 0,45, 5,60,180,10,"", "", "", COLOR1, COLOR1, COLOR2, COLOR1, COLOR0, redrawScreen); cnt += 5; } } void RenderTrendArrow(String trend) { int charSize = 4; if(trend.equalsIgnoreCase("DoubleUp")) { display.drawChar(235, 60, 0x18, COLOR2, COLOR0, charSize); display.drawChar(255, 60, 0x18, COLOR2, COLOR0, charSize); } else if(trend.equalsIgnoreCase("SingleUp")) { display.drawChar(235, 60, 0x18, COLOR2, COLOR0, charSize); } else if(trend.equalsIgnoreCase("FortyFiveUp")) { display.drawChar(235, 60, 0xBA, COLOR1, COLOR0, charSize); } else if(trend.equalsIgnoreCase("Flat")) { display.drawChar(235, 60, 0x1A, COLOR1, COLOR0, charSize); } else if (trend.equalsIgnoreCase("FortyFiveDown")) { display.drawChar(235, 60, 0xC9, COLOR1, COLOR0, charSize); } else if( trend.equalsIgnoreCase("SingleDown")) { display.drawChar(235, 60, 0x19, COLOR2, COLOR0, charSize); } else if (trend.equalsIgnoreCase("DoubleDown")) { display.drawChar(235, 60, 0x19, COLOR2, COLOR0, charSize); display.drawChar(255, 60, 0x19, COLOR2, COLOR0, charSize); } else { Serial.print("An unrecognized trend was received. The trend is "); Serial.println(trend); } } void RenderRightPane(JsonVariant sgvRecord) { display.setCursor(235, 10); display.setTextSize(3); display.setTextColor(COLOR2); display.print(sgvRecord["sgv"].as()); display.setCursor(235, 35); display.setTextSize(3); display.setTextColor(COLOR1); int delta = int(sgvRecord["delta"].as()); if(delta > 0) display.print("+"); display.print(delta); //display.setCursor(235, 70); //display.setTextSize(3); //display.setTextColor(COLOR1); //display.drawChar(235, 60, 0x1A, COLOR1, COLOR0, 3); RenderTrendArrow(sgvRecord["direction"].as()); //time String sysTime = sgvRecord["sysTime"].as(); int idx = sysTime.indexOf("T"); String timePart = sysTime.substring(idx + 1, idx + 6); display.setCursor(235, 90); display.setTextSize(2); display.setTextColor(COLOR1); display.print(timePart); } void loop() { // Because we deep sleep, this will _NEVER_ be run } String httpGETRequest(const char* url) { HTTPClient http; // Your IP address with path or Domain name with URL path http.begin(url); http.addHeader("Content-Type", "application/json"); http.addHeader("Accept", "application/json"); // Send HTTP POST request int httpResponseCode = http.GET(); String payload = "{}"; if (httpResponseCode>0) { Serial.print("HTTP Response code: "); Serial.println(httpResponseCode); payload = http.getString(); } else { Serial.print("Error code: "); Serial.println(httpResponseCode); } // Free resources http.end(); return payload; } void ConnectToWifi() { //WiFi.mode(WIFI_STA); //WiFi.disconnect(); WiFi.begin(ssid, password); Serial.println("Connecting to WiFi..."); while(WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("status:" ); Serial.println(WiFi.status()); Serial.println(WiFi.localIP()); } Serial.println(""); Serial.print("Connected to WiFi network with IP Address: "); Serial.println(WiFi.localIP()); } /* function to draw a cartesian coordinate system and plot whatever data you want just pass x and y and the graph will be drawn huge arguement list &d name of your display object x = x data point y = y datapont gx = x graph location (lower left) gy = y graph location (lower left) w = width of graph h = height of graph xlo = lower bound of x axis xhi = upper bound of x asis xinc = division of x axis (distance not count) ylo = lower bound of y axis yhi = upper bound of y asis yinc = division of y axis (distance not count) title = title of graph xlabel = x asis label ylabel = y asis label gcolor = graph line colors acolor = axi ine colors pcolor = color of your plotted data tcolor = text color bcolor = background color &redraw = flag to redraw graph on fist call only */ double ox , oy ; void Graph(ThinkInk_290_Tricolor_Z10 &d, double x, double y, double gx, double gy, double w, double h, double xlo, double xhi, double xinc, double ylo, double yhi, double yinc, String title, String xlabel, String ylabel, unsigned int gcolor, unsigned int acolor, unsigned int pcolor, unsigned int tcolor, unsigned int bcolor, boolean &redraw) { double ydiv, xdiv; // initialize old x and old y in order to draw the first point of the graph // but save the transformed value // note my transform funcition is the same as the map function, except the map uses long and we need doubles //static double ox = (x - xlo) * ( w) / (xhi - xlo) + gx; //static double oy = (y - ylo) * (gy - h - gy) / (yhi - ylo) + gy; double i; double temp; int rot, newrot; if (redraw == true) { redraw = false; ox = (x - xlo) * ( w) / (xhi - xlo) + gx; oy = (y - ylo) * (gy - h - gy) / (yhi - ylo) + gy; // draw y scale for ( i = ylo; i <= yhi; i += yinc) { // compute the transform temp = (i - ylo) * (gy - h - gy) / (yhi - ylo) + gy; if (i == 0) { d.drawLine(gx, temp, gx + w, temp, acolor); } else { d.drawLine(gx, temp, gx + w, temp, gcolor); } d.setTextSize(1); d.setTextColor(tcolor, bcolor); d.setCursor(gx - 20, temp); // precision is default Arduino--this could really use some format control d.println(int(i)); } // draw x scale for (i = xlo; i <= xhi; i += xinc) { // compute the transform temp = (i - xlo) * ( w) / (xhi - xlo) + gx; if (i == 0) { d.drawLine(temp, gy, temp, gy - h, acolor); } else { d.drawLine(temp, gy, temp, gy - h, gcolor); } d.setTextSize(1); d.setTextColor(tcolor, bcolor); d.setCursor(temp, gy + 5); // precision is default Arduino--this could really use some format control d.println(int(i)); } //now draw the labels d.setTextSize(2); d.setTextColor(tcolor, bcolor); d.setCursor(gx , gy - h - 30); d.println(title); d.setTextSize(1); d.setTextColor(acolor, bcolor); d.setCursor(gx , gy + 20); d.println(xlabel); d.setTextSize(1); d.setTextColor(acolor, bcolor); d.setCursor(gx - 30, gy - h - 10); d.println(ylabel); } //graph drawn now plot the data // the entire plotting code are these few lines... // recall that ox and oy are initialized as static above x = (x - xlo) * ( w) / (xhi - xlo) + gx; y = (y - ylo) * (gy - h - gy) / (yhi - ylo) + gy; d.drawLine(ox, oy, x, y, pcolor); d.drawLine(ox, oy + 1, x, y + 1, pcolor); d.drawLine(ox, oy - 1, x, y - 1, pcolor); ox = x; oy = y; }