// ESP32_ILI9341_openweathermap
// downloads json string from openweather.com
// for environmental conditions of (any) town
// displays alphanumerically, with gauges and with icons
//
// updates every five minutes in Loop - check the variable named timerDelay
//
// microcontroller ESP32-WROOM-32
// current display TFT 018 = 320*240 ILI9341 controller
// implements Bodmer's TFT_ESPI library
// implements Bodmer's rainbow scale gauge
//
// json instructions by Rui Santos
// at https://RandomNerdTutorials.com/esp32-http-get-open-weather-map-thingspeak-arduino/
//
// public domain
// July 6, 2021 - minor issues fixed, compass pointer fixed
// Floris Wouterlood
//
// Make sure all the display driver and pin conections are correct by
// editing the User_Setup.h file in the TFT_eSPI library folder.
//
// ######################################################################################
// ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE TFT_eSPI LIBRARY #####
// ######################################################################################
#include <TFT_eSPI.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include "Free_Fonts.h" // include the header file attached to this sketch - part of TFT_eSPI package
#include "zCalibri48.h"
#include "zCalibri24.h"
#include "calibri72.h"
#include "MoonRise.h"
#include <TimeLib.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
WiFiUDP ntpUDP;
// By default 'pool.ntp.org' is used with 60 seconds update interval and
// no offset
NTPClient timeClient(ntpUDP);
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
TFT_eSprite moon_prev = TFT_eSprite(&tft);
TFT_eSprite moon_cur = TFT_eSprite(&tft);
TFT_eSprite moon_next = TFT_eSprite(&tft);
#define WHITE 0xFFFF
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define GREY 0x2108
#define COLOR_CLOCK 0xFDC1
#define COLOR_MOON 0xFFA6
#define SCALE0 0xC655 // accent color for unused scale segments
#define SCALE1 0x5DEE // accent color for unused scale segments
#define TEXT_COLOR 0xFFFF // is currently white
// rainbow scale ring meter color scheme
#define RED2RED 0
#define GREEN2GREEN 1
#define BLUE2BLUE 2
#define BLUE2RED 3
#define GREEN2RED 4
#define RED2GREEN 5
#define DEG2RAD 0.0174532925 // conversion factor degrees to radials
time_t t;
int utcOffset = 3 * 60 * 60;
double latitude = 55.7522 ;
double longitude = 37.6156 ;
// data connection and acquisition variables
const char* ssid = "Wokwi-GUEST"; // network wifi credentials
const char* password = ""; // wifi network key
String openWeatherMapApiKey = "09a7aeb9f0441ca1a0607f253e25d670"; // API Openweathermap account
String city = "Ho Chi Minh City";
String countryCode = "1566083"; // "NL" for The Netherlands
String jsonDocument (1024); // memory allocation json string
// timer internet access for server download
unsigned long lastTime = 0; // testing purposes = 30 seconds
unsigned long timerDelay; // is set at 1 seconds in Setup and to 5 minutes in Loop
// joint variables
float temp_01=10;
float hum_01;
int hum_02;
// rainbow scale ring meter variables
uint32_t runTime = -99999; // time for next update
int reading = 10; // value to be displayed in circular scale
int tesmod = 0;
int rGaugePos_x = 0; // these two variables govern the position
int rGaugePos_y = 155; // of the square + gauge on the display
int ringmeterRadius = 65; // governs diameter of rainbow gauge
char* ringlabel[] = {"","*C","%","mBar"}; // some custom labels
float tempRainbowgauge;
// governs position of numerical output rainbow scale
// small needle meter
int j;
int pivotNeedle_x = 165; // pivot coordinates needle of small gauge
int pivotNeedle_y = 222;
float center_x1 = 160; // center x of edge markers circle left gauge
float center_y1 = 228; // center y of edge markers circle left gauge
int radius_s = 65; // for scale markers
int needleLength = 45; // gauge needle length
int edgemarkerLength = 5; // edge marker length
float edge_x1, edge_y1, edge_x1_out, edge_y1_out;
float angleNeedle = 0;
float needle_x, needle_y;
float needle_x_old, needle_y_old;
float angleCircle = 0;
int pivot_x = 165; // pivot coordinates needle of small gauge
int pivot_y = 222;
// circle segment 'pie chart' meter variables
byte inc = 0;
unsigned int col = 0;
float pivotHumcircle__x = 198;
float pivotHumcircle__y = 274;
float startAngle = 0;
float subAngle; // subtended angle
int r = 34; // circle radius
// compass wind direction pointer
float compassPivot_x = 158;
float compassPivot_y = 65;
float c_x1, c_x2,c_x3, c_x4;
float c_y1, c_y2,c_y3, c_y4;
float c_x1_old,c_x2_old,c_x3_old, c_x4_old;
float c_y1_old,c_y2_old,c_y3_old, c_y4_old;
float windDir_01;
float compassAngle;
int compass_r = 22;
char* sector [] = {"N", "NE", "E", "SE", "S", "SW", "W", "NW", "N"}; // wind sector labels
int h; // indicator for wind sector
void setup() {
moon_prev.setColorDepth(16);
moon_cur.setColorDepth(16);
moon_next.setColorDepth(16);
moon_prev.createSprite(76, 78);
moon_cur.createSprite(76, 78);
moon_cur.createSprite(76, 78);
Serial.begin (9600);
tft.init ();
tft.setRotation (2);
tft.fillScreen (TFT_BLACK);
tft.setTextSize (2);
tft.println ("network ");
tft.println ("connecting");
WiFi.begin (ssid, password);
Serial.println ("connecting");
while(WiFi.status() != WL_CONNECTED) {
delay (500);
Serial.print (".");
tft.print (".");
}
Serial.println ();
Serial.print ("Connected to WiFi network ");
tft.println ("connected to ");
tft.println ();
Serial.print (ssid);
tft.println (ssid);
delay(1000);
tft.fillScreen (TFT_BLACK);
Serial.print (" - IP adress: ");
Serial.println (WiFi.localIP());
Serial.println ("timer set to 30 seconds (timerDelay variable) - it will take 30 seconds before publishing the first reading.");
timeClient.begin();
timeClient.setTimeOffset(3*60*60);
timeClient.update();
setTime(timeClient.getEpochTime());
t = now();
char timeStr[100];
strftime(timeStr, sizeof(timeStr), "%H:%M", localtime(&t));
spr.setColorDepth(16);
spr.createSprite(tft.width(), 80);
spr.fillSprite(TFT_BLACK);
spr.loadFont(calibri72);
spr.setTextColor (COLOR_CLOCK,BLACK);
//spr.setTextDatum(TR_DATUM);
spr.drawString(timeStr,50,0);
spr.pushSprite(0,0);
/*
spr.fillSprite(TFT_BLACK);
spr.loadFont(zCalibri24);
spr.setTextColor (COLOR_MOON,BLACK);
spr.drawString("луна",80,0);
spr.pushSprite(0,220); */
MoonRise mr;
mr.calculate(latitude, longitude, t - utcOffset);
// Returned values:
bool moonVisible = mr.isVisible;
bool moonHasRise = mr.hasRise;
bool moonHasSet = mr.hasSet;
float moonRiseAz = mr.riseAz; // Where the moon will rise/set in degrees from
float moonSetAz = mr.setAz; // North.
// Additional returned values requiring conversion from UTC to local time zone
// on the Arduino.
time_t moonQueryTime = mr.queryTime + utcOffset;
time_t moonRiseTime = mr.riseTime + utcOffset;
time_t moonSetTime = mr.setTime + utcOffset;
strftime(timeStr, sizeof(timeStr), "%H:%M", localtime(&moonRiseTime));
Serial.println(timeStr);
strftime(timeStr, sizeof(timeStr), "%H:%M", localtime(&moonSetTime));
Serial.println(timeStr);
if ((!mr.hasRise || (mr.hasRise && mr.riseTime > mr.queryTime)) &&
(!mr.hasSet || (mr.hasSet && mr.setTime > mr.queryTime)))
Serial.printf("\tNo moon rise or set during preceding %d hours\n", MR_WINDOW/2);
if (mr.hasRise && mr.riseTime < mr.queryTime)
Serial.printf("\tMoon rise at %.24s, Azimuth %.2f\n", ctime(&mr.riseTime), mr.riseAz);
if (mr.hasSet && mr.setTime < mr.queryTime)
Serial.printf("\tMoon set at %.24s, Azimuth %.2f\n", ctime(&mr.setTime), mr.setAz);
Serial.printf("Succeeding event:\n");
if ((!mr.hasRise || (mr.hasRise && mr.riseTime < mr.queryTime)) &&
(!mr.hasSet || (mr.hasSet && mr.setTime < mr.queryTime)))
Serial.printf("\tNo moon rise or set during succeeding %d hours\n", MR_WINDOW/2);
if (mr.hasRise && mr.riseTime > mr.queryTime)
Serial.printf("\tMoon rise at %.24s, Azimuth %.2f\n", ctime(&mr.riseTime), mr.riseAz);
if (mr.hasSet && mr.setTime > mr.queryTime)
Serial.printf("\tMoon set at %.24s, Azimuth %.2f\n", ctime(&mr.setTime), mr.setAz);
if (mr.isVisible)
Serial.printf("Moon visible.\n");
else
Serial.printf("Moon not visible.\n");
spr.unloadFont();
drawStatic();
drawMoonDay(-1);
drawMoonDay(0);
drawMoonDay(1);
// add wind sector reporter
}
String displayTime(time_t time) {
char timeStr[100];
strftime(timeStr, sizeof(timeStr), "%H:%M", localtime(&time));
return (String) timeStr;
}
void drawMoonDay(int day) {
moon_prev.fillSprite(TFT_BLACK);
moon_prev.loadFont(zCalibri24);
moon_prev.setTextColor (COLOR_MOON,BLACK);
MoonRise mr;
mr.calculate(latitude, longitude, t - utcOffset + 86400*day);
time_t moonRiseTime = mr.riseTime + utcOffset;
time_t moonSetTime = mr.setTime + utcOffset;
moon_prev.loadFont(zCalibri48);
moon_prev.drawString("26",10,0);
moon_prev.unloadFont();
moon_prev.loadFont(zCalibri24);
moon_prev.drawString(displayTime(moonRiseTime),10,40);
moon_prev.drawString(displayTime(moonSetTime),10,60);
moon_prev.pushSprite(4+80*day+80,241);
moon_prev.unloadFont();
}
void drawStatic() {
tft.loadFont(zCalibri24);
tft.setTextColor (COLOR_MOON,BLACK);
tft.drawString("луна",80,220);
tft.drawRoundRect ( 2, 240, 78, 319, 4, YELLOW);
// tft.drawRoundRect ( 82, 240, 158, 319, 4, GREEN);
// tft.drawRoundRect ( 83, 241, 157, 318, 4, GREEN);
tft.drawRoundRect ( 162, 240, 239, 319, 4, YELLOW);
}
void loop() {
/*
tft.fillScreen(TFT_DARKGREY);
int xpos = tft.width() / 2; // Half the screen width
int ypos = 50;
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Small font
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
spr.loadFont(zCalibri48); // Must load the font first into the sprite class
spr.createSprite(240, 50); // Create a sprite 100 pixels wide and 50 high
spr.fillSprite(TFT_BLUE);
spr.drawRect(0, 0, 100, 50, TFT_WHITE); // Draw sprite border outline (so we see extent)
spr.setTextColor(TFT_YELLOW, TFT_DARKGREY); // Set the sprite font colour and the background colour
spr.setTextDatum(MC_DATUM); // Middle Centre datum
spr.drawString("15pt font", 50, 25 ); // Coords of middle of 100 x 50 Sprite
spr.pushSprite(10, 10); // Push to TFT screen coord 10, 10
spr.pushSprite(10, 70, TFT_BLUE); // Push to TFT screen, TFT_BLUE is transparent
spr.unloadFont(); // Remove the font from sprite class to recover memory used
delay(4000);
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Large font
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
tft.fillScreen(TFT_BLACK);
// Beware: Sprites are a different "class" to TFT, so different fonts can be loaded
// in the tft and sprite instances, so load the font in the class instance you use!
// In this example this means the spr. instance.
spr.loadFont(zCalibri48); // Load another different font into the sprite instance
// 100 x 50 sprite was created above and still exists...
spr.fillSprite(TFT_GREEN);
spr.setTextColor(TFT_BLACK, TFT_GREEN); // Set the font colour and the background colour
spr.setTextDatum(MC_DATUM); // Middle Centre datum
spr.drawString("Fits", 50, 25); // Make sure text fits in the Sprite!
spr.pushSprite(10, 10); // Push to TFT screen coord 10, 10
spr.fillSprite(TFT_RED);
spr.setTextColor(TFT_WHITE, TFT_RED); // Set the font colour and the background colour
spr.drawString("Too big", 50, 25); // Text is too big to all fit in the Sprite!
spr.pushSprite(10, 70); // Push to TFT screen coord 10, 70
// Draw changing numbers - no flicker using this plot method!
// >>>> Note: it is best to use drawNumber() and drawFloat() for numeric values <<<<
// >>>> this reduces digit position movement when the value changes <<<<
// >>>> drawNumber() and drawFloat() functions behave like drawString() and are <<<<
// >>>> supported by setTextDatum() and setTextPadding() <<<<
spr.setTextDatum(TC_DATUM); // Top Centre datum
spr.setTextColor(TFT_WHITE, TFT_BLUE); // Set the font colour and the background colour
for (int i = 0; i <= 200; i++) {
spr.fillSprite(TFT_BLUE);
spr.drawFloat(i / 100.0, 2, 50, 10); // draw with 2 decimal places at 50,10 in sprite
spr.pushSprite(10, 130); // Push to TFT screen coord 10, 130
delay (20);
}
spr.unloadFont(); // Remove the font to recover memory used
spr.deleteSprite(); // Recover memory
delay(1000);
// doTheHardWork ();
*/
}