Skip to content

Instantly share code, notes, and snippets.

@corny
Created February 12, 2017 23:04
Show Gist options
  • Select an option

  • Save corny/23ee508606bb7dd3579bfaba849230ba to your computer and use it in GitHub Desktop.

Select an option

Save corny/23ee508606bb7dd3579bfaba849230ba to your computer and use it in GitHub Desktop.
ESP8266 + SDS011 + BME280
#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