Browse Source

working prototype

Toby Chui 1 year ago
parent
commit
336b46a468
8 changed files with 204 additions and 219 deletions
  1. 78 29
      InkyDash/InkyDash.ino
  2. 14 11
      InkyDash/draw.ino
  3. 14 0
      InkyDash/time.ino
  4. 76 0
      InkyDash/utils.ino
  5. 22 179
      InkyDash/weather.ino
  6. BIN
      lib/ESP8266TrueRandom.zip
  7. BIN
      lib/GxEPD-master.zip
  8. BIN
      lib/WiFiManager.zip

+ 78 - 29
InkyDash/InkyDash.ino

@@ -32,12 +32,17 @@
 
 /* WiFi and Connections */
 #include <ESP8266WiFi.h>
+#include <WiFiManager.h>
+#include <ESP8266TrueRandom.h>
 #include <NTPClient.h>
 #include <WiFiUdp.h>
-#include <WiFiManager.h>
 #include <ArduinoJson.h>
 #include <TaskScheduler.h>
 
+
+/* Power Settings */
+#define LOW_POWER_MODE true
+
 /* WiFi and Connections */
 WiFiManager wifiManager;
 WiFiUDP UDPconn;
@@ -51,30 +56,36 @@ GxEPD_Class display(io, /*RST=D4*/ 2, /*BUSY=D2*/ 4); // default selection of D4
 const String weekDays[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
 const String weeksDaysFull[7] = { "SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY" };
 const String months[12] = {"JANUARY",  "FEBRUARY",  "MARCH",  "APRIL",  "MAY",  "JUNE",  "JULY",  "AUGUST",  "SEPTEMBER",  "OCTOBER",  "NOVEMBER",  "DECEMBER"};
-const String deviceName = "InkyDash v0.1";
-
-/* Forcast */
-int forcastTempMin[7] = {0,0,0,0,0,0,0};
-int forcastTempMax[7] = {25,25,25,25,25,25,25};
-int forcastRhMax[7] = {100,100,100,100,100,100,100};
+const char* ntpServers[] = {
+  "0.debian.pool.ntp.org",
+  "1.debian.pool.ntp.org",
+  "2.debian.pool.ntp.org",
+  "3.debian.pool.ntp.org",
+  "pool.ntp.org",
+  "0.pool.ntp.org",
+  "1.pool.ntp.org",
+  "2.pool.ntp.org",
+  "3.pool.ntp.org",
+  "0.arch.pool.ntp.org",
+  "1.arch.pool.ntp.org",
+  "2.arch.pool.ntp.org",
+  "3.arch.pool.ntp.org"
+};
 
 /* Global Variables */
+const String deviceName = "InkyDash v1.0";
 int currentTemp = 25;
 int currentHumd = 50;
-int currentRain = 0;
-bool currentIsRaining = false;
-bool renderForcast = true;
 
 /* Schedulers */
-/*
-  void datetimeUpdateCallback();
-  void todayWeatherUpdateCallback();
-  void weatherForcastCallback();
-  Task t_dt(10000, TASK_FOREVER, &datetimeUpdateCallback); //Date-time update task
-  Task t_wt(3600000, TASK_FOREVER, &todayWeatherUpdateCallback); //Weather update task
-  Task t_wf(3600000, TASK_FOREVER, &weatherForcastCallback); //Forcast update task
-  Scheduler runner;
-*/
+void datetimeUpdateCallback();
+//void todayWeatherUpdateCallback();
+//void weatherForcastCallback();
+Task t_dt(1800000, TASK_FOREVER, &datetimeUpdateCallback); //Date-time update task
+//Task t_wt(3600000, TASK_FOREVER, &todayWeatherUpdateCallback); //Weather update task
+//Task t_wf(3600000, TASK_FOREVER, &weatherForcastCallback); //Forcast update task
+Scheduler runner;
+
 void setup() {
   //Start debug serial
   Serial.begin(115200);
@@ -84,22 +95,60 @@ void setup() {
 
   //Draw starting frame
   display.setRotation(3);
-  display.drawPaged(drawStartingFrame);
-  delay(100);
+  if (!LOW_POWER_MODE) {
+    display.drawPaged(drawStartingFrame);
+    delay(100);
+  }
 
   // Connect to Wi-Fi
-  wifiManager.setClass("invert");
-  wifiManager.autoConnect("InkyDash Setup");
-
+  /*
+  //Hard code version 
+  WiFi.begin("SSID", "password");
+  while (WiFi.status() != WL_CONNECTED) {
+    delay(500);
+    Serial.print('.');
+  }
+  Serial.println('\n');
+  Serial.println("Connection established!");  
+  Serial.print("IP address:\t");
+  Serial.println(WiFi.localIP());   
+  */
+  
+  wifiManager.setAPCallback(configModeCallback);
+  wifiManager.autoConnect("InkyDash");
+  
+  if (WiFi.status() == WL_CONNECTED) {
+    Serial.println("WiFi Connected");
+  } else {
+    Serial.println("Network error");
+    display.drawPaged(drawNoNetwork);
+    return;
+  }
+
+  delay(1000);
+  
+  //Randomly get one of the NTP server from the list
+  const char* randomNtpServer = getRandomNtpServer(ntpServers, 13);
+  Serial.println("Using NTP Server: " + String(randomNtpServer));
+  NTPClient timeClient(UDPconn, randomNtpServer);
   ntpClient.begin();
   ntpClient.setTimeOffset(28800);
-  
+
   //Draw home page
-  Serial.println("WiFi Connected");
-  display.drawPaged(drawHomeFrame);
+  if (LOW_POWER_MODE) {
+    display.drawPaged(drawHomeFrame);
+    Serial.println("Display updated. Entering deep sleep mode");
+    ESP.deepSleep(1800e6);
+    delay(10);
+    //After deep sleep mode, device will reset and not entering the loop() session
+  } else {
+    runner.init();
+    Serial.println("Initialized scheduler");
+    runner.addTask(t_dt); //Date-time update task
+    t_dt.enable();
+  }
 }
 
 void loop() {
-  // put your main code here, to run repeatedly:
-
+  runner.execute();
 }

+ 14 - 11
InkyDash/draw.ino

@@ -1,9 +1,8 @@
 /*
-   drawutils.ino
+   draw.ino
 
    This files contains utilities for drawing to the display
 
-
 */
 
 //Draw the frame for the home information panel
@@ -14,7 +13,8 @@ void drawHomeFrame() {
   display.fillScreen(GxEPD_WHITE);
   //Draw elements
   drawDate(10, 10); //Draw the date
-  //drawWeatherPallete(display.width() - 110, 10);
+  drawSunMoon(display.width() - 50, 140); //Draw the day / night icon
+  drawWeatherPallete(display.width() - 110, 10); //Draw current temp / humd info
   drawDayProgressBar(200, 10, GxEPD_BLACK);
   drawCalender(250);
   drawLastUpdateTimestamp();
@@ -27,7 +27,7 @@ void drawLastUpdateTimestamp() {
   time_t epochTime = ntpClient.getEpochTime();
   struct tm *ptm = gmtime ((time_t *)&epochTime);
   String currentDate = String(ptm->tm_mday) + "/" + String(ptm->tm_mon + 1) + "/" + String(ptm->tm_year + 1900) + " " + timeZeroPad(ntpClient.getHours()) + ":" + timeZeroPad(ntpClient.getMinutes()) + ":" + timeZeroPad(ntpClient.getSeconds());
-  drawHighlighedText("Update: " + currentDate, 10, display.height() - 22, 5, GxEPD_WHITE, GxEPD_BLACK);
+  drawHighlighedText("Update: " + currentDate, 10, display.height() - 24, 5, GxEPD_WHITE, GxEPD_BLACK);
   display.setTextSize(1);
 }
 
@@ -42,10 +42,14 @@ void drawDayProgressBar(int y, int padding, uint16_t color) {
   int progressBarBlockSize = 3;
   //Calculate how many no of blocks needed to be rendered
   int totalNumberOfBlocks = (display.width() - 2 * padding) / (progressBarBlockSize * 2) + 1;
-  totalNumberOfBlocks = int(float(totalNumberOfBlocks) * percentageOfDayPassed);
+  int numberOfBlocksToRender = int(float(totalNumberOfBlocks) * percentageOfDayPassed);
   display.setTextColor(GxEPD_RED);
-  for (int i = 0; i < totalNumberOfBlocks; i++) {
-    display.fillRect(padding + (i * (progressBarBlockSize * 2)), y + 5, progressBarBlockSize, 8, GxEPD_BLACK);
+  for (int i = 0; i < numberOfBlocksToRender; i++) {
+    int barHeight = 8;
+    if (i == int(totalNumberOfBlocks * 0.25) || i == int(totalNumberOfBlocks * 0.5) || i == int(totalNumberOfBlocks * 0.75)){
+      barHeight= 12;
+    }
+    display.fillRect(padding + (i * (progressBarBlockSize * 2)), y + 5, progressBarBlockSize, barHeight, GxEPD_BLACK);
   }
   display.setTextColor(GxEPD_BLACK);
 }
@@ -110,7 +114,7 @@ void drawCalender(int y) {
   int dayCounter = (1 - firstDayOfWeek);
 
   //Render the day of week line
-  display.fillRect(horizontalPadding, y - 12, display.width() - 2 * horizontalPadding, 30, GxEPD_BLACK);
+  display.fillRect(horizontalPadding, y - 12, display.width() - 2 * horizontalPadding, 30, GxEPD_RED);
   display.setTextColor(GxEPD_WHITE);
   for (int dx = 0; dx < 7; dx++) {
     display.getTextBounds(weekDays[dx], 0, 0, &tbx, &tby, &tbw, &tbh);
@@ -184,11 +188,10 @@ void drawStartingFrame() {
 
   //Draw initializing text
   display.setFont(&FreeSans9pt7b);
-  String waitingText = "Initializing Dashboard";
-  display.getTextBounds(waitingText, 0, 0, &tbx, &tby, &tbw, &tbh);
+  display.getTextBounds(deviceName, 0, 0, &tbx, &tby, &tbw, &tbh);
   x = 20;
   y = display.height() - 30;
-  drawHighlighedText(waitingText, x, y, 5, GxEPD_WHITE, GxEPD_BLACK);
+  drawHighlighedText(deviceName, x, y, 5, GxEPD_WHITE, GxEPD_BLACK);
 }
 
 

+ 14 - 0
InkyDash/time.ino

@@ -3,6 +3,12 @@
 
    This script handle date time related features
 */
+
+//Entry point for scheduler
+void datetimeUpdateCallback(){
+  display.drawPaged(drawHomeFrame);
+}
+
 //Pad any time string with 0
 String timeZeroPad(int input) {
   if (input < 10) {
@@ -34,3 +40,11 @@ bool isNight() {
   // Change this to fit your life style
   return (currentHour >= 19 || currentHour < 7);
 }
+
+//Grab a random NTP server
+const char* getRandomNtpServer(const char* ntpServers[], int numServers) {
+  int randomIndex = ESP8266TrueRandom.random(numServers - 1);
+  //int randomIndex = 0;
+  // Return the NTP server at the randomly chosen index
+  return ntpServers[randomIndex];
+}

+ 76 - 0
InkyDash/utils.ino

@@ -0,0 +1,76 @@
+/*
+ * 
+ * utils.ino
+ * 
+ * Handle utilities functions
+ */
+void configModeCallback(WiFiManager *myWiFiManager) {
+  Serial.println("WiFi not setup or not found. Entered config mode");
+  display.drawPaged(drawWifiSetupInstruction);
+}
+
+//Draw wifi connection error frame
+void drawWifiSetupInstruction() {
+  //Print initializing text
+  int16_t tbx, tby; uint16_t tbw, tbh;
+  display.fillScreen(GxEPD_WHITE);
+
+  //Render some style
+  display.fillRect(0, 0, display.width(), 60, GxEPD_BLACK);
+   
+  //Render instructions
+  display.setFont(&FreeSans12pt7b);
+  display.setTextColor(GxEPD_WHITE);
+  int x = 20;
+  int y = 40;
+  display.setCursor(x, y);
+  display.print("WiFi Setup Instruction");
+  display.setTextColor(GxEPD_BLACK);
+  display.println();
+  display.println();
+  display.setFont(&FreeSans9pt7b);
+  display.println("1. Connect to WiFi w/ SSID \"InkyDash\"");
+  String apIp = WiFi.softAPIP().toString();
+  display.println("2. Open http://" + apIp + " with web browser");
+  display.println("3. Enter your home WiFi SSID & password");
+  display.println("4. Save and wait for device to reboot");
+
+  display.println();
+  display.println();
+  display.println("This page will refresh automatically after the setup is completed.");
+  display.println();
+  display.setTextColor(GxEPD_RED);
+  display.println(deviceName);
+  
+  //Draw initializing text
+  display.setFont(&FreeSans9pt7b);
+  display.getTextBounds("WiFi Setup Mode", 0, 0, &tbx, &tby, &tbw, &tbh);
+  x = 20;
+  y = display.height() - 30;
+  drawHighlighedText("WiFi Setup Mode", x, y, 5, GxEPD_WHITE, GxEPD_RED);
+}
+
+//Draw the loading frame for the WiFi connection and settings
+void drawNoNetwork() {
+  //Print initializing text
+  display.fillScreen(GxEPD_WHITE);
+  int y_offset = -20; //Move the icon up by 20px
+
+  //Draw imus font logo
+  display.setFont(&FreeSans9pt7b);
+  display.setTextColor(GxEPD_BLACK);
+  int16_t tbx, tby; uint16_t tbw, tbh;
+  display.getTextBounds("NETWORK ERROR", 0, 0, &tbx, &tby, &tbw, &tbh);
+  uint16_t x = ((display.width() - tbw) / 2) - tbx;
+  uint16_t y = ((display.height() - tbh) / 2) - tby + y_offset;
+  display.setCursor(x, y);
+  display.print("NETWORK ERROR");
+
+
+  //Draw initializing text
+  display.setFont(&FreeSans9pt7b);
+  display.getTextBounds(deviceName, 0, 0, &tbx, &tby, &tbw, &tbh);
+  x = 20;
+  y = display.height() - 30;
+  drawHighlighedText(deviceName, x, y, 5, GxEPD_WHITE, GxEPD_BLACK);
+}

+ 22 - 179
InkyDash/weather.ino

@@ -19,190 +19,33 @@ void drawWeatherPallete(int x, int y) {
   int lineHeight = 44;
 
   //Print current temperature and humd
-  display.getTextBounds(String(currentTemp) + "C", 0, 0, &tbx, &tby, &tbw, &tbh);
-  display.setCursor(display.width() - tbw - 10, y + tbh);
-  display.print(String(currentTemp) + "C");
-
+  display.getTextBounds(String(currentTemp) + " C", 0, 0, &tbx, &tby, &tbw, &tbh);
+  int tempX = display.width() - tbw - 10;
+  display.setCursor(tempX, y + tbh);
+  display.print(String(currentTemp) + " C");
+  //Draw the degree sign as it is not supported in ASCII
+  display.getTextBounds(String(currentTemp), 0, 0, &tbx, &tby, &tbw, &tbh);
+  display.fillCircle(tempX + tbw + 10, y + 7, 6, textColor);
+  display.fillCircle(tempX + tbw + 10, y + 7, 3, GxEPD_WHITE);
+  
   y += tbh + 10;
+  display.setFont(&FreeSans18pt7b);
   display.getTextBounds(String(currentHumd) + "%", 0, 0, &tbx, &tby, &tbw, &tbh);
   display.setCursor(display.width() - tbw - 10, y + tbh);
   display.print(String(currentHumd) + "%");
-  y += tbh + 10;
-  display.setFont(&FreeSans9pt7b);
-  lineHeight = 18;
-
-  if (renderForcast) {
-    //Render the forcast for coming 5 days
-    //if data grab failed, skip the render
-    String forcast;
-    for (int i = 0; i < 5; i++) {
-      y += lineHeight;
-      forcast = String(forcastTempMin[i]) + "-" + String(forcastTempMax[i]) + "C " + String(forcastRhMax[i]) + "%";
-      display.getTextBounds(forcast, 0, 0, &tbx, &tby, &tbw, &tbh);
-      display.setCursor(display.width() - tbw - 10, y);
-      display.print(forcast);
-    }
-  }
-
 }
 
-
-//Current weather info
-const char* rhurl = "https://data.weather.gov.hk/weatherAPI/opendata/weather.php?dataType=rhrread&lang=en";
-//Weather forcast info
-const char* wfurl = "https://data.weather.gov.hk/weatherAPI/opendata/weather.php?dataType=fnd&lang=en";
-
-/*
-   Weather Forcast Loaders
-*/
-
-bool updateAndRenderForcastInfo() {
-  Serial.println("Updating weather forcast info...");
-  WiFiClientSecure httpsClient;  // Use WiFiClientSecure for HTTPS
-  httpsClient.setInsecure();     // Ignore SSL certificate verification (for simplicity)
-
-  if (httpsClient.connect("data.weather.gov.hk", 443)) {
-    Serial.println("Downloading forcast info");
-    httpsClient.print(String("GET ") + wfurl + " HTTP/1.1\r\n" +
-                      "Host: data.weather.gov.hk\r\n" +
-                      "Connection: close\r\n\r\n");
-
-    String response = "";
-    //Skip through the headers
-    while (httpsClient.connected()) {
-      String line = httpsClient.readStringUntil('\n');
-      if (line == "\r") {
-        break;
-      }
-    }
-
-    //Read the JSON body
-    String line = "";
-    while (httpsClient.available()) {
-      line = httpsClient.readStringUntil('\n');  //Read Line by Line
-      response += line;
-    }
-
-    //Trim the first unwannted byte and the last
-    int startTrimPos = response.indexOf("{");
-    int lastTrimPos = response.lastIndexOf("}");
-    response = response.substring(startTrimPos, lastTrimPos + 1);
-
-    //Serial.println(response);
-    if (response.length() < 10){
-      //TODO HANDLE ERROR
-      return false;
-    }
-    
-    // Build the filter
-    StaticJsonDocument<1024> filter;
-    for (int i = 0; i < 7; i++) {
-      filter["weatherForecast"][i]["week"] = true;
-      filter["weatherForecast"][i]["forecastMintemp"]["value"] = true;
-      filter["weatherForecast"][i]["forecastMaxtemp"]["value"] = true;
-      filter["weatherForecast"][i]["forecastMaxrh"]["value"] = true;
-      filter["weatherForecast"][i]["ForecastIcon"] = true;
-    }
-
-    // Parse JSON data
-    StaticJsonDocument<5120> jsonDoc;
-    auto error = deserializeJson(jsonDoc, response, DeserializationOption::Filter(filter));
-    if (error) {
-      Serial.print(F("deserializeJson() failed with code "));
-      Serial.println(error.c_str());
-      return false;
-    }
-
-    //serializeJsonPretty(jsonDoc, Serial);
-
-    //Render the weather forcast onto the HMI
-    for (int i = 0; i < 7; i++) {
-      //Get day of week
-      String dow = jsonDoc["weatherForecast"][i]["week"];
-      dow = dow.substring(0, 3);
-
-      //Get min-max temp
-      int minTemp = jsonDoc["weatherForecast"][i]["forecastMintemp"]["value"];
-      int maxTemp = jsonDoc["weatherForecast"][i]["forecastMaxtemp"]["value"];
-      int maxRh = jsonDoc["weatherForecast"][i]["forecastMaxrh"]["value"];
-      //Get forcast icon
-      int forcastIcon = jsonDoc["weatherForecast"][i]["ForecastIcon"];
-      //TODO: Implement this
-      //renderForcastField(String(i), dow, minTemp, maxTemp, maxRh, forcastIcon);
-      forcastTempMin[i] = minTemp;
-      forcastTempMax[i] = maxTemp;
-      forcastRhMax[i] = maxRh;
-    }
-  }
-  return true;
-}
-
-/*
-   Current Weather Loaders
-*/
-
-void updateCurrentWeatherInfo() {
-  WiFiClientSecure httpsClient;  // Use WiFiClientSecure for HTTPS
-  httpsClient.setInsecure();     // Ignore SSL certificate verification (for simplicity)
-  
-  if (httpsClient.connect("data.weather.gov.hk", 443)) {
-    Serial.println("Downloading current weather info");
-    httpsClient.print(String("GET ") + rhurl + " HTTP/1.1\r\n" +
-                      "Host: data.weather.gov.hk\r\n" +
-                      "Connection: close\r\n\r\n");
-
-    String response = "";
-    //Skip through the headers
-    while (httpsClient.connected()) {
-      String line = httpsClient.readStringUntil('\n');
-      if (line == "\r") {
-        break;
-      }
-    }
-
-    //Read the JSON body
-    String line;
-    while (httpsClient.available()) {
-      line = httpsClient.readStringUntil('\n');  //Read Line by Line
-      response += line;
-    }
-
-
-    //Trim the first unwannted byte and the last
-    int startTrimPos = response.indexOf("{");
-    int lastTrimPos = response.lastIndexOf("}");
-    response = response.substring(startTrimPos, lastTrimPos + 1);
-
-    //Serial.println(response);
-    
-    // Build the filter
-    StaticJsonDocument<1024> filter;
-    filter["temperature"]["data"][0]["value"] = true;
-    filter["humidity"]["data"][0]["value"] = true;
-    filter["rainfall"]["data"][0]["max"] = true;
-    filter["rainfall"]["data"][0]["main"] = true;
-    
-    // Parse JSON data
-    StaticJsonDocument<4096> jsonDoc;
-    auto error = deserializeJson(jsonDoc, response, DeserializationOption::Filter(filter));
-    if (error) {
-      Serial.print(F("deserializeJson() failed with code "));
-      Serial.println(error.c_str());
-      return;
-    }
-    
-    // Access and return the temperature value
-    currentTemp = jsonDoc["temperature"]["data"][19]["value"];
-    currentHumd = jsonDoc["humidity"]["data"][0]["value"];
-    currentRain = jsonDoc["rainfall"]["data"][16]["max"];
-    currentIsRaining = String(jsonDoc["rainfall"]["data"][16]["main"]) == "TRUE";
-
-    // Serial debug prints
-    Serial.println(int(jsonDoc["temperature"]["data"][19]["value"]));
-    Serial.println(int(jsonDoc["humidity"]["data"][0]["value"]));
-    Serial.println(int(jsonDoc["rainfall"]["data"][16]["max"]));
-    Serial.println(String(jsonDoc["rainfall"]["data"][16]["main"]));
-  } else {
-    Serial.println("Failed to connect to server");
+//Draw the sun or moon icon base on day / night time
+void drawSunMoon(int x, int y){
+  if (isNight()){
+    //Draw moon
+    display.fillCircle(x, y, 40, GxEPD_RED);
+    display.fillCircle(x - 30, y - 15, 38, GxEPD_WHITE);
+  }else{
+    //Draw sun
+    display.fillCircle(x, y, 40, GxEPD_RED);
+    //some clouds
+    display.fillCircle(x + 10, y + 28, 20, GxEPD_WHITE);
+    display.fillCircle(x + 40, y + 20, 30, GxEPD_WHITE);
   }
 }

BIN
lib/ESP8266TrueRandom.zip


BIN
lib/GxEPD-master.zip


BIN
lib/WiFiManager.zip