WeatherStation/Software/WeatherSensor_D1mini/WeatherSensor_D1mini.ino

448 lines
14 KiB
C++

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
// Update these with values suitable for your network.
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;
unsigned long t0;
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#define BME_SCK 13
#define BME_MISO 12p
#define BME_MOSI 11
#define BME_CS 10
#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme; // I2C
#include <Adafruit_TSL2561_U.h>
Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(TSL2561_ADDR_FLOAT, 12345);
unsigned long delayTime;
//##########################################
// Config
#include <EEPROM.h>
#define CONFIG_MAX_SIZE 1024
const int CONFIG_PIN = D0;
enum CONFIG_STATE {
CONFIG_NONE = 0x00,
CONFIG_VALID = 0x01,
};
///const String mqtt_topic_l = mqtt_client + String("/lux");
///const String mqtt_topic_led_state = mqtt_client + String("/led_state");
///const String mqtt_topic_led = mqtt_client + String("/led");
struct config_map {
char state;
char version;
char ssid[50];
char password[50];
byte mqtt_ip[4];
unsigned short mqtt_port;
char topic_base[20];
char topic_temperature[40];
char topic_pressure[40];
char topic_humidity[40];
//char topic_lux[20];
};
config_map config;
//##########################################
//##########################################
// Mode
enum RUN_MODE {
MODE_CONFIGURE,
MODE_RUN,
MODE_TEST
};
RUN_MODE mode = MODE_RUN;
//##########################################
//##########################################
// Config Server
#include <ESP8266WebServer.h>
ESP8266WebServer server(80);
#define CONFIG_SSID "WeatherConfig"
#define DISABLE_LOG 1
void print_init() {
#ifndef DISABLE_LOG
Serial.begin(115200);
#endif
}
template <class T>
void print(T val, bool newLine = true) {
#ifndef DISABLE_LOG
if (newLine) {
Serial.println(val);
} else {
Serial.print(val);
}
#endif
}
void setup_access_point() {
print<const char *>("Configuring access point...");
/* You can remove the password parameter if you want the AP to be open. */
WiFi.softAPConfig(IPAddress(192,168,4,1), IPAddress(192,168,4,1), IPAddress(255,255,255,0));
WiFi.softAP(CONFIG_SSID);
IPAddress myIP = WiFi.softAPIP();
print("AP IP address: ", false);
print<IPAddress>(myIP);
server.on("/", handleRoot);
server.on("/config", handleWriteConfig);
server.on("/reset", handleReset);
server.begin();
print<const char*>("HTTP server started");
}
void handleRoot() {
server.send(200, "text/html", "<html><head>"
"<title>WeatherSensor Config</title>"
"<style>label { display: inline-block; width: 165px; } h1 { text-align: center; } div { margin: 10px 0px; }</style>"
"</head><body>"
"<div style='margin: 0 auto; width: 400px;'>"
"<h1>Weather Sensor</h1>"
"<form id='SensorConfig' action='config' method='POST'>"
"<fieldset><legend>Config Details</legend>"
"<div><label for='ssid'>WLAN SSID:</label><input name='ssid' type='text' placeholder='WeatherSensorWiFi' value='' required autofocus></div>"
"<div><label for='pw'>WLAN Password:</label><input name='pw' type='text' placeholder='123456' required></div>"
"<div><label for='srv'>MQTT-Server IP:</label><input name='srv' type='text' placeholder='127.0.0.1' required></div>"
"<div><label for='port'>MQTT-Server Port:</label><input name='port' type='text' placeholder='1883' required></div>"
"<div><label for='dev'>Device Name:</label><input name='dev' type='text' placeholder='home/S000' required></div>"
"<div><label for='temperature'>Tag Temperatur:</label><input name='temperature' type='text' value='/temperature' required></div>"
"<div><label for='humidity'>Tag Humidity:</label><input name='humidity' type='text' value='/humidity' required></div>"
"<div><label for='pressure'>Tag Pressure:</label><input name='pressure' type='text' value='/pressure' required></div>"
"<div><input name='config' type='submit' value='Save'></div>"
"</fieldset></form>"
"<form method='POST' action='update' enctype='multipart/form-data'><fieldset disabled='disabled'>"
"<legend>Firmware Update</legend>"
"<div><input type='file' name='update'><input type='submit' value='Update'></div>"
"</fieldset></form></div></body></html>");
}
void handleWriteConfig() {
int args = server.args();
int errors = 0x1F;
if (args > 0) {
/*for(int i=0; i< args; ++i) {
Serial.print(server.argName(i));
Serial.print(" = ");
Serial.println(server.arg(i));
}*/
if (server.hasArg("ssid")) {
const String &ssid = server.arg("ssid");
ssid.toCharArray(config.ssid, 50);
errors &= ~(0x01);
}
if (server.hasArg("pw")) {
const String &pw = server.arg("pw");
pw.toCharArray(config.password, 50);
errors &= ~(0x02);
}
if (server.hasArg("dev")) {
const String &device = server.arg("dev");
device.toCharArray(config.topic_base, 20);
if (server.hasArg("temperature")) {
const String temperature = device + server.arg("temperature");
temperature.toCharArray(config.topic_temperature, 40);
errors &= ~(0x10);
}
if (server.hasArg("humidity")) {
const String humidity = device + server.arg("humidity");
humidity.toCharArray(config.topic_humidity, 40);
errors &= ~(0x10);
}
if (server.hasArg("pressure")) {
const String pressure = device + server.arg("pressure");
pressure.toCharArray(config.topic_pressure, 40);
errors &= ~(0x10);
}
}
if (server.hasArg("port")) {
int port = server.arg("port").toInt();
if (0 < port && port < 65535) {
config.mqtt_port = port;
errors &= ~(0x04);
}
}
if (server.hasArg("srv")) {
String addr = server.arg("srv");
for (int i=0; i<4; ++i) {
int val = addr.toInt();
if (0<=val && val<256) {
config.mqtt_ip[i] = val;
} else {
server.send(500, "text/html", "<div style='margin: 0 auto; width: 400px; text-align: center;background: #0F0; border: 1px solid #F00;'>"
"<h1>Weather Sensor</h1>"
"<div style='background: #FFF;'>ERROR - 4</div>"
"<div>Error while parsing the IP address!</div></div>");
errors &= ~(0x08);
}
int pos = addr.indexOf('.');
addr = addr.substring(pos+1);
}
errors &= ~(0x08);
}
print<int>(errors);
if (errors == 0x00) {
EEPROM.begin(CONFIG_MAX_SIZE);
config.state = CONFIG_VALID;
EEPROM.put(0, config);
EEPROM.end();
server.send(200, "text/html", "<div style='margin: 0 auto; width: 400px; text-align: center;background: #0F0; border: 1px solid #0F0;'>"
"<h1>Weather Sensor</h1>"
"<div style='background: #FFF;'>OK</div>"
"<div>Disable config mode and reboot device!</div></div>");
// @todo add reset link to html content
// @todo add current memory content to input boxes
return;
} else {
server.send(500, "text/html", "<div style='margin: 0 auto; width: 400px; text-align: center;background: #F00; border: 1px solid #F00;'>"
"<h1>Weather Sensor</h1>"
"<div style='background: #FFF;'>ERROR - 2</div>"
"<div>Error while parsing config values! Code: " + String(errors) + "</div></div>");
}
}
server.send(500, "text/html", "<div style='margin: 0 auto; width: 400px; text-align: center;background: #F00; border: 1px solid #0F0;'>"
"<h1>Weather Sensor</h1>"
"<div style='background: #FFF;'>ERROR - 1</div>"
"<div>Disable config mode and reboot device! </div></div>");
}
void handleReset() {
server.send(405, "text/html", "<div style='margin: 0 auto; width: 400px; text-align: center;background: #F00; border: 1px solid #0F0;'>"
"<h1>Weather Sensor</h1>"
"<div style='background: #FFF;'>ERROR - 3</div>"
"<div>Not implemented yet!</div></div>");
}
//##########################################
void setup() {
print_init();
//enable configuration Pin
pinMode(CONFIG_PIN, INPUT);
// read config from EEPROM
EEPROM.begin(CONFIG_MAX_SIZE);
EEPROM.get(0, config);
EEPROM.end();
if (digitalRead(CONFIG_PIN) == HIGH) {
mode = MODE_CONFIGURE;
//mode = MODE_TEST;
}
if (config.state != CONFIG_VALID && mode != MODE_CONFIGURE) {
mode = MODE_CONFIGURE;
}
switch(mode) {
case MODE_CONFIGURE:
print<const char*>("config");
pinMode(D4, OUTPUT);
digitalWrite(D4, LOW);
setup_access_point();
return;
case MODE_RUN:
print<const char*>("run");
setup_wifi();
setup_sensor();
print<const char *>("MQTT Addr: ");
print<int>(config.mqtt_ip[0], false);
print<const char *>(".", false);
print<int>(config.mqtt_ip[1], false);
print<const char *>(".", false);
print<int>(config.mqtt_ip[2], false);
print<const char *>(".", false);
print<int>(config.mqtt_ip[3], false);
print<const char *>(":", false);
print<long>(config.mqtt_port);
client.setServer(IPAddress(config.mqtt_ip[0], config.mqtt_ip[1], config.mqtt_ip[2], config.mqtt_ip[3]), config.mqtt_port);
//client.setCallback(callback);
if (!client.connected()) {
reconnect();
}
delay(500);
client.loop();
delay(500);
process_sensor();
delay(500);
client.loop();
delay(500);
ESP.deepSleep(300e6); // 300 sec = 5 min
return;
case MODE_TEST:
print<const char *>("testing");
WiFi.mode(WIFI_AP_STA);
WiFi.begin("AcS", "67993776724373201548");
if (WiFi.waitForConnectResult() == WL_CONNECTED) {
print<const char *>("Connected ", false);
print<IPAddress>(WiFi.localIP());
//server.on("/", handleRoot);
//server.on("/config", handleWriteConfig);
//todo file upload
server.begin();
print<const char *>("HTTP server started");
} else {
print<const char *>("WiFi Failed");
}
return;
}
}
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
print<const char*>("Connecting to ");
print<const char*>(config.ssid);
WiFi.begin(config.ssid, config.password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
print<const char*>(".", false);
}
print<const char *>("");
print<const char*>("WiFi connected");
print<const char*>("IP address: ", false);
print<IPAddress>(WiFi.localIP());
}
void setup_sensor() {
print<const __FlashStringHelper*>(F("BME280 setup... "));
bool status;
// default settings
// (you can also pass in a Wire library object like &Wire2)
status = bme.begin();
if (!status) {
while(1) {
print<const char*>("Could not find a valid BME280 sensor, check wiring!");
//while (1);
delay(2000);
}
}
tsl.enableAutoRange(true);
tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_101MS);
delayTime = 1000;
Serial.println("done.");
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
// Switch on the LED if an 1 was received as first character
if ((char)payload[0] == '1') {
digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level
// but actually the LED is on; this is because
// it is acive low on the ESP-01)
} else {
digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH
}
}
void reconnect() {
// Loop until we're reconnected
const String start_topic = config.topic_base + String("/client");
while (!client.connected()) {
print<const char*>("Attempting MQTT connection...", false);
// Attempt to connect
if (client.connect(config.topic_base)) {
print<const char*>("connected");
// Once connected, publish an announcement...
client.publish(start_topic.c_str(), "hello world");
// ... and resubscribe
///client.subscribe(mqtt_topic_led.c_str());
} else {
sprintf(msg, "failed, rc=%d try again in 5 seconds", client.state());
print<char *>(msg);
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void process_sensor() {
unsigned long t1 = millis();
print<const char*>("ReadSensor: ", false);
print<unsigned long>(t1-t0);
t0 = t1;
auto temperatur = bme.readTemperature();
auto humidity = bme.readHumidity();
auto pressure = bme.readPressure() / 100.0F;
int led = !digitalRead(BUILTIN_LED);
sensors_event_t event;
tsl.getEvent(&event);
float light = 0.0;
if (event.light) {
light = event.light;
}
snprintf(msg, 75, "T: %0.2f *C, P: %0.2f hPa, H: %0.2f, L: %0.0f LED: %d",
temperatur, pressure, humidity, light, led);
client.publish(config.topic_temperature, String(temperatur).c_str());
client.publish(config.topic_humidity, String(humidity).c_str());
client.publish(config.topic_pressure, String(pressure).c_str());
///client.publish(mqtt_topic_l.c_str(), String(light).c_str());
///client.publish(mqtt_topic_led_state.c_str(), String(led).c_str());
print<char*>(msg);
}
void loop() {
if (MODE_CONFIGURE == mode) {
server.handleClient();
} else {
delay(500);
}
}