DoorBellGateway/src/main.cpp

319 lines
9.3 KiB
C++

#include <Arduino.h>
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <string.h>
// Update these with values suitable for your network.
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xEA };
IPAddress ip(192, 168, 89, 205);
IPAddress mqttServerIP(192, 168, 89, 95);
const char *mqttUser = "default";
const char *mqttPassword = "12345678";
const char *doorBellTopic = "doorBell";
const char *postboxFlapTopic = "postboxFlap";
const char *postboxDoorTopic = "postboxDoor";
const char *doorBellTriggerSoundTopic = "doorBellTrigger";
const char *postboxLightTopic = "postboxLight";
EthernetClient ethClient;
EthernetServer server(80);
PubSubClient mqttClient(ethClient);
#define DOOR_BELL_BUTTON_PIN 7
#define POSTBOX_FLAP_PIN 5
#define POSTBOX_DOOR_PIN 3
// Output pins
#define LED_PIN 13
#define DOOR_BELL_BUZZER_PIN 6
#define POSTBOX_LIGHT_PIN 4
struct {
const uint8_t pin;
const unsigned long quietAfterTriggerFor_ms;
unsigned long lastTrigger_ts;
bool isTriggered;
const char *topic;
} myInputs[] = {
{ DOOR_BELL_BUTTON_PIN, 500, 0 , false, doorBellTopic },
{ POSTBOX_FLAP_PIN, 1000, 0, false, postboxFlapTopic },
{ POSTBOX_DOOR_PIN, 1000, 0, false, postboxDoorTopic }
};
#define NUM_INPUTS (sizeof(myInputs) / sizeof(myInputs[0]))
int ledState = LOW;
struct {
const uint8_t pin;
const unsigned long triggerTime_ms;
bool isTriggered;
unsigned long lastTrigger_ts;
const char *listenTopic; // can be triggered by mqtt message as well
} myOutputs[] = {
{ DOOR_BELL_BUZZER_PIN, 500, false, 0, doorBellTriggerSoundTopic},
{ POSTBOX_LIGHT_PIN, 30000, false, 0, postboxLightTopic}
};
#define NUM_OUTPUTS (sizeof(myOutputs) / sizeof(myOutputs[0]))
void switchOutputOn(unsigned int outputNumber) {
Serial.print(F("Switch output on: "));
Serial.print(outputNumber);
Serial.print(F(" (pin "));
Serial.print(myOutputs[outputNumber].pin);
Serial.print(F(") topic "));
Serial.println(myOutputs[outputNumber].listenTopic);
digitalWrite(myOutputs[outputNumber].pin, LOW);
myOutputs[outputNumber].isTriggered = true;
myOutputs[outputNumber].lastTrigger_ts = millis();
}
void switchOutputOff(unsigned int outputNumber) {
Serial.print(F("Switch output off: "));
Serial.print(outputNumber);
Serial.print(F(" (pin "));
Serial.print(myOutputs[outputNumber].pin);
Serial.print(F(") topic "));
Serial.println(myOutputs[outputNumber].listenTopic);
digitalWrite(myOutputs[outputNumber].pin, HIGH);
myOutputs[outputNumber].isTriggered = false;
}
void checkOutputAutoOff() {
static bool headerDone = false;
for (unsigned int outputNumber=0; outputNumber<NUM_OUTPUTS; ++outputNumber) {
if (myOutputs[outputNumber].isTriggered) {
if (!headerDone) {
Serial.print(F("checkOutputAutoOff: ts=")); Serial.println(millis());
headerDone = true;
}
//Serial.print(outputNumber); Serial.print(": ");
//Serial.println(myOutputs[outputNumber].lastTrigger_ts + myOutputs[outputNumber].triggerTime_ms);
if ((myOutputs[outputNumber].lastTrigger_ts + myOutputs[outputNumber].triggerTime_ms) < millis()) {
Serial.println(F("Auto off output:"));
switchOutputOff(outputNumber);
}
}
}
}
void mqttReceiveMessage(char* topic, uint8_t* payload, unsigned int length) {
Serial.print(F("Message arrived ["));
Serial.print(topic);
Serial.print(F("] "));
Serial.print(length);
Serial.print(F(" bytes: "));
Serial.println((char *) payload);
for (unsigned int i=0;i<length;i++) {
Serial.print((char)payload[i]);
}
Serial.println();
bool handlerFound = false;
for (unsigned int i=0; i<NUM_OUTPUTS; ++i) {
if (strcmp(myOutputs[i].listenTopic, topic) == 0) {
handlerFound = true;
if (strncmp((char *) payload, "on", length < 2 ? length : 2) == 0) {
switchOutputOn(i);
} else if (strncmp((char *) payload, "off", length < 3 ? length : 3) == 0) {
switchOutputOff(i);
} else {
Serial.print(F("Unknown MQTT message payload received, topic: "));
Serial.print(topic);
Serial.print(F(", payload: "));
Serial.println((char *) payload);
}
}
}
if (!handlerFound) {
Serial.print(F("Unknown MQTT message received, topic: "));
Serial.print(topic);
Serial.print(F(", payload: "));
Serial.println((char *) payload);
}
}
void reconnect() {
// Loop until we're reconnected
while (!mqttClient.connected()) {
Serial.print(F("Attempting MQTT connection..."));
// Attempt to connect
if (mqttClient.connect("doorBellGateway", mqttUser, mqttPassword)) {
Serial.println(F("connected"));
// Once connected, publish an announcement...
// mqttClient.publish("hello","hello world");
// ... and resubscribe
Serial.println(F("MQTT subscribe doorbell"));
if (!mqttClient.subscribe("doorbell")) {
Serial.println(F("ERROR: Failed to subscribe"));
}
for (unsigned int i=0; i<NUM_OUTPUTS; ++i) {
Serial.print(F("MQTT subscribe ")); Serial.println(myOutputs[i].listenTopic);
if (!mqttClient.subscribe(myOutputs[i].listenTopic)) {
Serial.println(F("ERROR: Failed to subscribe"));
}
}
} else {
Serial.print(F("failed, rc="));
Serial.print(mqttClient.state());
Serial.println(F(" try again in 5 seconds"));
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void sendMqttMessage(const char *topic, const char *message) {
reconnect();
mqttClient.publish(topic, message);
}
void setup()
{
// Initialize serial port
Serial.begin(9600);
while (!Serial) continue;
Serial.println(F("Arduino Door Bell MQTT Gateway started."));
mqttClient.setServer(mqttServerIP, 1883);
mqttClient.setCallback(mqttReceiveMessage);
// Initialize Ethernet libary
if (!Ethernet.begin(mac)) {
Serial.println(F("Failed to initialize Ethernet library"));
return;
}
// Start to listen
server.begin();
Serial.println(F("Server is ready."));
Serial.print(F("Please connect to http://"));
Serial.println(Ethernet.localIP());
// setup the input and output pins
for (unsigned int i = 0; i < NUM_INPUTS; i++) {
pinMode(myInputs[i].pin, INPUT_PULLUP);
}
// Setup the LED :
for (unsigned int i=0; i < NUM_OUTPUTS; ++i) {
pinMode(myOutputs[i].pin, OUTPUT);
switchOutputOff(i);
}
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, ledState);
}
void checkInputSignals() {
bool needToToggleLed = false;
for (unsigned int i = 0; i < NUM_INPUTS; i++) {
if (millis()-myInputs[i].lastTrigger_ts > myInputs[i].quietAfterTriggerFor_ms) {
if (!digitalRead(myInputs[i].pin)) {
// input is triggered, compare with existing state
if (myInputs[i].isTriggered) {
// was triggered as well, could send a retrigger now
Serial.print(F("Re-Trigger pin ")); Serial.println(myInputs[i].pin);
sendMqttMessage(myInputs[i].topic, "re-trigger");
needToToggleLed = true;
} else {
// handle trigger now
myInputs[i].isTriggered = true;
// send Trigger
Serial.print(F("Trigger pin ")); Serial.println(myInputs[i].pin);
sendMqttMessage(myInputs[i].topic, "trigger");
needToToggleLed = true;
}
myInputs[i].lastTrigger_ts = millis();
} else {
// no trigger seen, if it was before, send a "released" message
if (myInputs[i].isTriggered) {
myInputs[i].isTriggered = false;
myInputs[i].lastTrigger_ts = millis();
Serial.print(F("Released pin ")); Serial.println(myInputs[i].pin);
sendMqttMessage(myInputs[i].topic, "release");
needToToggleLed = true;
}
}
}
}
// if a LED toggle has been flagged :
if ( needToToggleLed ) {
// Toggle LED state :
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
}
}
void checkWebRequest() {
// Wait for an incomming connection
EthernetClient client = server.available();
// Do we have a client?
if (!client) return;
Serial.println(F("New client on web ingress"));
// Read the request (we ignore the content in this example)
while (client.available()) client.read();
// Allocate a temporary JsonDocument
// Use arduinojson.org/v6/assistant to compute the capacity.
StaticJsonDocument<500> doc;
for (unsigned int pin = 0; pin < NUM_INPUTS; pin++) {
doc[myInputs[pin].topic] = myInputs[pin].isTriggered ? "triggered" : "released";
}
// Create the "digital" array
JsonArray digitalValues = doc.createNestedArray("digitalPin");
for (unsigned int pin = 0; pin < 14; pin++) {
// Read the digital input
int value = digitalRead(pin);
// Add the value at the end of the array
digitalValues.add(value);
}
Serial.print(F("Sending: "));
serializeJson(doc, Serial);
Serial.println();
// Write response headers
client.println(F("HTTP/1.0 200 OK"));
client.println(F("Content-Type: application/json"));
client.println(F("Connection: close"));
client.print(F("Content-Length: "));
// client.println(measureJsonPretty(doc));
client.println(measureJson(doc));
client.println();
// Write JSON document
//serializeJsonPretty(doc, client);
serializeJson(doc, client);
// Disconnect
client.stop();
}
void loop() {
// Client
if (!mqttClient.connected()) {
reconnect();
}
mqttClient.loop();
checkInputSignals();
checkWebRequest();
checkOutputAutoOff();
}