621 lines
24 KiB
C
621 lines
24 KiB
C
/*
|
|
* Copyright (c) 2023, Erich Styger
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* Useful read: https://github.com/particle-iot/lwip/blob/master/doc/mqtt_client.txt
|
|
*/
|
|
|
|
#include "platform.h"
|
|
#if PL_CONFIG_USE_MQTT_CLIENT
|
|
|
|
#include "pico/cyw43_arch.h"
|
|
#include "lwip/apps/mqtt.h"
|
|
#include "lwip/dns.h"
|
|
#include "mqtt_client.h"
|
|
#include "dns_resolver.h"
|
|
#include "McuLog.h"
|
|
#include "McuUtility.h"
|
|
#include "McuRTOS.h"
|
|
#if PL_CONFIG_USE_MINI
|
|
#include "minIni/McuMinINI.h"
|
|
#include "MinIniKeys.h"
|
|
#endif
|
|
#if MQTT_CLIENT_IS_EV_CHARGER
|
|
#include "Modbus/McuHeidelberg.h"
|
|
#endif
|
|
|
|
#if LWIP_TCP
|
|
|
|
#define MQTT_EXTRA_LOGS (0) /* set to 1 to produce extra log output */
|
|
|
|
#if MQTT_CLIENT_IS_EV_CHARGER
|
|
/* HomeAssistant Tesla Powerwall topics */
|
|
#define TOPIC_NAME_SOLAR_POWER "homeassistant/sensor/powerwall_solar_now/state"
|
|
#define TOPIC_NAME_SITE_POWER "homeassistant/sensor/powerwall_load_now/state"
|
|
#define TOPIC_NAME_GRID_POWER "homeassistant/sensor/powerwall_site_now/state"
|
|
#define TOPIC_NAME_BATTERY_POWER "homeassistant/sensor/powerwall_battery_now/state"
|
|
#define TOPIC_NAME_BATTERY_PERCENTAGE "homeassistant/sensor/powerwall_charge/state"
|
|
#define TOPIC_NAME_CHARGER_CHARGING_POWER "home/charger/power"
|
|
#endif
|
|
|
|
/* default entries for the MQTT broker connection */
|
|
#define MQTT_DEFAULT_BROKER "homeassistant"
|
|
#define MQTT_DEFAULT_CLIENT "client"
|
|
#define MQTT_DEFAULT_USER "user"
|
|
#define MQTT_DEFAULT_PASS "password"
|
|
#define MQTT_DEFAULT_PUBLISH true
|
|
#define DEFAULT_TOPIC_NAME_SENSORS_UPDATE "user/room/device/update"
|
|
#define DEFAULT_TOPIC_NAME_SEND_MEASUREMENT "user/room/device/cmd/measure"
|
|
|
|
typedef struct mqtt_t {
|
|
mqtt_client_t *mqtt_client; /* lwIP MQTT client handle */
|
|
DnsResolver_info_t addr; /* broker lwip address, resolved by DNS if hostname is used */
|
|
unsigned char broker[32]; /* broker IP or hostname string. For hostname, DNS will be used */
|
|
unsigned char client_id[32]; /* client ID used for connection: each client should have a unique ID */
|
|
unsigned char client_user[32]; /* client user name used for connection */
|
|
unsigned char client_pass[96]; /* client user password */
|
|
unsigned char sensors_update_topic[64]; /* topic name for sensor updates */
|
|
unsigned char send_measurement_topic[64]; /* topic name for sending measurement */
|
|
topic_ID_e in_pub_ID; /* incoming published ID, set in the incoming_publish_cb and used in the incoming_data_cb */
|
|
/* configuration settings */
|
|
bool doLogging; /* if it shall write log messages */
|
|
bool doPublishing; /* if it publish messages */
|
|
} mqtt_t;
|
|
|
|
static mqtt_t mqtt; /* information used for MQTT connection */
|
|
|
|
static const struct mqtt_connect_client_info_t mqtt_client_info = {
|
|
mqtt.client_id, /* client ID */
|
|
mqtt.client_user, /* client user name */
|
|
mqtt.client_pass, /* client password */
|
|
100, /* keep alive timeout in seconds */
|
|
NULL, /* will_topic */
|
|
NULL, /* will_msg */
|
|
0, /* will_qos */
|
|
0 /* will_retain */
|
|
#if LWIP_ALTCP && LWIP_ALTCP_TLS
|
|
, NULL
|
|
#endif
|
|
};
|
|
|
|
// Static variable to hold the callback pointer
|
|
static MqttClient_MessageCallback_t app_message_callback = NULL;
|
|
|
|
void MqttClient_RegisterMessageCallback(MqttClient_MessageCallback_t cb) {
|
|
app_message_callback = cb;
|
|
}
|
|
|
|
static void mqtt_publish_request_cb(void *arg, err_t err) {
|
|
#if 0 && MQTT_EXTRA_LOGS
|
|
const struct mqtt_connect_client_info_t *client_info = (const struct mqtt_connect_client_info_t*)arg;
|
|
McuLog_trace("MQTT client \"%s\" publish request cb: err %d", client_info->client_id, (int)err);
|
|
#endif
|
|
}
|
|
|
|
#if MQTT_CLIENT_IS_SENSOR
|
|
int MqttClient_Publish_SensorValues(float temperature, float humidity) {
|
|
err_t res;
|
|
uint8_t buf[64];
|
|
const uint8_t qos = 0; /* quos: 0: fire&forget, 1: at least once */
|
|
const uint8_t retain = 0;
|
|
|
|
if (!MqttClient_GetDoPublish()) {
|
|
return ERR_DISABLED;
|
|
}
|
|
|
|
if (mqtt.mqtt_client != NULL) { /* connected? */
|
|
if (mqtt.doLogging) {
|
|
McuLog_trace("publish t:%f h:%f", temperature, humidity);
|
|
}
|
|
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"{\"values\": {\"temperature\": ");
|
|
McuUtility_strcatNumFloat(buf, sizeof(buf), temperature, 2);
|
|
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)", \"humidity\": ");
|
|
McuUtility_strcatNumFloat(buf, sizeof(buf), humidity, 2);
|
|
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"}}");
|
|
|
|
res = mqtt_publish(mqtt.mqtt_client, mqtt.sensors_update_topic, buf, strlen(buf), qos, retain, mqtt_publish_request_cb, NULL);
|
|
if (res != ERR_OK) {
|
|
McuLog_error("Failed sensor values mqtt_publish: %d", res);
|
|
(void)MqttClient_Disconnect(); /* try disconnect and connect again */
|
|
(void)MqttClient_Connect();
|
|
return res;
|
|
}
|
|
return ERR_OK;
|
|
} else {
|
|
return ERR_FAILED;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if MQTT_CLIENT_IS_EV_CHARGER
|
|
int MqttClient_Publish_ChargingPower(uint32_t powerW) {
|
|
err_t res;
|
|
uint8_t buf[64];
|
|
const uint8_t qos = 0; /* quos: 0: fire&forget, 1: at least once */
|
|
const uint8_t retain = 0;
|
|
|
|
if (!MqttClient_GetDoPublish()) {
|
|
return ERR_DISABLED;
|
|
}
|
|
|
|
if (mqtt.mqtt_client!=NULL) { /* connected? */
|
|
if (mqtt.doLogging) {
|
|
McuLog_trace("publish P: %d W", powerW);
|
|
}
|
|
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"{\"chargeP\": ");
|
|
McuUtility_strcatNum32u(buf, sizeof(buf), powerW);
|
|
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)", \"unit\": \"W\"}");
|
|
res = mqtt_publish(mqtt.mqtt_client, TOPIC_NAME_CHARGER_CHARGING_POWER, buf, strlen(buf), qos, retain, mqtt_publish_request_cb, NULL);
|
|
if (res!=ERR_OK) {
|
|
McuLog_error("Failed charging power mqtt_publish: %d", res);
|
|
(void)MqttClient_Disconnect(); /* try disconnect and connect again */
|
|
(void)MqttClient_Connect();
|
|
return res;
|
|
}
|
|
return ERR_OK;
|
|
} else {
|
|
return ERR_FAILED;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
bool MqttClient_GetDoPublish(void) {
|
|
return mqtt.doPublishing && mqtt.mqtt_client!=NULL;
|
|
}
|
|
|
|
void MqttClient_SetDoPublish(bool publish) {
|
|
#if PL_CONFIG_USE_MINI
|
|
McuMinINI_ini_putl(NVMC_MININI_SECTION_MQTT, NVMC_MININI_KEY_MQTT_PUBLISH, publish, NVMC_MININI_FILE_NAME);
|
|
#endif
|
|
mqtt.doPublishing = publish;
|
|
}
|
|
|
|
int MqttClient_Publish(const unsigned char *topic, const unsigned char *value) {
|
|
err_t res;
|
|
uint8_t buf[64];
|
|
const uint8_t qos = 0; /* quos: 0: fire&forget, 1: at least once */
|
|
const uint8_t retain = 0;
|
|
|
|
if (!MqttClient_GetDoPublish()) {
|
|
return ERR_DISABLED;
|
|
}
|
|
|
|
if (mqtt.mqtt_client!=NULL) { /* connected? */
|
|
if (mqtt.doLogging) {
|
|
McuLog_trace("publish topic: \"%s\" value: \"%s\"", topic, value);
|
|
}
|
|
res = mqtt_publish(mqtt.mqtt_client, topic, value, strlen(value), qos, retain, mqtt_publish_request_cb, NULL);
|
|
if (res!=ERR_OK) {
|
|
McuLog_error("Failed mqtt_publish: %d", res);
|
|
(void)MqttClient_Disconnect(); /* try disconnect and connect again */
|
|
(void)MqttClient_Connect();
|
|
return res;
|
|
}
|
|
return ERR_OK;
|
|
} else {
|
|
return ERR_FAILED;
|
|
}
|
|
}
|
|
|
|
static void GetDataString(unsigned char *buf, size_t bufSize, const u8_t *data, u16_t len) {
|
|
buf[0] = '\0';
|
|
for(int i=0; i<len; i++){
|
|
McuUtility_chcat(buf, bufSize, data[i]);
|
|
}
|
|
}
|
|
|
|
#if MQTT_CLIENT_IS_EV_CHARGER
|
|
static int32_t scanWattValue(const unsigned char *str) {
|
|
/* string in in kW and it returns the number in Watt, so for example:
|
|
* "1" => 1000, * "1.2" => 1200, "3.025" = 3025, "-1.002" = -1002
|
|
*/
|
|
int32_t watt;
|
|
float f;
|
|
|
|
if (sscanf(str, "%f", &f)!=1) { /* check number of conversions, shall be 1 */
|
|
McuLog_error("failed conversion of %s", str);
|
|
return 0; /* conversion failed, return 0 */
|
|
}
|
|
watt = (int)(f*1000.0f);
|
|
return watt;
|
|
}
|
|
#endif
|
|
|
|
static void mqtt_incoming_data_cb(void *arg, const u8_t *data, u16_t len, u8_t flags) {
|
|
const struct mqtt_connect_client_info_t *client_info = (const struct mqtt_connect_client_info_t*)arg;
|
|
LWIP_UNUSED_ARG(data);
|
|
unsigned char buf[32];
|
|
int32_t watt;
|
|
|
|
#if MQTT_EXTRA_LOGS
|
|
McuLog_trace("MQTT client \"%s\" data cb: len %d, flags %d", client_info->client_id, (int)len, (int)flags);
|
|
#endif
|
|
if(flags & MQTT_DATA_FLAG_LAST) {
|
|
/* Last fragment of payload received (or whole part if payload fits receive buffer. See MQTT_VAR_HEADER_BUFFER_LEN) */
|
|
#if MQTT_CLIENT_IS_EV_CHARGER
|
|
if (mqtt.in_pub_ID == Topic_ID_Solar_Power) {
|
|
GetDataString(buf, sizeof(buf), data, len);
|
|
if (mqtt.doLogging) {
|
|
McuLog_trace("solarP: %s kW", buf);
|
|
}
|
|
watt = scanWattValue(buf);
|
|
if (watt>=0) { /* can only be positive */
|
|
McuHeidelberg_SetSolarPowerWatt(watt);
|
|
}
|
|
} else if(mqtt.in_pub_ID == Topic_ID_Site_Power) {
|
|
GetDataString(buf, sizeof(buf), data, len);
|
|
if (mqtt.doLogging) {
|
|
McuLog_trace("siteP: %s kW", buf);
|
|
}
|
|
watt = scanWattValue(buf);
|
|
if (watt>=0) { /* can only be positive */
|
|
McuHeidelberg_SetSitePowerWatt(watt);
|
|
}
|
|
} else if(mqtt.in_pub_ID == Topic_ID_Grid_Power) {
|
|
GetDataString(buf, sizeof(buf), data, len);
|
|
if (mqtt.doLogging) {
|
|
McuLog_trace("gridP: %s kW", buf);
|
|
}
|
|
watt = scanWattValue(buf);
|
|
McuHeidelberg_SetGridPowerWatt(watt);
|
|
} else if(mqtt.in_pub_ID == Topic_ID_Battery_Power) {
|
|
GetDataString(buf, sizeof(buf), data, len);
|
|
if (mqtt.doLogging) {
|
|
McuLog_trace("battP: %s, kW", buf);
|
|
}
|
|
} else if(mqtt.in_pub_ID == Topic_ID_Battery_Percentage) {
|
|
GetDataString(buf, sizeof(buf), data, len);
|
|
if (mqtt.doLogging) {
|
|
McuLog_trace("bat%%: %s%%", buf);
|
|
}
|
|
#elif MQTT_CLIENT_IS_SENSOR
|
|
if (app_message_callback != NULL) {
|
|
app_message_callback(mqtt.in_pub_ID, data, len);
|
|
#endif
|
|
} else {
|
|
McuLog_trace("mqtt_incoming_data_cb: Ignoring payload...");
|
|
}
|
|
} else {
|
|
McuLog_trace("mqtt_incoming_data_cb: fragmented payload ...");
|
|
/* Handle fragmented payload, store in buffer, write to file or whatever */
|
|
}
|
|
}
|
|
|
|
static void mqtt_incoming_publish_cb(void *arg, const char *topic, u32_t tot_len) {
|
|
const struct mqtt_connect_client_info_t *client_info = (const struct mqtt_connect_client_info_t*)arg;
|
|
|
|
#if MQTT_CLIENT_IS_EV_CHARGER
|
|
if (McuUtility_strcmp(topic, TOPIC_NAME_SOLAR_POWER)==0) {
|
|
mqtt.in_pub_ID = Topic_ID_Solar_Power;
|
|
} else if (McuUtility_strcmp(topic, TOPIC_NAME_SITE_POWER)==0) {
|
|
mqtt.in_pub_ID = Topic_ID_Site_Power;
|
|
} else if (McuUtility_strcmp(topic, TOPIC_NAME_GRID_POWER)==0) {
|
|
mqtt.in_pub_ID = Topic_ID_Grid_Power;
|
|
} else if (McuUtility_strcmp(topic, TOPIC_NAME_BATTERY_POWER)==0) {
|
|
mqtt.in_pub_ID = Topic_ID_Battery_Power;
|
|
} else if (McuUtility_strcmp(topic, TOPIC_NAME_BATTERY_PERCENTAGE)==0) {
|
|
mqtt.in_pub_ID = Topic_ID_Battery_Percentage;
|
|
} else if (McuUtility_strcmp(topic, TOPIC_NAME_CHARGER_CHARGING_POWER)==0) {
|
|
mqtt.in_pub_ID = Topic_ID_Charging_Power;
|
|
#elif MQTT_CLIENT_IS_SENSOR
|
|
if (McuUtility_strcmp(topic, mqtt.sensors_update_topic)==0) {
|
|
mqtt.in_pub_ID = Topic_ID_Sensor_Update;
|
|
} else if (McuUtility_strcmp(topic, mqtt.send_measurement_topic)==0) {
|
|
mqtt.in_pub_ID = Topic_ID_Send_Measurement;
|
|
#endif
|
|
} else { /* unknown */
|
|
McuLog_trace("MQTT client \"%s\" publish cb: topic %s, len %d", client_info->client_id, topic, (int)tot_len);
|
|
mqtt.in_pub_ID = Topic_ID_None;
|
|
}
|
|
}
|
|
|
|
static void mqtt_request_cb(void *arg, err_t err) {
|
|
#if MQTT_EXTRA_LOGS
|
|
const struct mqtt_connect_client_info_t *client_info = (const struct mqtt_connect_client_info_t*)arg;
|
|
McuLog_trace("MQTT client \"%s\" request cb: err %d", client_info->client_id, (int)err);
|
|
#endif
|
|
}
|
|
|
|
static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status) {
|
|
const struct mqtt_connect_client_info_t *client_info = (const struct mqtt_connect_client_info_t*)arg;
|
|
LWIP_UNUSED_ARG(client);
|
|
err_t err;
|
|
|
|
#if MQTT_EXTRA_LOGS
|
|
McuLog_trace("MQTT client \"%s\" connection cb: status %d", client_info->client_id, (int)status);
|
|
#endif
|
|
if (status!=MQTT_CONNECT_ACCEPTED) {
|
|
McuLog_error("MQTT client \"%s\" connection cb: FAILED status %d", client_info->client_id, (int)status);
|
|
}
|
|
/* subscribe to topics */
|
|
if (status == MQTT_CONNECT_ACCEPTED) {
|
|
McuLog_trace("MQTT connect accepted");
|
|
#if MQTT_CLIENT_IS_EV_CHARGER
|
|
err = mqtt_sub_unsub(client,
|
|
TOPIC_NAME_SOLAR_POWER, /* solar P in kW */
|
|
1, /* quos: 0: fire&forget, 1: at least once */
|
|
mqtt_request_cb, /* callback */
|
|
LWIP_CONST_CAST(void*, client_info),
|
|
1 /* subscribe */
|
|
);
|
|
if (err!=ERR_OK) {
|
|
McuLog_error("failed subscribing, err %d", err);
|
|
}
|
|
err = mqtt_sub_unsub(client,
|
|
TOPIC_NAME_SITE_POWER, /* site/house P in kW */
|
|
1, /* quos */
|
|
mqtt_request_cb,
|
|
LWIP_CONST_CAST(void*, client_info),
|
|
1);
|
|
if (err!=ERR_OK) {
|
|
McuLog_error("failed subscribing, err %d", err);
|
|
}
|
|
err = mqtt_sub_unsub(client,
|
|
TOPIC_NAME_GRID_POWER, /* grid P in kW */
|
|
1, /* quos */
|
|
mqtt_request_cb,
|
|
LWIP_CONST_CAST(void*, client_info),
|
|
1);
|
|
if (err!=ERR_OK) {
|
|
McuLog_error("failed subscribing, err %d", err);
|
|
}
|
|
err = mqtt_sub_unsub(client,
|
|
TOPIC_NAME_BATTERY_POWER, /* battery P in kW */
|
|
1, /* quos */
|
|
mqtt_request_cb,
|
|
LWIP_CONST_CAST(void*, client_info),
|
|
1);
|
|
if (err!=ERR_OK) {
|
|
McuLog_error("failed subscribing, err %d", err);
|
|
}
|
|
err = mqtt_sub_unsub(client,
|
|
TOPIC_NAME_BATTERY_PERCENTAGE, /* topic: percentage of battery charge */
|
|
1, /* quos */
|
|
mqtt_request_cb, LWIP_CONST_CAST(void*, client_info),
|
|
1);
|
|
if (err!=ERR_OK) {
|
|
McuLog_error("failed subscribing, err %d", err);
|
|
}
|
|
#elif MQTT_CLIENT_IS_SENSOR
|
|
err = mqtt_sub_unsub(client,
|
|
mqtt.send_measurement_topic, /* topic: send measurement command */
|
|
1, /* quality of service */
|
|
mqtt_request_cb, // Callback to call when subscribe/unsubscribe response is received
|
|
LWIP_CONST_CAST(void*, client_info),
|
|
1); // 1 for subscribe, 0 for unsubscribe
|
|
if (err!=ERR_OK) {
|
|
McuLog_error("failed subscribing, err %d", err);
|
|
}
|
|
#endif
|
|
} else if (status==MQTT_CONNECT_DISCONNECTED) {
|
|
McuLog_trace("MQTT connect disconnect");
|
|
}
|
|
}
|
|
#endif /* LWIP_TCP */
|
|
|
|
uint8_t MqttClient_Connect(void) {
|
|
#if LWIP_TCP
|
|
int nofRetry;
|
|
mqtt.mqtt_client = mqtt_client_new(); /* create client handle */
|
|
err_t err;
|
|
|
|
if (mqtt.mqtt_client==NULL) {
|
|
McuLog_fatal("new mqtt client is NULL");
|
|
}
|
|
|
|
/* setup connection information */
|
|
#if PL_CONFIG_USE_MINI
|
|
McuMinINI_ini_gets(NVMC_MININI_SECTION_MQTT, NVMC_MININI_KEY_MQTT_BROKER, MQTT_DEFAULT_BROKER, mqtt.broker, sizeof(mqtt.broker), NVMC_MININI_FILE_NAME);
|
|
McuMinINI_ini_gets(NVMC_MININI_SECTION_MQTT, NVMC_MININI_KEY_MQTT_CLIENT, MQTT_DEFAULT_CLIENT, mqtt.client_id, sizeof(mqtt.client_id), NVMC_MININI_FILE_NAME);
|
|
McuMinINI_ini_gets(NVMC_MININI_SECTION_MQTT, NVMC_MININI_KEY_MQTT_USER, MQTT_DEFAULT_USER, mqtt.client_user, sizeof(mqtt.client_user), NVMC_MININI_FILE_NAME);
|
|
McuMinINI_ini_gets(NVMC_MININI_SECTION_MQTT, NVMC_MININI_KEY_MQTT_PASS, MQTT_DEFAULT_PASS, mqtt.client_pass, sizeof(mqtt.client_pass), NVMC_MININI_FILE_NAME);
|
|
McuMinINI_ini_gets(NVMC_MININI_SECTION_MQTT, NVMC_TOPIC_NAME_SENSORS_UPDATE, DEFAULT_TOPIC_NAME_SENSORS_UPDATE, mqtt.sensors_update_topic, sizeof(mqtt.sensors_update_topic), NVMC_MININI_FILE_NAME);
|
|
McuMinINI_ini_gets(NVMC_MININI_SECTION_MQTT, NVMC_TOPIC_NAME_SEND_MEASUREMENT, DEFAULT_TOPIC_NAME_SEND_MEASUREMENT, mqtt.send_measurement_topic, sizeof(mqtt.send_measurement_topic), NVMC_MININI_FILE_NAME);
|
|
mqtt.doPublishing = McuMinINI_ini_getbool(NVMC_MININI_SECTION_MQTT, NVMC_MININI_KEY_MQTT_PASS, MQTT_DEFAULT_PUBLISH, NVMC_MININI_FILE_NAME);
|
|
#else
|
|
McuUtility_strcpy(mqtt.broker, sizeof(mqtt.broker), MQTT_DEFAULT_BROKER);
|
|
McuUtility_strcpy(mqtt.client_id, sizeof(mqtt.client_id), MQTT_DEFAULT_CLIENT);
|
|
McuUtility_strcpy(mqtt.client_user, sizeof(mqtt.client_user), MQTT_DEFAULT_USER);
|
|
McuUtility_strcpy(mqtt.client_pass, sizeof(mqtt.client_pass), MQTT_DEFAULT_PASS);
|
|
mqtt.doPublishing = MQTT_DEFAULT_PUBLISH;
|
|
#endif
|
|
|
|
/* resolve host name to IP address: */
|
|
for (nofRetry=60; nofRetry>=0; nofRetry--) {
|
|
if (DnsResolver_ResolveName(mqtt.broker, &mqtt.addr, 5*1000)!=0) { /* use DNS to resolve name to IP address */
|
|
McuLog_error("failed to resolve broker name %s, retry ...", mqtt.broker);
|
|
vTaskDelay(pdMS_TO_TICKS(5000));
|
|
} else {
|
|
break; /* success! leaving loop */
|
|
}
|
|
}
|
|
if (nofRetry<0) {
|
|
McuLog_fatal("failed to resolve broker name %s, giving up", mqtt.broker);
|
|
return ERR_FAILED;
|
|
}
|
|
/* setup callbacks for incoming data: */
|
|
mqtt_set_inpub_callback(
|
|
mqtt.mqtt_client, /* client handle */
|
|
mqtt_incoming_publish_cb, /* callback for incoming publish messages */
|
|
mqtt_incoming_data_cb, /* callback for incoming data */
|
|
LWIP_CONST_CAST(void*, &mqtt_client_info) /* argument for callbacks */
|
|
);
|
|
/* connect to broker */
|
|
cyw43_arch_lwip_begin(); /* start section for to lwIP access */
|
|
err = mqtt_client_connect(
|
|
mqtt.mqtt_client, /* client handle */
|
|
&mqtt.addr.resolved_addr, /* broker IP address */
|
|
MQTT_PORT, /* port to be used */
|
|
mqtt_connection_cb, LWIP_CONST_CAST(void*, &mqtt_client_info), /* connection callback with argument */
|
|
&mqtt_client_info /* client information */
|
|
);
|
|
cyw43_arch_lwip_end(); /* end section accessing lwIP */
|
|
if (err!=ERR_OK) {
|
|
McuLog_error("failed connecting client to server");
|
|
} else {
|
|
McuLog_trace("client connecting");
|
|
}
|
|
return err;
|
|
#else
|
|
return ERR_FAILED;
|
|
#endif /* LWIP_TCP */
|
|
}
|
|
|
|
uint8_t MqttClient_Disconnect(void) {
|
|
if (mqtt.mqtt_client!=NULL) {
|
|
McuLog_trace("disconnecting client");
|
|
cyw43_arch_lwip_begin(); /* start section for to lwIP access */
|
|
mqtt_disconnect(mqtt.mqtt_client);
|
|
mqtt_client_free(mqtt.mqtt_client);
|
|
cyw43_arch_lwip_end(); /* end section accessing lwIP */
|
|
mqtt.mqtt_client = NULL;
|
|
}
|
|
return ERR_OK;
|
|
}
|
|
|
|
static uint8_t SetBroker(const unsigned char *broker) {
|
|
unsigned char buf[64];
|
|
|
|
McuUtility_ScanDoubleQuotedString(&broker, buf, sizeof(buf));
|
|
McuUtility_strcpy(mqtt.broker, sizeof(mqtt.broker), buf);
|
|
#if PL_CONFIG_USE_MINI
|
|
McuMinINI_ini_puts(NVMC_MININI_SECTION_MQTT, NVMC_MININI_KEY_MQTT_BROKER, mqtt.broker, NVMC_MININI_FILE_NAME);
|
|
#endif
|
|
return ERR_OK;
|
|
}
|
|
|
|
static uint8_t SetID(const unsigned char *id) {
|
|
unsigned char buf[64];
|
|
|
|
McuUtility_ScanDoubleQuotedString(&id, buf, sizeof(buf));
|
|
McuUtility_strcpy(mqtt.client_id, sizeof(mqtt.client_id), buf);
|
|
#if PL_CONFIG_USE_MINI
|
|
McuMinINI_ini_puts(NVMC_MININI_SECTION_MQTT, NVMC_MININI_KEY_MQTT_CLIENT, mqtt.client_id, NVMC_MININI_FILE_NAME);
|
|
#endif
|
|
return ERR_OK;
|
|
}
|
|
|
|
static uint8_t SetUser(const unsigned char *user) {
|
|
unsigned char buf[64];
|
|
|
|
McuUtility_ScanDoubleQuotedString(&user, buf, sizeof(buf));
|
|
McuUtility_strcpy(mqtt.client_user, sizeof(mqtt.client_user), buf);
|
|
#if PL_CONFIG_USE_MINI
|
|
McuMinINI_ini_puts(NVMC_MININI_SECTION_MQTT, NVMC_MININI_KEY_MQTT_USER, mqtt.client_user, NVMC_MININI_FILE_NAME);
|
|
#endif
|
|
return ERR_OK;
|
|
}
|
|
|
|
static uint8_t SetPassword(const unsigned char *pass) {
|
|
unsigned char buf[96];
|
|
|
|
McuUtility_ScanDoubleQuotedString(&pass, buf, sizeof(buf));
|
|
McuUtility_strcpy(mqtt.client_pass, sizeof(mqtt.client_pass), buf);
|
|
#if PL_CONFIG_USE_MINI
|
|
McuMinINI_ini_puts(NVMC_MININI_SECTION_MQTT, NVMC_MININI_KEY_MQTT_PASS, mqtt.client_pass, NVMC_MININI_FILE_NAME);
|
|
#endif
|
|
return ERR_OK;
|
|
}
|
|
|
|
static uint8_t PrintStatus(const McuShell_StdIOType *io) {
|
|
McuShell_SendStatusStr((unsigned char*)"mqttclient", (unsigned char*)"mqttclient status\r\n", io->stdOut);
|
|
McuShell_SendStatusStr((unsigned char*)" log", mqtt.doLogging?(unsigned char*)"on\r\n":(unsigned char*)"off\r\n", io->stdOut);
|
|
McuShell_SendStatusStr((unsigned char*)" publish", MqttClient_GetDoPublish()?(unsigned char*)"on\r\n":(unsigned char*)"off\r\n", io->stdOut);
|
|
McuShell_SendStatusStr((unsigned char*)" client", mqtt.mqtt_client==NULL?(unsigned char*)"diconnected\r\n":(unsigned char*)"connected\r\n", io->stdOut);
|
|
McuShell_SendStatusStr((unsigned char*)" broker", mqtt.broker, io->stdOut);
|
|
McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
|
|
McuShell_SendStatusStr((unsigned char*)" client ID", mqtt.client_id, io->stdOut);
|
|
McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
|
|
McuShell_SendStatusStr((unsigned char*)" client user", mqtt.client_user, io->stdOut);
|
|
McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
|
|
McuShell_SendStatusStr((unsigned char*)" client password", mqtt.client_pass, io->stdOut);
|
|
McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
|
|
return ERR_OK;
|
|
}
|
|
|
|
static uint8_t PrintHelp(const McuShell_StdIOType *io) {
|
|
McuShell_SendHelpStr((unsigned char*)"mqttclient", (unsigned char*)"Group of mqttclient commands\r\n", io->stdOut);
|
|
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Print help or status information\r\n", io->stdOut);
|
|
McuShell_SendHelpStr((unsigned char*)" log on|off", (unsigned char*)"Turn logging on or off\r\n", io->stdOut);
|
|
McuShell_SendHelpStr((unsigned char*)" publish on|off", (unsigned char*)"Publishing on or off\r\n", io->stdOut);
|
|
McuShell_SendHelpStr((unsigned char*)" connect|disconnect", (unsigned char*)"Connect or disconnect from server\r\n", io->stdOut);
|
|
McuShell_SendHelpStr((unsigned char*)" set broker \"<broker>\"", (unsigned char*)"Set broker name\r\n", io->stdOut);
|
|
McuShell_SendHelpStr((unsigned char*)" set id \"<id>\"", (unsigned char*)"Set client ID\r\n", io->stdOut);
|
|
McuShell_SendHelpStr((unsigned char*)" set user \"<user>\"", (unsigned char*)"Set client user name\r\n", io->stdOut);
|
|
McuShell_SendHelpStr((unsigned char*)" set pass \"<password>\"", (unsigned char*)"Set client password\r\n", io->stdOut);
|
|
McuShell_SendHelpStr((unsigned char*)" pub \"<topic>\" \"<txt>\"", (unsigned char*)"Publish text to a topic\r\n", io->stdOut);
|
|
return ERR_OK;
|
|
}
|
|
|
|
uint8_t MqttClient_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io) {
|
|
const unsigned char *p;
|
|
uint16_t val16u;
|
|
|
|
if (McuUtility_strcmp((char*)cmd, McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, "mqttclient help")==0) {
|
|
*handled = true;
|
|
return PrintHelp(io);
|
|
} else if ((McuUtility_strcmp((char*)cmd, McuShell_CMD_STATUS)==0) || (McuUtility_strcmp((char*)cmd, "mqttclient status")==0)) {
|
|
*handled = true;
|
|
return PrintStatus(io);
|
|
} else if (McuUtility_strcmp((char*)cmd, "mqttclient log on")==0) {
|
|
*handled = true;
|
|
mqtt.doLogging = true;
|
|
} else if (McuUtility_strcmp((char*)cmd, "mqttclient log off")==0) {
|
|
*handled = true;
|
|
mqtt.doLogging = false;
|
|
} else if (McuUtility_strcmp((char*)cmd, "mqttclient publish on")==0) {
|
|
*handled = true;
|
|
MqttClient_SetDoPublish(true);
|
|
} else if (McuUtility_strcmp((char*)cmd, "mqttclient publish off")==0) {
|
|
*handled = true;
|
|
MqttClient_SetDoPublish(false);
|
|
} else if (McuUtility_strcmp((char*)cmd, "mqttclient connect")==0) {
|
|
*handled = true;
|
|
MqttClient_Connect();
|
|
} else if (McuUtility_strcmp((char*)cmd, "mqttclient disconnect")==0) {
|
|
*handled = true;
|
|
MqttClient_Disconnect();
|
|
} else if (McuUtility_strncmp((char*)cmd, "mqttclient set broker ", sizeof("mqttclient set broker ")-1)==0) {
|
|
*handled = TRUE;
|
|
p = cmd + sizeof("mqttclient set broker ")-1;
|
|
return SetBroker(p);
|
|
} else if (McuUtility_strncmp((char*)cmd, "mqttclient set id ", sizeof("mqttclient set id ")-1)==0) {
|
|
*handled = TRUE;
|
|
p = cmd + sizeof("mqttclient set id ")-1;
|
|
return SetID(p);
|
|
} else if (McuUtility_strncmp((char*)cmd, "mqttclient set user ", sizeof("mqttclient set user ")-1)==0) {
|
|
*handled = TRUE;
|
|
p = cmd + sizeof("mqttclient set user ")-1;
|
|
return SetUser(p);
|
|
} else if (McuUtility_strncmp((char*)cmd, "mqttclient set pass ", sizeof("mqttclient set pass ")-1)==0) {
|
|
*handled = TRUE;
|
|
p = cmd + sizeof("mqttclient set pass ")-1;
|
|
return SetPassword(p);
|
|
} else if (McuUtility_strncmp((char*)cmd, "mqttclient pub ", sizeof("mqttclient pub ")-1)==0) {
|
|
unsigned char topic[128], text[128];
|
|
*handled = TRUE;
|
|
p = cmd + sizeof("mqttclient pub ")-1;
|
|
if (McuUtility_ScanDoubleQuotedString(&p, topic, sizeof(topic))!=ERR_OK) {
|
|
return ERR_FAILED;
|
|
}
|
|
McuUtility_SkipSpaces(&p);
|
|
if (McuUtility_ScanDoubleQuotedString(&p, text, sizeof(text))!=ERR_OK) {
|
|
return ERR_FAILED;
|
|
}
|
|
return MqttClient_Publish(topic, text);
|
|
}
|
|
return ERR_OK;
|
|
}
|
|
|
|
void MqttClient_Deinit(void) {
|
|
MqttClient_Disconnect();
|
|
}
|
|
|
|
void MqttClient_Init(void) {
|
|
mqtt.doLogging = true;
|
|
mqtt.doPublishing = MQTT_DEFAULT_PUBLISH;
|
|
}
|
|
|
|
#endif /* PL_CONFIG_USE_MQTT_CLIENT */
|