Initial commit
This commit is contained in:
318
src/main.cpp
Normal file
318
src/main.cpp
Normal file
@@ -0,0 +1,318 @@
|
||||
#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();
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user