448 lines
14 KiB
C++
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);
|
|
}
|
|
}
|