Created
February 12, 2017 23:04
-
-
Save corny/23ee508606bb7dd3579bfaba849230ba to your computer and use it in GitHub Desktop.
ESP8266 + SDS011 + BME280
This file contains hidden or 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 <ESP8266WiFi.h> | |
| #include <ESP8266WebServer.h> | |
| #include <WiFiManager.h> | |
| #include <WiFiClientSecure.h> | |
| #include <SoftwareSerial.h> | |
| #include <Wire.h> | |
| #include <Adafruit_BME280.h> | |
| #include "Sds011.h" | |
| // SDS011, der etwas teuerere Feinstaubsensor | |
| // Serial confusion: These definitions are based on SoftSerial | |
| // TX (transmitting) pin on one side goes to RX (receiving) pin on other side | |
| // SoftSerial RX PIN is D1 and goes to SDS TX | |
| // SoftSerial TX PIN is D2 and goes to SDS RX | |
| #define SDS_PIN_RX D1 | |
| #define SDS_PIN_TX D2 | |
| // BME280, Luftdruck-Sensor | |
| #define BME280_PIN_SCL D4 | |
| #define BME280_PIN_SDA D3 | |
| uint32_t esp_chipid; | |
| // Unit for timestamps and intervals is milliseconds | |
| unsigned long now; // current time | |
| unsigned long starttime = 0; // start time of the current measuring period | |
| const unsigned long sending_interval = 60*1000; | |
| // Wifi | |
| WiFiManager wifiManager; | |
| // SDS011 | |
| bool sds011_read = 1; | |
| SoftwareSerial serialSDS(SDS_PIN_RX, SDS_PIN_TX, false, 128); | |
| sds011::Sds011 sensorSDS(serialSDS); | |
| // BME280 | |
| bool bme280_read = 1; | |
| Adafruit_BME280 bme280; | |
| // Data for the luftdaten API endpoint | |
| const char* dusti_host = "api.luftdaten.info"; | |
| const int dusti_port = 80; | |
| const char* dusti_path = "/v1/push-sensor-data/"; | |
| struct { | |
| bool valid; | |
| float pm10; // 10 | |
| float pm25; // 2.5 | |
| } sds011_result = {}; | |
| struct { | |
| bool valid; | |
| float t; // Temperature | |
| float h; // Humidity | |
| float p; // Pressure | |
| } bme280_result = {}; | |
| void debugf(char *fmt, ... ){ | |
| char buf[128]; // resulting string limited to 128 chars | |
| va_list args; | |
| va_start (args, fmt ); | |
| vsnprintf(buf, 128, fmt, args); | |
| va_end (args); | |
| Serial.print(buf); | |
| } | |
| void debugf_float(char *fmt, float val){ | |
| char buf[15]; | |
| dtostrf(val, 13, 2, buf); | |
| debugf(fmt, buf); | |
| } | |
| /***************************************************************** | |
| /* convert float to string with a * | |
| /* precision of two decimal places * | |
| /*****************************************************************/ | |
| String Float2String(const float value) { | |
| // Convert a float to String with two decimals. | |
| char temp[13]; | |
| String s; | |
| dtostrf(value,9, 2, temp); | |
| s = String(temp); | |
| s.trim(); | |
| return s; | |
| } | |
| /***************************************************************** | |
| /* convert value to json string * | |
| /*****************************************************************/ | |
| String Value2Json(const String& type, const String& value) { | |
| String s = F("{\"value_type\":\"{t}\",\"value\":\"{v}\"},"); | |
| s.replace("{t}",type); | |
| s.replace("{v}",value); | |
| return s; | |
| } | |
| String Value2Json(const String& type, float value) { | |
| String s = F("{\"value_type\":\"{t}\",\"value\":{v}},"); | |
| s.replace("{t}",type); | |
| s.replace("{v}",Float2String(value)); | |
| return s; | |
| } | |
| bool initBME280() { | |
| Wire.pins(BME280_PIN_SDA, BME280_PIN_SCL); | |
| Wire.begin(BME280_PIN_SDA, BME280_PIN_SCL); | |
| return initBME280addr(0x76) || initBME280addr(0x77); | |
| } | |
| bool initBME280addr(char addr) { | |
| debugf("Trying BME280 sensor on 0x%02X ... ", addr); | |
| if (bme280.begin(addr)) { | |
| debugf("found\n"); | |
| return true; | |
| } else { | |
| debugf("not found\n"); | |
| return false; | |
| } | |
| } | |
| void readBME280() { | |
| debugf("Reading BME280\n"); | |
| bme280_result.t = bme280.readTemperature(); | |
| bme280_result.h = bme280.readHumidity(); | |
| bme280_result.p = bme280.readPressure(); | |
| bme280_result.valid = !isnan(bme280_result.t) && !isnan(bme280_result.h) && !isnan(bme280_result.p); | |
| if (bme280_result.valid) { | |
| debugf_float("Temperature: %s C\n", bme280_result.t); | |
| debugf_float("Humidity: %s %%\n", bme280_result.h); | |
| debugf_float("Pressure: %s hPa\n", bme280_result.p/100); | |
| } else { | |
| debugf("failed to read\n"); | |
| } | |
| } | |
| void initSDS(){ | |
| sensorSDS.set_sleep(false); | |
| sensorSDS.set_mode(sds011::QUERY); | |
| sensorSDS.set_sleep(true); | |
| } | |
| void readSDS() { | |
| int pm25, pm10; | |
| debugf("Reading SDS011\n"); | |
| sensorSDS.set_sleep(false); | |
| delay(1000); | |
| sds011_result.valid = sensorSDS.query_data_auto(&pm25, &pm10, 10); | |
| if (sds011_result.valid) { | |
| debugf("PM2.5: %d\n", pm10); | |
| debugf("PM10: %d\n", pm25); | |
| sds011_result.pm10 = float(pm10)/10; | |
| sds011_result.pm25 = float(pm25)/10; | |
| } else { | |
| debugf("failed to read\n"); | |
| } | |
| sensorSDS.set_sleep(true); | |
| } | |
| /***************************************************************** | |
| /* send data to rest api * | |
| /*****************************************************************/ | |
| void sendData(const String& body, const char* host, const int port, const char* path, const String& contentType) { | |
| debugf("connecting to %s:%d\n", host, port); | |
| String head = F("POST "); head += String(path); head += F(" HTTP/1.0\r\n"); | |
| #define append_head(key, value) head += String(F(key)) + ": " + value + "\r\n"; | |
| append_head("Host", host); | |
| append_head("Content-Type", contentType); | |
| append_head("Sensor", "esp8266-"+ esp_chipid); | |
| append_head("Content-Length", body.length()); | |
| #undef append_head | |
| // Use WiFiClient class to create TCP connections | |
| WiFiClient client; | |
| client.setNoDelay(true); | |
| if (!client.connect(host, port)) { | |
| debugf("connection failed\n"); | |
| return; | |
| } | |
| Serial.println(head); | |
| Serial.println(body); | |
| client.println(head); | |
| client.print(body); | |
| delay(10); | |
| // Read reply from server and print them | |
| while(client.available()){ | |
| char c = client.read(); | |
| Serial.print(c); | |
| } | |
| debugf("\nclosing connection\n"); | |
| yield(); | |
| } | |
| void readAndSend(){ | |
| if (sds011_read) readSDS(); | |
| if (bme280_read) readBME280(); | |
| String data = F("{\"software_version\": \"meine eigene\", \"sensordatavalues\":["); | |
| if (sds011_read && sds011_result.valid) { | |
| data += Value2Json(F("SDS_P1"), sds011_result.pm10); | |
| data += Value2Json(F("SDS_P2"), sds011_result.pm25); | |
| } | |
| if (bme280_read && bme280_result.valid) { | |
| data += Value2Json(F("temperature"), bme280_result.t); | |
| data += Value2Json(F("humidity"), bme280_result.h); | |
| data += Value2Json(F("pressure"), bme280_result.p); | |
| } | |
| if (!data.endsWith(",")){ | |
| debugf("No data available\n"); | |
| return; | |
| } | |
| data.remove(data.length()-1); | |
| data += "]}"; | |
| sendData(data, dusti_host, dusti_port, dusti_path, F("application/json")); | |
| } | |
| void setup() { | |
| Serial.begin(115200); // Output to USB/TTL | |
| serialSDS.begin(9600); | |
| esp_chipid = ESP.getChipId(); | |
| delay(10); | |
| debugf("\nESP8266 Chip-ID: 0x%06x / %d\n", esp_chipid, esp_chipid); | |
| wifiManager.autoConnect(); | |
| if (sds011_read) { | |
| Serial.print(F("SDS011 Firmware Version: ")); | |
| Serial.println(sensorSDS.firmware_version()); | |
| } | |
| if (bme280_read && !initBME280()) { | |
| debugf("Check BME280 wiring\n"); | |
| bme280_read = 0; | |
| } | |
| } | |
| void loop() { | |
| now = millis(); | |
| bool send_now = starttime == 0 || (now-starttime) > sending_interval; | |
| if (send_now) { | |
| readAndSend(); | |
| starttime = now; // update the start time | |
| } | |
| yield(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment