Hallo zusammen,
ich habe einen Code für eine Wetterstation aus dem Internet. Ich möchte jedoch die WLAN-Daten und die Standort-Daten flexibel änderbar machen. Zu diesem Zweck habe ich einen Code geschrieben, der einen Access-Point einrichtet. Dort kann man die WLAN-Daten eingeben und seinen Standort angeben, wobei ein API-Key die genauen Standortdaten abruft. Leider gelingt es mir nicht, beide Codes erfolgreich zu kombinieren. Ich hoffe daher auf eure Unterstützung.
Vielen Dank im Voraus!
Viele Grüße
der_beste_itsystemelektroniker
Der Code für die Wetterstation:
/* ESP32 Weather Display using an EPD 4.2" Display, obtains data from Open Weather Map, decodes it 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
*/
#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
#include <WiFi.h> // Built-in
#include "time.h" // Built-in
#include <SPI.h> // Built-in
#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"
//#include "lang_cz.h" // Localisation (Czech)
//#include "lang_fr.h" // Localisation (French)
#include "lang_gr.h" // Localisation (German)
//#include "lang_it.h" // Localisation (Italian)
//#include "lang_nl.h" // Localisation (Dutch)
//#include "lang_pl.h" // Localisation (Polish)
#define SCREEN_WIDTH 400.0 // Set for landscape mode, don't remove the decimal place!
#define SCREEN_HEIGHT 300.0
enum alignment {LEFT, RIGHT, CENTER};
/* // Connections for e.g. LOLIN D32
static const uint8_t EPD_BUSY = 4; // to EPD BUSY
static const uint8_t EPD_CS = 5; // to EPD CS
static const uint8_t EPD_RST = 16; // to EPD RST
static const uint8_t EPD_DC = 17; // to EPD DC
static const uint8_t EPD_SCK = 18; // to EPD CLK
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; // to EPD DIN
*/
// 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 = 12; // Master-In Slave-Out not used, as no data from display
static const uint8_t EPD_MOSI = 14;
GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> display(GxEPD2_420_GDEY042T81(/*CS=D8*/ EPD_CS, /*DC=D3*/ EPD_DC, /*RST=D4*/ EPD_RST, /*BUSY=D2*/ 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
//################ VERSION ##########################
String version = "12.5"; // Version of this program
//################ VARIABLES ###########################
boolean LargeIcon = true, SmallIcon = false;
#define Large 11 // 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 data
int wifi_signal, CurrentHour = 0, CurrentMin = 0, CurrentSec = 0;
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 = 30; // 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 = 7; // Don't wakeup until after 07:00 to save battery power
int SleepTime = 23; // Sleep after (23+1) 00:00 to save battery power
//#########################################################################################
void setup() {
StartTime = millis();
Serial.begin(115200);
if (StartWiFi() == WL_CONNECTED && SetupTime() == true) {
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
}
}
}
BeginSleep();
}
//#########################################################################################
void loop() { // this will never run!
}
//#########################################################################################
void BeginSleep() {
display.powerOff();
long SleepTimer = (SleepDuration * 60 - ((CurrentMin % SleepDuration) * 60 + CurrentSec)); //Some ESP32 are too fast to maintain accurate time
esp_sleep_enable_timer_wakeup((SleepTimer+20) * 1000000LL); // Added +20 seconnds to cover ESP32 RTC timer source inaccuracies
#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. 30 minutes
}
//#########################################################################################
void DisplayWeather() { // 4.2" e-paper display is 400x300 resolution
DrawHeadingSection(); // Top line of the display
DrawMainWeatherSection(172, 70); // Centre section of display for Location, temperature, Weather report, current Wx Symbol and wind direction
DrawForecastSection(233, 15); // 3hr forecast boxes
DisplayPrecipitationSection(233, 82); // Precipitation sectio
if (WxConditions[0].Visibility > 0) Visibility(335, 100, String(WxConditions[0].Visibility) + "M");
if (WxConditions[0].Cloudcover > 0) CloudCover(350, 125, WxConditions[0].Cloudcover);
DrawAstronomySection(233, 74); // Astronomy section Sun rise/set, Moon phase and Moon icon
}
//#########################################################################################
void DrawHeadingSection() {
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
drawString(SCREEN_WIDTH / 2, 0, City, CENTER);
drawString(SCREEN_WIDTH, 0, date_str, RIGHT);
drawString(4, 0, time_str, LEFT);
DrawBattery(65, 12);
display.drawLine(0, 12, SCREEN_WIDTH, 12, GxEPD_BLACK);
}
//#########################################################################################
void DrawMainWeatherSection(int x, int y) {
DisplayDisplayWindSection(x - 115, y - 3, WxConditions[0].Winddir, WxConditions[0].Windspeed, 40);
DisplayWXicon(x + 5, y - 5, WxConditions[0].Icon, LargeIcon);
u8g2Fonts.setFont(u8g2_font_helvB10_tf);
DrawPressureAndTrend(x - 120, y + 58, WxConditions[0].Pressure, WxConditions[0].Trend);
u8g2Fonts.setFont(u8g2_font_helvB12_tf);
String Wx_Description = WxConditions[0].Forecast0;
if (WxConditions[0].Forecast1 != "") Wx_Description += " & " + WxConditions[0].Forecast1;
if (WxConditions[0].Forecast2 != "" && WxConditions[0].Forecast1 != WxConditions[0].Forecast2) Wx_Description += " & " + WxConditions[0].Forecast2;
drawStringMaxWidth(x - 170, y + 83, 28, TitleCase(Wx_Description), LEFT);
DrawMainWx(x, y + 60);
display.drawRect(0, y + 68, 232, 48, GxEPD_BLACK);
}
//#########################################################################################
void DrawForecastSection(int x, int y) {
u8g2Fonts.setFont(u8g2_font_helvB10_tf);
DrawForecastWeather(x, y, 0);
DrawForecastWeather(x + 56, y, 1);
DrawForecastWeather(x + 112, y, 2);
// (x,y,width,height,MinValue, MaxValue, Title, Data Array, AutoScale, ChartMode)
for (int r = 1; r < max_readings; r++) {
if (Units == "I") {
pressure_readings[r] = WxForecast[r].Pressure * 0.02953;
rain_readings[r] = WxForecast[r].Rainfall * 0.0393701;
}
else {
pressure_readings[r] = WxForecast[r].Pressure;
rain_readings[r] = WxForecast[r].Rainfall;
}
temperature_readings[r] = WxForecast[r].Temperature;
}
display.drawLine(0, y + 172, SCREEN_WIDTH, y + 172, GxEPD_BLACK);
u8g2Fonts.setFont(u8g2_font_helvB12_tf);
drawString(SCREEN_WIDTH / 2, y + 180, TXT_FORECAST_VALUES, CENTER);
u8g2Fonts.setFont(u8g2_font_helvB10_tf);
DrawGraph(SCREEN_WIDTH / 400 * 30, SCREEN_HEIGHT / 300 * 221, SCREEN_WIDTH / 4, SCREEN_HEIGHT / 5, 900, 1050, Units == "M" ? TXT_PRESSURE_HPA : TXT_PRESSURE_IN, pressure_readings, max_readings, autoscale_on, barchart_off);
DrawGraph(SCREEN_WIDTH / 400 * 158, SCREEN_HEIGHT / 300 * 221, SCREEN_WIDTH / 4, SCREEN_HEIGHT / 5, 10, 30, Units == "M" ? TXT_TEMPERATURE_C : TXT_TEMPERATURE_F, temperature_readings, max_readings, autoscale_on, barchart_off);
DrawGraph(SCREEN_WIDTH / 400 * 288, SCREEN_HEIGHT / 300 * 221, SCREEN_WIDTH / 4, SCREEN_HEIGHT / 5, 0, 30, Units == "M" ? TXT_RAINFALL_MM : TXT_RAINFALL_IN, rain_readings, max_readings, autoscale_on, barchart_on);
}
//#########################################################################################
void DrawForecastWeather(int x, int y, int index) {
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
display.drawRect(x, y, 55, 65, GxEPD_BLACK);
display.drawLine(x + 1, y + 13, x + 54, y + 13, GxEPD_BLACK);
DisplayWXicon(x + 28, y + 35, WxForecast[index].Icon, SmallIcon);
drawString(x + 31, y + 3, String(ConvertUnixTime(WxForecast[index].Dt + WxConditions[0].Timezone).substring(0,5)), CENTER);
drawString(x + 41, y + 52, String(WxForecast[index].High, 0) + "° / " + String(WxForecast[index].Low, 0) + "°", CENTER);
}
//#########################################################################################
void DrawMainWx(int x, int y) {
u8g2Fonts.setFont(u8g2_font_helvB14_tf);
drawString(x - 25, y - 22, String(WxConditions[0].Temperature, 1) + "°" + (Units == "M" ? "C" : "F"), CENTER); // Show current Temperature
u8g2Fonts.setFont(u8g2_font_helvB12_tf);
drawString(x - 15, y - 3, String(WxConditions[0].High, 0) + "° | " + String(WxConditions[0].Low, 0) + "°", CENTER); // Show forecast high and Low
drawString(x + 30, y - 22, String(WxConditions[0].Humidity, 0) + "%", CENTER);
u8g2Fonts.setFont(u8g2_font_helvB10_tf);
drawString(x + 32, y - 3, "RH", CENTER);
}
//#########################################################################################
void DisplayDisplayWindSection(int x, int y, float angle, float windspeed, int Cradius) {
arrow(x, y, Cradius - 7, angle, 12, 18); // Show wind direction on outer circle of width and length
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
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);
}
drawString(x, y - Cradius - 10, TXT_N, CENTER);
drawString(x, y + Cradius + 5, TXT_S, CENTER);
drawString(x - Cradius - 10, y - 3, TXT_W, CENTER);
drawString(x + Cradius + 8, y - 3, TXT_E, CENTER);
drawString(x - 2, y - 20, WindDegToDirection(angle), CENTER);
drawString(x + 3, y + 12, String(angle, 0) + "°", CENTER);
drawString(x + 3, y - 3, String(windspeed, 1) + (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 DrawPressureAndTrend(int x, int y, float pressure, String slope) {
drawString(x, y, String(pressure, (Units == "M" ? 0 : 1)) + (Units == "M" ? "mb" : "in"), CENTER);
x = x + 40; y = y + 2;
if (slope == "+") {
display.drawLine(x, y, x + 4, y - 4, GxEPD_BLACK);
display.drawLine(x + 4, y - 4, x + 8, y, GxEPD_BLACK);
}
else if (slope == "0") {
display.drawLine(x + 4, y - 4, x + 8, y, GxEPD_BLACK);
display.drawLine(x + 4, y + 4, x + 8, y, GxEPD_BLACK);
}
else if (slope == "-") {
display.drawLine(x, y, x + 4, y + 4, GxEPD_BLACK);
display.drawLine(x + 4, y + 4, x + 8, y, GxEPD_BLACK);
}
}
//#########################################################################################
void DisplayPrecipitationSection(int x, int y) {
display.drawRect(x, y - 1, 167, 56, GxEPD_BLACK); // precipitation outline
u8g2Fonts.setFont(u8g2_font_helvB10_tf);
if (WxForecast[1].Rainfall > 0.005) { // Ignore small amounts
drawString(x + 5, y + 15, String(WxForecast[1].Rainfall, 2) + (Units == "M" ? "mm" : "in"), LEFT); // Only display rainfall total today if > 0
addraindrop(x + 65 - (Units == "I" ? 10 : 0), y + 16, 7);
}
if (WxForecast[1].Snowfall > 0.005) // Ignore small amounts
drawString(x + 5, y + 35, String(WxForecast[1].Snowfall, 2) + (Units == "M" ? "mm" : "in") + " * *", LEFT); // Only display snowfall total today if > 0
}
//#########################################################################################
void DrawAstronomySection(int x, int y) {
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
display.drawRect(x, y + 64, 167, 48, GxEPD_BLACK);
drawString(x + 7, y + 70, ConvertUnixTime(WxConditions[0].Sunrise).substring(0, (Units == "M" ? 5 : 7)) + " " + TXT_SUNRISE, LEFT);
drawString(x + 7, y + 85, ConvertUnixTime(WxConditions[0].Sunset).substring(0, (Units == "M" ? 5 : 7)) + " " + 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 + 7, y + 100, MoonPhase(day_utc, month_utc, year_utc), LEFT);
DrawMoon(x + 105, y + 50, 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 <= 45; Ypos++) {
double Xpos = sqrt(45 * 45 - 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 + 1, GxEPD_BLACK);
}
//#########################################################################################
String MoonPhase(int d, int m, int y) {
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 */
Hemisphere.toLowerCase();
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 arrow(int x, int y, int asize, float aangle, int pwidth, int plength) {
float dx = (asize + 28) * cos((aangle - 90) * PI / 180) + x; // calculate X position
float dy = (asize + 28) * 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;
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);
}
//#########################################################################################
void DisplayWXicon(int x, int y, String IconName, bool IconSize) {
Serial.println(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);
}
//#########################################################################################
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); // deprecated
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);
}
//#########################################################################################
boolean SetupTime() {
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer, "time.nist.gov"); //(gmtOffset_sec, daylightOffset_sec, ntpServer)
setenv("TZ", Timezone, 1); //setenv()adds the "TZ" variable to the environment with a value TimeZone, only used if set to 1, 0 means no change
tzset(); // Set the TZ environment variable
delay(100);
bool TimeStatus = UpdateLocalTime();
return TimeStatus;
}
//#########################################################################################
boolean UpdateLocalTime() {
struct tm timeinfo;
char time_output[30], day_output[30], update_time[30];
while (!getLocalTime(&timeinfo, 10000)) { // 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;
//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 == "CZ") || (Language == "DE") || (Language == "NL") || (Language == "PL")) {
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' and change from 30 to 8 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
sprintf(time_output, "%s", 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", update_time);
}
date_str = day_output;
time_str = time_output;
return true;
}
//#########################################################################################
// 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, offset = 3;
if (IconSize == LargeIcon) {
scale = Large;
y = y - 8;
offset = 18;
} else y = y - 3; // Shift up small sun icon
if (IconName.endsWith("n")) addmoon(x, y + offset, 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 = 3;
if (IconSize == LargeIcon) {
scale = Large;
offset = 10;
} else 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 - 35, 5, linesize); // Cloud top right
addcloud(x - 20, y - 25, 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 + 10, 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 + 15, 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) {
x = x + 12; y = y + 12;
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);
}
//#########################################################################################
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 + 65, y - 11, String(percentage) + "%", RIGHT);
//drawString(x + 13, y + 5, String(voltage, 2) + "v", CENTER);
}
}
//#########################################################################################
/* (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 bar and line graphs
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
float maxYscale = -10000;
float minYscale = 10000;
int last_x, last_y;
float x1, y1, 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);
u8g2Fonts.setFont(u8g2_font_helvB08_tf);
drawString(x_pos + gwidth / 2, y_pos - 12, title, CENTER);
// Draw the data
for (int gx = 1; gx < readings; gx++) {
x1 = last_x;
y1 = last_y;
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 + 1, 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 + 10, 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, true, 2, false);
// display.init(); for older Waveshare HAT's
SPI.end();
SPI.begin(EPD_SCK, EPD_MISO, EPD_MOSI, EPD_CS);
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();
}
/*
Version 12.0 reformatted to use u8g2 fonts
1. Screen layout revised
2. Made consitent with other versions specifically 7x5 variant
3. Introduced Visibility in Metres, Cloud cover in % and RH in %
4. Correct sunrise/sunset time when in imperial mode.
Version 12.1 Clarified Waveshare ESP32 driver board connections
Version 12.2 Changed GxEPD2 initialisation from 115200 to 0
1. display.init(115200); becomes display.init(0); to stop blank screen following update to GxEPD2
Version 12.3
1. Added 20-secs to allow for slow ESP32 RTC timers
Version 12.4
1. Improved graph drawing function for negative numbers Line 808
Version 12.5
1. Modified for GxEPD2 changes
*/
Die dazugehörige owm_credentials.h:
const char* ssid = "********";
const char* password = "*********";
// Use your own API key by signing up for a free developer account at https://openweathermap.org/
String apikey = "****************************"; // See: https://openweathermap.org/
const char server[] = "api.openweathermap.org";
//http://api.openweathermap.org/data/2.5/forecast?q=Melksham,UK&APPID=your_OWM_API_key&mode=json&units=metric&cnt=40
//http://api.openweathermap.org/data/2.5/weather?q=Melksham,UK&APPID=your_OWM_API_key&mode=json&units=metric&cnt=1
//Set your location according to OWM locations
String LAT = "*******"; // Home location Latitude
String LON = "*******"; // Home location Longitude
String City = "********"; // Your home city See: http://bulk.openweathermap.org/sample/
String Country = "********"; // Your _ISO-3166-1_two-letter_country_code country code, on OWM find your nearest city and the country code is displayed
// https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes
String Language = "********"; // NOTE: Only the weather description is translated by OWM
// Examples: Arabic (AR) Czech (CZ) English (EN) Greek (EL) Persian(Farsi) (FA) Galician (GL) Hungarian (HU) Japanese (JA)
// Korean (KR) Latvian (LA) Lithuanian (LT) Macedonian (MK) Slovak (SK) Slovenian (SL) Vietnamese (VI)
String Hemisphere = "******"; // or "south"
String Units = "*******"; // Use 'M' for Metric or I for Imperial
const char* Timezone = "********"; // Choose your time zone from: https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
// See below for examples
const char* ntpServer = "*****************"; // Or, choose a time server close to you, but in most cases it's best to use pool.ntp.org to find an NTP server
// then the NTP system decides e.g. 0.pool.ntp.org, 1.pool.ntp.org as the NTP syem tries to find the closest available servers
// EU "0.europe.pool.ntp.org"
// US "0.north-america.pool.ntp.org"
// See: https://www.ntppool.org/en/
int gmtOffset_sec = ****; // UK normal time is GMT, so GMT Offset is 0, for US (-5Hrs) is typically -18000, AU is typically (+8hrs) 28800
int daylightOffset_sec = ****; // In the UK DST is +1hr or 3600-secs, other countries may use 2hrs 7200 or 30-mins 1800 or 5.5hrs 19800 Ahead of GMT use + offset behind - offset
// Example time zones
//const char* Timezone = "MET-1METDST,M3.5.0/01,M10.5.0/02"; // Most of Europe
//const char* Timezone = "CET-1CEST,M3.5.0,M10.5.0/3"; // Central Europe
//const char* Timezone = "EST-2METDST,M3.5.0/01,M10.5.0/02"; // Most of Europe
//const char* Timezone = "EST5EDT,M3.2.0,M11.1.0"; // EST USA
//const char* Timezone = "CST6CDT,M3.2.0,M11.1.0"; // CST USA
//const char* Timezone = "MST7MDT,M4.1.0,M10.5.0"; // MST USA
//const char* Timezone = "NZST-12NZDT,M9.5.0,M4.1.0/3"; // Auckland
//const char* Timezone = "EET-2EEST,M3.5.5/0,M10.5.5/0"; // Asia
//const char* Timezone = "ACST-9:30ACDT,M10.1.0,M4.1.0/3": // Australia
Und hier noch mein Code für den Access Point:
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
// Access Point Konfigurationsparameter
const char *ap_ssid = "**********";
const char *ap_password = "**********";
// HERE API Zugangsdaten
const char *here_api_key = "***********************";
// Webserver auf Port 80 initialisieren
AsyncWebServer server(80);
// Variable zur Speicherung der ausgewählten WLAN-Verbindung
String selected_ssid;
String selected_password;
void setup() {
// Serielle Kommunikation starten
Serial.begin(115200);
delay(1000);
// Starten des Access Points
Serial.println("Einrichten des Access Points...");
WiFi.softAP(ap_ssid, ap_password);
// Überprüfen, ob der Access Point erfolgreich gestartet wurde
if (WiFi.softAPIP()) {
Serial.print("Access Point gestartet. IP-Adresse: ");
Serial.println(WiFi.softAPIP());
} else {
Serial.println("Fehler beim Starten des Access Points!");
}
// Route für die Wurzel-URL definieren und Formular zur WLAN-Auswahl senden
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
String html = "<html><body>";
html += "<h1>WLAN Auswahl</h1>";
html += "<form action='/connect' method='post'>";
html += "SSID: <input type='text' name='ssid'><br>";
html += "Passwort: <input type='password' name='password'><br>";
html += "<input type='submit' value='Verbinden'>";
html += "</form></body></html>";
request->send(200, "text/html", html);
});
// Route für die Verarbeitung des WLAN-Verbindung Formulars
server.on("/connect", HTTP_POST, [](AsyncWebServerRequest *request){
// Lesen der Parameter aus dem Formular
if (request->hasParam("ssid", true) && request->hasParam("password", true)) {
selected_ssid = request->getParam("ssid", true)->value();
selected_password = request->getParam("password", true)->value();
// Verbindung mit dem ausgewählten WLAN herstellen
Serial.print("Verbinde mit WLAN: ");
Serial.println(selected_ssid);
WiFi.begin(selected_ssid.c_str(), selected_password.c_str());
// Warten auf die Verbindung zum WLAN
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
yield(); // dem Betriebssystem Zeit zum Ausführen anderer Aufgaben geben
}
// Erfolgreiche Verbindung anzeigen
Serial.println("");
Serial.println("Verbunden mit dem WLAN Netzwerk!");
Serial.print("IP-Adresse: ");
Serial.println(WiFi.localIP());
// Weiterleitung zur Seite für die Ortseingabe
request->redirect("/location");
} else {
request->send(400); // Fehler, wenn nicht alle Parameter erhalten wurden
}
});
// Route für die Ortseingabe
server.on("/location", HTTP_GET, [](AsyncWebServerRequest *request){
String html = "<html><body>";
html += "<h1>Ort Eingabe</h1>";
html += "<form action='/getlocation' method='post'>";
html += "Ort: <input type='text' name='location'><br>";
html += "<input type='submit' value='Informationen abrufen'>";
html += "</form></body></html>";
request->send(200, "text/html", html);
});
// Route für die Verarbeitung der Ort-Informationen
server.on("/getlocation", HTTP_POST, [](AsyncWebServerRequest *request){
// Lesen des Orts aus dem Formular
if (request->hasParam("location", true)) {
String location = request->getParam("location", true)->value();
// Anfrage an die HERE Geocoding API senden
WiFiClientSecure client;
client.setInsecure(); // SSL-Zertifikatprüfung deaktivieren (nicht empfohlen für Produktion)
if (client.connect("geocode.search.hereapi.com", 443)) {
Serial.println("Verbunden mit HERE API");
String url = "/v1/geocode?q=";
url += urlEncode(location);
url += "&apiKey=";
url += here_api_key;
Serial.print("Senden der Anfrage an: ");
Serial.println(url);
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: geocode.search.hereapi.com\r\n" +
"Connection: close\r\n\r\n");
// Warten auf die Antwort der API
String response = "";
bool isBody = false;
while (client.connected()) {
String line = client.readStringUntil('\n');
if (line == "\r") {
isBody = true; // Trennen der Header von den tatsächlichen JSON-Daten
}
if (isBody) {
response += line + "\n";
}
yield(); // dem Betriebssystem Zeit zum Ausführen anderer Aufgaben geben
}
client.stop();
// JSON-Daten analysieren, nur wenn eine Antwort vorhanden ist
if (response.length() > 0) {
Serial.println("Antwort erhalten:");
Serial.println(response);
DynamicJsonDocument doc(4096); // Erhöhen Sie die Größe des JSON-Dokuments
DeserializationError error = deserializeJson(doc, response);
if (error) {
Serial.print("JSON Deserialisierungsfehler: ");
Serial.println(error.c_str());
request->send(500); // Fehler bei der JSON-Analyse
return;
}
// Überprüfen, ob die Antwort ein JSON-Objekt ist
if (doc.is<JsonObject>()) {
// Daten extrahieren
if (doc.containsKey("items") && doc["items"].size() > 0) {
String lat = String(doc["items"][0]["position"]["lat"].as<float>(), 6);
String lon = String(doc["items"][0]["position"]["lng"].as<float>(), 6);
String country = doc["items"][0]["address"]["countryName"].as<String>();
// HTML-Antwort zusammenstellen
String html = "<html><body>";
html += "<h1>Ort Informationen</h1>";
html += "<p><b>Latitude:</b> " + lat + "</p>";
html += "<p><b>Longitude:</b> " + lon + "</p>";
html += "<p><b>Country:</b> " + country + "</p>";
html += "</body></html>";
// Antwort an den Client senden
request->send(200, "text/html", html);
} else {
Serial.println("Keine gültigen Daten in der Antwort");
request->send(500); // Keine gültigen Daten in der API-Antwort
}
} else {
Serial.println("Antwort ist kein JSON-Objekt");
request->send(500); // Fehler bei der JSON-Analyse
}
} else {
Serial.println("Keine Antwort von der HERE API erhalten");
request->send(500); // Keine Antwort von der API erhalten
}
} else {
Serial.println("Verbindung zur HERE API fehlgeschlagen");
request->send(500); // Fehler, wenn keine Verbindung zur API hergestellt werden kann
}
} else {
request->send(400); // Fehler, wenn kein Ort angegeben wurde
}
});
// Server starten
server.begin();
}
void loop() {
// Der Loop kann hier für weitere Aufgaben genutzt werden
}
// Funktion zum URL-Encoding von Strings
String urlEncode(const String &str) {
String encodedString = "";
char c;
char code0;
char code1;
for (unsigned int i = 0; i < str.length(); i++) {
c = str.charAt(i);
if (c == ' ') {
encodedString += '+';
} else if (isalnum(c)) {
encodedString += c;
} else {
code1 = (c & 0xf) + '0';
if ((c & 0xf) > 9) {
code1 = (c & 0xf) - 10 + 'A';
}
c = (c >> 4) & 0xf;
code0 = c + '0';
if (c > 9) {
code0 = c - 10 + 'A';
}
encodedString += '%';
encodedString += code0;
encodedString += code1;
}
yield(); // dem Betriebssystem Zeit zum Ausführen anderer Aufgaben geben
}
return encodedString;
}