|
|
|
/*
|
|
|
|
* WetterStation
|
|
|
|
* Version: 0.0
|
|
|
|
* Author: Hendrik Langer <hendrik+dev@xd0.de>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <Arduino.h>
|
|
|
|
#include <ESP8266WiFi.h>
|
|
|
|
#include <ESP8266WiFiMulti.h>
|
|
|
|
extern "C" {
|
|
|
|
#include <espnow.h>
|
|
|
|
#include "user_interface.h"
|
|
|
|
}
|
|
|
|
|
|
|
|
#include <ESP8266HTTPClient.h>
|
|
|
|
#include <ArduinoJson.h>
|
|
|
|
#include <MQTTClient.h>
|
|
|
|
|
|
|
|
#include <Wire.h>
|
|
|
|
#include <SPI.h>
|
|
|
|
#include <Adafruit_Sensor.h>
|
|
|
|
#include <Adafruit_BMP085_U.h>
|
|
|
|
#include <DHT.h>
|
|
|
|
|
|
|
|
#include <RunningAverage.h>
|
|
|
|
|
|
|
|
#include <SDS011.h>
|
|
|
|
|
|
|
|
const char* server = "ingress.opensensemap.org";
|
|
|
|
//const char* ssid = "Freifunk";
|
|
|
|
//const char* password = "";
|
|
|
|
|
|
|
|
#define MQTT_MAX_PACKET_SIZE 512
|
|
|
|
const char* mqttserver = "home.xd0.de";
|
|
|
|
const char* mqttusername = "esp-weatherstation";
|
|
|
|
const char* mqttpassword = "password4";
|
|
|
|
|
|
|
|
constexpr unsigned int postingInterval = 60000; //Uploadintervall in Millisekunden
|
|
|
|
constexpr unsigned int dhcp_interval = 60*60*1000;
|
|
|
|
|
|
|
|
//senseBox ID
|
|
|
|
#define SENSEBOX_ID "5a9e9e38f55bff001a494877"
|
|
|
|
|
|
|
|
//Sensor IDs
|
|
|
|
// Temperature
|
|
|
|
#define SENSOR1_ID "5a9e9e38f55bff001a49487e"
|
|
|
|
// Humidity
|
|
|
|
#define SENSOR2_ID "5a9e9e38f55bff001a49487d"
|
|
|
|
// Pressure
|
|
|
|
#define SENSOR3_ID "5a9e9e38f55bff001a49487c"
|
|
|
|
// PM10
|
|
|
|
#define SENSOR4_ID "5a9e9e38f55bff001a49487b"
|
|
|
|
// PM2.5
|
|
|
|
#define SENSOR5_ID "5a9e9e38f55bff001a49487a"
|
|
|
|
// Radioactivity
|
|
|
|
#define SENSOR6_ID "5a9e9e38f55bff001a494879"
|
|
|
|
// Voltage
|
|
|
|
#define SENSOR7_ID "5a9e9e38f55bff001a494878"
|
|
|
|
// RSSI
|
|
|
|
#define SENSOR8_ID "5a9eddb1f55bff001a51de52"
|
|
|
|
|
|
|
|
|
|
|
|
static constexpr uint8_t BMP_SCL = D4;
|
|
|
|
static constexpr uint8_t BMP_SDA = D3;
|
|
|
|
|
|
|
|
static constexpr uint8_t DHT22_PIN = D7;
|
|
|
|
static constexpr uint8_t DHTTYPE = DHT22; // DHT 22 (AM2302)
|
|
|
|
|
|
|
|
static constexpr uint8_t SDS_TX = D1;
|
|
|
|
static constexpr uint8_t SDS_RX = D2;
|
|
|
|
|
|
|
|
static constexpr uint8_t GEIGER_PIN = D6;
|
|
|
|
static constexpr float CONV_FACTOR = 0.008120;
|
|
|
|
static constexpr float OWN_BACKGROUND_CPS = 0; // documentation says 0.2 (make sure value doesn't get negative if subtracting!)
|
|
|
|
|
|
|
|
ADC_MODE(ADC_VCC);
|
|
|
|
|
|
|
|
ESP8266WiFiMulti wifiMulti;
|
|
|
|
|
|
|
|
os_timer_t Timer1;
|
|
|
|
RunningAverage geigeraverage(10);
|
|
|
|
|
|
|
|
Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10085);
|
|
|
|
DHT dht(DHT22_PIN, DHTTYPE);
|
|
|
|
SDS011 sds;
|
|
|
|
volatile unsigned long geiger_counts = 0;
|
|
|
|
unsigned long last_wifi_activity = 0;
|
|
|
|
unsigned long last_dhcp = 0;
|
|
|
|
IPAddress ip, dns, gateway, subnet;
|
|
|
|
char ssid[64];
|
|
|
|
char password[64];
|
|
|
|
|
|
|
|
struct __attribute__((packed)) SENSOR_DATA {
|
|
|
|
float temperature;
|
|
|
|
float humidity;
|
|
|
|
float pressure;
|
|
|
|
float temp2;
|
|
|
|
float p10;
|
|
|
|
float p25;
|
|
|
|
float cpm;
|
|
|
|
float radioactivity;
|
|
|
|
float voltage;
|
|
|
|
float rssi;
|
|
|
|
} sd;
|
|
|
|
|
|
|
|
/*
|
|
|
|
void sendESPNOW() {
|
|
|
|
// unsigned long espnowmillis = millis();
|
|
|
|
uint8_t remoteMac[] = {0x5C, 0xCF, 0x7F, 0x5, 0xFD, 0xF0};
|
|
|
|
#define WIFI_CHANNEL 1
|
|
|
|
|
|
|
|
WiFi.forceSleepWake();
|
|
|
|
delay(1); // yield();
|
|
|
|
digitalWrite(LED_BUILTIN, LOW);
|
|
|
|
WiFi.persistent(false); // don't load and save credentials to flash
|
|
|
|
WiFi.mode(WIFI_STA);
|
|
|
|
if (esp_now_init()==0) {
|
|
|
|
if(esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER)==0){
|
|
|
|
|
|
|
|
esp_now_add_peer(remoteMac, ESP_NOW_ROLE_SLAVE, WIFI_CHANNEL, NULL, 0);
|
|
|
|
u8 bs[sizeof(sd)];
|
|
|
|
memcpy(bs, &sd, sizeof(sd));
|
|
|
|
for (int i=0; i<20; i++)
|
|
|
|
esp_now_send(NULL, bs, sizeof(sd)); // max ESP_NOW_MAX_DATA_LEN
|
|
|
|
|
|
|
|
} else {
|
|
|
|
Serial.println("error configuring ESP NOW");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Serial.println("error initializing ESP NOW");
|
|
|
|
}
|
|
|
|
last_wifi_activity = millis();
|
|
|
|
esp_now_deinit();
|
|
|
|
WiFi.mode(WIFI_OFF);
|
|
|
|
WiFi.forceSleepBegin();
|
|
|
|
delay(1); // yield();
|
|
|
|
digitalWrite(LED_BUILTIN, HIGH);
|
|
|
|
// Serial.printf("sendESPNOW() took %d ms\n", millis()-espnowmillis);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
void ICACHE_FLASH_ATTR sendValues() {
|
|
|
|
sd.temperature = dht.readTemperature();
|
|
|
|
sd.humidity = dht.readHumidity();
|
|
|
|
|
|
|
|
bmp.getPressure(&(sd.pressure));
|
|
|
|
sd.pressure /= 100;
|
|
|
|
bmp.getTemperature(&(sd.temp2));
|
|
|
|
|
|
|
|
sd.voltage = ESP.getVcc()/1024.0;
|
|
|
|
|
|
|
|
int sds_error = sds.read(&(sd.p25), &(sd.p10));
|
|
|
|
if(!sds_error) {
|
|
|
|
// Serial.println("SDS011 updated.");
|
|
|
|
} else {
|
|
|
|
// Serial.println("SDS011 no new values.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// sds.sleep();
|
|
|
|
|
|
|
|
sd.radioactivity = -1;
|
|
|
|
sd.cpm = geigeraverage.getAverage()*10;
|
|
|
|
float constexpr own_cpm = OWN_BACKGROUND_CPS * 60;
|
|
|
|
sd.radioactivity = (sd.cpm - own_cpm) * CONV_FACTOR;
|
|
|
|
|
|
|
|
Serial.printf("Temperature : %6.2f°C (DHT22)\n", sd.temperature);
|
|
|
|
Serial.printf("Humidity : %6.2f%% (DHT22)\n", sd.humidity);
|
|
|
|
Serial.printf("Temperature : %6.2f°C (BMP180)\n", sd.temp2);
|
|
|
|
Serial.printf("Pressure : %6.2fhPa (BMP180)\n", sd.pressure);
|
|
|
|
if (!sds_error) Serial.printf("Particles 10 : %6.2fµg/m³ (SDS011)\n", sd.p10);
|
|
|
|
if (!sds_error) Serial.printf("Particles 2.5: %6.2fµg/m³ (SDS011)\n", sd.p25);
|
|
|
|
if (sd.cpm > 0) Serial.printf("Radiation : %6.2fµSv/h (J305)\n", sd.radioactivity);
|
|
|
|
|
|
|
|
DynamicJsonBuffer jsonBuffer;
|
|
|
|
JsonArray& array = jsonBuffer.createArray();
|
|
|
|
DynamicJsonBuffer jsonBuffer2;
|
|
|
|
JsonObject& root = jsonBuffer.createObject();
|
|
|
|
|
|
|
|
JsonObject& temperatureObject = array.createNestedObject();
|
|
|
|
temperatureObject["sensor"] = SENSOR1_ID;
|
|
|
|
temperatureObject["value"] = sd.temperature;
|
|
|
|
root["temperature"] = sd.temperature;
|
|
|
|
|
|
|
|
JsonObject& humidityObject = array.createNestedObject();
|
|
|
|
humidityObject["sensor"] = SENSOR2_ID;
|
|
|
|
humidityObject["value"] = sd.humidity;
|
|
|
|
root["humidity"] = sd.humidity;
|
|
|
|
|
|
|
|
JsonObject& pressureObject = array.createNestedObject();
|
|
|
|
pressureObject["sensor"] = SENSOR3_ID;
|
|
|
|
pressureObject["value"] = sd.pressure;
|
|
|
|
root["pressure"] = sd.pressure;
|
|
|
|
|
|
|
|
if (!sds_error) {
|
|
|
|
JsonObject& pm10Object = array.createNestedObject();
|
|
|
|
pm10Object["sensor"] = SENSOR4_ID;
|
|
|
|
pm10Object["value"] = sd.p10;
|
|
|
|
root["pm10"] = sd.p10;
|
|
|
|
|
|
|
|
JsonObject& pm25Object = array.createNestedObject();
|
|
|
|
pm25Object["sensor"] = SENSOR5_ID;
|
|
|
|
pm25Object["value"] = sd.p25;
|
|
|
|
root["pm2.5"] = sd.p25;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sd.cpm > 0) {
|
|
|
|
JsonObject& cpmObject = array.createNestedObject();
|
|
|
|
cpmObject["sensor"] = SENSOR6_ID;
|
|
|
|
cpmObject["value"] = sd.radioactivity;
|
|
|
|
root["cpm"] = sd.cpm;
|
|
|
|
root["radioactivity"] = sd.radioactivity;
|
|
|
|
}
|
|
|
|
|
|
|
|
JsonObject& voltageObject = array.createNestedObject();
|
|
|
|
voltageObject["sensor"] = SENSOR7_ID;
|
|
|
|
voltageObject["value"] = sd.voltage;
|
|
|
|
root["voltage"] = sd.voltage;
|
|
|
|
|
|
|
|
JsonObject& rssiObject = array.createNestedObject();
|
|
|
|
rssiObject["sensor"] = SENSOR8_ID;
|
|
|
|
rssiObject["value"] = sd.rssi;
|
|
|
|
root["rssi"] = sd.rssi;
|
|
|
|
|
|
|
|
|
|
|
|
char buffer[500];
|
|
|
|
array.printTo(buffer, sizeof(buffer));
|
|
|
|
|
|
|
|
WiFi.forceSleepWake();
|
|
|
|
delay(1); // yield();
|
|
|
|
WiFi.persistent(false); // don't load and save credentials to flash
|
|
|
|
WiFi.mode(WIFI_STA);
|
|
|
|
wifiMulti.addAP("nether.net", "password1");
|
|
|
|
wifiMulti.addAP("LNet", "password2");
|
|
|
|
wifiMulti.addAP("hw1_gast", "password3");
|
|
|
|
wifiMulti.addAP("Freifunk", "");
|
|
|
|
|
|
|
|
if ( ip != INADDR_NONE && dns != INADDR_NONE && gateway != INADDR_NONE && subnet != INADDR_NONE
|
|
|
|
&& ((ip[0] == 192 && ip[1] == 168) || (ip[0] == 172 && ip[1] == 16))
|
|
|
|
&& strlen(ssid) > 0 && strlen(password) > 0
|
|
|
|
&& (millis() - last_dhcp < dhcp_interval)
|
|
|
|
) {
|
|
|
|
|
|
|
|
Serial.println("static ip");
|
|
|
|
WiFi.config(ip, dns, gateway, subnet);
|
|
|
|
WiFi.begin(ssid, password);
|
|
|
|
int tries = 0;
|
|
|
|
constexpr unsigned int retry_delay = 500;
|
|
|
|
constexpr unsigned int max_retry_delay = 10000;
|
|
|
|
while (WiFi.status() != WL_CONNECTED) {
|
|
|
|
tries++;
|
|
|
|
Serial.print(".");
|
|
|
|
if (tries*retry_delay >= max_retry_delay) {
|
|
|
|
Serial.println(" [ERROR]");
|
|
|
|
Serial.println("Rebooting..");
|
|
|
|
ESP.restart();
|
|
|
|
}
|
|
|
|
delay(retry_delay);
|
|
|
|
}
|
|
|
|
Serial.println(" [CONNECTED, static]");
|
|
|
|
Serial.print("IP address: ");
|
|
|
|
Serial.println(WiFi.localIP());
|
|
|
|
|
|
|
|
} else {
|
|
|
|
Serial.println("dhcp");
|
|
|
|
int tries = 0;
|
|
|
|
constexpr unsigned int retry_delay = 500;
|
|
|
|
constexpr unsigned int max_retry_delay = 12000;
|
|
|
|
while (wifiMulti.run() != WL_CONNECTED) {
|
|
|
|
tries++;
|
|
|
|
Serial.print(".");
|
|
|
|
if (tries*retry_delay >= max_retry_delay) {
|
|
|
|
Serial.println(" [ERROR]");
|
|
|
|
Serial.println("Rebooting..");
|
|
|
|
ESP.restart();
|
|
|
|
}
|
|
|
|
delay(retry_delay);
|
|
|
|
}
|
|
|
|
Serial.println(" [CONNECTED, dhcp]");
|
|
|
|
Serial.print("IP address: ");
|
|
|
|
Serial.println(WiFi.localIP());
|
|
|
|
|
|
|
|
ip = WiFi.localIP();
|
|
|
|
dns = WiFi.dnsIP();
|
|
|
|
gateway = WiFi.gatewayIP();
|
|
|
|
subnet = WiFi.subnetMask();
|
|
|
|
strncpy(ssid, WiFi.SSID().c_str(), 64);
|
|
|
|
strncpy(password, WiFi.psk().c_str(), 64);
|
|
|
|
last_dhcp = millis();
|
|
|
|
}
|
|
|
|
|
|
|
|
sd.rssi = WiFi.RSSI();
|
|
|
|
|
|
|
|
int httpCode = 0;
|
|
|
|
for (int tries=0; tries<3 && httpCode != HTTP_CODE_CREATED; tries++) {
|
|
|
|
|
|
|
|
HTTPClient httpclient;
|
|
|
|
char url[100];
|
|
|
|
sprintf(url, "http://%s/boxes/%s/data", server, SENSEBOX_ID);
|
|
|
|
|
|
|
|
httpclient.begin(url);
|
|
|
|
httpclient.addHeader("Content-Type", "application/json");
|
|
|
|
|
|
|
|
httpCode = httpclient.POST(buffer);
|
|
|
|
|
|
|
|
if (httpCode > 0) {
|
|
|
|
if (httpCode == HTTP_CODE_CREATED) {
|
|
|
|
httpclient.writeToStream(&Serial);
|
|
|
|
Serial.println();
|
|
|
|
} else {
|
|
|
|
Serial.printf("[HTTP] POST... failed, error: %s\n", httpclient.errorToString(httpCode).c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
httpclient.end();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
WiFiClientSecure net;
|
|
|
|
MQTTClient mqttclient(MQTT_MAX_PACKET_SIZE);
|
|
|
|
|
|
|
|
mqttclient.begin(mqttserver, 8883, net);
|
|
|
|
int tries = 0;
|
|
|
|
constexpr unsigned int retry_delay = 500;
|
|
|
|
constexpr unsigned int max_retry_delay = 5000;
|
|
|
|
while (!mqttclient.connect(mqttusername, mqttusername, mqttpassword)) {
|
|
|
|
tries++;
|
|
|
|
Serial.print(".");
|
|
|
|
if (tries*retry_delay >= max_retry_delay) {
|
|
|
|
Serial.println(" [ERROR]");
|
|
|
|
Serial.println("Rebooting..");
|
|
|
|
ESP.restart();
|
|
|
|
}
|
|
|
|
delay(retry_delay);
|
|
|
|
}
|
|
|
|
|
|
|
|
mqttclient.loop();
|
|
|
|
root.printTo(buffer, sizeof(buffer));
|
|
|
|
if (mqttclient.publish("sensor/esp-weatherstation/01/json", buffer, strlen(buffer))) {
|
|
|
|
Serial.println("mqtt done");
|
|
|
|
} else {
|
|
|
|
Serial.println("mqtt failed");
|
|
|
|
}
|
|
|
|
mqttclient.loop();
|
|
|
|
mqttclient.disconnect();
|
|
|
|
|
|
|
|
last_wifi_activity = millis();
|
|
|
|
WiFi.disconnect();
|
|
|
|
WiFi.mode(WIFI_OFF);
|
|
|
|
WiFi.forceSleepBegin();
|
|
|
|
delay(1); // yield();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ICACHE_RAM_ATTR ISR_geiger_impulse() {
|
|
|
|
geiger_counts++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// will be called every 6 seconds
|
|
|
|
void timerCallback(void *pArg) {
|
|
|
|
//Serial.printf("running average counts: %d average: %6.2f\n", geiger_counts, geigeraverage.getAverage());
|
|
|
|
geigeraverage.addValue(geiger_counts);
|
|
|
|
geiger_counts = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void setup() {
|
|
|
|
Serial.begin(115200);
|
|
|
|
|
|
|
|
pinMode(LED_BUILTIN, OUTPUT);
|
|
|
|
digitalWrite(LED_BUILTIN, HIGH); // turn OFF board led
|
|
|
|
|
|
|
|
sds.begin(SDS_TX, SDS_RX);
|
|
|
|
sds.mode_mon_300();
|
|
|
|
|
|
|
|
Wire.begin(BMP_SDA, BMP_SCL);
|
|
|
|
if (!bmp.begin(BMP085_MODE_STANDARD)) {
|
|
|
|
Serial.println("No valid BMP085 sensor!");
|
|
|
|
}
|
|
|
|
|
|
|
|
dht.begin();
|
|
|
|
|
|
|
|
pinMode(GEIGER_PIN, INPUT);
|
|
|
|
attachInterrupt(digitalPinToInterrupt(GEIGER_PIN), ISR_geiger_impulse, FALLING);
|
|
|
|
|
|
|
|
geigeraverage.clear();
|
|
|
|
geigeraverage.addValue(2);
|
|
|
|
os_timer_setfn(&Timer1, timerCallback, NULL);
|
|
|
|
os_timer_arm(&Timer1, 6000, true);
|
|
|
|
|
|
|
|
Serial.println("ready."); Serial.flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
void loop() {
|
|
|
|
//Serial.println(millis() - last_wifi_activity);
|
|
|
|
sendValues();
|
|
|
|
delay(postingInterval);
|
|
|
|
}
|