From 5f3f348b9011d27dec80cc54c682c31820e4bc4f Mon Sep 17 00:00:00 2001 From: Jonas Arnold Date: Fri, 9 Dec 2022 15:29:26 +0100 Subject: [PATCH] made MQTT broker IP configurable via shell, implemented storing the IP to NVS and retrieving it on startup --- ADIS_ESP32_Eclipse/main/Shell.c | 2 + ADIS_ESP32_Eclipse/main/challenge_nvs.c | 258 +++++++++++++++++------- ADIS_ESP32_Eclipse/main/challenge_nvs.h | 18 +- ADIS_ESP32_Eclipse/main/myMqtt.c | 64 +++++- ADIS_ESP32_Eclipse/main/myMqtt.h | 14 ++ 5 files changed, 283 insertions(+), 73 deletions(-) diff --git a/ADIS_ESP32_Eclipse/main/Shell.c b/ADIS_ESP32_Eclipse/main/Shell.c index 0fbb4dc..59f664e 100644 --- a/ADIS_ESP32_Eclipse/main/Shell.c +++ b/ADIS_ESP32_Eclipse/main/Shell.c @@ -43,6 +43,7 @@ #include "driver/gpio.h" #if PL_CONFIG_CHALLENGE_APP_ACTIVATED #include "challenge_app.h" + #include "myMqtt.h" #endif #define SHELL_ESP32_UART_DEVICE (UART_NUM_0) /* Uart for bootloader and connection to robot */ @@ -80,6 +81,7 @@ static const McuShell_ParseCommandCallback CmdParserTable[] = #endif #if PL_CONFIG_CHALLENGE_APP_ACTIVATED Challenge_ParseShellCommand, + MyMqtt_ParseShellCommand, #endif NULL /* Sentinel */ }; diff --git a/ADIS_ESP32_Eclipse/main/challenge_nvs.c b/ADIS_ESP32_Eclipse/main/challenge_nvs.c index fc7f6c3..f8df98a 100644 --- a/ADIS_ESP32_Eclipse/main/challenge_nvs.c +++ b/ADIS_ESP32_Eclipse/main/challenge_nvs.c @@ -10,19 +10,32 @@ #include "esp_system.h" #include "nvs_flash.h" #include "nvs.h" +#include "McuUtility.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" /* defines / constants */ #define TAG "CHALLENGE_NVS" /* tag for logging with ESP_LOG */ const char* NAMESPACE_NVS = "storage"; const char* ROBOT_MODE_KEY = "robot_mode"; +const char* MQTT_IP_KEY = "mqtt_ip"; /* local vars */ bool initializedSuccessfully = false; +SemaphoreHandle_t semaphore = NULL; /*! \brief Initialize the NVS storage. */ void Challenge_Nvs_Init(void){ ESP_LOGI(TAG, "Initializing..."); + // initialize semaphore + semaphore = xSemaphoreCreateRecursiveMutex(); + if(semaphore == NULL){ + ESP_LOGE(TAG, "Failed to initialize semaphore for Challenge NVS!"); + }else{ + vQueueAddToRegistry(semaphore, "challenge_nvs_semphr"); + } + // Initialize NVS esp_err_t err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -42,97 +55,206 @@ void Challenge_Nvs_Deinit(void){ ESP_LOGI(TAG, "Deinitializing..."); esp_err_t err = nvs_flash_deinit(); ESP_LOGI(TAG, "Deinitialized. Success=%s", err==ESP_OK?"true":"false"); + + vSemaphoreDelete(semaphore); +} + +/* HELPERS FOR SEMAPHORE */ +static bool Challenge_Nvs_AquireMutex(void){ + if(xSemaphoreTakeRecursive(semaphore, pdMS_TO_TICKS(100)) != pdTRUE){ + /* timeout */ + ESP_LOGW(TAG, "Timemout while aquiring challenge NVS semaphore."); + return false; + } + return true; +} + +static void Challenge_Nvs_ReleaseMutex(void){ + xSemaphoreGive(semaphore); } -/*! -* \brief Stores the passed robot mode in NVS. -* \param modeStationary Mode to store. True = stationary mode, False = mobile mode -* \return true on success -*/ bool Challenge_Nvs_StoreRobotModeToNVS(bool mode){ bool success = false; - // Open - ESP_LOGI(TAG, "Opening Non-Volatile Storage (NVS) handle... "); - nvs_handle_t my_handle; - esp_err_t err = nvs_open(NAMESPACE_NVS, NVS_READWRITE, &my_handle); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Error (%s) opening NVS handle!\n", esp_err_to_name(err)); - return false; - } + if(Challenge_Nvs_AquireMutex()){ + // Open + ESP_LOGI(TAG, "Opening Non-Volatile Storage (NVS) handle... "); + nvs_handle_t my_handle; + esp_err_t err = nvs_open(NAMESPACE_NVS, NVS_READWRITE, &my_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + return false; + } - // Write - uint8_t value = mode?1:0; - ESP_LOGI(TAG, "Updating robotMode in NVS ... "); - err = nvs_set_u8(my_handle, ROBOT_MODE_KEY, value); - if(err != ESP_OK){ - ESP_LOGE(TAG, "Failed to update robotMode in NVS. Error=(%s)", esp_err_to_name(err)); - success = false; - }else{ // successfully set - // Commit written value. - // After setting any values, nvs_commit() must be called to ensure changes are written - // to flash storage. Implementations may write to storage at other times, - // but this is not guaranteed. - ESP_LOGI(TAG, "Committing updates in NVS ... "); - err = nvs_commit(my_handle); + // Write + uint8_t value = mode?1:0; + ESP_LOGI(TAG, "Updating robotMode in NVS ... "); + err = nvs_set_u8(my_handle, ROBOT_MODE_KEY, value); if(err != ESP_OK){ - ESP_LOGE(TAG, "Failed to commit to NVS. Error=(%s)", esp_err_to_name(err)); + ESP_LOGE(TAG, "Failed to update robotMode in NVS. Error=(%s)", esp_err_to_name(err)); success = false; - }else{ - ESP_LOGI(TAG, "Successfully committed changes to NVS."); - success = true; + }else{ // successfully set + // Commit written value. + // After setting any values, nvs_commit() must be called to ensure changes are written + // to flash storage. Implementations may write to storage at other times, + // but this is not guaranteed. + ESP_LOGI(TAG, "Committing updates in NVS ... "); + err = nvs_commit(my_handle); + if(err != ESP_OK){ + ESP_LOGE(TAG, "Failed to commit to NVS. Error=(%s)", esp_err_to_name(err)); + success = false; + }else{ + ESP_LOGI(TAG, "Successfully committed changes to NVS."); + success = true; + } } - } - // Close - nvs_close(my_handle); + // Close + nvs_close(my_handle); + + Challenge_Nvs_ReleaseMutex(); + } return success; } -/*! - * \brief Retrieve the robot mode stored in NVS. - * \param mode Pointer to the location where to retrieve the mode to. True = stationary mode, False = mobile mode - * \return true on success - */ bool Challenge_Nvs_GetRobotModeFromNVS(bool *mode){ bool success = false; bool notInitialized = false; - // Open - ESP_LOGI(TAG, "Opening Non-Volatile Storage (NVS) handle... "); - nvs_handle_t my_handle; - esp_err_t err = nvs_open(NAMESPACE_NVS, NVS_READWRITE, &my_handle); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Error (%s) opening NVS handle!\n", esp_err_to_name(err)); - return false; - } - - // Read - ESP_LOGI(TAG, "Reading robot mode from NVS ... "); - uint8_t value = 0; - err = nvs_get_u8(my_handle, ROBOT_MODE_KEY, &value); - switch (err) { - case ESP_OK: - ESP_LOGI(TAG, "Reading robot mode done. Read robotMode = %i", value); - *mode = (bool)(value==1?true:false); // set value at pointer location - success = true; - break; - case ESP_ERR_NVS_NOT_FOUND: - ESP_LOGI(TAG, "Reading robot mode done. The value is not initialized yet!"); - notInitialized = true; - break; - default : - ESP_LOGI(TAG, "Error reading robot mode. Error=(%s)", esp_err_to_name(err)); + if(Challenge_Nvs_AquireMutex()){ + // Open + ESP_LOGI(TAG, "Opening Non-Volatile Storage (NVS) handle... "); + nvs_handle_t my_handle; + esp_err_t err = nvs_open(NAMESPACE_NVS, NVS_READWRITE, &my_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + return false; + } + + // Read + ESP_LOGI(TAG, "Reading robot mode from NVS ... "); + uint8_t value = 0; + err = nvs_get_u8(my_handle, ROBOT_MODE_KEY, &value); + switch (err) { + case ESP_OK: + ESP_LOGI(TAG, "Reading robot mode done. Read robotMode = %i", value); + *mode = (bool)(value==1?true:false); // set value at pointer location + success = true; + break; + case ESP_ERR_NVS_NOT_FOUND: + ESP_LOGI(TAG, "Reading robot mode done. The value is not initialized yet!"); + notInitialized = true; + break; + default : + ESP_LOGI(TAG, "Error reading robot mode. Error=(%s)", esp_err_to_name(err)); + success = false; + } + + // Close + nvs_close(my_handle); + + if(notInitialized){ + success = Challenge_Nvs_StoreRobotModeToNVS(false); // initialize to false + } + + Challenge_Nvs_ReleaseMutex(); + } + + return success; +} + + +bool Challenge_Nvs_StoreBrokerIpToNVS(char* ip){ + bool success = false; + + if(Challenge_Nvs_AquireMutex()){ + // Open + ESP_LOGI(TAG, "Opening Non-Volatile Storage (NVS) handle... "); + nvs_handle_t my_handle; + esp_err_t err = nvs_open(NAMESPACE_NVS, NVS_READWRITE, &my_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + return false; + } + + // Write + ESP_LOGI(TAG, "Updating mqtt broker IP in NVS ... "); + err = nvs_set_str(my_handle, MQTT_IP_KEY, ip); + if(err != ESP_OK){ + ESP_LOGE(TAG, "Failed to update mqtt broker IP in NVS. Error=(%s)", esp_err_to_name(err)); success = false; + }else{ // successfully set + // Commit written value. + // After setting any values, nvs_commit() must be called to ensure changes are written + // to flash storage. Implementations may write to storage at other times, + // but this is not guaranteed. + ESP_LOGI(TAG, "Committing updates in NVS ... "); + err = nvs_commit(my_handle); + if(err != ESP_OK){ + ESP_LOGE(TAG, "Failed to commit to NVS. Error=(%s)", esp_err_to_name(err)); + success = false; + }else{ + ESP_LOGI(TAG, "Successfully committed changes to NVS."); + success = true; + } + } + + // Close + nvs_close(my_handle); + + Challenge_Nvs_ReleaseMutex(); } - // Close - nvs_close(my_handle); + return success; +} + +bool Challenge_Nvs_GetBrokerIpFromNVS(char *ip, size_t length){ + bool success = false; + bool notInitialized = false; + + if(Challenge_Nvs_AquireMutex()){ + // Open + ESP_LOGI(TAG, "Opening Non-Volatile Storage (NVS) handle... "); + nvs_handle_t my_handle; + esp_err_t err = nvs_open(NAMESPACE_NVS, NVS_READWRITE, &my_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + return false; + } - if(notInitialized){ - success = Challenge_Nvs_StoreRobotModeToNVS(false); // initialize to false + // Read + ESP_LOGI(TAG, "Reading mqtt broker IP from NVS ... "); + size_t required_size; + nvs_get_str(my_handle, MQTT_IP_KEY, NULL, &required_size); // retrieve required size + char* value = pvPortMalloc(required_size); + err = nvs_get_str(my_handle, MQTT_IP_KEY, value, &required_size); + switch (err) { + case ESP_OK: + ESP_LOGI(TAG, "Reading mqtt broker ip done. Read IP = %s", value); + McuUtility_strcpy((unsigned char*)ip, length, (unsigned char *)value); // copy ip to destination + ESP_LOGI(TAG, "Copied mqtt broker ip. ip=%s", ip); + success = true; + break; + case ESP_ERR_NVS_NOT_FOUND: + ESP_LOGI(TAG, "Reading mqtt broker ip done. The value is not initialized yet!"); + notInitialized = true; + break; + default : + ESP_LOGI(TAG, "Error reading mqtt broker ip. Error=(%s)", esp_err_to_name(err)); + success = false; + } + + // Close + nvs_close(my_handle); + + if(notInitialized){ + success = Challenge_Nvs_StoreBrokerIpToNVS("10.0.1.1"); // initialize to default IP + } + + Challenge_Nvs_ReleaseMutex(); } return success; } + diff --git a/ADIS_ESP32_Eclipse/main/challenge_nvs.h b/ADIS_ESP32_Eclipse/main/challenge_nvs.h index e1655be..a39cb02 100644 --- a/ADIS_ESP32_Eclipse/main/challenge_nvs.h +++ b/ADIS_ESP32_Eclipse/main/challenge_nvs.h @@ -9,6 +9,7 @@ #define MAIN_CHALLENGE_NVS_H_ #include +#include /*! \brief Initialize the NVS storage. */ void Challenge_Nvs_Init(void); @@ -18,7 +19,7 @@ void Challenge_Nvs_Deinit(void); /*! * \brief Stores the passed robot mode in NVS. -* \param modeStationary Mode to store. True = stationary mode, False = mobile mode +* \param mode Stationary Mode to store. True = stationary mode, False = mobile mode * \return true on success */ bool Challenge_Nvs_StoreRobotModeToNVS(bool mode); @@ -30,4 +31,19 @@ bool Challenge_Nvs_StoreRobotModeToNVS(bool mode); */ bool Challenge_Nvs_GetRobotModeFromNVS(bool *mode); +/*! +* \brief Stores the passed MQTT Broker IP in NVS. +* \param ip IP to store. +* \return true on success +*/ +bool Challenge_Nvs_StoreBrokerIpToNVS(char* ip); + +/*! + * \brief Retrieve the MQTT Broker IP stored in NVS. If the Broker IP is not yet stored in NVS, it will be initially stored as "10.0.0.1". + * \param ip Pointer to the location where to store the retrieved the IP to. + * \param length Size of the array ip. + * \return true on success + */ +bool Challenge_Nvs_GetBrokerIpFromNVS(char *ip, size_t length); + #endif /* MAIN_CHALLENGE_NVS_H_ */ diff --git a/ADIS_ESP32_Eclipse/main/myMqtt.c b/ADIS_ESP32_Eclipse/main/myMqtt.c index 8edf14d..c2d19e9 100644 --- a/ADIS_ESP32_Eclipse/main/myMqtt.c +++ b/ADIS_ESP32_Eclipse/main/myMqtt.c @@ -12,13 +12,15 @@ #include "esp_log.h" #include "mqtt_client.h" #include "challenge_com.h" - -// Settings -#define CONFIG_BROKER_URL "mqtt://10.180.254.80" +#include "challenge_nvs.h" +#include "McuUtility.h" // tag for logging with ESP_LOG static const char *TAG = "MY_MQTT"; +// local cars +char _brokerIp[] = "255.255.255.255"; // max length of ip + // declarations static void log_error_if_nonzero(const char *message, int error_code); static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data); @@ -27,8 +29,15 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ esp_mqtt_client_handle_t client; bool MyMqtt_Init(void){ + char brokerUri[50]; + // get from NVS + Challenge_Nvs_GetBrokerIpFromNVS(_brokerIp, sizeof(_brokerIp)); + ESP_LOGI(TAG, "Got Broker IP '%s' from NVS", _brokerIp); + McuUtility_strcpy((unsigned char*)brokerUri, sizeof(brokerUri), (unsigned char*)"mqtt://"); + McuUtility_strcat((unsigned char*)brokerUri, sizeof(brokerUri), (unsigned char*)_brokerIp); + ESP_LOGI(TAG, "Connecting to Mqtt Broker URI '%s'", brokerUri); esp_mqtt_client_config_t mqtt_cfg = { - .uri = CONFIG_BROKER_URL, + .uri = brokerUri, }; client = esp_mqtt_client_init(&mqtt_cfg); /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */ @@ -56,6 +65,18 @@ void MyMqtt_Subscribe(const char *topic){ ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d, success=%s", msg_id, msg_id==-1 ? "false" : "true"); } +char* MyMqtt_GetBrokerIP(void){ + return _brokerIp; +} + +void MyMqtt_SetBrokerIP(char* ip){ + ESP_LOGI(TAG, "Set Broker IP to '%s'", ip); + McuUtility_strcpy((unsigned char*)_brokerIp, sizeof(_brokerIp), (unsigned char*)ip); + // store to NVS + Challenge_Nvs_StoreBrokerIpToNVS(_brokerIp); +} + + /***********/ /* HELPERS */ /***********/ @@ -124,3 +145,38 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ break; } } + +/*********/ +/* SHELL */ +/*********/ +#if PL_CONFIG_USE_SHELL +static uint8_t PrintStatus(const McuShell_StdIOType *io) { + McuShell_SendStatusStr((unsigned char*)"mqtt", (unsigned char*)"ESP32 MQTT status\r\n", io->stdOut); + McuShell_SendStatusStr((unsigned char*)" ip", (unsigned char*)MyMqtt_GetBrokerIP(), io->stdOut); + return ERR_OK; +} + +static uint8_t PrintHelp(const McuShell_StdIOType *io) { + McuShell_SendHelpStr((unsigned char*)"mqtt", (unsigned char*)"Group of ESP32 MQTT commands\r\n", io->stdOut); + McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows MQTT help or status\r\n", io->stdOut); + McuShell_SendHelpStr((unsigned char*)" setIp ", (unsigned char*)"Sets the IP address of the MQTT broker\r\n", io->stdOut); + return ERR_OK; +} + +uint8_t MyMqtt_ParseShellCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) { + if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"mqtt help")==0) { + *handled = TRUE; + return PrintHelp(io); + } else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"mqtt status")==0) { + *handled = TRUE; + return PrintStatus(io); + } else if (McuUtility_strncmp((char*)cmd,"mqtt setIp",sizeof("mqtt setIp")-1) == 0) { // contains + *handled = TRUE; + char ip[] = "255.255.255.255"; // maximum size of IP + McuUtility_strcpy((unsigned char*)ip, sizeof(ip), (unsigned char *)cmd + strlen((char*)"mqtt setIp ")); // cut front + McuUtility_strCutTail((unsigned char*)ip, (unsigned char*)"\0\0\0"); // cut tail + MyMqtt_SetBrokerIP(ip); // set ip + } + return ERR_OK; +} +#endif /* PL_CONFIG_USE_SHELL */ diff --git a/ADIS_ESP32_Eclipse/main/myMqtt.h b/ADIS_ESP32_Eclipse/main/myMqtt.h index b5324d2..bdc2e3e 100644 --- a/ADIS_ESP32_Eclipse/main/myMqtt.h +++ b/ADIS_ESP32_Eclipse/main/myMqtt.h @@ -8,6 +8,7 @@ #ifndef MAIN_MYMQTT_H_ #define MAIN_MYMQTT_H_ +#include "platform.h" #include bool MyMqtt_Init(void); @@ -18,4 +19,17 @@ void MyMqtt_Publish(const char *topic, const char *data); void MyMqtt_Subscribe(const char *topic); +#if PL_CONFIG_USE_SHELL + #include "McuShell.h" + + /*! + * \brief Command line and shell handler + * \param cmd The command to be parsed + * \param handled If command has been recognized and handled + * \param io I/O handler to be used + * \return error code, otherwise ERR_OK + */ + uint8_t MyMqtt_ParseShellCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io); +#endif + #endif /* MAIN_MYMQTT_H_ */