├── Blynk_Template_Definition.png ├── IMG_2617B43DD8C8-1.jpeg ├── IMG_2951.jpg ├── New_Blynk_App.jpeg ├── Node-Red-Dashboard.png ├── README.md ├── Settings24.h ├── Solar-WiFi-Weather-Station-V2.4.ino ├── Translation24.h └── history ├── Settings.h ├── Solar-WiFi-Weather-Station-V2.3 MQTT.ino ├── Solar-WiFi-Weather-Station-V2.3.ino └── Translation.h /Blynk_Template_Definition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3KUdelta/Solar_WiFi_Weather_Station/4f419d2c59b641209ee3fad52b0ca1aee21e02d4/Blynk_Template_Definition.png -------------------------------------------------------------------------------- /IMG_2617B43DD8C8-1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3KUdelta/Solar_WiFi_Weather_Station/4f419d2c59b641209ee3fad52b0ca1aee21e02d4/IMG_2617B43DD8C8-1.jpeg -------------------------------------------------------------------------------- /IMG_2951.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3KUdelta/Solar_WiFi_Weather_Station/4f419d2c59b641209ee3fad52b0ca1aee21e02d4/IMG_2951.jpg -------------------------------------------------------------------------------- /New_Blynk_App.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3KUdelta/Solar_WiFi_Weather_Station/4f419d2c59b641209ee3fad52b0ca1aee21e02d4/New_Blynk_App.jpeg -------------------------------------------------------------------------------- /Node-Red-Dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3KUdelta/Solar_WiFi_Weather_Station/4f419d2c59b641209ee3fad52b0ca1aee21e02d4/Node-Red-Dashboard.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zambretti Forecaster on Solar WiFi Weather Station 2 | Based on the work of Open Green Energy. 3 | https://www.instructables.com/id/Solar-Powered-WiFi-Weather-Station-V20/ 4 | Authors of the base code: Keith Hungerford and Debasish Dutta - Excellent work, gentlemen! 5 | 6 | ## FLASH Memory at the end of its lifespan! 7 | Dear Weather Station fans. For the ones who are using their Weather Station already from the start (we started 5 years ago), the flash memory is probably getting at its end. Let's do a quick calculation: 8 | 5 years = 1'825 days. As we are doing 144 read/write cycles per day (all 10 Minutes) this results in 262'800 read/write clycles by now. Flash memory has a finite lifetime of about 100,000 write cycles (source: https://learn.adafruit.com/memories-of-an-arduino/arduino-memories#). Here we go. This is exactly what happened to my station. A flash write error causes the ESP8266 to loop for ever and sucking the battery empty. I discovered this just recently. 9 | 10 | Easy fix: 11 | 12 | Code has been changed in order to reduce writing to flash memory by a factor of 3. Updating is highly recommended. 13 | Get a new ESP8266 D1 mini Pro CH9102 16M (e.g. https://www.aliexpress.com/item/1005006018009983.html) for roughly a dollar incl. shipment. 14 | 15 | ## BLYNK UPDATE! Move now to new Version! 16 | Running Blynk legacy will drain your battery and your device will stop working. Please update to new Blynk (free version works very well). 17 | 1. Create new Blynk account (https://blynk.io) Top right. 18 | 2. Add new template (see example below) 19 | 3. Add new device using your new template 20 | 4. Load Blynk App for your mobile device 21 | 5. Add widgets as before 22 | 6. done! 23 | 24 | ## Zambretti Weather Station 25 | Major changes: 26 | - simplified, restructured code (used Adafruit libraries for BME280 instead, sorry for this Keith) 27 | - added relative pressure, dewpoint, dewpoint spread and heatindex calculations 28 | - allow Blynk (** UPDATED, PLEASE SEE CHANGES **), ThingSpeak and MQTT data transmission 29 | - redesigned box (simplified printing, less plastic usage, full snap-in) 30 | - available languages in Version V2.3 (a big thank you to the contributors!) 31 | - English 32 | - German 33 | - Italian (Chak10) 34 | - Polish (TomaszDom) 35 | - Romanian (zangaby) 36 | - French (Ludestru) 37 | - Spanish (Fedecatt) 38 | - Turkish (Mert Sarac) 39 | - Dutch (Rickthefrog) 40 | - Norwegian (solbero) 41 | 42 | Changes in V2.3 43 | - included famous Zambretti forecaster (see Blynk example) 44 | - added translation table for Zambretti forecast 45 | - added multi language feature 46 | 47 | Changes in V2.31 48 | - added Dewpoint Spread 49 | - fixed some minor things 50 | - added Zambretti forecast in Thingspeak (thank you ThomaszDom) 51 | 52 | Changes in V2.31 (MQTT version) 53 | - allows to publish data to MQTT broker (alternative .ino file) 54 | 55 | Changes in V2.32 56 | - Battery monitoring and going to hibernate if battery low (battery protection) 57 | - Warning text will be shown instead of Zambretti prediction if batt low 58 | 59 | Changes in V2.33 60 | - Corrected bug in the winter/summer adjustment for the Zambretti forecast 61 | 62 | Changes in V2.34 63 | - added August-Roche-Magnus approximation to automatically adjust humidity with temperature corrections 64 | - Code cleanup 65 | 66 | Changes in V2.35 67 | - corrected TingSpeak communication changes (needs now Channel ID and KEY) 68 | - moved V2.35 into history folder - will not maintained by me anymore 69 | (pull requests for this version are still welcome) 70 | 71 | Changes in V2.4 72 | - updated Blynk (simple code changes - needs more to do on the Blynk server side) 73 | - reformatted data into json 74 | - decide in settings24.h if you want MQTT or not 75 | - non-blocking MQTT connector if broker is not available 76 | - added one-wire 18d20 temperature sensor (better temperature buffering in sunshine - more lazy) to P4 solder pull-up resistor 4.7 kOhm between signal and V+ 77 | - minor bug fixes 78 | - changed translation to summer and winter messages (unfortunately, only german and english translation for this version available) If you still need different languages please translate into your language in translation24.h 79 | 80 | Print the box yourself: https://www.thingiverse.com/thing:3551386 81 | 82 | [![Solar Wifi Weather Station](https://github.com/3KUdelta/Solar_WiFi_Weather_Station/blob/master/IMG_2951.jpg)](https://github.com/3KUdelta/Solar_WiFi_Weather_Station) 83 | 84 | New Blynk App Example (free widgets) 85 | [![Solar Wifi Weather Station](https://github.com/3KUdelta/Solar_WiFi_Weather_Station/blob/master/New_Blynk_App.jpeg)](https://github.com/3KUdelta/Solar_WiFi_Weather_Station) 86 | 87 | Blynk template definition 88 | [![Solar Wifi Weather Station](https://github.com/3KUdelta/Solar_WiFi_Weather_Station/blob/master/Blynk_Template_Definition.png)](https://github.com/3KUdelta/Solar_WiFi_Weather_Station) 89 | 90 | Showing the data on a LED display: https://github.com/3KUdelta/MDparola_MQTT_monitor 91 | [![LED matrix MQTT monitor](https://github.com/3KUdelta/MDparola_MQTT_monitor/blob/master/pictures/IMG_3180.JPG)](https://github.com/3KUdelta/MDparola_MQTT_monitor) 92 | 93 | Node-Red example showing MQTT messages on dashboard. 94 | [![Solar Wifi Weather Station](https://github.com/3KUdelta/Solar_WiFi_Weather_Station/blob/master/Node-Red-Dashboard.png)](https://github.com/3KUdelta/Solar_WiFi_Weather_Station) 95 | 96 | ThinkSpeak Example (ThingView iOS): 97 | [![Solar Wifi Weather Station](https://github.com/3KUdelta/Solar_WiFi_Weather_Station/blob/master/IMG_2617B43DD8C8-1.jpeg)](https://github.com/3KUdelta/Solar_WiFi_Weather_Station) 98 | -------------------------------------------------------------------------------- /Settings24.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------------------------- 2 | Project Name : Solar Powered WiFi Weather Station 3 | Features: temperature, dewpoint, dewpoint spread, heat index, humidity, absolute pressure, relative pressure, battery status and 4 | the famous Zambretti Forecaster (multi lingual) 5 | Authors: Keith Hungerford, Debasish Dutta and Marc Stähli 6 | Website : www.opengreenenergy.com */ 7 | 8 | const String StationName = "SWS_YourPlace"; // SolarWeatherStation (SWS) 9 | const String Version = "2.44"; 10 | 11 | /******* Hardware settings ****************************************************/ 12 | #define EIGHTEENDTWENTY // if you use a DS18B20 or DS18S20 temperature sensor, uncomment this line 13 | float bat_calib_factor = 5.1; // change this value to calibrate the battery voltage 14 | float bat_volt_minimum = 3.6; // minimum battery voltage, when the device goes to deep sleep 15 | 16 | /******* Blynk ****************************************************************/ 17 | #define BLYNK // uncomment to enable Blynk 18 | const char* BLYNK_AUTH_TOKEN = "YOUR_TOKEN"; 19 | 20 | /******* Thingspeak ***********************************************************/ 21 | // #define THINGSPEAK // uncomment to enable Thingspeak 22 | const unsigned long THINGSPEAK_CH_ID = 0000000; // Thingspeak Channel ID 23 | const char* THINGSPEAK_API_KEY = ""; // API write key 24 | 25 | /****** WiFi Settings ******************************************************/ 26 | 27 | char ssid[] = "YOUR_SSID"; // WiFi Router ssid 28 | char pass[] = "YOUR_PASSWORD"; // WiFi Router password 29 | 30 | /****** MQTT Settings ********************************************************/ 31 | 32 | #define MQTT // uncomment to enable MQTT 33 | const char* mqtt_server = "broker.hivemq.com"; // MQTT Server (broker) address 34 | const char* mqtt_user = ""; // MQTT Server (broker) userid 35 | const char* mqtt_pass = ""; // MQTT Server (broker) password 36 | const char* mqtt_topic = "YOUR_TOPIC"; // e.g. myname/weather/my_location 37 | const char* mqtt_status = "YOUR_STATUS"; // e.g. myname/status 38 | 39 | /****** Additional Settings **************************************************/ 40 | 41 | String LANGUAGE = "DE"; // Using 'DE' for German for summer and winter messages ('EN' for English as example still in the code) 42 | 43 | #define TEMP_CORR (0) // Manual correction of temp sensor, humidity will automatically corrected with August-Roche-Magnus approximation 44 | 45 | #define ELEVATION (420) // Enter your elevation in m ASL to calculate rel pressure (ASL/QNH) at your place 46 | 47 | #define sleepTimeMin (10) // setting of deepsleep time in minutes (default: 10) 48 | 49 | // NTP (reading UTC; local timezone does not matter, time is only needed for raising/falling pressure calcualtions) 50 | #define NTP_SERVER "ch.pool.ntp.org" 51 | -------------------------------------------------------------------------------- /Solar-WiFi-Weather-Station-V2.4.ino: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------------------------- 2 | Project Name : Solar Powered WiFi Weather Station V2.43 3 | Features: temperature, dewpoint, dewpoint spread, heat index, humidity, absolute pressure, relative pressure, battery status and 4 | the famous Zambretti Forecaster (multi lingual) 5 | Authors: Keith Hungerford, Debasish Dutta and Marc Stähli 6 | Website : www.opengreenenergy.com 7 | 8 | Main microcontroller (ESP8266) and BME280 both sleep between measurements 9 | BME280 is used in single shot mode ("forced mode") 10 | CODE: https://github.com/3KUdelta/Solar_WiFi_Weather_Station 11 | INSTRUCTIONS & HARDWARE: https://www.instructables.com/id/Solar-Powered-WiFi-Weather-Station-V20/ 12 | 3D FILES: https://www.thingiverse.com/thing:3551386 13 | 14 | CREDITS: 15 | Inspiration and code fragments of Dewpoint and Heatindex calculations are taken from: 16 | https://arduinotronics.blogspot.com/2013/12/temp-humidity-w-dew-point-calcualtions.html 17 | For Zambretti Ideas: 18 | http://drkfs.net/zambretti.htm or http://integritext.net/DrKFS/zambretti.htm 19 | https://raspberrypiandstuff.wordpress.com 20 | David Bird: https://github.com/G6EJD/ESP32_Weather_Forecaster_TN061 21 | 22 | CREDITS for Adafruit libraries: 23 | This is a library for the BME280 humidity, temperature & pressure sensor 24 | Designed specifically to work with the Adafruit BME280 Breakout 25 | ----> http://www.adafruit.com/products/2650 26 | These sensors use I2C or SPI to communicate, 2 or 4 pins are required 27 | to interface. The device's I2C address is either 0x76 or 0x77. 28 | Adafruit invests time and resources providing this open source code, 29 | please support Adafruit andopen-source hardware by purchasing products 30 | from Adafruit! 31 | Written by Limor Fried & Kevin Townsend for Adafruit Industries. 32 | BSD license, all text above must be included in any redistribution 33 | 34 | Hardware Settings Mac: 35 | LOLIN(WEMOS) D1 mini Pro, 80 MHz, Flash, 16M (14M SPIFFS), v2 Lower Memory, Disable, None, Only Sketch, 921600 on /dev/cu.SLAB_USBtoUART 36 | major update on 15/05/2019 37 | -added Zambretti Forecster 38 | -added translation feature 39 | -added English language 40 | -added German language 41 | updated on 03/06/2019 42 | -added Dewpoint Spread 43 | -minor code corrections 44 | updated 28/06/19 45 | -added MQTT (publishing all data to MQTT) 46 | -added Italian and Polish tranlation (Chak10) and (TomaszDom) 47 | updated 27/11/19 to V2.32 48 | -added battery protection at 3.3V, sending "batt empty" message and go to hybernate mode 49 | updated 11/05/20 to v2.33 50 | -corrected bug in adjustments for summer/winter 51 | updated 27/05/20 to v2.34 52 | - added August-Roche-Magnus approximation to automatically adjust humidity with temperature corrections 53 | updated 18/01/21 to v2.35 54 | - added HDC1080 temp&humi sensor because the Bosch BME280 is not accurate enough for me on temp and humidity 55 | - added winter prediction with different translation table 56 | updated 11/01/22 to v2.36 57 | - added one-wire 18d20 temperature sensor (better temperature buffering in sunshine - more lazy) to P4 solder pull-up resistor 4.7 kOhm between signal and V+ 58 | - removed HDC1080 again (unreliable humidity readings) 59 | - added PressureState to indicate current pressure situation (High presure, stormy low, etc) 60 | 61 | major update March 2022 to v2.4 62 | - Skipped Thingspeak 63 | - Changed translation.h structure for switching from summer to winter messages (rain vs. snow predictions) 64 | - Introduced login credentials for MQTT 65 | - Changed message type for MQTT to Json 66 | - Changed passowrd login to MQTT 67 | 68 | minor update June 2022 to v2.41 69 | - Adding time code in EPOCH format into MQTT message (time is UTC) 70 | - clean-ups 71 | 72 | updated 07/01/23 73 | - updated Blynk settings (if you were using Blynk legacy, please switch to new Blynk) 74 | - Important: New ArduinoJson version causes troubles with Blynk - works perfect with ArduinoJson Verson 6.20.1 75 | 76 | updated 03/10/23 77 | - MQTT, set to non-blocking in case of a broker error 78 | - switched to open broker HiveMQ 79 | 80 | updated 01/07/24 81 | - reduce writetoSpiffs by factor 3 --> 3 times more lifespan for Flash Memory 82 | - writings per day now: 48 / per year: 17'520 83 | - estimated lifespan of Flash Mem: 5.7 years with an expected 100'000 write cycles 84 | 85 | updated 2025-03-21 86 | - readd thingspeak, restructure settings 87 | 88 | /*---------------------------------------------------------------------------------------------------- 89 | 90 | Features : 91 | 1. Connect to Wi-Fi, and upload the data to Blynk, Thingspeak and or any MQTT broker 92 | 2. Monitoring Weather parameters like Temperature, Pressure abs, Pressure MSL and Humidity. 93 | 3. Extra Ports to add more Weather Sensors like UV Index, Light and Rain Guage etc. 94 | 4. Remote Battery Status Monitoring 95 | 5. Using Sleep mode to reduce the energy consumed 96 | 97 | /*---------------------------------------------------------------------------------------------------- 98 | 99 | VERY IMPORTANT: 100 | Enter your personal settings in Settings24.h ! 101 | 102 | /*----------------------------------------------------------------------------------------------------*/ 103 | 104 | #include "Settings24.h" 105 | #include "Translation24.h" 106 | 107 | #ifdef EIGHTEENDTWENTY 108 | #include // for temperature sensor 18d20 109 | #include // for temperature sensor 18d20 110 | #endif 111 | #include 112 | #include 113 | #include 114 | 115 | #ifdef MQTT 116 | #include // !!! ONLY WORKS currently with library version 6.20.1 !!! 117 | #include 118 | #endif 119 | #ifdef BLYNK 120 | #include //https://github.com/blynkkk/blynk-library 121 | #endif 122 | #ifdef THINGSPEAK 123 | #include "ThingSpeak.h" 124 | #endif 125 | 126 | #include 127 | #include "FS.h" 128 | #include //https://github.com/aharshac/EasyNTPClient 129 | #include //https://github.com/PaulStoffregen/Time.git 130 | 131 | Adafruit_BME280 bme; // I2C 132 | #ifdef EIGHTEENDTWENTY 133 | OneWire oneWire(13); // Data wire 18d20 Sensor is plugged into port 13 = D7 @ ESP8266 134 | DallasTemperature s18d20(&oneWire); // Pass oneWire reference to Dallas Temperature Sensor 18d20 135 | #endif 136 | WiFiUDP udp; 137 | EasyNTPClient ntpClient(udp, NTP_SERVER, 0); // reading UTC 138 | 139 | //varialbes of measured or calculated sensor data 140 | float measured_temp_dal; 141 | float measured_temp_bme; 142 | float measured_temp; 143 | float adjusted_temp; 144 | float measured_humi; 145 | float measured_humi_bme; 146 | float adjusted_humi; 147 | float measured_pres; 148 | float SLpressure_hPa; // needed for rel pressure calculation 149 | float HeatIndex; // Heat Index in °C 150 | float volt; 151 | int batterypercentage; 152 | int rel_pressure_rounded; 153 | double DewpointTemperature; 154 | float DewPointSpread; // Difference between actual temperature and dewpoint 155 | 156 | //variables for trend calculation 157 | unsigned long current_timestamp; // Actual timestamp read from NTPtime_t now; 158 | unsigned long saved_timestamp; // Timestamp stored in SPIFFS 159 | float pressure_value[12]; // Array for the historical pressure values (6 hours, all 30 mins), where as pressure_value[0] is always the most recent value 160 | float pressure_difference[12]; // Array to calculate trend with pressure differences 161 | 162 | // variables for forcasting result 163 | int accuracy; // Counter, if enough values for accurate forecasting 164 | String ZambrettisWords; // Final statement about weather forecast 165 | char z_letter; 166 | String trend_in_words; // Trend in words 167 | String forecast_in_words; // Weather forecast in words 168 | String pressure_in_words; // Air pressure in words 169 | String accuracy_in_words; // Zambretti's prediction accuracy in words 170 | String PressureState; // current state of pressure (high, low, stormy low, etc) 171 | 172 | void(* resetFunc) (void) = 0; // declare reset function @ address 0 173 | 174 | WiFiClient espClient; 175 | #ifdef MQTT 176 | PubSubClient client(espClient); 177 | #endif 178 | 179 | void setup() { 180 | Serial.begin(115200); while (!Serial); delay(200); 181 | Serial.println(); 182 | Serial.print("Start of "); 183 | Serial.print(StationName); 184 | Serial.print(", Version "); 185 | Serial.println(Version); 186 | 187 | translate(); // assign translation variables 188 | 189 | #ifdef MQTT 190 | // Increasing PubSubClient buffer 191 | client.setBufferSize(512); 192 | #endif 193 | 194 | //******Battery Voltage Monitoring (first thing to do: is battery still ok?)*********** 195 | 196 | // Voltage divider R1 = 220k+100k+220k =540k and R2=100k 197 | unsigned long raw = analogRead(A0); 198 | volt = raw * bat_calib_factor / 1024; 199 | 200 | Serial.print("Voltage = "); 201 | Serial.print(volt, 2); // print with 2 decimal places 202 | Serial.println (" V"); 203 | 204 | batterypercentage = (volt - bat_volt_minimum) * 100 / 0.6; // 3.6 V is the lower limint set to 0%, bandwith 0.6 V 205 | if (batterypercentage > 100) batterypercentage = 100; 206 | Serial.print("Battery charge: "); 207 | Serial.print(batterypercentage); 208 | Serial.println("%"); 209 | 210 | // **************Application going online********************************************** 211 | 212 | WiFi.mode(WIFI_STA); 213 | WiFi.hostname(StationName); //This changes the hostname of the ESP8266 to display neatly on the network esp on router. 214 | WiFi.begin(ssid, pass); 215 | Serial.print("---> Connecting to WiFi "); 216 | int i = 0; 217 | while (WiFi.status() != WL_CONNECTED) { 218 | delay(500); 219 | i++; 220 | if (i > 20) { 221 | Serial.println("Could not connect to WiFi!"); 222 | Serial.println("Going to sleep for 10 minutes and try again."); 223 | if (volt > bat_volt_minimum) { 224 | goToSleep(10); // go to sleep and retry after 10 min 225 | } 226 | else { 227 | goToSleep(0); // hybernate because batt empty - this is just to avoid that an endless 228 | } // try to get a WiFi signal will drain the battery empty 229 | } 230 | Serial.print("."); 231 | } 232 | Serial.println(" Wifi connected ok"); 233 | 234 | // init transports 235 | #ifdef BLYNK 236 | Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass); 237 | #endif 238 | #ifdef THINGSPEAK 239 | ThingSpeak.begin(espClient); 240 | #endif 241 | #ifdef MQTT 242 | connect_to_MQTT(); 243 | #endif 244 | 245 | //*****************Checking if SPIFFS available******************************** 246 | 247 | Serial.println("SPIFFS Initialization: (First time run can last up to 30 sec - be patient)"); 248 | 249 | boolean mounted = SPIFFS.begin(); // load config if it exists. Otherwise use defaults. 250 | if (!mounted) { 251 | Serial.println("FS not formatted. Doing that now... (can last up to 30 sec)."); 252 | SPIFFS.format(); 253 | Serial.println("FS formatted..."); 254 | SPIFFS.begin(); 255 | } 256 | 257 | //******** GETTING THE TIME FROM NTP SERVER *********************************** 258 | 259 | Serial.println("---> Now reading time from NTP Server"); 260 | int ii = 0; 261 | while (!ntpClient.getUnixTime()) { 262 | delay(100); 263 | ii++; 264 | if (ii > 20) { 265 | Serial.println("Could not connect to NTP Server!"); 266 | Serial.println("Doing a reset now and retry a connection from scratch."); 267 | resetFunc(); 268 | } 269 | Serial.print("."); 270 | } 271 | current_timestamp = ntpClient.getUnixTime(); // get UNIX timestamp (seconds from 1.1.1970 on) 272 | 273 | Serial.print("Current UNIX Timestamp: "); 274 | Serial.println(current_timestamp); 275 | 276 | Serial.print("Time & Date: "); 277 | Serial.print(hour(current_timestamp)); 278 | Serial.print(":"); 279 | Serial.print(minute(current_timestamp)); 280 | Serial.print(":"); 281 | Serial.print(second(current_timestamp)); 282 | Serial.print("; "); 283 | Serial.print(day(current_timestamp)); 284 | Serial.print("."); 285 | Serial.print(month(current_timestamp)); // needed later: month as integer for Zambretti calcualtion 286 | Serial.print("."); 287 | Serial.print(year(current_timestamp)); 288 | Serial.println("Local timezone does not matter - we just need always the same timezone --> using UTC"); 289 | 290 | //******** GETTING RELATIVE PRESSURE DATA FROM SENSOR (BME680) ******************** 291 | 292 | bool bme_status = bme.begin(0x76); //address either 0x76 or 0x77 293 | if (!bme_status) { 294 | Serial.println("Could not find a valid BME280 sensor, check wiring!"); 295 | } 296 | 297 | Serial.println("forced mode, 1x temperature / 1x humidity / 1x pressure oversampling,"); 298 | Serial.println("filter off"); 299 | 300 | bme.setSampling(Adafruit_BME280::MODE_FORCED, 301 | Adafruit_BME280::SAMPLING_X1, // temperature 302 | Adafruit_BME280::SAMPLING_X1, // pressure 303 | Adafruit_BME280::SAMPLING_X1, // humidity 304 | Adafruit_BME280::FILTER_OFF ); 305 | 306 | #ifdef EIGHTEENDTWENTY 307 | s18d20.begin(); // starting 18d20 308 | s18d20.requestTemperatures(); // Send the command to get temperatures 309 | #endif 310 | 311 | measurementEvent(); //calling function to get all data from the different sensors 312 | 313 | //*******************SPIFFS operations*************************************************************** 314 | 315 | ReadFromSPIFFS(); //read stored values and update data if more recent data is available 316 | 317 | Serial.print("Timestamp difference: "); 318 | Serial.println(current_timestamp - saved_timestamp); 319 | 320 | if (current_timestamp - saved_timestamp > 21600) { // last save older than 6 hours -> re-initialize values 321 | FirstTimeRun(); 322 | } 323 | else if (current_timestamp - saved_timestamp > 1700) { // it is time for pressure update (1800 sec = 30 min) 324 | 325 | for (int i = 11; i >= 1; i = i - 1) { 326 | pressure_value[i] = pressure_value[i - 1]; // shifting values one to the right 327 | } 328 | 329 | pressure_value[0] = rel_pressure_rounded; // updating with acutal rel pressure (newest value) 330 | 331 | if (accuracy < 12) { 332 | accuracy = accuracy + 1; // one value more -> accuracy rises (up to 12 = 100%) 333 | } 334 | WriteToSPIFFS(current_timestamp); // update timestamp on storage 335 | } 336 | 337 | //**************************Calculate Zambretti Forecast******************************************* 338 | 339 | int accuracy_in_percent = accuracy * 94 / 12; // 94% is the max predicion accuracy of Zambretti 340 | if ( volt > bat_volt_minimum ) { // check if batt is still ok 341 | ZambrettisWords = ZambrettiSays(char(ZambrettiLetter())); 342 | forecast_in_words = TEXT_ZAMBRETTI_FORECAST; 343 | pressure_in_words = TEXT_AIR_PRESSURE; 344 | accuracy_in_words = TEXT_ZAMBRETTI_ACCURACY; 345 | } 346 | else { 347 | ZambrettisWords = ZambrettiSays('0'); // send Message that battery is empty 348 | } 349 | 350 | Serial.println("********************************************************"); 351 | Serial.print("Zambretti says: "); 352 | Serial.print(ZambrettisWords); 353 | Serial.print(", "); 354 | Serial.println(trend_in_words); 355 | Serial.print("Prediction accuracy: "); 356 | Serial.print(accuracy_in_percent); 357 | Serial.println("%"); 358 | if (accuracy < 12) { 359 | Serial.println("Reason: Not enough weather data yet."); 360 | Serial.print("We need "); 361 | Serial.print((12 - accuracy) / 2); 362 | Serial.println(" hours more to get sufficient data."); 363 | } 364 | Serial.println("********************************************************"); 365 | 366 | #ifdef BLYNK // Write to Blynk 367 | Blynk.virtualWrite(V0, adjusted_temp); // virtual pin 0 368 | Blynk.virtualWrite(V1, adjusted_humi); // virtual pin 1 369 | Blynk.virtualWrite(V2, measured_pres); // virtual pin 2 370 | Blynk.virtualWrite(V3, rel_pressure_rounded); // virtual pin 3 371 | Blynk.virtualWrite(V4, volt); // virtual pin 4 372 | Blynk.virtualWrite(V5, DewpointTemperature); // virtual pin 5 373 | Blynk.virtualWrite(V6, HeatIndex); // virtual pin 6 374 | Blynk.virtualWrite(V7, ZambrettisWords); // virtual pin 7 375 | Blynk.virtualWrite(V8, accuracy_in_percent); // virtual pin 8 376 | Blynk.virtualWrite(V9, trend_in_words); // virtual pin 9 377 | Blynk.virtualWrite(V10, DewPointSpread); // virtual pin 10 378 | Blynk.virtualWrite(V11, PressureState); // virtual pin 11 379 | Serial.println("Data written to Blynk ..."); 380 | #endif 381 | 382 | #ifdef THINGSPEAK // Write to Thingspeak 383 | ThingSpeak.setField(1, rel_pressure_rounded); 384 | ThingSpeak.setField(2, adjusted_temp); 385 | ThingSpeak.setField(3, adjusted_humi); 386 | ThingSpeak.setField(4, volt); 387 | ThingSpeak.setField(5, measured_pres); 388 | ThingSpeak.setField(6, float(DewpointTemperature)); 389 | ThingSpeak.setField(7, HeatIndex); 390 | ThingSpeak.setStatus(String(ZambrettisWords + ", " + trend_in_words)); 391 | 392 | int feedback = ThingSpeak.writeFields(THINGSPEAK_CH_ID, THINGSPEAK_API_KEY); 393 | if (feedback == 200) { 394 | Serial.println("Writing to Thingspeak was successful!"); 395 | } 396 | else { 397 | Serial.println("Problem updating Thingspeak. HTTP error code " + String(feedback)); 398 | } 399 | #endif 400 | 401 | #ifdef MQTT 402 | StaticJsonDocument<512> jsonDoc; 403 | JsonObject rootObj = jsonDoc.createNestedObject("root"); 404 | 405 | // Write the the values. Here you can use any C++ type (and you can refer to variables) 406 | rootObj["temperature"] = adjusted_temp; 407 | rootObj["humidity"] = adjusted_humi; 408 | rootObj["dewpoint"] = DewpointTemperature; 409 | rootObj["relativepressure"] = rel_pressure_rounded; 410 | rootObj["pressurestate"] = PressureState; 411 | rootObj["battery"] = volt; 412 | rootObj["batterypercentage"] = batterypercentage; 413 | rootObj["absolutepressure"] = measured_pres; 414 | rootObj["heatindex"] = HeatIndex; 415 | rootObj["accuracy"] = accuracy_in_percent; 416 | rootObj["dewpointspread"] = DewPointSpread; 417 | rootObj["zambrettisays"] = ZambrettisWords; 418 | rootObj["trendinwords"] = trend_in_words; 419 | rootObj["trend"] = pressure_difference[11]; 420 | rootObj["zletter"] = String(z_letter); 421 | rootObj["wifi_strength"] = int(WiFi.RSSI()); 422 | rootObj["timestamp"] = current_timestamp; 423 | 424 | char jsonBuffer[512]; 425 | serializeJson(jsonDoc, jsonBuffer); 426 | 427 | Serial.print("mqtt message: "); 428 | Serial.println(jsonBuffer); 429 | 430 | client.publish(mqtt_topic, jsonBuffer, 1); // , 1 = retained 431 | delay(500); 432 | #endif 433 | 434 | if (volt > bat_volt_minimum) { //check if batt still ok, if yes 435 | goToSleep(sleepTimeMin); //go for a nap 436 | } 437 | else { //if not, 438 | goToSleep(0); //hybernate because batt is empty 439 | } 440 | } // end of void setup() 441 | 442 | void loop() { //loop is not used 443 | } // end of void loop() 444 | 445 | void measurementEvent() { 446 | 447 | //Measures absolute Pressure, Temperature, Humidity, Voltage, calculate relative pressure, 448 | //Dewpoint, Dewpoint Spread, Heat Index, current pressure state 449 | 450 | bme.takeForcedMeasurement(); 451 | 452 | // Get temperature 453 | measured_temp_bme = bme.readTemperature(); 454 | #ifdef EIGHTEENDTWENTY 455 | measured_temp_dal = getTemperature(); 456 | measured_temp = measured_temp_dal; 457 | #else 458 | measured_temp = measured_temp_bme; 459 | #endif 460 | // print on serial monitor 461 | Serial.print("Temp BME: "); 462 | Serial.print(measured_temp_bme); 463 | Serial.println("°C; "); 464 | 465 | #ifdef EIGHTEENDTWENTY 466 | Serial.print("Temp Dallas: "); 467 | Serial.print(measured_temp_dal); 468 | Serial.println("°C; "); 469 | #endif 470 | 471 | // Get humidity 472 | measured_humi_bme = bme.readHumidity(); 473 | measured_humi = measured_humi_bme; 474 | 475 | Serial.print("Humidity BME: "); 476 | Serial.print(measured_humi_bme); 477 | Serial.println("%; "); 478 | 479 | // Get pressure 480 | measured_pres = bme.readPressure() / 100.0F; 481 | // print on serial monitor 482 | Serial.print("Pressure: "); 483 | Serial.print(measured_pres); 484 | Serial.print("hPa; "); 485 | 486 | // Calculate and print relative pressure 487 | SLpressure_hPa = (((measured_pres * 100.0) / pow((1 - ((float)(ELEVATION)) / 44330), 5.255)) / 100.0); 488 | rel_pressure_rounded = (int)(SLpressure_hPa + .5); 489 | // print on serial monitor 490 | Serial.print("Pressure rel: "); 491 | Serial.print(rel_pressure_rounded); 492 | Serial.print("hPa; "); 493 | 494 | // Calculate dewpoint 495 | double a = 17.271; 496 | double b = 237.7; 497 | double tempcalc = (a * measured_temp) / (b + measured_temp) + log(measured_humi * 0.01); 498 | DewpointTemperature = (b * tempcalc) / (a - tempcalc); 499 | Serial.print("Dewpoint: "); 500 | Serial.print(DewpointTemperature); 501 | Serial.println("°C; "); 502 | 503 | if (TEMP_CORR != 0) { 504 | // With the dewpoint calculated we can correct temp and automatically calculate humidity 505 | adjusted_temp = measured_temp + TEMP_CORR; 506 | if (adjusted_temp < DewpointTemperature) adjusted_temp = DewpointTemperature; //compensation, if offset too high 507 | //August-Roche-Magnus approximation (http://bmcnoldy.rsmas.miami.edu/Humidity.html) 508 | adjusted_humi = 100 * (exp((a * DewpointTemperature) / (b + DewpointTemperature)) / exp((a * adjusted_temp) / (b + adjusted_temp))); 509 | if (adjusted_humi > 100) adjusted_humi = 100; // just in case 510 | // print on serial monitor 511 | Serial.print("Temp adjusted: "); 512 | Serial.print(adjusted_temp); 513 | Serial.print("°C; "); 514 | Serial.print("Humidity adjusted: "); 515 | Serial.print(adjusted_humi); 516 | Serial.print("%; "); 517 | } 518 | else 519 | { 520 | adjusted_temp = measured_temp; 521 | adjusted_humi = measured_humi; 522 | } 523 | 524 | // Calculate dewpoint spread (difference between actual temp and dewpoint -> the smaller the number: rain or fog 525 | 526 | DewPointSpread = adjusted_temp - DewpointTemperature; 527 | Serial.print("Dewpoint Spread: "); 528 | Serial.print(DewPointSpread); 529 | Serial.println("°C; "); 530 | 531 | // Calculate HI (heatindex in °C) --> HI starts working above 26,7 °C 532 | if (adjusted_temp > 26.7) { 533 | double c1 = -8.784, c2 = 1.611, c3 = 2.338, c4 = -0.146, c5 = -1.230e-2, c6 = -1.642e-2, c7 = 2.211e-3, c8 = 7.254e-4, c9 = -2.582e-6 ; 534 | double T = adjusted_temp; 535 | double R = adjusted_humi; 536 | 537 | double A = (( c5 * T) + c2) * T + c1; 538 | double B = ((c7 * T) + c4) * T + c3; 539 | double C = ((c9 * T) + c8) * T + c6; 540 | HeatIndex = (C * R + B) * R + A; 541 | } 542 | else { 543 | HeatIndex = adjusted_temp; 544 | Serial.println("Not warm enough (less than 26.7 °C) for Heatindex"); 545 | } 546 | Serial.print("HeatIndex: "); 547 | Serial.print(HeatIndex); 548 | Serial.println("°C; "); 549 | 550 | // calculation of current pressure state (global variable String PressureState) 551 | if (rel_pressure_rounded < 990) PressureState = TEXT_STORM_LOW; 552 | else if (rel_pressure_rounded >= 990 && rel_pressure_rounded < 1000) PressureState = TEXT_STRONG_LOW; 553 | else if (rel_pressure_rounded >= 1000 && rel_pressure_rounded < 1013) PressureState = TEXT_LOWPRESSURE; 554 | else if (rel_pressure_rounded >= 1013 && rel_pressure_rounded < 1025) PressureState = TEXT_HIGHPRESSURE; 555 | else if (rel_pressure_rounded >= 1025) PressureState = TEXT_STRONG_HIGH; 556 | 557 | Serial.print("Pressure State: "); 558 | Serial.println(PressureState); 559 | 560 | } // end of void measurementEvent() 561 | 562 | int CalculateTrend() { 563 | int trend; // -1 falling; 0 steady; 1 raising 564 | Serial.println("---> Calculating trend"); 565 | 566 | //--> giving the most recent pressure reads more weight 567 | pressure_difference[0] = (pressure_value[0] - pressure_value[1]) * 1.5; 568 | pressure_difference[1] = (pressure_value[0] - pressure_value[2]); 569 | pressure_difference[2] = (pressure_value[0] - pressure_value[3]) / 1.5; 570 | pressure_difference[3] = (pressure_value[0] - pressure_value[4]) / 2; 571 | pressure_difference[4] = (pressure_value[0] - pressure_value[5]) / 2.5; 572 | pressure_difference[5] = (pressure_value[0] - pressure_value[6]) / 3; 573 | pressure_difference[6] = (pressure_value[0] - pressure_value[7]) / 3.5; 574 | pressure_difference[7] = (pressure_value[0] - pressure_value[8]) / 4; 575 | pressure_difference[8] = (pressure_value[0] - pressure_value[9]) / 4.5; 576 | pressure_difference[9] = (pressure_value[0] - pressure_value[10]) / 5; 577 | pressure_difference[10] = (pressure_value[0] - pressure_value[11]) / 5.5; 578 | 579 | //--> calculating the average and storing it into [11] 580 | pressure_difference[11] = ( pressure_difference[0] 581 | + pressure_difference[1] 582 | + pressure_difference[2] 583 | + pressure_difference[3] 584 | + pressure_difference[4] 585 | + pressure_difference[5] 586 | + pressure_difference[6] 587 | + pressure_difference[7] 588 | + pressure_difference[8] 589 | + pressure_difference[9] 590 | + pressure_difference[10]) / 11; 591 | 592 | Serial.print("Current trend: "); 593 | Serial.println(pressure_difference[11]); 594 | 595 | if (pressure_difference[11] > 3.5) { 596 | trend_in_words = TEXT_RISING_FAST; 597 | trend = 1; 598 | } 599 | else if (pressure_difference[11] > 1.5 && pressure_difference[11] <= 3.5) { 600 | trend_in_words = TEXT_RISING; 601 | trend = 1; 602 | } 603 | else if (pressure_difference[11] > 0.25 && pressure_difference[11] <= 1.5) { 604 | trend_in_words = TEXT_RISING_SLOW; 605 | trend = 1; 606 | } 607 | else if (pressure_difference[11] > -0.25 && pressure_difference[11] < 0.25) { 608 | trend_in_words = TEXT_STEADY; 609 | trend = 0; 610 | } 611 | else if (pressure_difference[11] >= -1.5 && pressure_difference[11] < -0.25) { 612 | trend_in_words = TEXT_FALLING_SLOW; 613 | trend = -1; 614 | } 615 | else if (pressure_difference[11] >= -3.5 && pressure_difference[11] < -1.5) { 616 | trend_in_words = TEXT_FALLING; 617 | trend = -1; 618 | } 619 | else if (pressure_difference[11] <= -3.5) { 620 | trend_in_words = TEXT_FALLING_FAST; 621 | trend = -1; 622 | } 623 | 624 | Serial.println(trend_in_words); 625 | return trend; 626 | } 627 | 628 | char ZambrettiLetter() { 629 | Serial.println("---> Calculating Zambretti letter"); 630 | int(z_trend) = CalculateTrend(); 631 | // Case trend is falling 632 | if (z_trend == -1) { 633 | float zambretti = 0.0009746 * rel_pressure_rounded * rel_pressure_rounded - 2.1068 * rel_pressure_rounded + 1138.7019; 634 | //A Winter falling generally results in a Z value lower by 1 unit 635 | if (month(current_timestamp) < 4 || month(current_timestamp) > 9) zambretti = zambretti + 1; 636 | if (zambretti > 9) zambretti = 9; 637 | Serial.print("Calculated and rounded Zambretti in numbers: "); 638 | Serial.println(round(zambretti)); 639 | switch (int(round(zambretti))) { 640 | case 0: z_letter = 'A'; break; //Settled Fine 641 | case 1: z_letter = 'A'; break; //Settled Fine 642 | case 2: z_letter = 'B'; break; //Fine Weather 643 | case 3: z_letter = 'D'; break; //Fine Becoming Less Settled 644 | case 4: z_letter = 'H'; break; //Fairly Fine Showers Later 645 | case 5: z_letter = 'O'; break; //Showery Becoming unsettled 646 | case 6: z_letter = 'R'; break; //Unsettled, Rain later 647 | case 7: z_letter = 'U'; break; //Rain at times, worse later 648 | case 8: z_letter = 'V'; break; //Rain at times, becoming very unsettled 649 | case 9: z_letter = 'X'; break; //Very Unsettled, Rain 650 | } 651 | } 652 | // Case trend is steady 653 | if (z_trend == 0) { 654 | float zambretti = 138.24 - 0.133 * rel_pressure_rounded; 655 | Serial.print("Calculated and rounded Zambretti in numbers: "); 656 | Serial.println(round(zambretti)); 657 | switch (int(round(zambretti))) { 658 | case 0: z_letter = 'A'; break; //Settled Fine 659 | case 1: z_letter = 'A'; break; //Settled Fine 660 | case 2: z_letter = 'B'; break; //Fine Weather 661 | case 3: z_letter = 'E'; break; //Fine, Possibly showers 662 | case 4: z_letter = 'K'; break; //Fairly Fine, Showers likely 663 | case 5: z_letter = 'N'; break; //Showery Bright Intervals 664 | case 6: z_letter = 'P'; break; //Changeable some rain 665 | case 7: z_letter = 'S'; break; //Unsettled, rain at times 666 | case 8: z_letter = 'W'; break; //Rain at Frequent Intervals 667 | case 9: z_letter = 'X'; break; //Very Unsettled, Rain 668 | case 10: z_letter = 'Z'; break; //Stormy, much rain 669 | } 670 | } 671 | // Case trend is rising 672 | if (z_trend == 1) { 673 | float zambretti = 142.57 - 0.1376 * rel_pressure_rounded; 674 | //A Summer rising, improves the prospects by 1 unit over a Winter rising 675 | if (month(current_timestamp) >= 4 && month(current_timestamp) <= 9) zambretti = zambretti - 1; 676 | if (zambretti < 0) zambretti = 0; 677 | Serial.print("Calculated and rounded Zambretti in numbers: "); 678 | Serial.println(round(zambretti)); 679 | switch (int(round(zambretti))) { 680 | case 0: z_letter = 'A'; break; //Settled Fine 681 | case 1: z_letter = 'A'; break; //Settled Fine 682 | case 2: z_letter = 'B'; break; //Fine Weather 683 | case 3: z_letter = 'C'; break; //Becoming Fine 684 | case 4: z_letter = 'F'; break; //Fairly Fine, Improving 685 | case 5: z_letter = 'G'; break; //Fairly Fine, Possibly showers, early 686 | case 6: z_letter = 'I'; break; //Showery Early, Improving 687 | case 7: z_letter = 'J'; break; //Changeable, Improving 688 | case 8: z_letter = 'L'; break; //Rather Unsettled Clearing Later 689 | case 9: z_letter = 'M'; break; //Unsettled, Probably Improving 690 | case 10: z_letter = 'Q'; break; //Unsettled, short fine Intervals 691 | case 11: z_letter = 'T'; break; //Very Unsettled, Finer at times 692 | case 12: z_letter = 'Y'; break; //Stormy, possibly improving 693 | case 13: z_letter = 'Z'; break;; //Stormy, much rain 694 | } 695 | } 696 | Serial.print("This is Zambretti's famous letter: "); 697 | Serial.println(z_letter); 698 | return z_letter; 699 | } 700 | 701 | String ZambrettiSays(char code) { 702 | String zambrettis_words = ""; 703 | 704 | // switch from summer (rain) to winter (snow) 705 | if (measured_temp <= 2 && LANGUAGE == "DE") LANGUAGE = "DW"; 706 | if (measured_temp > 2 && LANGUAGE == "DW") LANGUAGE = "DE"; 707 | translate(); 708 | 709 | switch (code) { 710 | case 'A': zambrettis_words = TEXT_ZAMBRETTI_A; break; //see Tranlation.h 711 | case 'B': zambrettis_words = TEXT_ZAMBRETTI_B; break; 712 | case 'C': zambrettis_words = TEXT_ZAMBRETTI_C; break; 713 | case 'D': zambrettis_words = TEXT_ZAMBRETTI_D; break; 714 | case 'E': zambrettis_words = TEXT_ZAMBRETTI_E; break; 715 | case 'F': zambrettis_words = TEXT_ZAMBRETTI_F; break; 716 | case 'G': zambrettis_words = TEXT_ZAMBRETTI_G; break; 717 | case 'H': zambrettis_words = TEXT_ZAMBRETTI_H; break; 718 | case 'I': zambrettis_words = TEXT_ZAMBRETTI_I; break; 719 | case 'J': zambrettis_words = TEXT_ZAMBRETTI_J; break; 720 | case 'K': zambrettis_words = TEXT_ZAMBRETTI_K; break; 721 | case 'L': zambrettis_words = TEXT_ZAMBRETTI_L; break; 722 | case 'M': zambrettis_words = TEXT_ZAMBRETTI_M; break; 723 | case 'N': zambrettis_words = TEXT_ZAMBRETTI_N; break; 724 | case 'O': zambrettis_words = TEXT_ZAMBRETTI_O; break; 725 | case 'P': zambrettis_words = TEXT_ZAMBRETTI_P; break; 726 | case 'Q': zambrettis_words = TEXT_ZAMBRETTI_Q; break; 727 | case 'R': zambrettis_words = TEXT_ZAMBRETTI_R; break; 728 | case 'S': zambrettis_words = TEXT_ZAMBRETTI_S; break; 729 | case 'T': zambrettis_words = TEXT_ZAMBRETTI_T; break; 730 | case 'U': zambrettis_words = TEXT_ZAMBRETTI_U; break; 731 | case 'V': zambrettis_words = TEXT_ZAMBRETTI_V; break; 732 | case 'W': zambrettis_words = TEXT_ZAMBRETTI_W; break; 733 | case 'X': zambrettis_words = TEXT_ZAMBRETTI_X; break; 734 | case 'Y': zambrettis_words = TEXT_ZAMBRETTI_Y; break; 735 | case 'Z': zambrettis_words = TEXT_ZAMBRETTI_Z; break; 736 | case '0': zambrettis_words = TEXT_ZAMBRETTI_0; break; 737 | default: zambrettis_words = TEXT_ZAMBRETTI_DEFAULT; break; 738 | } 739 | return zambrettis_words; 740 | } 741 | 742 | void ReadFromSPIFFS() { 743 | char filename [] = "/data.txt"; 744 | File myDataFile = SPIFFS.open(filename, "r"); // Open file for reading 745 | if (!myDataFile) { 746 | Serial.println("Failed to open file"); 747 | FirstTimeRun(); // no file there -> initializing 748 | } 749 | 750 | Serial.println("---> Now reading from SPIFFS"); 751 | 752 | String temp_data; 753 | 754 | temp_data = myDataFile.readStringUntil('\n'); 755 | saved_timestamp = temp_data.toInt(); 756 | Serial.print("Timestamp from SPIFFS: "); Serial.println(saved_timestamp); 757 | 758 | temp_data = myDataFile.readStringUntil('\n'); 759 | accuracy = temp_data.toInt(); 760 | Serial.print("Accuracy value read from SPIFFS: "); Serial.println(accuracy); 761 | 762 | Serial.print("Last 12 saved pressure values: "); 763 | for (int i = 0; i <= 11; i++) { 764 | temp_data = myDataFile.readStringUntil('\n'); 765 | pressure_value[i] = temp_data.toInt(); 766 | Serial.print(pressure_value[i]); 767 | Serial.print("; "); 768 | } 769 | myDataFile.close(); 770 | Serial.println(); 771 | } 772 | 773 | void WriteToSPIFFS(int write_timestamp) { 774 | char filename [] = "/data.txt"; 775 | File myDataFile = SPIFFS.open(filename, "w"); // Open file for writing (appending) 776 | if (!myDataFile) { 777 | Serial.println("Failed to open file"); 778 | } 779 | 780 | Serial.println("---> Now writing to SPIFFS"); 781 | 782 | myDataFile.println(write_timestamp); // Saving timestamp to /data.txt 783 | myDataFile.println(accuracy); // Saving accuracy value to /data.txt 784 | 785 | for ( int i = 0; i <= 11; i++) { 786 | myDataFile.println(pressure_value[i]); // Filling pressure array with updated values 787 | } 788 | myDataFile.close(); 789 | 790 | Serial.println("File written. Now reading file again."); 791 | myDataFile = SPIFFS.open(filename, "r"); // Open file for reading 792 | Serial.print("Found in /data.txt = "); 793 | while (myDataFile.available()) { 794 | Serial.print(myDataFile.readStringUntil('\n')); 795 | Serial.print("; "); 796 | } 797 | Serial.println(); 798 | myDataFile.close(); 799 | } 800 | 801 | void FirstTimeRun() { 802 | Serial.println("---> Starting initializing process."); 803 | accuracy = 1; 804 | char filename [] = "/data.txt"; 805 | File myDataFile = SPIFFS.open(filename, "w"); // Open a file for writing 806 | if (!myDataFile) { 807 | Serial.println("Failed to open file"); 808 | Serial.println("Stopping process - maybe flash size not set (SPIFFS)."); 809 | exit(0); 810 | } 811 | myDataFile.println(current_timestamp); // Saving timestamp to /data.txt 812 | myDataFile.println(accuracy); // Saving accuracy value to /data.txt 813 | for ( int i = 0; i < 12; i++) { 814 | myDataFile.println(rel_pressure_rounded); // Filling pressure array with current pressure 815 | } 816 | Serial.println("** Saved initial pressure data. **"); 817 | myDataFile.close(); 818 | Serial.println("---> Doing a reset now."); 819 | resetFunc(); //call reset 820 | } 821 | 822 | #ifdef MQTT 823 | void connect_to_MQTT() { 824 | Serial.print("---> Connecting to MQTT, "); 825 | client.setServer(mqtt_server, 1883); 826 | 827 | if (!client.connected()) { 828 | Serial.println("reconnecting MQTT..."); 829 | reconnect(); 830 | } 831 | Serial.println("MQTT connected ok."); 832 | } //end connect_to_MQTT 833 | 834 | void reconnect() { 835 | Serial.print("Attempting MQTT connection with "); 836 | // Create a random client ID 837 | String clientId = "ESP8266Client-"; 838 | clientId += String(random(0xffff), HEX); 839 | Serial.print(clientId.c_str()); 840 | // Attempt to connect 841 | if (client.connect(clientId.c_str(), mqtt_user, mqtt_pass)) { 842 | Serial.println("MQTT is connected"); 843 | // Once connected, publish an announcement... 844 | char tmp[128]; 845 | String statusmessage = StationName + ", " + Version + ": client started"; 846 | statusmessage.toCharArray(tmp, 128); 847 | client.publish(mqtt_status, tmp); 848 | delay(50); 849 | } else { 850 | Serial.print(" ...failed, rc="); 851 | Serial.print(client.state()); 852 | Serial.println(" try again later"); 853 | } 854 | 855 | } //end void reconnect*/ 856 | #endif 857 | 858 | #ifdef EIGHTEENDTWENTY 859 | float getTemperature() { 860 | const int numReadings = 32; // the higher the value, the smoother the average (default: 32) 861 | float readings[numReadings]; // the readings from the analog input 862 | float total = 0; // the running total 863 | float tempTemp; 864 | 865 | for (int thisReading = 0; thisReading < numReadings; thisReading++) // take # of readings for smoothening 866 | { 867 | readings[thisReading] = s18d20.getTempCByIndex(0); 868 | total = total + readings[thisReading]; 869 | } 870 | tempTemp = total / numReadings; 871 | 872 | // if (!is_metric) tempTemp = tempTemp * 1.8 + 32; // conversion into Fahrenheit 873 | 874 | if (tempTemp > -127 && tempTemp < 85) { // error handling for bogus readings 875 | return tempTemp; 876 | } 877 | else { 878 | return -88; 879 | } 880 | } 881 | #endif 882 | 883 | void goToSleep(unsigned int sleepmin) { 884 | #ifdef MQTT 885 | char tmp[128]; 886 | String sleepmessage = StationName + ", " + Version + ": Taking a nap for " + String (sleepmin) + " Minutes"; 887 | sleepmessage.toCharArray(tmp, 128); 888 | client.publish(mqtt_status, tmp); 889 | delay(50); 890 | 891 | Serial.println("INFO: Closing the MQTT connection"); 892 | client.disconnect(); 893 | #endif 894 | 895 | Serial.println("INFO: Closing the Wifi connection"); 896 | WiFi.disconnect(); 897 | 898 | while (espClient.connected() || (WiFi.status() == WL_CONNECTED)) { 899 | Serial.println("Waiting for shutdown before sleeping"); 900 | delay(10); 901 | } 902 | delay(50); 903 | 904 | Serial.print ("Going to sleep now for "); 905 | Serial.print (sleepmin); 906 | Serial.print (" Minute(s)."); 907 | ESP.deepSleep(sleepmin * 60 * 1000000); // convert to microseconds 908 | } // end of goToSleep() 909 | -------------------------------------------------------------------------------- /Translation24.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------------------------- 2 | Project Name : Solar Powered WiFi Weather Station V2.4x 3 | Features: temperature, dewpoint, dewpoint spread, heat index, humidity, absolute pressure, relative pressure, battery status and 4 | the famous Zambretti Forecaster (multi lingual) 5 | Authors: Keith Hungerford, Debasish Dutta and Marc Stähli 6 | Website : www.opengreenenergy.com 7 | 8 | ******* Transaltion tables ****************************************/ 9 | //output variables pressure 10 | String TEXT_AIR_PRESSURE; 11 | String TEXT_RISING_FAST; 12 | String TEXT_RISING; 13 | String TEXT_RISING_SLOW; 14 | String TEXT_STEADY; 15 | String TEXT_FALLING_SLOW; 16 | String TEXT_FALLING; 17 | String TEXT_FALLING_FAST; 18 | 19 | String TEXT_STRONG_HIGH; 20 | String TEXT_HIGHPRESSURE; 21 | String TEXT_LOWPRESSURE; 22 | String TEXT_STRONG_LOW; 23 | String TEXT_STORM_LOW; 24 | 25 | //output variables forecast in words 26 | String TEXT_ZAMBRETTI_FORECAST; 27 | String TEXT_ZAMBRETTI_ACCURACY; 28 | String TEXT_ZAMBRETTI_A; 29 | String TEXT_ZAMBRETTI_B; 30 | String TEXT_ZAMBRETTI_C; 31 | String TEXT_ZAMBRETTI_D; 32 | String TEXT_ZAMBRETTI_E; 33 | String TEXT_ZAMBRETTI_F; 34 | String TEXT_ZAMBRETTI_G; 35 | String TEXT_ZAMBRETTI_H; 36 | String TEXT_ZAMBRETTI_I; 37 | String TEXT_ZAMBRETTI_J; 38 | String TEXT_ZAMBRETTI_K; 39 | String TEXT_ZAMBRETTI_L; 40 | String TEXT_ZAMBRETTI_M; 41 | String TEXT_ZAMBRETTI_N; 42 | String TEXT_ZAMBRETTI_O; 43 | String TEXT_ZAMBRETTI_P; 44 | String TEXT_ZAMBRETTI_Q; 45 | String TEXT_ZAMBRETTI_R; 46 | String TEXT_ZAMBRETTI_S; 47 | String TEXT_ZAMBRETTI_T; 48 | String TEXT_ZAMBRETTI_U; 49 | String TEXT_ZAMBRETTI_V; 50 | String TEXT_ZAMBRETTI_W; 51 | String TEXT_ZAMBRETTI_X; 52 | String TEXT_ZAMBRETTI_Y; 53 | String TEXT_ZAMBRETTI_Z; 54 | String TEXT_ZAMBRETTI_0; 55 | String TEXT_ZAMBRETTI_DEFAULT; 56 | 57 | void translate() { 58 | 59 | if (LANGUAGE == "EN") { 60 | TEXT_AIR_PRESSURE = "Pressure"; 61 | TEXT_RISING_FAST = "rising fast"; 62 | TEXT_RISING = "rising"; 63 | TEXT_RISING_SLOW = "rising slow"; 64 | TEXT_STEADY = "steady"; 65 | TEXT_FALLING_SLOW = "falling slow"; 66 | TEXT_FALLING = "falling"; 67 | TEXT_FALLING_FAST = "falling fast"; 68 | 69 | TEXT_STRONG_HIGH = "strong high"; 70 | TEXT_HIGHPRESSURE = "high pressure"; 71 | TEXT_LOWPRESSURE = "low pressure"; 72 | TEXT_STRONG_LOW = "strong low"; 73 | TEXT_STORM_LOW = "stormy low"; 74 | 75 | TEXT_ZAMBRETTI_FORECAST = "Forecast"; 76 | TEXT_ZAMBRETTI_ACCURACY = "Prediction accuracy"; 77 | TEXT_ZAMBRETTI_A = "Settled Fine Weather"; 78 | TEXT_ZAMBRETTI_B = "Fine Weather"; 79 | TEXT_ZAMBRETTI_C = "Becoming Fine"; 80 | TEXT_ZAMBRETTI_D = "Fine, Becoming Less Settled"; 81 | TEXT_ZAMBRETTI_E = "Fine, Possibly showers"; 82 | TEXT_ZAMBRETTI_G = "Fairly Fine, Possibly showers early"; 83 | TEXT_ZAMBRETTI_H = "Fairly Fine, Showers Later"; 84 | TEXT_ZAMBRETTI_I = "Showery Early, Improving"; 85 | TEXT_ZAMBRETTI_J = "Changeable Improving"; 86 | TEXT_ZAMBRETTI_K = "Fairly Fine, Showers likely"; 87 | TEXT_ZAMBRETTI_L = "Rather Unsettled Clearing Later"; 88 | TEXT_ZAMBRETTI_M = "Unsettled, Probably Improving"; 89 | TEXT_ZAMBRETTI_N = "Showery Bright Intervals"; 90 | TEXT_ZAMBRETTI_O = "Showery Becoming Unsettled"; 91 | TEXT_ZAMBRETTI_Q = "Unsettled, short fine Intervals"; 92 | TEXT_ZAMBRETTI_R = "Unsettled, Rain later"; 93 | TEXT_ZAMBRETTI_S = "Unsettled, rain at times"; 94 | TEXT_ZAMBRETTI_T = "Very Unsettled, Finer at times"; 95 | TEXT_ZAMBRETTI_U = "Rain at times, Worse later"; 96 | TEXT_ZAMBRETTI_V = "Rain at times, becoming very unsettled"; 97 | TEXT_ZAMBRETTI_W = "Rain at Frequent Intervals"; 98 | TEXT_ZAMBRETTI_X = "Very Unsettled, Rain"; 99 | TEXT_ZAMBRETTI_Y = "Stormy, possibly improving"; 100 | TEXT_ZAMBRETTI_Z = "Stormy, much rain"; 101 | TEXT_ZAMBRETTI_0 = "Battery empty, please recharge!"; 102 | TEXT_ZAMBRETTI_DEFAULT = "Sorry, no forecast for the moment"; 103 | } 104 | 105 | if (LANGUAGE == "DE") { 106 | TEXT_AIR_PRESSURE = "Luftdruck"; 107 | TEXT_RISING_FAST = "rasch steigend"; 108 | TEXT_RISING = "steigend"; 109 | TEXT_RISING_SLOW = "langsam steigend"; 110 | TEXT_STEADY = "beständig"; 111 | TEXT_FALLING_SLOW = "langsam fallend"; 112 | TEXT_FALLING = "fallend"; 113 | TEXT_FALLING_FAST = "rasch fallend"; 114 | 115 | TEXT_STRONG_HIGH = "Kräftiges Hoch"; 116 | TEXT_HIGHPRESSURE = "Hochdruck"; 117 | TEXT_LOWPRESSURE = "Tiefdruck"; 118 | TEXT_STRONG_LOW = "Ausgewachsenes Tief"; 119 | TEXT_STORM_LOW = "Sturmtief"; 120 | 121 | TEXT_ZAMBRETTI_FORECAST = "Wettervorhersage"; 122 | TEXT_ZAMBRETTI_ACCURACY = "Vorhersagegenauigkeit"; 123 | TEXT_ZAMBRETTI_A = "Beständiges Schönwetter"; 124 | TEXT_ZAMBRETTI_B = "Schönes Wetter"; 125 | TEXT_ZAMBRETTI_C = "Wetter wird gut"; 126 | TEXT_ZAMBRETTI_D = "Schön, wird wechselhaft"; 127 | TEXT_ZAMBRETTI_E = "Schön, Regenschauer möglich"; 128 | TEXT_ZAMBRETTI_F = "Ziemlich gut, verbessert sich"; 129 | TEXT_ZAMBRETTI_G = "Ziemlich gut, frühe Regenschauer möglich"; 130 | TEXT_ZAMBRETTI_H = "Ziemlich gut, spätere Regenschauer"; 131 | TEXT_ZAMBRETTI_I = "Früh schauerhaft, verbessert sich"; 132 | TEXT_ZAMBRETTI_J = "Wechselhaft, verbessert sich"; 133 | TEXT_ZAMBRETTI_K = "Ziemlich gut, Regenschauer möglich"; 134 | TEXT_ZAMBRETTI_L = "Eher veränderlich, klart später auf"; 135 | TEXT_ZAMBRETTI_M = "Veränderlich, verbessert sich wahrscheinlich"; 136 | TEXT_ZAMBRETTI_N = "Regnerisch mit Aufhellungen"; 137 | TEXT_ZAMBRETTI_O = "Regnerisch, wird veränderlich"; 138 | TEXT_ZAMBRETTI_P = "Veränderlich mit wenig Regen"; 139 | TEXT_ZAMBRETTI_Q = "Veränderlich, mit kurzen schönen Intervallen"; 140 | TEXT_ZAMBRETTI_R = "Veränderlich, später Regen"; 141 | TEXT_ZAMBRETTI_S = "Veränderlich, zeitweise Regen"; 142 | TEXT_ZAMBRETTI_T = "Stark wechselnd, zeitweise schöner"; 143 | TEXT_ZAMBRETTI_U = "Zeitweise Regen, verschlechtert sich"; 144 | TEXT_ZAMBRETTI_V = "Zeitweise Regen, wird sehr unruhig"; 145 | TEXT_ZAMBRETTI_W = "Regen in regelmässigen Abständen"; 146 | TEXT_ZAMBRETTI_X = "Sehr veränderlich, Regen"; 147 | TEXT_ZAMBRETTI_Y = "Stürmisch, verbessert sich wahrscheinlich"; 148 | TEXT_ZAMBRETTI_Z = "Stürmisch, viel Regen"; 149 | TEXT_ZAMBRETTI_0 = "Batterie leer, bitte nachladen!"; 150 | TEXT_ZAMBRETTI_DEFAULT = "Im Moment keine Prognose möglich"; 151 | } 152 | 153 | if (LANGUAGE == "DW") { // German - winter version 154 | TEXT_AIR_PRESSURE = "Luftdruck"; 155 | TEXT_RISING_FAST = "rasch steigend"; 156 | TEXT_RISING = "steigend"; 157 | TEXT_RISING_SLOW = "langsam steigend"; 158 | TEXT_STEADY = "beständig"; 159 | TEXT_FALLING_SLOW = "langsam fallend"; 160 | TEXT_FALLING = "fallend"; 161 | TEXT_FALLING_FAST = "rasch fallend"; 162 | 163 | TEXT_STRONG_HIGH = "Kräftiges Hoch"; 164 | TEXT_HIGHPRESSURE = "Hochdruck"; 165 | TEXT_LOWPRESSURE = "Tiefdruck"; 166 | TEXT_STRONG_LOW = "Ausgewachsenes Tief"; 167 | TEXT_STORM_LOW = "Sturmtief"; 168 | 169 | TEXT_ZAMBRETTI_FORECAST = "Wettervorhersage"; 170 | TEXT_ZAMBRETTI_ACCURACY = "Vorhersagegenauigkeit"; 171 | TEXT_ZAMBRETTI_A = "Beständig schönes Winterwetter"; 172 | TEXT_ZAMBRETTI_B = "Schönes Winterwetter"; 173 | TEXT_ZAMBRETTI_C = "Wetter wird gut"; 174 | TEXT_ZAMBRETTI_D = "Schön, wird wechselhaft"; 175 | TEXT_ZAMBRETTI_E = "Schön, Schneefall möglich"; 176 | TEXT_ZAMBRETTI_F = "Ziemlich gut, verbessert sich"; 177 | TEXT_ZAMBRETTI_G = "Ziemlich gut, früher Schneefall möglich"; 178 | TEXT_ZAMBRETTI_H = "Ziemlich gut, späterer Schneefall"; 179 | TEXT_ZAMBRETTI_I = "Früh schauerhaft, verbessert sich"; 180 | TEXT_ZAMBRETTI_J = "Wechselhaft, verbessert sich"; 181 | TEXT_ZAMBRETTI_K = "Ziemlich gut, Schneefall möglich"; 182 | TEXT_ZAMBRETTI_L = "Eher veränderlich, klart später auf"; 183 | TEXT_ZAMBRETTI_M = "Veränderlich, verbessert sich wahrscheinlich"; 184 | TEXT_ZAMBRETTI_N = "Schneefall mit Aufhellungen"; 185 | TEXT_ZAMBRETTI_O = "Schnee möglich, wird veränderlich"; 186 | TEXT_ZAMBRETTI_P = "Veränderlich mit wenig Schnee"; 187 | TEXT_ZAMBRETTI_Q = "Veränderlich, mit kurzen schönen Intervallen"; 188 | TEXT_ZAMBRETTI_R = "Veränderlich, später Schnee"; 189 | TEXT_ZAMBRETTI_S = "Veränderlich, zeitweise Schnee"; 190 | TEXT_ZAMBRETTI_T = "Stark wechselnd, zeitweise schöner"; 191 | TEXT_ZAMBRETTI_U = "Zeitweise Schnee, verschlechtert sich"; 192 | TEXT_ZAMBRETTI_V = "Zeitweise Schnee, wird sehr unruhig"; 193 | TEXT_ZAMBRETTI_W = "Schnee in regelmässigen Abständen"; 194 | TEXT_ZAMBRETTI_X = "Sehr veränderlich, Schnee"; 195 | TEXT_ZAMBRETTI_Y = "Stürmisch, verbessert sich wahrscheinlich"; 196 | TEXT_ZAMBRETTI_Z = "Stürmisch, viel Schnee"; 197 | TEXT_ZAMBRETTI_0 = "Batterie leer, bitte nachladen!"; 198 | TEXT_ZAMBRETTI_DEFAULT = "Im Moment keine Prognose moeglich"; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /history/Settings.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------------------------- 2 | Project Name : Solar Powered WiFi Weather Station V2.35 3 | Features: temperature, dewpoint, dewpoint spread, heat index, humidity, absolute pressure, relative pressure, battery status and 4 | the famous Zambretti Forecaster (multi lingual) 5 | Authors: Keith Hungerford, Debasish Dutta and Marc Stähli 6 | Website : www.opengreenenergy.com 7 | 8 | ******* configuration control constant for use of Blynk and/or Thingspeak ***/ 9 | 10 | const String App1 = "BLYNK"; // empty string if not applicable -> "" else "BLYNK" 11 | const String App2 = "THINGSPEAK"; // empty string if not applicable -> "" else "THINGSPEAK" 12 | 13 | 14 | /****** Blink or ThingSpeak Settings ****************************************/ 15 | 16 | char auth[] = ""; // Blynk Auth Token 17 | 18 | char ssid[] = ""; // WiFi Router ssid 19 | char pass[] = ""; // WiFi Router password 20 | 21 | unsigned long ts_ch_id = 000000; // Thingspeak Channel ID 22 | const char* ts_api_key = ""; // API write key 23 | 24 | 25 | /****** MQTT Settings ********************************************************/ 26 | 27 | const char* mqtt_server = "192.xxx.xxx.xx"; // MQTT Server (broker) address 28 | 29 | /****** Additional Settings **************************************************/ 30 | 31 | #define LANGUAGE 'DE' // either 'DE' for German or 'EN' for English 32 | 33 | #define TEMP_CORR (-1) // Manual correction of temp sensor 34 | #define ELEVATION (130) // Enter your elevation in m ASL to calculate rel pressure (ASL/QNH) at your place 35 | 36 | #define sleepTimeMin (10) // setting of deepsleep time in minutes (default: 10) 37 | 38 | // NTP 39 | #define NTP_SERVER "ch.pool.ntp.org" // Swiss NTP pool - use any in your country 40 | #define TZ 1 // (utc+) TZ in hours 41 | #define DST_MN 60 // use 60mn for summer time in some countries 42 | 43 | #define TZ_SEC ((TZ)*3600) 44 | #define DST_SEC ((DST_MN)*60) 45 | 46 | /**********Blynk & ThingSpeak assginments --------------------------------- 47 | 48 | Blynk: 49 | 50 | virtual pin 0 Temperature (Celcius) 51 | virtual pin 1 Humidity (%) 52 | virtual pin 2 Absolute Pressure (hPa) 53 | virtual pin 3 Relative Pressure (hPa) 54 | virtual pin 4 Battery Volts (V) 55 | virtual pin 5 Dewpoint (Celcius) 56 | virtual pin 6 HeatIndex (Celcius) 57 | virtual pin 7 Zambrettis Words 58 | virtual pin 8 Accuracy in percent (%) 59 | virtual pin 9 Tend in Words 60 | virtual pin 10 Dewpoint Spread 61 | 62 | ThingSpeak: 63 | 64 | Field 1: Relative Pressure (hPa) 65 | Field 2: Temperature (Celcius) 66 | Field 3: Humidity (%) 67 | Field 4: Battery (V) 68 | Field 5: Absolute Pressure (hPa) 69 | Field 6: Dewpoint (Celcius) 70 | Field 7: HeatIndex (Celcius) 71 | 72 | MQTT 73 | 74 | home/weather/solarweatherstation/tempc 75 | home/weather//solarweatherstation/heatindexc 76 | home/weather/solarweatherstation/dewpointc 77 | home/weather/solarweatherstation/spreadc 78 | home/weather/solarweatherstation/abshpa 79 | home/weather/solarweatherstation/relhpa 80 | home/weather/solarweatherstation/humi 81 | home/weather/solarweatherstation/battv 82 | home/weather/solarweatherstation/zambrettisays 83 | home/weather/solarweatherstation/trend 84 | home/weather/solarweatherstation/accuracy 85 | 86 | ***************************************************************************/ 87 | -------------------------------------------------------------------------------- /history/Solar-WiFi-Weather-Station-V2.3 MQTT.ino: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------------------------- 2 | Project Name : Solar Powered WiFi Weather Station V2.35 3 | Features: temperature, dewpoint, dewpoint spread, heat index, humidity, absolute pressure, relative pressure, battery status and 4 | the famous Zambretti Forecaster (multi lingual) 5 | Authors: Keith Hungerford, Debasish Dutta and Marc Stähli 6 | Website : www.opengreenenergy.com 7 | 8 | Main microcontroller (ESP8266) and BME280 both sleep between measurements 9 | BME280 is used in single shot mode ("forced mode") 10 | CODE: https://github.com/3KUdelta/Solar_WiFi_Weather_Station 11 | INSTRUCTIONS & HARDWARE: https://www.instructables.com/id/Solar-Powered-WiFi-Weather-Station-V20/ 12 | 3D FILES: https://www.thingiverse.com/thing:3551386 13 | 14 | CREDITS: 15 | Inspiration and code fragments of Dewpoint and Heatindex calculations are taken from: 16 | https://arduinotronics.blogspot.com/2013/12/temp-humidity-w-dew-point-calcualtions.html 17 | For Zambretti Ideas: 18 | http://drkfs.net/zambretti.htm or http://integritext.net/DrKFS/zambretti.htm 19 | https://raspberrypiandstuff.wordpress.com 20 | David Bird: https://github.com/G6EJD/ESP32_Weather_Forecaster_TN061 21 | 22 | Needed libraries: 23 | --> Adafruit unified sensor 24 | --> Adafrout BME280 sensor 25 | --> https://github.com/blynkkk/blynk-library 26 | 27 | 28 | "FS.h" 29 | --> https://github.com/aharshac/EasyNTPClient 30 | --> https://github.com/PaulStoffregen/Time.git 31 | 32 | CREDITS for Adafruit libraries: 33 | This is a library for the BME280 humidity, temperature & pressure sensor 34 | Designed specifically to work with the Adafruit BME280 Breakout 35 | ----> http://www.adafruit.com/products/2650 36 | These sensors use I2C or SPI to communicate, 2 or 4 pins are required 37 | to interface. The device's I2C address is either 0x76 or 0x77. 38 | Adafruit invests time and resources providing this open source code, 39 | please support Adafruit andopen-source hardware by purchasing products 40 | from Adafruit! 41 | Written by Limor Fried & Kevin Townsend for Adafruit Industries. 42 | BSD license, all text above must be included in any redistribution 43 | 44 | Hardware Settings Mac: 45 | LOLIN(WEMOS) D1 mini Pro, 80 MHz, Flash, 16M (14M SPIFFS), v2 Lower Memory, Disable, None, Only Sketch, 921600 on /dev/cu.SLAB_USBtoUART 46 | major update on 15/05/2019 47 | -added Zambretti Forecster 48 | -added translation feature 49 | -added English language 50 | -added German language 51 | updated on 03/06/2019 52 | -added Dewpoint Spread 53 | -minor code corrections 54 | updated 28/06/19 55 | -added MQTT (publishing all data to MQTT) 56 | -added Italian and Polish tranlation (Chak10) and (TomaszDom) 57 | updated 27/11/19 to V2.32 58 | -added battery protection at 3.3V, sending "batt empty" message and go to hybernate mode 59 | updated 11/05/20 to v2.33 60 | -corrected bug in adjustments for summer/winter 61 | updated 27/05/20 to v2.34 62 | - added August-Roche-Magnus approximation to automatically adjust humidity with temperature corrections 63 | updated 30/08/22 to v2.35 64 | -corrected Thingspeak communication issue 65 | updated 07/01/23 66 | - updated Blynk Settings (if you were on Blynk legacy, please move to new Blynk) 67 | 68 | //// Features : ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 69 | // 1. Connect to Wi-Fi, and upload the data to either Blynk App and/or Thingspeak and to any MQTT broker 70 | // 2. Monitoring Weather parameters like Temperature, Pressure abs, Pressure MSL and Humidity. 71 | // 3. Extra Ports to add more Weather Sensors like UV Index, Light and Rain Guage etc. 72 | // 4. Remote Battery Status Monitoring 73 | // 5. Using Sleep mode to reduce the energy consumed 74 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 75 | 76 | /*************************************************** 77 | * VERY IMPORTANT: * 78 | * * 79 | * Enter your personal settings in Settings.h ! * 80 | * * 81 | **************************************************/ 82 | 83 | #include "Settings.h" 84 | #include "Translation.h" 85 | 86 | #include 87 | #include 88 | #include //https://github.com/blynkkk/blynk-library 89 | #include 90 | #include 91 | #include "FS.h" 92 | #include //https://github.com/aharshac/EasyNTPClient 93 | #include //https://github.com/PaulStoffregen/Time.git 94 | #include // For MQTT (in this case publishing only) 95 | #include "ThingSpeak.h" 96 | 97 | Adafruit_BME280 bme; // I2C 98 | WiFiUDP udp; 99 | EasyNTPClient ntpClient(udp, NTP_SERVER, TZ_SEC + DST_SEC); 100 | 101 | float measured_temp; 102 | float adjusted_temp; 103 | float measured_humi; 104 | float adjusted_humi; 105 | float measured_pres; 106 | float SLpressure_hPa; // needed for rel pressure calculation 107 | float HeatIndex; // Heat Index in °C 108 | float volt; 109 | int rel_pressure_rounded; 110 | float DewpointTemperature; 111 | float DewPointSpread; // Difference between actual temperature and dewpoint 112 | 113 | // FORECAST CALCULATION 114 | unsigned long current_timestamp; // Actual timestamp read from NTPtime_t now; 115 | unsigned long saved_timestamp; // Timestamp stored in SPIFFS 116 | 117 | float pressure_value[12]; // Array for the historical pressure values (6 hours, all 30 mins) 118 | // where as pressure_value[0] is always the most recent value 119 | float pressure_difference[12]; // Array to calculate trend with pressure differences 120 | 121 | // FORECAST RESULT 122 | int accuracy; // Counter, if enough values for accurate forecasting 123 | String ZambrettisWords; // Final statement about weather forecast 124 | String trend_in_words; // Trend in words 125 | String forecast_in_words; // Weather forecast in words 126 | String pressure_in_words; // Air pressure in words 127 | String accuracy_in_words; // Zambretti's prediction accuracy in words 128 | 129 | void(* resetFunc) (void) = 0; // declare reset function @ address 0 130 | 131 | WiFiClient espClient; // MQTT 132 | PubSubClient client(espClient); // MQTT 133 | 134 | void setup() { 135 | 136 | Serial.begin(115200); 137 | Serial.println(); 138 | Serial.println("Start of SolarWiFiWeatherStation V2.35"); 139 | 140 | //******Battery Voltage Monitoring (first thing to do: is battery still ok?)*********** 141 | 142 | // Voltage divider R1 = 220k+100k+220k =540k and R2=100k 143 | float calib_factor = 5.28; // change this value to calibrate the battery voltage 144 | unsigned long raw = analogRead(A0); 145 | volt = raw * calib_factor/1024; 146 | 147 | Serial.print( "Voltage = "); 148 | Serial.print(volt, 2); // print with 2 decimal places 149 | Serial.println (" V"); 150 | 151 | // **************Application going online********************************************** 152 | 153 | WiFi.mode(WIFI_STA); 154 | WiFi.hostname("SolarWeatherStation"); //This changes the hostname of the ESP8266 to display neatly on the network esp on router. 155 | WiFi.begin(ssid, pass); 156 | Serial.print("---> Connecting to WiFi "); 157 | int i = 0; 158 | while (WiFi.status() != WL_CONNECTED) { 159 | delay(500); 160 | i++; 161 | if (i > 20) { 162 | Serial.println("Could not connect to WiFi!"); 163 | Serial.println("Going to sleep for 10 minutes and try again."); 164 | if (volt > 3.3){ 165 | goToSleep(10); // go to sleep and retry after 10 min 166 | } 167 | else{ 168 | goToSleep(0); // hybernate because batt empty - this is just to avoid that an endless 169 | } // try to get a WiFi signal will drain the battery empty 170 | } 171 | Serial.print("."); 172 | } 173 | Serial.println(" Wifi connected ok"); 174 | 175 | if (App1 == "BLYNK") { // for posting data to Blynk App 176 | Blynk.begin(auth, ssid, pass); 177 | } 178 | if (App2 == "THINGSPEAK") { 179 | ThingSpeak.begin(espClient); // Initialize ThingSpeak 180 | } 181 | 182 | connect_to_MQTT(); // connecting to MQTT broker 183 | 184 | client.publish("home/debug", "SolarWeatherstation: Sensor started"); 185 | delay(50); 186 | 187 | //*****************Checking if SPIFFS available******************************** 188 | 189 | Serial.println("SPIFFS Initialization: (First time run can last up to 30 sec - be patient)"); 190 | 191 | boolean mounted = SPIFFS.begin(); // load config if it exists. Otherwise use defaults. 192 | if (!mounted) { 193 | Serial.println("FS not formatted. Doing that now... (can last up to 30 sec)."); 194 | SPIFFS.format(); 195 | Serial.println("FS formatted..."); 196 | SPIFFS.begin(); 197 | } 198 | 199 | //******** GETTING THE TIME FROM NTP SERVER *********************************** 200 | 201 | Serial.println("---> Now reading time from NTP Server"); 202 | int ii = 0; 203 | while(!ntpClient.getUnixTime()){ 204 | delay(100); 205 | ii++; 206 | if (ii > 20) { 207 | Serial.println("Could not connect to NTP Server!"); 208 | Serial.println("Doing a reset now and retry a connection from scratch."); 209 | resetFunc(); 210 | } 211 | Serial.print("."); 212 | } 213 | current_timestamp = ntpClient.getUnixTime(); // get UNIX timestamp (seconds from 1.1.1970 on) 214 | 215 | Serial.print("Current UNIX Timestamp: "); 216 | Serial.println(current_timestamp); 217 | 218 | Serial.print("Time & Date: "); 219 | Serial.print(hour(current_timestamp)); 220 | Serial.print(":"); 221 | Serial.print(minute(current_timestamp)); 222 | Serial.print(":"); 223 | Serial.print(second(current_timestamp)); 224 | Serial.print("; "); 225 | Serial.print(day(current_timestamp)); 226 | Serial.print("."); 227 | Serial.print(month(current_timestamp)); // needed later: month as integer for Zambretti calcualtion 228 | Serial.print("."); 229 | Serial.println(year(current_timestamp)); 230 | 231 | //******** GETTING RELATIVE PRESSURE DATA FROM SENSOR (BME680) ******************** 232 | 233 | bool bme_status; 234 | bme_status = bme.begin(0x76); //address either 0x76 or 0x77 235 | if (!bme_status) { 236 | Serial.println("Could not find a valid BME280 sensor, check wiring!"); 237 | } 238 | 239 | Serial.println("forced mode, 1x temperature / 1x humidity / 1x pressure oversampling,"); 240 | Serial.println("filter off"); 241 | 242 | bme.setSampling(Adafruit_BME280::MODE_FORCED, 243 | Adafruit_BME280::SAMPLING_X1, // temperature 244 | Adafruit_BME280::SAMPLING_X1, // pressure 245 | Adafruit_BME280::SAMPLING_X1, // humidity 246 | Adafruit_BME280::FILTER_OFF ); 247 | 248 | measurementEvent(); //calling function to get all data from the different sensors 249 | 250 | //*******************SPIFFS operations*************************************************************** 251 | 252 | ReadFromSPIFFS(); //read stored values and update data if more recent data is available 253 | 254 | Serial.print("Timestamp difference: "); 255 | Serial.println(current_timestamp - saved_timestamp); 256 | 257 | if (current_timestamp - saved_timestamp > 21600){ // last save older than 6 hours -> re-initialize values 258 | FirstTimeRun(); 259 | } 260 | else if (current_timestamp - saved_timestamp > 1800){ // it is time for pressure update (1800 sec = 30 min) 261 | 262 | for (int i = 11; i >= 1; i = i -1) { 263 | pressure_value[i] = pressure_value[i-1]; // shifting values one to the right 264 | } 265 | 266 | pressure_value[0] = rel_pressure_rounded; // updating with acutal rel pressure (newest value) 267 | 268 | if (accuracy < 12) { 269 | accuracy = accuracy + 1; // one value more -> accuracy rises (up to 12 = 100%) 270 | } 271 | WriteToSPIFFS(current_timestamp); // update timestamp on storage 272 | } 273 | else { 274 | WriteToSPIFFS(saved_timestamp); // do not update timestamp on storage 275 | } 276 | 277 | //**************************Calculate Zambretti Forecast******************************************* 278 | 279 | int accuracy_in_percent = accuracy*94/12; // 94% is the max predicion accuracy of Zambretti 280 | if ( volt > 3.4 ) { // check if batt is still ok 281 | ZambrettisWords = ZambrettiSays(char(ZambrettiLetter())); 282 | forecast_in_words = TEXT_ZAMBRETTI_FORECAST; 283 | pressure_in_words = TEXT_AIR_PRESSURE; 284 | accuracy_in_words = TEXT_ZAMBRETTI_ACCURACY; 285 | } 286 | else { 287 | ZambrettisWords = ZambrettiSays('0'); // send Message that battery is empty 288 | } 289 | 290 | Serial.println("********************************************************"); 291 | Serial.print("Zambretti says: "); 292 | Serial.print(ZambrettisWords); 293 | Serial.print(", "); 294 | Serial.println(trend_in_words); 295 | Serial.print("Prediction accuracy: "); 296 | Serial.print(accuracy_in_percent); 297 | Serial.println("%"); 298 | if (accuracy < 12){ 299 | Serial.println("Reason: Not enough weather data yet."); 300 | Serial.print("We need "); 301 | Serial.print((12 - accuracy) / 2); 302 | Serial.println(" hours more to get sufficient data."); 303 | } 304 | Serial.println("********************************************************"); 305 | 306 | //**************************Sending Data to Blynk and ThingSpeak********************************* 307 | // code block for uploading data to BLYNK App 308 | 309 | if (App1 == "BLYNK") { 310 | Blynk.virtualWrite(V0, adjusted_temp); // virtual pin 0 311 | Blynk.virtualWrite(V1, adjusted_humi); // virtual pin 1 312 | Blynk.virtualWrite(V2, measured_pres); // virtual pin 2 313 | Blynk.virtualWrite(V3, rel_pressure_rounded); // virtual pin 3 314 | Blynk.virtualWrite(V4, volt); // virtual pin 4 315 | Blynk.virtualWrite(V5, DewpointTemperature); // virtual pin 5 316 | Blynk.virtualWrite(V6, HeatIndex); // virtual pin 6 317 | Blynk.virtualWrite(V7, ZambrettisWords); // virtual pin 7 318 | Blynk.virtualWrite(V8, accuracy_in_percent); // virtual pin 8 319 | Blynk.virtualWrite(V9, trend_in_words); // virtual pin 9 320 | Blynk.virtualWrite(V10, DewPointSpread); // virtual pin 10 321 | Serial.println("Data written to Blink ..."); 322 | } 323 | 324 | //******************************************************************************* 325 | // code block for uploading data to Thingspeak website 326 | 327 | if (App2 == "THINGSPEAK") { 328 | 329 | ThingSpeak.setField(1, rel_pressure_rounded); 330 | ThingSpeak.setField(2, adjusted_temp); 331 | ThingSpeak.setField(3, adjusted_humi); 332 | ThingSpeak.setField(4, volt); 333 | ThingSpeak.setField(5, measured_pres); 334 | ThingSpeak.setField(6, DewpointTemperature); 335 | ThingSpeak.setField(7, HeatIndex); 336 | ThingSpeak.setStatus(String(ZambrettisWords + ", " + trend_in_words)); 337 | 338 | int feedback = ThingSpeak.writeFields(ts_ch_id, ts_api_key); 339 | if (feedback == 200) { 340 | Serial.println("Writing to Thingspeak was successful!"); 341 | } 342 | else { 343 | Serial.println("Problem updating Thingspeak. HTTP error code " + String(feedback)); 344 | } 345 | } 346 | 347 | //******************************************************************************* 348 | // code block for publishing all data to MQTT 349 | 350 | char _adjusted_temp[8]; // Buffer big enough for 7-character float 351 | dtostrf(adjusted_temp, 3, 1, _adjusted_temp); // Leave room for too large numbers! 352 | 353 | client.publish("home/weather/solarweatherstation/tempc", _adjusted_temp, 1); // ,1 = retained 354 | delay(50); 355 | 356 | char _adjusted_humi[8]; // Buffer big enough for 7-character float 357 | dtostrf(adjusted_humi, 3, 0, _adjusted_humi); // Leave room for too large numbers! 358 | 359 | client.publish("home/weather/solarweatherstation/humi", _adjusted_humi, 1); // ,1 = retained 360 | delay(50); 361 | 362 | char _measured_pres[8]; // Buffer big enough for 7-character float 363 | dtostrf(measured_pres, 3, 0, _measured_pres); // Leave room for too large numbers! 364 | 365 | client.publish("home/weather/solarweatherstation/abshpa", _measured_pres, 1); // ,1 = retained 366 | delay(50); 367 | 368 | char _rel_pressure_rounded[8]; // Buffer big enough for 7-character float 369 | dtostrf(rel_pressure_rounded, 3, 0, _rel_pressure_rounded); // Leave room for too large numbers! 370 | 371 | client.publish("home/weather/solarweatherstation/relhpa", _rel_pressure_rounded, 1); // ,1 = retained 372 | delay(50); 373 | 374 | char _volt[8]; // Buffer big enough for 7-character float 375 | dtostrf(volt, 3, 2, _volt); // Leave room for too large numbers! 376 | 377 | client.publish("home/weather/solarweatherstation/battv", _volt, 1); // ,1 = retained 378 | delay(50); 379 | 380 | char _DewpointTemperature[8]; // Buffer big enough for 7-character float 381 | dtostrf(DewpointTemperature, 3, 1, _DewpointTemperature); // Leave room for too large numbers! 382 | 383 | client.publish("home/weather/solarweatherstation/dewpointc", _DewpointTemperature, 1); // ,1 = retained 384 | delay(50); 385 | 386 | char _HeatIndex[8]; // Buffer big enough for 7-character float 387 | dtostrf(HeatIndex, 3, 1, _HeatIndex); // Leave room for too large numbers! 388 | 389 | client.publish("home/weather/solarweatherstation/heatindexc", _HeatIndex, 1); // ,1 = retained 390 | delay(50); 391 | 392 | char _accuracy_in_percent[8]; // Buffer big enough for 7-character float 393 | dtostrf(accuracy_in_percent, 3, 0, _accuracy_in_percent); // Leave room for too large numbers! 394 | 395 | client.publish("home/weather/solarweatherstation/accuracy", _accuracy_in_percent, 1); // ,1 = retained 396 | delay(50); 397 | 398 | char _DewPointSpread[8]; // Buffer big enough for 7-character float 399 | dtostrf(DewPointSpread, 3, 1, _DewPointSpread); // Leave room for too large numbers! 400 | 401 | client.publish("home/weather/solarweatherstation/spreadc", _DewPointSpread, 1); // ,1 = retained 402 | delay(50); 403 | 404 | char tmp1[128]; 405 | ZambrettisWords.toCharArray(tmp1, 128); 406 | client.publish("home/weather/solarweatherstation/zambrettisays", tmp1, 1); 407 | delay(50); 408 | 409 | char tmp2[128]; 410 | trend_in_words.toCharArray(tmp2, 128); 411 | client.publish("home/weather/solarweatherstation/trendinwords", tmp2, 1); 412 | delay(50); 413 | 414 | char _trend[8]; // Buffer big enough for 7-character float 415 | dtostrf(pressure_difference[11], 3, 2, _trend); // Leave room for too large numbers! 416 | 417 | client.publish("home/weather/solarweatherstation/trend", _trend, 1); // ,1 = retained 418 | delay(50); 419 | 420 | if (volt > 3.3) { //check if batt still ok, if yes 421 | goToSleep(sleepTimeMin); //go for a nap 422 | } 423 | else{ //if not, 424 | goToSleep(0); //hybernate because batt is empty 425 | } 426 | } // end of void setup() 427 | 428 | void loop() { //loop is not used 429 | } // end of void loop() 430 | 431 | void measurementEvent() { 432 | 433 | //Measures absolute Pressure, Temperature, Humidity, Voltage, calculate relative pressure, 434 | //Dewpoint, Dewpoint Spread, Heat Index 435 | 436 | bme.takeForcedMeasurement(); 437 | 438 | // Get temperature 439 | measured_temp = bme.readTemperature(); 440 | // print on serial monitor 441 | Serial.print("Temp: "); 442 | Serial.print(measured_temp); 443 | Serial.print("°C; "); 444 | 445 | // Get humidity 446 | measured_humi = bme.readHumidity(); 447 | // print on serial monitor 448 | Serial.print("Humidity: "); 449 | Serial.print(measured_humi); 450 | Serial.print("%; "); 451 | 452 | // Get pressure 453 | measured_pres = bme.readPressure() / 100.0F; 454 | // print on serial monitor 455 | Serial.print("Pressure: "); 456 | Serial.print(measured_pres); 457 | Serial.print("hPa; "); 458 | 459 | // Calculate and print relative pressure 460 | SLpressure_hPa = (((measured_pres * 100.0)/pow((1-((float)(ELEVATION))/44330), 5.255))/100.0); 461 | rel_pressure_rounded=(int)(SLpressure_hPa+.5); 462 | // print on serial monitor 463 | Serial.print("Pressure rel: "); 464 | Serial.print(rel_pressure_rounded); 465 | Serial.print("hPa; "); 466 | 467 | // Calculate dewpoint 468 | float a = 17.271; 469 | float b = 237.7; 470 | float tempcalc = (a * measured_temp) / (b + measured_temp) + log(measured_humi*0.01); 471 | DewpointTemperature = (b * tempcalc) / (a - tempcalc); 472 | Serial.print("Dewpoint: "); 473 | Serial.print(DewpointTemperature); 474 | Serial.println("°C; "); 475 | 476 | // With the dewpoint calculated we can correct temp and automatically calculate humidity 477 | adjusted_temp = measured_temp + TEMP_CORR; 478 | if (adjusted_temp < DewpointTemperature) adjusted_temp = DewpointTemperature; //compensation, if offset too high 479 | //August-Roche-Magnus approximation (http://bmcnoldy.rsmas.miami.edu/Humidity.html) 480 | adjusted_humi = 100 * (exp((a * DewpointTemperature) / (b + DewpointTemperature)) / exp((a * adjusted_temp) / (b + adjusted_temp))); 481 | if (adjusted_humi > 100) adjusted_humi = 100; // just in case 482 | // print on serial monitor 483 | Serial.print("Temp adjusted: "); 484 | Serial.print(adjusted_temp); 485 | Serial.print("°C; "); 486 | Serial.print("Humidity adjusted: "); 487 | Serial.print(adjusted_humi); 488 | Serial.print("%; "); 489 | 490 | // Calculate dewpoint spread (difference between actual temp and dewpoint -> the smaller the number: rain or fog 491 | 492 | DewPointSpread = adjusted_temp - DewpointTemperature; 493 | Serial.print("Dewpoint Spread: "); 494 | Serial.print(DewPointSpread); 495 | Serial.println("°C; "); 496 | 497 | // Calculate HI (heatindex in °C) --> HI starts working above 26,7 °C 498 | if (adjusted_temp > 26.7) { 499 | double c1 = -8.784, c2 = 1.611, c3 = 2.338, c4 = -0.146, c5= -1.230e-2, c6=-1.642e-2, c7=2.211e-3, c8=7.254e-4, c9=-2.582e-6 ; 500 | double T = adjusted_temp; 501 | double R = adjusted_humi; 502 | 503 | double A = (( c5 * T) + c2) * T + c1; 504 | double B = ((c7 * T) + c4) * T + c3; 505 | double C = ((c9 * T) + c8) * T + c6; 506 | HeatIndex = (C * R + B) * R + A; 507 | } 508 | else { 509 | HeatIndex = adjusted_temp; 510 | Serial.println("Not warm enough (less than 26.7 °C) for Heatindex"); 511 | } 512 | Serial.print("HeatIndex: "); 513 | Serial.print(HeatIndex); 514 | Serial.print("°C; "); 515 | 516 | } // end of void measurementEvent() 517 | 518 | int CalculateTrend(){ 519 | int trend; // -1 falling; 0 steady; 1 raising 520 | Serial.println("---> Calculating trend"); 521 | 522 | //--> giving the most recent pressure reads more weight 523 | pressure_difference[0] = (pressure_value[0] - pressure_value[1]) * 1.5; 524 | pressure_difference[1] = (pressure_value[0] - pressure_value[2]); 525 | pressure_difference[2] = (pressure_value[0] - pressure_value[3]) / 1.5; 526 | pressure_difference[3] = (pressure_value[0] - pressure_value[4]) / 2; 527 | pressure_difference[4] = (pressure_value[0] - pressure_value[5]) / 2.5; 528 | pressure_difference[5] = (pressure_value[0] - pressure_value[6]) / 3; 529 | pressure_difference[6] = (pressure_value[0] - pressure_value[7]) / 3.5; 530 | pressure_difference[7] = (pressure_value[0] - pressure_value[8]) / 4; 531 | pressure_difference[8] = (pressure_value[0] - pressure_value[9]) / 4.5; 532 | pressure_difference[9] = (pressure_value[0] - pressure_value[10]) / 5; 533 | pressure_difference[10] = (pressure_value[0] - pressure_value[11]) / 5.5; 534 | 535 | //--> calculating the average and storing it into [11] 536 | pressure_difference[11] = ( pressure_difference[0] 537 | + pressure_difference[1] 538 | + pressure_difference[2] 539 | + pressure_difference[3] 540 | + pressure_difference[4] 541 | + pressure_difference[5] 542 | + pressure_difference[6] 543 | + pressure_difference[7] 544 | + pressure_difference[8] 545 | + pressure_difference[9] 546 | + pressure_difference[10]) / 11; 547 | 548 | Serial.print("Current trend: "); 549 | Serial.println(pressure_difference[11]); 550 | 551 | if (pressure_difference[11] > 3.5) { 552 | trend_in_words = TEXT_RISING_FAST; 553 | trend = 1;} 554 | else if (pressure_difference[11] > 1.5 && pressure_difference[11] <= 3.5) { 555 | trend_in_words = TEXT_RISING; 556 | trend = 1; 557 | } 558 | else if (pressure_difference[11] > 0.25 && pressure_difference[11] <= 1.5) { 559 | trend_in_words = TEXT_RISING_SLOW; 560 | trend = 1; 561 | } 562 | else if (pressure_difference[11] > -0.25 && pressure_difference[11] < 0.25) { 563 | trend_in_words = TEXT_STEADY; 564 | trend = 0; 565 | } 566 | else if (pressure_difference[11] >= -1.5 && pressure_difference[11] < -0.25) { 567 | trend_in_words = TEXT_FALLING_SLOW; 568 | trend = -1; 569 | } 570 | else if (pressure_difference[11] >= -3.5 && pressure_difference[11] < -1.5) { 571 | trend_in_words = TEXT_FALLING; 572 | trend = -1; 573 | } 574 | else if (pressure_difference[11] <= -3.5) { 575 | trend_in_words = TEXT_FALLING_FAST; 576 | trend = -1; 577 | } 578 | 579 | Serial.println(trend_in_words); 580 | return trend; 581 | } 582 | 583 | char ZambrettiLetter() { 584 | Serial.println("---> Calculating Zambretti letter"); 585 | char z_letter; 586 | int(z_trend) = CalculateTrend(); 587 | // Case trend is falling 588 | if (z_trend == -1) { 589 | float zambretti = 0.0009746 * rel_pressure_rounded * rel_pressure_rounded - 2.1068 * rel_pressure_rounded + 1138.7019; 590 | //A Winter falling generally results in a Z value lower by 1 unit 591 | if (month(current_timestamp) < 4 || month(current_timestamp) > 9) zambretti = zambretti + 1; 592 | if (zambretti > 9) zambretti = 9; 593 | Serial.print("Calculated and rounded Zambretti in numbers: "); 594 | Serial.println(round(zambretti)); 595 | switch (int(round(zambretti))) { 596 | case 0: z_letter = 'A'; break; //Settled Fine 597 | case 1: z_letter = 'A'; break; //Settled Fine 598 | case 2: z_letter = 'B'; break; //Fine Weather 599 | case 3: z_letter = 'D'; break; //Fine Becoming Less Settled 600 | case 4: z_letter = 'H'; break; //Fairly Fine Showers Later 601 | case 5: z_letter = 'O'; break; //Showery Becoming unsettled 602 | case 6: z_letter = 'R'; break; //Unsettled, Rain later 603 | case 7: z_letter = 'U'; break; //Rain at times, worse later 604 | case 8: z_letter = 'V'; break; //Rain at times, becoming very unsettled 605 | case 9: z_letter = 'X'; break; //Very Unsettled, Rain 606 | } 607 | } 608 | // Case trend is steady 609 | if (z_trend == 0) { 610 | float zambretti = 138.24 - 0.133 * rel_pressure_rounded; 611 | Serial.print("Calculated and rounded Zambretti in numbers: "); 612 | Serial.println(round(zambretti)); 613 | switch (int(round(zambretti))) { 614 | case 0: z_letter = 'A'; break; //Settled Fine 615 | case 1: z_letter = 'A'; break; //Settled Fine 616 | case 2: z_letter = 'B'; break; //Fine Weather 617 | case 3: z_letter = 'E'; break; //Fine, Possibly showers 618 | case 4: z_letter = 'K'; break; //Fairly Fine, Showers likely 619 | case 5: z_letter = 'N'; break; //Showery Bright Intervals 620 | case 6: z_letter = 'P'; break; //Changeable some rain 621 | case 7: z_letter = 'S'; break; //Unsettled, rain at times 622 | case 8: z_letter = 'W'; break; //Rain at Frequent Intervals 623 | case 9: z_letter = 'X'; break; //Very Unsettled, Rain 624 | case 10: z_letter = 'Z'; break; //Stormy, much rain 625 | } 626 | } 627 | // Case trend is rising 628 | if (z_trend == 1) { 629 | float zambretti = 142.57 - 0.1376 * rel_pressure_rounded; 630 | //A Summer rising, improves the prospects by 1 unit over a Winter rising 631 | if (month(current_timestamp) >= 4 && month(current_timestamp) <= 9) zambretti = zambretti - 1; 632 | if (zambretti < 0) zambretti = 0; 633 | Serial.print("Calculated and rounded Zambretti in numbers: "); 634 | Serial.println(round(zambretti)); 635 | switch (int(round(zambretti))) { 636 | case 0: z_letter = 'A'; break; //Settled Fine 637 | case 1: z_letter = 'A'; break; //Settled Fine 638 | case 2: z_letter = 'B'; break; //Fine Weather 639 | case 3: z_letter = 'C'; break; //Becoming Fine 640 | case 4: z_letter = 'F'; break; //Fairly Fine, Improving 641 | case 5: z_letter = 'G'; break; //Fairly Fine, Possibly showers, early 642 | case 6: z_letter = 'I'; break; //Showery Early, Improving 643 | case 7: z_letter = 'J'; break; //Changeable, Improving 644 | case 8: z_letter = 'L'; break; //Rather Unsettled Clearing Later 645 | case 9: z_letter = 'M'; break; //Unsettled, Probably Improving 646 | case 10: z_letter = 'Q'; break; //Unsettled, short fine Intervals 647 | case 11: z_letter = 'T'; break; //Very Unsettled, Finer at times 648 | case 12: z_letter = 'Y'; break; //Stormy, possibly improving 649 | case 13: z_letter = 'Z'; break;; //Stormy, much rain 650 | } 651 | } 652 | char* tmp1 = &z_letter; 653 | client.publish("home/weather/solarweatherstation/zletter", tmp1, 1); // ,1 = retained 654 | delay(50); 655 | Serial.print("This is Zambretti's famous letter: "); 656 | Serial.println(z_letter); 657 | return z_letter; 658 | } 659 | 660 | String ZambrettiSays(char code){ 661 | String zambrettis_words = ""; 662 | switch (code) { 663 | case 'A': zambrettis_words = TEXT_ZAMBRETTI_A; break; //see Tranlation.h 664 | case 'B': zambrettis_words = TEXT_ZAMBRETTI_B; break; 665 | case 'C': zambrettis_words = TEXT_ZAMBRETTI_C; break; 666 | case 'D': zambrettis_words = TEXT_ZAMBRETTI_D; break; 667 | case 'E': zambrettis_words = TEXT_ZAMBRETTI_E; break; 668 | case 'F': zambrettis_words = TEXT_ZAMBRETTI_F; break; 669 | case 'G': zambrettis_words = TEXT_ZAMBRETTI_G; break; 670 | case 'H': zambrettis_words = TEXT_ZAMBRETTI_H; break; 671 | case 'I': zambrettis_words = TEXT_ZAMBRETTI_I; break; 672 | case 'J': zambrettis_words = TEXT_ZAMBRETTI_J; break; 673 | case 'K': zambrettis_words = TEXT_ZAMBRETTI_K; break; 674 | case 'L': zambrettis_words = TEXT_ZAMBRETTI_L; break; 675 | case 'M': zambrettis_words = TEXT_ZAMBRETTI_M; break; 676 | case 'N': zambrettis_words = TEXT_ZAMBRETTI_N; break; 677 | case 'O': zambrettis_words = TEXT_ZAMBRETTI_O; break; 678 | case 'P': zambrettis_words = TEXT_ZAMBRETTI_P; break; 679 | case 'Q': zambrettis_words = TEXT_ZAMBRETTI_Q; break; 680 | case 'R': zambrettis_words = TEXT_ZAMBRETTI_R; break; 681 | case 'S': zambrettis_words = TEXT_ZAMBRETTI_S; break; 682 | case 'T': zambrettis_words = TEXT_ZAMBRETTI_T; break; 683 | case 'U': zambrettis_words = TEXT_ZAMBRETTI_U; break; 684 | case 'V': zambrettis_words = TEXT_ZAMBRETTI_V; break; 685 | case 'W': zambrettis_words = TEXT_ZAMBRETTI_W; break; 686 | case 'X': zambrettis_words = TEXT_ZAMBRETTI_X; break; 687 | case 'Y': zambrettis_words = TEXT_ZAMBRETTI_Y; break; 688 | case 'Z': zambrettis_words = TEXT_ZAMBRETTI_Z; break; 689 | case '0': zambrettis_words = TEXT_ZAMBRETTI_0; break; 690 | default: zambrettis_words = TEXT_ZAMBRETTI_DEFAULT; break; 691 | } 692 | return zambrettis_words; 693 | } 694 | 695 | void ReadFromSPIFFS() { 696 | char filename [] = "/data.txt"; 697 | File myDataFile = SPIFFS.open(filename, "r"); // Open file for reading 698 | if (!myDataFile) { 699 | Serial.println("Failed to open file"); 700 | FirstTimeRun(); // no file there -> initializing 701 | } 702 | 703 | Serial.println("---> Now reading from SPIFFS"); 704 | 705 | String temp_data; 706 | 707 | temp_data = myDataFile.readStringUntil('\n'); 708 | saved_timestamp = temp_data.toInt(); 709 | Serial.print("Timestamp from SPIFFS: "); Serial.println(saved_timestamp); 710 | 711 | temp_data = myDataFile.readStringUntil('\n'); 712 | accuracy = temp_data.toInt(); 713 | Serial.print("Accuracy value read from SPIFFS: "); Serial.println(accuracy); 714 | 715 | Serial.print("Last 12 saved pressure values: "); 716 | for (int i = 0; i <= 11; i++) { 717 | temp_data = myDataFile.readStringUntil('\n'); 718 | pressure_value[i] = temp_data.toInt(); 719 | Serial.print(pressure_value[i]); 720 | Serial.print("; "); 721 | } 722 | myDataFile.close(); 723 | Serial.println(); 724 | } 725 | 726 | void WriteToSPIFFS(int write_timestamp) { 727 | char filename [] = "/data.txt"; 728 | File myDataFile = SPIFFS.open(filename, "w"); // Open file for writing (appending) 729 | if (!myDataFile) { 730 | Serial.println("Failed to open file"); 731 | } 732 | 733 | Serial.println("---> Now writing to SPIFFS"); 734 | 735 | myDataFile.println(write_timestamp); // Saving timestamp to /data.txt 736 | myDataFile.println(accuracy); // Saving accuracy value to /data.txt 737 | 738 | for ( int i = 0; i <= 11; i++) { 739 | myDataFile.println(pressure_value[i]); // Filling pressure array with updated values 740 | } 741 | myDataFile.close(); 742 | 743 | Serial.println("File written. Now reading file again."); 744 | myDataFile = SPIFFS.open(filename, "r"); // Open file for reading 745 | Serial.print("Found in /data.txt = "); 746 | while (myDataFile.available()) { 747 | Serial.print(myDataFile.readStringUntil('\n')); 748 | Serial.print("; "); 749 | } 750 | Serial.println(); 751 | myDataFile.close(); 752 | } 753 | 754 | void FirstTimeRun(){ 755 | Serial.println("---> Starting initializing process."); 756 | accuracy = 1; 757 | char filename [] = "/data.txt"; 758 | File myDataFile = SPIFFS.open(filename, "w"); // Open a file for writing 759 | if (!myDataFile) { 760 | Serial.println("Failed to open file"); 761 | Serial.println("Stopping process - maybe flash size not set (SPIFFS)."); 762 | exit(0); 763 | } 764 | myDataFile.println(current_timestamp); // Saving timestamp to /data.txt 765 | myDataFile.println(accuracy); // Saving accuracy value to /data.txt 766 | for ( int i = 0; i < 12; i++) { 767 | myDataFile.println(rel_pressure_rounded); // Filling pressure array with current pressure 768 | } 769 | Serial.println("** Saved initial pressure data. **"); 770 | myDataFile.close(); 771 | Serial.println("---> Doing a reset now."); 772 | resetFunc(); //call reset 773 | } 774 | 775 | void connect_to_MQTT() { 776 | Serial.print("---> Connecting to MQTT, "); 777 | client.setServer(mqtt_server, 1883); 778 | 779 | while (!client.connected()) { 780 | Serial.println("reconnecting MQTT..."); 781 | reconnect(); 782 | } 783 | Serial.println("MQTT connected ok."); 784 | } //end connect_to_MQTT 785 | 786 | void reconnect() { 787 | // Loop until we're reconnected 788 | while (!client.connected()) { 789 | Serial.print("Attempting MQTT connection with "); 790 | // Create a random client ID 791 | String clientId = "ESP8266Client-"; 792 | clientId += String(random(0xffff), HEX); 793 | Serial.print(clientId.c_str()); 794 | // Attempt to connect 795 | if (client.connect(clientId.c_str())) { 796 | Serial.println("connected"); 797 | // Once connected, publish an announcement... 798 | client.publish("home/debug", "SolarWeatherstation: client started..."); 799 | delay(50); 800 | } else { 801 | Serial.print(" ...failed, rc="); 802 | Serial.print(client.state()); 803 | Serial.println(" try again in 5 seconds"); 804 | // Wait 5 seconds before retrying 805 | delay(5000); 806 | } 807 | } 808 | } //end void reconnect*/ 809 | 810 | void goToSleep(unsigned int sleepmin) { 811 | char tmp[128]; 812 | String sleepmessage = "SolarWeatherstation: Taking a nap for " + String (sleepmin) + " Minutes"; 813 | sleepmessage.toCharArray(tmp, 128); 814 | client.publish("home/debug",tmp); 815 | delay(50); 816 | 817 | Serial.println("INFO: Closing the MQTT connection"); 818 | client.disconnect(); 819 | 820 | Serial.println("INFO: Closing the Wifi connection"); 821 | WiFi.disconnect(); 822 | 823 | while (client.connected() || (WiFi.status() == WL_CONNECTED)) { 824 | Serial.println("Waiting for shutdown before sleeping"); 825 | delay(10); 826 | } 827 | delay(50); 828 | 829 | Serial.print ("Going to sleep now for "); 830 | Serial.print (sleepmin); 831 | Serial.print (" Minute(s)."); 832 | ESP.deepSleep(sleepmin * 60 * 1000000); // convert to microseconds 833 | } // end of goToSleep() 834 | -------------------------------------------------------------------------------- /history/Solar-WiFi-Weather-Station-V2.3.ino: -------------------------------------------------------------------------------- 1 | 2 | /*---------------------------------------------------------------------------------------------------- 3 | Project Name : Solar Powered WiFi Weather Station V2.35 4 | Features: temperature, dewpoint, dewpoint spread, heat index, humidity, absolute pressure, relative pressure, battery status and 5 | the famous Zambretti Forecaster (multi lingual) 6 | Authors: Keith Hungerford, Debasish Dutta and Marc Stähli 7 | Website : www.opengreenenergy.com 8 | 9 | Main microcontroller (ESP8266) and BME280 both sleep between measurements 10 | BME280 is used in single shot mode ("forced mode") 11 | 12 | CODE: https://github.com/3KUdelta/Solar_WiFi_Weather_Station 13 | INSTRUCTIONS & HARDWARE: https://www.instructables.com/id/Solar-Powered-WiFi-Weather-Station-V20/ 14 | 3D FILES: https://www.thingiverse.com/thing:3551386 15 | 16 | CREDITS: 17 | 18 | Inspiration and code fragments of Dewpoint and Heatindex calculations are taken from: 19 | https://arduinotronics.blogspot.com/2013/12/temp-humidity-w-dew-point-calcualtions.html 20 | 21 | For Zambretti Ideas: 22 | http://drkfs.net/zambretti.htm or http://integritext.net/DrKFS/zambretti.htm 23 | https://raspberrypiandstuff.wordpress.com 24 | David Bird: https://github.com/G6EJD/ESP32_Weather_Forecaster_TN061 25 | 26 | Needed libraries: 27 | --> Adafruit unified sensor 28 | --> Adafrout BME280 sensor 29 | --> https://github.com/blynkkk/blynk-library 30 | 31 | 32 | "FS.h" 33 | --> https://github.com/aharshac/EasyNTPClient 34 | --> https://github.com/PaulStoffregen/Time.git 35 | 36 | CREDITS for Adafruit libraries: 37 | 38 | This is a library for the BME280 humidity, temperature & pressure sensor 39 | Designed specifically to work with the Adafruit BME280 Breakout 40 | ----> http://www.adafruit.com/products/2650 41 | These sensors use I2C or SPI to communicate, 2 or 4 pins are required 42 | to interface. The device's I2C address is either 0x76 or 0x77. 43 | Adafruit invests time and resources providing this open source code, 44 | please support Adafruit andopen-source hardware by purchasing products 45 | from Adafruit! 46 | Written by Limor Fried & Kevin Townsend for Adafruit Industries. 47 | BSD license, all text above must be included in any redistribution 48 | 49 | Hardware Settings Mac: 50 | LOLIN(WEMOS) D1 mini Pro, 80 MHz, Flash, 16M (14M SPIFFS), v2 Lower Memory, Disable, None, Only Sketch, 921600 on /dev/cu.SLAB_USBtoUART 51 | 52 | major update on 15/05/2019 53 | 54 | -added Zambretti Forecster 55 | -added translation feature 56 | -added English language 57 | -added German language 58 | 59 | updated on 03/06/2019 60 | 61 | -added Dewpoint Spread 62 | -minor code corrections 63 | 64 | updated 28/06/19 65 | -added Italian and Polish tranlation (Chak10) and (TomaszDom) 66 | 67 | updated 27/11/19 to V2.32 68 | -added battery protection at 3.3V, sending "batt empty" message and go to hybernate mode 69 | 70 | updated 11/05/20 to v2.33 71 | -corrected bug in adjustments for summer/winter 72 | 73 | updated 27/05/20 to v2.34 74 | - added August-Roche-Magnus approximation to automatically adjust humidity with temperature corrections 75 | 76 | updated 30/08/22 to v2.35 77 | - corrected Thingspeak issue v2.35 78 | 79 | updated 07/01/23 80 | - updated Blynk settings (if you were using Blynk legacy, please switch to new Blynk) 81 | 82 | 83 | //// Features : ////////////////////////////////////////////////////////////////////////////////////////////////////// 84 | 85 | // 1. Connect to Wi-Fi, and upload the data to either Blynk App and/or Thingspeak 86 | 87 | // 2. Monitoring Weather parameters like Temperature, Pressure abs, Pressure MSL and Humidity. 88 | 89 | // 3. Extra Ports to add more Weather Sensors like UV Index, Light and Rain Guage etc. 90 | 91 | // 4. Remote Battery Status Monitoring 92 | 93 | // 5. Using Sleep mode to reduce the energy consumed 94 | 95 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 96 | 97 | /*************************************************** 98 | VERY IMPORTANT: 99 | * * 100 | Enter your personal settings in Settings.h ! 101 | * * 102 | **************************************************/ 103 | 104 | #include "Settings.h" 105 | #include "Translation.h" 106 | 107 | #include 108 | #include 109 | #include //https://github.com/blynkkk/blynk-library 110 | #include 111 | #include 112 | #include "FS.h" 113 | #include //https://github.com/aharshac/EasyNTPClient 114 | #include //https://github.com/PaulStoffregen/Time.git 115 | #include "ThingSpeak.h" 116 | 117 | Adafruit_BME280 bme; // I2C 118 | WiFiUDP udp; 119 | EasyNTPClient ntpClient(udp, NTP_SERVER, TZ_SEC + DST_SEC); 120 | 121 | float measured_temp; 122 | float measured_humi; 123 | float measured_pres; 124 | float adjusted_temp; 125 | float adjusted_humi; 126 | float SLpressure_hPa; // needed for rel pressure calculation 127 | float HeatIndex; // Heat Index in °C 128 | float volt; 129 | int rel_pressure_rounded; 130 | float DewpointTemperature; 131 | float DewPointSpread; // Difference between actual temperature and dewpoint 132 | 133 | // FORECAST CALCULATION 134 | unsigned long current_timestamp; // Actual timestamp read from NTPtime_t now; 135 | unsigned long saved_timestamp; // Timestamp stored in SPIFFS 136 | 137 | float pressure_value[12]; // Array for the historical pressure values (6 hours, all 30 mins) 138 | // where as pressure_value[0] is always the most recent value 139 | float pressure_difference[12]; // Array to calculate trend with pressure differences 140 | 141 | // FORECAST RESULT 142 | int accuracy; // Counter, if enough values for accurate forecasting 143 | String ZambrettisWords; // Final statement about weather forecast 144 | String trend_in_words; // Trend in words 145 | String forecast_in_words; // Weather forecast in words 146 | String pressure_in_words; // Air pressure in words 147 | String accuracy_in_words; // Zambretti's prediction accuracy in words 148 | 149 | void(* resetFunc) (void) = 0; // declare reset function @ address 0 150 | 151 | WiFiClient client; 152 | 153 | void setup() { 154 | 155 | Serial.begin(115200); 156 | Serial.println(); 157 | Serial.println("Start of SolarWiFiWeatherStation V2.35"); 158 | 159 | //******Battery Voltage Monitoring********************************************* 160 | 161 | // Voltage divider R1 = 220k+100k+220k =540k and R2=100k 162 | float calib_factor = 5.28; // change this value to calibrate the battery voltage 163 | unsigned long raw = analogRead(A0); 164 | volt = raw * calib_factor / 1024; 165 | 166 | Serial.print( "Voltage = "); 167 | Serial.print(volt, 2); // print with 2 decimal places 168 | Serial.println (" V"); 169 | 170 | // **************Application going online********************************** 171 | 172 | WiFi.mode(WIFI_STA); 173 | WiFi.hostname("SolarWeatherStation"); //This changes the hostname of the ESP8266 to display neatly on the network esp on router. 174 | WiFi.begin(ssid, pass); 175 | Serial.print("---> Connecting to WiFi "); 176 | int i = 0; 177 | while (WiFi.status() != WL_CONNECTED) { 178 | delay(500); 179 | i++; 180 | if (i > 20) { 181 | Serial.println("Could not connect to WiFi!"); 182 | Serial.println("Going to sleep for 10 minutes and try again."); 183 | if (volt > 3.3) { 184 | goToSleep(10); // go to sleep and retry after 10 min 185 | } 186 | else { 187 | goToSleep(0); // hybernate because batt empty 188 | } 189 | } 190 | Serial.print("."); 191 | } 192 | Serial.println(" Wifi connected ok"); 193 | 194 | if (App1 == "BLYNK") { 195 | Blynk.begin(auth, ssid, pass); 196 | } 197 | if (App2 == "THINGSPEAK") { 198 | ThingSpeak.begin(client); // Initialize ThingSpeak 199 | } 200 | 201 | //*****************Checking if SPIFFS available******************************** 202 | 203 | Serial.println("SPIFFS Initialization: (First time run can last up to 30 sec - be patient)"); 204 | 205 | boolean mounted = SPIFFS.begin(); // load config if it exists. Otherwise use defaults. 206 | if (!mounted) { 207 | Serial.println("FS not formatted. Doing that now... (can last up to 30 sec)."); 208 | SPIFFS.format(); 209 | Serial.println("FS formatted..."); 210 | SPIFFS.begin(); 211 | } 212 | 213 | //******** GETTING THE TIME FROM NTP SERVER *********************************** 214 | 215 | Serial.println("---> Now reading time from NTP Server"); 216 | int ii = 0; 217 | while (!ntpClient.getUnixTime()) { 218 | delay(100); 219 | ii++; 220 | if (ii > 20) { 221 | Serial.println("Could not connect to NTP Server!"); 222 | Serial.println("Doing a reset now and retry a connection from scratch."); 223 | resetFunc(); 224 | } 225 | Serial.print("."); 226 | } 227 | current_timestamp = ntpClient.getUnixTime(); // get UNIX timestamp (seconds from 1.1.1970 on) 228 | 229 | Serial.print("Current UNIX Timestamp: "); 230 | Serial.println(current_timestamp); 231 | 232 | Serial.print("Time & Date: "); 233 | Serial.print(hour(current_timestamp)); 234 | Serial.print(":"); 235 | Serial.print(minute(current_timestamp)); 236 | Serial.print(":"); 237 | Serial.print(second(current_timestamp)); 238 | Serial.print("; "); 239 | Serial.print(day(current_timestamp)); 240 | Serial.print("."); 241 | Serial.print(month(current_timestamp)); // needed later: month as integer for Zambretti calcualtion 242 | Serial.print("."); 243 | Serial.println(year(current_timestamp)); 244 | 245 | //******** GETTING RELATIVE PRESSURE DATA FROM SENSOR (BME680) ******************** 246 | 247 | bool bme_status; 248 | bme_status = bme.begin(0x76); //address either 0x76 or 0x77 249 | if (!bme_status) { 250 | Serial.println("Could not find a valid BME280 sensor, check wiring!"); 251 | } 252 | 253 | Serial.println("forced mode, 1x temperature / 1x humidity / 1x pressure oversampling,"); 254 | Serial.println("filter off"); 255 | 256 | bme.setSampling(Adafruit_BME280::MODE_FORCED, 257 | Adafruit_BME280::SAMPLING_X1, // temperature 258 | Adafruit_BME280::SAMPLING_X1, // pressure 259 | Adafruit_BME280::SAMPLING_X1, // humidity 260 | Adafruit_BME280::FILTER_OFF ); 261 | 262 | measurementEvent(); //get all data from the different sensors 263 | 264 | //*******************SPIFFS operations*************************************************************** 265 | 266 | ReadFromSPIFFS(); //read stored values and update data if more recent data is available 267 | 268 | Serial.print("Timestamp difference: "); 269 | Serial.println(current_timestamp - saved_timestamp); 270 | 271 | if (current_timestamp - saved_timestamp > 21600) { // last save older than 6 hours -> re-initialize values 272 | FirstTimeRun(); 273 | } 274 | else if (current_timestamp - saved_timestamp > 1800) { // it is time for pressure update (1800 sec = 30 min) 275 | 276 | for (int i = 11; i >= 1; i = i - 1) { 277 | pressure_value[i] = pressure_value[i - 1]; // shifting values one to the right 278 | } 279 | 280 | pressure_value[0] = rel_pressure_rounded; // updating with acutal rel pressure (newest value) 281 | 282 | if (accuracy < 12) { 283 | accuracy = accuracy + 1; // one value more -> accuracy rises (up to 12 = 100%) 284 | } 285 | WriteToSPIFFS(current_timestamp); // update timestamp on storage 286 | } 287 | else { 288 | WriteToSPIFFS(saved_timestamp); // do not update timestamp on storage 289 | } 290 | 291 | //**************************Calculate Zambretti Forecast******************************************* 292 | 293 | int accuracy_in_percent = accuracy * 94 / 12; // 94% is the max predicion accuracy of Zambretti 294 | if ( volt > 3.4 ) { // check if batt is still ok 295 | ZambrettisWords = ZambrettiSays(char(ZambrettiLetter())); 296 | forecast_in_words = TEXT_ZAMBRETTI_FORECAST; 297 | pressure_in_words = TEXT_AIR_PRESSURE; 298 | accuracy_in_words = TEXT_ZAMBRETTI_ACCURACY; 299 | } 300 | else { 301 | ZambrettisWords = ZambrettiSays('0'); // send Message that battery is empty 302 | } 303 | 304 | Serial.println("********************************************************"); 305 | Serial.print(forecast_in_words); 306 | Serial.print(": "); 307 | Serial.println(ZambrettisWords); 308 | Serial.print(pressure_in_words); 309 | Serial.print(": "); 310 | Serial.println(trend_in_words); 311 | Serial.print(accuracy_in_words); 312 | Serial.print(": "); 313 | Serial.print(accuracy_in_percent); 314 | Serial.println("%"); 315 | if (accuracy < 12) { 316 | Serial.println("Reason: Not enough weather data yet."); 317 | Serial.print("We need "); 318 | Serial.print((12 - accuracy) / 2); 319 | Serial.println(" hours more to get sufficient data."); 320 | } 321 | Serial.println("********************************************************"); 322 | 323 | //**************************Sending Data to Blynk and ThingSpeak********************************* 324 | // code block for uploading data to BLYNK App 325 | 326 | if (App1 == "BLYNK") { 327 | Blynk.virtualWrite(V0, adjusted_temp); // virtual pin 0 328 | Blynk.virtualWrite(V1, adjusted_humi); // virtual pin 1 329 | Blynk.virtualWrite(V2, measured_pres); // virtual pin 2 330 | Blynk.virtualWrite(V3, rel_pressure_rounded); // virtual pin 3 331 | Blynk.virtualWrite(V4, volt); // virtual pin 4 332 | Blynk.virtualWrite(V5, DewpointTemperature); // virtual pin 5 333 | Blynk.virtualWrite(V6, HeatIndex); // virtual pin 6 334 | Blynk.virtualWrite(V7, ZambrettisWords); // virtual pin 7 335 | Blynk.virtualWrite(V8, accuracy_in_percent); // virtual pin 8 336 | Blynk.virtualWrite(V9, trend_in_words); // virtual pin 9 337 | Blynk.virtualWrite(V10, DewPointSpread); // virtual pin 10 338 | Serial.println("Data written to Blink ..."); 339 | } 340 | 341 | //******************************************************************************* 342 | // code block for uploading data to Thingspeak website 343 | 344 | if (App2 == "THINGSPEAK") { 345 | 346 | ThingSpeak.setField(1, rel_pressure_rounded); 347 | ThingSpeak.setField(2, adjusted_temp); 348 | ThingSpeak.setField(3, adjusted_humi); 349 | ThingSpeak.setField(4, volt); 350 | ThingSpeak.setField(5, measured_pres); 351 | ThingSpeak.setField(6, DewpointTemperature); 352 | ThingSpeak.setField(7, HeatIndex); 353 | ThingSpeak.setStatus(String(ZambrettisWords + ", " + trend_in_words)); 354 | 355 | int feedback = ThingSpeak.writeFields(ts_ch_id, ts_api_key); 356 | if (feedback == 200) { 357 | Serial.println("Writing to Thingspeak was successful!"); 358 | } 359 | else { 360 | Serial.println("Problem updating Thingspeak. HTTP error code " + String(feedback)); 361 | } 362 | } 363 | if (volt > 3.3) { //check if batt still ok, if yes 364 | goToSleep(sleepTimeMin); //go for a nap 365 | } 366 | else { //if not, 367 | goToSleep(0); //hybernate because batt is empty 368 | } 369 | } // end of void setup() 370 | 371 | void loop() { //loop is not used 372 | } // end of void loop() 373 | 374 | void measurementEvent() { 375 | 376 | //Measures absolute Pressure, Temperature, Humidity, Voltage, calculate relative pressure, 377 | //Dewpoint, Dewpoint Spread, Heat Index 378 | 379 | bme.takeForcedMeasurement(); 380 | 381 | // Get temperature 382 | measured_temp = bme.readTemperature(); 383 | // print on serial monitor 384 | Serial.print("Temp: "); 385 | Serial.print(measured_temp); 386 | Serial.print("°C; "); 387 | 388 | // Get humidity 389 | measured_humi = bme.readHumidity(); 390 | // print on serial monitor 391 | Serial.print("Humidity: "); 392 | Serial.print(measured_humi); 393 | Serial.print("%; "); 394 | 395 | // Get pressure 396 | measured_pres = bme.readPressure() / 100.0F; 397 | // print on serial monitor 398 | Serial.print("Pressure: "); 399 | Serial.print(measured_pres); 400 | Serial.print("hPa; "); 401 | 402 | // Calculate and print relative pressure 403 | SLpressure_hPa = (((measured_pres * 100.0) / pow((1 - ((float)(ELEVATION)) / 44330), 5.255)) / 100.0); 404 | rel_pressure_rounded = (int)(SLpressure_hPa + .5); 405 | // print on serial monitor 406 | Serial.print("Pressure rel: "); 407 | Serial.print(rel_pressure_rounded); 408 | Serial.print("hPa; "); 409 | 410 | // Calculate dewpoint 411 | float a = 17.271; 412 | float b = 237.7; 413 | float tempcalc = (a * measured_temp) / (b + measured_temp) + log(measured_humi * 0.01); 414 | DewpointTemperature = (b * tempcalc) / (a - tempcalc); 415 | Serial.print("Dewpoint: "); 416 | Serial.print(DewpointTemperature); 417 | Serial.println("°C; "); 418 | 419 | // With the dewpoint calculated we can correct temp and automatically calculate humidity 420 | adjusted_temp = measured_temp + TEMP_CORR; 421 | if (adjusted_temp < DewpointTemperature) adjusted_temp = DewpointTemperature; //compensation, if offset too high 422 | //August-Roche-Magnus approximation (http://bmcnoldy.rsmas.miami.edu/Humidity.html) 423 | adjusted_humi = 100 * (exp((a * DewpointTemperature) / (b + DewpointTemperature)) / exp((a * adjusted_temp) / (b + adjusted_temp))); 424 | if (adjusted_humi > 100) adjusted_humi = 100; // just in case 425 | // print on serial monitor 426 | Serial.print("Temp adjusted: "); 427 | Serial.print(adjusted_temp); 428 | Serial.print("°C; "); 429 | Serial.print("Humidity adjusted: "); 430 | Serial.print(adjusted_humi); 431 | Serial.print("%; "); 432 | 433 | // Calculate dewpoint spread (difference between actual temp and dewpoint -> the smaller the number: rain or fog 434 | 435 | DewPointSpread = measured_temp - DewpointTemperature; 436 | Serial.print("Dewpoint Spread: "); 437 | Serial.print(DewPointSpread); 438 | Serial.println("°C; "); 439 | 440 | // Calculate HI (heatindex in °C) --> HI starts working above 26,7 °C 441 | if (measured_temp > 26.7) { 442 | double c1 = -8.784, c2 = 1.611, c3 = 2.338, c4 = -0.146, c5 = -1.230e-2, c6 = -1.642e-2, c7 = 2.211e-3, c8 = 7.254e-4, c9 = -2.582e-6 ; 443 | double T = measured_temp; 444 | double R = measured_humi; 445 | 446 | double A = (( c5 * T) + c2) * T + c1; 447 | double B = ((c7 * T) + c4) * T + c3; 448 | double C = ((c9 * T) + c8) * T + c6; 449 | HeatIndex = (C * R + B) * R + A; 450 | } 451 | else { 452 | HeatIndex = measured_temp; 453 | Serial.println("Not warm enough (less than 26.7 °C) for Heatindex"); 454 | } 455 | Serial.print("HeatIndex: "); 456 | Serial.print(HeatIndex); 457 | Serial.print("°C; "); 458 | 459 | } // end of void measurementEvent() 460 | 461 | int CalculateTrend() { 462 | int trend; // -1 falling; 0 steady; 1 raising 463 | Serial.println("---> Calculating trend"); 464 | 465 | //--> giving the most recent pressure reads more weight 466 | pressure_difference[0] = (pressure_value[0] - pressure_value[1]) * 1.5; 467 | pressure_difference[1] = (pressure_value[0] - pressure_value[2]); 468 | pressure_difference[2] = (pressure_value[0] - pressure_value[3]) / 1.5; 469 | pressure_difference[3] = (pressure_value[0] - pressure_value[4]) / 2; 470 | pressure_difference[4] = (pressure_value[0] - pressure_value[5]) / 2.5; 471 | pressure_difference[5] = (pressure_value[0] - pressure_value[6]) / 3; 472 | pressure_difference[6] = (pressure_value[0] - pressure_value[7]) / 3.5; 473 | pressure_difference[7] = (pressure_value[0] - pressure_value[8]) / 4; 474 | pressure_difference[8] = (pressure_value[0] - pressure_value[9]) / 4.5; 475 | pressure_difference[9] = (pressure_value[0] - pressure_value[10]) / 5; 476 | pressure_difference[10] = (pressure_value[0] - pressure_value[11]) / 5.5; 477 | 478 | //--> calculating the average and storing it into [11] 479 | pressure_difference[11] = ( pressure_difference[0] 480 | + pressure_difference[1] 481 | + pressure_difference[2] 482 | + pressure_difference[3] 483 | + pressure_difference[4] 484 | + pressure_difference[5] 485 | + pressure_difference[6] 486 | + pressure_difference[7] 487 | + pressure_difference[8] 488 | + pressure_difference[9] 489 | + pressure_difference[10]) / 11; 490 | 491 | Serial.print("Current trend: "); 492 | Serial.println(pressure_difference[11]); 493 | 494 | if (pressure_difference[11] > 3.5) { 495 | trend_in_words = TEXT_RISING_FAST; 496 | trend = 1; 497 | } 498 | else if (pressure_difference[11] > 1.5 && pressure_difference[11] <= 3.5) { 499 | trend_in_words = TEXT_RISING; 500 | trend = 1; 501 | } 502 | else if (pressure_difference[11] > 0.25 && pressure_difference[11] <= 1.5) { 503 | trend_in_words = TEXT_RISING_SLOW; 504 | trend = 1; 505 | } 506 | else if (pressure_difference[11] > -0.25 && pressure_difference[11] < 0.25) { 507 | trend_in_words = TEXT_STEADY; 508 | trend = 0; 509 | } 510 | else if (pressure_difference[11] >= -1.5 && pressure_difference[11] < -0.25) { 511 | trend_in_words = TEXT_FALLING_SLOW; 512 | trend = -1; 513 | } 514 | else if (pressure_difference[11] >= -3.5 && pressure_difference[11] < -1.5) { 515 | trend_in_words = TEXT_FALLING; 516 | trend = -1; 517 | } 518 | else if (pressure_difference[11] <= -3.5) { 519 | trend_in_words = TEXT_FALLING_FAST; 520 | trend = -1; 521 | } 522 | 523 | Serial.println(trend_in_words); 524 | return trend; 525 | } 526 | 527 | char ZambrettiLetter() { 528 | Serial.println("---> Calculating Zambretti letter"); 529 | char z_letter; 530 | int(z_trend) = CalculateTrend(); 531 | // Case trend is falling 532 | if (z_trend == -1) { 533 | float zambretti = 0.0009746 * rel_pressure_rounded * rel_pressure_rounded - 2.1068 * rel_pressure_rounded + 1138.7019; 534 | //A Winter falling generally results in a Z value lower by 1 unit 535 | if (month(current_timestamp) < 4 || month(current_timestamp) > 9) zambretti = zambretti + 1; 536 | if (zambretti > 9) zambretti = 9; 537 | Serial.print("Calculated and rounded Zambretti in numbers: "); 538 | Serial.println(round(zambretti)); 539 | switch (int(round(zambretti))) { 540 | case 0: z_letter = 'A'; break; //Settled Fine 541 | case 1: z_letter = 'A'; break; //Settled Fine 542 | case 2: z_letter = 'B'; break; //Fine Weather 543 | case 3: z_letter = 'D'; break; //Fine Becoming Less Settled 544 | case 4: z_letter = 'H'; break; //Fairly Fine Showers Later 545 | case 5: z_letter = 'O'; break; //Showery Becoming unsettled 546 | case 6: z_letter = 'R'; break; //Unsettled, Rain later 547 | case 7: z_letter = 'U'; break; //Rain at times, worse later 548 | case 8: z_letter = 'V'; break; //Rain at times, becoming very unsettled 549 | case 9: z_letter = 'X'; break; //Very Unsettled, Rain 550 | } 551 | } 552 | // Case trend is steady 553 | if (z_trend == 0) { 554 | float zambretti = 138.24 - 0.133 * rel_pressure_rounded; 555 | Serial.print("Calculated and rounded Zambretti in numbers: "); 556 | Serial.println(round(zambretti)); 557 | switch (int(round(zambretti))) { 558 | case 0: z_letter = 'A'; break; //Settled Fine 559 | case 1: z_letter = 'A'; break; //Settled Fine 560 | case 2: z_letter = 'B'; break; //Fine Weather 561 | case 3: z_letter = 'E'; break; //Fine, Possibly showers 562 | case 4: z_letter = 'K'; break; //Fairly Fine, Showers likely 563 | case 5: z_letter = 'N'; break; //Showery Bright Intervals 564 | case 6: z_letter = 'P'; break; //Changeable some rain 565 | case 7: z_letter = 'S'; break; //Unsettled, rain at times 566 | case 8: z_letter = 'W'; break; //Rain at Frequent Intervals 567 | case 9: z_letter = 'X'; break; //Very Unsettled, Rain 568 | case 10: z_letter = 'Z'; break; //Stormy, much rain 569 | } 570 | } 571 | // Case trend is rising 572 | if (z_trend == 1) { 573 | float zambretti = 142.57 - 0.1376 * rel_pressure_rounded; 574 | //A Summer rising, improves the prospects by 1 unit over a Winter rising 575 | if (month(current_timestamp) >= 4 && month(current_timestamp) <= 9) zambretti = zambretti - 1; 576 | if (zambretti < 0) zambretti = 0; 577 | Serial.print("Calculated and rounded Zambretti in numbers: "); 578 | Serial.println(round(zambretti)); 579 | switch (int(round(zambretti))) { 580 | case 0: z_letter = 'A'; break; //Settled Fine 581 | case 1: z_letter = 'A'; break; //Settled Fine 582 | case 2: z_letter = 'B'; break; //Fine Weather 583 | case 3: z_letter = 'C'; break; //Becoming Fine 584 | case 4: z_letter = 'F'; break; //Fairly Fine, Improving 585 | case 5: z_letter = 'G'; break; //Fairly Fine, Possibly showers, early 586 | case 6: z_letter = 'I'; break; //Showery Early, Improving 587 | case 7: z_letter = 'J'; break; //Changeable, Improving 588 | case 8: z_letter = 'L'; break; //Rather Unsettled Clearing Later 589 | case 9: z_letter = 'M'; break; //Unsettled, Probably Improving 590 | case 10: z_letter = 'Q'; break; //Unsettled, short fine Intervals 591 | case 11: z_letter = 'T'; break; //Very Unsettled, Finer at times 592 | case 12: z_letter = 'Y'; break; //Stormy, possibly improving 593 | case 13: z_letter = 'Z'; break;; //Stormy, much rain 594 | } 595 | } 596 | Serial.print("This is Zambretti's famous letter: "); 597 | Serial.println(z_letter); 598 | return z_letter; 599 | } 600 | 601 | String ZambrettiSays(char code) { 602 | String zambrettis_words = ""; 603 | switch (code) { 604 | case 'A': zambrettis_words = TEXT_ZAMBRETTI_A; break; //see Tranlation.h 605 | case 'B': zambrettis_words = TEXT_ZAMBRETTI_B; break; 606 | case 'C': zambrettis_words = TEXT_ZAMBRETTI_C; break; 607 | case 'D': zambrettis_words = TEXT_ZAMBRETTI_D; break; 608 | case 'E': zambrettis_words = TEXT_ZAMBRETTI_E; break; 609 | case 'F': zambrettis_words = TEXT_ZAMBRETTI_F; break; 610 | case 'G': zambrettis_words = TEXT_ZAMBRETTI_G; break; 611 | case 'H': zambrettis_words = TEXT_ZAMBRETTI_H; break; 612 | case 'I': zambrettis_words = TEXT_ZAMBRETTI_I; break; 613 | case 'J': zambrettis_words = TEXT_ZAMBRETTI_J; break; 614 | case 'K': zambrettis_words = TEXT_ZAMBRETTI_K; break; 615 | case 'L': zambrettis_words = TEXT_ZAMBRETTI_L; break; 616 | case 'M': zambrettis_words = TEXT_ZAMBRETTI_M; break; 617 | case 'N': zambrettis_words = TEXT_ZAMBRETTI_N; break; 618 | case 'O': zambrettis_words = TEXT_ZAMBRETTI_O; break; 619 | case 'P': zambrettis_words = TEXT_ZAMBRETTI_P; break; 620 | case 'Q': zambrettis_words = TEXT_ZAMBRETTI_Q; break; 621 | case 'R': zambrettis_words = TEXT_ZAMBRETTI_R; break; 622 | case 'S': zambrettis_words = TEXT_ZAMBRETTI_S; break; 623 | case 'T': zambrettis_words = TEXT_ZAMBRETTI_T; break; 624 | case 'U': zambrettis_words = TEXT_ZAMBRETTI_U; break; 625 | case 'V': zambrettis_words = TEXT_ZAMBRETTI_V; break; 626 | case 'W': zambrettis_words = TEXT_ZAMBRETTI_W; break; 627 | case 'X': zambrettis_words = TEXT_ZAMBRETTI_X; break; 628 | case 'Y': zambrettis_words = TEXT_ZAMBRETTI_Y; break; 629 | case 'Z': zambrettis_words = TEXT_ZAMBRETTI_Z; break; 630 | case '0': zambrettis_words = TEXT_ZAMBRETTI_0; break; 631 | default: zambrettis_words = TEXT_ZAMBRETTI_DEFAULT; break; 632 | } 633 | return zambrettis_words; 634 | } 635 | 636 | void ReadFromSPIFFS() { 637 | char filename [] = "/data.txt"; 638 | File myDataFile = SPIFFS.open(filename, "r"); // Open file for reading 639 | if (!myDataFile) { 640 | Serial.println("Failed to open file"); 641 | FirstTimeRun(); // no file there -> initializing 642 | } 643 | 644 | Serial.println("---> Now reading from SPIFFS"); 645 | 646 | String temp_data; 647 | 648 | temp_data = myDataFile.readStringUntil('\n'); 649 | saved_timestamp = temp_data.toInt(); 650 | Serial.print("Timestamp from SPIFFS: "); Serial.println(saved_timestamp); 651 | 652 | temp_data = myDataFile.readStringUntil('\n'); 653 | accuracy = temp_data.toInt(); 654 | Serial.print("Accuracy value read from SPIFFS: "); Serial.println(accuracy); 655 | 656 | Serial.print("Last 12 saved pressure values: "); 657 | for (int i = 0; i <= 11; i++) { 658 | temp_data = myDataFile.readStringUntil('\n'); 659 | pressure_value[i] = temp_data.toInt(); 660 | Serial.print(pressure_value[i]); 661 | Serial.print("; "); 662 | } 663 | myDataFile.close(); 664 | Serial.println(); 665 | } 666 | 667 | void WriteToSPIFFS(int write_timestamp) { 668 | char filename [] = "/data.txt"; 669 | File myDataFile = SPIFFS.open(filename, "w"); // Open file for writing (appending) 670 | if (!myDataFile) { 671 | Serial.println("Failed to open file"); 672 | } 673 | 674 | Serial.println("---> Now writing to SPIFFS"); 675 | 676 | myDataFile.println(write_timestamp); // Saving timestamp to /data.txt 677 | myDataFile.println(accuracy); // Saving accuracy value to /data.txt 678 | 679 | for ( int i = 0; i <= 11; i++) { 680 | myDataFile.println(pressure_value[i]); // Filling pressure array with updated values 681 | } 682 | myDataFile.close(); 683 | 684 | Serial.println("File written. Now reading file again."); 685 | myDataFile = SPIFFS.open(filename, "r"); // Open file for reading 686 | Serial.print("Found in /data.txt = "); 687 | while (myDataFile.available()) { 688 | Serial.print(myDataFile.readStringUntil('\n')); 689 | Serial.print("; "); 690 | } 691 | Serial.println(); 692 | myDataFile.close(); 693 | } 694 | 695 | void FirstTimeRun() { 696 | Serial.println("---> Starting initializing process."); 697 | accuracy = 1; 698 | char filename [] = "/data.txt"; 699 | File myDataFile = SPIFFS.open(filename, "w"); // Open a file for writing 700 | if (!myDataFile) { 701 | Serial.println("Failed to open file"); 702 | Serial.println("Stopping process - maybe flash size not set (SPIFFS)."); 703 | exit(0); 704 | } 705 | myDataFile.println(current_timestamp); // Saving timestamp to /data.txt 706 | myDataFile.println(accuracy); // Saving accuracy value to /data.txt 707 | for ( int i = 0; i < 12; i++) { 708 | myDataFile.println(rel_pressure_rounded); // Filling pressure array with current pressure 709 | } 710 | Serial.println("** Saved initial pressure data. **"); 711 | myDataFile.close(); 712 | Serial.println("---> Doing a reset now."); 713 | resetFunc(); //call reset 714 | } 715 | 716 | void goToSleep(unsigned int sleepmin) { 717 | 718 | Serial.println("INFO: Closing the Wifi connection"); 719 | WiFi.disconnect(); 720 | 721 | while (client.connected() || (WiFi.status() == WL_CONNECTED)) { 722 | Serial.println("Waiting for shutdown before sleeping"); 723 | delay(10); 724 | } 725 | delay(50); 726 | 727 | Serial.print ("Going to sleep now for "); 728 | Serial.print (sleepmin); 729 | Serial.print (" Minute(s)."); 730 | ESP.deepSleep(sleepmin * 60 * 1000000); // convert to microseconds 731 | } // end of goToSleep() 732 | -------------------------------------------------------------------------------- /history/Translation.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------------------------- 2 | Project Name : Solar Powered WiFi Weather Station V2.35 3 | Features: temperature, dewpoint, dewpoint spread, heat index, humidity, absolute pressure, relative pressure, battery status and 4 | the famous Zambretti Forecaster (multi lingual) 5 | Authors: Keith Hungerford, Debasish Dutta and Marc Stähli 6 | Website : www.opengreenenergy.com 7 | 8 | ******* Transaltion tables ****************************************/ 9 | 10 | #if LANGUAGE == 'EN' 11 | const char TEXT_AIR_PRESSURE[] = "Pressure"; 12 | const char TEXT_RISING_FAST[] = "Rising fast"; 13 | const char TEXT_RISING[] = "Rising"; 14 | const char TEXT_RISING_SLOW[] = "Rising slow"; 15 | const char TEXT_STEADY[] = "Steady"; 16 | const char TEXT_FALLING_SLOW[] = "Falling slow"; 17 | const char TEXT_FALLING[] = "Falling"; 18 | const char TEXT_FALLING_FAST[] = "Falling fast"; 19 | 20 | const char TEXT_ZAMBRETTI_FORECAST[]= "Forecast"; 21 | const char TEXT_ZAMBRETTI_ACCURACY[]= "Prediction accuracy"; 22 | const char TEXT_ZAMBRETTI_A[] = "Settled fine weather"; 23 | const char TEXT_ZAMBRETTI_B[] = "Fine weather"; 24 | const char TEXT_ZAMBRETTI_C[] = "Becoming fine"; 25 | const char TEXT_ZAMBRETTI_D[] = "Fine, becoming less settled"; 26 | const char TEXT_ZAMBRETTI_E[] = "Fine, possibly showers"; 27 | const char TEXT_ZAMBRETTI_F[] = "Fairly fine, improving"; 28 | const char TEXT_ZAMBRETTI_G[] = "Fairly fine, possibly showers early"; 29 | const char TEXT_ZAMBRETTI_H[] = "Fairly fine, showers later"; 30 | const char TEXT_ZAMBRETTI_I[] = "Showery early, improving"; 31 | const char TEXT_ZAMBRETTI_J[] = "Changeable, improving"; 32 | const char TEXT_ZAMBRETTI_K[] = "Fairly fine, showers likely"; 33 | const char TEXT_ZAMBRETTI_L[] = "Rather unsettled, clearing later"; 34 | const char TEXT_ZAMBRETTI_M[] = "Unsettled, probably improving"; 35 | const char TEXT_ZAMBRETTI_N[] = "Showery, bright intervals"; 36 | const char TEXT_ZAMBRETTI_O[] = "Showery, becoming unsettled"; 37 | const char TEXT_ZAMBRETTI_P[] = "Changeable, some rain"; 38 | const char TEXT_ZAMBRETTI_Q[] = "Unsettled, short fine intervals"; 39 | const char TEXT_ZAMBRETTI_R[] = "Unsettled, rain later"; 40 | const char TEXT_ZAMBRETTI_S[] = "Unsettled, rain at times"; 41 | const char TEXT_ZAMBRETTI_T[] = "Very unsettled, finer at times"; 42 | const char TEXT_ZAMBRETTI_U[] = "Rain at times, worse later"; 43 | const char TEXT_ZAMBRETTI_V[] = "Rain at times, becoming very unsettled"; 44 | const char TEXT_ZAMBRETTI_W[] = "Rain at frequent intervals"; 45 | const char TEXT_ZAMBRETTI_X[] = "Very unsettled, rain"; 46 | const char TEXT_ZAMBRETTI_Y[] = "Stormy, possibly improving"; 47 | const char TEXT_ZAMBRETTI_Z[] = "Stormy, much rain"; 48 | const char TEXT_ZAMBRETTI_0[] = "Battery empty, please recharge!"; 49 | const char TEXT_ZAMBRETTI_DEFAULT[] = "Sorry, no forecast at the moment"; 50 | 51 | #elif LANGUAGE == 'DE' 52 | const char TEXT_AIR_PRESSURE[] = "Luftdruck"; 53 | const char TEXT_RISING_FAST[] = "rasch steigend"; 54 | const char TEXT_RISING[] = "steigend"; 55 | const char TEXT_RISING_SLOW[] = "langsam steigend"; 56 | const char TEXT_STEADY[] = "beständig"; 57 | const char TEXT_FALLING_SLOW[] = "langsam fallend"; 58 | const char TEXT_FALLING[] = "fallend"; 59 | const char TEXT_FALLING_FAST[] = "rasch fallend"; 60 | 61 | const char TEXT_ZAMBRETTI_FORECAST[]= "Wettervorhersage"; 62 | const char TEXT_ZAMBRETTI_ACCURACY[]= "Vorhersagegenauigkeit"; 63 | const char TEXT_ZAMBRETTI_A[] = "Beständiges Schönwetter"; 64 | const char TEXT_ZAMBRETTI_B[] = "Schönes Wetter"; 65 | const char TEXT_ZAMBRETTI_C[] = "Wetter wird gut"; 66 | const char TEXT_ZAMBRETTI_D[] = "Schön, wird wechselhaft"; 67 | const char TEXT_ZAMBRETTI_E[] = "Schön, Regenschauer möglich"; 68 | const char TEXT_ZAMBRETTI_F[] = "Ziemlich gut, verbessert sich"; 69 | const char TEXT_ZAMBRETTI_G[] = "Ziemlich gut, frühe Regenschauer möglich"; 70 | const char TEXT_ZAMBRETTI_H[] = "Ziemlich gut, spätere Regenschauer"; 71 | const char TEXT_ZAMBRETTI_I[] = "Früh schauerhaft, verbessert sich"; 72 | const char TEXT_ZAMBRETTI_J[] = "Wechselhaft, verbessert sich"; 73 | const char TEXT_ZAMBRETTI_K[] = "Ziemlich gut, Regenschauer möglich"; 74 | const char TEXT_ZAMBRETTI_L[] = "Eher veränderlich, klart später auf"; 75 | const char TEXT_ZAMBRETTI_M[] = "Veränderlich, verbessert sich wahrscheinlich"; 76 | const char TEXT_ZAMBRETTI_N[] = "Regnerisch mit Aufhellungen"; 77 | const char TEXT_ZAMBRETTI_O[] = "Regnerisch, wird veränderlich"; 78 | const char TEXT_ZAMBRETTI_P[] = "Veränderlich mit wenig Regen"; 79 | const char TEXT_ZAMBRETTI_Q[] = "Veränderlich, mit kurzen schönen Intervallen"; 80 | const char TEXT_ZAMBRETTI_R[] = "Veränderlich, später Regen"; 81 | const char TEXT_ZAMBRETTI_S[] = "Veränderlich, zeitweise Regen"; 82 | const char TEXT_ZAMBRETTI_T[] = "Stark wechselnd, zeitweise schöner"; 83 | const char TEXT_ZAMBRETTI_U[] = "Zeitweise Regen, verschlechtert sich"; 84 | const char TEXT_ZAMBRETTI_V[] = "Zeitweise Regen, wird sehr unruhig"; 85 | const char TEXT_ZAMBRETTI_W[] = "Regen in regelmässigen Abständen"; 86 | const char TEXT_ZAMBRETTI_X[] = "Sehr veränderlich, Regen"; 87 | const char TEXT_ZAMBRETTI_Y[] = "Stürmisch, verbessert sich wahrscheinlich"; 88 | const char TEXT_ZAMBRETTI_Z[] = "Stürmisch, viel Regen"; 89 | const char TEXT_ZAMBRETTI_0[] = "Batterie leer, bitte nachladen!"; 90 | const char TEXT_ZAMBRETTI_DEFAULT[] = "Im Moment keine Prognose möglich"; 91 | 92 | #elif LANGUAGE == 'FR' 93 | const char TEXT_AIR_PRESSURE[] = "Pression"; 94 | const char TEXT_RISING_FAST[] = "en hausse rapide"; 95 | const char TEXT_RISING[] = "en hausse"; 96 | const char TEXT_RISING_SLOW[] = "en faible hausse"; 97 | const char TEXT_STEADY[] = "constante"; 98 | const char TEXT_FALLING_SLOW[] = "en faible baisse"; 99 | const char TEXT_FALLING[] = "en baisse"; 100 | const char TEXT_FALLING_FAST[] = "en baisse rapide"; 101 | 102 | const char TEXT_ZAMBRETTI_FORECAST[]= "Prévisions"; 103 | const char TEXT_ZAMBRETTI_ACCURACY[]= "Précision des prévisions"; 104 | const char TEXT_ZAMBRETTI_A[] = "Beau temps installé"; 105 | const char TEXT_ZAMBRETTI_B[] = "Beau temps"; 106 | const char TEXT_ZAMBRETTI_C[] = "Amélioration de la météo"; 107 | const char TEXT_ZAMBRETTI_D[] = "Beau temps, deviens changeant"; 108 | const char TEXT_ZAMBRETTI_E[] = "Beau temps, Possibilité d'averses"; 109 | const char TEXT_ZAMBRETTI_F[] = "Assez bon, en amélioration"; 110 | const char TEXT_ZAMBRETTI_G[] = "Assez bon, des averses précoces sont possibles"; 111 | const char TEXT_ZAMBRETTI_H[] = "Assez bon, des averses plus tard"; 112 | const char TEXT_ZAMBRETTI_I[] = "Pluie précoce, amélioration"; 113 | const char TEXT_ZAMBRETTI_J[] = "Changeant s'améliore"; 114 | const char TEXT_ZAMBRETTI_K[] = "Assez bon, averses probables"; 115 | const char TEXT_ZAMBRETTI_L[] = "Plutôt variable, s'éclaircira plus tard"; 116 | const char TEXT_ZAMBRETTI_M[] = "Instable, probablement en amélioration"; 117 | const char TEXT_ZAMBRETTI_N[] = "Pluie avec éclaircissement"; 118 | const char TEXT_ZAMBRETTI_O[] = "Pluvieux, devient instable"; 119 | const char TEXT_ZAMBRETTI_P[] = "Variable avec peu d'averses"; 120 | const char TEXT_ZAMBRETTI_Q[] = "Instable, avec quelques intervalles de beau"; 121 | const char TEXT_ZAMBRETTI_R[] = "Instable, averses plus tard"; 122 | const char TEXT_ZAMBRETTI_S[] = "Instable, averses occasionnelles"; 123 | const char TEXT_ZAMBRETTI_T[] = "Très Instable, parfois plus beau"; 124 | const char TEXT_ZAMBRETTI_U[] = "Averses par moments, plus mauvais plus tard"; 125 | const char TEXT_ZAMBRETTI_V[] = "Averses par moments, devient très instable"; 126 | const char TEXT_ZAMBRETTI_W[] = "Averses à intervalles fréquents"; 127 | const char TEXT_ZAMBRETTI_X[] = "Très instable, Pluie"; 128 | const char TEXT_ZAMBRETTI_Y[] = "Orageux, possible amélioration"; 129 | const char TEXT_ZAMBRETTI_Z[] = "Orageux, beaucoup de pluie"; 130 | const char TEXT_ZAMBRETTI_0[] = "Batterie vide, veuillez recharger!"; 131 | const char TEXT_ZAMBRETTI_DEFAULT[] = "Désolé, aucune prévision possible pour le moment"; 132 | 133 | #elif LANGUAGE == 'IT' 134 | const char TEXT_AIR_PRESSURE[] = "Pressione atmosferica"; 135 | const char TEXT_RISING_FAST[] = "in rapido aumento"; 136 | const char TEXT_RISING[] = "in aumento"; 137 | const char TEXT_RISING_SLOW[] = "in lento aumento"; 138 | const char TEXT_STEADY[] = "stabile"; 139 | const char TEXT_FALLING_SLOW[] = "in lenta discesa"; 140 | const char TEXT_FALLING[] = "in discesa"; 141 | const char TEXT_FALLING_FAST[] = "in rapida discesa"; 142 | 143 | const char TEXT_ZAMBRETTI_FORECAST[]= "Previsioni meteo"; 144 | const char TEXT_ZAMBRETTI_ACCURACY[]= "Precisione"; 145 | const char TEXT_ZAMBRETTI_A[] = "Bel tempo stabile"; 146 | const char TEXT_ZAMBRETTI_B[] = "Bel tempo"; 147 | const char TEXT_ZAMBRETTI_C[] = "In miglioramento"; 148 | const char TEXT_ZAMBRETTI_D[] = "Bel tempo con possibile peggioramento"; 149 | const char TEXT_ZAMBRETTI_E[] = "Bel tempo con possibili rovesci"; 150 | const char TEXT_ZAMBRETTI_F[] = "Tempo abbastanza buono, in miglioramento"; 151 | const char TEXT_ZAMBRETTI_G[] = "Tempo abbastanza buono ma con possibili piogge a breve"; 152 | const char TEXT_ZAMBRETTI_H[] = "Tempo abbastanza buono ma con piogge a breve"; 153 | const char TEXT_ZAMBRETTI_I[] = "Piogge a breve ma in miglioramento"; 154 | const char TEXT_ZAMBRETTI_J[] = "Variabile"; 155 | const char TEXT_ZAMBRETTI_K[] = "Tempo abbastanza buono con probabili piogge"; 156 | const char TEXT_ZAMBRETTI_L[] = "Tempo incerto con schiarite a breve"; 157 | const char TEXT_ZAMBRETTI_M[] = "Tempo incerto con probabili miglioramenti"; 158 | const char TEXT_ZAMBRETTI_N[] = "Piovoso con schiarite ad intervalli"; 159 | const char TEXT_ZAMBRETTI_O[] = "Uggioso ed incerto"; 160 | const char TEXT_ZAMBRETTI_P[] = "Variabile con rovesci di piogge"; 161 | const char TEXT_ZAMBRETTI_Q[] = "Incerto con brevi intervalli di bel tempo"; 162 | const char TEXT_ZAMBRETTI_R[] = "Incerto con possibili rovesci a breve"; 163 | const char TEXT_ZAMBRETTI_S[] = "Incerto con piaggia a tratti"; 164 | const char TEXT_ZAMBRETTI_T[] = "Molto incerto con sprazzi di bel tempo"; 165 | const char TEXT_ZAMBRETTI_U[] = "Pioggia a tratti in peggioramento"; 166 | const char TEXT_ZAMBRETTI_V[] = "Pioggia a tratti con forte instabilità"; 167 | const char TEXT_ZAMBRETTI_W[] = "Pioggia a intervalli frequenti"; 168 | const char TEXT_ZAMBRETTI_X[] = "Molto instabile, Pioggia"; 169 | const char TEXT_ZAMBRETTI_Y[] = "Tempestoso con possibili miglioramenti"; 170 | const char TEXT_ZAMBRETTI_Z[] = "Tempestoso e moto piovoso"; 171 | const char TEXT_ZAMBRETTI_0[] = "Batteria scarica, si prega di ricaricare!"; 172 | const char TEXT_ZAMBRETTI_DEFAULT[] = "Mi dispiace, nessuna previsione disponibile al momento"; 173 | 174 | #elif LANGUAGE == 'PL' 175 | const char TEXT_AIR_PRESSURE[] = "Ciśnienie"; 176 | const char TEXT_RISING_FAST[] = "szybko rośnie"; 177 | const char TEXT_RISING[] = "rośnie"; 178 | const char TEXT_RISING_SLOW[] = "powoli rośnie"; 179 | const char TEXT_STEADY[] = "stałe"; 180 | const char TEXT_FALLING_SLOW[] = "powoli spada"; 181 | const char TEXT_FALLING[] = "spada"; 182 | const char TEXT_FALLING_FAST[] = "szybko spada"; 183 | 184 | const char TEXT_ZAMBRETTI_FORECAST[]= "Prognoza"; 185 | const char TEXT_ZAMBRETTI_ACCURACY[]= "Dokładność"; 186 | const char TEXT_ZAMBRETTI_A[] = "Dobra pogoda, stabilna"; 187 | const char TEXT_ZAMBRETTI_B[] = "Dobra pogoda"; 188 | const char TEXT_ZAMBRETTI_C[] = "Poprawia się"; 189 | const char TEXT_ZAMBRETTI_D[] = "Dobra, pogarsza się"; 190 | const char TEXT_ZAMBRETTI_E[] = "Dobra, możliwy deszcz"; 191 | const char TEXT_ZAMBRETTI_F[] = "Całkiem dobra, polepsza się"; 192 | const char TEXT_ZAMBRETTI_G[] = "Całkiem dobra, wkrótce możliwe deszcze"; 193 | const char TEXT_ZAMBRETTI_H[] = "Całkiem dobra, później deszcze"; 194 | const char TEXT_ZAMBRETTI_I[] = "Wkrótce deszczowo, polepsza się"; 195 | const char TEXT_ZAMBRETTI_J[] = "Zmienna, polepsza się"; 196 | const char TEXT_ZAMBRETTI_K[] = "Całkiem dobra, możliwe deszcze"; 197 | const char TEXT_ZAMBRETTI_L[] = "Raczej niepewna, później rozpogodzi się"; 198 | const char TEXT_ZAMBRETTI_M[] = "Niepewna, możliwe rozpogodzenia"; 199 | const char TEXT_ZAMBRETTI_N[] = "Deszcze z przejaśnieniami"; 200 | const char TEXT_ZAMBRETTI_O[] = "Deszczowo, staje się niepewna"; 201 | const char TEXT_ZAMBRETTI_P[] = "Zmienna, niewielkie opady"; 202 | const char TEXT_ZAMBRETTI_Q[] = "Niepewna, krótkie przejaśnienia"; 203 | const char TEXT_ZAMBRETTI_R[] = "Niepewna, później deszcz"; 204 | const char TEXT_ZAMBRETTI_S[] = "Niepewna, okresowe opady"; 205 | const char TEXT_ZAMBRETTI_T[] = "Bardzo niepewna, z przejaśnieniami"; 206 | const char TEXT_ZAMBRETTI_U[] = "Okresowe opady, później się pogorszy"; 207 | const char TEXT_ZAMBRETTI_V[] = "Okresowe opady, staje się bardzo niepewna"; 208 | const char TEXT_ZAMBRETTI_W[] = "Częste opady"; 209 | const char TEXT_ZAMBRETTI_X[] = "Bardzo niepewna, deszcz"; 210 | const char TEXT_ZAMBRETTI_Y[] = "Burzowa, możliwe polepszenie"; 211 | const char TEXT_ZAMBRETTI_Z[] = "Burzowa, duże opady deszczu"; 212 | const char TEXT_ZAMBRETTI_0[] = "Bateria rozładowana, proszę naładować!"; 213 | const char TEXT_ZAMBRETTI_DEFAULT[] = "Obecnie brak prognozy"; 214 | 215 | #elif LANGUAGE == 'RO' 216 | const char TEXT_AIR_PRESSURE[] = "Presiune"; 217 | const char TEXT_RISING_FAST[] = "crestere rapida"; 218 | const char TEXT_RISING[] = "crestere"; 219 | const char TEXT_RISING_SLOW[] = "crestere usoara"; 220 | const char TEXT_STEADY[] = "stabila"; 221 | const char TEXT_FALLING_SLOW[] = "scadere usoara"; 222 | const char TEXT_FALLING[] = "scadere"; 223 | const char TEXT_FALLING_FAST[] = "scadere rapida"; 224 | 225 | const char TEXT_ZAMBRETTI_FORECAST[]= "Prognoza"; 226 | const char TEXT_ZAMBRETTI_ACCURACY[]= "Acuratetea prognozei"; 227 | const char TEXT_ZAMBRETTI_A[] = "Stabil Vreme buna"; 228 | const char TEXT_ZAMBRETTI_B[] = "Vreme buna"; 229 | const char TEXT_ZAMBRETTI_C[] = "Devine buna"; 230 | const char TEXT_ZAMBRETTI_D[] = "Buna, devine instabila"; 231 | const char TEXT_ZAMBRETTI_E[] = "Buna, posibile averse"; 232 | const char TEXT_ZAMBRETTI_F[] = "Destul de buna, Se imbunatateste"; 233 | const char TEXT_ZAMBRETTI_G[] = "Destul de buna, Posibile averse in curand"; 234 | const char TEXT_ZAMBRETTI_H[] = "Destul de buna, Posibile averse mai tarziu"; 235 | const char TEXT_ZAMBRETTI_I[] = "Averse in curand, Se imbunatateste"; 236 | const char TEXT_ZAMBRETTI_J[] = "Schimbatoare Se imbunatateste"; 237 | const char TEXT_ZAMBRETTI_K[] = "Destul de buna, Posibile averse"; 238 | const char TEXT_ZAMBRETTI_L[] = "Mai degraba Instabil, se imbunatateste mai tarziu"; 239 | const char TEXT_ZAMBRETTI_M[] = "Instabil, Probabil se imbunateste"; 240 | const char TEXT_ZAMBRETTI_N[] = "Averse cu intervale senine"; 241 | const char TEXT_ZAMBRETTI_O[] = "Averse, devine instabil"; 242 | const char TEXT_ZAMBRETTI_P[] = "Schimbatoare ceva averse"; 243 | const char TEXT_ZAMBRETTI_Q[] = "Instabil, scurte intervale mici"; 244 | const char TEXT_ZAMBRETTI_R[] = "Instabil, Averse mai tarziu"; 245 | const char TEXT_ZAMBRETTI_S[] = "Instabil, Averse uneori"; 246 | const char TEXT_ZAMBRETTI_T[] = "Foarte instabil, intervale mici uneori"; 247 | const char TEXT_ZAMBRETTI_U[] = "Ploaie uneori, mai tarziu se inrautateste"; 248 | const char TEXT_ZAMBRETTI_V[] = "Ploaie uneori, devine foarte instabil"; 249 | const char TEXT_ZAMBRETTI_W[] = "Ploaie la intervale frecvente"; 250 | const char TEXT_ZAMBRETTI_X[] = "Foarte instabil, ploaie"; 251 | const char TEXT_ZAMBRETTI_Y[] = "Furtuna, Posibil se imbunatateste"; 252 | const char TEXT_ZAMBRETTI_Z[] = "Furtuna, multa ploaie"; 253 | const char TEXT_ZAMBRETTI_0[] = "Baterie descarcata, te rog reincarca!"; 254 | const char TEXT_ZAMBRETTI_DEFAULT[] = "Imi pare rau, nici o prognoza momentan"; 255 | 256 | #elif LANGUAGE == 'SP' 257 | const char TEXT_AIR_PRESSURE[] = "Presión"; 258 | const char TEXT_RISING_FAST[] = "en rápido aumento"; 259 | const char TEXT_RISING[] = "en aumento"; 260 | const char TEXT_RISING_SLOW[] = "en aumento lento"; 261 | const char TEXT_STEADY[] = "estable"; 262 | const char TEXT_FALLING_SLOW[] = "en lento descenso"; 263 | const char TEXT_FALLING[] = "en descenso"; 264 | const char TEXT_FALLING_FAST[] = "en rápido descenso"; 265 | 266 | const char TEXT_ZAMBRETTI_FORECAST[]= "Pronóstico"; 267 | const char TEXT_ZAMBRETTI_ACCURACY[]= "Precisión de pronóstico"; 268 | const char TEXT_ZAMBRETTI_A[] = "Clima estable"; 269 | const char TEXT_ZAMBRETTI_B[] = "Buen clima"; 270 | const char TEXT_ZAMBRETTI_C[] = "Mejorando"; 271 | const char TEXT_ZAMBRETTI_D[] = "Buen clima, pero inestable"; 272 | const char TEXT_ZAMBRETTI_E[] = "Buen clima con posibles lluvias"; 273 | const char TEXT_ZAMBRETTI_F[] = "Muy buen clima, mejorando"; 274 | const char TEXT_ZAMBRETTI_G[] = "Buen clima pero con posibles lloviznas"; 275 | const char TEXT_ZAMBRETTI_H[] = "Buen clima pero con lloviznas"; 276 | const char TEXT_ZAMBRETTI_I[] = "Lloviznas pero mejorando"; 277 | const char TEXT_ZAMBRETTI_J[] = "Variable mejora"; 278 | const char TEXT_ZAMBRETTI_K[] = "Buen clima con probables lluvias"; 279 | const char TEXT_ZAMBRETTI_L[] = "Inestable, mejorando después "; 280 | const char TEXT_ZAMBRETTI_M[] = "Clima inestable con probables mejoras"; 281 | const char TEXT_ZAMBRETTI_N[] = "Lluvioso con intervalos"; 282 | const char TEXT_ZAMBRETTI_O[] = "Lluvioso, se vuelve inestable"; 283 | const char TEXT_ZAMBRETTI_P[] = "Variable con chubascos"; 284 | const char TEXT_ZAMBRETTI_Q[] = "Inestable con intervalos cortos de buen tiempo"; 285 | const char TEXT_ZAMBRETTI_R[] = "Inestable, luego lluvia"; 286 | const char TEXT_ZAMBRETTI_S[] = "Inestable, lluvia ocasionales"; 287 | const char TEXT_ZAMBRETTI_T[] = "Muy inestable, pequeños intervalos a veces"; 288 | const char TEXT_ZAMBRETTI_U[] = "Llueve a veces, luego empeora"; 289 | const char TEXT_ZAMBRETTI_V[] = "Lluvia ocasional con fuerte inestabilidad"; 290 | const char TEXT_ZAMBRETTI_W[] = "Precipitaciones frecuentes"; 291 | const char TEXT_ZAMBRETTI_X[] = "Muy inestable, lluvia"; 292 | const char TEXT_ZAMBRETTI_Y[] = "Tormenta con posibles mejoras"; 293 | const char TEXT_ZAMBRETTI_Z[] = "Tormenta, mucha lluvia"; 294 | const char TEXT_ZAMBRETTI_0[] = "¡Batería baja, por favor recargue!"; 295 | const char TEXT_ZAMBRETTI_DEFAULT[] = "Sin pronóstico por el momento"; 296 | 297 | #elif LANGUAGE == 'TR' 298 | const char TEXT_AIR_PRESSURE[] = "Basınç"; 299 | const char TEXT_RISING_FAST[] = "hızla artıyor"; 300 | const char TEXT_RISING[] = "artıyor"; 301 | const char TEXT_RISING_SLOW[] = "yavaş yavaş artıyor"; 302 | const char TEXT_STEADY[] = "kararlı"; 303 | const char TEXT_FALLING_SLOW[] = "yavaş yavaş azalıyor"; 304 | const char TEXT_FALLING[] = "azalıyor"; 305 | const char TEXT_FALLING_FAST[] = "hızla azalıyor"; 306 | 307 | const char TEXT_ZAMBRETTI_FORECAST[]= "Tahmin"; 308 | const char TEXT_ZAMBRETTI_ACCURACY[]= "Tahmin doğruluğu"; 309 | const char TEXT_ZAMBRETTI_A[] = "Stabil Güzel Hava"; 310 | const char TEXT_ZAMBRETTI_B[] = "Güzel Hava"; 311 | const char TEXT_ZAMBRETTI_C[] = "İyileşen Hava"; 312 | const char TEXT_ZAMBRETTI_D[] = "Güzel, ancak kararsız"; 313 | const char TEXT_ZAMBRETTI_E[] = "Yağmurlu ve güzel hava"; 314 | const char TEXT_ZAMBRETTI_F[] = "Çok iyi hava, gelişiyor"; 315 | const char TEXT_ZAMBRETTI_G[] = "Güzel hava, ancak sağanak yağışlı"; 316 | const char TEXT_ZAMBRETTI_H[] = "Hava iyi, ancak çiseliyor"; 317 | const char TEXT_ZAMBRETTI_I[] = "Çiseliyor ama düzeliyor"; 318 | const char TEXT_ZAMBRETTI_J[] = "Değişken iyileşme"; 319 | const char TEXT_ZAMBRETTI_K[] = "Muhtemel yağışlı, güzel hava"; 320 | const char TEXT_ZAMBRETTI_L[] = "Kararsız, gelişiyor"; 321 | const char TEXT_ZAMBRETTI_M[] = "Olası iyileştirmelerle kararsız hava"; 322 | const char TEXT_ZAMBRETTI_N[] = "Aralıklarla yağmurlu"; 323 | const char TEXT_ZAMBRETTI_O[] = "Yağmurlu, kararsız"; 324 | const char TEXT_ZAMBRETTI_P[] = "Sağanak Yağmur"; 325 | const char TEXT_ZAMBRETTI_Q[] = "Kısa süreli iyi hava, kararsız"; 326 | const char TEXT_ZAMBRETTI_R[] = "Kararsız, sonrasında yağmurlu"; 327 | const char TEXT_ZAMBRETTI_S[] = "Kararsız, zaman zaman yağmurlu"; 328 | const char TEXT_ZAMBRETTI_T[] = "Çok kararsız, sonrasında iyileşiyor"; 329 | const char TEXT_ZAMBRETTI_U[] = "Yağmur ithimali, sonrasında kötüleşiyor"; 330 | const char TEXT_ZAMBRETTI_V[] = "Aralıklı yağmur, çok kararsız"; 331 | const char TEXT_ZAMBRETTI_W[] = "Sık Aralıklı Yağmur"; 332 | const char TEXT_ZAMBRETTI_X[] = "Çok kararsız, yağmurlu"; 333 | const char TEXT_ZAMBRETTI_Y[] = "Fırtınalı, muhtemelen gelişiyor"; 334 | const char TEXT_ZAMBRETTI_Z[] = "Fırtınalı, çok yağmurlu"; 335 | const char TEXT_ZAMBRETTI_0[] = "Pil boş, lütfen şarj edin!"; 336 | const char TEXT_ZAMBRETTI_DEFAULT[] = "Üzgünüz, şu an için tahmin yok"; 337 | 338 | #elif LANGUAGE == 'NL' 339 | const char TEXT_AIR_PRESSURE[] = "Luchtdruk"; 340 | const char TEXT_RISING_FAST[] = "neemt snel toe"; 341 | const char TEXT_RISING[] = "neemt toe"; 342 | const char TEXT_RISING_SLOW[] = "neemt langzaam ote"; 343 | const char TEXT_STEADY[] = "stabiel"; 344 | const char TEXT_FALLING_SLOW[] = "neemt langzaam af"; 345 | const char TEXT_FALLING[] = "neemt af"; 346 | const char TEXT_FALLING_FAST[] = "neemt snel aff"; 347 | 348 | const char TEXT_ZAMBRETTI_FORECAST[]= "Voorspelling"; 349 | const char TEXT_ZAMBRETTI_ACCURACY[]= "Voorspellings nauwkeurigheid"; 350 | const char TEXT_ZAMBRETTI_A[] = "Stabiel mooi weer"; 351 | const char TEXT_ZAMBRETTI_B[] = "Nooi Weer"; 352 | const char TEXT_ZAMBRETTI_C[] = "Wordt mooi"; 353 | const char TEXT_ZAMBRETTI_D[] = "Mooi, minder stabield"; 354 | const char TEXT_ZAMBRETTI_E[] = "Mooi, mogelijke buien"; 355 | const char TEXT_ZAMBRETTI_F[] = "Redelijk mooi, Verbeterd"; 356 | const char TEXT_ZAMBRETTI_G[] = "Redelijk mooi, Mogelijk vroege regen"; 357 | const char TEXT_ZAMBRETTI_H[] = "Redelijk mooi, met later regen"; 358 | const char TEXT_ZAMBRETTI_I[] = "Vroege regen, met opklaringen"; 359 | const char TEXT_ZAMBRETTI_J[] = "Veranderlijk met opklaringen"; 360 | const char TEXT_ZAMBRETTI_K[] = "Redlijk mooi, Regen verwacht"; 361 | const char TEXT_ZAMBRETTI_L[] = "Redelijk veranderlijk met later opklaringen"; 362 | const char TEXT_ZAMBRETTI_M[] = "Veranderlijk, wordt mogelijk beter"; 363 | const char TEXT_ZAMBRETTI_N[] = "Af en toe bewolkt"; 364 | const char TEXT_ZAMBRETTI_O[] = "Zwaar bewolkt"; 365 | const char TEXT_ZAMBRETTI_P[] = "Veranderlijk af en te regen"; 366 | const char TEXT_ZAMBRETTI_Q[] = "Onbestendig, korte opklaringen"; 367 | const char TEXT_ZAMBRETTI_R[] = "Onbestendig, later regen"; 368 | const char TEXT_ZAMBRETTI_S[] = "Onbestendig, af en toe regen"; 369 | const char TEXT_ZAMBRETTI_T[] = "Erg onbestendig, wordt stabiler"; 370 | const char TEXT_ZAMBRETTI_U[] = "Af en toe regen, wordt slechter"; 371 | const char TEXT_ZAMBRETTI_V[] = "Af en toe regen, wordt onbestendig"; 372 | const char TEXT_ZAMBRETTI_W[] = "Regen regelmatig"; 373 | const char TEXT_ZAMBRETTI_X[] = "Heel onbestendig, Regen"; 374 | const char TEXT_ZAMBRETTI_Y[] = "Stormachtig, mogelijk beter"; 375 | const char TEXT_ZAMBRETTI_Z[] = "Stormachtig, hevige regenval"; 376 | const char TEXT_ZAMBRETTI_0[] = "Batterij leeg, laad op!"; 377 | const char TEXT_ZAMBRETTI_DEFAULT[] = "Sorry, geen voorspelling beschikbaar"; 378 | 379 | #elif LANGUAGE == 'NO' 380 | const char TEXT_AIR_PRESSURE[] = "Lufttrykk"; 381 | const char TEXT_RISING_FAST[] = "Raskt stigende"; 382 | const char TEXT_RISING[] = "Stigende"; 383 | const char TEXT_RISING_SLOW[] = "Sakte stigende"; 384 | const char TEXT_STEADY[] = "Stabilt"; 385 | const char TEXT_FALLING_SLOW[] = "Sakte fallende"; 386 | const char TEXT_FALLING[] = "Fallende"; 387 | const char TEXT_FALLING_FAST[] = "Raskt fallende"; 388 | 389 | const char TEXT_ZAMBRETTI_FORECAST[]= "Værvarsel"; 390 | const char TEXT_ZAMBRETTI_ACCURACY[]= "Værvarslingsnøyaktighet"; 391 | const char TEXT_ZAMBRETTI_A[] = "Stabilt pent vær"; 392 | const char TEXT_ZAMBRETTI_B[] = "Pent vær"; 393 | const char TEXT_ZAMBRETTI_C[] = "Oppklarnende vær"; 394 | const char TEXT_ZAMBRETTI_D[] = "Pent, tilskyende vær"; 395 | const char TEXT_ZAMBRETTI_E[] = "Pent, mulighet for lette byger"; 396 | const char TEXT_ZAMBRETTI_F[] = "Stort sett oppholdsvær, senere pent"; 397 | const char TEXT_ZAMBRETTI_G[] = "Stort sett oppholdsvær, mulighet for tidlige lette byger"; 398 | const char TEXT_ZAMBRETTI_H[] = "Stort sett oppholdsvær, senere lette byger"; 399 | const char TEXT_ZAMBRETTI_I[] = "Tidlig lett nedbør, senere oppklarnende vær"; 400 | const char TEXT_ZAMBRETTI_J[] = "Urolig, mulighet for lettere vær"; 401 | const char TEXT_ZAMBRETTI_K[] = "Stort sett oppholdsvær, sannsynligvis lette byger"; 402 | const char TEXT_ZAMBRETTI_L[] = "Ganske urolig, oppklarnende vær"; 403 | const char TEXT_ZAMBRETTI_M[] = "Urolig, mulighet for lettere vær"; 404 | const char TEXT_ZAMBRETTI_N[] = "Lett nedbør, noe sol"; 405 | const char TEXT_ZAMBRETTI_O[] = "Lett nedbør, senere urolig"; 406 | const char TEXT_ZAMBRETTI_P[] = "Vekslende vær, noe nedbør"; 407 | const char TEXT_ZAMBRETTI_Q[] = "Urolig, noe oppholdsvær"; 408 | const char TEXT_ZAMBRETTI_R[] = "Urolig, senere nedbør"; 409 | const char TEXT_ZAMBRETTI_S[] = "Urolig, noen byger"; 410 | const char TEXT_ZAMBRETTI_T[] = "Veldig urolig, noe oppholdsvær"; 411 | const char TEXT_ZAMBRETTI_U[] = "Noen byger, senere mer nedbør"; 412 | const char TEXT_ZAMBRETTI_V[] = "Noen byger, blir ganske urolig"; 413 | const char TEXT_ZAMBRETTI_W[] = "Byger"; 414 | const char TEXT_ZAMBRETTI_X[] = "Veldig urolig, nedbør"; 415 | const char TEXT_ZAMBRETTI_Y[] = "Uvær, mulighet for lettere vær"; 416 | const char TEXT_ZAMBRETTI_Z[] = "Uvær, store nedbørsmengder"; 417 | const char TEXT_ZAMBRETTI_0[] = "Tomt batteri, vennligst lad!"; 418 | const char TEXT_ZAMBRETTI_DEFAULT[] = "Beklager, ikke noe værvarsel for øyeblikket"; 419 | #endif 420 | --------------------------------------------------------------------------------