Advertisement
Guest User

La Crosse Weather Station

a guest
Nov 11th, 2024
149
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 28.94 KB | None | 0 0
  1. #include <TJpg_Decoder.h>
  2. #define USE_LINE_BUFFER  // Enable for faster rendering
  3. #define FS_NO_GLOBALS
  4. #include <FS.h>
  5. #ifdef ESP32
  6. #include "SPIFFS.h"  // ESP32 only
  7. #endif
  8. #include "SPI.h"
  9. #include <TFT_eSPI.h>  // Hardware-specific library
  10.  
  11.  #define MATRIX_WIDTH                192   // Single panel of 64 pixel width
  12.  
  13.  #define MATRIX_HEIGHT               96
  14.  #define FACTOR                       3
  15.  
  16.  #define CHAIN_LENGTH                3   // Number of modules chained together, i.e. 4 panels chained result in virtualmatrix 64x4=256 px long
  17.  
  18.  
  19. #include <Wire.h>
  20. #include <Arduino.h>
  21. #include "Adafruit_SHT31.h"
  22. #include <BlynkSimpleEsp32.h>
  23. #include <WiFi.h>
  24. #include <AsyncTCP.h>
  25. #include <ESPAsyncWebServer.h>
  26. #include <AsyncElegantOTA.h>
  27. #include "time.h"
  28.  
  29.  
  30. #include "constants.h"
  31. #include "ApiHandler.h"
  32. #include "./util.hpp"
  33.  
  34.  
  35. unsigned long time_counter = 0;
  36. ApiHandler* apiHandler;
  37.  
  38. //Define a score struct
  39. struct Score {
  40.   int home;
  41.   int away;
  42. };
  43.  
  44. TFT_eSPI tft = TFT_eSPI();  // Invoke custom library
  45.  
  46. Score currentScore;
  47.  
  48. HTTPClient http;
  49. DynamicJsonDocument doc(JSON_BUFFER_SIZE);
  50.  
  51. class Game {
  52. private:
  53.   String homeTeam;
  54.   int homeTeamId;
  55.   String awayTeam;
  56.   int awayTeamId;
  57.   int homeTeamScore;
  58.   int awayTeamScore;
  59.   int currentInning;
  60.   bool topOfInning;
  61.   bool firstBaseOccupied;
  62.   bool secondBaseOccupied;
  63.   bool thirdBaseOccupied;
  64.  
  65.   /* data */
  66. public:
  67.   Game(JsonObject game);
  68.   ~Game();
  69.   String getHomeTeam() {
  70.     return this->homeTeam;
  71.   };
  72.   int getHomeTeamId() {
  73.     return this->homeTeamId;
  74.   };
  75.   String getAwayTeam() {
  76.     return this->awayTeam;
  77.   };
  78.   int getAwayTeamId() {
  79.     return this->awayTeamId;
  80.   };
  81.   int getHomeTeamScore() {
  82.     return this->homeTeamScore;
  83.   };
  84.   int getAwayTeamScore() {
  85.     return this->awayTeamScore;
  86.   };
  87.   int getCurrentInning() {
  88.     return this->currentInning;
  89.   };
  90.   bool isTopOfInning() {
  91.     return this->topOfInning;
  92.   };
  93.   bool isFirstBaseOccupied() {
  94.     return this->firstBaseOccupied;
  95.   };
  96.   bool isSecondBaseOccupied() {
  97.     return this->secondBaseOccupied;
  98.   };
  99.   bool isThirdBaseOccupied() {
  100.     return this->thirdBaseOccupied;
  101.   };
  102. };
  103.  
  104. Game::Game(JsonObject game) {
  105.   this->homeTeam = game["teams"]["home"]["team"]["abbreviation"].as<String>();
  106.   this->homeTeamId = game["teams"]["home"]["team"]["id"].as<int>();
  107.   this->awayTeam = game["teams"]["away"]["team"]["abbreviation"].as<String>();
  108.   this->awayTeamId = game["teams"]["away"]["team"]["id"].as<int>();
  109.   this->homeTeamScore = game["linescore"]["teams"]["home"]["runs"].as<int>();
  110.   this->awayTeamScore = game["linescore"]["teams"]["away"]["runs"].as<int>();
  111.   this->currentInning = game["linescore"]["currentInning"].as<int>();
  112.   this->topOfInning = game["linescore"]["isTopInning"].as<bool>();
  113.   this->firstBaseOccupied = game["linescore"]["offense"]["first"].as<bool>();
  114.   this->secondBaseOccupied = game["linescore"]["offense"]["second"].as<bool>();
  115.   this->thirdBaseOccupied = game["linescore"]["offense"]["third"].as<bool>();
  116.  
  117.   Serial.println(game["linescore"]["currentInning"].as<int>());
  118. }
  119.  
  120. Game::~Game() {
  121. }
  122.  
  123.  
  124.  
  125.  
  126. class GameDrawer {
  127. private:
  128.   void drawLayout(Game* game);
  129.   void drawScores(Game* game);
  130.   void drawBox(int16_t x, int16_t y, String text, uint16_t bg_color, uint16_t fg_color);
  131.   void drawInning(Game* game);
  132.   void topInningTriangle();
  133.   void bottomInningTriangle();
  134.   void drawBases(Game* game);
  135.   void drawBase(int16_t x, int16_t y, bool occupied);
  136. public:
  137.   GameDrawer();
  138.   ~GameDrawer();
  139.   void drawGame(Game* game);
  140.   void drawLoading();
  141. };
  142.  
  143. GameDrawer gameDrawer;
  144.  
  145.  
  146. GameDrawer::GameDrawer(/* args */) {
  147. }
  148.  
  149. GameDrawer::~GameDrawer() {
  150. }
  151.  
  152. void GameDrawer::drawGame(Game* game) {
  153.   drawLayout(game);
  154.   drawScores(game);
  155.   drawBases(game);
  156. }
  157.  
  158. void GameDrawer::drawLayout(Game* game) {
  159.   tft.fillScreen(TFT_BLACK);
  160.   tft.setTextColor(tft.color565(255, 255, 255));
  161.  
  162.   int16_t upperLeftX, upperLeftY;
  163.   uint16_t width, height;
  164.  
  165.   drawBox(15, 15, game->getAwayTeam(), TEAM_BG_COLORS.find(game->getAwayTeamId())->second, TEAMS_TEXT_COLORS.find(game->getAwayTeamId())->second);
  166.   drawInning(game);
  167.   drawBox(MATRIX_WIDTH - 21, 15, game->getHomeTeam(), TEAM_BG_COLORS.find(game->getHomeTeamId())->second, TEAMS_TEXT_COLORS.find(game->getHomeTeamId())->second);
  168. }
  169.  
  170. void GameDrawer::topInningTriangle() {
  171.   tft.fillTriangle(24*FACTOR, 7*FACTOR, 32*FACTOR, 7*FACTOR, 28*FACTOR, 3*FACTOR, tft.color565(255, 255, 255));
  172. }
  173.  
  174. void GameDrawer::bottomInningTriangle() {
  175.   tft.fillTriangle(24*FACTOR, 3*FACTOR, 32*FACTOR, 3*FACTOR, 28*FACTOR, 7*FACTOR, tft.color565(255, 255, 255));
  176. }
  177.  
  178. void GameDrawer::drawInning(Game* game) {
  179.   if (game->isTopOfInning()) {
  180.     topInningTriangle();
  181.   } else {
  182.     bottomInningTriangle();
  183.   }
  184.   tft.setCursor(35*FACTOR, 2*FACTOR);
  185.   tft.print(game->getCurrentInning());
  186. }
  187.  
  188. void GameDrawer::drawBox(int16_t x, int16_t y, String text, uint16_t bg_color, uint16_t fg_color) {
  189.   int16_t upperLeftX, upperLeftY;
  190.   uint16_t width, height;
  191.  
  192.   //tft.getTextBounds(text.c_str(), x + BOX_PADDING, y + BOX_PADDING, &upperLeftX, &upperLeftY, &width, &height);
  193.   upperLeftX = x+2;
  194.   upperLeftY = y+2;
  195.   width = tft.textWidth(text.c_str());
  196.   height = 17;
  197.   tft.setCursor(upperLeftX, upperLeftY);
  198.  
  199.   // Draw Away team box - should always start at 0,0
  200.   tft.fillRect(x, y, width + 2 * BOX_PADDING - 1, height + 2 * BOX_PADDING - 1, bg_color);
  201.   tft.drawRect(x, y, width + 2 * BOX_PADDING - 1, height + 2 * BOX_PADDING - 1, fg_color);
  202.   tft.setTextColor(fg_color);
  203.   tft.print(text);
  204.   tft.setTextColor(COLORS::WHITE);
  205. }
  206.  
  207. void GameDrawer::drawScores(Game* game) {
  208.   drawBox(6*FACTOR, 13*FACTOR, String(game->getAwayTeamScore()), COLORS::BLACK, COLORS::WHITE);
  209.  
  210.   drawBox(MATRIX_WIDTH - 15, 13*FACTOR, String(game->getHomeTeamScore()), COLORS::BLACK, COLORS::WHITE);
  211. }
  212.  
  213. void GameDrawer::drawLoading() {
  214.   tft.fillScreen(TFT_BLACK);
  215.   tft.setTextColor(tft.color565(255, 255, 255));
  216.   tft.setCursor(0, 0);
  217.   tft.print("Loading...");
  218. }
  219.  
  220. /**
  221.  * @brief Draws a diamond base using two triangles
  222.  * @param x X coordinate of the top verex of the base
  223.  * @param y Y coordinate of the top verex of the base
  224.  * @param occupied if the base is occupied
  225. */
  226. void GameDrawer::drawBase(int16_t x, int16_t y, bool occupied) {
  227.   if (occupied) {
  228.     tft.fillTriangle(x, y, x - BASE_SIDE_LENGTH + 1, y + BASE_SIDE_LENGTH - 1, x, y + (2 * (BASE_SIDE_LENGTH - 1)), COLORS::YELLOW);
  229.     tft.fillTriangle(x, y, x + BASE_SIDE_LENGTH - 1, y + BASE_SIDE_LENGTH - 1, x, y + (2 * (BASE_SIDE_LENGTH - 1)), COLORS::YELLOW);
  230.   } else {
  231.     tft.drawTriangle(x, y, x - BASE_SIDE_LENGTH + 1, y + BASE_SIDE_LENGTH - 1, x, y + (2 * (BASE_SIDE_LENGTH - 1)), COLORS::YELLOW);
  232.     tft.drawTriangle(x, y, x + BASE_SIDE_LENGTH - 1, y + BASE_SIDE_LENGTH - 1, x, y + (2 * (BASE_SIDE_LENGTH - 1)), COLORS::YELLOW);
  233.  
  234.     // Remove line in the middle of the base
  235.     tft.drawLine(x, y + 1, x, y + (2 * (BASE_SIDE_LENGTH - 1)) - 1, COLORS::BLACK);
  236.   }
  237. }
  238.  
  239. void GameDrawer::drawBases(Game* game) {
  240.   drawBase(BASES_TOP_X + BASE_SIDE_LENGTH + 1, BASES_TOP_Y + BASE_SIDE_LENGTH + 1, game->isFirstBaseOccupied());
  241.   drawBase(BASES_TOP_X, BASES_TOP_Y, game->isSecondBaseOccupied());
  242.   drawBase(BASES_TOP_X - BASE_SIDE_LENGTH - 1, BASES_TOP_Y + BASE_SIDE_LENGTH + 1, game->isThirdBaseOccupied());
  243. }
  244.  
  245.  
  246.  
  247.  
  248.  
  249.  
  250. TFT_eSprite img = TFT_eSprite(&tft);
  251. TFT_eSprite img2 = TFT_eSprite(&tft);
  252. TFT_eSprite imgOrr = TFT_eSprite(&tft);  // Sprite class
  253.  
  254. #include <HTTPClient.h>
  255.  
  256. #include "support_functions.h"
  257.  
  258. #define VERSION 1.04
  259.  
  260. String titleLine = "***INDIANA v" + String(VERSION) + "***";
  261.  
  262. const char* ssid = "xxxxxx";
  263. const char* password = "xxxxxx";
  264. String powerstring = "0W";
  265.  
  266. /*#define AA_FONT_10 "YuGothicUI-Regular-10"
  267. #define AA_FONT_12 "YuGothicUI-Regular-12"
  268. #define AA_FONT_14 "YuGothicUI-Regular-14"
  269. #define AA_FONT_16 "YuGothicUI-Regular-16"
  270. #define AA_FONT_18 "YuGothicUI-Regular-18"*/
  271. #define AA_FONT_20 "YuGothicUI-Regular-20"
  272. #define AA_FONT_22 "NotoSans-Condensed-22"
  273. #define AA_FONT_24 "NotoSans-Condensed-24"
  274. #define AA_FONT_26 "NotoSans-Condensed-26"
  275.  
  276. const char* ntpServer = "pool.ntp.org";
  277. const long gmtOffset_sec = -18000;    //Replace with your GMT offset (secs)
  278. const int daylightOffset_sec = 3600;  //Replace with your daylight offset (secs)
  279. int hours, mins, secs;
  280.  
  281. char auth[] = "xxxxxx";
  282.  
  283. AsyncWebServer server(80);
  284.  
  285.  
  286. WidgetTerminal terminal(V10);
  287.  
  288.  
  289.  
  290. #define sunX tft.width() / 2
  291. #define sunY tft.height() / 2
  292.  
  293. uint16_t orb_inc;
  294. uint16_t planet_r;
  295.  
  296. #include <stdio.h>
  297. #include "astronomy.h"
  298. #define TIME_TEXT_BYTES 25
  299.  
  300. astro_time_t astro_time;
  301.  
  302. uint16_t grey;
  303.  
  304. static const astro_body_t body[] = {
  305.   BODY_SUN, BODY_MERCURY, BODY_VENUS, BODY_EARTH, BODY_MARS,
  306.   BODY_JUPITER, BODY_SATURN, BODY_URANUS, BODY_NEPTUNE
  307. };
  308.  
  309. static const uint16_t bodyColour[] = {
  310.   TFT_YELLOW, TFT_DARKGREY, TFT_ORANGE, TFT_BLUE, TFT_RED,
  311.   TFT_GOLD, TFT_BROWN, TFT_DARKCYAN, TFT_CYAN
  312. };
  313.  
  314. //TFT_eSprite img3 = TFT_eSprite(&tft);
  315. #define LED_PIN 32
  316.  
  317. int page = 1;
  318. uint16_t t_x = 0, t_y = 0;        // To store the touch coordinates
  319. uint16_t oldt_x = 0, oldt_y = 0;  // To store the touch coordinates
  320.  
  321.  
  322. #define every(interval) \
  323.   static uint32_t __every__##interval = millis(); \
  324.   if (millis() - __every__##interval >= interval && (__every__##interval = millis()))
  325.  
  326. bool enableHeater = false;
  327. uint8_t loopCnt = 0;
  328.  
  329. Adafruit_SHT31 sht31 = Adafruit_SHT31();
  330.  
  331. #define CALIBRATION_FILE "/TouchCalData1"
  332.  
  333. // Set REPEAT_CAL to true instead of false to run calibration
  334. // again, otherwise it will only be done once.
  335. // Repeat calibration if you change the screen rotation.
  336. #define REPEAT_CAL false
  337.  
  338.  
  339.  
  340. // Using two fonts since numbers are nice when bold
  341. #define LABEL1_FONT &FreeSansOblique12pt7b  // Key label font 1
  342. #define LABEL2_FONT &FreeSansBold12pt7b     // Key label font 2
  343.  
  344.  
  345. // We have a status line for messages
  346. #define STATUS_X 120  // Centred on this
  347. #define STATUS_Y 65
  348.  
  349. // Create 15 keys for the keypad
  350. char keyLabel[15][5] = { "New", "Del", "Send", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "0", "#" };
  351. uint16_t keyColor[15] = { TFT_RED, TFT_DARKGREY, TFT_DARKGREEN,
  352.                           TFT_BLUE, TFT_BLUE, TFT_BLUE,
  353.                           TFT_BLUE, TFT_BLUE, TFT_BLUE,
  354.                           TFT_BLUE, TFT_BLUE, TFT_BLUE,
  355.                           TFT_BLUE, TFT_BLUE, TFT_BLUE };
  356.  
  357. // Invoke the TFT_eSPI button class and create all the button objects
  358. TFT_eSPI_Button key[15];
  359.  
  360. // This next function will be called during decoding of the jpeg file to
  361. // render each block to the TFT.  If you use a different TFT library
  362. // you will need to adapt this function to suit.
  363. bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap) {
  364.   // Stop further decoding as image is running off bottom of screen
  365.   if (y >= tft.height()) return 0;
  366.  
  367.   // This function will clip the image block rendering automatically at the TFT boundaries
  368.   tft.pushImage(x, y, w, h, bitmap);
  369.  
  370.   // This might work instead if you adapt the sketch to use the Adafruit_GFX library
  371.   // tft.drawRGBBitmap(x, y, bitmap, w, h);
  372.  
  373.   // Return 1 to decode next block
  374.   return 1;
  375. }
  376.  
  377.  
  378.  
  379. //------------------------------------------------------------------------------------------
  380.  
  381. void touch_calibrate() {
  382.   uint16_t calData[5];
  383.   uint8_t calDataOK = 0;
  384.  
  385.   // check file system exists
  386.   if (!SPIFFS.begin()) {
  387.     Serial.println("formatting file system");
  388.     SPIFFS.format();
  389.     SPIFFS.begin();
  390.   }
  391.  
  392.   // check if calibration file exists and size is correct
  393.   if (SPIFFS.exists(CALIBRATION_FILE)) {
  394.     if (REPEAT_CAL) {
  395.       // Delete if we want to re-calibrate
  396.       SPIFFS.remove(CALIBRATION_FILE);
  397.     } else {
  398.       File f = SPIFFS.open(CALIBRATION_FILE, "r");
  399.       if (f) {
  400.         if (f.readBytes((char*)calData, 14) == 14)
  401.           calDataOK = 1;
  402.         f.close();
  403.       }
  404.     }
  405.   }
  406.  
  407.   if (calDataOK && !REPEAT_CAL) {
  408.     // calibration data valid
  409.     tft.setTouch(calData);
  410.   } else {
  411.     // data not valid so recalibrate
  412.     tft.fillScreen(TFT_BLACK);
  413.     tft.setCursor(20, 0);
  414.     tft.setTextFont(2);
  415.     tft.setTextSize(1);
  416.     tft.setTextColor(TFT_WHITE, TFT_BLACK);
  417.  
  418.     tft.println("Touch corners as indicated");
  419.  
  420.     tft.setTextFont(1);
  421.     tft.println();
  422.  
  423.     if (REPEAT_CAL) {
  424.       tft.setTextColor(TFT_RED, TFT_BLACK);
  425.       tft.println("Set REPEAT_CAL to false to stop this running again!");
  426.     }
  427.  
  428.     tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15);
  429.  
  430.     tft.setTextColor(TFT_GREEN, TFT_BLACK);
  431.     tft.println("Calibration complete!");
  432.  
  433.     // store data
  434.     File f = SPIFFS.open(CALIBRATION_FILE, "w");
  435.     if (f) {
  436.       f.write((const unsigned char*)calData, 14);
  437.       f.close();
  438.     }
  439.   }
  440. }
  441.  
  442. //------------------------------------------------------------------------------------------
  443.  
  444. float temp;
  445. float hum;
  446.  
  447. void printLocalTime() {
  448.   time_t rawtime;
  449.   struct tm* timeinfo;
  450.   time(&rawtime);
  451.   timeinfo = localtime(&rawtime);
  452.   terminal.println(asctime(timeinfo));
  453.   terminal.flush();
  454. }
  455.  
  456. int brightness = 32;
  457.  
  458. BLYNK_WRITE(V1) {
  459.   brightness = param.asInt();
  460.   analogWrite(LED_PIN, brightness);
  461. }
  462.  
  463. float temppool, pm25in, pm25out, bridgetemp, bridgehum, windspeed, winddir, windchill, windgust, humidex, bridgeco2, bridgeIrms, watts, kw, tempSHT, humSHT, co2SCD, presBME;
  464.  
  465. BLYNK_WRITE(V71) {
  466.   pm25in = param.asFloat();
  467. }
  468.  
  469. BLYNK_WRITE(V61) {
  470.   temppool = param.asFloat();
  471. }
  472.  
  473.  
  474. BLYNK_WRITE(V62) {
  475.   bridgetemp = param.asFloat();
  476. }
  477. BLYNK_WRITE(V63) {
  478.   bridgehum = param.asFloat();
  479. }
  480. BLYNK_WRITE(V64) {
  481.   windchill = param.asFloat();
  482. }
  483. BLYNK_WRITE(V65) {
  484.   humidex = param.asFloat();
  485. }
  486. BLYNK_WRITE(V66) {
  487.   windgust = param.asFloat();
  488. }
  489. BLYNK_WRITE(V67) {
  490.   pm25out = param.asFloat();
  491. }
  492. BLYNK_WRITE(V68) {
  493.   windspeed = param.asFloat();
  494. }
  495. BLYNK_WRITE(V69) {
  496.   winddir = param.asFloat();
  497. }
  498.  
  499.  
  500.  
  501.  
  502. BLYNK_WRITE(V77) {
  503.   bridgeco2 = param.asFloat();
  504. }
  505.  
  506. BLYNK_WRITE(V81) {
  507.   bridgeIrms = param.asFloat();
  508.   watts = bridgeIrms;
  509.   kw = watts / 1000.0;
  510. }
  511.  
  512. BLYNK_WRITE(V91) {
  513.   tempSHT = param.asFloat();
  514. }
  515. BLYNK_WRITE(V92) {
  516.   humSHT = param.asFloat();
  517. }
  518. BLYNK_WRITE(V93) {
  519.   co2SCD = param.asFloat();
  520. }
  521.  
  522. BLYNK_WRITE(V94) {
  523.   presBME = param.asFloat();
  524. }
  525.  
  526.  
  527. BLYNK_WRITE(V10) {
  528.   if (String("help") == param.asStr()) {
  529.     terminal.println("==List of available commands:==");
  530.     terminal.println("wifi");
  531.     terminal.println("==End of list.==");
  532.   }
  533.   if (String("wifi") == param.asStr()) {
  534.     terminal.print("Connected to: ");
  535.     terminal.println(ssid);
  536.     terminal.print("IP address:");
  537.     terminal.println(WiFi.localIP());
  538.     terminal.print("Signal strength: ");
  539.     terminal.println(WiFi.RSSI());
  540.     printLocalTime();
  541.   }
  542. }
  543.  
  544. String windDirection(int temp_wind_deg)  //Source http://snowfence.umn.edu/Components/winddirectionanddegreeswithouttable3.htm
  545. {
  546.   switch (temp_wind_deg) {
  547.     case 0 ... 11:
  548.       return "N";
  549.       break;
  550.     case 12 ... 33:
  551.       return "NNE";
  552.       break;
  553.     case 34 ... 56:
  554.       return "NE";
  555.       break;
  556.     case 57 ... 78:
  557.       return "ENE";
  558.       break;
  559.     case 79 ... 101:
  560.       return "E";
  561.       break;
  562.     case 102 ... 123:
  563.       return "ESE";
  564.       break;
  565.     case 124 ... 146:
  566.       return "SE";
  567.       break;
  568.     case 147 ... 168:
  569.       return "SSE";
  570.       break;
  571.     case 169 ... 191:
  572.       return "S";
  573.       break;
  574.     case 192 ... 213:
  575.       return "SSW";
  576.       break;
  577.     case 214 ... 236:
  578.       return "SW";
  579.       break;
  580.     case 237 ... 258:
  581.       return "WSW";
  582.       break;
  583.     case 259 ... 281:
  584.       return "W";
  585.       break;
  586.     case 282 ... 303:
  587.       return "WNW";
  588.       break;
  589.     case 304 ... 326:
  590.       return "NW";
  591.       break;
  592.     case 327 ... 348:
  593.       return "NNW";
  594.       break;
  595.     case 349 ... 360:
  596.       return "N";
  597.       break;
  598.     default:
  599.       return "error";
  600.       break;
  601.   }
  602. }
  603.  
  604. // =========================================================================
  605. // Get coordinates of end of a vector, pivot at x,y, length r, angle a
  606. // =========================================================================
  607. // Coordinates are returned to caller via the xp and yp pointers
  608. #define DEG2RAD 0.0174532925
  609. void getCoord(int x, int y, int* xp, int* yp, int r, float a) {
  610.   float sx1 = cos(-a * DEG2RAD);
  611.   float sy1 = sin(-a * DEG2RAD);
  612.   *xp = sx1 * r + x;
  613.   *yp = sy1 * r + y;
  614. }
  615.  
  616. // =========================================================================
  617. // Convert astronomical time to UTC and display
  618. // =========================================================================
  619. void showTime(astro_time_t time) {
  620.   astro_status_t status;
  621.   char text[TIME_TEXT_BYTES];
  622.  
  623.   status = Astronomy_FormatTime(time, TIME_FORMAT_SECOND, text, sizeof(text));
  624.   if (status != ASTRO_SUCCESS) {
  625.     fprintf(stderr, "\nFATAL(PrintTime): status %d\n", status);
  626.     exit(1);
  627.   }
  628.   tft.setTextDatum(TL_DATUM);
  629.   tft.drawString(text, 15, 10, 2);
  630. }
  631.  
  632. // =========================================================================
  633. // Plot planet positions as an Orrery
  634. // =========================================================================
  635. int plot_planets(void) {
  636.   astro_angle_result_t ang;
  637.  
  638.   int i;
  639.   int num_bodies = sizeof(body) / sizeof(body[0]);
  640.  
  641.   // i initialised to 1 so Sun is skipped
  642.   for (i = 1; i < num_bodies; ++i) {
  643.     ang = Astronomy_EclipticLongitude(body[i], astro_time);
  644.  
  645.     int x1 = 0;  // getCoord() will update these
  646.     int y1 = 0;
  647.  
  648.     getCoord(0, 0, &x1, &y1, i * 14, ang.angle);  // Get x1 ,y1
  649.  
  650.     imgOrr.fillSprite(TFT_TRANSPARENT);
  651.     imgOrr.fillCircle(9, 9, 5, TFT_BLACK);
  652.     imgOrr.drawCircle(9 - x1, 9 - y1, i * 14, grey);
  653.     imgOrr.fillCircle(9, 9, 3, bodyColour[i]);
  654.     imgOrr.pushSprite(sunX + x1 - 9, sunY + y1 - 9, TFT_TRANSPARENT);
  655.  
  656.     if (body[i] == BODY_EARTH) {
  657.       astro_angle_result_t mang = Astronomy_LongitudeFromSun(BODY_MOON, astro_time);
  658.  
  659.       int xm = 0;
  660.       int ym = 0;
  661.  
  662.       getCoord(x1, y1, &xm, &ym, 7, 180 + ang.angle + mang.angle);  // Get x1 ,y1
  663.  
  664.       imgOrr.fillSprite(TFT_TRANSPARENT);
  665.       imgOrr.fillCircle(9, 9, 4, TFT_BLACK);
  666.       imgOrr.drawCircle(9 - xm, 9 - ym, i * 14, grey);
  667.       imgOrr.fillCircle(9, 9, 1, TFT_WHITE);
  668.       imgOrr.pushSprite(sunX + xm - 9, sunY + ym - 9, TFT_TRANSPARENT);
  669.     }
  670.   }
  671.  
  672.   return 0;
  673. }
  674.  
  675. void prepOrrery() {
  676.   tft.fillScreen(TFT_BLACK);
  677.   astro_time = Astronomy_MakeTime(2020, 10, 16, 19, 31, 0);
  678.   tft.fillCircle(sunX, sunY, 5, TFT_YELLOW);  //10
  679.  
  680.   // i initialised to 1 so Sun is skipped
  681.   for (int i = 1; i < sizeof(body) / sizeof(body[0]); ++i) {
  682.     tft.drawCircle(sunX, sunY, i * 14, grey);
  683.   }
  684. }
  685.  
  686. void prepMLB() {
  687.   tft.fillScreen(TFT_BLACK);
  688.   tft.setTextSize(2);
  689.   tft.setTextColor(0xFFFF, 0x0000);
  690.     JsonObject schedule = apiHandler->getTeamScheduleToday(TEAM_ID::TORONTO_BLUE_JAYS);
  691.     Game* game = new Game(schedule["dates"][0]["games"][0].as<JsonObject>());
  692.   gameDrawer.drawGame(game);
  693. }
  694.  
  695. void prepDisplay() {
  696.   tft.fillScreen(TFT_BLACK);
  697.   TJpgDec.drawFsJpg(0, 0, "/ui.jpg");
  698. }
  699.  
  700. void prepDisplay2() {
  701.   tft.fillScreen(TFT_BLACK);
  702.   tft.setTextSize(1);
  703.   TJpgDec.drawFsJpg(0, 0, "/pg2.jpg");
  704. }
  705.  
  706. void doDisplay() {
  707.  
  708.   //float pm25in, pm25out, bridgetemp, bridgehum, windspeed, winddir, windchill, windgust, humidex, bridgeco2, bridgeIrms, watts, kw, tempSHT, humSHT, co2SCD;
  709.  
  710.   String tempstring = String(tempSHT, 1) + "°C";
  711.   String humstring = String(humSHT, 1) + "%";
  712.   String windstring = String(windspeed, 0) + "kph";
  713.   String pm25instring = String(pm25in, 0) + "g";
  714.   String upco2string = String(co2SCD, 0) + "p";
  715.   String presstring = String(presBME, 0) + "m";
  716.   String poolstring = String(temppool, 1) + "°C";
  717.  
  718.   String outtempstring = String(bridgetemp, 1) + "°C";
  719.   String outdewstring = String(bridgehum, 1) + "°C";
  720.   String winddirstring = windDirection(winddir);
  721.   String pm25outstring = String(pm25out, 0) + "g";
  722.   String downco2string = String(bridgeco2, 0) + "p";
  723.   // if (watts < 1000) {String powerstring = String(watts,0) + "W";}
  724.  
  725.   String powerstring = String(kw, 1) + "KW";
  726.  
  727.  
  728.   //String touchstring = String(t_x) + "," + String(t_y);
  729.   tft.setTextDatum(TR_DATUM);
  730.   img.fillSprite(TFT_BLACK);
  731.   img.drawString(tempstring, 73, 21);
  732.   img.drawString(humstring, 73, 62);
  733.   img.drawString(windstring, 73, 104);
  734.   img.drawString(pm25instring, 73, 146);
  735.   img.drawString(upco2string, 73, 192);
  736.   img.drawString(presstring, 73, 231);
  737.   img.drawString(poolstring, 73, 277);
  738.   img.pushSprite(46, 0);
  739.  
  740.   img2.fillSprite(TFT_BLACK);
  741.   img2.drawString(outtempstring, 73, 21);
  742.   img2.drawString(outdewstring, 73, 62);
  743.   img2.drawString(winddirstring, 73, 104);
  744.   img2.drawString(pm25outstring, 73, 146);
  745.   img2.drawString(downco2string, 73, 192);
  746.   img2.drawString(powerstring, 73, 231);
  747.   img2.pushSprite(155, 0);
  748. }
  749.  
  750. void doDisplay2() {
  751.   tft.setTextDatum(TR_DATUM);
  752.   tft.setTextFont(1);
  753.   tft.setCursor(115, 237);
  754.   tft.print(titleLine);
  755.   tft.setCursor(115, 247);
  756.   tft.print(ssid);
  757.   tft.setCursor(115, 257);
  758.   tft.print(WiFi.localIP());
  759.   time_t rawtime;
  760.   struct tm* timeinfo;
  761.   time(&rawtime);
  762.   timeinfo = localtime(&rawtime);
  763.   tft.setCursor(115, 267);
  764.   tft.print(asctime(timeinfo));
  765.   tft.setCursor(115, 277);
  766.   tft.print("My Temp: ");
  767.   tft.print(temp);
  768.   tft.print(" C");
  769.   tft.setCursor(115, 287);
  770.   tft.print("My Hum: ");
  771.   tft.print(hum);
  772.   tft.println("%");
  773. }
  774.  
  775.  
  776.  
  777. void doOrrery() {
  778.   plot_planets();
  779.   showTime(astro_time);
  780.  
  781.   // Add time increment (more than 0.6 days will lead to stray pixel on screen
  782.   // due to the way previous object images are erased)
  783.   astro_time = Astronomy_AddDays(astro_time, 0.25);  // 0.25 day (6 hour) increment
  784. }
  785.  
  786. void doMLB() {
  787.   every(30000) {
  788.     JsonObject schedule = apiHandler->getTeamScheduleToday(TEAM_ID::TORONTO_BLUE_JAYS);
  789.     Game* game = new Game(schedule["dates"][0]["games"][0].as<JsonObject>());
  790.     gameDrawer.drawGame(game);
  791.   }
  792. }
  793.  
  794. bool isSleeping = false;
  795.  
  796. void setup() {
  797.  
  798.   pinMode(LED_PIN, OUTPUT);
  799.   analogWrite(LED_PIN, brightness);
  800.  
  801.   Serial.begin(115200);
  802.   Serial.println("\n\n Testing TJpg_Decoder library");
  803.   Wire.begin(26, 25);
  804.   // Initialise SPIFFS
  805.   if (!SPIFFS.begin()) {
  806.     Serial.println("SPIFFS initialisation failed!");
  807.     while (1) yield();  // Stay here twiddling thumbs waiting
  808.   }
  809.   Serial.println("\r\nInitialisation done.");
  810.  
  811.   // Initialise the TFT
  812.   tft.begin();
  813.  
  814.   tft.setTextColor(0xFFFF, 0x0000);
  815.   tft.fillScreen(TFT_BLACK);
  816.   tft.setCursor(10, 10);
  817.   tft.setTextColor(TFT_WHITE, TFT_BLACK, true);
  818.   tft.setTextWrap(true);  // Wrap on width
  819.   tft.setTextFont(2);
  820.   tft.setTextSize(1);
  821.   tft.print("Connecting...");
  822.   tft.setCursor(15, 25);
  823.  
  824.   WiFi.mode(WIFI_STA);
  825.   WiFi.begin(ssid, password);
  826.   while (WiFi.status() != WL_CONNECTED) {
  827.     delay(250);
  828.     tft.print(".");
  829.   }
  830.   tft.fillScreen(TFT_BLACK);
  831.   tft.setCursor(15, 10);
  832.   tft.print("Connected!");
  833.   tft.setCursor(15, 25);
  834.   tft.print(titleLine);
  835.   tft.setCursor(15, 40);
  836.   tft.print(ssid);
  837.   tft.setCursor(15, 65);
  838.   tft.print(WiFi.localIP());
  839.   configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  840.   time_t rawtime;
  841.   struct tm* timeinfo;
  842.   time(&rawtime);
  843.   timeinfo = localtime(&rawtime);
  844.   tft.setCursor(15, 80);
  845.   tft.print(asctime(timeinfo));
  846.   tft.setCursor(15, 95);
  847.  
  848.   server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
  849.     request->send(200, "text/plain", "Hi! I am Indiana.");
  850.   });
  851.  
  852.   AsyncElegantOTA.begin(&server);  // Start ElegantOTA
  853.   server.begin();
  854.   tft.print("OTA at /update.");
  855.  
  856.   tft.setCursor(15, 110);
  857.   tft.print("Touch to continue.");
  858.  
  859.   //setPngPosition(0, 0);
  860.   //load_png("https://i.imgur.com/EeCUlxr.png");
  861.  
  862.   while(!tft.getTouch(&t_x, &t_y)){}
  863.   touch_calibrate();  
  864.   tft.setTextWrap(false);  // Wrap on width
  865.   img.setColorDepth(16);
  866.   img2.setColorDepth(16);
  867.   // ESP32 will crash if any of the fonts are missing
  868.   bool font_missing = false;
  869.   /*if (SPIFFS.exists("/YuGothicUI-Regular-10.vlw")    == false) font_missing = true;
  870.   if (SPIFFS.exists("/YuGothicUI-Regular-12.vlw")    == false) font_missing = true;
  871.   if (SPIFFS.exists("/YuGothicUI-Regular-14.vlw")    == false) font_missing = true;
  872.   if (SPIFFS.exists("/YuGothicUI-Regular-16.vlw")    == false) font_missing = true;
  873.   if (SPIFFS.exists("/YuGothicUI-Regular-18.vlw")    == false) font_missing = true;*/
  874.   if (SPIFFS.exists("/YuGothicUI-Regular-20.vlw") == false) font_missing = true;
  875.   if (SPIFFS.exists("/NotoSans-Condensed-22.vlw") == false) font_missing = true;
  876.   if (SPIFFS.exists("/NotoSans-Condensed-24.vlw") == false) font_missing = true;
  877.   if (SPIFFS.exists("/NotoSans-Condensed-26.vlw") == false) font_missing = true;
  878.   if (font_missing) {
  879.     Serial.println("\r\nFont missing in SPIFFS, did you upload it?");
  880.     tft.print("ERROR Fonts missing.");
  881.     while (1) yield();
  882.   }
  883.   tft.setSwapBytes(true);  // We need to swap the colour bytes (endianess)
  884.   // The jpeg image can be scaled by a factor of 1, 2, 4, or 8
  885.   TJpgDec.setJpgScale(1);
  886.   // The decoder must be given the exact name of the rendering function above
  887.   TJpgDec.setCallback(tft_output);
  888.  
  889.  
  890.  
  891.   Serial.println("SHT31 test");
  892.   if (!sht31.begin(0x44)) {  // Set to 0x45 for alternate i2c addr
  893.     Serial.println("Couldn't find SHT31");
  894.   } else {
  895.     Serial.println("Found SHT31");
  896.   }
  897.   temp = sht31.readTemperature();
  898.   hum = sht31.readHumidity();
  899.  
  900.   //uint16_t t_x = 0, t_y = 0; // To store the touch coordinates
  901.  
  902.  
  903.   //configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  904.   Blynk.config(auth, IPAddress(192, 168, 50, 197), 8080);
  905.   Blynk.connect();
  906.  
  907.   terminal.println(titleLine);
  908.   terminal.print("Connected to ");
  909.   terminal.println(ssid);
  910.   terminal.print("IP address: ");
  911.   terminal.println(WiFi.localIP());
  912.   printLocalTime();
  913.   terminal.flush();
  914.  
  915.   img.loadFont(AA_FONT_26);
  916.   img.createSprite(73, 320);
  917.   img.setTextDatum(TR_DATUM);
  918.   img.setTextColor(TFT_WHITE, TFT_BLACK, true);
  919.  
  920.   img2.loadFont(AA_FONT_26);
  921.   img2.createSprite(73, 263);
  922.   img2.setTextDatum(TR_DATUM);
  923.   img2.setTextColor(TFT_WHITE, TFT_BLACK, true);
  924.  
  925.   imgOrr.createSprite(19, 19);
  926.   grey = tft.color565(30, 30, 30);
  927.  
  928.   prepDisplay();
  929.   doDisplay();
  930.   tft.setTextFont(1);
  931.   apiHandler = new ApiHandler(&http, &doc);
  932. }
  933.  
  934. void loop() {
  935.   Blynk.run();
  936.   bool pressed = tft.getTouch(&t_x, &t_y);
  937.   if (pressed) {
  938.     tft.fillSmoothCircle(t_x, t_y, 4, TFT_YELLOW, TFT_BLACK);
  939.     every(250) {
  940.       Serial.print(t_x);
  941.       Serial.print(",");
  942.       Serial.println(t_y);
  943.     }
  944.  
  945.  
  946.     if (page == 3) {
  947.       page = 1;
  948.       prepDisplay();
  949.       doDisplay();
  950.     }
  951.     if (page == 4) {
  952.       page = 1;
  953.       prepDisplay();
  954.       doDisplay();
  955.     }
  956.     if (page == 2) {
  957.       if ((t_x > 31) && (t_y > 227) && (t_x < 100) && (t_y < 285)) {  //BACK button
  958.         delay(100);
  959.         page = 1;
  960.         prepDisplay();
  961.         doDisplay();
  962.       }
  963.       if ((t_x > 30) && (t_y > 30) && (t_x < 100) && (t_y < 87)) {  //BRIGHTNESS DOWN button
  964.         delay(250);
  965.         brightness -= 16;
  966.         if (brightness < 1) { brightness = 1; }
  967.         if (brightness > 255) { brightness = 255; }
  968.         analogWrite(LED_PIN, brightness);
  969.         Blynk.virtualWrite(V1, brightness);
  970.       }
  971.       if ((t_x > 137) && (t_y > 30) && (t_x < 205) && (t_y < 87)) {  //BRIGHTNESS UP button
  972.         delay(250);
  973.         brightness += 16;
  974.         if (brightness < 1) { brightness = 1; }
  975.         if (brightness > 255) { brightness = 255; }
  976.         analogWrite(LED_PIN, brightness);
  977.         Blynk.virtualWrite(V1, brightness);
  978.       }
  979.       if ((t_x > 31) && (t_y > 121) && (t_x < 102) && (t_y < 182)) {  //ORRERY  button
  980.         page = 3;
  981.         prepOrrery();
  982.         doOrrery();
  983.         delay(300);
  984.       }
  985.       if ((t_x > 137) && (t_y > 121) && (t_x < 205) && (t_y < 182)) {  //MLB button
  986.         page = 4;
  987.         prepMLB();
  988.         doMLB();
  989.         delay(300);
  990.       }
  991.     }
  992.     if (page == 1) {                                                   //MAIN display
  993.       if ((t_x > 130) && (t_y > 268) && (t_x < 240) && (t_y < 320)) {  //SETTINGS button
  994.         delay(100);
  995.         page = 2;
  996.         prepDisplay2();
  997.         doDisplay2();
  998.       }
  999.     }
  1000.   }
  1001.  
  1002.   // Pressed will be set true is there is a valid touch on the screen
  1003.  
  1004.  
  1005.   every(3000) {
  1006.     if (page == 1) { doDisplay(); }
  1007.     if (page == 2) { doDisplay2(); }
  1008.   }
  1009.   if (page == 3) { doOrrery(); }
  1010.  
  1011.   every(60000) {
  1012.     temp = sht31.readTemperature();
  1013.     hum = sht31.readHumidity();
  1014.         struct tm timeinfo;
  1015.   getLocalTime(&timeinfo);
  1016.   hours = timeinfo.tm_hour;
  1017.   mins = timeinfo.tm_min;
  1018.   secs = timeinfo.tm_sec;
  1019.     if ((hours == 18) && (!isSleeping)){
  1020.       for(int i=brightness; i<255; i++)
  1021.       {
  1022.         analogWrite(LED_PIN, i);
  1023.         delay(40);
  1024.       }
  1025.       analogWrite(LED_PIN, 32);
  1026.       isSleeping = true;
  1027.     }
  1028.       if ((hours == 7) && (isSleeping)){
  1029.       for (int i=255; i>brightness; i--)
  1030.         {
  1031.           analogWrite(LED_PIN, i);
  1032.           delay(40);
  1033.         }
  1034.         analogWrite(LED_PIN, brightness);
  1035.       isSleeping = false;
  1036.     }
  1037.   }
  1038.   delay(10);  // UI debouncing
  1039. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement