Last active
January 3, 2021 15:35
-
-
Save jtolio/1e1bf4219efd2e37bcbd6db114b6fcac to your computer and use it in GitHub Desktop.
esp8266 dht22 sensor
This file contains 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 <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()); | |
} | |
} |
This file contains 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
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