Compare commits
10 Commits
03de00b93f
...
4820d9111e
| Author | SHA1 | Date | |
|---|---|---|---|
| 4820d9111e | |||
| 96b2ab1f57 | |||
| 0d0dc5b129 | |||
| bd587a10c0 | |||
| 84f106eee5 | |||
| a0b2e048d4 | |||
| 6f95c7ba59 | |||
| 4892718736 | |||
| 66cc449143 | |||
| 12dfa85820 |
@@ -3,4 +3,4 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(esp32-evse)
|
||||
project(chargeflow)
|
||||
@@ -4,13 +4,13 @@ DEVICE_NAME=Plixin Evse
|
||||
LED_CHARGING=y
|
||||
LED_CHARGING_GPIO=14
|
||||
LED_ERROR=y
|
||||
LED_ERROR_GPIO=13
|
||||
LED_ERROR_GPIO=26
|
||||
LED_STOP=y
|
||||
LED_STOP_GPIO=12
|
||||
|
||||
#BUZZER
|
||||
BUZZER=y
|
||||
BUZZER_GPIO=21
|
||||
BUZZER_GPIO=27
|
||||
|
||||
#Button
|
||||
BUTTON_WIFI_GPIO=32
|
||||
|
||||
@@ -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)
|
||||
@@ -1,6 +0,0 @@
|
||||
#ifndef API_H_
|
||||
#define API_H_
|
||||
|
||||
void api_init(void);
|
||||
|
||||
#endif /* API_H_ */
|
||||
@@ -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 */
|
||||
@@ -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_ */
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
...
|
||||
17
components/api/lib/cAT/.gitignore
vendored
17
components/api/lib/cAT/.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
language: c
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
|
||||
before_script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake ..
|
||||
|
||||
script: make && make test
|
||||
@@ -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 )
|
||||
@@ -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.
|
||||
@@ -1,211 +0,0 @@
|
||||
[](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 */
|
||||
}
|
||||
|
||||
```
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#include "api.h"
|
||||
|
||||
void api_init(void)
|
||||
{
|
||||
//modbus_init();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
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}"
|
||||
INCLUDE_DIRS "include"
|
||||
|
||||
@@ -1,72 +1,32 @@
|
||||
#ifndef AUTH_H
|
||||
#define AUTH_H
|
||||
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "auth_types.h" // enum + MAX LEN para API
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Tamanho máximo da tag RFID (incluindo '\0')
|
||||
#define AUTH_TAG_MAX_LEN 20
|
||||
|
||||
// Evento enviado ao EVSE Manager após leitura de tag
|
||||
/* Evento auxiliar legado/útil (resultado local) */
|
||||
typedef struct {
|
||||
char tag[AUTH_TAG_MAX_LEN]; // Tag lida
|
||||
bool authorized; // true se tag for válida
|
||||
char tag[AUTH_TAG_MAX_LEN];
|
||||
bool authorized;
|
||||
} auth_event_t;
|
||||
|
||||
/**
|
||||
* @brief Inicializa o sistema de autenticação.
|
||||
* Carrega configuração e inicia o leitor Wiegand (wg26).
|
||||
*/
|
||||
void auth_init(void);
|
||||
void auth_init(void);
|
||||
void auth_set_mode(auth_mode_t mode);
|
||||
auth_mode_t auth_get_mode(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);
|
||||
|
||||
/**
|
||||
* @brief Remove uma tag previamente cadastrada.
|
||||
*/
|
||||
bool auth_remove_tag(const char *tag);
|
||||
|
||||
/**
|
||||
* @brief Verifica se uma tag está cadastrada.
|
||||
*/
|
||||
bool auth_tag_exists(const char *tag);
|
||||
|
||||
/**
|
||||
* @brief Lista as tags registradas (via ESP_LOG).
|
||||
*/
|
||||
void auth_list_tags(void);
|
||||
|
||||
/**
|
||||
* @brief Processa uma tag lida (usado pelo leitor Wiegand).
|
||||
*/
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // AUTH_H
|
||||
|
||||
29
components/auth/include/auth_events.h
Normal file
29
components/auth/include/auth_events.h
Normal 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;
|
||||
26
components/auth/include/auth_types.h
Normal file
26
components/auth/include/auth_types.h
Normal 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
|
||||
@@ -1,63 +1,123 @@
|
||||
/*
|
||||
* auth.c
|
||||
*/
|
||||
// components/auth/src/auth.c
|
||||
|
||||
#include "auth.h"
|
||||
#include "auth_events.h"
|
||||
#include "esp_event.h"
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <string.h>
|
||||
#include <strings.h> // <-- necessário para strcasecmp
|
||||
#include "wiegand_reader.h"
|
||||
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
#include "esp_random.h"
|
||||
|
||||
#define MAX_TAGS 50
|
||||
|
||||
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 int tag_count = 0;
|
||||
static uint32_t s_next_req_id = 1;
|
||||
|
||||
// Fila de eventos enviada ao EVSE Manager
|
||||
static QueueHandle_t event_queue = NULL;
|
||||
/* ===== NVS keys ===== */
|
||||
#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
|
||||
// ===========================
|
||||
|
||||
static void load_auth_config(void) {
|
||||
/* =========================
|
||||
* NVS Persistence (tags)
|
||||
* ========================= */
|
||||
static void load_tags_from_nvs(void) {
|
||||
nvs_handle_t handle;
|
||||
esp_err_t err = nvs_open("auth", NVS_READONLY, &handle);
|
||||
if (err == ESP_OK) {
|
||||
uint8_t val;
|
||||
if (nvs_get_u8(handle, "enabled", &val) == ESP_OK) {
|
||||
enabled = val;
|
||||
ESP_LOGI(TAG, "Loaded auth enabled = %d", enabled);
|
||||
if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle) != ESP_OK) {
|
||||
ESP_LOGW(TAG, "No stored tags in NVS");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t count = 0;
|
||||
if (nvs_get_u8(handle, NVS_TAG_COUNT_KEY, &count) != ESP_OK) {
|
||||
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);
|
||||
if (nvs_get_str(handle, key, tag_buf, &len) == 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++;
|
||||
}
|
||||
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;
|
||||
if (nvs_open("auth", NVS_READWRITE, &handle) == ESP_OK) {
|
||||
nvs_set_u8(handle, "enabled", enabled);
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
ESP_LOGI(TAG, "Auth config saved: enabled = %d", enabled);
|
||||
if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to open NVS to save tags");
|
||||
return;
|
||||
}
|
||||
|
||||
nvs_set_u8(handle, NVS_TAG_COUNT_KEY, tag_count);
|
||||
|
||||
for (int i = 0; i < tag_count; i++) {
|
||||
char key[16];
|
||||
snprintf(key, sizeof(key), "%s%d", NVS_TAG_PREFIX, i);
|
||||
nvs_set_str(handle, key, valid_tags[i]);
|
||||
}
|
||||
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
ESP_LOGI(TAG, "Tags saved to NVS (%d tags)", tag_count);
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* NVS Persistence (mode)
|
||||
* ========================= */
|
||||
static void load_mode_from_nvs(void) {
|
||||
nvs_handle_t h;
|
||||
if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &h) == ESP_OK) {
|
||||
uint8_t u = (uint8_t)AUTH_MODE_OPEN;
|
||||
if (nvs_get_u8(h, NVS_MODE_KEY, &u) == ESP_OK) {
|
||||
if (u <= (uint8_t)AUTH_MODE_OCPP_RFID) s_mode = (auth_mode_t)u;
|
||||
}
|
||||
nvs_close(h);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to save auth config.");
|
||||
ESP_LOGW(TAG, "No stored auth mode in NVS; default OPEN");
|
||||
}
|
||||
ESP_LOGI(TAG, "Loaded mode = %d (%s)", (int)s_mode, auth_mode_to_str(s_mode));
|
||||
}
|
||||
|
||||
static void save_mode_to_nvs(auth_mode_t mode) {
|
||||
nvs_handle_t h;
|
||||
if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &h) == ESP_OK) {
|
||||
nvs_set_u8(h, NVS_MODE_KEY, (uint8_t)mode);
|
||||
nvs_commit(h);
|
||||
nvs_close(h);
|
||||
ESP_LOGI(TAG, "Saved mode = %d (%s)", (int)mode, auth_mode_to_str(mode));
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to save auth mode to NVS");
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================
|
||||
// Internos
|
||||
// ===========================
|
||||
|
||||
/* =========================
|
||||
* 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) {
|
||||
@@ -67,32 +127,63 @@ static bool is_tag_valid(const char *tag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ===========================
|
||||
// API pública
|
||||
// ===========================
|
||||
/* =========================
|
||||
* Public API
|
||||
* ========================= */
|
||||
void auth_init(void) {
|
||||
load_mode_from_nvs();
|
||||
load_tags_from_nvs();
|
||||
|
||||
void auth_set_event_queue(QueueHandle_t queue) {
|
||||
event_queue = queue;
|
||||
if (s_mode == AUTH_MODE_LOCAL_RFID || s_mode == AUTH_MODE_OCPP_RFID) {
|
||||
initWiegand();
|
||||
ESP_LOGI(TAG, "Wiegand reader initialized (mode=%s)", auth_mode_to_str(s_mode));
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mode OPEN: Wiegand not started");
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void auth_set_enabled(bool value) {
|
||||
enabled = value;
|
||||
save_auth_config();
|
||||
ESP_LOGI(TAG, "Auth %s", enabled ? "ENABLED" : "DISABLED");
|
||||
void auth_set_mode(auth_mode_t mode) {
|
||||
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;
|
||||
}
|
||||
|
||||
s_mode = mode;
|
||||
save_mode_to_nvs(mode);
|
||||
|
||||
// Nota: se precisares, aqui podes parar/iniciar o Wiegand consoante o modo.
|
||||
if (s_mode == AUTH_MODE_OPEN) {
|
||||
ESP_LOGI(TAG, "Mode set to OPEN");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mode set to %s; ensure Wiegand reader is running", 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_is_enabled(void) {
|
||||
return enabled;
|
||||
auth_mode_t auth_get_mode(void) {
|
||||
return s_mode;
|
||||
}
|
||||
|
||||
bool auth_add_tag(const char *tag) {
|
||||
if (tag_count >= MAX_TAGS) return false;
|
||||
if (!tag || strlen(tag) >= AUTH_TAG_MAX_LEN) return false;
|
||||
if (is_tag_valid(tag)) return true;
|
||||
if (is_tag_valid(tag)) return true; // já existe
|
||||
|
||||
strncpy(valid_tags[tag_count], tag, AUTH_TAG_MAX_LEN - 1);
|
||||
valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
tag_count++;
|
||||
|
||||
save_tags_to_nvs();
|
||||
ESP_LOGI(TAG, "Tag added: %s", tag);
|
||||
return true;
|
||||
}
|
||||
@@ -104,6 +195,8 @@ bool auth_remove_tag(const char *tag) {
|
||||
strncpy(valid_tags[j], valid_tags[j + 1], AUTH_TAG_MAX_LEN);
|
||||
}
|
||||
tag_count--;
|
||||
|
||||
save_tags_to_nvs();
|
||||
ESP_LOGI(TAG, "Tag removed: %s", tag);
|
||||
return true;
|
||||
}
|
||||
@@ -122,30 +215,70 @@ void auth_list_tags(void) {
|
||||
}
|
||||
}
|
||||
|
||||
void auth_init(void) {
|
||||
load_auth_config(); // carrega estado de ativação
|
||||
initWiegand(); // inicia leitor RFID
|
||||
void auth_wait_for_tag_registration(void) {
|
||||
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) {
|
||||
if (!tag || !auth_is_enabled()) {
|
||||
ESP_LOGW(TAG, "Auth disabled or NULL tag received.");
|
||||
if (!tag || !*tag) {
|
||||
ESP_LOGW(TAG, "NULL/empty tag received");
|
||||
return;
|
||||
}
|
||||
|
||||
auth_event_t event;
|
||||
strncpy(event.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
event.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
event.authorized = is_tag_valid(tag);
|
||||
|
||||
ESP_LOGI(TAG, "Tag %s: %s", tag, event.authorized ? "AUTHORIZED" : "DENIED");
|
||||
|
||||
if (event_queue) {
|
||||
if (xQueueSend(event_queue, &event, pdMS_TO_TICKS(100)) != pdPASS) {
|
||||
ESP_LOGW(TAG, "Auth event queue full, dropping tag: %s", tag);
|
||||
switch (s_mode) {
|
||||
case AUTH_MODE_OPEN: {
|
||||
// Sem verificação; normalmente nem é necessário evento.
|
||||
ESP_LOGI(TAG, "Mode OPEN: tag=%s (no verification)", tag);
|
||||
break;
|
||||
}
|
||||
|
||||
case AUTH_MODE_LOCAL_RFID: {
|
||||
if (waiting_for_registration) {
|
||||
waiting_for_registration = false;
|
||||
|
||||
if (auth_add_tag(tag)) {
|
||||
auth_tag_event_data_t ev = {0};
|
||||
strncpy(ev.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
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;
|
||||
}
|
||||
|
||||
auth_tag_event_data_t ev = {0};
|
||||
strncpy(ev.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
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.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;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Auth event queue not set");
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
3
components/auth/src/auth_events.c
Normal file
3
components/auth/src/auth_events.c
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "auth_events.h"
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(AUTH_EVENTS);
|
||||
19
components/auth/src/auth_types.c
Executable file
19
components/auth/src/auth_types.c
Executable 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;
|
||||
}
|
||||
@@ -101,9 +101,11 @@ esp_err_t wiegand_reader_init(wiegand_reader_t *reader, gpio_num_t gpio_d0, gpio
|
||||
{
|
||||
CHECK_ARG(reader && buf_size && callback);
|
||||
|
||||
/*
|
||||
esp_err_t res = gpio_install_isr_service(0);
|
||||
if (res != ESP_OK && res != ESP_ERR_INVALID_STATE)
|
||||
return res;
|
||||
*/
|
||||
|
||||
memset(reader, 0, sizeof(wiegand_reader_t));
|
||||
reader->gpio_d0 = gpio_d0;
|
||||
|
||||
@@ -1,90 +1,279 @@
|
||||
/*
|
||||
* wiegand_reader.c
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_log.h>
|
||||
#include <wiegand.h>
|
||||
#include <evse_api.h>
|
||||
#include <ocpp.h>
|
||||
#include "auth.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define CONFIG_EXAMPLE_BUF_SIZE 50
|
||||
#define IDTAG_MAX_LEN 20
|
||||
|
||||
static const char *TAG = "WiegandReader";
|
||||
|
||||
// ---- Parâmetro global/locale 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 QueueHandle_t queue = NULL;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
uint8_t data[CONFIG_EXAMPLE_BUF_SIZE];
|
||||
size_t bits;
|
||||
size_t bytes;
|
||||
} data_packet_t;
|
||||
|
||||
static void reader_callback(wiegand_reader_t *r) {
|
||||
data_packet_t p;
|
||||
p.bits = r->bits;
|
||||
memcpy(p.data, r->buf, CONFIG_EXAMPLE_BUF_SIZE);
|
||||
xQueueSendToBack(queue, &p, 0);
|
||||
static inline uint8_t get_bit_msb_first(const uint8_t *buf, size_t bit_index)
|
||||
{
|
||||
return (buf[bit_index / 8] >> (7 - (bit_index % 8))) & 0x01;
|
||||
}
|
||||
|
||||
static void wiegand_task(void *arg) {
|
||||
queue = xQueueCreate(5, sizeof(data_packet_t));
|
||||
if (!queue) {
|
||||
// Versão parametrizável
|
||||
static bool check_parity_param(const uint8_t *buf, size_t bits, bool invert)
|
||||
{
|
||||
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
|
||||
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
|
||||
{
|
||||
*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);
|
||||
}
|
||||
|
||||
// Se o callback for de ISR, troque para xQueueSendToBackFromISR.
|
||||
static void reader_callback(wiegand_reader_t *r)
|
||||
{
|
||||
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);
|
||||
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
// Se NÃO for ISR, use xQueueSendToBack(queue, &p, 0);
|
||||
xQueueSendToBackFromISR(queue, &p, &xHigherPriorityTaskWoken);
|
||||
if (xHigherPriorityTaskWoken)
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
|
||||
static void wiegand_task(void *arg)
|
||||
{
|
||||
queue = xQueueCreate(8, sizeof(data_packet_t));
|
||||
if (!queue)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to create queue");
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(wiegand_reader_init(&reader, 19, 18,
|
||||
true, CONFIG_EXAMPLE_BUF_SIZE, reader_callback, WIEGAND_MSB_FIRST, WIEGAND_LSB_FIRST));
|
||||
ESP_ERROR_CHECK(wiegand_reader_init(&reader, 21, 22,
|
||||
true, CONFIG_EXAMPLE_BUF_SIZE, reader_callback, WIEGAND_MSB_FIRST, WIEGAND_LSB_FIRST));
|
||||
|
||||
data_packet_t p;
|
||||
while (1) {
|
||||
for (;;)
|
||||
{
|
||||
ESP_LOGI(TAG, "Waiting for Wiegand data...");
|
||||
if (xQueueReceive(queue, &p, portMAX_DELAY) == pdPASS) {
|
||||
ESP_LOGI(TAG, "Bits received: %d", p.bits);
|
||||
if (xQueueReceive(queue, &p, portMAX_DELAY) != pdPASS)
|
||||
continue;
|
||||
|
||||
char tag[20] = {0};
|
||||
ESP_LOGI(TAG, "Bits received: %d", (int)p.bits);
|
||||
|
||||
if (p.bits == 26) {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Tag read: %s", tag);
|
||||
char tag[21] = {0}; // OCPP 1.6: máx 20 chars (+NUL)
|
||||
uint32_t fc = 0, cn = 0;
|
||||
|
||||
if (!auth_is_enabled()) {
|
||||
ESP_LOGW(TAG, "Auth disabled, ignoring tag.");
|
||||
continue;
|
||||
}
|
||||
if (!wiegand_extract_fc_cn(p.data, p.bits, &fc, &cn))
|
||||
{
|
||||
ESP_LOGW(TAG, "Unsupported bit length: %d", (int)p.bits);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auth_tag_exists(tag)) {
|
||||
ESP_LOGI(TAG, "Authorized tag. Proceeding...");
|
||||
evse_authorize();
|
||||
bool ok = false;
|
||||
if (p.bits == 26)
|
||||
{
|
||||
ok = build_idtag_w26_4B(fc, cn, tag, sizeof(tag)); // 8 hex
|
||||
}
|
||||
else
|
||||
{ // 34
|
||||
ok = build_idtag_w34_7B(fc, cn, tag, sizeof(tag)); // 14 hex
|
||||
}
|
||||
|
||||
if (ocpp_is_TransactionActive()) {
|
||||
ocpp_end_transaction(tag);
|
||||
} else {
|
||||
ocpp_begin_transaction(tag);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Unauthorized tag: %s", tag);
|
||||
}
|
||||
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[] = {
|
||||
"0000004134962107",
|
||||
"W2602312345",
|
||||
"W34ABCDE12345",
|
||||
};
|
||||
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 20 segundos entre cada tag
|
||||
vTaskDelay(pdMS_TO_TICKS(30000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initWiegand(void) {
|
||||
void initWiegand(void)
|
||||
{
|
||||
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);
|
||||
|
||||
// ESP_LOGI(TAG, "Inicializando Wiegand simulado");
|
||||
// xTaskCreate(wiegand_sim_task, "WiegandSim", configMINIMAL_STACK_SIZE * 3, NULL, 3, NULL);
|
||||
}
|
||||
|
||||
12
components/buzzer/CMakeLists.txt
Executable file
12
components/buzzer/CMakeLists.txt
Executable 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
|
||||
)
|
||||
2
components/buzzer/idf_component.yml
Executable file
2
components/buzzer/idf_component.yml
Executable file
@@ -0,0 +1,2 @@
|
||||
version: "2.6.0"
|
||||
description: Authentication component
|
||||
66
components/buzzer/include/buzzer.h
Executable file
66
components/buzzer/include/buzzer.h
Executable 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
|
||||
27
components/buzzer/include/buzzer_events.h
Normal file
27
components/buzzer/include/buzzer_events.h
Normal 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;
|
||||
541
components/buzzer/src/buzzer.c
Executable file
541
components/buzzer/src/buzzer.c
Executable file
@@ -0,0 +1,541 @@
|
||||
// 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 (pode migrar para Kconfig) =====================
|
||||
#ifndef CONFIG_BUZZER_GPIO
|
||||
#define CONFIG_BUZZER_GPIO GPIO_NUM_27
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BUZZER_MODE_PASSIVE // 1 = PASSIVE (PWM), 0 = ACTIVE (on/off)
|
||||
#define CONFIG_BUZZER_MODE_PASSIVE 1
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BUZZER_FREQ_HZ
|
||||
#define CONFIG_BUZZER_FREQ_HZ 3500
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BUZZER_DUTY_PCT
|
||||
#define CONFIG_BUZZER_DUTY_PCT 70
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BUZZER_QUEUE_LEN
|
||||
#define CONFIG_BUZZER_QUEUE_LEN 8
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BUZZER_TASK_STACK
|
||||
#define CONFIG_BUZZER_TASK_STACK 2048
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BUZZER_TASK_PRIO
|
||||
#define CONFIG_BUZZER_TASK_PRIO (tskIDLE_PRIORITY + 1)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BUZZER_MIN_GAP_MS // anti-spam (gap mínimo entre toques)
|
||||
#define CONFIG_BUZZER_MIN_GAP_MS 70
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BUZZER_ENABLE_DEFAULT
|
||||
#define CONFIG_BUZZER_ENABLE_DEFAULT 1
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BUZZER_QUIET_START_MIN // quiet hours start (minutos desde 00:00)
|
||||
#define CONFIG_BUZZER_QUIET_START_MIN (22 * 60)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BUZZER_QUIET_END_MIN // quiet hours end (minutos desde 00:00)
|
||||
#define CONFIG_BUZZER_QUIET_END_MIN (7 * 60)
|
||||
#endif
|
||||
|
||||
// ===================== 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; // 0–100
|
||||
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, <) == 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;
|
||||
if (id == NETWORK_EVENT_AP_STARTED)
|
||||
{
|
||||
buzzer_event_data_t evt = {.pattern = BUZZER_PATTERN_AP_START};
|
||||
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, NETWORK_EVENT_AP_STARTED, 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, NETWORK_EVENT_AP_STARTED, 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");
|
||||
}
|
||||
3
components/buzzer/src/buzzer_events.c
Normal file
3
components/buzzer/src/buzzer_events.c
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "buzzer_events.h"
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(BUZZER_EVENTS);
|
||||
@@ -8,11 +8,15 @@ set(srcs
|
||||
evse_fsm.c
|
||||
evse_manager.c
|
||||
evse_hardware.c
|
||||
evse_pilot.c
|
||||
evse_meter.c
|
||||
evse_session.c
|
||||
evse_api.c
|
||||
)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${srcs}
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES nvs_flash
|
||||
REQUIRES peripherals auth
|
||||
PRIV_REQUIRES nvs_flash driver
|
||||
REQUIRES peripherals auth loadbalancer
|
||||
)
|
||||
37
components/evse/evse_api.c
Executable file
37
components/evse/evse_api.c
Executable 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);
|
||||
}
|
||||
@@ -1,279 +1,359 @@
|
||||
#include <inttypes.h> // Include for PRI macros
|
||||
|
||||
#include <inttypes.h> // For PRI macros
|
||||
#include "evse_config.h"
|
||||
#include "board_config.h"
|
||||
#include "evse_limits.h"
|
||||
#include "evse_api.h" // <— para evse_get_state / evse_state_is_charging
|
||||
#include "esp_log.h"
|
||||
#include "nvs.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
static const char *TAG = "evse_config";
|
||||
|
||||
static nvs_handle_t nvs;
|
||||
|
||||
// ========================
|
||||
// Configurable parameters
|
||||
static uint8_t max_charging_current = MAX_CHARGING_CURRENT_LIMIT;
|
||||
static uint8_t grid_max_current = MAX_GRID_CURRENT_LIMIT;
|
||||
static uint16_t charging_current;
|
||||
static bool socket_outlet;
|
||||
static bool rcm;
|
||||
static uint8_t temp_threshold = 60;
|
||||
static bool require_auth;
|
||||
// ========================
|
||||
static uint8_t max_charging_current = MAX_CHARGING_CURRENT_LIMIT;
|
||||
static uint16_t charging_current; // Persisted (NVS)
|
||||
static uint16_t charging_current_runtime = 0; // Runtime only
|
||||
static bool socket_outlet;
|
||||
static bool rcm;
|
||||
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_LOGI(TAG, "Opening NVS namespace");
|
||||
return nvs_open("evse", NVS_READWRITE, &nvs);
|
||||
}
|
||||
|
||||
void evse_check_defaults(void) {
|
||||
void evse_check_defaults(void)
|
||||
{
|
||||
esp_err_t err;
|
||||
uint8_t u8;
|
||||
uint16_t u16;
|
||||
uint32_t u32;
|
||||
bool needs_commit = false;
|
||||
uint8_t u8_bool;
|
||||
|
||||
ESP_LOGD(TAG, "Checking default parameters...");
|
||||
|
||||
// Max charging current
|
||||
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;
|
||||
nvs_set_u8(nvs, "max_chrg_curr", max_charging_current);
|
||||
needs_commit = true;
|
||||
ESP_LOGD(TAG, "Max charging current adjusted to: %d", max_charging_current);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Invalid or missing max_chrg_curr, resetting to %d", max_charging_current);
|
||||
}
|
||||
else
|
||||
{
|
||||
max_charging_current = u8;
|
||||
}
|
||||
|
||||
// Grid max current
|
||||
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)
|
||||
// Charging current (default, persisted)
|
||||
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 * 10) || u16 > (max_charging_current * 10)) {
|
||||
charging_current = max_charging_current * 10;
|
||||
if (err != ESP_OK || u16 < (MIN_CHARGING_CURRENT_LIMIT) || u16 > (max_charging_current))
|
||||
{
|
||||
charging_current = max_charging_current;
|
||||
nvs_set_u16(nvs, "def_chrg_curr", charging_current);
|
||||
needs_commit = true;
|
||||
ESP_LOGD(TAG, "Charging current adjusted to: %d", charging_current);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Invalid or missing def_chrg_curr, resetting to %d", charging_current);
|
||||
}
|
||||
else
|
||||
{
|
||||
charging_current = u16;
|
||||
}
|
||||
|
||||
// Runtime charging current initialized from persisted default
|
||||
charging_current_runtime = max_charging_current;
|
||||
ESP_LOGD(TAG, "Runtime charging current initialized to: %d", charging_current_runtime);
|
||||
|
||||
// Auth required
|
||||
err = nvs_get_u8(nvs, "require_auth", &u8);
|
||||
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);
|
||||
needs_commit = true;
|
||||
ESP_LOGD(TAG, "Require auth adjusted to: %d", require_auth);
|
||||
}
|
||||
|
||||
// Socket outlet
|
||||
err = nvs_get_u8(nvs, "socket_outlet", &u8);
|
||||
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);
|
||||
needs_commit = true;
|
||||
ESP_LOGD(TAG, "Socket outlet adjusted to: %d", socket_outlet);
|
||||
}
|
||||
|
||||
// RCM
|
||||
err = nvs_get_u8(nvs, "rcm", &u8);
|
||||
rcm = (err == ESP_OK && u8) && board_config.rcm;
|
||||
if (err != ESP_OK) {
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
nvs_set_u8(nvs, "rcm", rcm);
|
||||
needs_commit = true;
|
||||
ESP_LOGD(TAG, "RCM adjusted to: %d", rcm);
|
||||
}
|
||||
|
||||
// Temp threshold
|
||||
err = nvs_get_u8(nvs, "temp_threshold", &u8);
|
||||
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);
|
||||
needs_commit = true;
|
||||
ESP_LOGD(TAG, "Temp threshold adjusted to: %d", temp_threshold);
|
||||
}
|
||||
|
||||
// Additional limits
|
||||
if (nvs_get_u32(nvs, "def_cons_lim", &u32) == ESP_OK) {
|
||||
// Optional limits
|
||||
if (nvs_get_u32(nvs, "def_cons_lim", &u32) == ESP_OK)
|
||||
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);
|
||||
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);
|
||||
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) {
|
||||
nvs_commit(nvs);
|
||||
ESP_LOGD(TAG, "Changes committed to NVS.");
|
||||
// Enabled (persist)
|
||||
if (nvs_get_u8(nvs, "enabled", &u8_bool) == ESP_OK && u8_bool <= 1)
|
||||
{
|
||||
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).");
|
||||
}
|
||||
|
||||
// Save to NVS if needed
|
||||
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) {
|
||||
ESP_LOGI(TAG, "Max charging current read: %d", max_charging_current);
|
||||
// ========================
|
||||
// Charging current getters/setters
|
||||
// ========================
|
||||
uint8_t evse_get_max_charging_current(void)
|
||||
{
|
||||
return max_charging_current;
|
||||
}
|
||||
|
||||
esp_err_t evse_set_max_charging_current(uint8_t value) {
|
||||
ESP_LOGI(TAG, "Attempting to set max charging current: %d", value);
|
||||
esp_err_t evse_set_max_charging_current(uint8_t value)
|
||||
{
|
||||
if (value < MIN_CHARGING_CURRENT_LIMIT || value > MAX_CHARGING_CURRENT_LIMIT)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
max_charging_current = value;
|
||||
evse_set_runtime_charging_current(value);
|
||||
nvs_set_u8(nvs, "max_chrg_curr", value);
|
||||
nvs_commit(nvs);
|
||||
ESP_LOGD(TAG, "Max charging current adjusted to: %d", max_charging_current);
|
||||
return ESP_OK;
|
||||
return nvs_commit(nvs);
|
||||
}
|
||||
|
||||
uint8_t grid_get_max_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);
|
||||
uint16_t evse_get_charging_current(void)
|
||||
{
|
||||
return charging_current;
|
||||
}
|
||||
|
||||
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))
|
||||
esp_err_t evse_set_charging_current(uint16_t value)
|
||||
{
|
||||
if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current))
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
charging_current = value;
|
||||
nvs_set_u16(nvs, "def_chrg_curr", value);
|
||||
nvs_commit(nvs);
|
||||
ESP_LOGD(TAG, "Charging current adjusted to: %d", charging_current);
|
||||
return ESP_OK;
|
||||
return nvs_commit(nvs);
|
||||
}
|
||||
|
||||
uint16_t evse_get_default_charging_current(void) {
|
||||
uint16_t evse_get_default_charging_current(void)
|
||||
{
|
||||
uint16_t value;
|
||||
nvs_get_u16(nvs, "def_chrg_curr", &value);
|
||||
ESP_LOGD(TAG, "Default charging current read: %d", value);
|
||||
return value;
|
||||
if (nvs_get_u16(nvs, "def_chrg_curr", &value) == ESP_OK)
|
||||
return value;
|
||||
return charging_current;
|
||||
}
|
||||
|
||||
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))
|
||||
esp_err_t evse_set_default_charging_current(uint16_t value)
|
||||
{
|
||||
if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current))
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
nvs_set_u16(nvs, "def_chrg_curr", value);
|
||||
nvs_commit(nvs);
|
||||
ESP_LOGD(TAG, "Default charging current adjusted to: %d", value);
|
||||
return ESP_OK;
|
||||
return nvs_commit(nvs);
|
||||
}
|
||||
|
||||
// ========================
|
||||
// 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);
|
||||
|
||||
// --- PUBLICA ALTERAÇÃO DE CONFIG DO EVSE ---
|
||||
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
|
||||
bool evse_get_socket_outlet(void) {
|
||||
ESP_LOGD(TAG, "Socket outlet read: %d", socket_outlet);
|
||||
// ========================
|
||||
bool evse_get_socket_outlet(void)
|
||||
{
|
||||
return socket_outlet;
|
||||
}
|
||||
|
||||
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;
|
||||
esp_err_t evse_set_socket_outlet(bool value)
|
||||
{
|
||||
if (value && !board_config.proximity)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
socket_outlet = value;
|
||||
nvs_set_u8(nvs, "socket_outlet", value);
|
||||
nvs_commit(nvs);
|
||||
ESP_LOGD(TAG, "Socket outlet adjusted to: %d", socket_outlet);
|
||||
return ESP_OK;
|
||||
return nvs_commit(nvs);
|
||||
}
|
||||
|
||||
// ========================
|
||||
// RCM
|
||||
bool evse_is_rcm(void) {
|
||||
ESP_LOGD(TAG, "RCM read: %d", rcm);
|
||||
// ========================
|
||||
bool evse_is_rcm(void)
|
||||
{
|
||||
return rcm;
|
||||
}
|
||||
|
||||
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;
|
||||
esp_err_t evse_set_rcm(bool value)
|
||||
{
|
||||
if (value && !board_config.rcm)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
rcm = value;
|
||||
nvs_set_u8(nvs, "rcm", value);
|
||||
nvs_commit(nvs);
|
||||
ESP_LOGD(TAG, "RCM adjusted to: %d", rcm);
|
||||
return ESP_OK;
|
||||
return nvs_commit(nvs);
|
||||
}
|
||||
|
||||
// ========================
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
esp_err_t evse_set_temp_threshold(uint8_t value)
|
||||
{
|
||||
if (value < 40 || value > 80)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
temp_threshold = value;
|
||||
nvs_set_u8(nvs, "temp_threshold", value);
|
||||
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);
|
||||
return nvs_commit(nvs);
|
||||
}
|
||||
|
||||
// ========================
|
||||
// Availability
|
||||
static bool is_available = true;
|
||||
|
||||
bool evse_config_is_available(void) {
|
||||
ESP_LOGD(TAG, "Checking availability: %d", is_available);
|
||||
// ========================
|
||||
bool evse_config_is_available(void)
|
||||
{
|
||||
return is_available;
|
||||
}
|
||||
|
||||
void evse_config_set_available(bool available) {
|
||||
ESP_LOGD(TAG, "Setting availability to: %d", available);
|
||||
is_available = available;
|
||||
void evse_config_set_available(bool available)
|
||||
{
|
||||
is_available = available ? true : false;
|
||||
|
||||
// Persist
|
||||
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));
|
||||
}
|
||||
|
||||
// AVAILABLE_UPDATED
|
||||
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
|
||||
static bool is_enabled = true;
|
||||
|
||||
bool evse_config_is_enabled(void) {
|
||||
ESP_LOGD(TAG, "Checking if enabled: %d", is_enabled);
|
||||
// ========================
|
||||
bool evse_config_is_enabled(void)
|
||||
{
|
||||
return is_enabled;
|
||||
}
|
||||
|
||||
void evse_config_set_enabled(bool enabled) {
|
||||
ESP_LOGD(TAG, "Setting enabled state to: %d", enabled);
|
||||
is_enabled = enabled;
|
||||
void evse_config_set_enabled(bool enabled)
|
||||
{
|
||||
is_enabled = enabled ? true : false;
|
||||
|
||||
// Persist
|
||||
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));
|
||||
}
|
||||
|
||||
// ENABLE_UPDATED
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// evse_core.c - Função principal de controle do EVSE
|
||||
// evse_core.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 "pilot.h"
|
||||
#include "evse_session.h"
|
||||
#include "evse_pilot.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_log.h"
|
||||
@@ -13,25 +14,30 @@
|
||||
static const char *TAG = "evse_core";
|
||||
|
||||
static SemaphoreHandle_t mutex;
|
||||
|
||||
static evse_state_t last_state = EVSE_STATE_A;
|
||||
static bool authorized = false;
|
||||
static TickType_t auth_grant_to = 0;
|
||||
|
||||
static void evse_core_task(void *arg);
|
||||
|
||||
// ================================
|
||||
// Initialization
|
||||
// ================================
|
||||
|
||||
void evse_init(void) {
|
||||
ESP_LOGI(TAG, "EVSE Init");
|
||||
|
||||
mutex = xSemaphoreCreateMutex();
|
||||
mutex = xSemaphoreCreateMutex(); // Optional: use static version for deterministic memory
|
||||
|
||||
evse_check_defaults();
|
||||
evse_fsm_reset();
|
||||
pilot_set_level(true); // Estado inicial do piloto
|
||||
pilot_set_level(true); // Enable pilot output
|
||||
|
||||
xTaskCreate(evse_core_task, "evse_core_task", 4096, NULL, 5, NULL);
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Main Processing Logic
|
||||
// ================================
|
||||
|
||||
void evse_process(void) {
|
||||
xSemaphoreTake(mutex, portMAX_DELAY);
|
||||
|
||||
@@ -43,78 +49,34 @@ void evse_process(void) {
|
||||
|
||||
evse_error_check(pilot_voltage, is_n12v);
|
||||
|
||||
if (evse_get_error() == 0 && !evse_is_error_cleared()) {
|
||||
// Só chama FSM, que decide tudo
|
||||
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(
|
||||
pilot_voltage,
|
||||
authorized,
|
||||
evse_config_is_available(),
|
||||
evse_config_is_enabled()
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
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_mark_error_cleared();
|
||||
}
|
||||
evse_mark_error_cleared();
|
||||
|
||||
xSemaphoreGive(mutex);
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Interface pública
|
||||
// ================================
|
||||
|
||||
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
|
||||
// Background Task
|
||||
// ================================
|
||||
|
||||
static void evse_core_task(void *arg) {
|
||||
while (true) {
|
||||
evse_process();
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
vTaskDelay(pdMS_TO_TICKS(100)); // 10 Hz cycle
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ void evse_error_check(pilot_voltage_t pilot_voltage, bool 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);
|
||||
ESP_LOGW(TAG, "Erro: ausência de -12V no PWM (sem diodo)");
|
||||
ESP_LOGW(TAG, "Verificando erro: pilot_voltage = %d, is_n12v = %s", pilot_voltage, is_n12v ? "true" : "false");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#include "evse_events.h"
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(EVSE_EVENT);
|
||||
ESP_EVENT_DEFINE_BASE(EVSE_EVENTS);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// evse_fsm.c - Máquina de Estados EVSE com controle centralizado
|
||||
|
||||
#include "evse_fsm.h"
|
||||
#include "evse_api.h"
|
||||
#include "pilot.h"
|
||||
#include "evse_pilot.h"
|
||||
#include "evse_config.h"
|
||||
#include "esp_log.h"
|
||||
#include "ac_relay.h"
|
||||
#include "board_config.h"
|
||||
@@ -10,6 +9,7 @@
|
||||
#include "proximity.h"
|
||||
#include "rcm.h"
|
||||
#include "evse_state.h"
|
||||
#include "evse_error.h"
|
||||
|
||||
static const char *TAG = "evse_fsm";
|
||||
|
||||
@@ -26,7 +26,32 @@ void evse_fsm_reset(void) {
|
||||
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) {
|
||||
// ... includes e defines como já estão
|
||||
|
||||
static void update_outputs(evse_state_t state) {
|
||||
const uint16_t current = evse_get_runtime_charging_current();
|
||||
uint8_t cable_max_current = evse_get_max_charging_current();
|
||||
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);
|
||||
ESP_LOGW(TAG, "ERRO ativo: relé estava ligado, agora desligado por segurança!");
|
||||
}
|
||||
ac_relay_set_state(false); // redundância tolerável
|
||||
pilot_set_level(true); // sinal pilot sempre 12V (A)
|
||||
if (board_config.socket_lock && socket_outlet) {
|
||||
socket_lock_set_locked(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Fluxo normal
|
||||
switch (state) {
|
||||
case EVSE_STATE_A:
|
||||
case EVSE_STATE_E:
|
||||
@@ -36,7 +61,6 @@ static void update_outputs(evse_state_t state, uint16_t charging_current, uint8_
|
||||
if (board_config.socket_lock && socket_outlet) {
|
||||
socket_lock_set_locked(false);
|
||||
}
|
||||
//energy_meter_stop_session();
|
||||
break;
|
||||
|
||||
case EVSE_STATE_B1:
|
||||
@@ -47,44 +71,55 @@ static void update_outputs(evse_state_t state, uint16_t charging_current, uint8_
|
||||
}
|
||||
|
||||
if (rcm_test()) {
|
||||
ESP_LOGI(TAG, "RCM self test passed");
|
||||
//ESP_LOGI(TAG, "RCM self test passed");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "RCM self test failed");
|
||||
//ESP_LOGW(TAG, "RCM self test failed");
|
||||
}
|
||||
|
||||
if (socket_outlet) {
|
||||
cable_max_current = proximity_get_max_current();
|
||||
}
|
||||
|
||||
//energy_meter_start_session();
|
||||
break;
|
||||
|
||||
case EVSE_STATE_B2:
|
||||
pilot_set_amps(MIN(charging_current, cable_max_current * 10));
|
||||
pilot_set_amps(MIN(current, cable_max_current));
|
||||
ac_relay_set_state(false);
|
||||
break;
|
||||
|
||||
case EVSE_STATE_C1:
|
||||
case EVSE_STATE_D1:
|
||||
pilot_set_level(true);
|
||||
case EVSE_STATE_D1: {
|
||||
pilot_set_amps(MIN(current, cable_max_current)); // mantém PWM
|
||||
ac_relay_set_state(false); // relé ainda desligado
|
||||
c1_d1_waiting = true;
|
||||
c1_d1_relay_to = xTaskGetTickCount() + pdMS_TO_TICKS(6000);
|
||||
break;
|
||||
|
||||
}
|
||||
case EVSE_STATE_C2:
|
||||
case EVSE_STATE_D2:
|
||||
pilot_set_amps(MIN(charging_current, cable_max_current * 10));
|
||||
ac_relay_set_state(true);
|
||||
pilot_set_amps(MIN(current, cable_max_current));
|
||||
ac_relay_set_state(true); // Só chega aqui se não há erro!
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool available, bool enabled) {
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
evse_state_t previous_state = evse_get_state();
|
||||
evse_state_t current_state = previous_state;
|
||||
// FSM principal - centraliza a lógica de erro e de todos os estados
|
||||
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;
|
||||
}
|
||||
|
||||
switch (current_state) {
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
evse_state_t prev = evse_get_state();
|
||||
evse_state_t curr = prev;
|
||||
|
||||
switch (curr) {
|
||||
case EVSE_STATE_A:
|
||||
if (!available) {
|
||||
evse_set_state(EVSE_STATE_F);
|
||||
@@ -99,7 +134,6 @@ void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool avail
|
||||
evse_set_state(EVSE_STATE_F);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (pilot_voltage) {
|
||||
case PILOT_VOLTAGE_12:
|
||||
evse_set_state(EVSE_STATE_A);
|
||||
@@ -125,18 +159,15 @@ void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool avail
|
||||
break;
|
||||
}
|
||||
}
|
||||
// fallthrough intencional
|
||||
__attribute__((fallthrough));
|
||||
|
||||
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
|
||||
);
|
||||
evse_set_state((curr == EVSE_STATE_D2 || curr == 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);
|
||||
@@ -156,19 +187,23 @@ void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool avail
|
||||
break;
|
||||
|
||||
case EVSE_STATE_E:
|
||||
// Sem transições a partir de E
|
||||
// Estado elétrico grave: só reset manual
|
||||
break;
|
||||
|
||||
case EVSE_STATE_F:
|
||||
if (available) {
|
||||
// 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();
|
||||
if (new_state != previous_state) {
|
||||
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());
|
||||
evse_state_t next = evse_get_state();
|
||||
update_outputs(next);
|
||||
|
||||
if (next != prev) {
|
||||
ESP_LOGI(TAG, "State changed: %s -> %s",
|
||||
evse_state_to_str(prev),
|
||||
evse_state_to_str(next));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "evse_hardware.h"
|
||||
#include "pilot.h"
|
||||
#include "evse_pilot.h"
|
||||
#include "ac_relay.h"
|
||||
#include "socket_lock.h"
|
||||
#include "proximity.h"
|
||||
@@ -7,6 +7,7 @@
|
||||
static const char *TAG = "evse_hardware";
|
||||
|
||||
void evse_hardware_init(void) {
|
||||
pilot_init();
|
||||
pilot_set_level(true); // Sinal piloto em 12V (inicial)
|
||||
ac_relay_set_state(false); // Relé desligado
|
||||
//socket_lock_set_locked(false); // Destrava o conector
|
||||
|
||||
@@ -1,62 +1,111 @@
|
||||
#include <inttypes.h> // for PRIu32
|
||||
#include "evse_state.h"
|
||||
#include "evse_api.h"
|
||||
#include "evse_limits.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "evse_meter.h"
|
||||
#include "evse_session.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
|
||||
// ========================
|
||||
// Estado interno
|
||||
// External state references
|
||||
// ========================
|
||||
|
||||
static bool limit_reached = false;
|
||||
static uint32_t consumption_limit = 0;
|
||||
static uint32_t charging_time_limit = 0;
|
||||
static uint16_t under_power_limit = 0;
|
||||
//extern evse_state_t current_state; // Current EVSE FSM state
|
||||
//extern TickType_t session_start_tick; // Timestamp of charging session start
|
||||
|
||||
static uint32_t default_consumption_limit = 0;
|
||||
// ========================
|
||||
// Concurrency protection
|
||||
// ========================
|
||||
|
||||
static portMUX_TYPE evse_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
// ========================
|
||||
// Runtime state (volatile)
|
||||
// ========================
|
||||
|
||||
static bool limit_reached = false;
|
||||
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
|
||||
|
||||
// ========================
|
||||
// Default (persistent) limits
|
||||
// ========================
|
||||
|
||||
static uint32_t default_consumption_limit = 0;
|
||||
static uint32_t default_charging_time_limit = 0;
|
||||
static uint16_t default_under_power_limit = 0;
|
||||
static uint16_t default_under_power_limit = 0;
|
||||
|
||||
// ========================
|
||||
// Estado de controle
|
||||
// Limit status flag
|
||||
// ========================
|
||||
|
||||
void evse_set_limit_reached(uint8_t value) {
|
||||
limit_reached = (value != 0);
|
||||
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) {
|
||||
return limit_reached;
|
||||
void evse_set_limit_reached(bool v) {
|
||||
portENTER_CRITICAL(&evse_mux);
|
||||
limit_reached = v;
|
||||
portEXIT_CRITICAL(&evse_mux);
|
||||
}
|
||||
|
||||
// ========================
|
||||
// Limites em tempo de execução
|
||||
// Runtime limit accessors
|
||||
// ========================
|
||||
|
||||
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) {
|
||||
portENTER_CRITICAL(&evse_mux);
|
||||
consumption_limit = value;
|
||||
portEXIT_CRITICAL(&evse_mux);
|
||||
}
|
||||
|
||||
uint32_t evse_get_charging_time_limit(void) {
|
||||
return charging_time_limit;
|
||||
uint32_t val;
|
||||
portENTER_CRITICAL(&evse_mux);
|
||||
val = charging_time_limit;
|
||||
portEXIT_CRITICAL(&evse_mux);
|
||||
return val;
|
||||
}
|
||||
|
||||
void evse_set_charging_time_limit(uint32_t value) {
|
||||
portENTER_CRITICAL(&evse_mux);
|
||||
charging_time_limit = value;
|
||||
portEXIT_CRITICAL(&evse_mux);
|
||||
}
|
||||
|
||||
uint16_t evse_get_under_power_limit(void) {
|
||||
return under_power_limit;
|
||||
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) {
|
||||
portENTER_CRITICAL(&evse_mux);
|
||||
under_power_limit = value;
|
||||
portEXIT_CRITICAL(&evse_mux);
|
||||
}
|
||||
|
||||
// ========================
|
||||
// Limites padrão (persistentes)
|
||||
// Default (persistent) limit accessors
|
||||
// These values can be stored/restored via NVS
|
||||
// ========================
|
||||
|
||||
uint32_t evse_get_default_consumption_limit(void) {
|
||||
@@ -83,15 +132,57 @@ void evse_set_default_under_power_limit(uint16_t value) {
|
||||
default_under_power_limit = value;
|
||||
}
|
||||
|
||||
// ========================
|
||||
// Lógica de verificação de limites
|
||||
// ========================
|
||||
|
||||
void evse_limits_check(evse_state_t state) {
|
||||
// Se algum limite estiver ativo, verifique o estado
|
||||
if ((consumption_limit > 0 || charging_time_limit > 0 || under_power_limit > 0)
|
||||
&& evse_state_is_charging(state)) {
|
||||
// (Lógica real a ser aplicada aqui, ex: medição de consumo, tempo ou potência)
|
||||
evse_set_limit_reached(1);
|
||||
}
|
||||
bool evse_is_limit_reached(void) {
|
||||
return evse_get_limit_reached();
|
||||
}
|
||||
|
||||
// ========================
|
||||
// Limit checking logic
|
||||
// This function must be called periodically while charging.
|
||||
// It will flag the session as "limit reached" when thresholds are violated.
|
||||
// ========================
|
||||
void evse_limits_check(void) {
|
||||
// Only check during an active charging session
|
||||
if (!evse_state_is_charging(evse_get_state())) {
|
||||
return;
|
||||
}
|
||||
|
||||
evse_session_t sess;
|
||||
// Retrieve accumulated data for the current session
|
||||
if (!evse_session_get(&sess) || !sess.is_current) {
|
||||
// If there's no active session, abort
|
||||
return;
|
||||
}
|
||||
|
||||
bool reached = false;
|
||||
|
||||
// 1) Energy consumption limit (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) Charging time limit (seconds)
|
||||
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 limit (instantaneous power)
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
// === Início de: components/evse/evse_manager.c ===
|
||||
#include "evse_manager.h"
|
||||
#include "evse_state.h"
|
||||
#include "evse_error.h"
|
||||
#include "evse_hardware.h"
|
||||
#include "evse_config.h"
|
||||
#include "evse_api.h"
|
||||
#include "auth.h"
|
||||
#include "evse_meter.h"
|
||||
#include "evse_session.h"
|
||||
#include "evse_config.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
@@ -12,113 +15,320 @@
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_log.h"
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "auth_events.h"
|
||||
#include "loadbalancer_events.h"
|
||||
#include "ocpp_events.h"
|
||||
#include "esp_event.h"
|
||||
|
||||
static const char *TAG = "EVSE_Manager";
|
||||
|
||||
static TickType_t auth_expiration = 0;
|
||||
|
||||
static SemaphoreHandle_t evse_mutex;
|
||||
static QueueHandle_t auth_event_queue = NULL;
|
||||
static bool auth_enabled = false;
|
||||
|
||||
#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo
|
||||
// Estado de pausa controlado pelo Load Balancer
|
||||
static bool lb_paused = false;
|
||||
static bool lb_prev_authorized = false;
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
static void evse_manager_handle_auth_on_tick(void)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Se autenticação está desativada, garante autorização sempre ativa
|
||||
if (!evse_state_get_authorized())
|
||||
{
|
||||
evse_state_set_authorized(true);
|
||||
ESP_LOGI(TAG, "Authentication disabled → forced authorization.");
|
||||
// Em modo OPEN, pausa do LB não é tão relevante, mas limpamos mesmo assim
|
||||
lb_clear_pause_state();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Task de ciclo principal =====
|
||||
static void evse_manager_task(void *arg) {
|
||||
while (true) {
|
||||
static void evse_manager_task(void *arg)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
evse_manager_tick();
|
||||
vTaskDelay(pdMS_TO_TICKS(EVSE_MANAGER_TICK_PERIOD_MS));
|
||||
}
|
||||
}
|
||||
|
||||
static void evse_auth_event_task(void *arg) {
|
||||
auth_event_t evt;
|
||||
static void on_auth_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
if (base != AUTH_EVENTS || !data)
|
||||
return;
|
||||
|
||||
while (true) {
|
||||
if (xQueueReceive(auth_event_queue, &evt, portMAX_DELAY)) {
|
||||
ESP_LOGI(TAG, "Evento de autenticação recebido: %s (%s)",
|
||||
evt.tag, evt.authorized ? "AUTORIZADO" : "NEGADO");
|
||||
auth_mode_t g_mode = AUTH_MODE_OPEN;
|
||||
|
||||
if (evt.authorized) {
|
||||
evse_authorize();
|
||||
auth_expiration = xTaskGetTickCount() + pdMS_TO_TICKS(2 * 60 * 1000); // 2 minutos
|
||||
} else {
|
||||
evse_manager_set_authorized(false);
|
||||
ESP_LOGW(TAG, "Tag inválida, carregamento negado.");
|
||||
switch (id)
|
||||
{
|
||||
case AUTH_EVENT_TAG_PROCESSED:
|
||||
{
|
||||
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)
|
||||
{
|
||||
evse_state_set_authorized(true);
|
||||
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 loadbalancer =====
|
||||
static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
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);
|
||||
// Ações adicionais podem ser adicionadas aqui conforme necessário
|
||||
}
|
||||
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
|
||||
bool can_resume =
|
||||
(evse_get_error() == 0) &&
|
||||
evse_config_is_available() &&
|
||||
evse_config_is_enabled();
|
||||
|
||||
if (!can_resume)
|
||||
{
|
||||
ESP_LOGW(TAG,
|
||||
"[LB] limit=%uA → não retoma automaticamente (erro/indisponível/desabilitado)",
|
||||
evt->max_current);
|
||||
lb_clear_pause_state();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!auth_enabled)
|
||||
{
|
||||
// Modo OPEN: retoma sempre
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
if (base != OCPP_EVENTS)
|
||||
return;
|
||||
|
||||
// ===== Inicialização dos módulos do EVSE =====
|
||||
void evse_manager_init(void) {
|
||||
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");
|
||||
// StartTx em si não precisa mexer em auth, mas limpamos estado de pausa por segurança
|
||||
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);
|
||||
// Opcional: poderias também limpar a pausa aqui, dependendo da política
|
||||
// lb_clear_pause_state();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ESP_LOGD(TAG, "[OCPP] Unhandled event id=%" PRId32, id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Inicialização =====
|
||||
void evse_manager_init(void)
|
||||
{
|
||||
evse_mutex = xSemaphoreCreateMutex();
|
||||
|
||||
evse_config_init();
|
||||
evse_error_init();
|
||||
evse_hardware_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_LOGI(TAG, "EVSE Manager inicializado.");
|
||||
|
||||
xTaskCreate(evse_manager_task, "evse_manager_task", 4096, NULL, 5, NULL);
|
||||
}
|
||||
|
||||
// ===== Inicia processamento de eventos de autenticação =====
|
||||
void evse_manager_start(QueueHandle_t queue) {
|
||||
auth_event_queue = queue;
|
||||
xTaskCreate(evse_auth_event_task, "evse_auth_evt", 4096, NULL, 5, NULL);
|
||||
}
|
||||
|
||||
void evse_manager_tick(void) {
|
||||
// ===== Main Tick =====
|
||||
void evse_manager_tick(void)
|
||||
{
|
||||
xSemaphoreTake(evse_mutex, portMAX_DELAY);
|
||||
|
||||
evse_hardware_tick();
|
||||
evse_error_tick();
|
||||
evse_state_tick();
|
||||
evse_temperature_check();
|
||||
evse_session_tick();
|
||||
|
||||
// Verifica expiração de autorização somente se auth está habilitado
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
evse_manager_handle_auth_on_tick();
|
||||
|
||||
xSemaphoreGive(evse_mutex);
|
||||
}
|
||||
|
||||
// ===== Controles e status =====
|
||||
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();
|
||||
}
|
||||
// === Fim de: components/evse/evse_manager.c ===
|
||||
|
||||
121
components/evse/evse_meter.c
Normal file
121
components/evse/evse_meter.c
Normal 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_LOGI(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);
|
||||
}
|
||||
@@ -9,8 +9,8 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_rom_sys.h"
|
||||
|
||||
#include "pilot.h"
|
||||
#include "adc.h"
|
||||
#include "evse_pilot.h"
|
||||
#include "adc121s021_dma.h"
|
||||
#include "board_config.h"
|
||||
|
||||
#define PILOT_PWM_TIMER LEDC_TIMER_0
|
||||
@@ -21,17 +21,26 @@
|
||||
|
||||
#define NUM_PILOT_SAMPLES 100
|
||||
#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";
|
||||
static pilot_voltage_cache_t last_voltage = {0, 0};
|
||||
// ADC121S021 setup
|
||||
#define ADC121_VREF_MV 3300 // AJUSTE conforme Vref do seu hardware!
|
||||
#define ADC121_MAX 4095 // 12 bits
|
||||
|
||||
static inline uint16_t adc_to_mv(uint16_t x) {
|
||||
return (uint16_t)(((uint32_t)(x) * 3300U) / 4095U);
|
||||
static const char *TAG = "evse_pilot";
|
||||
|
||||
// Memoização de estado para evitar comandos/logs desnecessários
|
||||
static int last_pilot_level = -1;
|
||||
static uint32_t last_pwm_duty = 0;
|
||||
|
||||
// Função para converter leitura bruta do ADC para mV
|
||||
static int adc_raw_to_mv(uint16_t raw) {
|
||||
return (raw * ADC121_VREF_MV) / ADC121_MAX;
|
||||
}
|
||||
|
||||
void pilot_init(void)
|
||||
{
|
||||
// PWM (LEDC) configuração
|
||||
ledc_timer_config_t ledc_timer = {
|
||||
.speed_mode = PILOT_PWM_SPEED_MODE,
|
||||
.timer_num = PILOT_PWM_TIMER,
|
||||
@@ -52,70 +61,79 @@ void pilot_init(void)
|
||||
};
|
||||
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_fade_func_install(0));
|
||||
|
||||
ESP_ERROR_CHECK(ledc_fade_func_install(0));
|
||||
|
||||
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));
|
||||
// Inicializa ADC121S021 externo
|
||||
adc121s021_dma_init();
|
||||
}
|
||||
|
||||
void pilot_set_level(bool level)
|
||||
{
|
||||
if (last_pilot_level == level) return; // só muda se necessário
|
||||
last_pilot_level = level;
|
||||
|
||||
ESP_LOGI(TAG, "Set level %d", level);
|
||||
ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, level ? 1 : 0);
|
||||
last_pwm_duty = 0; // PWM parado
|
||||
}
|
||||
|
||||
void pilot_set_amps(uint16_t amps)
|
||||
{
|
||||
ESP_LOGI(TAG, "Set amps %d", amps);
|
||||
|
||||
if (amps < 60 || amps > 800) {
|
||||
ESP_LOGE(TAG, "Invalid ampere value: %d A*10", amps);
|
||||
if (amps < 6 || amps > 80) {
|
||||
ESP_LOGE(TAG, "Invalid ampere value: %d A (valid: 6–80 A)", amps);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t duty;
|
||||
if (amps <= 510) {
|
||||
duty = (PILOT_PWM_MAX_DUTY * amps) / 600;
|
||||
uint32_t duty_percent;
|
||||
|
||||
if (amps <= 51) {
|
||||
duty_percent = (amps * 10) / 6; // Duty (%) = Amps / 0.6
|
||||
} else {
|
||||
duty = ((PILOT_PWM_MAX_DUTY * amps) / 2500) + (64 * (PILOT_PWM_MAX_DUTY / 100));
|
||||
duty_percent = (amps * 10) / 25 + 64; // Duty (%) = (Amps / 2.5) + 64
|
||||
}
|
||||
|
||||
if (duty > PILOT_PWM_MAX_DUTY)
|
||||
duty = PILOT_PWM_MAX_DUTY;
|
||||
if (duty_percent > 100) duty_percent = 100;
|
||||
|
||||
uint32_t duty = (PILOT_PWM_MAX_DUTY * duty_percent) / 100;
|
||||
|
||||
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_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL);
|
||||
}
|
||||
|
||||
static int compare_u16(const void *a, const void *b) {
|
||||
return (*(uint16_t *)a - *(uint16_t *)b);
|
||||
bool pilot_get_state(void) {
|
||||
// true se estamos em 12V fixo; false se PWM ou -12V
|
||||
// Se quiser diferenciar PWM, guarde um flag quando chamar set_amps.
|
||||
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) {
|
||||
return (*(int *)a - *(int *)b);
|
||||
}
|
||||
|
||||
static int select_low_median_qsort(int *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);
|
||||
int *copy = alloca(n * sizeof(int));
|
||||
memcpy(copy, src, n * sizeof(int));
|
||||
qsort(copy, n, sizeof(int), compare_int);
|
||||
return copy[k / 2];
|
||||
}
|
||||
|
||||
static uint16_t select_high_median_qsort(uint16_t *src, int n, int percent) {
|
||||
static int select_high_median_qsort(int *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);
|
||||
int *copy = alloca(n * sizeof(int));
|
||||
memcpy(copy, src, n * sizeof(int));
|
||||
qsort(copy, n, sizeof(int), compare_int);
|
||||
return copy[n - k + (k / 2)];
|
||||
}
|
||||
|
||||
@@ -123,13 +141,15 @@ void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12)
|
||||
{
|
||||
ESP_LOGD(TAG, "pilot_measure");
|
||||
|
||||
uint16_t samples[NUM_PILOT_SAMPLES];
|
||||
int samples[NUM_PILOT_SAMPLES];
|
||||
int collected = 0, attempts = 0;
|
||||
uint16_t sample;
|
||||
uint16_t adc_sample = 0;
|
||||
|
||||
// Lê samples usando ADC121S021 externo
|
||||
while (collected < NUM_PILOT_SAMPLES && attempts < MAX_SAMPLE_ATTEMPTS) {
|
||||
if (adc_oneshot_read(adc_handle, board_config.pilot_adc_channel, &sample) == ESP_OK) {
|
||||
samples[collected++] = sample;
|
||||
adc_sample = 0;
|
||||
if (adc121s021_dma_get_sample(&adc_sample)) {
|
||||
samples[collected++] = adc_sample;
|
||||
esp_rom_delay_us(10);
|
||||
} else {
|
||||
esp_rom_delay_us(100);
|
||||
@@ -144,20 +164,13 @@ void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12)
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t high_raw = select_high_median_qsort(samples, collected, PILOT_EXTREME_PERCENT);
|
||||
uint16_t low_raw = select_low_median_qsort(samples, collected, PILOT_EXTREME_PERCENT);
|
||||
int high_raw = select_high_median_qsort(samples, collected, PILOT_EXTREME_PERCENT);
|
||||
int low_raw = select_low_median_qsort(samples, collected, PILOT_EXTREME_PERCENT);
|
||||
|
||||
int high_mv = 0;
|
||||
int low_mv = 0;
|
||||
|
||||
if (adc_cali_raw_to_voltage(adc_cali_handle, high_raw, &high_mv) != ESP_OK ||
|
||||
adc_cali_raw_to_voltage(adc_cali_handle, low_raw, &low_mv) != ESP_OK) {
|
||||
ESP_LOGW(TAG, "ADC calibration failed");
|
||||
*up_voltage = PILOT_VOLTAGE_1;
|
||||
*down_voltage_n12 = false;
|
||||
return;
|
||||
}
|
||||
int high_mv = adc_raw_to_mv(high_raw);
|
||||
int low_mv = adc_raw_to_mv(low_raw);
|
||||
|
||||
// Aplica thresholds definidos em board_config (em mV)
|
||||
if (high_mv >= board_config.pilot_down_threshold_12)
|
||||
*up_voltage = PILOT_VOLTAGE_12;
|
||||
else if (high_mv >= board_config.pilot_down_threshold_9)
|
||||
@@ -173,14 +186,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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
84
components/evse/evse_session.c
Normal file
84
components/evse/evse_session.c
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* evse_session.c
|
||||
* Implementation of evse_session module using instantaneous power accumulation
|
||||
*/
|
||||
#include <inttypes.h> // for PRIu32
|
||||
#include "evse_session.h"
|
||||
#include "evse_meter.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "evse_session";
|
||||
|
||||
// Internal state
|
||||
static TickType_t session_start_tick = 0;
|
||||
static uint32_t watt_seconds = 0; // accumulator of W·s
|
||||
static evse_session_t last_session;
|
||||
static bool last_session_valid = false;
|
||||
|
||||
void evse_session_init(void) {
|
||||
session_start_tick = 0;
|
||||
watt_seconds = 0;
|
||||
last_session_valid = false;
|
||||
}
|
||||
|
||||
void evse_session_start(void) {
|
||||
session_start_tick = xTaskGetTickCount();
|
||||
watt_seconds = 0;
|
||||
ESP_LOGI(TAG, "Session started at tick %u", (unsigned)session_start_tick);
|
||||
}
|
||||
|
||||
void evse_session_end(void) {
|
||||
if (session_start_tick == 0) {
|
||||
ESP_LOGW(TAG, "evse_session_end called without active session");
|
||||
return;
|
||||
}
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
uint32_t duration_s = (now - session_start_tick) / configTICK_RATE_HZ;
|
||||
uint32_t energy_wh = watt_seconds / 3600U;
|
||||
uint32_t avg_power = duration_s > 0 ? watt_seconds / duration_s : 0;
|
||||
|
||||
last_session.start_tick = session_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;
|
||||
|
||||
session_start_tick = 0;
|
||||
ESP_LOGI(TAG, "Session ended: duration=%" PRIu32 " s, energy=%" PRIu32 " Wh, avg_power=%" PRIu32 " W",
|
||||
(uint32_t)duration_s, (uint32_t)energy_wh, (uint32_t)avg_power);
|
||||
}
|
||||
|
||||
void evse_session_tick(void) {
|
||||
if (session_start_tick == 0) return;
|
||||
// Should be called every second (or known interval)
|
||||
uint32_t power_w = evse_meter_get_instant_power();
|
||||
watt_seconds += power_w;
|
||||
}
|
||||
|
||||
bool evse_session_get(evse_session_t *out) {
|
||||
if (out == NULL) return false;
|
||||
|
||||
if (session_start_tick != 0) {
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
uint32_t duration_s = (now - session_start_tick) / configTICK_RATE_HZ;
|
||||
uint32_t energy_wh = watt_seconds / 3600U;
|
||||
uint32_t avg_power = duration_s > 0 ? watt_seconds / duration_s : 0;
|
||||
|
||||
out->start_tick = session_start_tick;
|
||||
out->duration_s = duration_s;
|
||||
out->energy_wh = energy_wh;
|
||||
out->avg_power_w = avg_power;
|
||||
out->is_current = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (last_session_valid) {
|
||||
*out = last_session;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1,19 +1,93 @@
|
||||
#include "evse_api.h"
|
||||
#include "evse_state.h"
|
||||
#include "evse_session.h"
|
||||
#include "evse_events.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/portmacro.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
// =========================
|
||||
// Internal State Variables
|
||||
// =========================
|
||||
|
||||
static evse_state_t current_state = EVSE_STATE_A;
|
||||
static bool is_authorized = false;
|
||||
|
||||
// Proteção básica para variáveis globais em sistemas concorrentes
|
||||
static portMUX_TYPE state_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
void evse_set_state(evse_state_t state) {
|
||||
portENTER_CRITICAL(&state_mux);
|
||||
current_state = state;
|
||||
portEXIT_CRITICAL(&state_mux);
|
||||
static const char *TAG = "evse_state";
|
||||
|
||||
// =========================
|
||||
// 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) {
|
||||
portENTER_CRITICAL(&state_mux);
|
||||
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) {
|
||||
portENTER_CRITICAL(&state_mux);
|
||||
current_state = EVSE_STATE_A;
|
||||
is_authorized = false;
|
||||
is_authorized = true;
|
||||
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) {
|
||||
// Tick do estado (placeholder)
|
||||
// Placeholder for future state logic
|
||||
}
|
||||
|
||||
bool evse_state_is_charging(evse_state_t state) {
|
||||
|
||||
@@ -3,68 +3,86 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "evse_state.h" // Define evse_state_t
|
||||
#include "evse_state.h"
|
||||
#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);
|
||||
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);
|
||||
|
||||
|
||||
// ===============================
|
||||
// Limit Status
|
||||
// ===============================
|
||||
|
||||
/**
|
||||
* @brief Returns true if any runtime charging limit has been reached.
|
||||
*/
|
||||
bool evse_is_limit_reached(void);
|
||||
|
||||
// Autorização e disponibilidade
|
||||
bool evse_is_enabled(void);
|
||||
void evse_set_enabled(bool value);
|
||||
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
|
||||
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);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // EVSE_API_H
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "evse_events.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -17,10 +19,6 @@ extern "C" {
|
||||
#define MIN_CHARGING_CURRENT_LIMIT 6 // 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
|
||||
#define MIN_CABLE_CURRENT_LIMIT 6 // 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);
|
||||
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
|
||||
bool evse_get_socket_outlet(void);
|
||||
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
|
||||
bool evse_is_rcm(void);
|
||||
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);
|
||||
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
|
||||
bool evse_config_is_available(void);
|
||||
void evse_config_set_available(bool available);
|
||||
|
||||
17
components/evse/include/evse_core.h
Normal file
17
components/evse/include/evse_core.h
Normal 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
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "pilot.h"
|
||||
#include "evse_pilot.h"
|
||||
|
||||
|
||||
#define EVSE_ERR_AUTO_CLEAR_BITS ( \
|
||||
|
||||
@@ -1,33 +1,46 @@
|
||||
#ifndef EVSE_EVENTS_H
|
||||
#define EVSE_EVENTS_H
|
||||
|
||||
#include "evse_api.h"
|
||||
#include "esp_event_base.h"
|
||||
#pragma once
|
||||
#include "esp_event.h"
|
||||
|
||||
// Certifique-se de que ESP_EVENT_DECLARE_BASE seja corretamente reconhecido
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Declaração da base de eventos EVSE (será definida em evse_events.c)
|
||||
ESP_EVENT_DECLARE_BASE(EVSE_EVENT);
|
||||
ESP_EVENT_DECLARE_BASE(EVSE_EVENTS);
|
||||
|
||||
typedef enum {
|
||||
EVSE_EVENT_INIT,
|
||||
EVSE_EVENT_STATE_CHANGED,
|
||||
EVSE_EVENT_ERROR,
|
||||
EVSE_EVENT_ERROR_CLEARED,
|
||||
EVSE_EVENT_LIMIT_REACHED,
|
||||
EVSE_EVENT_AUTH_GRANTED
|
||||
EVSE_EVENT_CONFIG_UPDATED,
|
||||
EVSE_EVENT_ENABLE_UPDATED,
|
||||
EVSE_EVENT_AVAILABLE_UPDATED,
|
||||
} evse_event_id_t;
|
||||
|
||||
// Estrutura do evento de mudança de estado
|
||||
typedef struct {
|
||||
evse_state_t previous;
|
||||
evse_state_t current;
|
||||
} evse_event_state_changed_t;
|
||||
typedef enum {
|
||||
EVSE_STATE_EVENT_IDLE,
|
||||
EVSE_STATE_EVENT_WAITING,
|
||||
EVSE_STATE_EVENT_CHARGING,
|
||||
EVSE_STATE_EVENT_FAULT
|
||||
} evse_state_event_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
typedef struct {
|
||||
evse_state_event_t state;
|
||||
} evse_state_event_data_t;
|
||||
|
||||
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
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "evse_api.h"
|
||||
#include "pilot.h"
|
||||
#include "evse_pilot.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -9,24 +9,58 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// Estado dos limites
|
||||
void evse_set_limit_reached(uint8_t value);
|
||||
bool evse_is_limit_reached(void);
|
||||
// ============================
|
||||
// Limit Status & Evaluation
|
||||
// ============================
|
||||
|
||||
/// 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.
|
||||
* Called internally when a limit condition is triggered.
|
||||
*/
|
||||
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 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);
|
||||
void evse_set_consumption_limit(uint32_t value);
|
||||
|
||||
/**
|
||||
* @brief Get/set maximum charging time (in seconds).
|
||||
*/
|
||||
uint32_t evse_get_charging_time_limit(void);
|
||||
void evse_set_charging_time_limit(uint32_t value);
|
||||
|
||||
/**
|
||||
* @brief Get/set minimum acceptable power level (in Watts).
|
||||
* If the power remains below this for a long time, the session may be interrupted.
|
||||
*/
|
||||
uint16_t evse_get_under_power_limit(void);
|
||||
void evse_set_under_power_limit(uint16_t value);
|
||||
|
||||
/// Limites padrão (persistentes)
|
||||
// ============================
|
||||
// Default (Persistent) Limits
|
||||
// ============================
|
||||
|
||||
/**
|
||||
* @brief Default values used after system boot or reset.
|
||||
* These can be restored from NVS or fallback values.
|
||||
*/
|
||||
uint32_t evse_get_default_consumption_limit(void);
|
||||
void evse_set_default_consumption_limit(uint32_t value);
|
||||
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
#ifndef EVSE_MANAGER_H
|
||||
#define EVSE_MANAGER_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
@@ -30,40 +26,9 @@ void evse_manager_start(QueueHandle_t queue);
|
||||
*/
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif // EVSE_MANAGER_H
|
||||
|
||||
37
components/evse/include/evse_meter.h
Normal file
37
components/evse/include/evse_meter.h
Normal 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
|
||||
@@ -35,7 +35,7 @@ void pilot_set_level(bool level);
|
||||
/**
|
||||
* @brief Ativa o PWM do Pilot com corrente limitada
|
||||
*
|
||||
* @param amps Corrente em décimos de ampère (ex: 160 = 16A)
|
||||
* @param amps Corrente em ampères (ex: 16 = 16A)
|
||||
*/
|
||||
void pilot_set_amps(uint16_t amps);
|
||||
|
||||
53
components/evse/include/evse_session.h
Normal file
53
components/evse/include/evse_session.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* evse_session.h
|
||||
* Module to track and retrieve charging session data (current or last completed),
|
||||
* accumulating energy via periodic tick of instantaneous power.
|
||||
*/
|
||||
|
||||
#ifndef EVSE_SESSION_H
|
||||
#define EVSE_SESSION_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
/**
|
||||
* @brief Charging session statistics
|
||||
*/
|
||||
typedef struct {
|
||||
TickType_t start_tick; ///< tick when session began
|
||||
uint32_t duration_s; ///< total duration in seconds
|
||||
uint32_t energy_wh; ///< total energy consumed in Wh
|
||||
uint32_t avg_power_w; ///< average power in W
|
||||
bool is_current; ///< true if session still in progress
|
||||
} evse_session_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the session module
|
||||
*/
|
||||
void evse_session_init(void);
|
||||
|
||||
/**
|
||||
* @brief Mark the beginning of a charging session
|
||||
*/
|
||||
void evse_session_start(void);
|
||||
|
||||
/**
|
||||
* @brief Mark the end of the charging session and store it as "last session"
|
||||
*/
|
||||
void evse_session_end(void);
|
||||
|
||||
/**
|
||||
* @brief Periodic tick: must be called (e.g., each 1s) to accumulate energy from instant power
|
||||
*/
|
||||
void evse_session_tick(void);
|
||||
|
||||
/**
|
||||
* @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, false otherwise
|
||||
*/
|
||||
bool evse_session_get(evse_session_t *out);
|
||||
|
||||
#endif // EVSE_SESSION_H
|
||||
@@ -2,43 +2,97 @@
|
||||
#define EVSE_STATE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "evse_events.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// ============================
|
||||
// EVSE Pilot Signal States
|
||||
// ============================
|
||||
|
||||
// Estado do EVSE (pilot signal)
|
||||
typedef enum {
|
||||
EVSE_STATE_A,
|
||||
EVSE_STATE_B1,
|
||||
EVSE_STATE_B2,
|
||||
EVSE_STATE_C1,
|
||||
EVSE_STATE_C2,
|
||||
EVSE_STATE_D1,
|
||||
EVSE_STATE_D2,
|
||||
EVSE_STATE_E,
|
||||
EVSE_STATE_F
|
||||
EVSE_STATE_A, // EV Not Connected (12V)
|
||||
EVSE_STATE_B1, // EV Connected (9V, Not Authorized)
|
||||
EVSE_STATE_B2, // EV Connected (9V, Authorized and Ready)
|
||||
EVSE_STATE_C1, // Charging Requested (6V, Relay Off)
|
||||
EVSE_STATE_C2, // Charging Active (6V, Relay On)
|
||||
EVSE_STATE_D1, // Ventilation Required (3V, Relay Off)
|
||||
EVSE_STATE_D2, // Ventilation Active (3V, Relay On)
|
||||
EVSE_STATE_E, // Error: Pilot Short to Ground (0V)
|
||||
EVSE_STATE_F // Fault: No Pilot or EVSE Unavailable
|
||||
} evse_state_t;
|
||||
|
||||
// ============================
|
||||
// Initialization
|
||||
// ============================
|
||||
|
||||
// Funções públicas necessárias
|
||||
/**
|
||||
* @brief Initializes the EVSE state machine and default state.
|
||||
*/
|
||||
void evse_state_init(void);
|
||||
|
||||
/**
|
||||
* @brief Periodic tick for state handling (optional hook).
|
||||
*/
|
||||
void evse_state_tick(void);
|
||||
|
||||
void evse_state_set_authorized(bool authorized);
|
||||
bool evse_state_get_authorized(void);
|
||||
|
||||
// ============================
|
||||
// State Access & Control
|
||||
// ============================
|
||||
|
||||
/**
|
||||
* @brief Returns the current EVSE state.
|
||||
*/
|
||||
evse_state_t evse_get_state(void);
|
||||
|
||||
/**
|
||||
* @brief Sets the current EVSE state and emits a change event if needed.
|
||||
*/
|
||||
void evse_set_state(evse_state_t state);
|
||||
|
||||
// Converte o estado para string
|
||||
/**
|
||||
* @brief Converts the state enum into a human-readable string.
|
||||
*/
|
||||
const char* evse_state_to_str(evse_state_t state);
|
||||
|
||||
// Retorna true se o estado representa sessão ativa
|
||||
// ============================
|
||||
// State Evaluation Helpers
|
||||
// ============================
|
||||
|
||||
/**
|
||||
* @brief True if EV is in an active session (B2, C1, C2).
|
||||
*/
|
||||
bool evse_state_is_session(evse_state_t state);
|
||||
|
||||
// Retorna true se o estado representa carregamento ativo
|
||||
/**
|
||||
* @brief True if EV is actively charging (C1, C2).
|
||||
*/
|
||||
bool evse_state_is_charging(evse_state_t state);
|
||||
|
||||
// Retorna true se o estado representa veículo conectado
|
||||
/**
|
||||
* @brief True if EV is physically plugged in (B1 and beyond).
|
||||
*/
|
||||
bool evse_state_is_plugged(evse_state_t state);
|
||||
|
||||
// ============================
|
||||
// Authorization Control
|
||||
// ============================
|
||||
|
||||
/**
|
||||
* @brief Sets whether the EV is authorized to charge.
|
||||
*/
|
||||
void evse_state_set_authorized(bool authorized);
|
||||
|
||||
/**
|
||||
* @brief Gets whether the EV is currently authorized.
|
||||
*/
|
||||
bool evse_state_get_authorized(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // EVSE_STATE_H
|
||||
|
||||
16
components/evse_link/CMakeLists.txt
Executable file
16
components/evse_link/CMakeLists.txt
Executable file
@@ -0,0 +1,16 @@
|
||||
set(srcs
|
||||
"src/evse_link_master.c"
|
||||
"src/evse_link_slave.c"
|
||||
"src/evse_link_events.c"
|
||||
"src/evse_link_framing.c"
|
||||
"src/evse_link.c"
|
||||
)
|
||||
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES driver esp_timer nvs_flash
|
||||
REQUIRES config evse loadbalancer)
|
||||
|
||||
|
||||
|
||||
45
components/evse_link/include/evse_link.h
Executable file
45
components/evse_link/include/evse_link.h
Executable file
@@ -0,0 +1,45 @@
|
||||
#ifndef EVSE_LINK_H_
|
||||
#define EVSE_LINK_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Operation mode: slave or master
|
||||
typedef enum {
|
||||
EVSE_LINK_MODE_SLAVE = 0,
|
||||
EVSE_LINK_MODE_MASTER = 1
|
||||
} evse_link_mode_t;
|
||||
|
||||
// Callback invoked when a complete frame is received:
|
||||
// src: device address of sender (0–255)
|
||||
// dest: device address of receiver (0–255 or 0xFF broadcast)
|
||||
// payload: pointer to received data buffer (command + data)
|
||||
// len: length of payload (0–255)
|
||||
typedef void (*evse_link_rx_cb_t)(uint8_t src, uint8_t dest,
|
||||
const uint8_t *payload, uint8_t len);
|
||||
|
||||
// Initializes the EVSE-Link component
|
||||
void evse_link_init(void);
|
||||
|
||||
// Sends a framed payload to `dest` with length `len`.
|
||||
// The source address is automatically set from configuration.
|
||||
// Returns true on successful enqueue/transmit.
|
||||
bool evse_link_send(uint8_t dest, const uint8_t *payload, uint8_t len);
|
||||
|
||||
// Feeds a received byte into the framing parser.
|
||||
void evse_link_recv_byte(uint8_t byte);
|
||||
|
||||
// Registers a callback to receive complete frames.
|
||||
void evse_link_register_rx_cb(evse_link_rx_cb_t cb);
|
||||
|
||||
// Runtime configuration getters/setters
|
||||
void evse_link_set_mode(evse_link_mode_t mode);
|
||||
evse_link_mode_t evse_link_get_mode(void);
|
||||
|
||||
void evse_link_set_self_id(uint8_t id);
|
||||
uint8_t evse_link_get_self_id(void);
|
||||
|
||||
void evse_link_set_enabled(bool enabled);
|
||||
bool evse_link_is_enabled(void);
|
||||
|
||||
#endif // EVSE_LINK_H_
|
||||
16
components/evse_link/include/evse_link_events.h
Normal file
16
components/evse_link/include/evse_link_events.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef EVSE_LINK_EVENTS_H_
|
||||
#define EVSE_LINK_EVENTS_H_
|
||||
|
||||
#include "esp_event.h"
|
||||
|
||||
ESP_EVENT_DECLARE_BASE(EVSE_LINK_EVENTS);
|
||||
|
||||
typedef enum {
|
||||
LINK_EVENT_FRAME_RECEIVED, // qualquer frame válido
|
||||
LINK_EVENT_SLAVE_ONLINE, // heartbeat recebido primeira vez
|
||||
LINK_EVENT_SLAVE_OFFLINE, // sem heartbeat no timeout
|
||||
LINK_EVENT_MASTER_POLL_SENT, // opcional: poll enviado pelo master
|
||||
LINK_EVENT_CURRENT_LIMIT_APPLIED,
|
||||
LINK_EVENT_SLAVE_CONFIG_UPDATED // <- NOVO evento
|
||||
} evse_link_event_t;
|
||||
#endif // EVSE_LINK_EVENTS_H_
|
||||
42
components/evse_link/include/evse_link_framing.h
Normal file
42
components/evse_link/include/evse_link_framing.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef EVSE_LINK_FRAMING_H_
|
||||
#define EVSE_LINK_FRAMING_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "driver/uart.h"
|
||||
|
||||
// UART configuration
|
||||
#define UART_PORT UART_NUM_2
|
||||
#define UART_BAUDRATE 115200
|
||||
#define UART_RX_BUF_SIZE 256
|
||||
|
||||
// GPIO pin assignments for UART
|
||||
#define TX_PIN 21 // GPIO21 -> RX on other board
|
||||
#define RX_PIN 22 // GPIO22 -> TX on other board
|
||||
|
||||
// Frame delimiters
|
||||
#define MAGIC_START 0x7E
|
||||
#define MAGIC_END 0x7F
|
||||
|
||||
// Maximum payload (excluding sequence byte)
|
||||
#define EVSE_LINK_MAX_PAYLOAD 254
|
||||
|
||||
// Callback type for when a full frame is received
|
||||
typedef void (*evse_link_frame_cb_t)(uint8_t src, uint8_t dest,
|
||||
const uint8_t *payload, uint8_t len);
|
||||
|
||||
// Initialize framing module (mutex, UART driver, etc.)
|
||||
void evse_link_framing_init(void);
|
||||
|
||||
// Send a framed payload to `dest` with length `len`
|
||||
// Includes source address in the header
|
||||
bool evse_link_framing_send(uint8_t dest, uint8_t src,
|
||||
const uint8_t *payload, uint8_t len);
|
||||
|
||||
// Feed a received byte into the framing parser
|
||||
void evse_link_framing_recv_byte(uint8_t byte);
|
||||
|
||||
// Register a callback for complete frames
|
||||
void evse_link_framing_register_cb(evse_link_frame_cb_t cb);
|
||||
|
||||
#endif // EVSE_LINK_FRAMING_H_
|
||||
178
components/evse_link/src/evse_link.c
Executable file
178
components/evse_link/src/evse_link.c
Executable file
@@ -0,0 +1,178 @@
|
||||
// components/evse_link/src/evse_link.c
|
||||
|
||||
#include "evse_link.h"
|
||||
#include "evse_link_framing.h"
|
||||
#include "driver/uart.h"
|
||||
#include "nvs.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
static const char *TAG = "evse_link";
|
||||
|
||||
// NVS keys
|
||||
#define _NVS_NAMESPACE "evse_link"
|
||||
#define _NVS_MODE_KEY "mode"
|
||||
#define _NVS_ID_KEY "self_id"
|
||||
#define _NVS_ENABLED_KEY "enabled"
|
||||
|
||||
// UART parameters
|
||||
#define UART_PORT UART_NUM_2
|
||||
#define UART_RX_BUF_SIZE 256
|
||||
|
||||
// Runtime config
|
||||
static evse_link_mode_t _mode = EVSE_LINK_MODE_MASTER;
|
||||
static uint8_t _self_id = 0x01;
|
||||
static bool _enabled = false;
|
||||
|
||||
// Registered Rx callback
|
||||
static evse_link_rx_cb_t _rx_cb = NULL;
|
||||
|
||||
// Forward declarations
|
||||
extern void evse_link_master_init(void);
|
||||
extern void evse_link_slave_init(void);
|
||||
|
||||
static void framing_rx_cb(uint8_t src, uint8_t dest,
|
||||
const uint8_t *payload, uint8_t len)
|
||||
{
|
||||
ESP_LOGD(TAG, "framing_rx_cb: src=0x%02X dest=0x%02X len=%u", src, dest, len);
|
||||
if (_rx_cb)
|
||||
{
|
||||
_rx_cb(src, dest, payload, len);
|
||||
}
|
||||
}
|
||||
|
||||
// Register protocol-level Rx callback
|
||||
void evse_link_register_rx_cb(evse_link_rx_cb_t cb)
|
||||
{
|
||||
_rx_cb = cb;
|
||||
}
|
||||
|
||||
// Load config from NVS
|
||||
enum
|
||||
{
|
||||
EV_OK = ESP_OK
|
||||
};
|
||||
static void load_link_config(void)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
if (nvs_open(_NVS_NAMESPACE, NVS_READONLY, &handle) != EV_OK)
|
||||
{
|
||||
ESP_LOGW(TAG, "NVS open failed, using defaults");
|
||||
return;
|
||||
}
|
||||
uint8_t mode, id, en;
|
||||
if (nvs_get_u8(handle, _NVS_MODE_KEY, &mode) == EV_OK &&
|
||||
(mode == EVSE_LINK_MODE_MASTER || mode == EVSE_LINK_MODE_SLAVE))
|
||||
{
|
||||
_mode = (evse_link_mode_t)mode;
|
||||
}
|
||||
if (nvs_get_u8(handle, _NVS_ID_KEY, &id) == EV_OK)
|
||||
{
|
||||
_self_id = id;
|
||||
}
|
||||
if (nvs_get_u8(handle, _NVS_ENABLED_KEY, &en) == EV_OK)
|
||||
{
|
||||
_enabled = (en != 0);
|
||||
}
|
||||
nvs_close(handle);
|
||||
}
|
||||
|
||||
// Save config to NVS
|
||||
static void save_link_config(void)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
if (nvs_open(_NVS_NAMESPACE, NVS_READWRITE, &handle) == EV_OK)
|
||||
{
|
||||
nvs_set_u8(handle, _NVS_MODE_KEY, (uint8_t)_mode);
|
||||
nvs_set_u8(handle, _NVS_ID_KEY, _self_id);
|
||||
nvs_set_u8(handle, _NVS_ENABLED_KEY, _enabled ? 1 : 0);
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to save NVS");
|
||||
}
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
void evse_link_set_mode(evse_link_mode_t m)
|
||||
{
|
||||
_mode = m;
|
||||
save_link_config();
|
||||
}
|
||||
evse_link_mode_t evse_link_get_mode(void) { return _mode; }
|
||||
void evse_link_set_self_id(uint8_t id)
|
||||
{
|
||||
_self_id = id;
|
||||
save_link_config();
|
||||
}
|
||||
uint8_t evse_link_get_self_id(void) { return _self_id; }
|
||||
void evse_link_set_enabled(bool en)
|
||||
{
|
||||
_enabled = en;
|
||||
save_link_config();
|
||||
}
|
||||
bool evse_link_is_enabled(void) { return _enabled; }
|
||||
|
||||
// RX task: reads bytes from UART and feeds framing
|
||||
static void evse_link_rx_task(void *arg)
|
||||
{
|
||||
uint8_t buf[UART_RX_BUF_SIZE];
|
||||
while (true)
|
||||
{
|
||||
int len = uart_read_bytes(UART_PORT, buf, sizeof(buf), pdMS_TO_TICKS(1000));
|
||||
if (len > 0)
|
||||
{
|
||||
for (int i = 0; i < len; ++i)
|
||||
{
|
||||
evse_link_recv_byte(buf[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize EVSE-Link component
|
||||
void evse_link_init(void)
|
||||
{
|
||||
load_link_config();
|
||||
|
||||
ESP_LOGI(TAG, "Link init: mode=%c id=0x%02X enabled=%d",
|
||||
_mode == EVSE_LINK_MODE_MASTER ? 'M' : 'S',
|
||||
_self_id, _enabled);
|
||||
if (!_enabled)
|
||||
return;
|
||||
|
||||
// 1) framing layer init (sets up mutex, UART driver, etc.)
|
||||
evse_link_framing_init();
|
||||
evse_link_framing_register_cb(framing_rx_cb);
|
||||
|
||||
// 2) start RX task
|
||||
xTaskCreate(evse_link_rx_task, "evse_link_rx", 4096, NULL, 4, NULL);
|
||||
|
||||
// 3) delegate to master or slave
|
||||
if (_mode == EVSE_LINK_MODE_MASTER)
|
||||
{
|
||||
evse_link_master_init();
|
||||
}
|
||||
else
|
||||
{
|
||||
evse_link_slave_init();
|
||||
}
|
||||
}
|
||||
|
||||
// Send a frame (delegates to framing module)
|
||||
bool evse_link_send(uint8_t dest, const uint8_t *payload, uint8_t len)
|
||||
{
|
||||
if (!evse_link_is_enabled())
|
||||
return false;
|
||||
uint8_t src = evse_link_get_self_id();
|
||||
return evse_link_framing_send(dest, src, payload, len);
|
||||
}
|
||||
|
||||
// Receive byte (delegates to framing module)
|
||||
void evse_link_recv_byte(uint8_t byte)
|
||||
{
|
||||
evse_link_framing_recv_byte(byte);
|
||||
}
|
||||
4
components/evse_link/src/evse_link_events.c
Normal file
4
components/evse_link/src/evse_link_events.c
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "evse_link_events.h"
|
||||
|
||||
// Esta única linha insere o símbolo EVSE_LINK_EVENTS no binário
|
||||
ESP_EVENT_DEFINE_BASE(EVSE_LINK_EVENTS);
|
||||
168
components/evse_link/src/evse_link_framing.c
Normal file
168
components/evse_link/src/evse_link_framing.c
Normal file
@@ -0,0 +1,168 @@
|
||||
// components/evse_link_framing/src/evse_link_framing.c
|
||||
|
||||
#include "evse_link_framing.h"
|
||||
#include "driver/uart.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_log.h"
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "evse_framing";
|
||||
|
||||
static SemaphoreHandle_t tx_mutex;
|
||||
static uint8_t seq = 0;
|
||||
static evse_link_frame_cb_t rx_cb = NULL;
|
||||
|
||||
// CRC-8 (polynomial 0x07)
|
||||
static uint8_t crc8(const uint8_t *data, uint8_t len) {
|
||||
uint8_t crc = 0;
|
||||
for (uint8_t i = 0; i < len; ++i) {
|
||||
crc ^= data[i];
|
||||
for (uint8_t b = 0; b < 8; ++b) {
|
||||
crc = (crc & 0x80) ? (crc << 1) ^ 0x07 : (crc << 1);
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void evse_link_framing_init(void) {
|
||||
// Create mutex for TX
|
||||
tx_mutex = xSemaphoreCreateMutex();
|
||||
// Install UART driver
|
||||
uart_driver_install(UART_PORT, UART_RX_BUF_SIZE * 2, 0, 0, NULL, 0);
|
||||
uart_param_config(UART_PORT, &(uart_config_t){
|
||||
.baud_rate = UART_BAUDRATE,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
|
||||
});
|
||||
uart_set_pin(UART_PORT, TX_PIN, RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||
}
|
||||
|
||||
bool evse_link_framing_send(uint8_t dest, uint8_t src,
|
||||
const uint8_t *payload, uint8_t len) {
|
||||
if (len > EVSE_LINK_MAX_PAYLOAD) return false;
|
||||
if (xSemaphoreTake(tx_mutex, portMAX_DELAY) != pdTRUE) return false;
|
||||
|
||||
// Frame: START | DEST | SRC | LEN | SEQ | PAYLOAD | CRC | END
|
||||
uint8_t frame[EVSE_LINK_MAX_PAYLOAD + 7];
|
||||
int idx = 0;
|
||||
frame[idx++] = MAGIC_START;
|
||||
frame[idx++] = dest;
|
||||
frame[idx++] = src;
|
||||
frame[idx++] = len + 1; // +1 for SEQ
|
||||
frame[idx++] = seq;
|
||||
memcpy(&frame[idx], payload, len);
|
||||
idx += len;
|
||||
|
||||
// CRC covers DEST + SRC + LEN + SEQ + PAYLOAD
|
||||
uint8_t crc_input[3 + 1 + EVSE_LINK_MAX_PAYLOAD];
|
||||
memcpy(crc_input, &frame[1], 3 + 1 + len);
|
||||
frame[idx++] = crc8(crc_input, 3 + 1 + len);
|
||||
|
||||
frame[idx++] = MAGIC_END;
|
||||
|
||||
uart_write_bytes(UART_PORT, (const char *)frame, idx);
|
||||
uart_wait_tx_done(UART_PORT, pdMS_TO_TICKS(10));
|
||||
xSemaphoreGive(tx_mutex);
|
||||
|
||||
ESP_LOGD(TAG, "Sent frame dest=0x%02X src=0x%02X len=%u seq=%u",
|
||||
dest, src, len, seq);
|
||||
|
||||
seq++; // increment sequence after sending
|
||||
return true;
|
||||
}
|
||||
|
||||
void evse_link_framing_recv_byte(uint8_t b) {
|
||||
// State machine for frame parsing
|
||||
static enum {
|
||||
ST_WAIT_START,
|
||||
ST_WAIT_DEST,
|
||||
ST_WAIT_SRC,
|
||||
ST_WAIT_LEN,
|
||||
ST_WAIT_SEQ,
|
||||
ST_READING,
|
||||
ST_WAIT_CRC,
|
||||
ST_WAIT_END
|
||||
} rx_state = ST_WAIT_START;
|
||||
|
||||
static uint8_t rx_dest;
|
||||
static uint8_t rx_src;
|
||||
static uint8_t rx_len;
|
||||
static uint8_t rx_seq;
|
||||
static uint8_t rx_buf[EVSE_LINK_MAX_PAYLOAD];
|
||||
static uint8_t rx_pos;
|
||||
static uint8_t rx_crc;
|
||||
|
||||
switch (rx_state) {
|
||||
case ST_WAIT_START:
|
||||
if (b == MAGIC_START) {
|
||||
rx_state = ST_WAIT_DEST;
|
||||
}
|
||||
break;
|
||||
|
||||
case ST_WAIT_DEST:
|
||||
rx_dest = b;
|
||||
rx_state = ST_WAIT_SRC;
|
||||
break;
|
||||
|
||||
case ST_WAIT_SRC:
|
||||
rx_src = b;
|
||||
rx_state = ST_WAIT_LEN;
|
||||
break;
|
||||
|
||||
case ST_WAIT_LEN:
|
||||
rx_len = b; // includes SEQ + payload
|
||||
rx_pos = 0;
|
||||
rx_state = ST_WAIT_SEQ;
|
||||
break;
|
||||
|
||||
case ST_WAIT_SEQ:
|
||||
rx_seq = b;
|
||||
rx_state = (rx_len > 1) ? ST_READING : ST_WAIT_CRC;
|
||||
break;
|
||||
|
||||
case ST_READING:
|
||||
rx_buf[rx_pos++] = b;
|
||||
if (rx_pos >= (rx_len - 1)) { // all payload bytes read
|
||||
rx_state = ST_WAIT_CRC;
|
||||
}
|
||||
break;
|
||||
|
||||
case ST_WAIT_CRC:
|
||||
rx_crc = b;
|
||||
rx_state = ST_WAIT_END;
|
||||
break;
|
||||
|
||||
case ST_WAIT_END:
|
||||
if (b == MAGIC_END) {
|
||||
// Build data for CRC calculation
|
||||
uint8_t temp[3 + 1 + EVSE_LINK_MAX_PAYLOAD];
|
||||
int temp_len = 0;
|
||||
temp[temp_len++] = rx_dest;
|
||||
temp[temp_len++] = rx_src;
|
||||
temp[temp_len++] = rx_len;
|
||||
temp[temp_len++] = rx_seq;
|
||||
memcpy(&temp[temp_len], rx_buf, rx_len - 1);
|
||||
temp_len += rx_len - 1;
|
||||
|
||||
uint8_t expected = crc8(temp, temp_len);
|
||||
if (expected == rx_crc) {
|
||||
if (rx_cb) {
|
||||
rx_cb(rx_src, rx_dest, rx_buf, rx_len - 1);
|
||||
}
|
||||
ESP_LOGD(TAG, "Frame OK src=0x%02X dest=0x%02X len=%u seq=%u",
|
||||
rx_src, rx_dest, rx_len - 1, rx_seq);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "CRC mismatch: expected=0x%02X got=0x%02X",
|
||||
expected, rx_crc);
|
||||
}
|
||||
}
|
||||
rx_state = ST_WAIT_START;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void evse_link_framing_register_cb(evse_link_frame_cb_t cb) {
|
||||
rx_cb = cb;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user