Hallo Mein name ist Olaf und ich spiele zur zeit mit den ESP32
rum mein erstes Projekt war ein GitHub - G6EJD/ESP32-e-Paper-Weather-Display: An ESP32 and 2.9", 4.2" or 7.5" ePaper Display reads Weather Underground data via their API and then displays the weather das läuft seit zwei Wochen problemlos
nun habe ich den selben Code mit Raumttemperatur Messung gefunden
und was soll ich sagen beim aufspielen auf den ESP32 WEMOS Lolin32 V1.0.0
bekomme ich ein Fehler in zeile 664 und als Anfänger sagt der mir überhaupt nichts
so hier ist der Fehler
C:\Users\ich\Desktop\Waveshare_7_5_BME280\examples\Waveshare_7_5_plus_BME280_V1\Waveshare_7_5_plus_BME280_V1.ino: In function 'void DisplayBME280Data(int, int)':
Waveshare_7_5_plus_BME280_V1:664:25: error: 'TXT_ROOM' was not declared in this scope
drawString(x, y - 24, TXT_ROOM, CENTER); // declared by lang_gr.h
^
Mehrere Bibliotheken wurden für "WiFi.h" gefunden
Benutzt: C:\Users\ich\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\libraries\WiFi
Nicht benutzt: C:\Program Files (x86)\Arduino\libraries\WiFi
exit status 1
'TXT_ROOM' was not declared in this scope
und hier der komplette Code
[code]
/* ESP32 Weather Display using an EPD 7.5" Display, obtains data from Open Weather Map, decodes and then displays it.
####################################################################################################################################
This software, the ideas and concepts is Copyright (c) David Bird 2018. All rights to this software are reserved.
Any redistribution or reproduction of any part or all of the contents in any form is prohibited other than the following:
1. You may print or download to a local hard disk extracts for your personal and non-commercial use only.
2. You may copy the content to individual third parties for their personal use, but only if you acknowledge the author David Bird as the source of the material.
3. You may not, except with my express written permission, distribute or commercially exploit the content.
4. You may not transmit it or store it in any other website or other form of electronic retrieval system for commercial purposes.
The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the
software use is visible to an end-user.
THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT. FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY
OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
See more at http://www.dsbird.org.uk
*/
/*
* These changes will increase the power consumption
1. owm_credentials.h - As NTPserver can also be used the ISP router (e.g. Fritz!Box from AVM)
the DNS-name of the ISP - router is e.g. "WLAN-AP" and the defined NTP server are:
"2.europe.pool.ntp.org; de.pool.ntp.org"
2. changes the ePaper display information positions (X,y)
- e.g. Time_str and Date_str - moved to DisplayGeneralInfoSection()
3. if country="DE" create a German date format - e.g. "Freitag, 23. Juni 2019"
4. per minute a BME280 measurement and the values are stored in RTC memory
(calc average and min / max values for temperature and humidity)
5. introduction of a program status word (PSW)one byte to control the deep sleep timer
Bit Descriptiom
0 Initial setup for 1st and 2nd 1/2 hour required >> 0 = no and 1 = yes
1 1st 1/2 hour
2 2nd 1/2 hour
3 N/A
4 N/A
5 N/A
6 N/A
7 N/A
6. insert BME280 code in setup() part
*/
#include "owm_credentials.h" // See 'owm_credentials' tab and enter your OWM API key and set the Wifi SSID and PASSWORD
#include <ArduinoJson.h> // https://github.com/bblanchon/ArduinoJson needs version v6 or above
#include <WiFi.h> // Built-in
#include "time.h" // Built-in
#include <SPI.h> // Built-in
#include <Wire.h>
#include <Adafruit_BME280.h> // BME280 sensor
#include <Adafruit_Sensor.h> // BME280 sensor
#define SEALEVELPRESSURE_HPA (1013.25)// BME280 sensor
#define ENABLE_GxEPD2_display 0
#include <GxEPD2_BW.h>
#include <GxEPD2_3C.h>
#include <U8g2_for_Adafruit_GFX.h>
#include "epaper_fonts.h"
#include "forecast_record.h"
//#include "lang.h" // Localisation (English)
//#include "lang_fr.h" // Localisation (French)
#include "lang_gr.h" // Localisation (German)
//#include "lang_it.h" // Localisation (Italian)
#define SCREEN_WIDTH 640 // Set for landscape mode
#define SCREEN_HEIGHT 384
enum alignment {LEFT, RIGHT, CENTER};
// E-Paper pins to ESP32 GPIO pins e.g. LOLIN32 D32 or most ESP32 development boards
//static const uint8_t EPD_BUSY = 4;
//static const uint8_t EPD_CS = 5;
//static const uint8_t EPD_RST = 16; // Lolin D32 Pro pin N/A, so suggest using 12
//static const uint8_t EPD_DC = 17; // Lolin D32 Pro pin N/A, so suggest using 13
//static const uint8_t EPD_SCK = 18;
//static const uint8_t EPD_MISO = 19; // Master-In Slave-Out not used, as no data from display
//static const uint8_t EPD_MOSI = 23;
// Connections for e.g. Waveshare ESP32 e-Paper Driver Board
static const uint8_t EPD_BUSY = 25;
static const uint8_t EPD_CS = 15;
static const uint8_t EPD_RST = 26;
static const uint8_t EPD_DC = 27;
static const uint8_t EPD_SCK = 13;
static const uint8_t EPD_MISO = 19; // Master-In Slave-Out not used, as no data from display
static const uint8_t EPD_MOSI = 14;
GxEPD2_BW<GxEPD2_750, GxEPD2_750::HEIGHT> display(GxEPD2_750(/*CS=5*/ EPD_CS, /*DC=*/ EPD_DC, /*RST=*/ EPD_RST, /*BUSY=*/ EPD_BUSY));
U8G2_FOR_ADAFRUIT_GFX u8g2Fonts; // Select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall
// Using fonts:
// u8g2_font_helvB08_tf
// u8g2_font_helvB10_tf
// u8g2_font_helvB12_tf
// u8g2_font_helvB14_tf
// u8g2_font_helvB18_tf
// u8g2_font_helvB24_tf
// BME_280 pins to ESP32 GPIO pins
#define BME_MISO 19 // pink SDO = MISO
#define BME_CS 4 // green CSB = SS
#define BME_MOSI 23 // brown SDA = MOSI
#define BME_SCK 18 // yellow SCL = SCK
// GND // blue
// Power 3,3V // red
// Adafruit_BME280 bme; // I2C
Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI
//################ VERSION ###########################################
String version = "16.3_BME"; // Programme version, see change log at end
//################ VARIABLES ###########################################
boolean LargeIcon = true, SmallIcon = false;
#define Large 15 // For icon drawing, needs to be odd number for best effect
#define Small 5 // For icon drawing, needs to be odd number for best effect
String Time_str, Date_str; // strings to hold time and received weather date
String BME_T_str, BME_Tm_str, BME_H_str, BME_Hm_str; // indoor data
uint8_t CurrentHour = 0, CurrentMin = 0, CurrentSec = 0, CurrentDay = 0;
int wifi_signal;
long StartTime = 0;
//################ PROGRAM VARIABLES and OBJECTS ################
#define max_readings 24
Forecast_record_type WxConditions[1];
Forecast_record_type WxForecast[max_readings];
#include "common.h"
#define autoscale_on true
#define autoscale_off false
#define barchart_on true
#define barchart_off false
float pressure_readings[max_readings] = {0};
float temperature_readings[max_readings] = {0};
float humidity_readings[max_readings] = {0};
float rain_readings[max_readings] = {0};
float snow_readings[max_readings] = {0};
long SleepDuration = 1; // Sleep time in minutes, aligned to the nearest minute boundary, so if 30 will always update at 00 or 30 past the hour
int WakeupTime = 6; // Don't wakeup until after 06:00 to save battery power
int SleepTime = 23; // Sleep after (23+1) 00:00 to save battery power
// Variables stored in RTC memory
RTC_DATA_ATTR byte PSW = 0b00000001; // the 0b prefix indicates a binary constant
RTC_DATA_ATTR int Wakeup = 1; // Wakeup counter
RTC_DATA_ATTR int weekday = 8; // Init value
RTC_DATA_ATTR float temp1 = -99;
RTC_DATA_ATTR float temp2 = -99;
RTC_DATA_ATTR float temp3 = -99;
RTC_DATA_ATTR float tempmin = 99;
RTC_DATA_ATTR float tempmax = -99;
RTC_DATA_ATTR float tempavg = -99;
// RTC_DATA_ATTR float presure1 = -99;
// RTC_DATA_ATTR float presure2 = -99;
// RTC_DATA_ATTR float presure3 = -99;
// RTC_DATA_ATTR float presureavg = -99;
RTC_DATA_ATTR int humidity1 = -99;
RTC_DATA_ATTR int humidity2 = -99;
RTC_DATA_ATTR int humidity3 = -99;
RTC_DATA_ATTR int humiditymin = 100;
RTC_DATA_ATTR int humiditymax = 0;
RTC_DATA_ATTR int humidityavg = -99;
float BME_f;
int BME_i;
//#########################################################################################
void setup() {
StartTime = millis();
Serial.begin(115200);
/* BME280 - code */
if (!bme.begin()) { // SPI
Serial.println(F("Could not find a valid BMP280 sensor, check wiring!"));
while (1); }
if (StartWiFi() == WL_CONNECTED && SetupTime() == true) {
BMEsensor();
if((CurrentMin<30)&&( bitRead(PSW,1))){
// download OWM and display
if (DownloadOWM()) {
bitWrite(PSW, 1, 0);
bitWrite(PSW, 2, 1);}
Serial.println(PSW, BIN);}
if((CurrentMin>=30)&&( bitRead(PSW,2))){
// download OWM and display
if (DownloadOWM()) {
bitWrite(PSW, 1, 1);
bitWrite(PSW, 2, 0);}
Serial.println(PSW, BIN);}
}
BeginSleep();
}
//#########################################################################################
void loop() { // this will never run!
}
//#########################################################################################
void BMEsensor() {
Serial.println("No. of Wakeups: " + String(Wakeup));
if (Wakeup == 1) { BME_f = bme.readTemperature();
if((BME_f >= -20) && (BME_f <= 55)) { // valid measuring range check
temp1 = BME_f;}
// presure1 = bme.readPressure() / 100.0F;
BME_i = bme.readHumidity();
if((BME_i >= 0) && (BME_i <= 100)) { // valid measuring range check
humidity1 = BME_i;}}
if (Wakeup == 2) { BME_f = bme.readTemperature();
if((BME_f >= -20) && (BME_f <= 55)) { // valid measuring range check
temp2 = BME_f;}
// presure2 = bme.readPressure() / 100.0F;
BME_i = bme.readHumidity();
if((BME_i >= 0) && (BME_i <= 100)) { // valid measuring range check
humidity2 = BME_i;}}
if (Wakeup == 3) { BME_f = bme.readTemperature();
if((BME_f >= -20) && (BME_f <= 55)) { // valid measuring range check
temp3 = BME_f;}
// presure3 = bme.readPressure() / 100.0F;
BME_i = bme.readHumidity();
if((BME_i >= 0) && (BME_i <= 100)) { // valid measuring range check
humidity3 = BME_i;}}
if (temp3 != -99){ tempavg = ((temp1+temp2+temp3)/3);
// presureavg = ((presure1+presure2+presure3)/3);
humidityavg = ((humidity1+humidity2+humidity3)/3);
if(weekday == CurrentDay){
if(tempavg < tempmin) tempmin = tempavg;
if(tempavg > tempmax) tempmax = tempavg;
if(humidityavg < humiditymin) humiditymin = humidityavg;
if(humidityavg > humiditymax) humiditymax = humidityavg;}
else{
weekday = CurrentDay;
tempmin = tempavg;
tempmax = tempavg;
humiditymin = humidityavg;
humiditymax = humidityavg;}
}
++Wakeup;
if (Wakeup >= 4) Wakeup = 1;
printf("\rT1 = %.2f°C T2 = %.2f°C T3 = %.2f°C Tavg = %.2f°C Tmin = %.2f°C Tmax = %.2f°C\n", temp1, temp2, temp3, tempavg, tempmin, tempmax);
// printf("\rP1 = %.2f P2 = %.2f P3 = %.2f Pavg = %.2f\n", presure1, presure2, presure3, presureavg);
printf("\rH1 = %d%% H2 = %d%% H3 = %d%% Havg = %d%% Hmin%d%% Hmax%d%%\n", humidity1, humidity2, humidity3, humidityavg, humiditymin, humiditymax);
printf("\rweekday = %d, currentday = %d\n", weekday, CurrentDay);
}
//#########################################################################################
boolean DownloadOWM() {
if ((CurrentHour >= WakeupTime && CurrentHour <= SleepTime)) {
InitialiseDisplay(); // Give screen time to initialise by getting weather data!
byte Attempts = 1;
bool RxWeather = false, RxForecast = false;
WiFiClient client; // wifi client object
while ((RxWeather == false || RxForecast == false) && Attempts <= 2) { // Try up-to 2 time for Weather and Forecast data
if (RxWeather == false) RxWeather = obtain_wx_data(client, "weather");
if (RxForecast == false) RxForecast = obtain_wx_data(client, "forecast");
Attempts++;
}
if (RxWeather && RxForecast) { // Only if received both Weather or Forecast proceed
StopWiFi(); // Reduces power consumption
DisplayWeather();
display.display(false); // Full screen update mode
return true;
} else {
return false;
}
}
}
//#########################################################################################
void BeginSleep() {
display.powerOff();
long SleepTimer = (SleepDuration * 60 - ((CurrentMin % SleepDuration) * 60 + CurrentSec)); //Some ESP32 are too fast to maintain accurate time
// esp_sleep_enable_ext0_wakeup(GPIO_NUM_27,1); //1 = High, 0 = Low
esp_sleep_enable_timer_wakeup(SleepTimer * 1000000LL);
#ifdef BUILTIN_LED
pinMode(BUILTIN_LED, INPUT); // If it's On, turn it off and some boards use GPIO-5 for SPI-SS, which remains low after screen use
digitalWrite(BUILTIN_LED, HIGH);
#endif
Serial.println("Entering " + String(SleepTimer) + "-secs of sleep time");
Serial.println("Awake for : " + String((millis() - StartTime) / 1000.0, 3) + "-secs");
Serial.println("Starting deep-sleep period...");
esp_deep_sleep_start(); // Sleep for e.g. 1 minute(s)
}
//#########################################################################################
void DisplayWeather() { // 7.5" e-paper display is 640x384 resolution
DisplayGeneralInfoSection(); // Top line of the display
DisplayDisplayWindSection(87, 117, WxConditions[0].Winddir, WxConditions[0].Windspeed, 65);
DisplayMainWeatherSection(241, 80); // Centre section of display for Location, temperature, Weather report, current Wx Symbol and wind direction
DisplayForecastSection(174, 196); // 3hr forecast boxes
DisplayAstronomySection(0, 196); // Astronomy section Sun rise/set, Moon phase and Moon icon
DisplayStatusSection(548, 170, wifi_signal); // Wi-Fi signal strength and Battery voltage
DisplayBME280Data(418,170); // Room temparature and humidity
}
//#########################################################################################
void DisplayGeneralInfoSection() {
static uint8_t vwidth = 2;
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
drawString(10, 4+vwidth, Time_str, LEFT);
drawString(SCREEN_WIDTH-1, 4+vwidth, Date_str, RIGHT);
u8g2Fonts.setFont(u8g2_font_helvB10_tf);
drawString(SCREEN_WIDTH / 2, 3+vwidth, City, CENTER);
display.drawLine(0, 15+vwidth, SCREEN_WIDTH - 3, 15+vwidth, GxEPD_BLACK);
}
//#########################################################################################
void DisplayMainWeatherSection(int x, int y) {
// display.drawRect(x-67, y-65, 140, 182, GxEPD_BLACK);
display.drawLine(0, 34, SCREEN_WIDTH - 3, 34, GxEPD_BLACK);
DisplayConditionsSection(x + 2, y + 40, WxConditions[0].Icon, LargeIcon);
DisplayTemperatureSection(x + 125, y - 64, 110, 78);
DisplayPressureSection(x + 230, y - 64, WxConditions[0].Pressure, WxConditions[0].Trend, 105, 78);
DisplayPrecipitationSection(x + 330, y - 64, 105, 78);
DisplayForecastTextSection(x + 80, y + 17, 322, 49);
}
//#########################################################################################
void DisplayDisplayWindSection(int x, int y, float angle, float windspeed, int Cradius) {
x=x+2;
y=y+4;
arrow(x, y, Cradius - 17, angle, 15, 27); // Show wind direction on outer circle of width and length
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
drawString(x, y - Cradius - 33-2, TXT_WIND_SPEED_DIRECTION, CENTER);
int dxo, dyo, dxi, dyi;
display.drawLine(0, 15, 0, y + Cradius + 30, GxEPD_BLACK);
display.drawCircle(x, y, Cradius, GxEPD_BLACK); // Draw compass circle
display.drawCircle(x, y, Cradius + 1, GxEPD_BLACK); // Draw compass circle
display.drawCircle(x, y, Cradius * 0.7, GxEPD_BLACK); // Draw compass inner circle
for (float a = 0; a < 360; a = a + 22.5) {
dxo = Cradius * cos((a - 90) * PI / 180);
dyo = Cradius * sin((a - 90) * PI / 180);
if (a == 45) drawString(dxo + x + 10, dyo + y - 10, TXT_NE, CENTER);
if (a == 135) drawString(dxo + x + 7, dyo + y + 5, TXT_SE, CENTER);
if (a == 225) drawString(dxo + x - 15, dyo + y, TXT_SW, CENTER);
if (a == 315) drawString(dxo + x - 15, dyo + y - 10, TXT_NW, CENTER);
dxi = dxo * 0.9;
dyi = dyo * 0.9;
display.drawLine(dxo + x, dyo + y, dxi + x, dyi + y, GxEPD_BLACK);
dxo = dxo * 0.7;
dyo = dyo * 0.7;
dxi = dxo * 0.9;
dyi = dyo * 0.9;
display.drawLine(dxo + x, dyo + y, dxi + x, dyi + y, GxEPD_BLACK);
}
u8g2Fonts.setFont(u8g2_font_helvB12_tf);
drawString(x-2, y - Cradius - 10-3, TXT_N, CENTER);
drawString(x-2, y + Cradius + 5+5, TXT_S, CENTER);
drawString(x - Cradius - 16, y - 2, TXT_W, CENTER);
drawString(x + Cradius + 8, y - 2, TXT_E, CENTER);
drawString(x - 5, y - 29, WindDegToDirection(angle), CENTER);
// drawString(x + 5, y + 24, String(angle, 0) + "°", CENTER);
u8g2Fonts.setFont(u8g2_font_helvB18_tf);
drawString(x - 8, y, String(windspeed, 1), CENTER);
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
drawString(x, y + 13, (Units == "M" ? "m/s" : "mph"), CENTER);
}
//#########################################################################################
String WindDegToDirection(float winddirection) {
if (winddirection >= 348.75 || winddirection < 11.25) return TXT_N;
if (winddirection >= 11.25 && winddirection < 33.75) return TXT_NNE;
if (winddirection >= 33.75 && winddirection < 56.25) return TXT_NE;
if (winddirection >= 56.25 && winddirection < 78.75) return TXT_ENE;
if (winddirection >= 78.75 && winddirection < 101.25) return TXT_E;
if (winddirection >= 101.25 && winddirection < 123.75) return TXT_ESE;
if (winddirection >= 123.75 && winddirection < 146.25) return TXT_SE;
if (winddirection >= 146.25 && winddirection < 168.75) return TXT_SSE;
if (winddirection >= 168.75 && winddirection < 191.25) return TXT_S;
if (winddirection >= 191.25 && winddirection < 213.75) return TXT_SSW;
if (winddirection >= 213.75 && winddirection < 236.25) return TXT_SW;
if (winddirection >= 236.25 && winddirection < 258.75) return TXT_WSW;
if (winddirection >= 258.75 && winddirection < 281.25) return TXT_W;
if (winddirection >= 281.25 && winddirection < 303.75) return TXT_WNW;
if (winddirection >= 303.75 && winddirection < 326.25) return TXT_NW;
if (winddirection >= 326.25 && winddirection < 348.75) return TXT_NNW;
return "?";
}
//#########################################################################################
void DisplayTemperatureSection(int x, int y, int twidth, int tdepth) {
static uint8_t vwidth = 2;
display.drawRect(x - 51, y - 1 + vwidth, twidth, tdepth, GxEPD_BLACK); // temp outline
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
drawString(x, y + 4 + vwidth, TXT_TEMPERATURES, CENTER);
u8g2Fonts.setFont(u8g2_font_helvB10_tf);
drawString(x + 8, y + 66, String(WxConditions[0].High, 0) + "° | " + String(WxConditions[0].Low, 0) + "°", CENTER); // Show forecast high and Low
u8g2Fonts.setFont(u8g2_font_helvB24_tf);
drawString(x - 18, y + 43, String(WxConditions[0].Temperature, 1) + "°", CENTER); // Show current Temperature
u8g2Fonts.setFont(u8g2_font_helvB10_tf);
drawString(x + 35, y + 43, Units == "M" ? "C" : "F", LEFT);
}
//#########################################################################################
void DisplayForecastTextSection(int x, int y , int fwidth, int fdepth) {
display.drawRect(x - 6, y - 3, fwidth, fdepth, GxEPD_BLACK); // forecast text outline
u8g2Fonts.setFont(u8g2_font_helvB14_tf);
String Wx_Description = WxConditions[0].Forecast0;
if (WxConditions[0].Forecast1 != "") Wx_Description += ", " + WxConditions[0].Forecast1;
if (WxConditions[0].Forecast2 != "") Wx_Description += ", " + WxConditions[0].Forecast2;
int MsgWidth = 35; // Using proportional fonts, so be aware of making it too wide!
if (Language == "DE") drawStringMaxWidth(x - 3, y + 18, MsgWidth, Wx_Description, LEFT); // Leave German text in original format, 28 character screen width at this font size
else drawStringMaxWidth(x - 3, y + 18, MsgWidth, TitleCase(Wx_Description), LEFT); // 28 character screen width at this font size
u8g2Fonts.setFont(u8g2_font_helvB10_tf);
}
//#########################################################################################
void DisplayForecastWeather(int x, int y, int index) {
int fwidth = 58;
x = x + fwidth * index;
display.drawRect(x, y, fwidth - 1, 65, GxEPD_BLACK);
display.drawLine(x, y + 13, x + fwidth - 3, y + 13, GxEPD_BLACK);
DisplayConditionsSection(x + fwidth / 2, y + 35, WxForecast[index].Icon, SmallIcon);
drawString(x + fwidth / 2, y + 3, String(WxForecast[index].Period.substring(11, 16)), CENTER);
drawString(x + fwidth / 2 + 10, y + 53, String(WxForecast[index].High, 0) + "°/" + String(WxForecast[index].Low, 0) + "°", CENTER);
}
//#########################################################################################
void DisplayPressureSection(int x, int y, float pressure, String slope, int pwidth, int pdepth) {
static uint8_t vwidth = 2;
display.drawRect(x - 45, y - 1 + vwidth, pwidth, pdepth, GxEPD_BLACK); // pressure outline
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
drawString(x + 5, y + 4 + vwidth, TXT_PRESSURE, CENTER);
String slope_direction = TXT_PRESSURE_STEADY;
if (slope == "+") slope_direction = TXT_PRESSURE_RISING;
if (slope == "-") slope_direction = TXT_PRESSURE_FALLING;
display.drawRect(x + 27, y + 63, 33, 16, GxEPD_BLACK);
u8g2Fonts.setFont(u8g2_font_helvB24_tf);
if (Units == "I") drawString(x - 18, y + 44, String(pressure, 2), CENTER); // "Imperial"
else drawString(x - 15, y + 44, String(pressure, 0), CENTER); // "Metric"
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
drawString(x + 42, y + 67, (Units == "M" ? "hPa" : "in"), CENTER);
drawString(x - 03, y + 67, slope_direction, CENTER);
}
//#########################################################################################
void DisplayPrecipitationSection(int x, int y, int pwidth, int pdepth) {
static uint8_t vwidth = 2;
display.drawRect(x - 39, y - 1 + vwidth, pwidth, pdepth, GxEPD_BLACK); // precipitation outline
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
drawString(x + 20, y + 4 + vwidth, TXT_PRECIPITATION_SOON, CENTER);
u8g2Fonts.setFont(u8g2_font_helvB12_tf);
if (WxForecast[1].Rainfall >= 0.005) { // Ignore small amounts
drawString(x - 20, y + 30, String(WxForecast[1].Rainfall, 2) + (Units == "M" ? "mm" : "in"), LEFT); // Only display rainfall total today if > 0
addraindrop(x + 40, y + 32, 6);
}
if (WxForecast[1].Snowfall >= 0.005) // Ignore small amounts
drawString(x - 20, y + 57, String(WxForecast[1].Snowfall, 2) + (Units == "M" ? "mm" : "in") + " **", LEFT); // Only display snowfall total today if > 0
}
//#########################################################################################
void DisplayAstronomySection(int x, int y) {
display.drawRect(x, y + 13, 173, 52, GxEPD_BLACK);
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
drawString(x + 5, y + 18, ConvertUnixTime(WxConditions[0].Sunrise).substring(0, 5) + " " + TXT_SUNRISE, LEFT);
drawString(x + 5, y + 32, ConvertUnixTime(WxConditions[0].Sunset).substring(0, 5) + " " + TXT_SUNSET, LEFT);
time_t now = time(NULL);
struct tm * now_utc = gmtime(&now);
const int day_utc = now_utc->tm_mday;
const int month_utc = now_utc->tm_mon + 1;
const int year_utc = now_utc->tm_year + 1900;
drawString(x + 5, y + 50, MoonPhase(day_utc, month_utc, year_utc, Hemisphere), LEFT);
DrawMoon(x + 110, y, day_utc, month_utc, year_utc, Hemisphere);
}
//#########################################################################################
void DrawMoon(int x, int y, int dd, int mm, int yy, String hemisphere) {
const int diameter = 38;
double Phase = NormalizedMoonPhase(dd, mm, yy);
hemisphere.toLowerCase();
if (hemisphere == "south") Phase = 1 - Phase;
// Draw dark part of moon
display.fillCircle(x + diameter - 1, y + diameter, diameter / 2 + 1, GxEPD_BLACK);
const int number_of_lines = 90;
for (double Ypos = 0; Ypos <= number_of_lines / 2; Ypos++) {
double Xpos = sqrt(number_of_lines / 2 * number_of_lines / 2 - Ypos * Ypos);
// Determine the edges of the lighted part of the moon
double Rpos = 2 * Xpos;
double Xpos1, Xpos2;
if (Phase < 0.5) {
Xpos1 = -Xpos;
Xpos2 = Rpos - 2 * Phase * Rpos - Xpos;
}
else {
Xpos1 = Xpos;
Xpos2 = Xpos - 2 * Phase * Rpos + Rpos;
}
// Draw light part of moon
double pW1x = (Xpos1 + number_of_lines) / number_of_lines * diameter + x;
double pW1y = (number_of_lines - Ypos) / number_of_lines * diameter + y;
double pW2x = (Xpos2 + number_of_lines) / number_of_lines * diameter + x;
double pW2y = (number_of_lines - Ypos) / number_of_lines * diameter + y;
double pW3x = (Xpos1 + number_of_lines) / number_of_lines * diameter + x;
double pW3y = (Ypos + number_of_lines) / number_of_lines * diameter + y;
double pW4x = (Xpos2 + number_of_lines) / number_of_lines * diameter + x;
double pW4y = (Ypos + number_of_lines) / number_of_lines * diameter + y;
display.drawLine(pW1x, pW1y, pW2x, pW2y, GxEPD_WHITE);
display.drawLine(pW3x, pW3y, pW4x, pW4y, GxEPD_WHITE);
}
display.drawCircle(x + diameter - 1, y + diameter, diameter / 2, GxEPD_BLACK);
}
//#########################################################################################
String MoonPhase(int d, int m, int y, String hemisphere) {
int c, e;
double jd;
int b;
if (m < 3) {
y--;
m += 12;
}
++m;
c = 365.25 * y;
e = 30.6 * m;
jd = c + e + d - 694039.09; /* jd is total days elapsed */
jd /= 29.53059; /* divide by the moon cycle (29.53 days) */
b = jd; /* int(jd) -> b, take integer part of jd */
jd -= b; /* subtract integer part to leave fractional part of original jd */
b = jd * 8 + 0.5; /* scale fraction from 0-8 and round by adding 0.5 */
b = b & 7; /* 0 and 8 are the same phase so modulo 8 for 0 */
if (hemisphere == "south") b = 7 - b;
if (b == 0) return TXT_MOON_NEW; // New; 0% illuminated
if (b == 1) return TXT_MOON_WAXING_CRESCENT; // Waxing crescent; 25% illuminated
if (b == 2) return TXT_MOON_FIRST_QUARTER; // First quarter; 50% illuminated
if (b == 3) return TXT_MOON_WAXING_GIBBOUS; // Waxing gibbous; 75% illuminated
if (b == 4) return TXT_MOON_FULL; // Full; 100% illuminated
if (b == 5) return TXT_MOON_WANING_GIBBOUS; // Waning gibbous; 75% illuminated
if (b == 6) return TXT_MOON_THIRD_QUARTER; // Third quarter; 50% illuminated
if (b == 7) return TXT_MOON_WANING_CRESCENT; // Waning crescent; 25% illuminated
return "";
}
//#########################################################################################
void DisplayForecastSection(int x, int y) {
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
int f = 0;
do {
DisplayForecastWeather(x, y, f);
f++;
} while (f <= 7);
// Pre-load temporary arrays with with data - because C parses by reference
int r = 1;
do {
if (Units == "I") pressure_readings[r] = WxForecast[r].Pressure * 0.02953; else pressure_readings[r] = WxForecast[r].Pressure;
if (Units == "I") rain_readings[r] = WxForecast[r].Rainfall * 0.0393701; else rain_readings[r] = WxForecast[r].Rainfall;
if (Units == "I") snow_readings[r] = WxForecast[r].Snowfall * 0.0393701; else snow_readings[r] = WxForecast[r].Snowfall;
temperature_readings[r] = WxForecast[r].Temperature;
humidity_readings[r] = WxForecast[r].Humidity;
r++;
} while (r <= max_readings);
int gwidth = 120, gheight = 58;
int gx = (SCREEN_WIDTH - gwidth * 4) / 5 + 5;
int gy = 300;
int gap = gwidth + gx;
u8g2Fonts.setFont(u8g2_font_helvB10_tf);
drawString(SCREEN_WIDTH / 2, gy - 30, TXT_FORECAST_VALUES, CENTER); // Based on a graph height of 60
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
// (x,y,width,height,MinValue, MaxValue, Title, Data Array, AutoScale, ChartMode)
DrawGraph(gx + 0 * gap, gy, gwidth, gheight, 900, 1050, Units == "M" ? TXT_PRESSURE_HPA : TXT_PRESSURE_IN, pressure_readings, max_readings, autoscale_on, barchart_off);
DrawGraph(gx + 1 * gap, gy, gwidth, gheight, 10, 30, Units == "M" ? TXT_TEMPERATURE_C : TXT_TEMPERATURE_F, temperature_readings, max_readings, autoscale_on, barchart_off);
DrawGraph(gx + 2 * gap, gy, gwidth, gheight, 0, 100, TXT_HUMIDITY_PERCENT, humidity_readings, max_readings, autoscale_off, barchart_off);
if (SumOfPrecip(rain_readings, max_readings) >= SumOfPrecip(snow_readings, max_readings))
DrawGraph(gx + 3 * gap + 5, gy, gwidth, gheight, 0, 30, Units == "M" ? TXT_RAINFALL_MM : TXT_RAINFALL_IN, rain_readings, max_readings, autoscale_on, barchart_on);
else DrawGraph(gx + 3 * gap + 5, gy, gwidth, gheight, 0, 30, Units == "M" ? TXT_SNOWFALL_MM : TXT_SNOWFALL_IN, snow_readings, max_readings, autoscale_on, barchart_on);
}
//#########################################################################################
void DisplayConditionsSection(int x, int y, String IconName, bool IconSize) {
static uint8_t vwidth = 2;
Serial.println("Icon name: " + IconName);
if (IconName == "01d" || IconName == "01n") Sunny(x, y, IconSize, IconName);
else if (IconName == "02d" || IconName == "02n") MostlySunny(x, y, IconSize, IconName);
else if (IconName == "03d" || IconName == "03n") Cloudy(x, y, IconSize, IconName);
else if (IconName == "04d" || IconName == "04n") MostlySunny(x, y, IconSize, IconName);
else if (IconName == "09d" || IconName == "09n") ChanceRain(x, y, IconSize, IconName);
else if (IconName == "10d" || IconName == "10n") Rain(x, y, IconSize, IconName);
else if (IconName == "11d" || IconName == "11n") Tstorms(x, y, IconSize, IconName);
else if (IconName == "13d" || IconName == "13n") Snow(x, y, IconSize, IconName);
else if (IconName == "50d") Haze(x, y, IconSize, IconName);
else if (IconName == "50n") Fog(x, y, IconSize, IconName);
else Nodata(x, y, IconSize, IconName);
if (IconSize == LargeIcon) {
display.drawRect(x - 69, y - 105 + vwidth, 140, 180, GxEPD_BLACK);
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
drawString(x, y - 101 + vwidth, TXT_CONDITIONS, CENTER);
u8g2Fonts.setFont(u8g2_font_helvB14_tf);
drawString(x - 20, y + 64, String(WxConditions[0].Humidity, 0) + "%", CENTER);
u8g2Fonts.setFont(u8g2_font_helvB10_tf);
drawString(x + 28, y + 64, "RH", CENTER);
if (WxConditions[0].Visibility > 0) Visibility(x - 50, y - 78+4, String(WxConditions[0].Visibility) + "m");
if (WxConditions[0].Cloudcover > 0) CloudCover(x + 28, y - 78+4, WxConditions[0].Cloudcover);
}
}
//#########################################################################################
void arrow(int x, int y, int asize, float aangle, int pwidth, int plength) {
float dx = (asize - 10) * cos((aangle - 90) * PI / 180) + x; // calculate X position
float dy = (asize - 10) * sin((aangle - 90) * PI / 180) + y; // calculate Y position
float x1 = 0; float y1 = plength;
float x2 = pwidth / 2; float y2 = pwidth / 2;
float x3 = -pwidth / 2; float y3 = pwidth / 2;
float angle = aangle * PI / 180 - 135;
float xx1 = x1 * cos(angle) - y1 * sin(angle) + dx;
float yy1 = y1 * cos(angle) + x1 * sin(angle) + dy;
float xx2 = x2 * cos(angle) - y2 * sin(angle) + dx;
float yy2 = y2 * cos(angle) + x2 * sin(angle) + dy;
float xx3 = x3 * cos(angle) - y3 * sin(angle) + dx;
float yy3 = y3 * cos(angle) + x3 * sin(angle) + dy;
display.fillTriangle(xx1, yy1, xx3, yy3, xx2, yy2, GxEPD_BLACK);
}
//#########################################################################################
uint8_t StartWiFi() {
Serial.print("\r\nConnecting to: "); Serial.println(String(ssid));
IPAddress dns(8, 8, 8, 8); // Google DNS
WiFi.disconnect();
WiFi.mode(WIFI_STA); // switch off AP
WiFi.setAutoConnect(true);
WiFi.setAutoReconnect(true);
WiFi.begin(ssid, password);
unsigned long start = millis();
uint8_t connectionStatus;
bool AttemptConnection = true;
while (AttemptConnection) {
connectionStatus = WiFi.status();
if (millis() > start + 15000) { // Wait 15-secs maximum
AttemptConnection = false;
}
if (connectionStatus == WL_CONNECTED || connectionStatus == WL_CONNECT_FAILED) {
AttemptConnection = false;
}
delay(50);
}
if (connectionStatus == WL_CONNECTED) {
wifi_signal = WiFi.RSSI(); // Get Wifi Signal strength now, because the WiFi will be turned off to save power!
Serial.println("WiFi connected at: " + WiFi.localIP().toString());
}
else Serial.println("WiFi connection *** FAILED ***");
return connectionStatus;
}
//#########################################################################################
void StopWiFi() {
WiFi.disconnect();
WiFi.mode(WIFI_OFF);
}
//#########################################################################################
void DisplayBME280Data(int x, int y) {
static char BME_T[8]; // indoor temperature
static char BME_Tm[18]; // indoor temperature min | max
static char BME_H[4]; // indoor humidity
static char BME_Hm[12]; // indoor humidity min | max
if (temp3 != -99){
sprintf(BME_T,"%.1f°C",tempavg);
BME_T_str = BME_T;
sprintf(BME_Tm,"%.1f°C | %.1f°C",tempmin,tempmax);
BME_Tm_str = BME_Tm;
sprintf(BME_H,"%d%%",humidityavg);
BME_H_str = BME_H;
sprintf(BME_Hm,"%d%% | %d%%",humiditymin,humiditymax);
BME_Hm_str = BME_Hm;
} else {
BME_T_str = "==.=°C";
BME_H_str = "==%";
BME_Tm_str = " ==.=°C | ==.=°C";
BME_Hm_str = "==% | ==%";
// BME_Tm_str = TXT_TEMPERATURES;
// BME_Hm_str = TXT_HUMIDITY;
}
Serial.println(BME_T_str);
Serial.println(BME_Tm_str);
Serial.println(BME_H_str);
Serial.println(BME_Hm_str);
display.drawRect(x - 103, y - 26, 206, 51, GxEPD_BLACK);
display.drawLine(x - 103, y - 14, x - 104 + 206, y - 14, GxEPD_BLACK);
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
drawString(x, y - 24, TXT_ROOM, CENTER); // declared by lang_gr.h
drawString(x - 51+15, y + 10, BME_Tm_str, CENTER); // indoor temperature min_max
drawString(x + 51+3, y + 10, BME_Hm_str, CENTER); // indoor humidity min_max
u8g2Fonts.setFont(u8g2_font_helvB10_tf);
drawString(x -51, y - 3, BME_T_str, CENTER); // indoor temperature
drawString(x + 51, y - 3,BME_H_str, CENTER); // indoor humidity
}
//#########################################################################################
void DisplayStatusSection(int x, int y, int rssi) {
display.drawRect(x - 26, y - 26, 115, 51, GxEPD_BLACK);
display.drawLine(x - 26, y - 14, x - 26 + 114, y - 14, GxEPD_BLACK);
display.drawLine(x - 26 + 115 / 2, y - 15, x - 26 + 115 / 2, y - 26, GxEPD_BLACK);
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
drawString(x, y - 24, TXT_WIFI, CENTER);
drawString(x + 55, y - 24, TXT_POWER, CENTER);
DrawRSSI(x - 8, y + 5, rssi);
DrawBattery(x + 47, y + 5);;
}
//#########################################################################################
void DrawRSSI(int x, int y, int rssi) {
int WIFIsignal = 0;
int xpos = 1;
for (int _rssi = -100; _rssi <= rssi; _rssi = _rssi + 20) {
if (_rssi <= -20) WIFIsignal = 20; // <-20dbm displays 5-bars
if (_rssi <= -40) WIFIsignal = 16; // -40dbm to -21dbm displays 4-bars
if (_rssi <= -60) WIFIsignal = 12; // -60dbm to -41dbm displays 3-bars
if (_rssi <= -80) WIFIsignal = 8; // -80dbm to -61dbm displays 2-bars
if (_rssi <= -100) WIFIsignal = 4; // -100dbm to -81dbm displays 1-bar
display.fillRect(x + xpos * 5, y - WIFIsignal, 4, WIFIsignal, GxEPD_BLACK);
xpos++;
}
display.fillRect(x, y - 1, 4, 1, GxEPD_BLACK);
drawString(x + 5, y + 5, String(rssi) + "dBm", CENTER);
}
//#########################################################################################
boolean SetupTime() {
configTime(0, 0, ntpServer);
setenv("TZ", Timezone, 1);
delay(100);
bool TimeStatus = UpdateLocalTime();
return TimeStatus;
}
//#########################################################################################
boolean UpdateLocalTime() {
struct tm timeinfo;
char time_output[23], day_output[30], update_time[12];
while (!getLocalTime(&timeinfo, 5000)) { // Wait for 5-sec for time to synchronise
Serial.println("Failed to obtain time");
return false;
}
CurrentHour = timeinfo.tm_hour;
CurrentMin = timeinfo.tm_min;
CurrentSec = timeinfo.tm_sec;
CurrentDay = timeinfo.tm_wday;
// initial of 1st and 2nd 1/2 hour
if(bitRead(PSW, 0)) {
if(CurrentMin<29){
bitWrite(PSW, 1, 1);
bitWrite(PSW, 2, 0);}
else {
bitWrite(PSW, 1, 0);
bitWrite(PSW, 2, 1);}
bitWrite(PSW, 0, 0);
Serial.println(PSW, BIN);
}
//See http://www.cplusplus.com/reference/ctime/strftime/
//Serial.println(&timeinfo, "%a %b %d %Y %H:%M:%S"); // Displays: Saturday, June 24 2017 14:05:49
if (Units == "M") {
if (Language == "DE"){
sprintf(day_output,"%s, %02u. %s %04u",weekday_D[timeinfo.tm_wday],timeinfo.tm_mday,month_M[timeinfo.tm_mon],(timeinfo.tm_year)+1900);
// day_output >> So., 23. Juni 2019 <<
}
else {
sprintf(day_output, "%s %02u-%s-%04u", weekday_D[timeinfo.tm_wday], timeinfo.tm_mday, month_M[timeinfo.tm_mon], (timeinfo.tm_year) + 1900);
}
strftime(update_time, sizeof(update_time), "%H:%M:%S", &timeinfo); // Creates: '14:05:49'
sprintf(time_output, "%s %s", TXT_UPDATED, update_time);
}
else
{
strftime(day_output, sizeof(day_output), "%a %b-%d-%Y", &timeinfo); // Creates 'Sat May-31-2019'
strftime(update_time, sizeof(update_time), "%r", &timeinfo); // Creates: '02:05:49pm'
sprintf(time_output, "%s %s", TXT_UPDATED, update_time);
}
Date_str = day_output;
Time_str = time_output;
return true;
}
//#########################################################################################
void DrawBattery(int x, int y) {
uint8_t percentage = 100;
float voltage = analogRead(35) / 4096.0 * 7.46;
if (voltage > 1 ) { // Only display if there is a valid reading
Serial.println("Voltage = " + String(voltage));
percentage = 2836.9625 * pow(voltage, 4) - 43987.4889 * pow(voltage, 3) + 255233.8134 * pow(voltage, 2) - 656689.7123 * voltage + 632041.7303;
if (voltage >= 4.20) percentage = 100;
if (voltage <= 3.50) percentage = 0;
display.drawRect(x + 15, y - 12, 19, 10, GxEPD_BLACK);
display.fillRect(x + 34, y - 10, 2, 5, GxEPD_BLACK);
display.fillRect(x + 17, y - 10, 15 * percentage / 100.0, 6, GxEPD_BLACK);
drawString(x + 10, y - 11, String(percentage) + "%", RIGHT);
drawString(x + 13, y + 5, String(voltage, 2) + "v", CENTER);
}
}
//#########################################################################################
// Symbols are drawn on a relative 10x10grid and 1 scale unit = 1 drawing unit
void addcloud(int x, int y, int scale, int linesize) {
//Draw cloud outer
display.fillCircle(x - scale * 3, y, scale, GxEPD_BLACK); // Left most circle
display.fillCircle(x + scale * 3, y, scale, GxEPD_BLACK); // Right most circle
display.fillCircle(x - scale, y - scale, scale * 1.4, GxEPD_BLACK); // left middle upper circle
display.fillCircle(x + scale * 1.5, y - scale * 1.3, scale * 1.75, GxEPD_BLACK); // Right middle upper circle
display.fillRect(x - scale * 3 - 1, y - scale, scale * 6, scale * 2 + 1, GxEPD_BLACK); // Upper and lower lines
//Clear cloud inner
display.fillCircle(x - scale * 3, y, scale - linesize, GxEPD_WHITE); // Clear left most circle
display.fillCircle(x + scale * 3, y, scale - linesize, GxEPD_WHITE); // Clear right most circle
display.fillCircle(x - scale, y - scale, scale * 1.4 - linesize, GxEPD_WHITE); // left middle upper circle
display.fillCircle(x + scale * 1.5, y - scale * 1.3, scale * 1.75 - linesize, GxEPD_WHITE); // Right middle upper circle
display.fillRect(x - scale * 3 + 2, y - scale + linesize - 1, scale * 5.9, scale * 2 - linesize * 2 + 2, GxEPD_WHITE); // Upper and lower lines
}
//#########################################################################################
void addraindrop(int x, int y, int scale) {
display.fillCircle(x, y, scale / 2, GxEPD_BLACK);
display.fillTriangle(x - scale / 2, y, x, y - scale * 1.2, x + scale / 2, y , GxEPD_BLACK);
x = x + scale * 1.6; y = y + scale / 3;
display.fillCircle(x, y, scale / 2, GxEPD_BLACK);
display.fillTriangle(x - scale / 2, y, x, y - scale * 1.2, x + scale / 2, y , GxEPD_BLACK);
}
//#########################################################################################
void addrain(int x, int y, int scale, bool IconSize) {
if (IconSize == SmallIcon) scale *= 1.34;
for (int d = 0; d < 4; d++) {
addraindrop(x + scale * (7.8 - d * 1.95) - scale * 5.2, y + scale * 2.1 - scale / 6, scale / 1.6);
}
}
//#########################################################################################
void addsnow(int x, int y, int scale, bool IconSize) {
int dxo, dyo, dxi, dyi;
for (int flakes = 0; flakes < 5; flakes++) {
for (int i = 0; i < 360; i = i + 45) {
dxo = 0.5 * scale * cos((i - 90) * 3.14 / 180); dxi = dxo * 0.1;
dyo = 0.5 * scale * sin((i - 90) * 3.14 / 180); dyi = dyo * 0.1;
display.drawLine(dxo + x + flakes * 1.5 * scale - scale * 3, dyo + y + scale * 2, dxi + x + 0 + flakes * 1.5 * scale - scale * 3, dyi + y + scale * 2, GxEPD_BLACK);
}
}
}
//#########################################################################################
void addtstorm(int x, int y, int scale) {
y = y + scale / 2;
for (int i = 0; i < 5; i++) {
display.drawLine(x - scale * 4 + scale * i * 1.5 + 0, y + scale * 1.5, x - scale * 3.5 + scale * i * 1.5 + 0, y + scale, GxEPD_BLACK);
if (scale != Small) {
display.drawLine(x - scale * 4 + scale * i * 1.5 + 1, y + scale * 1.5, x - scale * 3.5 + scale * i * 1.5 + 1, y + scale, GxEPD_BLACK);
display.drawLine(x - scale * 4 + scale * i * 1.5 + 2, y + scale * 1.5, x - scale * 3.5 + scale * i * 1.5 + 2, y + scale, GxEPD_BLACK);
}
display.drawLine(x - scale * 4 + scale * i * 1.5, y + scale * 1.5 + 0, x - scale * 3 + scale * i * 1.5 + 0, y + scale * 1.5 + 0, GxEPD_BLACK);
if (scale != Small) {
display.drawLine(x - scale * 4 + scale * i * 1.5, y + scale * 1.5 + 1, x - scale * 3 + scale * i * 1.5 + 0, y + scale * 1.5 + 1, GxEPD_BLACK);
display.drawLine(x - scale * 4 + scale * i * 1.5, y + scale * 1.5 + 2, x - scale * 3 + scale * i * 1.5 + 0, y + scale * 1.5 + 2, GxEPD_BLACK);
}
display.drawLine(x - scale * 3.5 + scale * i * 1.4 + 0, y + scale * 2.5, x - scale * 3 + scale * i * 1.5 + 0, y + scale * 1.5, GxEPD_BLACK);
if (scale != Small) {
display.drawLine(x - scale * 3.5 + scale * i * 1.4 + 1, y + scale * 2.5, x - scale * 3 + scale * i * 1.5 + 1, y + scale * 1.5, GxEPD_BLACK);
display.drawLine(x - scale * 3.5 + scale * i * 1.4 + 2, y + scale * 2.5, x - scale * 3 + scale * i * 1.5 + 2, y + scale * 1.5, GxEPD_BLACK);
}
}
}
//#########################################################################################
void addsun(int x, int y, int scale, bool IconSize) {
int linesize = 3;
if (IconSize == SmallIcon) linesize = 1;
display.fillRect(x - scale * 2, y, scale * 4, linesize, GxEPD_BLACK);
display.fillRect(x, y - scale * 2, linesize, scale * 4, GxEPD_BLACK);
display.drawLine(x - scale * 1.3, y - scale * 1.3, x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK);
display.drawLine(x - scale * 1.3, y + scale * 1.3, x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK);
if (IconSize == LargeIcon) {
display.drawLine(1 + x - scale * 1.3, y - scale * 1.3, 1 + x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK);
display.drawLine(2 + x - scale * 1.3, y - scale * 1.3, 2 + x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK);
display.drawLine(3 + x - scale * 1.3, y - scale * 1.3, 3 + x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK);
display.drawLine(1 + x - scale * 1.3, y + scale * 1.3, 1 + x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK);
display.drawLine(2 + x - scale * 1.3, y + scale * 1.3, 2 + x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK);
display.drawLine(3 + x - scale * 1.3, y + scale * 1.3, 3 + x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK);
}
display.fillCircle(x, y, scale * 1.3, GxEPD_WHITE);
display.fillCircle(x, y, scale, GxEPD_BLACK);
display.fillCircle(x, y, scale - linesize, GxEPD_WHITE);
}
//#########################################################################################
void addfog(int x, int y, int scale, int linesize, bool IconSize) {
if (IconSize == SmallIcon) {
y -= 10;
linesize = 1;
}
for (int i = 0; i < 6; i++) {
display.fillRect(x - scale * 3, y + scale * 1.5, scale * 6, linesize, GxEPD_BLACK);
display.fillRect(x - scale * 3, y + scale * 2.0, scale * 6, linesize, GxEPD_BLACK);
display.fillRect(x - scale * 3, y + scale * 2.5, scale * 6, linesize, GxEPD_BLACK);
}
}
//#########################################################################################
void Sunny(int x, int y, bool IconSize, String IconName) {
int scale = Small;
if (IconSize == LargeIcon) scale = Large;
else y = y - 3; // Shift up small sun icon
if (IconName.endsWith("n")) addmoon(x, y + 3, scale, IconSize);
scale = scale * 1.6;
addsun(x, y, scale, IconSize);
}
//#########################################################################################
void MostlySunny(int x, int y, bool IconSize, String IconName) {
int scale = Small, linesize = 3, offset = 5;
if (IconSize == LargeIcon) {
scale = Large;
offset = 10;
}
if (scale == Small) linesize = 1;
if (IconName.endsWith("n")) addmoon(x, y + offset, scale, IconSize);
addcloud(x, y + offset, scale, linesize);
addsun(x - scale * 1.8, y - scale * 1.8 + offset, scale, IconSize);
}
//#########################################################################################
void MostlyCloudy(int x, int y, bool IconSize, String IconName) {
int scale = Small, linesize = 3;
if (IconSize == LargeIcon) {
scale = Large;
linesize = 1;
}
if (IconName.endsWith("n")) addmoon(x, y, scale, IconSize);
addcloud(x, y, scale, linesize);
addsun(x - scale * 1.8, y - scale * 1.8, scale, IconSize);
addcloud(x, y, scale, linesize);
}
//#########################################################################################
void Cloudy(int x, int y, bool IconSize, String IconName) {
int scale = Large, linesize = 3;
if (IconSize == SmallIcon) {
scale = Small;
if (IconName.endsWith("n")) addmoon(x, y, scale, IconSize);
linesize = 1;
addcloud(x, y, scale, linesize);
}
else {
y += 10;
if (IconName.endsWith("n")) addmoon(x, y, scale, IconSize);
addcloud(x + 30, y - 45, 5, linesize); // Cloud top right
addcloud(x - 20, y - 30, 7, linesize); // Cloud top left
addcloud(x, y, scale, linesize); // Main cloud
}
}
//#########################################################################################
void Rain(int x, int y, bool IconSize, String IconName) {
int scale = Large, linesize = 3;
if (IconSize == SmallIcon) {
scale = Small;
linesize = 1;
}
if (IconName.endsWith("n")) addmoon(x, y, scale, IconSize);
addcloud(x, y, scale, linesize);
addrain(x, y, scale, IconSize);
}
//#########################################################################################
void ExpectRain(int x, int y, bool IconSize, String IconName) {
int scale = Large, linesize = 3;
if (IconSize == SmallIcon) {
scale = Small;
linesize = 1;
}
if (IconName.endsWith("n")) addmoon(x, y, scale, IconSize);
addsun(x - scale * 1.8, y - scale * 1.8, scale, IconSize);
addcloud(x, y, scale, linesize);
addrain(x, y, scale, IconSize);
}
//#########################################################################################
void ChanceRain(int x, int y, bool IconSize, String IconName) {
int scale = Large, linesize = 3;
if (IconSize == SmallIcon) {
scale = Small;
linesize = 1;
}
if (IconName.endsWith("n")) addmoon(x, y, scale, IconSize);
addsun(x - scale * 1.8, y - scale * 1.8, scale, IconSize);
addcloud(x, y, scale, linesize);
addrain(x, y, scale, IconSize);
}
//#########################################################################################
void Tstorms(int x, int y, bool IconSize, String IconName) {
int scale = Large, linesize = 3;
if (IconSize == SmallIcon) {
scale = Small;
linesize = 1;
}
if (IconName.endsWith("n")) addmoon(x, y, scale, IconSize);
addcloud(x, y, scale, linesize);
addtstorm(x, y, scale);
}
//#########################################################################################
void Snow(int x, int y, bool IconSize, String IconName) {
int scale = Large, linesize = 3;
if (IconSize == SmallIcon) {
scale = Small;
linesize = 1;
}
if (IconName.endsWith("n")) addmoon(x, y, scale, IconSize);
addcloud(x, y, scale, linesize);
addsnow(x, y, scale, IconSize);
}
//#########################################################################################
void Fog(int x, int y, bool IconSize, String IconName) {
int linesize = 3, scale = Large;
if (IconSize == SmallIcon) {
scale = Small;
linesize = 1;
}
if (IconName.endsWith("n")) addmoon(x, y, scale, IconSize);
addcloud(x, y - 5, scale, linesize);
addfog(x, y - 5, scale, linesize, IconSize);
}
//#########################################################################################
void Haze(int x, int y, bool IconSize, String IconName) {
int linesize = 3, scale = Large;
if (IconSize == SmallIcon) {
scale = Small;
linesize = 1;
}
if (IconName.endsWith("n")) addmoon(x, y, scale, IconSize);
addsun(x, y - 5, scale * 1.4, IconSize);
addfog(x, y - 5, scale * 1.4, linesize, IconSize);
}
//#########################################################################################
void CloudCover(int x, int y, int CCover) {
addcloud(x - 9, y - 3, Small * 0.5, 2); // Cloud top left
addcloud(x + 3, y - 3, Small * 0.5, 2); // Cloud top right
addcloud(x, y, Small * 0.5, 2); // Main cloud
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
drawString(x + 15, y - 5, String(CCover) + "%", LEFT);
}
//#########################################################################################
void Visibility(int x, int y, String Visi) {
y = y - 3; //
float start_angle = 0.52, end_angle = 2.61;
int r = 10;
for (float i = start_angle; i < end_angle; i = i + 0.05) {
display.drawPixel(x + r * cos(i), y - r / 2 + r * sin(i), GxEPD_BLACK);
display.drawPixel(x + r * cos(i), 1 + y - r / 2 + r * sin(i), GxEPD_BLACK);
}
start_angle = 3.61; end_angle = 5.78;
for (float i = start_angle; i < end_angle; i = i + 0.05) {
display.drawPixel(x + r * cos(i), y + r / 2 + r * sin(i), GxEPD_BLACK);
display.drawPixel(x + r * cos(i), 1 + y + r / 2 + r * sin(i), GxEPD_BLACK);
}
display.fillCircle(x, y, r / 4, GxEPD_BLACK);
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
drawString(x + 12, y - 3, Visi, LEFT);
}
//#########################################################################################
void addmoon(int x, int y, int scale, bool IconSize) {
if (IconSize == LargeIcon) {
display.fillCircle(x - 50, y - 55, scale, GxEPD_BLACK);
display.fillCircle(x - 35, y - 55, scale * 1.6, GxEPD_WHITE);
}
else
{
display.fillCircle(x - 20, y - 12, scale, GxEPD_BLACK);
display.fillCircle(x - 15, y - 12, scale * 1.6, GxEPD_WHITE);
}
}
//#########################################################################################
void Nodata(int x, int y, bool IconSize, String IconName) {
if (IconSize == LargeIcon) u8g2Fonts.setFont(u8g2_font_helvB24_tf); else u8g2Fonts.setFont(u8g2_font_helvB10_tf);
drawString(x - 3, y - 8, "?", CENTER);
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
}
//#########################################################################################
/* (C) D L BIRD
This function will draw a graph on a ePaper/TFT/LCD display using data from an array containing data to be graphed.
The variable 'max_readings' determines the maximum number of data elements for each array. Call it with the following parametric data:
x_pos-the x axis top-left position of the graph
y_pos-the y-axis top-left position of the graph, e.g. 100, 200 would draw the graph 100 pixels along and 200 pixels down from the top-left of the screen
width-the width of the graph in pixels
height-height of the graph in pixels
Y1_Max-sets the scale of plotted data, for example 5000 would scale all data to a Y-axis of 5000 maximum
data_array1 is parsed by value, externally they can be called anything else, e.g. within the routine it is called data_array1, but externally could be temperature_readings
auto_scale-a logical value (TRUE or FALSE) that switches the Y-axis autoscale On or Off
barchart_on-a logical value (TRUE or FALSE) that switches the drawing mode between barhcart and line graph
barchart_colour-a sets the title and graph plotting colour
If called with Y!_Max value of 500 and the data never goes above 500, then autoscale will retain a 0-500 Y scale, if on, the scale increases/decreases to match the data.
auto_scale_margin, e.g. if set to 1000 then autoscale increments the scale by 1000 steps.
*/
void DrawGraph(int x_pos, int y_pos, int gwidth, int gheight, float Y1Min, float Y1Max, String title, float DataArray[], int readings, boolean auto_scale, boolean barchart_mode) {
#define auto_scale_margin 0 // Sets the autoscale increment, so axis steps up in units of e.g. 3
#define y_minor_axis 5 // 5 y-axis division markers
int maxYscale = -10000;
int minYscale = 10000;
int last_x, last_y;
float x2, y2;
if (auto_scale == true) {
for (int i = 1; i < readings; i++ ) {
if (DataArray[i] >= maxYscale) maxYscale = DataArray[i];
if (DataArray[i] <= minYscale) minYscale = DataArray[i];
}
maxYscale = round(maxYscale + auto_scale_margin); // Auto scale the graph and round to the nearest value defined, default was Y1Max
Y1Max = round(maxYscale + 0.5);
if (minYscale != 0) minYscale = round(minYscale - auto_scale_margin); // Auto scale the graph and round to the nearest value defined, default was Y1Min
Y1Min = round(minYscale);
}
// Draw the graph
last_x = x_pos + 1;
last_y = y_pos + (Y1Max - constrain(DataArray[1], Y1Min, Y1Max)) / (Y1Max - Y1Min) * gheight;
display.drawRect(x_pos, y_pos, gwidth + 3, gheight + 2, GxEPD_BLACK);
drawString(x_pos + gwidth / 2 + 5, y_pos - 13, title, CENTER);
// Draw the data
for (int gx = 1; gx < readings; gx++) {
x2 = x_pos + gx * gwidth / (readings - 1) - 1 ; // max_readings is the global variable that sets the maximum data that can be plotted
y2 = y_pos + (Y1Max - constrain(DataArray[gx], Y1Min, Y1Max)) / (Y1Max - Y1Min) * gheight + 1;
if (barchart_mode) {
display.fillRect(x2, y2, (gwidth / readings) - 1, y_pos + gheight - y2 + 2, GxEPD_BLACK);
} else {
display.drawLine(last_x, last_y, x2, y2, GxEPD_BLACK);
}
last_x = x2;
last_y = y2;
}
//Draw the Y-axis scale
#define number_of_dashes 15
for (int spacing = 0; spacing <= y_minor_axis; spacing++) {
for (int j = 0; j < number_of_dashes; j++) { // Draw dashed graph grid lines
if (spacing < y_minor_axis) display.drawFastHLine((x_pos + 3 + j * gwidth / number_of_dashes), y_pos + (gheight * spacing / y_minor_axis), gwidth / (2 * number_of_dashes), GxEPD_BLACK);
}
if ((Y1Max - (float)(Y1Max - Y1Min) / y_minor_axis * spacing) < 5 || title == TXT_PRESSURE_IN) {
drawString(x_pos, y_pos + gheight * spacing / y_minor_axis - 5, String((Y1Max - (float)(Y1Max - Y1Min) / y_minor_axis * spacing + 0.01), 1), RIGHT);
}
else
{
if (Y1Min < 1 && Y1Max < 10)
drawString(x_pos - 3, y_pos + gheight * spacing / y_minor_axis - 5, String((Y1Max - (float)(Y1Max - Y1Min) / y_minor_axis * spacing + 0.01), 1), RIGHT);
else
drawString(x_pos - 3, y_pos + gheight * spacing / y_minor_axis - 5, String((Y1Max - (float)(Y1Max - Y1Min) / y_minor_axis * spacing + 0.01), 0), RIGHT);
}
}
for (int i = 0; i <= 2; i++) {
drawString(15 + x_pos + gwidth / 3 * i, y_pos + gheight + 3, String(i), LEFT);
}
drawString(x_pos + gwidth / 2, y_pos + gheight + 14, TXT_DAYS, CENTER);
}
//#########################################################################################
void drawString(int x, int y, String text, alignment align) {
int16_t x1, y1; //the bounds of x,y and w and h of the variable 'text' in pixels.
uint16_t w, h;
display.setTextWrap(false);
display.getTextBounds(text, x, y, &x1, &y1, &w, &h);
if (align == RIGHT) x = x - w;
if (align == CENTER) x = x - w / 2;
u8g2Fonts.setCursor(x, y + h);
u8g2Fonts.print(text);
}
//#########################################################################################
void drawStringMaxWidth(int x, int y, unsigned int text_width, String text, alignment align) {
int16_t x1, y1; //the bounds of x,y and w and h of the variable 'text' in pixels.
uint16_t w, h;
display.getTextBounds(text, x, y, &x1, &y1, &w, &h);
if (align == RIGHT) x = x - w;
if (align == CENTER) x = x - w / 2;
u8g2Fonts.setCursor(x, y);
if (text.length() > text_width * 2) {
u8g2Fonts.setFont(u8g2_font_helvB10_tf);
text_width = 42;
y = y - 3;
}
u8g2Fonts.println(text.substring(0, text_width));
if (text.length() > text_width) {
u8g2Fonts.setCursor(x, y + h + 15);
String secondLine = text.substring(text_width);
secondLine.trim(); // Remove any leading spaces
u8g2Fonts.println(secondLine);
}
}
//#########################################################################################
void InitialiseDisplay() {
display.init(115200);
u8g2Fonts.begin(display); // connect u8g2 procedures to Adafruit GFX
u8g2Fonts.setFontMode(1); // use u8g2 transparent mode (this is default)
u8g2Fonts.setFontDirection(0); // left to right (this is default)
u8g2Fonts.setForegroundColor(GxEPD_BLACK); // apply Adafruit GFX color
u8g2Fonts.setBackgroundColor(GxEPD_WHITE); // apply Adafruit GFX color
u8g2Fonts.setFont(u8g2_font_helvB10_tf); // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall
display.fillScreen(GxEPD_WHITE);
display.setFullWindow();
}
//#########################################################################################
/*String Translate_EN_DE(String text) {
if (text == "clear") return "klar";
if (text == "sunny") return "sonnig";
if (text == "mist") return "Nebel";
if (text == "fog") return "Nebel";
if (text == "rain") return "Regen";
if (text == "shower") return "Regenschauer";
if (text == "cloudy") return "wolkig";
if (text == "clouds") return "Wolken";
if (text == "drizzle") return "Nieselregen";
if (text == "snow") return "Schnee";
if (text == "thunderstorm") return "Gewitter";
if (text == "light") return "leichter";
if (text == "heavy") return "schwer";
if (text == "mostly cloudy") return "größtenteils bewölkt";
if (text == "overcast clouds") return "überwiegend bewölkt";
if (text == "scattered clouds") return "aufgelockerte Bewölkung";
if (text == "few clouds") return "ein paar Wolken";
if (text == "clear sky") return "klarer Himmel";
if (text == "broken clouds") return "aufgerissene Bewölkung";
if (text == "light rain") return "leichter Regen";
return text;
}
*/
/*
Version 16.0 reformatted to use u8g2 fonts
1. Added ß to translations, eventually that conversion can move to the lang_xx.h file
2. Spaced temperature, pressure and precipitation equally, suggest in DE use 'niederschlag' for 'Rain/Snow'
3. No-longer displays Rain or Snow unless there has been any.
4. The nn-mm 'Rain suffix' has been replaced with two rain drops
5. Similarly for 'Snow' two snow flakes, no words and '=Rain' and '"=Snow' for none have gone.
6. Improved the Cloud Cover icon and only shows if reported, 0% cloud (clear sky) is no-report and no icon.
7. Added a Visibility icon and reported distance in Metres. Only shows if reported.
8. Fixed the occasional sleep time error resulting in constant restarts, occurred when updates took longer than expected.
9. Improved the smaller sun icon.
10. Added more space for the Sunrise/Sunset and moon phases when translated.
Version 16.1 Correct timing errors after sleep - persistent problem that is not deterministic
1. Removed Weather (Main) category e.g. previously 'Clear (Clear sky)', now only shows area category of 'Clear sky' and then ', caterory1' and ', category2'
2. Improved accented character displays
Version 16.2 Correct comestic icon issues
1. At night the addition of a moon icon overwrote the Visibility report, so order of drawing was changed to prevent this.
2. RainDrop icon was too close to the reported value of rain, moved right. Same for Snow Icon.
3. Improved large sun icon sun rays and improved all icon drawing logic, rain drops now use common shape.
5. Moved MostlyCloudy Icon down to align with the rest, same for MostlySunny.
6. Improved graph axis alignment.
Version 16.3 Correct comestic icon issues
1. Reverted some aspects of UpdateLocalTime() as locialisation changes were unecessary and can be achieved through lang_aa.h files
2. Correct configuration mistakes with moon calculations.
*/
[/code]
kann mir da jemand helfen
FG Olaf