Compare commits
14 Commits
03de00b93f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 286028b6a8 | |||
| 023644a887 | |||
| 82fa194bd8 | |||
| e6e2622a95 | |||
| 4820d9111e | |||
| 96b2ab1f57 | |||
| 0d0dc5b129 | |||
| bd587a10c0 | |||
| 84f106eee5 | |||
| a0b2e048d4 | |||
| 6f95c7ba59 | |||
| 4892718736 | |||
| 66cc449143 | |||
| 12dfa85820 |
@@ -3,4 +3,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
project(esp32-evse)
|
project(chargeflow)
|
||||||
209
README.md
209
README.md
@@ -1,85 +1,180 @@
|
|||||||

|
# ChargeFlow EVSE Firmware (ESP32, ESP-IDF 5.x)
|
||||||
|
|
||||||
J1772 EVSE firmware for ESP32 based devices.
|
Firmware for an AC EVSE (EV charger) based on ESP32 and ESP-IDF 5.x, with:
|
||||||
|
|
||||||

|
- IEC-style EVSE state machine (Control Pilot A/B/C/D)
|
||||||
[](LICENSE.md)
|
- Wi-Fi (STA + AP for local configuration)
|
||||||
|
- REST API served from SPIFFS
|
||||||
|
- Local authentication and OCPP integration
|
||||||
|
- Load balancing (master + slaves)
|
||||||
|
- Scheduler (time windows)
|
||||||
|
- Audible feedback (buzzer) and RGB LED status
|
||||||
|
- On-device ring-buffer logger
|
||||||
|
|
||||||
## Key features
|
---
|
||||||
- Hardware abstraction for device design
|
|
||||||
- Responsive web-interface
|
|
||||||
- OTA update
|
|
||||||
- Integrated energy meter
|
|
||||||
- Energy detection for relay control
|
|
||||||
- [REST](https://github.com/dzurikmiroslav/esp32-evse/wiki/Rest) API
|
|
||||||
- MQTT API
|
|
||||||
- [Modbus](https://github.com/dzurikmiroslav/esp32-evse/wiki/Modbus) (RS485, TCP)
|
|
||||||
- [Scripting](https://github.com/dzurikmiroslav/esp32-evse/wiki/Script)
|
|
||||||
- [Nextion HMI](https://github.com/dzurikmiroslav/esp32-evse/wiki/Nextion)
|
|
||||||
|
|
||||||
### Device definition method
|
## Features
|
||||||
|
|
||||||
_One firmware to rule them all._ Not really :-) one per device platform (ESP32, ESP32-S2...).
|
### Core EVSE
|
||||||
|
|
||||||
There is no need to compile the firmware for your EVSE design.
|
- EVSE manager (`evse_manager`) coordinating:
|
||||||
Source code ist not hardcoded to GPIOs or other hardware design features.
|
- Hardware layer (`evse_hardware`)
|
||||||
All code is written in ESP-IDF without additional mapping layer like Arduino.
|
- State machine (`evse_state`)
|
||||||
|
- Error handling (`evse_error`)
|
||||||
|
- Energy metering (`evse_meter` / `meter_manager`)
|
||||||
|
- Session tracking (`evse_session`)
|
||||||
|
- Runs a periodic tick (`evse_manager_tick()`) in its own FreeRTOS task.
|
||||||
|
- Supports multiple auth modes (OPEN / RFID / OCPP), with scheduling and load-balancer aware logic.
|
||||||
|
|
||||||
All configuration is written outside firmware in configuration file named _board.cfg_ on dedicated partition.
|
### Networking & REST
|
||||||
For example, on following scheme is minimal EVSE circuit with ESP32 devkit.
|
|
||||||
|
|
||||||

|
- Wi-Fi:
|
||||||
|
- Station mode for normal operation.
|
||||||
|
- Access point mode for local configuration, enabled by a physical button.
|
||||||
|
- REST server (`rest_main`) serving from `/data` SPIFFS mount:
|
||||||
|
- For configuration, status, logs, etc. (exact endpoints depend on your REST implementation).
|
||||||
|
|
||||||
For this circuit there is _board.cfg_, for more information's see [Wiki](https://github.com/dzurikmiroslav/esp32-evse/wiki/Board-config).
|
### Button & User Input
|
||||||
|
|
||||||
|
- One physical button (configured via `board_config`):
|
||||||
|
- **Short press** → Starts Wi-Fi AP mode for configuration.
|
||||||
|
- **Long press (~30s)** → Erases NVS and reboots (factory-like reset).
|
||||||
|
- Robust handling:
|
||||||
|
- ISR with software debounce and spinlock.
|
||||||
|
- Dedicated `user_input_task` that receives button press/release notifications via `xTaskNotify`.
|
||||||
|
|
||||||
```bash
|
### Storage
|
||||||
#Device name
|
|
||||||
DEVICE_NAME=ESP32 minimal EVSE
|
|
||||||
#Button
|
|
||||||
BUTTON_WIFI_GPIO=0
|
|
||||||
#Pilot
|
|
||||||
PILOT_PWM_GPIO=33
|
|
||||||
PILOT_ADC_CHANNEL=7
|
|
||||||
PILOT_DOWN_THRESHOLD_12=2410
|
|
||||||
PILOT_DOWN_THRESHOLD_9=2104
|
|
||||||
PILOT_DOWN_THRESHOLD_6=1797
|
|
||||||
PILOT_DOWN_THRESHOLD_3=1491
|
|
||||||
PILOT_DOWN_THRESHOLD_N12=265
|
|
||||||
#AC relay
|
|
||||||
AC_RELAY_GPIO=32
|
|
||||||
```
|
|
||||||
|
|
||||||
### Web interface
|
- SPIFFS used for:
|
||||||
|
- `/cfg` partition: persistent configuration.
|
||||||
|
- `/data` partition: web assets, runtime data, logs, etc.
|
||||||
|
- Two separate mounts:
|
||||||
|
- `cfg_conf` → `/cfg` (label: `cfg`)
|
||||||
|
- `data_conf` → `/data` (label: `data`)
|
||||||
|
|
||||||
Fully responsive web interface is accessible local network IP address on port 80.
|
### LED Subsystem
|
||||||
|
|
||||||
Dashboard page
|
- RGB LED driven by LEDC:
|
||||||
|
- `ledc_driver` abstracts LEDC timer + channels.
|
||||||
|
- `led` module maps EVSE state & sessions to colors/patterns.
|
||||||
|
- LED patterns per EVSE state:
|
||||||
|
- **IDLE** → Green solid.
|
||||||
|
- **WAITING** (vehicle plugged, not charging) → Blue slow blink.
|
||||||
|
- **CHARGING** → Blue “breathing” effect.
|
||||||
|
- **FAULT** → Red fast blink.
|
||||||
|
- Session effects:
|
||||||
|
- Distinct visual patterns when a session starts/finishes.
|
||||||
|
- Uses a one-shot timer and a dedicated effect state machine.
|
||||||
|
|
||||||

|
### Buzzer
|
||||||
|
|
||||||
Settings page
|
- Buzzer with multiple patterns (`buzzer` + `buzzer_events`):
|
||||||
|
- Plugged/unplugged, card read/denied, AP start, charging, fault, etc.
|
||||||
|
- Supported modes:
|
||||||
|
- Active buzzer (ON/OFF).
|
||||||
|
- Passive buzzer with LEDC PWM (frequency & duty configurable).
|
||||||
|
- Features:
|
||||||
|
- Central queue + dedicated `buzzer_task`.
|
||||||
|
- Quiet hours support (optionally suppress non-critical sounds at night).
|
||||||
|
- Anti-spam mechanism to avoid excessively frequent beeps.
|
||||||
|
- Integrated with:
|
||||||
|
- EVSE events (state changes & faults)
|
||||||
|
- Auth events (RFID card success/denied/added)
|
||||||
|
- Network events (AP/STA up)
|
||||||
|
|
||||||

|
### Load Balancer
|
||||||
|
|
||||||
Mobile dashboard page
|
- `loadbalancer` component:
|
||||||
|
- Monitors GRID meter and EVSE meter via `meter_events`.
|
||||||
|
- Supports one master + up to 255 slaves (connectors array).
|
||||||
|
- Fair distribution of current with:
|
||||||
|
- Headroom calculation based on grid limit and measured current.
|
||||||
|
- Min current guarantees (e.g. 6 A) using a “water-filling” algorithm.
|
||||||
|
- Session-age based priority (oldest sessions first).
|
||||||
|
- Per-connector hysteresis and LB suspension/resume flags.
|
||||||
|
- Publishes limits via `LOADBALANCER_EVENTS`:
|
||||||
|
- `LOADBALANCER_EVENT_MASTER_CURRENT_LIMIT`
|
||||||
|
- `LOADBALANCER_EVENT_SLAVE_CURRENT_LIMIT`
|
||||||
|
- Fail-safe behavior:
|
||||||
|
- If GRID meter data times out, clamps connectors to minimum safe current instead of ramping up.
|
||||||
|
|
||||||

|
### Scheduler
|
||||||
|
|
||||||
## Hardware
|
- Scheduler component (`scheduler`) emits `SCHED_EVENTS` with `allowed_now` flag:
|
||||||
|
- EVSE manager revokes authorization when the window closes.
|
||||||
|
- In OPEN mode, automatic re-authorization only happens when scheduler allows.
|
||||||
|
|
||||||
### ESP32DevkitC
|
### OCPP
|
||||||
|
|
||||||
Dev board with basic functionality, single phase energy meter, RS485. One side pcb, for DIY makers easy to make at home conditions ;-)
|
- `ocpp` module integration:
|
||||||
|
- Listens to OCPP events (`OCPP_EVENTS`).
|
||||||
|
- Handles:
|
||||||
|
- RemoteStart/Stop
|
||||||
|
- Authorization results
|
||||||
|
- ChangeAvailability (operative/inoperative) → mapped into local `enabled` config.
|
||||||
|
- EVSE manager mediates OCPP decisions with scheduler + load balancer.
|
||||||
|
|
||||||
[EasyEDA project](https://oshwlab.com/dzurik.miroslav/esp32-devkit-evse)
|
### Logger
|
||||||
|
|
||||||

|
- `logger` + `output_buffer` components:
|
||||||
|
- Central log sink with ring buffer in RAM.
|
||||||
|
- Thread-safe via FreeRTOS mutex.
|
||||||
|
- Integrated with ESP log system via `esp_log_set_vprintf(logger_vprintf);`
|
||||||
|
- Optionally mirrors to UART (controlled via `CONFIG_ESP_CONSOLE_UART`).
|
||||||
|
- Simple reader API:
|
||||||
|
- Iterate entries using an index.
|
||||||
|
- Handy for exposing logs over REST/Web UI.
|
||||||
|
|
||||||
### ESP32-S2 DIY ALPHA
|
---
|
||||||
|
|
||||||
ESP32-S2 based EVSE with advanced functionality, three phase energy meter, RS485, UART, 1WIRE, RCM, socket lock.
|
## Project Structure (Relevant Parts)
|
||||||
|
|
||||||
[EasyEDA project](https://oshwlab.com/dzurik.miroslav/esp32s2-diy-evse)
|
Approximate layout (names may vary slightly in your repo):
|
||||||
|
|
||||||

|
```text
|
||||||
|
main/
|
||||||
|
main.c # System entrypoint, button setup, module init
|
||||||
|
|
||||||
|
components/
|
||||||
|
evse/
|
||||||
|
evse_manager.c/.h # High-level EVSE orchestration
|
||||||
|
evse_state.c/.h # State machine & events
|
||||||
|
evse_error.c/.h # Error handling
|
||||||
|
evse_hardware.c/.h # Hardware abstraction
|
||||||
|
evse_session.c/.h # Session metrics
|
||||||
|
|
||||||
|
loadbalancer/
|
||||||
|
src/
|
||||||
|
loadbalancer.c
|
||||||
|
loadbalancer_events.c
|
||||||
|
input_filter.c
|
||||||
|
include/
|
||||||
|
loadbalancer.h
|
||||||
|
loadbalancer_events.h
|
||||||
|
input_filter.h
|
||||||
|
|
||||||
|
buzzer/
|
||||||
|
src/
|
||||||
|
buzzer.c
|
||||||
|
buzzer_events.c
|
||||||
|
include/
|
||||||
|
buzzer.h
|
||||||
|
buzzer_events.h
|
||||||
|
|
||||||
|
led/
|
||||||
|
src/
|
||||||
|
led.c
|
||||||
|
ledc_driver.c
|
||||||
|
include/
|
||||||
|
led.h
|
||||||
|
ledc_driver.h
|
||||||
|
|
||||||
|
logger/
|
||||||
|
src/
|
||||||
|
logger.c
|
||||||
|
output_buffer.c
|
||||||
|
include/
|
||||||
|
logger.h
|
||||||
|
output_buffer.h
|
||||||
|
|
||||||
|
# ... other modules: auth, ocpp, scheduler, meter_manager, evse_link, etc.
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
DEVICE_NAME=Custom EVSE
|
DEVICE_NAME=ChargeFlow
|
||||||
|
|
||||||
#LEDs
|
#LEDs
|
||||||
LED_CHARGING=n
|
led_blue=n
|
||||||
LED_CHARGING_GPIO=
|
led_blue_GPIO=
|
||||||
LED_ERROR=n
|
led_red=n
|
||||||
LED_ERROR_GPIO=
|
led_red_GPIO=
|
||||||
LED_STOP=n
|
led_green=n
|
||||||
LED_STOP_GPIO=
|
led_green_GPIO=
|
||||||
|
|
||||||
#Button
|
#Button
|
||||||
BUTTON_WIFI_GPIO=32
|
BUTTON_WIFI_GPIO=32
|
||||||
@@ -35,32 +35,3 @@ SOCKET_LOCK=n
|
|||||||
SOCKET_LOCK_A_GPIO=
|
SOCKET_LOCK_A_GPIO=
|
||||||
SOCKET_LOCK_B_GPIO=
|
SOCKET_LOCK_B_GPIO=
|
||||||
SOCKET_LOCK_DETECTION_GPIO=
|
SOCKET_LOCK_DETECTION_GPIO=
|
||||||
|
|
||||||
#Energy meter (none | cur | cur_vlt)
|
|
||||||
ENERGY_METER=none
|
|
||||||
ENERGY_METER_THREE_PHASES=n
|
|
||||||
|
|
||||||
#Energy meter internal (ENERGY_METER=cur | cur_vlt)
|
|
||||||
ENERGY_METER_L1_CUR_ADC_CHANNEL=
|
|
||||||
ENERGY_METER_L2_CUR_ADC_CHANNEL=
|
|
||||||
ENERGY_METER_L3_CUR_ADC_CHANNEL=
|
|
||||||
ENERGY_METER_CUR_SCALE=
|
|
||||||
|
|
||||||
#Energy meter internal (ENERGY_METER=cur_vlt)
|
|
||||||
ENERGY_METER_L1_VLT_ADC_CHANNEL=
|
|
||||||
ENERGY_METER_L2_VLT_ADC_CHANNEL=
|
|
||||||
ENERGY_METER_L3_VLT_ADC_CHANNEL=
|
|
||||||
ENERGY_METER_VLT_SCALE=
|
|
||||||
|
|
||||||
#Serial (SERIAL_X=none|uart|rs485)
|
|
||||||
SERIAL_1=none
|
|
||||||
SERIAL_1_NAME=UART 1
|
|
||||||
SERIAL_1_RXD_GPIO=
|
|
||||||
SERIAL_1_TXD_GPIO=
|
|
||||||
SERIAL_1_RTS_GPIO=
|
|
||||||
|
|
||||||
SERIAL_2=none
|
|
||||||
SERIAL_2_NAME=UART 2
|
|
||||||
SERIAL_2_RXD_GPIO=
|
|
||||||
SERIAL_2_TXD_GPIO=
|
|
||||||
SERIAL_2_RTS_GPIO=
|
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
DEVICE_NAME=Plixin Evse
|
DEVICE_NAME=ChargeFlow
|
||||||
|
|
||||||
#LEDs
|
#LEDs
|
||||||
LED_CHARGING=y
|
led_blue=y
|
||||||
LED_CHARGING_GPIO=14
|
led_blue_GPIO=14
|
||||||
LED_ERROR=y
|
led_red=y
|
||||||
LED_ERROR_GPIO=13
|
led_red_GPIO=26
|
||||||
LED_STOP=y
|
led_green=y
|
||||||
LED_STOP_GPIO=12
|
led_green_GPIO=12
|
||||||
|
|
||||||
#BUZZER
|
#BUZZER
|
||||||
BUZZER=y
|
BUZZER=y
|
||||||
BUZZER_GPIO=21
|
BUZZER_GPIO=27
|
||||||
|
|
||||||
#Button
|
#Button
|
||||||
BUTTON_WIFI_GPIO=32
|
BUTTON_WIFI_GPIO=32
|
||||||
@@ -42,84 +42,3 @@ SOCKET_LOCK=n
|
|||||||
SOCKET_LOCK_A_GPIO=
|
SOCKET_LOCK_A_GPIO=
|
||||||
SOCKET_LOCK_B_GPIO=
|
SOCKET_LOCK_B_GPIO=
|
||||||
SOCKET_LOCK_DETECTION_GPIO=
|
SOCKET_LOCK_DETECTION_GPIO=
|
||||||
|
|
||||||
#Energy meter (none | cur | cur_vlt)
|
|
||||||
ENERGY_METER=none
|
|
||||||
ENERGY_METER_THREE_PHASES=n
|
|
||||||
|
|
||||||
#Energy meter internal (ENERGY_METER=cur | cur_vlt)
|
|
||||||
ENERGY_METER_L1_CUR_ADC_CHANNEL=7
|
|
||||||
ENERGY_METER_L2_CUR_ADC_CHANNEL=
|
|
||||||
ENERGY_METER_L3_CUR_ADC_CHANNEL=
|
|
||||||
ENERGY_METER_CUR_SCALE=0.090909091
|
|
||||||
|
|
||||||
#Energy meter internal (ENERGY_METER=cur_vlt)
|
|
||||||
ENERGY_METER_L1_VLT_ADC_CHANNEL=
|
|
||||||
ENERGY_METER_L2_VLT_ADC_CHANNEL=
|
|
||||||
ENERGY_METER_L3_VLT_ADC_CHANNEL=
|
|
||||||
ENERGY_METER_VLT_SCALE=0.47
|
|
||||||
|
|
||||||
#AUX
|
|
||||||
AUX_IN_1=n
|
|
||||||
AUX_IN_1_NAME=
|
|
||||||
AUX_IN_1_GPIO=
|
|
||||||
|
|
||||||
AUX_IN_2=n
|
|
||||||
AUX_IN_2_NAME=
|
|
||||||
AUX_IN_2_GPIO=
|
|
||||||
|
|
||||||
AUX_IN_3=n
|
|
||||||
AUX_IN_3_NAME=
|
|
||||||
AUX_IN_3_GPIO=
|
|
||||||
|
|
||||||
AUX_IN_4=n
|
|
||||||
AUX_IN_4_NAME=
|
|
||||||
AUX_IN_4_GPIO=
|
|
||||||
|
|
||||||
AUX_OUT_1=n
|
|
||||||
AUX_OUT_1_NAME=
|
|
||||||
AUX_OUT_1_GPIO=
|
|
||||||
|
|
||||||
AUX_OUT_2=n
|
|
||||||
AUX_OUT_2_NAME=
|
|
||||||
AUX_OUT_2_GPIO=
|
|
||||||
|
|
||||||
AUX_OUT_3=n
|
|
||||||
AUX_OUT_3_NAME=
|
|
||||||
AUX_OUT_3_GPIO=
|
|
||||||
|
|
||||||
AUX_OUT_4=n
|
|
||||||
AUX_OUT_4_NAME=
|
|
||||||
AUX_OUT_4_GPIO=
|
|
||||||
|
|
||||||
AUX_AIN_1=n
|
|
||||||
AUX_AIN_1_NAME=
|
|
||||||
AUX_AIN_1_ADC_CHANNEL=
|
|
||||||
|
|
||||||
AUX_AIN_2=n
|
|
||||||
AUX_AIN_2_NAME=
|
|
||||||
AUX_AIN_2_ADC_CHANNEL=
|
|
||||||
|
|
||||||
#Serial (SERIAL_X=none|uart|rs485)
|
|
||||||
SERIAL_1=none
|
|
||||||
SERIAL_1_NAME=UART via USB
|
|
||||||
SERIAL_1_RXD_GPIO=
|
|
||||||
SERIAL_1_TXD_GPIO=
|
|
||||||
SERIAL_1_RTS_GPIO=
|
|
||||||
|
|
||||||
SERIAL_2=none
|
|
||||||
SERIAL_2_NAME=RS485
|
|
||||||
SERIAL_2_RXD_GPIO=
|
|
||||||
SERIAL_2_TXD_GPIO=
|
|
||||||
SERIAL_2_RTS_GPIO=
|
|
||||||
|
|
||||||
SERIAL_3=none
|
|
||||||
SERIAL_3_NAME=UART
|
|
||||||
SERIAL_3_RXD_GPIO=
|
|
||||||
SERIAL_3_TXD_GPIO=
|
|
||||||
SERIAL_3_RTS_GPIO=
|
|
||||||
|
|
||||||
#Onewire devices
|
|
||||||
ONEWIRE=n
|
|
||||||
ONEWIRE_GPIO=
|
|
||||||
ONEWIRE_TEMP_SENSOR=n
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
DEVICE_NAME=ESP32-S2-DA EVSE
|
DEVICE_NAME=ChargeFlow
|
||||||
|
|
||||||
#LEDs
|
#LEDs
|
||||||
LED_CHARGING=y
|
led_blue=y
|
||||||
LED_CHARGING_GPIO=36
|
led_blue_GPIO=36
|
||||||
LED_ERROR=y
|
led_red=y
|
||||||
LED_ERROR_GPIO=37
|
led_red_GPIO=37
|
||||||
LED_STOP=y
|
led_green=y
|
||||||
LED_STOP_GPIO=35
|
led_green_GPIO=35
|
||||||
|
|
||||||
#Button
|
#Button
|
||||||
BUTTON_WIFI_GPIO=32
|
BUTTON_WIFI_GPIO=32
|
||||||
@@ -42,78 +42,3 @@ SOCKET_LOCK_MIN_BREAK_TIME=1000
|
|||||||
RCM=n
|
RCM=n
|
||||||
RCM_GPIO=41
|
RCM_GPIO=41
|
||||||
RCM_TEST_GPIO=26
|
RCM_TEST_GPIO=26
|
||||||
|
|
||||||
#Energy meter (none | cur | cur_vlt)
|
|
||||||
ENERGY_METER=cur_vlt
|
|
||||||
ENERGY_METER_THREE_PHASES=y
|
|
||||||
|
|
||||||
#Energy meter internal (ENERGY_METER=cur | cur_vlt)
|
|
||||||
ENERGY_METER_L1_CUR_ADC_CHANNEL=4
|
|
||||||
ENERGY_METER_L2_CUR_ADC_CHANNEL=5
|
|
||||||
ENERGY_METER_L3_CUR_ADC_CHANNEL=6
|
|
||||||
ENERGY_METER_CUR_SCALE=0.090909091
|
|
||||||
|
|
||||||
#Energy meter internal (ENERGY_METER=cur_vlt)
|
|
||||||
ENERGY_METER_L1_VLT_ADC_CHANNEL=7
|
|
||||||
ENERGY_METER_L2_VLT_ADC_CHANNEL=8
|
|
||||||
ENERGY_METER_L3_VLT_ADC_CHANNEL=9
|
|
||||||
ENERGY_METER_VLT_SCALE=0.47
|
|
||||||
|
|
||||||
#AUX
|
|
||||||
AUX_IN_1=n
|
|
||||||
AUX_IN_1_NAME=
|
|
||||||
AUX_IN_1_GPIO=
|
|
||||||
|
|
||||||
AUX_IN_2=n
|
|
||||||
AUX_IN_2_NAME=
|
|
||||||
AUX_IN_2_GPIO=
|
|
||||||
|
|
||||||
AUX_IN_3=n
|
|
||||||
AUX_IN_3_NAME=
|
|
||||||
AUX_IN_3_GPIO=
|
|
||||||
|
|
||||||
AUX_IN_4=n
|
|
||||||
AUX_IN_4_NAME=
|
|
||||||
AUX_IN_4_GPIO=
|
|
||||||
|
|
||||||
AUX_OUT_1=n
|
|
||||||
AUX_OUT_1_NAME=
|
|
||||||
AUX_OUT_1_GPIO=
|
|
||||||
|
|
||||||
AUX_OUT_2=n
|
|
||||||
AUX_OUT_2_NAME=
|
|
||||||
AUX_OUT_2_GPIO=
|
|
||||||
|
|
||||||
AUX_OUT_3=n
|
|
||||||
AUX_OUT_3_NAME=
|
|
||||||
AUX_OUT_3_GPIO=
|
|
||||||
|
|
||||||
AUX_OUT_4=n
|
|
||||||
AUX_OUT_4_NAME=
|
|
||||||
AUX_OUT_4_GPIO=
|
|
||||||
|
|
||||||
AUX_AIN_1=n
|
|
||||||
AUX_AIN_1_NAME=
|
|
||||||
AUX_AIN_1_ADC_CHANNEL=
|
|
||||||
|
|
||||||
AUX_AIN_2=n
|
|
||||||
AUX_AIN_2_NAME=
|
|
||||||
AUX_AIN_2_ADC_CHANNEL=
|
|
||||||
|
|
||||||
#Serial (SERIAL_X=none|uart|rs485)
|
|
||||||
SERIAL_1=none
|
|
||||||
SERIAL_1_NAME=UART
|
|
||||||
SERIAL_1_RXD_GPIO=
|
|
||||||
SERIAL_1_TXD_GPIO=
|
|
||||||
SERIAL_1_RTS_GPIO=
|
|
||||||
|
|
||||||
SERIAL_2=none
|
|
||||||
SERIAL_2_NAME=RS-485
|
|
||||||
SERIAL_2_RXD_GPIO=
|
|
||||||
SERIAL_2_TXD_GPIO=
|
|
||||||
SERIAL_2_RTS_GPIO=
|
|
||||||
|
|
||||||
#Onewire devices
|
|
||||||
ONEWIRE=n
|
|
||||||
ONEWIRE_GPIO=
|
|
||||||
ONEWIRE_TEMP_SENSOR=n
|
|
||||||
@@ -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,7 +1,7 @@
|
|||||||
set(srcs "src/auth.c" "src/wiegand.c" "src/wiegand_reader.c")
|
set(srcs "src/auth_types.c" "src/auth.c" "src/wiegand.c" "src/wiegand_reader.c" "src/auth_events.c")
|
||||||
|
|
||||||
idf_component_register(SRCS "${srcs}"
|
idf_component_register(SRCS "${srcs}"
|
||||||
INCLUDE_DIRS "include"
|
INCLUDE_DIRS "include"
|
||||||
PRIV_INCLUDE_DIRS "src"
|
PRIV_INCLUDE_DIRS "src"
|
||||||
PRIV_REQUIRES nvs_flash driver esp_timer
|
PRIV_REQUIRES driver esp_timer
|
||||||
REQUIRES esp_event esp_idf_lib_helpers evse ocpp)
|
REQUIRES esp_event evse ocpp evse_link storage_service)
|
||||||
@@ -1,72 +1,32 @@
|
|||||||
#ifndef AUTH_H
|
#pragma once
|
||||||
#define AUTH_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <freertos/FreeRTOS.h>
|
#include "auth_types.h" // enum + MAX LEN para API
|
||||||
#include <freertos/queue.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Tamanho máximo da tag RFID (incluindo '\0')
|
/* Evento auxiliar legado/útil (resultado local) */
|
||||||
#define AUTH_TAG_MAX_LEN 20
|
|
||||||
|
|
||||||
// Evento enviado ao EVSE Manager após leitura de tag
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char tag[AUTH_TAG_MAX_LEN]; // Tag lida
|
char tag[AUTH_TAG_MAX_LEN];
|
||||||
bool authorized; // true se tag for válida
|
bool authorized;
|
||||||
} auth_event_t;
|
} auth_event_t;
|
||||||
|
|
||||||
/**
|
void auth_init(void);
|
||||||
* @brief Inicializa o sistema de autenticação.
|
void auth_set_mode(auth_mode_t mode);
|
||||||
* Carrega configuração e inicia o leitor Wiegand (wg26).
|
auth_mode_t auth_get_mode(void);
|
||||||
*/
|
|
||||||
void auth_init(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Define a fila de eventos que receberá auth_event_t.
|
|
||||||
*/
|
|
||||||
void auth_set_event_queue(QueueHandle_t queue);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Ativa ou desativa o módulo de autenticação (RFID).
|
|
||||||
* Essa configuração é salva em NVS.
|
|
||||||
*/
|
|
||||||
void auth_set_enabled(bool value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Verifica se a autenticação está habilitada.
|
|
||||||
*/
|
|
||||||
bool auth_is_enabled(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Adiciona uma nova tag válida.
|
|
||||||
*/
|
|
||||||
bool auth_add_tag(const char *tag);
|
bool auth_add_tag(const char *tag);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Remove uma tag previamente cadastrada.
|
|
||||||
*/
|
|
||||||
bool auth_remove_tag(const char *tag);
|
bool auth_remove_tag(const char *tag);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Verifica se uma tag está cadastrada.
|
|
||||||
*/
|
|
||||||
bool auth_tag_exists(const char *tag);
|
bool auth_tag_exists(const char *tag);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Lista as tags registradas (via ESP_LOG).
|
|
||||||
*/
|
|
||||||
void auth_list_tags(void);
|
void auth_list_tags(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Processa uma tag lida (usado pelo leitor Wiegand).
|
|
||||||
*/
|
|
||||||
void auth_process_tag(const char *tag);
|
void auth_process_tag(const char *tag);
|
||||||
|
void auth_wait_for_tag_registration(void);
|
||||||
|
|
||||||
|
int auth_get_tag_count(void);
|
||||||
|
const char *auth_get_tag_by_index(int index);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // AUTH_H
|
|
||||||
|
|||||||
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,151 +1,449 @@
|
|||||||
/*
|
|
||||||
* auth.c
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "auth.h"
|
#include "auth.h"
|
||||||
|
#include "auth_events.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/task.h>
|
|
||||||
#include <freertos/queue.h>
|
#include <freertos/queue.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
|
#include <esp_err.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "wiegand_reader.h"
|
#include "wiegand_reader.h"
|
||||||
|
|
||||||
#include "nvs_flash.h"
|
#include "storage_service.h"
|
||||||
#include "nvs.h"
|
|
||||||
|
#include "evse_link.h"
|
||||||
|
#include "evse_link_events.h"
|
||||||
|
|
||||||
#define MAX_TAGS 50
|
#define MAX_TAGS 50
|
||||||
|
|
||||||
static const char *TAG = "Auth";
|
static const char *TAG = "Auth";
|
||||||
static bool enabled = true;
|
|
||||||
|
/* ===== Estado ===== */
|
||||||
|
static auth_mode_t s_mode = AUTH_MODE_OPEN;
|
||||||
|
static bool waiting_for_registration = false;
|
||||||
static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN];
|
static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN];
|
||||||
static int tag_count = 0;
|
static int tag_count = 0;
|
||||||
|
static uint32_t s_next_req_id = 1;
|
||||||
|
static bool s_wiegand_started = false;
|
||||||
|
|
||||||
// Fila de eventos enviada ao EVSE Manager
|
/* ===== Storage keys ===== */
|
||||||
static QueueHandle_t event_queue = NULL;
|
#define NVS_NAMESPACE "auth"
|
||||||
|
#define NVS_TAG_PREFIX "tag_"
|
||||||
|
#define NVS_TAG_COUNT_KEY "count"
|
||||||
|
#define NVS_MODE_KEY "mode" // uint8_t
|
||||||
|
|
||||||
// ===========================
|
// timeout para operações sync do storage
|
||||||
// Persistência em NVS
|
#define STORAGE_TO pdMS_TO_TICKS(2000)
|
||||||
// ===========================
|
|
||||||
|
|
||||||
static void load_auth_config(void) {
|
/* =========================
|
||||||
nvs_handle_t handle;
|
* Helpers
|
||||||
esp_err_t err = nvs_open("auth", NVS_READONLY, &handle);
|
* ========================= */
|
||||||
if (err == ESP_OK) {
|
static bool is_tag_valid(const char *tag)
|
||||||
uint8_t val;
|
{
|
||||||
if (nvs_get_u8(handle, "enabled", &val) == ESP_OK) {
|
for (int i = 0; i < tag_count; i++)
|
||||||
enabled = val;
|
{
|
||||||
ESP_LOGI(TAG, "Loaded auth enabled = %d", enabled);
|
if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0)
|
||||||
}
|
|
||||||
nvs_close(handle);
|
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "No stored auth config found. Using default.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void save_auth_config(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);
|
|
||||||
} else {
|
|
||||||
ESP_LOGE(TAG, "Failed to save auth config.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===========================
|
|
||||||
// Internos
|
|
||||||
// ===========================
|
|
||||||
|
|
||||||
static bool is_tag_valid(const char *tag) {
|
|
||||||
for (int i = 0; i < tag_count; i++) {
|
|
||||||
if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================
|
/* =========================
|
||||||
// API pública
|
* Storage Persistence (tags)
|
||||||
// ===========================
|
* ========================= */
|
||||||
|
static void load_tags_from_storage(void)
|
||||||
|
{
|
||||||
|
uint8_t count = 0;
|
||||||
|
esp_err_t err = storage_get_u8_sync(NVS_NAMESPACE, NVS_TAG_COUNT_KEY, &count, STORAGE_TO);
|
||||||
|
if (err == ESP_ERR_NOT_FOUND)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "No stored tags (count not found)");
|
||||||
|
tag_count = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Failed to read tag count (%s)", esp_err_to_name(err));
|
||||||
|
tag_count = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void auth_set_event_queue(QueueHandle_t queue) {
|
tag_count = 0;
|
||||||
event_queue = queue;
|
for (int i = 0; i < (int)count && i < MAX_TAGS; i++)
|
||||||
|
{
|
||||||
|
char key[16];
|
||||||
|
char tag_buf[AUTH_TAG_MAX_LEN] = {0};
|
||||||
|
|
||||||
|
snprintf(key, sizeof(key), "%s%d", NVS_TAG_PREFIX, i);
|
||||||
|
|
||||||
|
err = storage_get_str_sync(NVS_NAMESPACE, key, tag_buf, sizeof(tag_buf), STORAGE_TO);
|
||||||
|
if (err == ESP_OK)
|
||||||
|
{
|
||||||
|
if (tag_buf[0] != '\0')
|
||||||
|
{
|
||||||
|
strncpy(valid_tags[tag_count], tag_buf, AUTH_TAG_MAX_LEN - 1);
|
||||||
|
valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||||
|
tag_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (err == ESP_ERR_NOT_FOUND)
|
||||||
|
{
|
||||||
|
// pode acontecer se count estiver desfasado; ignora
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Failed to load tag %d (%s)", i, esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Loaded %d tags from storage", tag_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void auth_set_enabled(bool value) {
|
static void save_tags_to_storage(void)
|
||||||
enabled = value;
|
{
|
||||||
save_auth_config();
|
// ler count antigo (para apagar keys antigas se removemos tags)
|
||||||
ESP_LOGI(TAG, "Auth %s", enabled ? "ENABLED" : "DISABLED");
|
uint8_t old_count = 0;
|
||||||
|
esp_err_t err = storage_get_u8_sync(NVS_NAMESPACE, NVS_TAG_COUNT_KEY, &old_count, STORAGE_TO);
|
||||||
|
if (err == ESP_ERR_NOT_FOUND)
|
||||||
|
old_count = 0;
|
||||||
|
|
||||||
|
// grava count + tags
|
||||||
|
(void)storage_set_u8_async(NVS_NAMESPACE, NVS_TAG_COUNT_KEY, (uint8_t)tag_count);
|
||||||
|
|
||||||
|
for (int i = 0; i < tag_count; i++)
|
||||||
|
{
|
||||||
|
char key[16];
|
||||||
|
snprintf(key, sizeof(key), "%s%d", NVS_TAG_PREFIX, i);
|
||||||
|
(void)storage_set_str_async(NVS_NAMESPACE, key, valid_tags[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// se removemos tags: apagar chaves antigas
|
||||||
|
if (old_count > (uint8_t)tag_count)
|
||||||
|
{
|
||||||
|
for (int i = tag_count; i < (int)old_count && i < MAX_TAGS; i++)
|
||||||
|
{
|
||||||
|
char key[16];
|
||||||
|
snprintf(key, sizeof(key), "%s%d", NVS_TAG_PREFIX, i);
|
||||||
|
(void)storage_erase_key_async(NVS_NAMESPACE, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// opcional: forçar commit “já”
|
||||||
|
(void)storage_flush_async();
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Tags saved to storage (%d tags)", tag_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool auth_is_enabled(void) {
|
/* =========================
|
||||||
return enabled;
|
* Storage Persistence (mode)
|
||||||
|
* ========================= */
|
||||||
|
static void load_mode_from_storage(void)
|
||||||
|
{
|
||||||
|
uint8_t u = (uint8_t)AUTH_MODE_OPEN;
|
||||||
|
esp_err_t err = storage_get_u8_sync(NVS_NAMESPACE, NVS_MODE_KEY, &u, STORAGE_TO);
|
||||||
|
|
||||||
|
if (err == ESP_OK)
|
||||||
|
{
|
||||||
|
if (u <= (uint8_t)AUTH_MODE_OCPP_RFID)
|
||||||
|
s_mode = (auth_mode_t)u;
|
||||||
|
else
|
||||||
|
s_mode = AUTH_MODE_OPEN;
|
||||||
|
}
|
||||||
|
else if (err == ESP_ERR_NOT_FOUND)
|
||||||
|
{
|
||||||
|
s_mode = AUTH_MODE_OPEN;
|
||||||
|
ESP_LOGD(TAG, "No stored mode -> default OPEN");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_mode = AUTH_MODE_OPEN;
|
||||||
|
ESP_LOGW(TAG, "Failed to read mode (%s) -> default OPEN", esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Loaded mode = %d (%s)", (int)s_mode, auth_mode_to_str(s_mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool auth_add_tag(const char *tag) {
|
static void save_mode_to_storage(auth_mode_t mode)
|
||||||
if (tag_count >= MAX_TAGS) return false;
|
{
|
||||||
if (!tag || strlen(tag) >= AUTH_TAG_MAX_LEN) return false;
|
(void)storage_set_u8_async(NVS_NAMESPACE, NVS_MODE_KEY, (uint8_t)mode);
|
||||||
if (is_tag_valid(tag)) return true;
|
(void)storage_flush_async(); // opcional: commit mais rápido
|
||||||
|
ESP_LOGD(TAG, "Saved mode = %d (%s)", (int)mode, auth_mode_to_str(mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
* Bridge: EVSE-Link -> AUTH (remote AUTH_GRANTED no slave)
|
||||||
|
* ========================= */
|
||||||
|
static void on_remote_auth_grant(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
if (base != EVSE_LINK_EVENTS || id != LINK_EVENT_REMOTE_AUTH_GRANTED || data == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const evse_link_auth_grant_event_t *src = (const evse_link_auth_grant_event_t *)data;
|
||||||
|
|
||||||
|
if (evse_link_get_mode() != EVSE_LINK_MODE_SLAVE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auth_tag_event_data_t ev = {0};
|
||||||
|
strncpy(ev.tag, src->tag, AUTH_TAG_MAX_LEN - 1);
|
||||||
|
ev.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||||
|
ev.authorized = true;
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Remote auth grant on SLAVE for tag=%s", ev.tag);
|
||||||
|
|
||||||
|
esp_err_t err = esp_event_post(
|
||||||
|
AUTH_EVENTS,
|
||||||
|
AUTH_EVENT_TAG_PROCESSED,
|
||||||
|
&ev,
|
||||||
|
sizeof(ev),
|
||||||
|
portMAX_DELAY);
|
||||||
|
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to post AUTH_EVENT_TAG_PROCESSED (remote grant): %s",
|
||||||
|
esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
* Public API
|
||||||
|
* ========================= */
|
||||||
|
void auth_init(void)
|
||||||
|
{
|
||||||
|
// garantir que o storage service está pronto
|
||||||
|
ESP_ERROR_CHECK(storage_service_init());
|
||||||
|
|
||||||
|
load_mode_from_storage();
|
||||||
|
load_tags_from_storage();
|
||||||
|
|
||||||
|
bool need_wiegand = (s_mode == AUTH_MODE_LOCAL_RFID || s_mode == AUTH_MODE_OCPP_RFID);
|
||||||
|
if (need_wiegand)
|
||||||
|
{
|
||||||
|
initWiegand();
|
||||||
|
s_wiegand_started = true;
|
||||||
|
ESP_LOGI(TAG, "Wiegand reader initialized (mode=%s)", auth_mode_to_str(s_mode));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Mode OPEN: Wiegand not started");
|
||||||
|
}
|
||||||
|
|
||||||
|
// bridge EVSE-Link -> AUTH
|
||||||
|
{
|
||||||
|
esp_err_t err = esp_event_handler_register(
|
||||||
|
EVSE_LINK_EVENTS,
|
||||||
|
LINK_EVENT_REMOTE_AUTH_GRANTED,
|
||||||
|
on_remote_auth_grant,
|
||||||
|
NULL);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to register EVSE-Link auth grant handler: %s",
|
||||||
|
esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auth_mode_event_data_t evt = {.mode = s_mode};
|
||||||
|
esp_event_post(AUTH_EVENTS, AUTH_EVENT_INIT, &evt, sizeof(evt), portMAX_DELAY);
|
||||||
|
ESP_LOGI(TAG, "AUTH INIT sent (mode=%s)", auth_mode_to_str(s_mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
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_LOGD(TAG, "Mode unchanged: %s", auth_mode_to_str(mode));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auth_mode_t old = s_mode;
|
||||||
|
s_mode = mode;
|
||||||
|
save_mode_to_storage(mode);
|
||||||
|
|
||||||
|
bool need_wiegand = (s_mode == AUTH_MODE_LOCAL_RFID || s_mode == AUTH_MODE_OCPP_RFID);
|
||||||
|
|
||||||
|
if (need_wiegand && !s_wiegand_started)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Mode changed %s -> %s, starting Wiegand",
|
||||||
|
auth_mode_to_str(old), auth_mode_to_str(s_mode));
|
||||||
|
initWiegand();
|
||||||
|
s_wiegand_started = true;
|
||||||
|
}
|
||||||
|
else if (!need_wiegand && s_wiegand_started)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Mode changed %s -> %s, Wiegand remains started (no deinit implemented)",
|
||||||
|
auth_mode_to_str(old), auth_mode_to_str(s_mode));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Mode changed %s -> %s, no change in Wiegand state",
|
||||||
|
auth_mode_to_str(old), 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
auth_mode_t auth_get_mode(void)
|
||||||
|
{
|
||||||
|
return s_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool auth_add_tag(const char *tag)
|
||||||
|
{
|
||||||
|
if (!tag || strlen(tag) >= AUTH_TAG_MAX_LEN)
|
||||||
|
return false;
|
||||||
|
if (tag_count >= MAX_TAGS)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (is_tag_valid(tag))
|
||||||
|
return true;
|
||||||
|
|
||||||
strncpy(valid_tags[tag_count], tag, AUTH_TAG_MAX_LEN - 1);
|
strncpy(valid_tags[tag_count], tag, AUTH_TAG_MAX_LEN - 1);
|
||||||
valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0';
|
valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||||
tag_count++;
|
tag_count++;
|
||||||
ESP_LOGI(TAG, "Tag added: %s", tag);
|
|
||||||
|
save_tags_to_storage();
|
||||||
|
ESP_LOGD(TAG, "Tag added: %s", tag);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool auth_remove_tag(const char *tag) {
|
bool auth_remove_tag(const char *tag)
|
||||||
for (int i = 0; i < tag_count; i++) {
|
{
|
||||||
if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0) {
|
if (!tag)
|
||||||
for (int j = i; j < tag_count - 1; j++) {
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < tag_count; i++)
|
||||||
|
{
|
||||||
|
if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0)
|
||||||
|
{
|
||||||
|
for (int j = i; j < tag_count - 1; j++)
|
||||||
|
{
|
||||||
strncpy(valid_tags[j], valid_tags[j + 1], AUTH_TAG_MAX_LEN);
|
strncpy(valid_tags[j], valid_tags[j + 1], AUTH_TAG_MAX_LEN);
|
||||||
|
valid_tags[j][AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||||
}
|
}
|
||||||
tag_count--;
|
tag_count--;
|
||||||
ESP_LOGI(TAG, "Tag removed: %s", tag);
|
|
||||||
|
save_tags_to_storage();
|
||||||
|
ESP_LOGD(TAG, "Tag removed: %s", tag);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool auth_tag_exists(const char *tag) {
|
bool auth_tag_exists(const char *tag)
|
||||||
|
{
|
||||||
|
if (!tag)
|
||||||
|
return false;
|
||||||
return is_tag_valid(tag);
|
return is_tag_valid(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void auth_list_tags(void) {
|
void auth_list_tags(void)
|
||||||
ESP_LOGI(TAG, "Registered Tags (%d):", tag_count);
|
{
|
||||||
for (int i = 0; i < tag_count; i++) {
|
ESP_LOGD(TAG, "Registered Tags (%d):", tag_count);
|
||||||
ESP_LOGI(TAG, "- %s", valid_tags[i]);
|
for (int i = 0; i < tag_count; i++)
|
||||||
|
ESP_LOGD(TAG, "- %s", valid_tags[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_LOGD(TAG, "Tag registration mode enabled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void auth_init(void) {
|
void auth_process_tag(const char *tag)
|
||||||
load_auth_config(); // carrega estado de ativação
|
{
|
||||||
initWiegand(); // inicia leitor RFID
|
if (!tag || !*tag)
|
||||||
}
|
{
|
||||||
|
ESP_LOGW(TAG, "NULL/empty tag received");
|
||||||
// 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.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auth_event_t event;
|
switch (s_mode)
|
||||||
strncpy(event.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
{
|
||||||
event.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
case AUTH_MODE_OPEN:
|
||||||
event.authorized = is_tag_valid(tag);
|
ESP_LOGD(TAG, "Mode OPEN: tag=%s (no verification)", tag);
|
||||||
|
break;
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Tag %s: %s", tag, event.authorized ? "AUTHORIZED" : "DENIED");
|
case AUTH_MODE_LOCAL_RFID:
|
||||||
|
{
|
||||||
|
if (waiting_for_registration)
|
||||||
|
{
|
||||||
|
waiting_for_registration = false;
|
||||||
|
|
||||||
if (event_queue) {
|
if (auth_add_tag(tag))
|
||||||
if (xQueueSend(event_queue, &event, pdMS_TO_TICKS(100)) != pdPASS) {
|
{
|
||||||
ESP_LOGW(TAG, "Auth event queue full, dropping tag: %s", tag);
|
auth_tag_event_data_t ev = {0};
|
||||||
|
strncpy(ev.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||||
|
ev.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||||
|
ev.authorized = true;
|
||||||
|
|
||||||
|
esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED,
|
||||||
|
&ev, sizeof(ev), portMAX_DELAY);
|
||||||
|
ESP_LOGD(TAG, "Tag registered: %s", tag);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Failed to register tag: %s", tag);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "Auth event queue not set");
|
auth_tag_event_data_t ev = {0};
|
||||||
|
strncpy(ev.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||||
|
ev.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||||
|
ev.authorized = is_tag_valid(tag);
|
||||||
|
|
||||||
|
ESP_LOGD(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:
|
||||||
|
{
|
||||||
|
auth_tag_verify_event_t rq = {0};
|
||||||
|
strncpy(rq.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||||
|
rq.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||||
|
rq.req_id = s_next_req_id++;
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "OCPP VERIFY requested for tag=%s (req_id=%u)",
|
||||||
|
rq.tag, (unsigned)rq.req_id);
|
||||||
|
|
||||||
|
esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_VERIFY,
|
||||||
|
&rq, sizeof(rq), portMAX_DELAY);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int auth_get_tag_count(void)
|
||||||
|
{
|
||||||
|
return tag_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *auth_get_tag_by_index(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= tag_count)
|
||||||
|
return NULL;
|
||||||
|
return valid_tags[index];
|
||||||
|
}
|
||||||
|
|||||||
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;
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// === Início de: components/auth/src/wiegand.c ===
|
||||||
/**
|
/**
|
||||||
* @file wiegand.c
|
* @file wiegand.c
|
||||||
*
|
*
|
||||||
@@ -6,9 +7,13 @@
|
|||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <esp_idf_lib_helpers.h>
|
#include <esp_attr.h> // <- para IRAM_ATTR
|
||||||
#include "wiegand.h"
|
#include "wiegand.h"
|
||||||
|
|
||||||
|
#ifndef IRAM_ATTR
|
||||||
|
#define IRAM_ATTR
|
||||||
|
#endif
|
||||||
|
|
||||||
static const char *TAG = "wiegand";
|
static const char *TAG = "wiegand";
|
||||||
|
|
||||||
#define TIMER_INTERVAL_US 50000 // 50ms
|
#define TIMER_INTERVAL_US 50000 // 50ms
|
||||||
@@ -39,11 +44,7 @@ static void isr_enable(wiegand_reader_t *reader)
|
|||||||
gpio_set_intr_type(reader->gpio_d1, GPIO_INTR_NEGEDGE);
|
gpio_set_intr_type(reader->gpio_d1, GPIO_INTR_NEGEDGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HELPER_TARGET_IS_ESP32
|
|
||||||
static void IRAM_ATTR isr_handler(void *arg)
|
static void IRAM_ATTR isr_handler(void *arg)
|
||||||
#else
|
|
||||||
static void isr_handler(void *arg)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
wiegand_reader_t *reader = (wiegand_reader_t *)arg;
|
wiegand_reader_t *reader = (wiegand_reader_t *)arg;
|
||||||
if (!reader->enabled)
|
if (!reader->enabled)
|
||||||
@@ -101,9 +102,11 @@ esp_err_t wiegand_reader_init(wiegand_reader_t *reader, gpio_num_t gpio_d0, gpio
|
|||||||
{
|
{
|
||||||
CHECK_ARG(reader && buf_size && callback);
|
CHECK_ARG(reader && buf_size && callback);
|
||||||
|
|
||||||
|
/*
|
||||||
esp_err_t res = gpio_install_isr_service(0);
|
esp_err_t res = gpio_install_isr_service(0);
|
||||||
if (res != ESP_OK && res != ESP_ERR_INVALID_STATE)
|
if (res != ESP_OK && res != ESP_ERR_INVALID_STATE)
|
||||||
return res;
|
return res;
|
||||||
|
*/
|
||||||
|
|
||||||
memset(reader, 0, sizeof(wiegand_reader_t));
|
memset(reader, 0, sizeof(wiegand_reader_t));
|
||||||
reader->gpio_d0 = gpio_d0;
|
reader->gpio_d0 = gpio_d0;
|
||||||
@@ -179,3 +182,5 @@ esp_err_t wiegand_reader_done(wiegand_reader_t *reader)
|
|||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === Fim de: components/auth/src/wiegand.c ===
|
||||||
|
|||||||
@@ -1,90 +1,307 @@
|
|||||||
/*
|
|
||||||
* wiegand_reader.c
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
#include <freertos/queue.h>
|
#include <freertos/queue.h>
|
||||||
|
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
|
|
||||||
#include <wiegand.h>
|
#include <wiegand.h>
|
||||||
#include <evse_api.h>
|
|
||||||
#include <ocpp.h>
|
|
||||||
#include "auth.h"
|
#include "auth.h"
|
||||||
|
|
||||||
#define CONFIG_EXAMPLE_BUF_SIZE 50
|
#define CONFIG_EXAMPLE_BUF_SIZE 50
|
||||||
|
#define IDTAG_MAX_LEN 20
|
||||||
|
|
||||||
static const char *TAG = "WiegandReader";
|
static const char *TAG = "WiegandReader";
|
||||||
|
|
||||||
|
// ---- Parâmetro global/local para controlar a convenção de paridade ----
|
||||||
|
static const bool PARITY_INVERTED = false; // mude para true se o seu leitor vier "invertido"
|
||||||
|
|
||||||
static wiegand_reader_t reader;
|
static wiegand_reader_t reader;
|
||||||
static QueueHandle_t queue = NULL;
|
static QueueHandle_t queue = NULL;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
uint8_t data[CONFIG_EXAMPLE_BUF_SIZE];
|
uint8_t data[CONFIG_EXAMPLE_BUF_SIZE];
|
||||||
size_t bits;
|
size_t bits;
|
||||||
|
size_t bytes;
|
||||||
} data_packet_t;
|
} data_packet_t;
|
||||||
|
|
||||||
static void reader_callback(wiegand_reader_t *r) {
|
static inline uint8_t get_bit_msb_first(const uint8_t *buf, size_t bit_index)
|
||||||
data_packet_t p;
|
{
|
||||||
p.bits = r->bits;
|
return (buf[bit_index / 8] >> (7 - (bit_index % 8))) & 0x01;
|
||||||
memcpy(p.data, r->buf, CONFIG_EXAMPLE_BUF_SIZE);
|
|
||||||
xQueueSendToBack(queue, &p, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wiegand_task(void *arg) {
|
// Versão parametrizável de verificação de paridade
|
||||||
queue = xQueueCreate(5, sizeof(data_packet_t));
|
static bool check_parity_param(const uint8_t *buf, size_t bits, bool invert)
|
||||||
if (!queue) {
|
{
|
||||||
|
if (bits != 26 && bits != 34)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const int first_parity = get_bit_msb_first(buf, 0);
|
||||||
|
const int last_parity = get_bit_msb_first(buf, bits - 1);
|
||||||
|
|
||||||
|
uint32_t left = 0, right = 0;
|
||||||
|
|
||||||
|
if (bits == 26)
|
||||||
|
{
|
||||||
|
for (int i = 1; i <= 12; i++)
|
||||||
|
if (get_bit_msb_first(buf, i))
|
||||||
|
left++;
|
||||||
|
for (int i = 13; i <= 24; i++)
|
||||||
|
if (get_bit_msb_first(buf, i))
|
||||||
|
right++;
|
||||||
|
}
|
||||||
|
else // 34 bits
|
||||||
|
{
|
||||||
|
for (int i = 1; i <= 16; i++)
|
||||||
|
if (get_bit_msb_first(buf, i))
|
||||||
|
left++;
|
||||||
|
for (int i = 17; i <= 32; i++)
|
||||||
|
if (get_bit_msb_first(buf, i))
|
||||||
|
right++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regra padrão (W26/W34 “normais”): primeiro = PAR (left), último = ÍMPAR (right)
|
||||||
|
bool exp_first = (left % 2 == 0);
|
||||||
|
bool exp_last = (right % 2 == 1);
|
||||||
|
|
||||||
|
// Se invertido, troca as expectativas (par <-> ímpar)
|
||||||
|
if (invert)
|
||||||
|
{
|
||||||
|
exp_first = !exp_first;
|
||||||
|
exp_last = !exp_last;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (first_parity == exp_first) && (last_parity == exp_last);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRC-8/ATM (polinómio 0x07, init 0x00, sem ref, xor 0x00)
|
||||||
|
static uint8_t crc8_atm(const uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t crc = 0x00;
|
||||||
|
for (size_t i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
crc ^= data[i];
|
||||||
|
for (int b = 0; b < 8; b++)
|
||||||
|
{
|
||||||
|
crc = (crc & 0x80) ? (uint8_t)((crc << 1) ^ 0x07) : (uint8_t)(crc << 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRC-16/IBM (CRC-16-ANSI) poly 0xA001 (refin/refout), init 0xFFFF
|
||||||
|
static uint16_t crc16_ibm(const uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
uint16_t crc = 0xFFFF;
|
||||||
|
for (size_t i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
crc ^= data[i];
|
||||||
|
for (int b = 0; b < 8; b++)
|
||||||
|
{
|
||||||
|
if (crc & 1)
|
||||||
|
crc = (crc >> 1) ^ 0xA001;
|
||||||
|
else
|
||||||
|
crc >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extrai payload (sem paridades) e devolve FC/CN para W26/34
|
||||||
|
static bool wiegand_extract_fc_cn(const uint8_t *buf, size_t bits, uint32_t *fc, uint32_t *cn)
|
||||||
|
{
|
||||||
|
if (bits != 26 && bits != 34)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint32_t payload = 0;
|
||||||
|
size_t payload_bits = bits - 2;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < payload_bits; i++)
|
||||||
|
{
|
||||||
|
size_t bit = 1 + i; // ignora bit de paridade inicial
|
||||||
|
uint8_t bitval = (buf[bit / 8] >> (7 - (bit % 8))) & 0x01;
|
||||||
|
payload = (payload << 1) | bitval;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bits == 26)
|
||||||
|
{
|
||||||
|
*fc = (payload >> 16) & 0xFF; // 8b
|
||||||
|
*cn = payload & 0xFFFF; // 16b
|
||||||
|
}
|
||||||
|
else // 34 bits
|
||||||
|
{
|
||||||
|
*fc = (payload >> 16) & 0xFFFF; // 16b
|
||||||
|
*cn = payload & 0xFFFF; // 16b
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool build_idtag_w26_4B(uint32_t fc, uint32_t cn, char *out, size_t outlen)
|
||||||
|
{
|
||||||
|
uint8_t raw[4] = {
|
||||||
|
(uint8_t)(fc & 0xFF),
|
||||||
|
(uint8_t)((cn >> 8) & 0xFF),
|
||||||
|
(uint8_t)(cn & 0xFF),
|
||||||
|
0};
|
||||||
|
|
||||||
|
raw[3] = crc8_atm(raw, 3);
|
||||||
|
|
||||||
|
if (outlen < 9)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int n = snprintf(out, outlen, "%02X%02X%02X%02X",
|
||||||
|
raw[0], raw[1], raw[2], raw[3]);
|
||||||
|
return (n > 0 && (size_t)n < outlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool build_idtag_w34_7B(uint32_t fc, uint32_t cn, char *out, size_t outlen)
|
||||||
|
{
|
||||||
|
uint8_t raw[7];
|
||||||
|
raw[0] = 0x34;
|
||||||
|
raw[1] = (uint8_t)((fc >> 8) & 0xFF);
|
||||||
|
raw[2] = (uint8_t)(fc & 0xFF);
|
||||||
|
raw[3] = (uint8_t)((cn >> 8) & 0xFF);
|
||||||
|
raw[4] = (uint8_t)(cn & 0xFF);
|
||||||
|
|
||||||
|
uint16_t crc = crc16_ibm(raw, 5);
|
||||||
|
raw[5] = (uint8_t)((crc >> 8) & 0xFF);
|
||||||
|
raw[6] = (uint8_t)(crc & 0xFF);
|
||||||
|
|
||||||
|
if (outlen < 15)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int n = snprintf(out, outlen, "%02X%02X%02X%02X%02X%02X%02X",
|
||||||
|
raw[0], raw[1], raw[2], raw[3],
|
||||||
|
raw[4], raw[5], raw[6]);
|
||||||
|
return (n > 0 && (size_t)n < outlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback chamado pelo esp_timer_task (contexto de task, NÃO ISR)
|
||||||
|
static void reader_callback(wiegand_reader_t *r)
|
||||||
|
{
|
||||||
|
if (!queue)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Queue not ready, dropping packet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_packet_t p = {0};
|
||||||
|
p.bits = r->bits;
|
||||||
|
p.bytes = (r->bits + 7) / 8;
|
||||||
|
|
||||||
|
if (p.bytes > sizeof(p.data))
|
||||||
|
p.bytes = sizeof(p.data);
|
||||||
|
|
||||||
|
memcpy(p.data, r->buf, p.bytes);
|
||||||
|
|
||||||
|
if (xQueueSendToBack(queue, &p, 0) != pdPASS)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Queue full, dropping Wiegand packet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wiegand_task(void *arg)
|
||||||
|
{
|
||||||
|
queue = xQueueCreate(8, sizeof(data_packet_t));
|
||||||
|
if (!queue)
|
||||||
|
{
|
||||||
ESP_LOGE(TAG, "Failed to create queue");
|
ESP_LOGE(TAG, "Failed to create queue");
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_ERROR_CHECK(wiegand_reader_init(&reader, 19, 18,
|
ESP_ERROR_CHECK(wiegand_reader_init(&reader,
|
||||||
true, CONFIG_EXAMPLE_BUF_SIZE, reader_callback, WIEGAND_MSB_FIRST, WIEGAND_LSB_FIRST));
|
21, 22, // GPIO D0, D1
|
||||||
|
true, // internal pullups
|
||||||
|
CONFIG_EXAMPLE_BUF_SIZE,
|
||||||
|
reader_callback,
|
||||||
|
WIEGAND_MSB_FIRST,
|
||||||
|
WIEGAND_LSB_FIRST));
|
||||||
|
|
||||||
data_packet_t p;
|
data_packet_t p;
|
||||||
while (1) {
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
ESP_LOGI(TAG, "Waiting for Wiegand data...");
|
ESP_LOGI(TAG, "Waiting for Wiegand data...");
|
||||||
if (xQueueReceive(queue, &p, portMAX_DELAY) == pdPASS) {
|
|
||||||
ESP_LOGI(TAG, "Bits received: %d", p.bits);
|
|
||||||
|
|
||||||
char tag[20] = {0};
|
if (xQueueReceive(queue, &p, portMAX_DELAY) != pdPASS)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (p.bits == 26) {
|
ESP_LOGI(TAG, "Bits received: %d", (int)p.bits);
|
||||||
snprintf(tag, sizeof(tag), "%03d%03d%03d", p.data[0], p.data[1], p.data[2]);
|
|
||||||
} else if (p.bits == 34) {
|
|
||||||
snprintf(tag, sizeof(tag), "%03d%03d%03d%03d", p.data[0], p.data[1], p.data[2], p.data[3]);
|
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "Unsupported bit length: %d", (int)p.bits);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Tag read: %s", tag);
|
if (!check_parity_param(p.data, p.bits, PARITY_INVERTED))
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Parity check failed (%d bits). Dump:", (int)p.bits);
|
||||||
|
ESP_LOG_BUFFER_HEX(TAG, p.data, p.bytes);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!auth_is_enabled()) {
|
char tag[IDTAG_MAX_LEN + 1] = {0}; // OCPP 1.6: máx 20 chars (+NUL)
|
||||||
ESP_LOGW(TAG, "Auth disabled, ignoring tag.");
|
uint32_t fc = 0, cn = 0;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auth_tag_exists(tag)) {
|
if (!wiegand_extract_fc_cn(p.data, p.bits, &fc, &cn))
|
||||||
ESP_LOGI(TAG, "Authorized tag. Proceeding...");
|
{
|
||||||
evse_authorize();
|
ESP_LOGW(TAG, "Unsupported bit length: %d", (int)p.bits);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (ocpp_is_TransactionActive()) {
|
bool ok = false;
|
||||||
ocpp_end_transaction(tag);
|
if (p.bits == 26)
|
||||||
} else {
|
{
|
||||||
ocpp_begin_transaction(tag);
|
ok = build_idtag_w26_4B(fc, cn, tag, sizeof(tag)); // 8 hex
|
||||||
}
|
}
|
||||||
} else {
|
else // 34
|
||||||
ESP_LOGW(TAG, "Unauthorized tag: %s", tag);
|
{
|
||||||
}
|
ok = build_idtag_w34_7B(fc, cn, tag, sizeof(tag)); // 14 hex
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Failed to build idTag");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "idTag: %s", tag); // apenas [0-9A-F], alfanumérico
|
||||||
|
auth_process_tag(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wiegand_sim_task(void *arg)
|
||||||
|
{
|
||||||
|
// lista fixa de idTags simuladas
|
||||||
|
static const char *idtaglist[] = {
|
||||||
|
"00000041349",
|
||||||
|
"W2602312345",
|
||||||
|
"W34ABCDE123",
|
||||||
|
};
|
||||||
|
const size_t list_size = sizeof(idtaglist) / sizeof(idtaglist[0]);
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < list_size; i++)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Simulação -> idTag: %s", idtaglist[i]);
|
||||||
|
auth_process_tag(idtaglist[i]);
|
||||||
|
|
||||||
|
// espera 30 segundos entre cada tag
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(30000));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void initWiegand(void) {
|
void initWiegand(void)
|
||||||
|
{
|
||||||
ESP_LOGI(TAG, "Initializing Wiegand reader");
|
ESP_LOGI(TAG, "Initializing Wiegand reader");
|
||||||
xTaskCreate(wiegand_task, TAG, configMINIMAL_STACK_SIZE * 4, NULL, 5, NULL);
|
xTaskCreate(wiegand_task, TAG, configMINIMAL_STACK_SIZE * 4, NULL, 4, NULL);
|
||||||
|
|
||||||
|
// Para testes, podes ativar o simulador:
|
||||||
|
// ESP_LOGI(TAG, "Inicializando Wiegand simulado");
|
||||||
|
// xTaskCreate(wiegand_sim_task, "WiegandSim",
|
||||||
|
// configMINIMAL_STACK_SIZE * 3, NULL, 3, NULL);
|
||||||
}
|
}
|
||||||
|
|||||||
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 esp_timer evse network
|
||||||
|
)
|
||||||
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;
|
||||||
537
components/buzzer/src/buzzer.c
Executable file
537
components/buzzer/src/buzzer.c
Executable file
@@ -0,0 +1,537 @@
|
|||||||
|
// buzzer.c
|
||||||
|
#include "buzzer_events.h"
|
||||||
|
#include "evse_events.h"
|
||||||
|
#include "auth_events.h"
|
||||||
|
#include "network_events.h"
|
||||||
|
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/queue.h"
|
||||||
|
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/ledc.h"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// ===================== Configuração padrão =====================
|
||||||
|
#define CONFIG_BUZZER_GPIO GPIO_NUM_27
|
||||||
|
|
||||||
|
// 1 = PASSIVE (PWM), 0 = ACTIVE (on/off)
|
||||||
|
#define CONFIG_BUZZER_MODE_PASSIVE 0
|
||||||
|
|
||||||
|
#define CONFIG_BUZZER_FREQ_HZ 3500
|
||||||
|
|
||||||
|
#define CONFIG_BUZZER_DUTY_PCT 70
|
||||||
|
|
||||||
|
#define CONFIG_BUZZER_QUEUE_LEN 8
|
||||||
|
|
||||||
|
#define CONFIG_BUZZER_TASK_STACK 2048
|
||||||
|
|
||||||
|
#define CONFIG_BUZZER_TASK_PRIO (tskIDLE_PRIORITY + 1)
|
||||||
|
|
||||||
|
// anti-spam (gap mínimo entre toques)
|
||||||
|
#define CONFIG_BUZZER_MIN_GAP_MS 70
|
||||||
|
|
||||||
|
#define CONFIG_BUZZER_ENABLE_DEFAULT 1
|
||||||
|
|
||||||
|
// quiet hours start (minutos desde 00:00)
|
||||||
|
#define CONFIG_BUZZER_QUIET_START_MIN (22 * 60)
|
||||||
|
|
||||||
|
// quiet hours end (minutos desde 00:00)
|
||||||
|
#define CONFIG_BUZZER_QUIET_END_MIN (7 * 60)
|
||||||
|
|
||||||
|
// ===================== Tipos e estado =====================
|
||||||
|
static const char *TAG = "Buzzer";
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
BUZZER_MODE_ACTIVE = 0,
|
||||||
|
BUZZER_MODE_PASSIVE
|
||||||
|
} buzzer_mode_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t on_ms;
|
||||||
|
uint16_t off_ms;
|
||||||
|
} buzzer_step_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const buzzer_step_t *steps;
|
||||||
|
size_t length;
|
||||||
|
} buzzer_pattern_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
buzzer_mode_t mode;
|
||||||
|
gpio_num_t gpio;
|
||||||
|
|
||||||
|
// PASSIVE (PWM)
|
||||||
|
uint32_t freq_hz;
|
||||||
|
uint8_t duty_percent; // 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;
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Network event id=%d", (int)id);
|
||||||
|
|
||||||
|
buzzer_event_data_t evt = {0};
|
||||||
|
|
||||||
|
switch ((network_event_id_t)id)
|
||||||
|
{
|
||||||
|
case NETWORK_EVENT_AP_STARTED:
|
||||||
|
case NETWORK_EVENT_STA_CONNECTED:
|
||||||
|
// Usa padrão de AP_START para indicar rede disponível
|
||||||
|
evt.pattern = BUZZER_PATTERN_AP_START;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Para já, ignorar outros eventos de rede
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_event_post(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, &evt, sizeof(evt), portMAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void auth_event_handler(void *arg, esp_event_base_t base, int32_t id, void *event_data)
|
||||||
|
{
|
||||||
|
if (base != AUTH_EVENTS || event_data == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
buzzer_event_data_t buzzer_evt = {0};
|
||||||
|
|
||||||
|
if (id == AUTH_EVENT_TAG_PROCESSED)
|
||||||
|
{
|
||||||
|
const auth_tag_event_data_t *evt = (const auth_tag_event_data_t *)event_data;
|
||||||
|
ESP_LOGD(TAG, "AUTH processed: tag=%s authorized=%d", evt->tag, evt->authorized);
|
||||||
|
buzzer_evt.pattern = evt->authorized ? BUZZER_PATTERN_CARD_READ : BUZZER_PATTERN_CARD_DENIED;
|
||||||
|
}
|
||||||
|
else if (id == AUTH_EVENT_TAG_SAVED)
|
||||||
|
{
|
||||||
|
buzzer_evt.pattern = BUZZER_PATTERN_CARD_ADD;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_event_post(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, &buzzer_evt, sizeof(buzzer_evt), portMAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== Inicialização / Deinit =====================
|
||||||
|
void buzzer_init(void)
|
||||||
|
{
|
||||||
|
// Config HW
|
||||||
|
if (s_buzzer_cfg.mode == BUZZER_MODE_PASSIVE)
|
||||||
|
{
|
||||||
|
ledc_timer_config_t tcfg = {
|
||||||
|
.speed_mode = s_buzzer_cfg.ledc_speed_mode,
|
||||||
|
.duty_resolution = s_buzzer_cfg.duty_resolution,
|
||||||
|
.timer_num = s_buzzer_cfg.ledc_timer,
|
||||||
|
.freq_hz = s_buzzer_cfg.freq_hz,
|
||||||
|
.clk_cfg = LEDC_AUTO_CLK};
|
||||||
|
ESP_ERROR_CHECK(ledc_timer_config(&tcfg));
|
||||||
|
|
||||||
|
ledc_channel_config_t ccfg = {
|
||||||
|
.gpio_num = s_buzzer_cfg.gpio,
|
||||||
|
.speed_mode = s_buzzer_cfg.ledc_speed_mode,
|
||||||
|
.channel = s_buzzer_cfg.ledc_channel,
|
||||||
|
.intr_type = LEDC_INTR_DISABLE,
|
||||||
|
.timer_sel = s_buzzer_cfg.ledc_timer,
|
||||||
|
.duty = 0,
|
||||||
|
.hpoint = 0};
|
||||||
|
ESP_ERROR_CHECK(ledc_channel_config(&ccfg));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gpio_config_t io = {
|
||||||
|
.pin_bit_mask = (1ULL << s_buzzer_cfg.gpio),
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pull_down_en = 0,
|
||||||
|
.pull_up_en = 0,
|
||||||
|
.intr_type = GPIO_INTR_DISABLE};
|
||||||
|
ESP_ERROR_CHECK(gpio_config(&io));
|
||||||
|
gpio_set_level(s_buzzer_cfg.gpio, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
buzzer_set_quiet_hours(false, 0, 0);
|
||||||
|
buzzer_set_enabled(true);
|
||||||
|
|
||||||
|
buzzer_off();
|
||||||
|
|
||||||
|
// Fila + Task
|
||||||
|
s_buzzer_q = xQueueCreate(CONFIG_BUZZER_QUEUE_LEN, sizeof(buzzer_pattern_id_t));
|
||||||
|
configASSERT(s_buzzer_q != NULL);
|
||||||
|
BaseType_t ok = xTaskCreatePinnedToCore(
|
||||||
|
buzzer_task,
|
||||||
|
"buzzer_task",
|
||||||
|
CONFIG_BUZZER_TASK_STACK,
|
||||||
|
NULL,
|
||||||
|
CONFIG_BUZZER_TASK_PRIO,
|
||||||
|
&s_buzzer_task,
|
||||||
|
tskNO_AFFINITY);
|
||||||
|
configASSERT(ok == pdPASS);
|
||||||
|
|
||||||
|
// Handlers
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, buzzer_event_handler, NULL));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED, evse_event_handler, NULL));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, auth_event_handler, NULL));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED, auth_event_handler, NULL));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(NETWORK_EVENTS, ESP_EVENT_ANY_ID, network_event_handler, NULL));
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Buzzer initialized on GPIO %d (%s), freq=%lu Hz, duty=%u%%, enabled=%d",
|
||||||
|
s_buzzer_cfg.gpio,
|
||||||
|
s_buzzer_cfg.mode == BUZZER_MODE_PASSIVE ? "passive/PWM" : "active/ON-OFF",
|
||||||
|
(unsigned long)s_buzzer_cfg.freq_hz,
|
||||||
|
(unsigned)s_buzzer_cfg.duty_percent,
|
||||||
|
(int)s_buzzer_ctl.enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void buzzer_deinit(void)
|
||||||
|
{
|
||||||
|
(void)esp_event_handler_unregister(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, buzzer_event_handler);
|
||||||
|
(void)esp_event_handler_unregister(EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED, evse_event_handler);
|
||||||
|
(void)esp_event_handler_unregister(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, auth_event_handler);
|
||||||
|
(void)esp_event_handler_unregister(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED, auth_event_handler);
|
||||||
|
(void)esp_event_handler_unregister(NETWORK_EVENTS, ESP_EVENT_ANY_ID, network_event_handler);
|
||||||
|
|
||||||
|
if (s_buzzer_q)
|
||||||
|
{
|
||||||
|
vQueueDelete(s_buzzer_q);
|
||||||
|
s_buzzer_q = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_buzzer_task)
|
||||||
|
{
|
||||||
|
vTaskDelete(s_buzzer_task);
|
||||||
|
s_buzzer_task = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
buzzer_off();
|
||||||
|
ESP_LOGI(TAG, "Buzzer deinitialized");
|
||||||
|
}
|
||||||
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);
|
||||||
@@ -2,5 +2,5 @@ set(srcs
|
|||||||
"board_config.c")
|
"board_config.c")
|
||||||
|
|
||||||
idf_component_register(SRCS "${srcs}"
|
idf_component_register(SRCS "${srcs}"
|
||||||
PRIV_REQUIRES nvs_flash
|
PRIV_REQUIRES
|
||||||
INCLUDE_DIRS "include")
|
INCLUDE_DIRS "include")
|
||||||
@@ -15,32 +15,6 @@ bool atob(const char *value)
|
|||||||
return value[0] == 'y';
|
return value[0] == 'y';
|
||||||
}
|
}
|
||||||
|
|
||||||
board_config_energy_meter_t atoem(const char *value)
|
|
||||||
{
|
|
||||||
if (!strcmp(value, "cur"))
|
|
||||||
{
|
|
||||||
return BOARD_CONFIG_ENERGY_METER_CUR;
|
|
||||||
}
|
|
||||||
if (!strcmp(value, "cur_vlt"))
|
|
||||||
{
|
|
||||||
return BOARD_CONFIG_ENERGY_METER_CUR_VLT;
|
|
||||||
}
|
|
||||||
return BOARD_CONFIG_ENERGY_METER_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
board_config_serial_t atoser(const char *value)
|
|
||||||
{
|
|
||||||
if (!strcmp(value, "uart"))
|
|
||||||
{
|
|
||||||
return BOARD_CONFIG_SERIAL_UART;
|
|
||||||
}
|
|
||||||
if (!strcmp(value, "rs485"))
|
|
||||||
{
|
|
||||||
return BOARD_CONFIG_SERIAL_RS485;
|
|
||||||
}
|
|
||||||
return BOARD_CONFIG_SERIAL_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SET_CONFIG_VALUE(name, prop, convert_fn) \
|
#define SET_CONFIG_VALUE(name, prop, convert_fn) \
|
||||||
if (!strcmp(key, name)) \
|
if (!strcmp(key, name)) \
|
||||||
{ \
|
{ \
|
||||||
@@ -94,16 +68,14 @@ void board_config_load()
|
|||||||
if (value != NULL)
|
if (value != NULL)
|
||||||
{
|
{
|
||||||
SET_CONFIG_VALUE_STR("DEVICE_NAME", device_name);
|
SET_CONFIG_VALUE_STR("DEVICE_NAME", device_name);
|
||||||
SET_CONFIG_VALUE("LED_CHARGING", led_charging, atob);
|
SET_CONFIG_VALUE("led_blue", led_blue, atob);
|
||||||
SET_CONFIG_VALUE("LED_CHARGING_GPIO", led_charging_gpio, atoi);
|
SET_CONFIG_VALUE("led_blue_GPIO", led_blue_gpio, atoi);
|
||||||
SET_CONFIG_VALUE("LED_ERROR", led_error, atob);
|
SET_CONFIG_VALUE("led_red", led_red, atob);
|
||||||
SET_CONFIG_VALUE("LED_ERROR_GPIO", led_error_gpio, atoi);
|
SET_CONFIG_VALUE("led_red_GPIO", led_red_gpio, atoi);
|
||||||
SET_CONFIG_VALUE("LED_STOP", led_stop, atob);
|
SET_CONFIG_VALUE("led_green", led_green, atob);
|
||||||
SET_CONFIG_VALUE("LED_STOP_GPIO", led_stop_gpio, atoi);
|
SET_CONFIG_VALUE("led_green_GPIO", led_green_gpio, atoi);
|
||||||
|
|
||||||
SET_CONFIG_VALUE("BUZZER", buzzer, atob);
|
SET_CONFIG_VALUE("BUZZER", buzzer, atob);
|
||||||
SET_CONFIG_VALUE("BUZZER_GPIO", buzzer_gpio, atoi);
|
SET_CONFIG_VALUE("BUZZER_GPIO", buzzer_gpio, atoi);
|
||||||
|
|
||||||
SET_CONFIG_VALUE("BUTTON_WIFI_GPIO", button_wifi_gpio, atoi);
|
SET_CONFIG_VALUE("BUTTON_WIFI_GPIO", button_wifi_gpio, atoi);
|
||||||
SET_CONFIG_VALUE("PILOT_PWM_GPIO", pilot_pwm_gpio, atoi);
|
SET_CONFIG_VALUE("PILOT_PWM_GPIO", pilot_pwm_gpio, atoi);
|
||||||
SET_CONFIG_VALUE("PILOT_ADC_CHANNEL", pilot_adc_channel, atoi);
|
SET_CONFIG_VALUE("PILOT_ADC_CHANNEL", pilot_adc_channel, atoi);
|
||||||
@@ -130,76 +102,6 @@ void board_config_load()
|
|||||||
SET_CONFIG_VALUE("RCM", rcm, atob);
|
SET_CONFIG_VALUE("RCM", rcm, atob);
|
||||||
SET_CONFIG_VALUE("RCM_GPIO", rcm_gpio, atoi);
|
SET_CONFIG_VALUE("RCM_GPIO", rcm_gpio, atoi);
|
||||||
SET_CONFIG_VALUE("RCM_TEST_GPIO", rcm_test_gpio, atoi);
|
SET_CONFIG_VALUE("RCM_TEST_GPIO", rcm_test_gpio, atoi);
|
||||||
SET_CONFIG_VALUE("ENERGY_METER", energy_meter, atoem);
|
|
||||||
SET_CONFIG_VALUE("ENERGY_METER_THREE_PHASES", energy_meter_three_phases, atob);
|
|
||||||
SET_CONFIG_VALUE("ENERGY_METER_L1_CUR_ADC_CHANNEL", energy_meter_l1_cur_adc_channel, atoi);
|
|
||||||
SET_CONFIG_VALUE("ENERGY_METER_L2_CUR_ADC_CHANNEL", energy_meter_l2_cur_adc_channel, atoi);
|
|
||||||
SET_CONFIG_VALUE("ENERGY_METER_L3_CUR_ADC_CHANNEL", energy_meter_l3_cur_adc_channel, atoi);
|
|
||||||
SET_CONFIG_VALUE("ENERGY_METER_CUR_SCALE", energy_meter_cur_scale, atoff);
|
|
||||||
SET_CONFIG_VALUE("ENERGY_METER_L1_VLT_ADC_CHANNEL", energy_meter_l1_vlt_adc_channel, atoi);
|
|
||||||
SET_CONFIG_VALUE("ENERGY_METER_L2_VLT_ADC_CHANNEL", energy_meter_l2_vlt_adc_channel, atoi);
|
|
||||||
SET_CONFIG_VALUE("ENERGY_METER_L3_VLT_ADC_CHANNEL", energy_meter_l3_vlt_adc_channel, atoi);
|
|
||||||
SET_CONFIG_VALUE("ENERGY_METER_VLT_SCALE", energy_meter_vlt_scale, atoff);
|
|
||||||
SET_CONFIG_VALUE("AUX_IN_1", aux_in_1, atob);
|
|
||||||
SET_CONFIG_VALUE_STR("AUX_IN_1_NAME", aux_in_1_name);
|
|
||||||
SET_CONFIG_VALUE("AUX_IN_1_GPIO", aux_in_1_gpio, atoi);
|
|
||||||
SET_CONFIG_VALUE("AUX_IN_2", aux_in_2, atob);
|
|
||||||
SET_CONFIG_VALUE_STR("AUX_IN_2_NAME", aux_in_2_name);
|
|
||||||
SET_CONFIG_VALUE("AUX_IN_2_GPIO", aux_in_2_gpio, atoi);
|
|
||||||
SET_CONFIG_VALUE("AUX_IN_3", aux_in_3, atob);
|
|
||||||
SET_CONFIG_VALUE_STR("AUX_IN_3_NAME", aux_in_3_name);
|
|
||||||
SET_CONFIG_VALUE("AUX_IN_3_GPIO", aux_in_3_gpio, atoi);
|
|
||||||
SET_CONFIG_VALUE("AUX_IN_4", aux_in_4, atob);
|
|
||||||
SET_CONFIG_VALUE_STR("AUX_IN_4_NAME", aux_in_4_name);
|
|
||||||
SET_CONFIG_VALUE("AUX_IN_4_GPIO", aux_in_4_gpio, atoi);
|
|
||||||
SET_CONFIG_VALUE("AUX_OUT_1", aux_out_1, atob);
|
|
||||||
SET_CONFIG_VALUE_STR("AUX_OUT_1_NAME", aux_out_1_name);
|
|
||||||
SET_CONFIG_VALUE("AUX_OUT_1_GPIO", aux_out_1_gpio, atoi);
|
|
||||||
SET_CONFIG_VALUE("AUX_OUT_2", aux_out_2, atob);
|
|
||||||
SET_CONFIG_VALUE_STR("AUX_OUT_2_NAME", aux_out_2_name);
|
|
||||||
SET_CONFIG_VALUE("AUX_OUT_2_GPIO", aux_out_2_gpio, atoi);
|
|
||||||
SET_CONFIG_VALUE("AUX_OUT_3", aux_out_3, atob);
|
|
||||||
SET_CONFIG_VALUE_STR("AUX_OUT_3_NAME", aux_out_3_name);
|
|
||||||
SET_CONFIG_VALUE("AUX_OUT_3_GPIO", aux_out_3_gpio, atoi);
|
|
||||||
SET_CONFIG_VALUE("AUX_OUT_4", aux_out_4, atob);
|
|
||||||
SET_CONFIG_VALUE_STR("AUX_OUT_4_NAME", aux_out_4_name);
|
|
||||||
SET_CONFIG_VALUE("AUX_OUT_4_GPIO", aux_out_4_gpio, atoi);
|
|
||||||
SET_CONFIG_VALUE("AUX_AIN_1", aux_ain_1, atob);
|
|
||||||
SET_CONFIG_VALUE_STR("AUX_AIN_1_NAME", aux_ain_1_name);
|
|
||||||
SET_CONFIG_VALUE("AUX_AIN_1_ADC_CHANNEL", aux_ain_1_adc_channel, atoi);
|
|
||||||
SET_CONFIG_VALUE("AUX_AIN_2", aux_ain_2, atob);
|
|
||||||
SET_CONFIG_VALUE_STR("AUX_AIN_2_NAME", aux_ain_2_name);
|
|
||||||
SET_CONFIG_VALUE("AUX_AIN_2_ADC_CHANNEL", aux_ain_2_adc_channel, atoi);
|
|
||||||
/*
|
|
||||||
#if CONFIG_ESP_CONSOLE_UART_NUM != 0
|
|
||||||
SET_CONFIG_VALUE("SERIAL_1", serial_1, atoser);
|
|
||||||
SET_CONFIG_VALUE_STR("SERIAL_1_NAME", serial_1_name);
|
|
||||||
SET_CONFIG_VALUE("SERIAL_1_RXD_GPIO", serial_1_rxd_gpio, atoi);
|
|
||||||
SET_CONFIG_VALUE("SERIAL_1_TXD_GPIO", serial_1_txd_gpio, atoi);
|
|
||||||
SET_CONFIG_VALUE("SERIAL_1_RTS_GPIO", serial_1_rts_gpio, atoi);
|
|
||||||
#endif // CONFIG_ESP_CONSOLE_UART_NUM != 0
|
|
||||||
#if CONFIG_ESP_CONSOLE_UART_NUM != 1
|
|
||||||
SET_CONFIG_VALUE("SERIAL_2", serial_2, atoser);
|
|
||||||
SET_CONFIG_VALUE_STR("SERIAL_2_NAME", serial_2_name);
|
|
||||||
SET_CONFIG_VALUE("SERIAL_2_RXD_GPIO", serial_2_rxd_gpio, atoi);
|
|
||||||
SET_CONFIG_VALUE("SERIAL_2_TXD_GPIO", serial_2_txd_gpio, atoi);
|
|
||||||
SET_CONFIG_VALUE("SERIAL_2_RTS_GPIO", serial_2_rts_gpio, atoi);
|
|
||||||
#endif // CONFIG_ESP_CONSOLE_UART_NUM != 1
|
|
||||||
#if SOC_UART_NUM > 2
|
|
||||||
#if CONFIG_ESP_CONSOLE_UART_NUM != 2
|
|
||||||
SET_CONFIG_VALUE("SERIAL_3", serial_3, atoser);
|
|
||||||
SET_CONFIG_VALUE_STR("SERIAL_3_NAME", serial_3_name);
|
|
||||||
SET_CONFIG_VALUE("SERIAL_3_RXD_GPIO", serial_3_rxd_gpio, atoi);
|
|
||||||
SET_CONFIG_VALUE("SERIAL_3_TXD_GPIO", serial_3_txd_gpio, atoi);
|
|
||||||
SET_CONFIG_VALUE("SERIAL_3_RTS_GPIO", serial_3_rts_gpio, atoi);
|
|
||||||
#endif / CONFIG_ESP_CONSOLE_UART_NUM != 2
|
|
||||||
#endif // SOC_UART_NUM > 2
|
|
||||||
SET_CONFIG_VALUE("ONEWIRE", onewire, atob);
|
|
||||||
SET_CONFIG_VALUE("ONEWIRE_GPIO", onewire_gpio, atoi);
|
|
||||||
SET_CONFIG_VALUE("ONEWIRE_TEMP_SENSOR", onewire_temp_sensor, atob);
|
|
||||||
|
|
||||||
ESP_LOGE(TAG, "Unknown config value %s=%s", key, value);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,15 @@
|
|||||||
#include "hal/gpio_types.h"
|
#include "hal/gpio_types.h"
|
||||||
#include "soc/soc_caps.h"
|
#include "soc/soc_caps.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
BOARD_CONFIG_ENERGY_METER_NONE,
|
BOARD_CONFIG_ENERGY_METER_NONE,
|
||||||
BOARD_CONFIG_ENERGY_METER_CUR,
|
BOARD_CONFIG_ENERGY_METER_CUR,
|
||||||
BOARD_CONFIG_ENERGY_METER_CUR_VLT
|
BOARD_CONFIG_ENERGY_METER_CUR_VLT
|
||||||
} board_config_energy_meter_t;
|
} board_config_energy_meter_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
BOARD_CONFIG_SERIAL_NONE,
|
BOARD_CONFIG_SERIAL_NONE,
|
||||||
BOARD_CONFIG_SERIAL_UART,
|
BOARD_CONFIG_SERIAL_UART,
|
||||||
BOARD_CONFIG_SERIAL_RS485
|
BOARD_CONFIG_SERIAL_RS485
|
||||||
@@ -21,12 +23,12 @@ typedef struct
|
|||||||
{
|
{
|
||||||
char device_name[32];
|
char device_name[32];
|
||||||
|
|
||||||
bool led_charging : 1;
|
bool led_blue : 1;
|
||||||
gpio_num_t led_charging_gpio;
|
gpio_num_t led_blue_gpio;
|
||||||
bool led_error : 1;
|
bool led_red : 1;
|
||||||
gpio_num_t led_error_gpio;
|
gpio_num_t led_red_gpio;
|
||||||
bool led_stop : 1;
|
bool led_green : 1;
|
||||||
gpio_num_t led_stop_gpio;
|
gpio_num_t led_green_gpio;
|
||||||
|
|
||||||
bool buzzer : 1;
|
bool buzzer : 1;
|
||||||
gpio_num_t buzzer_gpio;
|
gpio_num_t buzzer_gpio;
|
||||||
@@ -63,80 +65,6 @@ typedef struct
|
|||||||
gpio_num_t rcm_gpio;
|
gpio_num_t rcm_gpio;
|
||||||
gpio_num_t rcm_test_gpio;
|
gpio_num_t rcm_test_gpio;
|
||||||
|
|
||||||
board_config_energy_meter_t energy_meter;
|
|
||||||
bool energy_meter_three_phases : 1;
|
|
||||||
|
|
||||||
adc_channel_t energy_meter_l1_cur_adc_channel;
|
|
||||||
adc_channel_t energy_meter_l2_cur_adc_channel;
|
|
||||||
adc_channel_t energy_meter_l3_cur_adc_channel;
|
|
||||||
float energy_meter_cur_scale;
|
|
||||||
adc_channel_t energy_meter_l1_vlt_adc_channel;
|
|
||||||
adc_channel_t energy_meter_l2_vlt_adc_channel;
|
|
||||||
adc_channel_t energy_meter_l3_vlt_adc_channel;
|
|
||||||
float energy_meter_vlt_scale;
|
|
||||||
|
|
||||||
|
|
||||||
bool aux_in_1 : 1;
|
|
||||||
char aux_in_1_name[8];
|
|
||||||
gpio_num_t aux_in_1_gpio;
|
|
||||||
|
|
||||||
bool aux_in_2 : 1;
|
|
||||||
char aux_in_2_name[8];
|
|
||||||
gpio_num_t aux_in_2_gpio;
|
|
||||||
|
|
||||||
bool aux_in_3 : 1;
|
|
||||||
char aux_in_3_name[8];
|
|
||||||
gpio_num_t aux_in_3_gpio;
|
|
||||||
|
|
||||||
bool aux_in_4 : 1;
|
|
||||||
char aux_in_4_name[8];
|
|
||||||
gpio_num_t aux_in_4_gpio;
|
|
||||||
|
|
||||||
bool aux_out_1 : 1;
|
|
||||||
char aux_out_1_name[8];
|
|
||||||
gpio_num_t aux_out_1_gpio;
|
|
||||||
|
|
||||||
bool aux_out_2 : 1;
|
|
||||||
char aux_out_2_name[8];
|
|
||||||
gpio_num_t aux_out_2_gpio;
|
|
||||||
|
|
||||||
bool aux_out_3 : 1;
|
|
||||||
char aux_out_3_name[8];
|
|
||||||
gpio_num_t aux_out_3_gpio;
|
|
||||||
|
|
||||||
bool aux_out_4 : 1;
|
|
||||||
char aux_out_4_name[8];
|
|
||||||
gpio_num_t aux_out_4_gpio;
|
|
||||||
|
|
||||||
bool aux_ain_1 : 1;
|
|
||||||
char aux_ain_1_name[8];
|
|
||||||
adc_channel_t aux_ain_1_adc_channel;
|
|
||||||
|
|
||||||
bool aux_ain_2 : 1;
|
|
||||||
char aux_ain_2_name[8];
|
|
||||||
adc_channel_t aux_ain_2_adc_channel;
|
|
||||||
|
|
||||||
board_config_serial_t serial_1;
|
|
||||||
char serial_1_name[16];
|
|
||||||
gpio_num_t serial_1_rxd_gpio;
|
|
||||||
gpio_num_t serial_1_txd_gpio;
|
|
||||||
gpio_num_t serial_1_rts_gpio;
|
|
||||||
board_config_serial_t serial_2;
|
|
||||||
char serial_2_name[16];
|
|
||||||
gpio_num_t serial_2_rxd_gpio;
|
|
||||||
gpio_num_t serial_2_txd_gpio;
|
|
||||||
gpio_num_t serial_2_rts_gpio;
|
|
||||||
#if SOC_UART_NUM > 2
|
|
||||||
board_config_serial_t serial_3;
|
|
||||||
char serial_3_name[16];
|
|
||||||
gpio_num_t serial_3_rxd_gpio;
|
|
||||||
gpio_num_t serial_3_txd_gpio;
|
|
||||||
gpio_num_t serial_3_rts_gpio;
|
|
||||||
#endif /* SOC_UART_NUM > 2 */
|
|
||||||
|
|
||||||
bool onewire : 1;
|
|
||||||
gpio_num_t onewire_gpio;
|
|
||||||
bool onewire_temp_sensor : 1;
|
|
||||||
} board_config_t;
|
} board_config_t;
|
||||||
|
|
||||||
extern board_config_t board_config;
|
extern board_config_t board_config;
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
name: esp_idf_lib_helpers
|
|
||||||
description: Common support library for esp-idf-lib
|
|
||||||
version: 1.2.0
|
|
||||||
groups:
|
|
||||||
- common
|
|
||||||
code_owners:
|
|
||||||
- trombik
|
|
||||||
- UncleRus
|
|
||||||
depends:
|
|
||||||
- freertos
|
|
||||||
thread_safe: n/a
|
|
||||||
targets:
|
|
||||||
- esp32
|
|
||||||
- esp8266
|
|
||||||
- esp32s2
|
|
||||||
- esp32c3
|
|
||||||
license: ISC
|
|
||||||
copyrights:
|
|
||||||
- name: trombik
|
|
||||||
year: 2019
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
idf_component_register(
|
|
||||||
INCLUDE_DIRS .
|
|
||||||
REQUIRES freertos
|
|
||||||
)
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
Copyright (c) 2019 Tomoyuki Sakurai <y@trombik.org>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
COMPONENT_ADD_INCLUDEDIRS = .
|
|
||||||
|
|
||||||
ifdef CONFIG_IDF_TARGET_ESP8266
|
|
||||||
COMPONENT_DEPENDS = esp8266 freertos
|
|
||||||
else
|
|
||||||
COMPONENT_DEPENDS = freertos
|
|
||||||
endif
|
|
||||||
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2019 Tomoyuki Sakurai <y@trombik.org>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(__ESP_IDF_LIB_HELPERS__H__)
|
|
||||||
#define __ESP_IDF_LIB_HELPERS__H__
|
|
||||||
|
|
||||||
/* XXX this header file does not need to include freertos/FreeRTOS.h.
|
|
||||||
* but without it, ESP8266 RTOS SDK does not include `sdkconfig.h` in correct
|
|
||||||
* order. as this header depends on sdkconfig.h, sdkconfig.h must be included
|
|
||||||
* first. however, the SDK includes this header first, then includes
|
|
||||||
* `sdkconfig.h` when freertos/FreeRTOS.h is not explicitly included. an
|
|
||||||
* evidence can be found in `build/${COMPONENT}/${COMPONENT}.d` in a failed
|
|
||||||
* build.
|
|
||||||
*/
|
|
||||||
#include <freertos/FreeRTOS.h>
|
|
||||||
#include <esp_idf_version.h>
|
|
||||||
|
|
||||||
#if !defined(ESP_IDF_VERSION) || !defined(ESP_IDF_VERSION_VAL)
|
|
||||||
#error Unknown ESP-IDF/ESP8266 RTOS SDK version
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Minimal supported version for ESP32, ESP32S2 */
|
|
||||||
#define HELPER_ESP32_MIN_VER ESP_IDF_VERSION_VAL(3, 3, 5)
|
|
||||||
/* Minimal supported version for ESP8266 */
|
|
||||||
#define HELPER_ESP8266_MIN_VER ESP_IDF_VERSION_VAL(3, 3, 0)
|
|
||||||
|
|
||||||
/* HELPER_TARGET_IS_ESP32
|
|
||||||
* 1 when the target is esp32
|
|
||||||
*/
|
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32) \
|
|
||||||
|| defined(CONFIG_IDF_TARGET_ESP32S2) \
|
|
||||||
|| defined(CONFIG_IDF_TARGET_ESP32S3) \
|
|
||||||
|| defined(CONFIG_IDF_TARGET_ESP32C2) \
|
|
||||||
|| defined(CONFIG_IDF_TARGET_ESP32C3) \
|
|
||||||
|| defined(CONFIG_IDF_TARGET_ESP32C6) \
|
|
||||||
|| defined(CONFIG_IDF_TARGET_ESP32H2)
|
|
||||||
#define HELPER_TARGET_IS_ESP32 (1)
|
|
||||||
#define HELPER_TARGET_IS_ESP8266 (0)
|
|
||||||
|
|
||||||
/* HELPER_TARGET_IS_ESP8266
|
|
||||||
* 1 when the target is esp8266
|
|
||||||
*/
|
|
||||||
#elif defined(CONFIG_IDF_TARGET_ESP8266)
|
|
||||||
#define HELPER_TARGET_IS_ESP32 (0)
|
|
||||||
#define HELPER_TARGET_IS_ESP8266 (1)
|
|
||||||
#else
|
|
||||||
#error BUG: cannot determine the target
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HELPER_TARGET_IS_ESP32 && ESP_IDF_VERSION < HELPER_ESP32_MIN_VER
|
|
||||||
#error Unsupported ESP-IDF version. Please update!
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HELPER_TARGET_IS_ESP8266 && ESP_IDF_VERSION < HELPER_ESP8266_MIN_VER
|
|
||||||
#error Unsupported ESP8266 RTOS SDK version. Please update!
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* show the actual values for debugging */
|
|
||||||
#if DEBUG
|
|
||||||
#define VALUE_TO_STRING(x) #x
|
|
||||||
#define VALUE(x) VALUE_TO_STRING(x)
|
|
||||||
#define VAR_NAME_VALUE(var) #var "=" VALUE(var)
|
|
||||||
#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32C3))
|
|
||||||
#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32H2))
|
|
||||||
#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32S2))
|
|
||||||
#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32))
|
|
||||||
#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP8266))
|
|
||||||
#pragma message(VAR_NAME_VALUE(ESP_IDF_VERSION_MAJOR))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#if CONFIG_IDF_TARGET_ESP32
|
|
||||||
#include <esp32/rom/ets_sys.h>
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
|
||||||
#include <esp32c2/rom/ets_sys.h>
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
|
||||||
#include <esp32c3/rom/ets_sys.h>
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
|
||||||
#include <esp32c6/rom/ets_sys.h>
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
#include <esp32h2/rom/ets_sys.h>
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32H4
|
|
||||||
#include <esp32h4/rom/ets_sys.h>
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
|
||||||
#include <esp32s2/rom/ets_sys.h>
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
|
||||||
#include <esp32s3/rom/ets_sys.h>
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP8266
|
|
||||||
#include <rom/ets_sys.h>
|
|
||||||
#else
|
|
||||||
#error "ets_sys: Unknown target"
|
|
||||||
#endif
|
|
||||||
@@ -8,11 +8,15 @@ set(srcs
|
|||||||
evse_fsm.c
|
evse_fsm.c
|
||||||
evse_manager.c
|
evse_manager.c
|
||||||
evse_hardware.c
|
evse_hardware.c
|
||||||
|
evse_pilot.c
|
||||||
|
evse_meter.c
|
||||||
|
evse_session.c
|
||||||
|
evse_api.c
|
||||||
)
|
)
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS ${srcs}
|
SRCS ${srcs}
|
||||||
INCLUDE_DIRS "include"
|
INCLUDE_DIRS "include"
|
||||||
PRIV_REQUIRES nvs_flash
|
PRIV_REQUIRES driver
|
||||||
REQUIRES peripherals auth
|
REQUIRES peripherals auth loadbalancer scheduler storage_service
|
||||||
)
|
)
|
||||||
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,410 @@
|
|||||||
#include <inttypes.h> // Include for PRI macros
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
|
||||||
#include "evse_config.h"
|
#include "evse_config.h"
|
||||||
#include "board_config.h"
|
#include "board_config.h"
|
||||||
#include "evse_limits.h"
|
#include "evse_limits.h"
|
||||||
|
#include "evse_api.h"
|
||||||
|
#include "evse_state.h"
|
||||||
|
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "nvs.h"
|
#include "esp_err.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
|
||||||
|
#include "storage_service.h"
|
||||||
|
|
||||||
static const char *TAG = "evse_config";
|
static const char *TAG = "evse_config";
|
||||||
|
#define NVS_NAMESPACE "evse_config"
|
||||||
|
|
||||||
static nvs_handle_t nvs;
|
// ========================
|
||||||
|
// 3 variáveis (semântica simples)
|
||||||
|
// ========================
|
||||||
|
|
||||||
// Configurable parameters
|
// 1) Hardware (FIXO)
|
||||||
static uint8_t max_charging_current = MAX_CHARGING_CURRENT_LIMIT;
|
static const 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;
|
|
||||||
|
|
||||||
esp_err_t evse_config_init(void) {
|
// 2) Configurável (persistido)
|
||||||
ESP_LOGD(TAG, "Initializing NVS configuration...");
|
static uint16_t charging_current = MAX_CHARGING_CURRENT_LIMIT;
|
||||||
ESP_LOGI(TAG, "Opening NVS namespace");
|
|
||||||
return nvs_open("evse", NVS_READWRITE, &nvs);
|
// 3) Runtime (RAM)
|
||||||
|
static uint16_t charging_current_runtime = 0;
|
||||||
|
|
||||||
|
// Outros parâmetros (persistidos)
|
||||||
|
static bool socket_outlet = false;
|
||||||
|
static bool rcm = false;
|
||||||
|
static uint8_t temp_threshold = 60;
|
||||||
|
|
||||||
|
// Availability / Enable flags (persistidos)
|
||||||
|
static bool is_available = true;
|
||||||
|
static bool is_enabled = true;
|
||||||
|
|
||||||
|
static inline TickType_t TO_TICKS_MS(uint32_t ms) { return pdMS_TO_TICKS(ms); }
|
||||||
|
|
||||||
|
// Ajusta conforme o teu boot:
|
||||||
|
// 1000ms pode ser curto com Wi-Fi/FS/tasks; 2000ms é mais robusto em produto.
|
||||||
|
static inline TickType_t BOOT_TO(void) { return TO_TICKS_MS(2000); }
|
||||||
|
|
||||||
|
// ========================
|
||||||
|
// Initialization
|
||||||
|
// ========================
|
||||||
|
esp_err_t evse_config_init(void)
|
||||||
|
{
|
||||||
|
// garante storage iniciado
|
||||||
|
ESP_RETURN_ON_ERROR(storage_service_init(), TAG, "storage init failed");
|
||||||
|
ESP_LOGI(TAG, "EVSE config init OK (storage-backed)");
|
||||||
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void evse_check_defaults(void) {
|
void evse_check_defaults(void)
|
||||||
|
{
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
uint8_t u8;
|
uint8_t u8 = 0;
|
||||||
uint16_t u16;
|
uint16_t u16 = 0;
|
||||||
uint32_t u32;
|
|
||||||
bool needs_commit = false;
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Checking default parameters...");
|
// Timeouts: leitura e escrita no boot
|
||||||
|
const TickType_t rd_to = BOOT_TO();
|
||||||
|
const TickType_t wr_to = TO_TICKS_MS(2000);
|
||||||
|
|
||||||
// Max charging current
|
ESP_LOGD(TAG, "Checking default parameters (sync persistence)...");
|
||||||
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) {
|
// Charging current (default, persisted)
|
||||||
max_charging_current = MAX_CHARGING_CURRENT_LIMIT;
|
// -----------------------------------------
|
||||||
nvs_set_u8(nvs, "max_chrg_curr", max_charging_current);
|
err = storage_get_u16_sync(NVS_NAMESPACE, "def_chrg_curr", &u16, rd_to);
|
||||||
needs_commit = true;
|
if (err != ESP_OK || u16 < MIN_CHARGING_CURRENT_LIMIT || u16 > max_charging_current)
|
||||||
ESP_LOGD(TAG, "Max charging current adjusted to: %d", max_charging_current);
|
{
|
||||||
} else {
|
charging_current = max_charging_current;
|
||||||
max_charging_current = u8;
|
|
||||||
|
esp_err_t se = storage_set_u16_sync(NVS_NAMESPACE, "def_chrg_curr", charging_current, wr_to);
|
||||||
|
if (se != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to persist def_chrg_curr=%u: %s",
|
||||||
|
(unsigned)charging_current, esp_err_to_name(se));
|
||||||
|
// seguimos com RAM correta; persist pode falhar por flash/partição
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGW(TAG, "Invalid/missing def_chrg_curr (%s) -> reset to %u (sync persisted)",
|
||||||
|
esp_err_to_name(err), (unsigned)charging_current);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// 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)
|
|
||||||
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;
|
|
||||||
nvs_set_u16(nvs, "def_chrg_curr", charging_current);
|
|
||||||
needs_commit = true;
|
|
||||||
ESP_LOGD(TAG, "Charging current adjusted to: %d", charging_current);
|
|
||||||
} else {
|
|
||||||
charging_current = u16;
|
charging_current = u16;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = nvs_get_u8(nvs, "require_auth", &u8);
|
// runtime inicializa a partir do default
|
||||||
require_auth = (err == ESP_OK && u8 <= 1) ? u8 : false;
|
charging_current_runtime = charging_current;
|
||||||
if (err != ESP_OK) {
|
ESP_LOGD(TAG, "Runtime charging current initialized from default: %u",
|
||||||
nvs_set_u8(nvs, "require_auth", require_auth);
|
(unsigned)charging_current_runtime);
|
||||||
needs_commit = true;
|
|
||||||
ESP_LOGD(TAG, "Require auth adjusted to: %d", require_auth);
|
// -----------------------------------------
|
||||||
|
// Socket outlet (persisted) + capability gate
|
||||||
|
// -----------------------------------------
|
||||||
|
err = storage_get_u8_sync(NVS_NAMESPACE, "socket_outlet", &u8, rd_to);
|
||||||
|
if (err == ESP_OK && u8 <= 1)
|
||||||
|
{
|
||||||
|
bool wanted = (u8 != 0);
|
||||||
|
|
||||||
|
if (wanted && !board_config.proximity)
|
||||||
|
{
|
||||||
|
// NVS dizia 1, mas HW não suporta -> runtime false e persistir 0
|
||||||
|
socket_outlet = false;
|
||||||
|
|
||||||
|
esp_err_t se = storage_set_u8_sync(NVS_NAMESPACE, "socket_outlet", 0, wr_to);
|
||||||
|
if (se != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to persist socket_outlet=0 (capability mismatch): %s",
|
||||||
|
esp_err_to_name(se));
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGW(TAG, "socket_outlet requested but HW has no proximity -> forcing false (sync persisted)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
socket_outlet = wanted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
socket_outlet = false;
|
||||||
|
|
||||||
|
esp_err_t se = storage_set_u8_sync(NVS_NAMESPACE, "socket_outlet", 0, wr_to);
|
||||||
|
if (se != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to persist socket_outlet default=0: %s", esp_err_to_name(se));
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGW(TAG, "Missing/invalid socket_outlet (%s) -> default=false (sync persisted).",
|
||||||
|
esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
err = nvs_get_u8(nvs, "socket_outlet", &u8);
|
// -----------------------------------------
|
||||||
socket_outlet = (err == ESP_OK && u8) && board_config.proximity;
|
// RCM (persisted) + capability gate
|
||||||
if (err != ESP_OK) {
|
// -----------------------------------------
|
||||||
nvs_set_u8(nvs, "socket_outlet", socket_outlet);
|
err = storage_get_u8_sync(NVS_NAMESPACE, "rcm", &u8, rd_to);
|
||||||
needs_commit = true;
|
if (err == ESP_OK && u8 <= 1)
|
||||||
ESP_LOGD(TAG, "Socket outlet adjusted to: %d", socket_outlet);
|
{
|
||||||
|
bool wanted = (u8 != 0);
|
||||||
|
|
||||||
|
if (wanted && !board_config.rcm)
|
||||||
|
{
|
||||||
|
rcm = false;
|
||||||
|
|
||||||
|
esp_err_t se = storage_set_u8_sync(NVS_NAMESPACE, "rcm", 0, wr_to);
|
||||||
|
if (se != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to persist rcm=0 (capability mismatch): %s",
|
||||||
|
esp_err_to_name(se));
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGW(TAG, "rcm requested but HW has no RCM -> forcing false (sync persisted)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rcm = wanted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rcm = false;
|
||||||
|
|
||||||
|
esp_err_t se = storage_set_u8_sync(NVS_NAMESPACE, "rcm", 0, wr_to);
|
||||||
|
if (se != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to persist rcm default=0: %s", esp_err_to_name(se));
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGW(TAG, "Missing/invalid rcm (%s) -> default=false (sync persisted).",
|
||||||
|
esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
err = nvs_get_u8(nvs, "rcm", &u8);
|
// -----------------------------------------
|
||||||
rcm = (err == ESP_OK && u8) && board_config.rcm;
|
// Temp threshold (persisted)
|
||||||
if (err != ESP_OK) {
|
// -----------------------------------------
|
||||||
nvs_set_u8(nvs, "rcm", rcm);
|
err = storage_get_u8_sync(NVS_NAMESPACE, "temp_threshold", &u8, rd_to);
|
||||||
needs_commit = true;
|
if (err == ESP_OK && u8 >= 40 && u8 <= 80)
|
||||||
ESP_LOGD(TAG, "RCM adjusted to: %d", rcm);
|
{
|
||||||
|
temp_threshold = u8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
temp_threshold = 60;
|
||||||
|
|
||||||
|
esp_err_t se = storage_set_u8_sync(NVS_NAMESPACE, "temp_threshold", temp_threshold, wr_to);
|
||||||
|
if (se != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to persist temp_threshold=%u: %s",
|
||||||
|
(unsigned)temp_threshold, esp_err_to_name(se));
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGW(TAG, "Invalid/missing temp_threshold (%s) -> default=60 (sync persisted).",
|
||||||
|
esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
err = nvs_get_u8(nvs, "temp_threshold", &u8);
|
// -----------------------------------------
|
||||||
temp_threshold = (err == ESP_OK && u8 >= 40 && u8 <= 80) ? u8 : 60;
|
// Availability (persisted) [0/1]
|
||||||
if (err != ESP_OK) {
|
// -----------------------------------------
|
||||||
nvs_set_u8(nvs, "temp_threshold", temp_threshold);
|
err = storage_get_u8_sync(NVS_NAMESPACE, "available", &u8, rd_to);
|
||||||
needs_commit = true;
|
if (err == ESP_OK && u8 <= 1)
|
||||||
ESP_LOGD(TAG, "Temp threshold adjusted to: %d", temp_threshold);
|
{
|
||||||
|
is_available = (u8 != 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
is_available = true;
|
||||||
|
|
||||||
|
esp_err_t se = storage_set_u8_sync(NVS_NAMESPACE, "available", 1, wr_to);
|
||||||
|
if (se != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to persist available=1: %s", esp_err_to_name(se));
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGW(TAG, "Missing/invalid 'available' (%s) -> default=true (sync persisted).",
|
||||||
|
esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additional limits
|
// -----------------------------------------
|
||||||
if (nvs_get_u32(nvs, "def_cons_lim", &u32) == ESP_OK) {
|
// Enabled (persisted) [0/1]
|
||||||
evse_set_consumption_limit(u32);
|
// -----------------------------------------
|
||||||
ESP_LOGD(TAG, "Consumption limit read and applied: %" PRIu32, u32); // Updated to PRIu32
|
err = storage_get_u8_sync(NVS_NAMESPACE, "enabled", &u8, rd_to);
|
||||||
|
if (err == ESP_OK && u8 <= 1)
|
||||||
|
{
|
||||||
|
is_enabled = (u8 != 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
is_enabled = true;
|
||||||
|
|
||||||
|
esp_err_t se = storage_set_u8_sync(NVS_NAMESPACE, "enabled", 1, wr_to);
|
||||||
|
if (se != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to persist enabled=1: %s", esp_err_to_name(se));
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGW(TAG, "Missing/invalid 'enabled' (%s) -> default=true (sync persisted).",
|
||||||
|
esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nvs_get_u32(nvs, "def_ch_time_lim", &u32) == ESP_OK) {
|
// Flush explícito no boot:
|
||||||
evse_set_charging_time_limit(u32);
|
// - ajuda a garantir commit determinístico antes do resto do sistema avançar
|
||||||
ESP_LOGD(TAG, "Charging time limit read and applied: %" PRIu32, u32); // Updated to PRIu32
|
// - mantém-se útil mesmo com setters sync se o teu storage ainda estiver com debounce interno
|
||||||
}
|
esp_err_t fe = storage_flush_sync(wr_to);
|
||||||
|
if (fe != ESP_OK)
|
||||||
if (nvs_get_u16(nvs, "def_un_pwr_lim", &u16) == ESP_OK) {
|
ESP_LOGE(TAG, "storage_flush_sync failed: %s", esp_err_to_name(fe));
|
||||||
evse_set_under_power_limit(u16);
|
|
||||||
ESP_LOGD(TAG, "Under power limit read and applied: %d", u16);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needs_commit) {
|
|
||||||
nvs_commit(nvs);
|
|
||||||
ESP_LOGD(TAG, "Changes committed to NVS.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current
|
// ========================
|
||||||
uint8_t evse_get_max_charging_current(void) {
|
// Charging current getters/setters
|
||||||
ESP_LOGI(TAG, "Max charging current read: %d", max_charging_current);
|
// ========================
|
||||||
return max_charging_current;
|
uint8_t evse_get_max_charging_current(void) { return max_charging_current; }
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t evse_set_max_charging_current(uint8_t value) {
|
uint16_t evse_get_charging_current(void) { return charging_current; }
|
||||||
ESP_LOGI(TAG, "Attempting to set max charging current: %d", value);
|
|
||||||
if (value < MIN_CHARGING_CURRENT_LIMIT || value > MAX_CHARGING_CURRENT_LIMIT)
|
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;
|
return ESP_ERR_INVALID_ARG;
|
||||||
max_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t grid_get_max_current(void) {
|
if (value == charging_current)
|
||||||
ESP_LOGD(TAG, "Grid max current read: %d", grid_max_current);
|
{
|
||||||
return grid_max_current;
|
evse_set_runtime_charging_current(value);
|
||||||
}
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t grid_set_max_current(uint8_t value) {
|
|
||||||
ESP_LOGD(TAG, "Attempting to set grid max current: %d", value);
|
|
||||||
if (value < MIN_GRID_CURRENT_LIMIT || value > MAX_GRID_CURRENT_LIMIT)
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
grid_max_current = value;
|
|
||||||
nvs_set_u8(nvs, "grid_max_curr", value);
|
|
||||||
nvs_commit(nvs);
|
|
||||||
ESP_LOGD(TAG, "Grid max current adjusted to: %d", grid_max_current);
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t evse_get_charging_current(void) {
|
|
||||||
ESP_LOGD(TAG, "Charging current read: %d", charging_current);
|
|
||||||
return charging_current;
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
charging_current = value;
|
charging_current = value;
|
||||||
nvs_set_u16(nvs, "def_chrg_curr", value);
|
|
||||||
nvs_commit(nvs);
|
esp_err_t err = storage_set_u16_async(NVS_NAMESPACE, "def_chrg_curr", value);
|
||||||
ESP_LOGD(TAG, "Charging current adjusted to: %d", charging_current);
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
// Em runtime, isto pode falhar por fila cheia. RAM fica correta; persistência é best-effort.
|
||||||
|
ESP_LOGE(TAG, "Failed to persist def_chrg_curr async=%u: %s", (unsigned)value, esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
evse_set_runtime_charging_current(value);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t evse_get_default_charging_current(void) {
|
// ========================
|
||||||
uint16_t value;
|
// Runtime current (not saved)
|
||||||
nvs_get_u16(nvs, "def_chrg_curr", &value);
|
// ========================
|
||||||
ESP_LOGD(TAG, "Default charging current read: %d", value);
|
void evse_set_runtime_charging_current(uint16_t value)
|
||||||
return 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_err_t evse_set_default_charging_current(uint16_t value) {
|
uint16_t evse_get_runtime_charging_current(void) { return charging_current_runtime; }
|
||||||
ESP_LOGD(TAG, "Attempting to set default charging current: %d", value);
|
|
||||||
if (value < (MIN_CHARGING_CURRENT_LIMIT * 10) || value > (max_charging_current * 10))
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// ========================
|
||||||
// Socket outlet
|
// Socket outlet
|
||||||
bool evse_get_socket_outlet(void) {
|
// ========================
|
||||||
ESP_LOGD(TAG, "Socket outlet read: %d", socket_outlet);
|
bool evse_get_socket_outlet(void) { return socket_outlet; }
|
||||||
return socket_outlet;
|
|
||||||
}
|
esp_err_t evse_set_socket_outlet(bool value)
|
||||||
|
{
|
||||||
|
if (value && !board_config.proximity)
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
if (value == socket_outlet)
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
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;
|
|
||||||
socket_outlet = value;
|
socket_outlet = value;
|
||||||
nvs_set_u8(nvs, "socket_outlet", value);
|
|
||||||
nvs_commit(nvs);
|
esp_err_t err = storage_set_u8_async(NVS_NAMESPACE, "socket_outlet", (uint8_t)value);
|
||||||
ESP_LOGD(TAG, "Socket outlet adjusted to: %d", socket_outlet);
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to persist socket_outlet async=%u: %s", (unsigned)value, esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================
|
||||||
// RCM
|
// RCM
|
||||||
bool evse_is_rcm(void) {
|
// ========================
|
||||||
ESP_LOGD(TAG, "RCM read: %d", rcm);
|
bool evse_is_rcm(void) { return rcm; }
|
||||||
return rcm;
|
|
||||||
}
|
esp_err_t evse_set_rcm(bool value)
|
||||||
|
{
|
||||||
|
if (value && !board_config.rcm)
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
if (value == rcm)
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
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;
|
|
||||||
rcm = value;
|
rcm = value;
|
||||||
nvs_set_u8(nvs, "rcm", value);
|
|
||||||
nvs_commit(nvs);
|
esp_err_t err = storage_set_u8_async(NVS_NAMESPACE, "rcm", (uint8_t)value);
|
||||||
ESP_LOGD(TAG, "RCM adjusted to: %d", rcm);
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to persist rcm async=%u: %s", (unsigned)value, esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================
|
||||||
// Temperature
|
// Temperature
|
||||||
uint8_t evse_get_temp_threshold(void) {
|
// ========================
|
||||||
ESP_LOGD(TAG, "Temp threshold read: %d", temp_threshold);
|
uint8_t evse_get_temp_threshold(void) { return temp_threshold; }
|
||||||
return temp_threshold;
|
|
||||||
}
|
esp_err_t evse_set_temp_threshold(uint8_t value)
|
||||||
|
{
|
||||||
|
if (value < 40 || value > 80)
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
if (value == temp_threshold)
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
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;
|
|
||||||
temp_threshold = value;
|
temp_threshold = value;
|
||||||
nvs_set_u8(nvs, "temp_threshold", value);
|
|
||||||
nvs_commit(nvs);
|
esp_err_t err = storage_set_u8_async(NVS_NAMESPACE, "temp_threshold", value);
|
||||||
ESP_LOGI(TAG, "Temp threshold adjusted to: %d", temp_threshold);
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to persist temp_threshold async=%u: %s", (unsigned)value, esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authentication
|
// ========================
|
||||||
bool evse_is_require_auth(void) {
|
|
||||||
ESP_LOGD(TAG, "Require auth read: %d", require_auth);
|
|
||||||
return require_auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
void evse_set_require_auth(bool value) {
|
|
||||||
ESP_LOGI(TAG, "Attempting to set require auth: %d", value);
|
|
||||||
require_auth = value;
|
|
||||||
nvs_set_u8(nvs, "require_auth", value);
|
|
||||||
nvs_commit(nvs);
|
|
||||||
ESP_LOGD(TAG, "Require auth adjusted to: %d", require_auth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Availability
|
// Availability
|
||||||
static bool is_available = true;
|
// ========================
|
||||||
|
bool evse_config_is_available(void) { return is_available; }
|
||||||
|
|
||||||
bool evse_config_is_available(void) {
|
void evse_config_set_available(bool available)
|
||||||
ESP_LOGD(TAG, "Checking availability: %d", is_available);
|
{
|
||||||
return is_available;
|
bool newv = available;
|
||||||
}
|
if (newv == is_available)
|
||||||
|
return;
|
||||||
void evse_config_set_available(bool available) {
|
|
||||||
ESP_LOGD(TAG, "Setting availability to: %d", available);
|
is_available = newv;
|
||||||
is_available = available;
|
|
||||||
|
esp_err_t err = storage_set_u8_async(NVS_NAMESPACE, "available", (uint8_t)is_available);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
ESP_LOGE(TAG, "Failed to persist 'available' async=%u: %s", (unsigned)is_available, esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================
|
||||||
// Enable/Disable
|
// Enable/Disable
|
||||||
static bool is_enabled = true;
|
// ========================
|
||||||
|
bool evse_config_is_enabled(void) { return is_enabled; }
|
||||||
|
|
||||||
bool evse_config_is_enabled(void) {
|
void evse_config_set_enabled(bool enabled)
|
||||||
ESP_LOGD(TAG, "Checking if enabled: %d", is_enabled);
|
{
|
||||||
return is_enabled;
|
bool newv = enabled;
|
||||||
}
|
if (newv == is_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
void evse_config_set_enabled(bool enabled) {
|
is_enabled = newv;
|
||||||
ESP_LOGD(TAG, "Setting enabled state to: %d", enabled);
|
|
||||||
is_enabled = enabled;
|
esp_err_t err = storage_set_u8_async(NVS_NAMESPACE, "enabled", (uint8_t)is_enabled);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
ESP_LOGE(TAG, "Failed to persist 'enabled' async=%u: %s", (unsigned)is_enabled, esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// evse_core.c - Função principal de controle do EVSE
|
// components/evse/evse_core.c
|
||||||
|
|
||||||
#include "evse_fsm.h"
|
#include "evse_fsm.h"
|
||||||
#include "evse_error.h"
|
#include "evse_error.h"
|
||||||
#include "evse_limits.h"
|
#include "evse_limits.h"
|
||||||
#include "evse_config.h"
|
#include "evse_config.h"
|
||||||
#include "evse_api.h"
|
#include "evse_api.h"
|
||||||
#include "pilot.h"
|
#include "evse_session.h"
|
||||||
|
#include "evse_pilot.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
@@ -13,107 +13,116 @@
|
|||||||
static const char *TAG = "evse_core";
|
static const char *TAG = "evse_core";
|
||||||
|
|
||||||
static SemaphoreHandle_t mutex;
|
static SemaphoreHandle_t mutex;
|
||||||
|
|
||||||
static evse_state_t last_state = EVSE_STATE_A;
|
static evse_state_t last_state = EVSE_STATE_A;
|
||||||
static bool authorized = false;
|
|
||||||
static TickType_t auth_grant_to = 0;
|
|
||||||
|
|
||||||
|
// Filtro simples de histerese no pilot
|
||||||
|
#define PILOT_STABLE_SAMPLES 2
|
||||||
|
|
||||||
|
static pilot_voltage_t s_last_raw = PILOT_VOLTAGE_12;
|
||||||
|
static pilot_voltage_t s_filtered = PILOT_VOLTAGE_12;
|
||||||
|
static int s_stable_count = 0;
|
||||||
|
|
||||||
|
static pilot_voltage_t filter_pilot_voltage(pilot_voltage_t raw)
|
||||||
|
{
|
||||||
|
if (raw == s_last_raw)
|
||||||
|
{
|
||||||
|
if (s_stable_count < PILOT_STABLE_SAMPLES)
|
||||||
|
{
|
||||||
|
s_stable_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_last_raw = raw;
|
||||||
|
s_stable_count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_stable_count >= PILOT_STABLE_SAMPLES && raw != s_filtered)
|
||||||
|
{
|
||||||
|
s_filtered = raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void evse_process(void);
|
||||||
static void evse_core_task(void *arg);
|
static void evse_core_task(void *arg);
|
||||||
|
|
||||||
void evse_init(void) {
|
void evse_init(void)
|
||||||
|
{
|
||||||
ESP_LOGI(TAG, "EVSE Init");
|
ESP_LOGI(TAG, "EVSE Init");
|
||||||
|
|
||||||
mutex = xSemaphoreCreateMutex();
|
mutex = xSemaphoreCreateMutex();
|
||||||
|
if (!mutex)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to create EVSE core mutex");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
evse_check_defaults();
|
evse_check_defaults();
|
||||||
evse_fsm_reset();
|
evse_fsm_reset();
|
||||||
pilot_set_level(true); // Estado inicial do piloto
|
pilot_set_level(true);
|
||||||
|
|
||||||
xTaskCreate(evse_core_task, "evse_core_task", 4096, NULL, 5, NULL);
|
BaseType_t rc = xTaskCreate(evse_core_task, "evse_core_task", 4096, NULL, 6, NULL);
|
||||||
|
configASSERT(rc == pdPASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void evse_process(void) {
|
static void evse_process(void)
|
||||||
|
{
|
||||||
|
if (!mutex)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
xSemaphoreTake(mutex, portMAX_DELAY);
|
xSemaphoreTake(mutex, portMAX_DELAY);
|
||||||
|
|
||||||
pilot_voltage_t pilot_voltage;
|
pilot_voltage_t pilot_raw;
|
||||||
bool is_n12v = false;
|
bool is_n12v = false;
|
||||||
|
|
||||||
pilot_measure(&pilot_voltage, &is_n12v);
|
pilot_measure(&pilot_raw, &is_n12v);
|
||||||
ESP_LOGD(TAG, "Pilot: %d, -12V: %s", pilot_voltage, is_n12v ? "yes" : "no");
|
pilot_voltage_t pilot_voltage = filter_pilot_voltage(pilot_raw);
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Pilot(raw=%d, filt=%d), -12V: %s",
|
||||||
|
pilot_raw, pilot_voltage, is_n12v ? "yes" : "no");
|
||||||
|
|
||||||
|
// raw set/clear; erro visível mantém holdoff interno (60s após sumir)
|
||||||
evse_error_check(pilot_voltage, is_n12v);
|
evse_error_check(pilot_voltage, is_n12v);
|
||||||
|
|
||||||
if (evse_get_error() == 0 && !evse_is_error_cleared()) {
|
// ✅ Sem cooldown externo: disponibilidade depende só do erro "visível"
|
||||||
|
bool available = evse_config_is_available() && (evse_get_error() == 0);
|
||||||
|
bool enabled = evse_config_is_enabled();
|
||||||
|
|
||||||
bool authorized = evse_state_get_authorized();
|
evse_fsm_process(
|
||||||
|
pilot_voltage,
|
||||||
|
evse_state_get_authorized(),
|
||||||
|
available,
|
||||||
|
enabled);
|
||||||
|
|
||||||
evse_fsm_process(
|
evse_limits_check();
|
||||||
pilot_voltage,
|
|
||||||
authorized,
|
|
||||||
evse_config_is_available(),
|
|
||||||
evse_config_is_enabled()
|
|
||||||
);
|
|
||||||
|
|
||||||
evse_limits_check(evse_get_state());
|
if (evse_is_limit_reached())
|
||||||
|
{
|
||||||
evse_state_t current = evse_get_state();
|
if (evse_state_get_authorized())
|
||||||
if (current != last_state) {
|
{
|
||||||
ESP_LOGI(TAG, "State changed: %s → %s", evse_state_to_str(last_state), evse_state_to_str(current));
|
ESP_LOGW(TAG, "Charging limit reached → revoking authorization");
|
||||||
last_state = current;
|
evse_state_set_authorized(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evse_get_error() == 0) {
|
evse_state_t current = evse_get_state();
|
||||||
evse_mark_error_cleared();
|
if (current != last_state)
|
||||||
|
{
|
||||||
|
last_state = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
xSemaphoreGive(mutex);
|
xSemaphoreGive(mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================================
|
static void evse_core_task(void *arg)
|
||||||
// Interface pública
|
{
|
||||||
// ================================
|
(void)arg;
|
||||||
|
while (true)
|
||||||
bool evse_is_enabled(void) {
|
{
|
||||||
return evse_config_is_enabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
void evse_set_enabled(bool value) {
|
|
||||||
ESP_LOGI(TAG, "Set enabled %d", value);
|
|
||||||
evse_config_set_enabled(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool evse_is_available(void) {
|
|
||||||
return evse_config_is_available();
|
|
||||||
}
|
|
||||||
|
|
||||||
void evse_set_available(bool value) {
|
|
||||||
ESP_LOGI(TAG, "Set available %d", value);
|
|
||||||
evse_config_set_available(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool evse_is_pending_auth(void) {
|
|
||||||
return evse_state_is_session(evse_get_state()) && !authorized;
|
|
||||||
}
|
|
||||||
|
|
||||||
void evse_authorize(void) {
|
|
||||||
ESP_LOGI(TAG, "Authorize");
|
|
||||||
evse_state_set_authorized(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void evse_set_authorized(bool value) {
|
|
||||||
ESP_LOGI(TAG, "Set authorized %d", value);
|
|
||||||
xSemaphoreTake(mutex, portMAX_DELAY);
|
|
||||||
authorized = value;
|
|
||||||
xSemaphoreGive(mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================
|
|
||||||
// Tarefa principal
|
|
||||||
// ================================
|
|
||||||
|
|
||||||
static void evse_core_task(void *arg) {
|
|
||||||
while (true) {
|
|
||||||
evse_process();
|
evse_process();
|
||||||
vTaskDelay(pdMS_TO_TICKS(100));
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,119 +1,310 @@
|
|||||||
#include "evse_error.h"
|
#include "evse_error.h"
|
||||||
#include "evse_config.h"
|
|
||||||
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/portmacro.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "ntc_sensor.h"
|
#include "ntc_sensor.h"
|
||||||
|
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "evse_events.h"
|
||||||
|
#include "evse_config.h"
|
||||||
|
|
||||||
static const char *TAG = "evse_error";
|
static const char *TAG = "evse_error";
|
||||||
|
|
||||||
static uint32_t error_bits = 0;
|
// ----------------------------------------------------
|
||||||
static TickType_t auto_clear_timeout = 0;
|
// Estado interno
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// raw_bits = erros “instantâneos” conforme checks (set/clear)
|
||||||
|
// visible_bits = erros expostos ao resto do sistema (com holdoff)
|
||||||
|
// clear_deadline = quando pode finalmente limpar visible_bits para 0
|
||||||
|
static uint32_t raw_bits = 0;
|
||||||
|
static uint32_t visible_bits = 0;
|
||||||
|
static TickType_t clear_deadline = 0;
|
||||||
|
|
||||||
|
// Sticky flag: "todos erros visíveis foram limpos"
|
||||||
static bool error_cleared = false;
|
static bool error_cleared = false;
|
||||||
|
|
||||||
void evse_error_init(void) {
|
// Proteção contra concorrência
|
||||||
// Inicialização do sistema de erros
|
static portMUX_TYPE error_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||||
}
|
|
||||||
|
|
||||||
void evse_error_check(pilot_voltage_t pilot_voltage, bool is_n12v) {
|
// ----------------------------------------------------
|
||||||
ESP_LOGD(TAG, "Verificando erro: pilot_voltage = %d, is_n12v = %s",
|
// Helper: publicar evento de alteração de erro (visible_bits)
|
||||||
pilot_voltage, is_n12v ? "true" : "false");
|
// ----------------------------------------------------
|
||||||
|
static void evse_error_post_event(uint32_t new_bits, uint32_t changed_mask)
|
||||||
|
{
|
||||||
|
evse_error_event_data_t ev = {
|
||||||
|
.error_bits = new_bits,
|
||||||
|
.changed_mask = changed_mask,
|
||||||
|
.timestamp_us = esp_timer_get_time(),
|
||||||
|
};
|
||||||
|
|
||||||
// Falha elétrica geral no pilot
|
esp_err_t err = esp_event_post(
|
||||||
if (pilot_voltage == PILOT_VOLTAGE_1) {
|
EVSE_EVENTS,
|
||||||
if (!(error_bits & EVSE_ERR_PILOT_FAULT_BIT)) { // Verifica se o erro já foi registrado
|
EVSE_EVENT_ERROR_CHANGED,
|
||||||
evse_error_set(EVSE_ERR_PILOT_FAULT_BIT);
|
&ev,
|
||||||
ESP_LOGW(TAG, "Erro: pilot abaixo de 2V (falha)");
|
sizeof(ev),
|
||||||
}
|
portMAX_DELAY);
|
||||||
}
|
|
||||||
|
|
||||||
// Falta de -12V durante PWM (C ou D)
|
if (err != ESP_OK)
|
||||||
if ((pilot_voltage == PILOT_VOLTAGE_6 || pilot_voltage == PILOT_VOLTAGE_3) && !is_n12v) {
|
{
|
||||||
if (!(error_bits & EVSE_ERR_DIODE_SHORT_BIT)) { // Verifica se o erro já foi registrado
|
ESP_LOGW(TAG, "Falha ao publicar EVSE_EVENT_ERROR_CHANGED: %s",
|
||||||
evse_error_set(EVSE_ERR_DIODE_SHORT_BIT);
|
esp_err_to_name(err));
|
||||||
ESP_LOGW(TAG, "Erro: ausência de -12V no PWM (sem diodo)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void evse_temperature_check(void) {
|
// ----------------------------------------------------
|
||||||
float temp_c = ntc_temp_sensor(); // leitura atual (última medida válida)
|
// Helpers internos
|
||||||
uint8_t threshold = evse_get_temp_threshold(); // padrão 60°C, configurável
|
// ----------------------------------------------------
|
||||||
|
static bool raw_has_bit(uint32_t bit)
|
||||||
|
{
|
||||||
|
bool v;
|
||||||
|
portENTER_CRITICAL(&error_mux);
|
||||||
|
v = ((raw_bits & bit) != 0);
|
||||||
|
portEXIT_CRITICAL(&error_mux);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
// Log informativo com os valores
|
static void reconcile_visible_locked(TickType_t now)
|
||||||
ESP_LOGD(TAG, "Verificando temperatura: atual = %.2f °C, limite = %d °C", temp_c, threshold);
|
{
|
||||||
|
// Se existem erros reais, o visível segue imediatamente
|
||||||
// Se a temperatura parecer inválida, aplica erro de sensor
|
if (raw_bits != 0)
|
||||||
if (temp_c < -40.0f || temp_c > 150.0f) {
|
{
|
||||||
if (!(error_bits & EVSE_ERR_TEMPERATURE_FAULT_BIT)) { // Verifica se o erro já foi registrado
|
visible_bits = raw_bits;
|
||||||
evse_error_set(EVSE_ERR_TEMPERATURE_FAULT_BIT);
|
clear_deadline = 0;
|
||||||
ESP_LOGW(TAG, "Sensor NTC falhou ou está desconectado");
|
error_cleared = false;
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
evse_error_clear(EVSE_ERR_TEMPERATURE_FAULT_BIT); // leitura válida
|
// raw_bits == 0
|
||||||
|
if (visible_bits == 0)
|
||||||
if (temp_c >= threshold) {
|
{
|
||||||
if (!(error_bits & EVSE_ERR_TEMPERATURE_HIGH_BIT)) { // Verifica se o erro já foi registrado
|
clear_deadline = 0;
|
||||||
evse_error_set(EVSE_ERR_TEMPERATURE_HIGH_BIT);
|
return;
|
||||||
ESP_LOGW(TAG, "Temperatura acima do limite: %.2f °C ≥ %d °C", temp_c, threshold);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
evse_error_clear(EVSE_ERR_TEMPERATURE_HIGH_BIT);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t evse_get_error(void) {
|
// Ainda há erro visível (holdoff). Arma deadline 1x.
|
||||||
return error_bits;
|
if (clear_deadline == 0)
|
||||||
}
|
{
|
||||||
|
clear_deadline = now + pdMS_TO_TICKS(EVSE_ERROR_COOLDOWN_MS);
|
||||||
bool evse_is_error_cleared(void) {
|
return;
|
||||||
return error_cleared;
|
|
||||||
}
|
|
||||||
|
|
||||||
void evse_mark_error_cleared(void) {
|
|
||||||
error_cleared = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Já existentes
|
|
||||||
void evse_error_set(uint32_t bitmask) {
|
|
||||||
error_bits |= bitmask;
|
|
||||||
|
|
||||||
if (bitmask & EVSE_ERR_AUTO_CLEAR_BITS) {
|
|
||||||
auto_clear_timeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000); // 60s
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void evse_error_clear(uint32_t bitmask) {
|
// Expirou -> limpar finalmente
|
||||||
bool had_error = error_bits != 0;
|
if ((int32_t)(now - clear_deadline) >= 0)
|
||||||
error_bits &= ~bitmask;
|
{
|
||||||
|
visible_bits = 0;
|
||||||
if (had_error && error_bits == 0) {
|
clear_deadline = 0;
|
||||||
error_cleared = true;
|
error_cleared = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void evse_error_tick(void) {
|
// ----------------------------------------------------
|
||||||
if ((error_bits & EVSE_ERR_AUTO_CLEAR_BITS) && xTaskGetTickCount() >= auto_clear_timeout) {
|
// API pública
|
||||||
evse_error_clear(EVSE_ERR_AUTO_CLEAR_BITS);
|
// ----------------------------------------------------
|
||||||
auto_clear_timeout = 0;
|
void evse_error_init(void)
|
||||||
|
{
|
||||||
|
uint32_t old_vis, new_vis, changed;
|
||||||
|
bool post = false;
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&error_mux);
|
||||||
|
|
||||||
|
old_vis = visible_bits;
|
||||||
|
|
||||||
|
raw_bits = 0;
|
||||||
|
visible_bits = 0;
|
||||||
|
clear_deadline = 0;
|
||||||
|
error_cleared = false;
|
||||||
|
|
||||||
|
new_vis = visible_bits;
|
||||||
|
changed = old_vis ^ new_vis;
|
||||||
|
post = (changed != 0);
|
||||||
|
|
||||||
|
portEXIT_CRITICAL(&error_mux);
|
||||||
|
|
||||||
|
if (post)
|
||||||
|
{
|
||||||
|
evse_error_post_event(new_vis, changed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool evse_error_is_active(void) {
|
uint32_t evse_get_error(void)
|
||||||
return error_bits != 0;
|
{
|
||||||
|
portENTER_CRITICAL(&error_mux);
|
||||||
|
uint32_t val = visible_bits;
|
||||||
|
portEXIT_CRITICAL(&error_mux);
|
||||||
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t evse_error_get_bits(void) {
|
bool evse_error_is_active(void)
|
||||||
return error_bits;
|
{
|
||||||
|
return evse_get_error() != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void evse_error_reset_flag(void) {
|
uint32_t evse_error_get_bits(void)
|
||||||
|
{
|
||||||
|
return evse_get_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool evse_error_cleared_flag(void)
|
||||||
|
{
|
||||||
|
portENTER_CRITICAL(&error_mux);
|
||||||
|
bool v = error_cleared;
|
||||||
|
portEXIT_CRITICAL(&error_mux);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_error_reset_flag(void)
|
||||||
|
{
|
||||||
|
portENTER_CRITICAL(&error_mux);
|
||||||
error_cleared = false;
|
error_cleared = false;
|
||||||
|
portEXIT_CRITICAL(&error_mux);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool evse_error_cleared_flag(void) {
|
void evse_error_set(uint32_t bitmask)
|
||||||
return error_cleared;
|
{
|
||||||
|
uint32_t old_vis, new_vis, changed;
|
||||||
|
TickType_t now = xTaskGetTickCount();
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&error_mux);
|
||||||
|
|
||||||
|
old_vis = visible_bits;
|
||||||
|
|
||||||
|
raw_bits |= bitmask;
|
||||||
|
// se aparece qualquer erro, o "cleared" deixa de ser verdade
|
||||||
|
error_cleared = false;
|
||||||
|
|
||||||
|
reconcile_visible_locked(now);
|
||||||
|
|
||||||
|
new_vis = visible_bits;
|
||||||
|
changed = old_vis ^ new_vis;
|
||||||
|
|
||||||
|
portEXIT_CRITICAL(&error_mux);
|
||||||
|
|
||||||
|
if (changed != 0)
|
||||||
|
{
|
||||||
|
evse_error_post_event(new_vis, changed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_error_clear(uint32_t bitmask)
|
||||||
|
{
|
||||||
|
uint32_t old_vis, new_vis, changed;
|
||||||
|
TickType_t now = xTaskGetTickCount();
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&error_mux);
|
||||||
|
|
||||||
|
old_vis = visible_bits;
|
||||||
|
|
||||||
|
raw_bits &= ~bitmask;
|
||||||
|
|
||||||
|
// ✅ Aqui é onde o “60s depois do erro desaparecer” é armado:
|
||||||
|
// quando raw_bits chega a 0, reconcile arma clear_deadline (uma vez)
|
||||||
|
reconcile_visible_locked(now);
|
||||||
|
|
||||||
|
new_vis = visible_bits;
|
||||||
|
changed = old_vis ^ new_vis;
|
||||||
|
|
||||||
|
portEXIT_CRITICAL(&error_mux);
|
||||||
|
|
||||||
|
if (changed != 0)
|
||||||
|
{
|
||||||
|
evse_error_post_event(new_vis, changed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_error_tick(void)
|
||||||
|
{
|
||||||
|
uint32_t old_vis, new_vis, changed;
|
||||||
|
TickType_t now = xTaskGetTickCount();
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&error_mux);
|
||||||
|
|
||||||
|
old_vis = visible_bits;
|
||||||
|
reconcile_visible_locked(now);
|
||||||
|
new_vis = visible_bits;
|
||||||
|
changed = old_vis ^ new_vis;
|
||||||
|
|
||||||
|
portEXIT_CRITICAL(&error_mux);
|
||||||
|
|
||||||
|
if (changed != 0)
|
||||||
|
{
|
||||||
|
evse_error_post_event(new_vis, changed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// Checks (raw -> set/clear)
|
||||||
|
// ----------------------------------------------------
|
||||||
|
void evse_error_check(pilot_voltage_t pilot_voltage, bool is_n12v)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Verificando erro: pilot_voltage=%d, is_n12v=%s",
|
||||||
|
pilot_voltage, is_n12v ? "true" : "false");
|
||||||
|
|
||||||
|
// 1) Falha elétrica geral no pilot
|
||||||
|
if (pilot_voltage == PILOT_VOLTAGE_1)
|
||||||
|
{
|
||||||
|
if (!raw_has_bit(EVSE_ERR_PILOT_FAULT_BIT))
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Erro: pilot abaixo de 2V (falha)");
|
||||||
|
}
|
||||||
|
evse_error_set(EVSE_ERR_PILOT_FAULT_BIT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
evse_error_clear(EVSE_ERR_PILOT_FAULT_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Falta de -12V durante PWM (C ou D)
|
||||||
|
if ((pilot_voltage == PILOT_VOLTAGE_6 || pilot_voltage == PILOT_VOLTAGE_3) && !is_n12v)
|
||||||
|
{
|
||||||
|
if (!raw_has_bit(EVSE_ERR_DIODE_SHORT_BIT))
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Erro: ausência de -12V no PWM (sem diodo)");
|
||||||
|
}
|
||||||
|
evse_error_set(EVSE_ERR_DIODE_SHORT_BIT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
evse_error_clear(EVSE_ERR_DIODE_SHORT_BIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_temperature_check(void)
|
||||||
|
{
|
||||||
|
float temp_c = ntc_temp_sensor();
|
||||||
|
uint8_t threshold = evse_get_temp_threshold();
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Verificando temperatura: atual=%.2f °C, limite=%d °C",
|
||||||
|
temp_c, threshold);
|
||||||
|
|
||||||
|
// Temperatura inválida -> erro de sensor
|
||||||
|
if (temp_c < -40.0f || temp_c > 150.0f)
|
||||||
|
{
|
||||||
|
if (!raw_has_bit(EVSE_ERR_TEMPERATURE_FAULT_BIT))
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Sensor NTC falhou ou está desconectado");
|
||||||
|
}
|
||||||
|
evse_error_set(EVSE_ERR_TEMPERATURE_FAULT_BIT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leitura válida -> limpa erro de sensor
|
||||||
|
evse_error_clear(EVSE_ERR_TEMPERATURE_FAULT_BIT);
|
||||||
|
|
||||||
|
// Temperatura máxima
|
||||||
|
if (temp_c >= threshold)
|
||||||
|
{
|
||||||
|
if (!raw_has_bit(EVSE_ERR_TEMPERATURE_HIGH_BIT))
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Temperatura acima do limite: %.2f °C ≥ %d °C",
|
||||||
|
temp_c, threshold);
|
||||||
|
}
|
||||||
|
evse_error_set(EVSE_ERR_TEMPERATURE_HIGH_BIT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
evse_error_clear(EVSE_ERR_TEMPERATURE_HIGH_BIT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
#include "evse_events.h"
|
#include "evse_events.h"
|
||||||
|
|
||||||
ESP_EVENT_DEFINE_BASE(EVSE_EVENT);
|
ESP_EVENT_DEFINE_BASE(EVSE_EVENTS);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// evse_fsm.c - Máquina de Estados EVSE com controle centralizado
|
// components/evse/evse_fsm.c
|
||||||
|
|
||||||
#include "evse_fsm.h"
|
#include "evse_fsm.h"
|
||||||
#include "evse_api.h"
|
#include "evse_api.h"
|
||||||
#include "pilot.h"
|
#include "evse_pilot.h"
|
||||||
|
#include "evse_config.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "ac_relay.h"
|
#include "ac_relay.h"
|
||||||
#include "board_config.h"
|
#include "board_config.h"
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "proximity.h"
|
#include "proximity.h"
|
||||||
#include "rcm.h"
|
#include "rcm.h"
|
||||||
#include "evse_state.h"
|
#include "evse_state.h"
|
||||||
|
#include "evse_error.h"
|
||||||
|
|
||||||
static const char *TAG = "evse_fsm";
|
static const char *TAG = "evse_fsm";
|
||||||
|
|
||||||
@@ -17,158 +18,245 @@ static const char *TAG = "evse_fsm";
|
|||||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool c1_d1_waiting = false;
|
void evse_fsm_reset(void)
|
||||||
static TickType_t c1_d1_relay_to = 0;
|
{
|
||||||
|
|
||||||
void evse_fsm_reset(void) {
|
|
||||||
evse_set_state(EVSE_STATE_A);
|
evse_set_state(EVSE_STATE_A);
|
||||||
c1_d1_waiting = false;
|
|
||||||
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) {
|
/**
|
||||||
switch (state) {
|
* @brief Atualiza saídas de hardware (pilot, relé, trava) em função do estado lógico.
|
||||||
case EVSE_STATE_A:
|
*/
|
||||||
case EVSE_STATE_E:
|
static void update_outputs(evse_state_t state)
|
||||||
case EVSE_STATE_F:
|
{
|
||||||
|
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 total: qualquer erro ativo força saída segura
|
||||||
|
if (evse_get_error() != 0)
|
||||||
|
{
|
||||||
|
if (ac_relay_get_state())
|
||||||
|
{
|
||||||
ac_relay_set_state(false);
|
ac_relay_set_state(false);
|
||||||
pilot_set_level(state == EVSE_STATE_A);
|
ESP_LOGW(TAG, "ERRO ativo: relé estava ligado, agora desligado por segurança!");
|
||||||
if (board_config.socket_lock && socket_outlet) {
|
}
|
||||||
socket_lock_set_locked(false);
|
else
|
||||||
}
|
{
|
||||||
//energy_meter_stop_session();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EVSE_STATE_B1:
|
|
||||||
pilot_set_level(true);
|
|
||||||
ac_relay_set_state(false);
|
ac_relay_set_state(false);
|
||||||
if (board_config.socket_lock && socket_outlet) {
|
}
|
||||||
socket_lock_set_locked(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rcm_test()) {
|
// Em erro, garantir pilot OFF (não PWM / não +12V)
|
||||||
ESP_LOGI(TAG, "RCM self test passed");
|
pilot_set_level(true);
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "RCM self test failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (socket_outlet) {
|
if (board_config.socket_lock && socket_outlet)
|
||||||
cable_max_current = proximity_get_max_current();
|
{
|
||||||
}
|
socket_lock_set_locked(false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//energy_meter_start_session();
|
switch (state)
|
||||||
break;
|
{
|
||||||
|
case EVSE_STATE_A:
|
||||||
|
case EVSE_STATE_E:
|
||||||
|
case EVSE_STATE_F:
|
||||||
|
ac_relay_set_state(false);
|
||||||
|
|
||||||
case EVSE_STATE_B2:
|
// A → pilot alto (+12V), E/F → pilot OFF
|
||||||
pilot_set_amps(MIN(charging_current, cable_max_current * 10));
|
pilot_set_level(state == EVSE_STATE_A);
|
||||||
ac_relay_set_state(false);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EVSE_STATE_C1:
|
if (board_config.socket_lock && socket_outlet)
|
||||||
case EVSE_STATE_D1:
|
{
|
||||||
pilot_set_level(true);
|
socket_lock_set_locked(false);
|
||||||
c1_d1_waiting = true;
|
}
|
||||||
c1_d1_relay_to = xTaskGetTickCount() + pdMS_TO_TICKS(6000);
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case EVSE_STATE_C2:
|
case EVSE_STATE_B1:
|
||||||
case EVSE_STATE_D2:
|
pilot_set_level(true);
|
||||||
pilot_set_amps(MIN(charging_current, cable_max_current * 10));
|
ac_relay_set_state(false);
|
||||||
ac_relay_set_state(true);
|
|
||||||
break;
|
if (board_config.socket_lock && socket_outlet)
|
||||||
|
{
|
||||||
|
socket_lock_set_locked(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)rcm_test();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVSE_STATE_B2:
|
||||||
|
pilot_set_amps(MIN(current, cable_max_current));
|
||||||
|
ac_relay_set_state(false);
|
||||||
|
|
||||||
|
if (board_config.socket_lock && socket_outlet)
|
||||||
|
{
|
||||||
|
socket_lock_set_locked(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVSE_STATE_C1:
|
||||||
|
case EVSE_STATE_D1:
|
||||||
|
pilot_set_amps(MIN(current, cable_max_current));
|
||||||
|
ac_relay_set_state(false);
|
||||||
|
|
||||||
|
if (board_config.socket_lock && socket_outlet)
|
||||||
|
{
|
||||||
|
socket_lock_set_locked(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVSE_STATE_C2:
|
||||||
|
case EVSE_STATE_D2:
|
||||||
|
pilot_set_amps(MIN(current, cable_max_current));
|
||||||
|
ac_relay_set_state(true);
|
||||||
|
|
||||||
|
if (board_config.socket_lock && socket_outlet)
|
||||||
|
{
|
||||||
|
socket_lock_set_locked(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool available, bool enabled) {
|
/**
|
||||||
TickType_t now = xTaskGetTickCount();
|
* @brief Máquina de estados principal do EVSE (IEC 61851).
|
||||||
evse_state_t previous_state = evse_get_state();
|
*/
|
||||||
evse_state_t current_state = previous_state;
|
void evse_fsm_process(
|
||||||
|
pilot_voltage_t pilot_voltage,
|
||||||
|
bool authorized,
|
||||||
|
bool available,
|
||||||
|
bool enabled)
|
||||||
|
{
|
||||||
|
// 1) Erros globais: dominam qualquer outra lógica
|
||||||
|
uint32_t err_bits = evse_get_error();
|
||||||
|
if (err_bits != 0)
|
||||||
|
{
|
||||||
|
evse_state_t forced_state =
|
||||||
|
(err_bits & EVSE_ERR_PILOT_FAULT_BIT) ? EVSE_STATE_E : EVSE_STATE_F;
|
||||||
|
|
||||||
switch (current_state) {
|
if (evse_get_state() != forced_state)
|
||||||
case EVSE_STATE_A:
|
{
|
||||||
if (!available) {
|
ESP_LOGW(TAG, "Erro ativo detectado: forçando estado %s",
|
||||||
evse_set_state(EVSE_STATE_F);
|
evse_state_to_str(forced_state));
|
||||||
} else if (pilot_voltage == PILOT_VOLTAGE_9) {
|
evse_set_state(forced_state);
|
||||||
evse_set_state(EVSE_STATE_B1);
|
}
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EVSE_STATE_B1:
|
update_outputs(forced_state);
|
||||||
case EVSE_STATE_B2:
|
return;
|
||||||
if (!available) {
|
|
||||||
evse_set_state(EVSE_STATE_F);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (pilot_voltage) {
|
|
||||||
case PILOT_VOLTAGE_12:
|
|
||||||
evse_set_state(EVSE_STATE_A);
|
|
||||||
break;
|
|
||||||
case PILOT_VOLTAGE_9:
|
|
||||||
evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1);
|
|
||||||
break;
|
|
||||||
case PILOT_VOLTAGE_6:
|
|
||||||
evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EVSE_STATE_C1:
|
|
||||||
case EVSE_STATE_D1:
|
|
||||||
if (c1_d1_waiting && now >= c1_d1_relay_to) {
|
|
||||||
ac_relay_set_state(false);
|
|
||||||
c1_d1_waiting = false;
|
|
||||||
if (!available) {
|
|
||||||
evse_set_state(EVSE_STATE_F);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// fallthrough intencional
|
|
||||||
case EVSE_STATE_C2:
|
|
||||||
case EVSE_STATE_D2:
|
|
||||||
if (!enabled || !available) {
|
|
||||||
evse_set_state(
|
|
||||||
(current_state == EVSE_STATE_D2 || current_state == EVSE_STATE_D1)
|
|
||||||
? EVSE_STATE_D1
|
|
||||||
: EVSE_STATE_C1
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (pilot_voltage) {
|
|
||||||
case PILOT_VOLTAGE_6:
|
|
||||||
evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1);
|
|
||||||
break;
|
|
||||||
case PILOT_VOLTAGE_3:
|
|
||||||
evse_set_state((authorized && enabled) ? EVSE_STATE_D2 : EVSE_STATE_D1);
|
|
||||||
break;
|
|
||||||
case PILOT_VOLTAGE_9:
|
|
||||||
evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1);
|
|
||||||
break;
|
|
||||||
case PILOT_VOLTAGE_12:
|
|
||||||
evse_set_state(EVSE_STATE_A);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EVSE_STATE_E:
|
|
||||||
// Sem transições a partir de E
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EVSE_STATE_F:
|
|
||||||
if (available) {
|
|
||||||
evse_set_state(EVSE_STATE_A);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
evse_state_t new_state = evse_get_state();
|
evse_state_t curr = 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));
|
switch (curr)
|
||||||
update_outputs(new_state, evse_get_charging_current(), evse_get_max_charging_current(), evse_get_socket_outlet());
|
{
|
||||||
|
case EVSE_STATE_A:
|
||||||
|
if (!available)
|
||||||
|
{
|
||||||
|
evse_set_state(EVSE_STATE_F);
|
||||||
|
}
|
||||||
|
else if (pilot_voltage == PILOT_VOLTAGE_9)
|
||||||
|
{
|
||||||
|
evse_set_state(EVSE_STATE_B1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVSE_STATE_B1:
|
||||||
|
case EVSE_STATE_B2:
|
||||||
|
if (!available)
|
||||||
|
{
|
||||||
|
evse_set_state(EVSE_STATE_F);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pilot_voltage)
|
||||||
|
{
|
||||||
|
case PILOT_VOLTAGE_12:
|
||||||
|
evse_set_state(EVSE_STATE_A);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PILOT_VOLTAGE_9:
|
||||||
|
evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PILOT_VOLTAGE_6:
|
||||||
|
evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PILOT_VOLTAGE_3:
|
||||||
|
evse_set_state((authorized && enabled) ? EVSE_STATE_D2 : EVSE_STATE_D1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVSE_STATE_C1:
|
||||||
|
case EVSE_STATE_D1:
|
||||||
|
case EVSE_STATE_C2:
|
||||||
|
case EVSE_STATE_D2:
|
||||||
|
if (!available)
|
||||||
|
{
|
||||||
|
evse_set_state(EVSE_STATE_F);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!enabled)
|
||||||
|
{
|
||||||
|
if (curr == EVSE_STATE_C2)
|
||||||
|
{
|
||||||
|
evse_set_state(EVSE_STATE_C1);
|
||||||
|
}
|
||||||
|
else if (curr == EVSE_STATE_D2)
|
||||||
|
{
|
||||||
|
evse_set_state(EVSE_STATE_D1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pilot_voltage)
|
||||||
|
{
|
||||||
|
case PILOT_VOLTAGE_6:
|
||||||
|
evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PILOT_VOLTAGE_3:
|
||||||
|
evse_set_state((authorized && enabled) ? EVSE_STATE_D2 : EVSE_STATE_D1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PILOT_VOLTAGE_9:
|
||||||
|
evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PILOT_VOLTAGE_12:
|
||||||
|
evse_set_state(EVSE_STATE_A);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVSE_STATE_E:
|
||||||
|
// ✅ Agora recupera como F: se disponível e sem erro -> volta a A
|
||||||
|
if (available && evse_get_error() == 0)
|
||||||
|
{
|
||||||
|
evse_set_state(EVSE_STATE_A);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVSE_STATE_F:
|
||||||
|
if (available && evse_get_error() == 0)
|
||||||
|
{
|
||||||
|
evse_set_state(EVSE_STATE_A);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
evse_state_t next = evse_get_state();
|
||||||
|
update_outputs(next);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "evse_hardware.h"
|
#include "evse_hardware.h"
|
||||||
#include "pilot.h"
|
#include "evse_pilot.h"
|
||||||
#include "ac_relay.h"
|
#include "ac_relay.h"
|
||||||
#include "socket_lock.h"
|
#include "socket_lock.h"
|
||||||
#include "proximity.h"
|
#include "proximity.h"
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
static const char *TAG = "evse_hardware";
|
static const char *TAG = "evse_hardware";
|
||||||
|
|
||||||
void evse_hardware_init(void) {
|
void evse_hardware_init(void) {
|
||||||
|
pilot_init();
|
||||||
pilot_set_level(true); // Sinal piloto em 12V (inicial)
|
pilot_set_level(true); // Sinal piloto em 12V (inicial)
|
||||||
ac_relay_set_state(false); // Relé desligado
|
ac_relay_set_state(false); // Relé desligado
|
||||||
//socket_lock_set_locked(false); // Destrava o conector
|
//socket_lock_set_locked(false); // Destrava o conector
|
||||||
|
|||||||
@@ -1,97 +1,301 @@
|
|||||||
#include "evse_limits.h"
|
#include <inttypes.h>
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
// ========================
|
#include "evse_state.h"
|
||||||
// Estado interno
|
#include "evse_api.h"
|
||||||
// ========================
|
#include "evse_limits.h"
|
||||||
|
#include "evse_meter.h"
|
||||||
|
#include "evse_session.h"
|
||||||
|
|
||||||
static bool limit_reached = false;
|
#include "esp_log.h"
|
||||||
static uint32_t consumption_limit = 0;
|
#include "freertos/FreeRTOS.h"
|
||||||
static uint32_t charging_time_limit = 0;
|
#include "freertos/task.h"
|
||||||
static uint16_t under_power_limit = 0;
|
#include "esp_err.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
|
||||||
static uint32_t default_consumption_limit = 0;
|
#include "storage_service.h"
|
||||||
static uint32_t default_charging_time_limit = 0;
|
|
||||||
static uint16_t default_under_power_limit = 0;
|
|
||||||
|
|
||||||
// ========================
|
#define NVS_NAMESPACE "evse_limits"
|
||||||
// Estado de controle
|
static const char *TAG = "evse_limits";
|
||||||
// ========================
|
|
||||||
|
|
||||||
void evse_set_limit_reached(uint8_t value) {
|
static portMUX_TYPE evse_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||||
limit_reached = (value != 0);
|
|
||||||
|
static bool limit_reached = false;
|
||||||
|
static uint32_t consumption_limit = 0; // Wh
|
||||||
|
static uint32_t charging_time_limit = 0; // seconds
|
||||||
|
static uint16_t under_power_limit = 0; // W
|
||||||
|
|
||||||
|
static inline TickType_t TO_TICKS_MS(uint32_t ms) { return pdMS_TO_TICKS(ms); }
|
||||||
|
static inline TickType_t BOOT_TO(void) { return TO_TICKS_MS(1000); }
|
||||||
|
|
||||||
|
// ---------------------------------
|
||||||
|
// Init + defaults
|
||||||
|
// ---------------------------------
|
||||||
|
esp_err_t evse_limits_init(void)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_ERROR(storage_service_init(), TAG, "storage init failed");
|
||||||
|
ESP_LOGI(TAG, "EVSE limits init OK (storage-backed)");
|
||||||
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool evse_is_limit_reached(void) {
|
void evse_limits_check_defaults(void)
|
||||||
return limit_reached;
|
{
|
||||||
}
|
esp_err_t err;
|
||||||
|
bool needs_flush = false;
|
||||||
|
|
||||||
// ========================
|
uint32_t u32 = 0;
|
||||||
// Limites em tempo de execução
|
uint16_t u16 = 0;
|
||||||
// ========================
|
|
||||||
|
|
||||||
uint32_t evse_get_consumption_limit(void) {
|
ESP_LOGD(TAG, "Checking default limits...");
|
||||||
return consumption_limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
void evse_set_consumption_limit(uint32_t value) {
|
// Consumption limit (Wh) default = 0 (disabled)
|
||||||
consumption_limit = value;
|
err = storage_get_u32_sync(NVS_NAMESPACE, "def_cons_lim", &u32, BOOT_TO());
|
||||||
}
|
if (err == ESP_OK)
|
||||||
|
{
|
||||||
|
portENTER_CRITICAL(&evse_mux);
|
||||||
|
consumption_limit = u32;
|
||||||
|
portEXIT_CRITICAL(&evse_mux);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
portENTER_CRITICAL(&evse_mux);
|
||||||
|
consumption_limit = 0;
|
||||||
|
portEXIT_CRITICAL(&evse_mux);
|
||||||
|
|
||||||
uint32_t evse_get_charging_time_limit(void) {
|
(void)storage_set_u32_async(NVS_NAMESPACE, "def_cons_lim", 0);
|
||||||
return charging_time_limit;
|
needs_flush = true;
|
||||||
}
|
ESP_LOGW(TAG, "Missing def_cons_lim (%s) -> default=0 (persisted).", esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
|
||||||
void evse_set_charging_time_limit(uint32_t value) {
|
// Charging time limit (s) default = 0 (disabled)
|
||||||
charging_time_limit = value;
|
err = storage_get_u32_sync(NVS_NAMESPACE, "def_ch_time_lim", &u32, BOOT_TO());
|
||||||
}
|
if (err == ESP_OK)
|
||||||
|
{
|
||||||
|
portENTER_CRITICAL(&evse_mux);
|
||||||
|
charging_time_limit = u32;
|
||||||
|
portEXIT_CRITICAL(&evse_mux);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
portENTER_CRITICAL(&evse_mux);
|
||||||
|
charging_time_limit = 0;
|
||||||
|
portEXIT_CRITICAL(&evse_mux);
|
||||||
|
|
||||||
uint16_t evse_get_under_power_limit(void) {
|
(void)storage_set_u32_async(NVS_NAMESPACE, "def_ch_time_lim", 0);
|
||||||
return under_power_limit;
|
needs_flush = true;
|
||||||
}
|
ESP_LOGW(TAG, "Missing def_ch_time_lim (%s) -> default=0 (persisted).", esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
|
||||||
void evse_set_under_power_limit(uint16_t value) {
|
// Under-power limit (W) default = 0 (disabled)
|
||||||
under_power_limit = value;
|
err = storage_get_u16_sync(NVS_NAMESPACE, "def_un_pwr_lim", &u16, BOOT_TO());
|
||||||
}
|
if (err == ESP_OK)
|
||||||
|
{
|
||||||
|
portENTER_CRITICAL(&evse_mux);
|
||||||
|
under_power_limit = u16;
|
||||||
|
portEXIT_CRITICAL(&evse_mux);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
portENTER_CRITICAL(&evse_mux);
|
||||||
|
under_power_limit = 0;
|
||||||
|
portEXIT_CRITICAL(&evse_mux);
|
||||||
|
|
||||||
// ========================
|
(void)storage_set_u16_async(NVS_NAMESPACE, "def_un_pwr_lim", 0);
|
||||||
// Limites padrão (persistentes)
|
needs_flush = true;
|
||||||
// ========================
|
ESP_LOGW(TAG, "Missing def_un_pwr_lim (%s) -> default=0 (persisted).", esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t evse_get_default_consumption_limit(void) {
|
if (needs_flush)
|
||||||
return default_consumption_limit;
|
{
|
||||||
}
|
esp_err_t fe = storage_flush_sync(TO_TICKS_MS(2000));
|
||||||
|
if (fe != ESP_OK)
|
||||||
void evse_set_default_consumption_limit(uint32_t value) {
|
ESP_LOGE(TAG, "storage_flush_sync failed: %s", esp_err_to_name(fe));
|
||||||
default_consumption_limit = value;
|
else
|
||||||
}
|
ESP_LOGD(TAG, "Defaults committed (flush).");
|
||||||
|
|
||||||
uint32_t evse_get_default_charging_time_limit(void) {
|
|
||||||
return default_charging_time_limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
void evse_set_default_charging_time_limit(uint32_t value) {
|
|
||||||
default_charging_time_limit = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t evse_get_default_under_power_limit(void) {
|
|
||||||
return default_under_power_limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------
|
||||||
|
// Limit reached flag
|
||||||
|
// ---------------------------------
|
||||||
|
bool evse_get_limit_reached(void)
|
||||||
|
{
|
||||||
|
bool val;
|
||||||
|
portENTER_CRITICAL(&evse_mux);
|
||||||
|
val = limit_reached;
|
||||||
|
portEXIT_CRITICAL(&evse_mux);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_set_limit_reached(bool v)
|
||||||
|
{
|
||||||
|
portENTER_CRITICAL(&evse_mux);
|
||||||
|
limit_reached = v;
|
||||||
|
portEXIT_CRITICAL(&evse_mux);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool evse_is_limit_reached(void)
|
||||||
|
{
|
||||||
|
return evse_get_limit_reached();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------
|
||||||
|
// Consumption limit
|
||||||
|
// ---------------------------------
|
||||||
|
uint32_t evse_get_consumption_limit(void)
|
||||||
|
{
|
||||||
|
uint32_t val;
|
||||||
|
portENTER_CRITICAL(&evse_mux);
|
||||||
|
val = consumption_limit;
|
||||||
|
portEXIT_CRITICAL(&evse_mux);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_set_consumption_limit(uint32_t value)
|
||||||
|
{
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&evse_mux);
|
||||||
|
if (consumption_limit != value)
|
||||||
|
{
|
||||||
|
consumption_limit = value;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&evse_mux);
|
||||||
|
|
||||||
|
if (!changed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
esp_err_t err = storage_set_u32_async(NVS_NAMESPACE, "def_cons_lim", value);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG,
|
||||||
|
"Failed to persist consumption limit (%" PRIu32 " Wh): %s",
|
||||||
|
value, esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------
|
||||||
|
// Charging time limit
|
||||||
|
// ---------------------------------
|
||||||
|
uint32_t evse_get_charging_time_limit(void)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&evse_mux);
|
||||||
|
if (charging_time_limit != value)
|
||||||
|
{
|
||||||
|
charging_time_limit = value;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&evse_mux);
|
||||||
|
|
||||||
|
if (!changed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
esp_err_t err = storage_set_u32_async(NVS_NAMESPACE, "def_ch_time_lim", value);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG,
|
||||||
|
"Failed to persist charging time limit (%" PRIu32 " s): %s",
|
||||||
|
value, esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------
|
||||||
|
// Under-power limit
|
||||||
|
// ---------------------------------
|
||||||
|
uint16_t evse_get_under_power_limit(void)
|
||||||
|
{
|
||||||
|
uint16_t val;
|
||||||
|
portENTER_CRITICAL(&evse_mux);
|
||||||
|
val = under_power_limit;
|
||||||
|
portEXIT_CRITICAL(&evse_mux);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_set_under_power_limit(uint16_t value)
|
||||||
|
{
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&evse_mux);
|
||||||
|
if (under_power_limit != value)
|
||||||
|
{
|
||||||
|
under_power_limit = value;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&evse_mux);
|
||||||
|
|
||||||
|
if (!changed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
esp_err_t err = storage_set_u16_async(NVS_NAMESPACE, "def_un_pwr_lim", value);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG,
|
||||||
|
"Failed to persist under-power limit (%" PRIu32 " W): %s",
|
||||||
|
(uint32_t)value, esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------
|
||||||
|
// Runtime check
|
||||||
|
// ---------------------------------
|
||||||
|
void evse_limits_check(void)
|
||||||
|
{
|
||||||
|
// Só faz sentido quando há energia ativa (C2/D2)
|
||||||
|
if (!evse_state_is_charging(evse_get_state()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
evse_session_t sess;
|
||||||
|
if (!evse_session_get(&sess) || !sess.is_current)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32_t cons_lim;
|
||||||
|
uint32_t time_lim;
|
||||||
|
uint16_t unp_lim;
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&evse_mux);
|
||||||
|
cons_lim = consumption_limit;
|
||||||
|
time_lim = charging_time_limit;
|
||||||
|
unp_lim = under_power_limit;
|
||||||
|
portEXIT_CRITICAL(&evse_mux);
|
||||||
|
|
||||||
|
bool reached = false;
|
||||||
|
|
||||||
|
if (cons_lim > 0 && sess.energy_wh >= cons_lim)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Energy limit reached: %" PRIu32 " Wh ≥ %" PRIu32 " Wh",
|
||||||
|
sess.energy_wh, cons_lim);
|
||||||
|
reached = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time_lim > 0 && sess.duration_s >= time_lim)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Charging time limit reached: %" PRIu32 " s ≥ %" PRIu32 " s",
|
||||||
|
sess.duration_s, time_lim);
|
||||||
|
reached = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t p = evse_meter_get_instant_power();
|
||||||
|
uint32_t inst_power = (p > 0) ? (uint32_t)p : 0;
|
||||||
|
|
||||||
|
if (unp_lim > 0 && inst_power < (uint32_t)unp_lim)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Under-power limit reached: %" PRIu32 " W < %" PRIu32 " W",
|
||||||
|
inst_power, (uint32_t)unp_lim);
|
||||||
|
reached = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reached)
|
||||||
|
evse_set_limit_reached(true);
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,121 +4,375 @@
|
|||||||
#include "evse_hardware.h"
|
#include "evse_hardware.h"
|
||||||
#include "evse_config.h"
|
#include "evse_config.h"
|
||||||
#include "evse_api.h"
|
#include "evse_api.h"
|
||||||
#include "auth.h"
|
#include "evse_meter.h"
|
||||||
|
#include "evse_session.h"
|
||||||
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
#include "freertos/queue.h"
|
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include "auth_events.h"
|
||||||
|
#include "loadbalancer_events.h"
|
||||||
|
#include "ocpp_events.h"
|
||||||
|
#include "scheduler_events.h"
|
||||||
|
|
||||||
static const char *TAG = "EVSE_Manager";
|
static const char *TAG = "EVSE_Manager";
|
||||||
|
|
||||||
static TickType_t auth_expiration = 0;
|
|
||||||
|
|
||||||
static SemaphoreHandle_t evse_mutex;
|
static SemaphoreHandle_t evse_mutex;
|
||||||
static QueueHandle_t auth_event_queue = NULL;
|
|
||||||
|
|
||||||
#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo
|
// ✅ Proteção para flags partilhadas (event handlers vs task)
|
||||||
|
static portMUX_TYPE s_mgr_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||||
|
static bool auth_enabled = false;
|
||||||
|
|
||||||
// ===== Task de ciclo principal =====
|
// Estado de pausa controlado pelo Load Balancer
|
||||||
static void evse_manager_task(void *arg) {
|
static bool lb_paused = false;
|
||||||
while (true) {
|
static bool lb_prev_authorized = false;
|
||||||
|
|
||||||
|
// Estado de janela do scheduler
|
||||||
|
static bool s_sched_allowed = true;
|
||||||
|
static portMUX_TYPE s_sched_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||||
|
|
||||||
|
#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo
|
||||||
|
|
||||||
|
static void lb_clear_pause_state(void)
|
||||||
|
{
|
||||||
|
portENTER_CRITICAL(&s_mgr_mux);
|
||||||
|
lb_paused = false;
|
||||||
|
lb_prev_authorized = false;
|
||||||
|
portEXIT_CRITICAL(&s_mgr_mux);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool evse_sched_is_allowed(void)
|
||||||
|
{
|
||||||
|
bool v;
|
||||||
|
portENTER_CRITICAL(&s_sched_mux);
|
||||||
|
v = s_sched_allowed;
|
||||||
|
portEXIT_CRITICAL(&s_sched_mux);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void evse_manager_handle_auth_on_tick(void)
|
||||||
|
{
|
||||||
|
bool sched_allowed = evse_sched_is_allowed();
|
||||||
|
uint32_t err_bits = evse_get_error(); // inclui holdoff interno
|
||||||
|
bool has_error = (err_bits != 0);
|
||||||
|
|
||||||
|
bool local_auth_enabled;
|
||||||
|
bool local_lb_paused;
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_mgr_mux);
|
||||||
|
local_auth_enabled = auth_enabled;
|
||||||
|
local_lb_paused = lb_paused;
|
||||||
|
portEXIT_CRITICAL(&s_mgr_mux);
|
||||||
|
|
||||||
|
if (local_auth_enabled)
|
||||||
|
{
|
||||||
|
if (evse_state_get_authorized() && evse_get_state() == EVSE_STATE_A)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Vehicle disconnected → revoking authorization.");
|
||||||
|
evse_state_set_authorized(false);
|
||||||
|
lb_clear_pause_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_error && evse_state_get_authorized())
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"[AUTH] error active (err=0x%08" PRIx32 ") → revoking authorization.",
|
||||||
|
err_bits);
|
||||||
|
evse_state_set_authorized(false);
|
||||||
|
lb_clear_pause_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sched_allowed && evse_state_get_authorized())
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "[SCHED] window closed (auth mode) → revoking authorization.");
|
||||||
|
evse_state_set_authorized(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool limit_hit = evse_is_limit_reached();
|
||||||
|
bool can_operate = evse_config_is_available() && evse_config_is_enabled();
|
||||||
|
|
||||||
|
if ((has_error || limit_hit || !sched_allowed || !can_operate || local_lb_paused) &&
|
||||||
|
evse_state_get_authorized())
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"[OPEN] blocking (err=%d limit=%d sched=%d operate=%d lb_paused=%d) → revoking authorization.",
|
||||||
|
(int)has_error, (int)limit_hit, (int)sched_allowed, (int)can_operate, (int)local_lb_paused);
|
||||||
|
evse_state_set_authorized(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!local_lb_paused && sched_allowed && can_operate &&
|
||||||
|
!has_error && !limit_hit &&
|
||||||
|
!evse_state_get_authorized())
|
||||||
|
{
|
||||||
|
evse_state_set_authorized(true);
|
||||||
|
ESP_LOGI(TAG, "Authentication disabled → forced authorization (schedule ok, no error/limits).");
|
||||||
|
lb_clear_pause_state();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void evse_manager_task(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
evse_manager_tick();
|
evse_manager_tick();
|
||||||
vTaskDelay(pdMS_TO_TICKS(EVSE_MANAGER_TICK_PERIOD_MS));
|
vTaskDelay(pdMS_TO_TICKS(EVSE_MANAGER_TICK_PERIOD_MS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void evse_auth_event_task(void *arg) {
|
static void on_auth_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||||
auth_event_t evt;
|
{
|
||||||
|
(void)arg;
|
||||||
|
if (base != AUTH_EVENTS || !data) return;
|
||||||
|
|
||||||
while (true) {
|
switch (id)
|
||||||
if (xQueueReceive(auth_event_queue, &evt, portMAX_DELAY)) {
|
{
|
||||||
ESP_LOGI(TAG, "Evento de autenticação recebido: %s (%s)",
|
case AUTH_EVENT_TAG_PROCESSED:
|
||||||
evt.tag, evt.authorized ? "AUTORIZADO" : "NEGADO");
|
{
|
||||||
|
const auth_tag_event_data_t *evt = (const auth_tag_event_data_t *)data;
|
||||||
|
ESP_LOGI(TAG, "Tag %s -> %s", evt->tag, evt->authorized ? "AUTHORIZED" : "DENIED");
|
||||||
|
evse_state_set_authorized(evt->authorized);
|
||||||
|
lb_clear_pause_state();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (evt.authorized) {
|
case AUTH_EVENT_MODE_CHANGED:
|
||||||
evse_authorize();
|
case AUTH_EVENT_INIT:
|
||||||
auth_expiration = xTaskGetTickCount() + pdMS_TO_TICKS(2 * 60 * 1000); // 2 minutos
|
{
|
||||||
} else {
|
const auth_mode_event_data_t *evt = (const auth_mode_event_data_t *)data;
|
||||||
evse_manager_set_authorized(false);
|
ESP_LOGI(TAG, "Auth mode = %s", auth_mode_to_str(evt->mode));
|
||||||
ESP_LOGW(TAG, "Tag inválida, carregamento negado.");
|
|
||||||
|
portENTER_CRITICAL(&s_mgr_mux);
|
||||||
|
auth_enabled = (evt->mode != AUTH_MODE_OPEN);
|
||||||
|
portEXIT_CRITICAL(&s_mgr_mux);
|
||||||
|
|
||||||
|
evse_state_set_authorized(false);
|
||||||
|
lb_clear_pause_state();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void *event_data)
|
||||||
|
{
|
||||||
|
(void)handler_arg;
|
||||||
|
(void)event_base;
|
||||||
|
|
||||||
|
if (!event_data) return;
|
||||||
|
|
||||||
|
if (event_id == LOADBALANCER_EVENT_INIT || event_id == LOADBALANCER_EVENT_STATE_CHANGED)
|
||||||
|
{
|
||||||
|
const loadbalancer_state_event_t *evt = (const loadbalancer_state_event_t *)event_data;
|
||||||
|
ESP_LOGI(TAG, "Loadbalancer %s (ts: %lld)",
|
||||||
|
evt->enabled ? "ENABLED" : "DISABLED",
|
||||||
|
(long long)evt->timestamp_us);
|
||||||
|
}
|
||||||
|
else if (event_id == LOADBALANCER_EVENT_MASTER_CURRENT_LIMIT)
|
||||||
|
{
|
||||||
|
const loadbalancer_master_limit_event_t *evt =
|
||||||
|
(const loadbalancer_master_limit_event_t *)event_data;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Novo limite de corrente (master): %u A (ts: %lld)",
|
||||||
|
evt->max_current, (long long)evt->timestamp_us);
|
||||||
|
|
||||||
|
if (evt->max_current == 0)
|
||||||
|
{
|
||||||
|
bool prev_auth = evse_state_get_authorized();
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_mgr_mux);
|
||||||
|
lb_paused = true;
|
||||||
|
lb_prev_authorized = prev_auth;
|
||||||
|
portEXIT_CRITICAL(&s_mgr_mux);
|
||||||
|
|
||||||
|
if (prev_auth)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "[LB] limit=0A → pausando sessão (authorized=false)");
|
||||||
|
evse_state_set_authorized(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
evse_set_runtime_charging_current(evt->max_current);
|
||||||
|
|
||||||
|
bool was_paused;
|
||||||
|
bool prev_auth;
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_mgr_mux);
|
||||||
|
was_paused = lb_paused;
|
||||||
|
prev_auth = lb_prev_authorized;
|
||||||
|
portEXIT_CRITICAL(&s_mgr_mux);
|
||||||
|
|
||||||
|
if (was_paused)
|
||||||
|
{
|
||||||
|
bool can_resume =
|
||||||
|
(evse_get_error() == 0) &&
|
||||||
|
evse_config_is_available() &&
|
||||||
|
evse_config_is_enabled() &&
|
||||||
|
evse_sched_is_allowed() &&
|
||||||
|
!evse_is_limit_reached();
|
||||||
|
|
||||||
|
if (!can_resume)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG,
|
||||||
|
"[LB] limit=%uA → não retoma automaticamente (erro/indisp/desab/fora de horário/limite)",
|
||||||
|
evt->max_current);
|
||||||
|
lb_clear_pause_state();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool local_auth_enabled;
|
||||||
|
portENTER_CRITICAL(&s_mgr_mux);
|
||||||
|
local_auth_enabled = auth_enabled;
|
||||||
|
lb_paused = false; // já vai tentar retomar
|
||||||
|
portEXIT_CRITICAL(&s_mgr_mux);
|
||||||
|
|
||||||
|
if (!local_auth_enabled)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "[LB] limit=%uA → modo OPEN, reautorizando", evt->max_current);
|
||||||
|
evse_state_set_authorized(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (prev_auth)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "[LB] limit=%uA → RFID/OCPP, retomando autorização anterior", evt->max_current);
|
||||||
|
evse_state_set_authorized(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_mgr_mux);
|
||||||
|
lb_prev_authorized = false;
|
||||||
|
portEXIT_CRITICAL(&s_mgr_mux);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
if (base != OCPP_EVENTS) return;
|
||||||
|
|
||||||
// ===== Inicialização dos módulos do EVSE =====
|
switch (id)
|
||||||
void evse_manager_init(void) {
|
{
|
||||||
|
case OCPP_EVENT_AUTHORIZED:
|
||||||
|
ESP_LOGI(TAG, "[OCPP] Authorized");
|
||||||
|
evse_state_set_authorized(true);
|
||||||
|
lb_clear_pause_state();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OCPP_EVENT_AUTH_REJECTED:
|
||||||
|
case OCPP_EVENT_AUTH_TIMEOUT:
|
||||||
|
case OCPP_EVENT_REMOTE_STOP:
|
||||||
|
case OCPP_EVENT_STOP_TX:
|
||||||
|
ESP_LOGW(TAG, "[OCPP] Authorization/Stop");
|
||||||
|
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_START_TX:
|
||||||
|
ESP_LOGI(TAG, "[OCPP] StartTx");
|
||||||
|
lb_clear_pause_state();
|
||||||
|
break;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
evse_config_set_enabled(ev->operative);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
ESP_LOGD(TAG, "[OCPP] Unhandled event id=%" PRId32, id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_sched_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
if (base != SCHED_EVENTS || data == NULL) return;
|
||||||
|
|
||||||
|
const sched_event_state_t *ev = (const sched_event_state_t *)data;
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_sched_mux);
|
||||||
|
s_sched_allowed = ev->allowed_now;
|
||||||
|
portEXIT_CRITICAL(&s_sched_mux);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "[SCHED] allowed_now=%d", (int)ev->allowed_now);
|
||||||
|
|
||||||
|
if (!ev->allowed_now && evse_state_get_authorized())
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "[SCHED] window closed → stopping session (authorized=false)");
|
||||||
|
evse_state_set_authorized(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_manager_init(void)
|
||||||
|
{
|
||||||
evse_mutex = xSemaphoreCreateMutex();
|
evse_mutex = xSemaphoreCreateMutex();
|
||||||
|
configASSERT(evse_mutex != NULL);
|
||||||
|
|
||||||
|
esp_err_t err = evse_config_init();
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to init EVSE config NVS: %s", esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
|
||||||
evse_config_init();
|
|
||||||
evse_error_init();
|
evse_error_init();
|
||||||
evse_hardware_init();
|
evse_hardware_init();
|
||||||
evse_state_init();
|
evse_state_init();
|
||||||
|
evse_meter_init();
|
||||||
|
evse_session_init();
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS, ESP_EVENT_ANY_ID, &on_auth_event, NULL));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(LOADBALANCER_EVENTS, ESP_EVENT_ANY_ID, &on_loadbalancer_event, NULL));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(OCPP_EVENTS, ESP_EVENT_ANY_ID, &on_ocpp_event, NULL));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(SCHED_EVENTS, ESP_EVENT_ANY_ID, &on_sched_event, NULL));
|
||||||
|
|
||||||
ESP_LOGI(TAG, "EVSE Manager inicializado.");
|
ESP_LOGI(TAG, "EVSE Manager inicializado.");
|
||||||
|
|
||||||
xTaskCreate(evse_manager_task, "evse_manager_task", 4096, NULL, 5, NULL);
|
BaseType_t rc = xTaskCreate(evse_manager_task, "evse_manager_task", 8192, NULL, 4, NULL);
|
||||||
|
configASSERT(rc == pdPASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Inicia processamento de eventos de autenticação =====
|
void evse_manager_tick(void)
|
||||||
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) {
|
|
||||||
xSemaphoreTake(evse_mutex, portMAX_DELAY);
|
xSemaphoreTake(evse_mutex, portMAX_DELAY);
|
||||||
|
|
||||||
evse_hardware_tick();
|
evse_hardware_tick();
|
||||||
evse_error_tick();
|
evse_error_tick();
|
||||||
evse_state_tick();
|
evse_state_tick();
|
||||||
evse_temperature_check();
|
evse_temperature_check();
|
||||||
|
evse_session_tick();
|
||||||
|
|
||||||
// Verifica expiração de autorização somente se auth está habilitado
|
evse_manager_handle_auth_on_tick();
|
||||||
if (auth_is_enabled()) {
|
|
||||||
if (evse_state_get_authorized() && auth_expiration > 0 &&
|
|
||||||
xTaskGetTickCount() >= auth_expiration) {
|
|
||||||
ESP_LOGI(TAG, "Autorização expirada após 2 minutos.");
|
|
||||||
evse_state_set_authorized(false);
|
|
||||||
auth_expiration = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Se autenticação não é necessária, sempre considera autorizado
|
|
||||||
if (!evse_state_get_authorized()) {
|
|
||||||
evse_state_set_authorized(true);
|
|
||||||
ESP_LOGI(TAG, "Autenticação desativada: autorização forçada.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
xSemaphoreGive(evse_mutex);
|
xSemaphoreGive(evse_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Controles e status =====
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|||||||
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_LOGD(TAG,
|
||||||
|
"Meter updated: power[W]={%" PRIu32 ",%" PRIu32 ",%" PRIu32 "}, "
|
||||||
|
"voltage[V]={%.2f,%.2f,%.2f}, "
|
||||||
|
"current[A]={%.2f,%.2f,%.2f}, "
|
||||||
|
"total_energy=%" PRIu32 "Wh",
|
||||||
|
meter_data.power_watts[0], meter_data.power_watts[1], meter_data.power_watts[2],
|
||||||
|
meter_data.voltage[0], meter_data.voltage[1], meter_data.voltage[2],
|
||||||
|
meter_data.current[0], meter_data.current[1], meter_data.current[2],
|
||||||
|
meter_data.energy_wh);
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_meter_init(void)
|
||||||
|
{
|
||||||
|
meter_mutex = xSemaphoreCreateMutex();
|
||||||
|
ESP_ERROR_CHECK(meter_mutex ? ESP_OK : ESP_FAIL);
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(
|
||||||
|
METER_EVENT, METER_EVENT_DATA_READY,
|
||||||
|
on_meter_event_dispatcher, NULL));
|
||||||
|
memset(&meter_data, 0, sizeof(meter_data));
|
||||||
|
ESP_LOGI(TAG, "EVSE Meter listener registered.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int evse_meter_get_instant_power(void)
|
||||||
|
{
|
||||||
|
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
||||||
|
int sum = 0;
|
||||||
|
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
|
||||||
|
{
|
||||||
|
sum += meter_data.power_watts[i];
|
||||||
|
}
|
||||||
|
xSemaphoreGive(meter_mutex);
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
int evse_meter_get_total_energy(void)
|
||||||
|
{
|
||||||
|
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
||||||
|
int val = meter_data.energy_wh;
|
||||||
|
xSemaphoreGive(meter_mutex);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_meter_get_power(int power[EVSE_METER_PHASE_COUNT])
|
||||||
|
{
|
||||||
|
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
||||||
|
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
|
||||||
|
{
|
||||||
|
power[i] = meter_data.power_watts[i];
|
||||||
|
}
|
||||||
|
xSemaphoreGive(meter_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_meter_get_voltage(float voltage[EVSE_METER_PHASE_COUNT])
|
||||||
|
{
|
||||||
|
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
||||||
|
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
|
||||||
|
{
|
||||||
|
voltage[i] = meter_data.voltage[i];
|
||||||
|
}
|
||||||
|
xSemaphoreGive(meter_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_meter_get_current(float current[EVSE_METER_PHASE_COUNT])
|
||||||
|
{
|
||||||
|
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
||||||
|
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
|
||||||
|
{
|
||||||
|
current[i] = meter_data.current[i];
|
||||||
|
}
|
||||||
|
xSemaphoreGive(meter_mutex);
|
||||||
|
}
|
||||||
243
components/evse/evse_pilot.c
Executable file
243
components/evse/evse_pilot.c
Executable file
@@ -0,0 +1,243 @@
|
|||||||
|
// components/evse/evse_pilot.c
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "driver/ledc.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_rom_sys.h"
|
||||||
|
|
||||||
|
#include "evse_pilot.h"
|
||||||
|
#include "adc121s021_dma.h"
|
||||||
|
#include "board_config.h"
|
||||||
|
|
||||||
|
#define PILOT_PWM_TIMER LEDC_TIMER_0
|
||||||
|
#define PILOT_PWM_CHANNEL LEDC_CHANNEL_0
|
||||||
|
#define PILOT_PWM_SPEED_MODE LEDC_LOW_SPEED_MODE
|
||||||
|
#define PILOT_PWM_DUTY_RES LEDC_TIMER_10_BIT
|
||||||
|
#define PILOT_PWM_MAX_DUTY 1023
|
||||||
|
|
||||||
|
// --- Configuração de amostragem do Pilot ---
|
||||||
|
#define NUM_PILOT_SAMPLES 100
|
||||||
|
#define MAX_SAMPLE_ATTEMPTS 1000
|
||||||
|
|
||||||
|
#define PILOT_SAMPLE_DELAY_US 10
|
||||||
|
|
||||||
|
// Percentagem para descartar extremos superior/inferior (ruído)
|
||||||
|
#define PILOT_EXTREME_PERCENT 10 // 10% superior e inferior
|
||||||
|
|
||||||
|
// ADC referência
|
||||||
|
#define ADC121_VREF_MV 3300
|
||||||
|
#define ADC121_MAX 4095
|
||||||
|
|
||||||
|
static const char *TAG = "evse_pilot";
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PILOT_MODE_DC_HIGH = 0, // +12V (nível alto)
|
||||||
|
PILOT_MODE_DC_LOW, // nível baixo / pilot desligado (dependente do hardware)
|
||||||
|
PILOT_MODE_PWM // PWM ativo
|
||||||
|
} pilot_mode_t;
|
||||||
|
|
||||||
|
static pilot_mode_t s_mode = PILOT_MODE_DC_LOW;
|
||||||
|
static uint32_t last_pwm_duty = 0;
|
||||||
|
|
||||||
|
// ---------------------
|
||||||
|
// Helpers internos
|
||||||
|
// ---------------------
|
||||||
|
static int adc_raw_to_mv(uint16_t raw)
|
||||||
|
{
|
||||||
|
return (int)((raw * ADC121_VREF_MV) / ADC121_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compare_uint16(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
uint16_t va = *(const uint16_t *)a;
|
||||||
|
uint16_t vb = *(const uint16_t *)b;
|
||||||
|
if (va < vb) return -1;
|
||||||
|
if (va > vb) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------
|
||||||
|
// Inicialização PWM + ADC
|
||||||
|
// ---------------------
|
||||||
|
void pilot_init(void)
|
||||||
|
{
|
||||||
|
// Configura timer do PWM do Pilot (1 kHz)
|
||||||
|
ledc_timer_config_t ledc_timer = {
|
||||||
|
.speed_mode = PILOT_PWM_SPEED_MODE,
|
||||||
|
.timer_num = PILOT_PWM_TIMER,
|
||||||
|
.duty_resolution = PILOT_PWM_DUTY_RES,
|
||||||
|
.freq_hz = 1000, // 1 kHz (IEC 61851)
|
||||||
|
.clk_cfg = LEDC_AUTO_CLK
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
|
||||||
|
|
||||||
|
// Canal do PWM no pino configurado em board_config
|
||||||
|
ledc_channel_config_t ledc_channel = {
|
||||||
|
.speed_mode = PILOT_PWM_SPEED_MODE,
|
||||||
|
.channel = PILOT_PWM_CHANNEL,
|
||||||
|
.timer_sel = PILOT_PWM_TIMER,
|
||||||
|
.intr_type = LEDC_INTR_DISABLE,
|
||||||
|
.gpio_num = board_config.pilot_pwm_gpio,
|
||||||
|
.duty = 0,
|
||||||
|
.hpoint = 0
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
||||||
|
|
||||||
|
// Garante que começa parado e em idle baixo (pilot off)
|
||||||
|
ESP_ERROR_CHECK(ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, 0));
|
||||||
|
s_mode = PILOT_MODE_DC_LOW;
|
||||||
|
last_pwm_duty = 0;
|
||||||
|
|
||||||
|
// Inicializa driver do ADC121S021
|
||||||
|
adc121s021_dma_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------
|
||||||
|
// Controlo do modo do Pilot
|
||||||
|
// ---------------------
|
||||||
|
void pilot_set_level(bool high)
|
||||||
|
{
|
||||||
|
pilot_mode_t target = high ? PILOT_MODE_DC_HIGH : PILOT_MODE_DC_LOW;
|
||||||
|
|
||||||
|
// Se já estiver no modo DC desejado e sem PWM ativo, ignora
|
||||||
|
if (s_mode == target && last_pwm_duty == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Pilot set DC level: %s", high ? "HIGH(+12V)" : "LOW/OFF");
|
||||||
|
|
||||||
|
// Para PWM e fixa o nível idle do GPIO
|
||||||
|
ESP_ERROR_CHECK(ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, high ? 1 : 0));
|
||||||
|
|
||||||
|
s_mode = target;
|
||||||
|
last_pwm_duty = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pilot_set_amps(uint16_t amps)
|
||||||
|
{
|
||||||
|
if (amps < 6 || amps > 80)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Invalid ampere value: %d A (valid: 6–80 A)", amps);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t duty_percent;
|
||||||
|
if (amps <= 51)
|
||||||
|
{
|
||||||
|
duty_percent = (amps * 10) / 6;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
duty_percent = (amps * 10) / 25 + 64;
|
||||||
|
}
|
||||||
|
if (duty_percent > 100) duty_percent = 100;
|
||||||
|
|
||||||
|
uint32_t duty = (PILOT_PWM_MAX_DUTY * duty_percent) / 100;
|
||||||
|
|
||||||
|
// Se já estiver em PWM com o mesmo duty, ignora
|
||||||
|
if (s_mode == PILOT_MODE_PWM && last_pwm_duty == duty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_mode = PILOT_MODE_PWM;
|
||||||
|
last_pwm_duty = duty;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Pilot set PWM: %d A → %d/%d (≈ %d%% duty)",
|
||||||
|
amps, (int)duty, PILOT_PWM_MAX_DUTY, (int)duty_percent);
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(ledc_set_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, duty));
|
||||||
|
ESP_ERROR_CHECK(ledc_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pilot_get_state(void)
|
||||||
|
{
|
||||||
|
// "Alto" significa DC +12V (estado A). PWM não conta como DC high.
|
||||||
|
return (s_mode == PILOT_MODE_DC_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------
|
||||||
|
// Medição do sinal de Pilot (PWM 1 kHz J1772)
|
||||||
|
// ---------------------
|
||||||
|
void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "pilot_measure");
|
||||||
|
|
||||||
|
uint16_t samples[NUM_PILOT_SAMPLES];
|
||||||
|
int collected = 0;
|
||||||
|
int attempts = 0;
|
||||||
|
|
||||||
|
while (collected < NUM_PILOT_SAMPLES && attempts < MAX_SAMPLE_ATTEMPTS)
|
||||||
|
{
|
||||||
|
uint16_t adc_sample;
|
||||||
|
|
||||||
|
if (adc121s021_dma_get_sample(&adc_sample))
|
||||||
|
{
|
||||||
|
samples[collected++] = adc_sample;
|
||||||
|
esp_rom_delay_us(PILOT_SAMPLE_DELAY_US);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
esp_rom_delay_us(100);
|
||||||
|
attempts++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collected < NUM_PILOT_SAMPLES)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Timeout on sample read (%d/%d)", collected, NUM_PILOT_SAMPLES);
|
||||||
|
*up_voltage = PILOT_VOLTAGE_1;
|
||||||
|
*down_voltage_n12 = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ordena as amostras para eliminar extremos (ruído/espúrios)
|
||||||
|
qsort(samples, collected, sizeof(uint16_t), compare_uint16);
|
||||||
|
|
||||||
|
int k = (collected * PILOT_EXTREME_PERCENT) / 100;
|
||||||
|
if (k < 2) k = 2; // garante margem mínima
|
||||||
|
|
||||||
|
// descarta k/2 em cada lado (aprox. 10% total, mantendo simetria)
|
||||||
|
int low_index = k / 2;
|
||||||
|
int high_index = collected - 1 - (k / 2);
|
||||||
|
|
||||||
|
if (low_index < 0) low_index = 0;
|
||||||
|
if (high_index >= collected) high_index = collected - 1;
|
||||||
|
if (high_index <= low_index) high_index = low_index;
|
||||||
|
|
||||||
|
uint16_t low_raw = samples[low_index];
|
||||||
|
uint16_t high_raw = samples[high_index];
|
||||||
|
|
||||||
|
int high_mv = adc_raw_to_mv(high_raw);
|
||||||
|
int low_mv = adc_raw_to_mv(low_raw);
|
||||||
|
|
||||||
|
// Determina o nível positivo (+12, +9, +6, +3 ou <3 V)
|
||||||
|
if (high_mv >= board_config.pilot_down_threshold_12)
|
||||||
|
{
|
||||||
|
*up_voltage = PILOT_VOLTAGE_12;
|
||||||
|
}
|
||||||
|
else if (high_mv >= board_config.pilot_down_threshold_9)
|
||||||
|
{
|
||||||
|
*up_voltage = PILOT_VOLTAGE_9;
|
||||||
|
}
|
||||||
|
else if (high_mv >= board_config.pilot_down_threshold_6)
|
||||||
|
{
|
||||||
|
*up_voltage = PILOT_VOLTAGE_6;
|
||||||
|
}
|
||||||
|
else if (high_mv >= board_config.pilot_down_threshold_3)
|
||||||
|
{
|
||||||
|
*up_voltage = PILOT_VOLTAGE_3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*up_voltage = PILOT_VOLTAGE_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica se o nível negativo atinge -12 V (diodo presente, C/D válidos)
|
||||||
|
*down_voltage_n12 = (low_mv <= board_config.pilot_down_threshold_n12);
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Final: up_voltage=%d, down_voltage_n12=%d (high=%d mV, low=%d mV)",
|
||||||
|
*up_voltage, *down_voltage_n12, high_mv, low_mv);
|
||||||
|
}
|
||||||
222
components/evse/evse_session.c
Normal file
222
components/evse/evse_session.c
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
#include <inttypes.h>
|
||||||
|
#include "evse_session.h"
|
||||||
|
#include "evse_meter.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "evse_events.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "evse_limits.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
|
||||||
|
#define EVSE_EVENT_POST_TIMEOUT_MS 50
|
||||||
|
|
||||||
|
static const char *TAG = "evse_session";
|
||||||
|
|
||||||
|
static TickType_t session_start_tick = 0;
|
||||||
|
|
||||||
|
// Tempo real (microsegundos)
|
||||||
|
static int64_t session_start_us = 0;
|
||||||
|
static int64_t last_tick_us = 0;
|
||||||
|
|
||||||
|
// Energia integrada com tempo real: soma de (W * us)
|
||||||
|
static uint64_t watt_microseconds = 0;
|
||||||
|
|
||||||
|
static evse_session_t last_session;
|
||||||
|
static bool last_session_valid = false;
|
||||||
|
static uint32_t session_counter = 0;
|
||||||
|
|
||||||
|
static portMUX_TYPE session_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||||
|
|
||||||
|
static void post_session_event(const evse_session_event_data_t *evt)
|
||||||
|
{
|
||||||
|
esp_err_t err = esp_event_post(
|
||||||
|
EVSE_EVENTS,
|
||||||
|
EVSE_EVENT_SESSION,
|
||||||
|
evt,
|
||||||
|
sizeof(*evt),
|
||||||
|
portMAX_DELAY);
|
||||||
|
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "esp_event_post(EVSE_EVENT_SESSION) failed: %s", esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_session_init(void)
|
||||||
|
{
|
||||||
|
portENTER_CRITICAL(&session_mux);
|
||||||
|
session_start_tick = 0;
|
||||||
|
session_start_us = 0;
|
||||||
|
last_tick_us = 0;
|
||||||
|
watt_microseconds = 0;
|
||||||
|
last_session_valid = false;
|
||||||
|
session_counter = 0;
|
||||||
|
portEXIT_CRITICAL(&session_mux);
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_session_start(void)
|
||||||
|
{
|
||||||
|
TickType_t tick = xTaskGetTickCount();
|
||||||
|
int64_t now_us = esp_timer_get_time();
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&session_mux);
|
||||||
|
session_start_tick = tick;
|
||||||
|
session_start_us = now_us;
|
||||||
|
last_tick_us = now_us;
|
||||||
|
watt_microseconds = 0;
|
||||||
|
session_counter++;
|
||||||
|
uint32_t id = session_counter;
|
||||||
|
portEXIT_CRITICAL(&session_mux);
|
||||||
|
|
||||||
|
evse_set_limit_reached(false);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Session started (id=%" PRIu32 ") tick=%u us=%" PRId64,
|
||||||
|
id, (unsigned)tick, now_us);
|
||||||
|
|
||||||
|
evse_session_event_data_t evt = {
|
||||||
|
.type = EVSE_SESSION_EVENT_STARTED,
|
||||||
|
.session_id = id,
|
||||||
|
.duration_s = 0,
|
||||||
|
.energy_wh = 0,
|
||||||
|
.avg_power_w = 0,
|
||||||
|
.is_current = true,
|
||||||
|
};
|
||||||
|
post_session_event(&evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_session_end(void)
|
||||||
|
{
|
||||||
|
TickType_t start_tick;
|
||||||
|
int64_t start_us;
|
||||||
|
uint64_t w_us;
|
||||||
|
uint32_t id;
|
||||||
|
|
||||||
|
int64_t end_us = esp_timer_get_time();
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&session_mux);
|
||||||
|
if (session_start_tick == 0)
|
||||||
|
{
|
||||||
|
portEXIT_CRITICAL(&session_mux);
|
||||||
|
ESP_LOGW(TAG, "evse_session_end called without active session");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
start_tick = session_start_tick;
|
||||||
|
start_us = session_start_us;
|
||||||
|
w_us = watt_microseconds;
|
||||||
|
id = session_counter;
|
||||||
|
|
||||||
|
session_start_tick = 0;
|
||||||
|
session_start_us = 0;
|
||||||
|
last_tick_us = 0;
|
||||||
|
watt_microseconds = 0;
|
||||||
|
portEXIT_CRITICAL(&session_mux);
|
||||||
|
|
||||||
|
uint32_t duration_s = (end_us > start_us) ? (uint32_t)((end_us - start_us) / 1000000LL) : 0;
|
||||||
|
uint32_t energy_wh = (uint32_t)(w_us / (3600ULL * 1000000ULL));
|
||||||
|
uint64_t watt_seconds = (uint64_t)(w_us / 1000000ULL);
|
||||||
|
uint32_t avg_power = (duration_s > 0) ? (uint32_t)(watt_seconds / duration_s) : 0;
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&session_mux);
|
||||||
|
last_session.start_tick = start_tick;
|
||||||
|
last_session.duration_s = duration_s;
|
||||||
|
last_session.energy_wh = energy_wh;
|
||||||
|
last_session.avg_power_w = avg_power;
|
||||||
|
last_session.is_current = false;
|
||||||
|
last_session_valid = true;
|
||||||
|
portEXIT_CRITICAL(&session_mux);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"Session ended (id=%" PRIu32 "): duration=%" PRIu32 " s, energy=%" PRIu32
|
||||||
|
" Wh, avg_power=%" PRIu32 " W",
|
||||||
|
id, duration_s, energy_wh, avg_power);
|
||||||
|
|
||||||
|
evse_session_event_data_t evt = {
|
||||||
|
.type = EVSE_SESSION_EVENT_FINISHED,
|
||||||
|
.session_id = id,
|
||||||
|
.duration_s = duration_s,
|
||||||
|
.energy_wh = energy_wh,
|
||||||
|
.avg_power_w = avg_power,
|
||||||
|
.is_current = false,
|
||||||
|
};
|
||||||
|
post_session_event(&evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void evse_session_tick(void)
|
||||||
|
{
|
||||||
|
// Potência instantânea pode ser negativa (ruído/overflow de sensor) -> clamp
|
||||||
|
int p = evse_meter_get_instant_power();
|
||||||
|
uint32_t power_w = (p > 0) ? (uint32_t)p : 0;
|
||||||
|
|
||||||
|
int64_t now_us = esp_timer_get_time();
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&session_mux);
|
||||||
|
if (session_start_tick != 0)
|
||||||
|
{
|
||||||
|
if (last_tick_us == 0)
|
||||||
|
{
|
||||||
|
last_tick_us = now_us;
|
||||||
|
}
|
||||||
|
int64_t dt_us = now_us - last_tick_us;
|
||||||
|
if (dt_us > 0)
|
||||||
|
{
|
||||||
|
// Energia incremental: W * us (64-bit)
|
||||||
|
watt_microseconds += ((uint64_t)power_w * (uint64_t)dt_us);
|
||||||
|
last_tick_us = now_us;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// relógio não devia andar para trás; ignora
|
||||||
|
last_tick_us = now_us;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&session_mux);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool evse_session_get(evse_session_t *out)
|
||||||
|
{
|
||||||
|
if (out == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TickType_t start_tick;
|
||||||
|
int64_t start_us;
|
||||||
|
uint64_t w_us;
|
||||||
|
bool has_current;
|
||||||
|
evse_session_t last_copy;
|
||||||
|
bool last_valid;
|
||||||
|
|
||||||
|
int64_t now_us = esp_timer_get_time();
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&session_mux);
|
||||||
|
start_tick = session_start_tick;
|
||||||
|
start_us = session_start_us;
|
||||||
|
w_us = watt_microseconds;
|
||||||
|
has_current = (session_start_tick != 0);
|
||||||
|
last_copy = last_session;
|
||||||
|
last_valid = last_session_valid;
|
||||||
|
portEXIT_CRITICAL(&session_mux);
|
||||||
|
|
||||||
|
if (has_current)
|
||||||
|
{
|
||||||
|
uint32_t duration_s = (now_us > start_us) ? (uint32_t)((now_us - start_us) / 1000000LL) : 0;
|
||||||
|
uint32_t energy_wh = (uint32_t)(w_us / (3600ULL * 1000000ULL));
|
||||||
|
uint64_t watt_seconds = (uint64_t)(w_us / 1000000ULL);
|
||||||
|
uint32_t avg_power = (duration_s > 0) ? (uint32_t)(watt_seconds / duration_s) : 0;
|
||||||
|
|
||||||
|
out->start_tick = 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_valid)
|
||||||
|
{
|
||||||
|
*out = last_copy;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -1,73 +1,207 @@
|
|||||||
|
#include "evse_api.h"
|
||||||
#include "evse_state.h"
|
#include "evse_state.h"
|
||||||
|
#include "evse_session.h"
|
||||||
|
#include "evse_events.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/portmacro.h"
|
#include "freertos/portmacro.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
|
||||||
|
#define EVSE_EVENT_POST_TIMEOUT_MS 50
|
||||||
|
|
||||||
static evse_state_t current_state = EVSE_STATE_A;
|
static evse_state_t current_state = EVSE_STATE_A;
|
||||||
static bool is_authorized = false;
|
static bool is_authorized = false;
|
||||||
|
|
||||||
// Proteção básica para variáveis globais em sistemas concorrentes
|
|
||||||
static portMUX_TYPE state_mux = portMUX_INITIALIZER_UNLOCKED;
|
static portMUX_TYPE state_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||||
|
static const char *TAG = "evse_state";
|
||||||
|
|
||||||
void evse_set_state(evse_state_t state) {
|
static evse_state_event_t map_state_to_event(evse_state_t s)
|
||||||
portENTER_CRITICAL(&state_mux);
|
{
|
||||||
current_state = state;
|
switch (s)
|
||||||
portEXIT_CRITICAL(&state_mux);
|
{
|
||||||
|
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:
|
||||||
|
case EVSE_STATE_D1:
|
||||||
|
case EVSE_STATE_D2:
|
||||||
|
return EVSE_STATE_EVENT_CHARGING;
|
||||||
|
|
||||||
|
case EVSE_STATE_E:
|
||||||
|
case EVSE_STATE_F:
|
||||||
|
return EVSE_STATE_EVENT_FAULT;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return EVSE_STATE_EVENT_IDLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
evse_state_t evse_get_state(void) {
|
static void post_evse_event(evse_event_id_t id, const void *data, size_t len)
|
||||||
|
{
|
||||||
|
esp_err_t err = esp_event_post(
|
||||||
|
EVSE_EVENTS,
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
len,
|
||||||
|
portMAX_DELAY);
|
||||||
|
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "esp_event_post(id=%d) failed: %s", (int)id, esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool evse_state_is_charging(evse_state_t state)
|
||||||
|
{
|
||||||
|
// “charging” == energia efetiva (relé ON)
|
||||||
|
return (state == EVSE_STATE_C2 || state == EVSE_STATE_D2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool evse_state_is_power_flowing(evse_state_t state)
|
||||||
|
{
|
||||||
|
return evse_state_is_charging(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool evse_state_is_requesting(evse_state_t state)
|
||||||
|
{
|
||||||
|
// EV pediu carga mas o relé ainda está OFF
|
||||||
|
return (state == EVSE_STATE_C1 || state == EVSE_STATE_D1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool evse_state_is_plugged(evse_state_t state)
|
||||||
|
{
|
||||||
|
return state == EVSE_STATE_B1 || state == EVSE_STATE_B2 ||
|
||||||
|
state == EVSE_STATE_C1 || state == EVSE_STATE_C2 ||
|
||||||
|
state == EVSE_STATE_D1 || state == EVSE_STATE_D2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool evse_state_is_session(evse_state_t state)
|
||||||
|
{
|
||||||
|
// Sessão lógica: “autorizado/pronto” ou “a pedir/a fornecer energia”
|
||||||
|
return (state == EVSE_STATE_B2 ||
|
||||||
|
state == EVSE_STATE_C1 || state == EVSE_STATE_C2 ||
|
||||||
|
state == EVSE_STATE_D1 || state == EVSE_STATE_D2);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&state_mux);
|
||||||
|
prev_state = current_state;
|
||||||
|
|
||||||
|
if (new_state != current_state)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Sessão começa quando entra em energia (relé ON)
|
||||||
|
if (evse_state_is_power_flowing(new_state) && !evse_state_is_power_flowing(prev_state))
|
||||||
|
{
|
||||||
|
start_session = true;
|
||||||
|
}
|
||||||
|
// Sessão termina quando sai de energia
|
||||||
|
else if (!evse_state_is_power_flowing(new_state) && evse_state_is_power_flowing(prev_state))
|
||||||
|
{
|
||||||
|
end_session = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_state = new_state;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&state_mux);
|
||||||
|
|
||||||
|
// Fora da região crítica
|
||||||
|
if (start_session)
|
||||||
|
{
|
||||||
|
evse_session_start();
|
||||||
|
}
|
||||||
|
if (end_session)
|
||||||
|
{
|
||||||
|
evse_session_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "State changed: %s → %s",
|
||||||
|
evse_state_to_str(prev_state),
|
||||||
|
evse_state_to_str(new_state));
|
||||||
|
|
||||||
|
evse_state_event_data_t evt = {
|
||||||
|
.state = map_state_to_event(new_state)};
|
||||||
|
post_evse_event(EVSE_EVENT_STATE_CHANGED, &evt, sizeof(evt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
evse_state_t evse_get_state(void)
|
||||||
|
{
|
||||||
portENTER_CRITICAL(&state_mux);
|
portENTER_CRITICAL(&state_mux);
|
||||||
evse_state_t s = current_state;
|
evse_state_t s = current_state;
|
||||||
portEXIT_CRITICAL(&state_mux);
|
portEXIT_CRITICAL(&state_mux);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* evse_state_to_str(evse_state_t state) {
|
const char *evse_state_to_str(evse_state_t state)
|
||||||
switch (state) {
|
{
|
||||||
case EVSE_STATE_A: return "A - EV Not Connected (12V)";
|
switch (state)
|
||||||
case EVSE_STATE_B1: return "B1 - EV Connected (9V, Not Authorized)";
|
{
|
||||||
case EVSE_STATE_B2: return "B2 - EV Connected (9V, Authorized and Ready)";
|
case EVSE_STATE_A:
|
||||||
case EVSE_STATE_C1: return "C1 - Charging Requested (6V, Relay Off)";
|
return "A - EV Not Connected (12V)";
|
||||||
case EVSE_STATE_C2: return "C2 - Charging Active (6V, Relay On)";
|
case EVSE_STATE_B1:
|
||||||
case EVSE_STATE_D1: return "D1 - Ventilation Required (3V, Relay Off)";
|
return "B1 - EV Connected (9V, Not Authorized)";
|
||||||
case EVSE_STATE_D2: return "D2 - Ventilation Active (3V, Relay On)";
|
case EVSE_STATE_B2:
|
||||||
case EVSE_STATE_E: return "E - Error: Control Pilot Shorted to Ground (0V)";
|
return "B2 - EV Connected (9V, Authorized and Ready)";
|
||||||
case EVSE_STATE_F: return "F - Fault: EVSE Unavailable or No Pilot Signal";
|
case EVSE_STATE_C1:
|
||||||
default: return "Unknown State";
|
return "C1 - Charging Requested (6V, Relay Off)";
|
||||||
|
case EVSE_STATE_C2:
|
||||||
|
return "C2 - Charging Active (6V, Relay On)";
|
||||||
|
case EVSE_STATE_D1:
|
||||||
|
return "D1 - Ventilation Required (3V, Relay Off)";
|
||||||
|
case EVSE_STATE_D2:
|
||||||
|
return "D2 - Ventilation Active (3V, Relay On)";
|
||||||
|
case EVSE_STATE_E:
|
||||||
|
return "E - Error: Control Pilot Shorted to Ground (0V)";
|
||||||
|
case EVSE_STATE_F:
|
||||||
|
return "F - Fault: EVSE Unavailable or No Pilot Signal";
|
||||||
|
default:
|
||||||
|
return "Unknown State";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void evse_state_init(void) {
|
void evse_state_init(void)
|
||||||
|
{
|
||||||
portENTER_CRITICAL(&state_mux);
|
portENTER_CRITICAL(&state_mux);
|
||||||
current_state = EVSE_STATE_A;
|
current_state = EVSE_STATE_A;
|
||||||
is_authorized = false;
|
is_authorized = false;
|
||||||
portEXIT_CRITICAL(&state_mux);
|
portEXIT_CRITICAL(&state_mux);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Initialized in state: %s", evse_state_to_str(current_state));
|
||||||
|
|
||||||
|
evse_state_event_data_t evt = {
|
||||||
|
.state = map_state_to_event(current_state)};
|
||||||
|
post_evse_event(EVSE_EVENT_INIT, &evt, sizeof(evt));
|
||||||
}
|
}
|
||||||
|
|
||||||
void evse_state_tick(void) {
|
void evse_state_tick(void)
|
||||||
// Tick do estado (placeholder)
|
{
|
||||||
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
bool evse_state_is_charging(evse_state_t state) {
|
void evse_state_set_authorized(bool authorized)
|
||||||
return state == EVSE_STATE_C1 || state == EVSE_STATE_C2;
|
{
|
||||||
}
|
|
||||||
|
|
||||||
bool evse_state_is_plugged(evse_state_t state) {
|
|
||||||
return state == EVSE_STATE_B1 || state == EVSE_STATE_B2 ||
|
|
||||||
state == EVSE_STATE_C1 || state == EVSE_STATE_C2 ||
|
|
||||||
state == EVSE_STATE_D1 || state == EVSE_STATE_D2;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool evse_state_is_session(evse_state_t state) {
|
|
||||||
return state == EVSE_STATE_B2 || state == EVSE_STATE_C1 || state == EVSE_STATE_C2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void evse_state_set_authorized(bool authorized) {
|
|
||||||
portENTER_CRITICAL(&state_mux);
|
portENTER_CRITICAL(&state_mux);
|
||||||
is_authorized = authorized;
|
is_authorized = authorized;
|
||||||
portEXIT_CRITICAL(&state_mux);
|
portEXIT_CRITICAL(&state_mux);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool evse_state_get_authorized(void) {
|
bool evse_state_get_authorized(void)
|
||||||
|
{
|
||||||
portENTER_CRITICAL(&state_mux);
|
portENTER_CRITICAL(&state_mux);
|
||||||
bool result = is_authorized;
|
bool result = is_authorized;
|
||||||
portEXIT_CRITICAL(&state_mux);
|
portEXIT_CRITICAL(&state_mux);
|
||||||
|
|||||||
@@ -3,68 +3,104 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "esp_err.h"
|
#include "evse_state.h"
|
||||||
#include "evse_state.h" // Define evse_state_t
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "evse_session.h"
|
||||||
|
|
||||||
// Inicialização
|
|
||||||
void evse_init(void);
|
|
||||||
void evse_process(void);
|
|
||||||
|
|
||||||
// Estado
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// Core EVSE State
|
||||||
|
// ===============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get current EVSE state (e.g., A, B1, C2).
|
||||||
|
*/
|
||||||
evse_state_t evse_get_state(void);
|
evse_state_t evse_get_state(void);
|
||||||
const char* evse_state_to_str(evse_state_t state);
|
|
||||||
bool evse_is_connector_plugged(evse_state_t state);
|
/**
|
||||||
|
* @brief Set the EVSE state (e.g., called by FSM or hardware layer).
|
||||||
|
*/
|
||||||
|
void evse_set_state(evse_state_t state);
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// Charging Session Info
|
||||||
|
// ===============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieve statistics of either the current ongoing session (if any)
|
||||||
|
* or the last completed session.
|
||||||
|
* @param out pointer to evse_session_t to be filled
|
||||||
|
* @return true if there is a current or last session available
|
||||||
|
*/
|
||||||
|
bool evse_get_session(evse_session_t *out);
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// Charging Session Info
|
||||||
|
// ===============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if the EV is charging (C1 or C2).
|
||||||
|
*/
|
||||||
|
bool evse_state_is_charging(evse_state_t state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if the EV is connected (plugged).
|
||||||
|
*/
|
||||||
|
bool evse_state_is_plugged(evse_state_t state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if a charging session is active (B2, C1, C2).
|
||||||
|
*/
|
||||||
|
bool evse_state_is_session(evse_state_t state);
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// Authorization
|
||||||
|
// ===============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set whether the vehicle is authorized to charge.
|
||||||
|
*/
|
||||||
|
void evse_state_set_authorized(bool authorized);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get current authorization status.
|
||||||
|
*/
|
||||||
|
bool evse_state_get_authorized(void);
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// Configuration / Availability
|
||||||
|
// ===============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable or disable the EVSE (software flag, persisted in NVS).
|
||||||
|
*/
|
||||||
|
void evse_set_enabled(bool value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if the EVSE is currently available for use.
|
||||||
|
*/
|
||||||
|
bool evse_is_available(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set EVSE availability flag (may be persisted in NVS).
|
||||||
|
*/
|
||||||
|
void evse_set_available(bool value);
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// Limit Status
|
||||||
|
// ===============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if any runtime charging limit has been reached.
|
||||||
|
*/
|
||||||
bool evse_is_limit_reached(void);
|
bool evse_is_limit_reached(void);
|
||||||
|
|
||||||
// Autorização e disponibilidade
|
#ifdef __cplusplus
|
||||||
bool evse_is_enabled(void);
|
}
|
||||||
void evse_set_enabled(bool value);
|
#endif
|
||||||
bool evse_is_available(void);
|
|
||||||
void evse_set_available(bool value);
|
|
||||||
bool evse_is_require_auth(void);
|
|
||||||
void evse_set_require_auth(bool value);
|
|
||||||
void evse_authorize(void);
|
|
||||||
bool evse_is_pending_auth(void);
|
|
||||||
void evse_set_authorized(bool value);
|
|
||||||
|
|
||||||
// Corrente
|
|
||||||
uint16_t evse_get_charging_current(void);
|
|
||||||
esp_err_t evse_set_charging_current(uint16_t value);
|
|
||||||
uint16_t evse_get_default_charging_current(void);
|
|
||||||
esp_err_t evse_set_default_charging_current(uint16_t value);
|
|
||||||
uint8_t evse_get_max_charging_current(void);
|
|
||||||
esp_err_t evse_set_max_charging_current(uint8_t value);
|
|
||||||
|
|
||||||
// Grid
|
|
||||||
uint8_t grid_get_max_current(void);
|
|
||||||
esp_err_t grid_set_max_current(uint8_t value);
|
|
||||||
|
|
||||||
// Temperatura
|
|
||||||
uint8_t evse_get_temp_threshold(void);
|
|
||||||
esp_err_t evse_set_temp_threshold(uint8_t value);
|
|
||||||
|
|
||||||
// RCM / Socket
|
|
||||||
bool evse_get_socket_outlet(void);
|
|
||||||
esp_err_t evse_set_socket_outlet(bool value);
|
|
||||||
bool evse_is_rcm(void);
|
|
||||||
esp_err_t evse_set_rcm(bool value);
|
|
||||||
|
|
||||||
// Limites
|
|
||||||
uint32_t evse_get_consumption_limit(void);
|
|
||||||
void evse_set_consumption_limit(uint32_t value);
|
|
||||||
uint32_t evse_get_charging_time_limit(void);
|
|
||||||
void evse_set_charging_time_limit(uint32_t value);
|
|
||||||
uint16_t evse_get_under_power_limit(void);
|
|
||||||
void evse_set_under_power_limit(uint16_t value);
|
|
||||||
|
|
||||||
void evse_set_limit_reached(uint8_t value);
|
|
||||||
|
|
||||||
// Limites default
|
|
||||||
uint32_t evse_get_default_consumption_limit(void);
|
|
||||||
void evse_set_default_consumption_limit(uint32_t value);
|
|
||||||
uint32_t evse_get_default_charging_time_limit(void);
|
|
||||||
void evse_set_default_charging_time_limit(uint32_t value);
|
|
||||||
uint16_t evse_get_default_under_power_limit(void);
|
|
||||||
void evse_set_default_under_power_limit(uint16_t value);
|
|
||||||
|
|
||||||
#endif // EVSE_API_H
|
#endif // EVSE_API_H
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "evse_events.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -13,15 +15,9 @@ extern "C" {
|
|||||||
// Limites Globais (Defines)
|
// Limites Globais (Defines)
|
||||||
// ========================
|
// ========================
|
||||||
|
|
||||||
// Corrente máxima de carregamento (configurável pelo usuário)
|
|
||||||
#define MIN_CHARGING_CURRENT_LIMIT 6 // A
|
#define MIN_CHARGING_CURRENT_LIMIT 6 // A
|
||||||
#define MAX_CHARGING_CURRENT_LIMIT 32 // A
|
#define MAX_CHARGING_CURRENT_LIMIT 32 // A
|
||||||
|
|
||||||
// Corrente máxima da rede elétrica (grid)
|
|
||||||
#define MIN_GRID_CURRENT_LIMIT 6 // A
|
|
||||||
#define MAX_GRID_CURRENT_LIMIT 100 // A
|
|
||||||
|
|
||||||
// Corrente via cabo (proximity) — se configurável
|
|
||||||
#define MIN_CABLE_CURRENT_LIMIT 6 // A
|
#define MIN_CABLE_CURRENT_LIMIT 6 // A
|
||||||
#define MAX_CABLE_CURRENT_LIMIT 63 // A
|
#define MAX_CABLE_CURRENT_LIMIT 63 // A
|
||||||
|
|
||||||
@@ -33,21 +29,18 @@ extern "C" {
|
|||||||
esp_err_t evse_config_init(void);
|
esp_err_t evse_config_init(void);
|
||||||
void evse_check_defaults(void);
|
void evse_check_defaults(void);
|
||||||
|
|
||||||
// Corrente de carregamento
|
// Corrente máxima de hardware (fixa)
|
||||||
uint8_t evse_get_max_charging_current(void);
|
uint8_t evse_get_max_charging_current(void);
|
||||||
esp_err_t evse_set_max_charging_current(uint8_t value);
|
|
||||||
|
|
||||||
|
// Corrente configurável (persistida) <= max hardware
|
||||||
uint16_t evse_get_charging_current(void);
|
uint16_t evse_get_charging_current(void);
|
||||||
esp_err_t evse_set_charging_current(uint16_t value);
|
esp_err_t evse_set_charging_current(uint16_t value);
|
||||||
|
|
||||||
uint16_t evse_get_default_charging_current(void);
|
// Corrente runtime (RAM) <= max hardware (load balancer pode alterar)
|
||||||
esp_err_t evse_set_default_charging_current(uint16_t value);
|
void evse_set_runtime_charging_current(uint16_t value);
|
||||||
|
uint16_t evse_get_runtime_charging_current(void);
|
||||||
|
|
||||||
// Corrente da rede elétrica
|
// Socket outlet
|
||||||
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);
|
bool evse_get_socket_outlet(void);
|
||||||
esp_err_t evse_set_socket_outlet(bool socket_outlet);
|
esp_err_t evse_set_socket_outlet(bool socket_outlet);
|
||||||
|
|
||||||
@@ -59,10 +52,6 @@ esp_err_t evse_set_rcm(bool rcm);
|
|||||||
uint8_t evse_get_temp_threshold(void);
|
uint8_t evse_get_temp_threshold(void);
|
||||||
esp_err_t evse_set_temp_threshold(uint8_t threshold);
|
esp_err_t evse_set_temp_threshold(uint8_t threshold);
|
||||||
|
|
||||||
// Autenticação
|
|
||||||
bool evse_is_require_auth(void);
|
|
||||||
void evse_set_require_auth(bool require);
|
|
||||||
|
|
||||||
// Disponibilidade
|
// Disponibilidade
|
||||||
bool evse_config_is_available(void);
|
bool evse_config_is_available(void);
|
||||||
void evse_config_set_available(bool available);
|
void evse_config_set_available(bool available);
|
||||||
|
|||||||
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,43 +3,47 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "pilot.h"
|
#include "evse_pilot.h"
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
#define EVSE_ERR_AUTO_CLEAR_BITS ( \
|
// Holdoff interno pós-erro (sem expor "cooldown" ao resto)
|
||||||
EVSE_ERR_DIODE_SHORT_BIT | \
|
// ----------------------------------------------------
|
||||||
EVSE_ERR_TEMPERATURE_HIGH_BIT | \
|
// Após TODOS os erros reais desaparecerem (raw_bits == 0),
|
||||||
EVSE_ERR_RCM_TRIGGERED_BIT )
|
// o módulo mantém o erro "visível" durante este tempo.
|
||||||
|
// Durante este período, evse_get_error() continua != 0.
|
||||||
|
#define EVSE_ERROR_COOLDOWN_MS 60000
|
||||||
|
|
||||||
// Error bits
|
// Error bits
|
||||||
#define EVSE_ERR_DIODE_SHORT_BIT (1 << 0)
|
#define EVSE_ERR_DIODE_SHORT_BIT (1 << 0)
|
||||||
#define EVSE_ERR_LOCK_FAULT_BIT (1 << 1)
|
#define EVSE_ERR_LOCK_FAULT_BIT (1 << 1)
|
||||||
#define EVSE_ERR_UNLOCK_FAULT_BIT (1 << 2)
|
#define EVSE_ERR_UNLOCK_FAULT_BIT (1 << 2)
|
||||||
#define EVSE_ERR_RCM_SELFTEST_FAULT_BIT (1 << 3)
|
#define EVSE_ERR_RCM_SELFTEST_FAULT_BIT (1 << 3)
|
||||||
#define EVSE_ERR_RCM_TRIGGERED_BIT (1 << 4)
|
#define EVSE_ERR_RCM_TRIGGERED_BIT (1 << 4)
|
||||||
#define EVSE_ERR_TEMPERATURE_HIGH_BIT (1 << 5)
|
#define EVSE_ERR_TEMPERATURE_HIGH_BIT (1 << 5)
|
||||||
#define EVSE_ERR_PILOT_FAULT_BIT (1 << 6)
|
#define EVSE_ERR_PILOT_FAULT_BIT (1 << 6)
|
||||||
#define EVSE_ERR_TEMPERATURE_FAULT_BIT (1 << 7)
|
#define EVSE_ERR_TEMPERATURE_FAULT_BIT (1 << 7)
|
||||||
|
|
||||||
// Inicialização do módulo de erros
|
// Inicialização do módulo de erros
|
||||||
void evse_error_init(void);
|
void evse_error_init(void);
|
||||||
|
|
||||||
// Verificações e monitoramento
|
// Verificações e monitoramento
|
||||||
void evse_error_check(pilot_voltage_t pilot_voltage, bool is_n12v);
|
void evse_error_check(pilot_voltage_t pilot_voltage, bool is_n12v);
|
||||||
|
|
||||||
void evse_temperature_check(void);
|
void evse_temperature_check(void);
|
||||||
|
|
||||||
void evse_error_tick(void);
|
void evse_error_tick(void);
|
||||||
|
|
||||||
// Leitura e controle de erros
|
// Leitura e controle de erros (estado "visível" com holdoff)
|
||||||
uint32_t evse_get_error(void);
|
uint32_t evse_get_error(void);
|
||||||
bool evse_is_error_cleared(void);
|
|
||||||
void evse_mark_error_cleared(void);
|
|
||||||
void evse_error_set(uint32_t bitmask);
|
void evse_error_set(uint32_t bitmask);
|
||||||
void evse_error_clear(uint32_t bitmask);
|
void evse_error_clear(uint32_t bitmask);
|
||||||
|
|
||||||
bool evse_error_is_active(void);
|
bool evse_error_is_active(void);
|
||||||
uint32_t evse_error_get_bits(void);
|
uint32_t evse_error_get_bits(void);
|
||||||
void evse_error_reset_flag(void);
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// Semântica sticky: flag "todos erros limpos"
|
||||||
|
// (fica true quando o erro visível chega a 0; pode ser útil para UI/logs)
|
||||||
|
// ----------------------------------------------------
|
||||||
bool evse_error_cleared_flag(void);
|
bool evse_error_cleared_flag(void);
|
||||||
|
void evse_error_reset_flag(void);
|
||||||
|
|
||||||
#endif // EVSE_ERROR_H
|
#endif // EVSE_ERROR_H
|
||||||
|
|||||||
@@ -1,33 +1,84 @@
|
|||||||
#ifndef EVSE_EVENTS_H
|
#ifndef EVSE_EVENTS_H
|
||||||
#define EVSE_EVENTS_H
|
#define EVSE_EVENTS_H
|
||||||
|
|
||||||
#include "evse_api.h"
|
#pragma once
|
||||||
#include "esp_event_base.h"
|
|
||||||
|
|
||||||
// Certifique-se de que ESP_EVENT_DECLARE_BASE seja corretamente reconhecido
|
#include <stdbool.h>
|
||||||
#ifdef __cplusplus
|
#include <stdint.h>
|
||||||
extern "C" {
|
#include "esp_event.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
// Declaração da base de eventos EVSE (será definida em evse_events.c)
|
ESP_EVENT_DECLARE_BASE(EVSE_EVENTS);
|
||||||
ESP_EVENT_DECLARE_BASE(EVSE_EVENT);
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
EVSE_EVENT_INIT,
|
||||||
EVSE_EVENT_STATE_CHANGED,
|
EVSE_EVENT_STATE_CHANGED,
|
||||||
EVSE_EVENT_ERROR,
|
EVSE_EVENT_CONFIG_UPDATED,
|
||||||
EVSE_EVENT_ERROR_CLEARED,
|
EVSE_EVENT_ENABLE_UPDATED,
|
||||||
EVSE_EVENT_LIMIT_REACHED,
|
EVSE_EVENT_AVAILABLE_UPDATED,
|
||||||
EVSE_EVENT_AUTH_GRANTED
|
EVSE_EVENT_SESSION,
|
||||||
|
EVSE_EVENT_ERROR_CHANGED,
|
||||||
} evse_event_id_t;
|
} evse_event_id_t;
|
||||||
|
|
||||||
// Estrutura do evento de mudança de estado
|
// -----------------
|
||||||
typedef struct {
|
// Eventos de STATE
|
||||||
evse_state_t previous;
|
// -----------------
|
||||||
evse_state_t current;
|
typedef enum {
|
||||||
} evse_event_state_changed_t;
|
EVSE_STATE_EVENT_IDLE,
|
||||||
|
EVSE_STATE_EVENT_WAITING,
|
||||||
|
EVSE_STATE_EVENT_CHARGING,
|
||||||
|
EVSE_STATE_EVENT_FAULT
|
||||||
|
} evse_state_event_t;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
typedef struct {
|
||||||
}
|
evse_state_event_t state;
|
||||||
#endif
|
} evse_state_event_data_t;
|
||||||
|
|
||||||
|
// -----------------
|
||||||
|
// Eventos de SESSÃO
|
||||||
|
// -----------------
|
||||||
|
typedef enum {
|
||||||
|
EVSE_SESSION_EVENT_STARTED = 0,
|
||||||
|
EVSE_SESSION_EVENT_FINISHED,
|
||||||
|
} evse_session_event_type_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
evse_session_event_type_t type; ///< STARTED / FINISHED
|
||||||
|
|
||||||
|
uint32_t session_id;
|
||||||
|
uint32_t duration_s;
|
||||||
|
uint32_t energy_wh;
|
||||||
|
uint32_t avg_power_w;
|
||||||
|
|
||||||
|
bool is_current;
|
||||||
|
} evse_session_event_data_t;
|
||||||
|
|
||||||
|
// -----------------
|
||||||
|
// Eventos de CONFIG
|
||||||
|
// -----------------
|
||||||
|
typedef struct {
|
||||||
|
bool charging;
|
||||||
|
float hw_max_current;
|
||||||
|
float runtime_current;
|
||||||
|
int64_t timestamp_us;
|
||||||
|
} evse_config_event_data_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool enabled;
|
||||||
|
int64_t timestamp_us;
|
||||||
|
} evse_enable_event_data_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool available;
|
||||||
|
int64_t timestamp_us;
|
||||||
|
} evse_available_event_data_t;
|
||||||
|
|
||||||
|
// -----------------
|
||||||
|
// Eventos de ERRO
|
||||||
|
// -----------------
|
||||||
|
typedef struct {
|
||||||
|
uint32_t error_bits; ///< estado atual (todos os bits de erro)
|
||||||
|
uint32_t changed_mask; ///< bits que mudaram nesta notificação
|
||||||
|
int64_t timestamp_us; ///< esp_timer_get_time()
|
||||||
|
} evse_error_event_data_t;
|
||||||
|
|
||||||
#endif // EVSE_EVENTS_H
|
#endif // EVSE_EVENTS_H
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "evse_api.h"
|
#include "evse_api.h"
|
||||||
#include "pilot.h"
|
#include "evse_pilot.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// === Início de: components/evse/include/evse_limits.h ===
|
||||||
#ifndef EVSE_LIMITS_H
|
#ifndef EVSE_LIMITS_H
|
||||||
#define EVSE_LIMITS_H
|
#define EVSE_LIMITS_H
|
||||||
|
|
||||||
@@ -9,35 +10,56 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Estado dos limites
|
// ============================
|
||||||
void evse_set_limit_reached(uint8_t value);
|
// Limit Status & Evaluation
|
||||||
bool evse_is_limit_reached(void);
|
// ============================
|
||||||
|
|
||||||
/// Verifica e aplica lógica de limites com base no estado atual do EVSE
|
/**
|
||||||
void evse_limits_check(evse_state_t state);
|
* @brief Sets the internal 'limit reached' flag.
|
||||||
|
*/
|
||||||
|
void evse_set_limit_reached(bool value);
|
||||||
|
|
||||||
/// Limites ativos (runtime)
|
/**
|
||||||
|
* @brief Returns true if any runtime charging limit has been reached.
|
||||||
|
*/
|
||||||
|
bool evse_get_limit_reached(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convenience alias for evse_get_limit_reached().
|
||||||
|
*/
|
||||||
|
bool evse_is_limit_reached(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if any session limit has been exceeded (energy, time or power).
|
||||||
|
* Should be called periodically during charging.
|
||||||
|
*/
|
||||||
|
void evse_limits_check(void);
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// Runtime Limit Configuration
|
||||||
|
// ============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get/set energy consumption limit (in Wh).
|
||||||
|
*/
|
||||||
uint32_t evse_get_consumption_limit(void);
|
uint32_t evse_get_consumption_limit(void);
|
||||||
void evse_set_consumption_limit(uint32_t value);
|
void evse_set_consumption_limit(uint32_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get/set maximum charging time (in seconds).
|
||||||
|
*/
|
||||||
uint32_t evse_get_charging_time_limit(void);
|
uint32_t evse_get_charging_time_limit(void);
|
||||||
void evse_set_charging_time_limit(uint32_t value);
|
void evse_set_charging_time_limit(uint32_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get/set minimum acceptable power level (in Watts).
|
||||||
|
*/
|
||||||
uint16_t evse_get_under_power_limit(void);
|
uint16_t evse_get_under_power_limit(void);
|
||||||
void evse_set_under_power_limit(uint16_t value);
|
void evse_set_under_power_limit(uint16_t value);
|
||||||
|
|
||||||
/// Limites padrão (persistentes)
|
|
||||||
uint32_t evse_get_default_consumption_limit(void);
|
|
||||||
void evse_set_default_consumption_limit(uint32_t value);
|
|
||||||
|
|
||||||
uint32_t evse_get_default_charging_time_limit(void);
|
|
||||||
void evse_set_default_charging_time_limit(uint32_t value);
|
|
||||||
|
|
||||||
uint16_t evse_get_default_under_power_limit(void);
|
|
||||||
void evse_set_default_under_power_limit(uint16_t value);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // EVSE_LIMITS_H
|
#endif // EVSE_LIMITS_H
|
||||||
|
// === Fim de: components/evse/include/evse_limits.h ===
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user