Compare commits
7 Commits
87c4833a10
...
c933b4cdcd
Author | SHA1 | Date |
---|---|---|
|
c933b4cdcd | |
|
1ea3de712e | |
|
d0e546571e | |
|
c9cacc540f | |
|
38df5d00c4 | |
|
6c1757b43d | |
|
e628508ff5 |
|
@ -9,4 +9,7 @@
|
|||
#define TOPIC_ERRORS "garden/errors"
|
||||
#define TOPIC_STATE "garden/state"
|
||||
|
||||
#define SLEEP_MILLIS (1000 * 60)
|
||||
#define SLEEP_MILLIS_EARLY (1000 * 60)
|
||||
#define SLEEP_MILLIS (1000 * 60 * 60)
|
||||
|
||||
#define VERSION __DATE__ " " __TIME__
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
INCTXT(RootCA, "isrgrootx1.pem");
|
||||
|
||||
RTC_DATA_ATTR uint32_t boot_count = 0;
|
||||
|
||||
WiFiClientSecure sock;
|
||||
PubSubClient mqtt(sock);
|
||||
|
||||
|
@ -18,19 +20,24 @@ Values values;
|
|||
Adafruit_seesaw ss;
|
||||
|
||||
void setup() {
|
||||
delay(1000); // VSCode is slow to open the serial console after upload
|
||||
|
||||
Serial.begin(115200);
|
||||
pinMode(13, OUTPUT);
|
||||
digitalWrite(13, 1);
|
||||
|
||||
// VSCode is slow to open the serial console after upload
|
||||
if (boot_count == 0) {
|
||||
delay(1000);
|
||||
}
|
||||
boot_count++;
|
||||
Serial.printf("This is boot number %d\n", boot_count);
|
||||
|
||||
if (!wifi_connect()) {
|
||||
Serial.printf("Failed to connect to WiFi, status %s\n", WiFi.status());
|
||||
reboot();
|
||||
retry_after_minutes(1);
|
||||
}
|
||||
if (!mqtt_connect()) {
|
||||
Serial.println("Could not connect to MQTT");
|
||||
reboot();
|
||||
retry_after_minutes(1);
|
||||
}
|
||||
|
||||
if (!publish_all_config(&mqtt, WiFi.macAddress().c_str())) {
|
||||
|
@ -42,20 +49,22 @@ void setup() {
|
|||
auto msg = "Seesaw not found";
|
||||
Serial.println(msg);
|
||||
mqtt_send_error(msg);
|
||||
reboot();
|
||||
retry_after_minutes(1);
|
||||
}
|
||||
|
||||
values.boot_count = boot_count;
|
||||
if (!values.read(&ss)) {
|
||||
auto msg = "Failed to get sensor values";
|
||||
Serial.println(msg);
|
||||
mqtt_send_error(msg);
|
||||
reboot();
|
||||
retry_after_minutes(1);
|
||||
} else {
|
||||
Serial.println("Got Values:");
|
||||
Serial.printf(" Moisture: %d\n", values.moisture);
|
||||
Serial.printf(" Temperature: %.02f\n", values.temperature);
|
||||
Serial.printf(" Battery: %.02f\n", values.battery);
|
||||
Serial.printf(" RSSI: %d\n", values.rssi);
|
||||
Serial.printf(" Boot Count: %d\n", values.boot_count);
|
||||
if (!values.send(&mqtt, TOPIC_STATE)) {
|
||||
Serial.println("Failed to send sensor values");
|
||||
}
|
||||
|
@ -65,9 +74,11 @@ void setup() {
|
|||
mqtt.disconnect();
|
||||
delay(1000);
|
||||
|
||||
Serial.println("Entering deep sleep ...");
|
||||
unsigned long timer =
|
||||
((boot_count <= 5 ? SLEEP_MILLIS_EARLY : SLEEP_MILLIS) - millis());
|
||||
Serial.printf("Entering deep sleep for %d milliseconds...\n", timer);
|
||||
Serial.flush();
|
||||
esp_sleep_enable_timer_wakeup((SLEEP_MILLIS - millis()) * 1000);
|
||||
esp_sleep_enable_timer_wakeup(timer * 1000);
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
|
||||
|
@ -76,15 +87,6 @@ void loop() {
|
|||
while (1) delay(1471228928);
|
||||
}
|
||||
|
||||
void error_led_blink() {
|
||||
while (1) {
|
||||
digitalWrite(13, 1);
|
||||
delay(500);
|
||||
digitalWrite(13, 0);
|
||||
delay(500);
|
||||
}
|
||||
}
|
||||
|
||||
boolean wifi_connect() {
|
||||
Serial.printf("Connecting to WiFi (%s) ", CFG_WIFI_SSID);
|
||||
WiFi.begin(CFG_WIFI_SSID, CFG_WIFI_PSK);
|
||||
|
@ -137,10 +139,6 @@ boolean mqtt_connect() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void mqtt_send_error(const __FlashStringHelper* msg) {
|
||||
mqtt_send_error((const char*)msg);
|
||||
}
|
||||
|
||||
void mqtt_send_error(const char* msg) {
|
||||
if (!mqtt.connected()) {
|
||||
Serial.println("MQTT client not connected, cannot send error message");
|
||||
|
@ -149,11 +147,10 @@ void mqtt_send_error(const char* msg) {
|
|||
mqtt.publish(TOPIC_ERRORS, msg);
|
||||
}
|
||||
|
||||
void reboot() {
|
||||
void retry_after_minutes(int minutes) {
|
||||
if (mqtt.connected()) {
|
||||
mqtt.disconnect();
|
||||
}
|
||||
Serial.println("Rebooting in 2 seconds");
|
||||
delay(2000);
|
||||
ESP.restart();
|
||||
esp_sleep_enable_timer_wakeup(minutes * 60 * 1000 * 1000);
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
|
|
|
@ -4,32 +4,52 @@
|
|||
|
||||
#include "constants.h"
|
||||
|
||||
struct sensor_config {
|
||||
const char* name;
|
||||
const char* unique_id;
|
||||
const char* value_template;
|
||||
const char* identifier;
|
||||
const char* device_class;
|
||||
const char* unit;
|
||||
const char* state_class;
|
||||
const char* entity_category;
|
||||
const char* icon;
|
||||
};
|
||||
|
||||
static bool publish_config(PubSubClient* mqtt, const char* topic,
|
||||
const char* name, const char* unique_id,
|
||||
const char* value_template, const char* identifier,
|
||||
const char* device_class, const char* unit) {
|
||||
StaticJsonDocument<256> doc;
|
||||
doc["unique_id"] = unique_id;
|
||||
doc["name"] = name;
|
||||
const struct sensor_config* config) {
|
||||
StaticJsonDocument<512> doc;
|
||||
doc["unique_id"] = config->unique_id;
|
||||
doc["name"] = config->name;
|
||||
doc["state_topic"] = TOPIC_STATE;
|
||||
doc["value_template"] = value_template;
|
||||
if (device_class != NULL) {
|
||||
doc["device_class"] = device_class;
|
||||
doc["value_template"] = config->value_template;
|
||||
if (config->device_class != NULL) {
|
||||
doc["device_class"] = config->device_class;
|
||||
}
|
||||
if (unit != NULL) {
|
||||
doc["unit_of_measurement"] = unit;
|
||||
if (config->unit != NULL) {
|
||||
doc["unit_of_measurement"] = config->unit;
|
||||
}
|
||||
if (config->state_class != NULL) {
|
||||
doc["state_class"] = config->state_class;
|
||||
}
|
||||
if (config->entity_category != NULL) {
|
||||
doc["entity_category"] = config->entity_category;
|
||||
}
|
||||
if (config->icon != NULL) {
|
||||
doc["icon"] = config->icon;
|
||||
}
|
||||
auto device = doc.createNestedObject("device");
|
||||
device["manufacturer"] = DEVICE_MANUFACTURER;
|
||||
device["name"] = DEVICE_NAME;
|
||||
device["model"] = DEVICE_MODEL;
|
||||
device["sw_version"] = VERSION;
|
||||
auto ident = device.createNestedArray("identifiers");
|
||||
ident.add(identifier);
|
||||
ident.add(config->identifier);
|
||||
String msg;
|
||||
serializeJson(doc, msg);
|
||||
auto payload = msg.c_str();
|
||||
Serial.printf("Publishing configuration for %s (%zd bytes) to %s ... ",
|
||||
unique_id, strlen(payload), topic);
|
||||
config->unique_id, strlen(payload), topic);
|
||||
auto r = mqtt->publish(topic, payload, true);
|
||||
Serial.println(r ? "OK" : "FAILED");
|
||||
return r;
|
||||
|
@ -37,18 +57,51 @@ static bool publish_config(PubSubClient* mqtt, const char* topic,
|
|||
|
||||
bool publish_all_config(PubSubClient* mqtt, const char* ident) {
|
||||
bool ret = true;
|
||||
publish_config(mqtt, TOPIC_CFG_MOISTURE, "Garden Sensor Moisture",
|
||||
"sensor.garden_sensor_moisture",
|
||||
"{{ value_json.moisture }}", ident, NULL, NULL);
|
||||
publish_config(mqtt, TOPIC_CFG_TEMPERATURE, "Garden Sensor Temperature",
|
||||
"sensor.garden_sensor_temperature",
|
||||
"{{ value_json.temperature }}", ident, "temperature", "°C");
|
||||
publish_config(mqtt, TOPIC_CFG_BATTERY, "Garden Sensor Battery Level",
|
||||
"sensor.garden_sensor_battery_level",
|
||||
"{{ value_json.battery_level }}", ident, "voltage", "V");
|
||||
publish_config(mqtt, TOPIC_CFG_RSSI, "Garden Sensor Signal Strength",
|
||||
"sensor.garden_sensor_rssi", "{{ value_json.rssi }}", ident,
|
||||
"signal_strength", "dBm");
|
||||
struct sensor_config moisture = {
|
||||
.name = "Garden Sensor Moisture",
|
||||
.unique_id = "sensor.garden_sensor_moisture",
|
||||
.value_template = "{{ value_json.moisture }}",
|
||||
.identifier = ident,
|
||||
.device_class = NULL,
|
||||
.unit = NULL,
|
||||
.state_class = "measurement",
|
||||
.entity_category = NULL,
|
||||
.icon = "mdi:water",
|
||||
};
|
||||
publish_config(mqtt, TOPIC_CFG_MOISTURE, &moisture);
|
||||
struct sensor_config temperature = {
|
||||
.name = "Garden Sensor Temperature",
|
||||
.unique_id = "sensor.garden_sensor_temperature",
|
||||
.value_template = "{{ value_json.temperature }}",
|
||||
.identifier = ident,
|
||||
.device_class = "temperature",
|
||||
.unit = "°C",
|
||||
.state_class = NULL,
|
||||
.entity_category = NULL,
|
||||
};
|
||||
publish_config(mqtt, TOPIC_CFG_TEMPERATURE, &temperature);
|
||||
struct sensor_config battery = {
|
||||
.name = "Garden Sensor Battery",
|
||||
.unique_id = "sensor.garden_sensor_battery",
|
||||
.value_template = "{{ value_json.battery }}",
|
||||
.identifier = ident,
|
||||
.device_class = "battery",
|
||||
.unit = "%",
|
||||
.state_class = NULL,
|
||||
.entity_category = "diagnostic",
|
||||
};
|
||||
publish_config(mqtt, TOPIC_CFG_BATTERY, &battery);
|
||||
struct sensor_config signal = {
|
||||
.name = "Garden Sensor Signal Strength",
|
||||
.unique_id = "sensor.garden_sensor_rssi",
|
||||
.value_template = "{{ value_json.rssi }}",
|
||||
.identifier = ident,
|
||||
.device_class = "signal_strength",
|
||||
.unit = "dBm",
|
||||
.state_class = NULL,
|
||||
.entity_category = "diagnostic",
|
||||
};
|
||||
publish_config(mqtt, TOPIC_CFG_RSSI, &signal);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -16,11 +16,13 @@ bool Values::read(Adafruit_seesaw* ss) {
|
|||
}
|
||||
|
||||
bool Values::send(PubSubClient* mqtt, const char* topic) {
|
||||
StaticJsonDocument<64> doc;
|
||||
StaticJsonDocument<96> doc;
|
||||
doc["moisture"] = moisture;
|
||||
doc["temperature"] = round2(temperature);
|
||||
doc["battery_level"] = round2(battery);
|
||||
doc["battery"] = (int)((battery - 3) / 0.95 * 100);
|
||||
doc["rssi"] = rssi;
|
||||
doc["boot_count"] = boot_count;
|
||||
|
||||
String msg;
|
||||
serializeJson(doc, msg);
|
||||
|
|
|
@ -7,6 +7,7 @@ class Values {
|
|||
float temperature;
|
||||
float battery;
|
||||
int8_t rssi;
|
||||
uint32_t boot_count;
|
||||
|
||||
Values() {};
|
||||
~Values() {};
|
||||
|
|
Loading…
Reference in New Issue