Created
October 16, 2021 15:07
-
-
Save Jotschi/63152f665cefe56a71b0d3f2d15bd797 to your computer and use it in GitHub Desktop.
Sonoff TH10 + AM2301 Sensor OTA Sketch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <DHT.h> | |
#include <DHT_U.h> | |
#include <ArduinoOTA.h> | |
#include <ESP8266WiFi.h> | |
#include <ESP8266WebServer.h> | |
#include <HampelFilter.h> | |
const char *ssid = "*********"; | |
const char *password = "********"; | |
const int BUTTON_PIN = 0; | |
const int RELAY_PIN = 12; | |
const int BLUE_LED_PIN = 13; | |
#define DHTPIN 14 | |
#define DHTTYPE DHT21 | |
#define INTERVAL_AUTOCHECK 20 | |
unsigned long tick = 0; | |
unsigned long marker = 0; | |
unsigned long marker2 = 0; | |
byte mac[6]; | |
ESP8266WebServer server(80); | |
#define HUM_CAL 11 | |
boolean relayMode = false; | |
DHT_Unified dht(DHTPIN, DHTTYPE); | |
boolean autoMode = true; | |
double humidityMax = 60.0; | |
double hys = 10; | |
sensors_event_t temp; | |
sensors_event_t humidity; | |
float medianHumidity; | |
float medianTemp; | |
int nMEASUREMENTS = 20; // 20 measurements over 1s each = 20s | |
HampelFilter humidityBuffer = HampelFilter(20, nMEASUREMENTS, 1.50); | |
HampelFilter tempBuffer = HampelFilter(20, nMEASUREMENTS, 1.50); | |
ICACHE_RAM_ATTR void buttonPress() { | |
toggle(); | |
} | |
void toggle() { | |
relayMode = !relayMode; | |
digitalWrite(RELAY_PIN, relayMode); | |
} | |
void on() { | |
relayMode = true; | |
digitalWrite(RELAY_PIN, relayMode); | |
} | |
void off() { | |
relayMode = false; | |
digitalWrite(RELAY_PIN, relayMode); | |
} | |
void setupPins() { | |
pinMode(RELAY_PIN, OUTPUT); | |
pinMode(BLUE_LED_PIN, OUTPUT); | |
pinMode(BUTTON_PIN, INPUT_PULLUP); | |
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonPress, RISING); | |
} | |
/** | |
* Initialize the wifi and over the air update mechanism | |
*/ | |
void initWLAN() { | |
Serial.println(); | |
Serial.print("Connecting to "); | |
Serial.println(ssid); | |
// Change MAC | |
byte *mac = (byte *)malloc(6); | |
WiFi.macAddress(mac); | |
mac[0] = 0x20; | |
mac[1] = 0x11; | |
mac[2] = 0xCA; | |
mac[3] = 0x8A; | |
mac[4] = 0x11; | |
mac[5] = 0x21; | |
wifi_set_macaddr(STATION_IF, mac); | |
free(mac); | |
WiFi.mode(WIFI_STA); | |
WiFi.hostname("sonoff-big"); | |
WiFi.begin(ssid, password); | |
while (WiFi.waitForConnectResult() != WL_CONNECTED) { | |
Serial.println("Connection Failed! Rebooting..."); | |
delay(5000); | |
ESP.restart(); | |
} | |
ArduinoOTA.setHostname("sonoff-big"); | |
ArduinoOTA.onStart([]() { | |
String type; | |
if (ArduinoOTA.getCommand() == U_FLASH) { | |
type = "sketch"; | |
} else { | |
// U_SPIFFS | |
type = "filesystem"; | |
} | |
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() | |
Serial.println("Start updating " + type); | |
}); | |
ArduinoOTA.onEnd([]() { | |
Serial.println("\nEnd"); | |
}); | |
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { | |
float val = (progress / (total / 100)); | |
Serial.printf("Progress: %u%%\r", val); | |
}); | |
ArduinoOTA.onError([](ota_error_t error) { | |
Serial.printf("Error[%u]: ", error); | |
if (error == OTA_AUTH_ERROR) { | |
Serial.println("Auth Failed"); | |
} else if (error == OTA_BEGIN_ERROR) { | |
Serial.println("Begin Failed"); | |
} else if (error == OTA_CONNECT_ERROR) { | |
Serial.println("Connect Failed"); | |
} else if (error == OTA_RECEIVE_ERROR) { | |
Serial.println("Receive Failed"); | |
} else if (error == OTA_END_ERROR) { | |
Serial.println("End Failed"); | |
} | |
}); | |
ArduinoOTA.begin(); | |
} | |
void handleNotFound() { | |
server.send(404, "text/plain", "Not found"); | |
} | |
float readCalHum() { | |
float hum = humidity.relative_humidity - HUM_CAL; | |
if (hum < 0) { | |
return 0; | |
} else { | |
return hum; | |
} | |
} | |
/** | |
* Add basic routes for the HTTP server | |
*/ | |
void setupServer() { | |
/** | |
* Turn the power relay on | |
*/ | |
server.on("/on", []() { | |
on(); | |
server.send(200, "text/plain", "ON"); | |
}); | |
/** | |
* Turn the power relay off | |
*/ | |
server.on("/off", []() { | |
off(); | |
server.send(200, "text/plain", "OFF"); | |
}); | |
/** | |
* Return measured temperature | |
*/ | |
server.on("/temp", []() { | |
server.send(200, "text/plain", String(temp.temperature)); | |
}); | |
/** | |
* Metrics endpoint which can be scraped by prometheus | |
*/ | |
server.on("/metrics", []() { | |
String output = ""; | |
output += "# TYPE humidity gauge\n"; | |
output += "# HELP humidity Humidity value of the sensor\n"; | |
output += "humidity " + String(readCalHum()) + "\n"; | |
output += "# TYPE humidity_med gauge\n"; | |
output += "# HELP humidity_med Median humidity value of the sensor\n"; | |
output += "humidity_med " + String(medianHumidity) + "\n"; | |
output += "# TYPE temp gauge\n"; | |
output += "# HELP temp Humidity value of the sensor\n"; | |
output += "temp " + String(temp.temperature) + "\n"; | |
output += "# TYPE temp_med gauge\n"; | |
output += "# HELP temp_med Median temperatur value of the sensor\n"; | |
output += "temp_med " + String(medianTemp) + "\n"; | |
output += "# TYPE humidity_max gauge\n"; | |
output += "# HELP humidity_max Configured humidity trigger value\n"; | |
output += "humidity_max " + String(humidityMax) + "\n"; | |
String toggleStr = relayMode ? "1" : "0"; | |
output += "# TYPE toggle gauge\n"; | |
output += "# HELP toggle Current state of the relay\n"; | |
output += "toggle " + toggleStr + "\n"; | |
output += "# TYPE mode gauge\n"; | |
output += "# HELP mode Mode of the ESP\n"; | |
String modeStr = autoMode ? "1" : "0"; | |
output += "mode " + modeStr + "\n"; | |
server.send(200, "text/plain; version=0.0.4;charset=UTF-8", output); | |
}); | |
/** | |
* Return the calibrated humidity value | |
*/ | |
server.on("/humidity", []() { | |
server.send(200, "text/plain", String(readCalHum())); | |
}); | |
/** | |
* Return the median temperature | |
*/ | |
server.on("/med_temp", []() { | |
server.send(200, "text/plain", String(medianTemp)); | |
}); | |
/** | |
* Return the median humidity | |
*/ | |
server.on("/med_humidity", []() { | |
server.send(200, "text/plain", String(medianHumidity)); | |
}); | |
/** | |
* Set the maximum humidity for the auto mode which turns on/off the power relay. | |
*/ | |
server.on("/max_humidity", []() { | |
String value = server.arg("val"); | |
if (value == "") { | |
server.send(200, "text/plain", "Parameter val not found"); | |
} else { | |
float max = value.toFloat(); | |
humidityMax = max; | |
server.send(200, "text/plain", "New maximum value set: " + String(max)); | |
} | |
}); | |
/** | |
* Enable the auto mode. Power relay will be enabled automatically. | |
*/ | |
server.on("/auto_on", []() { | |
autoMode = true; | |
server.send(200, "text/plain", "Auto Mode enabled"); | |
}); | |
/** | |
* Disable the auto mode. Power relay will not be enabled automatically. | |
*/ | |
server.on("/auto_off", []() { | |
autoMode = false; | |
server.send(200, "text/plain", "Auto Mode disabled"); | |
}); | |
/** | |
* Return the power relay state and current tick value | |
*/ | |
server.on("/status", []() { | |
String mode = relayMode ? "ON" : "OFF"; | |
server.send(200, "text/plain", mode + " " + String(tick)); | |
}); | |
server.on("/toggle", []() { | |
toggle(); | |
server.send(200, "text/plain", "TOGGLE"); | |
}); | |
server.onNotFound(handleNotFound); | |
server.begin(); | |
Serial.println("HTTP server started"); | |
} | |
/** | |
* Setup the sensors/wifi/buttons | |
*/ | |
void setup() { | |
Serial.begin(115200); | |
initWLAN(); | |
setupPins(); | |
setupServer(); | |
dht.begin(); | |
off(); | |
} | |
/** | |
* Read the sensor values and process them. | |
*/ | |
void measureValues() { | |
dht.humidity().getEvent(&humidity); | |
// Add the humidity value to the hampelfilter buffer to allow filtering | |
humidityBuffer.write(readCalHum()); | |
// Update the current median value via the filter | |
medianHumidity = humidityBuffer.readMedian(); | |
Serial.println("Current Humidity: " + String(humidity.relative_humidity)); | |
Serial.println("Med Humidity: " + String(medianHumidity)); | |
dht.temperature().getEvent(&temp); | |
if (!isnan(temp.temperature)) { | |
tempBuffer.write(temp.temperature); | |
} | |
medianTemp = tempBuffer.readMedian(); | |
Serial.println("Current Temp: " + String(temp.temperature)); | |
Serial.println("Med Temp: " + String(medianTemp)); | |
Serial.println(); | |
} | |
void loop() { | |
// Act upon OTA requests | |
ArduinoOTA.handle(); | |
// Act upon HTTP client request | |
server.handleClient(); | |
// Collect sensor data every 1s | |
if ((millis() - marker) > 1000) { | |
marker = millis(); | |
measureValues(); | |
tick++; | |
} | |
// Handle the auto mode when enabled and only every 20s | |
if (autoMode == true) { | |
if (tick >= marker2 + INTERVAL_AUTOCHECK) { | |
marker2 = tick + INTERVAL_AUTOCHECK; | |
// Enable power relay when median humidity exceeds max value + hysteresis value. | |
// The hysteresis value and interval check helps to avoid bogus power relay state changes | |
if (medianHumidity >= humidityMax + hys) { | |
on(); | |
} else if (medianHumidity < humidityMax - hys) { | |
off(); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment