Skip to content

Instantly share code, notes, and snippets.

@jtolio
Last active January 3, 2021 15:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jtolio/1e1bf4219efd2e37bcbd6db114b6fcac to your computer and use it in GitHub Desktop.
Save jtolio/1e1bf4219efd2e37bcbd6db114b6fcac to your computer and use it in GitHub Desktop.
esp8266 dht22 sensor
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
const char* WIFI_SSID = "<redacted>";
const char* WIFI_PASS = "<redacted>";
const char* POST_URL = "http://thermostat.lan:8081/sensor/";
const char* SENSOR_NAME = "bedroom";
DHT dht(5, DHT22);
String url(float temp, float humidity) {
String rv = POST_URL;
rv += "?type=dht22&name=";
rv += SENSOR_NAME;
rv += "&temp=";
rv += temp;
rv += "&hum=";
rv += humidity;
return rv;
}
int connect() {
if (WiFi.status() == WL_CONNECTED) {
return 0;
}
WiFi.persistent(false);
WiFi.mode(WIFI_OFF);
WiFi.mode(WIFI_STA);
Serial.print("Connecting to: ");
Serial.println(WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASS);
unsigned long connectStart = millis();
while (true) {
wl_status_t wstatus = WiFi.status();
if (wstatus == WL_CONNECTED) {
break;
}
if (wstatus == WL_CONNECT_FAILED) {
Serial.println("Connection failed.");
return -1;
}
if (millis() - connectStart > 15000) {
Serial.print("Timed out. Status: ");
Serial.println(wstatus);
return -1;
}
delay(500);
}
Serial.print("WiFi connected. IP: ");
Serial.println(WiFi.localIP());
return 0;
}
void setup() {
Serial.begin(9600);
Serial.setTimeout(2000);
while (!Serial) {}
Serial.println("awake");
dht.begin();
}
void post() {
float temp = dht.readTemperature();
float humid = dht.readHumidity();
if (isnan(temp) || isnan(humid)) {
Serial.println("failed to read from sensor");
return;
}
HTTPClient http;
http.begin(url(temp, humid));
int sc = http.POST(0, 0);
switch (sc) {
case HTTP_CODE_OK:
break;
default:
Serial.print("Post failed. Status: ");
Serial.println(sc);
}
http.end();
}
void loop() {
delay(10000);
if (connect() == 0) {
post();
} else {
Serial.print("WiFi status: ");
Serial.println(WiFi.status());
}
}
package main
import (
"flag"
"fmt"
"log"
"net/http"
"net/url"
"os/exec"
"path/filepath"
"strconv"
"strings"
)
// this http server listens for url-encoded sensors
// and stores them to rrdtool databases.
//
// assuming you have a sensor of type 'dht22' named
// 'bedroom', you can create an rrdtool database that
// expects temperature and humidity updates every 10
// seconds, no longer than 20 seconds, like:
//
// mkdir dht22
// rrdtool create dht22/bedroom.rrd --step 10 \
// DS:temp:GAUGE:20:U:U \
// DS:hum:GAUGE:20:U:U \
// RRA:AVERAGE:0.5:1:8640 \
// RRA:MIN:0.5:360:2400 \
// RRA:MAX:0.5:360:2400 \
// RRA:AVERAGE:0.5:360:2400
//
// this rrdtool db will keep track of the last day's
// worth of updates, and then roll up older updates
// for 100 days by hour with average, max, and min
//
// you can make a graph of the last hour like:
//
// rrdtool graph out.png --start -1h \
// DEF:temp_c=dht22/bedroom.rrd:temp:AVERAGE \
// DEF:hum=dht22/bedroom.rrd:hum:AVERAGE \
// CDEF:temp_f=temp_c,9,*,5,/,32,+ \
// LINE2:temp_f#FF0000 \
// LINE2:hum#0000FF
//
var (
flagAddr = flag.String("addr", ":8081", "listen address")
flagBase = flag.String("base", ".", "base path for rrd files")
)
func copyValues(m url.Values) url.Values {
rv := make(url.Values, len(m))
for k, v := range m {
rv[k] = append([]string(nil), v...)
}
return rv
}
func clean(val string) string {
return strings.Map(func(r rune) rune {
if (r >= 'A' && r <= 'Z') ||
(r >= 'a' && r <= 'z') ||
(r >= '0' && r <= '9') {
return r
}
return -1
}, val)
}
func main() {
flag.Parse()
panic(http.ListenAndServe(*flagAddr, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
log.Printf("failed parsing: %+v", err)
w.WriteHeader(400)
return
}
form := copyValues(r.Form)
sensorType := clean(form.Get("type"))
delete(form, "type")
sensorName := clean(form.Get("name"))
delete(form, "name")
if sensorType == "" || sensorName == "" {
log.Printf("missing sensor type or name")
w.WriteHeader(400)
return
}
keys := make([]string, 0, len(form))
vals := make([]string, 0, len(form))
for name := range form {
val, err := strconv.ParseFloat(form.Get(name), 64)
if err != nil {
log.Printf("failed parsing %q: %+v", r.URL.String(), err)
w.WriteHeader(400)
return
}
cleaned := clean(name)
if cleaned == "" {
log.Printf("empty key name")
w.WriteHeader(400)
return
}
keys = append(keys, cleaned)
vals = append(vals, fmt.Sprint(val))
}
output, err := exec.Command("rrdtool", "update",
filepath.Join(*flagBase, sensorType, sensorName+".rrd"),
"-t", strings.Join(keys, ":"),
"N:"+strings.Join(vals, ":")).CombinedOutput()
if err != nil {
log.Printf("failed updating: %+v\n%q", err, string(output))
w.WriteHeader(500)
return
}
w.WriteHeader(200)
})))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment