gpio-webhook-arduino/smartswitch.ino

152 lines
3.9 KiB
Arduino
Raw Normal View History

2020-05-06 21:05:51 +00:00
#include "config.h"
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
2020-05-06 21:05:51 +00:00
// how long to delay between each request to the
// server, in ms.
const int REQUEST_RATE = 1500;
// how long to keep a momentary switch high.
// results approximate, and must be shorter than REQUEST_RATE.
const int MOMENTARY_PERIOD = 250;
// Custom pin status for momentary switch that shouldn't
// get toggled again yet.
const int OLD_HIGH = 255;
// Holds the current state of all the pins.
// Gets written to by parse_webhook_response().
// Gets read from by set_pins().
int pin_states[sizeof(PIN_MAP)] = {0};
// Just a static client object to avoid memory allocations.
HTTPClient client;
void init_serial() {
Serial.begin(9600);
}
void init_wifi() {
Serial.println("Attempting to (re)connect to wifi");
WiFi.disconnect();
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
int elapsed = 0;
while (WiFi.status() != WL_CONNECTED) {
Serial.print("Wifi connecting for ");
Serial.print(elapsed);
Serial.println(" seconds");
elapsed++;
delay(1000);
}
Serial.println("Wifi connected");
}
void setup() {
init_serial();
init_wifi();
}
void loop() {
poll_server();
set_pins();
int elapsed = handle_momentary();
Serial.flush();
delay(REQUEST_RATE - elapsed);
}
// if any momentary pins went high this loop, we
// wait the agreed upon delay and then turn them off.
// if any momentary data input went low, we "reset"
// the pin.
int handle_momentary() {
for (int i = 0; i < sizeof(pin_states); i++) {
// if [the pin is momentary] and [it was set high this cycle]
if (PIN_MAP[i][1] == 0 && pin_states[i] == HIGH) {
// wait for the specified amount of time, then set the pin
// back to low.
delay(MOMENTARY_PERIOD);
digitalWrite(PIN_MAP[i][0], LOW);
pin_states[i] = OLD_HIGH;
}
}
}
// poll_server makes the actual HTTP request and handles
// the result. It returns false if an error occurred.
bool poll_server() {
client.begin(WEBHOOK_URL);
int status = client.GET();
if (status < 0) {
Serial.print("Client error communicating with server: ");
Serial.println(status);
return false;
}
if (status <= 400) {
Serial.print("Received HTTP status code ");
Serial.println(status);
return false;
}
parse_webhook_response(client.getString());
}
void parse_webhook_response(String raw_data) {
// int data[sizeof(PIN_MAP)] = {-1};
// // TODO: split the data and turn it into numbers
// while (raw_data.length() > 0) {
// // read to a newline
// String line = raw_data.substring(0, raw_data.indexOf('\n'));
// raw_data.remove(0, raw_data.indexOf('\n') + 1);
// // extract data from the line and add it to our incoming data array
// int index = line.substring(0, raw_data.indexOf(' ')).toInt();
// int state = line.substring(raw_data.indexOf(' ')+1).toInt();
// data[index] = state;
// }
StaticJsonDocument<256> doc;
DeserializationError err = deserializeJson(doc, raw_data);
if (err) {
Serial.print("Couldn't parse json data. Error code: ");
Serial.println(err.c_str());
2020-05-06 21:05:51 +00:00
}
JsonArray data = doc.as<JsonArray>();
int i = 0;
for (JsonVariant item : data) {
2020-05-06 21:05:51 +00:00
if (tripped(i) && data[i] == HIGH) {
continue;
}
pin_states[i] = item;
i++;
}
if (data.size() < sizeof(pin_states) / sizeof(*pin_states)) {
Serial.println("Did not receive data for all pins.");
2020-05-06 21:05:51 +00:00
}
}
// Uses the current pin_states to actually write to the pins
void set_pins() {
for (int i = 0; i < sizeof(PIN_MAP); i++) {
if (tripped(i)) {
continue;
}
digitalWrite(PIN_MAP[i][0], pin_states[i]);
}
}
// returns true if pin at map index i (not pinout numbering) is momentary
// and has been tripped (received a 1 without a subsequent 0 yet)
// returns false otherwise
bool tripped(int i) {
return PIN_MAP[i][1] == 0 && pin_states[i] == OLD_HIGH;
}