From c1dd34243278f759007a20acb73fe3e6fd17b46d Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 3 Apr 2025 12:37:40 +0530 Subject: [PATCH] Added Azuma wifi backup code --- .clang-format | 9 + .clangd | 3 + .cproject | 17 + .project | 20 + .settings/org.eclipse.core.resources.prefs | 2 + CMakeLists.txt | 9 + LICENSE | 5 + README.md | 45 +- main/CMakeLists.txt | 16 + main/Kconfig.projbuild | 14 + main/MCP39F501.h | 35 + main/adc_ifx.c | 187 ++ main/adc_ifx.h | 18 + main/comms.c | 2106 +++++++++++++ main/comms.h | 152 + main/data_processing.c | 474 +++ main/data_processing.h | 298 ++ main/hmi.c | 721 +++++ main/hmi.h | 56 + main/main.c | 677 +++++ main/main.h | 38 + main/modem.c | 3109 ++++++++++++++++++++ main/modem.h | 181 ++ main/nvm.c | 1118 +++++++ main/nvm.h | 74 + main/ota.c | 424 +++ main/ota.h | 32 + main/port.c | 258 ++ main/port.h | 55 + main/rtc.c | 437 +++ main/rtc.h | 83 + main/uart_ifx.c | 272 ++ main/uart_ifx.h | 26 + main/ulp/sensors_registers.h | 321 ++ main/ulp/ulp_main.c | 730 +++++ main/wifi_Client.c | 710 +++++ main/wifi_Client.h | 52 + main/wifi_Init.c | 679 +++++ main/wifi_Init.h | 68 + main/wifi_OTA.c | 284 ++ main/wifi_OTA.h | 22 + main/wifi_webServer.c | 1654 +++++++++++ main/wifi_webServer.h | 30 + partitions.csv | 7 + 44 files changed, 15527 insertions(+), 1 deletion(-) create mode 100644 .clang-format create mode 100644 .clangd create mode 100644 .cproject create mode 100644 .project create mode 100644 .settings/org.eclipse.core.resources.prefs create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 main/CMakeLists.txt create mode 100644 main/Kconfig.projbuild create mode 100644 main/MCP39F501.h create mode 100644 main/adc_ifx.c create mode 100644 main/adc_ifx.h create mode 100644 main/comms.c create mode 100644 main/comms.h create mode 100644 main/data_processing.c create mode 100644 main/data_processing.h create mode 100644 main/hmi.c create mode 100644 main/hmi.h create mode 100644 main/main.c create mode 100644 main/main.h create mode 100644 main/modem.c create mode 100644 main/modem.h create mode 100644 main/nvm.c create mode 100644 main/nvm.h create mode 100644 main/ota.c create mode 100644 main/ota.h create mode 100644 main/port.c create mode 100644 main/port.h create mode 100644 main/rtc.c create mode 100644 main/rtc.h create mode 100644 main/uart_ifx.c create mode 100644 main/uart_ifx.h create mode 100644 main/ulp/sensors_registers.h create mode 100644 main/ulp/ulp_main.c create mode 100644 main/wifi_Client.c create mode 100644 main/wifi_Client.h create mode 100644 main/wifi_Init.c create mode 100644 main/wifi_Init.h create mode 100644 main/wifi_OTA.c create mode 100644 main/wifi_OTA.h create mode 100644 main/wifi_webServer.c create mode 100644 main/wifi_webServer.h create mode 100644 partitions.csv diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..0c7b3aa --- /dev/null +++ b/.clang-format @@ -0,0 +1,9 @@ +# We'll use defaults from the LLVM style, but with some modifications so that it's close to the CDT K&R style. +BasedOnStyle: LLVM +UseTab: Always +IndentWidth: 4 +TabWidth: 4 +PackConstructorInitializers: NextLineOnly +BreakConstructorInitializers: AfterColon +IndentAccessModifiers: false +AccessModifierOffset: -4 diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..22e5ea0 --- /dev/null +++ b/.clangd @@ -0,0 +1,3 @@ +CompileFlags: + CompilationDatabase: build + Remove: [-m*, -f*] diff --git a/.cproject b/.cproject new file mode 100644 index 0000000..d730a0a --- /dev/null +++ b/.cproject @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.project b/.project new file mode 100644 index 0000000..d7da86d --- /dev/null +++ b/.project @@ -0,0 +1,20 @@ + + + Azuma-wifi-switch + + + + + + org.eclipse.cdt.core.cBuilder + clean,full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + com.espressif.idf.core.idfNature + + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..99f26c0 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1e448a3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(PROJECT_VER "2013") +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +get_filename_component(ProjectId ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" ProjectId ${ProjectId}) +project(${ProjectId}) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..17fb21c --- /dev/null +++ b/LICENSE @@ -0,0 +1,5 @@ +Code in this repository is in the Public Domain (or CC0 licensed, at your option.) + +Unless required by applicable law or agreed to in writing, this +software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. diff --git a/README.md b/README.md index 3d0900e..16eff96 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,45 @@ -# Azuma_wifi_switch +**Edit a file, create a new file, and clone from Bitbucket in under 2 minutes** +When you're done, you can delete the content in this README and update the file with details for others getting started with your repository. + +*We recommend that you open this README in another tab as you perform the tasks below. You can [watch our video](https://youtu.be/0ocf7u76WSo) for a full demo of all the steps in this tutorial. Open the video in a new tab to avoid leaving Bitbucket.* + +--- + +## Edit a file + +You’ll start by editing this README file to learn how to edit a file in Bitbucket. + +1. Click **Source** on the left side. +2. Click the README.md link from the list of files. +3. Click the **Edit** button. +4. Delete the following text: *Delete this line to make a change to the README from Bitbucket.* +5. After making your change, click **Commit** and then **Commit** again in the dialog. The commit page will open and you’ll see the change you just made. +6. Go back to the **Source** page. + +--- + +## Create a file + +Next, you’ll add a new file to this repository. + +1. Click the **New file** button at the top of the **Source** page. +2. Give the file a filename of **contributors.txt**. +3. Enter your name in the empty file space. +4. Click **Commit** and then **Commit** again in the dialog. +5. Go back to the **Source** page. + +Before you move on, go ahead and explore the repository. You've already seen the **Source** page, but check out the **Commits**, **Branches**, and **Settings** pages. + +--- + +## Clone a repository + +Use these steps to clone from SourceTree, our client for using the repository command-line free. Cloning allows you to work on your files locally. If you don't yet have SourceTree, [download and install first](https://www.sourcetreeapp.com/). If you prefer to clone from the command line, see [Clone a repository](https://confluence.atlassian.com/x/4whODQ). + +1. You’ll see the clone button under the **Source** heading. Click that button. +2. Now click **Check out in SourceTree**. You may need to create a SourceTree account or log in. +3. When you see the **Clone New** dialog in SourceTree, update the destination path and name if you’d like to and then click **Clone**. +4. Open the directory you just created to see your repository’s files. + +Now that you're more familiar with your Bitbucket repository, go ahead and add a new file locally. You can [push your change back to Bitbucket with SourceTree](https://confluence.atlassian.com/x/iqyBMg), or you can [add, commit,](https://confluence.atlassian.com/x/8QhODQ) and [push from the command line](https://confluence.atlassian.com/x/NQ0zDQ). \ No newline at end of file diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..0525a8c --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,16 @@ +# See the build system documentation in IDF programming guide +# for more information about component CMakeLists.txt files. + +idf_component_register( + SRCS main.c uart_ifx.c port.c adc_ifx.c modem.c comms.c rtc.c data_processing.c ota.c hmi.c wifi_Init.c wifi_Client.c wifi_OTA.c wifi_webServer.c nvm.c # list the source files of this component + INCLUDE_DIRS "."# optional, add here public include directories + PRIV_INCLUDE_DIRS # optional, add here private include directories + REQUIRES driver esp_adc nvs_flash app_update esp_timer esp_event esp-tls esp_http_client esp_https_server ulp soc esp_wifi lwip mqtt# optional, list the component requirements + PRIV_REQUIRES # optional, list the private requirements +) + +set(ulp_app_name ulp_${COMPONENT_NAME}) +set(ulp_riscv_sources "ulp/ulp_main.c") +set(ulp_exp_dep_srcs "main.c") + +ulp_embed_binary(${ulp_app_name} "${ulp_riscv_sources}" "${ulp_exp_dep_srcs}") \ No newline at end of file diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild new file mode 100644 index 0000000..7e23439 --- /dev/null +++ b/main/Kconfig.projbuild @@ -0,0 +1,14 @@ +# put here your custom config value +menu "Example Configuration" +config ESP_WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config ESP_WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. +endmenu diff --git a/main/MCP39F501.h b/main/MCP39F501.h new file mode 100644 index 0000000..6d778f5 --- /dev/null +++ b/main/MCP39F501.h @@ -0,0 +1,35 @@ +/* + * MCP39F501.h + * + * Created on: Feb 25, 2025 + * Author: Manticore + */ + +#ifndef MAIN_MCP39F501_H_ +#define MAIN_MCP39F501_H_ + +#define MCP39F501_HEADER "0xa5" +#define MCP39F501_OK "0x00" +#define MCP39F501_TIMEOUT "0xff" +#define MCP39F501_CSFAIL "0x01" +#define MCP39F501_BADCSUM "0x02" +#define MCP39F501_INOVFLOW "0x03" +#define MCP39F501_OUTOVFLOW "0x04" +#define MCP39F501_TOOSHORT "0x05" +#define MCP39F501_TOOLONG "0x06" +#define MCP39F501_INBAD "0x07" +#define MCP39F501_INSIZE "0x08" + + +#define MCP39F501_EV_OC "0x8000" +#define MCP39F501_EV_OV "0x4000" +#define MCP39F501_EV_OP "0x2000" +#define MCP39F501_EV_UV "0x1000" +#define MCP39F501_EV_OF "0x0800" +#define MCP39F501_EV_UF "0x0400" +#define MCP39F501_EV_OT "0x0200" +#define MCP39F501_EV_UT "0x0100" +#define MCP39F501_EV_VSA "0x0080" +#define MCP39F501_EV_VSU "0x0040" + +#endif /* MAIN_MCP39F501_H_ */ diff --git a/main/adc_ifx.c b/main/adc_ifx.c new file mode 100644 index 0000000..40f41aa --- /dev/null +++ b/main/adc_ifx.c @@ -0,0 +1,187 @@ +/* + * adc_ifc.c + * + * Created on: Jan 17, 2023 + * Author: Partha + */ +#include +#include +#include +#include +#include "esp_log.h" +//#include "driver/adc.h" +#include "esp_adc/adc_oneshot.h" +#include "esp_adc/adc_cali.h" +#include "esp_adc/adc_cali_scheme.h" +//#include "esp_adc_cal.h" +#include "data_processing.h" +#include "hmi.h" +#include "ulp_main.h" + +static const char* TAG = "ADC_IF"; + +#define LOG_LOCAL_LEVEL ESP_LOG_INFO + +//static esp_adc_cal_characteristics_t adc1_chars; +RTC_DATA_ATTR uint32_t light_data3; +RTC_DATA_ATTR uint32_t prev_light_data3; + +adc_oneshot_unit_handle_t adc1_handle; +adc_cali_handle_t adc1_cali_handle = NULL; +static int adc_raw[2]; +static int voltage[2]; + +static bool adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_cali_handle_t *out_handle) +{ + adc_cali_handle_t handle = NULL; + esp_err_t ret = ESP_FAIL; + bool calibrated = false; + +#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED + if (!calibrated) { + ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting"); + adc_cali_curve_fitting_config_t cali_config = { + .unit_id = unit, + .atten = atten, + .bitwidth = ADC_BITWIDTH_DEFAULT, + }; + ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle); + if (ret == ESP_OK) { + calibrated = true; + } + } +#endif + +#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED + if (!calibrated) { + ESP_LOGI(TAG, "calibration scheme version is %s", "Line Fitting"); + adc_cali_line_fitting_config_t cali_config = { + .unit_id = unit, + .atten = atten, + .bitwidth = ADC_BITWIDTH_DEFAULT, + }; + ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle); + if (ret == ESP_OK) { + calibrated = true; + } + } +#endif + + *out_handle = handle; + if (ret == ESP_OK) { + ESP_LOGI(TAG, "Calibration Success"); + } else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) { + ESP_LOGW(TAG, "eFuse not burnt, skip software calibration"); + } else { + ESP_LOGE(TAG, "Invalid arg or no memory"); + } + + return calibrated; +} + +void adc_ifx_init(void) +{ + /*esp_adc_cal_value_t val_type = esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_DEFAULT, 0, &adc1_chars); + + //Check type of calibration value used to characterize ADC + if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) + { + printf("ADC: eFuse Vref"); + } + else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) + { + printf("ADC: Two Point"); + } + else + { + printf("ADC: Default"); + } + adc1_ulp_enable(); + ESP_ERROR_CHECK(adc1_config_width(ADC_WIDTH_BIT_DEFAULT));*/ + + //-------------ADC1 Init---------------// + adc_oneshot_unit_init_cfg_t init_config1 = { + .unit_id = ADC_UNIT_1, + .ulp_mode = ADC_ULP_MODE_DISABLE + }; + ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle)); + + //-------------ADC1 Config---------------// + adc_oneshot_chan_cfg_t config = { + .bitwidth = ADC_BITWIDTH_13, + .atten = ADC_ATTEN_DB_11, + }; + ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, ADC_CHANNEL_0, &config)); + //ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, ADC_CHANNEL_1, &config)); + + //-------------ADC1 Calibration Init---------------// + if(false == adc_calibration_init(ADC_UNIT_1, ADC_ATTEN_DB_11, &adc1_cali_handle)) + { + ESP_LOGI(TAG, "ADC Calibration NOT Supported"); + } +} + +static void adc_cal_deinit(adc_cali_handle_t handle) +{ +#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED + ESP_LOGI(TAG, "deregister %s calibration scheme", "Curve Fitting"); + ESP_ERROR_CHECK(adc_cali_delete_scheme_curve_fitting(handle)); + +#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED + ESP_LOGI(TAG, "deregister %s calibration scheme", "Line Fitting"); + ESP_ERROR_CHECK(adc_cali_delete_scheme_line_fitting(handle)); +#endif +} + +void adx_ifx_deinit(void) +{ + //adc_cal_deinit(adc1_cali_handle); + adc_oneshot_del_unit(adc1_handle); +} + +uint32_t adc_if_get_batt_mV(void) +{ + //uint32_t val = 4*esp_adc_cal_raw_to_voltage(adc1_get_raw(ADC1_CHANNEL_0), &adc1_chars); + ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, ADC_CHANNEL_0, &adc_raw[0])); + ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_handle, adc_raw[0], &voltage[0])); + return 4*voltage[0]; +} + +#if 0 +uint32_t adc_if_get_light_mV(void) +{ + uint32_t test_val = 0; + //uint32_t val = esp_adc_cal_raw_to_voltage(adc1_get_raw(ADC1_CHANNEL_1), &adc1_chars); + ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, ADC_CHANNEL_1, &adc_raw[1])); + ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_handle, adc_raw[1], &voltage[1])); + + uint32_t val = voltage[1]; + + ESP_LOGI(TAG, "Light mV: %ld", val); + test_val = (val*255)/2504; + //if(data_get_light_sensor_gp() && data_get_light_sensor_gn()) + { + /*if the new light_sensor value is greater than GB_threshold or lower than GN_threshold*/ + //if((data_get_light_sensor_gn() > test_val) || (data_get_light_sensor_gp() < test_val)) + { + /* set the prev_light_data to the previous reading */ + //ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_handle, ulp_raw_light_data, (int *)&prev_light_data)); + //prev_light_data = ulp_light_data; + //ulp_prev_light_data=ulp_light_data;//light_data; + //prev_light_data = (255*prev_light_data)/2504; + //ulp_light_data = (255*val)/2504; + //ESP_LOGI(TAG,"LIGHT_GN = %ld\nLIGHT_GP = %ld",data_get_light_sensor_gn(),data_get_light_sensor_gp()); + ESP_LOGI(TAG,"Updating Prev_light_data"); + prev_light_data3 = light_data3; + light_data3 = test_val; + //ulp_light_data = val; + } + } + return val; +} + +uint32_t adc_if_get_prev_light_mV(void) +{ + return prev_light_data3; +} +#endif diff --git a/main/adc_ifx.h b/main/adc_ifx.h new file mode 100644 index 0000000..80ff1d1 --- /dev/null +++ b/main/adc_ifx.h @@ -0,0 +1,18 @@ +/* + * adc_ifx.h + * + * Created on: Jan 17, 2023 + * Author: Partha + */ + +#ifndef MAIN_ADC_IFX_H_ +#define MAIN_ADC_IFX_H_ + +void adc_ifx_init(void); +void adx_ifx_deinit(void); +uint32_t adc_if_get_batt_mV(void); +uint32_t adc_if_get_light_mV(void); +uint32_t adc_if_get_prev_light_mV(void); + + +#endif /* MAIN_ADC_IFX_H_ */ diff --git a/main/comms.c b/main/comms.c new file mode 100644 index 0000000..46bec4f --- /dev/null +++ b/main/comms.c @@ -0,0 +1,2106 @@ + +#include "esp_app_desc.h" +#include "main.h" +#include "rtc.h" +#include "hmi.h" +#include "wifi_Init.h" +#include +#include +#include +#include +#include +#include "modem.h" +#include "ota.h" +#include "comms.h" +#include "wifi_Client.h" +#include "mqtt_client.h" +#include "data_processing.h" +#include "driver/gpio.h" +#include "port.h" +#include "wifi_Client.h" +#include "wifi_OTA.h" + +// Development support: force the operation of the following configuration functions +#ifdef SET_CFG_BITS +// set 1 to force the appropriate functions +#define FORCE_MCU_CONFIG_DOWNLOAD 1 +#define FORCE_MCU_PROG_DOWNLOAD 1 +#define FORCE_MODEM_PROG_DOWNLOAD 1 +#else + + + +#define FORCE_MCU_CONFIG_DOWNLOAD 0 +#define FORCE_MCU_PROG_DOWNLOAD 0 +#define FORCE_MODEM_PROG_DOWNLOAD 0 +#endif + +/* Set 1 to use "unwired/[IMEI]", e.g. "unwired/862061049756488" +* Set 0 to use "unwiredct" +*/ +#define COMMS_PUBLISH_WITH_IMEI 0 +#define JWT_LEN 1024 + +#define CUSTOMER_SERVER 1 +#define TESTING_SERVER 0 +#define NEW_SERVER 0 + +#define COMMS_TIMER_ID 1 +#define OTA_STATUS_OK 0 + +#if CUSTOMER_SERVER +/* definitions */ +/*For developement: dev.api.mywirelessfence.com*/ +/*For production: api.mywirelessfence.com*/ +#define SERVER_URL_BASE "https://api.mywirelessfence.com/api/v1/trackers/tracker/%s/" //Partha - March 16, 2022 +#define UPDATE_URL SERVER_URL_BASE "update" +//#define MCU_CONFIG_URL "https://api.mywirelessfence.com/api/v2/trackers/tracker/%s/mcu_config_download" //Partha - March + +/*customer (Laxmana's endpoint)*/ +#define MCU_CONFIG_URL "https://ops.azuma.com/SMSListener/api/DeviceInfo/%s/update" + +/*customer (Laxmana's endpoint)*/ +#define MCU_SP4_URL "http://54.204.230.201:8081/hae/azuma/%s/mcu_config_download" + +/*customer endpoint*/ +#define MCU_SP5_URL "http://54.204.230.201:8081/hae/azuma/%s/mcu_pgm_download/version" + +/*customer endpoint*/ +#define MCU_SP6_URL "https://ops.azuma.com/SMSListener/api/DeviceInfo/%s/modem_pgm_download/version" + +/* Name of file to create in modem file system for the downloaded image. +* This is arbitrary, but we name the upper and lower version differently +* to avoid possible mixing up at the firmware level (the server can +* still mix them up if a lower app was put at the /higher URL) +*/ +#if ST_OTA_HIGHER_APPLICATION +/* A running "higher" app must download and install a "lower" app and vice versa. */ +#define MCU_FW_UPDATE_FILE "lrsm0005_fw_ota_image_lower.bin" +#else +#define MCU_FW_UPDATE_FILE "lrsm0005_fw_ota_image_higher.bin" +#endif + +//#define MODEM_FW_UPDATE_FILE "Update_BG77LAR02A04_01.009.01.009-R02A04_01.006.01.006.zip" +//#define MCU_PROG_URL_HIGHER "https://parthasarathimishra.com/hae/azuma/%s/mcu_pgm_download/lrsm0005_fw_ota_image_higher.bin" +//#define MCU_PROG_URL_LOWER "https://parthasarathimishra.com/hae/azuma/%s/mcu_pgm_download/lrsm0005_fw_ota_image_lower.bin" +#define MCU_PROG_VER_URL SERVER_URL_BASE "mcu_pgm_download/version" +//#define MODEM_PROG_URL SERVER_URL_BASE "modem_pgm_download" +#define MODEM_PROG_VER_URL SERVER_URL_BASE "modem_pgm_download/version" +#define WHITELIST_URL SERVER_URL_BASE "whitelist" + +/*Laxmana's server*/ +#define MCU_PROG_URL "https://ops.azuma.com/CSW/HAEMcuUpdates/New/" MCU_FW_UPDATE_FILE +//#define MODEM_PROG_URL "https://parthasarathimishra.com/hae/azuma/%s/modem_pgm_download/Update_BG77LAR02A04_01.009.01.009-R02A04_01.006.01.006.zip" +/*Laxmana's server*/ +#define MODEM_PROG_URL "https://ops.azuma.com/CSW/HAEModemUpdates/New/Update_BG77.bin" + +/*customer endpoint*/ +#define MQTT_HOST "azumamqtt1.cedalo.cloud" +#define MQTT_PORT 1883 +#endif + +#define MQTT_CLIENTID (BG96_deviceID) // TODO: Put ESP MAC ID here + +/* "unwired/862061049756488" (modem's IMEI) */ +#define MQTT_PUB_TOPIC_LEN 48 + +#define MCU_CONFIG_FLAG_WHITELIST_UPDATE 0x1 +#define MCU_CONFIG_FLAG_MCU_UPDATE 0x2 +#define MCU_CONFIG_FLAG_MODEM_UPDATE 0x4 + +#define HTTP_RETRY_COUNT 5 +#define HTTP_RETRY_WAIT 20000 + +#define HOURS_TO_SECONDS_COEFF 3600 + +#define HEARTBEAT_TIME_SEC 14400 +#define HEARTBEAT_TIME_RST 5 + +static const char* TAG = "COMMS"; + +#define LOG_LOCAL_LEVEL ESP_LOG_INFO + +#define STATUS_ERROR -1 + +#define MCU_OTA_FILE_LOCAL "OTA.bin" + +#define RELAY_ON 1 +#define RELAY_OFF 0 + + +extern bool isTestMode; +extern int heartbeat_time; +extern int qmtopen_retries; +extern bool qmtopen_fail; +#define QMTOPEN_RETRY_NUM 3 +extern bool qmtclose_fail; +extern bool cops_fail; +extern bool testmode_timeout; + +static int cops_reset_num = 0; + +bool deviceCfged = false; + +int config_heartbeat_time = HEARTBEAT_TIME_SEC; + +bool WakeupFlag = false; + +bool ConfigFlag = false; + +bool McuUpdateFlag = false; + +bool ModemUpdateFlag = false; + +bool PreviousRelayState = false; + +bool RelayState = false; + +char time_stamp_str[50]; + +char mac_address[16]; + +char SP1[150]; + +char board_version_no[3]={'0'}; + +//#define BROCKER_URL_TEST "mqtt://azumamqtt1.cedalo.cloud:1883" +#define BROCKER_URL_TEST "mqtt://broker.mqtt.cool:1883" +#define MQTT_TOPIC_PUB "/topic/qos0" +#define MQTT_TOPIC_SUB "/topic/qos0" + +esp_mqtt_client_handle_t client; + + + // Configure Target brocker URL + esp_mqtt_client_config_t mqtt_cfg = { + .broker.address.uri = BROCKER_URL_TEST, + }; + +void SP1_Mqtt_Tx(int ); + + +/* This function will handle led state depending on previous relay state. +*/ +static void update_relay_led() +{ + if(PreviousRelayState == true) + { + //board_led_all_off(); + //board_led_green_on(); + hmi_choose_led(HMI_LED_GREEN, HMI_LED_ON); + } + else + { + //board_led_all_off(); + //board_led_red_on(); + hmi_choose_led(HMI_LED_RED, HMI_LED_ON); + } +} + +typedef enum +{ + COMMS_EVENT_START, + COMMS_EVENT_TIMER, + COMMS_EVENT_STOP, + COMMS_EVENT_CONNECT, +} comms_event_t; + +/* State of the comms process */ +typedef enum +{ + COMMS_STATE_INIT, + COMMS_STATE_WAIT_WIFI, + COMMS_STATE_WAIT_NET_CONNECT, + COMMS_STATE_WAIT_MQTT_CONNECT, + COMMS_STATE_WAIT_MQTT_PUB, + COMMS_STATE_WAIT_MQTT_CLOSE, + COMMS_STATE_WAIT_HTTP_SEND_SP3, + COMMS_STATE_WAIT_HTTP_GET_SP3, + COMMS_STATE_WAIT_HTTP_SEND_SP4, + COMMS_STATE_WAIT_HTTP_GET_SP4, + COMMS_STATE_WAIT_HTTP_GET_MCU_UPDATE, /* Get version */ + COMMS_STATE_WAIT_HTTP_GET_MCU_FILE, /* Download file */ + COMMS_STATE_WAIT_MCU_UPDATE, /* Install update */ + COMMS_STATE_GOING_IDLE, + COMMS_STATE_IDLE, + //COMMS_STATE_RELAY_WAIT, + COMMS_STATE_WAIT, + COMMS_STATE_WAIT_HTTP_SEND_SP5, + COMMS_STATE_WAIT_HTTP_GET_SP5, + COMMS_STATE_WAIT_BG96_OFF, + +} comms_state_t; + +typedef struct +{ + comms_state_t state; + + /* Timer */ + bool poll_timer; + + /* User start callback */ + comms_op_cb_t start_cb; + bool is_starting; + + /* User connect callback */ + comms_op_cb_t connect_cb; + + char mqtt_pub_topic[MQTT_PUB_TOPIC_LEN]; + /* Printing an SP2 message: + * Header: 3 + 1 + [IMEI-15] +1 + 10 + 1+ 10 + 1 = 42 + * [32],[4],[4],[3] = 46 * 20 = 920 + * Footer: 3 + * 42 + 920 + 3 = 965 + * Add buffer overflow protection in writing routines. + */ + char mqtt_buf[1024]; /* Also general buffer */ + //char jwt[JWT_LEN]; + uint8_t overflow_check; + int mqtt_pub_counter; + + /* flagged actions to take */ + bool get_whitelist; + bool get_mcu_update; + bool get_modem_update; + + /* Status of the last communication cycle + * OK or ERROR + */ + int last_cycle_status; + + /* Status of last pending operation */ + bool last_op_success; + bool wf1_msg; + + int httpRetryCnt; + comms_state_t http_init_state; +} comms_t; + +/* Local process state */ + + +static comms_t comms; +uint8_t Comms_WF1_Flag = 2; //Partha - 22 March, 2022 +//extern void Bypass_Comms_Interval(uint8_t state); +extern void Copy_ScanData(void *src); + +//declaration of Comms_Check_TestMode. + +static void Comms_Check_TestMode(); + + +/* ------------------------------------------------------------------------- */ + +/* Comms process: + * 1. Get info from server via HTTP command sequence. + * 2. Publish SP1 record: device status. + * 3. Publish SP2 records: BLE scan data. Each record is one scan event. + */ + + +/* Record format + * + * To server: + * TYPE IMEI TIMESTAMP ... EP + * + * Fields are delineated by spaces. Data items within fields must use a comma delineator. + * + * Type: SP1 SP2 ... SPN + * + * Timestamp: epoch time, seconds + * E.g. 1599492628 + * + * SP1 fields: + * 1. Firmware version : string, e.g. v1.2.1-5f1ec90 + * 2. battery level : int mV reading + * 3. LTE RSSI : int dB + * + * SP1 862061049756488 1599777618 v1.2.1-5f1ec90 3650 -82 EP + * + * SP2 fields: + * 1 Epoch of scan : int + * 2 Array of SCAN_DATA[max 20] where + * SCAN_DATA: + * UUID,MAJOR,MINOR,BLE-RSSI + * UUID : hex string + * MAJOR: hex string + * MINOR: hex string + * RSSI : int dB + * + * Full uplink record example containing 3 SCAN DATA items: + * SP2 862061049756488 1599777618 1599775427 ABCD0123ABCD0123,ABCD,0123,-77 ABCD0123ABCD0123,ABCD,0124,-57 ABCD0123ABCD0123,ABCD,0125,-72 EP + * + * Testing: + * mosquito_pub + * -h 54.90.145.32 + * -m "SP2 862061049756488 1599777618 1599775427 ABCD0123ABCD0123,ABCD,0123,-77 ABCD0123ABCD0123,ABCD,0124,-57 ABCD0123ABCD0123,ABCD,0125,-72 EP" + * -t unwired/862061049756488 + * -i 862061049756488 + */ + + +/* +static int get_int_arg(void) +{ + char* p = strtok(0, " "); + int arg = CONFIG_BAD_INT_VALUE; + if (p) + { + arg = atoi(p); + } + return arg; +} +#*/ +/* +static uint32_t get_hex_arg(void) +{ + char* p = strtok(0, " "); + uint32_t arg = CONFIG_BAD_HEX_VALUE; + if (p) + { + arg = strtol(p, 0, 16); + } + return arg; +} +*/ + +static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { + event_id = 0; + if (event_base == WIFI_EVENT) { + switch (event_id) { + case WIFI_EVENT_STA_START: + ESP_LOGI(TAG, "Wi-Fi started, connecting..."); + esp_wifi_connect(); + break; + + case WIFI_EVENT_STA_CONNECTED: + ESP_LOGI(TAG, "Wi-Fi connected."); + break; + + case WIFI_EVENT_STA_DISCONNECTED: + ESP_LOGW(TAG, "Wi-Fi disconnected, reconnecting..."); + esp_wifi_connect(); + break; + + default: + ESP_LOGI(TAG, "Unhandled Wi-Fi event: %ld", event_id); + break; + } + } + else if (event_base == IP_EVENT) { + switch (event_id) { + case IP_EVENT_STA_GOT_IP: { + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip)); + break; + } + } + } +} + + +static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) +{ + ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32 "", base, event_id); + esp_mqtt_event_handle_t event = event_data; + esp_mqtt_client_handle_t client = event->client; + + switch ((esp_mqtt_event_id_t)event_id) { + case MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT Connected!"); + comms.poll_timer = true; + break; + case MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT Message Published!"); + comms.poll_timer = true; + break; + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "MQTT Broker Disconnected!"); + comms.poll_timer = true; + break; + + case MQTT_EVENT_DATA: + ESP_LOGI(TAG, "Received on topic: %.*s, Message: %.*s", event->topic_len, event->topic, event->data_len, event->data); + ESP_LOGI(TAG, "string copy start"); + char tmp[20] = {0}; // Ensure it's properly initialized + strncpy(tmp, event->data, sizeof(tmp) - 1); // Copy message safely + ESP_LOGI(TAG, "string copy value : %s",tmp); + ESP_LOGI(TAG, "string copy end"); + comms.poll_timer = true; + break; + + default: + break; + } +} + +/* ------------------------------------------------------------------------- */ + +static const char* state_str(comms_state_t s) +{ + switch (s) { + case COMMS_STATE_INIT:return "INIT"; + case COMMS_STATE_WAIT_WIFI:return "WAIT_WIFI"; + case COMMS_STATE_WAIT_NET_CONNECT:return "WAIT_NET_CONNECT"; + case COMMS_STATE_WAIT_MQTT_CONNECT:return "WAIT_MQTT_CONNECT"; + case COMMS_STATE_WAIT_MQTT_PUB:return "WAIT_MQTT_PUB"; + case COMMS_STATE_WAIT_MQTT_CLOSE:return "WAIT_MQTT_CLOSE"; + case COMMS_STATE_WAIT_HTTP_SEND_SP3:return "WAIT_HTTP_SEND_CONFIG_STATUS"; + case COMMS_STATE_WAIT_HTTP_GET_SP3:return "WAIT_HTTP_GET_CONFIG_STATUS"; + case COMMS_STATE_WAIT_HTTP_SEND_SP4:return "WAIT_HTTP_SEND_MCU_CONFIG"; + case COMMS_STATE_WAIT_HTTP_GET_SP4:return "WAIT_HTTP_GET_MCU_CONFIG"; + case COMMS_STATE_WAIT_HTTP_GET_MCU_UPDATE:return "WAIT_HTTP_GET_MCU_UPDATE"; + case COMMS_STATE_WAIT_HTTP_GET_MCU_FILE:return "WAIT_HTTP_GET_MCU_FILE"; + case COMMS_STATE_WAIT_MCU_UPDATE:return "WAIT_MCU_UPDATE"; + case COMMS_STATE_GOING_IDLE: return "COMMS_STATE_GOING_IDLE"; + case COMMS_STATE_IDLE:return "IDLE"; + default: + return ""; + } +} + +static void transition(comms_state_t state_to) +{ + + ESP_LOGI(TAG, "Transistion function "); + ESP_LOGD(TAG, "%s->%s\n", state_str(comms.state), state_str(state_to)); + ESP_LOGI(TAG, "%s->%s\n", state_str(comms.state), state_str(state_to)); + comms.state = state_to; +} + + +static int start_mqtt_publish_sequence(void) //Partha - March 11, 2022 +{ + /* We have synchronized the configuration + * trigger the start completed event if we were starting. + */ + if (comms.is_starting) { + comms.is_starting = false; + if (comms.start_cb) { + comms.start_cb(COMMS_STATUS_OK); + comms.start_cb = 0; + } + } + + /* Reset the operation status flag */ + comms.last_op_success = false; + + + + /* Run the MQTT publish sequence */ + int retval = 0; + retval = esp_mqtt_client_start(client); /*Mqtt client connect */ + // SP1_Mqtt_Tx(config_heartbeat_time); + + if (retval == WIFI_STATUS_OK) + { + /* Indicate offloading data, moved to BG96 init for TFS-7 */ + //hmi_set_offload_mode(true); + ESP_LOGI(TAG,"Connecting to MQTT broker\r\n"); + transition(COMMS_STATE_WAIT_MQTT_CONNECT); + } + else + { + ESP_LOGE(TAG, "Connecting to MQTT broker failed.\r\n"); + comms.poll_timer = true; + transition(COMMS_STATE_GOING_IDLE); + } + return retval; +} + + + +static int ota_file_open_cb(uint32_t fileSize) +{ + int retval = MODEM_STATUS_OK; + + esp_err_t res = ota_init(fileSize); + if (ESP_OK == res) { + ESP_LOGI(TAG,"OTA file opened\r\n"); + } + else { + ESP_LOGI(TAG,"OTA file open error: %d\r\n", res); + /* This will abort the entire update process. */ + retval = MODEM_STATUS_ERROR; + } + + return retval; +} + +static int ota_file_data_cb(const char* buf, int buf_len) +{ + int retval = MODEM_STATUS_OK; + + /* Data callback: we can receive a buffer up to MODEM_FREAD_SIZE bytes */ + esp_err_t res = ota_get_data_to_buffer(buf, buf_len); + + if (ESP_OK == res) { + ESP_LOGI(TAG,"%d bytes OTA data processed\r\n", buf_len); + } + else { + ESP_LOGI(TAG,"%d bytes OTA data error: %d\r\n", buf_len, res); + /* This will abort the entire update process. */ + retval = MODEM_STATUS_ERROR; + } + + return retval; +} + +/** +This function is used to check if MAC has correct length (15) +to check if the parsed MAC is same as hardware MAC +*/ +static bool mac_check_valid(char *message_mac) +{ + //find the length of message imei + int len = strlen(message_mac); + ESP_LOGI(TAG, "Parsed MAC : %s\r\n",message_mac); + + //check length of MAC field + if (len != 15){ + //invalid IMEI + ESP_LOGW(TAG, "Invalid MAC length"); + return false; + } + + if(!strcmp(message_mac, MAC_ID)){ + ESP_LOGI(TAG,"Message MAC matches device MAC\r\n"); + return true; + } + else{ + ESP_LOGW(TAG, "Message MAC doesn't match device MAC\r\n"); + return false; + } +} + + +/* These handlers handle completion events for commands issued by the BG96 modem driver. +* If success, the comms process continues by way of a "timer" event. +* If fail, the comms process stops what it was doing and goes back to "idle" state. +* The comms process is started once at startup and does not stop thereafter. +*/ +static void wifi_event_cb(int status) +{ + comms.poll_timer = true; + if (WIFI_STATUS_OK == status) + { + /* started */ + transition(COMMS_STATE_WAIT_NET_CONNECT); + } + else + { + /* Nope - back to init */ + ESP_LOGW(TAG, "WiFi startup failed.\r\n"); + transition(COMMS_STATE_GOING_IDLE); + } +} + +static void net_connect_cb(int status) +{ + comms.poll_timer = true; + if (WIFI_STATUS_OK == status) + { + /* connected */ + } + else + { + /* Nope - back to init */ + ESP_LOGW(TAG, "Network connection failed.\r\n"); + if(cops_fail){ + + transition(COMMS_STATE_GOING_IDLE); + } + else{ + transition(COMMS_STATE_GOING_IDLE); + } + } + update_relay_led(); +} + +static void mqtt_pub_cb(int status) +{ + comms.poll_timer = true; + if (WIFI_STATUS_OK == status) + { + /* connected */ + } + else + { + + /* Nope - back to idle */ + ESP_LOGW(TAG,"MQTT publish failed.\r\n"); + transition(COMMS_STATE_GOING_IDLE); + + } + update_relay_led(); +} + + +static void mqtt_close_cb(int status) +{ + comms.poll_timer = true; + if (WIFI_STATUS_OK == status) + { + /* connected */ + } + else + { + if(qmtopen_fail){ + if(qmtopen_retries >= QMTOPEN_RETRY_NUM){ + qmtopen_retries = 0; + qmtopen_fail = false; + ESP_LOGI(TAG,"MQTT open retries exceeded. bypass SP1\r\n"); + transition(COMMS_STATE_WAIT_MQTT_CLOSE); + } + else{ + qmtopen_fail = false; + ESP_LOGW(TAG,"MQTT close failed.\r\n"); + transition(COMMS_STATE_WAIT_NET_CONNECT); + } + } + + + } +} + +static void mqtt_connect_cb(int status) +{ + comms.poll_timer = true; + if (WIFI_STATUS_OK == status) + { + /* connected */ + } + else + { + /* Nope - back idle */ + ESP_LOGW(TAG,"MQTT connection failed.\r\n"); + + /*if open fail flag is set then mqtt connection is closed + and then attempt another mqtt sequence*/ + if(qmtopen_fail){ + //qmtopen_fail = false; + ESP_LOGI(TAG,"Closing connection\r\n"); + //GPIO_ResetBits(GPIO_Pin_22); + //heartbeat_time = 5; + transition(COMMS_STATE_WAIT_MQTT_PUB); + } + else{ + transition(COMMS_STATE_GOING_IDLE); + } + } + + + update_relay_led(); +} + +static void http_op_cb(int status) +{ + if (WIFI_STATUS_OK == status) + { + /*Connected. Set the poll_timer to be true*/ + comms.poll_timer = true; + } + else + { + /*If not connected then check the number of http retries if it's greater than zero*/ + if(comms.httpRetryCnt) + { + /*decrement the number of retries by 1*/ + comms.httpRetryCnt--; + + /*start a timer for 20sec*/ + // timer_start(COMMS_TIMER_ID,HTTP_RETRY_WAIT); + + /*Logging waiting message*/ + ESP_LOGI(TAG,"Waiting for another HTTP Retry.\r\n"); + + /*transition to COMMS_STATE_WAIT state*/ + transition(COMMS_STATE_WAIT); + } + else + { + /*Reinitialize the counter of http retries with 3*/ + comms.httpRetryCnt = HTTP_RETRY_COUNT; + + /*Set the poll_timer to be true*/ + comms.poll_timer = true; + + /* Nope - back to idle */ + ESP_LOGW(TAG,"HTTP operation failed.\r\n"); +/* =======_send SP1 message with the count field as 'F'_=======*/ + //uint8_t heartbeat_time = 1 ; + + SP1_Mqtt_Tx(heartbeat_time); //creating sp1 message + int retval = start_mqtt_publish_sequence(); /* Run the MQTT publish sequence if nothing else pending */ + if(WIFI_STATUS_OK==retval) + { + ESP_LOGI(TAG,"MQTT Publish SP1 message success"); + } + else + { + ESP_LOGI(TAG,"MQTT Publish SP1 message failed"); + + + } + /*transition to COMMS_STATE_GOING_IDLE state*/ + transition(COMMS_STATE_GOING_IDLE); + + } + } + update_relay_led(); +} + +static void fotadl_op_cb(int status) +{ + comms.poll_timer = true; + if (WIFI_STATUS_OK == status) + { + /* completed. */ + ESP_LOGI(TAG,"Modem upgrade SUCCESS.\r\n"); + } + else + { + /* failed. */ + ESP_LOGW(TAG,"Modem upgrade FAILED.\r\n"); + } + /* Let state handle it */ +} + +static int start_http_get_request(comms_state_t init_state,const char* url, const char* log_msg, comms_state_t success_state) +{ + /* Reset the operation status flag */ + comms.last_op_success = false; + /* Initialize the comms.http_init_state to init_state */ + comms.http_init_state = init_state; +// char mac_str[15]; + /* Now make the GET request. */ + /* Setup URL */ + memset(comms.mqtt_buf, 0, sizeof(comms.mqtt_buf)); + snprintf(comms.mqtt_buf, sizeof(comms.mqtt_buf), url, MAC_ID); + ESP_LOGI(TAG,"URL %s\r\n", comms.mqtt_buf); + /* Download the GET data to modem */ + int retval = -1; + char temp[512] = {0}; + retval = http_client_get((const char*)comms.mqtt_buf,temp); + + ESP_LOGI(TAG,"GET %s\r\n", temp); + + + if (retval == WIFI_STATUS_OK) + { + // clear_reset_timestamp(); TODO: Track reset & clear it here + ESP_LOGI(TAG,"GET %s\r\n", log_msg); + comms.poll_timer = true; + transition(success_state); + } + else + { + ESP_LOGE(TAG, "GET %s failed.\r\n", log_msg); + comms.poll_timer = true; + transition(COMMS_STATE_GOING_IDLE); + } + return retval; +} + +static void fwdl_op_cb(int status) +{ + + + if (WIFI_STATUS_OK == status) + { + /* Firmware file was downloaded to the modem. */ + ESP_LOGI(TAG,"MCU FW download SUCCESS.\r\n"); + + comms.last_op_success = true; + /* Continue in the current state */ + comms.poll_timer = true; + } + else + { + if(comms.httpRetryCnt) + { + /*decrement the number of retries by 1*/ + comms.httpRetryCnt--; + + /*start a timer for 20sec*/ + // timer_start(COMMS_TIMER_ID,HTTP_RETRY_WAIT); + + /*Logging waiting message*/ + ESP_LOGI(TAG,"Waiting for another HTTP Retry.\r\n"); + + /*transition to COMMS_STATE_WAIT state*/ + transition(COMMS_STATE_WAIT); + } + else + { + /*Reinitialize the counter of http retries with 3*/ + comms.httpRetryCnt = HTTP_RETRY_COUNT; + + /*Set the poll_timer to be true*/ + comms.poll_timer = true; + + /* Nope - back to idle */ + /* Firmware file not downloaded. */ + ESP_LOGW(TAG,"MCU FW download operation failed.\r\n"); + /* =======_send SP1 message with the count field as 'F'_=======*/ + uint8_t heartbeat_time = 1 ; + SP1_Mqtt_Tx(heartbeat_time); //creating sp1 message + int retval = start_mqtt_publish_sequence(); /* Run the MQTT publish sequence if nothing else pending */ + if(WIFI_STATUS_OK==retval) + { + ESP_LOGI(TAG,"MQTT Publish SP1 message success"); + } + else + { + ESP_LOGI(TAG,"MQTT Publish SP1 message failed"); + } + + comms.last_op_success = false; + + /*transition to COMMS_STATE_GOING_IDLE state*/ + transition(COMMS_STATE_GOING_IDLE); + } + } +} + +static void fwupd_op_cb(int status) +{ + comms.poll_timer = true; + if (WIFI_STATUS_OK == status) + { + /* Firmware file was installed into device. */ + ESP_LOGI(TAG,"MCU FW update SUCCESS.\r\n"); + + comms.last_op_success = true; + } + else + { + /* Firmware file not installed. */ + ESP_LOGW(TAG,"MCU FW installation failed.\r\n"); + comms.last_op_success = false; + /* Restore the LED status */ + update_relay_led(); + transition(COMMS_STATE_GOING_IDLE); + } +} + +//function to prepare SP1, loads the MQTT TX buffer +void SP1_Mqtt_Tx(int config_heartbeat_time){ + + //char relay_state[3]; + //rtc_timestamp_t* gettime; + char build_version[60]={'0'}; + + + char* ptr_version = NULL; + + //create the SP1 message + //SP1 IMEI F/W VER DATE_TIME SENS_EN HEARTBEAT_TIME TEST MODE COUNT RSSI EP format + get_time(time_stamp_str, sizeof(time_stamp_str)); // Pass buffer to function + ESP_LOGI(TAG, "Time_Date : %s", time_stamp_str); + + // Get firmwae version + strcpy(build_version,BUILD_VERSION_STRING); + + ptr_version = strstr(build_version,"_v"); + strncpy(board_version_no,ptr_version + 2,3); + + // wifi_get_mac_addr(mac_address); + + if(PreviousRelayState == 1){ + sprintf(SP1,"SP1 %s %s %s %d H_%d T_%d %s %d EP",MAC_ID,BUILD_VERSION_STRING,time_stamp_str,PreviousRelayState,(config_heartbeat_time / HOURS_TO_SECONDS_COEFF) ,isTestMode,"ON",(HTTP_RETRY_COUNT-comms.httpRetryCnt)); + } + else{ + sprintf(SP1,"SP1 %s %s %s %d H_%d T_%d %s %d EP",MAC_ID,BUILD_VERSION_STRING,time_stamp_str,PreviousRelayState,(config_heartbeat_time / HOURS_TO_SECONDS_COEFF) ,isTestMode,"OFF",(HTTP_RETRY_COUNT-comms.httpRetryCnt)); + } + + ESP_LOGI(TAG, "SP1 message :%s\r\n",SP1); + + //copy sp1 message in mqtt buffer + strcpy(comms.mqtt_buf,SP1); + +} + + +/* function for parsing SP5 message */ +// format example + +static void Comms_Parse_SP5(char *buf, int len) +{ + char messageheader[4] = {0}; + char messageMAC[16] = {0}; + char timestamp[30] = {0}; //array to store the timestamp from sp5 message + char firmware_version[11] = {0}; //array to store the firmware_version from sp5 message + char *strret = NULL; //temporary pointer to store the address of the searched string (to store the returned address from strstr fn ) + float new_fw_version = 0; + float current_fw_version = 0; + char epcheck[2] = {0}; + int ret_sp5 = STATUS_ERROR; + + strret = strtok(buf, " "); + strcpy(messageheader, strret); + ESP_LOGI(TAG, "%s message is received \r\n",messageheader); + + while(strret != NULL){ + + strret = strtok (NULL," "); + strcpy(messageMAC, strret); + + ret_sp5 = mac_check_valid(messageMAC); + if(ret_sp5 != false){ + //ok IMEI + ESP_LOGI(TAG, "Fetched MAC matches board.\r\n"); + } + else{ + ESP_LOGW(TAG, "INVALID MAC!!\r\n"); + comms.poll_timer = true; + transition(COMMS_STATE_GOING_IDLE); + //return ret_sp5; + } + + strret = strtok (NULL," "); + strcpy(timestamp, strret); + ESP_LOGI(TAG,"Time Stamp : %s\r\n",timestamp); + + strret = strtok(NULL, "FW_ "); + + strcpy(firmware_version,strret); + + if((isdigit((unsigned char)firmware_version[0]) > 0) && (isdigit((unsigned char)firmware_version[2]) > 0)){ + /*if the MCU version string is numeric proceed*/ + /*update the version value*/ + new_fw_version = atof(firmware_version); + } + else{ + /*keep the board version as the new version so that update is not triggered*/ + ESP_LOGI(TAG,"NOT VALID FETCHED MCU FW VERSION VALUE!! \r\n"); + new_fw_version = atof(board_version_no); + } + + + current_fw_version = atof(board_version_no); + + strret = strtok (NULL," "); + strcpy(epcheck, strret); + + if(!strcmp(epcheck,"EP\"") || !strcmp(epcheck,"EP")){ + ESP_LOGI(TAG,"(%s) %s message is terminated\r\n", epcheck,messageheader); + break; + } + else{ + ESP_LOGI(TAG,"(%s) %s message is not terminated!! \r\n", epcheck,messageheader); + } + + } + + // new_fw_version = 1; + + if(!(new_fw_version > current_fw_version)) + { + comms.poll_timer = true; + transition(COMMS_STATE_GOING_IDLE); + } + else + { + /*Logging/printing this message*/ + ESP_LOGI(TAG,"F/W Update NEEDED \r\n"); + ESP_LOGI(TAG,"GET MCU UPDATE \r\n"); + comms.poll_timer = true; + transition(COMMS_STATE_WAIT_HTTP_GET_MCU_UPDATE); + /*reading OTA files*/ + } +} + + + +/* function to parsing SP4 message */ +// format example +//SP4 862061049734352 2022/10/20_13:10_4 1 H_1 T_0 EP +static int Comms_Parse_SP4(char *buf, int len) +{ + char messageheader[4] = {0}; + char timestamp[18] = {'0'}; + char messageMAC[16] = {'0'}; + char testMode[3] = {'0'}; + //bool relayState = false; +// char* relayStatePtr = NULL; + int sp4_ret = STATUS_ERROR; + char* strret = NULL; + char hbeat[5] = {'0'}; +// char temp[5] = {'0'}; + char epcheck[2] = {0}; + + strret = strtok(buf," "); + strcpy(messageheader, strret); + ESP_LOGI(TAG,"%s message is received \r\n",messageheader); + + while(strret != NULL){ + strret = strtok (NULL," "); + strcpy(messageMAC, strret); + + sp4_ret = mac_check_valid(messageMAC); + if(sp4_ret != false){ + //ok IMEI + ESP_LOGI(TAG, "Fetched MAC matches board.\r\n"); + } + else{ + ESP_LOGW(TAG, "INVALID MAC!!\r\n"); + comms.poll_timer = true; + transition(COMMS_STATE_GOING_IDLE); + return sp4_ret; + } + + + /*Parse string for date/time*/ + strret = strtok (NULL," "); + strcpy(timestamp, strret); + ESP_LOGI(TAG,"Time Stamp : %s\r\n",timestamp); + + strret = strtok (NULL," "); + if(strret[0] == '1'){ + RelayState = true; + } + else + { + RelayState = false; + } + + ESP_LOGI(TAG,"Relay State : %d\r\n",RelayState); + + strret = strtok (NULL," "); + strcpy(hbeat, strret+2); + ESP_LOGI(TAG,"Received Heartbeat Time : %s\r\n",hbeat); + config_heartbeat_time = atoi(hbeat); + + strret = strtok(NULL," "); + strcpy(testMode, strret); + if(testMode[2] == '1') + { + if(testmode_timeout){ + isTestMode = false; + testmode_timeout = false; + } + else{ + isTestMode = true; + } + + } + else + { + isTestMode = false; + } + //===================================== + ESP_LOGI(TAG,"Test Mode: %d\r\n",isTestMode); + //_____________________ + //maximum heartbeat time is 4 days (96 hours) + if((config_heartbeat_time != 0) && (config_heartbeat_time < 97 && config_heartbeat_time > 0)){ + ESP_LOGI(TAG,"Valid fetched Heartbeat time value\r\n"); + config_heartbeat_time *= HOURS_TO_SECONDS_COEFF; + deviceCfged = true; + } + else{ + config_heartbeat_time = HEARTBEAT_TIME_SEC; + deviceCfged = true; + ESP_LOGI(TAG,"Invalid fetched Heartbeat time value\r\n"); + } + + ESP_LOGI(TAG,"Heartbeat time in Sec : %d\r\n", config_heartbeat_time); + ESP_LOGI(TAG,"Heartbeat time in Hrs : %d\r\n", (config_heartbeat_time/HOURS_TO_SECONDS_COEFF)); + //___________________________ + + strret = strtok (NULL," "); + strcpy(epcheck, strret); + + if(!strcmp(epcheck,"EP\"") || !strcmp(epcheck,"EP")){ + ESP_LOGI(TAG,"(%s) %s message is terminated\r\n", epcheck,messageheader); + break; + } + else{ + ESP_LOGI(TAG,"(%s) %s message is not terminated!! \r\n", epcheck,messageheader); + } + + } + // TODO : Handle system time and print + /* + ESP_LOGI(TAG,"Year: %d\r\n",RTC_DateTime.Year); + ESP_LOGI(TAG,"Month: %d\r\n",RTC_DateTime.Month); + ESP_LOGI(TAG,"MonthDay: %d\r\n",RTC_DateTime.MonthDay); + ESP_LOGI(TAG,"Hour: %d\r\n",RTC_DateTime.Hour); + ESP_LOGI(TAG,"Minute: %d\r\n",RTC_DateTime.Minute); + ESP_LOGI(TAG,"WeekDay: %d\r\n",RTC_DateTime.WeekDay); + RTC_SetTimeDate(&RTC_DateTime); + */ + + // update relay state if changed + if(RelayState != PreviousRelayState) + { + ESP_LOGI(TAG,"Previous Relay State :%d \n",PreviousRelayState); + PreviousRelayState = RelayState; /* update PreviousRelayState */ + + if(RelayState == true) + { + /*============================*/ + port_vbatt_sense_enable(RELAY_ON); + ESP_LOGI(TAG,"Board relay state : %d\n",RelayState); + } + else + { + /*============================*/ + port_vbatt_sense_enable(RELAY_OFF); + ESP_LOGI(TAG,"Board relay state : %d\n ",RelayState); + } + } + else + { + ESP_LOGI(TAG,"Previous Relay State :%d \n",PreviousRelayState); + ESP_LOGI(TAG,"No changes in Relay State\r\n"); + /*Transition to idle state*/ + comms.poll_timer = true; //polling the comms_timer + //Comms_Check_TestMode(); + } + + int retval = COMMS_STATUS_OK; + //creating sp1 message + SP1_Mqtt_Tx(config_heartbeat_time); + + /* Run the MQTT publish sequence if nothing else pending */ + // retval = start_mqtt_publish_sequence(); + if(WIFI_STATUS_OK==retval) + { + ESP_LOGI(TAG,"MQTT Publish SP1 message success"); + + } + else + { + ESP_LOGI(TAG,"MQTT Publish SP1 message failed"); + + } + return retval; +} + + +/* ------------------------------------------------------------------------- */ + + +static esp_err_t data_parse_uint_value(uint32_t *target_buffer, const char needle[], const char *http_resp) +{ + esp_err_t retval = ESP_FAIL; + + char *ret = NULL; + char *start_add = NULL; + char *end_add = NULL; + char parse_value[] = {'0'}; + + int len = 0; + + /*1- Extract temperature limits*/ + ret = strstr(http_resp, needle); + if(ret) + { + /*2- get the starting address of the value*/ + start_add = strstr(ret,(char*)":") + 1; + + /*3- get the ending address of the value*/ + end_add = strstr(ret,(char*)",") + 1; + + if((char *)1 == end_add) + end_add = start_add + strlen(start_add) + 1; + + /*4- Calculate the length of the value*/ + len = end_add - start_add; + + /*5- copy the value into the buffer*/ + snprintf(parse_value, len, "%s", start_add); + + /*6- Assign the double value to the target buffer*/ + *target_buffer = atoi(parse_value); + + retval = ESP_OK; + } + + return retval; +} + +/*Handler for COMMS timer*/ +static void timer_handler(void* context) +{ + comms.poll_timer = true; +} + +/* Parsing SP3 Message + The job of the COMM process is to fetch SP3 data from the server + and parse its fields. + + Following is a sample for SP3 message, in JSON format. + { + "IMEI": "862061049734352", + "SIMNumber": "862061049734352", + "McuUpdate": 1, + "ModemUpdate": 0, + "ConfigUpdate": 1 + } + We need to parse only McuUpdate, ModemUpdate and ConfigUpdate */ +static int Comms_Parse_SP3(char *buf, int len) +{ + char *strret; + char messageMAC[15]; + int retval; + // Configuration global variable + uint32_t g_server_time = 0; + uint32_t g_ID; + uint32_t g_SensorInt; + uint32_t g_ConfigUpdate; + uint32_t g_McuUpdate; + uint32_t g_ModemUpdate; + int ret = STATUS_ERROR; + if(buf != NULL) + strret = strstr(buf, "IMEI"); + else + return -1; + strncpy(messageMAC, &strret[7],(sizeof(messageMAC)/sizeof(messageMAC[0]))); + messageMAC[15] = '\0'; + + ret = mac_check_valid(messageMAC); + if(ret != false){ + //ok IMEI + ESP_LOGI(TAG,"Fetched IMEI matches board.\r\n"); + } + else{ + ESP_LOGW(TAG,"INVALID IMEI!!\r\n"); + comms.poll_timer = true; + transition(COMMS_STATE_GOING_IDLE); + return ret; + } + + strret = strstr(buf, "SIMNumber"); + + /***********/ + + data_parse_uint_value(&g_ID,"id", buf); + data_parse_uint_value(&g_SensorInt,"SensorInt", buf); + data_parse_uint_value(&g_ConfigUpdate,"ConfigUpdate", buf); + data_parse_uint_value(&g_McuUpdate,"McuUpdate", buf); + data_parse_uint_value(&g_ModemUpdate,"ModemUpdate", buf); + ESP_LOGI(TAG, "ConfigUpdate: %luMcuUpdate: %lu ModemUpdate: %lu", g_ConfigUpdate, g_McuUpdate, g_ModemUpdate); + + // teast ota enable + // g_McuUpdate = 1; + + if (g_McuUpdate == 1) { + McuUpdateFlag = true; + ESP_LOGI(TAG, "McuUpdateFlag: %d", McuUpdateFlag); + } else if (g_McuUpdate == '0') { + McuUpdateFlag = false; + ESP_LOGI(TAG, "McuUpdateFlag: %d", McuUpdateFlag); + } else + ESP_LOGE(TAG, "not valid value for McuUpdateFlag"); + + /***********/ + + // g_ConfigUpdate = 1; + ESP_LOGI(TAG, "g_ConfigUpdate: %lu",g_ConfigUpdate); + + if(g_ConfigUpdate == 1) + { + ConfigFlag = true; + ESP_LOGI(TAG,"\ConfigFlag: %d\n", ConfigFlag); + } + else if(g_ConfigUpdate == 0) + { + ConfigFlag = false; + ESP_LOGI(TAG,"\ConfigFlag: %d\n", ConfigFlag); + } + else + { + ESP_LOGE(TAG,"Not valid value for ConfigFlag"); + ConfigFlag = false; + } + + if(McuUpdateFlag == true){ + ESP_LOGI(TAG,"Going to state COMMS_STATE_WAIT_HTTP_SEND_SP5\r\n"); + comms.poll_timer = true; + transition(COMMS_STATE_WAIT_HTTP_SEND_SP5); + } + else if(ConfigFlag == true){ + deviceCfged = true; + ESP_LOGI(TAG,"Going to state COMMS_STATE_WAIT_HTTP_SEND_SP4\r\n"); + comms.poll_timer = true; + transition(COMMS_STATE_WAIT_HTTP_SEND_SP4); + } + else{ + ESP_LOGI(TAG,"Going to state COMMS_STATE_GOING_IDLE\r\n"); + comms.poll_timer = true; + transition(COMMS_STATE_GOING_IDLE); + } + return 0; +} + +//function to check if in test mode if not check : McuUpdateFlag ,ModemUpdateFlag. +//area for improvement +static void Comms_Check_TestMode(){ + + if(isTestMode == true){ + ESP_LOGI(TAG,"Going to COMMS_STATE_WAIT_HTTP_SEND_SP3\r\n"); + comms.poll_timer = true; + transition(COMMS_STATE_WAIT_HTTP_SEND_SP3); + } + else + if(McuUpdateFlag == true) + { + ESP_LOGI(TAG,"Going to state COMMS_STATE_WAIT_HTTP_SEND_SP5\r\n"); + comms.poll_timer = true; + transition(COMMS_STATE_WAIT_HTTP_SEND_SP5); + } + else{ + if(ModemUpdateFlag == true) + { + ESP_LOGI(TAG,"Going to state COMMS_STATE_WAIT_HTTP_SEND_SP6\r\n"); + comms.poll_timer = true; + /*36-update-comms.c uncommented the following line to enable the transition to sp6 */ + transition(COMMS_STATE_WAIT_HTTP_SEND_SP5); + } + else{ + ESP_LOGI(TAG,"Going to state COMMS_STATE_GOING_IDLE\r\n"); + comms.poll_timer = true; + transition(COMMS_STATE_GOING_IDLE); + } + } + + +} +static void Check_SP1_Order(){ + + if(WakeupFlag){ + WakeupFlag = false; + ESP_LOGI(TAG,"Going to COMMS_STATE_WAIT_HTTP_SEND_SP3\r\n"); + comms.poll_timer = true; + transition(COMMS_STATE_WAIT_HTTP_SEND_SP3); + } + else{ + if(isTestMode == true){ + ESP_LOGI(TAG,"Going to COMMS_STATE_WAIT_HTTP_SEND_SP3\r\n"); + comms.poll_timer = true; + transition(COMMS_STATE_WAIT_HTTP_SEND_SP3); + } + else{ + if(McuUpdateFlag == true) + { + ESP_LOGI(TAG,"Going to state COMMS_STATE_WAIT_HTTP_SEND_SP5\r\n"); + comms.poll_timer = true; + transition(COMMS_STATE_WAIT_HTTP_SEND_SP5); + } + else + if(ModemUpdateFlag == true) + { + ESP_LOGI(TAG,"Going to state COMMS_STATE_WAIT_HTTP_SEND_SP6\r\n"); + comms.poll_timer = true; + /*36-update-comms.c uncommented the following line to enable the transition to sp6 */ + transition(COMMS_STATE_WAIT_HTTP_SEND_SP5); + } + else{ + ESP_LOGI(TAG,"Going to state COMMS_STATE_GOING_IDLE\r\n"); + comms.poll_timer = true; + transition(COMMS_STATE_GOING_IDLE); + } + } + + } + +} + +static void Wakeup_Send_SP1(void){ + + WakeupFlag = true; + int retval = COMMS_STATUS_OK; + //creating sp1 message + SP1_Mqtt_Tx(config_heartbeat_time); + + /* Run the MQTT publish sequence if nothing else pending */ + retval = start_mqtt_publish_sequence(); + + if(WIFI_STATUS_OK==retval) + { + ESP_LOGI(TAG,"MQTT Publish SP1 message success"); + + } + else + { + ESP_LOGI(TAG,"MQTT Publish SP1 message failed"); + + } + +} + + +static int wifi_check_net_connect(void) +{ + ESP_LOGI(TAG, "Attempting connection to google.com :)"); + + if(ESP_OK == http_client_test("https://www.google.com")) + { + ESP_LOGI(TAG, "Connection to internet successful."); + return WIFI_STATUS_OK; + } + else + { + + ESP_LOGI(TAG, "Connection to google.com failed.\n"); + return WIFI_STATUS_ERROR; + } +} + +static int state_machine(comms_event_t evt) +{ + int retval = COMMS_STATUS_OK; + switch (comms.state) + { + case COMMS_STATE_INIT: + if (evt == COMMS_EVENT_START) + { + /* Status is ERROR until cleared by successful completion. */ + comms.last_cycle_status = COMMS_STATUS_ERROR; + + if (wifi_isStarted()) + { + ESP_LOGI(TAG, "Wifi Started"); + wifi_event_cb(WIFI_STATUS_OK); + } + else + { + /* start the wifi process */ + retval = Connect_wifi_sta(WIFI_MODE_STA); + if (retval == WIFI_STATUS_OK) + { + comms.is_starting = true; + ESP_LOGI(TAG,"Wifi Connected....\n"); + transition(COMMS_STATE_WAIT_NET_CONNECT); + comms.poll_timer = true; + } + else + { + comms.is_starting = false; + ESP_LOGI(TAG,"Connecting to WiFi failed\n"); + transition(COMMS_STATE_GOING_IDLE); + comms.poll_timer = true; + } + } + } + break; + case COMMS_STATE_WAIT_WIFI: + break; + case COMMS_STATE_WAIT_NET_CONNECT: + ESP_LOGW(TAG,"Net connect .\n"); + + /* Wait for WiFi network connect event */ + if (evt == COMMS_EVENT_TIMER) + { + + retval = wifi_check_net_connect(); + if(retval == WIFI_STATUS_OK) + { + ESP_LOGI(TAG,"Network connected\r\n"); + + /* Sync time with NTP */ + obtain_time(); + client = esp_mqtt_client_init(&mqtt_cfg); /*Client init */ + esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); + + if (comms.connect_cb) { + comms.connect_cb(COMMS_STATUS_OK); + comms.connect_cb = 0; + } + comms.httpRetryCnt = HTTP_RETRY_COUNT; + ESP_LOGI(TAG,"Sending SP1 message\r\n"); + Wakeup_Send_SP1(); + } + else + { + comms.poll_timer = true; + transition(COMMS_STATE_GOING_IDLE); + } + + } + break; + + case COMMS_STATE_WAIT_HTTP_SEND_SP3: + ESP_LOGW(TAG,"COMMS_STATE_WAIT_HTTP_SEND_SP3.\n"); + + /* Wait for ESP-WiFi HTTP setup event */ + if (1) + // if (evt == COMMS_EVENT_TIMER) + { + /* Now make the GET request. */ + retval = start_http_get_request(COMMS_STATE_WAIT_HTTP_SEND_SP3, MCU_GET_CONFIG_FLAGS_URL1, "configuration", COMMS_STATE_WAIT_HTTP_GET_SP3); + // http_client_do_get_request(0, MAC_ID, response_buf); + + } + + break; + + case COMMS_STATE_WAIT_HTTP_GET_SP3: + + ESP_LOGI(TAG,"Start COMMS_STATE_WAIT_HTTP_GET_SP3"); + + /* Wait for BG96 HTTP GET config event */ + if (evt == COMMS_EVENT_TIMER) + { + /* Get the buffer and process the configuration. */ + int len = 27; + char buf[512] = {0}; + // char *buf = 0; + retval = http_get_response_buf(buf, len); + if (retval == WIFI_STATUS_OK) + { + + ESP_LOGI(TAG,"BUF :\n%s\n",buf); + Comms_Parse_SP3(buf, len); + + } + else{ + comms.poll_timer= true; + transition(COMMS_STATE_GOING_IDLE); + } + } + + break; + + case COMMS_STATE_WAIT_HTTP_SEND_SP4: + + /* Wait for BG96 HTTP setup event */ + if (evt == COMMS_EVENT_TIMER) + { + /* Now make the GET request. */ + retval = start_http_get_request(COMMS_STATE_WAIT_HTTP_SEND_SP4, MCU_SP4_URL, "configuration", COMMS_STATE_WAIT_HTTP_GET_SP4); + } + + break; + + case COMMS_STATE_WAIT_HTTP_GET_SP4: + + ESP_LOGI(TAG, "COMMS_STATE_WAIT_HTTP_GET_SP4 start"); + + /* Wait for BG96 HTTP GET config event */ + // if (evt == COMMS_EVENT_TIMER) + if (1) + { + /* Get the buffer and process the configuration. */ + int len = 0; + char buf[512] ={0}; + retval = http_get_response_buf(buf, &len); + if (retval == WIFI_STATUS_OK) + { + + ESP_LOGI(TAG,"SP4 BUF :\n%s\n",buf); + + //SP4 parsing... + Comms_Parse_SP4(buf, len); + transition(COMMS_STATE_WAIT_MQTT_CONNECT); + comms.poll_timer = true; + } + else + { + comms.poll_timer = true; + transition(COMMS_STATE_GOING_IDLE); + } + } + break; + + case COMMS_STATE_WAIT_HTTP_SEND_SP5: + + /* Wait for BG96 HTTP setup event */ + if (evt == COMMS_EVENT_TIMER) + { + /* Now make the GET request. */ + retval = start_http_get_request(COMMS_STATE_WAIT_HTTP_SEND_SP5, MCU_SP5_URL, "McuUpdate", COMMS_STATE_WAIT_HTTP_GET_SP5); + } + break; + + case COMMS_STATE_WAIT_HTTP_GET_SP5: + + /* Wait for BG96 HTTP GET config event */ + if (evt == COMMS_EVENT_TIMER) + { + /* Get the buffer and process the configuration. */ + int len = 0; + char buf[512] = {0}; + retval = http_get_response_buf(buf, &len); + if (retval == WIFI_STATUS_OK) + { + + ESP_LOGI(TAG,"SP5 BUF :\n%s\n",buf); + + //SP5 parsing... + Comms_Parse_SP5(buf, len); + } + else + { + comms.poll_timer = true; + transition(COMMS_STATE_GOING_IDLE); + } + } + break; + + case COMMS_STATE_WAIT_HTTP_GET_MCU_UPDATE: + + ESP_LOGI(TAG, "COMMS_STATE_WAIT_HTTP_GET_MCU_UPDATE start"); + + /* Set http_init_state to COMMS_STATE_WAIT_HTTP_GET_MCU_UPDATE */ + comms.http_init_state = COMMS_STATE_WAIT_HTTP_GET_MCU_UPDATE; + + /* Wait for BG96 HTTP GET MCU update event */ + //if (evt == COMMS_EVENT_TIMER) + { + comms.get_mcu_update = false; + /* Get the buffer and process the MCU fw version message. */ + + /* Enable WIFI-OTA if MCU-FW is less than the one in SP5 message */ + + wifi_ota_start_firmware_update(MAC_ID); + while(OTA_IN_PROGRESS == wifi_ota_get_status()) + { + if (OTA_FATAL_ERROR == wifi_ota_get_status){ + + comms.poll_timer= true; + transition(COMMS_STATE_GOING_IDLE); + break; + } + vTaskDelay(100/portTICK_PERIOD_MS); + } + + if (OTA_FATAL_ERROR == wifi_ota_get_status){ + + comms.poll_timer= true; + transition(COMMS_STATE_GOING_IDLE); + break; + } + + /*if (retval == WIFI_STATUS_OK) + { + ESP_LOGI(TAG,"MCU FW download started\r\n"); + ///hmi_set_offload_mode(false); + transition(COMMS_STATE_WAIT_HTTP_GET_MCU_FILE); + //hmi_set_update_mode(true); + break; + } + else + { + ESP_LOGE(TAG,"MCU FW download failed to start\r\n"); + transition(COMMS_STATE_WAIT_MQTT_CONNECT); + comms.poll_timer = true; + Continue with session + }*/ + // hmi_set_leds_state(BLUE_LED_FLASHING_OTA); + // while(OTA_IN_PROGRESS == wifi_ota_get_status()) + // { + // vTaskDelay(100/portTICK_PERIOD_MS); + // } + + + if (retval == WIFI_STATUS_OK) + { + // if (COMMS_STATUS_OK == check_mcu_update(buf, len)) + { + /* Different firmware on server. Use it. */ + /* Setup URL: + * Need to download either upper or lower version of the application, + * depending on what we're running now. (i.e. if we are the lower app, need + * to get the upper app) + */ + memset(comms.mqtt_buf, 0, sizeof(comms.mqtt_buf)); + +#if ST_OTA_HIGHER_APPLICATION +//// snprintf(comms.mqtt_buf, sizeof(comms.mqtt_buf), MCU_PROG_URL_LOWER, bg96_get_imei()); +#elif ST_OTA_LOWER_APPLICATION +// snprintf(comms.mqtt_buf, sizeof(comms.mqtt_buf), MCU_PROG_URL_HIGHER, bg96_get_imei()); +#else +//// snprintf(comms.mqtt_buf, sizeof(comms.mqtt_buf), MCU_PROG_URL_LOWER, bg96_get_imei()); +#endif + /*Creating file path and store it in comms.mqtt_buf*/ + // char mac_str[15]; + // wifi_get_mac_addr(mac_str); + // snprintf(comms.mqtt_buf, sizeof(comms.mqtt_buf), MCU_PROG_URL, mac_str); + + /* Download the file data to modem file system */ + int retval = modem_http_get_to_file(fwdl_op_cb, comms.mqtt_buf, MCU_OTA_FILE_LOCAL); + + if (retval == WIFI_STATUS_OK) + { + ESP_LOGI(TAG,"MCU FW download started\r\n"); + ///hmi_set_offload_mode(false); + transition(COMMS_STATE_WAIT_HTTP_GET_MCU_FILE); + //hmi_set_update_mode(true); + break; + } + else + { + ESP_LOGE(TAG,"MCU FW download failed to start\r\n"); + /* Continue with session */ + } + } + } + } + + break; + + case COMMS_STATE_WAIT_HTTP_GET_MCU_FILE: + + /* Wait for download complete callback. */ + if (evt == COMMS_EVENT_TIMER) + { + if (comms.last_op_success) + { + /* Download is ready. Now we can start the read from file operation on the bg96 driver + * and use the callbacks to drive the update. The bg96 process is driving the update. + */ + retval = modem_read_from_file( + fwupd_op_cb, /* called when completed or failed at any point */ + MCU_OTA_FILE_LOCAL, + ota_file_open_cb, /* Can fail and continue the comms state machine */ + ota_file_data_cb); /* Callback for the file data in chunks */ + + if (retval == WIFI_STATUS_OK) + { + ESP_LOGI(TAG,"MCU FW update started\r\n"); + transition(COMMS_STATE_WAIT_MCU_UPDATE); + break; + } + else + { + ESP_LOGE(TAG, "MCU FW update failed to start\r\n"); + /* Continue with session */ + //hmi_set_update_mode(false); + } + } + } + break; + + case COMMS_STATE_WAIT_MCU_UPDATE: + + /* Wait while updating. */ + if (evt == COMMS_EVENT_TIMER) + { + if (comms.last_op_success) + { + /* If it completes, it will ping this and here and we finish it up. + * OTA_terminate_connection sets the flag that is handled in OTA_Tick() + * and main to do a reset to start the new application after comm completes. + */ + ESP_LOGD(TAG, "Ending OTA session, ready for reset\r\n"); + +// OTA_terminate_connection(); + + /* Clear this, in case we have just turned on and have updated - + * no need to trigger the startup callback in main. + */ + comms.is_starting = false; + } + // hmi_set_update_mode(false); + //hmi_set_offload_mode(true); + + } + break; + + case COMMS_STATE_WAIT_MQTT_CONNECT: + /* Wait for MQTT connection event */ + if (evt == COMMS_EVENT_TIMER) + { + /* When connected, we will publish everything we've got. + * First is the status message. + * Follow that with all messages in the BLE scan queue. + */ + ESP_LOGI(TAG,"MQTT connected\r\n"); + + /* Create the pub topic */ + memset(comms.mqtt_pub_topic, 0, MQTT_PUB_TOPIC_LEN); +#if COMMS_PUBLISH_WITH_IMEI + snprintf(comms.mqtt_pub_topic, MQTT_PUB_TOPIC_LEN, "unwired/%s", bg96_get_imei()); +#else + snprintf(comms.mqtt_pub_topic, MQTT_PUB_TOPIC_LEN, "unwiredct"); /* TF-58 */ +#endif + ESP_LOGD(TAG, "pub topic: [%s]\r\n", comms.mqtt_pub_topic); + + char mac_str[15]={0}; + + sprintf(comms.mqtt_pub_topic,"unwired/%s", mac_str); + /* Create the WF1 status message payload */ + //memset(comms.mqtt_buf, 0, sizeof(comms.mqtt_buf)); + //int len = create_type1_record(comms.mqtt_buf, sizeof(comms.mqtt_buf)); + //ESP_LOGD(TAG, "Payload: [%s]\r\n", comms.mqtt_buf); + ESP_LOGI(TAG,"SP1: [%s]\r\n", comms.mqtt_buf); + comms.wf1_msg = true; + + //board_led_all_off(); + //board_led_blue_on(); + hmi_choose_led(HMI_LED_BLUE, HMI_LED_BLINK); + + /* Publish the message */ + retval = -1; + retval = esp_mqtt_client_publish(client, MQTT_TOPIC_PUB, comms.mqtt_buf, 0, 0, 0); // Publish a message + if (retval == WIFI_STATUS_OK) + { + ESP_LOGI(TAG,"Publishing to MQTT broker\r\n"); + transition(COMMS_STATE_WAIT_MQTT_PUB); + comms.poll_timer = true; + } + } + break; + + case COMMS_STATE_WAIT_MQTT_PUB: + ESP_LOGI(TAG,"MQTT PUB event start \r\n"); + /* Wait for MQTT publish event */ + if (evt == COMMS_EVENT_TIMER) + { + if(!qmtopen_fail){ + ESP_LOGI(TAG,"Message published\r\n"); + /* Indicate success in publishing the messages */ + } + if (comms.wf1_msg) + { + /* waiting for 500ms for PUblish to complete.*/ +// Clock_Enable(); +// uint32_t start = Clock_Time(); +// uint32_t now; +// do { +// now = Clock_Time(); +// } while (now - start <= 100); +// Clock_Disable(); + } + //hmi_set_offload_success(); + + /* Also use this point to clear the ERROR status for use with retries. + * That is, if offload succeeded, we don't need to retry. + */ + comms.last_cycle_status = COMMS_STATUS_OK; + + retval = esp_mqtt_client_disconnect(client); + if (retval == WIFI_STATUS_OK) + { + ESP_LOGI(TAG,"Closing connection\r\n"); + transition(COMMS_STATE_WAIT_MQTT_CLOSE); + } + } + + break; + case COMMS_STATE_WAIT_MQTT_CLOSE: + + /* Wait for MQTT close event */ + if (evt == COMMS_EVENT_TIMER) + { + ESP_LOGI(TAG,"Connection closed.\r\n"); + + /*note: handler is disabled anyway*/ +// if(qmtopen_fail == true){ +// qmtopen_fail = false; +// //ESP_LOGI(TAG,"Message published\r\n"); +// ESP_LOGI(TAG,"Closing connection\r\n"); +// comms.poll_timer = true; +// transition(COMMS_STATE_WAIT_NET_CONNECT); +// } + + +// else{ + Check_SP1_Order(); + +// } + + + //Comms_Check_TestMode(); + + } + break; + + case COMMS_STATE_GOING_IDLE: + + /* Clear offloading data */ + //hmi_set_offload_mode(false); + + /* Clean up state before going idle */ + ESP_LOGI(TAG,"Process going idle.\n"); + + /*===========================================*/ +// retval = bg96_stop(); + /*===========================================*/ + transition(COMMS_STATE_IDLE); + comms.poll_timer = true; + + break; + case COMMS_STATE_IDLE: + if (evt == COMMS_EVENT_TIMER) + { + /* Status is ERROR until cleared by successful completion. */ + // comms.last_cycle_status = COMMS_STATUS_ERROR; + // + // ESP_LOGI(TAG,"Connecting to server. battery=%d\r\n", hmi_get_batt_level()); + // if (bg96_is_ready()) + // { + // /* If modem is started and ready, proceed. */ + // wifi_event_cb(WIFI_STATUS_OK); + // } + // else if (!bg96_is_started()) + // { + // /* start the bg96 process only if not started. */ + // retval = bg96_start(wifi_event_cb); + // if (retval == WIFI_STATUS_OK) + // { + // ESP_LOGI(TAG,"Waiting for modem\r\n"); + // transition(COMMS_STATE_WAIT_BG96); + // } + // } + // else + // { + // ESP_LOGW(TAG,("Modem is busy. Comm cycle aborted.\r\n"); + // } + + + /*===========================================*/ + /* turn modem off*/ + int retval=0; +// retval = bg96_stop(); + if(!retval ){ + comms.poll_timer = true; + hmi_choose_led(HMI_LED_RED, HMI_LED_ON); + // transition(COMMS_STATE_WAIT_BG96_OFF); + }else + { + ESP_LOGI(TAG,"bg96 faild stop \r\n"); + } + + + /*===========================================*/ + + + // board_led_red_on(); + // board_relay_off(); + // ESP_LOGI(TAG,"going to http setup\n"); + /*===========================================*/ + // comms.poll_timer = true; + // transition(COMMS_STATE_WAIT_RELAY_WAIT); + // transition(COMMS_STATE_WAIT_HTTP_SEND_SP3); + + // } + // if (evt == COMMS_EVENT_TIMER) + // { + // + // } + // else if (evt == COMMS_EVENT_STOP) + // { + // ESP_LOGI(TAG,"Process stopped.\n"); + // transition(COMMS_STATE_INIT); + } + break; + +// case COMMS_STATE_RELAY_WAIT: +// if (evt == COMMS_EVENT_TIMER) +// { +// board_led_red_off(); +// board_relay_on(); +// ESP_LOGI(TAG,"RELAY OFF\n"); +// timer_stop(COMMS_TIMER_ID); +// transition(COMMS_STATE_GOING_IDLE); +// /*=========*/ +// comms.poll_timer = true; +// +// } +// break; + + case COMMS_STATE_WAIT: + +// timer_stop(COMMS_TIMER_ID); + comms.poll_timer = true; + transition(comms.http_init_state); + ESP_LOGI(TAG,"Number of remaining HTTP retry is : %d\n", comms.httpRetryCnt); + break; + + default: + break; + } + + return retval; +} + +/* ------------------------------------------------------------------------- */ + + /* Initialize. */ +int comms_init(void) +{ + int retval = COMMS_STATUS_OK; + + memset(&comms, 0, sizeof(comms)); + comms.overflow_check = 0xFF; + + /*Initializing the COMMS_TIMER */ +// timer_init(COMMS_TIMER_ID, timer_handler); + + ESP_LOGI(TAG,"Process initialized.\n"); +// register_wifi_callback(wifi_event_cb); + + return retval; +} + +/* Start the process */ +int comms_start(comms_op_cb_t start_cb, comms_op_cb_t connect_cb) +{ + ESP_LOGW(TAG, "Comms start ...."); + comms.start_cb = start_cb; + comms.connect_cb = connect_cb; + int retval = state_machine(COMMS_EVENT_START); + return retval; +} + +/* Stop the process */ +int comms_stop(void) +{ + int retval = state_machine(COMMS_EVENT_STOP); + return retval; +} + +/* Poll the process */ +int comms_poll(void) +{ + int retval = COMMS_STATUS_OK; + if (comms.poll_timer) + { + comms.poll_timer = false; + retval = state_machine(COMMS_EVENT_TIMER); + } + else if (0 /* */) + { + } + + return retval; +} + +int comms_connect(comms_op_cb_t cb) +{ + comms.connect_cb = cb; + int retval = state_machine(COMMS_EVENT_CONNECT); + return retval; +} + +/* Check if the process needs to be polled */ +bool comms_needs_poll(void) +{ + return + comms.poll_timer; +} + + +/* Check if the process is started */ +bool comms_is_started(void) +{ + return comms.state != COMMS_STATE_INIT; +} + +bool comms_is_idle(void) +{ + return comms.state == COMMS_STATE_IDLE; +} + +int comms_get_status(void) +{ + return comms.last_cycle_status; +} + +/* ------ Process Callbacks ------------------------- */ + +__attribute__((weak)) void comms_ready_cb(void) { } \ No newline at end of file diff --git a/main/comms.h b/main/comms.h new file mode 100644 index 0000000..7dcfde1 --- /dev/null +++ b/main/comms.h @@ -0,0 +1,152 @@ +#ifndef __COMMS_H_ +#define __COMMS_H_ + +#include +#include + +/* comms.h + * + * Copyright 2020 HAE Innovations + * + * Celluar Communications process manager. + * + * Handles communication to/from server over cell modem, + * collecting and sending events from queue, processing configuration + * and firmware updates from server, etc. + * + * Author: E. Ross + */ + +#define COMMS_STATUS_UPDATED 1 +#define COMMS_STATUS_OK 0 +#define COMMS_STATUS_ERROR -1 + + +#define MCU_BASE_URL1 "https://testdevice.tempstickapi.com" +#define MCU_BASE_URL2 "https://testdevice.tempstickapi.com" +#define MCU_BASE_URL3 "https://testdevice.tempstickapi.com" +#define MCU_BASE_URL4 "http://testdevice.tempstickapi.com" + + +#define MCU_ONBOARDING_URL "https://device-status.idealsciences%u.com" +#define POSTING_ONBOARDING "POST /sensor-account.php HTTP/1.0" +#define ON_BOARDING_SERVER_DOMAIN_NAME "device-status.idealsciences.com" +#define USER_AGENT_ONBOARDING "SensorDHT/1.0" +#define AUTHERIZATION_VALUE_ONBOARDING "Basic ZGV2OmRldjEyMw==" +#define CONTENT_TYPE_VALUE_ONBOARDING "application/x-www-form-urlencoded" +#define ONBOARDING_SERVER_POSTURL_MAX 3 +#define ONBOARDING_SERVER_POSTURL_MIN 1 + + +#define PARTHA_SERVER_BASE_URL "https://www.parthasarathimishra.com" +#define TESTING_SERVER_BASE_URL "http://54.204.230.201:8081" + +/*HTTP POST header component for tempstick server*/ +#define END_POINT_POSTING1 "POST / HTTP/1.1\r\n" +#define END_POINT_AUTHERIZATION "Authorization: Bearer 19730e88-886d-4c94-89d6-3066b79b7630\r\n" +#define AUTHERIZATION_VALUE "Bearer 19730e88-886d-4c94-89d6-3066b79b7630" +#define END_POINT_HOST "Host: testdevice.tempstickapi.com\r\n" +#define HOST_VALUE "testdevice.tempstickapi.com" +#define END_POINT_USER_AGENT "User-Agent: \"SensorDHT\\/1.0\"\r\n" +#define USER_AGENT_VALUE "\"SensorDHT\\/1.0\"" +#define END_POINT_CONTENT_TYPE "Content-Type: application/json\r\n" +#define CONTENT_TYPE_VALUE "application/json" +#define END_POINT_CONTENT_LEN "Content-Length: %u\r\n\r\n" + +#define HTTP_POST_HEADER END_POINT_POSTING1 END_POINT_AUTHERIZATION END_POINT_HOST END_POINT_USER_AGENT END_POINT_CONTENT_TYPE END_POINT_CONTENT_LEN + + +// URL to post on servers 1, 2, and 3 +#define MCU_POST_URL1 MCU_BASE_URL1 +#define MCU_POST_URL2 MCU_BASE_URL2 +#define MCU_POST_URL3 MCU_BASE_URL3 + +//URL to get configuration flags from servers 1, 2, and 3 +#define MCU_GET_CONFIG_FLAGS_URL1 TESTING_SERVER_BASE_URL"/hae/azuma/%s/update/" //MCU_BASE_URL1//config/flag/server1 +#define MCU_GET_CONFIG_FLAGS_URL2 TESTING_SERVER_BASE_URL"/hae/azuma/%s/update/" //MCU_BASE_URL2//config/flag/server2 +#define MCU_GET_CONFIG_FLAGS_URL3 TESTING_SERVER_BASE_URL"/hae/azuma/%s/update/" //MCU_BASE_URL3//config/flag/server3 + +/* this endpoint not implemented yet in the code (customer pending) */ +// if the config_update flag is set +// URL to get settings from servers 1, 2, and 3 +#define MCU_GET_SETTINGS_URL1 TESTING_SERVER_BASE_URL//settings/server1 +#define MCU_GET_SETTINGS_URL2 TESTING_SERVER_BASE_URL//settings/server2 +#define MCU_GET_SETTINGS_URL3 TESTING_SERVER_BASE_URL//settings/server3 + +//URL to get MCU FW version from servers 1, 2, and 3 +#define MCU_FW_VERSION_URL1 TESTING_SERVER_BASE_URL"/hae/azuma/%s/mcu_pgm_download/version/"/*MCU_BASE_URL1//mcu/fw/version/server1*/ +#define MCU_FW_VERSION_URL2 TESTING_SERVER_BASE_URL"/hae/azuma/%s/mcu_pgm_download/version/"//MCU_BASE_URL2//mcu/fw/version/server2 +#define MCU_FW_VERSION_URL3 TESTING_SERVER_BASE_URL"/hae/azuma/%s/mcu_pgm_download/version/"//MCU_BASE_URL3//mcu/fw/version/server3 + +//URL to get MCU FW bin file from servers 1, 2, and 3 +#define MCU_FW_BIN_FILE_URL1 TESTING_SERVER_BASE_URL"/hae/azuma/%s/mcu_pgm_download/OTA.bin"//MCU_BASE_URL1//mcu/fw/bin/file/server1 +#define MCU_FW_BIN_FILE_URL2 TESTING_SERVER_BASE_URL"/hae/azuma/%s/mcu_pgm_download/OTA.bin"//MCU_BASE_URL2//mcu/fw/bin/file/server2 +#define MCU_FW_BIN_FILE_URL3 TESTING_SERVER_BASE_URL"/hae/azuma/%s/mcu_pgm_download/OTA.bin"//MCU_BASE_URL3//mcu/fw/bin/file/server3 + +//URL to get MODEM FW version from servers 1, 2, and 3 +#define MODEM_FW_VERSION_URL1 TESTING_SERVER_BASE_URL"/hae/azuma/%s/modem_pgm_download/version/"//MCU_BASE_URL1//modem/fw/version/server1 +#define MODEM_FW_VERSION_URL2 TESTING_SERVER_BASE_URL"/hae/azuma/%s/modem_pgm_download/version/"//MCU_BASE_URL2//modem/fw/version/server2 +#define MODEM_FW_VERSION_URL3 TESTING_SERVER_BASE_URL"/hae/azuma/%s/modem_pgm_download/version/"//MCU_BASE_URL3//modem/fw/version/server3 + +//URL to get MODEM FW bin file from servers 1, 2, and 3 +#define MODEM_FW_BIN_FILE_URL1 TESTING_SERVER_BASE_URL"/hae/azuma/%s/modem_pgm_download/MOTA.bin"//MCU_BASE_URL1//modem/fw/bin/file/server1 +#define MODEM_FW_BIN_FILE_URL2 TESTING_SERVER_BASE_URL"/hae/azuma/%s/modem_pgm_download/MOTA.bin"//MCU_BASE_URL2//modem/fw/bin/file/server2 +#define MODEM_FW_BIN_FILE_URL3 TESTING_SERVER_BASE_URL"/hae/azuma/%s/modem_pgm_download/MOTA.bin"//MCU_BASE_URL3//modem/fw/bin/file/server3 + + +/* TEMPSTICK servers URL's */ +typedef enum { + TEMPSTICK_SERVER1=1, + TEMPSTICK_SERVER2, + TEMPSTICK_SERVER3 +}serverUrl_t; + + +#define COMMS_STATUS_UPDATED 1 +#define COMMS_STATUS_OK 0 +#define COMMS_STATUS_ERROR -1 + + /* Operation complete indication callback */ +typedef void (*comms_op_cb_t)(int status); + +/* Initialize process. */ +int comms_init(void); + +/* Start process. + * The server is contacted for configuration. + * When this processes completes, the callback is notified. + */ +int comms_start(comms_op_cb_t start_cb, comms_op_cb_t connect_cb); + +/* Stop process. */ +int comms_stop(void); + +/* Poll process. */ +int comms_poll(void); + +/* Check if the process needs to be polled. */ +bool comms_needs_poll(void); + +/* Connect to server once process has completed startup and is idle +* Optional callback is invoked when process completes. +*/ +int comms_connect(comms_op_cb_t cb); + +/* Check if the process is idle and not doing any communications. */ +bool comms_is_idle(void); + +/* Check if the process is started */ +bool comms_is_started(void); + +/* Get the status of the last communication attempt. + * OK - everything completed. + * ERROR - something failed. + */ +int comms_get_status(void); + + +void SP1_Mqtt_Tx(int ); + + +#endif /* __COMMS_H_ */ + diff --git a/main/data_processing.c b/main/data_processing.c new file mode 100644 index 0000000..8e99fd3 --- /dev/null +++ b/main/data_processing.c @@ -0,0 +1,474 @@ +/* + * data_processing.c + * + * Created on: Feb 3, 2023 + * Author: Partha + */ +#include +#include +#include "nvs_flash.h" +#include "esp_system.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include "sys/time.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "math.h" +#include "sdkconfig.h" +#include "time.h" +#include "rtc_wdt.h" +#include "modem.h" +#include "esp_mac.h" +#include "uart_ifx.h" +#include "esp_task_wdt.h" +#include "port.h" +#include "adc_ifx.h" +#include "data_processing.h" +#include "main.h" +#include "hmi.h" +#include "comms.h" +#include "lwip/ip4_addr.h" +#include "wifi_Init.h" +#include "nvm.h" +#include "wifi_OTA.h" +static const char* TAG = "DATA"; + +#define LOG_LOCAL_LEVEL ESP_LOG_INFO +#include "esp_log.h" + +#define IMEI "353165803930522" + +extern bool isCycleCompleted; +extern uint8_t comms_mode; + +extern uint8_t checkin_cycle_counter; +extern uint8_t number_of_check_in_to_do; + +static char thermocouple_type; + +uint32_t g_version = 0; +uint32_t g_wifiConnectTime; +float g_voltage = 0; +bool g_usbConnected = 0; +bool g_enableButtonTiming; +RTC_DATA_ATTR static bool g_prevUsbConnected; +uint32_t light_mv = 0; +uint8_t checkInAttemptNumber = 1; +time_t g_wakeUpTime = 0; +uint32_t g_StatusFlags = 0; +uint32_t g_triggerFlags1 = 0; +uint32_t g_triggerFlags2 = 0; + +// Configuration global variable +uint32_t g_server_time = 0; +uint32_t g_ID; +uint32_t g_SensorInt; +uint32_t g_ConfigUpdate; +uint32_t g_McuUpdate; +uint32_t g_ModemUpdate; + + +RTC_DATA_ATTR historyLog_t _logs[NVM_MAX_NUMBER_OF_IN_ONE_SECTOR] = {}; + + +typedef struct +{ + // Stored in RTC RAM during Deep Sleeps. This reduces wear on the FLASH. + + // TIMING + uint32_t timeOfLastRadioCalibration; + uint32_t checkInClock; + uint32_t retry1Clock; + uint32_t retry2Clock; + int8_t lteRssi; // Modem signal strength + + // WIFI CONFIG + uint8_t bssid[6]; + esp_netif_ip_info_t localIp; + esp_netif_dns_info_t dns1; + esp_netif_dns_info_t dns2; + uint8_t wifiChannel; + int32_t wifi_rssi; + + // OTHERS + uint32_t operatingMode; + uint32_t initializationVector[4]; + bool initializationVectorCreated; + uint32_t checkInCount; + float temperatureAtLastRadioCalibration; + bool retriesAreInProgress; +}rtcRamData_t; + +RTC_DATA_ATTR rtcRamData_t _rtcRamData; + +static esp_err_t data_parse_float_value(float *target_buffer, const char needle[], const char *http_resp) +{ + esp_err_t retval = ESP_FAIL; + + char *ret = NULL; + char *start_add = NULL; + char *end_add = NULL; + char parse_value[] = {'0'}; + + int len = 0; + + /*1- Extract temperature limits*/ + ret = strstr(http_resp, needle); + if(ret) + { + /*2- get the starting address of the value*/ + start_add = strstr(ret,(char*)":") + 1; + + /*3- get the ending address of the value*/ + end_add = strstr(ret,(char*)",") + 1; + + if((char *)1 == end_add) + end_add = start_add + strlen(start_add) + 1; + + /*4- Calculate the length of the value*/ + len = end_add - start_add; + + /*5- copy the value into the buffer*/ + snprintf(parse_value, len, "%s", start_add); + + /*6- Assign the double value to the target buffer*/ + *target_buffer = atof(parse_value); + + retval = ESP_OK; + } + + return retval; +} + +static esp_err_t data_parse_int_value(int32_t *target_buffer, const char needle[], const char *http_resp) +{ + esp_err_t retval = ESP_FAIL; + + char *ret = NULL; + char *start_add = NULL; + char *end_add = NULL; + char parse_value[] = {'0'}; + + int len = 0; + + /*1- Extract temperature limits*/ + ret = strstr(http_resp, needle); + if(ret) + { + /*2- get the starting address of the value*/ + start_add = strstr(ret,(char*)":") + 1; + + /*3- get the ending address of the value*/ + end_add = strstr(ret,(char*)",") + 1; + + if((char *)1 == end_add) + end_add = start_add + strlen(start_add) + 1; + + /*4- Calculate the length of the value*/ + len = end_add - start_add; + + /*5- copy the value into the buffer*/ + snprintf(parse_value, len, "%s", start_add); + + /*6- Assign the double value to the target buffer*/ + *target_buffer = atoi(parse_value); + + retval = ESP_OK; + } + + return retval; +} + +static esp_err_t data_parse_uint8_value(uint8_t *target_buffer, const char needle[], const char *http_resp) +{ + esp_err_t retval = ESP_FAIL; + + char *ret = NULL; + char *start_add = NULL; + char *end_add = NULL; + char parse_value[] = {'0'}; + + int len = 0; + + /*1- Extract temperature limits*/ + ret = strstr(http_resp, needle); + if(ret) + { + /*2- get the starting address of the value*/ + start_add = strstr(ret,(char*)":") + 1; + + /*3- get the ending address of the value*/ + end_add = strstr(ret,(char*)",") + 1; + + if((char *)1 == end_add) + end_add = start_add + strlen(start_add) + 1; + + /*4- Calculate the length of the value*/ + len = end_add - start_add; + + /*5- copy the value into the buffer*/ + snprintf(parse_value, len, "%s", start_add); + + /*6- Assign the double value to the target buffer*/ + *target_buffer = atoi(parse_value); + + retval = ESP_OK; + } + + return retval; +} + +static esp_err_t data_parse_uint16_value(uint16_t *target_buffer, const char needle[], const char *http_resp) +{ + esp_err_t retval = ESP_FAIL; + + char *ret = NULL; + char *start_add = NULL; + char *end_add = NULL; + char parse_value[] = {'0'}; + + int len = 0; + + /*1- Extract temperature limits*/ + ret = strstr(http_resp, needle); + if(ret) + { + /*2- get the starting address of the value*/ + start_add = strstr(ret,(char*)":") + 1; + + /*3- get the ending address of the value*/ + end_add = strstr(ret,(char*)",") + 1; + + if((char *)1 == end_add) + end_add = start_add + strlen(start_add) + 1; + + /*4- Calculate the length of the value*/ + len = end_add - start_add; + + /*5- copy the value into the buffer*/ + snprintf(parse_value, len, "%s", start_add); + + /*6- Assign the double value to the target buffer*/ + *target_buffer = atoi(parse_value); + + retval = ESP_OK; + } + + return retval; +} + +static esp_err_t data_parse_uint_value(uint32_t *target_buffer, const char needle[], const char *http_resp) +{ + esp_err_t retval = ESP_FAIL; + + char *ret = NULL; + char *start_add = NULL; + char *end_add = NULL; + char parse_value[] = {'0'}; + + int len = 0; + + /*1- Extract temperature limits*/ + ret = strstr(http_resp, needle); + if(ret) + { + /*2- get the starting address of the value*/ + start_add = strstr(ret,(char*)":") + 1; + + /*3- get the ending address of the value*/ + end_add = strstr(ret,(char*)",") + 1; + + if((char *)1 == end_add) + end_add = start_add + strlen(start_add) + 1; + + /*4- Calculate the length of the value*/ + len = end_add - start_add; + + /*5- copy the value into the buffer*/ + snprintf(parse_value, len, "%s", start_add); + + /*6- Assign the double value to the target buffer*/ + *target_buffer = atoi(parse_value); + + retval = ESP_OK; + } + + return retval; +} + +static esp_err_t data_parse_String_value(char *target_buffer, const char needle[], const char *http_resp) +{ + esp_err_t retval = ESP_FAIL; + + char *ret = NULL; + char *start_add = NULL; + char *end_add = NULL; + + + int len = 0; + + /*1- Extract temperature limits*/ + ret = strstr(http_resp, needle); + if(ret) + { + /*2- get the starting address of the value*/ + start_add = strstr(ret,(char*)":") + 2; + + /*3- get the ending address of the value*/ + end_add = strstr(ret,(char*)","); + + /*4- Calculate the length of the value*/ + len = end_add - start_add; + + /*5- copy the value into the buffer*/ + snprintf(target_buffer, len, "%s", start_add); + + retval = ESP_OK; + } + + return retval; +} + + +void data_set_ap_mac_addr(uint8_t *bssid) +{ + for(int i=0; i<6; i++) + { + _rtcRamData.bssid[i] = bssid[i]; + } +} + +uint8_t* data_get_ap_mac_addr(void) +{ + return ((uint8_t*)&_rtcRamData.bssid[0]); +} + +void data_set_local_ip_addr(esp_netif_ip_info_t* ip_infos) +{ + _rtcRamData.localIp.ip.addr = ip_infos->ip.addr; + _rtcRamData.localIp.gw.addr = ip_infos->gw.addr; + _rtcRamData.localIp.netmask.addr = ip_infos->netmask.addr; +} + +uint32_t data_get_ip_addr(void) +{ + return _rtcRamData.localIp.ip.addr; +} + +uint32_t data_get_gw_addr(void) +{ + return _rtcRamData.localIp.gw.addr; +} + +uint32_t data_get_netmask_addr(void) +{ + return _rtcRamData.localIp.netmask.addr; +} + +void data_set_wifi_channel(uint8_t channel) +{ + _rtcRamData.wifiChannel = channel; +} + +uint8_t data_get_wifi_channel(void) +{ + return _rtcRamData.wifiChannel; +} + +void data_set_wifi_rssi(int32_t rsi) +{ + _rtcRamData.wifi_rssi = rsi; +} + +int32_t data_get_wifi_rssi(void) +{ + return _rtcRamData.wifi_rssi; +} + +void data_set_main_and_backup_dns(esp_netif_dns_info_t* dns1, esp_netif_dns_info_t* dns2) +{ + _rtcRamData.dns1.ip.u_addr.ip4.addr = dns1->ip.u_addr.ip4.addr; + _rtcRamData.dns2.ip.u_addr.ip4.addr = dns2->ip.u_addr.ip4.addr; +} + +uint32_t data_get_mode(void) +{ + return _rtcRamData.operatingMode; +} + +void data_clearWifiConnectionSettings(void) +{ + _rtcRamData.wifiChannel = 1; // FCC allows only channels 1 through 13 in the USA, but 12 and 13 must be low power, which I don't think we can control. My linksys router only allows channels 1-11. + memset(_rtcRamData.bssid, 0, sizeof(_rtcRamData.bssid)); + _rtcRamData.localIp.ip.addr = 0; + _rtcRamData.localIp.gw.addr = 0; + _rtcRamData.localIp.netmask.addr = 0; + _rtcRamData.dns1.ip.u_addr.ip4.addr = 0; + _rtcRamData.dns2.ip.u_addr.ip4.addr = 0; +} + + + +/*******************************************************************/ +/* Buffer(Json Sample) */ +/*{ "request": "settings", "time":1675859084, "id":10608508, + * "SensorInt":1, "ConfigUpdate":0, "McuUpdate:0", "ModemUpdate":0 } + */ +/*******************************************************************/ + +esp_err_t data_parsing_config(char *buff,int len){ + esp_err_t retval = ESP_FAIL; + char server_request[11]={0}; +// char setting_req[]="settings"; +// ret = strstr(buff, (const char*)"settings"); +// data_parse_String_value(server_request,"request",buff); +// if( ret != NULL ){ + if(1){ + //retval = data_parse_uint_value(&g_server_time,"time", buff); + + retval = data_parse_uint_value(&g_ID,"id", buff); + retval = data_parse_uint_value(&g_SensorInt,"SensorInt", buff); + retval = data_parse_uint_value(&g_ConfigUpdate,"ConfigUpdate", buff); + retval = data_parse_uint_value(&g_McuUpdate,"McuUpdate", buff); + retval = data_parse_uint_value(&g_ModemUpdate,"ModemUpdate", buff); + ESP_LOGI(TAG, "ConfigUpdate: %luMcuUpdate: %lu ModemUpdate: %lu", g_ConfigUpdate, g_McuUpdate, g_ModemUpdate); + + + g_McuUpdate = 1; + //g_ConfigUpdate = 1; + if(g_ConfigUpdate == 1) + + ESP_LOGI(TAG, "Configuration update = 1 "); + } + if(g_McuUpdate == 1){ + + ESP_LOGI(TAG, "g_McuUpdate update = 1 "); + } + else + { + ESP_LOGI(TAG, "Invalid request :%s",server_request); + } + return retval; +} + + +/****************************************************************************/ + +/* function input + maxretrynum = HTTP_RETRY_COUNT = 5 + httpRetryCnt is the current retry count + + +*/ +esp_err_t check_in_attempts_count( int httpRetryCnt, int maxretrynum ) +{ + esp_err_t retval = ESP_OK; + + checkInAttemptNumber = 1 + maxretrynum - httpRetryCnt; + + return retval; +} + + + diff --git a/main/data_processing.h b/main/data_processing.h new file mode 100644 index 0000000..19a815a --- /dev/null +++ b/main/data_processing.h @@ -0,0 +1,298 @@ +/* + * data_processing.h + * + * Created on: Feb 3, 2023 + * Author: Partha + */ + +#ifndef MAIN_DATA_PROCESSING_H_ +#define MAIN_DATA_PROCESSING_H_ + +#include "sdkconfig.h" +#include "esp_netif.h" + +typedef struct +{ + // If at some point in the future more data needs to fit into this structure, the floats can be replaced with signed scaled int16s to save space. + // Simply convert the floats to scaled signed int16s on-the-fly when storing the log, and convert them back on-the-fly when displaying the log + // or sending them to the server. Because of the limited range utilized by the floats, you can probably multiply them by 100, store them as ints, + // and then divide them by 100 after retrieving them. Check to see if any weird out-of-range indicators in the floats would break this system + // before implementing it. You would need to alter the deviceSaveHistory(), deviceShowHistory(), and sendDataToServer() functions, and possibly + // the deviceClearHistory() functions, and adapt to the rtcRamData structure. + bool logHistoryRecorded; // Flag that indicates if the entry is empty or filled with valid data. + //int8_t logRSSI; + uint8_t logStatusFlags; + uint16_t logXaxis; + uint16_t logYaxis; + uint16_t logZaxis; + uint32_t logTriggerFlags1; + uint32_t logTriggerFlags2; + uint32_t logLight; + uint32_t logCheckInTime; + float logTemp; + float logHumidity; + float logThermocoupleTemp; +}historyLog_t; + +typedef enum +{ + HISTORY_TEMP_FIELD = 1, + HISTORY_HUMI_FIELD, + HISTORY_THERM_FIELD, + HISTORY_LIGHT_FIELD, + HISTORY_TIME_FIELD, + HISTORY_TRIG1_FIELD, + HISTORY_TRIG2_FIELD, + HISTORY_XAXIS_FIELD, + HISTORY_YAXIS_FIELD, + HISTORY_ZAXIS_FIELD, + HISTORY_RSSI_FIELD, + HISTORY_STAUS_FIELD, + HISTORY_RECORD_FIELD +}historyLog_field_t; + +#define ON_BOARDING_MODE 0x01010101 +#define ALERT_MODE 0x02020202 +#define CHECK_IN_MODE 0x03030303 +#define DEVICE_RESET_MODE 0x04040404 +#define USER_FEEDBACK_MODE 0x05050505 + +#define WAKE_HOST_LOWER_BYTE_DISABLED 0x00 +#define WAKE_HOST_LOWER_BYTE_ON_MOVEMENT_DETECTION 0x01 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_LOWER_BYTE_ON_LIGHT_TO_DARK 0x02 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_LOWER_BYTE_ON_DARK_TO_LIGHT 0x04 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_LOWER_BYTE_ON_SWITCH_FROM_USB_TO_BATTERY 0x08 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_LOWER_BYTE_ON_SWITCH_FROM_BATTERY_TO_USB 0x10 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_LOWER_BYTE_WHEN_THERMOCOUPLE_CROSSES_LIMITS 0x20 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_LOWER_BYTE_UNUSED_A 0x40 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_LOWER_BYTE_UNUSED_B 0x80 // 0 = disabled, 1 = enabled. + +#define WAKE_HOST_UPPER_BYTE_UNUSED_C 0x01 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_UPPER_BYTE_UNUSED_D 0x02 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_UPPER_BYTE_UNUSED_E 0x04 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_UPPER_BYTE_UNUSED_F 0x08 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_UPPER_BYTE_UNUSED_G 0x10 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_UPPER_BYTE_UNUSED_H 0x20 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_UPPER_BYTE_UNUSED_I 0x40 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_UPPER_BYTE_UNUSED_J 0x80 // 0 = disabled, 1 = enabled. + +// 16-bit masks +#define WAKE_HOST_DISABLED (uint16_t)WAKE_HOST_LOWER_BYTE_DISABLED +#define WAKE_HOST_ON_MOVEMENT_DETECTION (uint16_t)WAKE_HOST_LOWER_BYTE_ON_MOVEMENT_DETECTION +#define WAKE_HOST_ON_LIGHT_TO_DARK (uint16_t)WAKE_HOST_LOWER_BYTE_ON_LIGHT_TO_DARK +#define WAKE_HOST_ON_DARK_TO_LIGHT (uint16_t)WAKE_HOST_LOWER_BYTE_ON_DARK_TO_LIGHT +#define WAKE_HOST_ON_SWITCH_FROM_USB_TO_BATTERY (uint16_t)WAKE_HOST_LOWER_BYTE_ON_SWITCH_FROM_USB_TO_BATTERY +#define WAKE_HOST_ON_SWITCH_FROM_BATTERY_TO_USB (uint16_t)WAKE_HOST_LOWER_BYTE_ON_SWITCH_FROM_BATTERY_TO_USB +#define WAKE_HOST_WHEN_THERMOCOUPLE_CROSSES_LIMITS (uint16_t)WAKE_HOST_LOWER_BYTE_WHEN_THERMOCOUPLE_CROSSES_LIMITS +#define WAKE_HOST_UNUSED_A (uint16_t)WAKE_HOST_LOWER_BYTE_UNUSED_A +#define WAKE_HOST_UNUSED_B (uint16_t)WAKE_HOST_LOWER_BYTE_UNUSED_B +#define WAKE_HOST_UNUSED_C (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_C << 8) +#define WAKE_HOST_UNUSED_D (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_D << 8) +#define WAKE_HOST_UNUSED_E (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_E << 8) +#define WAKE_HOST_UNUSED_F (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_F << 8) +#define WAKE_HOST_UNUSED_G (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_G << 8) +#define WAKE_HOST_UNUSED_H (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_H << 8) +#define WAKE_HOST_UNUSED_I (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_I << 8) +#define WAKE_HOST_UNUSED_J (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_J << 8) + +#define STROBED_SENSOR_MEASUREMENT_EXCEEDED_THRESHOLD_A 0x0001 +#define STROBED_SENSOR_MEASUREMENT_RETURNED_FROM_ABOVE_THRESHOLD_A 0x0002 +#define STROBED_SENSOR_MEASUREMENT_EXCEEDED_THRESHOLD_B 0x0004 +#define STROBED_SENSOR_MEASUREMENT_RETURNED_FROM_ABOVE_THRESHOLD_B 0x0008 +#define STROBED_SENSOR_MEASUREMENT_EXCEEDED_THRESHOLD_C 0x0010 +#define STROBED_SENSOR_MEASUREMENT_RETURNED_FROM_ABOVE_THRESHOLD_C 0x0020 +#define STROBED_SENSOR_MEASUREMENT_FELL_BELOW_THRESHOLD_D 0x0040 +#define STROBED_SENSOR_MEASUREMENT_RETURNED_FROM_BELOW_THRESHOLD_D 0x0080 +#define STROBED_SENSOR_MEASUREMENT_FELL_BELOW_THRESHOLD_E 0x0100 +#define STROBED_SENSOR_MEASUREMENT_RETURNED_FROM_BELOW_THRESHOLD_E 0x0200 +#define STROBED_SENSOR_MEASUREMENT_FELL_BELOW_THRESHOLD_F 0x0400 +#define STROBED_SENSOR_MEASUREMENT_RETURNED_FROM_BELOW_THRESHOLD_F 0x0800 +#define STROBED_SENSOR_UNUSED_A 0x1000 +#define STROBED_SENSOR_UNUSED_B 0x2000 +#define STROBED_SENSOR_UNUSED_C 0x4000 +#define STROBED_SENSOR_UNUSED_D 0x8000 + +#define STROBED_MOTION_LOWER_BYTE_MOVEMENT_DETECTED 0x01 +#define STROBED_MOTION_LOWER_BYTE_UNUSED_A 0x02 +#define STROBED_MOTION_LOWER_BYTE_UNUSED_B 0x04 +#define STROBED_MOTION_LOWER_BYTE_UNUSED_C 0x08 +#define STROBED_MOTION_LOWER_BYTE_UNUSED_D 0x10 +#define STROBED_MOTION_LOWER_BYTE_UNUSED_E 0x20 +#define STROBED_MOTION_LOWER_BYTE_UNUSED_F 0x40 +#define STROBED_MOTION_LOWER_BYTE_UNUSED_G 0x80 + +#define STROBED_MOTION_UPPER_BYTE_UNUSED_H 0x01 +#define STROBED_MOTION_UPPER_BYTE_UNUSED_I 0x02 +#define STROBED_MOTION_UPPER_BYTE_UNUSED_J 0x04 +#define STROBED_MOTION_UPPER_BYTE_UNUSED_K 0x08 +#define STROBED_MOTION_UPPER_BYTE_UNUSED_L 0x10 +#define STROBED_MOTION_UPPER_BYTE_UNUSED_M 0x20 +#define STROBED_MOTION_UPPER_BYTE_UNUSED_N 0x40 +#define STROBED_MOTION_UPPER_BYTE_UNUSED_O 0x80 + +// 16-bit masks +#define STROBED_MOTION_MOVEMENT_DETECTED (uint16_t)STROBED_MOTION_LOWER_BYTE_MOVEMENT_DETECTED +#define STROBED_MOTION_UNUSED_A (uint16_t)STROBED_MOTION_LOWER_BYTE_UNUSED_A +#define STROBED_MOTION_UNUSED_B (uint16_t)STROBED_MOTION_LOWER_BYTE_UNUSED_B +#define STROBED_MOTION_UNUSED_C (uint16_t)STROBED_MOTION_LOWER_BYTE_UNUSED_C +#define STROBED_MOTION_UNUSED_D (uint16_t)STROBED_MOTION_LOWER_BYTE_UNUSED_D +#define STROBED_MOTION_UNUSED_E (uint16_t)STROBED_MOTION_LOWER_BYTE_UNUSED_E +#define STROBED_MOTION_UNUSED_F (uint16_t)STROBED_MOTION_LOWER_BYTE_UNUSED_F +#define STROBED_MOTION_UNUSED_G (uint16_t)STROBED_MOTION_LOWER_BYTE_UNUSED_G +#define STROBED_MOTION_UNUSED_H (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_H << 8) +#define STROBED_MOTION_UNUSED_I (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_I << 8) +#define STROBED_MOTION_UNUSED_J (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_J << 8) +#define STROBED_MOTION_UNUSED_K (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_K << 8) +#define STROBED_MOTION_UNUSED_L (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_L << 8) +#define STROBED_MOTION_UNUSED_M (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_M << 8) +#define STROBED_MOTION_UNUSED_N (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_N << 8) +#define STROBED_MOTION_UNUSED_O (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_O << 8) + +#define STROBED_MISC_LOWER_BYTE_CHECK_IN_REQUESTED 0x01 +#define STROBED_MISC_LOWER_BYTE_CHANGE_FROM_LIGHT_TO_DARK 0x02 +#define STROBED_MISC_LOWER_BYTE_CHANGE_FROM_DARK_TO_LIGHT 0x04 +#define STROBED_MISC_LOWER_BYTE_CHANGE_FROM_USB_TO_BATTERY 0x08 +#define STROBED_MISC_LOWER_BYTE_CHANGE_FROM_BATTERY_TO_USB 0x10 +#define STROBED_MISC_LOWER_BYTE_THERMOCOUPLE_PLUGGED_IN 0x20 +#define STROBED_MISC_LOWER_BYTE_THERMOCOUPLE_UNPLUGGED 0x40 +#define STROBED_MISC_LOWER_BYTE_REGULAR_SCHEDULED_CHECK_IN 0x80 + +#define STROBED_MISC_UPPER_BYTE_CHECK_IN_REQ_AFTER_POWER_ON 0x01 +#define STROBED_MISC_UPPER_BYTE_DEVICE_OPERATE_IN_ALERT_MODE 0x02 +#define STROBED_MISC_UPPER_BYTE_UNUSED_D 0x04 +#define STROBED_MISC_UPPER_BYTE_UNUSED_E 0x08 +#define STROBED_MISC_UPPER_BYTE_UNUSED_F 0x10 +#define STROBED_MISC_UPPER_BYTE_UNUSED_G 0x20 +#define STROBED_MISC_UPPER_BYTE_UNUSED_H 0x40 +#define STROBED_MISC_UPPER_BYTE_UNUSED_I 0x80 + +// 16-bit masks +#define STROBED_MISC_CHECK_IN_REQUESTED (uint16_t)STROBED_MISC_LOWER_BYTE_CHECK_IN_REQUESTED +#define STROBED_MISC_CHANGE_FROM_LIGHT_TO_DARK (uint16_t)STROBED_MISC_LOWER_BYTE_CHANGE_FROM_LIGHT_TO_DARK +#define STROBED_MISC_CHANGE_FROM_DARK_TO_LIGHT (uint16_t)STROBED_MISC_LOWER_BYTE_CHANGE_FROM_DARK_TO_LIGHT +#define STROBED_MISC_CHANGE_FROM_USB_TO_BATTERY (uint16_t)STROBED_MISC_LOWER_BYTE_CHANGE_FROM_USB_TO_BATTERY +#define STROBED_MISC_CHANGE_FROM_BATTERY_TO_USB (uint16_t)STROBED_MISC_LOWER_BYTE_CHANGE_FROM_BATTERY_TO_USB +#define STROBED_MISC_THERMOCOUPLE_PLUGGED_IN (uint16_t)STROBED_MISC_LOWER_BYTE_THERMOCOUPLE_PLUGGED_IN +#define STROBED_MISC_THERMOCOUPLE_UNPLUGGED (uint16_t)STROBED_MISC_LOWER_BYTE_THERMOCOUPLE_UNPLUGGED +#define STROBED_MISC_REGULAR_SCHEDULED_CHECK_IN (uint16_t)STROBED_MISC_LOWER_BYTE_REGULAR_SCHEDULED_CHECK_IN +#define STROBED_MISC_CHECK_IN_REQ_AFTER_POWER_ON (uint16_t)(STROBED_MISC_UPPER_BYTE_CHECK_IN_REQ_AFTER_POWER_ON << 8) +#define STROBED_MISC_DEVICE_OPERATE_IN_ALERT_MODE (uint16_t)(STROBED_MISC_UPPER_BYTE_DEVICE_OPERATE_IN_ALERT_MODE << 8) +#define STROBED_MISC_UNUSED_D (uint16_t)(STROBED_MISC_UPPER_BYTE_UNUSED_D << 8) +#define STROBED_MISC_UNUSED_E (uint16_t)(STROBED_MISC_UPPER_BYTE_UNUSED_E << 8) +#define STROBED_MISC_UNUSED_F (uint16_t)(STROBED_MISC_UPPER_BYTE_UNUSED_F << 8) +#define STROBED_MISC_UNUSED_G (uint16_t)(STROBED_MISC_UPPER_BYTE_UNUSED_G << 8) +#define STROBED_MISC_UNUSED_H (uint16_t)(STROBED_MISC_UPPER_BYTE_UNUSED_H << 8) +#define STROBED_MISC_UNUSED_I (uint16_t)(STROBED_MISC_UPPER_BYTE_UNUSED_I << 8) + +#define CURRENT_STATUS_LIGHT_DETECTED 0x01 +#define CURRENT_STATUS_POWERED_BY_USB 0x02 +#define CURRENT_STATUS_UNUSED_A 0x04 +#define CURRENT_STATUS_UNUSED_B 0x08 +#define CURRENT_STATUS_UNUSED_C 0x10 +#define CURRENT_STATUS_UNUSED_D 0x20 +#define CURRENT_STATUS_UNUSED_E 0x40 +#define CURRENT_STATUS_UNUSED_F 0x80 + +#define LIGHT_SENSOR_VREF_1V024 0x00 // Vref = 1.024V +#define LIGHT_SENSOR_VREF_2V048 0x01 // Vref = 2.048V +#define LIGHT_SENSOR_VREF_2V500 0x02 // Vref = 2.500V +#define LIGHT_SENSOR_VREF_VDD 0x03 // Vref = Vdd + +#define LIGHT_DETECTED true +#define LIGHT_UNDETECTED false + +/* TRIGGER 1 bits */ +#define STROBED_TEMPERATURE_MEASUREMENT_EXCEEDED_UPPER_THRESHOLD 0x0001 +#define STROBED_TEMPERATURE_MEASUREMENT_FELL_BELOW_LOWER_THRESHOLD 0x0002 +#define STROBED_HUMIDITY_MEASUREMENT_EXCEEDED_UPPER_THRESHOLD 0x0004 +#define STROBED_HUMIDITY_MEASUREMENT_FELL_BELOW_LOWER_THRESHOLD 0x0008 +#define STROBED_THERMOCOUPLE_MEASUREMENT_EXCEEDED_UPPER_THRESHOLD 0x0010 +#define STROBED_THERMOCOUPLE_MEASUREMENT_FELL_BELOW_LOWER_THRESHOLD 0x0020 +#define STROBED_EXTERNAL_TEMP_PROBE_MEASUREMENT_EXCEEDED_UPPER_THRESHOLD 0x0040 +#define STROBED_EXTERNAL_TEMP_PROBE_MEASUREMENT_FELL_BELOW_LOWER_THRESHOLD 0x0080 +#define STROBED_DEVICE_OPERATE_IN_ALERT_MODE 0x0100 + + +/* TRIGGER 2 bits */ +#define STROBED_REGULAR_SCHEDULED_SENDINTERVAL_CHECKIN 0x0001 +#define STROBED_POWER_CHANGE_FROM_BATTERY_TO_USB 0x0002 +#define STROBED_POWER_CHANGE_FROM_USB_TO_BATTERY 0x0004 +#define STROBED_MOTION_MOVEMENT_WAS_DETECTED 0x0008 +#define STROBED_LIGHT_TRANSITION_FROM_LIGHT_TO_DARK 0x0010 +#define STROBED_LIGHT_TRANSITION_FROM_DARK_TO_LIGHT 0x0020 +#define STROBED_CHECK_IN_REQUESTED_AFTER_POWER_ON 0x4000 +#define STROBED_BUTTON_CHECK_IN_REQUESTED 0x8000 + + +#define STROBED_SENSOR_MEASUREMENT_EXCEEDED_UPPER_THRESHOLD 0x0001 +#define STROBED_SENSOR_MEASUREMENT_FELL_BELOW_LOWER_THRESHOLD 0x0002 + +typedef enum +{ + TEMPERATURE_SENSOR, + HUMIDITY_SENSOR, + THERMOCOUPLE_SENSOR, + LIGHT_SENSOR, + ACCELEROMETER_SENSOR, + POWER_MONITOR_SENSOR +}sensorType_t; + +typedef enum +{ + BATTERY_CHARGING, + BATTERY_CHARGED, + BATTERY_FAULT +}batteryChargingState_t; + +void data_initialize_rtc_ram(void); +bool data_setTempPresent(bool status); +bool data_setAccelPresent(bool status); +bool data_setThermocouplePresent(bool status); + +uint16_t data_get_motion_threshold(void); +uint32_t data_get_send_interval(void); +bool data_is_usb_connected(void); +void data_task(void *pvParameters); +void data_update_trigger_flags(void); +void data_create_checkin_string(char *buff, size_t len); + +void data_parse_http_response(const char *http_resp); +void data_processSensorModes(sensorType_t sensor); +uint8_t data_process_tempHumi_thresholds(float raw_data, float prev_raw_data, sensorType_t sensor); +uint8_t data_process_lightSen_thresholds(uint32_t prev_raw_data, uint32_t raw_data); +#if 0 +uint32_t data_get_light_sensor_gp(void); +uint32_t data_get_light_sensor_gn(void); +#endif +uint8_t data_get_light_sensor_threshold(void); +void data_process_power_thresholds(void); + +void data_save_history(void); +void data_show_history(uint8_t sector, bool print_logs, size_t* size_of_written_sector_data); +bool data_clear_history(void); +uint32_t data_get_mode(void); +esp_err_t data_parsing_config(char *buff,int len); +esp_err_t check_in_attempts_count( int httpRetryCnt, int maxretrynum ); +void init_send_Details(void); +int32_t data_get_postToServer(void); +int32_t data_get_postUrlMax(void); +void data_init_postUrlMax(int32_t val); +void data_set_postToServer(int32_t val); +void data_init_postToServer(void); +uint32_t data_get_alertInterval(void); + +void data_set_ap_mac_addr(uint8_t *bssid); +void data_set_wifi_rssi(int32_t rsi); +int32_t data_get_wifi_rssi(void); +uint8_t* data_get_ap_mac_addr(void); +void data_set_local_ip_addr(esp_netif_ip_info_t* ip_infos); +uint32_t data_get_ip_addr(void); +uint32_t data_get_gw_addr(void); +uint32_t data_get_netmask_addr(void); +void data_set_wifi_channel(uint8_t channel); +uint8_t data_get_wifi_channel(void); +void data_clearWifiConnectionSettings(void); +void data_set_main_and_backup_dns(esp_netif_dns_info_t* dns1, esp_netif_dns_info_t* dns2); + +void data_set_urlConnect(void); +#endif /* MAIN_DATA_PROCESSING_H_ */ diff --git a/main/hmi.c b/main/hmi.c new file mode 100644 index 0000000..d0f23a5 --- /dev/null +++ b/main/hmi.c @@ -0,0 +1,721 @@ +/* + * hmi.c + * + * Created on: Feb 13, 2023 + * Author: Sword + */ + +#include +#include "nvs_flash.h" +#include "esp_system.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "sdkconfig.h" +#include "esp_timer.h" +#include "uart_ifx.h" +#include "esp_task_wdt.h" +#include "modem.h" +#include "comms.h" +#include "port.h" +#include "adc_ifx.h" +#include "data_processing.h" +#include "hmi.h" + + +static const char* TAG = "HMI"; + +#define LOG_LOCAL_LEVEL ESP_LOG_INFO + +#define HMI_BUF_SIZE 20 +#define HMI_EVENTS_ITERATION_COUNTS 8 + +#define LED_SOLID_DURATION 300 +#define OTA_UPDATE_DURATION 1000 // Duration for BLUE_FLASHING event when OTA session is happening +#define BLUE_FLASHING_DURARION 500 // Duration for BLUE_FLASHING led_event +#define RED_FLASHING_DURARION 1000 // Duration for RED_FLASHING led_event +#define BLUE_RED_FLASHING_1SEC_DURARION 1000 // Duration for BLUE_RED_FLASHING led_event +#define BLUE_RED_SOLID_CHARGED_DURARION 9700 // Duration for BLUE_RED_SOLID_CHARGED led_event +#define BLUE_RED_FLASHING_CHARGED_DURARION 100 // Duration for BLUE_RED_FLASHING_CHARGING led_event +#define BLUE_RED_FLASHING_200ms_DURARION 200 // Duration for BLUE_RED_FLASHING led_event +#define NORMAL_HMI_DURATION 1000 // Duration for NORMAL_HMI cycle (cycle without triggered events) + +/*LED definitions*/ +#define RED_TRI_LED 0 +#define GREEN_TRI_LED 1 +#define BLUE_TRI_LED 2 + +static QueueHandle_t hmi_queue; // FIFO queue that stores the leds event + + +static bool usb_connection_state = false; // USB connection state flag (true--> connected, false--> disconnected) +static bool continue_current_leds_state; // Led_event Continuing flag +static bool pushbutton_state = false; // pushbutton state flag +static bool blue_red_sync = false; // Synchronization flag when usb_charging is the current usb_led_event +static bool device_failure_state = false; +static bool device_not_onboarded_state = false; // ON-BOARDED flag when device data didn't get sent to server +static bool ota_update_session = false; // OTA-Update flag to indicate that firmware update is happening right now +static bool alternate_cycle_for_both = false; // A flag to make both LEDs blink in an alternating cycle + +static hmi_leds_state_t continued_leds_state; // Chosen led_event to be continued after 5 times of execution +static hmi_leds_state_t leds_state = BLUE_RED_LEDS_OFF; // Led_event when usb is not connected +static hmi_leds_state_t prev_leds_state = BLUE_RED_LEDS_OFF; // prev Led_event when usb is not connected + + +static hmi_leds_state_t usb_leds_state = BLUE_RED_LEDS_OFF; // Led_event when usb is connected + +static uint8_t num_events_in_queue; // Number of Events in hmi_queue +static uint16_t hmi_duration = NORMAL_HMI_DURATION; // Duration for each Led_event cycle +static uint8_t fully_charged_counter; + +/************* STATIC FUNCTIONS ****************/ + +/* This function should be executed when the pushbutton is pressed (HIGHEST priority)*/ +static void hmi_pushbutton_pressed_task(void) +{ + /* 1- Execute the BLUE_LED_SOLID event*/ + hmi_duration = LED_SOLID_DURATION; + port_blue_led_on(); + port_red_led_off(); + + + /* 2- Delay duration is equal to BLUE_FLASHING_DURARION*/ + vTaskDelay(hmi_duration / portTICK_PERIOD_MS); + + + /* 3- Check if the pushbutton is not pressed*/ + if(port_is_pushbuttonNotPressed()) + { + /* If yes ---> reset pushbutton_state flag*/ + pushbutton_state = false; + } + +} + +/* This function should be executed when the device failure is occurred (second HIGHEST priority)*/ +static void hmi_device_failure_task(void) +{ + /* 1- Execute the RED_LED_SOLID event*/ + hmi_duration = LED_SOLID_DURATION; + port_blue_led_off(); + port_red_led_on(); + + + /* 2- Delay duration is equal to BLUE_FLASHING_DURARION*/ + vTaskDelay(hmi_duration / portTICK_PERIOD_MS); + +} + +/* This function should be executed when the device is not on-boarded yet (Third HIGHEST priority)*/ +static void hmi_device_not_onboarded_task(void) +{ + /* 1- Execute the RED_LED_FLASHING event*/ + hmi_duration = RED_FLASHING_DURARION; + port_blue_led_off(); + port_red_led_toggle(); + + + /* 2- Delay duration is equal to BLUE_FLASHING_DURARION*/ + vTaskDelay(hmi_duration / portTICK_PERIOD_MS); +} + +/* This function should be executed when OTA Update is happening ( device_not_onboarded > priority > events_in_queue)*/ +static void hmi_ota_firmware_update_task(void) +{ + /* 1- Execute the RED_LED_FLASHING event*/ + hmi_duration = OTA_UPDATE_DURATION; + port_blue_led_toggle(); + port_red_led_toggle(); + + alternate_cycle_for_both = true; + + /* 2- Delay duration is equal to BLUE_FLASHING_DURARION*/ + vTaskDelay(hmi_duration / portTICK_PERIOD_MS); +} + +/* This function should be executed when USB is not connected ( device_failure > priority > USB)*/ +static void hmi_events_in_queue_task(void) +{ + int counter = 0; + + /*Check if the current led_event shall be continued or not */ + if((true == continue_current_leds_state) && (continued_leds_state == prev_leds_state)) + { + /*if true: the new led_state is the same as the old led state */ + leds_state = prev_leds_state; + } + else + { + /*Extract the event from the queue (if exists)*/ + xQueueReceive(hmi_queue, &leds_state, (TickType_t)100); + + /*Set the previous led_state*/ + prev_leds_state = leds_state; + + + /*Reduce number of led_events in queue by 1*/ + num_events_in_queue--; + + /*Log*/ + ESP_LOGI(TAG,"Extracting LED_EVENT = %d from HMI-QUEUE to execute it",leds_state); + + } + + /*Check if BLUE & RED LED should be synchronized*/ + if((BLUE_RED_LEDS_FLASHING_200ms == leds_state) && (false == blue_red_sync)) + { + /*Turn off both leds for synchronization*/ + port_red_led_off(); + port_blue_led_off(); + blue_red_sync = true; + } + + /*Each event should occur 5 times to observe it clearly*/ + for(counter = 0; counter < HMI_EVENTS_ITERATION_COUNTS; counter++) + { + /* 1- Evaluate the extracted event*/ + switch(leds_state) + { + case BLUE_LED_FLASHING: //LTE_connecting + hmi_duration = BLUE_FLASHING_DURARION; + port_blue_led_toggle(); + port_red_led_off(); + break; + + case BLUE_LED_SOLID: //pushbutton_pressed + break; + + case RED_LED_FLASHING: //Device has not been on-boarded + break; + + case RED_LED_SOLID: //Device Failure. Need to reset + break; + + case BLUE_RED_LEDS_FLASHING_200ms: //low battery warning + hmi_duration = BLUE_RED_FLASHING_200ms_DURARION; + port_red_led_toggle(); + port_blue_led_toggle(); + break; + + case BLUE_RED_LEDS_FLASHING_1SEC: //USB_PLUGGED_CHARGING + case BLUE_RED_LEDS_SOLD: //USB_PLUGGED_CHARGED + case BLUE_RED_LEDS_USB_OFF: + case BLUE_LED_FLASHING_OTA: // OTA-SESSION is happening + break; + + case BLUE_RED_LEDS_OFF: + hmi_duration = NORMAL_HMI_DURATION; + port_red_led_off(); + port_blue_led_off(); + break; + } + + /* 2- Delay duration is dynamic according the extracted led_event*/ + vTaskDelay(hmi_duration / portTICK_PERIOD_MS); + + /* 3- Check the pushbutton state and device failure state*/ + if((true == pushbutton_state) || (true == device_failure_state)) + { + /*If yes then break the for-loop*/ + break; + } + } + + /*Reset the Synchronization flag*/ + blue_red_sync = false; +} + +/* This function should be executed when USB is connected (LOWEST priority)*/ +static void hmi_usb_connected_task(void) +{ + /*Evaluate the extracted event*/ + switch(usb_leds_state) + { + case BLUE_LED_FLASHING: + case BLUE_LED_SOLID: + case RED_LED_FLASHING: + case RED_LED_SOLID: + case BLUE_LED_FLASHING_OTA: + case BLUE_RED_LEDS_FLASHING_200ms: + break; + + /* Charging LED-event is violate toggling each one second (on, off, on, off and continue like this) */ + case BLUE_RED_LEDS_FLASHING_1SEC: + hmi_duration = BLUE_RED_FLASHING_1SEC_DURARION; + port_red_led_toggle(); + port_blue_led_toggle(); + break; + + /* Charged LED-event is violate toggling 2 times each 100ms and then of for 9.7 second */ + case BLUE_RED_LEDS_SOLD: + if(fully_charged_counter < 3) + { + /* Increase the fully_charged_counter by 1 */ + fully_charged_counter++; + + hmi_duration = BLUE_RED_FLASHING_CHARGED_DURARION; + port_red_led_toggle(); + port_blue_led_toggle(); + } + else + { + /* Reset the fully_charged_counter to 0 */ + fully_charged_counter = 0; + + hmi_duration = BLUE_RED_SOLID_CHARGED_DURARION; + port_red_led_off(); + port_blue_led_off(); + } + break; + + case BLUE_RED_LEDS_USB_OFF: + hmi_duration = BLUE_RED_FLASHING_1SEC_DURARION; + port_red_led_off(); + port_blue_led_off(); + break; + + case BLUE_RED_LEDS_OFF: + break; + } + + vTaskDelay(hmi_duration / portTICK_PERIOD_MS); +} + + + + +/************ NON-STATIC FUNCTIONS *************/ +/* This function is to initialize HMI service (Creating hmi_queu and turn off both leds)*/ +void hmi_init(void) +{ + /* 1- Create the HMI-QUEUE*/ + hmi_queue = xQueueCreate(HMI_BUF_SIZE, sizeof(uint8_t)); + + /* 2- Set the hmi_duration to normal and turn off both leds*/ + hmi_duration = NORMAL_HMI_DURATION; + num_events_in_queue = 0; + continue_current_leds_state = false; + port_red_led_off(); + port_blue_led_off(); + + ESP_LOGI(TAG,"HMI-Service has been initialized successfully"); +} + +/* This function is used to set the state of USB-CONNECTION*/ +void hmi_set_usb_connection_state(bool usb_state) +{ + usb_connection_state = usb_state; +} + +#if 0 +void hmi_do_usb_event_inside_hmi(bool decision) +{ + do_it_inside_hmi = decision; +} + +void hmi_exception_usb_event(void) +{ + /* Check if to do the USB event inside hmi-task or outside it (inside pausing-task) */ + if(do_it_inside_hmi) + { + /* Check the USB-LED-event */ + if(usb_leds_state == BLUE_RED_LEDS_FLASHING_1SEC) + { + /* Turn off both LEDs */ + port_red_led_off(); + port_blue_led_off(); + + /* Wait for about 300ms (to simulate the measurement time) */ + vTaskDelay(300/portTICK_PERIOD_MS); + + /* Turn on both LEDs */ + port_red_led_on(); + port_blue_led_on(); + + /* Wait for about 333ms */ + vTaskDelay(333/portTICK_PERIOD_MS); + + /* Turn off both LEDs */ + port_red_led_off(); + port_blue_led_off(); + + /* Wait for about 333ms */ + vTaskDelay(333/portTICK_PERIOD_MS); + + /* Turn on both LEDs */ + port_red_led_on(); + port_blue_led_on(); + + /* Wait for about 333ms */ + vTaskDelay(333/portTICK_PERIOD_MS); + + /* Turn off both LEDs */ + port_red_led_off(); + port_blue_led_off(); + } + else if(usb_leds_state == BLUE_RED_LEDS_SOLD) + { + /* Turn off both LEDs */ + port_red_led_off(); + port_blue_led_off(); + + /* Wait for about 300ms (to simulate the measurement time) */ + vTaskDelay(300/portTICK_PERIOD_MS); + + /* Turn on both LEDs */ + port_red_led_on(); + port_blue_led_on(); + + /* Wait for about 1000ms */ + vTaskDelay(1000/portTICK_PERIOD_MS); + + /* Turn off both LEDs */ + port_red_led_off(); + port_blue_led_off(); + } + } + else + { + if(usb_leds_state == BLUE_RED_LEDS_FLASHING_1SEC) + { + port_red_led_on(); + port_blue_led_on(); + + vTaskDelay(333/portTICK_PERIOD_MS); + + port_red_led_off(); + port_blue_led_off(); + + vTaskDelay(333/portTICK_PERIOD_MS); + + port_red_led_on(); + port_blue_led_on(); + + vTaskDelay(333/portTICK_PERIOD_MS); + + port_red_led_off(); + port_blue_led_off(); + } + else if(usb_leds_state == BLUE_RED_LEDS_SOLD) + { + port_red_led_on(); + port_blue_led_on(); + + vTaskDelay(1000/portTICK_PERIOD_MS); + + port_red_led_off(); + port_blue_led_off(); + } + } +} +#endif + +/* This function is used to set which Led_event should be executed next time*/ +void hmi_set_leds_state(hmi_leds_state_t status) +{ + uint32_t batt_mv; + float g_voltage ; + + /*Check if the led-event should be pushed to the hmi_queue*/ + switch(status) + { + case BLUE_LED_FLASHING: //Should be pushed to hmi_queue + + /*Continue executing this led_event till it's stopped by hmi_stop_continued_led_state() function */ + continue_current_leds_state = true; // RISE the led_event continuing_Flag + continued_leds_state = BLUE_LED_FLASHING; // Set the flag to be continued to be BLUE_LED_FLASHING + num_events_in_queue++; // increase the number of events in hmi_queue by 1 + xQueueSend(hmi_queue, &status, 100); // Push the new led_state to the hmi_queue (if possible) + blue_red_sync = false; // reset synchronization flag + ESP_LOGI(TAG,"BLUE Flashing (continuing) has been added to hmi_queue"); + + break; + + case RED_LED_FLASHING: //Should not be pushed to hmi_queue(it has its own function --> hmi_device_not_onboarded_task()) + device_not_onboarded_state = true; //Set the device_not_onboarded_state flag to be true + blue_red_sync = false; //reset synchronization flag + ESP_LOGI(TAG,"RED LED should Flashes now (if no higher-priority event is occurred)"); + break; + + case BLUE_LED_SOLID: //Should not be pushed to hmi_queue(it has its own function --> hmi_pushbutton_pressed_task()) + pushbutton_state = true; //Set the pushbutton_state flag to be true + blue_red_sync = false; //reset synchronization flag + break; + + case RED_LED_SOLID: //Should not be pushed to hmi_queue(it has its own function --> hmi_device_failure_task()) + device_failure_state = true; //Set the device_failure_state flag to be true + blue_red_sync = false; //reset synchronization flag + break; + + case BLUE_RED_LEDS_OFF: //Should be pushed to hmi_queue + + xQueueSend(hmi_queue, &status, 100); // Push the new led_state to the hmi_queue (if possible) + num_events_in_queue++; // increase the number of events in hmi_queue by 1 + blue_red_sync = false; // reset synchronization flag + + break; + + case BLUE_RED_LEDS_FLASHING_200ms: + num_events_in_queue++; // increase the number of events in hmi_queue by 1 + xQueueSend(hmi_queue, &status, 100); // Push the new led_state to the hmi_queue (if possible) + ESP_LOGI(TAG,"BLUE_RED_LED_FLASHING_200ms has been added to hmi_queue"); + break; + + case BLUE_RED_LEDS_FLASHING_1SEC: //Should not be pushed to hmi_queue(it has its own function --> hmi_usb_connected_task()) + + /* Check if the USB is plugged and the battery is existed */ + if((true == usb_connection_state)) + { + /*Update the current usb_leds_state*/ + usb_leds_state = status; + + ESP_LOGI(TAG,"Voilate Flashing has been set"); + } + else + { + ESP_LOGI(TAG,"Voilate Flashing can't be set. Battery may not be connected"); + } + break; + + case BLUE_RED_LEDS_SOLD: //Should not be pushed to hmi_queue(it has its own function --> hmi_usb_connected_task()) + + /* Check if the USB is plugged and the battery is existed and its voltage level is fine */ + if((true == usb_connection_state)) + { + /*Update the current usb_leds_state*/ + usb_leds_state = status; + + /*reset synchronization flag*/ + blue_red_sync = false; + + ESP_LOGI(TAG,"Voilate Solid has been set"); + } + else + { + ESP_LOGI(TAG,"Voilate Solid can't be set. Battery may not be connected or its volt. level is too low"); + } + break; + + case BLUE_LED_FLASHING_OTA: //Should not be pushed to hmi_queue(it has its own function --> hmi_ota_firmware_update_task()) + + /*reset synchronization flag*/ + blue_red_sync = false; + + /* set ota_flag*/ + ota_update_session = true; + + break; + + + case BLUE_RED_LEDS_USB_OFF: + /* Check if the USB is plugged and the battery is existed and its voltage level is fine */ + if((true == usb_connection_state)) + { + /*Update the current usb_leds_state*/ + usb_leds_state = status; + + /*reset synchronization flag*/ + blue_red_sync = false; + + //ESP_LOGI(TAG,"Voilate Solid/Flashing has been stopped"); + } + } + + /*Exit the critical shared section between different tasks*/ + //taskEXIT_CRITICAL(&hmi_mutex); +} + +/* This function is used to stop the continuing led_event*/ +void hmi_stop_continued_led_state(hmi_leds_state_t led_event) +{ + /* Check if the led_event that should be stopped is the same as the current continuing led_event*/ + if(continued_leds_state == led_event) + { + /*If yes then : */ + /* 1- Disallow continuing the current running Led_event */ + continue_current_leds_state = false; + + /* 2- Set the continued Led_event to be BLUE_RED_LEDS_OFF (and it won't executed because the continue flag is false)*/ + continued_leds_state = BLUE_RED_LEDS_OFF; + + /*Log*/ + ESP_LOGI(TAG,"Stopping the current continued LED_EVENT = %d",led_event); + } +} + +/* This function is used to stop red flashing*/ +void hmi_stop_red_flashing(void) +{ + device_not_onboarded_state = false; +} + +/* This function is used to stop ota_LED-event */ +void hmi_stop_ota_event(void) +{ + ota_update_session = false; +} + +bool leds_enabled = true; + + +void set_leds_enable(bool status) +{ + leds_enabled = status; + if(status == false) + { + port_red_led_off(); + port_blue_led_off(); + } +} +/* State of the hmi process */ +typedef enum +{ + HMI_STATE_INIT, + HMI_STATE_RUN, +} hmi_state_t; + +typedef struct +{ + hmi_state_t state; + /* Timer */ + bool poll_timer; + /* General 1 sec timer */ + int sec_timer; + int active_led; + bool red_led_on; + bool green_led_on; + bool blue_led_on; + uint8_t led_state; + int** blink_pattern; +} hmi_t; + +static hmi_t hmi; + +int hmi_choose_led(int led_color,uint8_t op_mode) +{ + // int retval=HMI_STATUS_OK; + hmi.led_state = op_mode; /*led state ( on,off or blinking).*/ + + switch (led_color) + { + + case RED_TRI_LED: + hmi.red_led_on = 1; + hmi.green_led_on = 0; + hmi.blue_led_on = 0; + break; + + case GREEN_TRI_LED: + hmi.red_led_on = 0; + hmi.green_led_on = 1; + hmi.blue_led_on = 0; + + break; + case BLUE_TRI_LED: + hmi.red_led_on = 0; + hmi.green_led_on = 0; + hmi.blue_led_on = 1; + + break; + default: + hmi.red_led_on = 0; + hmi.green_led_on = 0; + hmi.blue_led_on = 0; + break; + } + return 0; +} + + +/* This is the hmi_task that will be executed periodically*/ +void hmi_task(void *pvParameters) +{ + for(;;) + { + while(leds_enabled == false) + { + vTaskDelay(10 / portTICK_PERIOD_MS); + port_red_led_off(); + port_blue_led_off(); + blue_red_sync = false; + } + + + if(true == pushbutton_state) // 1) Check if the pushbutton is pressed + { + /* reset the alternating flag */ + alternate_cycle_for_both = false; + + /*If pushbutton is pressed then do the following task*/ + hmi_pushbutton_pressed_task(); + } + else if(true == device_failure_state) // 2) Check if the device failure is occurred + { + /* reset the alternating flag */ + alternate_cycle_for_both = false; + + /*If device failure is occurred then do the following task*/ + hmi_device_failure_task(); + } + else if(true == device_not_onboarded_state) // 3) Check if the device failure is occurred + { + /* reset the alternating flag */ + alternate_cycle_for_both = false; + + /*If device has not been onBoarded yet then do the following task*/ + hmi_device_not_onboarded_task(); + } + else if(true == ota_update_session) // 4) Check if the OTA-Session is happening now + { + /*Check if the alternating flag is false*/ + if(false == alternate_cycle_for_both) + { + /*Turn on the BLUE and turn off the RED*/ + port_red_led_off(); + port_blue_led_on(); + alternate_cycle_for_both = true; + } + /*If device is doing OTA-Update session now then do the following task*/ + hmi_ota_firmware_update_task(); + } + else + { + if((0 < num_events_in_queue) || (true == continue_current_leds_state)) // 5) Check if there are led_events in the hmi_queue + { + /* reset the alternating flag */ + alternate_cycle_for_both = false; + + /*If there are led_events inside the hmi_queue then do the following task*/ + hmi_events_in_queue_task(); + } + else + { + if(true == usb_connection_state) // 6) Check if the USB is plugged + { + /*Check if the synchronization flag is false*/ + if(false == blue_red_sync) + { + /*Turn off both leds for synchronization*/ + port_red_led_off(); + port_blue_led_off(); + blue_red_sync = true; + } + /*If USB is connected then do the following task*/ + hmi_usb_connected_task(); + } + else + { + // If none of the previous conditions were true then turn off both leds + hmi_duration = NORMAL_HMI_DURATION; + port_red_led_off(); + port_blue_led_off(); + vTaskDelay(hmi_duration / portTICK_PERIOD_MS); + } + } + } + } + vTaskDelete(NULL); +} diff --git a/main/hmi.h b/main/hmi.h new file mode 100644 index 0000000..8c2cc9e --- /dev/null +++ b/main/hmi.h @@ -0,0 +1,56 @@ +/* + * hmi.h + * + * Created on: Feb 13, 2023 + * Author: Sword + */ + +#ifndef MAIN_HMI_H_ +#define MAIN_HMI_H_ + +#include +#include +#include +typedef enum{ + BLUE_LED_FLASHING, // Connecting to LTE Network + BLUE_LED_FLASHING_OTA, // OTA Update Firmware + BLUE_LED_SOLID, // Button pressed for a User invoked check-in + RED_LED_FLASHING, // Device has not been on-boarded + RED_LED_SOLID, // Device Failure. Need to reset + BLUE_RED_LEDS_FLASHING_200ms, // Low battery warning + BLUE_RED_LEDS_FLASHING_1SEC, // Battery Charging + BLUE_RED_LEDS_SOLD, // Battery Charged 100% + BLUE_RED_LEDS_USB_OFF, + BLUE_RED_LEDS_OFF +}hmi_leds_state_t; + +typedef enum +{ + HMI_LED_RED, + HMI_LED_GREEN, + HMI_LED_BLUE, + + HMI_LED__MAX__ +} hmi_led_type_t; + +typedef enum +{ + HMI_LED_OFF = 0, + HMI_LED_ON, + HMI_LED_BLINK, + +} hmi_led_state_t; + +void hmi_init(void); +void hmi_set_leds_state(hmi_leds_state_t status); +void hmi_stop_continued_led_state(hmi_leds_state_t led_event); +void hmi_set_usb_connection_state(bool usb_state); +void hmi_stop_red_flashing(void); +void hmi_stop_ota_event(void); +void hmi_task(void *pvParameters); +void set_leds_enable(bool status); +void hmi_do_usb_event_inside_hmi(bool decision); +void hmi_exception_usb_event(void); +int hmi_choose_led(int led_color,uint8_t op_mode); + +#endif /* MAIN_HMI_H_ */ diff --git a/main/main.c b/main/main.c new file mode 100644 index 0000000..bb741a7 --- /dev/null +++ b/main/main.c @@ -0,0 +1,677 @@ + +#include +#include +#include +#include +#include +#include "esp_log.h" +#include "nvm.h" +#include "port.h" +#include "uart_ifx.h" +#include "wifi_webServer.h" +#include "comms.h" +#include "main.h" +#include "ota.h" +#include "MCP39F501.h" +#include "wifi_OTA.h" +#include "wifi_Init.h" +#include "MCP39F501.h" +#include "esp_system.h" +#include "esp_mac.h" +#include "mqtt_client.h" +#include "esp_ota_ops.h" + +#include "lwip/sockets.h" +#include "lwip/dns.h" +#include "lwip/netdb.h" + + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" + +#include "esp_timer.h" +#include +#include "rtc.h" +#include "wifi_Client.h" +#include "data_processing.h" +#include "driver/gpio.h" +#include "comms.h" +#include "driver/timer.h" + +#include "port.h" + +#define HEARTBEAT_TIME_SEC_TEST 2 +#define HEARTBEAT_TIME_SEC 2 //14400 + +/* Unique timer ID for checking the value of isTestMode variable (should not be true for more than 1hr) */ +#define COMMS_TESTMODE_ID 3 +#define TESTMODE_DURATION 3600000 + +//#define RESTART_THRESHOLD 10 // Threshold in seconds + +//communication init flag +extern bool deviceCfged; +extern int config_heartbeat_time; +bool isCommInit = false; +bool isTestMode = false; +bool isFirstRun = true; +bool isTestModeTimerOff = true; + +int heartbeat_time = HEARTBEAT_TIME_SEC; +bool testmode_timeout = false; +static bool heartbeat_event_flag = false; + +// Global variable to store the initialization timestamp +uint32_t init_timestamp = 0; +// Global variable to store the last check timestamp +uint32_t last_check_timestamp = 0; + + + + +#define IMEI "353165803930522" +#define CONFIG_BROKER_URL "mqtt://broker.mqtt.cool" +#define MQTT_TOPIC_SUB "/topic/qos0" +#define MQTT_TOPIC_PUB "/topic/qos0" + +#define WEB_SERVER "54.204.230.201" +#define WEB_PORT "8081" +#define WEB_PATH "/hae/azuma/353165803930522/update/" +#define MCU_CONFIG "/hae/azuma/353165803930522/mcu_config_download/" +#define MCU_PROG_VER "/hae/azuma/353165803930522/mcu_pgm_download/version/" +#define MCU_UPDATE "/hae/azuma/353165803930522/update/" + +#define BROCKER_URL_TEST "mqtt://broker.mqtt.cool:1883" + +// #define BROCKER_URL_TEST "mqtt://azumamqtt1.cedalo.cloud:1883" + +char MAC_ID[16] ="353165803930522\0"; + +uint8_t comms_mode = DEFAULT_COMMS_MODE; +char HTTP_GET_DATA[512]; + + +bool RelayState_1 = false; +bool PreviousRelayState_1 = false; + +#define RELAY_PIN 42 // Define GPIO14 +#define RELAY_ON 1 +#define RELAY_OFF 0 +#define HOURS_TO_SECONDS_COEFF 3600 + + +#define TIMER_DIVIDER 16 // Prescaler (80MHz / 16 = 5MHz) +#define TIMER_SCALE (APB_CLK_FREQ / TIMER_DIVIDER) // 5,000,000 ticks per second +#define TIMER_INTERVAL_SEC 5 // 1-minute interval +bool timer_on = false; + +const char *TAG = "main"; + +/* This timer triggers server connections based on a fundamental + * timer (e.g. heartbeat timer). It never resets. + */ +static uint32_t comms_timer = 0; +uint32_t extern_comms_timer = 0; +/* flag associated with comm process intervals signaling that a + * connection should be made. + */ +bool initialize_completed_event_flag = false; + +/*Relay-Controlling*/ +void board_relay_on(void) +{ + gpio_set_level(RELAY_PIN, RELAY_ON); // Set GPIO14 HIGH + ESP_LOGI(TAG,"Relay ON"); +} + +void board_relay_off(void) +{ + gpio_set_level(RELAY_PIN, RELAY_OFF); // Set GPIO14 LOW + ESP_LOGI(TAG,"Relay OFF"); +} + +static void comms_started_cb(int status) +{ + /* Comms process has started and configuration has been updated. */ + initialize_completed_event_flag = true; +} +static void comms_connect_cb(int status) +{ + isCommInit = true; +} + +bool main_hearbeat = 0; + +int app_tick() +{ + static int retval; + + /* This heartbeat is for timing communication cycles and other main system functions. */ + if (heartbeat_event_flag) + { + heartbeat_event_flag = false; + + comms_timer++; + extern_comms_timer++; + + ESP_LOGI(TAG, "Comms timer value = %d",(uint8_t)comms_timer); + + if(comms_timer >= heartbeat_time) //2 mins + { + comms_timer = 0; + extern_comms_timer = 0; + main_hearbeat = 1; + } + + if(main_hearbeat) + { + main_hearbeat = 0; + + if(isCommInit == true) + { + isCommInit = false ; + isFirstRun = false; + + comms_start(comms_started_cb, comms_connect_cb); + } + } + + if(isTestMode) + { + heartbeat_time = HEARTBEAT_TIME_SEC_TEST; + + if(isTestModeTimerOff) + { + isTestModeTimerOff = false; + /*Start COMMS_TESTMODE_TIMER to prevent the "TESTMODE" to last more than 1hr*/ +// timer_start(COMMS_TESTMODE_ID,TESTMODE_DURATION); + } + + } + else + { + //Seraj + // if(isFirstRun == false){ + if(deviceCfged == true) { + //don't set hbt as default value + heartbeat_time = config_heartbeat_time; + //heartbeat_time = 180; + } + else{ + heartbeat_time = HEARTBEAT_TIME_SEC; + //heartbeat_time = 180; + } + // } + } + + /* Heartbeat event is driven by watchdog. Servicing is automatic */ +// watchdog_service(); + + /* comms process */ + if (comms_needs_poll()) + { + int retval = comms_poll(); + if (retval != 0) + { + ESP_LOGE(TAG, "comms poll error: %d\r\n", retval); + } + } + } + return retval; +} + +int sum_digits(int digit) +{ + int sum = 0; + while (digit > 0) + { + sum = sum + digit % 10; + digit = digit / 10; + } + return sum; +} + +void get_mac_add(void) { // The function modifies the passed buffer + uint8_t mac[6]; + esp_efuse_mac_get_default(mac); + + printf("MAC Address (Hex): %02X:%02X:%02X:%02X:%02X:%02X\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + // Convert MAC address to a decimal string + char full_mac_decimal[20]; + int len = snprintf(full_mac_decimal, sizeof(full_mac_decimal), "%d%d%d%d%d%d", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + // Ensure exactly 15 characters + if (len < 15) { + memset(MAC_ID, '0', 15 - len); + strcpy(MAC_ID + (15 - len), full_mac_decimal); + } else { + strncpy(MAC_ID, full_mac_decimal, 15); + } + // MAC_ID[15] = '\0'; + printf("Formatted MAC (15 Characters): %s\n", MAC_ID); +} + + +static bool mac_check_valid(char *message_mac) +{ + //find the length of message imei + int len = strlen(message_mac); + int sum = 0; + ESP_LOGI(TAG,"MESSAGE MAC_ID : %s\r\n",message_mac); + ESP_LOGI(TAG,"BOARD MAC_ID : %s\r\n",MAC_ID); + + ESP_LOGI(TAG,"SIZE OF MAC ID : %d\r\n",len); + + //check length of imei field + if (len != 15){ + //invalid IMEI + ESP_LOGW(TAG,"Invalid MAC length"); + return false; + } + if(!strcmp(message_mac,MAC_ID)){ + + for(int i = len-1 ; i >= 0; i--) + { + //check if message imei fields contain numbers (0-9) + if((message_mac[i] > 47) && (message_mac[i] < 58)){ + int digit = (message_mac[i] - 48); + // Doubling every alternate beginning at [13] digit + //ignore last number [14] and first number [0] as well + if (i % 2 != 0) + digit = 2 * digit; + // Finding sum of the digits + // if digit*2 is a two digit number add the two digits + sum += sum_digits(digit); + + } + else{ + ESP_LOGW(TAG,"Invalid MAC (not numerical values)\r\n"); + return false; + } + } + } + else{ + ESP_LOGI(TAG,"Message MAC doesn't match board MAC ID\r\n"); + return false; + } + + return (sum % 10 == 0); +} + +static int Comms_Parse_SP4(char *buf, int len){ + + char messageheader[4] = {0}; + char date_time[18] = {'0'}; + char messageIMEI[15] = {'0'}; + char testMode[3] = {'0'}; + int sp4_ret = -1; + char* strret = NULL; + char hbeat[5] = {'0'}; + char epcheck[2] = {0}; + + strret = strtok(buf," "); + strcpy(messageheader, strret); + ESP_LOGI(TAG,"%s message is received \r\n",messageheader); + while(strret != NULL){ + + + strret = strtok (NULL," "); + strcpy(messageIMEI, strret); + //messageIMEI[15] = '\0'; + + sp4_ret = mac_check_valid(messageIMEI); + if(sp4_ret != false){ + //ok IMEI + ESP_LOGI(TAG,"Fetched IMEI matches board.\r\n"); + } + else{ + ESP_LOGI(TAG,"INVALID IMEI!!\r\n"); + // comms.poll_timer = true; + // transition(COMMS_STATE_GOING_IDLE); + return sp4_ret; + } + + /*Parse string for date/time*/ + strret = strtok (NULL," "); + strcpy(date_time, strret); + + + + ESP_LOGI(TAG,"Date Time : %s\r\n",date_time); + + strret = strtok (NULL," "); + if(strret[0] == '1'){ + RelayState_1 = true; + }else + { + RelayState_1 = false; + } + + ESP_LOGI(TAG,"Relay State : %d\r\n",RelayState_1); +// //heartbeat_time = (buf[43] - 48) * HOURS_TO_SECONDS_COEFF ; +// heartbeat_time = (buf[44] - 48) * HOURS_TO_SECONDS_COEFF ; +// +// LOG_INFO("Heartbeat time in hrs : %d\r\n", (heartbeat_time/HOURS_TO_SECONDS_COEFF)); + + strret = strtok (NULL," "); + strcpy(hbeat, strret+2); + + ESP_LOGI(TAG,"fetched Heartbeat time value %s \r\n", hbeat); + ESP_LOGI(TAG,"previous Heartbeat time value %d \r\n", config_heartbeat_time); + + + /*moved changing heartbeat so that it comes after looking for test mode field and configuring it*/ + config_heartbeat_time = atoi(hbeat); + strret = strtok(NULL," "); + strcpy(testMode, strret); + if(testMode[2] == '1') + { + if(testmode_timeout){ + isTestMode = false; + testmode_timeout = false; + } + else{ + isTestMode = true; + } + + } + else + { + isTestMode = false; + } + //===================================== + ESP_LOGI(TAG,"Test Mode: %d\r\n",isTestMode); + + //maximum heartbeat time is 4 days (96 hours) + if((config_heartbeat_time != 0) && (config_heartbeat_time < 97 && config_heartbeat_time > 0)){ + ESP_LOGI(TAG,"Valid fetched Heartbeat time value\r\n"); + config_heartbeat_time *= HOURS_TO_SECONDS_COEFF; + deviceCfged = true; + } + else{ + config_heartbeat_time = HEARTBEAT_TIME_SEC; + deviceCfged = true; + ESP_LOGI(TAG,"Invalid fetched Heartbeat time value\r\n"); + } + + ESP_LOGI(TAG,"Heartbeat time in Sec : %d\r\n", config_heartbeat_time); + ESP_LOGI(TAG,"Heartbeat time in Hrs : %d\r\n", (config_heartbeat_time/HOURS_TO_SECONDS_COEFF)); + //___________________________ + + strret = strtok (NULL," "); + strcpy(epcheck, strret); + + if(!strcmp(epcheck,"EP\"") || !strcmp(epcheck,"EP")){ + ESP_LOGI(TAG,"(%s) %s message is terminated\r\n", epcheck,messageheader); + break; + } + else{ + ESP_LOGI(TAG,"(%s) %s message is not terminated!! \r\n", epcheck,messageheader); + } + + } + /* ============================ + temp[0] = date_time[0]; + temp[1] = date_time[1]; + temp[2] = date_time[2]; + temp[3] = date_time[3]; + + RTC_DateTime.Year = atoi(temp); + + ==============TEST============== + LOG_INFO("Year: %d\r\n",RTC_DateTime.Year); + ============================ + + temp[0] = date_time[5]; + temp[1] = date_time[6]; + temp[2] = ' '; + temp[3] = ' '; + RTC_DateTime.Month = atoi(temp); //mm value to integer + + ==============TEST============== + LOG_INFO("Month: %d\r\n",RTC_DateTime.Month); + ============================ + + temp[0] = date_time[8]; + temp[1] = date_time[9]; + temp[2] = ' '; + temp[3] =' '; + RTC_DateTime.MonthDay = atoi(temp); //dd value to integer + + ==============TEST============== + LOG_INFO("MonthDay: %d\r\n",RTC_DateTime.MonthDay); + ============================ + temp[0] = date_time[11]; + temp[1] = date_time[12]; + temp[2] = ' '; + temp[3] =' '; + RTC_DateTime.Hour = atoi(temp); //HH value to integer + + ==============TEST============== + LOG_INFO("Hour: %d\r\n",RTC_DateTime.Hour); + ============================ + temp[0] = date_time[14]; + temp[1] = date_time[15]; + temp[2] = ' '; + temp[3] = ' '; + RTC_DateTime.Minute = atoi(temp); //hh value t integer + + ==============TEST============== + LOG_INFO("Minute: %d\r\n",RTC_DateTime.Minute); + ============================ + temp[0] = date_time[17]; + temp[1] = ' '; + temp[2] = ' '; + temp[3] = ' '; + RTC_DateTime.WeekDay = atoi(temp); + + ==============TEST============== + LOG_INFO("WeekDay: %d\r\n",RTC_DateTime.WeekDay); + ============================ + //rtc_set_timestamp(&rtcData); + RTC_SetTimeDate(&RTC_DateTime);*/ + + // update relay state if changed + if(RelayState_1 != PreviousRelayState_1) + { + + + ESP_LOGI(TAG,"Previous Relay State :%d \n",PreviousRelayState_1); + PreviousRelayState_1 = RelayState_1; /* update PreviousRelayState */ + + if(RelayState_1 == true) + { + /*============================*/ + board_relay_on(); + //board_led_all_off(); + //board_led_green_on(); + //hmi_choose_led(HMI_LED_GREEN, HMI_LED_ON); + + ESP_LOGI(TAG,"Board relay state : %d\n",RelayState_1); + } + else + { + /*============================*/ + board_relay_off(); + //board_led_all_off(); + //board_led_red_on(); + // hmi_choose_led(HMI_LED_RED, HMI_LED_ON); + + ESP_LOGI(TAG,"Board relay state : %d\n ",RelayState_1); + } + + } + else + { + ESP_LOGI(TAG,"Previous Relay State :%d \n",PreviousRelayState_1); + ESP_LOGI(TAG,"No changes in Relay State\r\n"); + /*Transition to idle state*/ + //comms.poll_timer = true; //polling the comms_timer + //Comms_Check_TestMode(); + } + + + int retval = COMMS_STATUS_OK; + //creating sp1 message + SP1_Mqtt_Tx(config_heartbeat_time); + + /* Run the MQTT publish sequence if nothing else pending */ + // retval = start_mqtt_publish_sequence(); + if(retval == 1) + { + ESP_LOGI(TAG,"MQTT Publish SP1 message success"); + + } + else + { + ESP_LOGI(TAG,"MQTT Publish SP1 message failed"); + + } + return 0; +} + +// Interrupt Service Routine (ISR) +void IRAM_ATTR timer_isr_callback(void *param) { + int timer_idx = (int)param; + + // Clear the interrupt flag + timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, timer_idx); + + // Re-enable the timer alarm + timer_group_enable_alarm_in_isr(TIMER_GROUP_0, timer_idx); + + heartbeat_event_flag = true; +} + +// Timer Initialization +void my_timer_init() { + // Disable existing interrupts before registering a new one + timer_deinit(TIMER_GROUP_0, TIMER_0); + + timer_config_t config = { + .divider = TIMER_DIVIDER, + .counter_dir = TIMER_COUNT_UP, + .alarm_en = TIMER_ALARM_EN, + .auto_reload = true, // Restart timer after reaching the alarm value + .intr_type = TIMER_INTR_LEVEL + }; + + timer_init(TIMER_GROUP_0, TIMER_0, &config); + timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0); + timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, TIMER_SCALE * TIMER_INTERVAL_SEC); + timer_enable_intr(TIMER_GROUP_0, TIMER_0); + + // Register the ISR with SHARED interrupt flag + esp_err_t err = timer_isr_callback_add(TIMER_GROUP_0, TIMER_0, timer_isr_callback, (void*)TIMER_0, ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to register timer ISR: %s", esp_err_to_name(err)); + return; + } + + // Start the timer + timer_start(TIMER_GROUP_0, TIMER_0); +} + +void app_main(void) +{ + + ESP_LOGI(TAG,"*** Starting app_main ***"); + + /* Initialize the dedicated NVS partition */ + nvm_init(); + port_init(); + uart_ifx_init(); + + /* Create the UART tasks for both UART0 and UART1 */ + uart_create_rx_tasks(); +/* + while(1) { + + port_red_led_on(); + vTaskDelay(1000 / portTICK_PERIOD_MS); + port_red_led_off(); + vTaskDelay(1000 / portTICK_PERIOD_MS); + port_blue_led_on(); + vTaskDelay(1000 / portTICK_PERIOD_MS); + port_blue_led_off(); + vTaskDelay(1000 / portTICK_PERIOD_MS); + port_green_led_on(); + vTaskDelay(1000 / portTICK_PERIOD_MS); + port_green_led_off(); + + // app_tick(); + ESP_LOGI(TAG, "LED testing running ...."); + }*/ + + + // get_mac(mac_address); + // printf("MAC Address in main: %s\n", mac_address); + + /*while(1){ + + + port_vbatt_sense_enable(0); + ESP_LOGI(TAG, "Realy ON"); + vTaskDelay(1500/portTICK_PERIOD_MS); + + port_accel_pwr_enable(1); + ESP_LOGI(TAG, "Realy OFF"); + vTaskDelay(1500/portTICK_PERIOD_MS); + + }*/ + + + /* Start On-Boarding process */ + webserver_start(); + + get_mac_add(); + + /* Wait till the onboarding process is completed */ + while(!webserver_get_status()) + { + vTaskDelay(750/portTICK_PERIOD_MS); + ESP_LOGI(TAG, "Waiting from wifi connect ....."); + port_blue_led_toggle(); + } + port_red_led_off(); + port_green_led_on(); + wifi_first_init(); + +// Connect_wifi_sta(WIFI_MODE_STA); + + uint8_t ret = comms_init(); + if ( ret == COMMS_STATUS_OK ) + { + ESP_LOGI(TAG, "Comms init Successfully :) "); + comms_start(comms_started_cb, comms_connect_cb); + } else + { + ESP_LOGW(TAG, "Comms init faild :("); + } + + my_timer_init(); + +// char url_sp4[]="http://54.204.230.201:8081/hae/azuma/128101153943920/mcu_config_download/"; +// char res[512]; +// http_client_get(url_sp4, res); +// ESP_LOGI(TAG, "SP4 ===> messgage \n %s",res); +// +// Comms_Parse_SP4(res, sizeof(res)); + + while(1) { + + vTaskDelay(1000 / portTICK_PERIOD_MS); + app_tick(); + ESP_LOGI(TAG, "App tick running ...."); + } + +} + + + diff --git a/main/main.h b/main/main.h new file mode 100644 index 0000000..d9fc15d --- /dev/null +++ b/main/main.h @@ -0,0 +1,38 @@ +/* + * main.h + * + * Created on: Feb 11, 2023 + * Author: Nael + */ + +#ifndef MAIN_MAIN_H_ +#define MAIN_MAIN_H_ + +#include +extern char MAC_ID[16]; + +#define SENS_INT_WAKEUP 0 +#define SENS_INT_WAKEUP0 1 + +/*************** Cellular Configs **************/ +#define FAKE_NETWORK 0 +#define TEMPSTICK_SERVER 1 +#define TESTING_SERVER 1 // 0 Partha server, 1 Ranjeet server. +/***********************************************/ + +/***************** WIFI Configs ****************/ +#define WIFI_NEEDED 1 +/***********************************************/ + +#define DEFAULT_COMMS_MODE 1 + +#define BUILD_VERSION_STRING "LRSM0009C_Azuma_v0.5" + +void update_checkin_timer(uint64_t checkin_interval); +#if (SENS_INT_WAKEUP == 1) +void set_periodic_sleep_mode(); +#else +void set_sleep_mode(); +#endif + +#endif /* MAIN_MAIN_H_ */ diff --git a/main/modem.c b/main/modem.c new file mode 100644 index 0000000..4c6d673 --- /dev/null +++ b/main/modem.c @@ -0,0 +1,3109 @@ +/* + * bg96.c + * + * Created on: Jan 11, 2023 + * Author: Sword + */ + +/* Includes -----------------------------------------------------------------*/ +#include "uart_ifx.h" +#include "port.h" +#include "esp_system.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include +#include "esp_timer.h" +#include +#include +#include "rtc.h" +#include "modem.h" + +static const char* TAG = "BG77"; + +#define LOG_LOCAL_LEVEL ESP_LOG_INFO +#include "esp_log.h" + +/* Logging configuration */ +#define LOG_MODULE "BG77" +#define LOG_LEVEL LOG_LEVEL_INFO + +/* Local buffer to formulate commands as necessary */ +#define MODEM_TX_BUFFER_LEN 3072 + +/* Modem receiver buffer: expected to hold entire response for any command */ +#define MODEM_RX_BUFFER_LEN 4096 + +/* This drive is expecting this particular device in the ATI response */ +#define MODEM_DEVICE_MATCH_STRING "BG77" + +/* Match the expected service mode int the QCSQ command for signal strength. + * For CAT-M1, string differs my modem model, e.g. "CAT-M1" for BG96, "eMTC" for BG77 + */ +#define MODEM_QCSQ_SERIVCE_MODE_MATCH_1 "eMTC" +#define MODEM_QCSQ_SERIVCE_MODE_MATCH_2 "NBIoT" + +/* Modem QFREAD chunk size. This must fit into the RX buffer taking into + * account possible responses + */ +#define MODEM_FREAD_SIZE (MODEM_RX_BUFFER_LEN - 128) + +/* Maximum number of QFREAD command issuances (retries) we will use to transfer a file. + * The actual number of QFREAD command issuances is dependent upon the file size determined by QFLST. + */ +#define MODEM_FREAD_MAX_PACKET_TRANSFERS 196 + +#define MODEM_IMEI_START 2 /* ignore first 2 characters */ +#define MODEM_IMEI_LEN 16 + +#define BG96_ICCID_START 10 /* ignore first 8 characters */ +#define BG96_ICCID_LEN 20 + +#define JWT_LEN 442 + +/* Max number of bytes that can be sent after QMTPUB command */ +#define MODEM_MQTT_PUB_BYTES_MAX 1548 + +/* Set to 1 to enable the use of BG96 STATUS pin to improve reliability + * and speed of modem power up and shutdown operations. + */ +#define MODEM_USE_STATUS_PIN 1 + +/* Time to wait for modem to turn on */ +#define MODEM_STARTUP_STATUS_TIMEOUT_MS 10000 + +/* Time to wait for modem AT interface to ready */ +#define MODEM_STARTUP_RDY_TIMEOUT_MS 5000 + +/* Granularity of the process timer for polling events */ +#define MODEM_TIMER_GRANULARITY_MS 500 + +/* Maximum number of commands supported in one sequence */ +#define MODEM_MAX_SEQUENCE 17 + +/* Unique timer ID for this process for use with timer module */ +//#define MODEM_TIMER_ID 0 + +/* 500-1000 ms */ +#define MODEM_PWRKEY_ON_DELAY_MS 750 + +/* 650-1500 ms */ +#define MODEM_PWRKEY_OFF_DELAY_MS 1000 + +/* >= 2000 ms */ +#define MODEM_PWRKEY_RESET_DELAY_MS 2500 + +/* Set to enable QHTTP OK parsing, clear to timeout and parse anyways */ +#define USE_QHTTPREAD_OK 1 + +/* Periodic monitoring of STATUS while waiting to confirm shutdown */ +#define MODEM_SHUTDOWN_STATUS_MONITOR_PERIOD_MS 2000 + +#define MODEM_SHUTDOWN_COUNT_WARNING_SEC 10 + +#define MODEM_SHUTDOWN_COUNT_LIMIT_SEC 65 + +/* Indicate modem power as indicated with STATUS line on green LED. */ +#define MODEM_DEBUG_MODEM_POWER_STATE 0 + +/* Enable the "blind modem" option to ignore the STATUS line state + * and the "APP RDY" indication when turning on and off the modem. + */ +#define MODEM_BLIND_MODEM 0 + +#define MODEM_MODEM_FWVER_FROM_QGMR 1 + +/* ------------------------------------------------------------------------- */ + +typedef enum { + MODEM_EVENT_START, + MODEM_EVENT_TIMER, + MODEM_EVENT_UART, + MODEM_EVENT_STOP, + MODEM_EVENT_SEND_CMD, +} modem_event_t; + +/* State of the bg96 process */ +typedef enum { + MODEM_STATE_INIT, + MODEM_STATE_READY, + MODEM_STATE_WAIT_POWERUP, + MODEM_STATE_WAIT_STARTUP, + MODEM_STATE_WAIT_STARTUP_RDY, + MODEM_STATE_WAIT_POWERDOWN, + MODEM_STATE_WAIT_POWERKEY, + MODEM_STATE_CMD, + +} modem_state_t; + +/* Status is OK (match response), ERROR (match error) or TIMEOUT (retries and/or wait exhausted). + * Returns OK or ERROR in processing to help direct which command to call subsequently in + * the sequence. + */ +typedef int (*modem_cmd_handler_t)(int status, const char *buf, int len); + +/* Command setup function, passed the parameters + * Intended to write to tx buf and assign that as the command. + * Return OK to assign buf as cmd_str. + * Must print a null-terminated string. + */ +typedef int (*modem_cmd_setup_t)(const void *params, char *buf, int len); + +/* Allows command to send custom information */ +typedef int (*modem_send_t)(void); + +typedef struct { + const char *host; + int port; + const char *client_id; + //const char* jwt; + const char *pub_topic; + const char *pub_data; + int pub_data_len; + +} modem_mqtt_setup_params_t; + +typedef struct { + /* input params */ + const char *url; + const char *file; /* for get_to_file */ + const char *post_message; + + /* output params */ + char *buf; + int len; + +} modem_http_setup_params_t; + +typedef struct { + const char *file; + uint32_t file_size; + uint32_t transferred_bytes; + uint16_t chunk_size; + uint16_t expected_bytes; + uint16_t read_size; + uint16_t file_handle; /* from +QFOPEN: */ + + modem_read_from_file_open open_cb; + modem_read_from_file_data data_cb; + +} modem_file_params_t; + +typedef struct { + + const char *cmd_str; + const char *response_match_str; /* null allowed */ + const char *err_match_str; /* null allowed */ + + const int wait_ms; + const int retries; + + /* Called after command is sent */ + const modem_send_t send; + + /* Called on timeout or match on response or err strings. */ + const modem_cmd_handler_t handler; + + /* Called on command setup */ + const modem_cmd_setup_t setup; + + /* Pointer to setup parameters */ + const void *params; + +} modem_cmd_t; + +typedef struct { + modem_state_t state; + + /* Timer */ + bool poll_timer; + + /* UART queue flag */ + bool poll_uart; + + /* Poll periods counter in units of timer granularity */ + int poll_periods; + + /* User start callback */ + modem_start_cb_t start_cb; + + /* Command processing state */ + bool is_startup; /* in startup mode */ + + /* Command sequencer */ + const modem_cmd_t *cmd_list[MODEM_MAX_SEQUENCE]; /* list of commands*/ + int cmd_num; /* number in list */ + int cmd_retries; /* count of retries */ + int cmd_sequence; /* current command in list */ + bool cmd_repeat; /* option to repeat a command if set true */ + modem_op_cb_t cmd_complete_cb; + + /* Device IMEI */ + char imei[MODEM_IMEI_LEN]; + char iccid[BG96_ICCID_LEN]; + + /* Device FW version e.g. BG95M2LAR01A01 or BG77LAR02A02_01.001.01.001 */ + char fw_ver[MODEM_FWVER_LEN]; + + modem_signal_strength_t sig_strength; + + /* Command parameters */ + modem_mqtt_setup_params_t mqtt_params; + modem_http_setup_params_t http_params; + modem_file_params_t file_params; + + /* Shutdown state monitoring */ + int shutdown_counter; + bool shutdown_qpowd; + uint16_t shutdown_qpowd_failures; + uint16_t shutdown_pwrkey_failures; + +} modem_t; + +/* Local process state */ + +static modem_t bg96; + +esp_timer_handle_t modem_timer; +/* ------------------------------------------------------------------------- */ + +/* comm buffers */ +static char txBuf[MODEM_TX_BUFFER_LEN]; +static char rxBuf[MODEM_RX_BUFFER_LEN]; +static uint16_t rx_idx = 0; + +/************** Device-Comms-Data *************/ +extern uint8_t comms_mode; + +/* Signal strength data stored after GetSignalStrength is called. */ +//static MODEM_SignalStrength_t m_signalStrength; +/* Wrapping scanner treats RX buffer like a circular buffer and retains state + * over calls. It allows scanning for strings that might wrap around. + */ +typedef struct { + int RxScannerState; + int RxScannerIdx; +} ScanContext_t; + +static ScanContext_t RxScanContextSuccess; /* scanner for success string */ +static ScanContext_t RxScanContextError; /* scanner for failure string */ +/* AT Commands -------------------------------------------------------------*/ + +// Note: These AT commands end with AT_OK response +// ATI, +#define ATI "ATI\r\n" +#define AT_QPOWD "AT+QPOWD\r\n" +//#define AT_CGDCONT "AT+CGDCONT=1,\"IPV4V6\",\"\",\"0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0\",0,0,0,0\r\n" +//#define AT_CGDCONT_ALT "AT+CGDCONT=1,\"IP\",\"\"\r\n" +#define AT_CGDCONT "AT+CGDCONT=1,\"IPV4V6\",\"hologram\"\r\n" +#define AT_QICSGP "AT+QICSGP=1,1,\"\",\"\",\"\",0\r\n" +#define AT_QIACT "AT+QIACT=1\r\n" + +#define AT_QHTTPPOST "AT+QHTTPPOST=%u,80,60\r\n" +#define AT_QHTTPCFG_RHD "AT+QHTTPCFG=\"requestheader\",1\r\n" +#define AT_QHTTPCFG_RHD_0 "AT+QHTTPCFG=\"requestheader\",0\r\n" +#define AT_QHTTPCFG_CTX "AT+QHTTPCFG=\"contextid\",1\r\n" +#define AT_QHTTPURL "AT+QHTTPURL=%u,80\r\n" /* host */ +#define AT_QHTTPGET "AT+QHTTPGET=80\r\n" +#define AT_QHTTPREAD "AT+QHTTPREAD=80\r\n" +#define AT_QHTTPREADFILE "AT+QHTTPREADFILE=\"%s\",80\r\n" /* file name */ + +#define AT_QFLDS "AT+QFLDS=\"UFS\"" +#define AT_QFLST "AT+QFLST=\"%s\"\r\n" /* file name */ +#define AT_QFOPEN "AT+QFOPEN=\"%s\",2\r\n" /* file name, mode=2 (readonly, fail if not found) */ +#define AT_QFREAD "AT+QFREAD=%d,%u\r\n" /* file handle, chunk size */ +#define AT_QFCLOSE "AT+QFCLOSE=%d\r\n" /* file handle */ +#define AT_QFOTADL "AT+QFOTADL=%s\r\n" /* host */ +#define AT_ECHO_OFF "ATE0\r\n" +#define AT_CGSN "AT+CGSN\r\n" +#define AT_QCCID "AT+QCCID\r\n" +#define AT_CGMR "AT+CGMR\r\n" +#define AT_QGMR "AT+QGMR\r\n" +#define AT_CPIN "AT+CPIN?\r\n" +#define AT_CFUN_OFF "AT+CFUN=0\r\n" +#define AT_CFUN_FULL "AT+CFUN=1\r\n" +#define AT_COPS "AT+COPS?\r\n" +#define AT_QCFG_IOTOPMODE "AT+QCFG=\"iotopmode\",2,1\r\n" /* LTE-M immediately */ +#define AT_QMTCFG_WILL "AT+QMTCFG=\"will\",0\r\n" +#define AT_QMTCFG_TIMEOUT "AT+QMTCFG=\"timeout\",0,60,3,1\r\n" /* Qianwen, used to be: "AT+QMTCFG=\"timeout\",0,60,3,0\r\n", 1000 */ +#define AT_QMTCFG_KEEPALIVE "AT+QMTCFG=\"keepalive\",0,60\r\n" +#define AT_QMTOPEN "AT+QMTOPEN=0,\"%s\",%d\r\n" /* host, port */ +#define AT_QMTCLOSE "AT+QMTCLOSE=0\r\n" +#define AT_QMTDISC "AT+QMTDISC=0\r\n" +//#define AT_QMTCONN_CID "AT+QMTCONN=0,\"projects/pwc-production/locations/us-central1/registries/unwiredprime/devices/CTD-%s\",\"unused\",\"%s\"\r\n" /* clientID, username,password */ +#define AT_QMTCONN_CID "AT+QMTCONN=0,\"%s\"\r\n" /* clientID*/ +#define AT_QMTPUB "AT+QMTPUB=0,0,0,0,\"/devices/CTD-%s/events\"\r\n" /* topic, note that actual message data follows " and that \r\n is likely sent to the server */ +#define AT_QCSQ "AT+QCSQ\r\n" +#define AT_CTZU "AT+CTZU=1\r\n" +#define AT_CCLK "AT+CCLK?\r\n" +#define AT_IFC "AT+IFC=2,2\r\n" +#define AT_QFDEL "AT+QFDEL=\"" MCU_OTA_FILE_LOCAL "\"\r\n" +#define AT_QFUPL "AT+QFUPL=\"cacert.pem\",1244,100\r\n" +#define AT_QMTCFG_SSL "AT+QMTCFG=\"ssl\",0,1,0\r\n" +#define AT_QSSLCFG_CACERT "AT+QSSLCFG=\"cacert\",0,\"cacert.pem\"\r\n" +#define AT_QSSLCFG_SECLEVEL "AT+QSSLCFG=\"seclevel\",0,0\r\n" +#define AT_QSSLCFG_SSLVER "AT+QSSLCFG=\"sslversion\",0,3\r\n" +#define AT_QSSLCFG_CIPH "AT+QSSLCFG=\"ciphersuite\",0,0XFFFF\r\n" +#define AT_QSSLCFG_IGNRTIME "AT+QSSLCFG=\"ignorelocaltime\",0,1\r\n" +#define AT_QMTCFG_VERSION "AT+QMTCFG=\"version\",0,4\r\n" + +/* Responses */ +#define AT_OK "\r\nOK\r\n" +#define AT_ERROR "\r\nERROR\r\n" +#define AT_CONNECT "CONNECT\r\n" +#define AT_HTTPPOST_SUCCESS "+QHTTPPOST: 0,200" +#define AT_QHTTPGET_SUCCESS "\r\n+QHTTPGET: 0,200" +#define AT_QHTTPREAD_SUCCESS "+QHTTPREAD: 0" +#define AT_QHTTPREADFILE_SUCCESS "\r\n+QHTTPREADFILE: 0" +#define AT_QFLST_SUCCESS "\r\n+QFLST: \"%s\"" +#define AT_QFOPEN_SUCCESS "\r\n+QFOPEN:" +#define AT_CPIN_RESPONSE "\r\n+CPIN: READY" /* every response will be at least this long */ +#define AT_IND_QMTOPEN "\r\n+QMTOPEN: 0,0" +#define AT_IND_QMTCONN "\r\n+QMTCONN: 0,0,0" +#define AT_IND_QMTPUB "\r\n+QMTPUB: 0,0,0" +#define AT_IND_QFUPL "\r\n+QFUPL:" +#define AT_IND_QMTCLOSE "\r\n+QMTCLOSE: 0,0" +#define AT_IND_CME_ERROR "\r\n+CME ERROR:" +#define AT_QENG_RESPONSE "\r\n+QENG:" //Partha = March 11, 2022 + +/* Unsolicited Result Codes */ +#define URC_RDY "\r\nRDY\r\n" + +#define CTRLZ (0x1A) +#define ESC (0x1B) + +/* Notes: + * Possiblity of getting time from the network. + * Using NITZ method, BGxx may automatically obtain it and set it in internal RTC. + * Command sequence: + * AT+CTZU=1 enable automatic timezone update (in effect automatic time update) + * optional: AT+CTZR=2 automatic timezone reporting: emit +CTZV indication + * Get time: AT+CCLK? + * + */ + +/* --------------------------------------------------------------------------*/ + +//definition a new function to return signal strength +int modem_Rssi() { + + return bg96.sig_strength.rssi; + +} + +char* modem_get_rxbuf(void) +{ + return (char*)(rxBuf); +} + + +/* Transfer bytes from queue to supplied buffer */ +int board_uart_rx_stream_poll(uint8_t* buf, uint16_t* rx_idx, int buf_len) +{ + int bytesCopied = 0; + //vTaskDelay(10 / portTICK_PERIOD_MS); + while(uart_ifx_uart1_ifx_get_rx_data(&buf[(*rx_idx)++]) && (*rx_idx <= buf_len)) + { + bytesCopied++; + } + return bytesCopied; +} + + +/* forward decl */ +static int start_cmd_sequence(void); + +static int handle_imei_cmd(int status, const char *buf, int len) { + int retval = MODEM_STATUS_ERROR; + + if (status == MODEM_STATUS_OK) { + strncpy(bg96.imei, buf + MODEM_IMEI_START, MODEM_IMEI_LEN); + bg96.imei[MODEM_IMEI_LEN - 1] = 0; + + ESP_LOGI(TAG,"IMEI No: %s\r\n", bg96.imei); + + retval = MODEM_STATUS_OK; + } + + return retval; +} + +#define ATI_REVISION_STR "Revision: " + + + +static int handle_iccid_cmd(int status, const char* buf, int len){ + + int retval = MODEM_STATUS_ERROR; + + if (status == MODEM_STATUS_OK) + { + strncpy(bg96.iccid, buf + BG96_ICCID_START, BG96_ICCID_LEN); + bg96.iccid[BG96_ICCID_LEN - 1] = '\0'; + + ESP_LOGI(TAG,"ICCID No: %s", bg96.iccid); + retval = MODEM_STATUS_OK; + } + + return retval; +} + +static int handle_ati_cmd(int status, const char *buf, int len) { + int retval = MODEM_STATUS_ERROR; + + if (status == MODEM_STATUS_OK) { + /* Find the expected device type */ + if (strstr(buf, MODEM_DEVICE_MATCH_STRING) != NULL) { + retval = MODEM_STATUS_OK; + + ESP_LOGI(TAG,MODEM_DEVICE_MATCH_STRING " detected!\r\n"); + } else { + ESP_LOGW(TAG,"Device " MODEM_DEVICE_MATCH_STRING "not detected\r\n"); + } + +#if MODEM_MODEM_FWVER_FROM_QGMR == 0 + /* Find the firmware version */ + char* start_ptr = strstr(buf, ATI_REVISION_STR); + if (start_ptr) { + start_ptr += strlen(ATI_REVISION_STR); + char* end_ptr = strstr(start_ptr, "\r"); + if (end_ptr) { + int len = end_ptr - start_ptr; + len = (len > MODEM_FWVER_LEN-1) ? MODEM_FWVER_LEN-1 : len; + memcpy(bg96.fw_ver, start_ptr, len); + bg96.fw_ver[MODEM_FWVER_LEN - 1] = 0; /* ensure terminated */ + } + } + + if (bg96.fw_ver[0]) { + ESP_LOGI(TAG,"FW: %s\r\n", bg96.fw_ver); + } + else { + ESP_LOGI(TAG,"Cannot get firmware version\r\n"); + } +#endif + + } + + return retval; +} + +#if MODEM_MODEM_FWVER_FROM_QGMR +static int handle_qgmr_cmd(int status, const char *buf, int len) { + int retval = MODEM_STATUS_ERROR; + + if (status == MODEM_STATUS_OK) { + /* firmware version is modem+application string returned by modem before OK + e.g. BG77LAR02A02_01.001.01.001 + */ + const char *start_ptr = buf; + if (start_ptr) { + /* If modem starts revision string with \r\n, skip those. */ + if (start_ptr[0] == '\r' && start_ptr[1] == '\n') { + start_ptr += 2; + } + /* look for a trailing \r\n, which may be part of version, or part of \r\nOK\r\n sent after + */ + const char *end_ptr = strstr(start_ptr, "\r\n"); + if (end_ptr) { + int len = end_ptr - start_ptr; + len = (len > MODEM_FWVER_LEN - 1) ? MODEM_FWVER_LEN - 1 : len; + memcpy(bg96.fw_ver, start_ptr, len); + bg96.fw_ver[MODEM_FWVER_LEN - 1] = 0; /* ensure terminated */ + } + } + + if (bg96.fw_ver[0]) { + ESP_LOGI(TAG,"FW: %s\r\n", bg96.fw_ver); + } else { + ESP_LOGI(TAG,"Cannot get firmware version\r\n"); + } + + /* OK regardless; modem can continue to run */ + retval = MODEM_STATUS_OK; + } + + return retval; +} +#endif + +/*static int handle_flds_cmd(int status, const char *buf, int len) { + + ESP_LOGI(TAG,"AT+QFLDS response is: %s",buf); + return status; +}*/ + +static int handle_cops_cmd(int status, const char *buf, int len) { +#if FAKE_NETWORK + return MODEM_STATUS_OK; +#else + /* Return OK to pass this command, or anything else to continue polling. */ + char *token; + if ((strstr(rxBuf, "\"") != NULL)) { + token = strtok(rxBuf, "\""); + token = strtok(NULL, "\""); + ESP_LOGI(TAG,"The Operator is: %s\r\n", token); + ESP_LOGI(TAG,"Connected to network.\r\n"); + status = MODEM_STATUS_OK; + } + else + { + /* If the comms_mode is CELL-ONLY then make at cops command-retries infinite. otherwise make it limited to 120 retries */ + if(comms_mode == 3) + { + bg96.cmd_repeat = true; + } + else + { + status = MODEM_STATUS_TIMEOUT; + } + } + + return status; +#endif +} + +static int handle_qfdel_cmd(int status, const char *buf, int len) { + status = MODEM_STATUS_OK; + return status; +} + +#define AT_CCLK_IND "+CCLK:" + +static int handle_cclk_cmd(int status, const char *buf, int len) { + int retval = MODEM_STATUS_ERROR; + + /* Working with response like: +CCLK: "20/09/09,16:11:40-16" yy/MM/dd,hh:mm:ss-zz + * Timezone offset is in quarter-hour units. -16 is -4 hours from GMT/UTC. + * A negative offset means we need to add those hours to the local epoch to get back + * to GMT. + * NOTE!! GMT is reqired to be the system-wide time base: + * - Each modem provides local time with offset; we convert to GMT + * - Server time must also be in GMT. Currently, this is correct: SP1 timestamps + * generated by are in GMT. (https://dev.web.unwiredprime.com/api/v1/trackers/tracker/866349040012464/mcu_config_download) + */ + /* The modem under test actually spits out a nonsense time of +CCLK: "80/01/06,00:25:27 + * when not connected to a network. (Year = 2080) + */ + if (status == MODEM_STATUS_OK) { + char *s = strstr(rxBuf, AT_CCLK_IND); + //char* s = "+CCLK: \"20/09/09,16:11:40-16\""; + if (s) { + rtc_timestamp_t ts; + s += strlen(AT_CCLK_IND) + 2; /* past space and " */ + ts.Year = atoi(s) + 2000; + s += 3; + ts.Month = atoi(s); + s += 3; + ts.MonthDay = atoi(s); + s += 3; + ts.Hour = atoi(s); + s += 3; + ts.Minute = atoi(s); + s += 3; + ts.Second = atoi(s); + /* Try to convert the offset, plus or minus. If it can't it is 0 and + * no harm done. + */ + s += 2; + int tz_offset_sec = atoi(s) * 15 * 60; /* modem provides 1/4 hour increments */ +#if SET_TIME_FROM_SERVER == 0 + + /* Set time just once - here, or from the SP1 record */ + if (!rtc_is_set()) { + rtc_set_timestamp_tz(&ts, tz_offset_sec); + } + + ESP_LOGI(TAG,"Set time: %d/%d/%d,%d:%d:%d %d -> %ld\r\n", + ts.Year, ts.Month, ts.MonthDay, ts.Hour, ts.Minute, ts.Second, tz_offset_sec, + rtc_get_epoch()); +#else + /* Print time modem provides, but do not use it. */ + ESP_LOGI(TAG,"Modem time: %d/%d/%d,%d:%d:%d %d\r\n", + ts.Year, ts.Month, ts.MonthDay, ts.Hour, ts.Minute, ts.Second, tz_offset_sec); +#endif + } + + retval = MODEM_STATUS_OK; + } + return retval; +} + + + +static int handle_qcsq_cmd(int status, const char *buf, int len) { + int retval = MODEM_STATUS_ERROR; + + /* Working with response like: +QCSQ: "CAT-M1",-52,-81,195,-10 */ + if (status == MODEM_STATUS_OK) { + char *s = strtok(rxBuf, ","); + if ((s != NULL) && (strstr(s, MODEM_QCSQ_SERIVCE_MODE_MATCH_1) || strstr(s, MODEM_QCSQ_SERIVCE_MODE_MATCH_2))) { + /* parse the 4 fields */ + s = strtok(NULL, ","); + if (s){ + bg96.sig_strength.rssi = atoi(s); + s = strtok(NULL, ","); + if (s){ + bg96.sig_strength.rsrp = atoi(s); + s = strtok(NULL, ","); + if (s){ + bg96.sig_strength.sinr = atoi(s); + s = strtok(NULL, ","); + if (s){ + bg96.sig_strength.rsrq = atoi(s); + retval = MODEM_STATUS_OK; + ESP_LOGI(TAG,"Signal strength: %d %d %d %d\r\n", + bg96.sig_strength.rssi, + bg96.sig_strength.rsrp, + bg96.sig_strength.sinr, + bg96.sig_strength.rsrq); + } + } + } + } + retval = MODEM_STATUS_OK; + } else { + ESP_LOGW(TAG,"Signal strength : did not find expected service mode\r\n"); + bg96.cmd_repeat = true; + } + // to pass the command anyways + // retval = MODEM_STATUS_ERROR; + } + return retval; +} +/* +static int setup_qmtopen_cmd(const void *params, char *buf, int len) { + snprintf(buf, len, AT_QMTOPEN, bg96.mqtt_params.host, + bg96.mqtt_params.port); + + return MODEM_STATUS_OK; +} + +static int setup_qmtconn_cmd(const void *params, char *buf, int len) { + snprintf(buf, len, AT_QMTCONN_CID,bg96.mqtt_params.client_id); //, bg96.imei, bg96.mqtt_params.jwt);//bg96.mqtt_params.client_id,); + return MODEM_STATUS_OK; +} + +static int setup_qmtpub_cmd(const void *params, char *buf, int len) { + snprintf(buf, len, AT_QMTPUB, bg96.imei); //bg96.mqtt_params.pub_topic); + + return MODEM_STATUS_OK; +} +*/ + +/* Write the length of the URL in the command */ +static int setup_qhttpurl_cmd(const void *params, char *buf, int len) { + snprintf(buf, len, AT_QHTTPURL, strlen(bg96.http_params.url)); + + return MODEM_STATUS_OK; +} + + +/* Write the length of the POST message in the command */ +static int setup_qhttppost_cmd(const void *params, char *buf, int len) { + snprintf(buf, len, AT_QHTTPPOST, strlen(bg96.http_params.post_message)); + vTaskDelay(1000 / portTICK_PERIOD_MS); + return MODEM_STATUS_OK; +} + + +/* Write the length of the URL in the command */ +static int setup_qhttpreadfile_cmd(const void *params, char *buf, int len) { + snprintf(buf, len, AT_QHTTPREADFILE, bg96.http_params.file); + + return MODEM_STATUS_OK; +} + +static int setup_qfotadl_cmd(const void *params, char *buf, int len) { + /* Send the URL */ + snprintf(buf, len, AT_QFOTADL, bg96.http_params.url); + ESP_LOGW(TAG,"setup_qfotadl_cmd"); + ESP_LOGI(TAG,"buf: %s",buf); + return MODEM_STATUS_OK; +} + + +static int handle_qhttpread_cmd(int status, const char *buf, int len) { + bg96.http_params.buf = 0; + bg96.http_params.len = 0; + + /* Parse response */ +#if USE_QHTTPREAD_OK + if (status == MODEM_STATUS_OK) +#else + if (status == MODEM_STATUS_TIMEOUT) +#endif + { + /* status is error until we prove correctness of response and + * user arguments are updated + */ + status = MODEM_STATUS_ERROR; + + /* Ensure null-termination when searching for strings */ + rxBuf[MODEM_RX_BUFFER_LEN - 1] = 0; + + /* Find the connect response */ + char *start_ptr = strstr(rxBuf, AT_CONNECT); + if (start_ptr != NULL) { + start_ptr += strlen(AT_CONNECT); /* point to next */ + +#if USE_QHTTPREAD_OK /* Use OK to frame response. */ + /* Find the trailing OK response */ + char *end_ptr = strstr(start_ptr, AT_OK); + if (end_ptr != NULL) { + int rlen = end_ptr - start_ptr; + + bg96.http_params.buf = start_ptr; + bg96.http_params.len = rlen; + /* terminate the buffer at the OK before returning to user */ + *end_ptr = 0; + /* now we are complete */ + status = MODEM_STATUS_OK; + } +#else /* Do NOT use OK to frame response. */ + /* Without OK to frame response, we just gather all bytes received. */ + bg96.http_params.buf = start_ptr; + bg96.http_params.len = strlen(start_ptr); + status = MODEM_STATUS_OK; +#endif + } + } + + return status; +} + +/* Send the URL after the CONNECT response or as a send + * handler for any command. + */ +static int send_qhttpurl_cmd(void) { + ESP_LOGI(TAG,"Send URL: %s\r\n", bg96.http_params.url); + uart_ifx_uart1_send_bytes((uint8_t *)bg96.http_params.url, strlen(bg96.http_params.url)); + + return MODEM_STATUS_OK; +} + +static int send_qhttpost_cmd(void) { + ESP_LOGI(TAG,"Send post_message: %s\r\n", bg96.http_params.post_message); + uart_ifx_uart1_send_bytes((uint8_t *)bg96.http_params.post_message, strlen(bg96.http_params.post_message)); + + return MODEM_STATUS_OK; +} + +#if 0 +static int handle_qhttppost_cmd(int status, const char *buf, int len) +{ + + /* Parse the response from the rx buffer */ + char *ptr = strstr((char*) buf, "+QHTTPPOST: 0,200,"); + if (ptr) + { + /*if that response exists then returns OK*/ + return MODEM_STATUS_OK; + } + else + { + /*if that response doesn't exists ----> then search if there's an CME error message*/ + ptr = strstr((char*) buf, "+CME ERROR:"); + if (ptr) + return MODEM_STATUS_ERROR; + else + return MODEM_STATUS_HTTP_POST_WAITRES; + } +} +#endif + +static int send_qmtpub_cmd(void) { + if (bg96.mqtt_params.pub_data && bg96.mqtt_params.pub_data_len > 0) { + ESP_LOGI(TAG,"publishing: [%s]\r\n", bg96.mqtt_params.pub_data); + uart_ifx_uart1_send_bytes((uint8_t*)bg96.mqtt_params.pub_data, bg96.mqtt_params.pub_data_len); + } else { + ESP_LOGW(TAG,"publishing null data\r\n"); + } + + char buf[1] = { + /* Send end of message character (ctrl+z) */ + CTRLZ, + //0 /* null-terminate for nice trace printing */ + }; + uart_ifx_uart1_send_bytes((uint8_t*)buf, sizeof(buf)); + + return MODEM_STATUS_OK; +} + +static int send_qfupl_cmd(void) { + char buf[1244] = + "-----BEGIN CERTIFICATE----- \ +MIIDazCCAlOgAwIBAgIUHMeQp85hERg7OQNJpIpRenYRRwkwDQYJKoZIhvcNAQEL \ +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM \ +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTAyMjUxMzUwNDdaFw0yMTAz \ +MjcxMzUwNDdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw \ +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB \ +AQUAA4IBDwAwggEKAoIBAQDFMVwzDmlbfaZ+oYQwva49wQ5MB2uTLlJ3APHmSOos \ +2bsRXzZf/68rsvZNF/khLoreoW/oi0lJAA/vi7oJuKvdZKovtrwiOk8z4uYEZR8D \ +I3Y7xnIen4jZMjUrGnAUpRd2NQOqt2kScTbSxXvS2GuDxt+nBh2PS34WWBWp1HP0 \ +4zztRYHFUfsnsc21/DMzL86uc2jZQq0fJEWu0qwxTDunIXn6tQYFDFgLasSmZv4d \ +/NSNcbKLYjvUoUnbdqdTEzeshGTBM+zLxakN+Zdxc88Bb83vvNa686lzPZF/LAXT \ +erEr+Yq4SxpQ5Hn9Nj5d5wxRQSDzmtns/jJmk1zhLrIZAgMBAAGjUzBRMB0GA1Ud \ +DgQWBBQrE77qN2Tw7ia5DMHBvgBmwr0zHDAfBgNVHSMEGDAWgBQrE77qN2Tw7ia5 \ +DMHBvgBmwr0zHDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBm \ +eqBPGy1TCNEDa3dMWgO9pK9zsuDGv+J9b2TiqnTvub5uW5z+qV2f7bBsuOZRWzv3 \ +4P9oGjgH+/HFpz+kSvBIRoEjjtX48LyD5H9ah0ALzQt5vXN7Q6QR4bWEtZFYlaOW \ +6Yd4v2DOtyBCa6g33Jf0Hw1OaYPzRmRsbqdB/Izg60ENZFXKCPFvHNIVP7IytEaz \ +gdghpaHdqGSFlbBxtfzk7vNqVoepX9kULaOhNdaDlMZE+nA+unHZc4zd0wDLVB+p \ +8fYZf6JfLRkQoZmdprsa0mtVOXLBwH6C2+lj8mxz6T9Or07LjxpkJB2dxGBiE6b9 \ +zkcHc7vljutOr+sbR1aK \ +-----END CERTIFICATE-----"; + int len = strlen(buf); + ESP_LOGI(TAG,"Buffer: [%d]\r\n", len); + uart_ifx_uart1_send_bytes((uint8_t*)buf, len); + + char cbuf[1] = { + /* Send end of message character (ctrl+z) */ + CTRLZ, + //0 /* null-terminate for nice trace printing */ + }; + uart_ifx_uart1_send_bytes((uint8_t*)cbuf, sizeof(cbuf)); + + return MODEM_STATUS_OK; +} + +/* --------------------------------------------------------------------------*/ +/* File read command processing. + */ + +static int setup_qflst_cmd(const void *params, char *buf, int len) { + /* Send the file name */ + snprintf(buf, len, AT_QFLST, bg96.file_params.file); + + return MODEM_STATUS_OK; +} + +static int handle_qflst_cmd(int status, const char *buf, int len) { + int retval = MODEM_STATUS_ERROR; + + bg96.file_params.file_size = 0; + + if (status == MODEM_STATUS_OK) { + /* Parse the file size from the received response */ + char *ptr = strstr((char*) rxBuf, ","); + if (ptr) { + ptr++; + bg96.file_params.file_size = (uint32_t) atoi(ptr); + ESP_LOGI(TAG,"Found file [%s] on modem. size=%ld\r\n", + bg96.file_params.file, bg96.file_params.file_size); + retval = MODEM_STATUS_OK; + } else { + ESP_LOGI(TAG,"Cannot find file size for [%s]\r\n", + bg96.file_params.file); + } + } else { + ESP_LOGI(TAG,"Cannot find downloaded file [%s] on modem\r\n", + bg96.file_params.file); + } + + return retval; +} + +static int handle_qfotadl_cmd(int status, const char* buf, int len){ + ESP_LOGI(TAG,"DFOTA handle_qfotadl_cmd "); + char* s = NULL; + char errorcode[2] ={0}; + int dfotaerr_idx = -1; + + s = strstr(rxBuf, "HTTPEND"); + if(s == NULL) + { + ESP_LOGI(TAG,"DFOTA package checking\\validation has been failed "); + return MODEM_STATUS_ERROR; + } + ESP_LOGI(TAG,"DFOTA ERROR %s",s); + strncpy(errorcode, &s[9], 1 /*only error index*/); + dfotaerr_idx = atoi(errorcode); + + ESP_LOGI(TAG,"DFOTA ERROR IDX %d",dfotaerr_idx); + + if(dfotaerr_idx != 0){ + + status = MODEM_STATUS_ERROR; + } + else{ + + status = MODEM_STATUS_OK; + } + + return status; +} + +static int setup_qfopen_cmd(const void *params, char *buf, int len) { + /* Send the file name */ + snprintf(buf, len, AT_QFOPEN, bg96.file_params.file); + + return MODEM_STATUS_OK; +} + +static int handle_qfopen_cmd(int status, const char *buf, int len) { + if (status == MODEM_STATUS_OK) { + /* Need the file handle */ + char *ptr = strstr((char*) rxBuf, "+QFOPEN:"); + if (ptr) { + ptr += strlen("+QFOPEN:"); + bg96.file_params.file_handle = atoi(ptr); + /* Initialize the file state */ + bg96.file_params.transferred_bytes = 0; + bg96.file_params.chunk_size = MODEM_FREAD_SIZE; + ESP_LOGI(TAG,"Opened file %s with handle: %d\r\n", + bg96.file_params.file, bg96.file_params.file_handle); + } else { + /* Can't get handle? Uh oh */ + status = MODEM_STATUS_ERROR; + ESP_LOGI(TAG,"Unable to get handle for %s\r\n", bg96.file_params.file); + } + + if (status == MODEM_STATUS_OK) { + /* Call the user callback */ + if (bg96.file_params.open_cb) { + /* User callback can return error, which will fail the whole read procedure */ + status = bg96.file_params.open_cb(bg96.file_params.file_size); + bg96.file_params.open_cb = 0; /* call only once */ + } + } + } + return status; +} + +/* BG96 response search strings */ +#define SFU_APP_MODEM_CONNECT_SEARCH "\r\nCONNECT 0\r\n" +#define SFU_APP_MODEM_CONNECT_PREFIX "\r\nCONNECT " + +/* Length of the CONNECT response string that we will support, including null-terminator */ +#define SFU_APP_MODEM_CONNECT_BUF_LEN sizeof("\r\nCONNECT 999999\r\n") + +/* Compute the expected number of bytes in the modem's response to an FREAD */ +static int expectedNumFREADResponseBytes(uint32_t len) { + int n = strlen(SFU_APP_MODEM_CONNECT_PREFIX); + + /* digits in CONNECT read_length field */ + n += (len < 10) ? 1 : (len < 100) ? 2 : (len < 1000) ? 3 : 4; + + n += 2; /* trailing \r\n */ + + n += len; + + n += strlen(AT_OK); + + ESP_LOGI(TAG,"FREAD expect response len=%d for %ld\r\n", n, len); + + return n; +} + +/* + * Process the FREAD response from the BG96 modem. + * + * The FREAD response must begin at offset 0 in the provided buffer. + * The potential length of the entire response + * (e.g. maximum size of provided buffer) must be specified in buf_len parameter. + * It is not required to know the exact response length as this API will figure it out safely. + * + * Return MODEM_STATUS_ERROR if anything fails including buffer not containing a CONNECT response + * or properly formed CONNECT response. + */ +static int ParseFREAD(const char *buf, int buf_len, /* input arguments: pointer to receive buffer and its length */ +const char **data_, int *data_len_) /* output arguments: pointer to recovered data and its length */ +{ + /* Deal with the buffer expecting a response from the BG96 of the form: + * \r\nCONNECT \r\n + * + * \r\nOK\r\n + * + * We cannot assume the caller's buffer is null-terminated anywhere. + * + * We must: + * a) extract the read_length parameter + * b) find the offset of the data in the buffer + * c) copy the data to a word-aligned buffer for processing the SFU_APP_Data(). + * + * The input buffer can be of any length. We will chunk it into our own buffer + * and call the underlying API as often as needed to consume all bytes in the + * caller's buffer before returning. + */ + + /* Temporary buffer to search for the connect string safely */ + char connect_buf[SFU_APP_MODEM_CONNECT_BUF_LEN]; + + /* Check arguments and basic buffer length sanity check */ + if (buf == NULL || buf_len < strlen(SFU_APP_MODEM_CONNECT_SEARCH)) { + return MODEM_STATUS_ERROR; + } + + /* First we need to build a null-terminated string to operate on, so we copy what + * could be the modem response into our buffer and terminate it. + * Maximum size will be limited to 6 digits, so that is + * "\r\nCONNECT 999999\r\n" = 18 characters. + */ + const int max_copy = SFU_APP_MODEM_CONNECT_BUF_LEN - 1; + /* Copy up to BUF LEN characters into temp buffer */ + const int init_copy = buf_len > max_copy ? max_copy : buf_len; + memcpy(connect_buf, buf, init_copy); + connect_buf[init_copy] = 0; /* null terminate for safe string operations on it */ + ESP_LOGI(TAG,"Copied %d bytes\r\n", init_copy); + + /* Find the length */ + const char *start_ptr = strstr(connect_buf, SFU_APP_MODEM_CONNECT_PREFIX); + if (start_ptr == NULL) { + return MODEM_STATUS_ERROR; + } + int offset = start_ptr - connect_buf; /* should be 0 but support other cases too */ + ESP_LOGI(TAG,"Offset=%d\r\n", offset); + const char *ptr = start_ptr + strlen(SFU_APP_MODEM_CONNECT_PREFIX); + int data_len = atoi(ptr); + if (data_len <= 0) { + return MODEM_STATUS_ERROR; + } + ESP_LOGI(TAG,"data_len=%d\r\n", data_len); + /* Offset of actual data immediately follows the \r\n */ + ptr = strstr(ptr, "\r\n"); + if (ptr == NULL) { + return MODEM_STATUS_ERROR; + } + ptr += 2; + + /* data offset into user buffer is specified finally here. */ + offset += (ptr - start_ptr); + ESP_LOGI(TAG,"Offset=%d\n", offset); + + /* check that the data length reported by CONNECT response is actually + * present in the buffer supplied. + */ + if (data_len > (buf_len - offset)) { + return MODEM_STATUS_ERROR; + } + + /* Return the actual data length and a pointer to it */ + *data_ = buf + offset; + *data_len_ = data_len; + + return MODEM_STATUS_OK; +} + +static int setup_qfread_cmd(const void *params, char *buf, int len) { + /* Send the number of bytes we read this call */ + bg96.file_params.read_size = bg96.file_params.chunk_size; + + /* Adjust to the actual number of bytes remaining */ + if ((bg96.file_params.file_size - bg96.file_params.transferred_bytes) + < bg96.file_params.chunk_size) { + bg96.file_params.read_size = bg96.file_params.file_size + - bg96.file_params.transferred_bytes; + } + + /* This is used in the handler to mark when we have received the response */ + bg96.file_params.expected_bytes = expectedNumFREADResponseBytes( + bg96.file_params.read_size); + + ESP_LOGI(TAG, + "setup qfread: handle:%d read_size:%d file_size:%ld chunk_size:%d xfered:%ld expect:%d\r\n", + bg96.file_params.file_handle, bg96.file_params.read_size, + bg96.file_params.file_size, bg96.file_params.chunk_size, + bg96.file_params.transferred_bytes, + bg96.file_params.expected_bytes); + + snprintf(buf, len, AT_QFREAD, bg96.file_params.file_handle, + bg96.file_params.read_size); + + return MODEM_STATUS_OK; +} + +/* This is how we are handling the QFREAD command response. + * We need to frame it somehow, and we do that by waiting for the expected number of + * bytes to arrive. But to have this handler called, we need to kick it with something, + * and we use the "\r\nOK\r\n" string expected at the end. We will, however, see this + * string embedded within the firmware image, so we must be careful not to end prematurely. + * + * Coming into this function, we will expect status = MODEM_STATUS_OK to indicate match for the \r\nOK\r\n string. + * then, we look for the number of bytes recevied and compare. If these match, we return STATUS_OK. + * If they do not, we must return TIMEOUT to keep looking. If the error condition was found, we have a + * similar problem with finding it in the stream, so we don't look for it and instead rely on the + * command timeout to abort. + * + * Also, we need to retry the FREAD command until the transfer is done, + * to do that we must return TIMEOUT always until it is done, then OK, or ERROR in any error state. + */ +static int handle_qfread_cmd(int status, const char *buf, int len) { + /* Pass ERROR and TIMEOUT through, only handle OK */ + if (status == MODEM_STATUS_OK) { + /* Found \r\nOK\r\n in the stream. Do we have the expected number of bytes? */ + if (rx_idx < bg96.file_params.expected_bytes) { + /* No, this was an embedded \r\nOK\r\n so continue scanning */ + status = MODEM_STATUS_TIMEOUT; + + /* Required to reset the scanner state (and update the index) for next OK */ + RxScanContextSuccess.RxScannerState = 0; + RxScanContextSuccess.RxScannerIdx = rx_idx; + + /* In the last UART event we got a numnber of bytes to bring it to rx_idx, + * and in that chunk we found OK but NOT expected number of bytes. This means + * we expect at least another UART event to kick the next scan. + */ + ESP_LOGI(TAG,"Found embedded OK, rx_idx=%d scanidx=%d\r\n", rx_idx, + RxScanContextSuccess.RxScannerIdx); + } else { + const char *data; + int data_len; + status = ParseFREAD(rxBuf, MODEM_RX_BUFFER_LEN, &data, &data_len); + if (status == MODEM_STATUS_OK) { + ESP_LOGI(TAG,"%d bytes file data read successfully\r\n", data_len); + /* Invoke user callback, which can also fail the transfer */ + if (bg96.file_params.data_cb) { + status = bg96.file_params.data_cb(data, data_len); + if (status == MODEM_STATUS_OK) { + /* Increment the number of bytes transfered only after success on user callback */ + bg96.file_params.transferred_bytes += + bg96.file_params.read_size; + ESP_LOGI(TAG,"%d bytes processed\r\n", data_len); + } else { + ESP_LOGI(TAG,"%d bytes failed to process.\r\n", data_len); + } + } + } else { + ESP_LOGI(TAG,"Failed to parse FREAD response\r\n"); + } + + ESP_LOGI(TAG,"Transfer %ld/%ld %ld%% complete\r\n", + bg96.file_params.transferred_bytes, + bg96.file_params.file_size, + (bg96.file_params.transferred_bytes * 100) + / bg96.file_params.file_size); + + /* If status is OK at this point, we must set it to TIMEOUT to continue reading the file */ + if (status == MODEM_STATUS_OK) { + /* If we have received all bytes, we can terminate the transfer */ + if (bg96.file_params.transferred_bytes + < bg96.file_params.file_size) { + bg96.cmd_repeat = true; + } + } + } + } + /* Must return TIMEOUT until all bytes have been tranferred, unless ERROR to signal + * transfer failed and abort the command sequence and OK to signal all done. + */ + return status; +} + +static int setup_qfclose_cmd(const void *params, char *buf, int len) { + /* Send the file handle */ + snprintf(buf, len, AT_QFCLOSE, bg96.file_params.file_handle); + + return MODEM_STATUS_OK; +} + +/* Command table + * Allows command chaining + * Each entry has a response process function + */ + +#define MODEM_DEFAULT_CMD_TIMEOUT_MS 5000 +#define MODEM_DEFAULT_CMD_RETRIES 5 + +/* Align with timeout given to modem in AT_QMTCFG_TIMEOUT */ +#define MODEM_MQTT_CONN_CMD_TIMEOUT_MS 60000 + +typedef enum { + AT_CMD_IDX_ECHO_OFF = 0, + AT_CMD_IDX_IFC, + AT_CMD_IDX_IDENT, +#if MODEM_MODEM_FWVER_FROM_QGMR + AT_CMD_IDX_QGMR, +#endif + AT_CMD_IDX_IMEI, + AT_CMD_IDX_ICCID, //AT_QCCID to get ICCID +#if FAKE_NETWORK + AT_CMD_IDX_SETCCLK, +#endif + AT_CMD_IDX_CTZU, + AT_CMD_IDX_CPIN, + AT_CMD_IDX_CGDCONT_ALT, + AT_CMD_IDX_CFUN_OFF, + AT_CMD_IDX_CFUN, + AT_CMD_IDX_COPS, + AT_CMD_IDX_QCSQ, + AT_CMD_IDX_QCFG_IOTMODE, + AT_CMD_IDX_CCLK, + AT_CMD_IDX_QICSGP, + AT_CMD_IDX_QIACT, + + //AT_CMD_IDX_QMTCFG_SSL, + AT_CMD_IDX_QFDEL, + //AT_CMD_IDX_QFUPL, + /*AT_CMD_IDX_QFUPL_SEND, + AT_CMD_IDX_QSSLCFG_CACERT, + AT_CMD_IDX_QSSLCFG_SECLEVEL, + AT_CMD_IDX_QSSLCFG_SSLVER, + AT_CMD_IDX_QSSLCFG_CIPH, + AT_CMD_IDX_QSSLCFG_IGNRTIME, + AT_CMD_IDX_QMTCFG_WILL, + AT_CMD_IDX_QMTCFG_TIMEOUT, + AT_CMD_IDX_QMTCFG_KEEPALIVE, + AT_CMD_IDX_QMTCFG_VERSION, + AT_CMD_IDX_QMTOPEN, + AT_CMD_IDX_QMTCONN_CID,*/ + + //AT_CMD_IDX_QMTPUB, + //AT_CMD_IDX_QMTPUB_SEND, /* not an actual command, part 2 of the QMTPUB operation */ + //AT_CMD_IDX_QMTCLOSE, + + AT_CMD_IDX_QHTTPCFG_CTX, + AT_CMD_IDX_QHTTPCFG_RHD, + AT_CMD_IDX_QHTTPCFG_RHD_0, + AT_CMD_IDX_QHTTPURL, + AT_CMD_IDX_QHTTPURL_SET, + AT_CMD_IDX_QHTTPPOST, + AT_CMD_IDX_QHTTPPOST_SET, + AT_CMD_IDX_QHTTPGET, + AT_CMD_IDX_QHTTPREAD, + AT_CMD_IDX_QHTTPREADFILE, + + AT_CMD_IDX_QPOWD, + + AT_CMD_IDX_QFOTADL, + AT_CMD_IDX_QFOTADL_WAIT_HTTPEND, + AT_CMD_IDX_QFOTADL_WAIT_END, + + //AT_CMD_IDX_QFLDS, + AT_CMD_IDX_QFLST, + AT_CMD_IDX_QFOPEN, + AT_CMD_IDX_QFREAD, + AT_CMD_IDX_QFCLOSE, + +// AT_CMD_IDX_QENG_SVCELL, //Partha - March 11, 2022 +// AT_CMD_IDX_QENG_NBCELL //Partha - March 11, 2022 +} modem_at_cmd_idx_t; + +static const modem_cmd_t cmd_table[] = + { + + /* ATE0 */ + { AT_ECHO_OFF, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 }, + + /* IFC */ + { AT_IFC, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 }, + + /* ATI */ + { ATI, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES, + 0, handle_ati_cmd, 0, 0 }, + +#if MODEM_MODEM_FWVER_FROM_QGMR + /* QGMR */ + { AT_QGMR, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, handle_qgmr_cmd, 0, 0 }, +#endif + + /* CGSN */ + { AT_CGSN, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, handle_imei_cmd, 0, 0 }, + /* QCCID */ + { AT_QCCID, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES, 0, handle_iccid_cmd, 0, 0 }, + +#if FAKE_NETWORK + /* SETCCLK */ + /* Set the modem's RTC for testing without network + * Note: timezone offset is in units of 1/4 hour, so -20 is -5 hours (EST timezone) + * Below time is local to EST. It should be converted to GMT epoch and that is used + * as the system-wide time base for SP1/2 publish messages. + */ + { "AT+CCLK=\"20/10/04,15:43:10-20\"\r\n", AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 }, +#endif + + /* Network connection */ + + /* CTZU */ + { AT_CTZU, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 }, + + /* CPIN */ + { AT_CPIN, AT_OK, 0, 5000, MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 }, + + /* CGDCONT_ALT */ + { AT_CGDCONT, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 }, + + /* CFUN_OFF */ + { AT_CFUN_OFF, AT_OK, AT_IND_CME_ERROR, 15000, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 }, + + /* CFUN */ + { AT_CFUN_FULL, AT_OK, AT_IND_CME_ERROR, 15000, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 }, + + /* COPS + * Wait for timeout and use handler function to look for the operators we want to connect with. + * Retry for up to 2 minutes. + */ + // { AT_COPS, 0, 0, 1000, 120, 0, handle_cops_cmd, 0, 0 }, + { AT_COPS, AT_OK, 0, 2000, 60, 0, handle_cops_cmd, 0, 0 }, + /* QCSQ */ + { AT_QCSQ, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + 20, 0, handle_qcsq_cmd, 0, 0 }, + + /* QCFG_IOTMODE */ + { AT_QCFG_IOTOPMODE, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 }, + + /* CCLK */ + { AT_CCLK, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, handle_cclk_cmd, 0, 0 }, + + /* PDP context commands: + * CGDCONT sets it up + * CGACT activates it + * OR + * QICSGP sets it up + * QIACT activates it + * QI commands are in the TCP(IP) AT command manual, while CG are standard AT commands. + * https://forums.quectel.com/t/what-is-the-difference-between-cgdcont-and-qicsgp/152 + * "When using module’s embedded stacks like TCP and UDP protocol, you need to use AT+QICSGP to configure the APN. + * While using PPP, you need to use AT+CGDCONT to configure the APN." + * I also suspect that these commands need to be run before MQTT communications. We are not using PPP. + * They are included in the network connection sequence. + */ + + /* QICSGP */ + { AT_QICSGP, AT_OK, AT_ERROR, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 }, + + /* QIACT + * Maximum response time 150 seconds. + */ + { AT_QIACT, 0, 0, 5000, MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 }, + + /* QSSLCFG_CACERT */ + /*{ AT_QMTCFG_SSL, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/ + /* MQTT connection */ + { AT_QFDEL, 0, 0, 5000, MODEM_DEFAULT_CMD_RETRIES, 0, + handle_qfdel_cmd, 0, 0 }, + + /* MQTT connection */ + /*{ AT_QFUPL, "CONNECT", AT_IND_CME_ERROR, 5000, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/ + /* Send data then wait for indication. */ + //{ 0 /* no command */, AT_OK, AT_IND_CME_ERROR, 180 * 1000, + // 0 /* no retries */, send_qfupl_cmd, 0, 0, 0 }, + + /* QSSLCFG_CACERT */ + /*{ AT_QSSLCFG_CACERT, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/ + + /* QMTCFG_WILL */ + /*{ AT_QSSLCFG_SECLEVEL, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/ + + /* QMTCFG_WILL */ + /*{ AT_QSSLCFG_SSLVER, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/ + + /* QMTCFG_WILL */ + /*{ AT_QSSLCFG_CIPH, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/ + + /* QMTCFG_WILL */ + /*{ AT_QSSLCFG_IGNRTIME, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/ + + /* QMTCFG_WILL */ + /*{ AT_QMTCFG_WILL, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/ + /* AT_QMTCFG_TIMEOUT */ + /*{ AT_QMTCFG_TIMEOUT, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/ + + /* AT_QMTCFG_KEEPALIVE */ + /*{ AT_QMTCFG_KEEPALIVE, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/ + + /* AT_QMTCFG_VERSION */ + /*{ AT_QMTCFG_VERSION, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/ + + /* QMTOPEN */ + /*{ AT_QMTOPEN, AT_IND_QMTOPEN, 0, MODEM_MQTT_CONN_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, setup_qmtopen_cmd, 0 },*/ + + /* QMTCONN_CID */ + /*{ AT_QMTCONN_CID, AT_IND_QMTCONN, 0, + MODEM_MQTT_CONN_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES, + 0, 0, setup_qmtconn_cmd, 0 },*/ + + /* MQTT publish */ + + /* QMTPUB + * Publish messages. + * AT+QMTPUB=0,0,0,0,"topic/pub" + * >This is test data, hello MQTT. //After receiving >, input data "This is test data, hello MQTT." and + * then send it. The maximum length of the data is 1548 bytes and the + * data that beyond 1548 bytes will be omitted. After inputting data, + * tap Ctrl+Z to send. + * OK + * +QMTPUB: 0,0,0 + * + * Note: need to wait for ">" from modem after command is sent before sending our data. + * Publish is then split into two operations: AT_QMTPUB command which waits for ">", + * then send the data and waits for the indication. + */ + /*{ AT_QMTPUB, ">", AT_IND_CME_ERROR, 5000, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, setup_qmtpub_cmd, 0 },*/ + /* Send data then wait for indication. */ + //{ 0 /* no command */, AT_IND_QMTPUB, AT_IND_CME_ERROR, 180 + // * 1000, 0 /* no retries */, send_qmtpub_cmd, 0, 0, 0 }, + + /* QMTCLOSE + * Note: + * The QMTCLOSE: indication does not appear within a 10s timeout. The manual has no specification on how long + * this might take. Instead, we wait for the command's OK response and continue as if the connection + * had closed. + */ + //{ AT_QMTCLOSE, AT_OK /* AT_IND_QMTCLOSE */, 0, + // MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES, + // 0, 0, 0, 0 }, + + /* HTTP */ + + /* QHTTPCFG_CTX */ + { AT_QHTTPCFG_CTX, AT_OK, AT_IND_CME_ERROR, + MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES, + 0, 0, 0, 0 }, + + /* QHTTPCFG_RHD */ + { AT_QHTTPCFG_RHD, AT_OK, AT_IND_CME_ERROR, + MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES, + 0, 0, 0, 0 }, + + { AT_QHTTPCFG_RHD_0, AT_OK, AT_IND_CME_ERROR, + MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES, + 0, 0, 0, 0 }, + + /* QHTTPURL + * Wait for CONNECT + */ + { AT_QHTTPURL, AT_CONNECT, AT_IND_CME_ERROR, + MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES, + 0, 0, setup_qhttpurl_cmd, 0 }, + /* then send URL then wait for OK */ + { 0, AT_OK, AT_IND_CME_ERROR, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, send_qhttpurl_cmd, 0, 0, 0 }, + + /* QHTTPPOST + * Wait for CONNECT + */ + { AT_QHTTPPOST, AT_CONNECT, AT_IND_CME_ERROR, + 80000, MODEM_DEFAULT_CMD_RETRIES, + 0, 0, setup_qhttppost_cmd, 0 }, + /* then send POST-Message then wait for OK */ + { 0, AT_HTTPPOST_SUCCESS, AT_IND_CME_ERROR, 60000, + MODEM_DEFAULT_CMD_RETRIES, send_qhttpost_cmd, 0, 0, 0 }, + + /* QHTTPGET + * If is 0, then wait for +QHTTPGET indication. Then use QHTTPREAD to get the response. + * If is 1, then wait for CONNECT then OK to frame response from this command + * Default appears to be 0. + */ + { AT_QHTTPGET, AT_QHTTPGET_SUCCESS, AT_IND_CME_ERROR, 80000, + 0 /* no retries */, 0, 0, 0, 0 }, + + /* QHTTPREAD + * Look for OK response before +QHTTPREAD indication to frame the GET content. */ +#if USE_QHTTPREAD_OK + /* OK, +QHTTPREAD response expected. Timeout equal to QHTTPREAD command timeout. */ + { AT_QHTTPREAD, AT_QHTTPREAD_SUCCESS, /*AT_IND_CME_ERROR*/ 0, 80000, + 3, 0, handle_qhttpread_cmd, 0, 0 }, +#else + /* No OK Response expected, wait for timeout then parse. */ + { AT_QHTTPREAD, 0, 0, 3000, MODEM_DEFAULT_CMD_RETRIES, 0, handle_qhttpread_cmd, 0, 0 }, +#endif + + /* QHTTPREADFILE + * Look for OK response to frame the GET content. */ + { AT_QHTTPREADFILE, AT_QHTTPREADFILE_SUCCESS, AT_IND_CME_ERROR, + 5*60*1000, MODEM_DEFAULT_CMD_RETRIES, 0, 0, + setup_qhttpreadfile_cmd, 0 }, + + /* QPOWD + * This command default is "1" - normal shutdown. + It is recommended to execute AT+QPOWD command to power off the module, as it is the safest and best + way. This procedure is realized by letting the module log off from the network and allowing the software to + enter a secure and safe data state before disconnecting the power supply. + After sending AT+QPOWD, do not enter any other AT commands. When the command is executed + successfully, the module will output POWERED DOWN and set the STATUS pin as low to enter power-off + state. In order to avoid data loss, it is suggested to wait for 1s at least to disconnect the power supply after + the STATUS pin is set as low and the URC POWERED DOWN is outputted. If POWERED DOWN cannot + be received within 65s, the power supply shall be disconnected compulsorily. + * + * Our procedure is to enter command and wait for standard OK to complete command sequence, + * then we monitor STATUS pin for the 65 seconds. If it is still up after that we + * can try PWRKEY and start waiting again. + */ + { AT_QPOWD, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 }, + + /* Send QFOTADL with URL, wait for OK */ + { AT_QFOTADL, AT_OK, AT_IND_CME_ERROR, + MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES, + 0, 0, setup_qfotadl_cmd, 0 }, + /* then wait 25 mins for process to complete with "END" + * Need multiple phases: download from server phase: HTTPEND + * Update phase: END. If server download didn't complete, fail it. + */ + { 0, "\"HTTPEND\",0", "\"HTTPEND\",", 25 * 60 * 1000, 0, 0, handle_qfotadl_cmd,0, 0 }, + { 0, "\"END\"", AT_IND_CME_ERROR, 25 * 60* 1000, 0, 0, 0, 0, 0 }, + + + //{ AT_QFLDS, AT_OK, AT_IND_CME_ERROR, MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES, 0, handle_flds_cmd, 0, 0 }, + /* File transfer procedure: + * 1. Look for specific file, get file size from response. setup, handler : set size + * 2. Open file. handler: success : get file handle, call user open cb, init transfer state. + * 3 read file repeatedly until bytes consumed. Use handler to control this as a retry. + */ + { AT_QFLST, AT_OK, AT_IND_CME_ERROR, + MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES, + 0, handle_qflst_cmd, setup_qflst_cmd, 0 }, { AT_QFOPEN, + AT_OK, AT_IND_CME_ERROR, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, handle_qfopen_cmd, + setup_qfopen_cmd, 0 }, + /* Look for OK response to frame FREAD data. This is dangerous by itself because \r\nOK\r\n is likely located within + * the byte stream. To make this work, we use the handler in combination to check that we have found \r\nOK\r\n AND the + * expected number of bytes. + */ + { AT_QFREAD, AT_OK /* See note */, + 0 /* do not look for error response */, + 10000 /* timeout */, 0 /* no retries */, 0, + handle_qfread_cmd, setup_qfread_cmd, 0 }, { AT_QFCLOSE, + AT_OK, AT_IND_CME_ERROR, MODEM_DEFAULT_CMD_TIMEOUT_MS, + MODEM_DEFAULT_CMD_RETRIES, 0, 0, setup_qfclose_cmd, 0 } + + }; + +#define MODEM_NUM_CMDS (sizeof(cmd_table) / sizeof(cmd_table[0])) + +static int add_cmd(const modem_cmd_t *cmd) { + int retval = MODEM_STATUS_ERROR; + if (cmd) { + if (bg96.cmd_num < MODEM_MAX_SEQUENCE) { + bg96.cmd_list[bg96.cmd_num++] = cmd; + retval = MODEM_STATUS_OK; + } + } + return retval; +} + +int modem_network_connect(modem_op_cb_t cb) { + bg96.cmd_num = 0; + + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_CTZU])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_CPIN])) { + return MODEM_STATUS_ERROR; + } + /*if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_CGDCONT_ALT])) { + return MODEM_STATUS_ERROR; + }*/ + /*if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_CFUN_OFF])) { + return MODEM_STATUS_ERROR; + }*/ + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_CFUN])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_COPS])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QCSQ])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QCFG_IOTMODE])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFDEL])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QCFG_IOTMODE])) { + return MODEM_STATUS_ERROR; + } + /*if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QICSGP])) { + return MODEM_STATUS_ERROR; + }*/ +#if FAKE_NETWORK +#else + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QIACT])) { + return MODEM_STATUS_ERROR; + } +#endif + + bg96.cmd_complete_cb = cb; + + int retval = start_cmd_sequence(); + + if (retval == MODEM_STATUS_OK) { + ESP_LOGI(TAG,"Starting connect network sequence %d\r\n", bg96.cmd_num); + } + return retval; + +} + +#if 0 +int modem_mqtt_connect(modem_op_cb_t cb, const char *host, int port, + const char *client_id) //, const char* jwt) +{ + bg96.cmd_num = 0; + /*if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTCFG_SSL])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFDEL])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFUPL])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFUPL_SEND])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QSSLCFG_CACERT])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QSSLCFG_SECLEVEL])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QSSLCFG_SSLVER])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QSSLCFG_CIPH])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QSSLCFG_IGNRTIME])) { + return MODEM_STATUS_ERROR; + }*/ + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTCFG_WILL])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTCFG_TIMEOUT])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTCFG_KEEPALIVE])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTCFG_VERSION])) { + return MODEM_STATUS_ERROR; + } + + /* Get signal strength at this location, more reliable than right after COPS. */ + /*if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QCSQ])) { //Not needed - Partha March 14, 2022 + return MODEM_STATUS_ERROR; + }*/ + + /* Also get time here, may be more reliable. Time is needed before the first publish, below. */ + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_CCLK])) { + return MODEM_STATUS_ERROR; + } +#if FAKE_NETWORK + /*Don't do the actual MQTT connection */ + //For testing the new WF1 and WF2 messages - Partha - March 16, 2022 + + +#else + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTOPEN])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTCONN_CID])) { + return MODEM_STATUS_ERROR; + } + + // //Get Serving cell data - Partha - March 12, 2022 + // if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QENG_SVCELL])) { + // return MODEM_STATUS_ERROR; + // } + // + // //GEt neighbout cell list - Partha - March 12, 2022 + // if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QENG_NBCELL])) { + // return MODEM_STATUS_ERROR; + // } + +#endif + + bg96.cmd_complete_cb = cb; + bg96.mqtt_params.host = host; + bg96.mqtt_params.port = port; + //bg96.mqtt_params.jwt = jwt; + bg96.mqtt_params.client_id = client_id; + + int retval = start_cmd_sequence(); + + if (retval == MODEM_STATUS_OK) { + ESP_LOGI(TAG,"Starting connect mqtt sequence %d\r\n", bg96.cmd_num); + } + return retval; +} + +/* Publish data to the specific topic */ +int modem_mqtt_publish(modem_op_cb_t cb, const char *topic, const char *data, + int len) { + bg96.cmd_num = 0; + + if (!topic || !data) { + return MODEM_STATUS_ERROR; + } +#if FAKE_NETWORK + /*Don't do the actual MQTT connection */ +#else + /* Two-step operation, see QMTPUB command description. */ + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTPUB])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTPUB_SEND])) { + return MODEM_STATUS_ERROR; + } +#endif + bg96.cmd_complete_cb = cb; + bg96.mqtt_params.pub_topic = topic; + bg96.mqtt_params.pub_data = data; + bg96.mqtt_params.pub_data_len = len; + + int retval = start_cmd_sequence(); + + if (retval == MODEM_STATUS_OK) { + ESP_LOGI(TAG,"Starting mqtt publish %d bytes to %s\r\n", len, topic); + } + return retval; +} + +int modem_mqtt_close(modem_op_cb_t cb) { + bg96.cmd_num = 0; + +#if FAKE_NETWORK + /*Don't do the actual MQTT connection */ +#else + /* + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTDISC])) { + return MODEM_STATUS_ERROR; + } + */ + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTCLOSE])) { + return MODEM_STATUS_ERROR; + } +#endif + bg96.cmd_complete_cb = cb; + + int retval = start_cmd_sequence(); + + if (retval == MODEM_STATUS_OK) { + ESP_LOGI(TAG,"Starting mqtt close\r\n"); + } + return retval; +} +#endif +/* Setup the HTTP context */ +int modem_http_setup(modem_op_cb_t cb) { + bg96.cmd_num = 0; + + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPCFG_CTX])) { + return MODEM_STATUS_ERROR; + } + + bg96.cmd_complete_cb = cb; + + int retval = start_cmd_sequence(); + + if (retval == MODEM_STATUS_OK) { + ESP_LOGI(TAG,"Starting http setup\r\n"); + } + return retval; +} + +/* Make an HTTP GET request and store result in supplied buffer */ +int modem_http_get_to_buf(modem_op_cb_t cb, const char *url) { + bg96.cmd_num = 0; + +#if FAKE_NETWORK + /*Don't do the actual HTTP connection */ +#else + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPCFG_RHD_0])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPURL])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPURL_SET])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPGET])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPREAD])) { + return MODEM_STATUS_ERROR; + } +#endif + bg96.cmd_complete_cb = cb; + bg96.http_params.url = url; + + int retval = start_cmd_sequence(); + + if (retval == MODEM_STATUS_OK) { + ESP_LOGI(TAG,"Starting http get to buffer from url: %s\r\n", url); + } + return retval; +} + +/* Make an HTTP GET request and store result to file on modem file system. + * Use modem_read_from_file to get contents. + */ +int modem_http_get_to_file(modem_op_cb_t cb, const char *url, const char *file) { + bg96.cmd_num = 0; +#if FAKE_NETWORK + /*Don't do the actual HTTP connection */ +#else + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPURL])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPURL_SET])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPGET])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPREADFILE])) { + return MODEM_STATUS_ERROR; + } +#endif + bg96.cmd_complete_cb = cb; + bg96.http_params.url = url; + bg96.http_params.file = file; + + int retval = start_cmd_sequence(); + + if (retval == MODEM_STATUS_OK) { + ESP_LOGI(TAG,"Starting http get to file from url: %s file: %s\r\n", + bg96.http_params.url, bg96.http_params.file); + } + return retval; +} + +int modem_http_post_message(modem_op_cb_t cb, const char *url, const char *message) { + bg96.cmd_num = 0; +#if FAKE_NETWORK + /*Don't do the actual HTTP connection */ +#else + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPCFG_RHD])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPURL])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPURL_SET])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPPOST])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPPOST_SET])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPREAD])) { + return MODEM_STATUS_ERROR; + } +#endif + bg96.cmd_complete_cb = cb; + bg96.http_params.url = url; + bg96.http_params.post_message = message; + + int retval = start_cmd_sequence(); + + if (retval == MODEM_STATUS_OK) { + ESP_LOGI(TAG,"Starting http-post to send the json-formatted message");//: %s to url: %s \r\n", + //bg96.http_params.post_message, bg96.http_params.url); + } + return retval; +} + +int modem_read_from_file(modem_op_cb_t cb, const char *file, + modem_read_from_file_open cbOpen, modem_read_from_file_data cbData) { + bg96.cmd_num = 0; +#if FAKE_NETWORK + /*Don't do the actual read from file connection */ +#else + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFLST])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFOPEN])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFREAD])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFCLOSE])) { + return MODEM_STATUS_ERROR; + } +#endif + bg96.cmd_complete_cb = cb; + /* setup handler for QFLST uses this field for file name */ + bg96.file_params.file = file; + bg96.file_params.open_cb = cbOpen; + bg96.file_params.data_cb = cbData; + + int retval = start_cmd_sequence(); + + if (retval == MODEM_STATUS_OK) { + ESP_LOGI(TAG,"Starting read from file: %s\r\n", file); + } + return retval; +} + +/* Update modem command sequence. */ +int modem_update_modem(modem_op_cb_t cb, const char *url) { + bg96.cmd_num = 0; + ESP_LOGI(TAG,"cmd-> AT_CMD_IDX_QFOTADL"); + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFOTADL])) { + return MODEM_STATUS_ERROR; + } + + ESP_LOGI(TAG,"cmd-> AT_CMD_IDX_QFOTADL_WAIT_HTTPEND"); + if (MODEM_STATUS_ERROR + == add_cmd(&cmd_table[AT_CMD_IDX_QFOTADL_WAIT_HTTPEND])) { + return MODEM_STATUS_ERROR; + } + ESP_LOGI(TAG,"cmd-> AT_CMD_IDX_QFOTADL_WAIT_END"); + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFOTADL_WAIT_END])) { + return MODEM_STATUS_ERROR; + } + + bg96.cmd_complete_cb = cb; + bg96.http_params.url = url; + + int retval = start_cmd_sequence(); + + if (retval == MODEM_STATUS_OK) { + ESP_LOGI(TAG,"Started modem update sequence\r\n"); + } + return retval; +} + +/* Internal command sequence */ +static int shutdown_sequence(modem_op_cb_t cb) { + bg96.cmd_num = 0; + +#if 1 // Set 0 for testing error handling + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QPOWD])) { + return MODEM_STATUS_ERROR; + } +#endif + bg96.cmd_complete_cb = cb; + + int retval = start_cmd_sequence(); + + if (retval == MODEM_STATUS_OK) { + ESP_LOGI(TAG,"Starting shutdown sequence\r\n"); + } + return retval; +} + +/* Functions ----------------------------------------------------------------*/ +extern bool uart1_rx_needed; + +static int start_rx_stream() { + memset(&RxScanContextSuccess, 0, sizeof(RxScanContextSuccess)); + memset(&RxScanContextError, 0, sizeof(RxScanContextError)); + memset(rxBuf, 0, MODEM_RX_BUFFER_LEN); + rx_idx = 0; + + uart1_rx_needed = true; + //bg96.poll_uart = true; + + return MODEM_STATUS_OK; +} + +static void stop_rx_stream() { + uart1_rx_needed = false; + bg96.poll_uart = false; +} + +/* Scan RX buffer with len characters in it for given string. + * Different from strstr, this function won't terminate on finding 0 in rxBuf. + * This handles modem's startup \0RDY indication. + */ +int scan_rx_buffer(int num_bytes, const char *str, ScanContext_t *scanContext, + const char *buf, int len) { + int str_len = strlen(str); + while (num_bytes > 0) { + /* Look for consecutive sequence of characters from str in buffer */ + if (buf[scanContext->RxScannerIdx] + == str[scanContext->RxScannerState]) { + scanContext->RxScannerState++; + if (scanContext->RxScannerState == str_len) { + return MODEM_STATUS_OK; + } + } else if (buf[scanContext->RxScannerIdx] == str[0]) { + scanContext->RxScannerState = 1; + if (scanContext->RxScannerState == str_len) { + return MODEM_STATUS_OK; + } + } else { + scanContext->RxScannerState = 0; + } + + if (++scanContext->RxScannerIdx == len) { + /* wrap the scanner around */ + scanContext->RxScannerIdx = 0; + } + + num_bytes--; + } + return MODEM_STATUS_ERROR; +} + +/* ------------------------------------------------------------------------- */ + +static const char* state_str(modem_state_t s) { + switch (s) { + case MODEM_STATE_INIT: + return "INIT"; + case MODEM_STATE_READY: + return "READY"; + case MODEM_STATE_WAIT_POWERUP: + return "WAIT_POWERUP"; + case MODEM_STATE_WAIT_STARTUP: + return "WAIT_STARTUP"; + case MODEM_STATE_WAIT_STARTUP_RDY: + return "WAIT_STARTUP_RDY"; + case MODEM_STATE_WAIT_POWERDOWN: + return "WAIT_POWERDOWN"; + case MODEM_STATE_CMD: + return "CMD"; + default: + return ""; + } +} + +static void transition(modem_state_t state_to) { + ESP_LOGI(TAG,"%s->%s\n", state_str(bg96.state), state_str(state_to)); + bg96.state = state_to; +} + +static int transition_ready(void) { + transition(MODEM_STATE_READY); + if (bg96.start_cb) { + bg96.start_cb(MODEM_STATUS_OK); + bg96.start_cb = 0; /* only send notification once */ + } + return MODEM_STATUS_OK; +} + +/* Monitor the stream periodically */ +static int monitor_rx_stream(const char *success, const char *error) { + /* Copy bytes from the circular buffer to the receive buffer for processing. + * The goal is to look for complete success or error strings + */ + uint16_t prev_rx_idx = rx_idx; + + int bytesCopied = board_uart_rx_stream_poll((uint8_t*) rxBuf, &rx_idx, sizeof(rxBuf)); + + if(bytesCopied > 0) + { + //ESP_LOGI(TAG,"Got %d bytes %d\r\n", bytesCopied, rx_idx); + //ESP_LOGI(TAG, "Uart RX: %s", rxBuf); + if(strstr(rxBuf,"+QIND: \"FOTA\",\"DOWNLOADING\"")) + { + + rx_idx = 0; + //ESP_LOGI(TAG, "Unecessary DFOTA DOWNLOADING LOGS"); + ESP_LOGI(TAG, "%s",rxBuf); + } + else + { + ESP_LOGI(TAG, "Uart RX:"); + } + + } + else + { + rx_idx = prev_rx_idx; + } + + if (rx_idx != 0) // if something in the buffer + { + /* Check for success and error strings in buffer */ + if (success && scan_rx_buffer(bytesCopied, success, &RxScanContextSuccess, rxBuf, MODEM_RX_BUFFER_LEN - 1) == MODEM_STATUS_OK) + { + ESP_LOGI(TAG, "VERIFIED RESPONSE: %s", success); + return MODEM_STATUS_OK; + } + + if (error && scan_rx_buffer(bytesCopied, error, &RxScanContextError, rxBuf, MODEM_RX_BUFFER_LEN - 1) == MODEM_STATUS_OK) + { + return MODEM_STATUS_ERROR; + } + } + + if(strstr(rxBuf,success)) + { + ESP_LOGI(TAG, "VERIFIED RESPONSE: %s", success); + return MODEM_STATUS_OK; + } + + if(strstr(rxBuf,error)) + { + return MODEM_STATUS_ERROR; + } + + + //ESP_LOGI(TAG, "Timeout .... didn't fined %s or %s", success,error); + + + + /* So far found nothing that matches */ + return MODEM_STATUS_TIMEOUT; +} + +/* Send the current command in the sequence */ +static int send_command(const modem_cmd_t *cmd) // const char* cmd, int timeout_ms) +{ + int retval = MODEM_STATUS_OK; + + /*Checking the passed command(check it it's zero or not)*/ + if (cmd->response_match_str) { + /* Start the receive stream */ + retval = start_rx_stream(); + } else { + /*return MODEM_STATUS_OK */ + retval = MODEM_STATUS_OK; + } + + if (MODEM_STATUS_OK == retval) { + if(ESP_OK == esp_timer_start_once(modem_timer, 1000*(cmd->wait_ms))) + { + retval = MODEM_STATUS_OK; + } + if (MODEM_STATUS_OK == retval) { + const char *cmd_str = cmd->cmd_str; + /* Invoke setup function */ + if (cmd->setup) { + if (MODEM_STATUS_OK + == cmd->setup(cmd->params, txBuf, sizeof(txBuf))) { + cmd_str = txBuf; + } + } + if (cmd_str) { + /* trace the command sent to modem */ + ESP_LOGI(TAG,"SEND: %s",cmd_str); + uart_ifx_uart1_send_bytes((uint8_t *)cmd_str, strlen(cmd_str)); + } + /* Invoke the optional command-specific send function */ + if (cmd->send) { + cmd->send(); + } + + transition(MODEM_STATE_CMD); + } else { + stop_rx_stream(); + } + } + + return retval; +} + +/* Run a command sequence + * Return index of last command the suceeded, + * if it matchs the number of commands-1 then it completed. + */ + +static int start_cmd_sequence(void) { +#if FAKE_COMMS + /* Don't issue any commands */ + if (bg96.cmd_complete_cb) { + bg96.cmd_complete_cb(MODEM_STATUS_OK); + bg96.cmd_complete_cb = 0; + } + return MODEM_STATUS_OK; +#else + int retval = MODEM_STATUS_OK; + + bg96.cmd_sequence = 0; + + /* Submit each command to the command state */ + if (bg96.cmd_sequence < bg96.cmd_num) { + const modem_cmd_t *cmd = bg96.cmd_list[bg96.cmd_sequence]; + if (cmd) { + bg96.cmd_retries = 0; + retval = send_command(cmd); + } + } else { + /* Sequence wasn't started for whatever reason, indicate to caller + * that it is done. + */ + if (bg96.cmd_complete_cb) { + bg96.cmd_complete_cb(MODEM_STATUS_OK); + bg96.cmd_complete_cb = 0; + } + } + return retval; +#endif +} + +static int end_cmd_sequence(int status) { + stop_rx_stream(); + + ESP_LOGI(TAG,"Ending command sequence : %d %d\r\n", bg96.is_startup, status); + + /* end a command sequence + * notify callback with result + */ + if (bg96.cmd_complete_cb) { + bg96.cmd_complete_cb(status); + bg96.cmd_complete_cb = 0; + } + + if (bg96.is_startup) { + bg96.is_startup = false; + /* First time in ready */ + transition_ready(); + } else { + /* Go back to ready - only if we were in the CMD state. + * This is to support cases where callbacks modify the state + */ + if (bg96.state == MODEM_STATE_CMD) { + transition(MODEM_STATE_READY); + } + } + + return MODEM_STATUS_OK; +} + +/* Go to the next command if any. + Otherwise end it successfully. + */ +static int next_cmd_sequence(void) { + int retval = MODEM_STATUS_ERROR; + + if (bg96.cmd_repeat) { + ESP_LOGI(TAG,"Repeating command\r\n"); + /* repeat the same command if requested by a command handler. */ + bg96.cmd_repeat = false; + } else { + /* Move to next command in sequence */ + bg96.cmd_sequence++; + } + + ESP_LOGI(TAG,"Next command sequence: %d/%d\r\n", bg96.cmd_sequence + 1, + bg96.cmd_num); + + if (bg96.cmd_sequence == bg96.cmd_num) { + /* Notify success because we've completed all commands. */ + retval = end_cmd_sequence(MODEM_STATUS_OK); + + } else { + const modem_cmd_t *cmd = bg96.cmd_list[bg96.cmd_sequence]; + if (cmd) { + bg96.cmd_retries = 0; + retval = send_command(cmd); + } + } + return retval; +} + +/* Command sequence performed by modem at each startup before entering READY state. */ +static int start_ready_cmd_sequence(void) { + bg96.cmd_num = 0; + + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_ECHO_OFF])) { + return MODEM_STATUS_ERROR; + } +#if BOARD_USE_CTS_RTS + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_IFC])) { + return MODEM_STATUS_ERROR; + } +#endif + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_IDENT])) { + return MODEM_STATUS_ERROR; + } +#if MODEM_MODEM_FWVER_FROM_QGMR + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QGMR])) { + return MODEM_STATUS_ERROR; + } +#endif + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_IMEI])) { + return MODEM_STATUS_ERROR; + } + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_ICCID])) { + return MODEM_STATUS_ERROR; + } +#if FAKE_NETWORK + /* Set time on modem to test programming of time with CCLK otherwise + * modem's default time is year 2080!! + */ + if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_SETCCLK])) { + return MODEM_STATUS_ERROR; +} +#endif + + ESP_LOGI(TAG,"Starting READY command sequence %d\r\n", bg96.cmd_num); + + return start_cmd_sequence(); +} + +static int start_modem(void) { + int retval = MODEM_STATUS_OK; + + bg96.is_startup = true; + +#if MODEM_BLIND_MODEM == 0 + + /* Modem already started ?! */ + if (port_modem_is_on()) { +#if MODEM_DEBUG_MODEM_POWER_STATE + //board_led_green_on(); + hmi_choose_led(HMI_LED_GREEN); +#endif + ESP_LOGI(TAG,"Modem already started. Interface ready.\r\n"); + retval = start_ready_cmd_sequence(); + } else +#endif /* MODEM_BLIND_MODEM */ + { + /* Start the receive stream before we start the modem to support the + * RDY method of determining when modem interface is up. + * We go through two phase: one is waiting for status to go high, then wait + * for RDY or a timeout. + */ + retval = start_rx_stream(); + port_modem_ldo_pin(1); + vTaskDelay(100 / portTICK_PERIOD_MS); + if (MODEM_STATUS_OK == retval) { + /* Start a timer to time power key duration. */ + if(ESP_OK == esp_timer_start_once(modem_timer, 1000*MODEM_PWRKEY_ON_DELAY_MS)) + { + retval = MODEM_STATUS_OK; + } + + if (MODEM_STATUS_OK == retval) { + ESP_LOGI(TAG,"Turning on - asserting power key\r\n"); + port_modem_assert_pwrkey(); + transition(MODEM_STATE_WAIT_POWERUP); +#if MODEM_DEBUG_MODEM_POWER_STATE + //board_led_green_on(); + hmi_choose_led(HMI_LED_GREEN); +#endif + } else { + ESP_LOGI(TAG,"Failed to turn on modem\r\n"); + stop_rx_stream(); + } + } + } + return retval; +} + +#if MODEM_BLIND_MODEM == 0 +static int stop_modem_pwrkey(void) { + int retval = MODEM_STATUS_OK; + + /* Stop any timer that might be on going */ + esp_timer_stop(modem_timer); + + if (port_modem_is_on()) { + /* Start a timer to time power key duration. */ + if(ESP_OK == esp_timer_start_once(modem_timer, 1000*MODEM_PWRKEY_OFF_DELAY_MS)) + { + retval = MODEM_STATUS_OK; + } + if (MODEM_STATUS_OK == retval) { + ESP_LOGI(TAG,"Turning off - asserting power key\r\n"); + bg96.shutdown_counter = 0; + bg96.shutdown_qpowd = false; + port_modem_assert_pwrkey(); + transition(MODEM_STATE_WAIT_POWERKEY); + } else { + ESP_LOGI(TAG,"Failed to turn off modem\r\n"); + transition(MODEM_STATE_INIT); + port_modem_ldo_pin(0); + } + } else { + ESP_LOGI(TAG,"Turning off - modem already off\r\n"); + transition(MODEM_STATE_INIT); + } + return retval; +} +#endif + +static void shutdown_seq_complete_cb(int retval) { + if (MODEM_STATUS_OK == retval) { + esp_timer_stop(modem_timer); + /* Start a timer to monitor STATUS for shutdown. */ + if(ESP_OK == esp_timer_start_once(modem_timer, 1000*MODEM_SHUTDOWN_STATUS_MONITOR_PERIOD_MS)) + { + retval = MODEM_STATUS_OK; + } + bg96.shutdown_counter = 0; + bg96.shutdown_qpowd = true; + transition(MODEM_STATE_WAIT_POWERDOWN); + } else { + ESP_LOGI(TAG,"Failed to turn off with command\r\n"); +#if MODEM_BLIND_MODEM + transition(MODEM_STATE_INIT); + port_modem_ldo_pin(0); +#else + stop_modem_pwrkey(); +#endif + } +} + +static int stop_modem(void) { + int retval = MODEM_STATUS_OK; + + /* Stop any timer that might be on going */ + esp_timer_stop(modem_timer); + +#if MODEM_BLIND_MODEM == 0 + if (port_modem_is_on()) +#endif + { + /* Start the shutdown sequence */ + retval = shutdown_sequence(shutdown_seq_complete_cb); + if (MODEM_STATUS_OK != retval) { + ESP_LOGI(TAG,"Failed to turn off modem with command\r\n"); +#if MODEM_BLIND_MODEM == 0 + retval = stop_modem_pwrkey(); +#endif + } + } +#if MODEM_BLIND_MODEM == 0 + else { + ESP_LOGI(TAG,"Turning off - modem already off\r\n"); + transition(MODEM_STATE_INIT); + } +#endif + return retval; +} + +/* !! Wait an additional period for the UART interface to become active. + * An alternative is: (from BG96 UART app note) + * Note: AT command can be input through UART port only after module is powered + * on and the Unsolicited Result Code "RDY" is output. + * Note: observed that a 0 is output before the RDY code. + * Note: Device outputs "RDY", then later "APP RDY". It doesn't respond until APP RDY. + * Note: Normally this is about 4500 ms after bootup. In some instances, this period + * elapses without detecting "APP RDY", even though modem responds to AT commands. + * + * Notes for BG77: + * RDY seems to appear about when STATUS is raised, this is about 1-2 seconds later. + * APP RDY seems to appear about 3 seconds after RDY. + */ +static int wait_modem_ready(void) { + int retval = MODEM_STATUS_OK; + + /* Note that modem rx stream was started at begining of startup process + * to capture RDY whenever it arrives + */ + if(ESP_OK == esp_timer_start_once(modem_timer, 1000*MODEM_STARTUP_RDY_TIMEOUT_MS)) + { + retval = MODEM_STATUS_OK; + } + + if (MODEM_STATUS_OK == retval) { + transition(MODEM_STATE_WAIT_STARTUP_RDY); + ESP_LOGI(TAG,"Waiting for AT interface activation\r\n"); + } + + return retval; +} + +/* ------------------------------------------------------------------------- */ +#if (WIFI_NEEDED == 1) +extern bool server_uart_flag; +#endif +void uart_ifx_uart1_rx_cb(void) { + bg96.poll_uart = true; +#if (WIFI_NEEDED == 1) + server_uart_flag = 1; +#endif +} + +static void timer_handler(void *context) +{ + bg96.poll_timer = true; +} + +static int state_machine(modem_event_t evt) { + int retval = MODEM_STATUS_OK; + switch (bg96.state) { + case MODEM_STATE_INIT: + if (evt == MODEM_EVENT_START) { + /* Indicate offloading data */ + //hmi_set_offload_mode(true); + ESP_LOGI(TAG,"Process started.\n"); + + /* Start modem and set next state */ + retval = start_modem(); + } else { + //ESP_LOGW(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state); + } + break; + + /* wait for modem powerkey assertion delay */ + case MODEM_STATE_WAIT_POWERUP: + if (evt == MODEM_EVENT_TIMER) { + //board_modem_deassert_pwrkey(); + port_modem_deassert_pwrkey(); + /* Start a timer to poll for status. */ + if(ESP_OK == esp_timer_start_once(modem_timer, 1000*MODEM_TIMER_GRANULARITY_MS)) + { + retval = MODEM_STATUS_OK; + } + bg96.poll_periods = MODEM_STARTUP_STATUS_TIMEOUT_MS + / MODEM_TIMER_GRANULARITY_MS; + transition(MODEM_STATE_WAIT_STARTUP); + ESP_LOGI(TAG,"Waiting for status activation\r\n"); + } else if (evt == MODEM_EVENT_UART) { + /* Ignore UART events */ + } else { + ESP_LOGW(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state); + } + break; + + /* wait for modem status to go high */ + case MODEM_STATE_WAIT_STARTUP: + if (evt == MODEM_EVENT_TIMER) { +#if MODEM_BLIND_MODEM + /* Wait for the AT interface to ready */ + retval = wait_modem_ready(); +#else + if (port_modem_is_on()) { + /* Wait for the AT interface to ready */ + retval = wait_modem_ready(); + } else if (--bg96.poll_periods > 0) { + /* Keep waiting */ + esp_timer_start_once(modem_timer, 1000*MODEM_TIMER_GRANULARITY_MS); + } else { + /* Timed out. */ + ESP_LOGI(TAG,"Modem startup timed out\r\n"); + stop_rx_stream(); + transition(MODEM_STATE_INIT); + bg96.start_cb(MODEM_STATUS_ERROR); + } +#endif /* MODEM_BLIND_MODEM */ + } else if (evt == MODEM_EVENT_UART) { + /* Ignore UART events */ + } else { + ESP_LOGI(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state); + retval = MODEM_STATUS_ERROR; + } + break; + + /* wait for modem AT interface to come up */ + case MODEM_STATE_WAIT_STARTUP_RDY: + + if (evt == MODEM_EVENT_UART) { + /* monitor for the modem interface "ready" indication */ + int res = monitor_rx_stream("RDY", NULL); + if (MODEM_STATUS_OK == res) { + /* Stop the timeout timer waiting for APP RDY now that we have it */ + /* Note: APP RDY seems to come about 3 seconds after RDY. */ + esp_timer_stop(modem_timer); + ESP_LOGI(TAG,"AT interface activated.\r\n"); + // hmi_set_modem_error(false); /* clear modem error on off chance it actually recovered. */ + retval = start_ready_cmd_sequence(); + } else { + /* Keep listening */ + } + } else if (evt == MODEM_EVENT_TIMER) { +#if MODEM_BLIND_MODEM + /* clear this anyways for blind modem testing */ + hmi_set_modem_error(false); +#endif + /* Timed out. */ + ESP_LOGW(TAG,"timeout waiting for modem ready\r\n"); + /* Start ready sequence anyways */ + //retval = start_ready_cmd_sequence(); + retval = MODEM_STATUS_ERROR; + } else { + ESP_LOGI(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state); + retval = MODEM_STATUS_ERROR; + } + break; + + /* waiting for response from command */ + case MODEM_STATE_CMD: + + if (evt == MODEM_EVENT_TIMER) { + /* Timeout */ + stop_rx_stream(); + + const modem_cmd_t *cmd = bg96.cmd_list[bg96.cmd_sequence]; + if (cmd && cmd->response_match_str) { + /* We will stop the sequence unless a handler can reverse this decision by + * returning an OK or there are retries available. + */ + int res = MODEM_STATUS_ERROR; + + /* If the command had no responses to wait for, invoke the handler now */ + //if (cmd->response_match_str == 0 && cmd->err_match_str == 0) + { + if (cmd->handler) { + res = cmd->handler(MODEM_STATUS_TIMEOUT, rxBuf, rx_idx); + } + } + + if (res == MODEM_STATUS_OK) { + /* A success response from the handler can continue the sequence. */ + retval = next_cmd_sequence(); + } else { + /* Handler was not available or did not reverse the error condition. */ + ESP_LOGW(TAG,"Command response timed out\r\n"); + + /* See if we need to retry */ + if (bg96.cmd_retries++ < cmd->retries) { + ESP_LOGW(TAG,"retry (%d/%d) command (%d/%d): %s\r\n", + bg96.cmd_retries, cmd->retries, + bg96.cmd_sequence + 1, bg96.cmd_num, + cmd->cmd_str); + retval = send_command(cmd); + } else { + if(cmd->cmd_str) + { + ESP_LOGW(TAG,"Command failed: %s", cmd->cmd_str); /* command strings have \r\n */ + } + else + { + ESP_LOGW(TAG,"The command that has the response %s ---> get failed", cmd->response_match_str); + } + retval = end_cmd_sequence(MODEM_STATUS_ERROR); + } + } + } else if (cmd->response_match_str == 0) { + /*Check if there's a timeout handle to be executed*/ + if (cmd->handler) { + /*Call the Command handler*/ + cmd->handler(MODEM_STATUS_OK, rxBuf, rx_idx); + } + /*Execute the next command in the command list*/ + retval = next_cmd_sequence(); + } + } + else if (evt == MODEM_EVENT_UART) { + /* Process the current command */ + const modem_cmd_t *cmd = bg96.cmd_list[bg96.cmd_sequence]; + if (cmd && cmd->response_match_str) + { + /* monitor for responses */ + int res = monitor_rx_stream(cmd->response_match_str, cmd->err_match_str); + + /* Run the handler for found success or error responses */ + if((MODEM_STATUS_OK == res) || (MODEM_STATUS_ERROR == res)) + { + if (cmd->handler) + { + /* A success response from the handler can continue the sequence. */ + ESP_LOGI(TAG," going to handler"); + res = cmd->handler(res, rxBuf, rx_idx); + } + } + + if (MODEM_STATUS_ERROR == res) + { + esp_timer_stop(modem_timer); + /* matched the expected error response, fail the sequence */ + ESP_LOGI(TAG,"MODEM_STATUS_ERROR -> end_cmd_sequence "); + retval = end_cmd_sequence(MODEM_STATUS_ERROR); + } + else if (MODEM_STATUS_OK == res) + { + esp_timer_stop(modem_timer); + ESP_LOGI(TAG,"MODEM_STATUS_OK -> next_cmd_sequence "); + /* matched the expected success response move on to the next */ + retval = next_cmd_sequence(); + } + else + { + /* nothing found yet */ + } + } + } else { + ESP_LOGI(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state); + retval = MODEM_STATUS_ERROR; + } + + break; + + case MODEM_STATE_READY: + if (evt == MODEM_EVENT_TIMER) { + + } else if (evt == MODEM_EVENT_SEND_CMD) { + /* request to send a command sequence */ + /* must have already been setup */ + } else if (evt == MODEM_EVENT_STOP) { + ESP_LOGI(TAG,"Process stopping.\n"); + retval = stop_modem(); + } else { + ESP_LOGI(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state); + } + break; + + case MODEM_STATE_WAIT_POWERDOWN: + if (evt == MODEM_EVENT_TIMER) { + /* Monitor STATUS for the shutdown duration. If it is still active every 10 seconds + * we note that in a warning log. + * If it doesn't shutdown after 65 seconds as datasheet says we start an LED + * error sequence. + */ +#if MODEM_BLIND_MODEM == 0 + if (port_modem_is_on()) { + bg96.shutdown_counter++; + /* Start a timer to monitor STATUS for shutdown. */ + if(ESP_OK == esp_timer_start_once(modem_timer, 1000*MODEM_SHUTDOWN_STATUS_MONITOR_PERIOD_MS)) + { + retval = MODEM_STATUS_OK; + } + if ((bg96.shutdown_counter % MODEM_SHUTDOWN_COUNT_WARNING_SEC) + == 0) { + ESP_LOGW(TAG,"Shutting down, modem still on %d\r\n", + bg96.shutdown_counter + / MODEM_SHUTDOWN_COUNT_WARNING_SEC); + } + + if (bg96.shutdown_counter > MODEM_SHUTDOWN_COUNT_LIMIT_SEC) { + /* Waited as long as we're going to. Try power key approach if we were + * doing the QPOWD approach. + */ + if (bg96.shutdown_qpowd) { + bg96.shutdown_qpowd_failures++; + ESP_LOGI(TAG,"Failed to shutdown modem with command. %d\r\n", + bg96.shutdown_qpowd_failures); + retval = stop_modem_pwrkey(); + } else { + /* This is bad. Modem does not appear to be shutdown, + * and it is likely on next power up cycle it won't + * be in the right state to use. + * Solution is to have hard power control of the modem: + * disconnect the power rails, hopefully guaranteeing + * a startup in a known state. + * Without hard power control user must remove batteries. + */ + bg96.shutdown_pwrkey_failures++; + ESP_LOGI(TAG,"Failed to shutdown modem. %d\r\n", + bg96.shutdown_pwrkey_failures); + /* Install an LED sequence to indicate this error condition. + * User would need to remove batteries to recover likely. + */ + // hmi_set_modem_error(true); + transition(MODEM_STATE_INIT); + port_modem_ldo_pin(0); + } + } + } else +#endif /* MODEM_BLIND_MODEM */ + { +#if MODEM_DEBUG_MODEM_POWER_STATE + board_led_green_off(); +#endif + ESP_LOGI(TAG,"Powered down, count=%d [%d %d]\r\n", + bg96.shutdown_counter, bg96.shutdown_qpowd_failures, + bg96.shutdown_pwrkey_failures); + transition(MODEM_STATE_INIT); + port_modem_ldo_pin(0); + } + } else { + ESP_LOGW(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state); + } + break; + + case MODEM_STATE_WAIT_POWERKEY: + if (evt == MODEM_EVENT_TIMER) { + port_modem_deassert_pwrkey(); + ESP_LOGI(TAG,"Powering down with power key.\r\n"); + if(ESP_OK == esp_timer_start_once(modem_timer, 1000*MODEM_SHUTDOWN_STATUS_MONITOR_PERIOD_MS)) + { + retval = MODEM_STATUS_OK; + } + transition(MODEM_STATE_WAIT_POWERDOWN); + } else { + ESP_LOGW(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state); + } + break; + + default: + break; + } + + return retval; +} + +/* ------------------------------------------------------------------------- */ + +/* Initialize. */ +int modem_init(void) { + int retval = MODEM_STATUS_OK; + + memset(&bg96, 0, sizeof(bg96)); + + /* Note imei is 15 character string */ + memset(bg96.imei, '0', sizeof(bg96.imei) - 1); + + //timer_initiating(MODEM_TIMER_ID, timer_handler); + //timer_starting(MODEM_TIMER_ID, 1000); + const esp_timer_create_args_t modem_timer_args = { + .callback = &timer_handler, + /* argument specified here will be passed to timer callback function */ + .arg = (void*) modem_timer, + .name = "modem-timer" + }; + esp_timer_create(&modem_timer_args, &modem_timer); + + ESP_LOGI(TAG,"Process initialized.\n"); + + return retval; +} + +/* Start the process */ +int modem_start(modem_start_cb_t cb) { + +#if FAKE_COMMS + /* Go directly to ready state. Add an IMEI and fw ver */ + strcpy(bg96.fw_ver, "BG95M2LAR01A01"); // BG77LAR02A04 + + // Units deployed + //strcpy(bg96.imei, "866349041329859"); + //strcpy(bg96.imei, "866349041332549"); + //strcpy(bg96.imei, "866349041331970"); + + // 866349041329859 -> (extract) 904132985 -> (hex) 35E3F979 -> major=0x35E3 minor=0xF979 + strcpy(bg96.imei, "866349041329859"); + + ESP_LOGI(TAG,"IMEI No: %s\r\n", bg96.imei); + config_set_device_id_from_imei(bg96.imei); + + transition(MODEM_STATE_READY); + if (cb) { + cb(MODEM_STATUS_OK); + } + return MODEM_STATUS_OK; +#else + bg96.start_cb = cb; + int retval = state_machine(MODEM_EVENT_START); + //xTaskCreate(modem_poll, "BG77_POLL", 2048, NULL, 9, NULL); + return retval; +#endif +} + +/* Stop the process */ +int modem_stop(void) { +#if FAKE_COMMS + transition(MODEM_STATE_INIT); + return MODEM_STATUS_OK; +#else + int retval = state_machine(MODEM_EVENT_STOP); + return retval; +#endif +} + +/* Poll the process */ +int modem_poll(void) { + int retval = MODEM_STATUS_OK; + if (bg96.poll_timer) { + bg96.poll_timer = false; + retval = state_machine(MODEM_EVENT_TIMER); + } else if (bg96.poll_uart) { + //bg96.poll_uart = false; + retval = state_machine(MODEM_EVENT_UART); + } + + return retval; +} + +/* Check if the process needs to be polled */ +bool modem_needs_poll(void) { + return bg96.poll_timer || bg96.poll_uart; +} + +/* Check if the process is started */ +bool modem_is_started(void) { + return bg96.state != MODEM_STATE_INIT; +} + +/* Check if the process is ready */ +bool modem_is_ready(void) { + return bg96.state == MODEM_STATE_READY; +} + +const char* modem_get_imei(void) { + return bg96.imei; +} + +const char* modem_get_iccid(void) +{ + return bg96.iccid; +} + + +const modem_signal_strength_t* modem_get_signal_strength(void) { + return &bg96.sig_strength; +} + +const char* modem_get_fw_ver(void) { + return bg96.fw_ver; +} + +int modem_http_get_buf(char **bufptr, int *len) { +#if FAKE_COMMS || FAKE_NETWORK + +#if 0 // TEST MODEM UPDATE SP5 SEQUENCE + + /* First comms: GET CONFIG (ignore flags because first contact) then GET WHITELIST. + * scond comms: GET CONFIG, check flags, then GET WHITELIST + */ + static int iter = 0; + if (iter == 0) { + /* Put in a test configuration record SP3 for comms process to use */ + bg96.http_params.buf = rxBuf; + /* flags scan_int_SEC scan_win_MSEC tx_pwr_DB broadcast_int_MSEC comms_int_MIN */ + /* 0x1 - Whitelist + * 0x2 - MCU update + * 0x4 - Modem update + */ + bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP3 %s 1599777618 05 15 2500 1 800 2 EP", bg96.imei); + } + else if (iter == 1) { + /* Put in a test configuration record SP4 for comms process to use */ + bg96.http_params.buf = rxBuf; + bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP4 %s 1599777618 01,0AFF4C001005511C4EAB522599000000 02,20026425603AB04037501A9E3E3EC785 03,9FFE0000000000000000000000000000 EP", bg96.imei); + + } + else if (iter == 2) { + /* Put in a test configuration record SP3 for comms process to use */ + bg96.http_params.buf = rxBuf; + /* flags scan_int_SEC scan_win_MSEC tx_pwr_DB broadcast_int_MSEC comms_int_MIN */ + bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP3 %s 1599777618 05 15 2500 1 800 2 EP", bg96.imei); + } + else if (iter == 3) { + /* Put in a test configuration record SP4 for comms process to use */ + bg96.http_params.buf = rxBuf; + bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP4 %s 1599777618 01,0AFF4C001005091C2AC447CC28D56500 02,000906031EC0A8014500000001000000 03,000906031EC0A8014500000001000000 EP", bg96.imei); + } + else if (iter == 4) { + /* Put in a test configuration record SP5 for comms process to use */ + bg96.http_params.buf = rxBuf; + //bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP5 %s 1599777618 BG77LAR02A04 EP", bg96.imei); + bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP5 %s 1599777618 BG77LAR02A03 EP", bg96.imei); + } + + if (++iter > 4) { + iter = 0; + } + +#endif + + +#if 0 // TEST MCU UPDATE SP5 SEQUENCE + + /* First comms: GET CONFIG (ignore flags because first contact) then GET WHITELIST. + * scond comms: GET CONFIG, check flags, then GET WHITELIST + */ + static int iter = 0; + if (iter == 0) { + /* Put in a test configuration record SP3 for comms process to use */ + bg96.http_params.buf = rxBuf; + /* flags scan_int_SEC scan_win_MSEC tx_pwr_DB broadcast_int_MSEC comms_int_MIN */ + /* 0x1 - Whitelist + * 0x2 - MCU update + * 0x4 - Modem update + */ + bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP3 %s 1599777618 02 15 2500 1 800 2 EP", bg96.imei); + } + else if (iter == 1) { + /* Put in a test configuration record SP4 for comms process to use */ + bg96.http_params.buf = rxBuf; + bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP5 %s 1599777618 LRSM0005_v2 EP", bg96.imei); + + }else if (iter == 2) { + /* Put in a test configuration record SP3 for comms process to use */ + bg96.http_params.buf = rxBuf; + /* flags scan_int_SEC scan_win_MSEC tx_pwr_DB broadcast_int_MSEC comms_int_MIN */ + bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP3 %s 1599777618 01 15 2500 1 800 2 EP", bg96.imei); + } + else if (iter == 3) { + /* Put in a test configuration record SP4 for comms process to use */ + bg96.http_params.buf = rxBuf; + bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP4 %s 1599777618 01,0AFF4C001005091C2AC447CC28D56500 02,000906031EC0A8014500000001000000 03,000906031EC0A8014500000001000000 EP", bg96.imei); + } + else if (iter == 4) { + /* Put in a test configuration record SP5 for comms process to use */ + bg96.http_params.buf = rxBuf; + //bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP5 %s 1599777618 BG77LAR02A04 EP", bg96.imei); + bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP5 %s 1599777618 BG77LAR02A03 EP", bg96.imei); + } + + if (++iter > 4) { + iter = 0; + } + +#endif + +#if 1 + + /* First comms: GET CONFIG (ignore flags because first contact) then GET WHITELIST. + * scond comms: GET CONFIG, check flags, then GET WHITELIST + */ + static int iter = 0; + if (iter == 0) { + /* Put in a test configuration record SP3 for comms process to use */ + bg96.http_params.buf = rxBuf; + /* flags scan_int_SEC scan_win_MSEC tx_pwr_DB broadcast_int_MSEC comms_int_MIN */ + /* 0x1 - Whitelist + * 0x2 - MCU update + * 0x4 - Modem update + */ + bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP3 %s 1599777618 01 15 2500 1 800 2 EP", bg96.imei); + } + else if (iter == 1) { + /* Put in a test configuration record SP4 for comms process to use */ + bg96.http_params.buf = rxBuf; + bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP4 %s 1599777618 01,0AFF4C001005511C4EAB522599000000 02,20026425603AB04037501A9E3E3EC785 03,9FFE0000000000000000000000000000 EP", bg96.imei); + + } + else if (iter == 2) { + /* Put in a test configuration record SP3 for comms process to use */ + bg96.http_params.buf = rxBuf; + /* flags scan_int_SEC scan_win_MSEC tx_pwr_DB broadcast_int_MSEC comms_int_MIN */ + bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP3 %s 1599777618 05 15 2500 1 800 2 EP", bg96.imei); + } + else if (iter == 3) { + /* Put in a test configuration record SP4 for comms process to use */ + bg96.http_params.buf = rxBuf; + bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP4 %s 1599777618 01,0AFF4C001005091C2AC447CC28D56500 02,000906031EC0A8014500000001000000 03,000906031EC0A8014500000001000000 EP", bg96.imei); + } + else if (iter == 4) { + /* Put in a test configuration record SP5 for comms process to use */ + bg96.http_params.buf = rxBuf; + //bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP5 %s 1599777618 BG77LAR02A04 EP", bg96.imei); + bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP5 %s 1599777618 BG77LAR02A03 EP", bg96.imei); + } + + if (++iter > 4) { + iter = 0; + } + +#endif + +#endif + + if (bufptr && len) { + if (bg96.http_params.buf && bg96.http_params.len > 0) { + *bufptr = bg96.http_params.buf; + *len = bg96.http_params.len; + return MODEM_STATUS_OK; + } + *bufptr = 0; + *len = 0; + } + return MODEM_STATUS_ERROR; +} + +/* ------ Process Callbacks ------------------------- */ + +__attribute__((weak)) void modem_ready_cb(void) { +} + diff --git a/main/modem.h b/main/modem.h new file mode 100644 index 0000000..5990088 --- /dev/null +++ b/main/modem.h @@ -0,0 +1,181 @@ +/* + * bg96.h + * + * Created on: Jan 11, 2023 + * Author: Sword + */ + +#ifndef MAIN_MODEM_H_ +#define MAIN_MODEM_H_ + +#include +#include +#include "sdkconfig.h" + +#define MODEM_STATUS_OK 0 +#define MODEM_STATUS_ERROR -1 +#define MODEM_STATUS_TIMEOUT -2 +#define MODEM_STATUS_MQTT_PUB_OVERFLOW -10 +#define MODEM_STATUS_HTTP_POST_WAITRES -11 +#define MCU_OTA_FILE_LOCAL "OTA.bin" + + +typedef struct +{ + /* Fields read from AT+QCSQ command */ + int16_t rssi; + int16_t rsrp; + int16_t sinr; + int16_t rsrq; + +} modem_signal_strength_t; + + +/* Modem's unique identifier IMEI string, null terminated. + * Maximum size of buffer returned via modem_get_imei(). +*/ + +#define MODEM_IMEI_LEN 16 + + +/* Modem's firmware version string, null terminated. + * This can include format where modem+application versions are reported. + * Maximum size of buffer returned via modem_get_fw_ver(). +*/ +#define MODEM_FWVER_LEN 32 + + +/////////////////////////////////////////////////////////////// +// BG96 module process interface + +typedef void (*modem_start_cb_t)(int status); + +/* Operation complete indication callback */ +typedef void (*modem_op_cb_t)(int status); + + +/* Connect to network service provider + * once the modem is ready. + * + * Non-blocking. + * + * Returns OK if command sequence started. + * Callback is invoked when completed. +*/ +int modem_network_connect(modem_op_cb_t cb); + +/*==== HTTP API ==== */ + +/* Setup the HTTP context */ +int modem_http_setup(modem_op_cb_t cb); + +/* Make an HTTP GET request and frame result in internal buffer */ +int modem_http_get_to_buf(modem_op_cb_t cb, const char* url); +/* Call to get access to framed buffer. */ +int modem_http_get_buf(char** bufptr, int* len); + +/* Make an HTTP GET request and store result to file on modem file system. + * Use modem_read_from_file to get contents. + */ + +int modem_http_post_message(modem_op_cb_t cb, const char *url, const char *message); + +int modem_http_get_to_file(modem_op_cb_t cb, const char* url, const char* file); + +/* Read data from a file on the modem file system. + * Calls user callback Open if the file is found and successfully opened. + * Calls user callback Data to transfer data in chunks. + * + * The file is streamed in chunks via the user Data callback, in the context of + * the bg96 process. It will request from the modem a chunk of data, yield, when + * response is received it invokes Data immediately. When user is finished with the data + * the bg96 process repeats the command/response cycle with the modem. Other processes will + * get to run at least once in between the command/response. + */ +typedef int(*modem_read_from_file_open)(uint32_t fileSize); +typedef int(*modem_read_from_file_data)(const char* buf, int buf_len); + +/* Return OK if operation started, ERROR otherwise. */ +int modem_read_from_file( + modem_op_cb_t cb, /* callback on overall API success of failure */ + const char* file, /* file name string */ + modem_read_from_file_open cbOpen, /* callback when file is opened successfully on modem */ + modem_read_from_file_data cbData); /* callback for each chunk of streamed data of 1024 bytes or less. */ + +/*==== MQTT API ==== */ + +/* Connect to an MQTT server at given port. + * server may be IP address or domain. + * Client ID for MQTT CONNECT +*/ +int modem_mqtt_connect(modem_op_cb_t cb, const char* host, int port, const char* clientID);//, const char* jwt); + +/* Publish data to the specific topic */ +int modem_mqtt_publish(modem_op_cb_t cb, const char* topic, const char* data, int len); + +/* Close MQTT session */ +int modem_mqtt_close(modem_op_cb_t cb); + +/*==== Update API ==== */ + +/* Update the modem from BGXX firmware image available +* at specified URL. +* Must be connected to a network. +*/ +int modem_update_modem(modem_op_cb_t cb, const char* url); + +/*====================*/ + +/* Initialize process. */ +int modem_init(void); + +/* Start process. */ +int modem_start(modem_start_cb_t cb); + +/* Stop process. */ +int modem_stop(void); + +/* Poll process. */ +int modem_poll(void); + +/*declaration signal strength function*/ +int modem_Rssi(void); + +/* Check if the process needs to be polled. */ +bool modem_needs_poll(void); + +/* Check if the process is started */ +bool modem_is_started(void); + +/* Check if the process is ready */ +bool modem_is_ready(void); + + +/**/ +char* modem_get_rxbuf(void); + +/* Get modem IMEI. + * String is valid only after modem has been started and is ready. + */ +const char* modem_get_imei(void); + +/* Get modem ICCID. + * String is valid only after modem has been started and is ready. + */ +const char* modem_get_iccid(void); + +/* Get modem version. + * String is valid only after modem has been started and is ready. + */ +const char* modem_get_fw_ver(void); + +/* Get the signal strength data - only valid if connected */ +const modem_signal_strength_t* modem_get_signal_strength(void); + +/* Get Serving cell data*/ +const char* modem_get_serving_cell(void); + +/* Get neighbour cell list*/ +const char* modem_get_neighbour_cell(void); + +#endif /* MAIN_MODEM_H_ */ diff --git a/main/nvm.c b/main/nvm.c new file mode 100644 index 0000000..25912d0 --- /dev/null +++ b/main/nvm.c @@ -0,0 +1,1118 @@ +/* + * nvm.c + * + * Created on: Aug 14, 2023 + * Author: Sword + */ + +#include +#include "nvs_flash.h" +#include "nvs.h" +#include "esp_system.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include "hmi.h" +#include "nvm.h" +#include "main.h" + +static const char* TAG = "NVM"; + +#define LOG_LOCAL_LEVEL ESP_LOG_INFO +#include "esp_log.h" + + +nvs_handle_t my_handle; + +RTC_DATA_ATTR static uint8_t last_posted_sector; + +extern uint8_t onBoarded; + + +/**/ +void nvm_init(void) +{ + /* Initialize the dedicated NVS partition */ + esp_err_t ret = nvs_flash_init(); + + /* Check if NVS partition doesn't contain any empty pages or contains data in new format and cannot be recognized by this version of code */ + if((ret == ESP_ERR_NVS_NO_FREE_PAGES) || (ret == ESP_ERR_NVS_NEW_VERSION_FOUND)) + { + /* If so ----> then erase the NVS partition reinitialize it one more time*/ + ESP_ERROR_CHECK(nvs_flash_erase()); + + /* reinitialize NVS partition one more time */ + ret = nvs_flash_init(); + } + + //Check that there is no problem with initializing NVS partition + ESP_ERROR_CHECK(ret); +} + +/**/ +void nvm_clear(void) +{ + /* erase the NVS partition */ + ESP_ERROR_CHECK(nvs_flash_erase()); +} + + + +/**/ +uint8_t nvm_read_onboarding_flag(onboarding_type_t flag_key) +{ + uint8_t onBoarding; + + /*Storing the on-boarding value in the NVS so that to indicate that each time power on/off the value would be the same*/ + //ESP_LOGI(TAG,"Opening Non-Volatile Storage (NVS) handle... "); + esp_err_t err = nvs_open("storage", NVS_READWRITE, &my_handle); + + /* Make sure that NVS is initialized */ + if(ESP_ERR_NVS_NOT_INITIALIZED == err) + { + /* initialized it if not */ + err = nvs_flash_init(); + + /* Check if the response is OK then OPEN it */ + if(err == ESP_OK) + { + /* Retry to open the NVS */ + err = nvs_open("storage", NVS_READWRITE, &my_handle); + } + else + { + /* Print this LOG if failed to initialize it */ + ESP_LOGI(TAG,"Failed to reinitialized The NVS partition"); + } + } + + if (err != ESP_OK) + { + ESP_LOGI(TAG,"Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + } + else + { + //ESP_LOGI(TAG,"Done\nReading on-boarding value from NVS ... "); + + if(WIFI_ONBOARDING_KEY == flag_key) + { + err = nvs_get_u8(my_handle, NVM_WIFI_ONBOARDING_KEY, &onBoarding); + } + else + { + err = nvs_get_u8(my_handle, NVM_CELL_ONBOARDING_KEY, &onBoarding); + } + + switch (err) + { + case ESP_OK: + + ESP_LOGI(TAG,"On-Boarding = %d\n",onBoarding); + + // Close the opened NVS + nvs_close(my_handle); + + return onBoarding; + + break; + + case ESP_ERR_NVS_NOT_FOUND: + ESP_LOGI(TAG,"The on-boarding value is not initialized yet!\n"); + break; + + default: + ESP_LOGI(TAG,"Error (%s) reading!\n", esp_err_to_name(err)); + } + } + + // Close the opened NVS + nvs_close(my_handle); + + return 0; + +} + +/**/ +void nvm_write_onboarding_flag(onboarding_type_t flag_key, uint8_t flag_value) +{ + esp_err_t err; + + /*Storing the on-boarding value in the NVS so that to indicate that each time power on/off the value would be the same*/ + //ESP_LOGI(TAG,"Opening Non-Volatile Storage (NVS) handle... "); + err = nvs_open("storage", NVS_READWRITE, &my_handle); + + /* Make sure that NVS is initialized */ + if(ESP_ERR_NVS_NOT_INITIALIZED == err) + { + /* initialized it if not */ + err = nvs_flash_init(); + + /* Check if the response is OK then OPEN it */ + if(err == ESP_OK) + { + /* Retry to open the NVS */ + err = nvs_open("storage", NVS_READWRITE, &my_handle); + } + else + { + /* Print this LOG if failed to initialize it */ + ESP_LOGI(TAG,"Failed to reinitialized The NVS partition"); + } + } + + if (err != ESP_OK) + { + ESP_LOGI(TAG,"Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + } + else + { + //ESP_LOGI(TAG,"Done\nUpdating on-boarding value in NVS ... "); + + if(WIFI_ONBOARDING_KEY == flag_key) + { + err = nvs_set_u8(my_handle, NVM_WIFI_ONBOARDING_KEY, flag_value); + } + else + { + err = nvs_set_u8(my_handle, NVM_CELL_ONBOARDING_KEY, flag_value); + } + + if (err != ESP_OK) + { + ESP_LOGI(TAG,"Failed"); + } + else + { + //ESP_LOGI(TAG,"Done"); + } + + // 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. + hmi_stop_red_flashing(); + + //ESP_LOGI(TAG,"Committing updates in NVS ... "); + + err = nvs_commit(my_handle); + + if (err != ESP_OK) + { + ESP_LOGI(TAG,"Failed Updating on-boarding value in NVS"); + } + else + { + ESP_LOGI(TAG,"Done Updating on-boarding value in NVS"); + } + } + + // Close the opened NVS + nvs_close(my_handle); +} + + + +/**/ +uint8_t nvm_read_comms_mode(void) +{ + uint8_t commsMode; + esp_err_t err; + + /*Storing the on-boarding value in the NVS so that to indicate that each time power on/off the value would be the same*/ + //ESP_LOGI(TAG,"Opening Non-Volatile Storage (NVS) handle... "); + err = nvs_open("storage", NVS_READWRITE, &my_handle); + + /* Make sure that NVS is initialized */ + if(ESP_ERR_NVS_NOT_INITIALIZED == err) + { + /* initialized it if not */ + err = nvs_flash_init(); + + /* Check if the response is OK then OPEN it */ + if(err == ESP_OK) + { + /* Retry to open the NVS */ + err = nvs_open("storage", NVS_READWRITE, &my_handle); + } + else + { + /* Print this LOG if failed to initialize it */ + ESP_LOGI(TAG,"Failed to reinitialized The NVS partition"); + } + } + + if (err != ESP_OK) + { + ESP_LOGI(TAG,"Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + } + else + { + //ESP_LOGI(TAG,"Done\nReading Communication-Mode value from NVS ... "); + + err = nvs_get_u8(my_handle, NVM_COMMS_MODE_KEY, &commsMode); + + switch (err) + { + case ESP_OK: + + ESP_LOGI(TAG,"Communication Mode = %d\n",commsMode); + + // Close the opened NVS + nvs_close(my_handle); + + return commsMode; + + break; + + case ESP_ERR_NVS_NOT_FOUND: + ESP_LOGI(TAG,"The Communication-Mode value is not initialized yet!\n"); + break; + + default: + ESP_LOGI(TAG,"Error (%s) reading!\n", esp_err_to_name(err)); + } + } + + // Close the opened NVS + nvs_close(my_handle); + + return DEFAULT_COMMS_MODE; + +} + +/**/ +void nvm_write_comms_mode(uint8_t commsMode) +{ + esp_err_t err; + + /*Storing the on-boarding value in the NVS so that to indicate that each time power on/off the value would be the same*/ + //ESP_LOGI(TAG,"Opening Non-Volatile Storage (NVS) handle... "); + err = nvs_open("storage", NVS_READWRITE, &my_handle); + + /* Make sure that NVS is initialized */ + if(ESP_ERR_NVS_NOT_INITIALIZED == err) + { + /* initialized it if not */ + err = nvs_flash_init(); + + /* Check if the response is OK then OPEN it */ + if(err == ESP_OK) + { + /* Retry to open the NVS */ + err = nvs_open("storage", NVS_READWRITE, &my_handle); + } + else + { + /* Print this LOG if failed to initialize it */ + ESP_LOGI(TAG,"Failed to reinitialized The NVS partition"); + } + } + + if (err != ESP_OK) + { + ESP_LOGI(TAG,"Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + } + else + { + //ESP_LOGI(TAG,"Done\nUpdating Communication-Mode value in NVS ... "); + + err = nvs_set_u8(my_handle, NVM_COMMS_MODE_KEY, commsMode); + + if (err != ESP_OK) + { + ESP_LOGI(TAG,"Failed"); + } + else + { + //ESP_LOGI(TAG,"Done"); + } + + //ESP_LOGI(TAG,"Committing updates in NVS ... "); + + err = nvs_commit(my_handle); + + if (err != ESP_OK) + { + ESP_LOGI(TAG,"Failed Updating Communication-Mode value in NVS"); + } + else + { + ESP_LOGI(TAG,"Done Updating Communication-Mode value in NVS"); + } + } + + // Close the opened NVS + nvs_close(my_handle); +} + + + +/**/ +esp_err_t nvm_read_wifi_credentials(char* ssid, char* pswd) +{ + esp_err_t err; + size_t ssid_len; + size_t pswd_len; + + /*Storing the on-boarding value in the NVS so that to indicate that each time power on/off the value would be the same*/ + //ESP_LOGI(TAG,"Opening Non-Volatile Storage (NVS) handle... "); + err = nvs_open("storage", NVS_READWRITE, &my_handle); + + /* Make sure that NVS is initialized */ + if(ESP_ERR_NVS_NOT_INITIALIZED == err) + { + /* initialized it if not */ + err = nvs_flash_init(); + + /* Check if the response is OK then OPEN it */ + if(err == ESP_OK) + { + /* Retry to open the NVS */ + err = nvs_open("storage", NVS_READWRITE, &my_handle); + } + else + { + /* Print this LOG if failed to initialize it */ + ESP_LOGI(TAG,"Failed to reinitialized The NVS partition"); + } + } + + /* Make sure that NVS storage has been opened successfully */ + if (err != ESP_OK) + { + ESP_LOGI(TAG,"Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + } + else + { + /* Get the length of the WIFI-SSID */ + nvs_get_str(my_handle, NVM_WIFI_SSID_KEY, NULL, (size_t*)&ssid_len); + + /* Get the length of the WIFI-PSWD */ + nvs_get_str(my_handle, NVM_WIFI_PSWD_KEY, NULL, (size_t*)&pswd_len); + + ESP_LOGI(TAG,"NVM-SSID len = %d, and NVM-PWD len = %d",ssid_len,pswd_len); + + //char* sid = malloc(ssid_len); + //char* pwd = malloc(pswd_len); + + /* Retrieve the WIFI-SSID */ + if(ESP_OK == err) + err = nvs_get_str(my_handle, (const char*)NVM_WIFI_SSID_KEY, ssid, (size_t*)&ssid_len); + + /* Retrieve the WIFI-PSWD */ + if(ESP_OK == err) + err = nvs_get_str(my_handle, (const char*)NVM_WIFI_PSWD_KEY, pswd, (size_t*)&pswd_len); + + /* Check the result of the NVS reading */ + switch (err) + { + case ESP_OK: + + //strcpy(ssid,sid); + //strcpy(pswd,pwd); + ESP_LOGI(TAG,"WIFI_SSID is: %s and WIFI_PSWD is: %s",ssid,pswd); + break; + + case ESP_ERR_NVS_NOT_FOUND: + ESP_LOGI(TAG,"The WIFI_Credentials not initialized yet!\n"); + break; + + default: + ESP_LOGI(TAG,"Error (%s) reading!\n", esp_err_to_name(err)); + } + + //free(sid); + //free(pwd); + } + + // Close the opened NVS + nvs_close(my_handle); + + return err; +} + +/**/ +void nvm_write_wifi_credentials(char* ssid, uint8_t ssid_len, char* pswd, uint8_t pswd_len) +{ + esp_err_t err; + + /*Storing the on-boarding value in the NVS so that to indicate that each time power on/off the value would be the same*/ + //ESP_LOGI(TAG,"Opening Non-Volatile Storage (NVS) handle... "); + err = nvs_open("storage", NVS_READWRITE, &my_handle); + + /* Make sure that NVS is initialized */ + if(ESP_ERR_NVS_NOT_INITIALIZED == err) + { + /* initialized it if not */ + err = nvs_flash_init(); + + /* Check if the response is OK then OPEN it */ + if(err == ESP_OK) + { + /* Retry to open the NVS */ + err = nvs_open("storage", NVS_READWRITE, &my_handle); + } + else + { + /* Print this LOG if failed to initialize it */ + ESP_LOGE(TAG,"Failed to reinitialized The NVS partition"); + } + } + + /* Make sure that NVS storage has been opened successfully */ + if (err != ESP_OK) + { + ESP_LOGE(TAG,"Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + } + else + { + //ESP_LOGI(TAG,"Done\nUpdating WIFI-Credentials in NVS ... "); + + /* Store the WIFI-SSID string in NVS */ + err = nvs_set_str(my_handle, (const char*)NVM_WIFI_SSID_KEY, (const char*)ssid); + + /* If WIFI-SSID stored successfully in NVS then ---> Store the WIFI-PSWD string in NVS */ + if(ESP_OK == err) + err = nvs_set_str(my_handle, (const char*)NVM_WIFI_PSWD_KEY, (const char*)pswd); + + /* Inform the user with one of these logs to indicate failing or succeeding to store wifi-credentials */ + if (err != ESP_OK) + { + ESP_LOGE(TAG,"Failed"); + } + else + { + //ESP_LOGI(TAG,"Done"); + } + + // 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); + vTaskDelay(100 / portTICK_PERIOD_MS); + + if (err != ESP_OK) + { + ESP_LOGE(TAG,"Failed Updating WIFI-Credentials in NVS"); + } + else + { + ESP_LOGI(TAG,"Done Updating WIFI-Credentials in NVS"); + } + } + + // Close the opened NVS + nvs_close(my_handle); +} + + +#if 0 +/**/ +void nvm_read_wifi_backup_credentials(char* ssid, char* pswd, char* key1, char* key2) +{ + esp_err_t err; + size_t ssid_len; + size_t pswd_len; + + /*Storing the on-boarding value in the NVS so that to indicate that each time power on/off the value would be the same*/ + ESP_LOGI(TAG,"Opening Non-Volatile Storage (NVS) handle... "); + err = nvs_open("storage", NVS_READWRITE, &my_handle); + + /* Make sure that NVS is initialized */ + if(ESP_ERR_NVS_NOT_INITIALIZED == err) + { + /* initialized it if not */ + err = nvs_flash_init(); + + /* Check if the response is OK then OPEN it */ + if(err == ESP_OK) + { + /* Retry to open the NVS */ + err = nvs_open("storage", NVS_READWRITE, &my_handle); + } + else + { + /* Print this LOG if failed to initialize it */ + ESP_LOGI(TAG,"Failed to reinitialized The NVS partition"); + } + } + + /* Make sure that NVS storage has been opened successfully */ + if (err != ESP_OK) + { + ESP_LOGI(TAG,"Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + } + else + { + /* Get the length of the WIFI-SSID */ + nvs_get_str(my_handle, key1, NULL, (size_t*)&ssid_len); + + /* Get the length of the WIFI-PSWD */ + nvs_get_str(my_handle, key2, NULL, (size_t*)&pswd_len); + + ESP_LOGI(TAG,"NVM-SSID len = %d, and NVM-PWD len = %d",ssid_len,pswd_len); + + //char* sid = malloc(ssid_len); + //char* pwd = malloc(pswd_len); + + /* Retrieve the WIFI-SSID */ + if(ESP_OK == err) + err = nvs_get_str(my_handle, (const char*)key1, ssid, (size_t*)&ssid_len); + + /* Retrieve the WIFI-PSWD */ + if(ESP_OK == err) + err = nvs_get_str(my_handle, (const char*)key2, pswd, (size_t*)&pswd_len); + + /* Check the result of the NVS reading */ + switch (err) + { + case ESP_OK: + + //strcpy(ssid,sid); + //strcpy(pswd,pwd); + ESP_LOGI(TAG,"WIFI_SSID is: %s and WIFI_PSWD is: %s",ssid,pswd); + break; + + case ESP_ERR_NVS_NOT_FOUND: + ESP_LOGI(TAG,"The WIFI_Credentials not initialized yet!\n"); + break; + + default: + ESP_LOGI(TAG,"Error (%s) reading!\n", esp_err_to_name(err)); + } + + //free(sid); + //free(pwd); + } + + // Close the opened NVS + nvs_close(my_handle); +} + +/**/ +void nvm_write_wifi_backup_credentials(char* ssid1, char* pswd1, char* ssid2, char* pswd2) +{ + esp_err_t err; + + /*Storing the on-boarding value in the NVS so that to indicate that each time power on/off the value would be the same*/ + ESP_LOGI(TAG,"Opening Non-Volatile Storage (NVS) handle... "); + err = nvs_open("storage", NVS_READWRITE, &my_handle); + + /* Make sure that NVS is initialized */ + if(ESP_ERR_NVS_NOT_INITIALIZED == err) + { + /* initialized it if not */ + err = nvs_flash_init(); + + /* Check if the response is OK then OPEN it */ + if(err == ESP_OK) + { + /* Retry to open the NVS */ + err = nvs_open("storage", NVS_READWRITE, &my_handle); + } + else + { + /* Print this LOG if failed to initialize it */ + ESP_LOGE(TAG,"Failed to reinitialized The NVS partition"); + } + } + + /* Make sure that NVS storage has been opened successfully */ + if (err != ESP_OK) + { + ESP_LOGE(TAG,"Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + } + else + { + ESP_LOGI(TAG,"Done\nUpdating WIFI-Credentials in NVS ... "); + + /* Store the WIFI-SSID string in NVS */ + err = nvs_set_str(my_handle, (const char*)NVM_WIFI_SSID_KEY, (const char*)ssid); + + /* If WIFI-SSID stored successfully in NVS then ---> Store the WIFI-PSWD string in NVS */ + if(ESP_OK == err) + err = nvs_set_str(my_handle, (const char*)NVM_WIFI_PSWD_KEY, (const char*)pswd); + + /* Inform the user with one of these logs to indicate failing or succeeding to store wifi-credentials */ + if (err != ESP_OK) + { + ESP_LOGE(TAG,"Failed"); + } + else + { + ESP_LOGI(TAG,"Done"); + } + + // 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"); + } + else + { + ESP_LOGI(TAG,"Done"); + } + } + + // Close the opened NVS + nvs_close(my_handle); +} +#endif + + +/**/ +void nvm_write_history_data(historyLog_t* history_data) +{ + esp_err_t err; + size_t required_size = 0; // value will default to 0, if not set yet in NVS + + char key_str[18]; + uint8_t sector_counter = 1; + + /*Storing the on-boarding value in the NVS so that to indicate that each time power on/off the value would be the same*/ + //ESP_LOGI(TAG,"Opening Non-Volatile Storage (NVS) handle... "); + err = nvs_open("storage", NVS_READWRITE, &my_handle); + + /* Make sure that NVS is initialized */ + if(ESP_ERR_NVS_NOT_INITIALIZED == err) + { + /* initialized it if not */ + err = nvs_flash_init(); + + /* Check if the response is OK then OPEN it */ + if(err == ESP_OK) + { + /* Retry to open the NVS */ + err = nvs_open("storage", NVS_READWRITE, &my_handle); + } + else + { + /* Print this LOG if failed to initialize it */ + ESP_LOGE(TAG,"Failed to reinitialized The NVS partition"); + } + } + + /* Make sure that NVS storage has been opened successfully */ + if (err != ESP_OK) + { + ESP_LOGE(TAG,"Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + } + else + { + do + { + /* Setup the NVS sector to write to */ + sprintf(key_str,NVM_HISTORY_DATA_KEY,sector_counter); + + /* get the size of the history space */ + err = nvs_get_blob(my_handle, key_str, NULL, &required_size); + + /* Check if we can store data at that sector or not*/ + if(required_size >= (NVM_MAX_NUMBER_OF_IN_ONE_SECTOR*sizeof(historyLog_t))) + { + /* Reset the required_size again to 0 */ + required_size = 0; + + /* if not then check the next sector */ + sector_counter++; + } + else + { + /* if yes then increase the size of data to be stored by one element */ + required_size += sizeof(historyLog_t); + break; + } + }while(sector_counter <= NVM_NUMBER_OF_SECTORS); + + /* Make sure that there are some free sectors to store data in */ + if(sector_counter <= NVM_NUMBER_OF_SECTORS) + { + /* set the new values for the history data */ + err = nvs_set_blob(my_handle, key_str, history_data, required_size); + + if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) + ESP_LOGI(TAG," ---> History space not there"); + + if (required_size == 0) + { + ESP_LOGI(TAG," ---> Nothing saved yet"); + } + else + { + // Commit + err = nvs_commit(my_handle); + if (err == ESP_OK) + ESP_LOGI(TAG," ---> the new history data has been committed successfully to nvm-sector %d\nand its size now is %d (max allowed size is %d)",sector_counter,required_size,(NVM_MAX_NUMBER_OF_IN_ONE_SECTOR*sizeof(historyLog_t))); + } + } + else + { + ESP_LOGE(TAG," ----> All the NVM sectors are full. can't store additional data"); + } + } + + // Close + nvs_close(my_handle); +} + +/**/ +size_t nvm_read_history_data(historyLog_t* history_data, uint8_t sector_number) +{ + esp_err_t err; + size_t required_size = 0; // value will default to 0, if not set yet in NVS + + char key_str[18]; + + /*Storing the on-boarding value in the NVS so that to indicate that each time power on/off the value would be the same*/ + //ESP_LOGI(TAG,"Opening Non-Volatile Storage (NVS) handle... "); + err = nvs_open("storage", NVS_READWRITE, &my_handle); + + /* Make sure that NVS is initialized */ + if(ESP_ERR_NVS_NOT_INITIALIZED == err) + { + /* initialized it if not */ + err = nvs_flash_init(); + + /* Check if the response is OK then OPEN it */ + if(err == ESP_OK) + { + /* Retry to open the NVS */ + err = nvs_open("storage", NVS_READWRITE, &my_handle); + } + else + { + /* Print this LOG if failed to initialize it */ + ESP_LOGI(TAG,"Failed to reinitialized The NVS partition"); + } + } + + if (err != ESP_OK) + { + ESP_LOGI(TAG,"Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + } + else + { + /* Setup the NVS sector to write to */ + sprintf(key_str,NVM_HISTORY_DATA_KEY,sector_number); + + // obtain required memory space to store blob being read from NVS + err = nvs_get_blob(my_handle, key_str, NULL, &required_size); + + if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) + ESP_LOGI(TAG," ---> History not found yet"); + + //ESP_LOGI(TAG,"History data are:"); + + if (required_size == 0) + { + ESP_LOGI(TAG," ---> Nothing saved yet!\n"); + } + else + { + err = nvs_get_blob(my_handle, key_str, history_data, &required_size); + if (err != ESP_OK) + { + ESP_LOGI(TAG," ---> ERROR in retrieving history data for NVM sector %d",sector_number); + } + } + } + + // Close + nvs_close(my_handle); + + return required_size; +} + +/**/ +void nvm_clear_history_sector(uint8_t sector_num) +{ + esp_err_t wifi_err; + uint8_t wifi_onboarding; + uint8_t cell_onboarding; + uint8_t comms_mode; + char ssid[32] = {0}; + char pswd[64] = {0}; + + ESP_LOGI(TAG,"Clearing NVM History Sectors After successful POST process"); + + /* If there are some written data in the first sector ---> then this means that you should clear it. otherwise no (just to save read-write cycles for nvs) */ + if(nvm_read_history_data(NULL,1)) + { + /* 1- Firstly retrieve stored data in NVS */ + wifi_onboarding = nvm_read_onboarding_flag(WIFI_ONBOARDING_KEY); + cell_onboarding = nvm_read_onboarding_flag(CELL_ONBOARDING_KEY); + comms_mode = nvm_read_comms_mode(); + wifi_err = nvm_read_wifi_credentials(ssid,pswd); + + /* Reload the WDT */ + vTaskDelay(10/portTICK_PERIOD_MS); + + /* 2- Clear NVS partition */ + nvm_clear(); + + /* Reload the WDT */ + vTaskDelay(10/portTICK_PERIOD_MS); + + /* 3- Store the retrieved data again in NVS */ + nvm_write_onboarding_flag(WIFI_ONBOARDING_KEY,wifi_onboarding); + nvm_write_onboarding_flag(CELL_ONBOARDING_KEY,cell_onboarding); + nvm_write_comms_mode(comms_mode); + if(wifi_err == ESP_OK) + nvm_write_wifi_credentials(ssid,32,pswd,64); + } + + +#if 0 + esp_err_t err; + char key_str[18]; + + /*Storing the on-boarding value in the NVS so that to indicate that each time power on/off the value would be the same*/ + ESP_LOGI(TAG,"Opening Non-Volatile Storage (NVS) handle... "); + err = nvs_open("storage", NVS_READWRITE, &my_handle); + + /* Make sure that NVS is initialized */ + if(ESP_ERR_NVS_NOT_INITIALIZED == err) + { + /* initialized it if not */ + err = nvs_flash_init(); + + /* Check if the response is OK then OPEN it */ + if(err == ESP_OK) + { + /* Retry to open the NVS */ + err = nvs_open("storage", NVS_READWRITE, &my_handle); + } + else + { + /* Print this LOG if failed to initialize it */ + ESP_LOGI(TAG,"Failed to reinitialized The NVS partition"); + } + } + + if (err != ESP_OK) + { + ESP_LOGI(TAG,"Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + } + else + { + for(int sector_counter=1; sector_counter<=NVM_NUMBER_OF_SECTORS; sector_counter++) + { + /* Setup the NVS sector to clear */ + sprintf(key_str,NVM_HISTORY_DATA_KEY,sector_counter); + + /* Check which sector number need to be cleared */ + if((sector_num == sector_counter) || ((sector_num == NVM_HISTORY_ALL_SECTORS))) + { + /* set the new values for the history data */ + err = nvs_set_blob(my_handle, key_str, NULL, 0); + if(err != ESP_OK) + { + ESP_LOGI(TAG,"Failed to clear sector %d",sector_counter); + break; + } + else + { + /* Check if the sector_num isn't equal to the NVM_HISTORY_ALL_SECTORS then break the loop after first successful clear process */ + if(sector_num != NVM_HISTORY_ALL_SECTORS) + { + break; + } + } + } + } +#endif +#if 0 + if((sector_num == NVM_HISTORY_SECTOR_1) || ((sector_num == NVM_HISTORY_ALL_SECTORS))) + { + /* set the new values for the history data */ + err = nvs_set_blob(my_handle, NVM_HISTORY_SECTOR1, NULL, 0); + if(err != ESP_OK) + { + ESP_LOGI(TAG,"Failed to clear sector 1"); + } + } + + if((err == ESP_OK) && ((sector_num == NVM_HISTORY_SECTOR_2) || (sector_num == NVM_HISTORY_ALL_SECTORS))) + { + /* set the new values for the history data */ + err = nvs_set_blob(my_handle, NVM_HISTORY_SECTOR2, NULL, 0); + if(err != ESP_OK) + { + ESP_LOGI(TAG,"Failed to clear sector 2"); + } + } + + if((err == ESP_OK) && ((sector_num == NVM_HISTORY_SECTOR_3) || (sector_num == NVM_HISTORY_ALL_SECTORS))) + { + /* set the new values for the history data */ + err = nvs_set_blob(my_handle, NVM_HISTORY_SECTOR3, NULL, 0); + if(err != ESP_OK) + { + ESP_LOGI(TAG,"Failed to clear sector 3"); + } + } + + if((err == ESP_OK) && ((sector_num == NVM_HISTORY_SECTOR_4) || (sector_num == NVM_HISTORY_ALL_SECTORS))) + { + /* set the new values for the history data */ + err = nvs_set_blob(my_handle, NVM_HISTORY_SECTOR4, NULL, 0); + if(err != ESP_OK) + { + ESP_LOGI(TAG,"Failed to clear sector 4"); + } + } + + if((err == ESP_OK) && ((sector_num == NVM_HISTORY_SECTOR_5) || (sector_num == NVM_HISTORY_ALL_SECTORS))) + { + /* set the new values for the history data */ + err = nvs_set_blob(my_handle, NVM_HISTORY_SECTOR5, NULL, 0); + if(err != ESP_OK) + { + ESP_LOGI(TAG,"Failed to clear sector 5"); + } + } + + if((err == ESP_OK) && ((sector_num == NVM_HISTORY_SECTOR_6) || (sector_num == NVM_HISTORY_ALL_SECTORS))) + { + /* set the new values for the history data */ + err = nvs_set_blob(my_handle, NVM_HISTORY_SECTOR6, NULL, 0); + if(err != ESP_OK) + { + ESP_LOGI(TAG,"Failed to clear sector 6"); + } + } + + if((err == ESP_OK) && ((sector_num == NVM_HISTORY_SECTOR_7) || (sector_num == NVM_HISTORY_ALL_SECTORS))) + { + /* set the new values for the history data */ + err = nvs_set_blob(my_handle, NVM_HISTORY_SECTOR7, NULL, 0); + if(err != ESP_OK) + { + ESP_LOGI(TAG,"Failed to clear sector 7"); + } + } + + if((err == ESP_OK) && ((sector_num == NVM_HISTORY_SECTOR_8) || (sector_num == NVM_HISTORY_ALL_SECTORS))) + { + /* set the new values for the history data */ + err = nvs_set_blob(my_handle, NVM_HISTORY_SECTOR8, NULL, 0); + if(err != ESP_OK) + { + ESP_LOGI(TAG,"Failed to clear sector 8"); + } + } +#endif +// err = nvs_commit(my_handle); +// } + + // Close +// nvs_close(my_handle); +} + +/**/ +uint8_t nvm_get_last_written_history_sector(void) +{ + esp_err_t err; + size_t required_size = 0; // value will default to 0, if not set yet in NVS + + char key_str[18]; + uint8_t sector_counter = 1; + + /*Storing the on-boarding value in the NVS so that to indicate that each time power on/off the value would be the same*/ + //ESP_LOGI(TAG,"Opening Non-Volatile Storage (NVS) handle... "); + err = nvs_open("storage", NVS_READWRITE, &my_handle); + + /* Make sure that NVS is initialized */ + if(ESP_ERR_NVS_NOT_INITIALIZED == err) + { + /* initialized it if not */ + err = nvs_flash_init(); + + /* Check if the response is OK then OPEN it */ + if(err == ESP_OK) + { + /* Retry to open the NVS */ + err = nvs_open("storage", NVS_READWRITE, &my_handle); + } + else + { + /* Print this LOG if failed to initialize it */ + ESP_LOGI(TAG,"Failed to reinitialized The NVS partition"); + } + } + + if (err != ESP_OK) + { + ESP_LOGI(TAG,"Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + } + else + { + do + { + /* Setup the key of NVS sector to read the size of it's written data */ + sprintf(key_str,NVM_HISTORY_DATA_KEY,sector_counter); + + /* get the size of the history space */ + err = nvs_get_blob(my_handle, key_str, NULL, &required_size); + + /* Check if that sector is not full */ + if(required_size >= (NVM_MAX_NUMBER_OF_IN_ONE_SECTOR*sizeof(historyLog_t))) + { + /* Reset the required_size again to 0 */ + required_size = 0; + + /* if full then check the next sector */ + sector_counter++; + } + else + { + /* if not full then return the sector_number */ + break; + } + }while(sector_counter <= NVM_NUMBER_OF_SECTORS); + + if(sector_counter > NVM_NUMBER_OF_SECTORS) + { + /* Setup the key of NVS sector to read it's written data */ + sector_counter--; + } + } + + // Close + nvs_close(my_handle); + + return sector_counter; +} + + + +/**/ +void nvm_set_last_posted_history_sector(uint8_t sector_num) +{ + if(sector_num <= NVM_NUMBER_OF_SECTORS) + last_posted_sector = sector_num; + else + last_posted_sector = 0; +} + +/**/ +uint8_t nvm_get_last_posted_history_sector(void) +{ + return last_posted_sector; +} + + + + diff --git a/main/nvm.h b/main/nvm.h new file mode 100644 index 0000000..cbf0e83 --- /dev/null +++ b/main/nvm.h @@ -0,0 +1,74 @@ +/* + * nvm.h + * + * Created on: Aug 14, 2023 + * Author: Sword + */ + +#ifndef MAIN_NVM_H_ +#define MAIN_NVM_H_ + +#include "data_processing.h" + +#define NVM_CELL_ONBOARDING_KEY "on_boarding" +#define NVM_WIFI_ONBOARDING_KEY "wifi_onboarding" +#define NVM_WIFI_SSID_KEY "wifi_ssid" +#define NVM_WIFI_SSID_LENGTH_KEY "wifi_ssid_len" +#define NVM_WIFI_PSWD_KEY "wifi_pswd" +#define NVM_WIFI_PSWD_LENGTH_KEY "wifi_pswd_len" +#define NVM_COMMS_MODE_KEY "commsMode" +#define NVM_HISTORY_DATA_KEY "history_data%u" + +#define NVM_HISTORY_SECTOR1 "history_data1" +#define NVM_HISTORY_SECTOR2 "history_data2" +#define NVM_HISTORY_SECTOR3 "history_data3" +#define NVM_HISTORY_SECTOR4 "history_data4" +#define NVM_HISTORY_SECTOR5 "history_data5" +#define NVM_HISTORY_SECTOR6 "history_data6" +#define NVM_HISTORY_SECTOR7 "history_data7" +#define NVM_HISTORY_SECTOR8 "history_data8" + +#define NVM_ONBOARDING_SET_VAL 1 +#define NVM_ONBOARDING_NOT_SET_VAL 0 +#define NVM_ONBOARDING_NOT_STORED_VAL 2 + +#define NVM_MAX_NUMBER_OF_ALL_READINGS 750 //max number of sensor_readings in all NVM sector +#define NVM_MAX_NUMBER_OF_IN_ONE_SECTOR 20 //max number of readings in one NVM/NVS sector +#define NVM_NUMBER_OF_SECTORS ((NVM_MAX_NUMBER_OF_ALL_READINGS) / NVM_MAX_NUMBER_OF_IN_ONE_SECTOR) // ----> number_of_sectors = max_num_readings / number of readings in one NVM sector +#define NVM_ONE_SECOTR_SIZE (37 * (NVM_NUMBER_OF_SECTORS)) + +#define NVM_HISTORY_ALL_SECTORS (NVM_NUMBER_OF_SECTORS+1) + +typedef enum +{ + WIFI_ONBOARDING_KEY, + CELL_ONBOARDING_KEY +}onboarding_type_t; + + +void nvm_init(void); +void nvm_clear(void); + + +uint8_t nvm_read_onboarding_flag(onboarding_type_t flag_key); +void nvm_write_onboarding_flag(onboarding_type_t flag_key, uint8_t flag_value); + + +uint8_t nvm_read_comms_mode(void); +void nvm_write_comms_mode(uint8_t commsMode); + + +esp_err_t nvm_read_wifi_credentials(char* ssid, char* pswd); +void nvm_write_wifi_credentials(char* ssid, uint8_t ssid_len, char* pswd, uint8_t pswd_len); + + +void nvm_write_history_data(historyLog_t* history_data); +size_t nvm_read_history_data(historyLog_t* history_data, uint8_t sector_number); +void nvm_clear_history_sector(uint8_t sector_num); +uint8_t nvm_get_last_written_history_sector(void); + + +void nvm_set_last_posted_history_sector(uint8_t sector_num); +uint8_t nvm_get_last_posted_history_sector(void); + +#endif /* MAIN_NVM_H_ */ diff --git a/main/ota.c b/main/ota.c new file mode 100644 index 0000000..d5b891a --- /dev/null +++ b/main/ota.c @@ -0,0 +1,424 @@ +/* + * ota.c + * + * Created on: Feb 13, 2023 + * Author: Sword + */ + + +/* OTA example + This example code is in the Public Domain (or CC0 licensed, at your option.) + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. + */ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_ota_ops.h" +#include "esp_app_format.h" +#include "esp_log.h" +#include "esp_flash_partitions.h" +#include "esp_partition.h" +#include "nvs.h" +#include "nvs_flash.h" +#include "driver/gpio.h" +#include "ota.h" + + +#define BUFFSIZE 2048 +#define HASH_LEN 32 /* SHA-256 digest length */ +#define LOG_LOCAL_LEVEL ESP_LOG_INFO + +static const char *TAG = "OTA"; + +/*an ota data write buffer ready to write to the flash*/ +static char *ota_write_data; +static ota_bytes_status_t ota_bytes = OTA_BYTES_WRITTEN_WAIT; +static uint32_t ota_file_size = OTA_WITH_SEQUENTIAL_WRITES; + +esp_ota_handle_t update_handle; +const esp_partition_t *update_partition = NULL; +const esp_partition_t *configured = NULL; +const esp_partition_t *running = NULL; + +static uint8_t ota_needed = OTA_NOT_NEEDED; +static int data_read = -1; +int binary_file_length; +bool image_header_was_checked; + +#define OTA_URL_SIZE 256 + + +/******************************************************/ +/********** Static Functions for OTA-Process **********/ +/******************************************************/ +static void print_sha256 (const uint8_t *image_hash, const char *label) +{ + char hash_print[HASH_LEN * 2 + 1]; + hash_print[HASH_LEN * 2] = 0; + for (int i = 0; i < HASH_LEN; ++i) { + sprintf(&hash_print[i * 2], "%02x", image_hash[i]); + } + ESP_LOGI(TAG, "%s: %s", label, hash_print); +} + +static void ota_start_session(void) +{ + /* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */ + update_handle = 0; + update_partition = NULL; + + /* Get partition info of currently configured boot app (the partition for current app)*/ + configured = esp_ota_get_boot_partition(); + /* Get partition info of currently running app (the partition for current app)*/ + running = esp_ota_get_running_partition(); + + + ESP_LOGI(TAG, "Starting OTA session"); + + /* The result of esp_ota_get_boot_partition() is usually the same as esp_ota_get_running_partition(). + * The two results are not equal if the configured boot partition does not contain a valid app (meaning that + * the running partition will be an app that the bootloader chose via fallback) */ + if (configured != running) { + ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x", + (unsigned int)configured->address, (unsigned int)running->address); + ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)"); + } + ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)", + running->type, running->subtype, (unsigned int)running->address); + + /* The following line returns the next OTA app partition which should be written with a new firmware*/ + /* So, update_partition is the partition where the new image will be written*/ + update_partition = esp_ota_get_next_update_partition(NULL); + + assert(update_partition != NULL); + ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x", + update_partition->subtype, (unsigned int)update_partition->address); + + /*deal with all receive packet*/ + image_header_was_checked = false; + +} + +static void ota_process_incoming_image(void) +{ + esp_err_t err; + + if ((data_read < 0) && (OTA_NEEDED == ota_needed)) { + ESP_LOGE(TAG, "Error: No OTA data have been received yet"); + } + else if ((data_read > 0) && (OTA_NEEDED == ota_needed)) { + if (image_header_was_checked == false) { + esp_app_desc_t new_app_info; + if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) { + // check current version with downloading + /* 1- Check the firmware version of the new fetched-app*/ + memcpy(&new_app_info, &ota_write_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t)); + ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version); + + /* 2- Check the firmware version of the current running app*/ + esp_app_desc_t running_app_info; + if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) { + ESP_LOGI(TAG, "Running firmware version: %s", running_app_info.version); + } + + /* 3- Check the firmware version of the last disabled/aborted/invalid app (which is an app that )*/ + const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition(); + esp_app_desc_t invalid_app_info; + if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) { + /*---> Stop OTA updating*/ + ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version); + } + + // check current version with last invalid partition + /* 4- Compare the new fetched-image with the last invalid-image*/ + if (last_invalid_app != NULL) { + if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) { + /*---> Stop OTA updating*/ + ESP_LOGW(TAG, "New version is the same as invalid version."); + ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version); + ESP_LOGW(TAG, "The firmware has been rolled back to the previous version."); + + /* EXIT OTA task*/ + ota_bytes = OTA_BYTES_WRITTEN_FAILED; + return; + } + } + + /* 5- Compare the new-fetched-image with the current running-image*/ + if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) { + /*---> Stop OTA updating*/ + ESP_LOGW(TAG, "Current running version is the same as a new. We will not continue the update."); + + /* EXIT OTA task*/ + ota_bytes = OTA_BYTES_WRITTEN_FAILED; + return; + } + + /* 7- Start an OTA-update writing to the specified partition. + The specified partition is erased to the specified image size. + If image size is not yet known, pass OTA_SIZE_UNKNOWN which will cause the entire partition to be erased.*/ + err = esp_ota_begin(update_partition, ota_file_size, &update_handle); + if (err != ESP_OK) { + /*---> Stop OTA updating*/ + ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err)); + esp_ota_abort(update_handle); + ESP_LOGI(TAG, "Aborting the update-data partition"); + + /* EXIT OTA task*/ + ota_bytes = OTA_BYTES_WRITTEN_FAILED; + return; + } + + /* 6- Set the image_header_checked flag to be true*/ + image_header_was_checked = true; + + ESP_LOGI(TAG, "esp_ota_begin succeeded"); + } else { + /*---> Stop OTA updating*/ + ESP_LOGE(TAG, "received package is not fit len"); + esp_ota_abort(update_handle); + ESP_LOGI(TAG, "Aborting the update-data partition"); + + /* EXIT OTA task*/ + ota_bytes = OTA_BYTES_WRITTEN_FAILED; + return; + } + } + /* 8- Write OTA update data to partition. + * This function can be called multiple times as data is received during the OTA operation. + * Data is written sequentially to the partition */ + err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read); + if (err != ESP_OK) { + /*---> Stop OTA updating*/ + esp_ota_abort(update_handle); + ESP_LOGI(TAG, "Aborting the update-data partition"); + + /* EXIT OTA task*/ + ota_bytes = OTA_BYTES_WRITTEN_FAILED; + return; + } + /* 9- Set the binary file length*/ + binary_file_length += data_read; + /* Reset data_read variable to 0*/ + data_read = 0; + ESP_LOGD(TAG, "Written bytes length %d", binary_file_length); + ota_bytes = OTA_BYTES_WRITTEN_SUCCESS; + } + else if ((data_read == 0) && (OTA_FINISHED == ota_needed)) { + ESP_LOGI(TAG, "OTA session finished"); + ota_bytes = OTA_BYTES_WRITTEN_SUCCESS; + return; + } +} + +static void ota_end_session(void) +{ + esp_err_t err; + if(OTA_BYTES_WRITTEN_SUCCESS == ota_bytes) + { + ESP_LOGI(TAG, "Total Write binary data length: %d", binary_file_length); + /* 10- Checks if entire data in the response has been read without any error */ + if ( OTA_FINISHED != ota_needed ) { + ESP_LOGI(TAG, "OTA session stopped for errors but didn't get finished"); + esp_ota_abort(update_handle); + ESP_LOGI(TAG, "Aborting the update-data partition"); + /* EXIT OTA task*/ + return ; + } + + /* 11- Finish OTA update and validate newly written app image*/ + err = esp_ota_end(update_handle); + if (err != ESP_OK) { + /*---> Stop OTA updating*/ + if (err == ESP_ERR_OTA_VALIDATE_FAILED) { + ESP_LOGI(TAG, "Image validation failed, image is corrupted"); + } else { + ESP_LOGI(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err)); + } + /* EXIT OTA task*/ + return ; + } + + /* 12- Set the new validated-written image to be the boot partition (Configure OTA data for a new boot partition) + * On the next restart, ESP will boot from that new partition*/ + err = esp_ota_set_boot_partition(update_partition); + if (err != ESP_OK) { + /*Stop OTA update*/ + ESP_LOGI(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err)); + + /* EXIT OTA task*/ + return ; + } + /* 13- Restart the device (ESP32)*/ + ESP_LOGI(TAG, "Prepare to restart system!"); + esp_restart(); + return ; + } + else if(OTA_BYTES_WRITTEN_FAILED == ota_bytes) + { + ESP_LOGI(TAG,"No Changing in boot_partition"); + } +} + +static bool diagnostic(void) +{ + /* gpio_config_t io_conf; + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pin_bit_mask = (1ULL << CONFIG_EXAMPLE_GPIO_DIAGNOSTIC); + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_ENABLE; + gpio_config(&io_conf); + + ESP_LOGI(TAG, "Diagnostics (5 sec)..."); + vTaskDelay(5000 / portTICK_PERIOD_MS); + + bool diagnostic_is_ok = gpio_get_level(CONFIG_EXAMPLE_GPIO_DIAGNOSTIC); + + gpio_reset_pin(CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);*/ + return true; +} + + +void ota_set_needed(void) +{ + ota_needed = OTA_NEEDED; +} + +void ota_set_not_needed(void) +{ + ota_needed = OTA_NOT_NEEDED; +} + +void ota_set_finished(void) +{ + ota_needed = OTA_FINISHED; +} + +/******************************************************/ +/* Functions that gonna be used in comms state machine*/ +/******************************************************/ +esp_err_t ota_init(uint32_t fileSize) +{ + /*This function should be invoked in the ota_file_open_cb() in comms.c (instead of the OTA_Offline_Init())*/ + uint8_t sha_256[HASH_LEN] = { 0 }; + esp_partition_t partition; + + // get sha256 digest for the partition table + partition.address = ESP_PARTITION_TABLE_OFFSET; + partition.size = ESP_PARTITION_TABLE_MAX_LEN; + partition.type = ESP_PARTITION_TYPE_DATA; + esp_partition_get_sha256(&partition, sha_256); + print_sha256(sha_256, "SHA-256 for the partition table: "); + + // get sha256 digest for bootloader + partition.address = ESP_BOOTLOADER_OFFSET; + partition.size = ESP_PARTITION_TABLE_OFFSET; + partition.type = ESP_PARTITION_TYPE_APP; + esp_partition_get_sha256(&partition, sha_256); + print_sha256(sha_256, "SHA-256 for bootloader: "); + + // get sha256 digest for running partition + esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256); + print_sha256(sha_256, "SHA-256 for current firmware: "); + + const esp_partition_t *running = esp_ota_get_running_partition(); + esp_ota_img_states_t ota_state; + if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) { + if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) { + // run diagnostic function ... + bool diagnostic_is_ok = diagnostic(); + if (diagnostic_is_ok) { + ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution ..."); + esp_ota_mark_app_valid_cancel_rollback(); + } else { + ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version ..."); + esp_ota_mark_app_invalid_rollback_and_reboot(); + + } + } + } + + /*set the ota file size */ + if(0 == fileSize) + fileSize = OTA_WITH_SEQUENTIAL_WRITES; + else + ota_file_size = fileSize; + + /* Initialize OTA session*/ + ota_start_session(); + + /* */ + return ESP_OK; +} + +esp_err_t ota_get_data_to_buffer(const char* buf, int buf_len) +{ + /* This function should be called in comms.c inside the callback function ota_file_data_cb() */ + /* This function should copy the bytes that we get from qfread command via uart*/ + /* handle of qfread cmd stores the incoming bytes from modem via uart in buf */ + /* After that we gonna use ota-buffer in ota_process_incoming_image() function to start ota-session*/ + + esp_err_t retval = ESP_FAIL; + int index = 0; + + /*Check if the number of bytes to be processed is greater than ota-buffer size*/ + /*if(BUFFSIZE < buf_len) + { + ESP_LOGI(TAG, "Number of bytes to be processed is greater than the size of OTA buffer"); + return retval; + }*/ + + /* Set the status of ota-bytes to be processed ---> OTA_BYTES_WRITTEN_WAIT*/ + ota_bytes = OTA_BYTES_WRITTEN_WAIT; + + /* Reset the number of bytes that have been read to 0 (because the initial value is -1)*/ + data_read = 0; + + /*Copy the incoming bytes (from uart) to ota-buffer */ + //for(index = 0; index < buf_len; index++) + { + ota_write_data = (char*)buf; + data_read = buf_len; + } + + /*Start OTA_Writing session*/ + ota_set_needed(); + + /*OTA process and writing for the copied bytes*/ + ota_process_incoming_image(); + + /* Check the status of the bytes after OTA-processing/OTA-writing */ + if(OTA_BYTES_WRITTEN_SUCCESS == ota_bytes) + retval = ESP_OK; + else if(OTA_BYTES_WRITTEN_FAILED == ota_bytes) + retval = ESP_FAIL; + + return retval; +} + +void ota_finish_processing(void) +{ + /*This function should be invoked in the COMMS_STATE_WAIT_MCU_UPDATE state in comms state machine + * (instead of the OTA_terminate_connection() )*/ + + /* Reset data_read variable to 0*/ + data_read = 0; + + /* Rise ota_finish flag*/ + ota_set_finished(); + + /* Rise the bytes_success flag*/ + ota_process_incoming_image(); + + /* End the ota_session by validating the image and then rebooting */ + ota_end_session(); +} + + + + diff --git a/main/ota.h b/main/ota.h new file mode 100644 index 0000000..df3ae5d --- /dev/null +++ b/main/ota.h @@ -0,0 +1,32 @@ +/* + * ota.h + * + * Created on: Feb 13, 2023 + * Author: Sword + */ + +#ifndef MAIN_OTA_H_ +#define MAIN_OTA_H_ + +typedef enum{ + OTA_NEEDED, + OTA_NOT_NEEDED, + OTA_FINISHED +}ota_status_t; + + +typedef enum{ + OTA_BYTES_WRITTEN_SUCCESS, + OTA_BYTES_WRITTEN_FAILED, + OTA_BYTES_WRITTEN_WAIT +}ota_bytes_status_t; + +void ota_set_needed(void); +void ota_set_not_needed(void); +void ota_set_finished(void); + +esp_err_t ota_init(uint32_t fileSize); +esp_err_t ota_get_data_to_buffer(const char* buf, int buf_len); +void ota_finish_processing(void); + +#endif /* MAIN_OTA_H_ */ diff --git a/main/port.c b/main/port.c new file mode 100644 index 0000000..87f8d6c --- /dev/null +++ b/main/port.c @@ -0,0 +1,258 @@ +/* + * port.c + * + * Created on: Jan 16, 2023 + * Author: Partha + */ + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/rtc_io.h" +#include "freertos/queue.h" +#include "driver/gpio.h" +#include "port.h" +#include "uart_ifx.h" + +static const char* TAG = "PORT"; + +#define LOG_LOCAL_LEVEL ESP_LOG_INFO +#include "esp_log.h" + +#define ESP_INTR_FLAG_DEFAULT 0 + +#define LED_BLUE_PIN 26 +#define LED_RED_PIN 20 +#define LED_GREEN_PIN 21 + +#define MODEM_LDO_EN_PIN 33 + +#define RAD_STATUS_PIN 19 + +#define USB_CHG_STATUS_PIN 34 + +#define LIGHT_SENSOR_EN_PIN 35 +#define ACCEL_PWN_PIN 11 + +#define ADC_BATT_MON_PIN 1 +#define ADC_LIGHT_SEN_PIN 2 + +#define VBAT_SENSE_EN_PIN 42 + +#define LOWV_DETECT_PIN 38 + +#define PIN_MASK(x) (1ULL << x) + +extern void IRAM_ATTR accel_int1_isr(void *args); +extern void IRAM_ATTR vusb_detect_isr(void *args); +extern void IRAM_ATTR lowv_detect_isr(void *args); +extern void IRAM_ATTR push_button_isr(void *args); + +static bool red_led_status = 0; +static bool blue_led_status = 0; +static bool green_led_status = 0; + +static void init_outputs(void) +{ + gpio_config_t io_conf = {0}; + + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = PIN_MASK(LED_RED_PIN) | \ + PIN_MASK(LED_BLUE_PIN) | \ + PIN_MASK(LED_GREEN_PIN) | \ + PIN_MASK(VBAT_SENSE_EN_PIN); + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; + + ESP_ERROR_CHECK(gpio_config(&io_conf)); + + //Initialize all Outputs to LOW + gpio_set_level(LED_RED_PIN, 1); + gpio_set_level(LED_BLUE_PIN, 1); + gpio_set_level(LED_GREEN_PIN, 1); + gpio_set_level(VBAT_SENSE_EN_PIN, 0); +} + +static void init_inputs(void) +{ + gpio_config_t io_conf = {0}; + + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pin_bit_mask = PIN_MASK(ADC_BATT_MON_PIN) | \ + PIN_MASK(ADC_LIGHT_SEN_PIN) | \ + /* PIN_MASK(ALER1_IN_PIN) | \*/ + PIN_MASK(ALER2_IN_PIN) | \ + PIN_MASK(ALER3_IN_PIN) | \ + PIN_MASK(ALER4_IN_PIN) | \ + PIN_MASK(RAD_STATUS_PIN) | \ + PIN_MASK(USB_CHG_STATUS_PIN); + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; + + ESP_ERROR_CHECK(gpio_config(&io_conf)); +} + +static void init_isrs(void) +{ + /*gpio_config_t io_conf = {0}; + + io_conf.intr_type = GPIO_INTR_ANYEDGE; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pin_bit_mask = PIN_MASK(ACCEL_INT1_PIN) | \ + PIN_MASK(VUSB_DETECT_PIN) | \ + PIN_MASK(LOWV_DETECT_PIN) | \ + PIN_MASK(SWITCH_INPUT_PIN); + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; + ESP_ERROR_CHECK(gpio_config(&io_conf)); + + gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); + gpio_isr_handler_add(ACCEL_INT1_PIN, accel_int1_isr, (void *)ACCEL_INT1_PIN); + gpio_isr_handler_add(VUSB_DETECT_PIN, vusb_detect_isr, (void *)VUSB_DETECT_PIN); + gpio_isr_handler_add(LOWV_DETECT_PIN, lowv_detect_isr, (void *)LOWV_DETECT_PIN); + gpio_isr_handler_add(SWITCH_INPUT_PIN, push_button_isr, (void *)SWITCH_INPUT_PIN);*/ +} + +void port_init(void) +{ + init_outputs(); +// init_inputs(); +// init_isrs(); + +// gpio_deep_sleep_hold_dis(); + /*rtc_gpio_init(LED_BLUE_PIN); + rtc_gpio_set_direction(LED_BLUE_PIN, RTC_GPIO_MODE_OUTPUT_ONLY); + rtc_gpio_pulldown_dis(LED_BLUE_PIN); + rtc_gpio_pullup_dis(LED_BLUE_PIN); + rtc_gpio_set_level(LED_BLUE_PIN, 1);*/ +} + +void port_red_led_on(void) +{ + gpio_set_level(LED_RED_PIN, 0); + red_led_status = 1; +} +void port_red_led_off(void) +{ + gpio_set_level(LED_RED_PIN, 1); + red_led_status = 0; +} + +void port_green_led_off(void) +{ + gpio_set_level(LED_GREEN_PIN, 1); + green_led_status = 0; +} + +void port_green_led_on(void) +{ + gpio_set_level(LED_GREEN_PIN, 0); + green_led_status = 1; +} + +void port_red_led_toggle(void) +{ + if(red_led_status) + { + port_red_led_off(); + } + else + { + port_red_led_on(); + } +} + +bool port_red_led_is_on(void) +{ + return red_led_status; +} + +void port_blue_led_on(void) +{ + gpio_set_level(LED_BLUE_PIN, 0); + blue_led_status = 1; +} +void port_blue_led_off(void) +{ + gpio_set_level(LED_BLUE_PIN, 1); + blue_led_status = 0; +} + +int port_is_pushbuttonNotPressed(void) +{ + return gpio_get_level(SWITCH_INPUT_PIN); +} + +void port_blue_led_toggle(void) +{ + if(blue_led_status) + { + port_blue_led_off(); + } + else + { + port_blue_led_on(); + } +} + +bool port_blue_led_is_on(void) +{ + return blue_led_status; +} + +/*void port_modem_assert_pwrkey(void) +{ + gpio_set_level(UD_RAD_PWRKEY_PIN, 1); +} + +void port_modem_deassert_pwrkey(void) +{ + gpio_set_level(UD_RAD_PWRKEY_PIN, 0); +}*/ + +int port_modem_is_on(void) +{ + return gpio_get_level(RAD_STATUS_PIN); +} + +int port_is_usb_connected(void) +{ + return gpio_get_level(VUSB_DETECT_PIN); +} + +int port_is_charging(void) +{ + return gpio_get_level(USB_CHG_STATUS_PIN); +} + +void port_vbatt_sense_enable(int en) +{ + gpio_set_level(VBAT_SENSE_EN_PIN, en); +} + +void port_accel_pwr_enable(int en) +{ + gpio_set_level(ACCEL_PWN_PIN, en); +} + +void port_light_sensor_enable(int en) +{ + gpio_set_level(LIGHT_SENSOR_EN_PIN, en); +} + +void port_modem_ldo_pin(uint8_t en) +{ + gpio_set_level(MODEM_LDO_EN_PIN, en); +} + +int port_usb_charge_status(void) +{ + return gpio_get_level(USB_CHG_STATUS_PIN); +} + + + diff --git a/main/port.h b/main/port.h new file mode 100644 index 0000000..b72d457 --- /dev/null +++ b/main/port.h @@ -0,0 +1,55 @@ +/* + * port.h + * + * Created on: Jan 16, 2023 + * Author: Partha + */ + +#ifndef MAIN_PORT_H_ +#define MAIN_PORT_H_ + +#include +#include "sdkconfig.h" +#include "stdbool.h" + +#define ALER1_IN_PIN 4 +#define ALER2_IN_PIN 6 +#define ALER3_IN_PIN 7 +#define ALER4_IN_PIN 37 + +#define SWITCH_INPUT_PIN 5 + +#define ACCEL_INT1_PIN 9 +#define VUSB_DETECT_PIN 10 + +void port_init(void); + +uint8_t *port_get_isr(void); + +void port_red_led_on(void); +void port_red_led_off(void); +void port_red_led_toggle(void); +bool port_red_led_is_on(void); + +void port_blue_led_on(void); +void port_blue_led_off(void); +void port_blue_led_toggle(void); +bool port_blue_led_is_on(void); + +void port_green_led_on(void); +void port_green_led_off(void); + +void port_modem_assert_pwrkey(void); +void port_modem_deassert_pwrkey(void); +int port_is_usb_connected(void); +int port_modem_is_on(void); +int port_is_charging(void); +int port_is_pushbuttonNotPressed(void); +void port_vbatt_sense_enable(int en); +void port_accel_pwr_enable(int en); +void port_light_sensor_enable(int en); +void port_modem_ldo_pin(uint8_t en); +int port_usb_charge_status(void); + + +#endif /* MAIN_PORT_H_ */ diff --git a/main/rtc.c b/main/rtc.c new file mode 100644 index 0000000..022997c --- /dev/null +++ b/main/rtc.c @@ -0,0 +1,437 @@ +/* + * rtc.c + * + * Created on: Jan 16, 2023 + * Author: Sword + */ +#include "rtc.h" +#include +#include +#include "esp_sntp.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "lwip/sockets.h" +#include "lwip/err.h" +#include "lwip/netdb.h" +#include "lwip/icmp.h" +#include "esp_system.h" +#include "esp_log.h" +#include +#include +#include +#include "nvm.h" +#include "nvs_flash.h" + + + +/** Date and time set to: 1st of December 2014, 23 hour 59 min and 31 seconds */ +#define SET_HOUR 13 /*< Set hour to the RTC */ +#define SET_MINUTE 0 /*< Set minute to the RTC */ +#define SET_SECOND 0 /*< Set second to the RTC */ +#define SET_WEEKDAY 3 /*< Set weekday to the RTC */ +#define SET_YEARDAY 16 /*< day in the year, range 0 to 365 */ +#define SET_DAY 26 /*< Set day to the RTC */ +#define SET_MONTH 2 /*< Set month to the RTC */ +#define SET_YEAR 2025 /*< Set year to the RTC */ + +#define RTC_SUBSECOND_COUNTER_LOAD_VALUE 32767 + +static const char* TAG = "RTC"; + +#define LOG_LOCAL_LEVEL ESP_LOG_INFO +#include "esp_log.h" + +#include "esp_sntp.h" + +static bool rtc_set = false; + +#define PING_HOST "8.8.8.8" // Google DNS +#define CHECK_INTERVAL 1 //60000 // Check every 60 seconds +#define MAX_DISCONNECT_TIME 5 //86400 // 24 hours in seconds +#define SEC 3600 +static time_t last_successful_ping = 0; + + +/// Internet monitor // + + +time_t get_sntp_time() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec; // Return UNIX timestamp +} + + +bool ping_test() +{ + struct sockaddr_in target_addr; + target_addr.sin_addr.s_addr = inet_addr(PING_HOST); + target_addr.sin_family = AF_INET; + target_addr.sin_port = htons(0); + + int sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (sock < 0) + { + ESP_LOGE(TAG, "Socket creation failed"); + return false; + } + + struct icmp_echo_hdr echo_req; + memset(&echo_req, 0, sizeof(echo_req)); + echo_req.id = htons(1); + echo_req.seqno = htons(1); + echo_req.type = ICMP_ECHO; + + if (sendto(sock, &echo_req, sizeof(echo_req), 0, (struct sockaddr *)&target_addr, sizeof(target_addr)) < 0) + { + ESP_LOGE(TAG, "Ping send failed"); + close(sock); + return false; + } + + fd_set read_fds; + struct timeval timeout = {1, 0}; // 1 second timeout + FD_ZERO(&read_fds); + FD_SET(sock, &read_fds); + + if (select(sock + 1, &read_fds, NULL, NULL, &timeout) > 0) + { + ESP_LOGI(TAG, "Internet is available"); + close(sock); + return true; + } + + ESP_LOGW(TAG, "Ping timeout, no internet"); + close(sock); + return false; +} + + +void internet_monitor_task(void *pvParameter) +{ + while (1) + { + time_t current_time = get_sntp_time(); + + if (ping_test()) + { + last_successful_ping = current_time; + } + + if (last_successful_ping > 0 && (current_time - last_successful_ping) > MAX_DISCONNECT_TIME) + { + ESP_LOGE(TAG, "No internet for %d hours, restarting...", (MAX_DISCONNECT_TIME/SEC)); + /* erase the NVS partition */ + ESP_ERROR_CHECK(nvs_flash_erase()); + esp_restart(); + } + + vTaskDelay(pdMS_TO_TICKS(CHECK_INTERVAL)); // Wait before next check + } +} + + +// END /// + +int app_rtc_init(void) +{ + int retval = RTC_STATUS_OK; + + //RTC_InitType RTC_Init_struct; + //RTC_StructInit(&RTC_Init_struct); + + //SysCtrl_PeripheralClockCmd(CLOCK_PERIPH_RTC, ENABLE); + /* Try to enable the down-counter to use as a millisecond counter, but + * does not seem to work without this __NOP delay. + */ + //RTC_Init_struct.RTC_TLR1 = RTC_SUBSECOND_COUNTER_LOAD_VALUE; + //RTC_Init(&RTC_Init_struct); + + /** Delay between two write in RTC->TCR register has to be + * at least 3 x 32k cycle + 2 CPU cycle. For that reason it + * is neccessary to add the delay. + */ + // for (volatile uint32_t i = 0; i < 600; i++) { + // __asm("NOP"); + // } + + //RTC_DateTimeType RTC_DateTime; + + struct tm rtc_time; + + /* Set the present time and date */ + /*RTC_DateTime.Second = SET_SECOND; + RTC_DateTime.Minute = SET_MINUTE; + RTC_DateTime.Hour = SET_HOUR; + RTC_DateTime.WeekDay = SET_WEEKDAY; + RTC_DateTime.MonthDay = SET_DAY; + RTC_DateTime.Month = SET_MONTH; + RTC_DateTime.Year = SET_YEAR; + RTC_SetTimeDate(&RTC_DateTime);*/ + + rtc_time.tm_sec = SET_SECOND; + rtc_time.tm_min = SET_MINUTE; + rtc_time.tm_hour = SET_HOUR; + rtc_time.tm_wday = SET_WEEKDAY; + rtc_time.tm_mday = SET_DAY; + rtc_time.tm_mon = SET_MONTH; + rtc_time.tm_year = SET_YEAR; + rtc_time.tm_yday = SET_YEARDAY; + + time_t t = mktime(&rtc_time); + struct timeval now = { .tv_sec = t }; + + settimeofday(&now, NULL); + + /* Enable RTC */ + // RTC_Cmd(ENABLE); + /* Enable RTC clockwatch */ + // RTC_ClockwatchCmd(ENABLE); + + rtc_set = false; + + return retval; +} + +void rtc_get_timestamp(rtc_timestamp_t* ts) +{ + if (ts) { + //RTC_DateTimeType RTC_DateTime; + //RTC_GetTimeDate(&RTC_DateTime); + /* RTC subsecond counter is a count-down counter */ + struct timeval tv; + gettimeofday(&tv, NULL); + + struct tm *info = localtime( &tv.tv_sec ); + + ts->Millisecond = 7+1000*(info->tm_sec); //((RTC_SUBSECOND_COUNTER_LOAD_VALUE - RTC_GetTimerValue()) * 1000 + RTC_SUBSECOND_COUNTER_LOAD_VALUE) / + ts->Second = info->tm_sec; + ts->Minute = info->tm_min; + ts->Hour = info->tm_hour; + ts->WeekDay = info->tm_wday; + ts->MonthDay = info->tm_mday; + ts->Month = info->tm_mon; + ts->Year = info->tm_year; + } +} + +void rtc_set_timestamp(rtc_timestamp_t* ts) +{ + if (ts) { + + rtc_set_timestamp_tz(ts, 0); + } +} + +/* Set timestamp including a timezone offset in seconds. +* The timezone offset may be positive or negative. It is applied +* by subtraction, so a -ve offset (e.g. UTC-5) results in the +* timezone offset being added to the local timestamp +*/ +void rtc_set_timestamp_tz(rtc_timestamp_t* ts, int32_t tz_offset_sec) +{ + if (ts) { + + struct tm buf; + buf.tm_year = ts->Year - 1900; + buf.tm_mon = ts->Month - 1; + buf.tm_mday = ts->MonthDay; + buf.tm_hour = ts->Hour; + buf.tm_min = ts->Minute; + buf.tm_sec = ts->Second; + time_t t = mktime(&buf); + + /* Apply offset */ + t -= tz_offset_sec; + /* + if (!rtc_is_set()) { + rtc_set_epoch((uint32_t)t); + } + */ + rtc_set_epoch((uint32_t)t); + } +} + +void rtc_set_epoch(uint32_t epoch) +{ + /* + * struct tm + Member Type Meaning Range + tm_sec int seconds after the minute 0 - 60 * + tm_min int minutes after the hour 0 - 59 + tm_hour int hours since midnight 0 - 23 + tm_mday int day of the month 1 - 31 + tm_mon int months since January 0 - 11 + tm_year int years since 1900 + tm_wday int days since Sunday 0 - 6 + tm_yday int days since January 1 0 - 365 + tm_isdst int Daylight Saving Time flag + */ + + struct tm* buf; + time_t t = epoch; + + buf = localtime(&t); + + ESP_LOGD(TAG,"rtc_set_epoch: %s\n", asctime(buf)); + + //RTC_DateTimeType RTC_DateTime; + // + ///* Set the present time and date */ + //RTC_DateTime.Second = buf->tm_sec; + //RTC_DateTime.Minute = buf->tm_min; + //RTC_DateTime.Hour = buf->tm_hour; + //RTC_DateTime.WeekDay = buf->tm_wday; + //RTC_DateTime.MonthDay = buf->tm_mday; + //RTC_DateTime.Month = buf->tm_mon + 1; + //RTC_DateTime.Year = buf->tm_year + 1900; + //RTC_SetTimeDate(&RTC_DateTime); + + time_t tmi = mktime(buf); + struct timeval now = { .tv_sec = tmi }; + + settimeofday(&now, NULL); + + rtc_set = true; +} + +uint32_t rtc_get_epoch(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + + struct tm *info = localtime( &tv.tv_sec ); + + struct tm buf; + buf.tm_year = info->tm_year - 1900; + buf.tm_mon = info->tm_mon - 1; + buf.tm_mday = info->tm_mday; + buf.tm_hour = info->tm_hour; + buf.tm_min = info->tm_min; + buf.tm_sec = info->tm_sec; + buf.tm_isdst = 0; + + time_t t = mktime(&buf); + + ESP_LOGD(TAG,"rtc_get_epoch: %s\n", ctime(&t)); + + return (uint32_t)t; +} + +bool rtc_is_set(void) +{ + return rtc_set; +} + + + +/////////////////////////////////////////////////////// +/* +void time_sync_notification_cb(struct timeval_1 *tv) { + ESP_LOGI(TAG, "Time synchronization event received"); +} +*/ + + +void obtain_time(void) { + ESP_LOGI(TAG, "Initializing SNTP"); + + const char *ntp_servers[] = { + "pool.ntp.org", + "time.google.com", + "1.pool.ntp.org" + }; + int server_index = 0; + int num_servers = sizeof(ntp_servers) / sizeof(ntp_servers[0]); + + time_t now = 0; + struct tm timeinfo = { 0 }; + + while (server_index < num_servers) { + ESP_LOGI(TAG, "Using NTP server: %s", ntp_servers[server_index]); + + esp_sntp_setoperatingmode(SNTP_OPMODE_POLL); + esp_sntp_setservername(0, ntp_servers[server_index]); + esp_sntp_init(); + + int retry = 0; + const int max_retries = 10; + + while (timeinfo.tm_year < (2020 - 1900) && retry < max_retries) { + ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry + 1, max_retries); + vTaskDelay(2000 / portTICK_PERIOD_MS); + time(&now); + localtime_r(&now, &timeinfo); + retry++; + } + + if (timeinfo.tm_year >= (2020 - 1900)) { + ESP_LOGI(TAG, "Time synchronized: %s", asctime(&timeinfo)); + + // create internet monitor task + xTaskCreate(&internet_monitor_task, "internet_monitor_task", 4096, NULL, 5, NULL); + return; // Exit the function since time is successfully obtained + } + + ESP_LOGE(TAG, "Failed to sync time with %s, switching to next server...", ntp_servers[server_index]); + esp_sntp_stop(); // Stop SNTP before switching + server_index++; // Move to the next server + } + + ESP_LOGE(TAG, "All NTP servers failed! Check network settings."); +} + +/*void obtain_time(void) { + ESP_LOGI(TAG, "Initializing SNTP"); + + esp_sntp_setoperatingmode(SNTP_OPMODE_POLL); + esp_sntp_setservername(0, "pool.ntp.org"); // Set NTP server + // sntp_set_time_sync_notification_cb(time_sync_notification_cb); + esp_sntp_init(); + + // Wait for time to be set + time_t now = 0; + struct tm timeinfo = { 0 }; + int retry = 0; + const int max_retries = 10; + + while (timeinfo.tm_year < (2020 - 1900) && ++retry < max_retries) { + ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, max_retries); + vTaskDelay(2000 / portTICK_PERIOD_MS); + time(&now); + localtime_r(&now, &timeinfo); + } + + if (retry >= max_retries) { + ESP_LOGE(TAG, "Failed to obtain time"); + } else { + ESP_LOGI(TAG, "Time synchronized"); + } +}*/ + +void get_time(char *time_str, size_t size) { // Buffer is provided by the caller + time_t now; + struct tm timeinfo; + + time(&now); + // ESP_LOGI(TAG, "Time print start %ld", now); + + localtime_r(&now, &timeinfo); + + const char *weekdays[] = {"0", "1", "2", "3", + "4", "5", "6"}; + + // Format time string + snprintf(time_str, size, "%04d/%02d/%02d_%02d:%02d:%02d_%s", + timeinfo.tm_year + 1900, + timeinfo.tm_mon + 1, timeinfo.tm_mday, + timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, + weekdays[timeinfo.tm_wday]); + + ESP_LOGI(TAG, "Formatted Time: %s", time_str); +} + + + +/////////////////////////////////////////////////////// \ No newline at end of file diff --git a/main/rtc.h b/main/rtc.h new file mode 100644 index 0000000..c191909 --- /dev/null +++ b/main/rtc.h @@ -0,0 +1,83 @@ +/* + * rtc.h + * + * Created on: Jan 16, 2023 + * Author: Sword + * APIs to access the RTC. + * + */ + + +#ifndef MAIN_RTC_H_ +#define MAIN_RTC_H_ + +#include +#include +#include "esp_sntp.h" + +#define RTC_STATUS_OK 0 +#define RTC_STATUS_ERROR -1 + +typedef struct +{ + uint16_t Millisecond; + + uint8_t Second; /*!< Specify the present second to be set to RTC clockwatch. + This parameter can be integer value in range from 0 to 59 */ + + uint8_t Minute; /*!< Specify the present minute to be set to RTC clockwatch. + This parameter can be integer value in range from 0 to 59 */ + + uint8_t Hour; /*!< Specify the present hour to be set to RTC clockwatch. + This parameter can be integer value in range from 0 to 23 */ + + uint8_t WeekDay; /*!< Specify the present day in the week to be set to RTC clockwatch. + This parameter can be an integer value in range from 0 to 7 */ + + uint8_t MonthDay; /*!< Specify the present day in the month to be set to RTC clockwatch. + This parameter can be an integer value in range from 1 to 31 (depends on month) */ + + uint8_t Month; /*!< Specify the present month to be set to RTC clockwatch. + This parameter can be an integer value in range from 1 to 12 (depends on month) */ + + uint16_t Year; /*!< Specify the present year to be set to RTC clockwatch. + This parameter can be an integer value in range from 0 to 3999 (depends on month) */ + +} rtc_timestamp_t; + +/* Initialize RTC. Must be called at each program startup to enable + * access to RTC. + * This will use a default time only once. Time is retained as long + * as backup power and 32 kHz OSC are running. + * Return true if RTC initialized from power-on-reset, false otherwise. + */ +int rtc_init(void); + +/* Get RTC time as a timestamp */ +void rtc_get_timestamp(rtc_timestamp_t* ts); +/* Set RTC time as a timestamp */ +void rtc_set_timestamp(rtc_timestamp_t* ts); + +/* Set timestamp including a timezone offset in seconds. +* The timezone offset may be positive or negative. It is applied +* by subtraction, so a -ve offset (e.g. UTC-5) results in the +* timezone offset being added to the local timestamp +*/ +void rtc_set_timestamp_tz(rtc_timestamp_t* ts, int32_t tz_offset_sec); + +/* true if rtc has been set by set_timestamp or set_epoch. */ +bool rtc_is_set(void); + +/* Get RTC time in units of epoch time (standard unix time: seconds since 1970) */ +uint32_t rtc_get_epoch(void); + +/* Set RTC current time according to epoch. Note, epoch is UTC/GMT. Should a "local" time be + * desired, subtract or add the necessary offset. E.g. MST is UTC-6 or -7 hours depending on DST. + */ +void rtc_set_epoch(uint32_t epoch); + + +void get_time(char *, size_t ); +void obtain_time(void); + +#endif /* MAIN_RTC_H_ */ diff --git a/main/uart_ifx.c b/main/uart_ifx.c new file mode 100644 index 0000000..f73ec7f --- /dev/null +++ b/main/uart_ifx.c @@ -0,0 +1,272 @@ +/* + * uart.c + * + * Created on: Jan 10, 2023 + * Author: Partha + */ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/uart.h" +#include "esp_log.h" +#include "uart_ifx.h" + +static const char *TAG = "UART_IFX"; +#define LOG_LOCAL_LEVEL ESP_LOG_INFO + +#define BUF_SIZE 4096 +#define RD_BUF_SIZE (BUF_SIZE) + +static QueueHandle_t uart0_queue; +static QueueHandle_t uart1_queue; + +static uint8_t buffer1[BUF_SIZE]; +static uint8_t buffer2[BUF_SIZE]; + +QueueHandle_t uart0_data_queue; +QueueHandle_t uart1_data_queue; + +bool uart1_rx_needed = false; + +static size_t buffer2_size = 0; + +static void uart0_event_task(void *pvParameters) +{ + uart_event_t event; + + for(;;) + { + //Waiting for UART event. + if(xQueueReceive(uart0_queue, (void * )&event, (TickType_t)portMAX_DELAY)) + { + bzero(buffer1, RD_BUF_SIZE); + ESP_LOGI(TAG, "uart[%d] event:", UART_NUM_0); + + switch(event.type) + { + //Event of UART receiving data + /*We'd better handler data event fast, there would be much more data events than + other types of events. If we take too much time on data event, the queue might + be full.*/ + case UART_DATA: + //ESP_LOGI(TAG, "[UART DATA]: %d", event.size); + /*uart_read_bytes(UART_NUM_0, buffer1, event.size, portMAX_DELAY); + + for(int index = 0; index < event.size; index++) + { + xQueueSend(uart0_data_queue, &buffer1[index], 10); + } + uart_ifx_uart0_rx_cb();*/ + break; + + //Event of HW FIFO overflow detected + case UART_FIFO_OVF: + ESP_LOGI(TAG, "hw fifo overflow"); + // If fifo overflow happened, you should consider adding flow control for your application. + // The ISR has already reset the rx FIFO, + // As an example, we directly flush the rx buffer here in order to read more data. + uart_flush_input(UART_NUM_0); + xQueueReset(uart0_queue); + break; + + //Event of UART ring buffer full + case UART_BUFFER_FULL: + ESP_LOGI(TAG, "ring buffer full"); + // If buffer full happened, you should consider increasing your buffer size + // As an example, we directly flush the rx buffer here in order to read more data. + uart_flush_input(UART_NUM_0); + xQueueReset(uart0_queue); + break; + + //Event of UART RX break detected + case UART_BREAK: + ESP_LOGI(TAG, "uart rx break"); + break; + + //Event of UART parity check error + case UART_PARITY_ERR: + ESP_LOGI(TAG, "uart parity error"); + break; + + //Event of UART frame error + case UART_FRAME_ERR: + ESP_LOGI(TAG, "uart frame error"); + break; + + //Others + default: + ESP_LOGI(TAG, "uart event type: %d", event.type); + break; + } + } + } + vTaskDelete(NULL); +} + + +static void uart1_event_task(void *pvParameters) +{ + uart_event_t event; + portMUX_TYPE uMutex = portMUX_INITIALIZER_UNLOCKED; + + for(;;) + { + //Waiting for UART event. + if(xQueueReceive(uart1_queue, (void * )&event, (TickType_t)portMAX_DELAY)) + { + bzero(buffer2, RD_BUF_SIZE); + //ESP_LOGI(TAG, "uart[%d] event:", UART_NUM_1); + + switch(event.type) + { + //Event of UART receiving data + /*We'd better handler data event fast, there would be much more data events than + other types of events. If we take too much time on data event, the queue might + be full.*/ + case UART_DATA: + //ESP_LOGI(TAG, "[UART DATA]: %d", event.size); + uart_read_bytes(UART_NUM_1, buffer2, event.size, portMAX_DELAY); + if(uart1_rx_needed) + { + buffer2_size = event.size; + //ESP_LOGI(TAG, "UART Rx: %s", buffer2); + taskENTER_CRITICAL(&uMutex); + for(int index = 0; index < event.size; index++) + { + xQueueSend(uart1_data_queue, &buffer2[index], 1000); + } + taskEXIT_CRITICAL(&uMutex); + uart_ifx_uart1_rx_cb(); + } + //ESP_LOGI(TAG, "UART RX: %s%s", buffer2, (!uart1_rx_needed)?" [MISSED]":""); + break; + //Event of HW FIFO overflow detected + + case UART_FIFO_OVF: + ESP_LOGI(TAG, "hw fifo overflow"); + // If fifo overflow happened, you should consider adding flow control for your application. + // The ISR has already reset the rx FIFO, + // As an example, we directly flush the rx buffer here in order to read more data. + uart_flush_input(UART_NUM_1); + xQueueReset(uart1_queue); + break; + + //Event of UART ring buffer full + case UART_BUFFER_FULL: + ESP_LOGI(TAG, "ring buffer full"); + // If buffer full happened, you should consider increasing your buffer size + // As an example, we directly flush the rx buffer here in order to read more data. + uart_flush_input(UART_NUM_1); + xQueueReset(uart1_queue); + break; + + //Event of UART RX break detected + case UART_BREAK: + ESP_LOGI(TAG, "uart rx break"); + break; + + //Event of UART parity check error + case UART_PARITY_ERR: + ESP_LOGI(TAG, "uart parity error"); + break; + + //Event of UART frame error + case UART_FRAME_ERR: + ESP_LOGI(TAG, "uart frame error"); + break; + + //Others + default: + ESP_LOGI(TAG, "uart event type: %d", event.type); + break; + } + } + } + vTaskDelete(NULL); +} + +void uart_create_rx_tasks(void) +{ + //Create a task to handler UART event from ISR + //xTaskCreate(uart0_event_task, "uart0_event_task", 2048, NULL, 18, NULL); + xTaskCreate(uart1_event_task, "uart1_event_task", 2048, NULL, 19, NULL); +} + +void uart_ifx_init(void) +{ + uart_config_t uart_config = { + .baud_rate = 4800, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_APB, + }; + + //Install UART driver, and get the queue. + //ESP_ERROR_CHECK(uart_driver_install(UART_NUM_0, (BUF_SIZE+1024), (BUF_SIZE+1024), 20, &uart0_queue, 0)); + //ESP_ERROR_CHECK(uart_param_config(UART_NUM_0, &uart_config)); + + + ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, (BUF_SIZE+1024), (BUF_SIZE+1024), 20, &uart1_queue, 0)); + ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart_config)); + + + //Set UART log level + esp_log_level_set(TAG, ESP_LOG_INFO); + + //Set UART pins (using UART0 default pins ie no changes.) + ESP_ERROR_CHECK(uart_set_pin(UART_NUM_0, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); + ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, 17, 18, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); + + //uart0_data_queue = xQueueCreate(BUF_SIZE, sizeof(uint8_t)); + uart1_data_queue = xQueueCreate(BUF_SIZE, sizeof(uint8_t)); +} + +uint8_t uart_ifx_uart0_ifx_get_rx_data(uint8_t *rx) +{ + if(xQueueReceive(uart0_data_queue, rx, (TickType_t)100) == pdPASS) + { + return 1; + } + return 0; +} + +uint8_t uart_ifx_uart1_ifx_get_rx_data(uint8_t *rx) +{ + if(xQueueReceive(uart1_data_queue, rx, (TickType_t)100) == pdPASS) + { + return 1; + } + return 0; +} + +void uart_ifx_uart0_send_byte(uint8_t data) +{ + uart_write_bytes(UART_NUM_0, &data, 1); +} + +void uart_ifx_uart1_send_byte(uint8_t data) +{ + uart_write_bytes(UART_NUM_1, &data, 1); +} + +void uart_ifx_uart0_send_bytes(uint8_t *data, size_t length) +{ + uart_write_bytes(UART_NUM_0, data, length); +} + +void uart_ifx_uart1_send_bytes(uint8_t *data, size_t length) +{ + uart_write_bytes(UART_NUM_1, data, length); +} + +uint8_t *uart_ifx_uart1_get_rx_buffer(size_t *len) +{ + *len = buffer2_size; + return buffer2; +} + diff --git a/main/uart_ifx.h b/main/uart_ifx.h new file mode 100644 index 0000000..5d918b7 --- /dev/null +++ b/main/uart_ifx.h @@ -0,0 +1,26 @@ +/* + * uart.h + * + * Created on: Jan 10, 2023 + * Author: Partha + */ + +#ifndef MAIN_UART_IFX_H_ +#define MAIN_UART_IFX_H_ + +#include +#include + +void uart_ifx_init(void); +uint8_t uart_ifx_uart0_ifx_get_rx_data(uint8_t *rx); +uint8_t uart_ifx_uart1_ifx_get_rx_data(uint8_t *rx); +void uart_ifx_uart0_send_byte(uint8_t data); +void uart_ifx_uart1_send_byte(uint8_t data); +void uart_ifx_uart0_send_bytes(uint8_t *data, size_t length); +void uart_ifx_uart1_send_bytes(uint8_t *data, size_t length); +uint8_t *uart_ifx_uart1_get_rx_buffer(size_t *len); +void uart_create_rx_tasks(void); +__attribute__((weak)) void uart_ifx_uart0_rx_cb(void); +__attribute__((weak)) void uart_ifx_uart1_rx_cb(void); + +#endif /* MAIN_UART_IFX_H_ */ diff --git a/main/ulp/sensors_registers.h b/main/ulp/sensors_registers.h new file mode 100644 index 0000000..0a6cefb --- /dev/null +++ b/main/ulp/sensors_registers.h @@ -0,0 +1,321 @@ +/* + * sensors_registers.h + * + * Created on: Mar 8, 2023 + * Author: Sword + */ + +#ifndef MAIN_ULP_SENSORS_REGISTERS_H_ +#define MAIN_ULP_SENSORS_REGISTERS_H_ + +// Sensor Addresses +#define ENS210_ADDR 0x43 // Base address of 0x43 left shifted one bit to make room for the read/#write bit. Temperature and humidity sensor +#define MCP9600_ADDR 0x60 // Base address of 0x60 left shifted one bit to make room for the read/#write bit. Thermocouple interface chip +#define MC3419_ADDR 0x6C + +// ENS210 Registers +#define ENS210_PART_ID 0x00 +#define ENS210_DIE_REV 0x02 +#define ENS210_UID 0x04 +#define ENS210_SYS_CTRL 0x10 +#define ENS210_SYS_STAT 0x11 +#define ENS210_SENS_RUN 0x21 +#define ENS210_SENS_START 0x22 +#define ENS210_SENS_STOP 0x23 +#define ENS210_SENS_STAT 0x24 +#define ENS210_T_VAL 0x30 +#define ENS210_H_VAL 0x33 + +// MCP9600 Registers +#define MCP9600_HOT_JUNCTION_REG 0x00 +#define MCP9600_DELTA_JUNCTION_REG 0x01 +#define MCP9600_COLD_JUNCTION_REG 0x02 +#define MCP9600_STATUS_REG 0x04 +#define MCP9600_THERMOCOUPLE_CONFIG 0x05 +#define MCP9600_DEVICE_CONFIG_REG 0x06 +#define MCP9600_ALERT1_CONFIG_REG 0x08 +#define MCP9600_ALERT2_CONFIG_REG 0x09 +#define MCP9600_ALERT3_CONFIG_REG 0x0A +#define MCP9600_ALERT4_CONFIG_REG 0x0B +#define MCP9600_ALERT1_HYST_REG 0x0C +#define MCP9600_ALERT2_HYST_REG 0x0D +#define MCP9600_ALERT3_HYST_REG 0x0E +#define MCP9600_ALERT4_HYST_REG 0x0F +#define MCP9600_ALERT1_LIMIT_REG 0x10 +#define MCP9600_ALERT2_LIMIT_REG 0x11 +#define MCP9600_ALERT3_LIMIT_REG 0x12 +#define MCP9600_ALERT4_LIMIT_REG 0x13 +#define MCP9600_DEVICE_ID_REVISION 0x20 + +// MCP9600 Bits +#define MCP9600_INPUT_RANGE_BIT 0x10 +#define MCP9600_BURST_COMPLETE_BIT 0x80 +#define MCP9600_TH_UPDATE_BIT 0x40 +#define MCP9600_ALERT_ENABLE_BIT 0x01 + +#define MCP9600_ALERT1_STATUS 0x01 +#define MCP9600_ALERT2_STATUS 0x02 +#define MCP9600_ALERT3_STATUS 0x04 +#define MCP9600_ALERT4_STATUS 0x08 + +// Thermocouple types: +#define THERMOCOUPLE_TYPE_K 0x00 +#define THERMOCOUPLE_TYPE_J 0x10 +#define THERMOCOUPLE_TYPE_T 0x20 +#define THERMOCOUPLE_TYPE_N 0x30 +#define THERMOCOUPLE_TYPE_S 0x40 +#define THERMOCOUPLE_TYPE_E 0x50 +#define THERMOCOUPLE_TYPE_B 0x60 +#define THERMOCOUPLE_TYPE_R 0x70 + + +#define WAKE_HOST_LOWER_BYTE_DISABLED 0x00 +#define WAKE_HOST_LOWER_BYTE_ON_MOVEMENT_DETECTION 0x01 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_LOWER_BYTE_ON_LIGHT_TO_DARK 0x02 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_LOWER_BYTE_ON_DARK_TO_LIGHT 0x04 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_LOWER_BYTE_ON_SWITCH_FROM_USB_TO_BATTERY 0x08 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_LOWER_BYTE_ON_SWITCH_FROM_BATTERY_TO_USB 0x10 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_LOWER_BYTE_WHEN_THERMOCOUPLE_CROSSES_LIMITS 0x20 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_LOWER_BYTE_UNUSED_A 0x40 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_LOWER_BYTE_UNUSED_B 0x80 // 0 = disabled, 1 = enabled. + +#define WAKE_HOST_UPPER_BYTE_UNUSED_C 0x01 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_UPPER_BYTE_UNUSED_D 0x02 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_UPPER_BYTE_UNUSED_E 0x04 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_UPPER_BYTE_UNUSED_F 0x08 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_UPPER_BYTE_UNUSED_G 0x10 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_UPPER_BYTE_UNUSED_H 0x20 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_UPPER_BYTE_UNUSED_I 0x40 // 0 = disabled, 1 = enabled. +#define WAKE_HOST_UPPER_BYTE_UNUSED_J 0x80 // 0 = disabled, 1 = enabled. + +// 16-bit masks +#define WAKE_HOST_DISABLED (uint16_t)WAKE_HOST_LOWER_BYTE_DISABLED +#define WAKE_HOST_ON_MOVEMENT_DETECTION (uint16_t)WAKE_HOST_LOWER_BYTE_ON_MOVEMENT_DETECTION +#define WAKE_HOST_ON_LIGHT_TO_DARK (uint16_t)WAKE_HOST_LOWER_BYTE_ON_LIGHT_TO_DARK +#define WAKE_HOST_ON_DARK_TO_LIGHT (uint16_t)WAKE_HOST_LOWER_BYTE_ON_DARK_TO_LIGHT +#define WAKE_HOST_ON_SWITCH_FROM_USB_TO_BATTERY (uint16_t)WAKE_HOST_LOWER_BYTE_ON_SWITCH_FROM_USB_TO_BATTERY +#define WAKE_HOST_ON_SWITCH_FROM_BATTERY_TO_USB (uint16_t)WAKE_HOST_LOWER_BYTE_ON_SWITCH_FROM_BATTERY_TO_USB +#define WAKE_HOST_WHEN_THERMOCOUPLE_CROSSES_LIMITS (uint16_t)WAKE_HOST_LOWER_BYTE_WHEN_THERMOCOUPLE_CROSSES_LIMITS +#define WAKE_HOST_UNUSED_A (uint16_t)WAKE_HOST_LOWER_BYTE_UNUSED_A +#define WAKE_HOST_UNUSED_B (uint16_t)WAKE_HOST_LOWER_BYTE_UNUSED_B +#define WAKE_HOST_UNUSED_C (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_C << 8) +#define WAKE_HOST_UNUSED_D (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_D << 8) +#define WAKE_HOST_UNUSED_E (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_E << 8) +#define WAKE_HOST_UNUSED_F (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_F << 8) +#define WAKE_HOST_UNUSED_G (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_G << 8) +#define WAKE_HOST_UNUSED_H (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_H << 8) +#define WAKE_HOST_UNUSED_I (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_I << 8) +#define WAKE_HOST_UNUSED_J (uint16_t)(WAKE_HOST_UPPER_BYTE_UNUSED_J << 8) + +#define STROBED_SENSOR_MEASUREMENT_EXCEEDED_THRESHOLD_A 0x0001 +#define STROBED_SENSOR_MEASUREMENT_RETURNED_FROM_ABOVE_THRESHOLD_A 0x0002 +#define STROBED_SENSOR_MEASUREMENT_EXCEEDED_THRESHOLD_B 0x0004 +#define STROBED_SENSOR_MEASUREMENT_RETURNED_FROM_ABOVE_THRESHOLD_B 0x0008 +#define STROBED_SENSOR_MEASUREMENT_EXCEEDED_THRESHOLD_C 0x0010 +#define STROBED_SENSOR_MEASUREMENT_RETURNED_FROM_ABOVE_THRESHOLD_C 0x0020 +#define STROBED_SENSOR_MEASUREMENT_FELL_BELOW_THRESHOLD_D 0x0040 +#define STROBED_SENSOR_MEASUREMENT_RETURNED_FROM_BELOW_THRESHOLD_D 0x0080 +#define STROBED_SENSOR_MEASUREMENT_FELL_BELOW_THRESHOLD_E 0x0100 +#define STROBED_SENSOR_MEASUREMENT_RETURNED_FROM_BELOW_THRESHOLD_E 0x0200 +#define STROBED_SENSOR_MEASUREMENT_FELL_BELOW_THRESHOLD_F 0x0400 +#define STROBED_SENSOR_MEASUREMENT_RETURNED_FROM_BELOW_THRESHOLD_F 0x0800 +#define STROBED_SENSOR_UNUSED_A 0x1000 +#define STROBED_SENSOR_UNUSED_B 0x2000 +#define STROBED_SENSOR_UNUSED_C 0x4000 +#define STROBED_SENSOR_UNUSED_D 0x8000 + +#define STROBED_MOTION_LOWER_BYTE_MOVEMENT_DETECTED 0x01 +#define STROBED_MOTION_LOWER_BYTE_UNUSED_A 0x02 +#define STROBED_MOTION_LOWER_BYTE_UNUSED_B 0x04 +#define STROBED_MOTION_LOWER_BYTE_UNUSED_C 0x08 +#define STROBED_MOTION_LOWER_BYTE_UNUSED_D 0x10 +#define STROBED_MOTION_LOWER_BYTE_UNUSED_E 0x20 +#define STROBED_MOTION_LOWER_BYTE_UNUSED_F 0x40 +#define STROBED_MOTION_LOWER_BYTE_UNUSED_G 0x80 + +#define STROBED_MOTION_UPPER_BYTE_UNUSED_H 0x01 +#define STROBED_MOTION_UPPER_BYTE_UNUSED_I 0x02 +#define STROBED_MOTION_UPPER_BYTE_UNUSED_J 0x04 +#define STROBED_MOTION_UPPER_BYTE_UNUSED_K 0x08 +#define STROBED_MOTION_UPPER_BYTE_UNUSED_L 0x10 +#define STROBED_MOTION_UPPER_BYTE_UNUSED_M 0x20 +#define STROBED_MOTION_UPPER_BYTE_UNUSED_N 0x40 +#define STROBED_MOTION_UPPER_BYTE_UNUSED_O 0x80 + +// 16-bit masks +#define STROBED_MOTION_MOVEMENT_DETECTED (uint16_t)STROBED_MOTION_LOWER_BYTE_MOVEMENT_DETECTED +#define STROBED_MOTION_UNUSED_A (uint16_t)STROBED_MOTION_LOWER_BYTE_UNUSED_A +#define STROBED_MOTION_UNUSED_B (uint16_t)STROBED_MOTION_LOWER_BYTE_UNUSED_B +#define STROBED_MOTION_UNUSED_C (uint16_t)STROBED_MOTION_LOWER_BYTE_UNUSED_C +#define STROBED_MOTION_UNUSED_D (uint16_t)STROBED_MOTION_LOWER_BYTE_UNUSED_D +#define STROBED_MOTION_UNUSED_E (uint16_t)STROBED_MOTION_LOWER_BYTE_UNUSED_E +#define STROBED_MOTION_UNUSED_F (uint16_t)STROBED_MOTION_LOWER_BYTE_UNUSED_F +#define STROBED_MOTION_UNUSED_G (uint16_t)STROBED_MOTION_LOWER_BYTE_UNUSED_G +#define STROBED_MOTION_UNUSED_H (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_H << 8) +#define STROBED_MOTION_UNUSED_I (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_I << 8) +#define STROBED_MOTION_UNUSED_J (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_J << 8) +#define STROBED_MOTION_UNUSED_K (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_K << 8) +#define STROBED_MOTION_UNUSED_L (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_L << 8) +#define STROBED_MOTION_UNUSED_M (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_M << 8) +#define STROBED_MOTION_UNUSED_N (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_N << 8) +#define STROBED_MOTION_UNUSED_O (uint16_t)(STROBED_MOTION_LOWER_BYTE_UNUSED_O << 8) + +#define STROBED_MISC_LOWER_BYTE_CHECK_IN_REQUESTED 0x01 +#define STROBED_MISC_LOWER_BYTE_CHANGE_FROM_LIGHT_TO_DARK 0x02 +#define STROBED_MISC_LOWER_BYTE_CHANGE_FROM_DARK_TO_LIGHT 0x04 +#define STROBED_MISC_LOWER_BYTE_CHANGE_FROM_USB_TO_BATTERY 0x08 +#define STROBED_MISC_LOWER_BYTE_CHANGE_FROM_BATTERY_TO_USB 0x10 +#define STROBED_MISC_LOWER_BYTE_THERMOCOUPLE_PLUGGED_IN 0x20 +#define STROBED_MISC_LOWER_BYTE_THERMOCOUPLE_UNPLUGGED 0x40 +#define STROBED_MISC_LOWER_BYTE_UNUSED_A 0x80 + +#define STROBED_MISC_UPPER_BYTE_UNUSED_B 0x01 +#define STROBED_MISC_UPPER_BYTE_UNUSED_C 0x02 +#define STROBED_MISC_UPPER_BYTE_UNUSED_D 0x04 +#define STROBED_MISC_UPPER_BYTE_UNUSED_E 0x08 +#define STROBED_MISC_UPPER_BYTE_UNUSED_F 0x10 +#define STROBED_MISC_UPPER_BYTE_UNUSED_G 0x20 +#define STROBED_MISC_UPPER_BYTE_UNUSED_H 0x40 +#define STROBED_MISC_UPPER_BYTE_UNUSED_I 0x80 + +// 16-bit masks +#define STROBED_MISC_CHECK_IN_REQUESTED (uint16_t)STROBED_MISC_LOWER_BYTE_CHECK_IN_REQUESTED +#define STROBED_MISC_CHANGE_FROM_LIGHT_TO_DARK (uint16_t)STROBED_MISC_LOWER_BYTE_CHANGE_FROM_LIGHT_TO_DARK +#define STROBED_MISC_CHANGE_FROM_DARK_TO_LIGHT (uint16_t)STROBED_MISC_LOWER_BYTE_CHANGE_FROM_DARK_TO_LIGHT +#define STROBED_MISC_CHANGE_FROM_USB_TO_BATTERY (uint16_t)STROBED_MISC_LOWER_BYTE_CHANGE_FROM_USB_TO_BATTERY +#define STROBED_MISC_CHANGE_FROM_BATTERY_TO_USB (uint16_t)STROBED_MISC_LOWER_BYTE_CHANGE_FROM_BATTERY_TO_USB +#define STROBED_MISC_THERMOCOUPLE_PLUGGED_IN (uint16_t)STROBED_MISC_LOWER_BYTE_THERMOCOUPLE_PLUGGED_IN +#define STROBED_MISC_THERMOCOUPLE_UNPLUGGED (uint16_t)STROBED_MISC_LOWER_BYTE_THERMOCOUPLE_UNPLUGGED +#define STROBED_MISC_REGULAR_SCHEDULED_CHECK_IN (uint16_t)STROBED_MISC_LOWER_BYTE_REGULAR_SCHEDULED_CHECK_IN +#define STROBED_MISC_CHECK_IN_REQ_AFTER_POWER_ON (uint16_t)(STROBED_MISC_UPPER_BYTE_CHECK_IN_REQ_AFTER_POWER_ON << 8) +#define STROBED_MISC_DEVICE_OPERATE_IN_ALERT_MODE (uint16_t)(STROBED_MISC_UPPER_BYTE_DEVICE_OPERATE_IN_ALERT_MODE << 8) +#define STROBED_MISC_UNUSED_D (uint16_t)(STROBED_MISC_UPPER_BYTE_UNUSED_D << 8) +#define STROBED_MISC_UNUSED_E (uint16_t)(STROBED_MISC_UPPER_BYTE_UNUSED_E << 8) +#define STROBED_MISC_UNUSED_F (uint16_t)(STROBED_MISC_UPPER_BYTE_UNUSED_F << 8) +#define STROBED_MISC_UNUSED_G (uint16_t)(STROBED_MISC_UPPER_BYTE_UNUSED_G << 8) +#define STROBED_MISC_UNUSED_H (uint16_t)(STROBED_MISC_UPPER_BYTE_UNUSED_H << 8) +#define STROBED_MISC_UNUSED_I (uint16_t)(STROBED_MISC_UPPER_BYTE_UNUSED_I << 8) + +#define CURRENT_STATUS_LIGHT_DETECTED 0x01 +#define CURRENT_STATUS_POWERED_BY_USB 0x02 +#define CURRENT_STATUS_UNUSED_A 0x04 +#define CURRENT_STATUS_UNUSED_B 0x08 +#define CURRENT_STATUS_UNUSED_C 0x10 +#define CURRENT_STATUS_UNUSED_D 0x20 +#define CURRENT_STATUS_UNUSED_E 0x40 +#define CURRENT_STATUS_UNUSED_F 0x80 + +#define LIGHT_SENSOR_VREF_1V024 0x00 // Vref = 1.024V +#define LIGHT_SENSOR_VREF_2V048 0x01 // Vref = 2.048V +#define LIGHT_SENSOR_VREF_2V500 0x02 // Vref = 2.500V +#define LIGHT_SENSOR_VREF_VDD 0x03 // Vref = Vdd + +#define LIGHT_DETECTED true +#define LIGHT_UNDETECTED false + + +/* TRIGGER 1 bits */ +#define STROBED_TEMPERATURE_MEASUREMENT_EXCEEDED_UPPER_THRESHOLD 0x0001 +#define STROBED_TEMPERATURE_MEASUREMENT_FELL_BELOW_LOWER_THRESHOLD 0x0002 +#define STROBED_HUMIDITY_MEASUREMENT_EXCEEDED_UPPER_THRESHOLD 0x0004 +#define STROBED_HUMIDITY_MEASUREMENT_FELL_BELOW_LOWER_THRESHOLD 0x0008 +#define STROBED_THERMOCOUPLE_MEASUREMENT_EXCEEDED_UPPER_THRESHOLD 0x0010 +#define STROBED_THERMOCOUPLE_MEASUREMENT_FELL_BELOW_LOWER_THRESHOLD 0x0020 +#define STROBED_EXTERNAL_TEMP_PROBE_MEASUREMENT_EXCEEDED_UPPER_THRESHOLD 0x0040 +#define STROBED_EXTERNAL_TEMP_PROBE_MEASUREMENT_FELL_BELOW_LOWER_THRESHOLD 0x0080 +#define STROBED_DEVICE_OPERATE_IN_ALERT_MODE 0x0100 + + +/* TRIGGER 2 bits */ +#define STROBED_REGULAR_SCHEDULED_SENDINTERVAL_CHECKIN 0x0001 +#define STROBED_POWER_CHANGE_FROM_BATTERY_TO_USB 0x0002 +#define STROBED_POWER_CHANGE_FROM_USB_TO_BATTERY 0x0004 +#define STROBED_MOTION_MOVEMENT_WAS_DETECTED 0x0008 +#define STROBED_LIGHT_TRANSITION_FROM_LIGHT_TO_DARK 0x0010 +#define STROBED_LIGHT_TRANSITION_FROM_DARK_TO_LIGHT 0x0020 +#define STROBED_CHECK_IN_REQUESTED_AFTER_POWER_ON 0x4000 +#define STROBED_BUTTON_CHECK_IN_REQUESTED 0x8000 + + +#define STROBED_SENSOR_MEASUREMENT_EXCEEDED_UPPER_THRESHOLD 0x0001 +#define STROBED_SENSOR_MEASUREMENT_FELL_BELOW_LOWER_THRESHOLD 0x0002 + + +typedef enum +{ + ULP_OK, + ULP_ERROR +}ULP_STATUS_t; + + +typedef enum +{ + ULP_TEMPERATURE_SENSOR, + ULP_HUMIDITY_SENSOR, + ULP_THERMOCOUPLE_SENSOR, + ULP_LIGHT_SENSOR, + ULP_ACCELEROMETER_SENSOR, + ULP_POWER_MONITOR_SENSOR +}ulpSensorType_t; + + +typedef struct{ + + //Sensor Data + uint8_t thermocoupleMode; + uint8_t thermocoupleType; + uint8_t lightSensorMode; + + uint8_t accelerometerMode; + uint8_t accelerometerInterval; + uint8_t accelerometerLockoutInSeconds; + uint8_t powerMonitorMode; + uint8_t powerMonitorDebounceInSeconds; + + //uint8_t lightSensorLevelGP; + //uint8_t lightSensorLevelGN; + + //lightTh + uint8_t lightSensorLevel; + + + // OTHERS + uint8_t historyIndex; + uint8_t numberOfHistoryLogEntriesUntilRetriesAreNotAllowed; + + uint8_t sendDetails; + + uint16_t lightSensorLockoutInSeconds; + //acclTh + uint16_t accelerometerLevel; + + // INTERVALS + uint32_t sendInterval; + uint32_t alertInterval; + + //Limits + int32_t minHum; + int32_t maxHum; + + // DYNAMIC VARS + int32_t postURLNumber; + int32_t postURLMax; // Highest URL to try + + //Limits + float minTemp; + float maxTemp; + float voltageQuit; + + //limits Tc + float maxTcTemp; + float minTcTemp; + + //limits probe + float maxProbeTemp; + float minProbeTemp; + +}settings; + + + +#endif /* MAIN_ULP_SENSORS_REGISTERS_H_ */ diff --git a/main/ulp/ulp_main.c b/main/ulp/ulp_main.c new file mode 100644 index 0000000..d49c100 --- /dev/null +++ b/main/ulp/ulp_main.c @@ -0,0 +1,730 @@ +/* + * ulp_main.c + * + * Created on: Feb 17, 2023 + * Author: Partha + */ +#include +#include +#include "ulp_riscv.h" +#include "ulp_riscv_utils.h" +#include "ulp_riscv_gpio.h" +#include "sensors_registers.h" +#include "ulp_riscv_i2c_ulp_core.h" +#include "sdkconfig.h" + +#define I2C_MASTER_NUM 0 +#define ULP_CLOCK_CYCLE_IN_MS 8500 + + +volatile uint32_t server_time = 0; +volatile uint32_t is_sleep = 0; + +/*Settings structure*/ +volatile settings settings_structure; + + +/*Sensor variables*/ +//Sensor Data +float ulp_temperature; +float ulp_humidity; +float ulp_thermocoupleTemp; + +//Sensor previous Data +float ulp_prev_temperature; +float ulp_prev_humidity; +float ulp_prev_thermocoupleTemp; + + +/*Sensor flags and triggers*/ +uint16_t ulp_triggersThatCanWakeTheHost; +uint16_t ulp_strobedHumidityFlags; +uint16_t ulp_strobedMiscFlags; +uint16_t ulp_strobedMotionFlags; +uint16_t ulp_strobedTemperatureFlags; +uint16_t ulp_strobedThermocoupleFlags; + + +uint32_t wakeup_type = 0; +uint32_t ens_dev_id = 0; +uint32_t mcp9600_dev_id = 0; +uint32_t temp_needed = 0; + + +//Temp, Humi, Thermo raw_thresholds +uint32_t humidity_raw_max_thr = 0; +uint32_t humidity_raw_min_thr = 0; +uint32_t temperature_raw_max_thr = 0; +uint32_t temperature_raw_min_thr = 0; +uint32_t thermo_raw_max_thr = 0; +int32_t thermo_raw_min_thr = 0; + +//Sensor Raw values +uint32_t humidity_raw = 0; +uint32_t prev_humidity_raw = 0; +uint32_t thermo_temp_raw = 0; //need to be signed +uint32_t prev_thermo_temp_raw = 0; //need to be signed +uint32_t ens_temp_raw = 0; +uint32_t prev_ens_temp_raw = 0; + + +uint8_t data[8]; +uint32_t ens_dev_id; +uint32_t opt_dev_id; +uint32_t opt_manf_id; +uint32_t config_reg = 0; +uint32_t result_reg = 0; +uint32_t light_exponent = 0; +uint32_t light_data_opt = 0; +uint32_t prev_light_data_opt = 0; + +static uint8_t buff[8]; + +/****************************************************************************************/ +/********************************** General ULP-Functions *******************************/ + +static void ulp_initialize_variables(void) +{ + server_time = 0; + + //Sensor Data + ulp_temperature = 0; + ulp_humidity = 0; + ulp_thermocoupleTemp = 0; + + //Sensor previous Data + ulp_prev_temperature = 0; + ulp_prev_humidity = 0; + ulp_prev_thermocoupleTemp = 0; + + //Strobes and triggers variables + ulp_triggersThatCanWakeTheHost = 0; + ulp_strobedHumidityFlags = 0; + ulp_strobedMiscFlags = 0; + ulp_strobedMotionFlags = 0; + ulp_strobedTemperatureFlags = 0; + ulp_strobedThermocoupleFlags = 0; + + //Settings structure + settings_structure.accelerometerInterval = 0; + settings_structure.accelerometerLevel = 0; + settings_structure.accelerometerLockoutInSeconds = 0; + settings_structure.accelerometerMode = 0; + //settings_structure.accelerometerTokens = 0; + settings_structure.alertInterval = 0; + settings_structure.historyIndex = 0; +} + +/****************************************************************************************/ +/******************************** MCP9600-Thermocouple Sensor ***************************/ +static void set_thermocouple_type(uint8_t type) +{ + uint8_t tempThermocoupleType; + tempThermocoupleType = type; + tempThermocoupleType &= 0x70; + + /*Start new conversion */ + buff[0] = tempThermocoupleType; + ulp_riscv_i2c_master_set_slave_addr(0x60); + ulp_riscv_i2c_master_set_slave_reg_addr(0x05); + ulp_riscv_i2c_master_write_to_device(buff, 1); + + ulp_riscv_delay_cycles(500 * ULP_CLOCK_CYCLE_IN_MS); + ulp_riscv_delay_cycles(500 * ULP_CLOCK_CYCLE_IN_MS); +} + +static void get_thermocouple_dev_id(void) +{ + memset(buff, 0, 2); + + ulp_riscv_i2c_master_set_slave_addr(0x60); + ulp_riscv_i2c_master_set_slave_reg_addr(0x20); + ulp_riscv_i2c_master_read_from_device(buff, 1); + + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); + + mcp9600_dev_id = buff[0];//buff[1] | (buff[0] << 8); +} +#if 1 + +static void mcp_set_burst_mode(void) +{ + //printf("Setting Burst mode\n"); + ulp_riscv_i2c_master_set_slave_addr(0x60); + ulp_riscv_i2c_master_set_slave_reg_addr(0x06); + buff[0] = 0xA2; + ulp_riscv_i2c_master_write_to_device(buff, 1); + ulp_riscv_delay_cycles(1000 * ULP_CLOCK_CYCLE_IN_MS); +} + +static void mcp_start_conversion(void) +{ + memset(buff, 0, 2); + + //printf("Starting thermocouple conversion\n"); + ulp_riscv_i2c_master_set_slave_addr(0x60); + + ulp_riscv_i2c_master_set_slave_reg_addr(0x04); + buff[0] = 0xC0; + ulp_riscv_i2c_master_write_to_device(buff, 1); + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); + + mcp_set_burst_mode(); +} + +static void mcp_get_temp(void) +{ + uint16_t temp = 0; + uint8_t counter = 0; + uint8_t err = 1; + + memset(buff, 0, 6); + + mcp_start_conversion(); + + do + { + //ulp_riscv_i2c_master_set_slave_addr(0x60); + ulp_riscv_i2c_master_set_slave_reg_addr(0x04); + ulp_riscv_i2c_master_read_from_device(buff, 1); + + if(counter > 3) + { + //printf("Timeout for getting thermocouple temp\n"); + err = 0; + break; + } + counter++; + ulp_riscv_delay_cycles(111 * ULP_CLOCK_CYCLE_IN_MS); + } + while (!(buff[0] & 0x40)); + + if(err && !(buff[0] & 0x10)) + { + memset(buff, 0, 6); + //ulp_riscv_i2c_master_set_slave_addr(0x60); + + ulp_riscv_i2c_master_set_slave_reg_addr(0x00); + ulp_riscv_i2c_master_read_from_device(buff, 1); + + ulp_riscv_i2c_master_set_slave_reg_addr(0x00); + //ulp_riscv_i2c_master_read_from_device(buff + 1, 1); + ulp_riscv_i2c_master_read_from_device(buff + 1, 2); + + ulp_riscv_delay_cycles(10 * ULP_CLOCK_CYCLE_IN_MS); + prev_thermo_temp_raw = thermo_temp_raw; + + temp = buff[1] | (buff[0] << 8); + + + thermo_temp_raw = temp; + thermo_temp_raw /= 16; + + if(temp & 0x8000) // Adjust for sign bit + thermo_temp_raw -= 4096; + + } + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); +} +#endif + + +/****************************************************************************************/ +/************************** ENS210-Temperature & Humidity Sensor ************************/ + +static void ens_set_active(void) +{ + //ESN Setting to active mode + //printf("ESN Setting to active mode\n"); + ulp_riscv_i2c_master_set_slave_reg_addr(0x10); + buff[0] = 0; + ulp_riscv_i2c_master_write_to_device(buff, 1); +} + +static void ens_init(void) +{ + ulp_riscv_i2c_master_set_slave_addr(0x43); + ens_set_active(); + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); + ens_set_active(); + + ulp_riscv_delay_cycles(500 * ULP_CLOCK_CYCLE_IN_MS); + + //ESN Reading sys stat + //("ESN Reading sys stat\n"); + ulp_riscv_i2c_master_set_slave_reg_addr(0x11); + buff[0] = 0; + ulp_riscv_i2c_master_read_from_device(buff, 1); + //("ENS Sys stat: %d\n", buff[0]); + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); + + //Reading device id + //("ESN Reading device id\n"); + memset(buff, 0, 2); + ulp_riscv_i2c_master_set_slave_reg_addr(0x00); + ulp_riscv_i2c_master_read_from_device(buff, 1); + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); + + ulp_riscv_i2c_master_set_slave_reg_addr(0x01); + ulp_riscv_i2c_master_read_from_device(buff + 1, 1); + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); + ens_dev_id = buff[0] | (buff[1] << 8); +} + +static void ens_start(void) +{ + ulp_riscv_i2c_master_set_slave_addr(0x43); + + //Sens Run + ulp_riscv_i2c_master_set_slave_reg_addr(0x21); + buff[0] = 3; + ulp_riscv_i2c_master_write_to_device(buff, 1); + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); + + //Sens Start + ulp_riscv_i2c_master_set_slave_reg_addr(0x22); + buff[0] = 3; + ulp_riscv_i2c_master_write_to_device(buff, 1); + ulp_riscv_delay_cycles(500 * ULP_CLOCK_CYCLE_IN_MS); +} + +static void ens_read_temp(void) +{ + uint8_t index = 0; + + memset(buff, 0, 6); + + ulp_riscv_i2c_master_set_slave_addr(0x43); + ulp_riscv_i2c_master_set_slave_reg_addr(0x30); + + ulp_riscv_i2c_master_read_from_device(buff, 1); + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); + + ulp_riscv_i2c_master_set_slave_reg_addr(0x31); + ulp_riscv_i2c_master_read_from_device(buff + 1, 1); + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); + + ulp_riscv_i2c_master_set_slave_reg_addr(0x32); + ulp_riscv_i2c_master_read_from_device(buff + 2, 1); + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); + + ulp_riscv_i2c_master_set_slave_reg_addr(0x33); + ulp_riscv_i2c_master_read_from_device(buff + 3, 1); + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); + + ulp_riscv_i2c_master_set_slave_reg_addr(0x34); + ulp_riscv_i2c_master_read_from_device(buff + 4, 1); + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); + + ulp_riscv_i2c_master_set_slave_reg_addr(0x35); + ulp_riscv_i2c_master_read_from_device(buff + 5, 1); + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); + + if(buff[2] & 0x01) + { + prev_ens_temp_raw = ens_temp_raw; + ens_temp_raw = buff[0] | (buff[1] << 8); + //tempC = temp/64.0; + //tempC -= 273.15; + } + + if(buff[5] & 0x01) + { + prev_humidity_raw = humidity_raw; + humidity_raw = buff[3] | (buff[4] << 8); + //hum = hum/512.0; + } +} + +uint8_t ulp_data_process_tempHumi_thresholds(uint32_t raw_data, uint32_t prev_raw_data, ulpSensorType_t sensor) +{ + uint8_t useTokens = 0; + uint8_t wakeHostForStrobedTrigger = 0; + + switch(sensor) + { + case ULP_LIGHT_SENSOR: + break; + + case ULP_ACCELEROMETER_SENSOR: + break; + + case ULP_POWER_MONITOR_SENSOR: + break; + + case ULP_TEMPERATURE_SENSOR: + + + /* Process thresholds for ENS210-TempData*/ + if((raw_data > temperature_raw_max_thr) && (prev_raw_data < temperature_raw_max_thr)) + { + (ulp_strobedTemperatureFlags) |= STROBED_SENSOR_MEASUREMENT_EXCEEDED_UPPER_THRESHOLD; + wakeup_type |= (1<<3); + wakeHostForStrobedTrigger = 1; + } + else if((raw_data < temperature_raw_min_thr) && (prev_raw_data > temperature_raw_min_thr)) + { + (ulp_strobedTemperatureFlags) |= STROBED_SENSOR_MEASUREMENT_FELL_BELOW_LOWER_THRESHOLD; + wakeup_type |= (1<<4); + wakeHostForStrobedTrigger = 1; + } + + break; + + case ULP_HUMIDITY_SENSOR: + + /* Process thresholds for ENS210-HumiData*/ + if((raw_data > humidity_raw_max_thr) && (prev_raw_data < humidity_raw_max_thr)) + { + (ulp_strobedHumidityFlags) |= STROBED_SENSOR_MEASUREMENT_EXCEEDED_UPPER_THRESHOLD; + wakeup_type |= (1<<5); + wakeHostForStrobedTrigger = 1; + } + else if((raw_data < humidity_raw_min_thr) && (prev_raw_data > humidity_raw_min_thr)) + { + (ulp_strobedHumidityFlags) |= STROBED_SENSOR_MEASUREMENT_FELL_BELOW_LOWER_THRESHOLD; + wakeup_type |= (1<<6); + wakeHostForStrobedTrigger = 1; + } + break; + + case ULP_THERMOCOUPLE_SENSOR: + + if((ulp_triggersThatCanWakeTheHost & WAKE_HOST_WHEN_THERMOCOUPLE_CROSSES_LIMITS)) + { + /* Process thresholds for MCP9600-TcData*/ + if((raw_data > thermo_raw_max_thr) && (prev_raw_data < thermo_raw_max_thr)) + { + (ulp_strobedThermocoupleFlags) |= STROBED_SENSOR_MEASUREMENT_EXCEEDED_UPPER_THRESHOLD; + wakeup_type |= (1<<7); + wakeHostForStrobedTrigger = 1; + } + else if((raw_data < thermo_raw_min_thr) && (prev_raw_data > thermo_raw_min_thr)) + { + (ulp_strobedThermocoupleFlags) |= STROBED_SENSOR_MEASUREMENT_FELL_BELOW_LOWER_THRESHOLD; + wakeup_type |= (1<<8); + wakeHostForStrobedTrigger = 1; + } + break; + } + break; + } + + return wakeHostForStrobedTrigger; +} + + +uint8_t ulp_data_process_tempHumi_thresholds2(uint32_t raw_data, uint32_t prev_raw_data, uint8_t sensor) +{ + //uint8_t enable_thsProcess = 1; + uint8_t useTokens = 0; + uint8_t wakeHostForStrobedTrigger = 0; + uint32_t thr = 0; + uint32_t thr2 = 0; + + switch(sensor) + { + case 1: + + /* Set the tokens register to be ThermoCouple token-registers */ + //tokenregister = &(esp_settings->temperatureTokens); + + thr = ((26.6+273.15)*64); + thr2 = ((-1.1+273.15)*64); + /* Process thresholds for ENS210-TempData*/ + if((raw_data > thr) && (prev_raw_data < thr)) + { + wakeup_type |= (1<<3); + wakeHostForStrobedTrigger = 1; + } + else if((raw_data < thr2) && (prev_raw_data > thr2)) + { + wakeup_type |= (1<<4); + wakeHostForStrobedTrigger = 1; + } + + break; + + case 2: + + /* Set the tokens register to be Humidity token registers */ + //tokenregister = &(esp_settings->humidityTokens); + + /* Process thresholds for ENS210-HumiData*/ + if((raw_data > (512*80)) && (prev_raw_data < (512*80))) + { + wakeup_type |= (1<<5); + wakeHostForStrobedTrigger = 1; + } + else if((raw_data < (512*10)) && (prev_raw_data > (512*10))) + { + wakeup_type |= (1<<6); + wakeHostForStrobedTrigger = 1; + } + break; + + case 3: + + { + /* Set the tokens register to be ThermoCouple token-registers */ + //tokenregister = &(esp_settings->thermocoupleTokens); + + /* Process thresholds for MCP9600-TcData*/ + if((raw_data > 50) && (prev_raw_data < 50)) + { + wakeup_type |= (1<<7); + wakeHostForStrobedTrigger = 1; + } + else if((raw_data < -100) && (prev_raw_data > -100)) + { + wakeup_type |= (1<<8); + wakeHostForStrobedTrigger = 1; + } + break; + } + break; + } + + return wakeHostForStrobedTrigger; +} + + + +/**********************************************************************/ +/************************Light sensor**********************************/ +/**********************************************************************/ +void opt_init(void) +{ + /* Fetch manufacturer ID */ + data[0] = 0; + data[1] = 0; + ulp_riscv_i2c_master_set_slave_addr(0x44); + ulp_riscv_i2c_master_set_slave_reg_addr(0x7E); + ulp_riscv_i2c_master_read_from_device(data, 2); + ulp_riscv_delay_cycles(500 * ULP_CLOCK_CYCLE_IN_MS); + opt_manf_id = (data[0]<<8)|(data[1]); + + /* Fetch Device ID */ + data[0] = 0; + data[1] = 0; + ulp_riscv_i2c_master_set_slave_addr(0x44); + ulp_riscv_i2c_master_set_slave_reg_addr(0x7F); + ulp_riscv_i2c_master_read_from_device(data, 2); + ulp_riscv_delay_cycles(500 * ULP_CLOCK_CYCLE_IN_MS); + opt_dev_id = (data[0]<<8)|(data[1]); +} + +void opt_read_reg(void) +{ + uint8_t data[8]; + uint8_t exponent = 0; + uint16_t result =1 ; + uint32_t result_reg2 = 0; + + + /* Fetch Configuration register */ + data[0] = 0; + data[1] = 0; + ulp_riscv_i2c_master_set_slave_addr(0x44); + ulp_riscv_i2c_master_set_slave_reg_addr(0x01); + ulp_riscv_i2c_master_read_from_device(data, 1); + ulp_riscv_i2c_master_read_from_device(data+1, 2); + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); + config_reg = (data[0]<<8)|(data[1]); + + /* Set the single shot mode */ + config_reg = config_reg | (1<<9); + config_reg = config_reg & (~(1<<10)); + + + /* Send the new value of the configuration register */ + data[0] = config_reg>>8; + data[1] = config_reg&255; + ulp_riscv_i2c_master_set_slave_reg_addr(0x01); + ulp_riscv_i2c_master_write_to_device(data, 2); + ulp_riscv_delay_cycles(800 * ULP_CLOCK_CYCLE_IN_MS); + + /* Wait for the CRF from the opt3001 sensor */ + while(config_reg & (1<<9)) + { + ulp_riscv_i2c_master_set_slave_addr(0x44); + ulp_riscv_i2c_master_set_slave_reg_addr(0x01); + ulp_riscv_i2c_master_read_from_device(data, 1); + ulp_riscv_i2c_master_read_from_device(data+1, 2); + ulp_riscv_delay_cycles(100 * ULP_CLOCK_CYCLE_IN_MS); + config_reg = (data[0]<<8)|(data[1]); + } + + /* Fetch Light sensor result register */ + data[0] = 0; + data[1] = 0; + ulp_riscv_i2c_master_set_slave_addr(0x44); + ulp_riscv_i2c_master_set_slave_reg_addr(0x00); + ulp_riscv_i2c_master_read_from_device(data, 1); + ulp_riscv_i2c_master_read_from_device(data+1, 2); + result_reg = (data[0]<<8)|(data[1]); + result_reg2 = result_reg & 0xFFF; + + for(int i=1; i<=light_exponent; i++) + result *=2; + + + prev_light_data_opt = light_data_opt; + light_data_opt = 0.01*result*result_reg2; + //light_data_opt = 0.16 * result_reg2; +} + +uint8_t ulp_data_process_lightSen_thresholds(uint32_t prev_raw_data, uint32_t raw_data) +{ + uint8_t wakeHostForStrobedTrigger = 0; + +#if 0 + /* Check if light now has been detected and in the previous state there were no light*/ + /* (Current_data > Threshold_GP) && (Previous_data < Threshold_GN)*/ + if((raw_data > settings_structure.lightSensorLevelGP) && (prev_raw_data < settings_structure.lightSensorLevelGN)) + { + + /* Check if we should do check-in when changing from dark to light*/ + if(ulp_triggersThatCanWakeTheHost & WAKE_HOST_ON_DARK_TO_LIGHT) + { + ulp_strobedMiscFlags |= STROBED_MISC_CHANGE_FROM_DARK_TO_LIGHT; + + /* Reduce light tokens by 1*/ + if(settings_structure.lightSensorTokens) + { + settings_structure.lightSensorTokens--; + wakeHostForStrobedTrigger = 1; + } + } + }/* (Current_data < Threshold_GN) && (Previous_data > Threshold_GP)*/ + else if((raw_data < settings_structure.lightSensorLevelGN) && (prev_raw_data > settings_structure.lightSensorLevelGP)) + { + + /* Check if we should do check-in when changing from light to dark*/ + if(ulp_triggersThatCanWakeTheHost & WAKE_HOST_ON_LIGHT_TO_DARK) + { + ulp_strobedMiscFlags |= STROBED_MISC_CHANGE_FROM_LIGHT_TO_DARK; + + /* Reduce light tokens by 1*/ + if(settings_structure.lightSensorTokens) + { + settings_structure.lightSensorTokens--; + wakeHostForStrobedTrigger = 1; + } + } + } + else + { + } +#endif + + + /* Check if light now has been detected and in the previous state there were no light*/ + if((raw_data > settings_structure.lightSensorLevel) && (prev_raw_data < settings_structure.lightSensorLevel)) + { + /* Check if we should do check-in when changing from dark to light*/ + if(ulp_triggersThatCanWakeTheHost & WAKE_HOST_ON_DARK_TO_LIGHT) + { + ulp_strobedMiscFlags |= STROBED_MISC_CHANGE_FROM_DARK_TO_LIGHT; + wakeHostForStrobedTrigger = 1; + wakeup_type |= (1<<1); + } + } + else if((raw_data < settings_structure.lightSensorLevel) && (prev_raw_data > settings_structure.lightSensorLevel)) + { + /* Check if we should do check-in when changing from light to dark*/ + if(ulp_triggersThatCanWakeTheHost & WAKE_HOST_ON_LIGHT_TO_DARK) + { + ulp_strobedMiscFlags |= STROBED_MISC_CHANGE_FROM_LIGHT_TO_DARK; + wakeHostForStrobedTrigger = 1; + wakeup_type |= (1<<2); + } + } + + return wakeHostForStrobedTrigger; +} + +int main(void) +{ + int cycle_count = 0; + int wakeHostForStrobedTrigger = 0; + ulp_initialize_variables(); + + ens_init(); + set_thermocouple_type(0); + get_thermocouple_dev_id(); + mcp_start_conversion(); + ens_start(); + + for(;;) + { + /*if(temp_needed) + { + ens_read_temp(); + mcp_get_temp(); + }*/ + if(is_sleep) + { + server_time += 2; + + ens_read_temp(); + //wakeHostForStrobedTrigger += ulp_data_process_tempHumi_thresholds2(ens_temp_raw, prev_ens_temp_raw, 1); + wakeHostForStrobedTrigger += ulp_data_process_tempHumi_thresholds(ens_temp_raw, prev_ens_temp_raw, ULP_TEMPERATURE_SENSOR); + //wakeHostForStrobedTrigger += ulp_data_process_tempHumi_thresholds2(humidity_raw, prev_humidity_raw, 2); + wakeHostForStrobedTrigger += ulp_data_process_tempHumi_thresholds(humidity_raw, prev_humidity_raw, ULP_HUMIDITY_SENSOR); + + if((ulp_triggersThatCanWakeTheHost & WAKE_HOST_WHEN_THERMOCOUPLE_CROSSES_LIMITS)) + { + mcp_get_temp(); + //wakeHostForStrobedTrigger += ulp_data_process_tempHumi_thresholds2(thermo_temp_raw, prev_thermo_temp_raw, 3); + wakeHostForStrobedTrigger += ulp_data_process_tempHumi_thresholds(thermo_temp_raw, prev_thermo_temp_raw, ULP_THERMOCOUPLE_SENSOR); + } + + if(ulp_triggersThatCanWakeTheHost & (WAKE_HOST_ON_DARK_TO_LIGHT|WAKE_HOST_ON_LIGHT_TO_DARK)) + { + opt_read_reg(); + wakeHostForStrobedTrigger += ulp_data_process_lightSen_thresholds(prev_light_data_opt,light_data_opt); + } + + + + if(wakeHostForStrobedTrigger > 0) + { + wakeHostForStrobedTrigger = 0; + is_sleep = 0; + ulp_riscv_wakeup_main_processor(); + } +#if 0 + //if(tempC > 50.0) (tempC > (threshold+273.15)*64) + if(ens_temp_raw > 20681) + { + wakeup_type |= 1; + } + + if(thermo_temp_raw > 800) + { + wakeup_type |= 2; + } + /*if(thermoTemp > 50.0) + { + is_sleep = 0; + temp_needed = 0; + wakeup_type = 2; + ulp_riscv_wakeup_main_processor(); + }*/ + if(wakeup_type) + { + is_sleep = 0; + temp_needed = 0; + ulp_riscv_wakeup_main_processor(); + } +#endif + } + + ulp_riscv_delay_cycles(1000 * ULP_CLOCK_CYCLE_IN_MS); + } + return 0; +} + + + diff --git a/main/wifi_Client.c b/main/wifi_Client.c new file mode 100644 index 0000000..afd3c6c --- /dev/null +++ b/main/wifi_Client.c @@ -0,0 +1,710 @@ +/* + * tempstick_wifi_client.c + * + * Created on: Jul 28, 2023 + * Author: Sword + */ + + +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_log.h" +#include "esp_netif_types.h" +#include "main.h" +#include "nvs_flash.h" +#include "esp_event.h" +#include "esp_netif.h" +#include "esp_tls.h" +#include "esp_crt_bundle.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "esp_http_client.h" +#include "wifi_Client.h" +#include "wifi_Init.h" +#include "comms.h" +#include "modem.h" +#include "wifi_OTA.h" +#include "esp_mac.h" +#include "data_processing.h" +#include "hmi.h" +#include "comms.h" + +#if (WIFI_NEEDED == 1) +static const char* TAG = "CLIENT"; +#define LOG_LOCAL_LEVEL ESP_LOG_INFO + +extern uint32_t g_version; +extern uint32_t g_McuUpdate; + +char http_resp[512]; + +bool isDataNeeded = false; +bool getBinFile; + +static char *output_buffer; +static server_type_t selected_server; +deviceToServer_msgType_t post_msg_type; + +char url[200]; +//char onboarding_host[] = ON_BOARDING_SERVER_DOMAIN_NAME; + +char *params; +uint8_t http_client_test_task_end = 0; +uint8_t http_client_post_task_end = 0; +uint8_t http_client_get_task_end = 0; +esp_err_t http_client_test_err; +esp_err_t http_client_post_err; +esp_err_t http_client_get_err; + + + +esp_err_t http_event_handler(esp_http_client_event_t *evt) +{ + //static char *output_buffer; // Buffer to store response of http request from event handler + //static int output_len; // Stores number of bytes read + switch(evt->event_id) + { + case HTTP_EVENT_ERROR: + ESP_LOGD(TAG, "HTTP_EVENT_ERROR"); + break; + + case HTTP_EVENT_ON_CONNECTED: + ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED"); + break; + + case HTTP_EVENT_HEADER_SENT: + ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT"); + break; + + case HTTP_EVENT_ON_HEADER: + ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value); + break; + + case HTTP_EVENT_ON_DATA: + ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len); + /* + * Check for chunked encoding is added as the URL for chunked encoding used in this example returns binary data. + * However, event handler can also be used in case chunked encoding is used. + */ + /*if (!esp_http_client_is_chunked_response(evt->client)) + { + // If user_data buffer is configured, copy the response into the buffer + if (evt->user_data) + { + memcpy(evt->user_data + output_len, evt->data, evt->data_len); + } + else + { + if (output_buffer == NULL) + { + output_buffer = (char *)pvPortMalloc(esp_http_client_get_content_length(evt->client)); + output_len = 0; + if (output_buffer == NULL) + { + ESP_LOGE(TAG, "Failed to allocate memory for output buffer"); + return ESP_FAIL; + } + } + memcpy(output_buffer + output_len, evt->data, evt->data_len); + } + output_len += evt->data_len; + }*/ + if(isDataNeeded && (evt->data_len > 0)) + { + //output_buffer = (char *)pvPortMalloc(evt->data_len); + memcpy(output_buffer, evt->data, evt->data_len); + output_buffer[evt->data_len] = 0; + ESP_LOGI(TAG, "%s\n", output_buffer); + strcpy(http_resp, output_buffer); + ESP_LOGI(TAG, "http_resp ==> %s\n", http_resp); + } + + break; + + case HTTP_EVENT_ON_FINISH: + ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH"); + /*if (output_buffer != NULL) + { + ESP_LOGI(TAG, "HTTP Response:\n%s", output_buffer); + vPortFree(output_buffer); + output_buffer = NULL; + } + output_len = 0;*/ + break; + + case HTTP_EVENT_DISCONNECTED: + //isDataNeeded = false; + //vPortFree(output_buffer); + + ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED"); + int mbedtls_err = 0; + esp_err_t err = esp_tls_get_and_clear_last_error((esp_tls_error_handle_t)evt->data, &mbedtls_err, NULL); + if (err != 0) + { + ESP_LOGI(TAG, "Last esp error code: 0x%x\nLast mbedtls failure: 0x%x", err, mbedtls_err); + } + /*if (output_buffer != NULL) + { + vPortFree(output_buffer); + output_buffer = NULL; + } + output_len = 0;*/ + break; + + case HTTP_EVENT_REDIRECT: + ESP_LOGI(TAG, "HTTP_EVENT_REDIRECT"); + esp_http_client_set_redirection(evt->client); + break; + } + return ESP_OK; +} + +static int sum_digits(int digit) +{ + int sum = 0; + while (digit > 0) + { + sum = sum + digit % 10; + digit = digit / 10; + } + return sum; +} + + +static bool mac_check_valid(char *message_imei) +{ + //find the length of message imei + int len = strlen(message_imei); + ESP_LOGI(TAG,"Received MAC ID : %s",message_imei); + ESP_LOGI(TAG,"Board MAC ID : %s",MAC_ID); + + //check length of imei field + if (len != 15){ + //invalid IMEI + ESP_LOGW(TAG,"Invalid MAC length"); + return false; + } + if(!strcmp(message_imei,MAC_ID)){ + + ESP_LOGW(TAG,"Valid MAC "); + return true; + } + else{ + ESP_LOGI(TAG,"Message IMEI doesn't match board IMEI"); + return false; + } +} + + +static bool imei_check_valid(char *message_imei) +{ + //find the length of message imei + int len = strlen(message_imei); + int sum = 0; + ESP_LOGI(TAG,"IMEI : %s",message_imei); + ESP_LOGI(TAG,"Board IMEI : %s",MAC_ID); + + //check length of imei field + if (len != 15){ + //invalid IMEI + ESP_LOGW(TAG,"Invalid MAC length"); + return false; + } + if(!strcmp(message_imei,MAC_ID)){ + + for(int i = len-1 ; i >= 0; i--) + { + //check if message imei fields contain numbers (0-9) + if((message_imei[i] > 47) && (message_imei[i] < 58)){ + int digit = (message_imei[i] - 48); + // Doubling every alternate beginning at [13] digit + //ignore last number [14] and first number [0] as well + if (i % 2 != 0) + digit = 2 * digit; + // Finding sum of the digits + // if digit*2 is a two digit number add the two digits + sum += sum_digits(digit); + + ESP_LOGW(TAG,"Sum value = %d",sum); + ESP_LOGW(TAG,"Sum mod value = %d",sum %10); + + } + else{ + ESP_LOGW(TAG,"Invalid IMEI (not numerical values)"); + return false; + } + } + } + else{ + ESP_LOGI(TAG,"Message IMEI doesn't match board IMEI"); + return false; + } + + return (sum % 10 == 0); +} + +/*SP5 866349048889004 1676395843 FW_2002 EP */ +static void wifi_Parse_SP5(char *buf, int len) +{ + char messageheader[4] = {0}; /*ex: SPx*/ + char messageIMEI[15] = {0}; + // char messageIMEI[] = "866349048890473";/*//hard coded IMEI*/ + char epochTime[30] = {0}; + char firmware_version[10] = {0}; + char *strret = NULL; + uint32_t new_fw_version = 0; + int ret_sp5 = -1; + + /**********************************************************************************************************/ + /*SP5*/ + strret = strtok(buf, " "); + strcpy(messageheader, strret); + ESP_LOGI(TAG,"%s message is received",messageheader); + + /**********************************************************************************************************/ + /*IMEI*/ + strret = strtok (NULL," "); + strcpy(messageIMEI, strret); + ret_sp5 = mac_check_valid(messageIMEI); + if(ret_sp5 != false){ + //ok IMEI + ESP_LOGI(TAG,"Fetched IMEI matches board"); + } + else{ + ESP_LOGW(TAG,"INVALID IMEI"); + return; + //return ret_sp5; + } + + /**********************************************************************************************************/ + /*epoch time*/ + strret = strtok (NULL," "); + strcpy(epochTime, strret); + ESP_LOGI(TAG,"epoch Time : %s",epochTime); + + /**********************************************************************************************************/ + /*new FW version*/ + strret = strtok(NULL, "FW_"); + strcpy(firmware_version,strret); + new_fw_version = (uint32_t)atoi(firmware_version); + ESP_LOGI(TAG,"Firmware version from SP5: %lu\nBoard Firmware version: %lu",new_fw_version,g_version); //logging/printing FR_version message for tracing + + /**********************************************************************************************************/ + + // test version + new_fw_version = 1; + + /*check if the new version is greater than current version*/ + if(!(new_fw_version > g_version)) + { + ESP_LOGI(TAG,"MCU F/W UP TO DATE"); + } + else + { + /*Logging/printing this message*/ + ESP_LOGI(TAG,"F/W Update NEEDED\nGET MCU UPDATE"); + + /*reading OTA files*/ + getBinFile = true; + /*reading OTA files*/ + } + +} + + + +static void http_client_test_task(void *pvParameters) +{ + esp_http_client_config_t config = { + .url = (const char*)url, + .crt_bundle_attach = esp_crt_bundle_attach, + .event_handler = http_event_handler + }; + + //config.event_handler = http_event_handler; + //config.url = (char *)pvPortMalloc((1 + strlen(url))*sizeof(char)); + //strcpy((char *)config.url, url); + + esp_http_client_handle_t client = esp_http_client_init(&config); + esp_err_t err = esp_http_client_perform(client); + + if (err == ESP_OK) + { + ESP_LOGI(TAG, "HTTPS Status = %d, content_length = %lld", esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); + } + else + { + ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err)); + } + //vPortFree((char *)config.url); + esp_http_client_cleanup(client); + http_client_test_err = err; + http_client_test_task_end = 1; + vTaskDelete(NULL); +} + +esp_http_client_handle_t client_post; +static void http_client_post_task(void *pvParameters) +{ + /* make sure that the client_post is not initialized before (to be able to initialized it without any problems) */ + if(!client_post) + { + /* Set the configurations */ + esp_http_client_config_t config = { + .url = (const char*)url, + .method = HTTP_METHOD_POST, + .buffer_size = 2560, + .crt_bundle_attach = esp_crt_bundle_attach, + .event_handler = http_event_handler + }; + + /* Initialize client_post with the entered configurations */ + client_post = esp_http_client_init(&config); + } + + /* Set the header of the post message */ + if(post_msg_type == CHECK_IN_STR) + { + esp_http_client_set_header(client_post, "Authorization", AUTHERIZATION_VALUE); + //esp_http_client_set_header(client, "Host", "testdevice.tempstickapi.com"); + esp_http_client_set_header(client_post, "User-Agent", USER_AGENT_VALUE); + esp_http_client_set_header(client_post, "Content-Type", CONTENT_TYPE_VALUE); + } + else if(post_msg_type == ON_BOARDING_STR) + { + esp_http_client_set_header(client_post, "Authorization", AUTHERIZATION_VALUE_ONBOARDING); + //esp_http_client_set_header(client, "Host", ON_BOARDING_SERVER_DOMAIN_NAME); + esp_http_client_set_header(client_post, "User-Agent", USER_AGENT_ONBOARDING); + esp_http_client_set_header(client_post, "Content-Type", CONTENT_TYPE_VALUE_ONBOARDING); + } + + /* Set the check-in message */ + esp_http_client_set_post_field(client_post, params, strlen(params)); + + esp_err_t err = esp_http_client_perform(client_post); + + if (err == ESP_OK) + { + ESP_LOGI(TAG, "HTTPS Status = %d, content_length = %lld", esp_http_client_get_status_code(client_post), esp_http_client_get_content_length(client_post)); + } + else + { + ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err)); + } + + //esp_http_client_cleanup(client_post); + http_client_post_err = err; + http_client_post_task_end = 1; + vTaskDelete(NULL); +} + +static void http_client_get_task(void *pvParameters) +{ + esp_http_client_config_t config = { + .url = (const char*)url, // Replace with your desired URL + .method = HTTP_METHOD_GET, + .cert_pem = NULL, + .user_data = http_resp, + .event_handler = http_event_handler + }; + + esp_http_client_handle_t client = esp_http_client_init(&config); + esp_err_t err = esp_http_client_perform(client); + + if (err == ESP_OK) { + ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %lld", + esp_http_client_get_status_code(client), + esp_http_client_get_content_length(client)); + } else { + ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err)); + } + + esp_http_client_cleanup(client); + http_client_get_err = err; + http_client_get_task_end = 1; + vTaskDelete(NULL); +} + + +esp_err_t http_client_test(char *url1) +{ + strcpy(url,url1); + isDataNeeded = false; + http_client_test_task_end = 0; + xTaskCreate(http_client_test_task, "http_client_test_task", 4096, NULL, 17, NULL); + while(!http_client_test_task_end) + { + vTaskDelay(10 / portTICK_PERIOD_MS); + } + return http_client_test_err; +} + +esp_err_t http_client_post(char *url1, char *params1, char *response) +{ + http_client_post_task_end = 0; + strcpy(url,url1); + params = params1; + output_buffer = response; + isDataNeeded = true; + xTaskCreate(http_client_post_task, "http_client_post_task", 4096, NULL, 17, NULL); + while(!http_client_post_task_end) + { + vTaskDelay(10 / portTICK_PERIOD_MS); + } + return http_client_post_err; +} + +void http_client_post_stop(void) +{ + esp_http_client_cleanup(client_post); + client_post = NULL; +} + +esp_err_t http_client_get(const char *url1, char *response) +{ + http_client_get_task_end = 0; + strcpy(url,url1); + output_buffer = response; + isDataNeeded = true; + xTaskCreate(http_client_get_task, "http_client_get_task", 4096, NULL, 17, NULL); + while(!http_client_get_task_end) + { + vTaskDelay(10 / portTICK_PERIOD_MS); + } + isDataNeeded = false; + return http_client_get_err; +} + +esp_err_t http_get_response_buf(char* bufptr, int len) +{ + if (bufptr && len) + { + uint16_t buf_len = strlen(http_resp); + ESP_LOGI(TAG, "output_buffer => : %s\n",output_buffer); + ESP_LOGI(TAG, "http_resp => : %s\n",http_resp); + ESP_LOGI(TAG, " buf len %d", buf_len); + if (http_resp && buf_len > 0) { + strcpy(bufptr, http_resp); + // *bufptr = output_buffer; + len = buf_len; + ESP_LOGI(TAG, "BUF: %s\nLEN : %d", bufptr, len); + return ESP_OK; + } + bufptr = 0; + len = 0; + } + return ESP_FAIL; +} + +esp_err_t http_client_do_get_request(get_req_type_t target_url, char* device_imei, char *response_buf) +{ + uint8_t retval = WIFI_FAIL; + char server_url[200]; + + switch(target_url) + { + case GET_CONFIGURATIONS: + + ESP_LOGI(TAG,"/******** Configuration -SP3- GET request ********/"); + + /* Try with server-1 */ + if(selected_server == SERVER_ONE) + { + sprintf(server_url, MCU_GET_CONFIG_FLAGS_URL1, device_imei); + retval = http_client_get((const char*)server_url,response_buf); + if(retval != WIFI_OK) + { + ESP_LOGI(TAG,"Server-1 fails to connect. Switching to Server-2"); + selected_server = SERVER_TWO; + } + } + + /* Try with server-2 if & only if server-1 fails to connect */ + if(selected_server == SERVER_TWO) + { + sprintf(server_url, MCU_GET_CONFIG_FLAGS_URL2, device_imei); + http_client_get((const char*)server_url,response_buf); + if(retval != WIFI_OK) + { + selected_server = SERVER_THREE; + ESP_LOGI(TAG,"Server-2 fails to connect. Switching to Server-3"); + } + } + + /* Try with server-3 if & only if server-2 fails to connect */ + if(selected_server == SERVER_THREE) + { + sprintf(server_url, MCU_GET_CONFIG_FLAGS_URL3, device_imei); + http_client_get((const char*)server_url,response_buf); + if(retval != WIFI_OK) + { + selected_server = SERVER_ONE; + ESP_LOGI(TAG,"Server-3 fails to connect. Stopping WIFI-Client."); + retval = WIFI_FAIL; + } + } + + /* Parse the SP3 response */ + if(retval == ESP_OK) + data_parsing_config(response_buf, strlen(response_buf)); + else + break; + + /* IF MCU-Update flag is set then continue to the next case. if not then break */ + if(!g_McuUpdate) + { + break; + } + + case GET_MCU_UPDATE_VERSION: + + ESP_LOGI(TAG,"/******** MCU_Update -SP5- GET request ********/"); + + /* Try with server-1 */ + if(selected_server == SERVER_ONE) + { + sprintf(server_url, MCU_FW_VERSION_URL1, device_imei); + retval = http_client_get((const char*)server_url,response_buf); + if(retval != WIFI_OK) + { + selected_server = SERVER_TWO; + ESP_LOGI(TAG,"Server-1 fails to connect. Switching to Server-2"); + } + } + + /* Try with server-2 if & only if server-1 fails to connect */ + if(selected_server == SERVER_TWO) + { + sprintf(server_url, MCU_FW_VERSION_URL2, device_imei); + http_client_get((const char*)server_url,response_buf); + if(retval != WIFI_OK) + { + ESP_LOGI(TAG,"Server-2 fails to connect. Switching to Server-3"); + selected_server = SERVER_THREE; + } + } + + /* Try with server-3 if & only if server-2 fails to connect */ + if(selected_server == SERVER_THREE) + { + sprintf(server_url, MCU_FW_VERSION_URL3, device_imei); + http_client_get((const char*)server_url,response_buf); + if(retval != WIFI_OK) + { + selected_server = SERVER_ONE; + ESP_LOGI(TAG,"Server-3 fails to connect. Stopping WIFI-Client."); + retval = WIFI_FAIL; + } + } + + /* Parse the SP5 response */ + if(retval == ESP_OK) + wifi_Parse_SP5(response_buf, strlen(response_buf)); + + /* Enable WIFI-OTA if MCU-FW is less than the one in SP5 message */ + if(getBinFile) + { + wifi_ota_start_firmware_update(device_imei); + hmi_set_leds_state(BLUE_LED_FLASHING_OTA); + while(OTA_IN_PROGRESS == wifi_ota_get_status()) + { + vTaskDelay(100/portTICK_PERIOD_MS); + } + + /* LOG this error message */ + hmi_stop_ota_event(); + ESP_LOGE(TAG,"Error in WIFI-OTA process. continuing on the current firmware."); + retval = ESP_FAIL; + } + + break; + + case GET_MODEM_UPDATE_VERSION: + break; + + default: + break; + } + + return retval; +} + +esp_err_t http_client_do_post_request(char *post_params, char *response_buf) +{ + uint8_t retval = WIFI_FAIL; + selected_server = SERVER_ONE; + + /* Try with server-1 */ + if(selected_server == SERVER_ONE) + { + /* Set the POST-URL to be Server1 */ + data_set_postToServer(TEMPSTICK_SERVER1); + + /* Create the Check-in message */ + data_create_checkin_string(post_params,2304); + + /* Printing the check-in string */ + ESP_LOGI(TAG,"%s",post_params); + + /* Post that Check-in message to the target server */ + retval = http_client_post((char*)MCU_POST_URL1,post_params,response_buf); + if(retval != WIFI_OK) + { + selected_server = SERVER_TWO; + ESP_LOGI(TAG,"Server-1 fails to connect. Switching to Server-2"); + } + } + + /* Try with server-2 if & only if server-1 fails to connect */ + if(selected_server == SERVER_TWO) + { + /* Set the POST-URL to be Server2 */ + data_set_postToServer(TEMPSTICK_SERVER2); + + /* Create the Check-in message */ + data_create_checkin_string(post_params,2304); + + /* Post that Check-in message to the target server */ + retval = http_client_post((char*)MCU_POST_URL2,post_params,response_buf); + if(retval != WIFI_OK) + { + ESP_LOGI(TAG,"Server-2 fails to connect. Switching to Server-3"); + selected_server = SERVER_THREE; + } + } + + /* Try with server-3 if & only if server-2 fails to connect */ + if(selected_server == SERVER_THREE) + { + /* Set the POST-URL to be Server3 */ + data_set_postToServer(TEMPSTICK_SERVER3); + + /* Create the Check-in message */ + data_create_checkin_string(post_params,2304); + + /* Post that Check-in message to the target server */ + retval = http_client_post((char*)MCU_POST_URL3,post_params,response_buf); + if(retval != WIFI_OK) + { + selected_server = SERVER_ONE; + ESP_LOGI(TAG,"Server-3 fails to connect. Stopping WIFI-Client."); + retval = WIFI_FAIL; + } + } + + return retval; +} + +void wifi_set_post_str_type(deviceToServer_msgType_t str) +{ + post_msg_type = str; +} + + +#endif + + diff --git a/main/wifi_Client.h b/main/wifi_Client.h new file mode 100644 index 0000000..b64d2ac --- /dev/null +++ b/main/wifi_Client.h @@ -0,0 +1,52 @@ +/* + * tempstick_wifi_client.h + * + * Created on: Jul 28, 2023 + * Author: Sword + */ + +#ifndef MAIN_WIFI_CLIENT_H_ +#define MAIN_WIFI_CLIENT_H_ + +#define WIFI_OK 0 +#define WIFI_FAIL -1 +#define WIFI_MCU_UPDATE_NEEDED 2 + +#define WIFI_OK 0 +#define WIFI_FAIL -1 +#define WIFI_MCU_UPDATE_NEEDED 2 + +typedef enum +{ + GET_CONFIGURATIONS, + GET_MCU_UPDATE_VERSION, + GET_MODEM_UPDATE_VERSION +}get_req_type_t; + +typedef enum +{ + SERVER_ONE, + SERVER_TWO, + SERVER_THREE +}server_type_t; + +#include "wifi_Init.h" +#include "main.h" +#if (WIFI_NEEDED == 1) +esp_err_t http_client_test(char *url); +esp_err_t http_client_post(char *url, char *params, char *response); +void http_client_post_stop(void); +esp_err_t http_client_get(const char *url1, char *response); + +esp_err_t http_get_response_buf(char* bufptr, int len); + +esp_err_t http_client_do_get_request(get_req_type_t target_url, char* device_imei, char *response_buf); +esp_err_t http_client_do_post_request(char *post_params, char *response_buf); +void wifi_set_post_str_type(deviceToServer_msgType_t str);; + +bool registerAcctWithServer(char *typeStr, char *customerAccountIdStr, char *customerAccountPasswordStr, char *customerAccountPasswordAgainStr, char *responseStr, uint16_t responseStrSize); +#endif + + + +#endif /* MAIN_WIFI_CLIENT_H_ */ diff --git a/main/wifi_Init.c b/main/wifi_Init.c new file mode 100644 index 0000000..4a29caa --- /dev/null +++ b/main/wifi_Init.c @@ -0,0 +1,679 @@ +/* + * wifi_Init.c + * + * Created on: 1/8/2023 + * Author: Sword + */ + + + +#include "esp_system.h" +#include "esp_event.h" +#include "esp_netif.h" +#include "esp_wifi.h" +#include "esp_mac.h" +#include "lwip/ip4_addr.h" +#include "freertos/event_groups.h" +#include "wifi_Init.h" +#include "wifi_Client.h" +#include "string.h" +#include "main.h" +#include "nvm.h" +#include "data_processing.h" +#include "port.h" + +#if (WIFI_NEEDED == 1) +static const char* TAG = "WIFI"; + +#define LOG_LOCAL_LEVEL ESP_LOG_INFO +#include "esp_log.h" + +#define ESP_MAXIMUM_RETRY 5 +#define WIFI_CONNECTED_BIT BIT0 +#define WIFI_FAIL_BIT BIT1 +#define WIFI_SCAN_DONE_BIT BIT2 +#define WIFI_AP_CONNECTED_BIT BIT3 +#define WIFI_AP_DISCONNECTED_BIT BIT4 +#define WIFI_DISCONNECTED_BIT BIT5 + +#define WIFI_AP_SSID "Azuma Wifi Setup " + +static char g_pWifiSsidA[MAX_SSID_LEN]; +static char g_pWifiPasswordA[MAX_PASSPHRASE_LEN]; +static char g_pWifiSsidB[MAX_SSID_LEN]; +static char g_pWifiPasswordB[MAX_PASSPHRASE_LEN]; + +/* WIFI connection TIME */ +uint32_t g_wifiStartTime; +extern uint32_t g_wifiConnectTime; + +/* WIFI SSID's & PASSWORDS */ +static char Sta_Ssid[MAX_SSID_LEN]; +static char Sta_Pwd[MAX_PASSPHRASE_LEN] = {0}; + +RTC_DATA_ATTR uint32_t wifiSelection; +wifi_ap_record_t scanned_networks[20]; +uint8_t device_wifi_onboarded; + +/* WIFI RSSI & STATUS */ +static uint8_t isStaConnected; +static bool isStarted; +static bool isInitialized; +static bool isHandlerRegister; + +/* FreeRTOS event group to signal when we are connected*/ +static EventGroupHandle_t s_wifi_event_group; +static bool set_wifi_bits; +static int s_retry_num = 0; +static bool turnOffStation; + +int qmtopen_retries = 0; +bool qmtopen_fail = false; +bool qmtclose_fail = false; +bool cops_fail = false; + +/* This structure holds the WIFI configurations (modes, ip's, ssid&pwd ...etc )*/ +esp_netif_t *sta_netif; +esp_netif_t *ap_netif; +wifi_config_t wifi_config; +esp_netif_ip_info_t wifi_sta_ip_infos; + + +/* This is the handler that will be executed when receiving any WIFI-events or IP-events */ +static void event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) + { + /* This Log will be printed as a result of esp_wifi_start (when the wifi mode is STA or AP&STA )*/ + ESP_LOGI(TAG, " -----> Station(Client) mode Start"); + } + else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_START) + { + /* This Log will be printed as a result of esp_wifi_start (when the wifi mode is AP or AP&STA )*/ + ESP_LOGI(TAG, " -----> ACCESS-POINT Start"); + } + else if(event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) + { + /* This Log will be printed as a result of esp_wifi_connect (when the wifi mode is STA or AP&STA ) + * Indicates successful connection to the wifi-network or to the access point */ + ESP_LOGI(TAG, " -----> Station(client) Connected to the registered AP"); + } + else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) + { + if(turnOffStation) + { + if(set_wifi_bits) + { + xEventGroupSetBits(s_wifi_event_group, WIFI_DISCONNECTED_BIT); + } + ESP_LOGI(TAG, " ---> Disconnecting wifi-station successfully"); + } + else + { + isStaConnected = 0; + if (s_retry_num < ESP_MAXIMUM_RETRY) + { + esp_wifi_connect(); + s_retry_num++; + ESP_LOGI(TAG, " ---> Retry to connect to the AP !!!"); + } + else + { + if(set_wifi_bits) + { + xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + } + } + ESP_LOGI(TAG, " ---> Connecting to the registered AP failed !!!"); + } + } + else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) + { + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + ESP_LOGI(TAG, "------> The Device got the ip-address:" IPSTR, IP2STR(&event->ip_info.ip)); + s_retry_num = 0; + if(set_wifi_bits) + { + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + } + + /* 1- Take and store the ip-address infos */ + data_set_local_ip_addr(&event->ip_info); + } + else if(event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) + { + if(set_wifi_bits) + { + xEventGroupSetBits(s_wifi_event_group, WIFI_SCAN_DONE_BIT); + } + } + else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED) + { + wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data; + ESP_LOGI(TAG, "station "MACSTR" join, AID=%d", MAC2STR(event->mac), event->aid); + if(set_wifi_bits) + { + xEventGroupSetBits(s_wifi_event_group, WIFI_AP_CONNECTED_BIT); + } + } + else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STADISCONNECTED) + { + wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data; + ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d", MAC2STR(event->mac), event->aid); + if(set_wifi_bits) + { + xEventGroupSetBits(s_wifi_event_group, WIFI_AP_DISCONNECTED_BIT); + } + } +} + + +/* Functions for Starting WIFI as Client, Server and Scan nearby networks */ +esp_err_t Connect_wifi_sta(uint8_t mode) +{ + /* Create the return variable */ + esp_err_t retval = ESP_FAIL; + + time_t timeNow; + time(&timeNow); + uint32_t temp_time=0; + + g_wifiStartTime = (uint32_t)timeNow; + + /* Check if the esp-32 was connected to network before. If so then disconnect it and reconnect again after 3sec */ + if(isStaConnected) + { + wifi_sta_disconnecting(); + vTaskDelay(3000/portTICK_PERIOD_MS); + } + + /* Create a WIFI-event group and create its corresponding bits */ + s_wifi_event_group = xEventGroupCreate(); + set_wifi_bits = true; + EventBits_t bits; + + /* Set the wifi_disconnecting flag to be false */ + turnOffStation = false; + + // 2 - Wi-Fi Configuration Phase + + /* Assign a handler that will be executed when receiving any WIFI-events */ + //esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL); + + /* Update the SSID and Password that ESP32 will use them to connect to the network when acting acting as WIFI-STATION */ + /*if(wifiSelection) + { + strcpy((char *)wifi_config.sta.ssid, g_pWifiSsidB); + strcpy((char *)wifi_config.sta.password, g_pWifiPasswordB); + } + else*/ + { +#ifdef STORING_TO_NVM + //device_wifi_onboarded = nvm_read_onboarding_flag(WIFI_ONBOARDING_KEY); + if(device_wifi_onboarded) + { + /* Connecting to the network that has been registered since the last onboarding process + * This if-block will be executed when doing the CHECK-IN process */ + ESP_LOGI(TAG," Device Onboarded.....Reading WiFi Credentials...."); + nvm_read_wifi_credentials(Sta_Ssid,Sta_Pwd); + } +#endif + + /* Copying the network credentials to the wifi_config structure so that the esp-client can connect to */ + strcpy((char *)wifi_config.sta.ssid, Sta_Ssid); + strcpy((char *)wifi_config.sta.password, Sta_Pwd); + + } + + /* Make sure that there's a valid entered SSID to connect to */ + if((Sta_Ssid[0] == 0)) + { + //ESP_LOGE(TAG,"----> There's no valid SSID to connect to"); + return ESP_FAIL; + } + + /* Set the WIFI-mode & WIFI-configs then start and connect to the WIFI network */ + ESP_LOGI(TAG," Initializing and starting WIFI-Client "); + ESP_ERROR_CHECK(esp_wifi_set_mode(mode)); + wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + if(!isStarted) + ESP_ERROR_CHECK(esp_wifi_start()); + + /**/ + ESP_ERROR_CHECK(esp_wifi_connect()); + + //ESP_LOGI(TAG, "wifi_init_sta finished."); + ESP_LOGI(TAG, "WIFI STA connection Start..."); + + /* block to wait for one or more bits to be set within a previously created event group (s_wifi_event_group) */ + bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + + /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually + * happened. */ + if (bits & WIFI_CONNECTED_BIT) + { + /* Set the WIFI connect Time */ + time(&timeNow); + temp_time = (timeNow - g_wifiStartTime); + g_wifiConnectTime = (uint8_t)((temp_time > 255)? 255 : temp_time); + + /* Get the RSSI value of WIFI */ + /*int wifi_rssi; + esp_wifi_sta_get_rssi((int*)&wifi_rssi);*/ + wifi_ap_record_t ap_info; + esp_wifi_sta_get_ap_info(&ap_info); + data_set_wifi_rssi(ap_info.rssi); + ESP_LOGI(TAG, "Connected to AP SSID:%s password:%s", Sta_Ssid, Sta_Pwd); + + /* 2- Take and store the dns-server info */ + esp_netif_dns_info_t dns1, dns2; + esp_netif_get_dns_info(sta_netif,ESP_NETIF_DNS_MAIN,&dns1); + esp_netif_get_dns_info(sta_netif,ESP_NETIF_DNS_BACKUP,&dns2); + data_set_main_and_backup_dns(&dns1,&dns2); + + /* 3- Take and store the mac-address of connected AP */ + data_set_ap_mac_addr((uint8_t*)&wifi_config.sta.bssid); + + /* 4- Take and store the wifi-channel */ + data_set_wifi_channel(wifi_config.sta.channel); + + /* Set the connection flag */ + isStaConnected = 1; + + /* set the return value to be ESP_OK */ + retval = ESP_OK; + } + else if (bits & WIFI_FAIL_BIT) + { + ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", Sta_Ssid, Sta_Pwd); + isStaConnected = 0; + + /* set the return value to be ESP_FAIL */ + retval = ESP_FAIL; + } + else + { + ESP_LOGE(TAG, "UNEXPECTED EVENT"); + } + + /* Delete the created event group after receiving the required bits or "UNEXPECTED EVENT" */ + vEventGroupDelete(s_wifi_event_group); + set_wifi_bits = false; + + /* return this value */ + return retval; +} + +void Wifi_Init_SoftAp(void) +{ + port_red_led_on(); + char ap_ssid[32]; + char mac_str[15]; + uint8_t mac_base[6] = {0}; + + /* Check if the onboarded-flag is false (Don't start AP unless the device hasn't been on-boarded) */ + //if(!device_wifi_onboarded) + { + /* Read the device mac-address */ + esp_efuse_mac_get_default(mac_base); + esp_read_mac(mac_base, ESP_MAC_WIFI_SOFTAP); + + /* Set the SSID-name of the esp32-AP (Sensor Setup -deviceID-) */ + strcpy(ap_ssid, WIFI_AP_SSID); + sprintf(mac_str, "%02X%02X%02X", mac_base[3], mac_base[4], mac_base[5]); + strcat(ap_ssid, mac_str); + ESP_LOGI(TAG, "AP SSID: %s", ap_ssid); + + /* Start event group to read the connection bits */ + s_wifi_event_group = xEventGroupCreate(); + set_wifi_bits = true; + + /* Set the configurations for the esp-32 AP */ + wifi_config.ap.max_connection = 4; + wifi_config.ap.authmode = WIFI_AUTH_OPEN; + strcpy((char *)wifi_config.sta.ssid, ""); + strcpy((char *)wifi_config.sta.password, ""); + wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; + + /* Set the SSID and PASSWORD of the esp-32 AP */ + strcpy((char *)wifi_config.ap.ssid, ap_ssid); + wifi_config.ap.ssid_len = strlen(ap_ssid); + + /* Set the WIFI-mode to be AP&STA */ + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + isStarted = true; + + ESP_LOGI(TAG, "Access-Point Initialization finished."); + + /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum + * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ + EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_AP_CONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + + /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually + * happened. */ + if (bits & WIFI_AP_CONNECTED_BIT) + { + ESP_LOGI(TAG, "AP Connected"); + } + else if (bits & WIFI_FAIL_BIT) + { + ESP_LOGI(TAG, "AP failed to connect"); + } + else + { + ESP_LOGE(TAG, "UNEXPECTED EVENT"); + } + + vEventGroupDelete(s_wifi_event_group); + set_wifi_bits = false; + } +} + +void wifi_scan_start(uint16_t *apCount, wifi_ap_record_t **list) +{ + s_wifi_event_group = xEventGroupCreate(); + set_wifi_bits = true; + + wifi_scan_config_t scan_config = { + .ssid = NULL, + .bssid = NULL, + .channel = 0, + .show_hidden = true, + .scan_type = WIFI_SCAN_TYPE_ACTIVE, + .scan_time.active.min = 10, + .scan_time.active.max = 10000 + }; + ESP_LOGI(TAG, "Start Scan"); + + esp_wifi_scan_start(&scan_config, true); + EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_SCAN_DONE_BIT, pdFALSE, pdFALSE, 5000 / portTICK_PERIOD_MS); + esp_wifi_scan_stop(); + ESP_LOGI(TAG, "Stop Scan"); + + vTaskDelay(100/portTICK_PERIOD_MS); + + if (bits & WIFI_SCAN_DONE_BIT) + { + esp_wifi_scan_get_ap_num(apCount); + + if(apCount) + { + //*list = (wifi_ap_record_t *)pvPortMalloc(sizeof(wifi_ap_record_t) * (*apCount)); + *list = scanned_networks; + if((*apCount)<20) + ESP_ERROR_CHECK((esp_wifi_scan_get_ap_records(apCount, *list))); + else + { + *apCount = 20; + ESP_ERROR_CHECK((esp_wifi_scan_get_ap_records(apCount, *list))); + } + } + } + + vEventGroupDelete(s_wifi_event_group); + set_wifi_bits = false; +} + + + +/* Update the wifi credentials (SSID&PWD) of the target network */ +void wifi_update_credentials(char *ssid, char *pwd) +{ + /* Update the SSID and password that the ESP will use when acting as wifi-client (station) */ + strcpy(Sta_Ssid, ssid); + strcpy(Sta_Pwd, pwd); +} + +uint8_t wifi_station_connected(void) +{ + return isStaConnected; +} + +bool wifi_isStarted(void) +{ + return isStarted; +} + +char *wifi_get_ssid(void) +{ + return Sta_Ssid; +} + +char *wifi_get_pswd(void) +{ + return Sta_Pwd; +} + +void wifi_switchToPrimaryNetwork(void) +{ + if(0 != wifiSelection) + { + wifiSelection = 0; // Primary WiFi + data_clearWifiConnectionSettings(); + } +} // End of switchToPrimaryWifiNetwork() + + + +/* Functions to Set/get WIFI-credentials of networkA */ +char *wifi_get_ssidA(void) +{ + return g_pWifiSsidA; +} + +char *wifi_get_pswdA(void) +{ + return g_pWifiPasswordA; +} + +void wifi_set_ssidA(char* ssid) +{ + strcpy(g_pWifiSsidA,ssid); +} + +void wifi_set_pswdA(char* pswd) +{ + strcpy(g_pWifiPasswordA,pswd); +} + + + +/* Functions to Set/get WIFI-credentials of networkA */ +char *wifi_get_ssidB(void) +{ + return g_pWifiSsidB; +} + +char *wifi_get_pswdB(void) +{ + return g_pWifiPasswordB; +} + +void wifi_set_ssidB(char* ssid) +{ + strcpy(g_pWifiSsidB,ssid); +} + +void wifi_set_pswdB(char* pswd) +{ + strcpy(g_pWifiPasswordB,pswd); +} + + + +/* WIFI functions for Initialization and De-initialization */ +esp_err_t wifi_first_init(void) +{ + /* Create the return variable */ + esp_err_t retval = ESP_FAIL; + + if(isInitialized) + { + return ESP_OK; + } + + /* Initialize the underlying TCP/IP stack */ + retval = esp_netif_init(); + + /* Create default event loop */ + retval = esp_event_loop_create_default(); + + /* Do the required initialization for setting the device -esp32- to act as WiFi-station and WIFI-AP */ + sta_netif = esp_netif_create_default_wifi_sta(); + + // if(!device_wifi_onboarded) + { + esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap(); + /* Stop the DHCP server to change the IP address of AP-gateway */ + retval = esp_netif_dhcps_stop(ap_netif); + + /* Create the structure of the new IP-address of the AP */ + esp_netif_ip_info_t ip_info; + + /* Set the IP-address to [10.10.1.1] and the same thing for gateway and subnet-mask is [255.255.255.0] */ + IP4_ADDR(&ip_info.ip, 10, 10, 1, 1); + IP4_ADDR(&ip_info.gw, 10, 10, 1, 1); + IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0); + + /* Update the IP-address info with the new ones */ + retval = esp_netif_set_ip_info(ap_netif, &ip_info); + ESP_LOGI(TAG, "esp_netif_set_ip_info(): 0x%X", retval); + + /* Restart the DHCP server again */ + retval = esp_netif_dhcps_start(ap_netif); + } + + /* Load the default init_configurations */ + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + retval = esp_wifi_init(&cfg); + + /* Set the initialization flag */ + isInitialized = true; + + /* Initialize and register the wifi and ip event-handler */ + wifi_register_event_handlers(); + + /* return the result */ + return retval; +} + +/* function for registering the handler for the received wifi or ip events (called once at the beginning of the program)*/ +void wifi_register_event_handlers(void) +{ + if(!isHandlerRegister) + { + /* 1- Assign the handler for receiving any wifi-event */ + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); + + /* 2- Assign a handler that will be executed when receiving the STA_GOT_IP event (when the ESP32 receive an IP from the DHCP of the router or the Access-Point) */ + esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler, NULL); + + isHandlerRegister = true; + } +} + +esp_err_t wifi_stop(void) +{ + /* Create the return variable */ + esp_err_t retval = ESP_FAIL; + + /* Create a WIFI-event group and create its corresponding bits */ + s_wifi_event_group = xEventGroupCreate(); + set_wifi_bits = true; + EventBits_t bits; + + //esp_wifi_restore(); + //esp_wifi_deinit(); + esp_wifi_stop(); + + /* block to wait for one or more bits to be set within a previously created event group (s_wifi_event_group) */ + bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_AP_DISCONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + + /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually + * happened. */ + if (bits & WIFI_AP_DISCONNECTED_BIT) + { + ESP_LOGI(TAG, "WIFI has been Stopped"); + retval = ESP_OK; + } + else + { + ESP_LOGE(TAG, "Failed to Stop WIFI. UNEXPECTED EVENT"); + retval = ESP_FAIL; + } + + /* Delete the created event group after receiving the required bits or "UNEXPECTED EVENT" */ + vEventGroupDelete(s_wifi_event_group); + set_wifi_bits = false; + + //ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler)); + isStaConnected = 0; + isStarted = false; + ap_netif = NULL; + //isInitialized = false; + + /* return the result */ + return retval; + +} + +void wifi_sta_disconnecting(void) +{ + EventBits_t bits; + + if(isStaConnected) + { + /* Create a WIFI-event group and create its corresponding bits */ + s_wifi_event_group = xEventGroupCreate(); + set_wifi_bits = true; + + /* Set the station turn-off flag to be true */ + turnOffStation = true; + + /* Disconnect the WIFI-Station */ + ESP_ERROR_CHECK(esp_wifi_disconnect()); + + /* block to wait for one or more bits to be set within a previously created event group (s_wifi_event_group) */ + bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_DISCONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + + /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually + * happened. */ + if (bits & WIFI_DISCONNECTED_BIT) + { + ESP_LOGI(TAG, "WIFI has been disconnected"); + //ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP,&event_handler)); + //isHandlerRegister = false; + isStaConnected = 0; + } + else + { + ESP_LOGE(TAG, "Failed to disconnect WIFI-Station. UNEXPECTED EVENT"); + } + + /* Delete the created event group after receiving the required bits or "UNEXPECTED EVENT" */ + vEventGroupDelete(s_wifi_event_group); + set_wifi_bits = false; + } +} + + + +bool safeStrCat(char *dest, uint16_t destSize, char *source) +{ + + if(strlen(source) > (destSize - 1 - strlen(dest))) + return(false); + + strcat(dest, source); + return(true); +} // End of safeStrCat + + +#endif + + + diff --git a/main/wifi_Init.h b/main/wifi_Init.h new file mode 100644 index 0000000..1a50aba --- /dev/null +++ b/main/wifi_Init.h @@ -0,0 +1,68 @@ +/* + * tempstick_wifi.h + * + * Created on: Jul 28, 2023 + * Author: Sword + */ + +#ifndef MAIN_WIFI_INIT_H_ +#define MAIN_WIFI_INIT_H_ + +#include "main.h" +#if (WIFI_NEEDED == 1) +#include "esp_wifi.h" + +typedef enum +{ + CHECK_IN_STR, + ON_BOARDING_STR +}deviceToServer_msgType_t; + + +#define STORING_TO_NVM + +#define WIFI_STATUS_OK 0 +#define WIFI_STATUS_ERROR -1 +#define WIFI_STATUS_TIMEOUT -2 +#define WIFI_STATUS_MQTT_PUB_OVERFLOW -10 + +#define WIFI_CLIENT_MODE 1 +#define WIFI_AP_MODE 2 +#define WIFI_CLIENT_AP_MODE 3 + + +esp_err_t Connect_wifi_sta(uint8_t mode); +uint8_t wifi_station_connected(void); +bool wifi_isStarted(void); +void Wifi_Init_SoftAp(void); + +esp_err_t wifi_first_init(void); +esp_err_t wifi_stop(void); +void wifi_register_event_handlers(void); +void wifi_sta_disconnecting(void); +void wifi_scan_start(uint16_t *apCount, wifi_ap_record_t **list); + + +void wifi_update_credentials(char *ssid, char *pwd); +char *wifi_get_ssid(void); +char *wifi_get_pswd(void); +void wifi_switchToPrimaryNetwork(void); + + +char *wifi_get_ssidA(void); +char *wifi_get_pswdA(void); +void wifi_set_ssidA(char* ssid); +void wifi_set_pswdA(char* pswd); + +char *wifi_get_ssidB(void); +char *wifi_get_pswdB(void); +void wifi_set_ssidB(char* ssid); +void wifi_set_pswdB(char* pswd); + +bool safeStrCat(char *dest, uint16_t destSize, char *source); + +#endif + + + +#endif /* MAIN_WIFI_INIT_H_ */ diff --git a/main/wifi_OTA.c b/main/wifi_OTA.c new file mode 100644 index 0000000..792f026 --- /dev/null +++ b/main/wifi_OTA.c @@ -0,0 +1,284 @@ +/* OTA example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_ota_ops.h" +#include "esp_crt_bundle.h" +#include "esp_app_format.h" +#include "esp_http_client.h" +#include "esp_flash_partitions.h" +#include "esp_partition.h" +#include "errno.h" +#include "main.h" +#include "comms.h" +#include "wifi_OTA.h" +#include "comms.h" + +#define BUFFSIZE 1024 +#define HASH_LEN 32 /* SHA-256 digest length */ + +static const char *TAG = "WIFI_OTA"; +/*an ota data write buffer ready to write to the flash*/ +static char ota_write_data[BUFFSIZE + 1] = { 0 }; + +#define OTA_URL_SIZE 256 + +char ota_url[OTA_URL_SIZE]; +wifi_ota_status_t ota_process_state; + +static void http_cleanup(esp_http_client_handle_t client) +{ + esp_http_client_close(client); + esp_http_client_cleanup(client); +} + +static void task_fatal_error(void) +{ + ESP_LOGE(TAG, "Exiting task due to fatal error during ota process..."); + (void)vTaskDelete(NULL); + ota_process_state = OTA_FATAL_ERROR; +} + +static void print_sha256 (const uint8_t *image_hash, const char *label) +{ + char hash_print[HASH_LEN * 2 + 1]; + hash_print[HASH_LEN * 2] = 0; + for (int i = 0; i < HASH_LEN; ++i) { + sprintf(&hash_print[i * 2], "%02x", image_hash[i]); + } + ESP_LOGI(TAG, "%s: %s", label, hash_print); +} + +static void infinite_loop(void) +{ + int i = 0; + ESP_LOGI(TAG, "The firmware that is available on the server is an OLD image\n WIFI-OTA stopped."); + ota_process_state = OTA_FATAL_ERROR; +} + +static void wifi_ota_task(void *pvParameter) +{ + esp_err_t err; + /* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */ + esp_ota_handle_t update_handle = 0 ; + const esp_partition_t *update_partition = NULL; + + ESP_LOGI(TAG, "Starting WIFI Firmware Update Process"); + + const esp_partition_t *configured = esp_ota_get_boot_partition(); + const esp_partition_t *running = esp_ota_get_running_partition(); + + if (configured != running) { + ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08"PRIx32", but running from offset 0x%08"PRIx32,configured->address, running->address); + ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)"); + } + ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08"PRIx32")",running->type, running->subtype, running->address); + + esp_http_client_config_t config = { + .url = ota_url, + .crt_bundle_attach = esp_crt_bundle_attach, + .timeout_ms = 5000, + .keep_alive_enable = true, + }; + + esp_http_client_handle_t client = esp_http_client_init(&config); + if (client == NULL) { + ESP_LOGE(TAG, "Failed to initialise HTTP connection"); + task_fatal_error(); + } + else + { + ESP_LOGI(TAG,"URL is : %s",config.url); + } + + err = esp_http_client_open(client, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err)); + esp_http_client_cleanup(client); + task_fatal_error(); + } + esp_http_client_fetch_headers(client); + + update_partition = esp_ota_get_next_update_partition(NULL); + assert(update_partition != NULL); + ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%"PRIx32,update_partition->subtype, update_partition->address); + + int binary_file_length = 0; + /*deal with all receive packet*/ + bool image_header_was_checked = false; + while (1) { + int data_read = esp_http_client_read(client, ota_write_data, BUFFSIZE); + if (data_read < 0) { + ESP_LOGE(TAG, "Error: SSL data read error"); + http_cleanup(client); + task_fatal_error(); + } else if (data_read > 0) { + if (image_header_was_checked == false) { + esp_app_desc_t new_app_info; + if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) { + // check current version with downloading + memcpy(&new_app_info, &ota_write_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t)); + ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version); + + esp_app_desc_t running_app_info; + if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) { + ESP_LOGI(TAG, "Running firmware version: %s", running_app_info.version); + } + + const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition(); + esp_app_desc_t invalid_app_info; + if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) { + ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version); + } + + // check current version with last invalid partition + if (last_invalid_app != NULL) { + if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) { + ESP_LOGW(TAG, "New version is the same as invalid version."); + ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version); + ESP_LOGW(TAG, "The firmware has been rolled back to the previous version."); + http_cleanup(client); + infinite_loop(); + } + } +#ifndef CONFIG_EXAMPLE_SKIP_VERSION_CHECK + if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) { + ESP_LOGW(TAG, "Current running version is the same as a new. We will not continue the update."); + http_cleanup(client); + infinite_loop(); + } +#endif + + image_header_was_checked = true; + + err = esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err)); + http_cleanup(client); + esp_ota_abort(update_handle); + task_fatal_error(); + } + ESP_LOGI(TAG, "esp_ota_begin succeeded"); + } else { + ESP_LOGE(TAG, "received package is not fit len"); + http_cleanup(client); + esp_ota_abort(update_handle); + task_fatal_error(); + } + } + err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read); + if (err != ESP_OK) { + http_cleanup(client); + esp_ota_abort(update_handle); + task_fatal_error(); + } + binary_file_length += data_read; + ESP_LOGI(TAG, "Written image length %d", binary_file_length); + } else if (data_read == 0) { + /* + * As esp_http_client_read never returns negative error code, we rely on + * `errno` to check for underlying transport connectivity closure if any + */ + if (errno == ECONNRESET || errno == ENOTCONN) { + ESP_LOGE(TAG, "Connection closed, errno = %d", errno); + break; + } + if (esp_http_client_is_complete_data_received(client) == true) { + ESP_LOGI(TAG, "Connection closed"); + break; + } + } + } + ESP_LOGI(TAG, "Total Write binary data length: %d", binary_file_length); + if (esp_http_client_is_complete_data_received(client) != true) { + ESP_LOGE(TAG, "Error in receiving complete file"); + http_cleanup(client); + esp_ota_abort(update_handle); + task_fatal_error(); + } + + err = esp_ota_end(update_handle); + if (err != ESP_OK) { + if (err == ESP_ERR_OTA_VALIDATE_FAILED) { + ESP_LOGE(TAG, "Image validation failed, image is corrupted"); + } else { + ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err)); + } + http_cleanup(client); + task_fatal_error(); + } + + err = esp_ota_set_boot_partition(update_partition); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err)); + http_cleanup(client); + task_fatal_error(); + } + ESP_LOGI(TAG, "Prepare to restart system!"); + esp_restart(); + return ; +} + +wifi_ota_status_t wifi_ota_get_status(void) +{ + return ota_process_state; +} + +void wifi_ota_start_firmware_update(char* device_imei) +{ + ESP_LOGI(TAG, "/****************** WIFI-OTA ********************/"); + + sprintf(ota_url,MCU_FW_BIN_FILE_URL1,device_imei); + + ota_process_state = OTA_IN_PROGRESS; + + uint8_t sha_256[HASH_LEN] = { 0 }; + esp_partition_t partition; + + // get sha256 digest for the partition table + partition.address = ESP_PARTITION_TABLE_OFFSET; + partition.size = ESP_PARTITION_TABLE_MAX_LEN; + partition.type = ESP_PARTITION_TYPE_DATA; + esp_partition_get_sha256(&partition, sha_256); + print_sha256(sha_256, "SHA-256 for the partition table: "); + + // get sha256 digest for bootloader + partition.address = ESP_BOOTLOADER_OFFSET; + partition.size = ESP_PARTITION_TABLE_OFFSET; + partition.type = ESP_PARTITION_TYPE_APP; + esp_partition_get_sha256(&partition, sha_256); + print_sha256(sha_256, "SHA-256 for bootloader: "); + + // get sha256 digest for running partition + esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256); + print_sha256(sha_256, "SHA-256 for current firmware: "); + + const esp_partition_t *running = esp_ota_get_running_partition(); + esp_ota_img_states_t ota_state; + if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) { + if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) { + // run diagnostic function ... + bool diagnostic_is_ok = true; + if (diagnostic_is_ok) { + ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution ..."); + esp_ota_mark_app_valid_cancel_rollback(); + } else { + ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version ..."); + esp_ota_mark_app_invalid_rollback_and_reboot(); + } + } + } + + xTaskCreate(&wifi_ota_task, "wifi_ota_task", 4096, NULL, 5, NULL); +} diff --git a/main/wifi_OTA.h b/main/wifi_OTA.h new file mode 100644 index 0000000..9c6263a --- /dev/null +++ b/main/wifi_OTA.h @@ -0,0 +1,22 @@ +/* + * wifi_OTA.h + * + * Created on: Jul 31, 2023 + * Author: Sword + */ + +#ifndef WIFI_OTA_H_ +#define WIFI_OTA_H_ + +typedef enum +{ + OTA_IS_STOPPED, + OTA_IN_PROGRESS, + OTA_FATAL_ERROR, + OTA_COMPLETED +}wifi_ota_status_t; + +void wifi_ota_start_firmware_update(char* device_imei); +wifi_ota_status_t wifi_ota_get_status(void); + +#endif /* WIFI_OTA_H_ */ diff --git a/main/wifi_webServer.c b/main/wifi_webServer.c new file mode 100644 index 0000000..32eefba --- /dev/null +++ b/main/wifi_webServer.c @@ -0,0 +1,1654 @@ +/* + * tempstick_wifi_server.c + * + * Created on: Jul 28, 2023 + * Author: Sword + */ + +#include "esp_system.h" +#include "sdkconfig.h" +#include "esp_wifi.h" +#include "esp_netif.h" +#include "esp_event.h" +#include "freertos/event_groups.h" +#include "lwip/sockets.h" +#include "lwip/dns.h" +#include "lwip/netdb.h" +#include "esp_tls_crypto.h" +#include "esp_mac.h" +#include "wifi_Client.h" +#include "wifi_WebServer.h" +#include "wifi_Init.h" +#include "comms.h" +#include "data_processing.h" +#include "hmi.h" +#include "nvm.h" +#include "modem.h" +#include "mbedtls/base64.h" +#include "port.h" + +#include +#include + +#include "lwip/tcp.h" +#include "lwip/udp.h" + +#if (WIFI_NEEDED == 1) +static const char* TAG = "SERVER"; + +#define LOG_LOCAL_LEVEL ESP_LOG_INFO +#include "esp_log.h" + +//#define BOARD_TEST + +#define SSID_MAX_LENGTH 32 // Max length corrected to 32 bytes in board version 3.1.1 in ESP8266WiFiSTA.cpp file +#define PASSWORD_MAX_LENGTH 64 // Set by the ESP32 + + +typedef enum +{ WLAN_NOT_TESTING, + WLAN_WIFI_CONNECTING, + WLAN_WIFI_WAITING_TO_CONNECT, + WLAN_WIFI_CONNECTED, + WLAN_TEST_SUCCESS, + WLAN_CONNECT_TO_SERVER_2, + WLAN_SERVER_1_WAITING_TO_CONNECT, + WLAN_SERVER_2_WAITING_TO_CONNECT +}wlanTesting_t; + +typedef enum +{ WLAN_NO_ERROR, + WLAN_SSID_TOO_LONG, + WLAN_PASSWORD_TOO_LONG, + WLAN_FAILED_WIFI, + WLAN_FAILED_INTERNET, + WLAN_SSID_EMPTY +}wlanError_t; + +typedef enum +{ WLAN_IDLE, + WLAN_WORKING, + WLAN_ERROR, + WLAN_DONE, + WLAN_TRY_WIFI, + WLAN_TRY_INTERNET +}wlanStatus_t; + + +extern uint32_t g_version; + +uint8_t g_onboarding_status = 99; +bool g_onboardingCompleted = false; +bool g_wlanOnboardingDone = false; +bool g_pageServed = false; + +uint32_t g_onboardingStepStartTime; // Used for on-boarding time-out. +uint32_t g_onboardingStepTimeout; // Used for on-boarding time-out. + +/*char g_wlanPasswordStr[PASSWORD_MAX_LENGTH + 1] = {0}; +char g_wlanSsidStr[SSID_MAX_LENGTH + 1] = {0};*/ +char g_wlanTargetSsid = 0; + +wlanError_t g_wlanError = WLAN_NO_ERROR; +wlanStatus_t g_wlanStatus = WLAN_IDLE; +wlanTesting_t g_wlanTesting = WLAN_NOT_TESTING; +wlanTesting_t g_wlanTestingPreviousState = WLAN_NOT_TESTING; + +bool g_servedRoot; +bool g_servedSid; +bool g_servedStatus; + +static httpd_handle_t serverHandle; +static bool acc_registered; + +extern uint8_t device_wifi_onboarded; + +/* Function to filter multipart form data */ +//////////////////////////////////////////////////////////// + +// Function to extract the value between two boundaries +void extract_field_value(const char *start, const char *end, char *result, int max_length) { + int length = end - start; + if (length > max_length) { + length = max_length; + } + strncpy(result, start, length); + + // TODO : Test when length is exceeded + result[length] = '\0'; // Null-terminate the string +} + +// Function to find and extract SSID and Password from the content +void extract_ssid_and_password(const char *content, char *ssid, char *password) { + const char *ssid_start, *ssid_end; + const char *password_start, *password_end; + + // Find the SSID field + ssid_start = strstr(content, "Content-Disposition: form-data; name=\"ssid\""); + if (ssid_start) { + ssid_start = strstr(ssid_start, "\r\n\r\n"); // Skip the header and find the actual value + if (ssid_start) { + ssid_start += 4; // Skip the "\r\n\r\n" part + ssid_end = strstr(ssid_start, "\r\n"); + if (ssid_end) { + extract_field_value(ssid_start, ssid_end, ssid, SSID_MAX_LENGTH); + } + } + } + + // Find the Password field + password_start = strstr(content, "Content-Disposition: form-data; name=\"password\""); + if (password_start) { + password_start = strstr(password_start, "\r\n\r\n"); // Skip the header and find the actual value + if (password_start) { + password_start += 4; // Skip the "\r\n\r\n" part + password_end = strstr(password_start, "\r\n"); + if (password_end) { + extract_field_value(password_start, password_end, password, PASSWORD_MAX_LENGTH); + + } + } + } +} + +////////////////////////////////////end ////////////////////// + +/* Function to get millis */ +uint32_t get_millis(void) +{ + TickType_t ticks = xTaskGetTickCount(); + return ((ticks * 1000) / configTICK_RATE_HZ); +} + +/* Converts a hex character to its integer value */ +char from_hex(char ch) { + return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; +} + +/* Converts an integer value to its hex character*/ +char to_hex(char code) { + static char hex[] = "0123456789abcdef"; + return hex[code & 15]; +} + +char *url_decode(char *str) +{ + char *pstr = str, *buf = pvPortMalloc(strlen(str) + 1), *pbuf = buf; + while (*pstr) { + if (*pstr == '%') { + if (pstr[1] && pstr[2]) { + *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]); + pstr += 2; + } + } else if (*pstr == '+') { + *pbuf++ = ' '; + } else { + *pbuf++ = *pstr; + } + pstr++; + } + *pbuf = '\0'; + return buf; +} + + + + +#ifdef BOARD_TEST +const char *main_resp = " Temp Stick Setup - Ideal Sciences

Temp Stick Setup

Step 1 Select WiFi Network
Step 2 Test Connection
Step 3 Complete

Select a WiFi Network

(Leave blank if no password required)

Test Connection

Give us about 30 seconds as we test your WiFi connection

WiFi Setup Complete

The sensor successfully connected to your WiFi network

Let's assign your sensor to an Ideal Sciences account:

-or-


Create Account

Login

The email and/or password you submitted is incorrect.

Account Setup Complete


SENSOR ID:

MAC ADDR:

Board Testing

Battery
Light
ENS210
MC3419
MCP9600T
BG77

LEDs

"; +#else +const char *main_resp = "WiFi Switch Setup - Azuma

Azuma WiFi Setup

Step 1 Select WiFi Network
Step 2 Test Connection
Step 3 Complete

Select a WiFi Network

Enter Custom SSID (Hidden Networks)

(Leave blank if no password required)

Test Connection

Give us about 30 seconds as wetest your WiFi connection

WiFi Setup Complete

The sensor successfully connected to your WiFi network


SENSOR ID:

MAC ADDR:

"; +//const char* main_resp = "Temp Stick Setup - Ideal Sciences

Temp Stick Setup

Step 1Select WiFi Network
Step 2Test Connection
Step 3Complete

Select a WiFi Network

(Leave blank if no password required)

Test Connection

Give us about 30 seconds as we test your WiFi connection


"; + +#endif +static esp_err_t root_handler(httpd_req_t *req) +{ + httpd_resp_send(req, main_resp, strlen(main_resp)); + + vTaskDelay(50/portTICK_PERIOD_MS); + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + return ESP_OK; +} +static const httpd_uri_t root = { + .uri = "/", + .method = HTTP_GET, + .handler = root_handler, + .user_ctx = NULL +}; + + + +const char *promise_min_js = "(function(a){function b(){this._callbacks=[];}b.prototype.then=function(a,c){var d;if(this._isdone)d=a.apply(c,this.result);else{d=new b();this._callbacks.push(function(){var b=a.apply(c,arguments);if(b&&typeof b.then==='function')b.then(d.done,d);});}return d;};b.prototype.done=function(){this.result=arguments;this._isdone=true;for(var a=0;a=300)&&j.status!==304);h.done(a,j.responseText,j);}};j.send(k);return h;}function h(a){return function(b,c,d){return g(a,b,c,d);};}var i={Promise:b,join:c,chain:d,ajax:g,get:h('GET'),post:h('POST'),put:h('PUT'),del:h('DELETE'),ENOXHR:1,ETIMEOUT:2,ajaxTimeout:0};if(typeof define==='function'&&define.amd)define(function(){return i;});else a.promise=i;})(this);"; +static esp_err_t promise_min_js_handler(httpd_req_t *req) +{ + /* Respond with empty body */ + httpd_resp_send(req, promise_min_js, strlen(promise_min_js)); + + vTaskDelay(50/portTICK_PERIOD_MS); + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + return ESP_OK; +} +static const httpd_uri_t promise_min_js_t = { + .uri = "/promise.min.js", + .method = HTTP_GET, + .handler = promise_min_js_handler, + .user_ctx = NULL +}; + + + +const char *ki_min_js = "!function(a,b,c,d){function e(c){b.push.apply(this,c&&c.nodeType?[c]:\"\"+c===c?a.querySelectorAll(c):d)}$=function(b){return/^f/.test(typeof b)?/c/.test(a.readyState)?b():$(a).on(\"DOMContentLoaded\",b):new e(b)},$[c]=e[c]=$.fn=e.fn={length:0,on:function(a,b){return this.each(function(c){c.addEventListener(a,b)})},off:function(a,b){return this.each(function(c){c.removeEventListener(a,b)})},each:function(a,c){return b.forEach.call(this,a,c),this},splice:b.splice}}(document,[],\"prototype\");"; +static esp_err_t ki_min_js_handler(httpd_req_t *req) +{ + /* Respond with empty body */ + httpd_resp_send(req, ki_min_js, strlen(ki_min_js)); + + vTaskDelay(50/portTICK_PERIOD_MS); + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + return ESP_OK; +} +static const httpd_uri_t ki_min_js_t = { + .uri = "/ki.min.js", + .method = HTTP_GET, + .handler = ki_min_js_handler, + .user_ctx = NULL +}; + + + +const char *style = "body { font-family: Arial, sans-serif; background-color: #eaeaea; padding: 0; margin: 0; } h2 { margin: 0 0 10px 0; } label { display: block; margin: 10px 0px; } button { font-size: 18px; font-family: DroidSans, Arial, sans-serif; border: 0; background-color: #0b5bb5; color: #fff; padding: 15px; cursor: pointer; display: block; width: 100%; } button:disabled { background-color: #eaeaea; color: #aaaaaa; cursor: not-allowed; } .header { padding: 15px 0; background: #111; color: #fff; margin-bottom: 8px; } .header h1 { font-size: 26px; line-height: 26px; font-weight: bold; margin: 0; text-align: center; } .container { padding: 15px; } #setup-steps { list-style-type: none; padding: 0px; margin: 0px; position: relative; } #setup-steps li { position: absolute; display: inline-block; } /* #setup-steps li span { padding: 10px; font-size: 18px; background-color: #a8a8a8; color: white; font-weight: normal; border-radius: 20px; min-width: 20px; display: inline-block; text-align: center; */ } #setup-steps li.active span { /*background-color: #7ed600;*/ background-color: #0b5bb5; font-weight: bold; } #setup-step-1 { left: 0px; } #setup-step-2 { left: 42%; } #setup-step-3 { right: 0px; } input:not([type=\"radio\"]):not([type=\"checkbox\"]), select { font-size: 18px; font-family: DroidSans, Arial, sans-serif; padding: 10px; width: 95% } input[type=\"checkbox\"] { float: left; margin-right: 10px; } .col_50 { width: 49%; display: inline-block; } .section { transition: margin-left ease-in 0.5s } .step_name { display: inline-block; min-width: 260px; background-color: #a8a8a8; color: white; padding: 10px; display: inline-block; text-shadow: 1px 1px 1px black; position: relative; } .step_name.active { background-color: #7ed600 } .show-for-medium-down { display: none; } @media screen and (max-width: 800px) { .col_50 { width: 100%; display: block; margin-bottom: 15px; } .section { width: 100%; max-width: 100% !important; } .show-for-medium-down { display: block; } } @media screen and (max-width:1023px) { .hide_for_medium_down { display: none; } } .step_name .step_num { font-weight: bold; font-size: 30px; text-transform: uppercase; } .step_name .step_label { position: absolute; right: 15px; top: 18px; } .align-top { vertical-align: top; } .loader { content: url('data:image/gif;base64,R0lGODlhFAAUAKUAACRqvIy23Mza7GSWzOTu9KzG5Hym1PT6/Dx+xLzS7Jy65Nzm9Gye1PT2/NTi9Oz2/ISu3EyKzMTa7Dx6xJS23Mze7Gya1Ozy/LTO7Pz6/MTW7KTC5HSe1CRuvGSa1OTu/LTK5ISq3ER+xLzW7KS+5OTq9Iyy3FSOzJS63Mze9Pz+/HSi1P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQICgAAACwAAAAAFAAUAAAGwECWcDgMUIhIIkHREDJWwothkRQuLBshB8qCTFJVIYpRYj1ZDgSXeKGyLgxTN6TyiMosibsAKVxYBRgZQhkKCiwLAwAWQg0gEBAjVSomHR0rf0MEJJJVECcOYSphGYNDIyCBCWFCEROvAwmpIBisLK6wSKOTYQ8CoVULEk1DBw60bkkOCRgOphUgKU0LDrsq1CwHAhgVjZkHGAIsAmASCcQXxETbTSMab9ysF81CGu9oGJlJDRWm9kLa1NmqAKxKEAAh+QQICgAAACwAAAAAFAAUAIUMWrSErtzE2uxUjszk6vSsyuR0ntTs9vxEgsScuuTU4vRkmtQkary80uz8+vzM2uzs8vwUYrSUutxklszk7vS0yuR8ptT09vykwuRsmtTM3uwMXrSMstxcksycvuTc5vQkbrzE1uz8/vzk7vy0zuyEqtz0+vxsntTM3vT///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGxsCUcDgsVIhIIqRhEiYSwkuCkhRSPCEnNOU5farChgeSkkA/py3xQk5BPKQUBiOSnNoKakpD0lz2Gg5CIhVHFCUDHEIOKCQkX0kiGAMDCW1RApBJGBaaSCJggkQoIaUaYEIGHR0TFhqlIaeoJxOrAZ9gJqBJBw8KYBwDv0MmChWPVSIcICAWbRoVKH8fCrsOFiUpHxMMGVFtJiQPKQ8oIggRkJlJDyR/DVkPAAOoECTDpUILAAJgF4FC8qWg0GEYKiEaDCYJAgAh+QQICgAAACwAAAAAFAAUAIUMWrSErtzE2uxMhsTk6vRsmtSsxuTs9vycuuRcksw8esTU4vR8ptRcjsz8+vyUttzM2uxUjszs8vy80uykwuREgsQUXrRUiszk7vR0otT09vykvuRkmtREfsSEqtzM3uwMXrSMstxMhsxsntS0zuycvuRklsw8fsTc5vR8qtT8/vyUutzE1uzk7vz0+vzM3vT///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGv0CYcDh8LYhIoubjELJYQtdEkhRKSEfY01nCVIUfkkYLxZQmSQ0VpiFBYIIPjLQ5CAnr8GeMWqiEDh9yEhQBBoAvJCQoXxMBAWJKAoxVExteVX+ZSC9PLHJfMA+PARsfnqBfo48bSJpJTVUHEFlJFCmUUQuKuUQqFA0RK2MwYRBjKR4ugAgIMBgBFyFCakIoIAMOCSYqHB2UC5hEFyAvMCIiMAsnGaEsAAVCFekwHgq1SAsRawP0Egy9Qo1o9yUIACH5BAgKAAAALAAAAAAUABQAhSRqvIy23Mza7FySzOTu9KzG5Hym1PT6/Dx+xNzm9Gye1LzS7Jy65PT2/NTi9Gya1Oz2/ISu3FSKzDx6xJS23Mze7GSa1Ozy/LTO7Pz6/OTq9HSe1MTa7CRuvGSW1OTu/LTK5ISq3ER+xNzq9MTW7KTC5Iyy3FSOzJS63Mze9Pz+/HSi1P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbEQJZwOEw5iEhio5IRkkjCQ6WRFF4wR9ZT6MBcqsIKhrqFgCrJxpfVwAhYHDRnQWVd6uIpK+FQCVV8bHlCGSkYGAlgXVhNQw0ciVUJHHVJflWXQwMTnBJgQhggBRgkHpwTnp8YowVQQyqNSQeKJyZgCyUERBcrAB0mmUQLJiFjQh4AHiMsDAyNKiAYdiURBUIJbywjCAosIREqJgprI2tEKwiJCissGg8MnxUTEUIb7CwlD5FJIwZr9kIgMND1aUgACp+CAAAh+QQICgAAACwAAAAAFAAUAIUMWrSErtzE2uxUiszk6vSsyuRsntScuuTs9vzc5vRkltREgsQkaryMttzU4vS80ux8ptT8+vzM2uxckszs8vykwuQUXrRcjszk7vS0yuSkvuT09vxsmtSUttyEqtzM3uwMXrSMstxUjsx0otScvuTc6vRkmtQkbrzE1ux8qtT8/vzk7vy0zuz0+vyUutzM3vT///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGvUCYcDh8OYhIouNCEaJQwtZnk3QCOE4ozMFqVmED0Av2hCEyn2RCInRYFirJWPCgwih2E0NRgqU8LUIqCUcbHyxpdyMMJyEqVQksLA4RSiIhXwkCXkgqlVWPRB4TEwojX0ISTygvKQqkp6gvq0dDnl+hahAVmQJ2QhQdIhO8kJKUQiEiARgwGSyfcWOGiEIEtSsGDTAVvCwaCMC/QwccBDAHB3ckD6gJHMUu6mQkzVUYJHbpUQ+cqDAFMqAKAgAh+QQICgAAACwAAAAAFAAUAIUMWrSErtzE2uxMhsTk6vSsxuRsmtTs9vycuuQ8esTc5vRcksy80uzU4vR8ptT8+vyUttzM2uxcjszs8vykwuREgsQUYrRUjszk7vS0zux0otT09vykvuREfsTE1uzM3uwMXrSMstxMhsy0yuRsntScvuQ8fsTc6vRkmtS81uyEqtz8/vyUutzk7vz0+vzM3vT///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGwUCYcDgkaYhI4skxEQ5EQoykkRS+EgEnFIYCCKpCjUkBq0AjoEuSQIURTAbYYrGqWMiwyUYYugQwMAgIK0IuKg4wLhEZH0ITEBcSFIRIKwoZGQ0PRAoOFGAKAntJK5tVlEMlAasIYEIfHrEvqqyuMBGxHm1DqEimSRgcKaCiRBsjqx5gDQyZpgUBBU0fH5SWVBsfjI4njiUjtxEwAhkujqNEDCVNsTAHDOJgGCXKMO0w2k1VEwzm9vUuPqCz9WFXkiAAOzhzTTFRUytROC9ESnl3ODZPR05pdUFRdkRLdlFTM3FwWDVaSnhUdytxN243cEtNVFFNNWo3cmV3YjRsa1EvTjE='); } .hide { display: none; } .button_account { background-color: white; color: #444; padding: 10px 8px; border: 1px solid #444; margin-right: 15px; } .button_account.active { background-color: #0b5bb5; color: white; } .red { color: red; }"; +static esp_err_t style_css_handler(httpd_req_t *req) +{ + /* Respond with empty body */ + httpd_resp_send(req, style, strlen(style)); + + vTaskDelay(50/portTICK_PERIOD_MS); + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + return ESP_OK; +} +static const httpd_uri_t style_css = { + .uri = "/style.css", + .method = HTTP_GET, + .handler = style_css_handler, + .user_ctx = NULL +}; + +char *sid = "{\"sid\":\"%s\",\"mac\":\"%s\",\"version\":\"%ld\"}"; +char *sid_data; +static esp_err_t sid_handler(httpd_req_t *req) +{ + char mac_str[20]; + uint8_t mac_base[6] = {0}; + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + esp_efuse_mac_get_default(mac_base); + esp_read_mac(mac_base, ESP_MAC_WIFI_STA); + + sprintf(mac_str, "%02X%02X%02X%02X%02X%02X", mac_base[0], mac_base[1], mac_base[2], mac_base[3], mac_base[4], mac_base[5]); + + sid_data = pvPortMalloc(100 * sizeof(char)); + sprintf(sid_data, sid, mac_str, mac_str,g_version); + + httpd_resp_send(req, sid_data, strlen(sid_data)); + vTaskDelay(50/portTICK_PERIOD_MS); + + vPortFree(sid_data); + return ESP_OK; +} +static const httpd_uri_t sid_t = { + .uri = "/sid", + .method = HTTP_GET, + .handler = sid_handler, + .user_ctx = NULL +}; + + + +char *scan_response; +static esp_err_t wirelessNetworks_handler(httpd_req_t *req) +{ + uint16_t apCount = 0; + wifi_ap_record_t *list = 0; + char temp[50]; + bool duplicateSsid; + int x; + + + ESP_LOGI(TAG,"OnBoarding : Step 1 > Scanning for Networks... Waiting for User to enter SSID and Password and click 'Next'..."); + + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + scan_response = pvPortMalloc(1024 * sizeof(char)); + + if( (WLAN_IDLE == g_wlanStatus) || (WLAN_DONE == g_wlanStatus) ) + { + wifi_scan_start(&apCount, &list); + } + + ESP_LOGI(TAG,"Access points found: %d",apCount); + + if(apCount) + { + strcpy(scan_response, "["); + + for(int ap_idx = 0; ap_idx < apCount; ap_idx++) + { + // CHECK FOR DUPLICATE SSIDs. (SAME NETWORK, BUT SOMETIMES ON A DIFFERENT CHANNEL, SOMETIMES ON SAME CHANNEL) + duplicateSsid = false; + for(x=0; x 0) + { + strcat(scan_response, ","); + } + strcat(scan_response, "{\"ssid\":\""); + strcat(scan_response, (char *)list[ap_idx].ssid); + strcat(scan_response, "\",\"password\":"); + + if(list[ap_idx].group_cipher == WIFI_CIPHER_TYPE_NONE) + { + sprintf(temp, "0"); + } + else + { + sprintf(temp, "1"); + } + + strcat(scan_response, temp); + /*sprintf(temp, "%d", list[ap_idx].rssi); + strcat(scan_response, "\",\"rssi\":"); + strcat(scan_response, temp);*/ + strcat(scan_response, "}"); + } + + ESP_LOGI(TAG,"Index: %d, SSID: %s, Encryption: %d, RSSI: %d",ap_idx,(char *)list[ap_idx].ssid,list[ap_idx].group_cipher,list[ap_idx].rssi); + } + + strcat(scan_response, "]"); + //vPortFree(list); + } + else + { + strcpy(scan_response, "{\"error\":\"No available wifi networks\"}"); + } + + httpd_resp_send(req, scan_response, strlen(scan_response)); + + vTaskDelay(100/portTICK_PERIOD_MS); + vPortFree(scan_response); + + return ESP_OK; +} +static const httpd_uri_t wirelessNetworks = { + .uri = "/wirelessNetworks", + .method = HTTP_GET, + .handler = wirelessNetworks_handler, + .user_ctx = NULL +}; + + +static esp_err_t wlan_handler(httpd_req_t *req) +{ + char *content; + char temp[30]; + memset(temp, 0, 30); + + ESP_LOGI(TAG,"OnBoarding : Step 2 > Testing Connection..."); + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + content = pvPortMalloc(300 * sizeof(char)); + memset(content, 0, 300); + httpd_req_recv(req, content, 300); + + if(strlen(content)) + { + g_wlanError = WLAN_NO_ERROR; + startOnboardingGetSsidAndPassword(content); + + Connect_wifi_sta(WIFI_CLIENT_AP_MODE); + + /* Start LED blinking */ + + + if(wifi_station_connected()) + { + + + // STORE WIFI CREDENTIALS TEMPORARILY. THEY ARE NOT COMMITTED TO FLASH UNTIL /restart + memset(wifi_get_ssidA(), 0, (SSID_MAX_LENGTH + 1)); + memset(wifi_get_pswdA(), 0, (PASSWORD_MAX_LENGTH + 1)); + memset(wifi_get_ssidB(), 0, (SSID_MAX_LENGTH + 1)); + memset(wifi_get_pswdB(), 0, (PASSWORD_MAX_LENGTH + 1)); + + strncpy(wifi_get_ssidA(), wifi_get_ssid(), SSID_MAX_LENGTH); + strncpy(wifi_get_pswdA(), wifi_get_pswd(), PASSWORD_MAX_LENGTH); + + // SAVE AP'S MAC ADDR + //data_set_ap_mac_addr(data_get_ap_mac_addr()); + + // ESP_LOGI(TAG,"find heap size"); + // int mem = esp_get_free_heap_size(); + // ESP_LOGI(TAG, "Free heap size: %u", mem); + + vTaskDelay(2000/portTICK_PERIOD_MS); + + + + + ESP_LOGI(TAG, "Attempting connection to google.com :)"); + + if(ESP_OK == http_client_test("https://www.google.com")) + { + ESP_LOGI(TAG, "Connection to internet successful.\nOnBoarding : Step 2 > Wifi Connection Success!\n"); + g_onboarding_status = 9; + } + else + { + + ESP_LOGI(TAG, "Connection to google.com failed.\nUnable to connect to the internet."); + g_onboarding_status = 1; + } + } + else + { + ESP_LOGI(TAG,"Failed to connect to WiFi. No SSID available."); + g_onboarding_status = 0; + } + } + + vPortFree(content); + + httpd_resp_send(req, "", 0); + + vTaskDelay(50/portTICK_PERIOD_MS); + + return ESP_OK; +} +static const httpd_uri_t wlan = +{ + .uri = "/wlan", + .method = HTTP_POST, + .handler = wlan_handler, + .user_ctx = NULL +}; + + + +static esp_err_t status_handler(httpd_req_t *req) +{ + char response[10]; + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + strcpy(response, ""); + + strcpy(response, "0"); + + switch(g_onboarding_status) + { + case 0: + strcpy(response, "0"); + break; + + case 1: + strcpy(response, "1"); + break; + + case 9: + // WIFI CREDENTIALS WORKED + strcpy(response, "9"); + break; + + case 99: + strcpy(response, "99"); + break; + + default: + strcpy(response, "99"); + break; + } + + httpd_resp_send(req, response, strlen(response)); + + vTaskDelay(50/portTICK_PERIOD_MS); + + return ESP_OK; +} +static const httpd_uri_t status = +{ + .uri = "/status", + .method = HTTP_GET, + .handler = status_handler, + .user_ctx = NULL +}; + +static esp_err_t acc_handler(httpd_req_t *req) +{ + char response[180];// = (char *)pvPortMalloc(100 * sizeof(char)); + char t[50];//= (char *)pvPortMalloc(50 * sizeof(char)); + char e[50];// = (char *)pvPortMalloc(50 * sizeof(char)); + char p[50];// = (char *)pvPortMalloc(50 * sizeof(char)); + char pa[50];// = (char *)pvPortMalloc(50 * sizeof(char)); + char content[100];// = (char *)pvPortMalloc(100 * sizeof(char)); + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + bool restart_cond = false; + + memset(content, 0, 100); + memset(t, 0, 50); + memset(e, 0, 50); + memset(p, 0, 50); + memset(pa, 0, 50); + httpd_req_recv(req, content, 100); + + if(strlen(content)) + { + //ESP_LOGI(TAG, "%s", content); + + char *h = strtok(content, "&"); + strcpy(t, h); + h = strtok(NULL, "&"); + strcpy(e, h); + h = strtok(NULL, "&"); + strcpy(p, h); + h = strtok(NULL, "&"); + if(h != NULL) + { + strcpy(pa, h); + } + + h = strtok(t, "="); + if(!strcmp(h, "t")) + { + h = strtok(NULL, "="); + strcpy(t, h); + } + + h = strtok(e, "="); + if(!strcmp(h, "e")) + { + h = strtok(NULL, "="); + strcpy(e, h); + } + + h = strtok(p, "="); + if(!strcmp(h, "p")) + { + h = strtok(NULL, "="); + strcpy(p, h); + } + + if(!strcmp(t, "c")) + { + h = strtok(pa, "="); + if(!strcmp(h, "pa")) + { + h = strtok(NULL, "="); + strcpy(pa, h); + } + } + else + { + strcpy(pa, ""); + } + //ESP_LOGI(TAG, "t=%s e=%s p=%s pa=%s", t, e, p, pa); + } + + if(!registerAcctWithServer(t, e, p, pa, response, strlen(response))) + { + ESP_LOGI(TAG,"Connection to server failed."); + restart_cond = true; + } + + ESP_LOGI(TAG,"Server response is: %s",response); + + httpd_resp_send(req, response, strlen(response)); + + if(restart_cond) + { + esp_restart(); + } + + //vTaskDelay(200/portTICK_PERIOD_MS); + + /*vPortFree(t); + vPortFree(e); + vPortFree(p); + vPortFree(pa); + + vPortFree(urlConnectionString); + vPortFree(content); + vPortFree(post_params); + vPortFree(mac_str); + vPortFree(response);*/ + + //start_remaining_tasks(); + + return ESP_OK; +} +static const httpd_uri_t acc = { + .uri = "/acc", + .method = HTTP_POST, + .handler = acc_handler, + .user_ctx = NULL +}; + +static esp_err_t restart_handler(httpd_req_t *req) +{ + ESP_LOGI(TAG,"OnBoarding : Step 3 > Account Creation Success!"); + + httpd_resp_send(req, "", 0); + +#ifdef STORING_TO_NVM + /* CLEAR SIMULATED EEPROMS AND HISTORY SECTORS */ + nvm_clear(); +#endif + + g_onboardingCompleted = true; + + /* Set the wifi-onboarded flag to be true */ + device_wifi_onboarded = true; + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + vTaskDelay(50/portTICK_PERIOD_MS); + + return ESP_OK; +} +static const httpd_uri_t restart = { + .uri = "/restart", + .method = HTTP_POST, + .handler = restart_handler, + .user_ctx = NULL +}; + + +static esp_err_t getWlan_handler(httpd_req_t *req) +{ + char response[100]; + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + sprintf(response,"{\"wlanA\":\"%s\",\"wlanB\":\"%s\"}",wifi_get_ssidA(),wifi_get_ssidB()); + + httpd_resp_send(req, response, strlen(response)); + + vTaskDelay(50/portTICK_PERIOD_MS); + + return ESP_OK; +} +static const httpd_uri_t getWlan = { + .uri = "/get/wlan", + .method = HTTP_GET, + .handler = getWlan_handler, + .user_ctx = NULL +}; + + + +static esp_err_t setWlan_handler(httpd_req_t *req) +{ + char *content; + + char *pPassword; + char *pSsid; + char val[10]; + char buf[100]; + int ret, buflen; + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + content = pvPortMalloc(100 * sizeof(char)); + memset(content, 0, 100); + httpd_req_recv(req, content, 100); + + if( (WLAN_IDLE == g_wlanStatus) || (WLAN_DONE == g_wlanStatus) ) + { + ESP_LOGI(TAG,"Get SSID and password"); + + g_wlanTargetSsid = 0; // Default + /*memset(g_wlanSsidStr, 0, (SSID_MAX_LENGTH + 1)); + memset(g_wlanPasswordStr, 0, (PASSWORD_MAX_LENGTH + 1));*/ + wifi_update_credentials((char*)0, (char*)0); + + g_wlanError = WLAN_NO_ERROR; + + // Update global copy of SSID and password for wlan testing state machine to be able to store the SSID and password after successful testing. + startOnboardingGetSsidAndPassword(content); + + // Check to see if WiFi credentials testing is requested + if(WLAN_NO_ERROR == g_wlanError) + { + // Determine target SSID and password + buflen = httpd_req_get_url_query_len(req) + 1; + if (buflen > 1) { + ret = httpd_req_get_url_query_str(req, buf, buflen); + if (ret == ESP_OK) { + char param[32]; + if (httpd_query_key_value(buf, "wlan", param, sizeof(param)) == ESP_OK) { + if (strcmp(param, "A") == 0) { + g_wlanTargetSsid = 'A'; + } else if (strcmp(param, "B") == 0) { + g_wlanTargetSsid = 'B'; + } + } + } + } + + // Update global copy of SSID and password for wlan testing state machine to be able to store the SSID and password after successful testing. + /*strncpy(g_wlanSsidStr, ssidStr, SSID_MAX_LENGTH); + strncpy(g_wlanPasswordStr, passwordStr, PASSWORD_MAX_LENGTH);*/ + //wifi_update_credentials(ssidStr, passwordStr); + + // Determine if WiFi testing is requested + if(ESP_OK == httpd_req_get_hdr_value_str(req,"test",val,strlen(val))) + { + if(0 == strcmp("1", val)) + { + ESP_LOGI(TAG,"Begin testing WiFi credentials"); + + // Begin testing WiFi credentials + if(wifi_station_connected()) // Disconnect if WiFi is already connected. + { + wifi_sta_disconnecting(); + vTaskDelay(10); // Delay after disconnect function call + } + + vTaskDelay(10); // Most of the other WiFi routines require a minimum of 4 millisecond vTaskDelay. This delay is added just to be safe. + + //WiFi.config(IPAddress(0,0,0,0), IPAddress(0,0,0,0), IPAddress(0,0,0,0), IPAddress(0,0,0,0), IPAddress(0,0,0,0)); + + if (/*0 != passwordStr[0]*/wifi_get_pswd()) + Connect_wifi_sta(WIFI_CLIENT_AP_MODE); + else + Connect_wifi_sta(WIFI_CLIENT_AP_MODE); + vTaskDelay(10); + + // Update WLAN validation state + g_wlanStatus = WLAN_TRY_WIFI; + g_wlanError = WLAN_NO_ERROR; + + g_wlanTesting = WLAN_WIFI_CONNECTING; // Set the status here instead of in the wifi testing state machine, because the smart phone app could poll the status before the state machine could set it. + } + } + + if(WLAN_WIFI_CONNECTING != g_wlanTesting) // Is testing being skipped? + { + ESP_LOGI(TAG,"WiFi credentials testing skipped"); + + pSsid = NULL; + pPassword = NULL; // Prevent compiler error + + // Save WiFi credentials + if('A' == g_wlanTargetSsid) + { + pSsid = wifi_get_ssidA(); + pPassword = wifi_get_pswdA(); + } + + if('B' == g_wlanTargetSsid) + { + pSsid = wifi_get_ssidB(); + pPassword = wifi_get_pswdB(); + } + + if(NULL != pSsid) // Make sure the pointers are set. + { + memset(pSsid, 0, (SSID_MAX_LENGTH + 1)); + memset(pPassword, 0, (PASSWORD_MAX_LENGTH + 1)); + + strncpy(pSsid, wifi_get_ssid(), SSID_MAX_LENGTH); + strncpy(pPassword, wifi_get_pswd(), PASSWORD_MAX_LENGTH); + + //writeWifiCredentialsToFlash(); + } + } + } + } + + vTaskDelay(50/portTICK_PERIOD_MS); + + return ESP_OK; +} +static const httpd_uri_t setWlan = { + .uri = "/set/wlan", + .method = HTTP_GET, + .handler = setWlan_handler, + .user_ctx = NULL +}; + + +static esp_err_t deleteWlanA_handler(httpd_req_t *req) +{ + + char response[100]; + char *sidA = wifi_get_ssidA(); + char *pwdA = wifi_get_ssidA(); + char *sidB = wifi_get_ssidB(); + char *pwdB = wifi_get_pswdB(); + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + // Delete SSID A + memset(sidA, 0, (SSID_MAX_LENGTH + 1)); + memset(pwdA, 0, (PASSWORD_MAX_LENGTH + 1)); + + if(sidB) // If SSID B exists, bump it up to become SSID A + { + strncpy(sidA, sidB, SSID_MAX_LENGTH); + strncpy(pwdA, sidB, PASSWORD_MAX_LENGTH); + + memset(sidB, 0, (SSID_MAX_LENGTH + 1)); + memset(pwdB, 0, (PASSWORD_MAX_LENGTH + 1)); + } + + //writeWifiCredentialsToFlash(); + + strcpy(response,"{\"success\":\"1\"}"); + httpd_resp_send(req, response, strlen(response)); + + vTaskDelay(50/portTICK_PERIOD_MS); + + return ESP_OK; +} +static const httpd_uri_t deleteWlanA = { + .uri = "/delete/wlanA", + .method = HTTP_GET, + .handler = deleteWlanA_handler, + .user_ctx = NULL +}; + + +static esp_err_t deleteWlanB_handler(httpd_req_t *req) +{ + char response[100]; + char *sidB = wifi_get_ssidB(); + char *pwdB = wifi_get_pswdB(); + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + // Delete SSID B + memset(sidB, 0, (SSID_MAX_LENGTH + 1)); + memset(pwdB, 0, (PASSWORD_MAX_LENGTH + 1)); + + //writeWifiCredentialsToFlash(); + + strcpy(response,"{\"success\":\"1\"}"); + httpd_resp_send(req, response, strlen(response)); + + vTaskDelay(50/portTICK_PERIOD_MS); + return ESP_OK; +} +static const httpd_uri_t deleteWlanB = { + .uri = "/delete/wlanB", + .method = HTTP_GET, + .handler = deleteWlanB_handler, + .user_ctx = NULL +}; + + +static esp_err_t getWlanStatus_handler(httpd_req_t *req) +{ + char response[100]; + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + safeStrCat(response, sizeof(response), (char*)"{\"status\":\""); + switch(g_wlanStatus) + { + case WLAN_IDLE: safeStrCat(response, sizeof(response), (char*)"idle"); ESP_LOGI(TAG,"WLAN status: idle"); break; + case WLAN_WORKING: safeStrCat(response, sizeof(response), (char*)"working"); ESP_LOGI(TAG,"WLAN status: working"); break; + case WLAN_ERROR: safeStrCat(response, sizeof(response), (char*)"error"); ESP_LOGI(TAG,"WLAN status: error"); g_wlanStatus = WLAN_IDLE; break; // Clear the error flag because it was reported. + case WLAN_DONE: safeStrCat(response, sizeof(response), (char*)"done"); ESP_LOGI(TAG,"WLAN status: done"); break; + case WLAN_TRY_WIFI: safeStrCat(response, sizeof(response), (char*)"try_wifi"); ESP_LOGI(TAG,"WLAN status: try_wifi"); break; + case WLAN_TRY_INTERNET: safeStrCat(response, sizeof(response), (char*)"try_internet"); ESP_LOGI(TAG,"WLAN status: try_internet"); break; + default: safeStrCat(response, sizeof(response), (char*)"undefined_status"); ESP_LOGI(TAG,"WLAN status: undefined_status"); break; + } + + safeStrCat(response, sizeof(response), (char*)"\",\"error\":\""); + switch(g_wlanError) + { + case WLAN_NO_ERROR: safeStrCat(response, sizeof(response), (char*)"\"}"); ESP_LOGI(TAG,"WLAN error: no_error\n"); break; + case WLAN_SSID_TOO_LONG: safeStrCat(response, sizeof(response), (char*)"ssid_too_long\"}"); ESP_LOGI(TAG,"WLAN error: ssid_too_long\n"); break; + case WLAN_PASSWORD_TOO_LONG: safeStrCat(response, sizeof(response), (char*)"password_too_long\"}"); ESP_LOGI(TAG,"WLAN error: password_too_long\n"); break; + case WLAN_FAILED_WIFI: safeStrCat(response, sizeof(response), (char*)"failed_wifi\"}"); ESP_LOGI(TAG,"WLAN error: failed_wifi\n"); break; + case WLAN_FAILED_INTERNET: safeStrCat(response, sizeof(response), (char*)"failed_internet\"}"); ESP_LOGI(TAG,"WLAN error: failed_internet\n"); break; + default: safeStrCat(response, sizeof(response), (char*)"undefined_error\"}"); ESP_LOGI(TAG,"WLAN error: undefined_error\n"); break; + } + g_wlanError = WLAN_NO_ERROR; + + strcpy(response,"{\"success\":\"1\"}"); + httpd_resp_send(req, response, strlen(response)); + + vTaskDelay(50/portTICK_PERIOD_MS); + return ESP_OK; +} +static const httpd_uri_t getWlanStatus = { + .uri = "/get/wlanStatus", + .method = HTTP_GET, + .handler = getWlanStatus_handler, + .user_ctx = NULL +}; + + +static esp_err_t readSensors_handler(httpd_req_t *req) +{ + char response[200]; + char floatStr[32]; + + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + + //getSensorsDataDirectly(); + + response[0] = 0; + safeStrCat(response, sizeof(response), (char*)"{\"tempC\":"); + + safeStrCat(response, sizeof(response), floatStr); + + safeStrCat(response, sizeof(response), (char*)"{,\"humidity\":"); + + safeStrCat(response, sizeof(response), floatStr); + + safeStrCat(response, sizeof(response), (char*)"}"); + + httpd_resp_send(req, response, strlen(response)); + + vTaskDelay(50/portTICK_PERIOD_MS); + return ESP_OK; +} +static const httpd_uri_t readSensors = { + .uri = "/get/readings", + .method = HTTP_GET, + .handler = readSensors_handler, + .user_ctx = NULL +}; + + +static esp_err_t done_handler(httpd_req_t *req) +{ + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + g_wlanOnboardingDone = true; + + vTaskDelay(50/portTICK_PERIOD_MS); + return ESP_OK; +} +static const httpd_uri_t doneOn = { + .uri = "/done", + .method = HTTP_GET, + .handler = done_handler, + .user_ctx = NULL +}; + + +static esp_err_t factoryReset_handler(httpd_req_t *req) +{ + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (10 * 60 * 1000); // 10 minutes. + + startOnboardingShutdown(); + + esp_restart(); + + vTaskDelay(50/portTICK_PERIOD_MS); + return ESP_OK; +} +static const httpd_uri_t factoryReset = { + .uri = "/factoryReset", + .method = HTTP_GET, + .handler = factoryReset_handler, + .user_ctx = NULL +}; + + + + +void startOnboarding(void *pvParameters) +{ + char *pPassword; + char *pSsid; + char initialPasswordA[PASSWORD_MAX_LENGTH + 1]; + char initialPasswordB[PASSWORD_MAX_LENGTH + 1]; + char initialSsidA[SSID_MAX_LENGTH + 1]; + char initialSsidB[SSID_MAX_LENGTH + 1]; + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + config.lru_purge_enable = true; + config.max_uri_handlers = 19; + uint32_t timeout = 0; + uint8_t serverConnectionAttempt = 0; + //int8_t currentWifiChannel; + + g_wlanError = WLAN_NO_ERROR; + g_wlanStatus = WLAN_IDLE; + g_wlanTesting = WLAN_NOT_TESTING; + g_wlanTestingPreviousState = WLAN_NOT_TESTING; + + g_servedRoot = false; + g_servedSid = false; + g_servedStatus = false; + //currentWifiChannel = -1; // Initialized so that the current wifi channel will be printed to the monitor. + + memcpy(initialSsidA, wifi_get_ssidA(), sizeof(initialSsidA)); + memcpy(initialPasswordA, wifi_get_pswdA(), sizeof(initialPasswordA)); + memcpy(initialSsidB, wifi_get_ssidB(), sizeof(initialSsidB)); + memcpy(initialPasswordB, wifi_get_pswdB(), sizeof(initialPasswordB)); + + // Start the httpd server + ESP_LOGI(TAG, "******** START ON-BOARDING ********\nStarting server on port: '%d'", config.server_port); + + /* Start the hmi-not-onbooarded LED-event */ + hmi_set_leds_state(RED_LED_FLASHING); + + if (httpd_start(&serverHandle, &config) == ESP_OK) + { + /**/ + g_onboardingStepStartTime = get_millis(); + g_onboardingStepTimeout = (20 * 60 * 1000); // 20 minutes. + + // Set URI handlers + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &root)); + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &ki_min_js_t)); + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &promise_min_js_t)); + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &style_css)); + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &sid_t)); + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &wirelessNetworks)); + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &wlan)); + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &status)); + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &acc)); + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &restart)); + + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &getWlan)); + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &setWlan)); + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &deleteWlanA)); + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &deleteWlanB)); + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &getWlanStatus)); + + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &readSensors)); + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &doneOn)); + //ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &deleteWifiAndReset)); + ESP_ERROR_CHECK(httpd_register_uri_handler(serverHandle, &factoryReset)); + ESP_LOGI(TAG, "OnBoarding : Step 0 > Waiting for User to connect to soft AP and download configuration page..."); + + while(1) + { + // Exit onboarding? + if(g_onboardingCompleted || g_wlanOnboardingDone) + { + ESP_LOGI(TAG,"On-boarding complete."); + port_red_led_off(); + +#ifdef STORING_TO_NVM + /* Store the wifi-onboarded flag in nvs */ + nvm_write_onboarding_flag(WIFI_ONBOARDING_KEY, device_wifi_onboarded); + + /* Store the wifi-credentials to nvm */ + nvm_write_wifi_credentials(wifi_get_ssid(), (uint8_t)strlen(wifi_get_ssid()), wifi_get_pswd(), (uint8_t)strlen(wifi_get_pswd())); +#endif + + break; + } + + // Onboarding timeout? + if ( ((get_millis() - g_onboardingStepStartTime) > g_onboardingStepTimeout)) + { + ESP_LOGI(TAG,"\nOn-boarding timed-out.\n"); + g_onboardingCompleted = false; + g_wlanOnboardingDone = false; + break; + } + + // Process the wlan testing state machine + if(g_wlanTestingPreviousState != g_wlanTesting) + { + g_wlanTestingPreviousState = g_wlanTesting; + ESP_LOGI(TAG,"\nWLAN testing switched to state: %d",g_wlanTesting); + } + + // Test if pushbutton requested server mode has timed out. + /*if( g_serverModeRequested && *g_pWifiSsidA && !g_pageServed && (300000 < (millis() - serverModeStartTime)) ) + { + Serial.println(F("\nPushbutton initiated server mode timed out.\n")); + g_onboardingCompleted = false; + g_wlanOnboardingDone = true; + break; + }*/ + + switch(g_wlanTesting) + { + case WLAN_NOT_TESTING: + break; + + + case WLAN_WIFI_CONNECTING: + //currentWifiChannel = -1; + ESP_LOGI(TAG,"Attempting to connect using WiFi channel:"); + + timeout = get_millis(); + g_wlanTesting = WLAN_WIFI_WAITING_TO_CONNECT; + break; + + + case WLAN_WIFI_WAITING_TO_CONNECT: + if(wifi_station_connected()) + { +// ESP_LOGI(TAG,"\nConnected to WiFi on channel: %d",data_get_wifi_channel()); + + // Save AP's MAC address + //data_set_ap_mac_addr(data_get_ap_mac_addr()); + + serverConnectionAttempt = 0; + g_wlanTesting = WLAN_WIFI_CONNECTED; + } + else + { + if(30000 < (get_millis() - timeout)) // WiFi connection attempt timed out? + { + ESP_LOGI(TAG,"Failed to connect to WiFi!"); + + if(wifi_station_connected()) // Disconnect if WiFi is connected. + { + wifi_sta_disconnecting(); + vTaskDelay(10/portTICK_PERIOD_MS); // Delay after disconnect function call + } + + g_wlanStatus = WLAN_ERROR; + g_wlanError = WLAN_FAILED_WIFI; + + g_wlanTesting = WLAN_NOT_TESTING; // Reset state machine. + } + } + break; + + + case WLAN_WIFI_CONNECTED: + + ESP_LOGI(TAG,"Attempting connection to google.com"); + + g_wlanStatus = WLAN_TRY_INTERNET; + + serverConnectionAttempt++; + + vTaskDelay(50/portTICK_PERIOD_MS); // Feed the watchdog timer + /*if(g_client.connected()) + { + g_client.stop(); + yield(); // Feed the watchdog timer + g_client.flush(); + yield(); // Feed the watchdog timer + }*/ + + if(http_client_test("https://www.google.com")) + { + ESP_LOGI(TAG,"Connected to google.com"); + + vTaskDelay(100/portTICK_PERIOD_MS); // Feed the watchdog timer + /*if(g_client.connected()) + { + g_client.stop(); + yield(); // Feed the watchdog timer + g_client.flush(); + yield(); // Feed the watchdog timer + }*/ + + g_wlanTesting = WLAN_TEST_SUCCESS; + } + else + { + if(10 < serverConnectionAttempt) // MAKE REPEATED ATTEMPTS OVER 10 SECONDS // http://forum.arduino.cc/index.php?topic=433836.0 + { + ESP_LOGI(TAG,"Failed to connect to google.com"); + + serverConnectionAttempt = 0; + g_wlanTesting = WLAN_CONNECT_TO_SERVER_2; + } + else + { + timeout = get_millis(); + g_wlanTesting = WLAN_SERVER_1_WAITING_TO_CONNECT; + } + } + break; + + + case WLAN_SERVER_1_WAITING_TO_CONNECT: + if(1000 < (get_millis() - timeout)) + g_wlanTesting = WLAN_WIFI_CONNECTED; + break; + + + case WLAN_CONNECT_TO_SERVER_2: + ESP_LOGI(TAG,"Attempting connection to tempstick.com"); + + serverConnectionAttempt++; + + vTaskDelay(50/portTICK_PERIOD_MS); // Feed the watchdog timer + /*if(g_client.connected()) + { + g_client.stop(); + yield(); // Feed the watchdog timer + g_client.flush(); + yield(); // Feed the watchdog timer + }*/ + + if(http_client_test("http://www.tempstick.com")) + { + ESP_LOGI(TAG,"Connected to tempstick.com"); + + vTaskDelay(50/portTICK_PERIOD_MS); // Feed the watchdog timer + /*if(g_client.connected()) + { + g_client.stop(); + yield(); // Feed the watchdog timer + g_client.flush(); + yield(); // Feed the watchdog timer + }*/ + + g_wlanTesting = WLAN_TEST_SUCCESS; + } + else + { + if(10 < serverConnectionAttempt) // MAKE REPEATED ATTEMPTS OVER 10 SECONDS // http://forum.arduino.cc/index.php?topic=433836.0 + { + ESP_LOGI(TAG,"Failed to connect to tempstick.com"); + + vTaskDelay(50/portTICK_PERIOD_MS); // Feed the watchdog timer + /*if(g_client.connected()) + { + g_client.stop(); + yield(); // Feed the watchdog timer + g_client.flush(); + yield(); // Feed the watchdog timer + }*/ + + if(wifi_station_connected()) // Disconnect if WiFi is connected. + { + wifi_sta_disconnecting(); + vTaskDelay(100/portTICK_PERIOD_MS); // Delay after disconnect function call + } + + g_wlanStatus = WLAN_ERROR; + g_wlanError = WLAN_FAILED_INTERNET; + + g_wlanTesting = WLAN_NOT_TESTING; // Reset state machine. + } + else + { + timeout = get_millis(); + g_wlanTesting = WLAN_SERVER_2_WAITING_TO_CONNECT; + } + } + break; + + + case WLAN_SERVER_2_WAITING_TO_CONNECT: + if(1000 < (get_millis() - timeout)) + g_wlanTesting = WLAN_CONNECT_TO_SERVER_2; + break; + + + case WLAN_TEST_SUCCESS: + ESP_LOGI(TAG,"WLAN test success"); + + // Save WiFi credentials + pSsid = NULL; + pPassword = NULL; // Prevent compiler error. + + if('A' == g_wlanTargetSsid) + { + pSsid = wifi_get_ssidA(); + pPassword = wifi_get_pswdA(); + } + + if('B' == g_wlanTargetSsid) + { + pSsid = wifi_get_ssidB(); + pPassword = wifi_get_pswdB(); + } + + if(NULL != pSsid) + { + memset(pSsid, 0, (SSID_MAX_LENGTH + 1)); + memset(pPassword, 0, (PASSWORD_MAX_LENGTH + 1)); + + /*strncpy(pSsid, g_wlanSsidStr, SSID_MAX_LENGTH); + strncpy(pPassword, g_wlanPasswordStr, PASSWORD_MAX_LENGTH);*/ + strncpy(pSsid, wifi_get_ssid(), SSID_MAX_LENGTH); + strncpy(pPassword, wifi_get_pswd(), PASSWORD_MAX_LENGTH); + + //writeWifiCredentialsToFlash(); + } + + g_wlanStatus = WLAN_DONE; + g_wlanError = WLAN_NO_ERROR; + + g_wlanTesting = WLAN_NOT_TESTING; // Reset state machine. + break; + + + default: + ESP_LOGI(TAG,"\nWLAN testing programming error. Locking up."); + while(1) + { + vTaskDelay(100/portTICK_PERIOD_MS); + } + break; + } // End of switch(g_wlanTesting) + + vTaskDelay(100/portTICK_PERIOD_MS); + } + + startOnboardingShutdown(); + + if(g_wlanOnboardingDone) // Real-time server mode exit. + { + //readWifiCredentialsFromFlash(NULL, 0, NULL, 0, NULL, 0, NULL, 0); + + if(wifi_get_ssidA()) // SSID set? + { + if( (0 != strcmp(initialSsidA, wifi_get_ssidA())) || (0 != strcmp(initialPasswordA, wifi_get_pswdA())) || + (0 != strcmp(initialSsidB, wifi_get_ssidB())) || (0 != strcmp(initialPasswordB, wifi_get_pswdB())) ) // Did the customer change any WiFi settings? + { + ESP_LOGI(TAG,"Smart phone app done. WiFi settings exist. WiFi changed. Resetting."); + wifi_switchToPrimaryNetwork(); + esp_restart(); //resetCoprocessor(); // Reset Temp Stick to force a check in with the new WiFi settings. This function does not return. + } + else + { + ESP_LOGI(TAG,"Smart phone app done. WiFi settings exist. WiFi not changed. Returning to sleep"); + //goToSleep(0xFFFFFFFE, _rtcRamData.rfWakeMode); // Return to sleep. (Sleep may have been interrupted by the server mode request). This function does not return. + } + } + else + { + ESP_LOGI(TAG,"Smart phone app done. No WiFi settings. Resetting."); + esp_restart(); //resetCoprocessor(); // Reset Temp Stick to re-enter onboarding. This function does not return. + } + } + else + { + if(g_onboardingCompleted) + { + ESP_LOGI(TAG,"Legacy onboarding completed. Resetting."); + wifi_switchToPrimaryNetwork(); +#ifdef STORING_TO_NVM + esp_restart(); //resetCoprocessor(); // Reset Temp Stick to force check in with the new WiFi settings. This function does not return. +#endif + } + else + { + // Onboarding timed out. + + if( (g_servedSid || g_servedStatus) && (!g_servedRoot) ) // Did smartphone app connection timeout, instead of legacy 10.10.1.1? + { + ESP_LOGI(TAG,"Smart phone app connection timed out. Returning to sleep."); + //goToSleep(0xFFFFFFFE, _rtcRamData.rfWakeMode); // Return to sleep. (Sleep may have been interrupted by the server mode request). This function does not return. + } + else + { + ESP_LOGI(TAG,"Legacy onboarding timed out. Locking up."); + //goToSleep(0xFFFFFFFF, RF_CAL); // Onboarding timed out. Shut down until a POR occurs. The 0xFFFFFFFF is a special argument that puts the coprocessor into its own deep sleep until a POR occurs. This function does not return. + } + } + } + + vTaskDelete(NULL); + //return; + } + + ESP_LOGI(TAG, "Error starting server!"); + vTaskDelete(NULL); + //return NULL; +} + +void webserver_stop(httpd_handle_t server) +{ + if (server) { + /* Stop the httpd server */ + if(ESP_FAIL == httpd_stop(server)) + { + ESP_LOGI(TAG, "Failed to stop server"); + } + } +} + +void webserver_start(void) +{ + + +#ifdef STORING_TO_NVM + device_wifi_onboarded = nvm_read_onboarding_flag(WIFI_ONBOARDING_KEY); + port_red_led_off(); +#endif + + if(!device_wifi_onboarded) + { + wifi_first_init(); + Wifi_Init_SoftAp(); + xTaskCreate(startOnboarding, "onboarding_task", 8192, NULL, 16, NULL); + port_red_led_off(); + + + } + else + { + g_onboardingCompleted = true; + port_red_led_off(); + + } +} + +bool webserver_get_status(void) +{ + if(g_onboardingCompleted && device_wifi_onboarded) + return true; + else + return false; +} + + + +void startOnboardingShutdown(void) +{ + ESP_LOGI(TAG,"Shutting down on-boaring server"); + + /* Stop the on-boarding server */ + webserver_stop(serverHandle); + + /* Disconnecting the wifi-station from wifi-network */ + wifi_sta_disconnecting(); + + /* Stop the wifi service */ + wifi_stop(); + + ESP_LOGI(TAG," ----> on-boaring server has been shut down"); + /* wait for 1.5sec */ + vTaskDelay(1500/portTICK_PERIOD_MS); + + /* Turn off on_boarding led event */ + hmi_stop_red_flashing(); + +} + +void startOnboardingGetSsidAndPassword(char *content) +{ + char *ssid; + char *pwd_encoded; + char *pwd; + + ssid = pvPortMalloc((SSID_MAX_LENGTH + 1) * sizeof(char)); + pwd_encoded = pvPortMalloc((PASSWORD_MAX_LENGTH + 1) * sizeof(char)); + pwd = pvPortMalloc((PASSWORD_MAX_LENGTH + 1) * sizeof(char)); + + if (!(*content == '-')) // Content conforms to query parameters + { + /* Fetch SSID from query parameter */ + if ( httpd_query_key_value(content, "ssid", ssid, SSID_MAX_LENGTH ) != ESP_OK ) + ESP_LOGI(TAG, "Failed to get SSID : %s", ssid); + + /* Fetch password from query parameter */ + if ( httpd_query_key_value(content, "password", pwd_encoded,PASSWORD_MAX_LENGTH ) == ESP_OK ) { + pwd = url_decode(pwd_encoded); + } + else + ESP_LOGI(TAG, "Failed to fetch WiFi password"); + } + else { // Check if Content starts wit Multi-part form data format + extract_field_value(content, "ssid", ssid, SSID_MAX_LENGTH); + extract_field_value(content, "password", pwd_encoded, PASSWORD_MAX_LENGTH); + pwd = url_decode(pwd_encoded); + } + + wifi_update_credentials(ssid, pwd); + + vPortFree(ssid); + vPortFree(pwd_encoded); + vPortFree(pwd); +} + + +bool registerAcctWithServer(char *typeStr, char *customerAccountIdStr, char *customerAccountPasswordStr, char *customerAccountPasswordAgainStr, char *responseStr, uint16_t responseStrSize) +{ + char urlConnectionString[100];// = (char *)pvPortMalloc(100 * sizeof(char)); + char *post_params = modem_get_rxbuf();// = (char *)pvPortMalloc(1024 * sizeof(char)); + char mac_str[20];// = (char *)pvPortMalloc(20 * sizeof(char)); + uint8_t mac_base[6] = {0}; + char chipId[7]; + bool retval = false; + + uint8_t connection_test = 1; + esp_err_t connection_result = ESP_FAIL; + + memset(chipId, 0, 7); + memset(post_params, 0, 100); + + + esp_efuse_mac_get_default(mac_base); + esp_read_mac(mac_base, ESP_MAC_WIFI_STA); + sprintf(mac_str, "%02X%02X%02X%02X%02X%02X", mac_base[0], mac_base[1], mac_base[2], mac_base[3], mac_base[4], mac_base[5]); + + strcpy(chipId, mac_str + 6); + + sprintf(post_params, "type=%s&email=%s&pass=%s&pass_again=%s&sensor=%s&mac=%s&wifi=%s&wallEquip=%d&version=%ld", + typeStr, customerAccountIdStr, customerAccountPasswordStr, customerAccountPasswordAgainStr, chipId, mac_str, wifi_get_ssid(), 0, g_version); + + ESP_LOGI(TAG,"On-boarding string length is: %u\nOn-boarding string content is: %s",strlen(post_params),post_params); + + do + { + /* Setup the URL of the target onboarding server */ + //sprintf(urlConnectionString, MCU_ONBOARDING_URL,connection_test); + strcpy(urlConnectionString, "https://device-status.idealsciences.com"); + ESP_LOGI(TAG, "Attempting connection to on-boarding server: %s", urlConnectionString); + + /* Setup and concatenate the path of the target URL */ + strcat(urlConnectionString, "/sensor-account.php"); + ESP_LOGI(TAG, "Sending POST data to %s", urlConnectionString); + + /* Setup the posting-string to be ON_BOARDING_STR */ + wifi_set_post_str_type(ON_BOARDING_STR); + + /* wait for 5 seconds before doing any connection with the server (just to give some time of disconnection with server if the device has connected to it before that) */ + vTaskDelay(5000/portTICK_PERIOD_MS); + + /* Do the HTTP-POST request with the onboarding string */ + connection_result = http_client_post(urlConnectionString, post_params, responseStr); + + /* Close the connection of http-post */ + http_client_post_stop(); + + /**/ + //sprintf(onboarding_host,ON_BOARDING_SERVER_DOMAIN_NAME,connection_test); + + /* Test the connection with the onboarding server */ + //connection_result = http_client_test(urlConnectionString); + + /* Check the result of the connection */ + if(ESP_OK == connection_result) + break; + + /* Increase the connection_test counter by 1 */ + connection_test++; + + }while(connection_test < 4); + + /* Check if the connection was successful */ + if(connection_test < 4/*ESP_OK == connection_result*/) + { + /* Print this LOG */ + //ESP_LOGI(TAG, "Connected to on-boarding server."); + // + ///* Delay for 8 seconds */ + //vTaskDelay(8000/portTICK_PERIOD_MS); + // + ///**/ + //strcat(urlConnectionString, "/sensor-account.php"); + //ESP_LOGI(TAG, "Sending POST data to %s", urlConnectionString); + // + ///**/ + //wifi_set_post_str_type(ON_BOARDING_STR); + + /**/ + if(ESP_OK == connection_result/*http_client_post(urlConnectionString, post_params, responseStr)*/) + { + ESP_LOGI(TAG, "Response OK\n%s", responseStr); + acc_registered = true; + retval = true; + } + else + { + ESP_LOGI(TAG, "Error Response"); + strcpy(responseStr, "{\"err\":1,\"msg\":\"Server timed out, refresh the page and try again.\"}"); + retval = true; + } + } + else + { + strcpy(responseStr, "{\"err\":1,\"msg\":\"Server or internet unavailable, Server will restart. connect,refresh the page and try again.\"}"); + retval = false; + } + + wifi_set_post_str_type(CHECK_IN_STR); + + return retval; +} + + +#endif + + diff --git a/main/wifi_webServer.h b/main/wifi_webServer.h new file mode 100644 index 0000000..406f9d7 --- /dev/null +++ b/main/wifi_webServer.h @@ -0,0 +1,30 @@ +/* + * tempstick_wifi_server.h + * + * Created on: Jul 28, 2023 + * Author: Sword + */ + +#ifndef MAIN_WIFI_WEBSERVER_H_ +#define MAIN_WIFI_WEBSERVER_H_ + +#include "main.h" +#if (WIFI_NEEDED == 1) +#include "esp_http_server.h" + + +typedef enum +{ + SERVER_IS_STOPPED, + SERVER_IS_RUNNING +}wifi_webServer_status_t; + +bool webserver_get_status(void); +void webserver_stop(httpd_handle_t server); +void webserver_start(void); + +void startOnboardingGetSsidAndPassword(char *content); +void startOnboardingShutdown(void); +bool registerAcctWithServer(char *typeStr, char *customerAccountIdStr, char *customerAccountPasswordStr, char *customerAccountPasswordAgainStr, char *responseStr, uint16_t responseStrSize); +#endif +#endif /* MAIN_WIFI_WEBSERVER_H_ */ diff --git a/partitions.csv b/partitions.csv new file mode 100644 index 0000000..36aa1f5 --- /dev/null +++ b/partitions.csv @@ -0,0 +1,7 @@ +# ESP-IDF Partition Table,,,,, +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs,0x9000, 0x14000, +otadata, data, ota,0x1d000, 0x2000, +phy_init, data, phy,0x1f000, 0x1000, +ota_0, app, ota_0,0x20000,0x1a0000, +ota_1, app, ota_1,0x1c0000,0x1a0000, \ No newline at end of file