commit 0d603b7d2622fe6199be00382abd9fd43009d2f8 Author: Kai Jan Kriegel Date: Mon Jul 24 02:02:48 2023 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ecdf904 --- /dev/null +++ b/.gitignore @@ -0,0 +1,101 @@ +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,c,c++ +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,c,c++ + +### C ### +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +### C++ ### +# Prerequisites + +# Compiled Object files +*.slo + +# Precompiled Headers + +# Compiled Dynamic libraries + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai + +# Executables + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,c,c++ + +build/ +managed_components/ +sdkconfig +sdkconfig.old diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4598611 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "idf.adapterTargetName": "esp32c3", + "idf.flashType": "UART", + "idf.port": "/dev/cu.usbserial-1320" +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1e80c6d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five 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.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(fabmeter) diff --git a/dependencies.lock b/dependencies.lock new file mode 100644 index 0000000..aad4a8b --- /dev/null +++ b/dependencies.lock @@ -0,0 +1,51 @@ +dependencies: + espressif/cbor: + component_hash: 440f4ee4504841cc9b4f3a8ef755776a612ac9dace355514c68b999868f990ff + source: + service_url: https://api.components.espressif.com/ + type: service + version: 0.6.0~1 + espressif/esp-modbus: + component_hash: a03225cc0775fb4f495ff1ca56a91d0b4d4f7ba9961838dee6b04476e71a5347 + source: + service_url: https://api.components.espressif.com/ + type: service + version: 1.0.11 + espressif/esp_diag_data_store: + component_hash: 31f8e570af6186788e3130fc67c5391593f17ac3cbc761a8affc50510de7cdbe + source: + service_url: https://api.components.espressif.com/ + type: service + version: 1.0.0 + espressif/esp_diagnostics: + component_hash: 856abf2f06f5e43a52767e221a6ac2554c944f7708781156f939249491cea3f1 + source: + service_url: https://api.components.espressif.com/ + type: service + version: 1.0.0 + espressif/esp_insights: + component_hash: 94ca222800cdb5abb4d68ef4e8f11c62c9d46614c63783e0d3e6fa15e248fc35 + source: + service_url: https://api.components.espressif.com/ + type: service + version: 1.0.0 + espressif/led_strip: + component_hash: 1f8cc130ebd557fde64c02fe5fad0cb36d69947498c540ace6f425e1083d7773 + source: + service_url: https://api.components.espressif.com/ + type: service + version: 2.4.1 + espressif/rmaker_common: + component_hash: 7b8398212884abfea6199430cb40162c501b93c0c26e0ae90bbe2a3d1112f2d0 + source: + service_url: https://api.components.espressif.com/ + type: service + version: 1.4.2 + idf: + component_hash: null + source: + type: idf + version: 5.1.0 +manifest_hash: de1b2f078e7c29abca615624a54fb96595c3dfdb9d0ecd691a3f119da081b339 +target: esp32c3 +version: 1.0.0 diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..e99822d --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "modbus_params.c" "fabmeter.c" "initializers.c" "mqtt_event_handler.c" "net_event_handler.c" + INCLUDE_DIRS ".") + +# component_compile_options(-Wno-error=format= -Wno-format) \ No newline at end of file diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild new file mode 100644 index 0000000..643ff56 --- /dev/null +++ b/main/Kconfig.projbuild @@ -0,0 +1,171 @@ +menu "FabMeter Configuration" + + config FABMETER_ID + int "FabMeter ID" + default 1 + help + Numerical ID of the FabLight device + + config PIN_DEBUG_LED + int "Debug LED pin" + default 8 + help + GPIO pin number for the debug LED + + menu "WiFi" + + config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + + config WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + + choice WIFI_SCAN_METHOD + prompt "scan method" + default WIFI_FAST_SCAN + help + scan method for the esp32 to use + + config WIFI_FAST_SCAN + bool "fast" + config WIFI_ALL_CHANNEL_SCAN + bool "all" + endchoice + + endmenu + + menu "MQTT" + config BROKER_URI + string "Broker URL" + default "mqtts://mqtt.eclipseprojects.io:8883" + help + URL of an mqtt broker which this example connects to. + + config BROKER_CERTIFICATE_OVERRIDE + string "Broker certificate override" + default "" + help + Please leave empty if broker certificate included from a textfile; otherwise fill in a base64 part of PEM + format certificate + + config BROKER_CERTIFICATE_OVERRIDDEN + bool + default y if BROKER_CERTIFICATE_OVERRIDE != "" + endmenu + + menu "Modbus Configuration" + + orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps" + + config MB_UART_PORT_ONE + bool + default y + depends on (ESP_CONSOLE_UART_NUM !=1) && (SOC_UART_NUM > 1) + + config MB_UART_PORT_TWO + bool + default y + depends on (ESP_CONSOLE_UART_NUM !=2) && (SOC_UART_NUM > 2) + + config MB_UART_PORT_NUM + int "UART port number" + range 0 2 if MB_UART_PORT_TWO + default 2 if MB_UART_PORT_TWO + range 0 1 if MB_UART_PORT_ONE + default 1 if MB_UART_PORT_ONE + help + UART communication port number for Modbus. + + config MB_UART_BAUD_RATE + int "UART communication speed" + range 1200 115200 + default 9600 + help + UART communication speed for Modbus. + + choice + prompt "UART parity" + default MB_UART_PARITY_DISABLE + help + UART parity for Modbus. + + config MB_UART_PARITY_DISABLE + bool "None" + + config MB_UART_PARITY_EVEN + bool "Even" + + config MB_UART_PARITY_ODD + bool "Odd" + endchoice + + config MB_UART_PARITY + int + default 0 if MB_UART_PARITY_DISABLE + default 2 if MB_UART_PARITY_EVEN + default 3 if MB_UART_PARITY_ODD + + config MB_UART_STOP_BITS + int "UART stop bits" + default 1 + range 1 2 + help + UART stop bits for Modbus. + + config MB_UART_RXD + int "UART RXD pin number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 22 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6 + default 8 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C3 ||\ + IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2 + help + GPIO number for UART RX pin. See UART documentation for more information + about available pin numbers for UART. + + config MB_UART_TXD + int "UART TXD pin number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 23 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6 + default 9 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C3 ||\ + IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2 + help + GPIO number for UART TX pin. See UART documentation for more information + about available pin numbers for UART. + + config MB_UART_RTS + int "UART RTS pin number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6 + default 10 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C3 ||\ + IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2 + help + GPIO number for UART RTS pin. This pin is connected to + ~RE/DE pin of RS485 transceiver to switch direction. + See UART documentation for more information about available pin + numbers for UART. + + choice MB_COMM_MODE + prompt "Modbus communication mode" + default MB_COMM_MODE_RTU if CONFIG_FMB_COMM_MODE_RTU_EN + help + Selection of Modbus communication mode option for Modbus. + + config MB_COMM_MODE_RTU + bool "RTU mode" + depends on FMB_COMM_MODE_RTU_EN + + config MB_COMM_MODE_ASCII + bool "ASCII mode" + depends on FMB_COMM_MODE_ASCII_EN + + endchoice + + endmenu + +endmenu diff --git a/main/fabmeter.c b/main/fabmeter.c new file mode 100644 index 0000000..c793cfd --- /dev/null +++ b/main/fabmeter.c @@ -0,0 +1,315 @@ +/* + * SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "modbus_params.h" // for modbus parameters structures +#include "mbcontroller.h" +#include "sdkconfig.h" +#include "modbus_utils.h" +#include "mqtt_event_handler.h" +#include "net_event_handler.h" +#include "initializers.h" + +#define ESP_INSIGHTS_AUTH_KEY "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYzI3N2M3NzUtZDNjNy00NjEzLWIwMzctYjYxZGVjNmUyMGRmIiwiaXNzIjoiZTMyMmI1OWMtNjNjYy00ZTQwLThlYTItNGU3NzY2NTQ1Y2NhIiwic3ViIjoiYjExZWMyZWEtZGE5Ni00ZTQyLWIzODktOWU2MGNlM2U3Y2ZkIiwiZXhwIjoyMDA1MDA0ODk0LCJpYXQiOjE2ODk2NDQ4OTR9.FOPLo6qvkVI5k0M0j8YWonc7jRrTc6wG1p0001awcC6bWEOIxoheY69ZXSbn94vFiCtLyWAJafce3QrLJWgW0THDxGZaUNrjijqLXdKie7qEOVinjsdwILomIMa_NpSVLwZimAkd6B_uw1hk5RqooW2JzJeLYLX5i7oUgSAwZgvjN_cfCS2emrSKpZXD-CaDlNMGjt97VXzj4UFuDsNCWLaW0gEr5RcMMnYFwSxkLgv1NOWGQKfm1mZCRA_swCyxvjX4UrjlWSQZemqiqBMgnW7sBAyG5-UFGu1Fc-MfBfoB9aGblPE7LDwMeT1jgJUaJ9puhDFMY-v4Ilw8jUttzA" + +// The number of parameters that intended to be used in the particular control process +#define MASTER_MAX_CIDS num_device_parameters + +// Number of reading of parameters from slave +#define MASTER_MAX_RETRY 300 + +// Timeout to update cid over Modbus +#define UPDATE_CIDS_TIMEOUT_MS (5000) +#define UPDATE_CIDS_TIMEOUT_TICS (UPDATE_CIDS_TIMEOUT_MS / portTICK_PERIOD_MS) + +// Timeout between polls +#define POLL_TIMEOUT_MS (1) +#define POLL_TIMEOUT_TICS (POLL_TIMEOUT_MS / portTICK_PERIOD_MS) + + +static const char *TAG = "FABMETER"; + +// Enumeration of modbus device addresses accessed by master device +enum { + MB_DEVICE_ADDR1 = 1 // Only one slave device used for the test (add other slave addresses here) +}; + +// Example Data (Object) Dictionary for Modbus parameters: +// The CID field in the table must be unique. +// Modbus Slave Addr field defines slave address of the device with correspond parameter. +// Modbus Reg Type - Type of Modbus register area (Holding register, Input Register and such). +// Reg Start field defines the start Modbus register number and Reg Size defines the number of registers for the characteristic accordingly. +// The Instance Offset defines offset in the appropriate parameter structure that will be used as instance to save parameter value. +// Data Type, Data Size specify type of the characteristic and its data size. +// Parameter Options field specifies the options that can be used to process parameter value (limits or masks). +// Access Mode - can be used to implement custom options for processing of characteristic (Read/Write restrictions, factory mode values and etc). +const mb_parameter_descriptor_t device_parameters[] = { + // { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode} + { CID_INP_U_L1N, STR("Phase 1 line to neutral"), STR("V"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0000, 2, + INPUT_OFFSET(voltage_L1N), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_U_L2N, STR("Phase 2 line to neutral"), STR("V"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0002, 2, + INPUT_OFFSET(voltage_L2N), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_U_L3N, STR("Phase 3 line to neutral"), STR("V"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0004, 2, + INPUT_OFFSET(voltage_L3N), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ}, + { CID_INP_I_L1, STR("Phase 1 current"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0006, 2, + INPUT_OFFSET(current_L1), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_I_L2, STR("Phase 2 current"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0008, 2, + INPUT_OFFSET(current_L2), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_I_L3, STR("Phase 3 current"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x000A, 2, + INPUT_OFFSET(current_L3), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_P_L1, STR("Phase 1 active power"), STR("W"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x000C, 2, + INPUT_OFFSET(active_power_L1), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_P_L2, STR("Phase 2 active power"), STR("W"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x000E, 2, + INPUT_OFFSET(active_power_L2), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_P_L3, STR("Phase 3 active power"), STR("W"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0010, 2, + INPUT_OFFSET(active_power_L3), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_S_L1, STR("Phase 1 apparent power"), STR("VA"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0012, 2, + INPUT_OFFSET(apparent_power_L1), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_S_L2, STR("Phase 2 apparent power"), STR("VA"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0014, 2, + INPUT_OFFSET(apparent_power_L2), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_S_L3, STR("Phase 3 apparent power"), STR("VA"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0016, 2, + INPUT_OFFSET(apparent_power_L3), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_Q_L1, STR("Phase 1 reactive power"), STR("VAR"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0018, + 2, INPUT_OFFSET(reactive_power_L1), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_Q_L2, STR("Phase 2 reactive power"), STR("VAR"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x001A, + 2, INPUT_OFFSET(reactive_power_L2), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_Q_L3, STR("Phase 3 reactive power"), STR("VAR"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x001C, + 2, INPUT_OFFSET(reactive_power_L3), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_PF_L1, STR("Phase 1 power factor"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x001E, 2, + INPUT_OFFSET(power_factor_L1), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_PF_L2, STR("Phase 2 power factor"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0020, 2, + INPUT_OFFSET(power_factor_L2), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_PF_L3, STR("Phase 3 power factor"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0022, 2, + INPUT_OFFSET(power_factor_L3), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_U_LN_AVG, STR("Average line-to-neutral voltage"), STR("V"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x002A, + 2, INPUT_OFFSET(voltage_avg_LN), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_I_L_AVG, STR("Average line current"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x002E, + 2, INPUT_OFFSET(current_avg), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_I_SUM, STR("Sum of line currents"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0030, + 2, INPUT_OFFSET(current_sum), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_P_SUM, STR("Total system power"), STR("W"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0034, + 2, INPUT_OFFSET(active_power_sum), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_S_SUM, STR("Total system apparent power"), STR("VA"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0038, + 2, INPUT_OFFSET(apparent_power_sum), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_Q_SUM, STR("Total system reactive power"), STR("VAR"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x003C, + 2, INPUT_OFFSET(reactive_power_sum), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_PF_SUM, STR("Total system power factor"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x003E, + 2, INPUT_OFFSET(power_factor_sum), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_FREQ, STR("Line frequency"), STR("Hz"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0046, 2, + INPUT_OFFSET(frequency), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_E_IMP, STR("Imported energy"), STR("kWh"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0048, 2, + INPUT_OFFSET(active_energy_import), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_E_EXP, STR("Exported energy"), STR("kWh"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x004A, 2, + INPUT_OFFSET(active_energy_export), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_U_L1L2, STR("Phase 1 to phase 2 voltage"), STR("V"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x004C, + 2, INPUT_OFFSET(voltage_L1L2), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_U_L2L3, STR("Phase 2 to phase 3 voltage"), STR("V"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x004E, + 2, INPUT_OFFSET(voltage_L2L3), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_U_L3L1, STR("Phase 3 to phase 1 voltage"), STR("V"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0050, + 2, INPUT_OFFSET(voltage_L3L1), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_U_LL_AVG, STR("Average line-to-line voltage"), STR("V"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0052, + 2, INPUT_OFFSET(voltage_avg_LL), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_I_N, STR("Neutral current"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0054, + 2, INPUT_OFFSET(current_N), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_E_SUM, STR("Total Energy"), STR("kWh"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0156, + 2, INPUT_OFFSET(active_energy_sum), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_Eq_SUM, STR("Total reactive Energy"), STR("kVARh"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0158, + 2, INPUT_OFFSET(reactive_energy_sum), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_E_RESET_SUM, STR("resettable total energy"), STR("kWh"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0180, + 2, INPUT_OFFSET(active_energy_resettable_sum), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_Eq_RESET_SUM, STR("resettable total reactive energy"), STR("kVARh"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0182, + 2, INPUT_OFFSET(reactive_energy_resettable_sum), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_E_RESET_IMP, STR("resettable total imported energy"), STR("kWh"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0184, + 2, INPUT_OFFSET(active_energy_import_resettable), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_E_RESET_EXP, STR("resettable total exported energy"), STR("kWh"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0186, + 2, INPUT_OFFSET(active_energy_export_resettable), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_E_NET, STR("Net kWh (Import - Export)"), STR("kWh"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x018C, + 2, INPUT_OFFSET(active_energy_net), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_P_IMP_SUM, STR("Total imported power"), STR("W"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0500, + 2, INPUT_OFFSET(active_power_import_sum), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_INP_P_EXP_SUM, STR("Total exported power"), STR("W"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0x0502, + 2, INPUT_OFFSET(active_power_export_sum), PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_HLD_SYS_TYPE, STR("System Type"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x000A, + 2, HOLD_OFFSET(sys_type), PARAM_TYPE_FLOAT, 4, OPTS( 1, 3, 0 ), PAR_PERMS_READ_WRITE }, + { CID_HLD_PULSE_WIDTH, STR("Pulse Width"), STR("ms"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x000C, + 2, HOLD_OFFSET(pulse_width), PARAM_TYPE_FLOAT, 4, OPTS( 60, 200, 0 ), PAR_PERMS_READ_WRITE }, + { CID_HLD_KPPA, STR("Key Parameter Programming Authorization (KPPA)"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x000E, + 2, HOLD_OFFSET(kppa), PARAM_TYPE_FLOAT, 4, OPTS( 0, 1, 0 ), PAR_PERMS_READ_WRITE }, + { CID_HLD_PARITY_AND_STOP, STR("Parity and stop bit"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x0012, + 2, HOLD_OFFSET(kppa), PARAM_TYPE_FLOAT, 4, OPTS( 0, 3, 0 ), PAR_PERMS_READ_WRITE}, + { CID_HLD_MB_ADDR, STR("Modbus address"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x0014, + 2, HOLD_OFFSET(modbus_addr), PARAM_TYPE_FLOAT, 4, OPTS( 1, 247, 0 ), PAR_PERMS_READ_WRITE }, + { CID_HLD_PULSE_CONST, STR("Pulse Constant"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x0016, + 2, HOLD_OFFSET(pulse_const), PARAM_TYPE_FLOAT, 4, OPTS( 0, 3, 0 ), PAR_PERMS_READ_WRITE }, + { CID_HLD_PASSWD, STR("Password"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x0018, + 2, HOLD_OFFSET(password), PARAM_TYPE_FLOAT, 4, OPTS( 0, 9999, 0 ), PAR_PERMS_READ_WRITE }, + { CID_HLD_BAUDRATE, STR("Baudrate"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x001C, + 2, HOLD_OFFSET(baudrate), PARAM_TYPE_FLOAT, 4, OPTS( 0, 5, 0 ), PAR_PERMS_READ_WRITE }, + { CID_HLD_AUTO_SCROLL_TIME, STR("Auto Scroll Time"), STR("s"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x003A, + 2, HOLD_OFFSET(auto_scroll_time), PARAM_TYPE_FLOAT, 4, OPTS( 0, 60, 0 ), PAR_PERMS_READ_WRITE }, + { CID_HLD_BACKLIGHT_TIME, STR("Backlight Time"), STR("min"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x003C, + 2, HOLD_OFFSET(backlight_time), PARAM_TYPE_FLOAT, 4, OPTS( 0, 121, 0 ), PAR_PERMS_READ_WRITE }, + { CID_HLD_PULSE_1_E_TYPE, STR("Pulse 1 Energy Type"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x0056, + 2, HOLD_OFFSET(pulse_1_energy_type), PARAM_TYPE_FLOAT, 4, OPTS( 0, 4, 0 ), PAR_PERMS_READ_WRITE }, + { CID_HLD_RST_HIST, STR("Reset historical data"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0xF010, + 1, HOLD_OFFSET(reset_history), PARAM_TYPE_U16, 2, OPTS( 0, 1, 0 ), PAR_PERMS_WRITE }, + { CID_HLD_SERIAL, STR("Serial number"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0xFC00, + 2, HOLD_OFFSET(serial_number), PARAM_TYPE_U16, 2, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_HLD_METER_TYPE, STR("Meter type"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0xFC02, + 1, HOLD_OFFSET(meter_type), PARAM_TYPE_U16, 2, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + { CID_HLD_METER_FIRMWARE, STR("Firmware version"), STR(""), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0xFC84, + 1, HOLD_OFFSET(fw_version), PARAM_TYPE_U16, 2, OPTS( 0, 0, 0 ), PAR_PERMS_READ }, + +}; + +// Calculate number of parameters in the table +const uint16_t num_device_parameters = (sizeof(device_parameters)/sizeof(device_parameters[0])); + +// User operation function to read slave values and check alarm +static void modbus_poller(void *arg) +{ + esp_err_t err = ESP_OK; + uint32_t value = 0; + uint8_t type = 0; + const mb_parameter_descriptor_t* param_descriptor = NULL; + + char topic[512]; + + ESP_LOGI(TAG, "Start modbus poller"); + + for(;;) { + // Read all found characteristics from slave(s) + for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++) + { + // Get data from parameters description table + // and use this information to fill the characteristics description table + // and having all required fields in just one table + err = mbc_master_get_cid_info(cid, ¶m_descriptor); + if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) { + void* temp_data_ptr = master_get_param_data(param_descriptor); + assert(temp_data_ptr); + type = param_descriptor->param_type; + extern esp_mqtt_client_handle_t mqtt_client; + err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key, + (uint8_t*)&value, &type); + if (err == ESP_OK) { + *(uint32_t*)temp_data_ptr = __beswap_32(value); + char payload[32]; + + switch (param_descriptor->param_type) + { + case PARAM_TYPE_FLOAT: + ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %f (0x%"PRIu32") read successful.", + param_descriptor->cid, + (char*)param_descriptor->param_key, + (char*)param_descriptor->param_units, + *(float*)temp_data_ptr, + *(uint32_t*)temp_data_ptr); + + snprintf(topic, 512, "fabmeter/%05d/%s", CONFIG_FABMETER_ID, (char*)param_descriptor->param_key); + + snprintf(payload, 32, "%f", *(float*)temp_data_ptr); + esp_mqtt_client_publish(mqtt_client, topic, payload, 0, 0, 0); + break; + + case PARAM_TYPE_U8: + ESP_LOGI(TAG, "Characteristic #%d %s value = 0x%"PRIu8" read successful.", + param_descriptor->cid, + (char*)param_descriptor->param_key, + *(uint8_t*)temp_data_ptr); + snprintf(topic, 512, "fabmeter/%05d/%s", CONFIG_FABMETER_ID, (char*)param_descriptor->param_key); + + snprintf(payload, 32, "0x%"PRIu8"", *(uint8_t*)temp_data_ptr); + esp_mqtt_client_publish(mqtt_client, topic, payload, 0, 0, 0); + break; + + case PARAM_TYPE_U16: + ESP_LOGI(TAG, "Characteristic #%d %s value = 0x%"PRIu16" read successful.", + param_descriptor->cid, + (char*)param_descriptor->param_key, + *(uint16_t*)temp_data_ptr); + snprintf(topic, 512, "fabmeter/%05d/%s", CONFIG_FABMETER_ID, (char*)param_descriptor->param_key); + + snprintf(payload, 32, "0x%"PRIu16"", *(uint16_t*)temp_data_ptr); + esp_mqtt_client_publish(mqtt_client, topic, payload, 0, 0, 0); + break; + + case PARAM_TYPE_U32: + ESP_LOGI(TAG, "Characteristic #%d %s value = 0x%"PRIu32" read successful.", + param_descriptor->cid, + (char*)param_descriptor->param_key, + *(uint32_t*)temp_data_ptr); + snprintf(topic, 512, "fabmeter/%05d/%s", CONFIG_FABMETER_ID, (char*)param_descriptor->param_key); + + snprintf(payload, 32, "0x%"PRIu32"", *(uint32_t*)temp_data_ptr); + esp_mqtt_client_publish(mqtt_client, topic, payload, 0, 0, 0); + break; + + case PARAM_TYPE_ASCII: + ESP_LOGI(TAG, "Characteristic #%d %s value = %s read successful.", + param_descriptor->cid, + (char*)param_descriptor->param_key, + (char*)temp_data_ptr); + snprintf(topic, 512, "fabmeter/%05d/%s", CONFIG_FABMETER_ID, (char*)param_descriptor->param_key); + + snprintf(payload, 32, "%s", (char*)temp_data_ptr); + esp_mqtt_client_publish(mqtt_client, topic, payload, 0, 0, 0); + break; + + default: + break; + } + } else { + ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%d (%s).", + param_descriptor->cid, + (char*)param_descriptor->param_key, + (int)err, + (char*)esp_err_to_name(err)); + } + + vTaskDelay(POLL_TIMEOUT_TICS); // delay between cids + } + } + vTaskDelay(UPDATE_CIDS_TIMEOUT_TICS); // delay between updates + } +} + + +void app_main(void) +{ + extern led_strip_handle_t debug_led; + + esp_insights_config_t config = { + .log_type = ESP_DIAG_LOG_TYPE_ERROR, + .auth_key = ESP_INSIGHTS_AUTH_KEY, + }; + + // Initialization of device peripheral and objects + init_led_strip(); + + ESP_ERROR_CHECK(led_strip_set_pixel(debug_led, 0, 0xFF, 0, 0)); + ESP_ERROR_CHECK(led_strip_refresh(debug_led)); + + init_eventloop(); + init_nvs(); + init_wifi(); + esp_insights_init(&config); + + init_mqtt(); + + ESP_ERROR_CHECK(init_modbus(&device_parameters[0], num_device_parameters)); + vTaskDelay(10); + + modbus_poller(NULL); +} \ No newline at end of file diff --git a/main/idf_component.yml b/main/idf_component.yml new file mode 100644 index 0000000..5199bfa --- /dev/null +++ b/main/idf_component.yml @@ -0,0 +1,19 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp-modbus: "^1.0.11" + espressif/led_strip: "^2.3.1" + espressif/esp_insights: "^1.0.0" + ## Required IDF version + idf: + version: ">=4.1.0" + # # Put list of dependencies here + # # For components maintained by Espressif: + # component: "~1.0.0" + # # For 3rd party components: + # username/component: ">=1.0.0,<2.0.0" + # username2/component2: + # version: "~1.0.0" + # # For transient dependencies `public` flag can be set. + # # `public` flag doesn't have an effect dependencies of the `main` component. + # # All dependencies of `main` are public by default. + # public: true diff --git a/main/initializers.c b/main/initializers.c new file mode 100644 index 0000000..bb387c2 --- /dev/null +++ b/main/initializers.c @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "initializers.h" +#include "net_event_handler.h" +#include "mqtt_event_handler.h" +#include "sdkconfig.h" + +#define LED_STRIP_RMT_RES_HZ (10 * 1000 * 1000) + +char TAG[] = "initializers"; +led_strip_handle_t debug_led; +esp_mqtt_client_handle_t mqtt_client; + +// Modbus master initialization +esp_err_t init_modbus(const mb_parameter_descriptor_t* device_parameters, const uint16_t num_device_parameters) +{ + // Initialize and start Modbus controller + mb_communication_info_t comm = { + .port = MB_PORT_NUM, +#if CONFIG_MB_COMM_MODE_ASCII + .mode = MB_MODE_ASCII, +#elif CONFIG_MB_COMM_MODE_RTU + .mode = MB_MODE_RTU, +#endif + .baudrate = MB_DEV_SPEED, + .parity = MB_PARITY + }; + void* master_handler = NULL; + + esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler); + MB_RETURN_ON_FALSE((master_handler != NULL), ESP_ERR_INVALID_STATE, TAG, + "mb controller initialization fail."); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb controller initialization fail, returns(0x%"PRIu32").", + (uint32_t)err); + err = mbc_master_setup((void*)&comm); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb controller setup fail, returns(0x%"PRIu32").", + (uint32_t)err); + + // Set UART pin numbers + err = uart_set_pin(MB_PORT_NUM, CONFIG_MB_UART_TXD, CONFIG_MB_UART_RXD, + CONFIG_MB_UART_RTS, UART_PIN_NO_CHANGE); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb serial set pin failure, uart_set_pin() returned (0x%"PRIu32").", (uint32_t)err); + + err = mbc_master_start(); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb controller start fail, returns(0x%"PRIu32").", + (uint32_t)err); + + // Set driver mode to Half Duplex + err = uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb serial set mode failure, uart_set_mode() returned (0x%"PRIu32").", (uint32_t)err); + + // Set UART stop bits + err = uart_set_stop_bits(MB_PORT_NUM, MB_STOP_BITS); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb serial set stop bits failure, uart_set_stop_bits() returned (0x%"PRIu32").", (uint32_t)err); + + vTaskDelay(5); + err = mbc_master_set_descriptor(device_parameters, num_device_parameters); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb controller set descriptor fail, returns(0x%"PRIu32").", + (uint32_t)err); + ESP_LOGI(TAG, "Modbus master stack initialized..."); + return err; +} + +esp_err_t init_led_strip() { + // LED strip general initialization, according to your led board design + led_strip_config_t debug_led_config = { + .strip_gpio_num = CONFIG_PIN_DEBUG_LED, // The GPIO that connected to the LED strip's data line + .max_leds = 1, // The number of LEDs in the strip, + .led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip + .led_model = LED_MODEL_WS2812, // LED strip model + .flags.invert_out = false, // whether to invert the output signal + }; + + // LED strip backend configuration: RMT + led_strip_rmt_config_t debug_led_rmt_config = { + .clk_src = RMT_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption + .resolution_hz = LED_STRIP_RMT_RES_HZ, // RMT counter clock frequency + .flags.with_dma = false, // DMA feature is available on ESP target like ESP32-S3 + }; + ESP_ERROR_CHECK(led_strip_new_rmt_device(&debug_led_config, &debug_led_rmt_config, &debug_led)); + ESP_LOGI("led", "Created LED strip object with RMT backend for debug LED"); + return ESP_OK; +} + +esp_err_t init_eventloop() { + esp_err_t ret = esp_event_loop_create_default(); + ESP_ERROR_CHECK(ret); + return ret; +} + +esp_err_t init_wifi() { + esp_err_t ret; + ret = esp_netif_init(); + ESP_ERROR_CHECK(ret); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ret = esp_wifi_init(&cfg); + ESP_ERROR_CHECK(ret); + + ret = esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &network_event_handler, NULL, NULL); + ESP_ERROR_CHECK(ret); + ret = esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &network_event_handler, NULL, NULL); + ESP_ERROR_CHECK(ret); + + + // Initialize default station as network interface instance (esp-netif) + esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta(); + assert(sta_netif); + + // Initialize and start WiFi + wifi_config_t wifi_config = { + .sta = { + .ssid = CONFIG_WIFI_SSID, + .password = CONFIG_WIFI_PASSWORD, + .scan_method = CONFIG_WIFI_FAST_SCAN ? WIFI_FAST_SCAN : WIFI_ALL_CHANNEL_SCAN, + .sort_method = WIFI_CONNECT_AP_BY_SIGNAL, + .threshold.rssi = -127, + .threshold.authmode = WIFI_AUTH_WPA2_PSK, + }, + }; + ret = esp_wifi_set_mode(WIFI_MODE_STA); + ESP_ERROR_CHECK(ret); + ret = esp_wifi_set_config(WIFI_IF_STA, &wifi_config); + ESP_ERROR_CHECK(ret); + ret = esp_wifi_start(); + ESP_ERROR_CHECK(ret); + + return ret; +} + +esp_err_t init_nvs() { + // Initialize NVS + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + return ret; +} + +esp_err_t init_mqtt() { + esp_err_t ret; + const esp_mqtt_client_config_t mqtt_cfg = { + .broker = { + .address.uri = CONFIG_BROKER_URI, + .verification.crt_bundle_attach = esp_crt_bundle_attach + } + }; + + esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); + /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */ + ret = esp_mqtt_client_register_event(client, MQTT_EVENT_ANY, mqtt_event_handler, NULL); + ESP_ERROR_CHECK(ret); + ret = esp_mqtt_client_start(client); + ESP_ERROR_CHECK(ret); + + mqtt_client = client; + + return ret; +} diff --git a/main/initializers.h b/main/initializers.h new file mode 100644 index 0000000..e003195 --- /dev/null +++ b/main/initializers.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include "mbcontroller.h" + +#define MB_PORT_NUM (CONFIG_MB_UART_PORT_NUM) // Number of UART port used for Modbus connection +#define MB_DEV_SPEED (CONFIG_MB_UART_BAUD_RATE) // The communication speed of the UART +#define MB_PARITY (CONFIG_MB_UART_PARITY) // Parity used for Modbus communication +#define MB_STOP_BITS (CONFIG_MB_UART_STOP_BITS) // Number of stop bits used for UART communication + +esp_err_t init_modbus(const mb_parameter_descriptor_t* device_parameters, const uint16_t num_device_parameters); +esp_err_t init_led_strip(); +esp_err_t init_eventloop(); +esp_err_t init_wifi(); +esp_err_t init_nvs(); +esp_err_t init_mqtt(); \ No newline at end of file diff --git a/main/modbus_params.c b/main/modbus_params.c new file mode 100644 index 0000000..d95c7f7 --- /dev/null +++ b/main/modbus_params.c @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +/*===================================================================================== + * Description: + * C file to define parameter storage instances + *====================================================================================*/ +#include "modbus_params.h" + +// Here are the user defined instances for device parameters packed by 1 byte +// These are keep the values that can be accessed from Modbus master +holding_reg_params_t holding_reg_params = { 0 }; + +input_reg_params_t input_reg_params = { 0 }; + +coil_reg_params_t coil_reg_params = { 0 }; + +discrete_reg_params_t discrete_reg_params = { 0 }; + diff --git a/main/modbus_params.h b/main/modbus_params.h new file mode 100644 index 0000000..5fc0f29 --- /dev/null +++ b/main/modbus_params.h @@ -0,0 +1,179 @@ +/* + * SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/*===================================================================================== + * Description: + * The Modbus parameter structures used to define Modbus instances that + * can be addressed by Modbus protocol. Define these structures per your needs in + * your application. Below is just an example of possible parameters. + *====================================================================================*/ +#ifndef _DEVICE_PARAMS +#define _DEVICE_PARAMS + +#include + +// This file defines structure of modbus parameters which reflect correspond modbus address space +// for each modbus register type (coils, discreet inputs, holding registers, input registers) +#pragma pack(push, 1) +typedef struct +{ + uint8_t discrete_input0:1; + uint8_t discrete_input1:1; + uint8_t discrete_input2:1; + uint8_t discrete_input3:1; + uint8_t discrete_input4:1; + uint8_t discrete_input5:1; + uint8_t discrete_input6:1; + uint8_t discrete_input7:1; + uint8_t discrete_input_port1:8; +} discrete_reg_params_t; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct +{ + uint8_t coils_port0; + uint8_t coils_port1; +} coil_reg_params_t; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct +{ + float voltage_L1N; // 0 + float voltage_L2N; // 2 + float voltage_L3N; // 4 + float current_L1; // 6 + float current_L2; // 8 + float current_L3; // 10 + float active_power_L1; // 12 + float active_power_L2; // 14 + float active_power_L3; // 16 + float apparent_power_L1; // 18 + float apparent_power_L2; // 20 + float apparent_power_L3; // 22 + float reactive_power_L1; // 24 + float reactive_power_L2; // 26 + float reactive_power_L3; // 28 + float power_factor_L1; // 30 + float power_factor_L2; // 32 + float power_factor_L3; // 34 + float voltage_avg_LN; // 36 + float current_avg; // 38 + float current_sum; // 40 + float active_power_sum; // 42 + float apparent_power_sum; // 44 + float reactive_power_sum; // 46 + float power_factor_sum; // 48 + float frequency; // 50 + float active_energy_import; // 52 + float active_energy_export; // 54 + float voltage_L1L2; // 56 + float voltage_L2L3; // 58 + float voltage_L3L1; // 60 + float voltage_avg_LL; // 62 + float current_N; // 64 + float active_energy_sum; // 66 + float reactive_energy_sum; // 68 + float active_energy_resettable_sum; // 70 + float reactive_energy_resettable_sum; // 72 + float active_energy_import_resettable; // 74 + float active_energy_export_resettable; // 76 + float active_energy_net; // 78 + float active_power_import_sum; // 80 + float active_power_export_sum; // 82 +} input_reg_params_t; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct +{ + float sys_type; // 0 + float pulse_width; // 2 + float kppa; // 4 + float parrity_and_stop; // 6 + float modbus_addr; // 8 + float pulse_const; // 10 + float password; // 12 + float baudrate; // 14 + float auto_scroll_time; // 16 + float backlight_time; // 18 + float pulse_1_energy_type; // 20 + uint16_t reset_history; // 22 + uint32_t serial_number; // 24 + uint16_t meter_type; // 28 + uint16_t fw_version; // 30 + +} holding_reg_params_t; +#pragma pack(pop) + +extern holding_reg_params_t holding_reg_params; +extern input_reg_params_t input_reg_params; +extern coil_reg_params_t coil_reg_params; +extern discrete_reg_params_t discrete_reg_params; + +// Enumeration of all supported CIDs for device (used in parameter definition table) +enum { + CID_INP_U_L1N = 0, // Input voltage L1-N + CID_INP_U_L2N, // Input voltage L2-N + CID_INP_U_L3N, // Input voltage L3-N + CID_INP_I_L1, // Input current L1 + CID_INP_I_L2, // Input current L2 + CID_INP_I_L3, // Input current L3 + CID_INP_P_L1, // Input active power L1 + CID_INP_P_L2, // Input active power L2 + CID_INP_P_L3, // Input active power L3 + CID_INP_S_L1, // Input apparent power L1 + CID_INP_S_L2, // Input apparent power L2 + CID_INP_S_L3, // Input apparent power L3 + CID_INP_Q_L1, // Input reactive power L1 + CID_INP_Q_L2, // Input reactive power L2 + CID_INP_Q_L3, // Input reactive power L3 + CID_INP_PF_L1, // Input power factor L1 + CID_INP_PF_L2, // Input power factor L2 + CID_INP_PF_L3, // Input power factor L3 + CID_INP_U_LN_AVG, // Input voltage L-N average + CID_INP_I_L_AVG, // Input current L average + CID_INP_I_SUM, // Input current sum + CID_INP_P_SUM, // Input active power sum + CID_INP_S_SUM, // Input apparent power sum + CID_INP_Q_SUM, // Input reactive power sum + CID_INP_PF_SUM, // Input power factor average + CID_INP_FREQ, // Input frequency + CID_INP_E_IMP, // Input energy import + CID_INP_E_EXP, // Input energy export + CID_INP_U_L1L2, // Input voltage L1-L2 + CID_INP_U_L2L3, // Input voltage L2-L3 + CID_INP_U_L3L1, // Input voltage L3-L1 + CID_INP_U_LL_AVG, // Input voltage L-L average + CID_INP_I_N, // Input current N + CID_INP_E_SUM, // Input energy sum + CID_INP_Eq_SUM, // Input energy reactive sum + CID_INP_E_RESET_SUM, // Input resettable energy sum + CID_INP_Eq_RESET_SUM, // Input resettable reactive energy sum + CID_INP_E_RESET_IMP, // Input resettable energy import + CID_INP_E_RESET_EXP, // Input resettable energy export + CID_INP_E_NET, // Input net energy *import - export* + CID_INP_P_IMP_SUM, // Input power import sum + CID_INP_P_EXP_SUM, // Input power export sum + CID_HLD_SYS_TYPE, // System type (1 - Single phase, 3 - Three phase) + CID_HLD_PULSE_WIDTH, // Pulse width (60ms, 100ms, 200ms) + CID_HLD_KPPA, // Key Parameter Programming Authorization (0 - not authorized, 1 - authorized) + CID_HLD_PARITY_AND_STOP, // Parity and stop bits (0 - 1 stop bit, no parity, 1 - 1 stop bit, even parity, 2 - 1 stop bit, odd parity, 3 - 2 stop bits, no parity) + CID_HLD_MB_ADDR, // Modbus address (1-247) + CID_HLD_PULSE_CONST,// Pulse constant (0 - 1000 imp/kWh, 1 - 100 imp/kWh, 2 - 10 imp/kWh, 3 - 1 imp/kWh) + CID_HLD_PASSWD, // Password (0 - 9999, default 1000) + CID_HLD_BAUDRATE, // Baudrate (0 - 2400, 1 - 4800, 2 - 9600, 3 - 19200, 5 - 1200) + CID_HLD_AUTO_SCROLL_TIME, // Auto scroll time (0-60 - time in seconds) + CID_HLD_BACKLIGHT_TIME, // Backlight time (0-121, 0 - always on, 121, always off, 1-120 - time in seconds) + CID_HLD_PULSE_1_E_TYPE, // Pulse 1 energy type (1 - import, 2 - total, 3 - export) + CID_HLD_RST_HIST, // Reset history (0x0003 - reset energy info) + CID_HLD_SERIAL, // Serial number (uint32_t, read only) + CID_HLD_METER_TYPE, // Meter type (0x0089 - SDM72D-M-2, read only) + CID_HLD_METER_FIRMWARE // Meter firmware (Format XX.YY, XX = data[0], YY = data[1] , read only) +}; + +#endif // !defined(_DEVICE_PARAMS) \ No newline at end of file diff --git a/main/modbus_utils.h b/main/modbus_utils.h new file mode 100644 index 0000000..079f43d --- /dev/null +++ b/main/modbus_utils.h @@ -0,0 +1,61 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// The macro to get offset for parameter in the appropriate structure +#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1)) +#define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) + 1)) +#define COIL_OFFSET(field) ((uint16_t)(offsetof(coil_reg_params_t, field) + 1)) +// Discrete offset macro +#define DISCR_OFFSET(field) ((uint16_t)(offsetof(discrete_reg_params_t, field) + 1)) + +#define STR(fieldname) ((const char*)( fieldname )) +// Options can be used as bit masks or parameter limits +#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val } + +// BigEndian 32bit swap +// just swap the two 16 bit registers since the two bytes in each +// register are already swapped (BigEndian) as per modbus standard. +# define __beswap_32(x) \ + (__extension__ \ + ({ uint32_t __bsx = (x); \ + ((((__bsx) >> 16) & 0xffff) | (((__bsx) & 0xffff) << 16)); })) + +// The function to get pointer to parameter storage (instance) according to parameter description table +static void* master_get_param_data(const mb_parameter_descriptor_t* param_descriptor) +{ + assert(param_descriptor != NULL); + void* instance_ptr = NULL; + if (param_descriptor->param_offset != 0) { + switch(param_descriptor->mb_param_type) + { + case MB_PARAM_HOLDING: + instance_ptr = ((void*)&holding_reg_params + param_descriptor->param_offset - 1); + break; + case MB_PARAM_INPUT: + instance_ptr = ((void*)&input_reg_params + param_descriptor->param_offset - 1); + break; + case MB_PARAM_COIL: + instance_ptr = ((void*)&coil_reg_params + param_descriptor->param_offset - 1); + break; + case MB_PARAM_DISCRETE: + instance_ptr = ((void*)&discrete_reg_params + param_descriptor->param_offset - 1); + break; + default: + instance_ptr = NULL; + break; + } + } else { + ESP_LOGE("MB_UTILS", "Wrong parameter offset for CID #%d", param_descriptor->cid); + assert(instance_ptr != NULL); + } + return instance_ptr; +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/main/mqtt_event_handler.c b/main/mqtt_event_handler.c new file mode 100644 index 0000000..157dd7a --- /dev/null +++ b/main/mqtt_event_handler.c @@ -0,0 +1,79 @@ +#include "mqtt_event_handler.h" +#include +#include +#include "sdkconfig.h" + +/* + * @brief Event handler registered to receive MQTT events + * + * This function is called by the MQTT client event loop. + * + * @param handler_args user data registered to the event. + * @param base Event base for the handler(always MQTT Base in this example). + * @param event_id The id for the received event. + * @param event_data The data for the event, esp_mqtt_event_handle_t. + */ +void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) { + ESP_LOGD("mqtt", "Event dispatched from event loop base=%s, event_id=%" PRIu32, base, event_id); + esp_mqtt_event_handle_t event = event_data; + esp_mqtt_client_handle_t client = event->client; + int msg_id; + switch ((esp_mqtt_event_id_t) event_id) { + case MQTT_EVENT_CONNECTED: + ESP_LOGI("mqtt", "MQTT_EVENT_CONNECTED"); + + char id[6] = "00000"; + sprintf(id, "%05d", CONFIG_FABMETER_ID); + + // Register Meter + msg_id = esp_mqtt_client_publish(client, "fabmeter", id, 0, 0, 0); + ESP_LOGI("mqtt", "sent publish successful, msg_id=%d", msg_id); + + break; + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI("mqtt", "MQTT_EVENT_DISCONNECTED"); + break; + + case MQTT_EVENT_SUBSCRIBED: + ESP_LOGI("mqtt", "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGI("mqtt", "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_PUBLISHED: + ESP_LOGI("mqtt", "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_DATA: + ESP_LOGI("mqtt", "MQTT_EVENT_DATA"); + printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); + printf("DATA=%.*s\r\n", event->data_len, event->data); + + char* topic = strndup(event->topic, event->topic_len); + + ESP_LOGI("mqtt", "TOPIC=%s", topic); + + free(topic); + + // if (ret != ESP_OK) { + // ESP_LOGE("mqtt", "Error handling MQTT command: %s", esp_err_to_name(ret)); + // } + break; + case MQTT_EVENT_ERROR: + ESP_LOGE("mqtt", "MQTT_EVENT_ERROR"); + if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) { + ESP_LOGE("mqtt", "Last error code reported from esp-tls: 0x%x", + event->error_handle->esp_tls_last_esp_err); + ESP_LOGE("mqtt", "Last tls stack error number: 0x%x", event->error_handle->esp_tls_stack_err); + ESP_LOGE("mqtt", "Last captured errno : %d (%s)", event->error_handle->esp_transport_sock_errno, + strerror(event->error_handle->esp_transport_sock_errno)); + } else if (event->error_handle->error_type == MQTT_ERROR_TYPE_CONNECTION_REFUSED) { + ESP_LOGE("mqtt", "Connection refused error: 0x%x", event->error_handle->connect_return_code); + } else { + ESP_LOGW("mqtt", "Unknown error type: 0x%x", event->error_handle->error_type); + } + break; + default: + ESP_LOGI("mqtt", "Other event id:%d", event->event_id); + break; + } +} diff --git a/main/mqtt_event_handler.h b/main/mqtt_event_handler.h new file mode 100644 index 0000000..89afefe --- /dev/null +++ b/main/mqtt_event_handler.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * @brief Event handler registered to receive MQTT events + * + * This function is called by the MQTT client event loop. + * + * @param handler_args user data registered to the event. + * @param base Event base for the handler(always MQTT Base in this example). + * @param event_id The id for the received event. + * @param event_data The data for the event, esp_mqtt_event_handle_t. + */ +void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/main/net_event_handler.c b/main/net_event_handler.c new file mode 100644 index 0000000..4d54a47 --- /dev/null +++ b/main/net_event_handler.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include "net_event_handler.h" + + +extern led_strip_handle_t debug_led; + +void network_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) { + ESP_ERROR_CHECK(led_strip_set_pixel(debug_led, 0, 0xFF, 0xFF, 0)); + ESP_ERROR_CHECK(led_strip_refresh(debug_led)); + esp_wifi_connect(); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + ESP_ERROR_CHECK(led_strip_set_pixel(debug_led, 0, 0xFF, 0xFF, 0)); + ESP_ERROR_CHECK(led_strip_refresh(debug_led)); + esp_wifi_connect(); + } 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("network", "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); + ESP_ERROR_CHECK(led_strip_set_pixel(debug_led, 0, 0, 0xFF, 0)); + ESP_ERROR_CHECK(led_strip_refresh(debug_led)); + vTaskDelay(pdMS_TO_TICKS(1000)); + ESP_ERROR_CHECK(led_strip_set_pixel(debug_led, 0, 0, 0, 0)); + ESP_ERROR_CHECK(led_strip_refresh(debug_led)); + } +} \ No newline at end of file diff --git a/main/net_event_handler.h b/main/net_event_handler.h new file mode 100644 index 0000000..c8ff1e6 --- /dev/null +++ b/main/net_event_handler.h @@ -0,0 +1,14 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +void network_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); + +#ifdef __cplusplus +} +#endif \ No newline at end of file