fiatlux/firmware/mqtt.cpp

344 lines
9.9 KiB
C++
Raw Permalink Normal View History

2021-07-02 16:45:29 +00:00
//
// Created by jedi on 25.06.21.
//
#include "mqtt.h"
2022-06-27 18:00:48 +00:00
#include "wifi.h"
2023-02-15 23:14:21 +00:00
#include "lux.h"
2021-07-06 20:49:45 +00:00
#include <stdio.h>
#include <string.h>
#include <espressif/esp_common.h>
#include <espressif/user_interface.h>
extern "C" {
#include <paho_mqtt_c/MQTTESP8266.h>
#include <paho_mqtt_c/MQTTClient.h>
}
#include <semphr.h>
/* You can use http://test.mosquitto.org/ to test mqtt_client instead
* of setting up your own MQTT server */
2022-06-27 18:00:48 +00:00
//#define MQTT_HOST ("172.16.0.42")
2023-02-15 23:14:21 +00:00
//#define MQTT_HOST ("10.23.42.187")
#define MQTT_HOST ("172.16.1.53")
2021-07-06 20:49:45 +00:00
#define MQTT_PORT 1883
#define MQTT_USER NULL
#define MQTT_PASS NULL
2023-02-15 23:14:21 +00:00
bool is_on = true;
2021-07-06 20:49:45 +00:00
2022-06-27 18:00:48 +00:00
QueueHandle_t publish_queue;
2023-02-19 12:36:53 +00:00
static void publish_state() {
2023-02-16 09:04:40 +00:00
char msg[PUB_MSG_LEN];
snprintf(msg, PUB_MSG_LEN, R"({"state":"%s", "brightness":%d})", is_on ? "ON" : "OFF", light_value >> 4);
if(xQueueSend(publish_queue, (void *) msg, 0) == pdFALSE) {
printf("Publish queue overflow.\r\n");
}
}
2021-07-06 20:49:45 +00:00
extern "C" void beat_task(void *pvParameters) {
TickType_t xLastWakeTime = xTaskGetTickCount();
2023-02-16 09:04:40 +00:00
while (true) {
2023-02-16 09:04:40 +00:00
vTaskDelayUntil(&xLastWakeTime, 20000 / portTICK_PERIOD_MS);
publish_state();
2021-07-06 20:49:45 +00:00
}
}
2023-02-15 23:14:21 +00:00
template<typename T>
class optional {
bool has_value_;
T value_;
public:
optional() : has_value_(false) {}
optional(T value) : has_value_(true), value_(value) {}
bool has_value() const { return has_value_; }
T value() const { return value_; }
T value_or(T default_value) const {
return has_value_ ? value_ : default_value;
}
T operator*() const { return value_; }
T operator->() const { return value_; }
operator bool() const { return has_value_; }
optional<T> &operator=(T value) {
has_value_ = true;
value_ = value;
return *this;
}
optional<T> &operator=(const optional<T> &other) {
has_value_ = other.has_value_;
value_ = other.value_;
return *this;
}
2023-02-19 12:36:53 +00:00
optional<T> &operator=(optional<T> &&other) noexcept {
2023-02-15 23:14:21 +00:00
has_value_ = other.has_value_;
value_ = other.value_;
return *this;
}
optional(const optional<T> &other) : has_value_(other.has_value_), value_(other.value_) {}
2023-02-19 12:36:53 +00:00
optional(optional<T> &&other) noexcept: has_value_(other.has_value_), value_(other.value_) {}
2023-02-15 23:14:21 +00:00
};
class parser {
char *head;
const char *end;
public:
parser(char *begin, const char *end) : head(begin), end(end) {}
bool consume_char(char c) {
if(head < end && *head == c) {
head++;
return true;
}
return false;
}
bool consume_string(const char *str, size_t len) {
if(head + len <= end && strncmp(head, str, len) == 0) {
head += len;
return true;
}
return false;
}
template<size_t N>
bool consume_string(const char (&str)[N]) {
return consume_string(str, N - 1);
}
optional<char> read_char() {
if(head < end) {
return {*head++};
}
return {};
}
optional<char> peek_char() {
if(head < end) {
return {*head};
}
return {};
}
optional<int> read_int() {
int value = 0;
bool negative = false;
if(consume_char('-')) {
negative = true;
}
auto peek = head;
while (peek < end && *peek >= '0' && *peek <= '9') {
value = value * 10 + (*peek - '0');
peek++;
}
if(peek == head) {
return {};
}
head = peek;
return {negative ? -value : value};
}
};
2021-07-06 20:49:45 +00:00
static void topic_received(mqtt_message_data_t *md) {
mqtt_message_t *message = md->message;
2023-02-15 23:14:21 +00:00
char *payload = (char *) message->payload;
char *payload_end = payload + message->payloadlen;
2021-07-06 20:49:45 +00:00
2023-02-15 23:14:21 +00:00
parser p(payload, payload_end);
if(!p.consume_char('{')) {
printf("expected '{' at start of payload\n");
return;
}
while (true) {
if(p.consume_string(R"("state":)")) {
if(p.consume_string("\"ON\"")) {
is_on = true;
printf("ON\n");
} else if(p.consume_string("\"OFF\"")) {
is_on = false;
printf("OFF\n");
} else {
printf("Invalid state\n");
return;
}
} else if(p.consume_string(R"("brightness":)")) {
auto brightness = p.read_int();
if(!brightness) {
printf("Invalid brightness\n");
return;
}
light_value = *brightness * 0xFFF / 255;
printf("Brightness: %d\n", *brightness);
} else {
printf("Invalid key\n");
return;
}
if(p.consume_char(',')) {
continue;
}
if(p.consume_char('}')) {
break;
}
printf("expected ',' or '}'\n");
return;
}
2023-02-16 09:04:40 +00:00
publish_state();
2021-07-06 20:49:45 +00:00
}
2023-02-16 09:04:40 +00:00
static const char *get_my_id() {
2021-07-06 20:49:45 +00:00
// Use MAC address for Station as unique ID
static char my_id[13];
static bool my_id_done = false;
int8_t i;
uint8_t x;
if(my_id_done)
return my_id;
if(!sdk_wifi_get_macaddr(STATION_IF, (uint8_t *) my_id))
2023-02-16 09:04:40 +00:00
return nullptr;
2021-07-06 20:49:45 +00:00
for (i = 5; i >= 0; --i) {
x = my_id[i] & 0x0F;
if(x > 9) x += 7;
my_id[i * 2 + 1] = x + '0';
x = my_id[i] >> 4;
if(x > 9) x += 7;
my_id[i * 2] = x + '0';
}
my_id[12] = '\0';
my_id_done = true;
return my_id;
}
2023-02-19 12:36:53 +00:00
const char disco_msg_fmt[] = R"json({ "name": "Fiatlux Test %s",
"unique_id": "fiatlux_%s",
"command_topic": "fiatlux/light/fiatlux_%s/set",
"state_topic": "fiatlux/light/fiatlux_%s/state",
"brightness": true, "schema": "json" })json";
const char disco_topic_fmt[] = "homeassistant/light/fiatlux_%s/config";
const char state_topic_fmt[] = "fiatlux/light/fiatlux_%s/state";
const char set_topic_fmt[] = "fiatlux/light/fiatlux_%s/set";
2021-07-06 20:49:45 +00:00
extern "C" void mqtt_task(void *pvParameters) {
int ret = 0;
2023-02-16 09:04:40 +00:00
mqtt_network network{};
2021-07-06 20:49:45 +00:00
mqtt_client_t client = mqtt_client_default;
char mqtt_client_id[20];
2023-02-15 23:14:21 +00:00
uint8_t mqtt_buf[1000];
uint8_t mqtt_readbuf[1000];
2021-07-06 20:49:45 +00:00
mqtt_packet_connect_data_t data = mqtt_packet_connect_data_initializer;
mqtt_network_new(&network);
2023-02-19 12:36:53 +00:00
snprintf(mqtt_client_id, sizeof(mqtt_client_id), "fiatlux-%s", get_my_id());
char disco_msg[sizeof(disco_msg_fmt) + 24];
snprintf(disco_msg, sizeof(disco_msg), disco_msg_fmt, &get_my_id()[6], &get_my_id()[6], &get_my_id()[6],
&get_my_id()[6]);
char disco_topic[sizeof(disco_topic_fmt) + 6];
snprintf(disco_topic, sizeof(disco_topic), disco_topic_fmt, &get_my_id()[6]);
char state_topic[sizeof(state_topic_fmt) + 6];
snprintf(state_topic, sizeof(state_topic), state_topic_fmt, &get_my_id()[6]);
char set_topic[sizeof(set_topic_fmt) + 6];
snprintf(set_topic, sizeof(set_topic), set_topic_fmt, &get_my_id()[6]);
2021-07-06 20:49:45 +00:00
2023-02-16 09:04:40 +00:00
while (true) {
2022-06-27 18:00:48 +00:00
xSemaphoreTake(wifi_alive, portMAX_DELAY);
2021-07-06 20:49:45 +00:00
printf("%s: started\n\r", __func__);
printf("%s: (Re)connecting to MQTT server %s ... ", __func__,
MQTT_HOST);
ret = mqtt_network_connect(&network, MQTT_HOST, MQTT_PORT);
if(ret) {
2022-06-27 18:00:48 +00:00
printf("error 1: %d\n\r", ret);
vTaskDelay(1000 / portTICK_PERIOD_MS);
2021-07-06 20:49:45 +00:00
continue;
}
printf("done\n\r");
2023-02-15 23:14:21 +00:00
mqtt_client_new(&client, &network, 5000, mqtt_buf, 1000,
mqtt_readbuf, 1000);
2021-07-06 20:49:45 +00:00
data.willFlag = 0;
data.MQTTVersion = 3;
data.clientID.cstring = mqtt_client_id;
data.username.cstring = MQTT_USER;
data.password.cstring = MQTT_PASS;
data.keepAliveInterval = 10;
data.cleansession = 0;
printf("Send MQTT connect ... ");
ret = mqtt_connect(&client, &data);
if(ret) {
2022-06-27 18:00:48 +00:00
printf("error 2: %d\n\r", ret);
2021-07-06 20:49:45 +00:00
mqtt_network_disconnect(&network);
2022-06-27 18:00:48 +00:00
vTaskDelay(1000 / portTICK_PERIOD_MS);
2021-07-06 20:49:45 +00:00
continue;
}
printf("done\r\n");
2023-02-15 23:14:21 +00:00
//Mqtt auto dicovery
2023-02-19 12:36:53 +00:00
mqtt_subscribe(&client, set_topic, MQTT_QOS1, topic_received);
2023-02-15 23:14:21 +00:00
//mqtt_subscribe(&client, "fiatlux/light/fiatlux_test/state", MQTT_QOS1, topic_received);
2021-07-06 20:49:45 +00:00
xQueueReset(publish_queue);
2023-02-15 23:14:21 +00:00
{
mqtt_message_t message;
2023-02-19 12:36:53 +00:00
message.payload = (void *) disco_msg;
message.payloadlen = strlen(disco_msg);
2023-02-15 23:14:21 +00:00
message.dup = 0;
message.qos = MQTT_QOS1;
message.retained = 0;
2023-02-19 12:36:53 +00:00
ret = mqtt_publish(&client, disco_topic, &message);
2023-02-15 23:14:21 +00:00
if(ret != MQTT_SUCCESS) {
2023-02-19 12:36:53 +00:00
printf("error: %d %s\n", ret, disco_msg);
2023-02-15 23:14:21 +00:00
}
}
2023-02-16 09:04:40 +00:00
while (true) {
2021-07-06 20:49:45 +00:00
char msg[PUB_MSG_LEN - 1] = "\0";
while (xQueueReceive(publish_queue, (void *) msg, 0) ==
pdTRUE) {
mqtt_message_t message;
message.payload = msg;
2023-02-15 23:14:21 +00:00
message.payloadlen = strlen(msg);
2021-07-06 20:49:45 +00:00
message.dup = 0;
message.qos = MQTT_QOS1;
message.retained = 0;
2023-02-19 12:36:53 +00:00
ret = mqtt_publish(&client, state_topic, &message);
2021-07-06 20:49:45 +00:00
if(ret != MQTT_SUCCESS) {
printf("error while publishing message: %d\n", ret);
break;
}
}
ret = mqtt_yield(&client, 1000);
if(ret == MQTT_DISCONNECTED)
break;
}
printf("Connection dropped, request restart\n\r");
mqtt_network_disconnect(&network);
taskYIELD();
}
2021-09-12 19:42:55 +00:00
}