Compare commits

...

12 Commits

Author SHA1 Message Date
82fa194bd8 v1.1 2025-12-10 12:59:55 +00:00
e6e2622a95 new module 2025-12-09 11:48:31 +00:00
4820d9111e new release 2025-11-20 07:45:00 +00:00
96b2ab1f57 add ocpp 2025-08-24 11:17:48 +01:00
0d0dc5b129 evse_link feature 2025-08-05 16:55:11 +01:00
bd587a10c0 new buzzer component 2025-07-22 00:09:58 +01:00
84f106eee5 fix ade7758 2025-06-25 06:34:03 +01:00
a0b2e048d4 new meter 2025-06-14 11:46:10 +01:00
6f95c7ba59 new meter 2025-06-14 10:27:29 +01:00
4892718736 add net manager 2025-06-09 10:51:02 +01:00
66cc449143 add orno driver 2025-06-08 19:26:46 +01:00
12dfa85820 add orno driver 2025-06-08 18:35:32 +01:00
285 changed files with 16844 additions and 26756 deletions

View File

@@ -3,4 +3,4 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp32-evse) project(chargeflow)

View File

@@ -1,12 +1,12 @@
DEVICE_NAME=Custom EVSE DEVICE_NAME=ChargeFlow
#LEDs #LEDs
LED_CHARGING=n led_blue=n
LED_CHARGING_GPIO= led_blue_GPIO=
LED_ERROR=n led_red=n
LED_ERROR_GPIO= led_red_GPIO=
LED_STOP=n led_green=n
LED_STOP_GPIO= led_green_GPIO=
#Button #Button
BUTTON_WIFI_GPIO=32 BUTTON_WIFI_GPIO=32
@@ -34,33 +34,4 @@ AC_RELAY_GPIO=25
SOCKET_LOCK=n SOCKET_LOCK=n
SOCKET_LOCK_A_GPIO= SOCKET_LOCK_A_GPIO=
SOCKET_LOCK_B_GPIO= SOCKET_LOCK_B_GPIO=
SOCKET_LOCK_DETECTION_GPIO= SOCKET_LOCK_DETECTION_GPIO=
#Energy meter (none | cur | cur_vlt)
ENERGY_METER=none
ENERGY_METER_THREE_PHASES=n
#Energy meter internal (ENERGY_METER=cur | cur_vlt)
ENERGY_METER_L1_CUR_ADC_CHANNEL=
ENERGY_METER_L2_CUR_ADC_CHANNEL=
ENERGY_METER_L3_CUR_ADC_CHANNEL=
ENERGY_METER_CUR_SCALE=
#Energy meter internal (ENERGY_METER=cur_vlt)
ENERGY_METER_L1_VLT_ADC_CHANNEL=
ENERGY_METER_L2_VLT_ADC_CHANNEL=
ENERGY_METER_L3_VLT_ADC_CHANNEL=
ENERGY_METER_VLT_SCALE=
#Serial (SERIAL_X=none|uart|rs485)
SERIAL_1=none
SERIAL_1_NAME=UART 1
SERIAL_1_RXD_GPIO=
SERIAL_1_TXD_GPIO=
SERIAL_1_RTS_GPIO=
SERIAL_2=none
SERIAL_2_NAME=UART 2
SERIAL_2_RXD_GPIO=
SERIAL_2_TXD_GPIO=
SERIAL_2_RTS_GPIO=

View File

@@ -1,16 +1,16 @@
DEVICE_NAME=Plixin Evse DEVICE_NAME=ChargeFlow
#LEDs #LEDs
LED_CHARGING=y led_blue=y
LED_CHARGING_GPIO=14 led_blue_GPIO=14
LED_ERROR=y led_red=y
LED_ERROR_GPIO=13 led_red_GPIO=26
LED_STOP=y led_green=y
LED_STOP_GPIO=12 led_green_GPIO=12
#BUZZER #BUZZER
BUZZER=y BUZZER=y
BUZZER_GPIO=21 BUZZER_GPIO=27
#Button #Button
BUTTON_WIFI_GPIO=32 BUTTON_WIFI_GPIO=32
@@ -41,85 +41,4 @@ AC_RELAY_GPIO=25
SOCKET_LOCK=n SOCKET_LOCK=n
SOCKET_LOCK_A_GPIO= SOCKET_LOCK_A_GPIO=
SOCKET_LOCK_B_GPIO= SOCKET_LOCK_B_GPIO=
SOCKET_LOCK_DETECTION_GPIO= SOCKET_LOCK_DETECTION_GPIO=
#Energy meter (none | cur | cur_vlt)
ENERGY_METER=none
ENERGY_METER_THREE_PHASES=n
#Energy meter internal (ENERGY_METER=cur | cur_vlt)
ENERGY_METER_L1_CUR_ADC_CHANNEL=7
ENERGY_METER_L2_CUR_ADC_CHANNEL=
ENERGY_METER_L3_CUR_ADC_CHANNEL=
ENERGY_METER_CUR_SCALE=0.090909091
#Energy meter internal (ENERGY_METER=cur_vlt)
ENERGY_METER_L1_VLT_ADC_CHANNEL=
ENERGY_METER_L2_VLT_ADC_CHANNEL=
ENERGY_METER_L3_VLT_ADC_CHANNEL=
ENERGY_METER_VLT_SCALE=0.47
#AUX
AUX_IN_1=n
AUX_IN_1_NAME=
AUX_IN_1_GPIO=
AUX_IN_2=n
AUX_IN_2_NAME=
AUX_IN_2_GPIO=
AUX_IN_3=n
AUX_IN_3_NAME=
AUX_IN_3_GPIO=
AUX_IN_4=n
AUX_IN_4_NAME=
AUX_IN_4_GPIO=
AUX_OUT_1=n
AUX_OUT_1_NAME=
AUX_OUT_1_GPIO=
AUX_OUT_2=n
AUX_OUT_2_NAME=
AUX_OUT_2_GPIO=
AUX_OUT_3=n
AUX_OUT_3_NAME=
AUX_OUT_3_GPIO=
AUX_OUT_4=n
AUX_OUT_4_NAME=
AUX_OUT_4_GPIO=
AUX_AIN_1=n
AUX_AIN_1_NAME=
AUX_AIN_1_ADC_CHANNEL=
AUX_AIN_2=n
AUX_AIN_2_NAME=
AUX_AIN_2_ADC_CHANNEL=
#Serial (SERIAL_X=none|uart|rs485)
SERIAL_1=none
SERIAL_1_NAME=UART via USB
SERIAL_1_RXD_GPIO=
SERIAL_1_TXD_GPIO=
SERIAL_1_RTS_GPIO=
SERIAL_2=none
SERIAL_2_NAME=RS485
SERIAL_2_RXD_GPIO=
SERIAL_2_TXD_GPIO=
SERIAL_2_RTS_GPIO=
SERIAL_3=none
SERIAL_3_NAME=UART
SERIAL_3_RXD_GPIO=
SERIAL_3_TXD_GPIO=
SERIAL_3_RTS_GPIO=
#Onewire devices
ONEWIRE=n
ONEWIRE_GPIO=
ONEWIRE_TEMP_SENSOR=n

View File

@@ -1,12 +1,12 @@
DEVICE_NAME=ESP32-S2-DA EVSE DEVICE_NAME=ChargeFlow
#LEDs #LEDs
LED_CHARGING=y led_blue=y
LED_CHARGING_GPIO=36 led_blue_GPIO=36
LED_ERROR=y led_red=y
LED_ERROR_GPIO=37 led_red_GPIO=37
LED_STOP=y led_green=y
LED_STOP_GPIO=35 led_green_GPIO=35
#Button #Button
BUTTON_WIFI_GPIO=32 BUTTON_WIFI_GPIO=32
@@ -42,78 +42,3 @@ SOCKET_LOCK_MIN_BREAK_TIME=1000
RCM=n RCM=n
RCM_GPIO=41 RCM_GPIO=41
RCM_TEST_GPIO=26 RCM_TEST_GPIO=26
#Energy meter (none | cur | cur_vlt)
ENERGY_METER=cur_vlt
ENERGY_METER_THREE_PHASES=y
#Energy meter internal (ENERGY_METER=cur | cur_vlt)
ENERGY_METER_L1_CUR_ADC_CHANNEL=4
ENERGY_METER_L2_CUR_ADC_CHANNEL=5
ENERGY_METER_L3_CUR_ADC_CHANNEL=6
ENERGY_METER_CUR_SCALE=0.090909091
#Energy meter internal (ENERGY_METER=cur_vlt)
ENERGY_METER_L1_VLT_ADC_CHANNEL=7
ENERGY_METER_L2_VLT_ADC_CHANNEL=8
ENERGY_METER_L3_VLT_ADC_CHANNEL=9
ENERGY_METER_VLT_SCALE=0.47
#AUX
AUX_IN_1=n
AUX_IN_1_NAME=
AUX_IN_1_GPIO=
AUX_IN_2=n
AUX_IN_2_NAME=
AUX_IN_2_GPIO=
AUX_IN_3=n
AUX_IN_3_NAME=
AUX_IN_3_GPIO=
AUX_IN_4=n
AUX_IN_4_NAME=
AUX_IN_4_GPIO=
AUX_OUT_1=n
AUX_OUT_1_NAME=
AUX_OUT_1_GPIO=
AUX_OUT_2=n
AUX_OUT_2_NAME=
AUX_OUT_2_GPIO=
AUX_OUT_3=n
AUX_OUT_3_NAME=
AUX_OUT_3_GPIO=
AUX_OUT_4=n
AUX_OUT_4_NAME=
AUX_OUT_4_GPIO=
AUX_AIN_1=n
AUX_AIN_1_NAME=
AUX_AIN_1_ADC_CHANNEL=
AUX_AIN_2=n
AUX_AIN_2_NAME=
AUX_AIN_2_ADC_CHANNEL=
#Serial (SERIAL_X=none|uart|rs485)
SERIAL_1=none
SERIAL_1_NAME=UART
SERIAL_1_RXD_GPIO=
SERIAL_1_TXD_GPIO=
SERIAL_1_RTS_GPIO=
SERIAL_2=none
SERIAL_2_NAME=RS-485
SERIAL_2_RXD_GPIO=
SERIAL_2_TXD_GPIO=
SERIAL_2_RTS_GPIO=
#Onewire devices
ONEWIRE=n
ONEWIRE_GPIO=
ONEWIRE_TEMP_SENSOR=n

View File

@@ -1,14 +0,0 @@
set(srcs
"src/api.c"
"src/ota.c"
"src/json.c"
"src/timeout_utils.c"
"lib/cAT/src/cat.c")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include" "lib/cAT/src"
PRIV_INCLUDE_DIRS "src"
PRIV_REQUIRES nvs_flash app_update json driver esp_http_client esp_netif esp_wifi esp_timer esp_hw_support
REQUIRES network config evse peripherals protocols meter_orno ocpp)
set_source_files_properties(lib/cAT/src/cat.c PROPERTIES COMPILE_FLAGS -Wno-maybe-uninitialized)

View File

@@ -1,6 +0,0 @@
#ifndef API_H_
#define API_H_
void api_init(void);
#endif /* API_H_ */

View File

@@ -1,43 +0,0 @@
#ifndef JSON_UTILS_H
#define JSON_UTILS_H
#include "esp_err.h"
#include "cJSON.h"
cJSON* json_get_evse_config(void);
esp_err_t json_set_evse_config(cJSON* root);
cJSON* json_get_wifi_config(void);
esp_err_t json_set_wifi_config(cJSON* root, bool timeout);
cJSON* json_get_wifi_scan(void);
cJSON* json_get_mqtt_config(void);
esp_err_t json_set_mqtt_config(cJSON* root);
//cJSON* json_get_serial_config(void);
//esp_err_t json_set_serial_config(cJSON* root);
cJSON* json_get_modbus_config(void);
esp_err_t json_set_modbus_config(cJSON* root);
//cJSON* json_get_script_config(void);
//esp_err_t json_set_script_config(cJSON* root);
cJSON* json_get_time_config(void);
esp_err_t json_set_time_config(cJSON* root);
cJSON* json_get_state(void);
cJSON* json_get_info(void);
cJSON* json_get_board_config(void);
#endif /* JSON_UTILS_H */

View File

@@ -1,15 +0,0 @@
#ifndef OTA_H_
#define OTA_H_
#include <stdbool.h>
#include "esp_err.h"
#define OTA_VERSION_URL "https://dzurikmiroslav.github.io/esp32-evse/firmware/version.txt"
#define OTA_FIRMWARE_URL "https://dzurikmiroslav.github.io/esp32-evse/firmware/"
esp_err_t ota_get_available_version(char* version);
bool ota_is_newer_version(const char* actual, const char* available);
#endif /* OTA_H_ */

View File

@@ -1,23 +0,0 @@
#ifndef TIMEOUT_UTILS_H
#define TIMEOUT_UTILS_H
#include <stdbool.h>
#include "esp_err.h"
/**
* @brief Restart in 5 seconds
*
*/
void timeout_restart(void);
/**
* @brief Set WiFi config in 1 second
*
* @param enabled
* @param ssid
* @param password
* @return esp_err_t
*/
esp_err_t timeout_wifi_set_config(bool enabled, const char* ssid, const char* password);
#endif /* TIMEOUT_UTILS_H */

View File

@@ -1,493 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
#
# clang-format configuration file. Intended for clang-format >= 4.
#
# For more information, see:
#
# Documentation/process/clang-format.rst
# https://clang.llvm.org/docs/ClangFormat.html
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
#
---
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
#AlignEscapedNewlines: Left # Unknown to clang-format-4.0
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
#AfterExternBlock: false # Unknown to clang-format-5.0
BeforeCatch: false
BeforeElse: false
IndentBraces: false
#SplitEmptyFunction: true # Unknown to clang-format-4.0
#SplitEmptyRecord: true # Unknown to clang-format-4.0
#SplitEmptyNamespace: true # Unknown to clang-format-4.0
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 160
CommentPragmas: '^ IWYU pragma:'
#CompactNamespaces: false # Unknown to clang-format-4.0
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
#FixNamespaceComments: false # Unknown to clang-format-4.0
# Taken from:
# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \
# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \
# | sort | uniq
ForEachMacros:
- 'apei_estatus_for_each_section'
- 'ata_for_each_dev'
- 'ata_for_each_link'
- '__ata_qc_for_each'
- 'ata_qc_for_each'
- 'ata_qc_for_each_raw'
- 'ata_qc_for_each_with_internal'
- 'ax25_for_each'
- 'ax25_uid_for_each'
- '__bio_for_each_bvec'
- 'bio_for_each_bvec'
- 'bio_for_each_integrity_vec'
- '__bio_for_each_segment'
- 'bio_for_each_segment'
- 'bio_for_each_segment_all'
- 'bio_list_for_each'
- 'bip_for_each_vec'
- 'blkg_for_each_descendant_post'
- 'blkg_for_each_descendant_pre'
- 'blk_queue_for_each_rl'
- 'bond_for_each_slave'
- 'bond_for_each_slave_rcu'
- 'bpf_for_each_spilled_reg'
- 'btree_for_each_safe128'
- 'btree_for_each_safe32'
- 'btree_for_each_safe64'
- 'btree_for_each_safel'
- 'card_for_each_dev'
- 'cgroup_taskset_for_each'
- 'cgroup_taskset_for_each_leader'
- 'cpufreq_for_each_entry'
- 'cpufreq_for_each_entry_idx'
- 'cpufreq_for_each_valid_entry'
- 'cpufreq_for_each_valid_entry_idx'
- 'css_for_each_child'
- 'css_for_each_descendant_post'
- 'css_for_each_descendant_pre'
- 'device_for_each_child_node'
- 'drm_atomic_crtc_for_each_plane'
- 'drm_atomic_crtc_state_for_each_plane'
- 'drm_atomic_crtc_state_for_each_plane_state'
- 'drm_atomic_for_each_plane_damage'
- 'drm_connector_for_each_possible_encoder'
- 'drm_for_each_connector_iter'
- 'drm_for_each_crtc'
- 'drm_for_each_encoder'
- 'drm_for_each_encoder_mask'
- 'drm_for_each_fb'
- 'drm_for_each_legacy_plane'
- 'drm_for_each_plane'
- 'drm_for_each_plane_mask'
- 'drm_for_each_privobj'
- 'drm_mm_for_each_hole'
- 'drm_mm_for_each_node'
- 'drm_mm_for_each_node_in_range'
- 'drm_mm_for_each_node_safe'
- 'flow_action_for_each'
- 'for_each_active_drhd_unit'
- 'for_each_active_iommu'
- 'for_each_available_child_of_node'
- 'for_each_bio'
- 'for_each_board_func_rsrc'
- 'for_each_bvec'
- 'for_each_card_components'
- 'for_each_card_links'
- 'for_each_card_links_safe'
- 'for_each_card_prelinks'
- 'for_each_card_rtds'
- 'for_each_card_rtds_safe'
- 'for_each_cgroup_storage_type'
- 'for_each_child_of_node'
- 'for_each_clear_bit'
- 'for_each_clear_bit_from'
- 'for_each_cmsghdr'
- 'for_each_compatible_node'
- 'for_each_component_dais'
- 'for_each_component_dais_safe'
- 'for_each_comp_order'
- 'for_each_console'
- 'for_each_cpu'
- 'for_each_cpu_and'
- 'for_each_cpu_not'
- 'for_each_cpu_wrap'
- 'for_each_dev_addr'
- 'for_each_dma_cap_mask'
- 'for_each_dpcm_be'
- 'for_each_dpcm_be_rollback'
- 'for_each_dpcm_be_safe'
- 'for_each_dpcm_fe'
- 'for_each_drhd_unit'
- 'for_each_dss_dev'
- 'for_each_efi_memory_desc'
- 'for_each_efi_memory_desc_in_map'
- 'for_each_element'
- 'for_each_element_extid'
- 'for_each_element_id'
- 'for_each_endpoint_of_node'
- 'for_each_evictable_lru'
- 'for_each_fib6_node_rt_rcu'
- 'for_each_fib6_walker_rt'
- 'for_each_free_mem_range'
- 'for_each_free_mem_range_reverse'
- 'for_each_func_rsrc'
- 'for_each_hstate'
- 'for_each_if'
- 'for_each_iommu'
- 'for_each_ip_tunnel_rcu'
- 'for_each_irq_nr'
- 'for_each_link_codecs'
- 'for_each_lru'
- 'for_each_matching_node'
- 'for_each_matching_node_and_match'
- 'for_each_memblock'
- 'for_each_memblock_type'
- 'for_each_memcg_cache_index'
- 'for_each_mem_pfn_range'
- 'for_each_mem_range'
- 'for_each_mem_range_rev'
- 'for_each_migratetype_order'
- 'for_each_msi_entry'
- 'for_each_msi_entry_safe'
- 'for_each_net'
- 'for_each_netdev'
- 'for_each_netdev_continue'
- 'for_each_netdev_continue_rcu'
- 'for_each_netdev_feature'
- 'for_each_netdev_in_bond_rcu'
- 'for_each_netdev_rcu'
- 'for_each_netdev_reverse'
- 'for_each_netdev_safe'
- 'for_each_net_rcu'
- 'for_each_new_connector_in_state'
- 'for_each_new_crtc_in_state'
- 'for_each_new_mst_mgr_in_state'
- 'for_each_new_plane_in_state'
- 'for_each_new_private_obj_in_state'
- 'for_each_node'
- 'for_each_node_by_name'
- 'for_each_node_by_type'
- 'for_each_node_mask'
- 'for_each_node_state'
- 'for_each_node_with_cpus'
- 'for_each_node_with_property'
- 'for_each_of_allnodes'
- 'for_each_of_allnodes_from'
- 'for_each_of_cpu_node'
- 'for_each_of_pci_range'
- 'for_each_old_connector_in_state'
- 'for_each_old_crtc_in_state'
- 'for_each_old_mst_mgr_in_state'
- 'for_each_oldnew_connector_in_state'
- 'for_each_oldnew_crtc_in_state'
- 'for_each_oldnew_mst_mgr_in_state'
- 'for_each_oldnew_plane_in_state'
- 'for_each_oldnew_plane_in_state_reverse'
- 'for_each_oldnew_private_obj_in_state'
- 'for_each_old_plane_in_state'
- 'for_each_old_private_obj_in_state'
- 'for_each_online_cpu'
- 'for_each_online_node'
- 'for_each_online_pgdat'
- 'for_each_pci_bridge'
- 'for_each_pci_dev'
- 'for_each_pci_msi_entry'
- 'for_each_populated_zone'
- 'for_each_possible_cpu'
- 'for_each_present_cpu'
- 'for_each_prime_number'
- 'for_each_prime_number_from'
- 'for_each_process'
- 'for_each_process_thread'
- 'for_each_property_of_node'
- 'for_each_registered_fb'
- 'for_each_reserved_mem_region'
- 'for_each_rtd_codec_dai'
- 'for_each_rtd_codec_dai_rollback'
- 'for_each_rtdcom'
- 'for_each_rtdcom_safe'
- 'for_each_set_bit'
- 'for_each_set_bit_from'
- 'for_each_sg'
- 'for_each_sg_dma_page'
- 'for_each_sg_page'
- 'for_each_sibling_event'
- 'for_each_subelement'
- 'for_each_subelement_extid'
- 'for_each_subelement_id'
- '__for_each_thread'
- 'for_each_thread'
- 'for_each_zone'
- 'for_each_zone_zonelist'
- 'for_each_zone_zonelist_nodemask'
- 'fwnode_for_each_available_child_node'
- 'fwnode_for_each_child_node'
- 'fwnode_graph_for_each_endpoint'
- 'gadget_for_each_ep'
- 'genradix_for_each'
- 'genradix_for_each_from'
- 'hash_for_each'
- 'hash_for_each_possible'
- 'hash_for_each_possible_rcu'
- 'hash_for_each_possible_rcu_notrace'
- 'hash_for_each_possible_safe'
- 'hash_for_each_rcu'
- 'hash_for_each_safe'
- 'hctx_for_each_ctx'
- 'hlist_bl_for_each_entry'
- 'hlist_bl_for_each_entry_rcu'
- 'hlist_bl_for_each_entry_safe'
- 'hlist_for_each'
- 'hlist_for_each_entry'
- 'hlist_for_each_entry_continue'
- 'hlist_for_each_entry_continue_rcu'
- 'hlist_for_each_entry_continue_rcu_bh'
- 'hlist_for_each_entry_from'
- 'hlist_for_each_entry_from_rcu'
- 'hlist_for_each_entry_rcu'
- 'hlist_for_each_entry_rcu_bh'
- 'hlist_for_each_entry_rcu_notrace'
- 'hlist_for_each_entry_safe'
- '__hlist_for_each_rcu'
- 'hlist_for_each_safe'
- 'hlist_nulls_for_each_entry'
- 'hlist_nulls_for_each_entry_from'
- 'hlist_nulls_for_each_entry_rcu'
- 'hlist_nulls_for_each_entry_safe'
- 'i3c_bus_for_each_i2cdev'
- 'i3c_bus_for_each_i3cdev'
- 'ide_host_for_each_port'
- 'ide_port_for_each_dev'
- 'ide_port_for_each_present_dev'
- 'idr_for_each_entry'
- 'idr_for_each_entry_continue'
- 'idr_for_each_entry_ul'
- 'inet_bind_bucket_for_each'
- 'inet_lhash2_for_each_icsk_rcu'
- 'key_for_each'
- 'key_for_each_safe'
- 'klp_for_each_func'
- 'klp_for_each_func_safe'
- 'klp_for_each_func_static'
- 'klp_for_each_object'
- 'klp_for_each_object_safe'
- 'klp_for_each_object_static'
- 'kvm_for_each_memslot'
- 'kvm_for_each_vcpu'
- 'list_for_each'
- 'list_for_each_codec'
- 'list_for_each_codec_safe'
- 'list_for_each_entry'
- 'list_for_each_entry_continue'
- 'list_for_each_entry_continue_rcu'
- 'list_for_each_entry_continue_reverse'
- 'list_for_each_entry_from'
- 'list_for_each_entry_from_rcu'
- 'list_for_each_entry_from_reverse'
- 'list_for_each_entry_lockless'
- 'list_for_each_entry_rcu'
- 'list_for_each_entry_reverse'
- 'list_for_each_entry_safe'
- 'list_for_each_entry_safe_continue'
- 'list_for_each_entry_safe_from'
- 'list_for_each_entry_safe_reverse'
- 'list_for_each_prev'
- 'list_for_each_prev_safe'
- 'list_for_each_safe'
- 'llist_for_each'
- 'llist_for_each_entry'
- 'llist_for_each_entry_safe'
- 'llist_for_each_safe'
- 'media_device_for_each_entity'
- 'media_device_for_each_intf'
- 'media_device_for_each_link'
- 'media_device_for_each_pad'
- 'mp_bvec_for_each_page'
- 'mp_bvec_for_each_segment'
- 'nanddev_io_for_each_page'
- 'netdev_for_each_lower_dev'
- 'netdev_for_each_lower_private'
- 'netdev_for_each_lower_private_rcu'
- 'netdev_for_each_mc_addr'
- 'netdev_for_each_uc_addr'
- 'netdev_for_each_upper_dev_rcu'
- 'netdev_hw_addr_list_for_each'
- 'nft_rule_for_each_expr'
- 'nla_for_each_attr'
- 'nla_for_each_nested'
- 'nlmsg_for_each_attr'
- 'nlmsg_for_each_msg'
- 'nr_neigh_for_each'
- 'nr_neigh_for_each_safe'
- 'nr_node_for_each'
- 'nr_node_for_each_safe'
- 'of_for_each_phandle'
- 'of_property_for_each_string'
- 'of_property_for_each_u32'
- 'pci_bus_for_each_resource'
- 'ping_portaddr_for_each_entry'
- 'plist_for_each'
- 'plist_for_each_continue'
- 'plist_for_each_entry'
- 'plist_for_each_entry_continue'
- 'plist_for_each_entry_safe'
- 'plist_for_each_safe'
- 'pnp_for_each_card'
- 'pnp_for_each_dev'
- 'protocol_for_each_card'
- 'protocol_for_each_dev'
- 'queue_for_each_hw_ctx'
- 'radix_tree_for_each_slot'
- 'radix_tree_for_each_tagged'
- 'rbtree_postorder_for_each_entry_safe'
- 'rdma_for_each_port'
- 'resource_list_for_each_entry'
- 'resource_list_for_each_entry_safe'
- 'rhl_for_each_entry_rcu'
- 'rhl_for_each_rcu'
- 'rht_for_each'
- 'rht_for_each_from'
- 'rht_for_each_entry'
- 'rht_for_each_entry_from'
- 'rht_for_each_entry_rcu'
- 'rht_for_each_entry_rcu_from'
- 'rht_for_each_entry_safe'
- 'rht_for_each_rcu'
- 'rht_for_each_rcu_from'
- '__rq_for_each_bio'
- 'rq_for_each_bvec'
- 'rq_for_each_segment'
- 'scsi_for_each_prot_sg'
- 'scsi_for_each_sg'
- 'sctp_for_each_hentry'
- 'sctp_skb_for_each'
- 'shdma_for_each_chan'
- '__shost_for_each_device'
- 'shost_for_each_device'
- 'sk_for_each'
- 'sk_for_each_bound'
- 'sk_for_each_entry_offset_rcu'
- 'sk_for_each_from'
- 'sk_for_each_rcu'
- 'sk_for_each_safe'
- 'sk_nulls_for_each'
- 'sk_nulls_for_each_from'
- 'sk_nulls_for_each_rcu'
- 'snd_array_for_each'
- 'snd_pcm_group_for_each_entry'
- 'snd_soc_dapm_widget_for_each_path'
- 'snd_soc_dapm_widget_for_each_path_safe'
- 'snd_soc_dapm_widget_for_each_sink_path'
- 'snd_soc_dapm_widget_for_each_source_path'
- 'tb_property_for_each'
- 'tcf_exts_for_each_action'
- 'udp_portaddr_for_each_entry'
- 'udp_portaddr_for_each_entry_rcu'
- 'usb_hub_for_each_child'
- 'v4l2_device_for_each_subdev'
- 'v4l2_m2m_for_each_dst_buf'
- 'v4l2_m2m_for_each_dst_buf_safe'
- 'v4l2_m2m_for_each_src_buf'
- 'v4l2_m2m_for_each_src_buf_safe'
- 'virtio_device_for_each_vq'
- 'xa_for_each'
- 'xa_for_each_marked'
- 'xa_for_each_start'
- 'xas_for_each'
- 'xas_for_each_conflict'
- 'xas_for_each_marked'
- 'zorro_for_each_dev'
#IncludeBlocks: Preserve # Unknown to clang-format-5.0
IncludeCategories:
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
#IndentPPDirectives: None # Unknown to clang-format-5.0
IndentWidth: 8
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
# Taken from git's rules
#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakString: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SortIncludes: false
#SortUsingDeclarations: false # Unknown to clang-format-4.0
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0
#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0
SpaceBeforeParens: ControlStatements
#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp03
TabWidth: 8
UseTab: Never
...

View File

@@ -1,17 +0,0 @@
.vscode
lib
bin
dist
build
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps

View File

@@ -1,11 +0,0 @@
language: c
compiler:
- gcc
before_script:
- mkdir build
- cd build
- cmake ..
script: make && make test

View File

@@ -1,148 +0,0 @@
cmake_minimum_required( VERSION 3.0 )
project( libcat LANGUAGES C )
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
enable_testing( )
include_directories( ${PROJECT_SOURCE_DIR}/src )
file( GLOB SRC_FILES src/*.c )
add_library( cat SHARED ${SRC_FILES} )
set_target_properties( cat PROPERTIES VERSION 0.11.1 SOVERSION 1 )
target_compile_options( cat PRIVATE -Werror -Wall -Wextra -pedantic )
install( TARGETS cat DESTINATION lib )
install( FILES src/cat.h DESTINATION include/cat )
add_executable( demo example/demo.c )
target_link_libraries( demo cat )
add_executable( basic example/basic.c )
target_link_libraries( basic cat )
add_executable( unsolicited example/unsolicited.c )
target_link_libraries( unsolicited cat )
add_executable( test_parse tests/test_parse.c )
target_link_libraries( test_parse cat )
add_test( test_parse ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_parse )
add_executable( test_run tests/test_run.c )
target_link_libraries( test_run cat )
add_test( test_run ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_run )
add_executable( test_read tests/test_read.c )
target_link_libraries( test_read cat )
add_test( test_read ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_read )
add_executable( test_write tests/test_write.c )
target_link_libraries( test_write cat )
add_test( test_write ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_write )
add_executable( test_write_parse tests/test_write_parse.c )
target_link_libraries( test_write_parse cat )
add_test( test_write_parse ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_write_parse )
add_executable( test_write_int_range tests/test_write_int_range.c )
target_link_libraries( test_write_int_range cat )
add_test( test_write_int_range ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_write_int_range )
add_executable( test_write_uint_range tests/test_write_uint_range.c )
target_link_libraries( test_write_uint_range cat )
add_test( test_write_uint_range ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_write_uint_range )
add_executable( test_write_hex_range tests/test_write_hex_range.c )
target_link_libraries( test_write_hex_range cat )
add_test( test_write_hex_range ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_write_hex_range )
add_executable( test_write_hex_buffer tests/test_write_hex_buffer.c )
target_link_libraries( test_write_hex_buffer cat )
add_test( test_write_hex_buffer ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_write_hex_buffer )
add_executable( test_write_string_buffer tests/test_write_string_buffer.c )
target_link_libraries( test_write_string_buffer cat )
add_test( test_write_string_buffer ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_write_string_buffer )
add_executable( test_read_args tests/test_read_args.c )
target_link_libraries( test_read_args cat )
add_test( test_read_args ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_read_args )
add_executable( test_test tests/test_test.c )
target_link_libraries( test_test cat )
add_test( test_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_test )
add_executable( test_test_args tests/test_test_args.c )
target_link_libraries( test_test_args cat )
add_test( test_test_args ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_test_args )
add_executable( test_mutex tests/test_mutex.c )
target_link_libraries( test_mutex cat )
add_test( test_mutex ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_mutex )
add_executable( test_unsolicited_read tests/test_unsolicited_read.c )
target_link_libraries( test_unsolicited_read cat )
add_test( test_unsolicited_read ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_unsolicited_read )
add_executable( test_unsolicited_read_stress tests/test_unsolicited_read_stress.c )
target_link_libraries( test_unsolicited_read_stress cat )
add_test( test_unsolicited_read_stress ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_unsolicited_read_stress )
add_executable( test_unsolicited_read_buffer tests/test_unsolicited_read_buffer.c ${SRC_FILES})
set_target_properties( test_unsolicited_read_buffer PROPERTIES COMPILE_DEFINITIONS "CAT_UNSOLICITED_CMD_BUFFER_SIZE=2" )
add_test( test_unsolicited_read_buffer ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_unsolicited_read_buffer )
add_executable( test_hold_state tests/test_hold_state.c )
target_link_libraries( test_hold_state cat )
add_test( test_hold_state ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_hold_state )
add_executable( test_return_read tests/test_return_read.c )
target_link_libraries( test_return_read cat )
add_test( test_return_read ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_return_read )
add_executable( test_return_write tests/test_return_write.c )
target_link_libraries( test_return_write cat )
add_test( test_return_write ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_return_write )
add_executable( test_unsolicited_test tests/test_unsolicited_test.c )
target_link_libraries( test_unsolicited_test cat )
add_test( test_unsolicited_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_unsolicited_test )
add_executable( test_return_test tests/test_return_test.c )
target_link_libraries( test_return_test cat )
add_test( test_return_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_return_test )
add_executable( test_return_run tests/test_return_run.c )
target_link_libraries( test_return_run cat )
add_test( test_return_run ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_return_run )
add_executable( test_test_only tests/test_test_only.c )
target_link_libraries( test_test_only cat )
add_test( test_test_only ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_test_only )
add_executable( test_search_cmd tests/test_search_cmd.c )
target_link_libraries( test_search_cmd cat )
add_test( test_search_cmd ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_search_cmd )
add_executable( test_order tests/test_order.c )
target_link_libraries( test_order cat )
add_test( test_order ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_order )
add_executable( test_cmd_list tests/test_cmd_list.c )
target_link_libraries( test_cmd_list cat )
add_test( test_cmd_list ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_cmd_list )
add_executable( test_var_access tests/test_var_access.c )
target_link_libraries( test_var_access cat )
add_test( test_var_access ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_var_access )
add_executable( test_shortcuts tests/test_shortcuts.c )
target_link_libraries( test_shortcuts cat )
add_test( test_shortcuts ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_shortcuts )
add_custom_target( check COMMAND ${CMAKE_CTEST_COMMAND} --verbose )
add_custom_target( cleanall COMMAND rm -rf Makefile CMakeCache.txt CMakeFiles/ bin/ lib/ cmake_install.cmake CTestTestfile.cmake Testing/ )
add_custom_target( uninstall COMMAND xargs rm < install_manifest.txt )

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,211 +0,0 @@
[![Build Status](https://travis-ci.org/marcinbor85/cat.svg?branch=master)](https://travis-ci.org/marcinbor85/cat)
# libcat (cAT)
Plain C library for parsing AT commands for use in host devices.
## Features
* blazing fast, non-blocking, robust implementation
* 100% static implementation (without any dynamic memory allocation)
* very small footprint (both RAM and ROM)
* support for READ, WRITE, TEST and RUN type commands
* commands shortcuts (auto select best command candidate)
* single request - multiple responses
* unsolicited read/test command support
* hold state for delayed responses for time-consuming tasks
* high-level memory variables mapping arguments parsing
* variables accessors (read and write, read only, write only)
* automatic arguments types validating
* automatic format test responses for commands with variables
* CRLF and LF compatible
* case-insensitive
* dedicated for embedded systems
* object-oriented architecture
* separated interface for low-level layer
* fully asynchronous input/output operations
* multiplatform and portable
* asynchronous api with event callbacks
* print registered commands list feature
* only two source files
* wide unit tests
## Build
Build and install:
```sh
cmake .
make
make test
sudo make install
```
## Example basic demo posibilities
```console
AT+PRINT=? # TEST command
+PRINT=<X:UINT8[RW]>,<Y:UINT8[RW]>,<MESSAGE:STRING[RW]> # Automatic response
Printing something special at (X,Y). # Automatic response
OK # Automatic acknowledge
AT+PRINT? # READ command
+PRINT=0,0,"" # Automatic response
OK # Automatic acknowledge
AT+PRINT=xyz,-2 # WRITE command
ERROR # Automatic acknowledge
AT+PRINT=1,2,"test" # WRITE command
OK # Automatic acknowledge
AT+PRINT # RUN command
some printing at (1,2) with text "test" # Manual response
OK # Automatic acknowledge
```
## Example unsolicited demo posibilities
```console
AT+START=? # TEST command
+START=<MODE:UINT32[WO]> # Automatic response
Start scanning after write (0 - wifi, 1 - bluetooth). # Automatic response
OK # Automatic acknowledge
AT+START=0 # WRITE command
+SCAN=-10,"wifi1" # Unsolicited read response
+SCAN=-50,"wifi2" # Unsolicited read response
+SCAN=-20,"wifi3" # Unsolicited read response
OK # Unsolicited acknowledge
AT+START=1 # WRITE command
+SCAN=-20,"bluetooth1" # Unsolicited read response
OK # Unsolicited acknowledge
AT+SCAN=? # TEST command
+SCAN=<RSSI:INT32[RO]>,<SSID:STRING[RO]> # Automatic response
Scan result record. # Automatic response
OK # Automatic acknowledge
```
## Usage
Define High-Level variables:
```c
static uint8_t x;
static uint8_t y;
static char msg[32];
static struct cat_variable go_vars[] = {
{
.type = CAT_VAR_UINT_DEC, /* unsigned int variable */
.data = &x,
.data_size = sizeof(x),
.write = x_write,
.name = "X",
.access = CAT_VAR_ACCESS_READ_WRITE,
},
{
.type = CAT_VAR_UINT_DEC, /* unsigned int variable */
.data = &y,
.data_size = sizeof(y),
.write = y_write,
.access = CAT_VAR_ACCESS_READ_WRITE,
},
{
.type = CAT_VAR_BUF_STRING, /* string variable */
.data = msg,
.data_size = sizeof(msg),
.write = msg_write,
.access = CAT_VAR_ACCESS_READ_WRITE,
}
};
```
Define AT commands descriptor:
```c
static struct cat_command cmds[] = {
{
.name = "TEST",
.read = test_read, /* read handler for ATTEST? command */
.write = test_write, /* write handler for ATTEST={val} command */
.run = test_run /* run handler for ATTEST command */
},
{
.name = "+NUM",
.write = num_write, /* write handler for AT+NUM={val} command */
.read = num_read /* read handler for AT+NUM? command */
},
{
.name = "+GO",
.write = go_write, /* write handler for AT+GO={x},{y},{msg} command */
.var = go_vars, /* attach variables to command */
.var_num = sizeof(go_vars) / sizeof(go_vars[0]),
.need_all_vars = true
},
{
.name = "RESTART",
.run = restart_run /* run handler for ATRESTART command */
}
};
```
Define AT command parser descriptor:
```c
static char working_buf[128]; /* working buffer, must be declared manually */
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = working_buf,
.buf_size = sizeof(working_buf),
};
```
Define IO low-level layer interface:
```c
static int write_char(char ch)
{
putc(ch, stdout);
return 1;
}
static int read_char(char *ch)
{
*ch = getch();
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
```
Initialize AT command parser and run:
```c
struct cat_object at; /* at command parser object */
cat_init(&at, &desc, &iface, NULL); /* initialize at command parser object */
while (1) {
cat_service(&at) /* periodically call at command parser service */
... /* other stuff, running in main loop */
}
```

View File

@@ -1,70 +0,0 @@
TODO:
- differentation of variables for read and write commands
- help command (printing all commands posibilities with descriptions)
- documentation updated (buffer sized, return enum types, write variable nums, buf size hints)
- helper setters and getters for variables
0.11.0
* optional context in commands and data_getter in variables
* optional extended read and write variable handler with parent command
0.10.1
* single working buffer insteads of two separated for atcmd and unsolicited events
0.10.0
* separate atcmd and unsolicited event state machines
0.9.0
* variables accessors
0.8.1
* fix commands shourtcuts
0.8.0
* print commands list feature
0.7.1
* more flexible command group initialization (pointers)
0.7.0
* function for checking unsolicited buffer
* some refactoring regarding unsolicited buffer
* helper function cat_search_variable_by_name added
* commands group with searching feature added
* disable feature for commands and groups added
0.6.0
* async non blocking io->write
* async unsolicited read/test event (injected at idle and hold state)
* hold commands parse processing feature (for delayed unsolicited read responses)
* behavior differentation by callbacks return values
* only-test flag in commands descriptor (for fast disable read/write/run)
* helper function for searching command by name
* multiple responses triggered by single request pattern support
* generic mutex descriptor interface
* return value states enum types
0.5.3
* function for checking internal busy state added
0.5.2
* small fix in auto description response
0.5.1
* description field in command added (used in auto help message)
0.5.0
* test command parser with auto help messages
* output string end-line encoding same as input string
* very basic demo example application added
0.4.0
* high level parsing for command arguments
* int, uint, hex, hexbuf, string argument types
* validating arguments values range
0.3.0
* independent buffers (parsing buffer and state buffer)
0.2.0
* passing command struct instead of command name in command handlers

View File

@@ -1,161 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
/* variables assigned to print command */
static uint8_t x;
static uint8_t y;
static char message[16];
/* helper variable used to exit demo code */
static bool quit_flag;
/* run command handler with application dependent login print code */
static int print_run(const struct cat_command *cmd)
{
printf("some printing at (%d,%d) with text \"%s\"\n", x, y, message);
return 0;
}
/* run command handler attached to HELP command for printing commands list */
static int print_cmd_list(const struct cat_command *cmd)
{
return CAT_RETURN_STATE_PRINT_CMD_LIST_OK;
}
/* run command handler with custom exit mechanism */
static int quit_run(const struct cat_command *cmd)
{
quit_flag = true;
return 0;
}
/* declaring print variables array */
static struct cat_variable print_vars[] = {
{
.type = CAT_VAR_UINT_DEC,
.data = &x,
.data_size = sizeof(x),
.name = "X",
.access = CAT_VAR_ACCESS_READ_WRITE,
},
{
.type = CAT_VAR_UINT_DEC,
.data = &y,
.data_size = sizeof(y),
.name = "Y",
.access = CAT_VAR_ACCESS_READ_WRITE,
},
{
.type = CAT_VAR_BUF_STRING,
.data = message,
.data_size = sizeof(message),
.name = "MESSAGE",
.access = CAT_VAR_ACCESS_READ_WRITE,
}
};
/* declaring commands array */
static struct cat_command cmds[] = {
{
.name = "+PRINT",
.description = "Printing something special at (X,Y).",
.run = print_run,
.var = print_vars,
.var_num = sizeof(print_vars) / sizeof(print_vars[0]),
.need_all_vars = true
},
{
.name = "#HELP",
.run = print_cmd_list,
},
{
.name = "#QUIT",
.run = quit_run
},
};
/* working buffer */
static char buf[128];
/* declaring parser descriptor */
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf)
};
/* custom target dependent input output handlers */
static int write_char(char ch)
{
putc(ch, stdout);
return 1;
}
static int read_char(char *ch)
{
*ch = getc(stdin);
return 1;
}
/* declaring input output interface descriptor for parser */
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
int main(int argc, char **argv)
{
struct cat_object at;
/* initializing */
cat_init(&at, &desc, &iface, NULL);
/* main loop with exit code conditions */
while ((cat_service(&at) != 0) && (quit_flag == 0)) {};
/* goodbye message */
printf("Bye!\n");
return 0;
}

View File

@@ -1,260 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static int32_t speed;
static uint16_t adr;
static uint8_t x;
static uint8_t y;
static uint8_t bytes_buf[4];
static char msg[16];
static bool quit_flag;
static int x_write(const struct cat_variable *var, size_t write_size)
{
printf("x variable updated internally to: %u\n", x);
return 0;
}
static int y_write(const struct cat_variable *var, size_t write_size)
{
printf("y variable updated internally to: %u\n", y);
return 0;
}
static int msg_write(const struct cat_variable *var, size_t write_size)
{
printf("msg variable updated %lu bytes internally to: <%s>\n", write_size, msg);
return 0;
}
static int speed_write(const struct cat_variable *var, size_t write_size)
{
printf("speed variable updated internally to: %d\n", speed);
return 0;
}
static int adr_write(const struct cat_variable *var, size_t write_size)
{
printf("adr variable updated internally to: 0x%04X\n", adr);
return 0;
}
static int bytesbuf_write(const struct cat_variable *var, size_t write_size)
{
int i = 0;
printf("bytes_buf variable updated %lu bytes internally to: ", write_size);
for (i = 0; i < sizeof(bytes_buf); i++)
printf("%02X", bytes_buf[i]);
printf("\n");
return 0;
}
static int go_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
int i = 0;
printf("<%s>: x=%d y=%d msg=%s @ speed=%d\n",
cmd->name,
*(uint8_t*)(cmd->var[0].data),
*(uint8_t*)(cmd->var[1].data),
msg,
speed
);
printf("<bytes>: ");
for (i = 0; i < sizeof(bytes_buf); i++)
printf("%02X", bytes_buf[i]);
printf("\n");
return 0;
}
static int set_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
printf("<%s>: SET SPEED TO = %d\n",
cmd->name,
speed
);
return 0;
}
static int set_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
return 0;
}
static int test_run(const struct cat_command *cmd)
{
printf("TEST: <%s>", cmd->name);
return 0;
}
static int quit_run(const struct cat_command *cmd)
{
printf("QUIT: <%s>", cmd->name);
quit_flag = true;
return 0;
}
static int print_cmd_list(const struct cat_command *cmd)
{
return CAT_RETURN_STATE_PRINT_CMD_LIST_OK;
}
static struct cat_variable go_vars[] = {
{
.type = CAT_VAR_UINT_DEC,
.data = &x,
.data_size = sizeof(x),
.write = x_write,
.name = "x"
},
{
.type = CAT_VAR_UINT_DEC,
.data = &y,
.data_size = sizeof(y),
.write = y_write,
.name = "y"
},
{
.type = CAT_VAR_BUF_STRING,
.data = msg,
.data_size = sizeof(msg),
.write = msg_write,
.name = "msg"
}
};
static struct cat_variable set_vars[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &speed,
.data_size = sizeof(speed),
.write = speed_write,
.name = "speed"
},
{
.type = CAT_VAR_NUM_HEX,
.data = &adr,
.data_size = sizeof(adr),
.write = adr_write,
.name = "address"
},
{
.type = CAT_VAR_BUF_HEX,
.data = &bytes_buf,
.data_size = sizeof(bytes_buf),
.write = bytesbuf_write,
.name = "buffer"
}
};
static struct cat_command cmds[] = {
{
.name = "+GO",
.write = go_write,
.var = go_vars,
.var_num = sizeof(go_vars) / sizeof(go_vars[0]),
.need_all_vars = true
},
{
.name = "+SET",
.write = set_write,
.read = set_read,
.var = set_vars,
.var_num = sizeof(set_vars) / sizeof(set_vars[0]),
},
{
.name = "#TEST",
.run = test_run
},
{
.name = "#HELP",
.run = print_cmd_list,
},
{
.name = "#QUIT",
.run = quit_run
},
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf)
};
static int write_char(char ch)
{
putc(ch, stdout);
return 1;
}
static int read_char(char *ch)
{
*ch = getc(stdin);
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
while ((cat_service(&at) != 0) && (quit_flag == 0)) {};
printf("Bye!\n");
return 0;
}

View File

@@ -1,251 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
/* variables assigned to scan command */
static int rssi;
static char ssid[16];
/* helper variable used to exit demo code */
static bool quit_flag;
/* variables assigned to start command */
static int mode;
/* main at command parser object */
struct cat_object at;
struct scan_results {
int rssi;
char ssid[16];
};
/* static const scan results */
static const struct scan_results results[2][3] = {
{
{
.rssi = -10,
.ssid = "wifi1",
},
{
.rssi = -50,
.ssid = "wifi2",
},
{
.rssi = -20,
.ssid = "wifi3",
}
},
{
{
.rssi = -20,
.ssid = "bluetooth1",
}
}
};
/* helper variable */
static int scan_index;
/* run command handler with custom exit mechanism */
static int quit_run(const struct cat_command *cmd)
{
quit_flag = true;
return 0;
}
/* helper function */
static void load_scan_results(int index)
{
rssi = results[mode][index].rssi;
strcpy(ssid, results[mode][index].ssid);
}
/* declaring scan variables array */
static struct cat_variable scan_vars[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &rssi,
.data_size = sizeof(rssi),
.name = "RSSI"
},
{
.type = CAT_VAR_BUF_STRING,
.data = ssid,
.data_size = sizeof(ssid),
.name = "SSID"
}
};
/* forward declaration */
static cat_return_state scan_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size);
/* unsolicited command */
static struct cat_command scan_cmd = {
.name = "+SCAN",
.read = scan_read,
.var = scan_vars,
.var_num = sizeof(scan_vars) / sizeof(scan_vars[0])
};
/* unsolicited read callback handler */
static cat_return_state scan_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
int max = (mode == 0) ? 3 : 1;
scan_index++;
if (scan_index > max)
return CAT_RETURN_STATE_HOLD_EXIT_OK;
load_scan_results(scan_index);
cat_trigger_unsolicited_read(&at, &scan_cmd);
return CAT_RETURN_STATE_DATA_NEXT;
}
/* mode variable validator */
static int mode_write(const struct cat_variable *var, const size_t write_size)
{
if (*(int*)var->data >= 2)
return -1;
return 0;
}
/* start write callback handler */
static cat_return_state start_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
scan_index = 0;
load_scan_results(scan_index);
cat_trigger_unsolicited_read(&at, &scan_cmd);
return CAT_RETURN_STATE_HOLD;
}
/* run command handler attached to HELP command for printing commands list */
static int print_cmd_list(const struct cat_command *cmd)
{
return CAT_RETURN_STATE_PRINT_CMD_LIST_OK;
}
/* declaring start command variables array */
static struct cat_variable start_vars[] = {
{
.type = CAT_VAR_UINT_DEC,
.data = &mode,
.data_size = sizeof(mode),
.name = "MODE",
.write = mode_write,
.access = CAT_VAR_ACCESS_WRITE_ONLY,
}
};
/* declaring commands array */
static struct cat_command cmds[] = {
{
.name = "+START",
.description = "Start scanning after write (0 - wifi, 1 - bluetooth).",
.write = start_write,
.var = start_vars,
.var_num = sizeof(start_vars) / sizeof(start_vars[0]),
.need_all_vars = true
},
{
.name = "+SCAN",
.description = "Scan result record.",
.only_test = true,
.var = scan_vars,
.var_num = sizeof(scan_vars) / sizeof(scan_vars[0])
},
{
.name = "#HELP",
.run = print_cmd_list,
},
{
.name = "#QUIT",
.run = quit_run
},
};
/* working buffer */
static char buf[128];
/* declaring parser descriptor */
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
/* custom target dependent input output handlers */
static int write_char(char ch)
{
putc(ch, stdout);
return 1;
}
static int read_char(char *ch)
{
*ch = getc(stdin);
return 1;
}
/* declaring input output interface descriptor for parser */
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
int main(int argc, char **argv)
{
/* initializing */
cat_init(&at, &desc, &iface, NULL);
/* main loop with exit code conditions */
while ((cat_service(&at) != 0) && (quit_flag == 0)) {};
/* goodbye message */
printf("Bye!\n");
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,557 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef CAT_H
#define CAT_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
/* only forward declarations (looks for definition below) */
struct cat_command;
struct cat_variable;
#ifndef CAT_UNSOLICITED_CMD_BUFFER_SIZE
/* unsolicited command buffer default size (can by override externally during compilation) */
#define CAT_UNSOLICITED_CMD_BUFFER_SIZE ((size_t)(1))
#endif
/* enum type with variable type definitions */
typedef enum {
CAT_VAR_INT_DEC = 0, /* decimal encoded signed integer variable */
CAT_VAR_UINT_DEC, /* decimal encoded unsigned integer variable */
CAT_VAR_NUM_HEX, /* hexadecimal encoded unsigned integer variable */
CAT_VAR_BUF_HEX, /* asciihex encoded bytes array */
CAT_VAR_BUF_STRING /* string variable */
} cat_var_type;
/* enum type wirh variable accessors definitions */
typedef enum {
CAT_VAR_ACCESS_READ_WRITE = 0, /* there will be possible to read and write variable */
CAT_VAR_ACCESS_READ_ONLY, /* there will be possible to read only variable */
CAT_VAR_ACCESS_WRITE_ONLY, /* there will be possible to write only variable */
} cat_var_access;
/* enum type with function status */
typedef enum {
CAT_STATUS_ERROR_BUFFER_EMPTY = -7,
CAT_STATUS_ERROR_NOT_HOLD = -6,
CAT_STATUS_ERROR_BUFFER_FULL = -5,
CAT_STATUS_ERROR_UNKNOWN_STATE = -4,
CAT_STATUS_ERROR_MUTEX_LOCK = -3,
CAT_STATUS_ERROR_MUTEX_UNLOCK = -2,
CAT_STATUS_ERROR = -1,
CAT_STATUS_OK = 0,
CAT_STATUS_BUSY = 1,
CAT_STATUS_HOLD = 2
} cat_status;
/**
* Write variable function handler
*
* This callback function is called after variable update immediatly.
* User application can validate writed value and check for amount of data size was written.
* This handler is optional, so when is not defined, operations will be fully automatically.
*
* @param var - pointer to struct descriptor of parsed variable
* @param write_size - size of data was written (useful especially with hexbuf var type)
* @return 0 - ok, else error and stop parsing
* */
typedef int (*cat_var_write_handler)(const struct cat_variable *var, const size_t write_size);
/**
* Read variable function handler
*
* This callback function is called just before variable value read.
* User application can copy data from private fields to variable connected with parsed command.
* This handler is optional, so when is not defined, operations will be fully automatically.
*
* @param var - pointer to struct descriptor of parsed variable
* @return 0 - ok, else error and stop parsing
* */
typedef int (*cat_var_read_handler)(const struct cat_variable *var);
/**
* Write variable extended function handler
*
* This callback function is called after variable update immediatly.
* User application can validate writed value and check for amount of data size was written.
* This handler is optional, so when is not defined, operations will be fully automatically.
*
* @param var - pointer to struct descriptor of parsed variable
* @param cmd - pointer to variable parent command structure
* @param write_size - size of data was written (useful especially with hexbuf var type)
* @return 0 - ok, else error and stop parsing
* */
typedef int (*cat_var_write_ex_handler)(const struct cat_variable *var, const struct cat_command *cmd, const size_t write_size);
/**
* Read variable function handler
*
* This callback function is called just before variable value read.
* User application can copy data from private fields to variable connected with parsed command.
* This handler is optional, so when is not defined, operations will be fully automatically.
*
* @param var - pointer to struct descriptor of parsed variable
* @param cmd - pointer to variable parent command structure
* @return 0 - ok, else error and stop parsing
* */
typedef int (*cat_var_read_ex_handler)(const struct cat_variable *var, const struct cat_command *cmd);
/**
* Data getter for variable.
* The purpose of this function is to replace statically allocated data and data_size fields
* with function pointer with context. Then the data and data_size from this getter would be
* changed and returned runtime.
*
* @param var - pointer to struct descriptor of parsed variable
* @param context - command context
* @param size - pointer to variable where should be placed data size
* @return pointer to data array from context
*/
typedef void* (*cat_var_data_getter)(const struct cat_variable *var, void *context, size_t *data_size);
struct cat_variable {
const char *name; /* variable name (optional - using only for auto format test command response) */
cat_var_type type; /* variable type (needed for parsing and validating) */
void *data; /* generic pointer to statically allocated memory for variable data read/write/validate operations */
size_t data_size; /* variable data size, pointed by data pointer */
cat_var_access access; /* variable accessor */
cat_var_write_handler write; /* write variable handler */
cat_var_read_handler read; /* read variable handler */
cat_var_write_ex_handler write_ex; /* optional write variable extended handler */
cat_var_read_ex_handler read_ex; /* optional read variable extended handler */
cat_var_data_getter data_getter; /* optional data getter for dynamic linking data and data_size */
};
/* enum type with command callbacks return values meaning */
typedef enum {
CAT_RETURN_STATE_ERROR = -1, /* immediatly error acknowledge */
CAT_RETURN_STATE_DATA_OK, /* send current data buffer followed by ok acknowledge */
CAT_RETURN_STATE_DATA_NEXT, /* send current data buffer and go to next callback iteration */
CAT_RETURN_STATE_NEXT, /* go to next callback iteration without sending anything */
CAT_RETURN_STATE_OK, /* immediatly ok acknowledge */
CAT_RETURN_STATE_HOLD, /* enable hold parser state */
CAT_RETURN_STATE_HOLD_EXIT_OK, /* exit from hold state with OK response */
CAT_RETURN_STATE_HOLD_EXIT_ERROR, /* exit from hold state with ERROR response */
CAT_RETURN_STATE_PRINT_CMD_LIST_OK, /* print commands list followed by ok acknowledge (only in TEST and RUN) */
} cat_return_state;
/**
* Write command function handler (AT+CMD=)
*
* This callback function is called after parsing all connected variables.
* User application can validate all variales at once at this moment, or copy them to the other application layer buffer.
* User can check number of variables or make custom process of parsing non standard arguments format.
* This handler is optional, so when is not defined, operations on variables will be fully automatically.
* If neither write handler nor variables not defined, then write command type is not available.
*
* @param cmd - pointer to struct descriptor of processed command
* @param data - pointer to arguments buffer for custom parsing
* @param data_size - length of arguments buffer
* @param args_num - number of passed arguments connected to variables
* @return according to cat_return_state enum definitions
* */
typedef cat_return_state (*cat_cmd_write_handler)(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num);
/**
* Read command function handler (AT+CMD?)
*
* This callback function is called after format all connected variables.
* User application can change automatic response, or add some custom data to response.
* This handler is optional, so when is not defined, operations on variables will be fully automatically.
* If neither read handler nor variables not defined, then read command type is not available.
*
* @param cmd - pointer to struct descriptor of processed command
* @param data - pointer to arguments buffer for custom parsing
* @param data_size - pointer to length of arguments buffer (can be modifed if needed)
* @param max_data_size - maximum length of buffer pointed by data pointer
* @return according to cat_return_state enum definitions
* */
typedef cat_return_state (*cat_cmd_read_handler)(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size);
/**
* Run command function handler (AT+CMD)
*
* No operations on variables are done.
* This handler is optional.
* If run handler not defined, then run command type is not available.
*
* @param cmd - pointer to struct descriptor of processed command
* @return according to cat_return_state enum definitions
* */
typedef cat_return_state (*cat_cmd_run_handler)(const struct cat_command *cmd);
/**
* Test command function handler (AT+CMD=?)
*
* This callback function is called after format all connected variables.
* User application can change automatic response, or add some custom data to response.
* This handler is optional, so when is not defined, operations on variables will be fully automatically.
* If neither test handler nor variables not defined, then test command type is not available.
* Exception of this rule is write command without variables.
* In this case, the "question mark" will be passed as a custom argument to the write handler.
*
* @param cmd - pointer to struct descriptor of processed command
* @param data - pointer to arguments buffer for custom parsing
* @param data_size - pointer to length of arguments buffer (can be modifed if needed)
* @param max_data_size - maximum length of buffer pointed by data pointer
* @return according to cat_return_state enum definitions
* */
typedef cat_return_state (*cat_cmd_test_handler)(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size);
/* enum type with main at parser fsm state */
typedef enum {
CAT_STATE_ERROR = -1,
CAT_STATE_IDLE,
CAT_STATE_PARSE_PREFIX,
CAT_STATE_PARSE_COMMAND_CHAR,
CAT_STATE_UPDATE_COMMAND_STATE,
CAT_STATE_WAIT_READ_ACKNOWLEDGE,
CAT_STATE_SEARCH_COMMAND,
CAT_STATE_COMMAND_FOUND,
CAT_STATE_COMMAND_NOT_FOUND,
CAT_STATE_PARSE_COMMAND_ARGS,
CAT_STATE_PARSE_WRITE_ARGS,
CAT_STATE_FORMAT_READ_ARGS,
CAT_STATE_WAIT_TEST_ACKNOWLEDGE,
CAT_STATE_FORMAT_TEST_ARGS,
CAT_STATE_WRITE_LOOP,
CAT_STATE_READ_LOOP,
CAT_STATE_TEST_LOOP,
CAT_STATE_RUN_LOOP,
CAT_STATE_HOLD,
CAT_STATE_FLUSH_IO_WRITE_WAIT,
CAT_STATE_FLUSH_IO_WRITE,
CAT_STATE_AFTER_FLUSH_RESET,
CAT_STATE_AFTER_FLUSH_OK,
CAT_STATE_AFTER_FLUSH_FORMAT_READ_ARGS,
CAT_STATE_AFTER_FLUSH_FORMAT_TEST_ARGS,
CAT_STATE_PRINT_CMD,
} cat_state;
/* enum type with type of command request */
typedef enum {
CAT_CMD_TYPE_NONE = -1,
CAT_CMD_TYPE_RUN,
CAT_CMD_TYPE_READ,
CAT_CMD_TYPE_WRITE,
CAT_CMD_TYPE_TEST,
CAT_CMD_TYPE__TOTAL_NUM
} cat_cmd_type;
/* structure with io interface functions */
struct cat_io_interface {
int (*write)(char ch); /* write char to output stream. return 1 if byte wrote successfully. */
int (*read)(char *ch); /* read char from input stream. return 1 if byte read successfully. */
};
/* structure with mutex interface functions */
struct cat_mutex_interface {
int (*lock)(void); /* lock mutex handler. return 0 if successfully locked, otherwise - cannot lock */
int (*unlock)(void); /* unlock mutex handler. return 0 if successfully unlocked, otherwise - cannot unlock */
};
/* structure with at command descriptor */
struct cat_command {
const char *name; /* at command name (case-insensitivity) */
const char *description; /* at command description (optionally - can be null) */
cat_cmd_write_handler write; /* write command handler */
cat_cmd_read_handler read; /* read command handler */
cat_cmd_run_handler run; /* run command handler */
cat_cmd_test_handler test; /* test command handler */
struct cat_variable const *var; /* pointer to array of variables assiocated with this command */
size_t var_num; /* number of variables in array */
bool need_all_vars; /* flag to need all vars parsing */
bool only_test; /* flag to disable read/write/run commands (only test auto description) */
bool disable; /* flag to completely disable command */
void* context; /* pointer to optional context structure */
};
struct cat_command_group {
const char *name; /* command group name (optional, for identification purpose) */
struct cat_command const *cmd; /* pointer to array of commands descriptor */
size_t cmd_num; /* number of commands in array */
bool disable; /* flag to completely disable all commands in group */
};
/* structure with at command parser descriptor */
struct cat_descriptor {
struct cat_command_group* const *cmd_group; /* pointer to array of commands group descriptor */
size_t cmd_group_num; /* number of commands group in array */
uint8_t *buf; /* pointer to working buffer (used to parse command argument) */
size_t buf_size; /* working buffer length */
/* optional unsolicited buffer, if not configured (NULL) */
/* then the buf will be divided into two smaller buffers */
uint8_t *unsolicited_buf; /* pointer to unsolicited working buffer (used to parse command argument) */
size_t unsolicited_buf_size; /* unsolicited working buffer length */
};
/* strcuture with unsolicited command buffered infos */
struct cat_unsolicited_cmd {
struct cat_command const *cmd; /* pointer to commands used to unsolicited event */
cat_cmd_type type; /* type of unsolicited event */
};
/* enum type with unsolicited events fsm state */
typedef enum {
CAT_UNSOLICITED_STATE_IDLE,
CAT_UNSOLICITED_STATE_FORMAT_READ_ARGS,
CAT_UNSOLICITED_STATE_FORMAT_TEST_ARGS,
CAT_UNSOLICITED_STATE_READ_LOOP,
CAT_UNSOLICITED_STATE_TEST_LOOP,
CAT_UNSOLICITED_STATE_FLUSH_IO_WRITE_WAIT,
CAT_UNSOLICITED_STATE_FLUSH_IO_WRITE,
CAT_UNSOLICITED_STATE_AFTER_FLUSH_RESET,
CAT_UNSOLICITED_STATE_AFTER_FLUSH_OK,
CAT_UNSOLICITED_STATE_AFTER_FLUSH_FORMAT_READ_ARGS,
CAT_UNSOLICITED_STATE_AFTER_FLUSH_FORMAT_TEST_ARGS,
} cat_unsolicited_state;
/* enum type with fsm type */
typedef enum {
CAT_FSM_TYPE_ATCMD,
CAT_FSM_TYPE_UNSOLICITED,
CAT_FSM_TYPE__TOTAL_NUM,
} cat_fsm_type;
struct cat_unsolicited_fsm {
cat_unsolicited_state state; /* current unsolicited fsm state */
size_t index; /* index used to iterate over commands and variables */
size_t position; /* position of actually parsed char in arguments string */
struct cat_command const *cmd; /* pointer to current command descriptor */
struct cat_variable const *var; /* pointer to current variable descriptor */
cat_cmd_type cmd_type; /* type of command request */
char const *write_buf; /* working buffer pointer used for asynch writing to io */
int write_state; /* before, data, after flush io write state */
cat_unsolicited_state write_state_after; /* parser state to set after flush io write */
struct cat_unsolicited_cmd unsolicited_cmd_buffer[CAT_UNSOLICITED_CMD_BUFFER_SIZE]; /* buffer with unsolicited commands used to unsolicited event */
size_t unsolicited_cmd_buffer_tail; /* tail index of unsolicited cmd buffer */
size_t unsolicited_cmd_buffer_head; /* head index of unsolicited cmd buffer */
size_t unsolicited_cmd_buffer_items_count; /* number of unsolicited cmd in buffer */
};
/* structure with main at command parser object */
struct cat_object {
struct cat_descriptor const *desc; /* pointer to at command parser descriptor */
struct cat_io_interface const *io; /* pointer to at command parser io interface */
struct cat_mutex_interface const *mutex; /* pointer to at command parser mutex interface */
size_t index; /* index used to iterate over commands and variables */
size_t partial_cntr; /* partial match commands counter */
size_t length; /* length of input command name and command arguments */
size_t position; /* position of actually parsed char in arguments string */
size_t write_size; /* size of parsed buffer hex or buffer string */
size_t commands_num; /* computed total number of registered commands */
struct cat_command const *cmd; /* pointer to current command descriptor */
struct cat_variable const *var; /* pointer to current variable descriptor */
cat_cmd_type cmd_type; /* type of command request */
char current_char; /* current received char from input stream */
cat_state state; /* current fsm state */
bool cr_flag; /* flag for detect <cr> char in input string */
bool hold_state_flag; /* status of hold state (independent from fsm states) */
int hold_exit_status; /* hold exit parameter with status */
char const *write_buf; /* working buffer pointer used for asynch writing to io */
int write_state; /* before, data, after flush io write state */
cat_state write_state_after; /* parser state to set after flush io write */
struct cat_unsolicited_fsm unsolicited_fsm;
};
/**
* Function used to initialize at command parser.
* Initialize starting values of object fields.
*
* @param self pointer to at command parser object to initialize
* @param desc pointer to at command parser descriptor
* @param io pointer to at command parser io low-level layer interface
* @param mutex pointer to at command partes mutex interface
*/
void cat_init(struct cat_object *self, const struct cat_descriptor *desc, const struct cat_io_interface *io, const struct cat_mutex_interface *mutex);
/**
* Function must be called periodically to asynchronoulsy run at command parser.
* Commands handlers will be call from this function context.
*
* @param self pointer to at command parser object
* @return according to cat_return_state enum definitions
*/
cat_status cat_service(struct cat_object *self);
/**
* Function return flag which indicating internal busy state.
* It is used to determine whether external application modules can use shared input / output interfaces functions.
* It is usefull especially in rtos environments.
* If internal parser state is busy by doing some processing then function return 1.
* If the function returns 0, then the external application modules can safely use the input / output interfaces functions shared with the library.
* If the function returns 1, then input / output interface function are used by internal parser functions.
*
* @param self pointer to at command parser object
* @return according to cat_return_state enum definitions
*/
cat_status cat_is_busy(struct cat_object *self);
/**
* Function return flag which indicating parsing hold state.
* If the function returns 0, then the at parsing process is normal.
* If the function returns 1, then the at parsing process is holded.
* To exit from hold state, user have to call cat_hold_exit or return HOLD_EXIT return value in callback.
*
* @param self pointer to at command parser object
* @return according to cat_return_state enum definitions
*/
cat_status cat_is_hold(struct cat_object *self);
/**
* Function return flag which indicating state of internal buffer of unsolicited events.
*
* @param self pointer to at command parser object
* @return CAT_STATUS_OK - buffer is not full, unsolicited event can be buffered
* CAT_STATUS_ERROR_BUFFER_FULL - buffer is full, unsolicited event cannot be buffered
* CAT_STATUS_ERROR_MUTEX_LOCK - cannot lock mutex error
* CAT_STATUS_ERROR_MUTEX_UNLOCK - cannot unlock mutex error
*/
cat_status cat_is_unsolicited_buffer_full(struct cat_object *self);
/**
* Function sends unsolicited event message.
* Command message is buffered inside parser in 1-level deep buffer and processed in cat_service context.
* Only command pointer is buffered, so command struct should be static or global until be fully processed.
*
* @param self pointer to at command parser object
* @param cmd pointer to command structure regarding which unsolicited event applies to
* @param type type of operation (only CAT_CMD_TYPE_READ and CAT_CMD_TYPE_TEST are allowed)
* @return according to cat_return_state enum definitions
*/
cat_status cat_trigger_unsolicited_event(struct cat_object *self, struct cat_command const *cmd, cat_cmd_type type);
/**
* Function sends unsolicited read event message.
* Command message is buffered inside parser in 1-level deep buffer and processed in cat_service context.
* Only command pointer is buffered, so command struct should be static or global until be fully processed.
*
* @param self pointer to at command parser object
* @param cmd pointer to command structure regarding which unsolicited read applies to
* @return according to cat_return_state enum definitions
*/
cat_status cat_trigger_unsolicited_read(struct cat_object *self, struct cat_command const *cmd);
/**
* Function sends unsolicited test event message.
* Command message is buffered inside parser in 1-level deep buffer and processed in cat_service context.
* Only command pointer is buffered, so command struct should be static or global until be fully processed.
*
* @param self pointer to at command parser object
* @param cmd pointer to command structure regarding which unsolicited test applies to
* @return according to cat_return_state enum definitions
*/
cat_status cat_trigger_unsolicited_test(struct cat_object *self, struct cat_command const *cmd);
/**
* Function used to exit from hold state with OK/ERROR response and back to idle state.
*
* @param self pointer to at command parser object
* @param status response status 0 - OK, else ERROR
* @return according to cat_return_state enum definitions
*/
cat_status cat_hold_exit(struct cat_object *self, cat_status status);
/**
* Function used to searching registered command by its name.
*
* @param self pointer to at command parser object
* @param name command name to search
* @return pointer to command object, NULL if command not found
*/
struct cat_command const* cat_search_command_by_name(struct cat_object *self, const char *name);
/**
* Function used to searching registered command group by its name.
*
* @param self pointer to at command parser object
* @param name command group name to search
* @return pointer to command group object, NULL if command group not found
*/
struct cat_command_group const* cat_search_command_group_by_name(struct cat_object *self, const char *name);
/**
* Function used to searching attached variable to command its name.
*
* @param self pointer to at command parser object
* @param cmd pointer to command in which variable will be searched
* @param name variable name to search
* @return pointer to command group object, NULL if command group not found
*/
struct cat_variable const* cat_search_variable_by_name(struct cat_object *self, struct cat_command const *cmd, const char *name);
/**
* Function used to check what command is currently processed.
* Function is not protected by mutex mechanism, due to processed cmd may change after function return.
* This only matters in multithreaded environments, it does not matter for one thread.
*
* @param self pointer to at command parser object
* @param fsm type of internal state machine to check current command
* @return pointer to command which is currently processed, NULL if no command is processed
*/
struct cat_command const* cat_get_processed_command(struct cat_object *self, cat_fsm_type fsm);
/**
* Function return unsolicited event command status.
* Function is not protected by mutex mechanism, due to processed cmd may change after function return.
* This only matters in multithreaded environments, it does not matter for one thread.
*
* @param self pointer to at command parser object
* @param cmd pointer to command in which variable will be searched
* @param type type of unsolicited event
* @return CAT_STATUS_OK - command is not buffered nor processed
* CAT_STATUS_BUSY - command is waiting in buffer or is processed
*/
cat_status cat_is_unsolicited_event_buffered(struct cat_object *self, struct cat_command const *cmd, cat_cmd_type type);
#ifdef __cplusplus
}
#endif
#endif /* CAT_H */

View File

@@ -1,340 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char ack_results[512];
static int8_t var1;
static int8_t var2;
static char const *input_text;
static size_t input_index;
static int cmd_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
return 0;
}
static int cmd_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
return 0;
}
static int cmd_test(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
return 0;
}
static int cmd_run(const struct cat_command *cmd)
{
return 0;
}
static int var1_write(const struct cat_variable *var, size_t write_size)
{
return 0;
}
static int var2_write(const struct cat_variable *var, size_t write_size)
{
return 0;
}
static int print_cmd_list(const struct cat_command *cmd)
{
return CAT_RETURN_STATE_PRINT_CMD_LIST_OK;
}
static struct cat_variable vars[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var1,
.data_size = sizeof(var1),
.write = var1_write
}
};
static struct cat_variable vars_ro[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var1,
.data_size = sizeof(var1),
.write = var1_write,
.access = CAT_VAR_ACCESS_READ_ONLY
}
};
static struct cat_variable vars_wo[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var1,
.data_size = sizeof(var1),
.write = var1_write,
.access = CAT_VAR_ACCESS_WRITE_ONLY
}
};
static struct cat_variable vars2[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var1,
.data_size = sizeof(var1),
.write = var1_write
},
{
.type = CAT_VAR_INT_DEC,
.data = &var2,
.data_size = sizeof(var2),
.write = var2_write
}
};
static struct cat_variable vars2_ro[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var1,
.data_size = sizeof(var1),
.write = var1_write,
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_INT_DEC,
.data = &var2,
.data_size = sizeof(var2),
.write = var2_write
}
};
static struct cat_variable vars2_wo[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var1,
.data_size = sizeof(var1),
.write = var1_write,
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_INT_DEC,
.data = &var2,
.data_size = sizeof(var2),
.write = var2_write
}
};
static struct cat_command cmds[] = {
{
.name = "+V1",
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0])
},
{
.name = "+V1RO",
.var = vars_ro,
.var_num = sizeof(vars_ro) / sizeof(vars_ro[0])
},
{
.name = "+V1RW",
.var = vars_wo,
.var_num = sizeof(vars_wo) / sizeof(vars_wo[0])
},
{
.name = "+V11",
.var = vars2,
.var_num = sizeof(vars2) / sizeof(vars2[0])
},
{
.name = "+V11RO",
.var = vars2_ro,
.var_num = sizeof(vars2_ro) / sizeof(vars2_ro[0])
},
{
.name = "+V11WO",
.var = vars2_wo,
.var_num = sizeof(vars2_wo) / sizeof(vars2_wo[0])
},
{
.name = "+V2",
.write = cmd_write,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0])
},
{
.name = "+V3",
.write = cmd_write,
.read = cmd_read,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0])
},
{
.name = "+V4",
.write = cmd_write,
.read = cmd_read,
.test = cmd_test,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0])
},
{
.name = "+V5",
.write = cmd_write,
.read = cmd_read,
.test = cmd_test,
.run = cmd_run,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0])
},
{
.name = "+S1",
},
{
.name = "+S2",
.write = cmd_write,
},
{
.name = "+S3",
.write = cmd_write,
.read = cmd_read,
},
{
.name = "+S4",
.write = cmd_write,
.read = cmd_read,
.test = cmd_test,
},
{
.name = "+S5",
.write = cmd_write,
.read = cmd_read,
.test = cmd_test,
.run = cmd_run,
},
{
.name = "+D1",
.write = cmd_write,
.read = cmd_read,
.test = cmd_test,
.run = cmd_run,
.disable = true,
},
{
.name = "+T1",
.write = cmd_write,
.read = cmd_read,
.test = cmd_test,
.run = cmd_run,
.only_test = true,
},
{
.name = "+T2",
.only_test = true,
},
{
.name = "#HELP",
.run = print_cmd_list,
}
};
static char buf[512];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
memset(ack_results, 0, sizeof(ack_results));
}
static void print_raw_text(char *p)
{
while (*p != '\0') {
if (*p == '\n') {
printf("\\n");
} else {
putchar(*p);
}
p++;
}
}
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input("\nAT#HELP\n");
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nAT+V1?\nAT+V1=\nAT+V1=?\n\nAT+V1RO?\nAT+V1RO=?\n\nAT+V1RW=\nAT+V1RW=?\n\nAT+V11?\nAT+V11=\nAT+V11=?\n\nAT+V11RO?\nAT+V11RO=\nAT+V11RO=?\n\nAT+V11WO?\nAT+V11WO=\nAT+V11WO=?\n\nAT+V2?\nAT+V2=\nAT+V2=?\n\nAT+V3?\nAT+V3=\nAT+V3=?\n\nAT+V4?\nAT+V4=\nAT+V4=?\n\nAT+V5\nAT+V5?\nAT+V5=\nAT+V5=?\n\nAT+S2=\n\nAT+S3?\nAT+S3=\n\nAT+S4?\nAT+S4=\nAT+S4=?\n\nAT+S5\nAT+S5?\nAT+S5=\nAT+S5=?\n\nAT+T1=?\n\nAT#HELP\n\nOK\n") == 0);
return 0;
}

View File

@@ -1,229 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char cmd_results[256];
static char var_read_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int var_x, var_u1, var_u2;
static struct cat_object at;
static struct cat_command u_cmds[];
static cat_return_state cmd_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
cat_status s;
strcat(cmd_results, " write:");
strcat(cmd_results, cmd->name);
if (var_x < 2) {
s = cat_trigger_unsolicited_read(&at, &u_cmds[var_x]);
assert(s == CAT_STATUS_OK);
return CAT_RETURN_STATE_HOLD;
}
return CAT_RETURN_STATE_ERROR;
}
static cat_return_state cmd1_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
cat_status s;
strcat(cmd_results, " read1:");
strcat(cmd_results, cmd->name);
if (var_u1 > 0) {
var_u1--;
s = cat_trigger_unsolicited_read(&at, cmd);
assert(s == CAT_STATUS_OK);
return CAT_RETURN_STATE_DATA_OK;
}
return CAT_RETURN_STATE_HOLD_EXIT_OK;
}
static cat_return_state cmd2_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
cat_status s;
strcat(cmd_results, " read2:");
strcat(cmd_results, cmd->name);
if (var_u2 > 0) {
var_u2--;
s = cat_trigger_unsolicited_read(&at, cmd);
assert(s == CAT_STATUS_OK);
return CAT_RETURN_STATE_DATA_OK;
}
cat_hold_exit(&at, CAT_STATUS_OK);
return CAT_RETURN_STATE_DATA_OK;
}
static int var_read(const struct cat_variable *var)
{
strcat(var_read_results, " var_read:");
strcat(var_read_results, var->name);
return 0;
}
static struct cat_variable u_vars[] = {
{
.name = "U1",
.type = CAT_VAR_INT_DEC,
.data = &var_u1,
.data_size = sizeof(var_u1),
.read = var_read
},
{
.name = "U2",
.type = CAT_VAR_INT_DEC,
.data = &var_u2,
.data_size = sizeof(var_u2),
.read = var_read
}
};
static struct cat_variable vars[] = {
{
.name = "X",
.type = CAT_VAR_INT_DEC,
.data = &var_x,
.data_size = sizeof(var_x),
.read = var_read
}
};
static struct cat_command cmds[] = {
{
.name = "+CMD",
.write = cmd_write,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0]),
}
};
static struct cat_command u_cmds[] = {
{
.name = "+U1CMD",
.read = cmd1_read,
.var = &u_vars[0],
.var_num = 1,
},
{
.name = "+U2CMD",
.read = cmd2_read,
.var = &u_vars[1],
.var_num = 1,
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
var_u1 = 2;
var_u2 = 3;
memset(ack_results, 0, sizeof(ack_results));
memset(cmd_results, 0, sizeof(cmd_results));
memset(var_read_results, 0, sizeof(var_read_results));
}
static const char test_case_1[] = "\nAT+CMD=0\n\nAT+CMD=1\n";
int main(int argc, char **argv)
{
cat_status s;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+U1CMD=2\n\n+U1CMD=1\n\nOK\n\n+U2CMD=3\n\n+U2CMD=2\n\n+U2CMD=1\n\n+U2CMD=0\n\nOK\n") == 0);
assert(strcmp(cmd_results, " write:+CMD read1:+U1CMD read1:+U1CMD read1:+U1CMD write:+CMD read2:+U2CMD read2:+U2CMD read2:+U2CMD read2:+U2CMD") == 0);
assert(strcmp(var_read_results, " var_read:U1 var_read:U1 var_read:U1 var_read:U2 var_read:U2 var_read:U2 var_read:U2") == 0);
return 0;
}

View File

@@ -1,185 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char run_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int a_run(const struct cat_command *cmd)
{
strcat(run_results, " A:");
strcat(run_results, cmd->name);
return 0;
}
static int ap_run(const struct cat_command *cmd)
{
strcat(run_results, " AP:");
strcat(run_results, cmd->name);
return 0;
}
static int test_run(const struct cat_command *cmd)
{
strcat(run_results, " +TEST:");
strcat(run_results, cmd->name);
return 0;
}
static struct cat_command cmds[] = {
{
.name = "A",
.run = a_run
},
{
.name = "AP",
.run = ap_run
},
{
.name = "+TEST",
.run = test_run
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static int mutex_ret_lock;
static int mutex_ret_unlock;
static int mutex_lock(void)
{
return mutex_ret_lock;
}
static int mutex_unlock(void)
{
return mutex_ret_unlock;
}
static struct cat_mutex_interface mutex = {
.lock = mutex_lock,
.unlock = mutex_unlock
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
mutex_ret_lock = 0;
mutex_ret_unlock = 0;
memset(run_results, 0, sizeof(run_results));
memset(ack_results, 0, sizeof(ack_results));
}
static const char test_case_1[] = "\nAT\nAT+test\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, &mutex);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n\nOK\n") == 0);
assert(strcmp(run_results, " +TEST:+TEST") == 0);
mutex_ret_lock = 1;
mutex_ret_unlock = 0;
assert(cat_service(&at) == CAT_STATUS_ERROR_MUTEX_LOCK);
mutex_ret_lock = 0;
mutex_ret_unlock = 1;
assert(cat_service(&at) == CAT_STATUS_ERROR_MUTEX_UNLOCK);
mutex_ret_lock = 1;
mutex_ret_unlock = 0;
assert(cat_is_busy(&at) == CAT_STATUS_ERROR_MUTEX_LOCK);
mutex_ret_lock = 0;
mutex_ret_unlock = 1;
assert(cat_is_busy(&at) == CAT_STATUS_ERROR_MUTEX_UNLOCK);
mutex_ret_lock = 0;
mutex_ret_unlock = 0;
assert(cat_service(&at) == CAT_STATUS_OK);
assert(cat_is_busy(&at) == CAT_STATUS_OK);
return 0;
}

View File

@@ -1,223 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char run_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int e_run(const struct cat_command *cmd)
{
strcat(run_results, " E:");
strcat(run_results, cmd->name);
return 0;
}
static int e0_run(const struct cat_command *cmd)
{
strcat(run_results, " E0:");
strcat(run_results, cmd->name);
return 0;
}
static int e1_run(const struct cat_command *cmd)
{
strcat(run_results, " E1:");
strcat(run_results, cmd->name);
return 0;
}
static struct cat_command cmds1[] = {
{
.name = "E",
.run = e_run,
},
{
.name = "E0",
.run = e0_run,
},
{
.name = "E1",
.run = e1_run,
}
};
static struct cat_command cmds2[] = {
{
.name = "E0",
.run = e0_run,
},
{
.name = "E1",
.run = e1_run,
},
{
.name = "E",
.run = e_run,
}
};
static struct cat_command cmds3[] = {
{
.name = "E0",
.run = e0_run,
},
{
.name = "E",
.run = e_run,
},
{
.name = "E1",
.run = e1_run,
}
};
static char buf[128];
static struct cat_command_group cmd_1_group = {
.cmd = cmds1,
.cmd_num = sizeof(cmds1) / sizeof(cmds1[0]),
};
static struct cat_command_group *cmd_1_desc[] = {
&cmd_1_group
};
static struct cat_descriptor desc_1 = {
.cmd_group = cmd_1_desc,
.cmd_group_num = sizeof(cmd_1_desc) / sizeof(cmd_1_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static struct cat_command_group cmd_2_group = {
.cmd = cmds2,
.cmd_num = sizeof(cmds2) / sizeof(cmds2[0]),
};
static struct cat_command_group *cmd_2_desc[] = {
&cmd_2_group
};
static struct cat_descriptor desc_2 = {
.cmd_group = cmd_2_desc,
.cmd_group_num = sizeof(cmd_2_desc) / sizeof(cmd_2_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static struct cat_command_group cmd_3_group = {
.cmd = cmds3,
.cmd_num = sizeof(cmds3) / sizeof(cmds3[0]),
};
static struct cat_command_group *cmd_3_desc[] = {
&cmd_3_group
};
static struct cat_descriptor desc_3 = {
.cmd_group = cmd_3_desc,
.cmd_group_num = sizeof(cmd_3_desc) / sizeof(cmd_3_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
memset(run_results, 0, sizeof(run_results));
memset(ack_results, 0, sizeof(ack_results));
}
static const char test_case_1[] = "\nATE\n\nATE0\n\nATE1\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc_1, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n\nOK\n\nOK\n") == 0);
assert(strcmp(run_results, " E:E E0:E0 E1:E1") == 0);
cat_init(&at, &desc_2, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n\nOK\n\nOK\n") == 0);
assert(strcmp(run_results, " E:E E0:E0 E1:E1") == 0);
cat_init(&at, &desc_3, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n\nOK\n\nOK\n") == 0);
assert(strcmp(run_results, " E:E E0:E0 E1:E1") == 0);
return 0;
}

View File

@@ -1,195 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char run_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int a_run(const struct cat_command *cmd)
{
strcat(run_results, " A:");
strcat(run_results, cmd->name);
return 0;
}
static int ap_run(const struct cat_command *cmd)
{
strcat(run_results, " AP:");
strcat(run_results, cmd->name);
return 0;
}
static int test_run(const struct cat_command *cmd)
{
strcat(run_results, " +TEST:");
strcat(run_results, cmd->name);
return 0;
}
static struct cat_command cmds[] = {
{
.name = "A",
.run = a_run,
.disable = false,
},
{
.name = "AP",
.run = ap_run,
.disable = false,
},
{
.name = "+TEST",
.run = test_run,
.disable = false,
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
memset(run_results, 0, sizeof(run_results));
memset(ack_results, 0, sizeof(ack_results));
}
static const char test_case_1[] = "\nsa\rAT\n\r\nAT\nAT+\n\nATA\r\natap\naaaattttap\na\n\r+test\r\n+testATA\nATAPATAP\n\rAT\rATA\nAT+test\r\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\r\nERROR\r\n\nOK\n\nOK\n\r\nOK\r\n\nOK\n\nERROR\n\nERROR\n\r\nERROR\r\n\nERROR\n\nERROR\n\r\nERROR\r\n\r\nOK\r\n") == 0);
assert(strcmp(run_results, " +TEST:+TEST A:A AP:AP +TEST:+TEST") == 0);
prepare_input("\nAT\n");
while (cat_service(&at) != 0) {};
assert(cat_is_busy(&at) == 0);
assert(strcmp(ack_results, "\nOK\n") == 0);
assert(strcmp(run_results, "") == 0);
prepare_input("\nAT+te");
while (cat_service(&at) != 0) {};
assert(cat_is_busy(&at) != 0);
assert(strcmp(ack_results, "") == 0);
assert(strcmp(run_results, "") == 0);
prepare_input("st\n");
while (cat_service(&at) != 0) {};
assert(cat_is_busy(&at) == 0);
assert(strcmp(ack_results, "\nOK\n") == 0);
assert(strcmp(run_results, " +TEST:+TEST") == 0);
struct cat_command *cmd;
cmd = (struct cat_command*)cat_search_command_by_name(&at, "A");
cmd->disable = true;
cmd = (struct cat_command*)cat_search_command_by_name(&at, "+TEST");
cmd->disable = true;
prepare_input("\nATA\n\nATAP\n\nAT+TEST\n");
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n\nOK\n\nERROR\n") == 0);
assert(strcmp(run_results, " AP:AP AP:AP") == 0);
struct cat_command_group *cmd_group;
cmd_group = (struct cat_command_group*)cat_search_command_group_by_name(&at, "standard");
assert(cmd_group == NULL);
cmd_desc[0]->name = "standard";
cmd_group = (struct cat_command_group*)cat_search_command_group_by_name(&at, "standard");
assert(cmd_group == cmd_desc[0]);
cmd_group->disable = true;
prepare_input("\nATA\n\nATAP\n\nAT+TEST\n");
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n\nERROR\n\nERROR\n") == 0);
assert(strcmp(run_results, "") == 0);
return 0;
}

View File

@@ -1,165 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char run_results[256];
static char read_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int a_run(const struct cat_command *cmd)
{
strcat(run_results, " A_");
strcat(run_results, cmd->name);
return 0;
}
static int a_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
strcat(read_results, " A:");
strcat(read_results, cmd->name);
snprintf(data, max_data_size, "%s=A-val", cmd->name);
*data_size = strlen(data);
return 0;
}
static int ap_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
strcat(read_results, " AP:");
strcat(read_results, cmd->name);
*data_size = 0;
return 0;
}
static int test_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
strcat(read_results, " +TEST:");
strcat(read_results, cmd->name);
return -1;
}
static struct cat_command cmds[] = {
{
.name = "A",
.read = a_read,
.run = a_run
},
{
.name = "AP",
.read = ap_read
},
{
.name = "+TEST",
.read = test_read
},
{
.name = "+EMPTY"
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf)
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
memset(run_results, 0, sizeof(run_results));
memset(ack_results, 0, sizeof(ack_results));
memset(read_results, 0, sizeof(read_results));
}
static const char test_case_1[] = "\nAT\r\nAT+\nAT+?\nATA?\r\nATAP\nATAP?\nATAPA?\nAT+TEST?\nAT+te?\nAT+e?\nAT+empTY?\r\nATA\r\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\r\nOK\r\n\nERROR\n\nERROR\n\r\nA=A-val\r\n\r\nOK\r\n\nERROR\n\nAP=\n\nOK\n\nERROR\n\nERROR\n\nERROR\n\nERROR\n\r\nERROR\r\n\r\nOK\r\n") == 0);
assert(strcmp(run_results, " A_A") == 0);
assert(strcmp(read_results, " A:A AP:AP +TEST:+TEST +TEST:+TEST") == 0);
return 0;
}

View File

@@ -1,225 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char ack_results[256];
static int8_t var_int;
static uint8_t var_uint;
static uint8_t var_hex8;
static uint16_t var_hex16;
static uint32_t var_hex32;
static uint8_t var_buf[4];
static char var_string[16];
static char const *input_text;
static size_t input_index;
static int common_cntr;
static uint8_t ctx;
static int cmd_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
return 0;
}
static int cmd2_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
sprintf(data, "%s=test", cmd->name);
*data_size = strlen(data);
return 0;
}
static int common_var_read_handler(const struct cat_variable *var)
{
common_cntr++;
return 0;
}
static void* var_int_data_getter(const struct cat_variable *var, void *context, size_t *data_size)
{
*data_size = sizeof(var_int);
assert(context == &ctx);
return &var_int;
}
static struct cat_variable vars[] = {
{
.type = CAT_VAR_INT_DEC,
.read = common_var_read_handler,
.data_getter = var_int_data_getter,
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint,
.data_size = sizeof(var_uint),
.read = common_var_read_handler
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex8,
.data_size = sizeof(var_hex8),
.read = common_var_read_handler
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex16,
.data_size = sizeof(var_hex16),
.read = common_var_read_handler
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex32,
.data_size = sizeof(var_hex32),
.read = common_var_read_handler
},
{
.type = CAT_VAR_BUF_HEX,
.data = &var_buf,
.data_size = sizeof(var_buf),
.read = common_var_read_handler
},
{
.type = CAT_VAR_BUF_STRING,
.data = &var_string,
.data_size = sizeof(var_string),
.read = common_var_read_handler
}
};
static struct cat_command cmds[] = {
{
.name = "+SET",
.read = cmd_read,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0]),
.context = &ctx,
},
{
.name = "+TEST",
.read = cmd2_read,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0]),
.context = &ctx,
},
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf)
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
var_int = -1;
var_uint = 255;
var_hex8 = 0xAA;
var_hex16 = 0x0123;
var_hex32 = 0xFF001234;
var_buf[0] = 0x12;
var_buf[1] = 0x34;
var_buf[2] = 0x56;
var_buf[3] = 0x78;
common_cntr = 0;
sprintf(var_string, "\\\"test\n");
memset(ack_results, 0, sizeof(ack_results));
}
static const char test_case_1[] = "\nAT+SET?\r\n";
static const char test_case_2[] = "\nAT+TEST?\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\r\n+SET=-1,255,0xAA,0x0123,0xFF001234,12345678,\"\\\\\\\"test\\n\"\r\n\r\nOK\r\n") == 0);
assert(common_cntr == 7);
prepare_input(test_case_2);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+TEST=test\n\nOK\n") == 0);
assert(common_cntr == 7);
return 0;
}

View File

@@ -1,206 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char cmd_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int var_x;
static struct cat_object at;
static cat_return_state ret;
static bool ret_error;
static cat_return_state cmd_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
strcat(cmd_results, " read:");
strcat(cmd_results, cmd->name);
if (cat_is_hold(&at) == CAT_STATUS_HOLD) {
var_x++;
if (var_x > 4) {
ret = (ret_error == false) ? CAT_RETURN_STATE_HOLD_EXIT_OK : CAT_RETURN_STATE_HOLD_EXIT_ERROR;
} else {
if (var_x == 4) {
strcpy(data, "test");
*data_size = strlen(data);
}
ret = CAT_RETURN_STATE_DATA_NEXT;
}
} else {
if ((ret == CAT_RETURN_STATE_DATA_NEXT) || (ret == CAT_RETURN_STATE_NEXT)) {
var_x++;
if (var_x > 2)
ret = CAT_RETURN_STATE_DATA_OK;
} else if (ret == CAT_RETURN_STATE_HOLD) {
cat_trigger_unsolicited_read(&at, cmd);
}
}
return ret;
}
static struct cat_variable vars[] = {
{
.name = "X",
.type = CAT_VAR_INT_DEC,
.data = &var_x,
.data_size = sizeof(var_x)
}
};
static struct cat_command cmds[] = {
{
.name = "+CMD",
.read = cmd_read,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0]),
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
var_x = 1;
memset(ack_results, 0, sizeof(ack_results));
memset(cmd_results, 0, sizeof(cmd_results));
}
static const char test_case_1[] = "\nAT+CMD?\n";
int main(int argc, char **argv)
{
cat_status s;
cat_init(&at, &desc, &iface, NULL);
ret = CAT_RETURN_STATE_ERROR;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n") == 0);
assert(strcmp(cmd_results, " read:+CMD") == 0);
ret = CAT_RETURN_STATE_DATA_OK;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+CMD=1\n\nOK\n") == 0);
assert(strcmp(cmd_results, " read:+CMD") == 0);
ret = CAT_RETURN_STATE_DATA_NEXT;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+CMD=1\n\n+CMD=2\n\nOK\n") == 0);
assert(strcmp(cmd_results, " read:+CMD read:+CMD") == 0);
ret = CAT_RETURN_STATE_NEXT;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+CMD=2\n\nOK\n") == 0);
assert(strcmp(cmd_results, " read:+CMD read:+CMD") == 0);
ret = CAT_RETURN_STATE_OK;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n") == 0);
assert(strcmp(cmd_results, " read:+CMD") == 0);
ret_error = false;
ret = CAT_RETURN_STATE_HOLD;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+CMD=1\n\n+CMD=2\n\ntest\n\nOK\n") == 0);
assert(strcmp(cmd_results, " read:+CMD read:+CMD read:+CMD read:+CMD read:+CMD") == 0);
ret_error = true;
ret = CAT_RETURN_STATE_HOLD;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+CMD=1\n\n+CMD=2\n\ntest\n\nERROR\n") == 0);
assert(strcmp(cmd_results, " read:+CMD read:+CMD read:+CMD read:+CMD read:+CMD") == 0);
return 0;
}

View File

@@ -1,236 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char cmd_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int var_x;
static struct cat_object at;
static cat_return_state ret;
static bool ret_error;
static cat_return_state cmd_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
strcat(cmd_results, " read:");
strcat(cmd_results, cmd->name);
var_x++;
if (var_x > 5) {
ret = (ret_error == false) ? CAT_RETURN_STATE_HOLD_EXIT_OK : CAT_RETURN_STATE_HOLD_EXIT_ERROR;
} else {
if (var_x == 5) {
strcpy(data, "test");
*data_size = strlen(data);
}
ret = CAT_RETURN_STATE_DATA_NEXT;
}
return ret;
}
static cat_return_state cmd_run(const struct cat_command *cmd)
{
strcat(cmd_results, " run:");
strcat(cmd_results, cmd->name);
if ((ret == CAT_RETURN_STATE_DATA_NEXT) || (ret == CAT_RETURN_STATE_NEXT)) {
var_x++;
if (var_x > 3)
ret = CAT_RETURN_STATE_DATA_OK;
} else if (ret == CAT_RETURN_STATE_HOLD) {
cat_trigger_unsolicited_read(&at, cmd);
}
return ret;
}
static struct cat_variable vars[] = {
{
.name = "X",
.type = CAT_VAR_INT_DEC,
.data = &var_x,
.data_size = sizeof(var_x)
}
};
static struct cat_command cmds[] = {
{
.name = "+CMD",
.run = cmd_run,
.read = cmd_read,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0]),
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
var_x = 2;
memset(ack_results, 0, sizeof(ack_results));
memset(cmd_results, 0, sizeof(cmd_results));
}
static const char test_case_1[] = "\nAT+CMD\n";
int main(int argc, char **argv)
{
cat_status s;
cat_init(&at, &desc, &iface, NULL);
ret = CAT_RETURN_STATE_ERROR;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n") == 0);
assert(strcmp(cmd_results, " run:+CMD") == 0);
assert(var_x == 2);
ret = CAT_RETURN_STATE_DATA_OK;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n") == 0);
assert(strcmp(cmd_results, " run:+CMD") == 0);
assert(var_x == 2);
ret = CAT_RETURN_STATE_DATA_NEXT;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n") == 0);
assert(strcmp(cmd_results, " run:+CMD run:+CMD") == 0);
assert(var_x == 4);
ret = CAT_RETURN_STATE_NEXT;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n") == 0);
assert(strcmp(cmd_results, " run:+CMD run:+CMD") == 0);
assert(var_x == 4);
ret = CAT_RETURN_STATE_OK;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n") == 0);
assert(strcmp(cmd_results, " run:+CMD") == 0);
assert(var_x == 2);
ret_error = false;
ret = CAT_RETURN_STATE_HOLD;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+CMD=2\n\n+CMD=3\n\ntest\n\nOK\n") == 0);
assert(strcmp(cmd_results, " run:+CMD read:+CMD read:+CMD read:+CMD read:+CMD") == 0);
assert(var_x == 6);
ret_error = true;
ret = CAT_RETURN_STATE_HOLD;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+CMD=2\n\n+CMD=3\n\ntest\n\nERROR\n") == 0);
assert(strcmp(cmd_results, " run:+CMD read:+CMD read:+CMD read:+CMD read:+CMD") == 0);
assert(var_x == 6);
ret = CAT_RETURN_STATE_HOLD_EXIT_ERROR;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n") == 0);
assert(strcmp(cmd_results, " run:+CMD") == 0);
assert(var_x == 2);
ret = CAT_RETURN_STATE_HOLD_EXIT_OK;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n") == 0);
assert(strcmp(cmd_results, " run:+CMD") == 0);
assert(var_x == 2);
return 0;
}

View File

@@ -1,206 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char cmd_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int var_x;
static struct cat_object at;
static cat_return_state ret;
static bool ret_error;
static cat_return_state cmd_test(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
strcat(cmd_results, " test:");
strcat(cmd_results, cmd->name);
if (cat_is_hold(&at) == CAT_STATUS_HOLD) {
var_x++;
if (var_x > 4) {
ret = (ret_error == false) ? CAT_RETURN_STATE_HOLD_EXIT_OK : CAT_RETURN_STATE_HOLD_EXIT_ERROR;
} else {
if (var_x == 4) {
strcpy(data, "test");
*data_size = strlen(data);
}
ret = CAT_RETURN_STATE_DATA_NEXT;
}
} else {
if ((ret == CAT_RETURN_STATE_DATA_NEXT) || (ret == CAT_RETURN_STATE_NEXT)) {
var_x++;
if (var_x > 2)
ret = CAT_RETURN_STATE_DATA_OK;
} else if (ret == CAT_RETURN_STATE_HOLD) {
cat_trigger_unsolicited_test(&at, cmd);
}
}
return ret;
}
static struct cat_variable vars[] = {
{
.name = "X",
.type = CAT_VAR_INT_DEC,
.data = &var_x,
.data_size = sizeof(var_x)
}
};
static struct cat_command cmds[] = {
{
.name = "+CMD",
.test = cmd_test,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0]),
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
var_x = 1;
memset(ack_results, 0, sizeof(ack_results));
memset(cmd_results, 0, sizeof(cmd_results));
}
static const char test_case_1[] = "\nAT+CMD=?\n";
int main(int argc, char **argv)
{
cat_status s;
cat_init(&at, &desc, &iface, NULL);
ret = CAT_RETURN_STATE_ERROR;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n") == 0);
assert(strcmp(cmd_results, " test:+CMD") == 0);
ret = CAT_RETURN_STATE_DATA_OK;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+CMD=<X:INT32[RW]>\n\nOK\n") == 0);
assert(strcmp(cmd_results, " test:+CMD") == 0);
ret = CAT_RETURN_STATE_DATA_NEXT;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+CMD=<X:INT32[RW]>\n\n+CMD=<X:INT32[RW]>\n\nOK\n") == 0);
assert(strcmp(cmd_results, " test:+CMD test:+CMD") == 0);
ret = CAT_RETURN_STATE_NEXT;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+CMD=<X:INT32[RW]>\n\nOK\n") == 0);
assert(strcmp(cmd_results, " test:+CMD test:+CMD") == 0);
ret = CAT_RETURN_STATE_OK;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n") == 0);
assert(strcmp(cmd_results, " test:+CMD") == 0);
ret_error = false;
ret = CAT_RETURN_STATE_HOLD;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+CMD=<X:INT32[RW]>\n\n+CMD=<X:INT32[RW]>\n\ntest\n\nOK\n") == 0);
assert(strcmp(cmd_results, " test:+CMD test:+CMD test:+CMD test:+CMD test:+CMD") == 0);
ret_error = true;
ret = CAT_RETURN_STATE_HOLD;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+CMD=<X:INT32[RW]>\n\n+CMD=<X:INT32[RW]>\n\ntest\n\nERROR\n") == 0);
assert(strcmp(cmd_results, " test:+CMD test:+CMD test:+CMD test:+CMD test:+CMD") == 0);
return 0;
}

View File

@@ -1,236 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char cmd_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int var_x;
static struct cat_object at;
static cat_return_state ret;
static bool ret_error;
static cat_return_state cmd_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
strcat(cmd_results, " read:");
strcat(cmd_results, cmd->name);
var_x++;
if (var_x > 5) {
ret = (ret_error == false) ? CAT_RETURN_STATE_HOLD_EXIT_OK : CAT_RETURN_STATE_HOLD_EXIT_ERROR;
} else {
if (var_x == 5) {
strcpy(data, "test");
*data_size = strlen(data);
}
ret = CAT_RETURN_STATE_DATA_NEXT;
}
return ret;
}
static cat_return_state cmd_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
strcat(cmd_results, " write:");
strcat(cmd_results, cmd->name);
if ((ret == CAT_RETURN_STATE_DATA_NEXT) || (ret == CAT_RETURN_STATE_NEXT)) {
var_x++;
if (var_x > 3)
ret = CAT_RETURN_STATE_DATA_OK;
} else if (ret == CAT_RETURN_STATE_HOLD) {
cat_trigger_unsolicited_read(&at, cmd);
}
return ret;
}
static struct cat_variable vars[] = {
{
.name = "X",
.type = CAT_VAR_INT_DEC,
.data = &var_x,
.data_size = sizeof(var_x)
}
};
static struct cat_command cmds[] = {
{
.name = "+CMD",
.write = cmd_write,
.read = cmd_read,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0]),
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
var_x = 1;
memset(ack_results, 0, sizeof(ack_results));
memset(cmd_results, 0, sizeof(cmd_results));
}
static const char test_case_1[] = "\nAT+CMD=2\n";
int main(int argc, char **argv)
{
cat_status s;
cat_init(&at, &desc, &iface, NULL);
ret = CAT_RETURN_STATE_ERROR;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n") == 0);
assert(strcmp(cmd_results, " write:+CMD") == 0);
assert(var_x == 2);
ret = CAT_RETURN_STATE_DATA_OK;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n") == 0);
assert(strcmp(cmd_results, " write:+CMD") == 0);
assert(var_x == 2);
ret = CAT_RETURN_STATE_DATA_NEXT;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n") == 0);
assert(strcmp(cmd_results, " write:+CMD write:+CMD") == 0);
assert(var_x == 4);
ret = CAT_RETURN_STATE_NEXT;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n") == 0);
assert(strcmp(cmd_results, " write:+CMD write:+CMD") == 0);
assert(var_x == 4);
ret = CAT_RETURN_STATE_OK;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n") == 0);
assert(strcmp(cmd_results, " write:+CMD") == 0);
assert(var_x == 2);
ret_error = false;
ret = CAT_RETURN_STATE_HOLD;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+CMD=2\n\n+CMD=3\n\ntest\n\nOK\n") == 0);
assert(strcmp(cmd_results, " write:+CMD read:+CMD read:+CMD read:+CMD read:+CMD") == 0);
assert(var_x == 6);
ret_error = true;
ret = CAT_RETURN_STATE_HOLD;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+CMD=2\n\n+CMD=3\n\ntest\n\nERROR\n") == 0);
assert(strcmp(cmd_results, " write:+CMD read:+CMD read:+CMD read:+CMD read:+CMD") == 0);
assert(var_x == 6);
ret = CAT_RETURN_STATE_HOLD_EXIT_ERROR;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n") == 0);
assert(strcmp(cmd_results, " write:+CMD") == 0);
assert(var_x == 2);
ret = CAT_RETURN_STATE_HOLD_EXIT_OK;
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n") == 0);
assert(strcmp(cmd_results, " write:+CMD") == 0);
assert(var_x == 2);
return 0;
}

View File

@@ -1,157 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char run_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int a_run(const struct cat_command *cmd)
{
strcat(run_results, " A:");
strcat(run_results, cmd->name);
return 0;
}
static int ap_run(const struct cat_command *cmd)
{
strcat(run_results, " AP:");
strcat(run_results, cmd->name);
return 0;
}
static int test_run(const struct cat_command *cmd)
{
strcat(run_results, " +TEST:");
strcat(run_results, cmd->name);
return 0;
}
static int force_run(const struct cat_command *cmd)
{
strcat(run_results, " FORCE:");
strcat(run_results, cmd->name);
return -1;
}
static struct cat_command cmds[] = {
{
.name = "A",
.run = a_run
},
{
.name = "AP",
.run = ap_run
},
{
.name = "+TEST",
.run = test_run
},
{
.name = "+EMPTY"
},
{
.name = "FORCE",
.run = force_run,
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf)
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
memset(run_results, 0, sizeof(run_results));
memset(ack_results, 0, sizeof(ack_results));
}
static const char test_case_1[] = "\nAT\nAT+\nATA\r\nATAP\nATAPA\nAT+TEST\nAT+te\nAT+e\nAT+empTY\naTf\nAtFoRcE\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n\nERROR\n\r\nOK\r\n\nOK\n\nERROR\n\nOK\n\nOK\n\nERROR\n\nERROR\n\nERROR\n\nERROR\n") == 0);
assert(strcmp(run_results, " A:A AP:AP +TEST:+TEST +TEST:+TEST FORCE:FORCE FORCE:FORCE") == 0);
return 0;
}

View File

@@ -1,224 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static int ap_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
return 0;
}
static int ap_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
return 0;
}
static struct cat_variable vars_ap1[] = {
{
.name = "var_ap1_1"
},
{
.name = "var_ap1_2"
},
{
.name = "var_ap1_3"
}
};
static struct cat_variable vars_apx2[] = {
{
.name = "var_apx2_1"
},
{
.name = "var_apx2_2"
},
{
.name = "var_apx2_3"
}
};
static struct cat_command cmds[] = {
{
.name = "AP1",
.write = ap_write,
.only_test = true,
.var = vars_ap1,
.var_num = sizeof(vars_ap1) / sizeof(vars_ap1[0])
},
{
.name = "AP2",
.read = ap_read,
.only_test = false
},
};
static struct cat_command cmds2[] = {
{
.name = "APX1",
.write = ap_write,
.only_test = true
},
{
.name = "APX2",
.read = ap_read,
.only_test = false,
.var = vars_apx2,
.var_num = sizeof(vars_apx2) / sizeof(vars_apx2[0])
},
};
static char buf[128];
static struct cat_command_group cmd_group1 = {
.name = "std",
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group cmd_group2 = {
.name = "ext",
.cmd = cmds2,
.cmd_num = sizeof(cmds2) / sizeof(cmds2[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group1,
&cmd_group2
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf)
};
static int write_char(char ch)
{
return 1;
}
static int read_char(char *ch)
{
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
int main(int argc, char **argv)
{
struct cat_object at;
struct cat_command const *cmd;
struct cat_command_group const *cmd_group;
struct cat_variable const *var;
cat_init(&at, &desc, &iface, NULL);
cmd = cat_search_command_by_name(&at, "A");
assert(cmd == NULL);
cmd = cat_search_command_by_name(&at, "AP");
assert(cmd == NULL);
cmd = cat_search_command_by_name(&at, "AP1");
assert(cmd == &cmds[0]);
cmd = cat_search_command_by_name(&at, "AP2");
assert(cmd == &cmds[1]);
cmd = cat_search_command_by_name(&at, "AP3");
assert(cmd == NULL);
cmd = cat_search_command_by_name(&at, "APX1");
assert(cmd == &cmds2[0]);
cmd = cat_search_command_by_name(&at, "APX2");
assert(cmd == &cmds2[1]);
cmd = cat_search_command_by_name(&at, "APX3");
assert(cmd == NULL);
cmd_group = cat_search_command_group_by_name(&at, "std");
assert(cmd_group == cmd_desc[0]);
cmd_group = cat_search_command_group_by_name(&at, "ext");
assert(cmd_group == cmd_desc[1]);
cmd_group = cat_search_command_group_by_name(&at, "not");
assert(cmd_group == NULL);
var = cat_search_variable_by_name(&at, &cmds[0], "v");
assert(var == NULL);
var = cat_search_variable_by_name(&at, &cmds[0], "var_ap");
assert(var == NULL);
var = cat_search_variable_by_name(&at, &cmds[0], "var_apx2");
assert(var == NULL);
var = cat_search_variable_by_name(&at, &cmds[0], "var_ap1_1");
assert(var == &vars_ap1[0]);
var = cat_search_variable_by_name(&at, &cmds[0], "var_ap1_2");
assert(var == &vars_ap1[1]);
var = cat_search_variable_by_name(&at, &cmds[0], "var_ap1_3");
assert(var == &vars_ap1[2]);
var = cat_search_variable_by_name(&at, &cmds[0], "var_ap1_4");
assert(var == NULL);
var = cat_search_variable_by_name(&at, &cmds[1], "var_ap1_1");
assert(var == NULL);
var = cat_search_variable_by_name(&at, &cmds2[1], "var_apx2_1");
assert(var == &vars_apx2[0]);
var = cat_search_variable_by_name(&at, &cmds2[1], "var_apx2_2");
assert(var == &vars_apx2[1]);
var = cat_search_variable_by_name(&at, &cmds2[1], "var_apx2_3");
assert(var == &vars_apx2[2]);
var = cat_search_variable_by_name(&at, &cmds2[1], "var_apx2_4");
assert(var == NULL);
var = cat_search_variable_by_name(&at, &cmds2[0], "var_apx2_1");
assert(var == NULL);
return 0;
}

View File

@@ -1,149 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char run_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int print_name(const struct cat_command *cmd)
{
strcat(run_results, cmd->name);
strcat(run_results, " ");
return 0;
}
static struct cat_command cmds[] = {
{
.name = "+TEST",
.run = print_name
},
{
.name = "+TEST_A",
.run = print_name
},
{
.name = "+TEST_B",
.run = print_name
},
{
.name = "+ONE",
.run = print_name
},
{
.name = "+TWO",
.run = print_name
},
};
static char buf[256];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf)
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
memset(run_results, 0, sizeof(run_results));
memset(ack_results, 0, sizeof(ack_results));
}
static const char test_case_1[] = "\nAT\nAT+\nAT+T\nAT+TE\nAT+TES\nAT+TEST\nAT+TEST_\nAT+TEST_A\nAT+TEST_B\nAT+O\nAT+ON\nAT+ONE\nAT+TW\nAT+TWO\n";
static void print_raw_text(char *p)
{
while (*p != '\0') {
if (*p == '\n') {
printf("\\n");
} else {
putchar(*p);
}
p++;
}
}
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n\nERROR\n\nERROR\n\nERROR\n\nERROR\n\nOK\n\nERROR\n\nOK\n\nOK\n\nOK\n\nOK\n\nOK\n\nOK\n\nOK\n") == 0);
assert(strcmp(run_results, "+TEST +TEST_A +TEST_B +ONE +ONE +ONE +TWO +TWO ") == 0);
return 0;
}

View File

@@ -1,183 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char test_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int a_test(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
strcat(test_results, " A:");
strcat(test_results, cmd->name);
snprintf(data, max_data_size, "%s=A-val", cmd->name);
*data_size = strlen(data);
return 0;
}
static int ap_test(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
strcat(test_results, " AP:");
strcat(test_results, cmd->name);
*data_size = 0;
return 0;
}
static int ap_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
strcat(test_results, " AP_W:");
strcat(test_results, cmd->name);
assert(args_num == 0);
assert(data[0] == 'a');
assert(data_size == 1);
return 0;
}
static int apw_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
strcat(test_results, " APW:");
strcat(test_results, cmd->name);
assert(args_num == 0);
assert(data[0] == '?');
assert(data_size == 1);
return 0;
}
static int test_test(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
strcat(test_results, " +TEST:");
strcat(test_results, cmd->name);
return -1;
}
static struct cat_command cmds[] = {
{
.name = "A",
.test = a_test
},
{
.name = "AP",
.test = ap_test,
.write = ap_write
},
{
.name = "APW",
.write = apw_write
},
{
.name = "+TEST",
.test = test_test
},
{
.name = "+EMPTY"
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf)
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
memset(ack_results, 0, sizeof(ack_results));
memset(test_results, 0, sizeof(test_results));
}
static const char test_case_1[] = "\nAT\r\nAT\nATAP=?\nATAP=?a\nATAP=a\nATAPW=?\nAT+TEST=?\nATA=?\nAT+EMPTY=?\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\r\nOK\r\n\nOK\n\nAP=\n\nOK\n\nERROR\n\nOK\n\nOK\n\nERROR\n\nA=A-val\n\nOK\n\nERROR\n") == 0);
assert(strcmp(test_results, " AP:AP AP_W:AP APW:APW +TEST:+TEST A:A") == 0);
return 0;
}

View File

@@ -1,459 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char ack_results[256];
static int8_t var_int8;
static int16_t var_int16;
static int32_t var_int32;
static uint8_t var_uint8;
static uint16_t var_uint16;
static uint32_t var_uint32;
static uint8_t var_hex8;
static uint16_t var_hex16;
static uint32_t var_hex32;
static uint8_t var_buf[4];
static char var_string[16];
static char const *input_text;
static size_t input_index;
static int cmd_override_test(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
strcat(data, "\ntest");
*data_size = strlen(data);
return 0;
}
static int cmd_error_test(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
return -1;
}
static int cmd_ok_test(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
strcpy(data, "test1");
*data_size = strlen(data);
return 0;
}
static int cmd_ok2_test(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
strcat(data, "test2");
*data_size = strlen(data);
return 0;
}
static struct cat_variable vars[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var_int8,
.data_size = sizeof(var_int8),
.name = "x"
},
{
.type = CAT_VAR_INT_DEC,
.data = &var_int16,
.data_size = sizeof(var_int16),
.name = "y"
},
{
.type = CAT_VAR_INT_DEC,
.data = &var_int32,
.data_size = sizeof(var_int32)
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint8,
.data_size = sizeof(var_uint8)
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint16,
.data_size = sizeof(var_uint16)
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint32,
.data_size = sizeof(var_uint32)
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex8,
.data_size = sizeof(var_hex8)
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex16,
.data_size = sizeof(var_hex16)
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex32,
.data_size = sizeof(var_hex32)
},
{
.type = CAT_VAR_BUF_HEX,
.data = &var_buf,
.data_size = sizeof(var_buf)
},
{
.type = CAT_VAR_BUF_STRING,
.data = &var_string,
.data_size = sizeof(var_string),
.name = "msg"
}
};
static struct cat_variable vars_ro[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var_int8,
.data_size = sizeof(var_int8),
.name = "x",
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_INT_DEC,
.data = &var_int16,
.data_size = sizeof(var_int16),
.name = "y",
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_INT_DEC,
.data = &var_int32,
.data_size = sizeof(var_int32),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint8,
.data_size = sizeof(var_uint8),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint16,
.data_size = sizeof(var_uint16),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint32,
.data_size = sizeof(var_uint32),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex8,
.data_size = sizeof(var_hex8),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex16,
.data_size = sizeof(var_hex16),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex32,
.data_size = sizeof(var_hex32),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_BUF_HEX,
.data = &var_buf,
.data_size = sizeof(var_buf),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_BUF_STRING,
.data = &var_string,
.data_size = sizeof(var_string),
.name = "msg",
.access = CAT_VAR_ACCESS_READ_ONLY
}
};
static struct cat_variable vars_wo[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var_int8,
.data_size = sizeof(var_int8),
.name = "x",
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_INT_DEC,
.data = &var_int16,
.data_size = sizeof(var_int16),
.name = "y",
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_INT_DEC,
.data = &var_int32,
.data_size = sizeof(var_int32),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint8,
.data_size = sizeof(var_uint8),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint16,
.data_size = sizeof(var_uint16),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint32,
.data_size = sizeof(var_uint32),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex8,
.data_size = sizeof(var_hex8),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex16,
.data_size = sizeof(var_hex16),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex32,
.data_size = sizeof(var_hex32),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_BUF_HEX,
.data = &var_buf,
.data_size = sizeof(var_buf),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_BUF_STRING,
.data = &var_string,
.data_size = sizeof(var_string),
.name = "msg",
.access = CAT_VAR_ACCESS_WRITE_ONLY
}
};
static struct cat_variable vars2[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var_int8,
.data_size = sizeof(var_int8),
.name = "var"
}
};
static struct cat_command cmds[] = {
{
.name = "+SET",
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0])
},
{
.name = "+SETRO",
.var = vars_ro,
.var_num = sizeof(vars_ro) / sizeof(vars_ro[0])
},
{
.name = "+SETWO",
.var = vars_wo,
.var_num = sizeof(vars_wo) / sizeof(vars_wo[0])
},
{
.name = "+TEST",
.description = "test_desc",
.test = cmd_override_test,
.var = vars2,
.var_num = sizeof(vars2) / sizeof(vars2[0])
},
{
.name = "+TEST2",
.description = "test2_desc",
.var = vars2,
.var_num = sizeof(vars2) / sizeof(vars2[0])
},
{
.name = "+AP",
.test = cmd_error_test,
.var = vars2,
.var_num = sizeof(vars2) / sizeof(vars2[0])
},
{
.name = "+ZZ",
.test = cmd_ok_test,
},
{
.name = "+ZZ2",
.description = "zz2_desc",
.test = cmd_ok_test,
},
{
.name = "+ZZ3",
.description = "zz3_desc",
.test = cmd_ok2_test,
}
};
static char buf[256];
static char unsolicited_buf[256];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
.unsolicited_buf = unsolicited_buf,
.unsolicited_buf_size = sizeof(unsolicited_buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
var_int8 = -8;
var_int16 = -16;
var_int32 = -32;
var_uint8 = 8;
var_uint8 = 16;
var_uint8 = 32;
var_hex8 = 0x08;
var_hex16 = 0x16;
var_hex32 = 0x32;
var_hex16 = 0x0123;
var_hex32 = 0xFF001234;
var_buf[0] = 0x12;
var_buf[1] = 0x34;
var_buf[2] = 0x56;
var_buf[3] = 0x78;
sprintf(var_string, "TST");
memset(ack_results, 0, sizeof(ack_results));
}
static const char test_case_1[] = "\nAT+SET=?\n";
static const char test_case_1_ro[] = "\nAT+SETRO=?\n";
static const char test_case_1_wo[] = "\nAT+SETWO=?\n";
static const char test_case_2[] = "\nAT+TEST=?\nAT+TEST2=?\r\nAT+AP=?\n";
static const char test_case_3[] = "\nAT+ZZ=?\nAT+ZZ2=?\nAT+ZZ3=?\r\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+SET=<x:INT8[RW]>,<y:INT16[RW]>,<INT32[RW]>,<UINT8[RW]>,<UINT16[RW]>,<UINT32[RW]>,<HEX8[RW]>,<HEX16[RW]>,<HEX32[RW]>,<HEXBUF[RW]>,<msg:STRING[RW]>\n\nOK\n") == 0);
prepare_input(test_case_1_ro);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+SETRO=<x:INT8[RO]>,<y:INT16[RO]>,<INT32[RO]>,<UINT8[RO]>,<UINT16[RO]>,<UINT32[RO]>,<HEX8[RO]>,<HEX16[RO]>,<HEX32[RO]>,<HEXBUF[RO]>,<msg:STRING[RO]>\n\nOK\n") == 0);
prepare_input(test_case_1_wo);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+SETWO=<x:INT8[WO]>,<y:INT16[WO]>,<INT32[WO]>,<UINT8[WO]>,<UINT16[WO]>,<UINT32[WO]>,<HEX8[WO]>,<HEX16[WO]>,<HEX32[WO]>,<HEXBUF[WO]>,<msg:STRING[WO]>\n\nOK\n") == 0);
prepare_input(test_case_2);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+TEST=<var:INT8[RW]>\ntest_desc\ntest\n\nOK\n\r\n+TEST2=<var:INT8[RW]>\r\ntest2_desc\r\n\r\nOK\r\n\nERROR\n") == 0);
prepare_input(test_case_3);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\ntest1\n\nOK\n\ntest1\n\nOK\n\r\n+ZZ3=\r\nzz3_desctest2\r\n\r\nOK\r\n") == 0);
return 0;
}

View File

@@ -1,173 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char cmd_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int ap_test(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
strcat(cmd_results, " test:");
strcat(cmd_results, cmd->name);
strcpy(data, "ap_test");
*data_size = strlen(data);
return 0;
}
static int ap_run(const struct cat_command *cmd)
{
strcat(cmd_results, " run:");
strcat(cmd_results, cmd->name);
return 0;
}
static int ap_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
strcat(cmd_results, " read:");
strcat(cmd_results, cmd->name);
strcpy(data, "ap_read");
*data_size = strlen(data);
return 0;
}
static int ap_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
strcat(cmd_results, " write:");
strcat(cmd_results, cmd->name);
assert(strcmp(data, "1") == 0);
return 0;
}
static struct cat_command cmds[] = {
{
.name = "AP1",
.test = ap_test,
.write = ap_write,
.read = ap_read,
.run = ap_run,
.only_test = true
},
{
.name = "AP2",
.test = ap_test,
.write = ap_write,
.read = ap_read,
.run = ap_run,
.only_test = false
},
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf)
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
memset(ack_results, 0, sizeof(ack_results));
memset(cmd_results, 0, sizeof(cmd_results));
}
static const char test_case_1[] = "\nATAP1=?\n\nATAP1?\n\nATAP1=1\n\nATAP1\n";
static const char test_case_2[] = "\nATAP2=?\n\nATAP2?\n\nATAP2=1\n\nATAP2\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nap_test\n\nOK\n\nERROR\n\nERROR\n\nERROR\n") == 0);
assert(strcmp(cmd_results, " test:AP1") == 0);
prepare_input(test_case_2);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nap_test\n\nOK\n\nap_read\n\nOK\n\nOK\n\nOK\n") == 0);
assert(strcmp(cmd_results, " test:AP2 read:AP2 write:AP2 run:AP2") == 0);
return 0;
}

View File

@@ -1,203 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char read_results[256];
static char var_read_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int var_x, var_u1, var_u2;
static struct cat_object at;
static struct cat_command u_cmds[];
static cat_return_state cmd_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
cat_status s;
strcat(read_results, " read:");
strcat(read_results, cmd->name);
if (strcmp(cmd->name, "+CMD") == 0) {
s = cat_trigger_unsolicited_read(&at, &u_cmds[1]);
assert(s == CAT_STATUS_OK);
}
return CAT_RETURN_STATE_DATA_OK;
}
static int var_read(const struct cat_variable *var)
{
strcat(var_read_results, " var_read:");
strcat(var_read_results, var->name);
return 0;
}
static struct cat_variable u_vars[] = {
{
.name = "U1",
.type = CAT_VAR_INT_DEC,
.data = &var_u1,
.data_size = sizeof(var_u1),
.read = var_read
},
{
.name = "U2",
.type = CAT_VAR_INT_DEC,
.data = &var_u2,
.data_size = sizeof(var_u2),
.read = var_read
}
};
static struct cat_variable vars[] = {
{
.name = "X",
.type = CAT_VAR_INT_DEC,
.data = &var_x,
.data_size = sizeof(var_x),
.read = var_read
}
};
static struct cat_command cmds[] = {
{
.name = "+CMD",
.read = cmd_read,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0]),
}
};
static struct cat_command u_cmds[] = {
{
.name = "+U1CMD",
.read = cmd_read,
.var = &u_vars[0],
.var_num = 1,
},
{
.name = "+U2CMD",
.read = cmd_read,
.var = &u_vars[1],
.var_num = 1,
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
var_x = 1;
var_u1 = 2;
var_u2 = 3;
memset(ack_results, 0, sizeof(ack_results));
memset(read_results, 0, sizeof(read_results));
memset(var_read_results, 0, sizeof(var_read_results));
}
static const char test_case_1[] = "\nAT+CMD?\n";
int main(int argc, char **argv)
{
cat_status s;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
s = cat_is_unsolicited_buffer_full(&at);
assert(s == CAT_STATUS_OK);
s = cat_trigger_unsolicited_event(&at, &u_cmds[0], CAT_CMD_TYPE_READ);
assert(s == CAT_STATUS_OK);
s = cat_is_unsolicited_buffer_full(&at);
assert(s == CAT_STATUS_ERROR_BUFFER_FULL);
s = cat_trigger_unsolicited_read(&at, &u_cmds[1]);
assert(s == CAT_STATUS_ERROR_BUFFER_FULL);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+U1CMD=2\n\n+CMD=1\n\n+U2CMD=3\n\nOK\n") == 0);
assert(strcmp(read_results, " read:+U1CMD read:+CMD read:+U2CMD") == 0);
assert(strcmp(var_read_results, " var_read:U1 var_read:X var_read:U2") == 0);
return 0;
}

View File

@@ -1,238 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char read_results[256];
static char var_read_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int var_x, var_u1, var_u2;
static struct cat_object at;
static struct cat_command u_cmds[];
static cat_return_state cmd_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
cat_status s;
strcat(read_results, " read:");
strcat(read_results, cmd->name);
if (strcmp(cmd->name, "+CMD") == 0) {
s = cat_trigger_unsolicited_read(&at, &u_cmds[1]);
assert(s == CAT_STATUS_OK);
}
return CAT_RETURN_STATE_DATA_OK;
}
static int var_read(const struct cat_variable *var)
{
strcat(var_read_results, " var_read:");
strcat(var_read_results, var->name);
return 0;
}
static struct cat_variable u_vars[] = {
{
.name = "U1",
.type = CAT_VAR_INT_DEC,
.data = &var_u1,
.data_size = sizeof(var_u1),
.read = var_read
},
{
.name = "U2",
.type = CAT_VAR_INT_DEC,
.data = &var_u2,
.data_size = sizeof(var_u2),
.read = var_read
}
};
static struct cat_variable vars[] = {
{
.name = "X",
.type = CAT_VAR_INT_DEC,
.data = &var_x,
.data_size = sizeof(var_x),
.read = var_read
}
};
static struct cat_command cmds[] = {
{
.name = "+CMD",
.read = cmd_read,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0]),
}
};
static struct cat_command u_cmds[] = {
{
.name = "+U1CMD",
.read = cmd_read,
.var = &u_vars[0],
.var_num = 1,
},
{
.name = "+U2CMD",
.read = cmd_read,
.var = &u_vars[1],
.var_num = 1,
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
var_x = 1;
var_u1 = 2;
var_u2 = 3;
memset(ack_results, 0, sizeof(ack_results));
memset(read_results, 0, sizeof(read_results));
memset(var_read_results, 0, sizeof(var_read_results));
}
static const char test_case_1[] = "\nAT+CMD?\n";
int main(int argc, char **argv)
{
cat_status s;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
s = cat_is_unsolicited_buffer_full(&at);
assert(s == CAT_STATUS_OK);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[0], CAT_CMD_TYPE_READ);
assert(s == CAT_STATUS_OK);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[0], CAT_CMD_TYPE_NONE);
assert(s == CAT_STATUS_OK);
s = cat_trigger_unsolicited_event(&at, &u_cmds[0], CAT_CMD_TYPE_READ);
assert(s == CAT_STATUS_OK);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[0], CAT_CMD_TYPE_READ);
assert(s == CAT_STATUS_BUSY);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[0], CAT_CMD_TYPE_TEST);
assert(s == CAT_STATUS_OK);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[0], CAT_CMD_TYPE_NONE);
assert(s == CAT_STATUS_BUSY);
s = cat_is_unsolicited_buffer_full(&at);
assert(s == CAT_STATUS_OK);
s = cat_trigger_unsolicited_event(&at, &u_cmds[1], CAT_CMD_TYPE_READ);
assert(s == CAT_STATUS_OK);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[0], CAT_CMD_TYPE_READ);
assert(s == CAT_STATUS_BUSY);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[0], CAT_CMD_TYPE_TEST);
assert(s == CAT_STATUS_OK);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[0], CAT_CMD_TYPE_NONE);
assert(s == CAT_STATUS_BUSY);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[1], CAT_CMD_TYPE_READ);
assert(s == CAT_STATUS_BUSY);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[1], CAT_CMD_TYPE_TEST);
assert(s == CAT_STATUS_OK);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[1], CAT_CMD_TYPE_NONE);
assert(s == CAT_STATUS_BUSY);
s = cat_is_unsolicited_buffer_full(&at);
assert(s == CAT_STATUS_ERROR_BUFFER_FULL);
s = cat_trigger_unsolicited_read(&at, &u_cmds[1]);
assert(s == CAT_STATUS_ERROR_BUFFER_FULL);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[0], CAT_CMD_TYPE_READ);
assert(s == CAT_STATUS_BUSY);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[0], CAT_CMD_TYPE_TEST);
assert(s == CAT_STATUS_OK);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[1], CAT_CMD_TYPE_NONE);
assert(s == CAT_STATUS_BUSY);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+U1CMD=2\n\n+CMD=1\n\n+U2CMD=3\n\nOK\n\n+U2CMD=3\n") == 0);
assert(strcmp(read_results, " read:+U1CMD read:+CMD read:+U2CMD read:+U2CMD") == 0);
assert(strcmp(var_read_results, " var_read:U1 var_read:X var_read:U2 var_read:U2") == 0);
s = cat_is_unsolicited_buffer_full(&at);
assert(s == CAT_STATUS_OK);
return 0;
}

View File

@@ -1,211 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char read_results[256];
static char var_read_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int var_x, var_u1;
static struct cat_object at;
static struct cat_command u_cmds[];
static cat_return_state cmd_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
cat_status s;
strcat(read_results, " read:");
strcat(read_results, cmd->name);
return CAT_RETURN_STATE_DATA_OK;
}
static int var_read(const struct cat_variable *var)
{
strcat(var_read_results, " var_read:");
strcat(var_read_results, var->name);
return 0;
}
static struct cat_variable u_vars[] = {
{
.name = "U1",
.type = CAT_VAR_INT_DEC,
.data = &var_u1,
.data_size = sizeof(var_u1),
.read = var_read
}
};
static struct cat_variable vars[] = {
{
.name = "X",
.type = CAT_VAR_INT_DEC,
.data = &var_x,
.data_size = sizeof(var_x),
.read = var_read
}
};
static struct cat_command cmds[] = {
{
.name = "+CMD",
.read = cmd_read,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0]),
}
};
static struct cat_command u_cmds[] = {
{
.name = "+UCMD",
.read = cmd_read,
.var = u_vars,
.var_num = sizeof(u_vars) / sizeof(u_vars[0]),
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
var_x = 1;
var_u1 = 2;
memset(ack_results, 0, sizeof(ack_results));
memset(read_results, 0, sizeof(read_results));
memset(var_read_results, 0, sizeof(var_read_results));
}
static const char test_case_1[] = "\nAT+CMD?\n";
int main(int argc, char **argv)
{
cat_status s;
int events = 4;
struct cat_command const *cmd;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
cmd = cat_get_processed_command(&at, CAT_FSM_TYPE_ATCMD);
assert(cmd == NULL);
cmd = cat_get_processed_command(&at, CAT_FSM_TYPE_UNSOLICITED);
assert(cmd == NULL);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[0], CAT_CMD_TYPE_READ);
assert(s == CAT_STATUS_OK);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[0], CAT_CMD_TYPE_NONE);
assert(s == CAT_STATUS_OK);
while (events > 0) {
s = cat_is_unsolicited_buffer_full(&at);
cmd = cat_get_processed_command(&at, CAT_FSM_TYPE_UNSOLICITED);
if ((s == CAT_STATUS_OK) && (cmd == NULL)) {
var_u1 = events;
s = cat_trigger_unsolicited_event(&at, &u_cmds[0], CAT_CMD_TYPE_READ);
assert(s == CAT_STATUS_OK);
events--;
} else {
assert(cmd == &u_cmds[0]);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[0], CAT_CMD_TYPE_READ);
assert(s == CAT_STATUS_BUSY);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[0], CAT_CMD_TYPE_TEST);
assert(s == CAT_STATUS_OK);
s = cat_is_unsolicited_event_buffered(&at, &u_cmds[0], CAT_CMD_TYPE_NONE);
assert(s == CAT_STATUS_BUSY);
}
s = cat_service(&at);
assert(s == CAT_STATUS_BUSY);
}
while (cat_service(&at) != 0) {};
cmd = cat_get_processed_command(&at, CAT_FSM_TYPE_ATCMD);
assert(cmd == NULL);
cmd = cat_get_processed_command(&at, CAT_FSM_TYPE_UNSOLICITED);
assert(cmd == NULL);
assert(strcmp(ack_results, "\n+UCMD=4\n\n+CMD=1\n\n+UCMD=3\n\nOK\n\n+UCMD=2\n\n+UCMD=1\n") == 0);
assert(strcmp(read_results, " read:+UCMD read:+CMD read:+UCMD read:+UCMD read:+UCMD") == 0);
assert(strcmp(var_read_results, " var_read:U1 var_read:X var_read:U1 var_read:U1 var_read:U1") == 0);
return 0;
}

View File

@@ -1,189 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char read_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int var_x, var_u1, var_u2;
static struct cat_object at;
static struct cat_command u_cmds[];
static cat_return_state cmd_test(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
cat_status s;
strcat(read_results, " test:");
strcat(read_results, cmd->name);
if (strcmp(cmd->name, "+CMD") == 0) {
s = cat_trigger_unsolicited_test(&at, &u_cmds[1]);
assert(s == CAT_STATUS_OK);
}
return CAT_RETURN_STATE_DATA_OK;
}
static struct cat_variable u_vars[] = {
{
.name = "U1",
.type = CAT_VAR_INT_DEC,
.data = &var_u1,
.data_size = sizeof(var_u1)
},
{
.name = "U2",
.type = CAT_VAR_INT_DEC,
.data = &var_u2,
.data_size = sizeof(var_u2)
}
};
static struct cat_variable vars[] = {
{
.name = "X",
.type = CAT_VAR_INT_DEC,
.data = &var_x,
.data_size = sizeof(var_x)
}
};
static struct cat_command cmds[] = {
{
.name = "+CMD",
.test = cmd_test,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0]),
}
};
static struct cat_command u_cmds[] = {
{
.name = "+U1CMD",
.test = cmd_test,
.var = &u_vars[0],
.var_num = 1,
},
{
.name = "+U2CMD",
.test = cmd_test,
.var = &u_vars[1],
.var_num = 1,
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
var_x = 1;
var_u1 = 2;
var_u2 = 3;
memset(ack_results, 0, sizeof(ack_results));
memset(read_results, 0, sizeof(read_results));
}
static const char test_case_1[] = "\nAT+CMD=?\n";
int main(int argc, char **argv)
{
cat_status s;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
s = cat_is_unsolicited_buffer_full(&at);
assert(s == CAT_STATUS_OK);
s = cat_trigger_unsolicited_event(&at, &u_cmds[0], CAT_CMD_TYPE_TEST);
assert(s == CAT_STATUS_OK);
s = cat_is_unsolicited_buffer_full(&at);
assert(s == CAT_STATUS_ERROR_BUFFER_FULL);
s = cat_trigger_unsolicited_test(&at, &u_cmds[1]);
assert(s == CAT_STATUS_ERROR_BUFFER_FULL);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+U1CMD=<U1:INT32[RW]>\n\n+CMD=<X:INT32[RW]>\n\n+U2CMD=<U2:INT32[RW]>\n\nOK\n") == 0);
assert(strcmp(read_results, " test:+U1CMD test:+CMD test:+U2CMD") == 0);
return 0;
}

View File

@@ -1,545 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char ack_results[256];
static int8_t var1;
static int8_t var2;
static int8_t var3;
static char const *input_text;
static size_t input_index;
static int var2_write_cntr;
static int var3_read_cntr;
static int8_t var_int8;
static int16_t var_int16;
static int32_t var_int32;
static uint8_t var_uint8;
static uint16_t var_uint16;
static uint32_t var_uint32;
static uint8_t var_hex8;
static uint16_t var_hex16;
static uint32_t var_hex32;
static uint8_t var_buf[4];
static char var_string[16];
static int cmd_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
return 0;
}
static int cmd_read(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
return 0;
}
static int cmd_test(const struct cat_command *cmd, uint8_t *data, size_t *data_size, const size_t max_data_size)
{
return 0;
}
static int cmd_run(const struct cat_command *cmd)
{
return 0;
}
static int var1_write(const struct cat_variable *var, size_t write_size)
{
return 0;
}
static int var1_read(const struct cat_variable *var)
{
return 0;
}
static int var2_write(const struct cat_variable *var, size_t write_size)
{
var2_write_cntr++;
return 0;
}
static int var2_read(const struct cat_variable *var)
{
return 0;
}
static int var3_write(const struct cat_variable *var, size_t write_size)
{
return 0;
}
static int var3_read(const struct cat_variable *var)
{
var3_read_cntr++;
return 0;
}
static int print_cmd_list(const struct cat_command *cmd)
{
return CAT_RETURN_STATE_PRINT_CMD_LIST_OK;
}
static struct cat_variable vars_ro[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var2,
.data_size = sizeof(var2),
.write = var2_write,
.read = var2_read,
.access = CAT_VAR_ACCESS_READ_ONLY
}
};
static struct cat_variable vars_wo[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var3,
.data_size = sizeof(var3),
.write = var3_write,
.read = var3_read,
.access = CAT_VAR_ACCESS_WRITE_ONLY
}
};
static struct cat_variable vars[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var1,
.data_size = sizeof(var1),
.write = var1_write,
.read = var1_read,
.access = CAT_VAR_ACCESS_READ_WRITE
},
{
.type = CAT_VAR_INT_DEC,
.data = &var2,
.data_size = sizeof(var2),
.write = var2_write,
.read = var2_read,
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_INT_DEC,
.data = &var3,
.data_size = sizeof(var3),
.write = var3_write,
.read = var3_read,
.access = CAT_VAR_ACCESS_WRITE_ONLY
}
};
static struct cat_variable vars_misc_ro[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var1,
.data_size = sizeof(var1),
.access = CAT_VAR_ACCESS_READ_WRITE
},
{
.type = CAT_VAR_INT_DEC,
.data = &var_int8,
.data_size = sizeof(var_int8),
.name = "x",
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_INT_DEC,
.data = &var_int16,
.data_size = sizeof(var_int16),
.name = "y",
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_INT_DEC,
.data = &var_int32,
.data_size = sizeof(var_int32),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint8,
.data_size = sizeof(var_uint8),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint16,
.data_size = sizeof(var_uint16),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint32,
.data_size = sizeof(var_uint32),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex8,
.data_size = sizeof(var_hex8),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex16,
.data_size = sizeof(var_hex16),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex32,
.data_size = sizeof(var_hex32),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_BUF_HEX,
.data = &var_buf,
.data_size = sizeof(var_buf),
.access = CAT_VAR_ACCESS_READ_ONLY
},
{
.type = CAT_VAR_BUF_STRING,
.data = &var_string,
.data_size = sizeof(var_string),
.name = "msg",
.access = CAT_VAR_ACCESS_READ_ONLY
}
};
static struct cat_variable vars_misc_wo[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var1,
.data_size = sizeof(var1),
.access = CAT_VAR_ACCESS_READ_WRITE
},
{
.type = CAT_VAR_INT_DEC,
.data = &var_int8,
.data_size = sizeof(var_int8),
.name = "x",
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_INT_DEC,
.data = &var_int16,
.data_size = sizeof(var_int16),
.name = "y",
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_INT_DEC,
.data = &var_int32,
.data_size = sizeof(var_int32),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint8,
.data_size = sizeof(var_uint8),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint16,
.data_size = sizeof(var_uint16),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var_uint32,
.data_size = sizeof(var_uint32),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex8,
.data_size = sizeof(var_hex8),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex16,
.data_size = sizeof(var_hex16),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var_hex32,
.data_size = sizeof(var_hex32),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_BUF_HEX,
.data = &var_buf,
.data_size = sizeof(var_buf),
.access = CAT_VAR_ACCESS_WRITE_ONLY
},
{
.type = CAT_VAR_BUF_STRING,
.data = &var_string,
.data_size = sizeof(var_string),
.name = "msg",
.access = CAT_VAR_ACCESS_WRITE_ONLY
}
};
static struct cat_command cmds[] = {
{
.name = "+VRW",
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0])
},
{
.name = "+VRO",
.var = vars_ro,
.var_num = sizeof(vars_ro) / sizeof(vars_ro[0])
},
{
.name = "+VWO",
.var = vars_wo,
.var_num = sizeof(vars_wo) / sizeof(vars_wo[0])
},
{
.name = "+MRO",
.var = vars_misc_ro,
.var_num = sizeof(vars_misc_ro) / sizeof(vars_misc_ro[0])
},
{
.name = "+MWO",
.var = vars_misc_wo,
.var_num = sizeof(vars_misc_wo) / sizeof(vars_misc_wo[0])
},
{
.name = "#HELP",
.run = print_cmd_list,
}
};
static char buf[256];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
memset(ack_results, 0, sizeof(ack_results));
}
static void print_raw_text(char *p)
{
while (*p != '\0') {
if (*p == '\n') {
printf("\\n");
} else {
putchar(*p);
}
p++;
}
}
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input("\nAT#HELP\n");
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nAT+VRW?\nAT+VRW=\nAT+VRW=?\n\nAT+VRO?\nAT+VRO=?\n\nAT+VWO=\nAT+VWO=?\n\nAT+MRO?\nAT+MRO=\nAT+MRO=?\n\nAT+MWO?\nAT+MWO=\nAT+MWO=?\n\nAT#HELP\n\nOK\n") == 0);
prepare_input("\nAT+VRW=?\n");
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+VRW=<INT8[RW]>,<INT8[RO]>,<INT8[WO]>\n\nOK\n") == 0);
prepare_input("\nAT+VRO=?\n");
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+VRO=<INT8[RO]>\n\nOK\n") == 0);
prepare_input("\nAT+VWO=?\n");
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+VWO=<INT8[WO]>\n\nOK\n") == 0);
var2 = 1;
prepare_input("\nAT+VRO=1\n");
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n") == 0);
assert(var2 == 1);
var3 = 3;
prepare_input("\nAT+VWO?\n");
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n") == 0);
assert(var3 == 3);
var1 = -1;
var2 = -2;
var3 = -3;
var2_write_cntr = 0;
var3_read_cntr = 0;
prepare_input("\nAT+VRW?\n");
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+VRW=-1,-2,0\n\nOK\n") == 0);
assert(var2_write_cntr == 0);
assert(var3_read_cntr == 1);
prepare_input("\nAT+VRW=1,2,3\n");
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n") == 0);
assert(var2_write_cntr == 1);
assert(var3_read_cntr == 1);
assert(var1 == 1);
assert(var2 == -2);
assert(var3 == 3);
var1 = 100;
var_int8 = 1;
var_int16 = 2;
var_int32 = 3;
var_uint8 = 4;
var_uint16 = 5;
var_uint32 = 6;
var_hex8 = 7;
var_hex16 = 8;
var_hex32 = 9;
var_buf[0] = 0x10;
var_buf[1] = 0x11;
var_buf[2] = 0x12;
var_buf[3] = 0x13;
strcpy(var_string, "test_string");
prepare_input("\nAT+MWO?\n");
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+MWO=100,0,0,0,0,0,0,0x00,0x0000,0x00000000,00000000,\"\"\n\nOK\n") == 0);
prepare_input("\nAT+MWO=1,2,3,4,5,6,7,0x08,0x0009,0x0000000A,01020304,\"abc\"\n");
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n") == 0);
assert(var1 == 1);
assert(var_int8 == 2);
assert(var_int16 == 3);
assert(var_int32 == 4);
assert(var_uint8 == 5);
assert(var_uint16 == 6);
assert(var_uint32 == 7);
assert(var_hex8 == 8);
assert(var_hex16 == 9);
assert(var_hex32 == 10);
assert(var_buf[0] == 0x01);
assert(var_buf[1] == 0x02);
assert(var_buf[2] == 0x03);
assert(var_buf[3] == 0x04);
assert(strcmp(var_string, "abc") == 0);
prepare_input("\nAT+MRO?\n");
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\n+MRO=1,2,3,4,5,6,7,0x08,0x0009,0x0000000A,01020304,\"abc\"\n\nOK\n") == 0);
prepare_input("\nAT+MRO=2,0,0,0,0,0,0,0x00,0x0000,0x00000000,00000000,\"cba\"\n");
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n") == 0);
assert(var1 == 2);
assert(var_int8 == 2);
assert(var_int16 == 3);
assert(var_int32 == 4);
assert(var_uint8 == 5);
assert(var_uint16 == 6);
assert(var_uint32 == 7);
assert(var_hex8 == 8);
assert(var_hex16 == 9);
assert(var_hex32 == 10);
assert(var_buf[0] == 0x01);
assert(var_buf[1] == 0x02);
assert(var_buf[2] == 0x03);
assert(var_buf[3] == 0x04);
assert(strcmp(var_string, "abc") == 0);
return 0;
}

View File

@@ -1,166 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char run_results[256];
static char write_results[256];
static char ack_results[256];
static char const *input_text;
static size_t input_index;
static int a_run(const struct cat_command *cmd)
{
strcat(run_results, " A_");
strcat(run_results, cmd->name);
return 0;
}
static int a_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
strcat(write_results, " A:");
strncat(write_results, data, data_size);
assert(args_num == 0);
return 0;
}
static int ap_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
strcat(write_results, " AP:");
strncat(write_results, data, data_size);
assert(args_num == 0);
return 0;
}
static int test_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
strcat(write_results, " +TEST:");
strncat(write_results, data, data_size);
assert(args_num == 0);
return -1;
}
static struct cat_command cmds[] = {
{
.name = "A",
.write = a_write,
.run = a_run
},
{
.name = "AP",
.write = ap_write
},
{
.name = "+TEST",
.write = test_write
},
{
.name = "+EMPTY"
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf)
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
memset(run_results, 0, sizeof(run_results));
memset(ack_results, 0, sizeof(ack_results));
memset(write_results, 0, sizeof(write_results));
}
static const char test_case_1[] = "\nAT\nAT+\nAT+?\nATA=123\r\nATA=\nATAP?\nATAP=11\r22\r\nAT+TEST=456\nAT+te=789\nAT+e=1\nAT+empTY=2\r\nATA\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n\nERROR\n\nERROR\n\r\nOK\r\n\nOK\n\nERROR\n\r\nOK\r\n\nERROR\n\nERROR\n\nERROR\n\r\nERROR\r\n\nOK\n") == 0);
assert(strcmp(run_results, " A_A") == 0);
assert(strcmp(write_results, " A:123 A: AP:1122 +TEST:456 +TEST:789") == 0);
return 0;
}

View File

@@ -1,174 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char write_results[256];
static char ack_results[256];
static uint8_t var[4];
static size_t var_write_size[4];
static int var_write_size_index;
static char const *input_text;
static size_t input_index;
static int cmd_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
strcat(write_results, " CMD:");
strncat(write_results, data, data_size);
return 0;
}
static int var_write(const struct cat_variable *var, size_t write_size)
{
var_write_size[var_write_size_index++] = write_size;
return 0;
}
static struct cat_variable vars[] = {
{
.type = CAT_VAR_BUF_HEX,
.data = var,
.data_size = sizeof(var),
.write = var_write
}
};
static struct cat_command cmds[] = {
{
.name = "+SET",
.write = cmd_write,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0])
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf)
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
memset(var, 0, sizeof(var));
memset(var_write_size, 0, sizeof(var_write_size));
var_write_size_index = 0;
memset(ack_results, 0, sizeof(ack_results));
memset(write_results, 0, sizeof(write_results));
}
static const char test_case_1[] = "\nAT+SET=0\nAT+SET=aa\nAT+SET=001\nAT+SET=12345678\nAT+SET=ffAA\n";
static const char test_case_2[] = "\nAT+SET=0x11\nAT+SET=11\nAT+SET=-1\nAT+SET=87654321\nAT+SET=0001\nAT+SET=1122334455\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n\nOK\n\nERROR\n\nOK\n\nOK\n") == 0);
assert(strcmp(write_results, " CMD:aa CMD:12345678 CMD:ffAA") == 0);
assert(var[0] == 0xFF);
assert(var[1] == 0xAA);
assert(var[2] == 0x56);
assert(var[3] == 0x78);
assert(var_write_size[0] == 1);
assert(var_write_size[1] == 4);
assert(var_write_size[2] == 2);
assert(var_write_size[3] == 0);
prepare_input(test_case_2);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n\nOK\n\nERROR\n\nOK\n\nOK\n\nERROR\n") == 0);
assert(strcmp(write_results, " CMD:11 CMD:87654321 CMD:0001") == 0);
assert(var[0] == 0x11);
assert(var[1] == 0x22);
assert(var[2] == 0x33);
assert(var[3] == 0x44);
assert(var_write_size[0] == 1);
assert(var_write_size[1] == 4);
assert(var_write_size[2] == 2);
assert(var_write_size[3] == 0);
return 0;
}

View File

@@ -1,212 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char write_results[256];
static char ack_results[256];
static uint8_t var1, var1b;
static uint16_t var2, var2b;
static uint32_t var3, var3b;
static char const *input_text;
static size_t input_index;
static int cmd_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
strcat(write_results, " CMD:");
strncat(write_results, data, data_size);
return 0;
}
static int var1_write(const struct cat_variable *var, size_t write_size)
{
assert(write_size == 1);
var1b = *(uint8_t*)(var->data);
return 0;
}
static int var2_write(const struct cat_variable *var, size_t write_size)
{
assert(write_size == 2);
var2b = *(uint16_t*)(var->data);
return 0;
}
static int var3_write(const struct cat_variable *var, size_t write_size)
{
assert(write_size == 4);
var3b = *(uint32_t*)(var->data);
return 0;
}
static struct cat_variable vars[] = {
{
.type = CAT_VAR_NUM_HEX,
.data = &var1,
.data_size = sizeof(var1),
.write = var1_write
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var2,
.data_size = sizeof(var2),
.write = var2_write
},
{
.type = CAT_VAR_NUM_HEX,
.data = &var3,
.data_size = sizeof(var3),
.write = var3_write
}
};
static struct cat_command cmds[] = {
{
.name = "+SET",
.write = cmd_write,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0])
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf)
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
var1 = 1;
var2 = 2;
var3 = 3;
var1b = 10;
var2b = 20;
var3b = 30;
memset(ack_results, 0, sizeof(ack_results));
memset(write_results, 0, sizeof(write_results));
}
static const char test_case_1[] = "\nAT+SET=0\nAT+SET=0x0\nAT+SET=0x01\nAT+SET=0x0ff\nAT+SET=0x100\n";
static const char test_case_2[] = "\nAT+SET=0x,0x00\nAT+SET=0x1,0x00\nAT+SET=0x2,0xFFf\nAT+SET=0x3,0xFFFF\nAT+SET=0x4,0xFFFFF\n";
static const char test_case_3[] = "\nAT+SET=0x0,0x0,0\nAT+SET=0x0,0x0,0x0000000000000\nAT+SET=0x0,0x0,0x1\nAT+SET=0x0,0x0,0xffffFFFF\nAT+SET=0x10,0x20,0x100000000\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n\nOK\n\nOK\n\nOK\n\nERROR\n") == 0);
assert(strcmp(write_results, " CMD:0x0 CMD:0x01 CMD:0x0ff") == 0);
assert(var1 == 255);
assert(var1b == var1);
assert(var2 == 2);
assert(var2b == 20);
assert(var3 == 3);
assert(var3b == 30);
prepare_input(test_case_2);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n\nOK\n\nOK\n\nOK\n\nERROR\n") == 0);
assert(strcmp(write_results, " CMD:0x1,0x00 CMD:0x2,0xFFf CMD:0x3,0xFFFF") == 0);
assert(var1 == 4);
assert(var1b == var1);
assert(var2 == 0xFFFF);
assert(var2b == var2);
assert(var3 == 3);
assert(var3b == 30);
prepare_input(test_case_3);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n\nOK\n\nOK\n\nOK\n\nERROR\n") == 0);
assert(strcmp(write_results, " CMD:0x0,0x0,0x0000000000000 CMD:0x0,0x0,0x1 CMD:0x0,0x0,0xffffFFFF") == 0);
assert(var1 == 0x10);
assert(var1b == var1);
assert(var2 == 0x20);
assert(var2b == var2);
assert(var3 == 0xFFFFFFFF);
assert(var3b == var3);
return 0;
}

View File

@@ -1,212 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char write_results[256];
static char ack_results[256];
static int8_t var1, var1b;
static int16_t var2, var2b;
static int32_t var3, var3b;
static char const *input_text;
static size_t input_index;
static int cmd_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
strcat(write_results, " CMD:");
strncat(write_results, data, data_size);
return 0;
}
static int var1_write(const struct cat_variable *var, size_t write_size)
{
assert(write_size == 1);
var1b = *(int8_t*)(var->data);
return 0;
}
static int var2_write(const struct cat_variable *var, size_t write_size)
{
assert(write_size == 2);
var2b = *(int16_t*)(var->data);
return 0;
}
static int var3_write(const struct cat_variable *var, size_t write_size)
{
assert(write_size == 4);
var3b = *(int32_t*)(var->data);
return 0;
}
static struct cat_variable vars[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var1,
.data_size = sizeof(var1),
.write = var1_write
},
{
.type = CAT_VAR_INT_DEC,
.data = &var2,
.data_size = sizeof(var2),
.write = var2_write
},
{
.type = CAT_VAR_INT_DEC,
.data = &var3,
.data_size = sizeof(var3),
.write = var3_write
}
};
static struct cat_command cmds[] = {
{
.name = "+SET",
.write = cmd_write,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0])
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf),
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
var1 = 1;
var2 = 2;
var3 = 3;
var1b = -1;
var2b = -2;
var3b = -3;
memset(ack_results, 0, sizeof(ack_results));
memset(write_results, 0, sizeof(write_results));
}
static const char test_case_1[] = "\nAT+SET=-128\nAT+SET=-129\nAT+SET=127\nAT+SET=128\n";
static const char test_case_2[] = "\nAT+SET=-128,-32768\nAT+SET=-128,-40000\nAT+SET=-128,32767\nAT+SET=-100,40000\n";
static const char test_case_3[] = "\nAT+SET=0,0,-2147483648\nAT+SET=0,0,-2147483649\nAT+SET=1,1,2147483647\nAT+SET=2,2,2147483648\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n\nERROR\n\nOK\n\nERROR\n") == 0);
assert(strcmp(write_results, " CMD:-128 CMD:127") == 0);
assert(var1 == 127);
assert(var1b == var1);
assert(var2 == 2);
assert(var2b == -2);
assert(var3 == 3);
assert(var3b == -3);
prepare_input(test_case_2);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n\nERROR\n\nOK\n\nERROR\n") == 0);
assert(strcmp(write_results, " CMD:-128,-32768 CMD:-128,32767") == 0);
assert(var1 == -100);
assert(var1b == var1);
assert(var2 == 32767);
assert(var2b == var2);
assert(var3 == 3);
assert(var3b == -3);
prepare_input(test_case_3);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n\nERROR\n\nOK\n\nERROR\n") == 0);
assert(strcmp(write_results, " CMD:0,0,-2147483648 CMD:1,1,2147483647") == 0);
assert(var1 == 2);
assert(var1b == var1);
assert(var2 == 2);
assert(var2b == var2);
assert(var3 == 2147483647);
assert(var3b == var3);
return 0;
}

View File

@@ -1,234 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char write_results[256];
static char ack_results[256];
static int8_t var1, var2, var3;
static int8_t var1b, var2b, var3b;
static char const *input_text;
static size_t input_index;
static int cmd_write1(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
char tmp[32];
sprintf(tmp, " CMD1_%ld:", args_num);
strcat(write_results, tmp);
strncat(write_results, data, data_size);
return 0;
}
static int cmd_write3(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
char tmp[32];
sprintf(tmp, " CMD3_%ld:", args_num);
strcat(write_results, tmp);
strncat(write_results, data, data_size);
return 0;
}
static int var1_write(const struct cat_variable *var, size_t write_size)
{
assert(write_size == 1);
var1b = *(int8_t*)(var->data);
return 0;
}
static int var2_write(const struct cat_variable *var, size_t write_size)
{
assert(write_size == 1);
var2b = *(int8_t*)(var->data);
return 0;
}
static int var3_write(const struct cat_variable *var, size_t write_size)
{
assert(write_size == 1);
var3b = *(int8_t*)(var->data);
return 0;
}
static struct cat_variable vars[] = {
{
.type = CAT_VAR_INT_DEC,
.data = &var1,
.data_size = sizeof(var1),
.write = var1_write
},
{
.type = CAT_VAR_INT_DEC,
.data = &var2,
.data_size = sizeof(var2),
.write = var2_write
},
{
.type = CAT_VAR_INT_DEC,
.data = &var3,
.data_size = sizeof(var3),
.write = var3_write
}
};
static struct cat_command cmds[] = {
{
.name = "+SET1",
.write = cmd_write1,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0])
},
{
.name = "+SET3",
.write = cmd_write3,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0])
},
{
.name = "+SETALL",
.write = cmd_write3,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0]),
.need_all_vars = true
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf)
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
var1 = 1;
var2 = 2;
var3 = 3;
memset(ack_results, 0, sizeof(ack_results));
memset(write_results, 0, sizeof(write_results));
}
static const char test_case_1[] = "\nAT+SET=-10,-20,-30\r\nAT+SET1=-10,-20,-30\r\nAT+SET1=-1\r\n";
static const char test_case_2[] = "\nAT+SET3=-1,-2,-3,0\nAT+SET3=-1,-2,-3\nAT+SET3=-100\n";
static const char test_case_3[] = "\nAT+SETALL=-11,-22,-33\nAT+SETALL=-1,-2,-3\nAT+SETALL=100\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\r\nERROR\r\n\r\nOK\r\n\r\nOK\r\n") == 0);
assert(strcmp(write_results, " CMD1_3:-10,-20,-30 CMD1_1:-1") == 0);
assert(var1 == -1);
assert(var2 == -20);
assert(var3 == -30);
assert(var1b == -1);
assert(var2b == -20);
assert(var3b == -30);
prepare_input(test_case_2);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n\nOK\n\nOK\n") == 0);
assert(strcmp(write_results, " CMD3_3:-1,-2,-3 CMD3_1:-100") == 0);
assert(var1 == -100);
assert(var2 == -2);
assert(var3 == -3);
assert(var1b == -100);
assert(var2b == -2);
assert(var3b == -3);
prepare_input(test_case_3);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nOK\n\nOK\n\nERROR\n") == 0);
assert(strcmp(write_results, " CMD3_3:-11,-22,-33 CMD3_3:-1,-2,-3") == 0);
assert(var1 == 100);
assert(var2 == -2);
assert(var3 == -3);
assert(var1b == 100);
assert(var2b == -2);
assert(var3b == -3);
return 0;
}

View File

@@ -1,168 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char write_results[256];
static char ack_results[256];
static uint8_t var[8];
static size_t var_write_size[4];
static int var_write_size_index;
static char const *input_text;
static size_t input_index;
static int cmd_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
strcat(write_results, " CMD:");
strncat(write_results, data, data_size);
return 0;
}
static int var_write(const struct cat_variable *var, size_t write_size)
{
var_write_size[var_write_size_index++] = write_size;
return 0;
}
static struct cat_variable vars[] = {
{
.type = CAT_VAR_BUF_STRING,
.data = var,
.data_size = sizeof(var),
.write = var_write
}
};
static struct cat_command cmds[] = {
{
.name = "+SET",
.write = cmd_write,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0])
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf)
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
memset(var, 0, sizeof(var));
memset(var_write_size, 0, sizeof(var_write_size));
var_write_size_index = 0;
memset(ack_results, 0, sizeof(ack_results));
memset(write_results, 0, sizeof(write_results));
}
static const char test_case_1[] = "\nAT+SET=0\nAT+SET=\"\\\"abcd\\\"\"\nAT+SET=\"\"a\nAT+SET=\"1122334\"\nAT+SET=\"t\"\r\n";
static const char test_case_2[] = "\nAT+SET=\"12345678\"\nAT+SET=\"\"\nAT+SET=\"\\\\\\\\\"\nAT+SET=\"r1\\nr2\\n\"\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n\nOK\n\nERROR\n\nOK\n\r\nOK\r\n") == 0);
assert(strcmp(write_results, " CMD:\"\\\"abcd\\\"\" CMD:\"1122334\" CMD:\"t\"") == 0);
assert(strcmp(var, "t") == 0);
assert(var_write_size[0] == 6);
assert(var_write_size[1] == 7);
assert(var_write_size[2] == 1);
assert(var_write_size[3] == 0);
prepare_input(test_case_2);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n\nOK\n\nOK\n\nOK\n") == 0);
assert(strcmp(write_results, " CMD:\"\" CMD:\"\\\\\\\\\" CMD:\"r1\\nr2\\n\"") == 0);
assert(strcmp(var, "r1\nr2\n") == 0);
assert(var_write_size[0] == 0);
assert(var_write_size[1] == 2);
assert(var_write_size[2] == 6);
assert(var_write_size[3] == 0);
return 0;
}

View File

@@ -1,212 +0,0 @@
/*
MIT License
Copyright (c) 2019 Marcin Borowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "../src/cat.h"
static char write_results[256];
static char ack_results[256];
static uint8_t var1, var1b;
static uint16_t var2, var2b;
static uint32_t var3, var3b;
static char const *input_text;
static size_t input_index;
static int cmd_write(const struct cat_command *cmd, const uint8_t *data, const size_t data_size, const size_t args_num)
{
strcat(write_results, " CMD:");
strncat(write_results, data, data_size);
return 0;
}
static int var1_write(const struct cat_variable *var, size_t write_size)
{
assert(write_size == 1);
var1b = *(uint8_t*)(var->data);
return 0;
}
static int var2_write(const struct cat_variable *var, size_t write_size)
{
assert(write_size == 2);
var2b = *(uint16_t*)(var->data);
return 0;
}
static int var3_write(const struct cat_variable *var, size_t write_size)
{
assert(write_size == 4);
var3b = *(uint32_t*)(var->data);
return 0;
}
static struct cat_variable vars[] = {
{
.type = CAT_VAR_UINT_DEC,
.data = &var1,
.data_size = sizeof(var1),
.write = var1_write
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var2,
.data_size = sizeof(var2),
.write = var2_write
},
{
.type = CAT_VAR_UINT_DEC,
.data = &var3,
.data_size = sizeof(var3),
.write = var3_write
}
};
static struct cat_command cmds[] = {
{
.name = "+SET",
.write = cmd_write,
.var = vars,
.var_num = sizeof(vars) / sizeof(vars[0])
}
};
static char buf[128];
static struct cat_command_group cmd_group = {
.cmd = cmds,
.cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};
static struct cat_command_group *cmd_desc[] = {
&cmd_group
};
static struct cat_descriptor desc = {
.cmd_group = cmd_desc,
.cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),
.buf = buf,
.buf_size = sizeof(buf)
};
static int write_char(char ch)
{
char str[2];
str[0] = ch;
str[1] = 0;
strcat(ack_results, str);
return 1;
}
static int read_char(char *ch)
{
if (input_index >= strlen(input_text))
return 0;
*ch = input_text[input_index];
input_index++;
return 1;
}
static struct cat_io_interface iface = {
.read = read_char,
.write = write_char
};
static void prepare_input(const char *text)
{
input_text = text;
input_index = 0;
var1 = 1;
var2 = 2;
var3 = 3;
var1b = 10;
var2b = 20;
var3b = 30;
memset(ack_results, 0, sizeof(ack_results));
memset(write_results, 0, sizeof(write_results));
}
static const char test_case_1[] = "\nAT+SET=-128\nAT+SET=0\nAT+SET=255\nAT+SET=256\n";
static const char test_case_2[] = "\nAT+SET=0,-1\nAT+SET=0,0\nAT+SET=0,65535\nAT+SET=1,65536\n";
static const char test_case_3[] = "\nAT+SET=0,0,-1\nAT+SET=0,0,0\nAT+SET=1,1,4294967295\nAT+SET=2,2,4294967296\n";
int main(int argc, char **argv)
{
struct cat_object at;
cat_init(&at, &desc, &iface, NULL);
prepare_input(test_case_1);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n\nOK\n\nOK\n\nERROR\n") == 0);
assert(strcmp(write_results, " CMD:0 CMD:255") == 0);
assert(var1 == 255);
assert(var1b == var1);
assert(var2 == 2);
assert(var2b == 20);
assert(var3 == 3);
assert(var3b == 30);
prepare_input(test_case_2);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n\nOK\n\nOK\n\nERROR\n") == 0);
assert(strcmp(write_results, " CMD:0,0 CMD:0,65535") == 0);
assert(var1 == 1);
assert(var1b == var1);
assert(var2 == 65535);
assert(var2b == var2);
assert(var3 == 3);
assert(var3b == 30);
prepare_input(test_case_3);
while (cat_service(&at) != 0) {};
assert(strcmp(ack_results, "\nERROR\n\nOK\n\nOK\n\nERROR\n") == 0);
assert(strcmp(write_results, " CMD:0,0,0 CMD:1,1,4294967295") == 0);
assert(var1 == 2);
assert(var1b == var1);
assert(var2 == 2);
assert(var2b == var2);
assert(var3 == 4294967295);
assert(var3b == var3);
return 0;
}

View File

@@ -1,6 +0,0 @@
#include "api.h"
void api_init(void)
{
//modbus_init();
}

View File

@@ -1,538 +0,0 @@
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "esp_wifi.h"
#include "esp_ota_ops.h"
#include "esp_timer.h"
#include "esp_chip_info.h"
#include "esp_mac.h"
#include "json.h"
#include "mqtt.h"
#include "wifi.h"
#include "timeout_utils.h"
#include "evse_error.h"
#include "evse_api.h"
#include "ocpp.h"
#include "board_config.h"
#include "socket_lock.h"
#include "proximity.h"
//#include "modbus.h"
//#include "modbus_tcp.h"
#include "rest.h"
#include "temp_sensor.h"
// #include "script.h"
#include "date_time.h"
#define RETURN_ON_ERROR(x) \
do \
{ \
esp_err_t err_rc_ = (x); \
if (unlikely(err_rc_ != ESP_OK)) \
{ \
return err_rc_; \
} \
} while (0)
cJSON *json_get_evse_config(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "maxChargingCurrent", evse_get_max_charging_current());
cJSON_AddNumberToObject(root, "chargingCurrent", evse_get_charging_current() / 10.0);
cJSON_AddNumberToObject(root, "defaultChargingCurrent", evse_get_default_charging_current() / 10.0);
cJSON_AddBoolToObject(root, "requireAuth", evse_is_require_auth());
cJSON_AddBoolToObject(root, "socketOutlet", evse_get_socket_outlet());
cJSON_AddBoolToObject(root, "rcm", evse_is_rcm());
cJSON_AddNumberToObject(root, "temperatureThreshold", evse_get_temp_threshold());
cJSON_AddNumberToObject(root, "consumptionLimit", evse_get_consumption_limit());
cJSON_AddNumberToObject(root, "defaultConsumptionLimit", evse_get_default_consumption_limit());
cJSON_AddNumberToObject(root, "chargingTimeLimit", evse_get_charging_time_limit());
cJSON_AddNumberToObject(root, "defaultChargingTimeLimit", evse_get_default_charging_time_limit());
cJSON_AddNumberToObject(root, "underPowerLimit", evse_get_under_power_limit());
cJSON_AddNumberToObject(root, "defaultUnderPowerLimit", evse_get_default_under_power_limit());
cJSON_AddNumberToObject(root, "socketLockOperatingTime", socket_lock_get_operating_time());
cJSON_AddNumberToObject(root, "socketLockBreakTime", socket_lock_get_break_time());
cJSON_AddBoolToObject(root, "socketLockDetectionHigh", socket_lock_is_detection_high());
cJSON_AddNumberToObject(root, "socketLockRetryCount", socket_lock_get_retry_count());
//cJSON_AddStringToObject(root, "modelMeter", meter_model_to_str(meter_get_model()));
//cJSON_AddNumberToObject(root, "maxGridCurrent", grid_get_max_current());
//cJSON_AddStringToObject(root, "stateMeter", meter_state_to_str(meter_get_state()));
char str[64];
cJSON_AddBoolToObject(root, "enabledocpp", ocpp_get_enabled());
ocpp_get_server(str);
cJSON_AddStringToObject(root, "serverocpp", str);
ocpp_get_rfid(str);
cJSON_AddStringToObject(root, "rfid", str);
return root;
}
esp_err_t json_set_evse_config(cJSON *root)
{
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "maxChargingCurrent")))
{
RETURN_ON_ERROR(evse_set_max_charging_current(cJSON_GetObjectItem(root, "maxChargingCurrent")->valuedouble));
}
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "chargingCurrent")))
{
RETURN_ON_ERROR(evse_set_charging_current(cJSON_GetObjectItem(root, "chargingCurrent")->valuedouble * 10));
}
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "defaultChargingCurrent")))
{
RETURN_ON_ERROR(evse_set_default_charging_current(cJSON_GetObjectItem(root, "defaultChargingCurrent")->valuedouble * 10));
}
if (cJSON_IsBool(cJSON_GetObjectItem(root, "requireAuth")))
{
evse_set_require_auth(cJSON_IsTrue(cJSON_GetObjectItem(root, "requireAuth")));
}
if (cJSON_IsBool(cJSON_GetObjectItem(root, "socketOutlet")))
{
RETURN_ON_ERROR(evse_set_socket_outlet(cJSON_IsTrue(cJSON_GetObjectItem(root, "socketOutlet"))));
}
if (cJSON_IsBool(cJSON_GetObjectItem(root, "rcm")))
{
RETURN_ON_ERROR(evse_set_rcm(cJSON_IsTrue(cJSON_GetObjectItem(root, "rcm"))));
}
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "temperatureThreshold")))
{
RETURN_ON_ERROR(evse_set_temp_threshold(cJSON_GetObjectItem(root, "temperatureThreshold")->valuedouble));
}
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "consumptionLimit")))
{
evse_set_consumption_limit(cJSON_GetObjectItem(root, "consumptionLimit")->valuedouble);
}
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "defaultConsumptionLimit")))
{
evse_set_default_consumption_limit(cJSON_GetObjectItem(root, "defaultConsumptionLimit")->valuedouble);
}
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "chargingTimeLimit")))
{
evse_set_charging_time_limit(cJSON_GetObjectItem(root, "chargingTimeLimit")->valuedouble);
}
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "defaultChargingTimeLimit")))
{
evse_set_default_charging_time_limit(cJSON_GetObjectItem(root, "defaultChargingTimeLimit")->valuedouble);
}
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "underPowerLimit")))
{
evse_set_under_power_limit(cJSON_GetObjectItem(root, "underPowerLimit")->valuedouble);
}
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "defaultUnderPowerLimit")))
{
evse_set_default_under_power_limit(cJSON_GetObjectItem(root, "defaultUnderPowerLimit")->valuedouble);
}
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "socketLockOperatingTime")))
{
RETURN_ON_ERROR(socket_lock_set_operating_time(cJSON_GetObjectItem(root, "socketLockOperatingTime")->valuedouble));
}
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "socketLockBreakTime")))
{
RETURN_ON_ERROR(socket_lock_set_break_time(cJSON_GetObjectItem(root, "socketLockBreakTime")->valuedouble));
}
if (cJSON_IsBool(cJSON_GetObjectItem(root, "socketLockDetectionHigh")))
{
socket_lock_set_detection_high(cJSON_IsTrue(cJSON_GetObjectItem(root, "socketLockDetectionHigh")));
}
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "socketLockRetryCount")))
{
socket_lock_set_retry_count(cJSON_GetObjectItem(root, "socketLockRetryCount")->valuedouble);
}
/*
if (cJSON_IsString(cJSON_GetObjectItem(root, "modelMeter")))
{
RETURN_ON_ERROR(meter_set_model(meter_str_to_model(cJSON_GetObjectItem(root, "modelMeter")->valuestring)));
}
if (cJSON_IsString(cJSON_GetObjectItem(root, "stateMeter")))
{
RETURN_ON_ERROR(meter_set_state(meter_str_to_state(cJSON_GetObjectItem(root, "stateMeter")->valuestring)));
}
*/
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "maxGridCurrent")))
{
RETURN_ON_ERROR(grid_set_max_current(cJSON_GetObjectItem(root, "maxGridCurrent")->valuedouble));
}
if (cJSON_IsBool(cJSON_GetObjectItem(root, "enabledocpp")))
{
ocpp_set_enabled(cJSON_IsTrue(cJSON_GetObjectItem(root, "enabledocpp")));
}
if (cJSON_IsString(cJSON_GetObjectItem(root, "serverocpp")))
{
ocpp_set_server(cJSON_GetStringValue(cJSON_GetObjectItem(root, "serverocpp")));
}
if (cJSON_IsString(cJSON_GetObjectItem(root, "rfid")))
{
ocpp_set_rfid(cJSON_GetStringValue(cJSON_GetObjectItem(root, "rfid")));
}
return ESP_OK;
}
cJSON *json_get_wifi_config(void)
{
cJSON *root = cJSON_CreateObject();
char str[32];
cJSON_AddBoolToObject(root, "enabled", wifi_get_enabled());
wifi_get_ssid(str);
cJSON_AddStringToObject(root, "ssid", str);
return root;
}
esp_err_t json_set_wifi_config(cJSON *root, bool timeout)
{
bool enabled = cJSON_IsTrue(cJSON_GetObjectItem(root, "enabled"));
char *ssid = cJSON_GetStringValue(cJSON_GetObjectItem(root, "ssid"));
char *password = cJSON_GetStringValue(cJSON_GetObjectItem(root, "password"));
if (timeout)
{
return timeout_wifi_set_config(enabled, ssid, password);
}
else
{
return wifi_set_config(enabled, ssid, password);
}
}
cJSON *json_get_wifi_scan(void)
{
cJSON *root = cJSON_CreateArray();
wifi_scan_ap_t scan_aps[WIFI_SCAN_SCAN_LIST_SIZE];
uint16_t number = wifi_scan(scan_aps);
for (int i = 0; i < number; i++)
{
cJSON *item = cJSON_CreateObject();
cJSON_AddStringToObject(item, "ssid", scan_aps[i].ssid);
cJSON_AddNumberToObject(item, "rssi", scan_aps[i].rssi);
cJSON_AddBoolToObject(item, "auth", scan_aps[i].auth);
cJSON_AddItemToArray(root, item);
}
return root;
}
cJSON *json_get_mqtt_config(void)
{
cJSON *root = cJSON_CreateObject();
char str[64];
cJSON_AddBoolToObject(root, "enabled", mqtt_get_enabled());
mqtt_get_server(str);
cJSON_AddStringToObject(root, "server", str);
mqtt_get_base_topic(str);
cJSON_AddStringToObject(root, "baseTopic", str);
mqtt_get_user(str);
cJSON_AddStringToObject(root, "user", str);
mqtt_get_password(str);
cJSON_AddStringToObject(root, "password", str);
cJSON_AddNumberToObject(root, "periodicity", mqtt_get_periodicity());
return root;
}
esp_err_t json_set_mqtt_config(cJSON *root)
{
bool enabled = cJSON_IsTrue(cJSON_GetObjectItem(root, "enabled"));
char *server = cJSON_GetStringValue(cJSON_GetObjectItem(root, "server"));
char *base_topic = cJSON_GetStringValue(cJSON_GetObjectItem(root, "baseTopic"));
char *user = cJSON_GetStringValue(cJSON_GetObjectItem(root, "user"));
char *password = cJSON_GetStringValue(cJSON_GetObjectItem(root, "password"));
uint16_t periodicity = cJSON_GetNumberValue(cJSON_GetObjectItem(root, "periodicity"));
return mqtt_set_config(enabled, server, base_topic, user, password, periodicity);
}
cJSON *json_get_modbus_config(void)
{
cJSON *root = cJSON_CreateObject();
//cJSON_AddBoolToObject(root, "tcpEnabled", modbus_tcp_is_enabled());
//cJSON_AddNumberToObject(root, "unitId", modbus_get_unit_id());
cJSON_AddBoolToObject(root, "tcpEnabled", false);
cJSON_AddNumberToObject(root, "unitId", 1);
return root;
}
esp_err_t json_set_modbus_config(cJSON *root)
{
//bool tcp_enabled = cJSON_IsTrue(cJSON_GetObjectItem(root, "tcpEnabled"));
//uint8_t unit_id = cJSON_GetObjectItem(root, "unitId")->valuedouble;
//modbus_tcp_set_enabled(tcp_enabled);
return 0; //modbus_set_unit_id(unit_id);
}
/*
cJSON* json_get_script_config(void)
{
cJSON* root = cJSON_CreateObject();
cJSON_AddBoolToObject(root, "enabled", script_is_enabled());
return root;
}
esp_err_t json_set_script_config(cJSON* root)
{
bool enabled = cJSON_IsTrue(cJSON_GetObjectItem(root, "enabled"));
script_set_enabled(enabled);
return ESP_OK;
}*/
cJSON *json_get_time_config(void)
{
cJSON *root = cJSON_CreateObject();
char str[64];
cJSON_AddBoolToObject(root, "ntpEnabled", date_time_is_ntp_enabled());
date_time_get_ntp_server(str);
cJSON_AddStringToObject(root, "ntpServer", str);
cJSON_AddBoolToObject(root, "ntpFromDhcp", date_time_is_ntp_from_dhcp());
date_time_get_timezone(str);
cJSON_AddStringToObject(root, "timezone", str);
return root;
}
esp_err_t json_set_time_config(cJSON *root)
{
char *ntp_server = cJSON_GetStringValue(cJSON_GetObjectItem(root, "ntpServer"));
bool ntp_enabled = cJSON_IsTrue(cJSON_GetObjectItem(root, "ntpEnabled"));
bool ntp_from_dhcp = cJSON_IsTrue(cJSON_GetObjectItem(root, "ntpFromDhcp"));
char *timezone = cJSON_GetStringValue(cJSON_GetObjectItem(root, "timezone"));
date_time_set_ntp_config(ntp_enabled, ntp_server, ntp_from_dhcp);
return date_time_set_timezone(timezone);
}
cJSON *json_get_state(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "state", evse_state_to_str(evse_get_state()));
cJSON_AddBoolToObject(root, "available", evse_is_available());
cJSON_AddBoolToObject(root, "enabled", evse_is_enabled());
cJSON_AddBoolToObject(root, "pendingAuth", evse_is_pending_auth());
cJSON_AddBoolToObject(root, "limitReached", evse_is_limit_reached());
uint32_t error = evse_error_get_bits();
if (error == 0)
{
cJSON_AddNullToObject(root, "errors");
}
else
{
cJSON *errors = cJSON_CreateArray();
if (error & EVSE_ERR_PILOT_FAULT_BIT)
{
cJSON_AddItemToArray(errors, cJSON_CreateString("pilot_fault"));
}
if (error & EVSE_ERR_DIODE_SHORT_BIT)
{
cJSON_AddItemToArray(errors, cJSON_CreateString("diode_short"));
}
if (error & EVSE_ERR_LOCK_FAULT_BIT)
{
cJSON_AddItemToArray(errors, cJSON_CreateString("lock_fault"));
}
if (error & EVSE_ERR_UNLOCK_FAULT_BIT)
{
cJSON_AddItemToArray(errors, cJSON_CreateString("unlock_fault"));
}
if (error & EVSE_ERR_RCM_TRIGGERED_BIT)
{
cJSON_AddItemToArray(errors, cJSON_CreateString("rcm_triggered"));
}
if (error & EVSE_ERR_RCM_SELFTEST_FAULT_BIT)
{
cJSON_AddItemToArray(errors, cJSON_CreateString("rcm_selftest_fault"));
}
if (error & EVSE_ERR_TEMPERATURE_HIGH_BIT)
{
cJSON_AddItemToArray(errors, cJSON_CreateString("temperature_high"));
}
if (error & EVSE_ERR_TEMPERATURE_FAULT_BIT)
{
cJSON_AddItemToArray(errors, cJSON_CreateString("temperature_fault"));
}
cJSON_AddItemToObject(root, "errors", errors);
}
/*
cJSON_AddNumberToObject(root, "sessionTime", energy_meter_get_session_time());
cJSON_AddNumberToObject(root, "chargingTime", energy_meter_get_charging_time());
cJSON_AddNumberToObject(root, "consumption", energy_meter_get_consumption());
cJSON_AddNumberToObject(root, "power", energy_meter_get_power());
float values[3];
energy_meter_get_voltage(values);
cJSON_AddItemToObject(root, "voltage", cJSON_CreateFloatArray(values, 3));
energy_meter_get_current(values);
cJSON_AddItemToObject(root, "current", cJSON_CreateFloatArray(values, 3));
*/
return root;
}
cJSON *json_get_info(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "uptime", esp_timer_get_time() / 1000000);
const esp_app_desc_t *app_desc = esp_app_get_description();
cJSON_AddStringToObject(root, "appVersion", app_desc->version);
cJSON_AddStringToObject(root, "appDate", app_desc->date);
cJSON_AddStringToObject(root, "appTime", app_desc->time);
cJSON_AddStringToObject(root, "idfVersion", app_desc->idf_ver);
cJSON_AddStringToObject(root, "chip", CONFIG_IDF_TARGET);
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
cJSON_AddNumberToObject(root, "chipCores", chip_info.cores);
chip_info.revision = 301;
cJSON_AddNumberToObject(root, "chipRevision", chip_info.revision / 100);
multi_heap_info_t heap_info;
heap_caps_get_info(&heap_info, MALLOC_CAP_INTERNAL);
cJSON_AddNumberToObject(root, "heapSize", heap_info.total_allocated_bytes);
cJSON_AddNumberToObject(root, "maxHeapSize", heap_info.total_free_bytes + heap_info.total_allocated_bytes);
uint8_t mac[6];
char str[32];
esp_wifi_get_mac(ESP_IF_WIFI_STA, mac);
sprintf(str, MACSTR, MAC2STR(mac));
cJSON_AddStringToObject(root, "mac", str);
esp_netif_ip_info_t ip_info;
esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info);
esp_ip4addr_ntoa(&ip_info.ip, str, sizeof(str));
cJSON_AddStringToObject(root, "ip", str);
esp_wifi_get_mac(ESP_IF_WIFI_AP, mac);
sprintf(str, MACSTR, MAC2STR(mac));
cJSON_AddStringToObject(root, "macAp", str);
esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"), &ip_info);
esp_ip4addr_ntoa(&ip_info.ip, str, sizeof(str));
cJSON_AddStringToObject(root, "ipAp", str);
cJSON_AddNumberToObject(root, "temperatureSensorCount", temp_sensor_get_count());
cJSON_AddNumberToObject(root, "temperatureLow", temp_sensor_get_low() / 100.0);
cJSON_AddNumberToObject(root, "temperatureHigh", temp_sensor_get_high() / 100.0);
return root;
}
static const char *serial_to_str(board_config_serial_t serial)
{
switch (serial)
{
case BOARD_CONFIG_SERIAL_UART:
return "uart";
case BOARD_CONFIG_SERIAL_RS485:
return "rs485";
default:
return "none";
}
}
cJSON *json_get_board_config(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "deviceName", board_config.device_name);
cJSON_AddBoolToObject(root, "socketLock", board_config.socket_lock);
cJSON_AddBoolToObject(root, "proximity", board_config.proximity);
cJSON_AddNumberToObject(root, "socketLockMinBreakTime", board_config.socket_lock_min_break_time);
cJSON_AddBoolToObject(root, "rcm", board_config.rcm);
cJSON_AddBoolToObject(root, "temperatureSensor", board_config.onewire && board_config.onewire_temp_sensor);
/*
switch (board_config.energy_meter)
{
case BOARD_CONFIG_ENERGY_METER_CUR:
cJSON_AddStringToObject(root, "energyMeter", "cur");
break;
case BOARD_CONFIG_ENERGY_METER_CUR_VLT:
cJSON_AddStringToObject(root, "energyMeter", "cur_vlt");
break;
default:
cJSON_AddStringToObject(root, "energyMeter", "none");
}
//cJSON_AddBoolToObject(root, "energyMeterThreePhases", board_config.energy_meter_three_phases);
*/
cJSON_AddStringToObject(root, "serial1", serial_to_str(board_config.serial_1));
cJSON_AddStringToObject(root, "serial1Name", board_config.serial_1_name);
cJSON_AddStringToObject(root, "serial2", serial_to_str(board_config.serial_2));
cJSON_AddStringToObject(root, "serial2Name", board_config.serial_2_name);
#if SOC_UART_NUM > 2
cJSON_AddStringToObject(root, "serial3", serial_to_str(board_config.serial_3));
cJSON_AddStringToObject(root, "serial3Name", board_config.serial_3_name);
#else
cJSON_AddStringToObject(root, "serial3", serial_to_str(BOARD_CONFIG_SERIAL_NONE));
cJSON_AddStringToObject(root, "serial3Name", "");
#endif
cJSON *aux = cJSON_CreateArray();
if (board_config.aux_in_1)
{
cJSON_AddItemToArray(aux, cJSON_CreateString(board_config.aux_in_1_name));
}
if (board_config.aux_in_2)
{
cJSON_AddItemToArray(aux, cJSON_CreateString(board_config.aux_in_2_name));
}
if (board_config.aux_in_3)
{
cJSON_AddItemToArray(aux, cJSON_CreateString(board_config.aux_in_3_name));
}
if (board_config.aux_in_4)
{
cJSON_AddItemToArray(aux, cJSON_CreateString(board_config.aux_in_4_name));
}
cJSON_AddItemToObject(root, "auxIn", aux);
aux = cJSON_CreateArray();
if (board_config.aux_out_1)
{
cJSON_AddItemToArray(aux, cJSON_CreateString(board_config.aux_out_1_name));
}
if (board_config.aux_out_2)
{
cJSON_AddItemToArray(aux, cJSON_CreateString(board_config.aux_out_2_name));
}
if (board_config.aux_out_3)
{
cJSON_AddItemToArray(aux, cJSON_CreateString(board_config.aux_out_3_name));
}
if (board_config.aux_out_4)
{
cJSON_AddItemToArray(aux, cJSON_CreateString(board_config.aux_out_4_name));
}
cJSON_AddItemToObject(root, "auxOut", aux);
aux = cJSON_CreateArray();
if (board_config.aux_ain_1)
{
cJSON_AddItemToArray(aux, cJSON_CreateString(board_config.aux_ain_1_name));
}
if (board_config.aux_ain_2)
{
cJSON_AddItemToArray(aux, cJSON_CreateString(board_config.aux_ain_2_name));
}
cJSON_AddItemToObject(root, "auxAin", aux);
return root;
}

View File

@@ -1,66 +0,0 @@
#include <string.h>
#include "esp_log.h"
#include "esp_err.h"
#include "esp_http_client.h"
#include "cJSON.h"
#include "ota.h"
static const char* TAG = "ota";
extern const char server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
extern const char server_cert_pem_end[] asm("_binary_ca_cert_pem_end");
static void http_client_cleanup(esp_http_client_handle_t client)
{
esp_http_client_close(client);
esp_http_client_cleanup(client);
}
esp_err_t ota_get_available_version(char* version)
{
esp_http_client_config_t config = {
.url = OTA_VERSION_URL,
.cert_pem = server_cert_pem_start
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t 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);
return err;
}
int content_length = esp_http_client_fetch_headers(client);
if (content_length > 0) {
esp_http_client_read(client, version, content_length);
version[content_length] = '\0';
http_client_cleanup(client);
return ESP_OK;
} else {
http_client_cleanup(client);
ESP_LOGI(TAG, "No firmware available");
return ESP_ERR_NOT_FOUND;
}
}
bool ota_is_newer_version(const char* actual, const char* available)
{
// available version has no suffix eg: vX.X.X-beta
char actual_trimed[32];
strcpy(actual_trimed, actual);
char* saveptr;
strtok_r(actual_trimed, "-", &saveptr);
bool actual_has_suffix = strtok_r(NULL, "-", &saveptr);
int diff = strcmp(available, actual_trimed);
if (diff == 0) {
return actual_has_suffix;
} else {
return diff > 0;
}
}

View File

@@ -1,74 +0,0 @@
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "timeout_utils.h"
#include "wifi.h"
#include "rest.h"
static void restart_func(void* arg)
{
vTaskDelay(pdMS_TO_TICKS(5000));
esp_restart();
vTaskDelete(NULL);
}
void timeout_restart()
{
xTaskCreate(restart_func, "restart_task", 2 * 1024, NULL, 10, NULL);
}
typedef struct
{
bool enabled;
bool ssid_blank;
char ssid[32];
bool password_blank;
char password[64];
} wifi_set_config_arg_t;
static void wifi_set_config_func(void* arg)
{
vTaskDelay(pdMS_TO_TICKS(1000));
wifi_set_config_arg_t* config = (wifi_set_config_arg_t*)arg;
wifi_set_config(config->enabled, config->ssid_blank ? NULL : config->ssid, config->password_blank ? NULL : config->password);
free((void*)config);
vTaskDelete(NULL);
}
esp_err_t timeout_wifi_set_config(bool enabled, const char* ssid, const char* password)
{
if (enabled) {
if (ssid == NULL || strlen(ssid) == 0) {
char old_ssid[32];
wifi_get_ssid(old_ssid);
if (strlen(old_ssid) == 0) {
return ESP_ERR_INVALID_ARG;
}
}
}
wifi_set_config_arg_t* config = (wifi_set_config_arg_t*)malloc(sizeof(wifi_set_config_arg_t));
config->enabled = enabled;
if (ssid == NULL || ssid[0] == '\0') {
config->ssid_blank = true;
} else {
config->ssid_blank = false;
strcpy(config->ssid, ssid);
}
if (password == NULL || password[0] == '\0') {
config->password_blank = true;
} else {
config->password_blank = false;
strcpy(config->password, password);
}
xTaskCreate(wifi_set_config_func, "wifi_set_config", 4 * 1024, (void*)config, 10, NULL);
return ESP_OK;
}

View File

@@ -1,7 +1,7 @@
set(srcs "src/auth.c" "src/wiegand.c" "src/wiegand_reader.c") set(srcs "src/auth_types.c" "src/auth.c" "src/wiegand.c" "src/wiegand_reader.c" "src/auth_events.c")
idf_component_register(SRCS "${srcs}" idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "src" PRIV_INCLUDE_DIRS "src"
PRIV_REQUIRES nvs_flash driver esp_timer PRIV_REQUIRES nvs_flash driver esp_timer
REQUIRES esp_event esp_idf_lib_helpers evse ocpp) REQUIRES esp_event evse ocpp evse_link)

View File

@@ -1,72 +1,32 @@
#ifndef AUTH_H #pragma once
#define AUTH_H
#include <stdbool.h> #include <stdbool.h>
#include <freertos/FreeRTOS.h> #include "auth_types.h" // enum + MAX LEN para API
#include <freertos/queue.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
// Tamanho máximo da tag RFID (incluindo '\0') /* Evento auxiliar legado/útil (resultado local) */
#define AUTH_TAG_MAX_LEN 20
// Evento enviado ao EVSE Manager após leitura de tag
typedef struct { typedef struct {
char tag[AUTH_TAG_MAX_LEN]; // Tag lida char tag[AUTH_TAG_MAX_LEN];
bool authorized; // true se tag for válida bool authorized;
} auth_event_t; } auth_event_t;
/** void auth_init(void);
* @brief Inicializa o sistema de autenticação. void auth_set_mode(auth_mode_t mode);
* Carrega configuração e inicia o leitor Wiegand (wg26). auth_mode_t auth_get_mode(void);
*/
void auth_init(void);
/**
* @brief Define a fila de eventos que receberá auth_event_t.
*/
void auth_set_event_queue(QueueHandle_t queue);
/**
* @brief Ativa ou desativa o módulo de autenticação (RFID).
* Essa configuração é salva em NVS.
*/
void auth_set_enabled(bool value);
/**
* @brief Verifica se a autenticação está habilitada.
*/
bool auth_is_enabled(void);
/**
* @brief Adiciona uma nova tag válida.
*/
bool auth_add_tag(const char *tag); bool auth_add_tag(const char *tag);
/**
* @brief Remove uma tag previamente cadastrada.
*/
bool auth_remove_tag(const char *tag); bool auth_remove_tag(const char *tag);
/**
* @brief Verifica se uma tag está cadastrada.
*/
bool auth_tag_exists(const char *tag); bool auth_tag_exists(const char *tag);
/**
* @brief Lista as tags registradas (via ESP_LOG).
*/
void auth_list_tags(void); void auth_list_tags(void);
/**
* @brief Processa uma tag lida (usado pelo leitor Wiegand).
*/
void auth_process_tag(const char *tag); void auth_process_tag(const char *tag);
void auth_wait_for_tag_registration(void);
int auth_get_tag_count(void);
const char *auth_get_tag_by_index(int index);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif // AUTH_H

View File

@@ -0,0 +1,29 @@
#pragma once
#include "esp_event.h"
#include "auth_types.h" // só tipos comuns; evita incluir auth.h
ESP_EVENT_DECLARE_BASE(AUTH_EVENTS);
/* IDs de eventos */
typedef enum {
AUTH_EVENT_TAG_PROCESSED = 0, // resultado LOCAL -> auth_tag_event_data_t
AUTH_EVENT_TAG_VERIFY, // pedir validação OCPP -> auth_tag_verify_event_t
AUTH_EVENT_TAG_SAVED, // registada (modo registo) -> auth_tag_event_data_t
AUTH_EVENT_MODE_CHANGED, // modo alterado -> auth_mode_event_data_t
AUTH_EVENT_INIT, // estado inicial -> auth_mode_event_data_t
} auth_event_id_t;
/* Payloads */
typedef struct {
char tag[AUTH_TAG_MAX_LEN];
bool authorized;
} auth_tag_event_data_t;
typedef struct {
char tag[AUTH_TAG_MAX_LEN];
uint32_t req_id; // opcional p/ correlacionar
} auth_tag_verify_event_t;
typedef struct {
auth_mode_t mode;
} auth_mode_event_data_t;

View File

@@ -0,0 +1,26 @@
#pragma once
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Tamanho máx. da tag (inclui NUL) */
#define AUTH_TAG_MAX_LEN 30
/* Modos de autorização */
typedef enum {
AUTH_MODE_OPEN = 0, // Sem autenticação
AUTH_MODE_LOCAL_RFID, // Lista local (NVS)
AUTH_MODE_OCPP_RFID // Validação via OCPP/CSMS
} auth_mode_t;
/* Converte enum -> "open"|"local"|"ocpp" (nunca NULL) */
const char *auth_mode_to_str(auth_mode_t mode);
/* Converte "open"|"local"|"ocpp" (case-insensitive) -> enum */
bool auth_mode_from_str(const char *s, auth_mode_t *out);
#ifdef __cplusplus
}
#endif

View File

@@ -1,109 +1,372 @@
/*
* auth.c
*/
#include "auth.h" #include "auth.h"
#include "auth_events.h"
#include "esp_event.h"
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h> #include <freertos/queue.h>
#include <esp_log.h> #include <esp_log.h>
#include <string.h> #include <string.h>
#include <strings.h> // strcasecmp
#include "wiegand_reader.h" #include "wiegand_reader.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "nvs.h" #include "nvs.h"
#include "evse_link.h"
#include "evse_link_events.h"
#define MAX_TAGS 50 #define MAX_TAGS 50
static const char *TAG = "Auth"; static const char *TAG = "Auth";
static bool enabled = true;
/* ===== Estado ===== */
static auth_mode_t s_mode = AUTH_MODE_OPEN;
static bool waiting_for_registration = false;
static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN]; static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN];
static int tag_count = 0; static int tag_count = 0;
static uint32_t s_next_req_id = 1;
static bool s_wiegand_started = false; // controla se o Wiegand já foi iniciado
// Fila de eventos enviada ao EVSE Manager /* ===== NVS keys ===== */
static QueueHandle_t event_queue = NULL; #define NVS_NAMESPACE "auth"
#define NVS_TAG_PREFIX "tag_"
#define NVS_TAG_COUNT_KEY "count"
#define NVS_MODE_KEY "mode" // uint8_t
// =========================== /* =========================
// Persistência em NVS * NVS Persistence (tags)
// =========================== * ========================= */
static void load_tags_from_nvs(void)
static void load_auth_config(void) { {
nvs_handle_t handle; nvs_handle_t handle;
esp_err_t err = nvs_open("auth", NVS_READONLY, &handle); esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle);
if (err == ESP_OK) { if (err != ESP_OK)
uint8_t val; {
if (nvs_get_u8(handle, "enabled", &val) == ESP_OK) { ESP_LOGW(TAG, "No stored tags in NVS (nvs_open: %s)", esp_err_to_name(err));
enabled = val; return;
ESP_LOGI(TAG, "Loaded auth enabled = %d", enabled); }
uint8_t count = 0;
err = nvs_get_u8(handle, NVS_TAG_COUNT_KEY, &count);
if (err != ESP_OK)
{
ESP_LOGW(TAG, "No tag count key in NVS (nvs_get_u8: %s)", esp_err_to_name(err));
nvs_close(handle);
return;
}
tag_count = 0;
for (int i = 0; i < count && i < MAX_TAGS; i++)
{
char key[16];
char tag_buf[AUTH_TAG_MAX_LEN];
size_t len = sizeof(tag_buf);
snprintf(key, sizeof(key), "%s%d", NVS_TAG_PREFIX, i);
err = nvs_get_str(handle, key, tag_buf, &len);
if (err == ESP_OK)
{
strncpy(valid_tags[tag_count], tag_buf, AUTH_TAG_MAX_LEN - 1);
valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0';
tag_count++;
}
else
{
ESP_LOGW(TAG, "Failed to load tag %d from NVS (%s)", i, esp_err_to_name(err));
} }
nvs_close(handle);
} else {
ESP_LOGW(TAG, "No stored auth config found. Using default.");
} }
nvs_close(handle);
ESP_LOGI(TAG, "Loaded %d tags from NVS", tag_count);
} }
static void save_auth_config(void) { static void save_tags_to_nvs(void)
{
nvs_handle_t handle; nvs_handle_t handle;
if (nvs_open("auth", NVS_READWRITE, &handle) == ESP_OK) { esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle);
nvs_set_u8(handle, "enabled", enabled); if (err != ESP_OK)
nvs_commit(handle); {
nvs_close(handle); ESP_LOGE(TAG, "Failed to open NVS to save tags: %s", esp_err_to_name(err));
ESP_LOGI(TAG, "Auth config saved: enabled = %d", enabled); return;
} else {
ESP_LOGE(TAG, "Failed to save auth config.");
} }
err = nvs_set_u8(handle, NVS_TAG_COUNT_KEY, tag_count);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "nvs_set_u8(count) failed: %s", esp_err_to_name(err));
nvs_close(handle);
return;
}
for (int i = 0; i < tag_count; i++)
{
char key[16];
snprintf(key, sizeof(key), "%s%d", NVS_TAG_PREFIX, i);
err = nvs_set_str(handle, key, valid_tags[i]);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "nvs_set_str(%s) failed: %s", key, esp_err_to_name(err));
nvs_close(handle);
return;
}
}
err = nvs_commit(handle);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "nvs_commit failed when saving tags: %s", esp_err_to_name(err));
nvs_close(handle);
return;
}
nvs_close(handle);
ESP_LOGI(TAG, "Tags saved to NVS (%d tags)", tag_count);
} }
// =========================== /* =========================
// Internos * NVS Persistence (mode)
// =========================== * ========================= */
static void load_mode_from_nvs(void)
{
nvs_handle_t h;
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &h);
if (err == ESP_OK)
{
uint8_t u = (uint8_t)AUTH_MODE_OPEN;
err = nvs_get_u8(h, NVS_MODE_KEY, &u);
if (err == ESP_OK)
{
if (u <= (uint8_t)AUTH_MODE_OCPP_RFID)
s_mode = (auth_mode_t)u;
}
else
{
ESP_LOGW(TAG, "No stored auth mode in NVS (nvs_get_u8: %s). Default OPEN", esp_err_to_name(err));
}
nvs_close(h);
}
else
{
ESP_LOGW(TAG, "No stored auth mode in NVS (nvs_open: %s). Default OPEN", esp_err_to_name(err));
}
ESP_LOGI(TAG, "Loaded mode = %d (%s)", (int)s_mode, auth_mode_to_str(s_mode));
}
static bool is_tag_valid(const char *tag) { static void save_mode_to_nvs(auth_mode_t mode)
for (int i = 0; i < tag_count; i++) { {
if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0) { nvs_handle_t h;
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &h);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to open NVS to save auth mode: %s", esp_err_to_name(err));
return;
}
err = nvs_set_u8(h, NVS_MODE_KEY, (uint8_t)mode);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "nvs_set_u8(mode) failed: %s", esp_err_to_name(err));
nvs_close(h);
return;
}
err = nvs_commit(h);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "nvs_commit failed when saving mode: %s", esp_err_to_name(err));
nvs_close(h);
return;
}
nvs_close(h);
ESP_LOGI(TAG, "Saved mode = %d (%s)", (int)mode, auth_mode_to_str(mode));
}
/* =========================
* Helpers
* ========================= */
static bool is_tag_valid(const char *tag)
{
for (int i = 0; i < tag_count; i++)
{
if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0)
{
return true; return true;
} }
} }
return false; return false;
} }
// =========================== /* =========================
// API pública * Bridge: EVSE-Link -> AUTH (remote AUTH_GRANTED no slave)
// =========================== * ========================= */
static void on_remote_auth_grant(void *arg, esp_event_base_t base, int32_t id, void *data)
{
if (base != EVSE_LINK_EVENTS || id != LINK_EVENT_REMOTE_AUTH_GRANTED || data == NULL)
{
return;
}
void auth_set_event_queue(QueueHandle_t queue) { const evse_link_auth_grant_event_t *src = (const evse_link_auth_grant_event_t *)data;
event_queue = queue;
// Só faz sentido em SLAVE; em MASTER este evento não deve aparecer
if (evse_link_get_mode() != EVSE_LINK_MODE_SLAVE)
{
return;
}
auth_tag_event_data_t ev = {0};
strncpy(ev.tag, src->tag, AUTH_TAG_MAX_LEN - 1);
ev.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
ev.authorized = true;
ESP_LOGI(TAG, "Remote auth grant on SLAVE for tag=%s", ev.tag);
esp_err_t err = esp_event_post(
AUTH_EVENTS,
AUTH_EVENT_TAG_PROCESSED,
&ev,
sizeof(ev),
portMAX_DELAY);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to post AUTH_EVENT_TAG_PROCESSED (remote grant): %s",
esp_err_to_name(err));
}
} }
void auth_set_enabled(bool value) { /* =========================
enabled = value; * Public API
save_auth_config(); * ========================= */
ESP_LOGI(TAG, "Auth %s", enabled ? "ENABLED" : "DISABLED"); void auth_init(void)
{
load_mode_from_nvs();
load_tags_from_nvs();
bool need_wiegand = (s_mode == AUTH_MODE_LOCAL_RFID || s_mode == AUTH_MODE_OCPP_RFID);
if (need_wiegand)
{
initWiegand();
s_wiegand_started = true;
ESP_LOGI(TAG, "Wiegand reader initialized (mode=%s)", auth_mode_to_str(s_mode));
}
else
{
ESP_LOGI(TAG, "Mode OPEN: Wiegand not started");
}
// Registar bridge para autorizações remotas vindas do EVSE-Link
{
esp_err_t err = esp_event_handler_register(
EVSE_LINK_EVENTS,
LINK_EVENT_REMOTE_AUTH_GRANTED,
on_remote_auth_grant,
NULL);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to register EVSE-Link auth grant handler: %s",
esp_err_to_name(err));
}
}
auth_mode_event_data_t evt = {.mode = s_mode};
esp_event_post(AUTH_EVENTS, AUTH_EVENT_INIT, &evt, sizeof(evt), portMAX_DELAY);
ESP_LOGI(TAG, "AUTH INIT sent (mode=%s)", auth_mode_to_str(s_mode));
} }
bool auth_is_enabled(void) { void auth_set_mode(auth_mode_t mode)
return enabled; {
if (mode < AUTH_MODE_OPEN || mode > AUTH_MODE_OCPP_RFID)
{
ESP_LOGW(TAG, "Invalid mode: %d", (int)mode);
return;
}
if (mode == s_mode)
{
ESP_LOGI(TAG, "Mode unchanged: %s", auth_mode_to_str(mode));
return;
}
auth_mode_t old = s_mode;
s_mode = mode;
save_mode_to_nvs(mode);
bool need_wiegand = (s_mode == AUTH_MODE_LOCAL_RFID || s_mode == AUTH_MODE_OCPP_RFID);
if (need_wiegand && !s_wiegand_started)
{
ESP_LOGI(TAG, "Mode changed %s -> %s, starting Wiegand",
auth_mode_to_str(old), auth_mode_to_str(s_mode));
initWiegand();
s_wiegand_started = true;
}
else if (!need_wiegand && s_wiegand_started)
{
// Aqui poderias implementar um wiegand_deinit() se o driver o expuser.
ESP_LOGI(TAG, "Mode changed %s -> %s, Wiegand remains started (no deinit implemented)",
auth_mode_to_str(old), auth_mode_to_str(s_mode));
}
else
{
ESP_LOGI(TAG, "Mode changed %s -> %s, no change in Wiegand state",
auth_mode_to_str(old), auth_mode_to_str(s_mode));
}
if (s_mode == AUTH_MODE_OPEN)
{
ESP_LOGI(TAG, "Mode set to OPEN");
}
else
{
ESP_LOGI(TAG, "Mode set to %s", auth_mode_to_str(s_mode));
}
auth_mode_event_data_t evt = {.mode = s_mode};
esp_event_post(AUTH_EVENTS, AUTH_EVENT_MODE_CHANGED, &evt, sizeof(evt), portMAX_DELAY);
} }
bool auth_add_tag(const char *tag) { auth_mode_t auth_get_mode(void)
if (tag_count >= MAX_TAGS) return false; {
if (!tag || strlen(tag) >= AUTH_TAG_MAX_LEN) return false; return s_mode;
if (is_tag_valid(tag)) return true; }
bool auth_add_tag(const char *tag)
{
if (!tag || strlen(tag) >= AUTH_TAG_MAX_LEN)
return false;
if (tag_count >= MAX_TAGS)
return false;
if (is_tag_valid(tag))
return true; // já existe
strncpy(valid_tags[tag_count], tag, AUTH_TAG_MAX_LEN - 1); strncpy(valid_tags[tag_count], tag, AUTH_TAG_MAX_LEN - 1);
valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0'; valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0';
tag_count++; tag_count++;
save_tags_to_nvs();
ESP_LOGI(TAG, "Tag added: %s", tag); ESP_LOGI(TAG, "Tag added: %s", tag);
return true; return true;
} }
bool auth_remove_tag(const char *tag) { bool auth_remove_tag(const char *tag)
for (int i = 0; i < tag_count; i++) { {
if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0) { if (!tag)
for (int j = i; j < tag_count - 1; j++) { return false;
for (int i = 0; i < tag_count; i++)
{
if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0)
{
for (int j = i; j < tag_count - 1; j++)
{
strncpy(valid_tags[j], valid_tags[j + 1], AUTH_TAG_MAX_LEN); strncpy(valid_tags[j], valid_tags[j + 1], AUTH_TAG_MAX_LEN);
valid_tags[j][AUTH_TAG_MAX_LEN - 1] = '\0';
} }
tag_count--; tag_count--;
save_tags_to_nvs();
ESP_LOGI(TAG, "Tag removed: %s", tag); ESP_LOGI(TAG, "Tag removed: %s", tag);
return true; return true;
} }
@@ -111,41 +374,109 @@ bool auth_remove_tag(const char *tag) {
return false; return false;
} }
bool auth_tag_exists(const char *tag) { bool auth_tag_exists(const char *tag)
{
if (!tag)
return false;
return is_tag_valid(tag); return is_tag_valid(tag);
} }
void auth_list_tags(void) { void auth_list_tags(void)
{
ESP_LOGI(TAG, "Registered Tags (%d):", tag_count); ESP_LOGI(TAG, "Registered Tags (%d):", tag_count);
for (int i = 0; i < tag_count; i++) { for (int i = 0; i < tag_count; i++)
{
ESP_LOGI(TAG, "- %s", valid_tags[i]); ESP_LOGI(TAG, "- %s", valid_tags[i]);
} }
} }
void auth_init(void) { void auth_wait_for_tag_registration(void)
load_auth_config(); // carrega estado de ativação {
initWiegand(); // inicia leitor RFID if (s_mode != AUTH_MODE_LOCAL_RFID)
{
ESP_LOGW(TAG, "Registration is only available in LOCAL mode");
return;
}
waiting_for_registration = true;
ESP_LOGI(TAG, "Tag registration mode enabled.");
} }
// Processa uma tag lida (chamada pelo leitor) void auth_process_tag(const char *tag)
void auth_process_tag(const char *tag) { {
if (!tag || !auth_is_enabled()) { if (!tag || !*tag)
ESP_LOGW(TAG, "Auth disabled or NULL tag received."); {
ESP_LOGW(TAG, "NULL/empty tag received");
return; return;
} }
auth_event_t event; switch (s_mode)
strncpy(event.tag, tag, AUTH_TAG_MAX_LEN - 1); {
event.tag[AUTH_TAG_MAX_LEN - 1] = '\0'; case AUTH_MODE_OPEN:
event.authorized = is_tag_valid(tag); {
// Sem verificação; normalmente nem é necessário evento.
ESP_LOGI(TAG, "Mode OPEN: tag=%s (no verification)", tag);
break;
}
ESP_LOGI(TAG, "Tag %s: %s", tag, event.authorized ? "AUTHORIZED" : "DENIED"); case AUTH_MODE_LOCAL_RFID:
{
if (waiting_for_registration)
{
waiting_for_registration = false;
if (event_queue) { if (auth_add_tag(tag))
if (xQueueSend(event_queue, &event, pdMS_TO_TICKS(100)) != pdPASS) { {
ESP_LOGW(TAG, "Auth event queue full, dropping tag: %s", tag); auth_tag_event_data_t ev = {0};
strncpy(ev.tag, tag, AUTH_TAG_MAX_LEN - 1);
ev.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
ev.authorized = true;
esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED,
&ev, sizeof(ev), portMAX_DELAY);
ESP_LOGI(TAG, "Tag registered: %s", tag);
}
else
{
ESP_LOGW(TAG, "Failed to register tag: %s", tag);
}
return;
} }
} else {
ESP_LOGW(TAG, "Auth event queue not set"); auth_tag_event_data_t ev = {0};
strncpy(ev.tag, tag, AUTH_TAG_MAX_LEN - 1);
ev.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
ev.authorized = is_tag_valid(tag);
ESP_LOGI(TAG, "LOCAL tag %s: %s", tag,
ev.authorized ? "AUTHORIZED" : "DENIED");
esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED,
&ev, sizeof(ev), portMAX_DELAY);
break;
}
case AUTH_MODE_OCPP_RFID:
{
// Não decide localmente. Pede validação ao OCPP.
auth_tag_verify_event_t rq = {0};
strncpy(rq.tag, tag, AUTH_TAG_MAX_LEN - 1);
rq.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
rq.req_id = s_next_req_id++;
ESP_LOGI(TAG, "OCPP VERIFY requested for tag=%s (req_id=%u)",
rq.tag, (unsigned)rq.req_id);
esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_VERIFY,
&rq, sizeof(rq), portMAX_DELAY);
break;
}
} }
} }
int auth_get_tag_count(void)
{
return tag_count;
}
const char *auth_get_tag_by_index(int index)
{
if (index < 0 || index >= tag_count)
return NULL;
return valid_tags[index];
}

View File

@@ -0,0 +1,3 @@
#include "auth_events.h"
ESP_EVENT_DEFINE_BASE(AUTH_EVENTS);

View File

@@ -0,0 +1,19 @@
#include "auth_types.h"
#include <strings.h> // strcasecmp
const char *auth_mode_to_str(auth_mode_t mode) {
switch (mode) {
case AUTH_MODE_OPEN: return "open";
case AUTH_MODE_LOCAL_RFID: return "local";
case AUTH_MODE_OCPP_RFID: return "ocpp";
default: return "open";
}
}
bool auth_mode_from_str(const char *s, auth_mode_t *out) {
if (!s || !out) return false;
if (!strcasecmp(s, "open")) { *out = AUTH_MODE_OPEN; return true; }
if (!strcasecmp(s, "local")) { *out = AUTH_MODE_LOCAL_RFID; return true; }
if (!strcasecmp(s, "ocpp")) { *out = AUTH_MODE_OCPP_RFID; return true; }
return false;
}

View File

@@ -1,3 +1,4 @@
// === Início de: components/auth/src/wiegand.c ===
/** /**
* @file wiegand.c * @file wiegand.c
* *
@@ -6,9 +7,13 @@
#include <esp_log.h> #include <esp_log.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <esp_idf_lib_helpers.h> #include <esp_attr.h> // <- para IRAM_ATTR
#include "wiegand.h" #include "wiegand.h"
#ifndef IRAM_ATTR
#define IRAM_ATTR
#endif
static const char *TAG = "wiegand"; static const char *TAG = "wiegand";
#define TIMER_INTERVAL_US 50000 // 50ms #define TIMER_INTERVAL_US 50000 // 50ms
@@ -39,11 +44,7 @@ static void isr_enable(wiegand_reader_t *reader)
gpio_set_intr_type(reader->gpio_d1, GPIO_INTR_NEGEDGE); gpio_set_intr_type(reader->gpio_d1, GPIO_INTR_NEGEDGE);
} }
#if HELPER_TARGET_IS_ESP32
static void IRAM_ATTR isr_handler(void *arg) static void IRAM_ATTR isr_handler(void *arg)
#else
static void isr_handler(void *arg)
#endif
{ {
wiegand_reader_t *reader = (wiegand_reader_t *)arg; wiegand_reader_t *reader = (wiegand_reader_t *)arg;
if (!reader->enabled) if (!reader->enabled)
@@ -101,9 +102,11 @@ esp_err_t wiegand_reader_init(wiegand_reader_t *reader, gpio_num_t gpio_d0, gpio
{ {
CHECK_ARG(reader && buf_size && callback); CHECK_ARG(reader && buf_size && callback);
/*
esp_err_t res = gpio_install_isr_service(0); esp_err_t res = gpio_install_isr_service(0);
if (res != ESP_OK && res != ESP_ERR_INVALID_STATE) if (res != ESP_OK && res != ESP_ERR_INVALID_STATE)
return res; return res;
*/
memset(reader, 0, sizeof(wiegand_reader_t)); memset(reader, 0, sizeof(wiegand_reader_t));
reader->gpio_d0 = gpio_d0; reader->gpio_d0 = gpio_d0;
@@ -179,3 +182,5 @@ esp_err_t wiegand_reader_done(wiegand_reader_t *reader)
return ESP_OK; return ESP_OK;
} }
// === Fim de: components/auth/src/wiegand.c ===

View File

@@ -1,90 +1,307 @@
/*
* wiegand_reader.c
*/
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <inttypes.h>
#include <stdbool.h>
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/task.h> #include <freertos/task.h>
#include <freertos/queue.h> #include <freertos/queue.h>
#include <esp_log.h> #include <esp_log.h>
#include <wiegand.h> #include <wiegand.h>
#include <evse_api.h>
#include <ocpp.h>
#include "auth.h" #include "auth.h"
#define CONFIG_EXAMPLE_BUF_SIZE 50 #define CONFIG_EXAMPLE_BUF_SIZE 50
#define IDTAG_MAX_LEN 20
static const char *TAG = "WiegandReader"; static const char *TAG = "WiegandReader";
// ---- Parâmetro global/local para controlar a convenção de paridade ----
static const bool PARITY_INVERTED = false; // mude para true se o seu leitor vier "invertido"
static wiegand_reader_t reader; static wiegand_reader_t reader;
static QueueHandle_t queue = NULL; static QueueHandle_t queue = NULL;
typedef struct { typedef struct
{
uint8_t data[CONFIG_EXAMPLE_BUF_SIZE]; uint8_t data[CONFIG_EXAMPLE_BUF_SIZE];
size_t bits; size_t bits;
size_t bytes;
} data_packet_t; } data_packet_t;
static void reader_callback(wiegand_reader_t *r) { static inline uint8_t get_bit_msb_first(const uint8_t *buf, size_t bit_index)
data_packet_t p; {
p.bits = r->bits; return (buf[bit_index / 8] >> (7 - (bit_index % 8))) & 0x01;
memcpy(p.data, r->buf, CONFIG_EXAMPLE_BUF_SIZE);
xQueueSendToBack(queue, &p, 0);
} }
static void wiegand_task(void *arg) { // Versão parametrizável de verificação de paridade
queue = xQueueCreate(5, sizeof(data_packet_t)); static bool check_parity_param(const uint8_t *buf, size_t bits, bool invert)
if (!queue) { {
if (bits != 26 && bits != 34)
return false;
const int first_parity = get_bit_msb_first(buf, 0);
const int last_parity = get_bit_msb_first(buf, bits - 1);
uint32_t left = 0, right = 0;
if (bits == 26)
{
for (int i = 1; i <= 12; i++)
if (get_bit_msb_first(buf, i))
left++;
for (int i = 13; i <= 24; i++)
if (get_bit_msb_first(buf, i))
right++;
}
else // 34 bits
{
for (int i = 1; i <= 16; i++)
if (get_bit_msb_first(buf, i))
left++;
for (int i = 17; i <= 32; i++)
if (get_bit_msb_first(buf, i))
right++;
}
// Regra padrão (W26/W34 “normais”): primeiro = PAR (left), último = ÍMPAR (right)
bool exp_first = (left % 2 == 0);
bool exp_last = (right % 2 == 1);
// Se invertido, troca as expectativas (par <-> ímpar)
if (invert)
{
exp_first = !exp_first;
exp_last = !exp_last;
}
return (first_parity == exp_first) && (last_parity == exp_last);
}
// CRC-8/ATM (polinómio 0x07, init 0x00, sem ref, xor 0x00)
static uint8_t crc8_atm(const uint8_t *data, size_t len)
{
uint8_t crc = 0x00;
for (size_t i = 0; i < len; i++)
{
crc ^= data[i];
for (int b = 0; b < 8; b++)
{
crc = (crc & 0x80) ? (uint8_t)((crc << 1) ^ 0x07) : (uint8_t)(crc << 1);
}
}
return crc;
}
// CRC-16/IBM (CRC-16-ANSI) poly 0xA001 (refin/refout), init 0xFFFF
static uint16_t crc16_ibm(const uint8_t *data, size_t len)
{
uint16_t crc = 0xFFFF;
for (size_t i = 0; i < len; i++)
{
crc ^= data[i];
for (int b = 0; b < 8; b++)
{
if (crc & 1)
crc = (crc >> 1) ^ 0xA001;
else
crc >>= 1;
}
}
return crc;
}
// Extrai payload (sem paridades) e devolve FC/CN para W26/34
static bool wiegand_extract_fc_cn(const uint8_t *buf, size_t bits, uint32_t *fc, uint32_t *cn)
{
if (bits != 26 && bits != 34)
return false;
uint32_t payload = 0;
size_t payload_bits = bits - 2;
for (size_t i = 0; i < payload_bits; i++)
{
size_t bit = 1 + i; // ignora bit de paridade inicial
uint8_t bitval = (buf[bit / 8] >> (7 - (bit % 8))) & 0x01;
payload = (payload << 1) | bitval;
}
if (bits == 26)
{
*fc = (payload >> 16) & 0xFF; // 8b
*cn = payload & 0xFFFF; // 16b
}
else // 34 bits
{
*fc = (payload >> 16) & 0xFFFF; // 16b
*cn = payload & 0xFFFF; // 16b
}
return true;
}
static bool build_idtag_w26_4B(uint32_t fc, uint32_t cn, char *out, size_t outlen)
{
uint8_t raw[4] = {
(uint8_t)(fc & 0xFF),
(uint8_t)((cn >> 8) & 0xFF),
(uint8_t)(cn & 0xFF),
0};
raw[3] = crc8_atm(raw, 3);
if (outlen < 9)
return false;
int n = snprintf(out, outlen, "%02X%02X%02X%02X",
raw[0], raw[1], raw[2], raw[3]);
return (n > 0 && (size_t)n < outlen);
}
static bool build_idtag_w34_7B(uint32_t fc, uint32_t cn, char *out, size_t outlen)
{
uint8_t raw[7];
raw[0] = 0x34;
raw[1] = (uint8_t)((fc >> 8) & 0xFF);
raw[2] = (uint8_t)(fc & 0xFF);
raw[3] = (uint8_t)((cn >> 8) & 0xFF);
raw[4] = (uint8_t)(cn & 0xFF);
uint16_t crc = crc16_ibm(raw, 5);
raw[5] = (uint8_t)((crc >> 8) & 0xFF);
raw[6] = (uint8_t)(crc & 0xFF);
if (outlen < 15)
return false;
int n = snprintf(out, outlen, "%02X%02X%02X%02X%02X%02X%02X",
raw[0], raw[1], raw[2], raw[3],
raw[4], raw[5], raw[6]);
return (n > 0 && (size_t)n < outlen);
}
// Callback chamado pelo esp_timer_task (contexto de task, NÃO ISR)
static void reader_callback(wiegand_reader_t *r)
{
if (!queue)
{
ESP_LOGW(TAG, "Queue not ready, dropping packet");
return;
}
data_packet_t p = {0};
p.bits = r->bits;
p.bytes = (r->bits + 7) / 8;
if (p.bytes > sizeof(p.data))
p.bytes = sizeof(p.data);
memcpy(p.data, r->buf, p.bytes);
if (xQueueSendToBack(queue, &p, 0) != pdPASS)
{
ESP_LOGW(TAG, "Queue full, dropping Wiegand packet");
}
}
static void wiegand_task(void *arg)
{
queue = xQueueCreate(8, sizeof(data_packet_t));
if (!queue)
{
ESP_LOGE(TAG, "Failed to create queue"); ESP_LOGE(TAG, "Failed to create queue");
vTaskDelete(NULL); vTaskDelete(NULL);
return; return;
} }
ESP_ERROR_CHECK(wiegand_reader_init(&reader, 19, 18, ESP_ERROR_CHECK(wiegand_reader_init(&reader,
true, CONFIG_EXAMPLE_BUF_SIZE, reader_callback, WIEGAND_MSB_FIRST, WIEGAND_LSB_FIRST)); 21, 22, // GPIO D0, D1
true, // internal pullups
CONFIG_EXAMPLE_BUF_SIZE,
reader_callback,
WIEGAND_MSB_FIRST,
WIEGAND_LSB_FIRST));
data_packet_t p; data_packet_t p;
while (1) {
for (;;)
{
ESP_LOGI(TAG, "Waiting for Wiegand data..."); ESP_LOGI(TAG, "Waiting for Wiegand data...");
if (xQueueReceive(queue, &p, portMAX_DELAY) == pdPASS) {
ESP_LOGI(TAG, "Bits received: %d", p.bits);
char tag[20] = {0}; if (xQueueReceive(queue, &p, portMAX_DELAY) != pdPASS)
continue;
if (p.bits == 26) { ESP_LOGI(TAG, "Bits received: %d", (int)p.bits);
snprintf(tag, sizeof(tag), "%03d%03d%03d", p.data[0], p.data[1], p.data[2]);
} else if (p.bits == 34) {
snprintf(tag, sizeof(tag), "%03d%03d%03d%03d", p.data[0], p.data[1], p.data[2], p.data[3]);
} else {
ESP_LOGW(TAG, "Unsupported bit length: %d", (int)p.bits);
continue;
}
ESP_LOGI(TAG, "Tag read: %s", tag); if (!check_parity_param(p.data, p.bits, PARITY_INVERTED))
{
ESP_LOGW(TAG, "Parity check failed (%d bits). Dump:", (int)p.bits);
ESP_LOG_BUFFER_HEX(TAG, p.data, p.bytes);
continue;
}
if (!auth_is_enabled()) { char tag[IDTAG_MAX_LEN + 1] = {0}; // OCPP 1.6: máx 20 chars (+NUL)
ESP_LOGW(TAG, "Auth disabled, ignoring tag."); uint32_t fc = 0, cn = 0;
continue;
}
if (auth_tag_exists(tag)) { if (!wiegand_extract_fc_cn(p.data, p.bits, &fc, &cn))
ESP_LOGI(TAG, "Authorized tag. Proceeding..."); {
evse_authorize(); ESP_LOGW(TAG, "Unsupported bit length: %d", (int)p.bits);
continue;
}
if (ocpp_is_TransactionActive()) { bool ok = false;
ocpp_end_transaction(tag); if (p.bits == 26)
} else { {
ocpp_begin_transaction(tag); ok = build_idtag_w26_4B(fc, cn, tag, sizeof(tag)); // 8 hex
} }
} else { else // 34
ESP_LOGW(TAG, "Unauthorized tag: %s", tag); {
} ok = build_idtag_w34_7B(fc, cn, tag, sizeof(tag)); // 14 hex
}
if (!ok)
{
ESP_LOGW(TAG, "Failed to build idTag");
continue;
}
ESP_LOGI(TAG, "idTag: %s", tag); // apenas [0-9A-F], alfanumérico
auth_process_tag(tag);
}
}
static void wiegand_sim_task(void *arg)
{
// lista fixa de idTags simuladas
static const char *idtaglist[] = {
"00000041349",
"W2602312345",
"W34ABCDE123",
};
const size_t list_size = sizeof(idtaglist) / sizeof(idtaglist[0]);
for (;;)
{
for (size_t i = 0; i < list_size; i++)
{
ESP_LOGI(TAG, "Simulação -> idTag: %s", idtaglist[i]);
auth_process_tag(idtaglist[i]);
// espera 30 segundos entre cada tag
vTaskDelay(pdMS_TO_TICKS(30000));
} }
} }
} }
void initWiegand(void) { void initWiegand(void)
{
ESP_LOGI(TAG, "Initializing Wiegand reader"); ESP_LOGI(TAG, "Initializing Wiegand reader");
xTaskCreate(wiegand_task, TAG, configMINIMAL_STACK_SIZE * 4, NULL, 5, NULL); xTaskCreate(wiegand_task, TAG, configMINIMAL_STACK_SIZE * 4, NULL, 4, NULL);
// Para testes, podes ativar o simulador:
//ESP_LOGI(TAG, "Inicializando Wiegand simulado");
//xTaskCreate(wiegand_sim_task, "WiegandSim",
// configMINIMAL_STACK_SIZE * 3, NULL, 3, NULL);
} }

View File

@@ -0,0 +1,12 @@
set(srcs
"src/buzzer.c"
"src/buzzer_events.c"
)
idf_component_register(
SRCS ${srcs}
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "src"
REQUIRES esp_event
PRIV_REQUIRES driver nvs_flash esp_timer evse
)

View File

@@ -0,0 +1,2 @@
version: "2.6.0"
description: Authentication component

View File

@@ -0,0 +1,66 @@
// buzzer.h
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "buzzer_events.h" // para buzzer_pattern_id_t
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Inicializa hardware, fila e task do buzzer; registra event handlers.
*/
void buzzer_init(void);
/**
* @brief Desinicializa o buzzer (opcional), removendo handlers e task.
*/
void buzzer_deinit(void);
/**
* @brief Solicita tocar um padrão de buzzer de forma assíncrona.
* @param id Enum do padrão (ver buzzer_events.h).
*/
void buzzer_play(buzzer_pattern_id_t id);
/**
* @brief Interrompe imediatamente qualquer padrão em execução e esvazia a fila.
*/
void buzzer_stop(void);
/**
* @brief Ativa/Desativa globalmente o buzzer (mute).
* Quando desativado, não toca e garante nível/desligado.
*/
void buzzer_set_enabled(bool enabled);
/**
* @brief Retorna o estado atual de enable/mute do buzzer.
*/
bool buzzer_get_enabled(void);
/**
* @brief Define quiet hours (janela silenciosa) em minutos desde 00:00.
* @param enabled Habilita/desabilita quiet hours.
* @param start_min Início (0..1439).
* @param end_min Fim (0..1439). Pode cruzar meia-noite.
*/
void buzzer_set_quiet_hours(bool enabled, uint16_t start_min, uint16_t end_min);
/**
* @brief Ajusta a frequência PWM (somente PASSIVE/LEDC; timer exclusivo recomendado).
* @return ESP_OK em sucesso; erro se modo inválido ou argumento fora do range.
*/
esp_err_t buzzer_set_frequency(uint32_t hz);
/**
* @brief Define o duty cycle em porcentagem (0..100) para PASSIVE/LEDC.
*/
void buzzer_set_duty_percent(uint8_t pct);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,27 @@
#pragma once
#include "esp_event.h"
ESP_EVENT_DECLARE_BASE(BUZZER_EVENTS);
typedef enum {
BUZZER_EVENT_PLAY_PATTERN,
} buzzer_event_id_t;
typedef enum {
BUZZER_PATTERN_NONE = 0,
BUZZER_PATTERN_PLUGGED,
BUZZER_PATTERN_UNPLUGGED,
BUZZER_PATTERN_CHARGING,
BUZZER_PATTERN_AP_START,
BUZZER_PATTERN_CARD_READ,
BUZZER_PATTERN_CARD_ADD,
BUZZER_PATTERN_CARD_DENIED,
BUZZER_PATTERN_FAULT,
BUZZER_PATTERN_MAX
} buzzer_pattern_id_t;
typedef struct {
buzzer_pattern_id_t pattern;
} buzzer_event_data_t;

537
components/buzzer/src/buzzer.c Executable file
View File

@@ -0,0 +1,537 @@
// buzzer.c
#include "buzzer_events.h"
#include "evse_events.h"
#include "auth_events.h"
#include "network_events.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "driver/ledc.h"
#include <time.h>
#include <string.h>
// ===================== Configuração padrão =====================
#define CONFIG_BUZZER_GPIO GPIO_NUM_27
// 1 = PASSIVE (PWM), 0 = ACTIVE (on/off)
#define CONFIG_BUZZER_MODE_PASSIVE 0
#define CONFIG_BUZZER_FREQ_HZ 3500
#define CONFIG_BUZZER_DUTY_PCT 70
#define CONFIG_BUZZER_QUEUE_LEN 8
#define CONFIG_BUZZER_TASK_STACK 2048
#define CONFIG_BUZZER_TASK_PRIO (tskIDLE_PRIORITY + 1)
// anti-spam (gap mínimo entre toques)
#define CONFIG_BUZZER_MIN_GAP_MS 70
#define CONFIG_BUZZER_ENABLE_DEFAULT 1
// quiet hours start (minutos desde 00:00)
#define CONFIG_BUZZER_QUIET_START_MIN (22 * 60)
// quiet hours end (minutos desde 00:00)
#define CONFIG_BUZZER_QUIET_END_MIN (7 * 60)
// ===================== Tipos e estado =====================
static const char *TAG = "Buzzer";
typedef enum
{
BUZZER_MODE_ACTIVE = 0,
BUZZER_MODE_PASSIVE
} buzzer_mode_t;
typedef struct
{
uint16_t on_ms;
uint16_t off_ms;
} buzzer_step_t;
typedef struct
{
const buzzer_step_t *steps;
size_t length;
} buzzer_pattern_t;
typedef struct
{
buzzer_mode_t mode;
gpio_num_t gpio;
// PASSIVE (PWM)
uint32_t freq_hz;
uint8_t duty_percent; // 0100
ledc_mode_t ledc_speed_mode; // LEDC_LOW_SPEED_MODE
ledc_timer_t ledc_timer; // LEDC_TIMER_1
ledc_channel_t ledc_channel; // LEDC_CHANNEL_1
ledc_timer_bit_t duty_resolution; // LEDC_TIMER_10_BIT
} buzzer_config_t;
// Controlo runtime
static struct
{
bool enabled;
uint16_t quiet_start_min; // [0..1440)
uint16_t quiet_end_min; // [0..1440)
bool quiet_enabled;
} s_buzzer_ctl = {
.enabled = CONFIG_BUZZER_ENABLE_DEFAULT,
.quiet_start_min = CONFIG_BUZZER_QUIET_START_MIN,
.quiet_end_min = CONFIG_BUZZER_QUIET_END_MIN,
.quiet_enabled = false,
};
// Hardware cfg
static buzzer_config_t s_buzzer_cfg = {
.mode = CONFIG_BUZZER_MODE_PASSIVE ? BUZZER_MODE_PASSIVE : BUZZER_MODE_ACTIVE,
.gpio = CONFIG_BUZZER_GPIO,
.freq_hz = CONFIG_BUZZER_FREQ_HZ,
.duty_percent = CONFIG_BUZZER_DUTY_PCT,
.ledc_speed_mode = LEDC_LOW_SPEED_MODE,
.ledc_timer = LEDC_TIMER_1,
.ledc_channel = LEDC_CHANNEL_1,
.duty_resolution = LEDC_TIMER_10_BIT};
// Fila e task
static QueueHandle_t s_buzzer_q = NULL;
static TaskHandle_t s_buzzer_task = NULL;
// Anti-spam básico
static TickType_t s_last_play_tick = 0;
static const TickType_t s_min_gap_ticks = pdMS_TO_TICKS(CONFIG_BUZZER_MIN_GAP_MS);
// ===================== Padrões (afinados) =====================
// NOTA: os enums BUZZER_PATTERN_* vêm de buzzer_events.h
// Curtos e claros
static const buzzer_step_t pattern_plugged[] = {{180, 120}}; // ~300 ms
static const buzzer_step_t pattern_card_read[] = {{80, 80}, {80, 0}}; // 160 ms audível
// Um pouco mais longos, ritmo marcante
static const buzzer_step_t pattern_unplugged[] = {{140, 140}, {140, 140}, {140, 0}}; // ~700 ms
static const buzzer_step_t pattern_card_add[] = {{100, 100}, {100, 100}, {120, 0}}; // ~520 ms
static const buzzer_step_t pattern_ap_start[] = {{250, 150}, {250, 0}}; // ~650 ms
// Charging mais curta (evento frequente)
static const buzzer_step_t pattern_charging[] = {{80, 90}, {90, 80}, {100, 0}}; // ~440 ms
// Denied mais “duro”
static const buzzer_step_t pattern_card_denied[] = {{250, 120}, {300, 0}}; // ~670 ms
// Fault crítico (duas trincas, ~1.7 s)
static const buzzer_step_t pattern_fault[] = {
{350, 120}, {350, 120}, {450, 250}, // bloco 1
{350, 120},
{350, 120},
{450, 0} // bloco 2
};
static const buzzer_pattern_t buzzer_patterns[] = {
[BUZZER_PATTERN_PLUGGED] = {pattern_plugged, sizeof(pattern_plugged) / sizeof(buzzer_step_t)},
[BUZZER_PATTERN_UNPLUGGED] = {pattern_unplugged, sizeof(pattern_unplugged) / sizeof(buzzer_step_t)},
[BUZZER_PATTERN_CHARGING] = {pattern_charging, sizeof(pattern_charging) / sizeof(buzzer_step_t)},
[BUZZER_PATTERN_AP_START] = {pattern_ap_start, sizeof(pattern_ap_start) / sizeof(buzzer_step_t)},
[BUZZER_PATTERN_CARD_READ] = {pattern_card_read, sizeof(pattern_card_read) / sizeof(buzzer_step_t)},
[BUZZER_PATTERN_CARD_ADD] = {pattern_card_add, sizeof(pattern_card_add) / sizeof(buzzer_step_t)},
[BUZZER_PATTERN_CARD_DENIED] = {pattern_card_denied, sizeof(pattern_card_denied) / sizeof(buzzer_step_t)},
[BUZZER_PATTERN_FAULT] = {pattern_fault, sizeof(pattern_fault) / sizeof(buzzer_step_t)},
};
// ===================== Helpers HW =====================
static inline uint32_t duty_from_percent(uint8_t pct, ledc_timer_bit_t res)
{
if (pct == 0)
return 0;
if (pct > 100)
pct = 100;
uint32_t max = (1U << res) - 1U;
return (uint32_t)((pct * max) / 100U);
}
static inline bool is_pattern_valid(buzzer_pattern_id_t id)
{
return (id > BUZZER_PATTERN_NONE && id < BUZZER_PATTERN_MAX);
}
static void buzzer_on(void)
{
if (s_buzzer_cfg.mode == BUZZER_MODE_PASSIVE)
{
// Não mudar freq a cada beep; já foi configurada na init.
esp_err_t err1 = ledc_set_duty(s_buzzer_cfg.ledc_speed_mode,
s_buzzer_cfg.ledc_channel,
duty_from_percent(s_buzzer_cfg.duty_percent, s_buzzer_cfg.duty_resolution));
if (err1 != ESP_OK)
ESP_LOGW(TAG, "ledc_set_duty err=%s", esp_err_to_name(err1));
esp_err_t err2 = ledc_update_duty(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_channel);
if (err2 != ESP_OK)
ESP_LOGW(TAG, "ledc_update_duty err=%s", esp_err_to_name(err2));
}
else
{
gpio_set_level(s_buzzer_cfg.gpio, 1);
}
}
static void buzzer_off(void)
{
if (s_buzzer_cfg.mode == BUZZER_MODE_PASSIVE)
{
esp_err_t err1 = ledc_set_duty(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_channel, 0);
if (err1 != ESP_OK)
ESP_LOGW(TAG, "ledc_set_duty(0) err=%s", esp_err_to_name(err1));
esp_err_t err2 = ledc_update_duty(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_channel);
if (err2 != ESP_OK)
ESP_LOGW(TAG, "ledc_update_duty err=%s", esp_err_to_name(err2));
}
else
{
gpio_set_level(s_buzzer_cfg.gpio, 0);
}
}
static bool in_quiet_hours(void)
{
if (!s_buzzer_ctl.quiet_enabled)
return false;
time_t now = time(NULL);
struct tm lt;
if (localtime_r(&now, &lt) == NULL)
return false;
uint16_t minutes = (uint16_t)(lt.tm_hour * 60 + lt.tm_min);
uint16_t start = s_buzzer_ctl.quiet_start_min;
uint16_t end = s_buzzer_ctl.quiet_end_min;
if (start == end)
return false; // desativado
if (start < end)
{
return (minutes >= start && minutes < end);
}
else
{ // janela cruza meia-noite
return (minutes >= start || minutes < end);
}
}
// ===================== Execução do padrão (apenas dentro da task) =====================
static void buzzer_execute(buzzer_pattern_id_t pattern_id)
{
if (!is_pattern_valid(pattern_id))
{
ESP_LOGW(TAG, "Invalid buzzer pattern id: %d", pattern_id);
return;
}
const buzzer_pattern_t *pattern = &buzzer_patterns[pattern_id];
for (size_t i = 0; i < pattern->length; i++)
{
buzzer_on();
vTaskDelay(pdMS_TO_TICKS(pattern->steps[i].on_ms));
buzzer_off();
if (pattern->steps[i].off_ms > 0)
{
vTaskDelay(pdMS_TO_TICKS(pattern->steps[i].off_ms));
}
}
}
// ===================== Task & Fila =====================
static void buzzer_task(void *arg)
{
buzzer_pattern_id_t id;
for (;;)
{
if (xQueueReceive(s_buzzer_q, &id, portMAX_DELAY) == pdTRUE)
{
ESP_LOGD(TAG, "dequeue pattern %d", id); // na buzzer_task()
if (!s_buzzer_ctl.enabled)
continue;
// Quiet hours: permitir apenas alertas críticos/negativos
if (in_quiet_hours())
{
if (!(id == BUZZER_PATTERN_FAULT || id == BUZZER_PATTERN_CARD_DENIED))
{
continue;
}
}
// Anti-spam global
TickType_t now = xTaskGetTickCount();
if ((now - s_last_play_tick) < s_min_gap_ticks)
{
continue;
}
s_last_play_tick = now;
buzzer_execute(id);
}
}
}
// ===================== API pública =====================
void buzzer_play(buzzer_pattern_id_t id)
{
ESP_LOGD(TAG, "enqueue pattern %d", id); // dentro de buzzer_play()
if (!is_pattern_valid(id) || s_buzzer_q == NULL)
return;
(void)xQueueSend(s_buzzer_q, &id, 0);
}
void buzzer_stop(void)
{
// Interrompe imediatamente: esvazia fila e desliga
if (s_buzzer_q)
xQueueReset(s_buzzer_q);
buzzer_off();
}
void buzzer_set_enabled(bool enabled)
{
s_buzzer_ctl.enabled = enabled;
if (!enabled)
buzzer_off();
}
bool buzzer_get_enabled(void)
{
return s_buzzer_ctl.enabled;
}
void buzzer_set_quiet_hours(bool enabled, uint16_t start_min, uint16_t end_min)
{
s_buzzer_ctl.quiet_enabled = enabled;
if (start_min < 1440)
s_buzzer_ctl.quiet_start_min = start_min;
if (end_min < 1440)
s_buzzer_ctl.quiet_end_min = end_min;
}
// Opcional: mudar tom dinamicamente (apenas se o timer for exclusivo do buzzer)
esp_err_t buzzer_set_frequency(uint32_t hz)
{
if (s_buzzer_cfg.mode != BUZZER_MODE_PASSIVE)
return ESP_ERR_INVALID_STATE;
if (hz < 50 || hz > 20000)
return ESP_ERR_INVALID_ARG;
s_buzzer_cfg.freq_hz = hz;
return ledc_set_freq(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_timer, hz);
}
void buzzer_set_duty_percent(uint8_t pct)
{
if (pct > 100)
pct = 100;
s_buzzer_cfg.duty_percent = pct;
}
// ===================== Event Handlers =====================
static void buzzer_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
{
if (base != BUZZER_EVENTS || id != BUZZER_EVENT_PLAY_PATTERN || data == NULL)
return;
buzzer_event_data_t *evt = (buzzer_event_data_t *)data;
if (!is_pattern_valid(evt->pattern))
{
ESP_LOGW(TAG, "Invalid buzzer pattern received: %d", evt->pattern);
return;
}
buzzer_play(evt->pattern);
}
static void evse_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
{
if (base != EVSE_EVENTS || id != EVSE_EVENT_STATE_CHANGED || data == NULL)
return;
const evse_state_event_data_t *evt = (const evse_state_event_data_t *)data;
ESP_LOGD(TAG, "EVSE event: state=%d", evt->state);
buzzer_event_data_t buzzer_evt = {0};
switch (evt->state)
{
case EVSE_STATE_EVENT_IDLE:
buzzer_evt.pattern = BUZZER_PATTERN_UNPLUGGED;
break;
case EVSE_STATE_EVENT_WAITING:
buzzer_evt.pattern = BUZZER_PATTERN_PLUGGED;
break;
case EVSE_STATE_EVENT_CHARGING:
buzzer_evt.pattern = BUZZER_PATTERN_CHARGING;
break;
case EVSE_STATE_EVENT_FAULT:
// Preempta qualquer beep em andamento
buzzer_stop();
buzzer_evt.pattern = BUZZER_PATTERN_FAULT;
break;
default:
return;
}
esp_event_post(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, &buzzer_evt, sizeof(buzzer_evt), portMAX_DELAY);
}
static void network_event_handler(void *handler_args, esp_event_base_t base, int32_t id, void *event_data)
{
if (base != NETWORK_EVENTS)
return;
ESP_LOGI(TAG, "Network event id=%d", (int)id);
buzzer_event_data_t evt = {0};
switch ((network_event_id_t)id)
{
case NETWORK_EVENT_AP_STARTED:
case NETWORK_EVENT_STA_CONNECTED:
// Usa padrão de AP_START para indicar rede disponível
evt.pattern = BUZZER_PATTERN_AP_START;
break;
default:
// Para já, ignorar outros eventos de rede
return;
}
esp_event_post(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, &evt, sizeof(evt), portMAX_DELAY);
}
static void auth_event_handler(void *arg, esp_event_base_t base, int32_t id, void *event_data)
{
if (base != AUTH_EVENTS || event_data == NULL)
return;
buzzer_event_data_t buzzer_evt = {0};
if (id == AUTH_EVENT_TAG_PROCESSED)
{
const auth_tag_event_data_t *evt = (const auth_tag_event_data_t *)event_data;
ESP_LOGD(TAG, "AUTH processed: tag=%s authorized=%d", evt->tag, evt->authorized);
buzzer_evt.pattern = evt->authorized ? BUZZER_PATTERN_CARD_READ : BUZZER_PATTERN_CARD_DENIED;
}
else if (id == AUTH_EVENT_TAG_SAVED)
{
buzzer_evt.pattern = BUZZER_PATTERN_CARD_ADD;
}
else
{
return;
}
esp_event_post(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, &buzzer_evt, sizeof(buzzer_evt), portMAX_DELAY);
}
// ===================== Inicialização / Deinit =====================
void buzzer_init(void)
{
// Config HW
if (s_buzzer_cfg.mode == BUZZER_MODE_PASSIVE)
{
ledc_timer_config_t tcfg = {
.speed_mode = s_buzzer_cfg.ledc_speed_mode,
.duty_resolution = s_buzzer_cfg.duty_resolution,
.timer_num = s_buzzer_cfg.ledc_timer,
.freq_hz = s_buzzer_cfg.freq_hz,
.clk_cfg = LEDC_AUTO_CLK};
ESP_ERROR_CHECK(ledc_timer_config(&tcfg));
ledc_channel_config_t ccfg = {
.gpio_num = s_buzzer_cfg.gpio,
.speed_mode = s_buzzer_cfg.ledc_speed_mode,
.channel = s_buzzer_cfg.ledc_channel,
.intr_type = LEDC_INTR_DISABLE,
.timer_sel = s_buzzer_cfg.ledc_timer,
.duty = 0,
.hpoint = 0};
ESP_ERROR_CHECK(ledc_channel_config(&ccfg));
}
else
{
gpio_config_t io = {
.pin_bit_mask = (1ULL << s_buzzer_cfg.gpio),
.mode = GPIO_MODE_OUTPUT,
.pull_down_en = 0,
.pull_up_en = 0,
.intr_type = GPIO_INTR_DISABLE};
ESP_ERROR_CHECK(gpio_config(&io));
gpio_set_level(s_buzzer_cfg.gpio, 0);
}
buzzer_set_quiet_hours(false, 0, 0);
buzzer_set_enabled(true);
buzzer_off();
// Fila + Task
s_buzzer_q = xQueueCreate(CONFIG_BUZZER_QUEUE_LEN, sizeof(buzzer_pattern_id_t));
configASSERT(s_buzzer_q != NULL);
BaseType_t ok = xTaskCreatePinnedToCore(
buzzer_task,
"buzzer_task",
CONFIG_BUZZER_TASK_STACK,
NULL,
CONFIG_BUZZER_TASK_PRIO,
&s_buzzer_task,
tskNO_AFFINITY);
configASSERT(ok == pdPASS);
// Handlers
ESP_ERROR_CHECK(esp_event_handler_register(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, buzzer_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED, evse_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, auth_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED, auth_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(NETWORK_EVENTS, ESP_EVENT_ANY_ID, network_event_handler, NULL));
ESP_LOGI(TAG, "Buzzer initialized on GPIO %d (%s), freq=%lu Hz, duty=%u%%, enabled=%d",
s_buzzer_cfg.gpio,
s_buzzer_cfg.mode == BUZZER_MODE_PASSIVE ? "passive/PWM" : "active/ON-OFF",
(unsigned long)s_buzzer_cfg.freq_hz,
(unsigned)s_buzzer_cfg.duty_percent,
(int)s_buzzer_ctl.enabled);
}
void buzzer_deinit(void)
{
(void)esp_event_handler_unregister(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, buzzer_event_handler);
(void)esp_event_handler_unregister(EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED, evse_event_handler);
(void)esp_event_handler_unregister(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, auth_event_handler);
(void)esp_event_handler_unregister(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED, auth_event_handler);
(void)esp_event_handler_unregister(NETWORK_EVENTS, ESP_EVENT_ANY_ID, network_event_handler);
if (s_buzzer_q)
{
vQueueDelete(s_buzzer_q);
s_buzzer_q = NULL;
}
if (s_buzzer_task)
{
vTaskDelete(s_buzzer_task);
s_buzzer_task = NULL;
}
buzzer_off();
ESP_LOGI(TAG, "Buzzer deinitialized");
}

View File

@@ -0,0 +1,3 @@
#include "buzzer_events.h"
ESP_EVENT_DEFINE_BASE(BUZZER_EVENTS);

View File

@@ -15,32 +15,6 @@ bool atob(const char *value)
return value[0] == 'y'; return value[0] == 'y';
} }
board_config_energy_meter_t atoem(const char *value)
{
if (!strcmp(value, "cur"))
{
return BOARD_CONFIG_ENERGY_METER_CUR;
}
if (!strcmp(value, "cur_vlt"))
{
return BOARD_CONFIG_ENERGY_METER_CUR_VLT;
}
return BOARD_CONFIG_ENERGY_METER_NONE;
}
board_config_serial_t atoser(const char *value)
{
if (!strcmp(value, "uart"))
{
return BOARD_CONFIG_SERIAL_UART;
}
if (!strcmp(value, "rs485"))
{
return BOARD_CONFIG_SERIAL_RS485;
}
return BOARD_CONFIG_SERIAL_NONE;
}
#define SET_CONFIG_VALUE(name, prop, convert_fn) \ #define SET_CONFIG_VALUE(name, prop, convert_fn) \
if (!strcmp(key, name)) \ if (!strcmp(key, name)) \
{ \ { \
@@ -94,16 +68,14 @@ void board_config_load()
if (value != NULL) if (value != NULL)
{ {
SET_CONFIG_VALUE_STR("DEVICE_NAME", device_name); SET_CONFIG_VALUE_STR("DEVICE_NAME", device_name);
SET_CONFIG_VALUE("LED_CHARGING", led_charging, atob); SET_CONFIG_VALUE("led_blue", led_blue, atob);
SET_CONFIG_VALUE("LED_CHARGING_GPIO", led_charging_gpio, atoi); SET_CONFIG_VALUE("led_blue_GPIO", led_blue_gpio, atoi);
SET_CONFIG_VALUE("LED_ERROR", led_error, atob); SET_CONFIG_VALUE("led_red", led_red, atob);
SET_CONFIG_VALUE("LED_ERROR_GPIO", led_error_gpio, atoi); SET_CONFIG_VALUE("led_red_GPIO", led_red_gpio, atoi);
SET_CONFIG_VALUE("LED_STOP", led_stop, atob); SET_CONFIG_VALUE("led_green", led_green, atob);
SET_CONFIG_VALUE("LED_STOP_GPIO", led_stop_gpio, atoi); SET_CONFIG_VALUE("led_green_GPIO", led_green_gpio, atoi);
SET_CONFIG_VALUE("BUZZER", buzzer, atob); SET_CONFIG_VALUE("BUZZER", buzzer, atob);
SET_CONFIG_VALUE("BUZZER_GPIO", buzzer_gpio, atoi); SET_CONFIG_VALUE("BUZZER_GPIO", buzzer_gpio, atoi);
SET_CONFIG_VALUE("BUTTON_WIFI_GPIO", button_wifi_gpio, atoi); SET_CONFIG_VALUE("BUTTON_WIFI_GPIO", button_wifi_gpio, atoi);
SET_CONFIG_VALUE("PILOT_PWM_GPIO", pilot_pwm_gpio, atoi); SET_CONFIG_VALUE("PILOT_PWM_GPIO", pilot_pwm_gpio, atoi);
SET_CONFIG_VALUE("PILOT_ADC_CHANNEL", pilot_adc_channel, atoi); SET_CONFIG_VALUE("PILOT_ADC_CHANNEL", pilot_adc_channel, atoi);
@@ -130,76 +102,6 @@ void board_config_load()
SET_CONFIG_VALUE("RCM", rcm, atob); SET_CONFIG_VALUE("RCM", rcm, atob);
SET_CONFIG_VALUE("RCM_GPIO", rcm_gpio, atoi); SET_CONFIG_VALUE("RCM_GPIO", rcm_gpio, atoi);
SET_CONFIG_VALUE("RCM_TEST_GPIO", rcm_test_gpio, atoi); SET_CONFIG_VALUE("RCM_TEST_GPIO", rcm_test_gpio, atoi);
SET_CONFIG_VALUE("ENERGY_METER", energy_meter, atoem);
SET_CONFIG_VALUE("ENERGY_METER_THREE_PHASES", energy_meter_three_phases, atob);
SET_CONFIG_VALUE("ENERGY_METER_L1_CUR_ADC_CHANNEL", energy_meter_l1_cur_adc_channel, atoi);
SET_CONFIG_VALUE("ENERGY_METER_L2_CUR_ADC_CHANNEL", energy_meter_l2_cur_adc_channel, atoi);
SET_CONFIG_VALUE("ENERGY_METER_L3_CUR_ADC_CHANNEL", energy_meter_l3_cur_adc_channel, atoi);
SET_CONFIG_VALUE("ENERGY_METER_CUR_SCALE", energy_meter_cur_scale, atoff);
SET_CONFIG_VALUE("ENERGY_METER_L1_VLT_ADC_CHANNEL", energy_meter_l1_vlt_adc_channel, atoi);
SET_CONFIG_VALUE("ENERGY_METER_L2_VLT_ADC_CHANNEL", energy_meter_l2_vlt_adc_channel, atoi);
SET_CONFIG_VALUE("ENERGY_METER_L3_VLT_ADC_CHANNEL", energy_meter_l3_vlt_adc_channel, atoi);
SET_CONFIG_VALUE("ENERGY_METER_VLT_SCALE", energy_meter_vlt_scale, atoff);
SET_CONFIG_VALUE("AUX_IN_1", aux_in_1, atob);
SET_CONFIG_VALUE_STR("AUX_IN_1_NAME", aux_in_1_name);
SET_CONFIG_VALUE("AUX_IN_1_GPIO", aux_in_1_gpio, atoi);
SET_CONFIG_VALUE("AUX_IN_2", aux_in_2, atob);
SET_CONFIG_VALUE_STR("AUX_IN_2_NAME", aux_in_2_name);
SET_CONFIG_VALUE("AUX_IN_2_GPIO", aux_in_2_gpio, atoi);
SET_CONFIG_VALUE("AUX_IN_3", aux_in_3, atob);
SET_CONFIG_VALUE_STR("AUX_IN_3_NAME", aux_in_3_name);
SET_CONFIG_VALUE("AUX_IN_3_GPIO", aux_in_3_gpio, atoi);
SET_CONFIG_VALUE("AUX_IN_4", aux_in_4, atob);
SET_CONFIG_VALUE_STR("AUX_IN_4_NAME", aux_in_4_name);
SET_CONFIG_VALUE("AUX_IN_4_GPIO", aux_in_4_gpio, atoi);
SET_CONFIG_VALUE("AUX_OUT_1", aux_out_1, atob);
SET_CONFIG_VALUE_STR("AUX_OUT_1_NAME", aux_out_1_name);
SET_CONFIG_VALUE("AUX_OUT_1_GPIO", aux_out_1_gpio, atoi);
SET_CONFIG_VALUE("AUX_OUT_2", aux_out_2, atob);
SET_CONFIG_VALUE_STR("AUX_OUT_2_NAME", aux_out_2_name);
SET_CONFIG_VALUE("AUX_OUT_2_GPIO", aux_out_2_gpio, atoi);
SET_CONFIG_VALUE("AUX_OUT_3", aux_out_3, atob);
SET_CONFIG_VALUE_STR("AUX_OUT_3_NAME", aux_out_3_name);
SET_CONFIG_VALUE("AUX_OUT_3_GPIO", aux_out_3_gpio, atoi);
SET_CONFIG_VALUE("AUX_OUT_4", aux_out_4, atob);
SET_CONFIG_VALUE_STR("AUX_OUT_4_NAME", aux_out_4_name);
SET_CONFIG_VALUE("AUX_OUT_4_GPIO", aux_out_4_gpio, atoi);
SET_CONFIG_VALUE("AUX_AIN_1", aux_ain_1, atob);
SET_CONFIG_VALUE_STR("AUX_AIN_1_NAME", aux_ain_1_name);
SET_CONFIG_VALUE("AUX_AIN_1_ADC_CHANNEL", aux_ain_1_adc_channel, atoi);
SET_CONFIG_VALUE("AUX_AIN_2", aux_ain_2, atob);
SET_CONFIG_VALUE_STR("AUX_AIN_2_NAME", aux_ain_2_name);
SET_CONFIG_VALUE("AUX_AIN_2_ADC_CHANNEL", aux_ain_2_adc_channel, atoi);
/*
#if CONFIG_ESP_CONSOLE_UART_NUM != 0
SET_CONFIG_VALUE("SERIAL_1", serial_1, atoser);
SET_CONFIG_VALUE_STR("SERIAL_1_NAME", serial_1_name);
SET_CONFIG_VALUE("SERIAL_1_RXD_GPIO", serial_1_rxd_gpio, atoi);
SET_CONFIG_VALUE("SERIAL_1_TXD_GPIO", serial_1_txd_gpio, atoi);
SET_CONFIG_VALUE("SERIAL_1_RTS_GPIO", serial_1_rts_gpio, atoi);
#endif // CONFIG_ESP_CONSOLE_UART_NUM != 0
#if CONFIG_ESP_CONSOLE_UART_NUM != 1
SET_CONFIG_VALUE("SERIAL_2", serial_2, atoser);
SET_CONFIG_VALUE_STR("SERIAL_2_NAME", serial_2_name);
SET_CONFIG_VALUE("SERIAL_2_RXD_GPIO", serial_2_rxd_gpio, atoi);
SET_CONFIG_VALUE("SERIAL_2_TXD_GPIO", serial_2_txd_gpio, atoi);
SET_CONFIG_VALUE("SERIAL_2_RTS_GPIO", serial_2_rts_gpio, atoi);
#endif // CONFIG_ESP_CONSOLE_UART_NUM != 1
#if SOC_UART_NUM > 2
#if CONFIG_ESP_CONSOLE_UART_NUM != 2
SET_CONFIG_VALUE("SERIAL_3", serial_3, atoser);
SET_CONFIG_VALUE_STR("SERIAL_3_NAME", serial_3_name);
SET_CONFIG_VALUE("SERIAL_3_RXD_GPIO", serial_3_rxd_gpio, atoi);
SET_CONFIG_VALUE("SERIAL_3_TXD_GPIO", serial_3_txd_gpio, atoi);
SET_CONFIG_VALUE("SERIAL_3_RTS_GPIO", serial_3_rts_gpio, atoi);
#endif / CONFIG_ESP_CONSOLE_UART_NUM != 2
#endif // SOC_UART_NUM > 2
SET_CONFIG_VALUE("ONEWIRE", onewire, atob);
SET_CONFIG_VALUE("ONEWIRE_GPIO", onewire_gpio, atoi);
SET_CONFIG_VALUE("ONEWIRE_TEMP_SENSOR", onewire_temp_sensor, atob);
ESP_LOGE(TAG, "Unknown config value %s=%s", key, value);
*/
} }
} }
} }

View File

@@ -5,13 +5,15 @@
#include "hal/gpio_types.h" #include "hal/gpio_types.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
typedef enum { typedef enum
{
BOARD_CONFIG_ENERGY_METER_NONE, BOARD_CONFIG_ENERGY_METER_NONE,
BOARD_CONFIG_ENERGY_METER_CUR, BOARD_CONFIG_ENERGY_METER_CUR,
BOARD_CONFIG_ENERGY_METER_CUR_VLT BOARD_CONFIG_ENERGY_METER_CUR_VLT
} board_config_energy_meter_t; } board_config_energy_meter_t;
typedef enum { typedef enum
{
BOARD_CONFIG_SERIAL_NONE, BOARD_CONFIG_SERIAL_NONE,
BOARD_CONFIG_SERIAL_UART, BOARD_CONFIG_SERIAL_UART,
BOARD_CONFIG_SERIAL_RS485 BOARD_CONFIG_SERIAL_RS485
@@ -21,12 +23,12 @@ typedef struct
{ {
char device_name[32]; char device_name[32];
bool led_charging : 1; bool led_blue : 1;
gpio_num_t led_charging_gpio; gpio_num_t led_blue_gpio;
bool led_error : 1; bool led_red : 1;
gpio_num_t led_error_gpio; gpio_num_t led_red_gpio;
bool led_stop : 1; bool led_green : 1;
gpio_num_t led_stop_gpio; gpio_num_t led_green_gpio;
bool buzzer : 1; bool buzzer : 1;
gpio_num_t buzzer_gpio; gpio_num_t buzzer_gpio;
@@ -63,80 +65,6 @@ typedef struct
gpio_num_t rcm_gpio; gpio_num_t rcm_gpio;
gpio_num_t rcm_test_gpio; gpio_num_t rcm_test_gpio;
board_config_energy_meter_t energy_meter;
bool energy_meter_three_phases : 1;
adc_channel_t energy_meter_l1_cur_adc_channel;
adc_channel_t energy_meter_l2_cur_adc_channel;
adc_channel_t energy_meter_l3_cur_adc_channel;
float energy_meter_cur_scale;
adc_channel_t energy_meter_l1_vlt_adc_channel;
adc_channel_t energy_meter_l2_vlt_adc_channel;
adc_channel_t energy_meter_l3_vlt_adc_channel;
float energy_meter_vlt_scale;
bool aux_in_1 : 1;
char aux_in_1_name[8];
gpio_num_t aux_in_1_gpio;
bool aux_in_2 : 1;
char aux_in_2_name[8];
gpio_num_t aux_in_2_gpio;
bool aux_in_3 : 1;
char aux_in_3_name[8];
gpio_num_t aux_in_3_gpio;
bool aux_in_4 : 1;
char aux_in_4_name[8];
gpio_num_t aux_in_4_gpio;
bool aux_out_1 : 1;
char aux_out_1_name[8];
gpio_num_t aux_out_1_gpio;
bool aux_out_2 : 1;
char aux_out_2_name[8];
gpio_num_t aux_out_2_gpio;
bool aux_out_3 : 1;
char aux_out_3_name[8];
gpio_num_t aux_out_3_gpio;
bool aux_out_4 : 1;
char aux_out_4_name[8];
gpio_num_t aux_out_4_gpio;
bool aux_ain_1 : 1;
char aux_ain_1_name[8];
adc_channel_t aux_ain_1_adc_channel;
bool aux_ain_2 : 1;
char aux_ain_2_name[8];
adc_channel_t aux_ain_2_adc_channel;
board_config_serial_t serial_1;
char serial_1_name[16];
gpio_num_t serial_1_rxd_gpio;
gpio_num_t serial_1_txd_gpio;
gpio_num_t serial_1_rts_gpio;
board_config_serial_t serial_2;
char serial_2_name[16];
gpio_num_t serial_2_rxd_gpio;
gpio_num_t serial_2_txd_gpio;
gpio_num_t serial_2_rts_gpio;
#if SOC_UART_NUM > 2
board_config_serial_t serial_3;
char serial_3_name[16];
gpio_num_t serial_3_rxd_gpio;
gpio_num_t serial_3_txd_gpio;
gpio_num_t serial_3_rts_gpio;
#endif /* SOC_UART_NUM > 2 */
bool onewire : 1;
gpio_num_t onewire_gpio;
bool onewire_temp_sensor : 1;
} board_config_t; } board_config_t;
extern board_config_t board_config; extern board_config_t board_config;

View File

@@ -1,20 +0,0 @@
name: esp_idf_lib_helpers
description: Common support library for esp-idf-lib
version: 1.2.0
groups:
- common
code_owners:
- trombik
- UncleRus
depends:
- freertos
thread_safe: n/a
targets:
- esp32
- esp8266
- esp32s2
- esp32c3
license: ISC
copyrights:
- name: trombik
year: 2019

View File

@@ -1,4 +0,0 @@
idf_component_register(
INCLUDE_DIRS .
REQUIRES freertos
)

View File

@@ -1,13 +0,0 @@
Copyright (c) 2019 Tomoyuki Sakurai <y@trombik.org>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -1,8 +0,0 @@
COMPONENT_ADD_INCLUDEDIRS = .
ifdef CONFIG_IDF_TARGET_ESP8266
COMPONENT_DEPENDS = esp8266 freertos
else
COMPONENT_DEPENDS = freertos
endif

View File

@@ -1,84 +0,0 @@
/*
* Copyright (c) 2019 Tomoyuki Sakurai <y@trombik.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#if !defined(__ESP_IDF_LIB_HELPERS__H__)
#define __ESP_IDF_LIB_HELPERS__H__
/* XXX this header file does not need to include freertos/FreeRTOS.h.
* but without it, ESP8266 RTOS SDK does not include `sdkconfig.h` in correct
* order. as this header depends on sdkconfig.h, sdkconfig.h must be included
* first. however, the SDK includes this header first, then includes
* `sdkconfig.h` when freertos/FreeRTOS.h is not explicitly included. an
* evidence can be found in `build/${COMPONENT}/${COMPONENT}.d` in a failed
* build.
*/
#include <freertos/FreeRTOS.h>
#include <esp_idf_version.h>
#if !defined(ESP_IDF_VERSION) || !defined(ESP_IDF_VERSION_VAL)
#error Unknown ESP-IDF/ESP8266 RTOS SDK version
#endif
/* Minimal supported version for ESP32, ESP32S2 */
#define HELPER_ESP32_MIN_VER ESP_IDF_VERSION_VAL(3, 3, 5)
/* Minimal supported version for ESP8266 */
#define HELPER_ESP8266_MIN_VER ESP_IDF_VERSION_VAL(3, 3, 0)
/* HELPER_TARGET_IS_ESP32
* 1 when the target is esp32
*/
#if defined(CONFIG_IDF_TARGET_ESP32) \
|| defined(CONFIG_IDF_TARGET_ESP32S2) \
|| defined(CONFIG_IDF_TARGET_ESP32S3) \
|| defined(CONFIG_IDF_TARGET_ESP32C2) \
|| defined(CONFIG_IDF_TARGET_ESP32C3) \
|| defined(CONFIG_IDF_TARGET_ESP32C6) \
|| defined(CONFIG_IDF_TARGET_ESP32H2)
#define HELPER_TARGET_IS_ESP32 (1)
#define HELPER_TARGET_IS_ESP8266 (0)
/* HELPER_TARGET_IS_ESP8266
* 1 when the target is esp8266
*/
#elif defined(CONFIG_IDF_TARGET_ESP8266)
#define HELPER_TARGET_IS_ESP32 (0)
#define HELPER_TARGET_IS_ESP8266 (1)
#else
#error BUG: cannot determine the target
#endif
#if HELPER_TARGET_IS_ESP32 && ESP_IDF_VERSION < HELPER_ESP32_MIN_VER
#error Unsupported ESP-IDF version. Please update!
#endif
#if HELPER_TARGET_IS_ESP8266 && ESP_IDF_VERSION < HELPER_ESP8266_MIN_VER
#error Unsupported ESP8266 RTOS SDK version. Please update!
#endif
/* show the actual values for debugging */
#if DEBUG
#define VALUE_TO_STRING(x) #x
#define VALUE(x) VALUE_TO_STRING(x)
#define VAR_NAME_VALUE(var) #var "=" VALUE(var)
#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32C3))
#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32H2))
#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32S2))
#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32))
#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP8266))
#pragma message(VAR_NAME_VALUE(ESP_IDF_VERSION_MAJOR))
#endif
#endif

View File

@@ -1,21 +0,0 @@
#if CONFIG_IDF_TARGET_ESP32
#include <esp32/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32C2
#include <esp32c2/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32C3
#include <esp32c3/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32C6
#include <esp32c6/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32H2
#include <esp32h2/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32H4
#include <esp32h4/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32S2
#include <esp32s2/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP32S3
#include <esp32s3/rom/ets_sys.h>
#elif CONFIG_IDF_TARGET_ESP8266
#include <rom/ets_sys.h>
#else
#error "ets_sys: Unknown target"
#endif

View File

@@ -8,11 +8,15 @@ set(srcs
evse_fsm.c evse_fsm.c
evse_manager.c evse_manager.c
evse_hardware.c evse_hardware.c
evse_pilot.c
evse_meter.c
evse_session.c
evse_api.c
) )
idf_component_register( idf_component_register(
SRCS ${srcs} SRCS ${srcs}
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
PRIV_REQUIRES nvs_flash PRIV_REQUIRES nvs_flash driver
REQUIRES peripherals auth REQUIRES peripherals auth loadbalancer scheduler
) )

37
components/evse/evse_api.c Executable file
View File

@@ -0,0 +1,37 @@
// evse_api.c - Main EVSE control logic
#include "evse_fsm.h"
#include "evse_error.h"
#include "evse_limits.h"
#include "evse_config.h"
#include "evse_api.h"
#include "evse_session.h"
#include "evse_pilot.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_log.h"
static const char *TAG = "evse_api";
// ================================
// Public Configuration Interface
// ================================
void evse_set_enabled(bool value) {
ESP_LOGI(TAG, "Set enabled %d", value);
evse_config_set_enabled(value);
}
bool evse_is_available(void) {
return evse_config_is_available();
}
void evse_set_available(bool value) {
ESP_LOGI(TAG, "Set available %d", value);
evse_config_set_available(value);
}
bool evse_get_session(evse_session_t *out) {
return evse_session_get(out);
}

View File

@@ -1,279 +1,352 @@
#include <inttypes.h> // Include for PRI macros #include <inttypes.h> // For PRI macros
#include "evse_config.h" #include "evse_config.h"
#include "board_config.h" #include "board_config.h"
#include "evse_limits.h" #include "evse_limits.h"
#include "evse_api.h"
#include "esp_log.h" #include "esp_log.h"
#include "nvs.h" #include "nvs.h"
#include "esp_timer.h"
static const char *TAG = "evse_config"; static const char *TAG = "evse_config";
static nvs_handle_t nvs; static nvs_handle_t nvs;
// ========================
// Configurable parameters // Configurable parameters
static uint8_t max_charging_current = MAX_CHARGING_CURRENT_LIMIT; // ========================
static uint8_t grid_max_current = MAX_GRID_CURRENT_LIMIT; static uint8_t max_charging_current = MAX_CHARGING_CURRENT_LIMIT;
static uint16_t charging_current; static uint16_t charging_current; // Persisted (NVS)
static bool socket_outlet; static uint16_t charging_current_runtime = 0; // Runtime only
static bool rcm; static bool socket_outlet;
static uint8_t temp_threshold = 60; static bool rcm;
static bool require_auth; static uint8_t temp_threshold = 60;
static bool require_auth;
esp_err_t evse_config_init(void) { // Availability / Enable flags
static bool is_available = true;
static bool is_enabled = true;
// ========================
// Initialization
// ========================
esp_err_t evse_config_init(void)
{
ESP_LOGD(TAG, "Initializing NVS configuration..."); ESP_LOGD(TAG, "Initializing NVS configuration...");
ESP_LOGI(TAG, "Opening NVS namespace");
return nvs_open("evse", NVS_READWRITE, &nvs); return nvs_open("evse", NVS_READWRITE, &nvs);
} }
void evse_check_defaults(void) { void evse_check_defaults(void)
{
esp_err_t err; esp_err_t err;
uint8_t u8; uint8_t u8;
uint16_t u16; uint16_t u16;
uint32_t u32; uint32_t u32;
bool needs_commit = false; bool needs_commit = false;
uint8_t u8_bool;
ESP_LOGD(TAG, "Checking default parameters..."); ESP_LOGD(TAG, "Checking default parameters...");
// Max charging current // Max charging current
err = nvs_get_u8(nvs, "max_chrg_curr", &u8); err = nvs_get_u8(nvs, "max_chrg_curr", &u8);
ESP_LOGD(TAG, "Max charging current read: %d", u8); if (err != ESP_OK || u8 < MIN_CHARGING_CURRENT_LIMIT || u8 > MAX_CHARGING_CURRENT_LIMIT)
if (err != ESP_OK || u8 < MIN_CHARGING_CURRENT_LIMIT || u8 > MAX_CHARGING_CURRENT_LIMIT) { {
max_charging_current = MAX_CHARGING_CURRENT_LIMIT; max_charging_current = MAX_CHARGING_CURRENT_LIMIT;
nvs_set_u8(nvs, "max_chrg_curr", max_charging_current); nvs_set_u8(nvs, "max_chrg_curr", max_charging_current);
needs_commit = true; needs_commit = true;
ESP_LOGD(TAG, "Max charging current adjusted to: %d", max_charging_current); ESP_LOGW(TAG, "Invalid or missing max_chrg_curr, resetting to %d", max_charging_current);
} else { }
else
{
max_charging_current = u8; max_charging_current = u8;
} }
// Grid max current // Charging current (default, persisted)
err = nvs_get_u8(nvs, "grid_max_curr", &u8);
ESP_LOGD(TAG, "Grid max current read: %d", u8);
if (err != ESP_OK || u8 < MIN_GRID_CURRENT_LIMIT || u8 > MAX_GRID_CURRENT_LIMIT) {
grid_max_current = MAX_GRID_CURRENT_LIMIT;
nvs_set_u8(nvs, "grid_max_curr", grid_max_current);
needs_commit = true;
ESP_LOGD(TAG, "Grid max current adjusted to: %d", grid_max_current);
} else {
grid_max_current = u8;
}
// Charging current (decA)
err = nvs_get_u16(nvs, "def_chrg_curr", &u16); err = nvs_get_u16(nvs, "def_chrg_curr", &u16);
ESP_LOGD(TAG, "Charging current read: %d", u16); if (err != ESP_OK || u16 < (MIN_CHARGING_CURRENT_LIMIT) || u16 > (max_charging_current))
if (err != ESP_OK || u16 < (MIN_CHARGING_CURRENT_LIMIT * 10) || u16 > (max_charging_current * 10)) { {
charging_current = max_charging_current * 10; charging_current = max_charging_current;
nvs_set_u16(nvs, "def_chrg_curr", charging_current); nvs_set_u16(nvs, "def_chrg_curr", charging_current);
needs_commit = true; needs_commit = true;
ESP_LOGD(TAG, "Charging current adjusted to: %d", charging_current); ESP_LOGW(TAG, "Invalid or missing def_chrg_curr, resetting to %d", charging_current);
} else { }
else
{
charging_current = u16; charging_current = u16;
} }
// Runtime charging current inicializado a partir do default persistido
charging_current_runtime = charging_current;
ESP_LOGD(TAG, "Runtime charging current initialized to: %d", charging_current_runtime);
// Auth required
err = nvs_get_u8(nvs, "require_auth", &u8); err = nvs_get_u8(nvs, "require_auth", &u8);
require_auth = (err == ESP_OK && u8 <= 1) ? u8 : false; require_auth = (err == ESP_OK && u8 <= 1) ? u8 : false;
if (err != ESP_OK) { if (err != ESP_OK)
{
nvs_set_u8(nvs, "require_auth", require_auth); nvs_set_u8(nvs, "require_auth", require_auth);
needs_commit = true; needs_commit = true;
ESP_LOGD(TAG, "Require auth adjusted to: %d", require_auth);
} }
// Socket outlet
err = nvs_get_u8(nvs, "socket_outlet", &u8); err = nvs_get_u8(nvs, "socket_outlet", &u8);
socket_outlet = (err == ESP_OK && u8) && board_config.proximity; socket_outlet = (err == ESP_OK && u8) && board_config.proximity;
if (err != ESP_OK) { if (err != ESP_OK)
{
nvs_set_u8(nvs, "socket_outlet", socket_outlet); nvs_set_u8(nvs, "socket_outlet", socket_outlet);
needs_commit = true; needs_commit = true;
ESP_LOGD(TAG, "Socket outlet adjusted to: %d", socket_outlet);
} }
// RCM
err = nvs_get_u8(nvs, "rcm", &u8); err = nvs_get_u8(nvs, "rcm", &u8);
rcm = (err == ESP_OK && u8) && board_config.rcm; rcm = (err == ESP_OK && u8) && board_config.rcm;
if (err != ESP_OK) { if (err != ESP_OK)
{
nvs_set_u8(nvs, "rcm", rcm); nvs_set_u8(nvs, "rcm", rcm);
needs_commit = true; needs_commit = true;
ESP_LOGD(TAG, "RCM adjusted to: %d", rcm);
} }
// Temp threshold
err = nvs_get_u8(nvs, "temp_threshold", &u8); err = nvs_get_u8(nvs, "temp_threshold", &u8);
temp_threshold = (err == ESP_OK && u8 >= 40 && u8 <= 80) ? u8 : 60; temp_threshold = (err == ESP_OK && u8 >= 40 && u8 <= 80) ? u8 : 60;
if (err != ESP_OK) { if (err != ESP_OK)
{
nvs_set_u8(nvs, "temp_threshold", temp_threshold); nvs_set_u8(nvs, "temp_threshold", temp_threshold);
needs_commit = true; needs_commit = true;
ESP_LOGD(TAG, "Temp threshold adjusted to: %d", temp_threshold);
} }
// Additional limits // Optional limits
if (nvs_get_u32(nvs, "def_cons_lim", &u32) == ESP_OK) { if (nvs_get_u32(nvs, "def_cons_lim", &u32) == ESP_OK)
evse_set_consumption_limit(u32); evse_set_consumption_limit(u32);
ESP_LOGD(TAG, "Consumption limit read and applied: %" PRIu32, u32); // Updated to PRIu32
}
if (nvs_get_u32(nvs, "def_ch_time_lim", &u32) == ESP_OK) { if (nvs_get_u32(nvs, "def_ch_time_lim", &u32) == ESP_OK)
evse_set_charging_time_limit(u32); evse_set_charging_time_limit(u32);
ESP_LOGD(TAG, "Charging time limit read and applied: %" PRIu32, u32); // Updated to PRIu32
}
if (nvs_get_u16(nvs, "def_un_pwr_lim", &u16) == ESP_OK) { if (nvs_get_u16(nvs, "def_un_pwr_lim", &u16) == ESP_OK)
evse_set_under_power_limit(u16); evse_set_under_power_limit(u16);
ESP_LOGD(TAG, "Under power limit read and applied: %d", u16);
// Availability (persist)
if (nvs_get_u8(nvs, "available", &u8_bool) == ESP_OK && u8_bool <= 1)
{
is_available = (u8_bool != 0);
}
else
{
is_available = true; // default
nvs_set_u8(nvs, "available", (uint8_t)is_available);
needs_commit = true;
ESP_LOGW(TAG, "Missing 'available' -> default=true (persisted).");
} }
if (needs_commit) { // Enabled (persist)
nvs_commit(nvs); if (nvs_get_u8(nvs, "enabled", &u8_bool) == ESP_OK && u8_bool <= 1)
ESP_LOGD(TAG, "Changes committed to NVS."); {
is_enabled = (u8_bool != 0);
}
else
{
is_enabled = true; // default
nvs_set_u8(nvs, "enabled", (uint8_t)is_enabled);
needs_commit = true;
ESP_LOGW(TAG, "Missing 'enabled' -> default=true (persisted).");
}
if (needs_commit)
{
err = nvs_commit(nvs);
if (err == ESP_OK)
{
ESP_LOGD(TAG, "Configuration committed to NVS.");
}
else
{
ESP_LOGE(TAG, "Failed to commit configuration to NVS: %s", esp_err_to_name(err));
}
} }
} }
// Current // ========================
uint8_t evse_get_max_charging_current(void) { // Charging current getters/setters
ESP_LOGI(TAG, "Max charging current read: %d", max_charging_current); // ========================
uint8_t evse_get_max_charging_current(void)
{
return max_charging_current; return max_charging_current;
} }
esp_err_t evse_set_max_charging_current(uint8_t value) { esp_err_t evse_set_max_charging_current(uint8_t value)
ESP_LOGI(TAG, "Attempting to set max charging current: %d", value); {
if (value < MIN_CHARGING_CURRENT_LIMIT || value > MAX_CHARGING_CURRENT_LIMIT) if (value < MIN_CHARGING_CURRENT_LIMIT || value > MAX_CHARGING_CURRENT_LIMIT)
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
max_charging_current = value; max_charging_current = value;
evse_set_runtime_charging_current(value);
nvs_set_u8(nvs, "max_chrg_curr", value); nvs_set_u8(nvs, "max_chrg_curr", value);
nvs_commit(nvs); return nvs_commit(nvs);
ESP_LOGD(TAG, "Max charging current adjusted to: %d", max_charging_current);
return ESP_OK;
} }
uint8_t grid_get_max_current(void) { uint16_t evse_get_charging_current(void)
ESP_LOGD(TAG, "Grid max current read: %d", grid_max_current); {
return grid_max_current;
}
esp_err_t grid_set_max_current(uint8_t value) {
ESP_LOGD(TAG, "Attempting to set grid max current: %d", value);
if (value < MIN_GRID_CURRENT_LIMIT || value > MAX_GRID_CURRENT_LIMIT)
return ESP_ERR_INVALID_ARG;
grid_max_current = value;
nvs_set_u8(nvs, "grid_max_curr", value);
nvs_commit(nvs);
ESP_LOGD(TAG, "Grid max current adjusted to: %d", grid_max_current);
return ESP_OK;
}
uint16_t evse_get_charging_current(void) {
ESP_LOGD(TAG, "Charging current read: %d", charging_current);
return charging_current; return charging_current;
} }
esp_err_t evse_set_charging_current(uint16_t value) { esp_err_t evse_set_charging_current(uint16_t value)
ESP_LOGD(TAG, "Attempting to set charging current: %d", value); {
if (value < (MIN_CHARGING_CURRENT_LIMIT * 10) || value > (max_charging_current * 10)) if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current))
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
charging_current = value; charging_current = value;
nvs_set_u16(nvs, "def_chrg_curr", value); nvs_set_u16(nvs, "def_chrg_curr", value);
nvs_commit(nvs); return nvs_commit(nvs);
ESP_LOGD(TAG, "Charging current adjusted to: %d", charging_current);
return ESP_OK;
} }
uint16_t evse_get_default_charging_current(void) { uint16_t evse_get_default_charging_current(void)
{
uint16_t value; uint16_t value;
nvs_get_u16(nvs, "def_chrg_curr", &value); if (nvs_get_u16(nvs, "def_chrg_curr", &value) == ESP_OK)
ESP_LOGD(TAG, "Default charging current read: %d", value); return value;
return value; return charging_current;
} }
esp_err_t evse_set_default_charging_current(uint16_t value) { esp_err_t evse_set_default_charging_current(uint16_t value)
ESP_LOGD(TAG, "Attempting to set default charging current: %d", value); {
if (value < (MIN_CHARGING_CURRENT_LIMIT * 10) || value > (max_charging_current * 10)) if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current))
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
nvs_set_u16(nvs, "def_chrg_curr", value); nvs_set_u16(nvs, "def_chrg_curr", value);
nvs_commit(nvs); return nvs_commit(nvs);
ESP_LOGD(TAG, "Default charging current adjusted to: %d", value);
return ESP_OK;
} }
// ========================
// Runtime current (not saved)
// ========================
void evse_set_runtime_charging_current(uint16_t value)
{
if (value > max_charging_current)
{
value = max_charging_current;
}
else if (value < MIN_CHARGING_CURRENT_LIMIT)
{
value = MIN_CHARGING_CURRENT_LIMIT;
}
charging_current_runtime = value;
ESP_LOGI(TAG, "Runtime charging current updated: %d", charging_current_runtime);
evse_config_event_data_t evt = {
.charging = evse_state_is_charging(evse_get_state()),
.hw_max_current = (float)evse_get_max_charging_current(),
.runtime_current = (float)evse_get_runtime_charging_current(),
.timestamp_us = esp_timer_get_time()};
esp_event_post(EVSE_EVENTS,
EVSE_EVENT_CONFIG_UPDATED,
&evt,
sizeof(evt),
portMAX_DELAY);
}
uint16_t evse_get_runtime_charging_current(void)
{
return charging_current_runtime;
}
// ========================
// Socket outlet // Socket outlet
bool evse_get_socket_outlet(void) { // ========================
ESP_LOGD(TAG, "Socket outlet read: %d", socket_outlet); bool evse_get_socket_outlet(void)
{
return socket_outlet; return socket_outlet;
} }
esp_err_t evse_set_socket_outlet(bool value) { esp_err_t evse_set_socket_outlet(bool value)
ESP_LOGD(TAG, "Attempting to set socket outlet: %d", value); {
if (value && !board_config.proximity) return ESP_ERR_INVALID_ARG; if (value && !board_config.proximity)
return ESP_ERR_INVALID_ARG;
socket_outlet = value; socket_outlet = value;
nvs_set_u8(nvs, "socket_outlet", value); nvs_set_u8(nvs, "socket_outlet", value);
nvs_commit(nvs); return nvs_commit(nvs);
ESP_LOGD(TAG, "Socket outlet adjusted to: %d", socket_outlet);
return ESP_OK;
} }
// ========================
// RCM // RCM
bool evse_is_rcm(void) { // ========================
ESP_LOGD(TAG, "RCM read: %d", rcm); bool evse_is_rcm(void)
{
return rcm; return rcm;
} }
esp_err_t evse_set_rcm(bool value) { esp_err_t evse_set_rcm(bool value)
ESP_LOGD(TAG, "Attempting to set RCM: %d", value); {
if (value && !board_config.rcm) return ESP_ERR_INVALID_ARG; if (value && !board_config.rcm)
return ESP_ERR_INVALID_ARG;
rcm = value; rcm = value;
nvs_set_u8(nvs, "rcm", value); nvs_set_u8(nvs, "rcm", value);
nvs_commit(nvs); return nvs_commit(nvs);
ESP_LOGD(TAG, "RCM adjusted to: %d", rcm);
return ESP_OK;
} }
// ========================
// Temperature // Temperature
uint8_t evse_get_temp_threshold(void) { // ========================
ESP_LOGD(TAG, "Temp threshold read: %d", temp_threshold); uint8_t evse_get_temp_threshold(void)
{
return temp_threshold; return temp_threshold;
} }
esp_err_t evse_set_temp_threshold(uint8_t value) { esp_err_t evse_set_temp_threshold(uint8_t value)
ESP_LOGI(TAG, "Attempting to set temp threshold: %d", value); {
if (value < 40 || value > 80) return ESP_ERR_INVALID_ARG; if (value < 40 || value > 80)
return ESP_ERR_INVALID_ARG;
temp_threshold = value; temp_threshold = value;
nvs_set_u8(nvs, "temp_threshold", value); nvs_set_u8(nvs, "temp_threshold", value);
nvs_commit(nvs); return nvs_commit(nvs);
ESP_LOGI(TAG, "Temp threshold adjusted to: %d", temp_threshold);
return ESP_OK;
}
// Authentication
bool evse_is_require_auth(void) {
ESP_LOGD(TAG, "Require auth read: %d", require_auth);
return require_auth;
}
void evse_set_require_auth(bool value) {
ESP_LOGI(TAG, "Attempting to set require auth: %d", value);
require_auth = value;
nvs_set_u8(nvs, "require_auth", value);
nvs_commit(nvs);
ESP_LOGD(TAG, "Require auth adjusted to: %d", require_auth);
} }
// ========================
// Availability // Availability
static bool is_available = true; // ========================
bool evse_config_is_available(void)
bool evse_config_is_available(void) { {
ESP_LOGD(TAG, "Checking availability: %d", is_available);
return is_available; return is_available;
} }
void evse_config_set_available(bool available) { void evse_config_set_available(bool available)
ESP_LOGD(TAG, "Setting availability to: %d", available); {
is_available = available; is_available = available ? true : false;
esp_err_t err = nvs_set_u8(nvs, "available", (uint8_t)is_available);
if (err == ESP_OK)
err = nvs_commit(nvs);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to persist 'available': %s", esp_err_to_name(err));
}
evse_available_event_data_t e = {
.available = is_available,
.timestamp_us = esp_timer_get_time()};
esp_event_post(EVSE_EVENTS, EVSE_EVENT_AVAILABLE_UPDATED, &e, sizeof(e), portMAX_DELAY);
} }
// ========================
// Enable/Disable // Enable/Disable
static bool is_enabled = true; // ========================
bool evse_config_is_enabled(void)
bool evse_config_is_enabled(void) { {
ESP_LOGD(TAG, "Checking if enabled: %d", is_enabled);
return is_enabled; return is_enabled;
} }
void evse_config_set_enabled(bool enabled) { void evse_config_set_enabled(bool enabled)
ESP_LOGD(TAG, "Setting enabled state to: %d", enabled); {
is_enabled = enabled; is_enabled = enabled ? true : false;
esp_err_t err = nvs_set_u8(nvs, "enabled", (uint8_t)is_enabled);
if (err == ESP_OK)
err = nvs_commit(nvs);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to persist 'enabled': %s", esp_err_to_name(err));
}
evse_enable_event_data_t e = {
.enabled = is_enabled,
.timestamp_us = esp_timer_get_time()};
esp_event_post(EVSE_EVENTS, EVSE_EVENT_ENABLE_UPDATED, &e, sizeof(e), portMAX_DELAY);
} }

View File

@@ -1,11 +1,10 @@
// evse_core.c - Função principal de controle do EVSE
#include "evse_fsm.h" #include "evse_fsm.h"
#include "evse_error.h" #include "evse_error.h"
#include "evse_limits.h" #include "evse_limits.h"
#include "evse_config.h" #include "evse_config.h"
#include "evse_api.h" #include "evse_api.h"
#include "pilot.h" #include "evse_session.h"
#include "evse_pilot.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
#include "esp_log.h" #include "esp_log.h"
@@ -13,26 +12,36 @@
static const char *TAG = "evse_core"; static const char *TAG = "evse_core";
static SemaphoreHandle_t mutex; static SemaphoreHandle_t mutex;
static evse_state_t last_state = EVSE_STATE_A; static evse_state_t last_state = EVSE_STATE_A;
static bool authorized = false;
static TickType_t auth_grant_to = 0;
static void evse_process(void);
static void evse_core_task(void *arg); static void evse_core_task(void *arg);
void evse_init(void) { void evse_init(void)
{
ESP_LOGI(TAG, "EVSE Init"); ESP_LOGI(TAG, "EVSE Init");
mutex = xSemaphoreCreateMutex(); mutex = xSemaphoreCreateMutex();
if (!mutex)
{
ESP_LOGE(TAG, "Failed to create EVSE core mutex");
return;
}
evse_check_defaults(); evse_check_defaults();
evse_fsm_reset(); evse_fsm_reset();
pilot_set_level(true); // Estado inicial do piloto pilot_set_level(true);
xTaskCreate(evse_core_task, "evse_core_task", 4096, NULL, 5, NULL); xTaskCreate(evse_core_task, "evse_core_task", 4096, NULL, 5, NULL);
} }
void evse_process(void) { static void evse_process(void)
{
if (!mutex)
{
return;
}
xSemaphoreTake(mutex, portMAX_DELAY); xSemaphoreTake(mutex, portMAX_DELAY);
pilot_voltage_t pilot_voltage; pilot_voltage_t pilot_voltage;
@@ -43,77 +52,37 @@ void evse_process(void) {
evse_error_check(pilot_voltage, is_n12v); evse_error_check(pilot_voltage, is_n12v);
if (evse_get_error() == 0 && !evse_is_error_cleared()) { evse_fsm_process(
pilot_voltage,
evse_state_get_authorized(),
evse_config_is_available(),
evse_config_is_enabled());
bool authorized = evse_state_get_authorized(); evse_limits_check();
evse_fsm_process( if (evse_is_limit_reached())
pilot_voltage, {
authorized, if (evse_state_get_authorized())
evse_config_is_available(), {
evse_config_is_enabled() ESP_LOGW(TAG, "Charging limit reached → revoking authorization");
); evse_state_set_authorized(false);
evse_limits_check(evse_get_state());
evse_state_t current = evse_get_state();
if (current != last_state) {
ESP_LOGI(TAG, "State changed: %s → %s", evse_state_to_str(last_state), evse_state_to_str(current));
last_state = current;
} }
} }
if (evse_get_error() == 0) { evse_state_t current = evse_get_state();
evse_mark_error_cleared(); if (current != last_state)
{
last_state = current;
} }
xSemaphoreGive(mutex); xSemaphoreGive(mutex);
} }
// ================================ static void evse_core_task(void *arg)
// Interface pública {
// ================================ (void)arg;
while (true)
bool evse_is_enabled(void) { {
return evse_config_is_enabled();
}
void evse_set_enabled(bool value) {
ESP_LOGI(TAG, "Set enabled %d", value);
evse_config_set_enabled(value);
}
bool evse_is_available(void) {
return evse_config_is_available();
}
void evse_set_available(bool value) {
ESP_LOGI(TAG, "Set available %d", value);
evse_config_set_available(value);
}
bool evse_is_pending_auth(void) {
return evse_state_is_session(evse_get_state()) && !authorized;
}
void evse_authorize(void) {
ESP_LOGI(TAG, "Authorize");
evse_state_set_authorized(true);
}
void evse_set_authorized(bool value) {
ESP_LOGI(TAG, "Set authorized %d", value);
xSemaphoreTake(mutex, portMAX_DELAY);
authorized = value;
xSemaphoreGive(mutex);
}
// ================================
// Tarefa principal
// ================================
static void evse_core_task(void *arg) {
while (true) {
evse_process(); evse_process();
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(100));
} }

View File

@@ -3,117 +3,227 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/portmacro.h"
#include "esp_log.h" #include "esp_log.h"
#include "ntc_sensor.h" #include "ntc_sensor.h"
static const char *TAG = "evse_error"; static const char *TAG = "evse_error";
// Estado global de erros
static uint32_t error_bits = 0; static uint32_t error_bits = 0;
static TickType_t auto_clear_timeout = 0; static TickType_t auto_clear_timeout = 0;
// Sticky flag: "todos erros foram limpos"
static bool error_cleared = false; static bool error_cleared = false;
void evse_error_init(void) { // Proteção contra concorrência
// Inicialização do sistema de erros static portMUX_TYPE error_mux = portMUX_INITIALIZER_UNLOCKED;
void evse_error_init(void)
{
portENTER_CRITICAL(&error_mux);
error_bits = 0;
auto_clear_timeout = 0;
error_cleared = false;
portEXIT_CRITICAL(&error_mux);
} }
void evse_error_check(pilot_voltage_t pilot_voltage, bool is_n12v) { void evse_error_check(pilot_voltage_t pilot_voltage, bool is_n12v)
ESP_LOGD(TAG, "Verificando erro: pilot_voltage = %d, is_n12v = %s", {
ESP_LOGD(TAG, "Verificando erro: pilot_voltage=%d, is_n12v=%s",
pilot_voltage, is_n12v ? "true" : "false"); pilot_voltage, is_n12v ? "true" : "false");
// Falha elétrica geral no pilot // 1) Falha elétrica geral no pilot
if (pilot_voltage == PILOT_VOLTAGE_1) { if (pilot_voltage == PILOT_VOLTAGE_1)
if (!(error_bits & EVSE_ERR_PILOT_FAULT_BIT)) { // Verifica se o erro já foi registrado {
evse_error_set(EVSE_ERR_PILOT_FAULT_BIT); bool first_time = false;
portENTER_CRITICAL(&error_mux);
if (!(error_bits & EVSE_ERR_PILOT_FAULT_BIT))
{
error_cleared = false;
error_bits |= EVSE_ERR_PILOT_FAULT_BIT;
first_time = true;
}
portEXIT_CRITICAL(&error_mux);
if (first_time)
{
ESP_LOGW(TAG, "Erro: pilot abaixo de 2V (falha)"); ESP_LOGW(TAG, "Erro: pilot abaixo de 2V (falha)");
} }
} }
else
{
// Pilot voltou a nível válido → limpa erro de pilot fault
evse_error_clear(EVSE_ERR_PILOT_FAULT_BIT);
}
// Falta de -12V durante PWM (C ou D) // 2) Falta de -12V durante PWM (C ou D)
if ((pilot_voltage == PILOT_VOLTAGE_6 || pilot_voltage == PILOT_VOLTAGE_3) && !is_n12v) { if ((pilot_voltage == PILOT_VOLTAGE_6 || pilot_voltage == PILOT_VOLTAGE_3) && !is_n12v)
if (!(error_bits & EVSE_ERR_DIODE_SHORT_BIT)) { // Verifica se o erro já foi registrado {
evse_error_set(EVSE_ERR_DIODE_SHORT_BIT); bool first_time = false;
portENTER_CRITICAL(&error_mux);
if (!(error_bits & EVSE_ERR_DIODE_SHORT_BIT))
{
error_cleared = false;
error_bits |= EVSE_ERR_DIODE_SHORT_BIT;
auto_clear_timeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000);
first_time = true;
}
portEXIT_CRITICAL(&error_mux);
if (first_time)
{
ESP_LOGW(TAG, "Erro: ausência de -12V no PWM (sem diodo)"); ESP_LOGW(TAG, "Erro: ausência de -12V no PWM (sem diodo)");
} }
} }
else
{
// Se já não estamos em C/D sem -12V, limpa o erro de diodo curto
evse_error_clear(EVSE_ERR_DIODE_SHORT_BIT);
}
} }
void evse_temperature_check(void) { void evse_temperature_check(void)
float temp_c = ntc_temp_sensor(); // leitura atual (última medida válida) {
uint8_t threshold = evse_get_temp_threshold(); // padrão 60°C, configurável float temp_c = ntc_temp_sensor();
uint8_t threshold = evse_get_temp_threshold();
// Log informativo com os valores ESP_LOGD(TAG, "Verificando temperatura: atual=%.2f °C, limite=%d °C",
ESP_LOGD(TAG, "Verificando temperatura: atual = %.2f °C, limite = %d °C", temp_c, threshold); temp_c, threshold);
// Se a temperatura parecer inválida, aplica erro de sensor // Temperatura inválida -> erro de sensor
if (temp_c < -40.0f || temp_c > 150.0f) { if (temp_c < -40.0f || temp_c > 150.0f)
if (!(error_bits & EVSE_ERR_TEMPERATURE_FAULT_BIT)) { // Verifica se o erro já foi registrado {
evse_error_set(EVSE_ERR_TEMPERATURE_FAULT_BIT); bool first_time = false;
portENTER_CRITICAL(&error_mux);
if (!(error_bits & EVSE_ERR_TEMPERATURE_FAULT_BIT))
{
error_cleared = false;
error_bits |= EVSE_ERR_TEMPERATURE_FAULT_BIT;
first_time = true;
}
portEXIT_CRITICAL(&error_mux);
if (first_time)
{
ESP_LOGW(TAG, "Sensor NTC falhou ou está desconectado"); ESP_LOGW(TAG, "Sensor NTC falhou ou está desconectado");
} }
return; return;
} }
evse_error_clear(EVSE_ERR_TEMPERATURE_FAULT_BIT); // leitura válida // Leitura válida -> limpa erro de sensor
evse_error_clear(EVSE_ERR_TEMPERATURE_FAULT_BIT);
if (temp_c >= threshold) { // Temperatura máxima
if (!(error_bits & EVSE_ERR_TEMPERATURE_HIGH_BIT)) { // Verifica se o erro já foi registrado if (temp_c >= threshold)
evse_error_set(EVSE_ERR_TEMPERATURE_HIGH_BIT); {
ESP_LOGW(TAG, "Temperatura acima do limite: %.2f °C ≥ %d °C", temp_c, threshold); bool first_time = false;
portENTER_CRITICAL(&error_mux);
if (!(error_bits & EVSE_ERR_TEMPERATURE_HIGH_BIT))
{
error_cleared = false;
error_bits |= EVSE_ERR_TEMPERATURE_HIGH_BIT;
auto_clear_timeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000);
first_time = true;
} }
} else { portEXIT_CRITICAL(&error_mux);
if (first_time)
{
ESP_LOGW(TAG, "Temperatura acima do limite: %.2f °C ≥ %d °C",
temp_c, threshold);
}
}
else
{
evse_error_clear(EVSE_ERR_TEMPERATURE_HIGH_BIT); evse_error_clear(EVSE_ERR_TEMPERATURE_HIGH_BIT);
} }
} }
uint32_t evse_get_error(void) { uint32_t evse_get_error(void)
return error_bits; {
portENTER_CRITICAL(&error_mux);
uint32_t val = error_bits;
portEXIT_CRITICAL(&error_mux);
return val;
} }
bool evse_is_error_cleared(void) { bool evse_error_cleared_flag(void)
return error_cleared; {
portENTER_CRITICAL(&error_mux);
bool v = error_cleared;
portEXIT_CRITICAL(&error_mux);
return v;
} }
void evse_mark_error_cleared(void) { void evse_error_reset_flag(void)
{
portENTER_CRITICAL(&error_mux);
error_cleared = false; error_cleared = false;
portEXIT_CRITICAL(&error_mux);
} }
// Já existentes void evse_error_set(uint32_t bitmask)
void evse_error_set(uint32_t bitmask) { {
portENTER_CRITICAL(&error_mux);
error_cleared = false;
error_bits |= bitmask; error_bits |= bitmask;
if (bitmask & EVSE_ERR_AUTO_CLEAR_BITS) { if (bitmask & EVSE_ERR_AUTO_CLEAR_BITS)
{
auto_clear_timeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000); // 60s auto_clear_timeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000); // 60s
} }
portEXIT_CRITICAL(&error_mux);
} }
void evse_error_clear(uint32_t bitmask) { void evse_error_clear(uint32_t bitmask)
bool had_error = error_bits != 0; {
portENTER_CRITICAL(&error_mux);
bool had_error = (error_bits != 0);
error_bits &= ~bitmask; error_bits &= ~bitmask;
if (had_error && error_bits == 0) { if (had_error && error_bits == 0)
{
error_cleared = true; error_cleared = true;
} }
portEXIT_CRITICAL(&error_mux);
} }
void evse_error_tick(void) { void evse_error_tick(void)
if ((error_bits & EVSE_ERR_AUTO_CLEAR_BITS) && xTaskGetTickCount() >= auto_clear_timeout) { {
evse_error_clear(EVSE_ERR_AUTO_CLEAR_BITS); portENTER_CRITICAL(&error_mux);
if ((error_bits & EVSE_ERR_AUTO_CLEAR_BITS) &&
auto_clear_timeout != 0 &&
xTaskGetTickCount() >= auto_clear_timeout)
{
error_bits &= ~EVSE_ERR_AUTO_CLEAR_BITS;
if (error_bits == 0)
{
error_cleared = true;
}
auto_clear_timeout = 0; auto_clear_timeout = 0;
} }
portEXIT_CRITICAL(&error_mux);
} }
bool evse_error_is_active(void) { bool evse_error_is_active(void)
return error_bits != 0; {
return evse_get_error() != 0;
} }
uint32_t evse_error_get_bits(void) { uint32_t evse_error_get_bits(void)
return error_bits; {
} return evse_get_error();
void evse_error_reset_flag(void) {
error_cleared = false;
}
bool evse_error_cleared_flag(void) {
return error_cleared;
} }

View File

@@ -1,3 +1,3 @@
#include "evse_events.h" #include "evse_events.h"
ESP_EVENT_DEFINE_BASE(EVSE_EVENT); ESP_EVENT_DEFINE_BASE(EVSE_EVENTS);

View File

@@ -1,8 +1,7 @@
// evse_fsm.c - Máquina de Estados EVSE com controle centralizado
#include "evse_fsm.h" #include "evse_fsm.h"
#include "evse_api.h" #include "evse_api.h"
#include "pilot.h" #include "evse_pilot.h"
#include "evse_config.h"
#include "esp_log.h" #include "esp_log.h"
#include "ac_relay.h" #include "ac_relay.h"
#include "board_config.h" #include "board_config.h"
@@ -10,6 +9,7 @@
#include "proximity.h" #include "proximity.h"
#include "rcm.h" #include "rcm.h"
#include "evse_state.h" #include "evse_state.h"
#include "evse_error.h"
static const char *TAG = "evse_fsm"; static const char *TAG = "evse_fsm";
@@ -20,155 +20,210 @@ static const char *TAG = "evse_fsm";
static bool c1_d1_waiting = false; static bool c1_d1_waiting = false;
static TickType_t c1_d1_relay_to = 0; static TickType_t c1_d1_relay_to = 0;
void evse_fsm_reset(void) { void evse_fsm_reset(void)
{
evse_set_state(EVSE_STATE_A); evse_set_state(EVSE_STATE_A);
c1_d1_waiting = false; c1_d1_waiting = false;
c1_d1_relay_to = 0; c1_d1_relay_to = 0;
} }
static void update_outputs(evse_state_t state, uint16_t charging_current, uint8_t cable_max_current, bool socket_outlet) { static void update_outputs(evse_state_t state)
switch (state) { {
case EVSE_STATE_A: const uint16_t current = evse_get_runtime_charging_current();
case EVSE_STATE_E: uint8_t cable_max_current = evse_get_max_charging_current();
case EVSE_STATE_F: const bool socket_outlet = evse_get_socket_outlet();
if (socket_outlet)
{
cable_max_current = proximity_get_max_current();
}
// Segurança: relé sempre off e outputs seguros em caso de erro
if (evse_get_error() != 0)
{
if (ac_relay_get_state())
{
ac_relay_set_state(false); ac_relay_set_state(false);
pilot_set_level(state == EVSE_STATE_A); ESP_LOGW(TAG, "ERRO ativo: relé estava ligado, agora desligado por segurança!");
if (board_config.socket_lock && socket_outlet) { }
socket_lock_set_locked(false); ac_relay_set_state(false);
} pilot_set_level(true);
//energy_meter_stop_session(); if (board_config.socket_lock && socket_outlet)
break; {
socket_lock_set_locked(false);
}
return;
}
case EVSE_STATE_B1: // Fluxo normal
pilot_set_level(true); switch (state)
ac_relay_set_state(false); {
if (board_config.socket_lock && socket_outlet) { case EVSE_STATE_A:
socket_lock_set_locked(true); case EVSE_STATE_E:
} case EVSE_STATE_F:
ac_relay_set_state(false);
pilot_set_level(state == EVSE_STATE_A);
if (board_config.socket_lock && socket_outlet)
{
socket_lock_set_locked(false);
}
break;
if (rcm_test()) { case EVSE_STATE_B1:
ESP_LOGI(TAG, "RCM self test passed"); pilot_set_level(true);
} else { ac_relay_set_state(false);
ESP_LOGW(TAG, "RCM self test failed"); if (board_config.socket_lock && socket_outlet)
} {
socket_lock_set_locked(true);
}
if (socket_outlet) { if (rcm_test())
cable_max_current = proximity_get_max_current(); {
} // ESP_LOGI(TAG, "RCM self test passed");
}
else
{
// ESP_LOGW(TAG, "RCM self test failed");
}
break;
//energy_meter_start_session(); case EVSE_STATE_B2:
break; pilot_set_amps(MIN(current, cable_max_current));
ac_relay_set_state(false);
break;
case EVSE_STATE_B2: case EVSE_STATE_C1:
pilot_set_amps(MIN(charging_current, cable_max_current * 10)); case EVSE_STATE_D1:
ac_relay_set_state(false); {
break; pilot_set_amps(MIN(current, cable_max_current));
ac_relay_set_state(false);
c1_d1_waiting = true;
c1_d1_relay_to = xTaskGetTickCount() + pdMS_TO_TICKS(6000);
break;
}
case EVSE_STATE_C1: case EVSE_STATE_C2:
case EVSE_STATE_D1: case EVSE_STATE_D2:
pilot_set_level(true); pilot_set_amps(MIN(current, cable_max_current));
c1_d1_waiting = true; ac_relay_set_state(true);
c1_d1_relay_to = xTaskGetTickCount() + pdMS_TO_TICKS(6000); break;
break;
case EVSE_STATE_C2:
case EVSE_STATE_D2:
pilot_set_amps(MIN(charging_current, cable_max_current * 10));
ac_relay_set_state(true);
break;
} }
} }
void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool available, bool enabled) { // FSM principal
void evse_fsm_process(
pilot_voltage_t pilot_voltage,
bool authorized,
bool available,
bool enabled)
{
// Proteção total: erro força F sempre!
if (evse_get_error() != 0)
{
if (evse_get_state() != EVSE_STATE_F)
{
ESP_LOGW(TAG, "Erro ativo detectado: forçando estado FAULT (F)");
evse_set_state(EVSE_STATE_F);
}
update_outputs(EVSE_STATE_F);
return;
}
TickType_t now = xTaskGetTickCount(); TickType_t now = xTaskGetTickCount();
evse_state_t previous_state = evse_get_state(); evse_state_t prev = evse_get_state();
evse_state_t current_state = previous_state; evse_state_t curr = prev;
switch (current_state) { switch (curr)
case EVSE_STATE_A: {
if (!available) { case EVSE_STATE_A:
evse_set_state(EVSE_STATE_F); if (!available)
} else if (pilot_voltage == PILOT_VOLTAGE_9) { {
evse_set_state(EVSE_STATE_B1); evse_set_state(EVSE_STATE_F);
} }
else if (pilot_voltage == PILOT_VOLTAGE_9)
{
evse_set_state(EVSE_STATE_B1);
}
break;
case EVSE_STATE_B1:
case EVSE_STATE_B2:
if (!available)
{
evse_set_state(EVSE_STATE_F);
break; break;
}
switch (pilot_voltage)
{
case PILOT_VOLTAGE_12:
evse_set_state(EVSE_STATE_A);
break;
case PILOT_VOLTAGE_9:
evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1);
break;
case PILOT_VOLTAGE_6:
evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1);
break;
default:
break;
}
break;
case EVSE_STATE_B1: case EVSE_STATE_C1:
case EVSE_STATE_B2: case EVSE_STATE_D1:
if (!available) { if (c1_d1_waiting && now >= c1_d1_relay_to)
{
ac_relay_set_state(false);
c1_d1_waiting = false;
if (!available)
{
evse_set_state(EVSE_STATE_F); evse_set_state(EVSE_STATE_F);
break; break;
} }
}
__attribute__((fallthrough));
switch (pilot_voltage) { case EVSE_STATE_C2:
case PILOT_VOLTAGE_12: case EVSE_STATE_D2:
evse_set_state(EVSE_STATE_A); if (!enabled || !available)
break; {
case PILOT_VOLTAGE_9: evse_set_state((curr == EVSE_STATE_D2 || curr == EVSE_STATE_D1)
evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1); ? EVSE_STATE_D1
break; : EVSE_STATE_C1);
case PILOT_VOLTAGE_6:
evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1);
break;
default:
break;
}
break; break;
}
case EVSE_STATE_C1: switch (pilot_voltage)
case EVSE_STATE_D1: {
if (c1_d1_waiting && now >= c1_d1_relay_to) { case PILOT_VOLTAGE_6:
ac_relay_set_state(false); evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1);
c1_d1_waiting = false;
if (!available) {
evse_set_state(EVSE_STATE_F);
break;
}
}
// fallthrough intencional
case EVSE_STATE_C2:
case EVSE_STATE_D2:
if (!enabled || !available) {
evse_set_state(
(current_state == EVSE_STATE_D2 || current_state == EVSE_STATE_D1)
? EVSE_STATE_D1
: EVSE_STATE_C1
);
break;
}
switch (pilot_voltage) {
case PILOT_VOLTAGE_6:
evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1);
break;
case PILOT_VOLTAGE_3:
evse_set_state((authorized && enabled) ? EVSE_STATE_D2 : EVSE_STATE_D1);
break;
case PILOT_VOLTAGE_9:
evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1);
break;
case PILOT_VOLTAGE_12:
evse_set_state(EVSE_STATE_A);
break;
default:
break;
}
break; break;
case PILOT_VOLTAGE_3:
case EVSE_STATE_E: evse_set_state((authorized && enabled) ? EVSE_STATE_D2 : EVSE_STATE_D1);
// Sem transições a partir de E
break; break;
case PILOT_VOLTAGE_9:
case EVSE_STATE_F: evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1);
if (available) {
evse_set_state(EVSE_STATE_A);
}
break; break;
case PILOT_VOLTAGE_12:
evse_set_state(EVSE_STATE_A);
break;
default:
break;
}
break;
case EVSE_STATE_E:
// Estado elétrico grave: só reset manual
break;
case EVSE_STATE_F:
// Fault: só sai se disponível e sem erro
if (available && evse_get_error() == 0)
{
evse_set_state(EVSE_STATE_A);
}
break;
} }
evse_state_t new_state = evse_get_state(); evse_state_t next = evse_get_state();
if (new_state != previous_state) { update_outputs(next);
ESP_LOGI(TAG, "State changed: %s -> %s", evse_state_to_str(previous_state), evse_state_to_str(new_state));
update_outputs(new_state, evse_get_charging_current(), evse_get_max_charging_current(), evse_get_socket_outlet());
}
} }

View File

@@ -1,5 +1,5 @@
#include "evse_hardware.h" #include "evse_hardware.h"
#include "pilot.h" #include "evse_pilot.h"
#include "ac_relay.h" #include "ac_relay.h"
#include "socket_lock.h" #include "socket_lock.h"
#include "proximity.h" #include "proximity.h"
@@ -7,6 +7,7 @@
static const char *TAG = "evse_hardware"; static const char *TAG = "evse_hardware";
void evse_hardware_init(void) { void evse_hardware_init(void) {
pilot_init();
pilot_set_level(true); // Sinal piloto em 12V (inicial) pilot_set_level(true); // Sinal piloto em 12V (inicial)
ac_relay_set_state(false); // Relé desligado ac_relay_set_state(false); // Relé desligado
//socket_lock_set_locked(false); // Destrava o conector //socket_lock_set_locked(false); // Destrava o conector

View File

@@ -1,97 +1,255 @@
#include <inttypes.h> // for PRIu32
#include "evse_state.h"
#include "evse_api.h"
#include "evse_limits.h" #include "evse_limits.h"
#include <stdint.h> #include "evse_meter.h"
#include <stdbool.h> #include "evse_session.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "nvs.h"
// ======================== // ========================
// Estado interno // Concurrency protection
// ======================== // ========================
static bool limit_reached = false; static portMUX_TYPE evse_mux = portMUX_INITIALIZER_UNLOCKED;
static uint32_t consumption_limit = 0;
static uint32_t charging_time_limit = 0;
static uint16_t under_power_limit = 0;
static uint32_t default_consumption_limit = 0;
static uint32_t default_charging_time_limit = 0;
static uint16_t default_under_power_limit = 0;
// ======================== // ========================
// Estado de controle // Runtime state (volatile)
// ======================== // ========================
void evse_set_limit_reached(uint8_t value) { static bool limit_reached = false;
limit_reached = (value != 0); static uint32_t consumption_limit = 0; // Energy limit in Wh
static uint32_t charging_time_limit = 0; // Time limit in seconds
static uint16_t under_power_limit = 0; // Minimum acceptable power in W
// ========================
// Limit status flag
// ========================
bool evse_get_limit_reached(void)
{
bool val;
portENTER_CRITICAL(&evse_mux);
val = limit_reached;
portEXIT_CRITICAL(&evse_mux);
return val;
} }
bool evse_is_limit_reached(void) { void evse_set_limit_reached(bool v)
return limit_reached; {
portENTER_CRITICAL(&evse_mux);
limit_reached = v;
portEXIT_CRITICAL(&evse_mux);
}
bool evse_is_limit_reached(void)
{
return evse_get_limit_reached();
} }
// ======================== // ========================
// Limites em tempo de execução // Runtime limit accessors
// ======================== // ========================
uint32_t evse_get_consumption_limit(void) { uint32_t evse_get_consumption_limit(void)
return consumption_limit; {
uint32_t val;
portENTER_CRITICAL(&evse_mux);
val = consumption_limit;
portEXIT_CRITICAL(&evse_mux);
return val;
} }
void evse_set_consumption_limit(uint32_t value) { void evse_set_consumption_limit(uint32_t value)
consumption_limit = value; {
} bool changed = false;
uint32_t evse_get_charging_time_limit(void) { portENTER_CRITICAL(&evse_mux);
return charging_time_limit; if (consumption_limit != value)
} {
consumption_limit = value;
changed = true;
}
portEXIT_CRITICAL(&evse_mux);
void evse_set_charging_time_limit(uint32_t value) { if (!changed)
charging_time_limit = value; return;
}
uint16_t evse_get_under_power_limit(void) { nvs_handle_t h;
return under_power_limit; esp_err_t err = nvs_open("evse", NVS_READWRITE, &h);
} if (err == ESP_OK)
{
err = nvs_set_u32(h, "def_cons_lim", value);
if (err == ESP_OK)
err = nvs_commit(h);
nvs_close(h);
void evse_set_under_power_limit(uint16_t value) { if (err != ESP_OK)
under_power_limit = value; {
} ESP_LOGE("EVSE_LIMITS",
"Failed to persist consumption limit (%" PRIu32 " Wh): %s",
// ======================== value, esp_err_to_name(err));
// Limites padrão (persistentes) }
// ======================== }
else
uint32_t evse_get_default_consumption_limit(void) { {
return default_consumption_limit; ESP_LOGE("EVSE_LIMITS",
} "Failed to open NVS for consumption limit: %s",
esp_err_to_name(err));
void evse_set_default_consumption_limit(uint32_t value) { }
default_consumption_limit = value; }
}
uint32_t evse_get_charging_time_limit(void)
uint32_t evse_get_default_charging_time_limit(void) { {
return default_charging_time_limit; uint32_t val;
} portENTER_CRITICAL(&evse_mux);
val = charging_time_limit;
void evse_set_default_charging_time_limit(uint32_t value) { portEXIT_CRITICAL(&evse_mux);
default_charging_time_limit = value; return val;
} }
uint16_t evse_get_default_under_power_limit(void) { void evse_set_charging_time_limit(uint32_t value)
return default_under_power_limit; {
} bool changed = false;
void evse_set_default_under_power_limit(uint16_t value) { portENTER_CRITICAL(&evse_mux);
default_under_power_limit = value; if (charging_time_limit != value)
} {
charging_time_limit = value;
// ======================== changed = true;
// Lógica de verificação de limites }
// ======================== portEXIT_CRITICAL(&evse_mux);
void evse_limits_check(evse_state_t state) { if (!changed)
// Se algum limite estiver ativo, verifique o estado return;
if ((consumption_limit > 0 || charging_time_limit > 0 || under_power_limit > 0)
&& evse_state_is_charging(state)) { nvs_handle_t h;
// (Lógica real a ser aplicada aqui, ex: medição de consumo, tempo ou potência) esp_err_t err = nvs_open("evse", NVS_READWRITE, &h);
evse_set_limit_reached(1); if (err == ESP_OK)
{
err = nvs_set_u32(h, "def_ch_time_lim", value);
if (err == ESP_OK)
err = nvs_commit(h);
nvs_close(h);
if (err != ESP_OK)
{
ESP_LOGE("EVSE_LIMITS",
"Failed to persist charging time limit (%" PRIu32 " s): %s",
value, esp_err_to_name(err));
}
}
else
{
ESP_LOGE("EVSE_LIMITS",
"Failed to open NVS for charging time limit: %s",
esp_err_to_name(err));
}
}
uint16_t evse_get_under_power_limit(void)
{
uint16_t val;
portENTER_CRITICAL(&evse_mux);
val = under_power_limit;
portEXIT_CRITICAL(&evse_mux);
return val;
}
void evse_set_under_power_limit(uint16_t value)
{
bool changed = false;
portENTER_CRITICAL(&evse_mux);
if (under_power_limit != value)
{
under_power_limit = value;
changed = true;
}
portEXIT_CRITICAL(&evse_mux);
if (!changed)
return;
nvs_handle_t h;
esp_err_t err = nvs_open("evse", NVS_READWRITE, &h);
if (err == ESP_OK)
{
err = nvs_set_u16(h, "def_un_pwr_lim", value);
if (err == ESP_OK)
err = nvs_commit(h);
nvs_close(h);
if (err != ESP_OK)
{
ESP_LOGE("EVSE_LIMITS",
"Failed to persist under-power limit (%" PRIu32 " W): %s",
(uint32_t)value, esp_err_to_name(err));
}
}
else
{
ESP_LOGE("EVSE_LIMITS",
"Failed to open NVS for under-power limit: %s",
esp_err_to_name(err));
}
}
// ========================
// Limit checking logic
// ========================
void evse_limits_check(void)
{
// Só faz sentido durante carregamento
if (!evse_state_is_charging(evse_get_state()))
{
return;
}
evse_session_t sess;
if (!evse_session_get(&sess) || !sess.is_current)
{
// Sem sessão ativa → nada a fazer
return;
}
bool reached = false;
// 1) Limite de energia (Wh)
if (consumption_limit > 0 && sess.energy_wh >= consumption_limit)
{
ESP_LOGW("EVSE_LIMITS",
"Energy limit reached: %" PRIu32 " Wh ≥ %" PRIu32 " Wh",
sess.energy_wh, consumption_limit);
reached = true;
}
// 2) Limite de tempo (s)
if (charging_time_limit > 0 && sess.duration_s >= charging_time_limit)
{
ESP_LOGW("EVSE_LIMITS",
"Charging time limit reached: %" PRIu32 " s ≥ %" PRIu32 " s",
sess.duration_s, charging_time_limit);
reached = true;
}
// 3) Under-power (potência instantânea)
uint32_t inst_power = evse_meter_get_instant_power();
if (under_power_limit > 0 && inst_power < under_power_limit)
{
ESP_LOGW("EVSE_LIMITS",
"Under-power limit reached: %" PRIu32 " W < %" PRIu32 " W",
(uint32_t)inst_power,
(uint32_t)under_power_limit);
reached = true;
}
if (reached)
{
evse_set_limit_reached(true);
} }
} }

View File

@@ -1,124 +1,416 @@
// === Início de: components/evse/evse_manager.c ===
#include "evse_manager.h" #include "evse_manager.h"
#include "evse_state.h" #include "evse_state.h"
#include "evse_error.h" #include "evse_error.h"
#include "evse_hardware.h" #include "evse_hardware.h"
#include "evse_config.h" #include "evse_config.h"
#include "evse_api.h" #include "evse_api.h"
#include "auth.h" #include "evse_meter.h"
#include "evse_session.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
#include "freertos/queue.h" #include "freertos/queue.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_event.h"
#include <string.h> #include <string.h>
#include <inttypes.h>
#include "auth_events.h"
#include "loadbalancer_events.h"
#include "ocpp_events.h"
#include "scheduler_events.h"
static const char *TAG = "EVSE_Manager"; static const char *TAG = "EVSE_Manager";
static TickType_t auth_expiration = 0;
static SemaphoreHandle_t evse_mutex; static SemaphoreHandle_t evse_mutex;
static QueueHandle_t auth_event_queue = NULL; static volatile bool auth_enabled = false;
#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo // Estado de pausa controlado pelo Load Balancer
static volatile bool lb_paused = false;
static volatile bool lb_prev_authorized = false;
// Estado de janela do scheduler
static volatile bool s_sched_allowed = true;
static portMUX_TYPE s_sched_mux = portMUX_INITIALIZER_UNLOCKED;
#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo
// ================= Helpers internos =================
static void lb_clear_pause_state(void)
{
lb_paused = false;
lb_prev_authorized = false;
}
// Exposto para outros módulos (se quiserem saber se o scheduler permite)
bool evse_sched_is_allowed(void)
{
bool v;
portENTER_CRITICAL(&s_sched_mux);
v = s_sched_allowed;
portEXIT_CRITICAL(&s_sched_mux);
return v;
}
static void evse_manager_handle_auth_on_tick(void)
{
bool sched_allowed = evse_sched_is_allowed();
if (auth_enabled)
{
// Se o carro foi desconectado, revoga autorização
if (evse_state_get_authorized() && evse_get_state() == EVSE_STATE_A)
{
ESP_LOGI(TAG, "Vehicle disconnected → revoking authorization.");
evse_state_set_authorized(false);
// Desconexão física invalida qualquer pausa pendente do LB
lb_clear_pause_state();
}
// Em modos RFID/OCPP, o scheduler pode também forçar paragem
if (!sched_allowed && evse_state_get_authorized())
{
ESP_LOGI(TAG, "[SCHED] window closed (auth mode) → revoking authorization.");
evse_state_set_authorized(false);
}
}
else
{
// Modo OPEN: só autoriza se LB e Scheduler permitirem
if (!lb_paused && sched_allowed && !evse_state_get_authorized())
{
evse_state_set_authorized(true);
ESP_LOGI(TAG, "Authentication disabled → forced authorization (within schedule).");
lb_clear_pause_state();
}
// Fora da janela, garantir que não fica autorizado
if (!sched_allowed && evse_state_get_authorized())
{
ESP_LOGI(TAG, "[SCHED] window closed (OPEN mode) → revoking authorization.");
evse_state_set_authorized(false);
}
}
}
// ===== Task de ciclo principal ===== // ===== Task de ciclo principal =====
static void evse_manager_task(void *arg) { static void evse_manager_task(void *arg)
while (true) { {
(void)arg;
while (true)
{
evse_manager_tick(); evse_manager_tick();
vTaskDelay(pdMS_TO_TICKS(EVSE_MANAGER_TICK_PERIOD_MS)); vTaskDelay(pdMS_TO_TICKS(EVSE_MANAGER_TICK_PERIOD_MS));
} }
} }
static void evse_auth_event_task(void *arg) { // ===== Tratador de eventos de AUTH =====
auth_event_t evt; static void on_auth_event(void *arg, esp_event_base_t base, int32_t id, void *data)
{
(void)arg;
while (true) { if (base != AUTH_EVENTS || !data)
if (xQueueReceive(auth_event_queue, &evt, portMAX_DELAY)) { return;
ESP_LOGI(TAG, "Evento de autenticação recebido: %s (%s)",
evt.tag, evt.authorized ? "AUTORIZADO" : "NEGADO");
if (evt.authorized) { auth_mode_t g_mode = AUTH_MODE_OPEN;
evse_authorize();
auth_expiration = xTaskGetTickCount() + pdMS_TO_TICKS(2 * 60 * 1000); // 2 minutos switch (id)
} else { {
evse_manager_set_authorized(false); case AUTH_EVENT_TAG_PROCESSED:
ESP_LOGW(TAG, "Tag inválida, carregamento negado."); {
const auth_tag_event_data_t *evt = (const auth_tag_event_data_t *)data;
ESP_LOGI(TAG, "Tag %s -> %s", evt->tag, evt->authorized ? "AUTHORIZED" : "DENIED");
evse_state_set_authorized(evt->authorized);
// Qualquer alteração explícita de auth invalida pausa do LB
lb_clear_pause_state();
break;
}
case AUTH_EVENT_MODE_CHANGED:
case AUTH_EVENT_INIT:
{
const auth_mode_event_data_t *evt = (const auth_mode_event_data_t *)data;
g_mode = evt->mode;
ESP_LOGI(TAG, "Auth mode = %s", auth_mode_to_str(g_mode));
if (g_mode == AUTH_MODE_OPEN)
{
// Em OPEN, a autorização passa a ser gerida por evse_manager_handle_auth_on_tick(),
// que também respeita o scheduler.
evse_state_set_authorized(false); // vai ser forçado no próximo tick se permitido
auth_enabled = false;
}
else
{
evse_state_set_authorized(false);
auth_enabled = true;
}
// Modo mudou -> qualquer pausa antiga deixa de fazer sentido
lb_clear_pause_state();
break;
}
}
}
// ===== Tratador de eventos de Load Balancer =====
static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
(void)handler_arg;
(void)event_base;
if (!event_data)
return;
if (event_id == LOADBALANCER_EVENT_INIT || event_id == LOADBALANCER_EVENT_STATE_CHANGED)
{
const loadbalancer_state_event_t *evt = (const loadbalancer_state_event_t *)event_data;
ESP_LOGI(TAG, "Loadbalancer %s (ts: %lld)",
evt->enabled ? "ENABLED" : "DISABLED",
(long long)evt->timestamp_us);
}
else if (event_id == LOADBALANCER_EVENT_MASTER_CURRENT_LIMIT)
{
const loadbalancer_master_limit_event_t *evt =
(const loadbalancer_master_limit_event_t *)event_data;
ESP_LOGI(TAG,
"Novo limite de corrente (master): %u A (ts: %lld)",
evt->max_current, (long long)evt->timestamp_us);
if (evt->max_current == 0)
{
// Suspensão por LB (não interessa se é OPEN ou RFID/OCPP)
lb_paused = true;
lb_prev_authorized = evse_state_get_authorized();
if (lb_prev_authorized)
{
ESP_LOGI(TAG, "[LB] limit=0A → pausando sessão (authorized=false)");
evse_state_set_authorized(false);
}
else
{
ESP_LOGD(TAG, "[LB] limit=0A → já não estava autorizado");
}
}
else
{
// Ajusta corrente em runtime
evse_set_runtime_charging_current(evt->max_current);
if (lb_paused)
{
lb_paused = false;
// Só retomamos se EVSE estiver operacional e scheduler permitir
bool can_resume =
(evse_get_error() == 0) &&
evse_config_is_available() &&
evse_config_is_enabled() &&
evse_sched_is_allowed();
if (!can_resume)
{
ESP_LOGW(TAG,
"[LB] limit=%uA → não retoma automaticamente (erro/indisponível/desabilitado/fora de horário)",
evt->max_current);
lb_clear_pause_state();
return;
}
if (!auth_enabled)
{
// Modo OPEN: retoma sempre (se dentro da janela do scheduler)
ESP_LOGI(TAG,
"[LB] limit=%uA → modo OPEN, reautorizando (authorized=true)",
evt->max_current);
evse_state_set_authorized(true);
}
else
{
// RFID/OCPP: só retoma se havia autorização antes da pausa
if (lb_prev_authorized)
{
ESP_LOGI(TAG,
"[LB] limit=%uA → RFID/OCPP, retomando autorização anterior (auto-resume)",
evt->max_current);
evse_state_set_authorized(true);
}
else
{
ESP_LOGI(TAG,
"[LB] limit=%uA → RFID/OCPP, sem autorização prévia, mantendo estado atual",
evt->max_current);
}
}
// Limpa estado prévio (não reaplicar em pausas futuras)
lb_prev_authorized = false;
}
else
{
// Caso normal: apenas ajuste de corrente, sem mexer em auth
ESP_LOGD(TAG,
"[LB] limit=%uA → ajustando corrente runtime (sem mudança de autorização)",
evt->max_current);
} }
} }
} }
} }
// ===== Tratador de eventos de OCPP =====
static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *data)
{
(void)arg;
// ===== Inicialização dos módulos do EVSE ===== if (base != OCPP_EVENTS)
void evse_manager_init(void) { return;
switch (id)
{
case OCPP_EVENT_AUTHORIZED:
ESP_LOGI(TAG, "[OCPP] Authorized");
evse_state_set_authorized(true);
lb_clear_pause_state();
break;
case OCPP_EVENT_AUTH_REJECTED:
ESP_LOGW(TAG, "[OCPP] Authorization rejected");
evse_state_set_authorized(false);
lb_clear_pause_state();
break;
case OCPP_EVENT_AUTH_TIMEOUT:
ESP_LOGW(TAG, "[OCPP] Authorization timeout");
evse_state_set_authorized(false);
lb_clear_pause_state();
break;
case OCPP_EVENT_REMOTE_START:
ESP_LOGI(TAG, "[OCPP] RemoteStart");
evse_state_set_authorized(true);
lb_clear_pause_state();
break;
case OCPP_EVENT_REMOTE_STOP:
ESP_LOGI(TAG, "[OCPP] RemoteStop");
evse_state_set_authorized(false);
lb_clear_pause_state();
break;
case OCPP_EVENT_START_TX:
ESP_LOGI(TAG, "[OCPP] StartTx");
lb_clear_pause_state();
break;
case OCPP_EVENT_STOP_TX:
ESP_LOGI(TAG, "[OCPP] StopTx");
evse_state_set_authorized(false);
lb_clear_pause_state();
break;
// ChangeAvailability remoto (operative/inoperative)
case OCPP_EVENT_OPERATIVE_UPDATED:
{
if (!data)
{
ESP_LOGW(TAG, "[OCPP] OperativeUpdated sem payload — ignorado");
break;
}
const ocpp_operative_event_t *ev = (const ocpp_operative_event_t *)data;
ESP_LOGI(TAG, "[OCPP] OperativeUpdated: operative=%d ts=%lld",
(int)ev->operative, (long long)ev->timestamp_us);
// Mapear operative → enabled local (persiste e emite EVSE_EVENT_ENABLE_UPDATED)
evse_config_set_enabled(ev->operative);
break;
}
default:
ESP_LOGD(TAG, "[OCPP] Unhandled event id=%" PRId32, id);
break;
}
}
// ===== Tratador de eventos de Scheduler =====
static void on_sched_event(void *arg,
esp_event_base_t base,
int32_t id,
void *data)
{
(void)arg;
if (base != SCHED_EVENTS || data == NULL)
return;
const sched_event_state_t *ev = (const sched_event_state_t *)data;
portENTER_CRITICAL(&s_sched_mux);
s_sched_allowed = ev->allowed_now;
portEXIT_CRITICAL(&s_sched_mux);
ESP_LOGI(TAG,
"[SCHED] event id=%" PRIi32 " allowed_now=%d",
id, (int)ev->allowed_now);
// Se a janela fechou, parar sessão (revogar autorização)
if (!ev->allowed_now && evse_state_get_authorized())
{
ESP_LOGI(TAG, "[SCHED] window closed → stopping session (authorized=false)");
evse_state_set_authorized(false);
}
// Se a janela abriu de novo, não auto-reautorizamos aqui.
// Deixamos que o utilizador / OCPP decida iniciar nova sessão.
// (Em modo OPEN, o tick trata disso respeitando o scheduler.)
}
// ===== Inicialização =====
void evse_manager_init(void)
{
evse_mutex = xSemaphoreCreateMutex(); evse_mutex = xSemaphoreCreateMutex();
configASSERT(evse_mutex != NULL);
evse_config_init(); evse_config_init();
evse_error_init(); evse_error_init();
evse_hardware_init(); evse_hardware_init();
evse_state_init(); evse_state_init();
evse_meter_init();
evse_session_init();
ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS, ESP_EVENT_ANY_ID, &on_auth_event, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(LOADBALANCER_EVENTS, ESP_EVENT_ANY_ID, &on_loadbalancer_event, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(OCPP_EVENTS, ESP_EVENT_ANY_ID, &on_ocpp_event, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(SCHED_EVENTS, ESP_EVENT_ANY_ID, &on_sched_event, NULL));
ESP_LOGI(TAG, "EVSE Manager inicializado."); ESP_LOGI(TAG, "EVSE Manager inicializado.");
xTaskCreate(evse_manager_task, "evse_manager_task", 4096, NULL, 5, NULL); BaseType_t rc = xTaskCreate(evse_manager_task, "evse_manager_task", 4096, NULL, 5, NULL);
configASSERT(rc == pdPASS);
} }
// ===== Inicia processamento de eventos de autenticação ===== // ===== Main Tick =====
void evse_manager_start(QueueHandle_t queue) { void evse_manager_tick(void)
auth_event_queue = queue; {
xTaskCreate(evse_auth_event_task, "evse_auth_evt", 4096, NULL, 5, NULL);
}
void evse_manager_tick(void) {
xSemaphoreTake(evse_mutex, portMAX_DELAY); xSemaphoreTake(evse_mutex, portMAX_DELAY);
evse_hardware_tick(); evse_hardware_tick();
evse_error_tick(); evse_error_tick();
evse_state_tick(); evse_state_tick();
evse_temperature_check(); evse_temperature_check();
evse_session_tick();
// Verifica expiração de autorização somente se auth está habilitado evse_manager_handle_auth_on_tick();
if (auth_is_enabled()) {
if (evse_state_get_authorized() && auth_expiration > 0 &&
xTaskGetTickCount() >= auth_expiration) {
ESP_LOGI(TAG, "Autorização expirada após 2 minutos.");
evse_state_set_authorized(false);
auth_expiration = 0;
}
} else {
// Se autenticação não é necessária, sempre considera autorizado
if (!evse_state_get_authorized()) {
evse_state_set_authorized(true);
ESP_LOGI(TAG, "Autenticação desativada: autorização forçada.");
}
}
xSemaphoreGive(evse_mutex); xSemaphoreGive(evse_mutex);
} }
// ===== Controles e status ===== // === Fim de: components/evse/evse_manager.c ===
bool evse_manager_is_available(void) {
return evse_config_is_available();
}
void evse_manager_set_available(bool available) {
evse_config_set_available(available);
}
void evse_manager_set_authorized(bool authorized) {
evse_state_set_authorized(authorized);
}
bool evse_manager_is_charging(void) {
return evse_state_is_charging(evse_get_state());
}
void evse_manager_set_enabled(bool enabled) {
evse_config_set_enabled(enabled);
}
bool evse_manager_is_enabled(void) {
return evse_config_is_enabled();
}

View File

@@ -0,0 +1,121 @@
#include "evse_meter.h"
#include "meter_events.h"
#include "esp_event.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include <string.h>
#include <inttypes.h>
static const char *TAG = "evse_meter";
static SemaphoreHandle_t meter_mutex;
typedef struct
{
uint32_t power_watts[EVSE_METER_PHASE_COUNT];
float voltage[EVSE_METER_PHASE_COUNT];
float current[EVSE_METER_PHASE_COUNT];
uint32_t energy_wh;
} evse_meter_data_t;
static evse_meter_data_t meter_data;
static void on_meter_event_dispatcher(void *arg, esp_event_base_t base, int32_t id, void *data)
{
if (base == METER_EVENT && id == METER_EVENT_DATA_READY && data)
{
const meter_event_data_t *evt = (const meter_event_data_t *)data;
if (strcmp(evt->source, "EVSE") == 0)
{
evse_meter_on_meter_event(arg, data);
}
}
}
void evse_meter_on_meter_event(void *arg, void *event_data)
{
const meter_event_data_t *evt = (const meter_event_data_t *)event_data;
if (!evt)
return;
xSemaphoreTake(meter_mutex, portMAX_DELAY);
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
{
meter_data.power_watts[i] = evt->watt[i];
meter_data.voltage[i] = evt->vrms[i];
meter_data.current[i] = evt->irms[i];
}
meter_data.energy_wh = (uint32_t)(evt->total_energy);
xSemaphoreGive(meter_mutex);
ESP_LOGD(TAG,
"Meter updated: power[W]={%" PRIu32 ",%" PRIu32 ",%" PRIu32 "}, "
"voltage[V]={%.2f,%.2f,%.2f}, "
"current[A]={%.2f,%.2f,%.2f}, "
"total_energy=%" PRIu32 "Wh",
meter_data.power_watts[0], meter_data.power_watts[1], meter_data.power_watts[2],
meter_data.voltage[0], meter_data.voltage[1], meter_data.voltage[2],
meter_data.current[0], meter_data.current[1], meter_data.current[2],
meter_data.energy_wh);
}
void evse_meter_init(void)
{
meter_mutex = xSemaphoreCreateMutex();
ESP_ERROR_CHECK(meter_mutex ? ESP_OK : ESP_FAIL);
ESP_ERROR_CHECK(esp_event_handler_register(
METER_EVENT, METER_EVENT_DATA_READY,
on_meter_event_dispatcher, NULL));
memset(&meter_data, 0, sizeof(meter_data));
ESP_LOGI(TAG, "EVSE Meter listener registered.");
}
int evse_meter_get_instant_power(void)
{
xSemaphoreTake(meter_mutex, portMAX_DELAY);
int sum = 0;
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
{
sum += meter_data.power_watts[i];
}
xSemaphoreGive(meter_mutex);
return sum;
}
int evse_meter_get_total_energy(void)
{
xSemaphoreTake(meter_mutex, portMAX_DELAY);
int val = meter_data.energy_wh;
xSemaphoreGive(meter_mutex);
return val;
}
void evse_meter_get_power(int power[EVSE_METER_PHASE_COUNT])
{
xSemaphoreTake(meter_mutex, portMAX_DELAY);
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
{
power[i] = meter_data.power_watts[i];
}
xSemaphoreGive(meter_mutex);
}
void evse_meter_get_voltage(float voltage[EVSE_METER_PHASE_COUNT])
{
xSemaphoreTake(meter_mutex, portMAX_DELAY);
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
{
voltage[i] = meter_data.voltage[i];
}
xSemaphoreGive(meter_mutex);
}
void evse_meter_get_current(float current[EVSE_METER_PHASE_COUNT])
{
xSemaphoreTake(meter_mutex, portMAX_DELAY);
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
{
current[i] = meter_data.current[i];
}
xSemaphoreGive(meter_mutex);
}

View File

@@ -9,8 +9,8 @@
#include "esp_log.h" #include "esp_log.h"
#include "esp_rom_sys.h" #include "esp_rom_sys.h"
#include "pilot.h" #include "evse_pilot.h"
#include "adc.h" #include "adc121s021_dma.h"
#include "board_config.h" #include "board_config.h"
#define PILOT_PWM_TIMER LEDC_TIMER_0 #define PILOT_PWM_TIMER LEDC_TIMER_0
@@ -21,13 +21,18 @@
#define NUM_PILOT_SAMPLES 100 #define NUM_PILOT_SAMPLES 100
#define MAX_SAMPLE_ATTEMPTS 1000 #define MAX_SAMPLE_ATTEMPTS 1000
#define PILOT_EXTREME_PERCENT 10 // 15% superior e inferior #define PILOT_EXTREME_PERCENT 10 // 10% superior e inferior
static const char *TAG = "pilot"; #define ADC121_VREF_MV 3300
static pilot_voltage_cache_t last_voltage = {0, 0}; #define ADC121_MAX 4095
static inline uint16_t adc_to_mv(uint16_t x) { static const char *TAG = "evse_pilot";
return (uint16_t)(((uint32_t)(x) * 3300U) / 4095U);
static int last_pilot_level = -1;
static uint32_t last_pwm_duty = 0;
static int adc_raw_to_mv(uint16_t raw) {
return (raw * ADC121_VREF_MV) / ADC121_MAX;
} }
void pilot_init(void) void pilot_init(void)
@@ -53,83 +58,67 @@ void pilot_init(void)
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
ESP_ERROR_CHECK(ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, 0)); ESP_ERROR_CHECK(ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, 0));
ESP_ERROR_CHECK(ledc_fade_func_install(0)); adc121s021_dma_init();
adc_oneshot_chan_cfg_t config = {
.bitwidth = ADC_BITWIDTH_DEFAULT,
.atten = ADC_ATTEN_DB_12,
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.pilot_adc_channel, &config));
} }
void pilot_set_level(bool level) void pilot_set_level(bool level)
{ {
if (last_pilot_level == level) return;
last_pilot_level = level;
ESP_LOGI(TAG, "Set level %d", level); ESP_LOGI(TAG, "Set level %d", level);
ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, level ? 1 : 0); ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, level ? 1 : 0);
last_pwm_duty = 0;
} }
void pilot_set_amps(uint16_t amps) void pilot_set_amps(uint16_t amps)
{ {
ESP_LOGI(TAG, "Set amps %d", amps); if (amps < 6 || amps > 80) {
ESP_LOGE(TAG, "Invalid ampere value: %d A (valid: 680 A)", amps);
if (amps < 60 || amps > 800) {
ESP_LOGE(TAG, "Invalid ampere value: %d A*10", amps);
return; return;
} }
uint32_t duty; uint32_t duty_percent;
if (amps <= 510) { if (amps <= 51) {
duty = (PILOT_PWM_MAX_DUTY * amps) / 600; duty_percent = (amps * 10) / 6;
} else { } else {
duty = ((PILOT_PWM_MAX_DUTY * amps) / 2500) + (64 * (PILOT_PWM_MAX_DUTY / 100)); duty_percent = (amps * 10) / 25 + 64;
} }
if (duty_percent > 100) duty_percent = 100;
if (duty > PILOT_PWM_MAX_DUTY) uint32_t duty = (PILOT_PWM_MAX_DUTY * duty_percent) / 100;
duty = PILOT_PWM_MAX_DUTY;
if (last_pilot_level == 0 && last_pwm_duty == duty) return;
last_pilot_level = 0;
last_pwm_duty = duty;
ESP_LOGI(TAG, "Pilot set: %d A → %d/%d (≈ %d%% duty)",
amps, (int)duty, PILOT_PWM_MAX_DUTY, (int)duty_percent);
ESP_LOGI(TAG, "Set amp %dA*10 -> duty %lu/%d", amps, duty, PILOT_PWM_MAX_DUTY);
ledc_set_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, duty); ledc_set_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, duty);
ledc_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL); ledc_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL);
} }
static int compare_u16(const void *a, const void *b) { bool pilot_get_state(void) {
return (*(uint16_t *)a - *(uint16_t *)b); return (last_pilot_level == 1) && (last_pwm_duty == 0);
} }
static uint16_t select_low_median_qsort(uint16_t *src, int n, int percent) { static int compare_int(const void *a, const void *b) {
int k = (n * percent) / 100; return (*(const int *)a - *(const int *)b);
if (k == 0) k = 1;
uint16_t *copy = alloca(n * sizeof(uint16_t));
memcpy(copy, src, n * sizeof(uint16_t));
qsort(copy, n, sizeof(uint16_t), compare_u16);
return copy[k / 2];
}
static uint16_t select_high_median_qsort(uint16_t *src, int n, int percent) {
int k = (n * percent) / 100;
if (k == 0) k = 1;
uint16_t *copy = alloca(n * sizeof(uint16_t));
memcpy(copy, src, n * sizeof(uint16_t));
qsort(copy, n, sizeof(uint16_t), compare_u16);
return copy[n - k + (k / 2)];
} }
void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12) void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12)
{ {
ESP_LOGD(TAG, "pilot_measure"); ESP_LOGD(TAG, "pilot_measure");
uint16_t samples[NUM_PILOT_SAMPLES]; int samples[NUM_PILOT_SAMPLES];
int collected = 0, attempts = 0; int collected = 0, attempts = 0;
uint16_t sample; uint16_t adc_sample = 0;
while (collected < NUM_PILOT_SAMPLES && attempts < MAX_SAMPLE_ATTEMPTS) { while (collected < NUM_PILOT_SAMPLES && attempts < MAX_SAMPLE_ATTEMPTS) {
if (adc_oneshot_read(adc_handle, board_config.pilot_adc_channel, &sample) == ESP_OK) { adc_sample = 0;
samples[collected++] = sample; if (adc121s021_dma_get_sample(&adc_sample)) {
samples[collected++] = adc_sample;
esp_rom_delay_us(10); esp_rom_delay_us(10);
} else { } else {
esp_rom_delay_us(100); esp_rom_delay_us(100);
@@ -144,19 +133,20 @@ void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12)
return; return;
} }
uint16_t high_raw = select_high_median_qsort(samples, collected, PILOT_EXTREME_PERCENT); qsort(samples, collected, sizeof(int), compare_int);
uint16_t low_raw = select_low_median_qsort(samples, collected, PILOT_EXTREME_PERCENT);
int high_mv = 0; int k = (collected * PILOT_EXTREME_PERCENT) / 100;
int low_mv = 0; if (k == 0) k = 1;
if (adc_cali_raw_to_voltage(adc_cali_handle, high_raw, &high_mv) != ESP_OK || int low_index = k / 2;
adc_cali_raw_to_voltage(adc_cali_handle, low_raw, &low_mv) != ESP_OK) { int high_index = collected - k + (k / 2);
ESP_LOGW(TAG, "ADC calibration failed"); if (high_index >= collected) high_index = collected - 1;
*up_voltage = PILOT_VOLTAGE_1;
*down_voltage_n12 = false; int low_raw = samples[low_index];
return; int high_raw = samples[high_index];
}
int high_mv = adc_raw_to_mv(high_raw);
int low_mv = adc_raw_to_mv(low_raw);
if (high_mv >= board_config.pilot_down_threshold_12) if (high_mv >= board_config.pilot_down_threshold_12)
*up_voltage = PILOT_VOLTAGE_12; *up_voltage = PILOT_VOLTAGE_12;
@@ -173,14 +163,3 @@ void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12)
ESP_LOGD(TAG, "Final: up_voltage=%d, down_voltage_n12=%d", *up_voltage, *down_voltage_n12); ESP_LOGD(TAG, "Final: up_voltage=%d, down_voltage_n12=%d", *up_voltage, *down_voltage_n12);
} }
bool pilot_get_state(void)
{
pilot_voltage_t voltage;
bool is_n12v;
pilot_measure(&voltage, &is_n12v);
// Considera que "estado alto" significa pelo menos 12V (standby ou pronto)
return voltage == PILOT_VOLTAGE_12;
}

View File

@@ -0,0 +1,166 @@
#include <inttypes.h>
#include "evse_session.h"
#include "evse_meter.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "evse_events.h"
#include "esp_event.h"
#include "evse_limits.h"
static const char *TAG = "evse_session";
static TickType_t session_start_tick = 0;
static uint32_t watt_seconds = 0;
static evse_session_t last_session;
static bool last_session_valid = false;
static uint32_t session_counter = 0;
static portMUX_TYPE session_mux = portMUX_INITIALIZER_UNLOCKED;
void evse_session_init(void)
{
portENTER_CRITICAL(&session_mux);
session_start_tick = 0;
watt_seconds = 0;
last_session_valid = false;
session_counter = 0;
portEXIT_CRITICAL(&session_mux);
}
void evse_session_start(void)
{
TickType_t tick = xTaskGetTickCount();
portENTER_CRITICAL(&session_mux);
session_start_tick = tick;
watt_seconds = 0;
session_counter++;
portEXIT_CRITICAL(&session_mux);
evse_set_limit_reached(false);
ESP_LOGI(TAG, "Session started at tick %u", (unsigned)tick);
evse_session_event_data_t evt = {
.type = EVSE_SESSION_EVENT_STARTED,
.session_id = session_counter,
.duration_s = 0,
.energy_wh = 0,
.avg_power_w = 0,
.is_current = true,
};
esp_event_post(EVSE_EVENTS,
EVSE_EVENT_SESSION,
&evt,
sizeof(evt),
portMAX_DELAY);
}
void evse_session_end(void)
{
TickType_t start_tick;
uint32_t ws;
uint32_t id;
portENTER_CRITICAL(&session_mux);
if (session_start_tick == 0) {
portEXIT_CRITICAL(&session_mux);
ESP_LOGW(TAG, "evse_session_end called without active session");
return;
}
start_tick = session_start_tick;
ws = watt_seconds;
id = session_counter;
session_start_tick = 0;
portEXIT_CRITICAL(&session_mux);
TickType_t now = xTaskGetTickCount();
uint32_t duration_s = (now - start_tick) / configTICK_RATE_HZ;
uint32_t energy_wh = ws / 3600U;
uint32_t avg_power = duration_s > 0 ? ws / duration_s : 0;
portENTER_CRITICAL(&session_mux);
last_session.start_tick = start_tick;
last_session.duration_s = duration_s;
last_session.energy_wh = energy_wh;
last_session.avg_power_w = avg_power;
last_session.is_current = false;
last_session_valid = true;
portEXIT_CRITICAL(&session_mux);
ESP_LOGI(TAG,
"Session ended: duration=%" PRIu32 " s, energy=%" PRIu32
" Wh, avg_power=%" PRIu32 " W",
duration_s, energy_wh, avg_power);
evse_session_event_data_t evt = {
.type = EVSE_SESSION_EVENT_FINISHED,
.session_id = id,
.duration_s = duration_s,
.energy_wh = energy_wh,
.avg_power_w = avg_power,
.is_current = false,
};
esp_event_post(EVSE_EVENTS,
EVSE_EVENT_SESSION,
&evt,
sizeof(evt),
portMAX_DELAY);
}
void evse_session_tick(void)
{
uint32_t power_w = evse_meter_get_instant_power();
portENTER_CRITICAL(&session_mux);
if (session_start_tick != 0) {
watt_seconds += power_w;
}
portEXIT_CRITICAL(&session_mux);
}
bool evse_session_get(evse_session_t *out)
{
if (out == NULL)
return false;
TickType_t start;
uint32_t ws;
bool has_current;
evse_session_t last_copy;
bool last_valid;
portENTER_CRITICAL(&session_mux);
start = session_start_tick;
ws = watt_seconds;
has_current = (session_start_tick != 0);
last_copy = last_session;
last_valid = last_session_valid;
portEXIT_CRITICAL(&session_mux);
if (has_current)
{
TickType_t now = xTaskGetTickCount();
uint32_t duration_s = (now - start) / configTICK_RATE_HZ;
uint32_t energy_wh = ws / 3600U;
uint32_t avg_power = duration_s > 0 ? ws / duration_s : 0;
out->start_tick = start;
out->duration_s = duration_s;
out->energy_wh = energy_wh;
out->avg_power_w = avg_power;
out->is_current = true;
return true;
}
if (last_valid)
{
*out = last_copy;
return true;
}
return false;
}

View File

@@ -1,19 +1,93 @@
#include "evse_api.h"
#include "evse_state.h" #include "evse_state.h"
#include "evse_session.h"
#include "evse_events.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h" #include "freertos/portmacro.h"
#include "esp_log.h"
// =========================
// Internal State Variables
// =========================
static evse_state_t current_state = EVSE_STATE_A; static evse_state_t current_state = EVSE_STATE_A;
static bool is_authorized = false; static bool is_authorized = false;
// Proteção básica para variáveis globais em sistemas concorrentes
static portMUX_TYPE state_mux = portMUX_INITIALIZER_UNLOCKED; static portMUX_TYPE state_mux = portMUX_INITIALIZER_UNLOCKED;
void evse_set_state(evse_state_t state) { static const char *TAG = "evse_state";
portENTER_CRITICAL(&state_mux);
current_state = state; // =========================
portEXIT_CRITICAL(&state_mux); // Internal Mapping
// =========================
static evse_state_event_t map_state_to_event(evse_state_t s) {
switch (s) {
case EVSE_STATE_A: return EVSE_STATE_EVENT_IDLE;
case EVSE_STATE_B1:
case EVSE_STATE_B2: return EVSE_STATE_EVENT_WAITING;
case EVSE_STATE_C1:
case EVSE_STATE_C2: return EVSE_STATE_EVENT_CHARGING;
case EVSE_STATE_E:
case EVSE_STATE_F: return EVSE_STATE_EVENT_FAULT;
default: return EVSE_STATE_EVENT_IDLE;
}
} }
// =========================
// Public API
// =========================
void evse_set_state(evse_state_t new_state) {
bool changed = false;
evse_state_t prev_state;
bool start_session = false;
bool end_session = false;
// 1) Detecta transição de estado dentro da região crítica
portENTER_CRITICAL(&state_mux);
prev_state = current_state;
if (new_state != current_state) {
// se entrou em charging pela primeira vez
if (evse_state_is_charging(new_state) && !evse_state_is_charging(prev_state)) {
start_session = true;
}
// se saiu de charging para qualquer outro
else if (!evse_state_is_charging(new_state) && evse_state_is_charging(prev_state)) {
end_session = true;
}
current_state = new_state;
changed = true;
}
portEXIT_CRITICAL(&state_mux);
// 2) Executa start/end de sessão FORA da região crítica, evitando logs/alloc dentro dela
if (start_session) {
evse_session_start();
}
if (end_session) {
evse_session_end();
}
// 3) Se mudou o estado, faz log e dispara evento
if (changed) {
const char *prev_str = evse_state_to_str(prev_state);
const char *curr_str = evse_state_to_str(new_state);
ESP_LOGI(TAG, "State changed: %s → %s", prev_str, curr_str);
evse_state_event_data_t evt = {
.state = map_state_to_event(new_state)
};
esp_event_post(EVSE_EVENTS,
EVSE_EVENT_STATE_CHANGED,
&evt,
sizeof(evt),
portMAX_DELAY);
}
}
evse_state_t evse_get_state(void) { evse_state_t evse_get_state(void) {
portENTER_CRITICAL(&state_mux); portENTER_CRITICAL(&state_mux);
evse_state_t s = current_state; evse_state_t s = current_state;
@@ -39,12 +113,19 @@ const char* evse_state_to_str(evse_state_t state) {
void evse_state_init(void) { void evse_state_init(void) {
portENTER_CRITICAL(&state_mux); portENTER_CRITICAL(&state_mux);
current_state = EVSE_STATE_A; current_state = EVSE_STATE_A;
is_authorized = false; is_authorized = true;
portEXIT_CRITICAL(&state_mux); portEXIT_CRITICAL(&state_mux);
ESP_LOGI("EVSE_STATE", "Initialized in state: %s", evse_state_to_str(current_state));
evse_state_event_data_t evt = {
.state = map_state_to_event(current_state)
};
esp_event_post(EVSE_EVENTS, EVSE_EVENT_INIT, &evt, sizeof(evt), portMAX_DELAY);
} }
void evse_state_tick(void) { void evse_state_tick(void) {
// Tick do estado (placeholder) // Placeholder for future state logic
} }
bool evse_state_is_charging(evse_state_t state) { bool evse_state_is_charging(evse_state_t state) {

View File

@@ -3,68 +3,104 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include "esp_err.h" #include "evse_state.h"
#include "evse_state.h" // Define evse_state_t #include "freertos/FreeRTOS.h"
#include "evse_session.h"
// Inicialização
void evse_init(void);
void evse_process(void);
// Estado #ifdef __cplusplus
extern "C" {
#endif
// ===============================
// Core EVSE State
// ===============================
/**
* @brief Get current EVSE state (e.g., A, B1, C2).
*/
evse_state_t evse_get_state(void); evse_state_t evse_get_state(void);
const char* evse_state_to_str(evse_state_t state);
bool evse_is_connector_plugged(evse_state_t state); /**
* @brief Set the EVSE state (e.g., called by FSM or hardware layer).
*/
void evse_set_state(evse_state_t state);
// ===============================
// Charging Session Info
// ===============================
/**
* @brief Retrieve statistics of either the current ongoing session (if any)
* or the last completed session.
* @param out pointer to evse_session_t to be filled
* @return true if there is a current or last session available
*/
bool evse_get_session(evse_session_t *out);
// ===============================
// Charging Session Info
// ===============================
/**
* @brief Returns true if the EV is charging (C1 or C2).
*/
bool evse_state_is_charging(evse_state_t state);
/**
* @brief Returns true if the EV is connected (plugged).
*/
bool evse_state_is_plugged(evse_state_t state);
/**
* @brief Returns true if a charging session is active (B2, C1, C2).
*/
bool evse_state_is_session(evse_state_t state);
// ===============================
// Authorization
// ===============================
/**
* @brief Set whether the vehicle is authorized to charge.
*/
void evse_state_set_authorized(bool authorized);
/**
* @brief Get current authorization status.
*/
bool evse_state_get_authorized(void);
// ===============================
// Configuration / Availability
// ===============================
/**
* @brief Enable or disable the EVSE (software flag, persisted in NVS).
*/
void evse_set_enabled(bool value);
/**
* @brief Returns true if the EVSE is currently available for use.
*/
bool evse_is_available(void);
/**
* @brief Set EVSE availability flag (may be persisted in NVS).
*/
void evse_set_available(bool value);
// ===============================
// Limit Status
// ===============================
/**
* @brief Returns true if any runtime charging limit has been reached.
*/
bool evse_is_limit_reached(void); bool evse_is_limit_reached(void);
// Autorização e disponibilidade #ifdef __cplusplus
bool evse_is_enabled(void); }
void evse_set_enabled(bool value); #endif
bool evse_is_available(void);
void evse_set_available(bool value);
bool evse_is_require_auth(void);
void evse_set_require_auth(bool value);
void evse_authorize(void);
bool evse_is_pending_auth(void);
void evse_set_authorized(bool value);
// Corrente #endif // EVSE_API_H
uint16_t evse_get_charging_current(void);
esp_err_t evse_set_charging_current(uint16_t value);
uint16_t evse_get_default_charging_current(void);
esp_err_t evse_set_default_charging_current(uint16_t value);
uint8_t evse_get_max_charging_current(void);
esp_err_t evse_set_max_charging_current(uint8_t value);
// Grid
uint8_t grid_get_max_current(void);
esp_err_t grid_set_max_current(uint8_t value);
// Temperatura
uint8_t evse_get_temp_threshold(void);
esp_err_t evse_set_temp_threshold(uint8_t value);
// RCM / Socket
bool evse_get_socket_outlet(void);
esp_err_t evse_set_socket_outlet(bool value);
bool evse_is_rcm(void);
esp_err_t evse_set_rcm(bool value);
// Limites
uint32_t evse_get_consumption_limit(void);
void evse_set_consumption_limit(uint32_t value);
uint32_t evse_get_charging_time_limit(void);
void evse_set_charging_time_limit(uint32_t value);
uint16_t evse_get_under_power_limit(void);
void evse_set_under_power_limit(uint16_t value);
void evse_set_limit_reached(uint8_t value);
// Limites default
uint32_t evse_get_default_consumption_limit(void);
void evse_set_default_consumption_limit(uint32_t value);
uint32_t evse_get_default_charging_time_limit(void);
void evse_set_default_charging_time_limit(uint32_t value);
uint16_t evse_get_default_under_power_limit(void);
void evse_set_default_under_power_limit(uint16_t value);
#endif // EVSE_API_H

View File

@@ -4,6 +4,8 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include "esp_err.h" #include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "evse_events.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -17,10 +19,6 @@ extern "C" {
#define MIN_CHARGING_CURRENT_LIMIT 6 // A #define MIN_CHARGING_CURRENT_LIMIT 6 // A
#define MAX_CHARGING_CURRENT_LIMIT 32 // A #define MAX_CHARGING_CURRENT_LIMIT 32 // A
// Corrente máxima da rede elétrica (grid)
#define MIN_GRID_CURRENT_LIMIT 6 // A
#define MAX_GRID_CURRENT_LIMIT 100 // A
// Corrente via cabo (proximity) — se configurável // Corrente via cabo (proximity) — se configurável
#define MIN_CABLE_CURRENT_LIMIT 6 // A #define MIN_CABLE_CURRENT_LIMIT 6 // A
#define MAX_CABLE_CURRENT_LIMIT 63 // A #define MAX_CABLE_CURRENT_LIMIT 63 // A
@@ -43,14 +41,14 @@ esp_err_t evse_set_charging_current(uint16_t value);
uint16_t evse_get_default_charging_current(void); uint16_t evse_get_default_charging_current(void);
esp_err_t evse_set_default_charging_current(uint16_t value); esp_err_t evse_set_default_charging_current(uint16_t value);
// Corrente da rede elétrica
uint8_t grid_get_max_current(void);
esp_err_t grid_set_max_current(uint8_t value);
// Configuração de socket outlet // Configuração de socket outlet
bool evse_get_socket_outlet(void); bool evse_get_socket_outlet(void);
esp_err_t evse_set_socket_outlet(bool socket_outlet); esp_err_t evse_set_socket_outlet(bool socket_outlet);
void evse_set_runtime_charging_current(uint16_t value);
uint16_t evse_get_runtime_charging_current(void);
// RCM // RCM
bool evse_is_rcm(void); bool evse_is_rcm(void);
esp_err_t evse_set_rcm(bool rcm); esp_err_t evse_set_rcm(bool rcm);
@@ -59,10 +57,6 @@ esp_err_t evse_set_rcm(bool rcm);
uint8_t evse_get_temp_threshold(void); uint8_t evse_get_temp_threshold(void);
esp_err_t evse_set_temp_threshold(uint8_t threshold); esp_err_t evse_set_temp_threshold(uint8_t threshold);
// Autenticação
bool evse_is_require_auth(void);
void evse_set_require_auth(bool require);
// Disponibilidade // Disponibilidade
bool evse_config_is_available(void); bool evse_config_is_available(void);
void evse_config_set_available(bool available); void evse_config_set_available(bool available);

View File

@@ -0,0 +1,17 @@
#ifndef EVSE_CORE_H
#define EVSE_CORE_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Initializes the EVSE system and starts core task loop.
*/
void evse_init(void);
#ifdef __cplusplus
}
#endif
#endif // EVSE_CORE_H

View File

@@ -1,45 +1,51 @@
// === Início de: components/evse/include/evse_error.h ===
#ifndef EVSE_ERROR_H #ifndef EVSE_ERROR_H
#define EVSE_ERROR_H #define EVSE_ERROR_H
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include "pilot.h" #include "evse_pilot.h"
// Bits que auto-limpam passado um timeout
#define EVSE_ERR_AUTO_CLEAR_BITS ( \ #define EVSE_ERR_AUTO_CLEAR_BITS ( \
EVSE_ERR_DIODE_SHORT_BIT | \ EVSE_ERR_DIODE_SHORT_BIT | \
EVSE_ERR_TEMPERATURE_HIGH_BIT | \ EVSE_ERR_TEMPERATURE_HIGH_BIT | \
EVSE_ERR_RCM_TRIGGERED_BIT ) EVSE_ERR_RCM_TRIGGERED_BIT)
// Error bits // Error bits
#define EVSE_ERR_DIODE_SHORT_BIT (1 << 0) #define EVSE_ERR_DIODE_SHORT_BIT (1 << 0)
#define EVSE_ERR_LOCK_FAULT_BIT (1 << 1) #define EVSE_ERR_LOCK_FAULT_BIT (1 << 1)
#define EVSE_ERR_UNLOCK_FAULT_BIT (1 << 2) #define EVSE_ERR_UNLOCK_FAULT_BIT (1 << 2)
#define EVSE_ERR_RCM_SELFTEST_FAULT_BIT (1 << 3) #define EVSE_ERR_RCM_SELFTEST_FAULT_BIT (1 << 3)
#define EVSE_ERR_RCM_TRIGGERED_BIT (1 << 4) #define EVSE_ERR_RCM_TRIGGERED_BIT (1 << 4)
#define EVSE_ERR_TEMPERATURE_HIGH_BIT (1 << 5) #define EVSE_ERR_TEMPERATURE_HIGH_BIT (1 << 5)
#define EVSE_ERR_PILOT_FAULT_BIT (1 << 6) #define EVSE_ERR_PILOT_FAULT_BIT (1 << 6)
#define EVSE_ERR_TEMPERATURE_FAULT_BIT (1 << 7) #define EVSE_ERR_TEMPERATURE_FAULT_BIT (1 << 7)
// Inicialização do módulo de erros // Inicialização do módulo de erros
void evse_error_init(void); void evse_error_init(void);
// Verificações e monitoramento // Verificações e monitoramento
void evse_error_check(pilot_voltage_t pilot_voltage, bool is_n12v); void evse_error_check(pilot_voltage_t pilot_voltage, bool is_n12v);
void evse_temperature_check(void); void evse_temperature_check(void);
void evse_error_tick(void); void evse_error_tick(void);
// Leitura e controle de erros // Leitura e controle de erros
uint32_t evse_get_error(void); uint32_t evse_get_error(void);
bool evse_is_error_cleared(void);
void evse_mark_error_cleared(void);
void evse_error_set(uint32_t bitmask); void evse_error_set(uint32_t bitmask);
void evse_error_clear(uint32_t bitmask); void evse_error_clear(uint32_t bitmask);
bool evse_error_is_active(void); bool evse_error_is_active(void);
uint32_t evse_error_get_bits(void); uint32_t evse_error_get_bits(void);
void evse_error_reset_flag(void);
// ----------------------------------------------------
// Semântica sticky: flag "todos erros limpos"
// ----------------------------------------------------
// Fica true quando TODOS os erros são limpos.
// Volta a false assim que qualquer erro novo aparece.
// Permanece true até o consumidor limpar explicitamente.
bool evse_error_cleared_flag(void); bool evse_error_cleared_flag(void);
void evse_error_reset_flag(void);
#endif // EVSE_ERROR_H #endif // EVSE_ERROR_H
// === Fim de: components/evse/include/evse_error.h ===

View File

@@ -1,33 +1,77 @@
#ifndef EVSE_EVENTS_H #ifndef EVSE_EVENTS_H
#define EVSE_EVENTS_H #define EVSE_EVENTS_H
#include "evse_api.h" #pragma once
#include "esp_event_base.h"
// Certifique-se de que ESP_EVENT_DECLARE_BASE seja corretamente reconhecido #include <stdbool.h>
#ifdef __cplusplus #include <stdint.h>
extern "C" { #include "esp_event.h"
#endif
// Declaração da base de eventos EVSE (será definida em evse_events.c) ESP_EVENT_DECLARE_BASE(EVSE_EVENTS);
ESP_EVENT_DECLARE_BASE(EVSE_EVENT);
typedef enum { typedef enum {
EVSE_EVENT_INIT,
EVSE_EVENT_STATE_CHANGED, EVSE_EVENT_STATE_CHANGED,
EVSE_EVENT_ERROR, EVSE_EVENT_CONFIG_UPDATED,
EVSE_EVENT_ERROR_CLEARED, EVSE_EVENT_ENABLE_UPDATED,
EVSE_EVENT_LIMIT_REACHED, EVSE_EVENT_AVAILABLE_UPDATED,
EVSE_EVENT_AUTH_GRANTED EVSE_EVENT_SESSION,
} evse_event_id_t; } evse_event_id_t;
// Estrutura do evento de mudança de estado // -----------------
typedef struct { // Eventos de STATE
evse_state_t previous; // -----------------
evse_state_t current; typedef enum {
} evse_event_state_changed_t; EVSE_STATE_EVENT_IDLE,
EVSE_STATE_EVENT_WAITING,
EVSE_STATE_EVENT_CHARGING,
EVSE_STATE_EVENT_FAULT
} evse_state_event_t;
#ifdef __cplusplus typedef struct {
} evse_state_event_t state;
#endif } evse_state_event_data_t;
// -----------------
// Eventos de SESSÃO
// -----------------
typedef enum {
EVSE_SESSION_EVENT_STARTED = 0,
EVSE_SESSION_EVENT_FINISHED,
} evse_session_event_type_t;
typedef struct {
evse_session_event_type_t type; ///< STARTED / FINISHED
// campos básicos da sessão, em tipos simples:
uint32_t session_id; ///< opcional, se tiveres um ID
uint32_t duration_s; ///< duração em segundos (0 no STARTED)
uint32_t energy_wh; ///< energia em Wh (0 no STARTED)
uint32_t avg_power_w; ///< potência média em W (0 no STARTED)
bool is_current; ///< true se ainda estiver em curso
} evse_session_event_data_t;
// -----------------
// Eventos de CONFIG
// -----------------
typedef struct {
bool charging; // Estado de carregamento
float hw_max_current; // Corrente máxima suportada pelo hardware
float runtime_current; // Corrente de carregamento em uso
int64_t timestamp_us; // Momento da atualização
} evse_config_event_data_t;
// Eventos simples e específicos
typedef struct {
bool enabled; // novo estado de enabled
int64_t timestamp_us; // epoch micros
} evse_enable_event_data_t;
typedef struct {
bool available; // novo estado de available
int64_t timestamp_us; // epoch micros
} evse_available_event_data_t;
#endif // EVSE_EVENTS_H #endif // EVSE_EVENTS_H

View File

@@ -4,7 +4,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include "evse_api.h" #include "evse_api.h"
#include "pilot.h" #include "evse_pilot.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -1,3 +1,4 @@
// === Início de: components/evse/include/evse_limits.h ===
#ifndef EVSE_LIMITS_H #ifndef EVSE_LIMITS_H
#define EVSE_LIMITS_H #define EVSE_LIMITS_H
@@ -9,35 +10,56 @@
extern "C" { extern "C" {
#endif #endif
/// Estado dos limites // ============================
void evse_set_limit_reached(uint8_t value); // Limit Status & Evaluation
bool evse_is_limit_reached(void); // ============================
/// Verifica e aplica lógica de limites com base no estado atual do EVSE /**
void evse_limits_check(evse_state_t state); * @brief Sets the internal 'limit reached' flag.
*/
void evse_set_limit_reached(bool value);
/// Limites ativos (runtime) /**
* @brief Returns true if any runtime charging limit has been reached.
*/
bool evse_get_limit_reached(void);
/**
* @brief Convenience alias for evse_get_limit_reached().
*/
bool evse_is_limit_reached(void);
/**
* @brief Checks if any session limit has been exceeded (energy, time or power).
* Should be called periodically during charging.
*/
void evse_limits_check(void);
// ============================
// Runtime Limit Configuration
// ============================
/**
* @brief Get/set energy consumption limit (in Wh).
*/
uint32_t evse_get_consumption_limit(void); uint32_t evse_get_consumption_limit(void);
void evse_set_consumption_limit(uint32_t value); void evse_set_consumption_limit(uint32_t value);
/**
* @brief Get/set maximum charging time (in seconds).
*/
uint32_t evse_get_charging_time_limit(void); uint32_t evse_get_charging_time_limit(void);
void evse_set_charging_time_limit(uint32_t value); void evse_set_charging_time_limit(uint32_t value);
/**
* @brief Get/set minimum acceptable power level (in Watts).
*/
uint16_t evse_get_under_power_limit(void); uint16_t evse_get_under_power_limit(void);
void evse_set_under_power_limit(uint16_t value); void evse_set_under_power_limit(uint16_t value);
/// Limites padrão (persistentes)
uint32_t evse_get_default_consumption_limit(void);
void evse_set_default_consumption_limit(uint32_t value);
uint32_t evse_get_default_charging_time_limit(void);
void evse_set_default_charging_time_limit(uint32_t value);
uint16_t evse_get_default_under_power_limit(void);
void evse_set_default_under_power_limit(uint16_t value);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif // EVSE_LIMITS_H #endif // EVSE_LIMITS_H
// === Fim de: components/evse/include/evse_limits.h ===

View File

@@ -1,27 +1,23 @@
#ifndef EVSE_MANAGER_H #ifndef EVSE_MANAGER_H
#define EVSE_MANAGER_H #define EVSE_MANAGER_H
#pragma once
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/queue.h> #include <freertos/queue.h>
/** /**
* @brief Inicializa os módulos internos do EVSE (hardware, estado, erros, etc.) * @brief Inicializa os módulos internos do EVSE (hardware, estado, erros, etc.)
* e inicia a tarefa de supervisão periódica (tick). * e inicia a tarefa de supervisão periódica (tick).
*/ */
void evse_manager_init(void); void evse_manager_init(void);
/**
* @brief Inicia a tarefa que processa eventos de autenticação recebidos via fila.
*
* @param queue Fila de eventos do tipo auth_event_t enviada pelo módulo auth.
*/
void evse_manager_start(QueueHandle_t queue);
/** /**
* @brief Executa uma iteração do ciclo de controle do EVSE. * @brief Executa uma iteração do ciclo de controle do EVSE.
* *
@@ -30,40 +26,9 @@ void evse_manager_start(QueueHandle_t queue);
*/ */
void evse_manager_tick(void); void evse_manager_tick(void);
/**
* @brief Verifica se o EVSE está disponível para uso.
*
* Isso considera falhas críticas, disponibilidade configurada, etc.
*/
bool evse_manager_is_available(void);
/**
* @brief Define se o EVSE deve estar disponível (ex: via controle remoto).
*/
void evse_manager_set_available(bool available);
/**
* @brief Define se o EVSE está autorizado a carregar (ex: após autenticação).
*/
void evse_manager_set_authorized(bool authorized);
/**
* @brief Verifica se o EVSE está atualmente carregando.
*/
bool evse_manager_is_charging(void);
/**
* @brief Ativa ou desativa logicamente o EVSE (controla habilitação geral).
*/
void evse_manager_set_enabled(bool enabled);
/**
* @brief Verifica se o EVSE está ativado logicamente.
*/
bool evse_manager_is_enabled(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif // EVSE_MANAGER_H #endif // EVSE_MANAGER_H

View File

@@ -0,0 +1,37 @@
#ifndef EVSE_METER_H
#define EVSE_METER_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define EVSE_METER_PHASE_COUNT 3
/// Inicializa o módulo EVSE Meter e registra os tratadores de eventos
void evse_meter_init(void);
/// Retorna a potência instantânea (soma das 3 fases, em watts)
int evse_meter_get_instant_power(void);
/// Retorna a energia total acumulada (em Wh)
int evse_meter_get_total_energy(void);
/// Retorna as potências instantâneas nas fases L1, L2 e L3 (em watts)
void evse_meter_get_power(int power[EVSE_METER_PHASE_COUNT]);
/// Retorna as tensões medidas nas fases L1, L2 e L3 (em volts)
void evse_meter_get_voltage(float voltage[EVSE_METER_PHASE_COUNT]);
/// Retorna as correntes medidas nas fases L1, L2 e L3 (em amperes)
void evse_meter_get_current(float current[EVSE_METER_PHASE_COUNT]);
/// Handler interno para eventos do medidor (não chamar externamente)
void evse_meter_on_meter_event(void* arg, void* event_data);
#ifdef __cplusplus
}
#endif
#endif // EVSE_METER_H

Some files were not shown because too many files have changed in this diff Show More