checkin
This commit is contained in:
114
firmware/main_mcu/src/main.cpp
Normal file
114
firmware/main_mcu/src/main.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <Wire.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "mqtt.h"
|
||||
#include "util.h"
|
||||
|
||||
#define BTN_PIN D0
|
||||
#define RED_LED_PIN 4
|
||||
#define GREEN_LED_PIN 5
|
||||
#define BLUE_LED_PIN 12
|
||||
|
||||
#define LONG_PRESS_MILLIS 1000
|
||||
|
||||
static WiFiClient wifiClient;
|
||||
static PubSubClient mqttClient;
|
||||
|
||||
void blink(int pin, int times, int onMillis, int offMillis) {
|
||||
// for (int i = 0; i < times; i++) {
|
||||
// digitalWrite(pin, HIGH);
|
||||
// delay(onMillis);
|
||||
// digitalWrite(pin, LOW);
|
||||
// delay(offMillis);
|
||||
// }
|
||||
}
|
||||
|
||||
void fail(const char *message) {
|
||||
PRINT(message);
|
||||
blink(RED_LED_PIN, 3, 50, 50);
|
||||
// TODO: go back to sleep
|
||||
}
|
||||
|
||||
void success() {
|
||||
blink(GREEN_LED_PIN, 1, 50, 0);
|
||||
}
|
||||
|
||||
static void initConnection() {
|
||||
initWifi(wifiClient);
|
||||
initMqtt(mqttClient, wifiClient);
|
||||
}
|
||||
|
||||
void handleShortButtonWakeup() {
|
||||
initConnection();
|
||||
|
||||
if (!publishTriggerMessage(mqttClient, String("short_press"))) {
|
||||
fail("failed to publish trigger message\n");
|
||||
}
|
||||
|
||||
if (!publishBatteryMessage(mqttClient, 100)) {
|
||||
fail("Failed to publish battery message\n");
|
||||
}
|
||||
}
|
||||
|
||||
void handleLongButtonWakeup() {
|
||||
initConnection();
|
||||
if (!publishHABatteryDiscoveryConfig(mqttClient)) {
|
||||
fail("Failed to publish battery config\n");
|
||||
}
|
||||
if (!publishHATriggerDiscoveryConfig(mqttClient)) {
|
||||
fail("Failed to publish trigger config\n");
|
||||
}
|
||||
if (!publishBatteryMessage(mqttClient, 100)) {
|
||||
fail("Failed to publish trigger message\n");
|
||||
}
|
||||
|
||||
success();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// Sample the input button ASAP.
|
||||
// pinMode(BTN_PIN, INPUT);
|
||||
// bool btn_value = digitalRead(BTN_PIN);
|
||||
|
||||
unsigned long startTimingMillis = millis();
|
||||
|
||||
pinMode(RED_LED_PIN, OUTPUT);
|
||||
pinMode(BLUE_LED_PIN, OUTPUT);
|
||||
pinMode(GREEN_LED_PIN, OUTPUT);
|
||||
|
||||
#if defined(DEBUG_SKETCH) || defined(DEBUG_SKETCH_TIMING)
|
||||
Serial.begin(9600);
|
||||
#endif
|
||||
|
||||
PRINTLN("\nBeginning setup\n");
|
||||
PRINT("Device name:");
|
||||
PRINTLN(getDeviceHumanName());
|
||||
|
||||
handleShortButtonWakeup();
|
||||
|
||||
digitalWrite(RED_LED_PIN, LOW);
|
||||
digitalWrite(GREEN_LED_PIN, LOW);
|
||||
digitalWrite(BLUE_LED_PIN, LOW);
|
||||
|
||||
// We've done our work- back to sleep.
|
||||
PRINT("Going to sleep...\n");
|
||||
#if defined(DEBUG_SKETCH_TIMING)
|
||||
unsigned long endTimeMillis = millis();
|
||||
Serial.print("Ran for " + String(endTimeMillis - startTimingMillis) + " milliseconds\n");
|
||||
#endif
|
||||
|
||||
// Drain all of our pending requests
|
||||
mqttClient.disconnect();
|
||||
wifiClient.flush();
|
||||
while (mqttClient.state() != -1) {
|
||||
yield();
|
||||
}
|
||||
ESP.deepSleep(0, RFMode::RF_NO_CAL);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
}
|
||||
146
firmware/main_mcu/src/mqtt.cpp
Normal file
146
firmware/main_mcu/src/mqtt.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
#include "mqtt.h"
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
|
||||
#define MAX_PACKET_SIZE 1024
|
||||
#define TIMEOUT_MILLIS 1000
|
||||
#define TRIGGER_KEY "event"
|
||||
|
||||
#define VALUE_TEMPLATE(key) ("{{ value_json." key " }}")
|
||||
|
||||
// TODO: This should really be a class, this is kinda busted
|
||||
static bool isDone = true;
|
||||
|
||||
void initMqtt(PubSubClient &mqttClient, WiFiClient &wifiClient) {
|
||||
mqttClient.setServer(CONFIG_MQTT_SERVER, CONFIG_MQTT_SERVER_PORT);
|
||||
mqttClient.setClient(wifiClient);
|
||||
mqttClient.setBufferSize(MAX_PACKET_SIZE);
|
||||
mqttClient.setCallback([](char *, unsigned char*, unsigned int){
|
||||
PRINTLN("Message sent!");
|
||||
isDone = true;
|
||||
});
|
||||
|
||||
PRINT("Connecting to MQTT broker...");
|
||||
while (!mqttClient.connected()) {
|
||||
PRINT(".");
|
||||
mqttClient.connect(getDeviceMachineName().c_str());
|
||||
}
|
||||
PRINTLN("\nConnected!");
|
||||
}
|
||||
|
||||
static bool waitTilDone(PubSubClient &mqttClient) {
|
||||
mqttClient.loop();
|
||||
return true;
|
||||
// unsigned long startTime = millis();
|
||||
|
||||
// do {
|
||||
// mqttClient.loop();
|
||||
// } while (!isDone && startTime + TIMEOUT_MILLIS > millis());
|
||||
|
||||
// return isDone;
|
||||
}
|
||||
|
||||
static String getHATriggerConfigTopic() {
|
||||
return String(CONFIG_HOMEASSISTANT_MQTT_PREFIX "device_automation/") + getDeviceMachineName() + "/config";
|
||||
}
|
||||
|
||||
static String getHASensorConfigTopic() {
|
||||
return String(CONFIG_HOMEASSISTANT_MQTT_PREFIX "sensor/") + getDeviceMachineName() + "/config";
|
||||
}
|
||||
|
||||
|
||||
static String getButtonTopicBase(bool abbrev) {
|
||||
if (abbrev) {
|
||||
return String("~");
|
||||
} else {
|
||||
return String(CONFIG_BUTTON_EVENT_TOPIC_PREFIX) + getDeviceMachineName();
|
||||
}
|
||||
}
|
||||
|
||||
static String getButtonStateTopic(bool abbrev) {
|
||||
return getButtonTopicBase(abbrev) + "/" CONFIG_BUTTON_STATE_TOPIC_NAME;
|
||||
}
|
||||
|
||||
static String getButtonTriggerTopic(bool abbrev) {
|
||||
return getButtonTopicBase(abbrev) + "/" CONFIG_BUTTON_TRIGGER_TOPIC_NAME;
|
||||
}
|
||||
|
||||
static bool publishJsonDocument(PubSubClient &mqttClient, String topic, JsonDocument &message, bool retain) {
|
||||
char str[MAX_PACKET_SIZE];
|
||||
size_t len = serializeJson(message, str, MAX_PACKET_SIZE);
|
||||
PRINT(topic);
|
||||
PRINT(":");
|
||||
PRINTLN(str);
|
||||
isDone = false;
|
||||
if (!mqttClient.publish(topic.c_str(), (uint8_t *) str, len, retain)) {
|
||||
return false;
|
||||
}
|
||||
return waitTilDone(mqttClient);
|
||||
}
|
||||
|
||||
static void addDeviceJson(JsonDocument &doc) {
|
||||
JsonObject device = doc.createNestedObject("device");
|
||||
device["name"] = getDeviceHumanName();
|
||||
device["mf"] = "DIY"; // manufacturer
|
||||
device["mdl"] = "IoT Button - ESP8266"; // model
|
||||
device["sw"] = "1.0"; // sw_version
|
||||
|
||||
JsonArray ids = device.createNestedArray("ids");
|
||||
ids.add(getDeviceId());
|
||||
}
|
||||
|
||||
bool publishBatteryMessage(PubSubClient &mqttClient, int percentage) {
|
||||
StaticJsonDocument<MAX_PACKET_SIZE> json;
|
||||
json["battery"] = percentage;
|
||||
|
||||
isDone = false;
|
||||
PRINTLN("Publishing battery message");
|
||||
return publishJsonDocument(mqttClient, getButtonStateTopic(false), json, true);
|
||||
}
|
||||
|
||||
bool publishTriggerMessage(PubSubClient &mqttClient, String message) {
|
||||
isDone = false;
|
||||
PRINTLN("Publishing trigger message");
|
||||
if (!mqttClient.publish(getButtonTriggerTopic(false).c_str(), message.c_str(), false)) {
|
||||
return false;
|
||||
}
|
||||
return waitTilDone(mqttClient);
|
||||
}
|
||||
|
||||
bool publishHABatteryDiscoveryConfig(PubSubClient &mqttClient) {
|
||||
StaticJsonDocument<MAX_PACKET_SIZE> json;
|
||||
bool abbrev = true;
|
||||
|
||||
json["~"] = getButtonTopicBase(false);
|
||||
json["stat_t"] = getButtonStateTopic(abbrev); // state_topic
|
||||
json["val_tpl"] = VALUE_TEMPLATE("battery"); // state_value_template
|
||||
json["uniq_id"] = getDeviceMachineName() + "-battery"; // unique_id
|
||||
json["dev_cla"] = "battery"; // device_class
|
||||
json["name"] = getDeviceHumanName() + ": Battery";
|
||||
|
||||
addDeviceJson(json);
|
||||
|
||||
PRINTLN("Publishing device sensor config");
|
||||
return publishJsonDocument(mqttClient, getHASensorConfigTopic(), json, true);
|
||||
}
|
||||
|
||||
bool publishHATriggerDiscoveryConfig(PubSubClient &mqttClient) {
|
||||
StaticJsonDocument<MAX_PACKET_SIZE> json;
|
||||
bool abbrev = true;
|
||||
|
||||
// json["name"] = getDeviceHumanName();
|
||||
json["atype"] = "trigger"; // automation_type
|
||||
json["type"] = "button_short_press";
|
||||
json["stype"] = "button_1"; // subtype
|
||||
|
||||
json["~"] = getButtonTopicBase(false);
|
||||
json["t"] = getButtonTriggerTopic(abbrev); // topic
|
||||
|
||||
addDeviceJson(json);
|
||||
|
||||
PRINTLN("Publishing device trigger discovery");
|
||||
return publishJsonDocument(mqttClient, getHATriggerConfigTopic(), json, true);
|
||||
}
|
||||
16
firmware/main_mcu/src/mqtt.h
Normal file
16
firmware/main_mcu/src/mqtt.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef __MQTT_H_
|
||||
#define __MQTT_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <PubSubClient.h>
|
||||
|
||||
void initMqtt(PubSubClient& client, WiFiClient &wifiClient);
|
||||
|
||||
bool publishHABatteryDiscoveryConfig(PubSubClient &client);
|
||||
bool publishHATriggerDiscoveryConfig(PubSubClient &client);
|
||||
|
||||
bool publishTriggerMessage(PubSubClient &client, String message);
|
||||
bool publishBatteryMessage(PubSubClient &client, int percent);
|
||||
|
||||
#endif // __MQTT_H_
|
||||
41
firmware/main_mcu/src/util.cpp
Normal file
41
firmware/main_mcu/src/util.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "util.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define DEVICE_NAME_PREFIX "button-"
|
||||
|
||||
#define PRINT_DELAY_MILLIS 500
|
||||
|
||||
String getDeviceId() {
|
||||
return String(ESP.getChipId(), HEX);
|
||||
}
|
||||
|
||||
String getDeviceMachineName() {
|
||||
return String(DEVICE_NAME_PREFIX) + getDeviceId();
|
||||
}
|
||||
|
||||
String getDeviceHumanName() {
|
||||
return "IoT Button (" + getDeviceId() + ")";
|
||||
}
|
||||
|
||||
void initWifi(WiFiClient &wifi) {
|
||||
PRINT("Connecting to WiFi using MAC: ");
|
||||
PRINTLN(WiFi.macAddress());
|
||||
|
||||
WiFi.hostname(getDeviceMachineName().c_str());
|
||||
WiFi.begin(CONFIG_SSID, CONFIG_PSK);
|
||||
long last_time = millis();
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
yield();
|
||||
long time = millis();
|
||||
if (time >= last_time + PRINT_DELAY_MILLIS) {
|
||||
PRINT(".");
|
||||
last_time = time;
|
||||
}
|
||||
}
|
||||
|
||||
PRINTLN("\nConnected!");
|
||||
PRINT("IP Address: ");
|
||||
PRINTLN(WiFi.localIP());
|
||||
|
||||
}
|
||||
21
firmware/main_mcu/src/util.h
Normal file
21
firmware/main_mcu/src/util.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef __UTIL_H_
|
||||
#define __UTIL_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
|
||||
#ifdef DEBUG_SKETCH
|
||||
#define PRINT(x) { Serial.print(x); } while (0)
|
||||
#define PRINTLN(x) { Serial.println(x); } while (0)
|
||||
#else
|
||||
#define PRINT(x)
|
||||
#define PRINTLN(x)
|
||||
#endif
|
||||
|
||||
String getDeviceHumanName();
|
||||
String getDeviceMachineName();
|
||||
String getDeviceId();
|
||||
void initWifi(WiFiClient &wifi);
|
||||
|
||||
|
||||
#endif // __UTIL_H_
|
||||
Reference in New Issue
Block a user