/*
* Simple Meteobridge network display using a 2x 20 LCD, a PCF8574 I2C I/O expander and a ESP8266.
* Aart 06-2023
*
* Update 08-2024: New DisplayMeteo() function, not sensitive to the sequence of message lines.
*/
// #define DEBUGMODE // Run on ESP8266 only, no button. Display not neccesary.
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x38,20,2); // The "standard" address for a PCF8574 LCD module seems to be 0x27.
// On the ESP8266: D1 = SCL, D2 = SDA. 4k7 pullups are placed.
const int ON_Board_LED = 2;
const char* ssid = "WifiNetwork";
const char* password = "WifiPassword";
const char* host = "http://meteobridge:MeteobridgePassword@192.168.178.30/";
static uint32_t UPDATE_INTERVAL = 10000;
static int BUTTON_PIN = 0; // Pin D3 on a Wemos D1 clone.
String GetAddress, LinkGet, getData;
int id = 0;
HTTPClient http;
WiFiClient client;
// Separates a string into substrings using a separator character.
String getValue(String data, char separator, int index)
{
int found = 0;
int strIndex[] = {
0, -1 };
int maxIndex = data.length()-1;
for(int i=0; i<=maxIndex && found<=index; i++){
if(data.charAt(i)==separator || i==maxIndex){
found++;
strIndex[0] = strIndex[1]+1;
strIndex[1] = (i == maxIndex) ? i+1 : i;
}
}
return found>index ? data.substring(strIndex[0], strIndex[1]) : "";
}
void connectWiFi() {
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
digitalWrite(ON_Board_LED, LOW);
delay(250);
digitalWrite(ON_Board_LED, HIGH);
delay(250);
}
digitalWrite(ON_Board_LED, HIGH);
Serial.println("");
Serial.print("Successfully connected to : ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.println();
}
String getMeteoData(void) {
if (WiFi.status() != WL_CONNECTED) {
connectWiFi();
}
Serial.println("----------------Connect to Server-----------------");
Serial.println("Get LED Status from Server or Database");
Serial.print("Request Link : ");
Serial.println(LinkGet);
HTTPClient http;
http.begin(client, LinkGet);
http.addHeader("Content-Type", "text/plain");
int httpCodeGet = http.GET();
String payloadGet = http.getString();
Serial.print("Response Code : ");
Serial.println(httpCodeGet);
Serial.println("Returned data from Server : ");
Serial.println(payloadGet);
Serial.println("----------------Closing Connection----------------");
http.end();
return (payloadGet);
}
void setup() {
lcd.init();
lcd.backlight();
lcd.setCursor(1,0);
lcd.print("MeteobridgeDisplay");
lcd.setCursor(6,1);
lcd.print("Aart 2023");
Serial.begin(115200);
delay(500);
GetAddress = "cgi-bin/livedata.cgi";
LinkGet = host + GetAddress;
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
pinMode(ON_Board_LED, OUTPUT);
digitalWrite(ON_Board_LED, HIGH);
pinMode(BUTTON_PIN, INPUT);
}
void displayLegend() {
lcd.clear();
lcd.setCursor(0,0);
lcd.print("TEMP");
lcd.setCursor(0,1);
lcd.print("HUMI");
lcd.setCursor(5,0);
lcd.print("WIND");
lcd.setCursor(5,1);
lcd.print("DIR.");
lcd.setCursor(10,0);
lcd.print("RAIN");
lcd.setCursor(10,1);
lcd.print("RATE");
lcd.setCursor(15,0);
lcd.print("SUN");
lcd.setCursor(15,1);
lcd.print("BARO");
}
void DisplayMeteo(String data) {
lcd.clear();
int i = 0, found = 0;
while (found < 5) {
if (getValue(data,' ', i) == "th0") {
Serial.print("- Index i th0: "); Serial.println(i);
found++;
i++;
lcd.setCursor(0,0); lcd.print(getValue(data,' ', i )); // TEMP
Serial.print("Temp: "); Serial.println(getValue(data,' ', i ));
i++;
lcd.setCursor(0,1); lcd.print(getValue(data,' ', i )); // HUMI
Serial.print("Humi: "); Serial.println(getValue(data,' ', i ));
}
if (getValue(data,' ', i) == "wind0") {
Serial.print("- Index i wind0: "); Serial.println(i);
found++;
i++;
lcd.setCursor(4,1); lcd.print(getValue(data,' ', i )); // DIR
Serial.print("Dir: "); Serial.println(getValue(data,' ', i ));
i++;
lcd.setCursor(5,0); lcd.print(getValue(data,' ', i )); // WIND
Serial.print("Wind: "); Serial.println(getValue(data,' ', i ));
}
if (getValue(data,' ', i) == "rain0") {
Serial.print("- Index i rain0: "); Serial.println(i);
found++;
i++;
lcd.setCursor(9,1); lcd.print(getValue(data,' ', i )); // RATE
Serial.print("Rate: "); Serial.println(getValue(data,' ', i ));
i++;
lcd.setCursor(10,0); lcd.print(getValue(data,' ', i )); // RAIN
Serial.print("Rain: "); Serial.println(getValue(data,' ', i ));
}
if (getValue(data,' ', i) == "sol0") {
Serial.print("- Index i sol0: "); Serial.println(i);
found++;
i++;
lcd.setCursor(16,0); lcd.print(getValue(data,' ', i )); // SOLAR
Serial.print("Solar: "); Serial.println(getValue(data,' ', i ));
}
if (getValue(data,' ', i) == "thb0") {
Serial.print("- Index i thb0: "); Serial.println(i);
found++;
i += 5;
lcd.setCursor(14,1); lcd.print(getValue(data,' ', i )); // BARO
Serial.print("Baro: "); Serial.println(getValue(data,' ', i ));
}
i++;
}
/* Message format, 08-2024.
20240829070537 th0 21.3 88 19.2 0
20240829070621 rain0 0.0 578.8 0.0 0
20240829070537 thb0 21.9 72 16.6 1012.9 1013.4 2 0
20240829070617 sol0 151 0
20240829070623 wind0 222 0.9 0.4 21.3 0
*/
}
void loop() {
static String meteoData;
static int state = 0, lastState = 0, lamp = 1;
static uint32_t lastbutton, lastUpdate;
enum state {START, WAIT, DB_DOWN, DOWN, DB_UP, UP}; // Button state machine. DB = DeBounce.
#ifdef DEBUGMODE
uint8_t button = 1;
#else
uint8_t button = digitalRead(BUTTON_PIN);
#endif
// debug
if (lastState != state) {
lastState = state;
Serial.print("New button state: "); Serial.println(state);
}
switch (state) {
case START:
meteoData = getMeteoData();
DisplayMeteo(meteoData);
state = WAIT;
break;
case WAIT:
if (button == LOW) {
lastbutton = millis();
state = DB_DOWN;
}
if (millis() - lastUpdate > UPDATE_INTERVAL ) {
meteoData = getMeteoData();
lastUpdate = millis();
DisplayMeteo(meteoData);
}
break;
case DB_DOWN:
if (millis() - lastbutton > 100) {
state = DOWN;
displayLegend();
lamp = !lamp;
if (lamp) lcd.backlight(); else lcd.noBacklight();
}
else if (button == HIGH) state = WAIT;
break;
case DOWN:
if (button == HIGH) {
lastbutton = millis();
state = DB_UP;
}
break;
case DB_UP:
if (millis() - lastbutton > 100) state = UP;
else if (button == LOW) state = DOWN;
break;
case UP:
DisplayMeteo(meteoData);
state = WAIT;
break;
}
}