cleanup sw req

This commit is contained in:
2026-02-01 19:47:53 +01:00
parent 0bdbcb1657
commit 304371c6b8
608 changed files with 47798 additions and 0 deletions

View File

@@ -1,548 +0,0 @@
# Sensor Hub Static Architecture
**Document Type:** Architecture Specification
**Version:** 1.0
**Date:** 2025-01-19
**Traceability:** SRS Section 3.2, Annex B
## 1. Purpose and Scope
This document defines the static architecture of the Sensor Hub firmware, including component structure, interfaces, data flows, and concurrency model. This architecture enforces separation of concerns, hardware abstraction, and state-aware operation as defined in the SRS and cross-feature constraints.
**Audience:** Software architects, developers, reviewers, and test engineers.
## 2. Architectural Principles
### 2.1 Layered Architecture
The Sensor Hub follows a **strict layered architecture** with the following principles:
1. **Dependency Direction:** Dependencies flow downward only (Application → Drivers → OSAL → HAL)
2. **Hardware Abstraction:** Application layer SHALL NOT access hardware directly (CFC-ARCH-01)
3. **Persistence Abstraction:** All persistence access SHALL go through DP component (CFC-ARCH-01, CFC-DATA-01)
4. **State Awareness:** All features SHALL respect system state restrictions (CFC-ARCH-02)
### 2.2 Component Responsibilities
- **Single Responsibility:** Each component has one well-defined purpose
- **Clear Interfaces:** Public APIs are minimal and well-documented
- **Event-Driven Communication:** Cross-component communication via Event System
- **State-Dependent Behavior:** Components adapt behavior based on system state
## 3. Architecture Views
### 3.1 Context View
The Context View shows the Sensor Hub and its external actors.
```mermaid
graph TB
subgraph External["External Actors"]
MainHub[Main Hub]
PeerHub[Peer Sensor Hub]
Sensors[Environmental Sensors]
SDCard[SD Card]
OLED[OLED Display]
Buttons[Buttons]
end
subgraph SensorHub["Sensor Hub System"]
AppLayer[Application Layer]
Drivers[Drivers Layer]
OSAL[OSAL Layer]
HAL[HAL/ESP-IDF]
end
MainHub <-->|"Encrypted Communication"| AppLayer
PeerHub <-->|"Peer Communication"| AppLayer
Sensors -->|"I2C/SPI/UART/Analog"| Drivers
SDCard <-->|"SPI/SD Protocol"| Drivers
OLED <-->|"I2C"| Drivers
Buttons -->|"GPIO"| Drivers
AppLayer --> Drivers
Drivers --> OSAL
OSAL --> HAL
```
**External Interfaces:**
- **Main Hub:** Bidirectional encrypted communication (Wi-Fi/Zigbee/LoRa)
- **Peer Sensor Hub:** Limited peer-to-peer communication
- **Sensors:** I2C, SPI, UART, Analog interfaces
- **SD Card:** SPI/SD protocol for persistent storage
- **OLED Display:** I2C for local HMI
- **Buttons:** GPIO inputs for user interaction
### 3.2 Component View
The Component View shows the major software components and their relationships.
```mermaid
graph TB
subgraph AppLayer["Application Layer"]
subgraph BusinessStack["Business Stack"]
STM[State Manager<br/>STM]
EventSys[Event System]
SensorMgr[Sensor Manager]
MCMgr[Machine Constant<br/>Manager]
OTAMgr[OTA Manager]
MainHubAPI[Main Hub APIs]
end
subgraph DPStack["DP Stack"]
DataPool[Data Pool]
Persistence[Persistence]
end
DiagTask[Diagnostics Task]
ErrorHandler[Error Handler]
end
subgraph Drivers["Drivers Layer"]
SensorDrivers[Sensor Drivers<br/>I2C/SPI/UART/ADC]
NetworkStack[Network Stack<br/>Wi-Fi/Zigbee/LoRa]
DiagProtocol[Diagnostic Protocol<br/>Stack]
SDDriver[SD Card Driver]
NVMDriver[NVM Driver]
OLEDDriver[OLED Driver]
ButtonDriver[Button Driver]
end
subgraph OSAL["OSAL Layer"]
TaskOS[Task Abstraction]
TimerOS[Software Timer]
SocketOS[Socket Abstraction]
end
subgraph Utils["Utilities"]
Logger[Logger]
TimeUtils[Time Utils]
end
STM --> EventSys
SensorMgr --> EventSys
SensorMgr --> SensorDrivers
MCMgr --> Persistence
OTAMgr --> NetworkStack
OTAMgr --> Persistence
MainHubAPI --> NetworkStack
MainHubAPI --> DataPool
EventSys --> DataPool
DataPool --> Persistence
Persistence --> SDDriver
Persistence --> NVMDriver
DiagTask --> Persistence
DiagTask --> EventSys
ErrorHandler --> STM
ErrorHandler --> DiagTask
SensorMgr --> Logger
OTAMgr --> Logger
MainHubAPI --> Logger
DiagTask --> Logger
SensorMgr --> TimeUtils
SensorDrivers --> TaskOS
NetworkStack --> SocketOS
NetworkStack --> TaskOS
Persistence --> TaskOS
SensorDrivers --> OSAL
NetworkStack --> OSAL
SDDriver --> OSAL
OLEDDriver --> OSAL
ButtonDriver --> OSAL
```
**Component Responsibilities:**
| Component | Responsibility | Non-Responsibility |
|-----------|----------------|-------------------|
| **State Manager (STM)** | System state machine, state transitions, teardown coordination | Feature logic, hardware access |
| **Event System** | Publish/subscribe event bus, cross-component communication | Business logic, state management |
| **Sensor Manager** | Sensor lifecycle, acquisition scheduling, data filtering | Hardware access, persistence, communication |
| **Machine Constant Manager** | MC loading, validation, update coordination | Sensor initialization, hardware access |
| **OTA Manager** | OTA negotiation, firmware reception, validation, activation | Network stack implementation, secure boot |
| **Main Hub APIs** | Main Hub communication protocol, message handling | Network stack implementation, sensor data generation |
| **Data Pool** | Runtime data storage, latest sensor values, system state | Persistence, communication |
| **Persistence** | Persistent storage abstraction, serialization, wear management | Business logic, hardware access |
| **Diagnostics Task** | Diagnostic event collection, storage, query interface | Fault detection, state management |
| **Error Handler** | Fault classification, escalation, state transition triggers | Diagnostic storage, feature logic |
### 3.3 Data Flow View
The Data Flow View shows how data flows through the system.
#### 3.3.1 Sensor Data Acquisition Flow
```mermaid
sequenceDiagram
participant Sensor as Sensor Hardware
participant Driver as Sensor Driver
participant SMgr as Sensor Manager
participant EventSys as Event System
participant DP as Data Pool
participant Persist as Persistence
participant MainHub as Main Hub APIs
Note over Sensor,MainHub: Normal Acquisition Cycle
SMgr->>Driver: readSensor(sensor_id)
Driver->>Sensor: I2C/SPI/UART read
Sensor-->>Driver: raw_sample
Driver-->>SMgr: raw_sample
loop 10 samples
SMgr->>Driver: readSensor(sensor_id)
Driver-->>SMgr: raw_sample
end
SMgr->>SMgr: filter(raw_samples)
SMgr->>SMgr: generateTimestamp()
SMgr->>EventSys: publish(SENSOR_DATA_UPDATE, record)
EventSys->>DP: update(sensor_id, record)
EventSys->>Persist: async_persist(record)
EventSys->>MainHub: notify(SENSOR_DATA_UPDATE)
MainHub->>MainHub: queueForTransmission(record)
```
#### 3.3.2 Diagnostic Event Flow
```mermaid
sequenceDiagram
participant Component as Any Component
participant ErrorHandler as Error Handler
participant DiagTask as Diagnostics Task
participant EventSys as Event System
participant STM as State Manager
participant Persist as Persistence
Component->>ErrorHandler: reportFault(diagnostic_code, severity)
ErrorHandler->>ErrorHandler: classifyFault()
ErrorHandler->>ErrorHandler: checkEscalation()
alt Severity == FATAL
ErrorHandler->>STM: triggerStateTransition(FAULT)
STM->>EventSys: publish(STATE_CHANGED, FAULT)
else Severity == WARNING
ErrorHandler->>STM: triggerStateTransition(WARNING)
end
ErrorHandler->>DiagTask: logDiagnostic(event)
DiagTask->>Persist: persistDiagnostic(event)
DiagTask->>EventSys: publish(DIAGNOSTIC_EVENT, event)
```
#### 3.3.3 OTA Update Flow
```mermaid
sequenceDiagram
participant MainHub as Main Hub
participant MainHubAPI as Main Hub APIs
participant OTAMgr as OTA Manager
participant STM as State Manager
participant Persist as Persistence
participant NetworkStack as Network Stack
participant Security as Security
MainHub->>MainHubAPI: OTA_REQUEST(message)
MainHubAPI->>OTAMgr: otaRequestReceived()
OTAMgr->>OTAMgr: validateReadiness()
OTAMgr->>STM: requestStateTransition(OTA_PREP)
STM->>STM: validateTransition()
STM->>EventSys: publish(STATE_CHANGED, OTA_PREP)
OTAMgr->>MainHubAPI: otaAcknowledge(ACCEPT)
MainHubAPI->>MainHub: OTA_ACK(ACCEPT)
OTAMgr->>STM: requestStateTransition(TEARDOWN)
STM->>Persist: flushCriticalData()
Persist-->>STM: flushComplete()
STM->>EventSys: publish(STATE_CHANGED, TEARDOWN)
STM->>STM: transitionTo(OTA_UPDATE)
loop Firmware Chunks
MainHub->>NetworkStack: firmwareChunk(data)
NetworkStack->>OTAMgr: firmwareChunkReceived(data)
OTAMgr->>Persist: storeFirmwareChunk(data)
end
OTAMgr->>Security: validateFirmwareIntegrity()
Security-->>OTAMgr: validationResult
alt Validation Success
OTAMgr->>OTAMgr: flashFirmware()
OTAMgr->>STM: reboot()
else Validation Failure
OTAMgr->>STM: requestStateTransition(FAULT)
OTAMgr->>MainHubAPI: otaStatus(FAILED)
end
```
### 3.4 Concurrency View
The Concurrency View shows the task model, priorities, and resource ownership.
```mermaid
graph TB
subgraph Tasks["RTOS Tasks"]
SensorTask["Sensor Acquisition Task<br/>Priority: HIGH<br/>Stack: 8KB<br/>Period: 1s"]
CommTask["Communication Task<br/>Priority: MEDIUM<br/>Stack: 12KB<br/>Event-driven"]
PersistTask["Persistence Task<br/>Priority: MEDIUM<br/>Stack: 6KB<br/>Event-driven"]
DiagTask["Diagnostics Task<br/>Priority: LOW<br/>Stack: 4KB<br/>Period: 10s"]
HMITask["HMI Task<br/>Priority: LOW<br/>Stack: 4KB<br/>Event-driven"]
OTATask["OTA Task<br/>Priority: HIGH<br/>Stack: 16KB<br/>Event-driven"]
SystemTask["System Management Task<br/>Priority: HIGH<br/>Stack: 6KB<br/>Event-driven"]
end
subgraph Components["Components"]
SensorMgr[Sensor Manager]
MainHubAPI[Main Hub APIs]
Persistence[Persistence]
DiagTaskComp[Diagnostics Task]
HMI[HMI]
OTAMgr[OTA Manager]
STM[State Manager]
end
SensorTask --> SensorMgr
CommTask --> MainHubAPI
PersistTask --> Persistence
DiagTask --> DiagTaskComp
HMITask --> HMI
OTATask --> OTAMgr
SystemTask --> STM
```
**Task Priorities and Responsibilities:**
| Task | Priority | Stack Size | Responsibility | Blocking Operations |
|------|----------|------------|---------------|---------------------|
| **Sensor Acquisition** | HIGH | 8KB | Sensor sampling, filtering | Sensor I/O (bounded) |
| **Communication** | MEDIUM | 12KB | Main Hub communication | Network I/O (bounded) |
| **Persistence** | MEDIUM | 6KB | Data persistence | Storage I/O (bounded) |
| **Diagnostics** | LOW | 4KB | Diagnostic collection | None (non-blocking) |
| **HMI** | LOW | 4KB | Display updates, button handling | Display I/O (bounded) |
| **OTA** | HIGH | 16KB | Firmware update operations | Network I/O, flash I/O |
| **System Management** | HIGH | 6KB | State machine, teardown | None (coordination only) |
**Resource Ownership:**
| Resource | Owner | Access Method | Concurrency Control |
|----------|-------|--------------|---------------------|
| **Sensor Drivers** | Sensor Acquisition Task | Direct (exclusive) | Task-level ownership |
| **Network Stack** | Communication Task | Direct (exclusive) | Task-level ownership |
| **SD Card** | Persistence Task | Direct (exclusive) | Mutex (if shared) |
| **NVM** | Persistence Task | Direct (exclusive) | Mutex (if shared) |
| **Data Pool** | All Tasks | Event System | Lock-free (atomic operations) |
| **Event System** | All Tasks | Publish/Subscribe | Lock-free queue |
| **State Machine** | System Management Task | Direct (exclusive) | Task-level ownership |
## 4. Component Specifications
### 4.1 State Manager (STM)
**Location:** `application_layer/business_stack/STM/`
**Responsibilities:**
- Implement system FSM as defined in System State Machine Specification
- Enforce valid state transitions
- Coordinate teardown sequences
- Notify components of state changes
**Public API:**
```c
// State query
system_state_t stm_getCurrentState(void);
bool stm_isStateValid(system_state_t state);
// State transition
bool stm_requestTransition(system_state_t target_state, transition_reason_t reason);
bool stm_validateTransition(system_state_t from, system_state_t to);
// Teardown coordination
bool stm_initiateTeardown(teardown_reason_t reason);
bool stm_isTeardownComplete(void);
// Component registration
bool stm_registerStateListener(state_listener_t listener);
```
**State Dependencies:**
- SHALL be initialized first (during INIT state)
- SHALL coordinate with Error Handler for fault-triggered transitions
- SHALL coordinate with OTA Manager for OTA-triggered transitions
### 4.2 Event System
**Location:** `application_layer/business_stack/event_system/`
**Responsibilities:**
- Provide publish/subscribe event bus
- Decouple components via events
- Ensure non-blocking event delivery
**Public API:**
```c
// Event types
typedef enum {
EVENT_SENSOR_DATA_UPDATE,
EVENT_DIAGNOSTIC_EVENT,
EVENT_STATE_CHANGED,
EVENT_OTA_REQUEST,
// ... other event types
} event_type_t;
// Publish event
bool event_publish(event_type_t type, void* payload, size_t payload_size);
// Subscribe to events
bool event_subscribe(event_type_t type, event_handler_t handler);
bool event_unsubscribe(event_type_t type, event_handler_t handler);
```
**Constraints:**
- SHALL be non-blocking (lock-free queue)
- SHALL support multiple subscribers per event type
- SHALL NOT perform blocking operations in event handlers
### 4.3 Sensor Manager
**Location:** `application_layer/business_stack/sensor_manager/`
**Responsibilities:**
- Sensor lifecycle management (detection, initialization, sampling)
- High-frequency sampling and filtering
- Timestamp generation
- Sensor failure detection
**Public API:**
```c
// Sensor lifecycle
bool sensorMgr_initialize(void);
bool sensorMgr_detectSensors(void);
bool sensorMgr_startAcquisition(void);
bool sensorMgr_stopAcquisition(void);
// Sensor data access
bool sensorMgr_getLatestData(uint8_t sensor_id, sensor_data_record_t* record);
bool sensorMgr_getAllSensorData(sensor_data_record_t* records, size_t* count);
// Sensor status
bool sensorMgr_isSensorPresent(uint8_t sensor_id);
bool sensorMgr_isSensorEnabled(uint8_t sensor_id);
```
**State Dependencies:**
- SHALL NOT perform acquisition during OTA_UPDATE, MC_UPDATE, TEARDOWN states
- SHALL pause acquisition during SERVICE state (optional)
- SHALL continue acquisition during SD_DEGRADED state (without persistence)
### 4.4 Data Persistence (DP Component)
**Location:** `application_layer/DP_stack/persistence/`
**Responsibilities:**
- Abstract storage media (SD card, NVM)
- Serialize/deserialize structured data
- Manage wear-aware storage
- Ensure data integrity
**Public API:**
```c
// Data persistence
bool persistence_writeSensorData(const sensor_data_record_t* record);
bool persistence_writeDiagnostic(const diagnostic_event_t* event);
bool persistence_writeMachineConstants(const machine_constants_t* mc);
// Data retrieval
bool persistence_readSensorData(sensor_data_record_t* records, size_t* count);
bool persistence_readDiagnostics(diagnostic_event_t* events, size_t* count);
bool persistence_readMachineConstants(machine_constants_t* mc);
// Flush operations
bool persistence_flushCriticalData(void);
bool persistence_isFlushComplete(void);
```
**Constraints:**
- SHALL be the sole interface for persistent storage access (CFC-ARCH-01)
- SHALL NOT allow direct hardware access from application layer
- SHALL verify persistence completion before state transitions (CFC-DATA-02)
### 4.5 OTA Manager
**Location:** `application_layer/business_stack/fw_upgrader/`
**Responsibilities:**
- OTA negotiation with Main Hub
- Firmware reception and storage
- Firmware integrity validation
- Controlled firmware activation
**Public API:**
```c
// OTA operations
bool ota_handleRequest(const ota_request_t* request);
bool ota_receiveFirmwareChunk(const uint8_t* chunk, size_t chunk_size);
bool ota_validateFirmware(void);
bool ota_activateFirmware(void);
// OTA status
ota_status_t ota_getStatus(void);
bool ota_isReady(void);
```
**State Dependencies:**
- SHALL NOT operate during WARNING, FAULT, SERVICE, SD_DEGRADED states
- SHALL coordinate with STM for state transitions
- SHALL coordinate with Persistence for data flush before activation
## 5. Architectural Constraints Mapping
| Constraint | Architectural Mechanism | Enforcement |
|------------|-------------------------|-------------|
| **CFC-ARCH-01** (Hardware Abstraction) | Driver layer abstraction, OSAL layer | Compile-time (no direct HAL includes in application) |
| **CFC-ARCH-02** (State-Aware Execution) | STM state queries, per-state execution rules | Runtime (state checks in components) |
| **CFC-TIME-01** (Non-Blocking) | Event System, async operations | Design-time (no blocking calls in critical paths) |
| **CFC-TIME-02** (Deterministic) | Static memory allocation, bounded operations | Design-time (no dynamic allocation in acquisition) |
| **CFC-DATA-01** (Single Source of Truth) | DP component as sole persistence interface | Compile-time (no direct storage access) |
| **CFC-DATA-02** (Data Consistency) | Persistence flush before state transitions | Runtime (STM coordination) |
| **CFC-SEC-01** (Security First) | Secure boot before application start | Boot-time (hardware-enforced) |
| **CFC-SEC-02** (Encrypted Channels) | TLS/DTLS in Network Stack | Runtime (mandatory encryption) |
| **CFC-DBG-01** (Debug Isolation) | Debug session authentication, state restrictions | Runtime (authentication checks) |
## 6. Repository Structure Mapping
| Architecture Layer | Repository Path | Components |
|-------------------|-----------------|------------|
| **Application Layer** | `application_layer/business_stack/` | STM, Event System, Sensor Manager, MC Manager, OTA Manager, Main Hub APIs |
| **DP Stack** | `application_layer/DP_stack/` | Data Pool, Persistence |
| **Diagnostics** | `application_layer/diag_task/`, `application_layer/error_handler/` | Diagnostics Task, Error Handler |
| **Drivers** | `drivers/` | Network Stack, Diagnostic Protocol, SD Card, NVM, Sensors |
| **ESP-IDF Wrappers** | `ESP_IDF_FW_wrappers/` | GPIO, I2C, SPI, UART, ADC, DMA, Wi-Fi |
| **OSAL** | `os/` | Task, Software Timer |
| **Utilities** | `utils/` | Logger, Time Utils |
## 7. Traceability
- **SRS Section 3.2:** Interface Requirements
- **SRS Section 3.4:** Design Constraints
- **Cross-Feature Constraints:** All architectural constraints mapped
- **System State Machine Specification:** State-aware component behavior
## 8. Diagrams Summary
- **Context View:** External interfaces and actors
- **Component View:** Major components and relationships
- **Data Flow View:** Sensor data, diagnostic, and OTA flows
- **Concurrency View:** Task model, priorities, resource ownership
---
**Next Steps:**
- Component-level specifications (detailed APIs per component)
- Sequence diagrams for additional flows (MC update, diagnostic session)
- Interface Control Documents (ICD) for external interfaces

View File

@@ -1,346 +0,0 @@
# ESP-IDF Firmware Wrappers
## Overview
This directory contains C++ object-oriented wrappers for ESP-IDF peripheral drivers. Each wrapper provides a clean, easy-to-use interface that encapsulates the ESP-IDF C APIs while maintaining full functionality and performance.
## Architecture
The wrapper architecture follows these principles:
- **Object-Oriented Design**: Each peripheral is wrapped in a C++ class
- **RAII Pattern**: Resources are automatically managed through constructors/destructors
- **Error Handling**: Comprehensive error checking with ESP-IDF error code logging
- **Type Safety**: Strong typing with enumerations instead of magic numbers
- **Documentation**: Full Doxygen documentation for all public APIs
- **Naming Convention**: Follows project coding guidelines (snake_case files, PascalCase classes)
## Implemented Modules
### ✅ GPIO Wrapper (`gpio/`)
- **Status**: Complete
- **Features**: Pin configuration, digital I/O, interrupt handling, pin validation
- **Key Classes**: `Gpio`
- **Dependencies**: `driver/gpio.h`, `esp_err.h`, `esp_log.h`
### ✅ UART Wrapper (`uart/`)
- **Status**: Complete
- **Features**: Multi-port support, configurable parameters, timeout support, flow control
- **Key Classes**: `Uart`
- **Dependencies**: `driver/uart.h`, `esp_err.h`, `esp_log.h`, `freertos/FreeRTOS.h`
### ✅ I2C Wrapper (`i2c/`)
- **Status**: Complete
- **Features**: Master/slave modes, register operations, bus scanning, timeout support
- **Key Classes**: `I2c`
- **Dependencies**: `driver/i2c.h`, `esp_err.h`, `esp_log.h`, `freertos/FreeRTOS.h`
### ✅ SPI Wrapper (`spi/`)
- **Status**: Complete
- **Features**: Multi-host support, device management, DMA support, command/address phases
- **Key Classes**: `Spi`
- **Dependencies**: `driver/spi_master.h`, `esp_err.h`, `esp_log.h`
### ✅ ADC Wrapper (`adc/`)
- **Status**: Complete
- **Features**: Multi-unit/channel support, automatic calibration, averaging, multiple resolutions
- **Key Classes**: `Adc`
- **Dependencies**: `esp_adc/adc_oneshot.h`, `esp_adc/adc_cali.h`, `esp_err.h`, `esp_log.h`
### ✅ WiFi Wrapper (`wifi/`)
- **Status**: Complete
- **Features**: STA/AP/APSTA modes, network scanning, event handling, security support
- **Key Classes**: `Wifi`
- **Dependencies**: `esp_wifi.h`, `esp_netif.h`, `esp_event.h`, `nvs_flash.h`
### ✅ DMA Wrapper (`dma/`)
- **Status**: Complete
- **Features**: GDMA channel management, memory transfers, descriptor management, DMA-capable memory allocation
- **Key Classes**: `Dma`
- **Dependencies**: `driver/gdma.h`, `esp_dma_utils.h`, `esp_hw_support`, `esp_heap_caps.h`
### ✅ Bluetooth Wrapper (`bt/`)
- **Status**: Complete
- **Features**: BLE/Classic/Dual modes, GATT server/client, advertising, scanning, pairing
- **Key Classes**: `Bluetooth`
- **Dependencies**: `esp_bt.h`, `esp_gap_ble_api.h`, `esp_gatts_api.h`, `esp_gattc_api.h`, `nvs_flash.h`
## Directory Structure
```
ESP_IDF_FW_wrappers/
├── README.md # This overview document
├── gpio/
│ ├── com/
│ │ ├── gpio.hpp # GPIO wrapper header
│ │ └── gpio.cpp # GPIO wrapper implementation
│ ├── test/ # Unit tests (empty)
│ ├── CMakeLists.txt # Build configuration
│ └── README.md # GPIO module documentation
├── uart/
│ ├── com/
│ │ ├── uart.hpp # UART wrapper header
│ │ └── uart.cpp # UART wrapper implementation
│ ├── test/ # Unit tests (empty)
│ ├── CMakeLists.txt # Build configuration
│ └── README.md # UART module documentation
├── i2c/
│ ├── com/
│ │ ├── i2c.hpp # I2C wrapper header
│ │ └── i2c.cpp # I2C wrapper implementation
│ ├── test/ # Unit tests (empty)
│ ├── CMakeLists.txt # Build configuration
│ └── README.md # I2C module documentation
├── spi/
│ ├── com/
│ │ ├── spi.hpp # SPI wrapper header
│ │ └── spi.cpp # SPI wrapper implementation
│ ├── test/ # Unit tests (empty)
│ ├── CMakeLists.txt # Build configuration
│ └── README.md # SPI module documentation
├── adc/
│ ├── com/
│ │ ├── adc.hpp # ADC wrapper header
│ │ └── adc.cpp # ADC wrapper implementation
│ ├── test/ # Unit tests (empty)
│ ├── CMakeLists.txt # Build configuration
│ └── README.md # ADC module documentation
├── wifi/
│ ├── com/
│ │ ├── wifi.hpp # WiFi wrapper header
│ │ └── wifi.cpp # WiFi wrapper implementation
│ ├── test/ # Unit tests (empty)
│ ├── CMakeLists.txt # Build configuration
│ └── README.md # WiFi module documentation
├── bt/ # Bluetooth wrapper (✅ Complete)
│ ├── com/
│ │ ├── bt.hpp # Bluetooth wrapper header
│ │ └── bt.cpp # Bluetooth wrapper implementation
│ ├── test/ # Unit tests (empty)
│ ├── CMakeLists.txt # Build configuration
│ └── README.md # Bluetooth module documentation
└── dma/ # DMA wrapper (✅ Complete)
├── com/
│ ├── dma.hpp # DMA wrapper header
│ └── dma.cpp # DMA wrapper implementation
├── test/ # Unit tests (empty)
├── CMakeLists.txt # Build configuration
└── README.md # DMA module documentation
```
## Usage Examples
### Basic GPIO Usage
```cpp
#include "gpio.hpp"
Gpio gpio;
gpio.configure(2, GpioMode::OUTPUT);
gpio.setLevel(2, 1); // Set pin high
```
### UART Communication
```cpp
#include "uart.hpp"
Uart uart;
UartConfig config = Uart::getDefaultConfig();
config.baudrate = UartBaudrate::BAUD_115200;
config.txPin = 1;
config.rxPin = 3;
uart.initialize(UartPort::PORT_0, config);
uart.transmit(UartPort::PORT_0, (uint8_t*)"Hello", 5);
```
### I2C Device Communication
```cpp
#include "i2c.hpp"
I2c i2c;
I2cConfig config = I2c::getDefaultConfig();
i2c.initialize(I2cPort::PORT_0, config);
uint8_t data[] = {0x01, 0x02};
i2c.write(I2cPort::PORT_0, 0x48, data, sizeof(data));
```
### SPI Device Communication
```cpp
#include "spi.hpp"
Spi spi;
SpiBusConfig busConfig = Spi::getDefaultBusConfig();
spi.initializeBus(SpiHost::SPI2_HOST, busConfig);
SpiDeviceConfig deviceConfig = Spi::getDefaultDeviceConfig();
spi_device_handle_t device;
spi.addDevice(SpiHost::SPI2_HOST, deviceConfig, &device);
uint8_t txData[] = {0xAA, 0x55};
uint8_t rxData[2];
spi.transmit(device, txData, rxData, 2);
```
### ADC Reading
```cpp
#include "adc.hpp"
Adc adc;
adc.initializeUnit(AdcUnit::UNIT_1, AdcBitwidth::WIDTH_12BIT);
AdcChannelConfig config = Adc::getDefaultChannelConfig();
adc.configureChannel(config);
int32_t voltage = adc.readVoltage(AdcUnit::UNIT_1, AdcChannel::CHANNEL_0);
```
### WiFi Connection
```cpp
#include "wifi.hpp"
Wifi wifi;
WifiConfig config = {};
config.mode = WifiMode::STA;
config.staConfig = Wifi::getDefaultStaConfig();
strcpy(config.staConfig.ssid, "MyNetwork");
strcpy(config.staConfig.password, "MyPassword");
wifi.initialize(config);
wifi.start();
wifi.connect();
```
## Build Integration
Each wrapper is a separate ESP-IDF component with its own `CMakeLists.txt`. To use a wrapper in your application:
1. Add the wrapper component to your project's `CMakeLists.txt`:
```cmake
set(EXTRA_COMPONENT_DIRS "components/ESP_IDF_FW_wrappers/gpio")
```
2. Include the wrapper in your component's dependencies:
```cmake
idf_component_register(
SRCS "main.cpp"
INCLUDE_DIRS "."
REQUIRES gpio_wrapper
)
```
3. Include the header in your code:
```cpp
#include "gpio.hpp"
```
## Design Patterns
### RAII (Resource Acquisition Is Initialization)
All wrappers follow RAII principles:
- Resources are acquired in constructors
- Resources are released in destructors
- Exception safety through automatic cleanup
### Error Handling
Consistent error handling across all wrappers:
- Boolean return values for success/failure
- Negative return values for error conditions
- ESP-IDF error codes logged with descriptive messages
- Input parameter validation
### Configuration Structures
Each wrapper provides configuration structures:
- Default configuration functions
- Type-safe enumerations
- Clear parameter documentation
## Testing
Each wrapper includes a `test/` directory for unit tests (currently empty). Future development should include:
- Unit tests for all public APIs
- Integration tests with real hardware
- Performance benchmarks
- Memory usage tests
## Future Development
### All Core Modules Implemented
All 8 core ESP-IDF peripheral wrapper modules have been successfully implemented:
- GPIO, UART, I2C, SPI, ADC, WiFi, DMA, and Bluetooth wrappers are complete
- Each module includes full functionality, documentation, and usage examples
### Enhancements for Existing Modules
1. **GPIO**
- Matrix keyboard support
- PWM integration
- Capacitive touch support
2. **UART**
- RS485 support
- Pattern detection
- DMA integration
3. **I2C**
- Multi-master support
- Clock stretching
- 10-bit addressing
4. **SPI**
- Slave mode support
- Quad SPI support
- LCD interface support
5. **ADC**
- Continuous mode
- DMA integration
- Multi-channel simultaneous sampling
6. **WiFi**
- WPS support
- Mesh networking
- Enterprise security
## Contributing
When contributing to the wrapper modules:
1. Follow the established coding guidelines in `6 Guidelines.md`
2. Maintain consistent API design across modules
3. Include comprehensive documentation
4. Add unit tests for new functionality
5. Update the module's README.md file
6. Ensure thread safety where applicable
## Performance Considerations
- All wrappers provide minimal overhead over direct ESP-IDF calls
- RAII pattern ensures efficient resource management
- Inline functions used where appropriate
- No dynamic memory allocation in critical paths
- Direct ESP-IDF function calls for optimal performance
## Memory Usage
- Fixed memory footprint per wrapper instance
- No hidden dynamic allocations
- Resource handles managed efficiently
- Stack-based configuration structures
## Thread Safety
- GPIO: Not thread-safe, requires external synchronization
- UART: Thread-safe (ESP-IDF driver is thread-safe)
- I2C: Thread-safe for different ports, synchronize access to same port
- SPI: Thread-safe (ESP-IDF driver is thread-safe)
- ADC: Thread-safe (ESP-IDF driver is thread-safe)
- WiFi: Thread-safe (ESP-IDF driver is thread-safe)
## Compatibility
- **ESP-IDF Version**: v5.x
- **ESP32 Variants**: ESP32, ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6
- **Compiler**: GCC with C++11 or later
- **Build System**: CMake-based ESP-IDF build system

View File

@@ -1,5 +0,0 @@
idf_component_register(
SRCS "com/adc.cpp"
INCLUDE_DIRS "com"
REQUIRES driver esp_adc logger
)

View File

@@ -1,276 +0,0 @@
# ADC Wrapper Module
## Overview
The ADC wrapper module provides a C++ object-oriented interface for ESP-IDF ADC functionality. This module encapsulates the ESP-IDF ADC oneshot driver functions and provides a clean, easy-to-use API for analog-to-digital conversion with automatic calibration support.
## Features
- **Multi-Unit Support**: Support for ADC1 and ADC2 units
- **Multi-Channel Support**: Support for up to 10 channels per unit
- **Automatic Calibration**: Built-in calibration for accurate voltage readings
- **Multiple Resolutions**: Support for 9-13 bit ADC resolution
- **Attenuation Control**: Configurable input voltage ranges
- **Averaging**: Built-in sample averaging for noise reduction
- **Error Handling**: Comprehensive error checking and logging
## Architecture
### Class Diagram
```
┌─────────────────────────────────────┐
│ Adc │
├─────────────────────────────────────┤
│ - m_adcHandle_[2]: handle_t │
│ - m_caliHandle_[2][10]: handle_t │
│ - m_unitInitialized_[2]: bool │
│ - m_channelConfigured_[2][10]: bool │
│ - m_unitBitwidth_[2]: AdcBitwidth │
├─────────────────────────────────────┤
│ + Adc() │
│ + ~Adc() │
│ + initializeUnit(unit, width): bool │
│ + deinitializeUnit(unit): bool │
│ + configureChannel(config): bool │
│ + readVoltage(unit, ch): int32_t │
│ + readRaw(unit, ch): int32_t │
│ + readVoltageAverage(...): int32_t │
│ + readRawAverage(...): int32_t │
│ + isUnitInitialized(unit): bool │
│ + isChannelConfigured(...): bool │
│ + getMaxRawValue(width): uint32_t │
│ + getDefaultChannelConfig(): Config │
│ - convertUnit(unit): adc_unit_t │
│ - convertChannel(ch): adc_channel_t │
│ - convertAttenuation(...): adc_att │
│ - convertBitwidth(...): adc_bit_t │
│ - initializeCalibration(...): bool │
│ - deinitializeCalibration(...): bool│
│ - getUnitIndex(unit): uint8_t │
│ - getChannelIndex(ch): uint8_t │
└─────────────────────────────────────┘
```
### Enumerations
#### AdcUnit
- `UNIT_1`: ADC unit 1
- `UNIT_2`: ADC unit 2
#### AdcChannel
- `CHANNEL_0` to `CHANNEL_9`: ADC channels 0-9
#### AdcAttenuation
- `ATTEN_0dB`: 0dB attenuation (0-950mV range)
- `ATTEN_2_5dB`: 2.5dB attenuation (0-1250mV range)
- `ATTEN_6dB`: 6dB attenuation (0-1750mV range)
- `ATTEN_11dB`: 11dB attenuation (0-3100mV range)
#### AdcBitwidth
- `WIDTH_9BIT`: 9-bit resolution (0-511)
- `WIDTH_10BIT`: 10-bit resolution (0-1023)
- `WIDTH_11BIT`: 11-bit resolution (0-2047)
- `WIDTH_12BIT`: 12-bit resolution (0-4095)
- `WIDTH_13BIT`: 13-bit resolution (0-8191)
### Configuration Structure
```cpp
struct AdcChannelConfig {
AdcUnit unit; // ADC unit
AdcChannel channel; // ADC channel
AdcAttenuation atten; // Attenuation level
AdcBitwidth bitwidth; // ADC resolution
};
```
## Usage Examples
### Basic ADC Reading
```cpp
#include "adc.hpp"
// Create ADC instance
Adc adc;
// Initialize ADC unit 1 with 12-bit resolution
adc.initializeUnit(AdcUnit::UNIT_1, AdcBitwidth::WIDTH_12BIT);
// Configure channel 0
AdcChannelConfig config = Adc::getDefaultChannelConfig();
config.unit = AdcUnit::UNIT_1;
config.channel = AdcChannel::CHANNEL_0;
config.atten = AdcAttenuation::ATTEN_11dB; // 0-3.1V range
config.bitwidth = AdcBitwidth::WIDTH_12BIT;
adc.configureChannel(config);
// Read voltage in millivolts
int32_t voltage = adc.readVoltage(AdcUnit::UNIT_1, AdcChannel::CHANNEL_0);
printf("Voltage: %ld mV\n", voltage);
// Read raw ADC value
int32_t raw = adc.readRaw(AdcUnit::UNIT_1, AdcChannel::CHANNEL_0);
printf("Raw value: %ld\n", raw);
```
### Multiple Channels
```cpp
// Configure multiple channels
AdcChannelConfig configs[] = {
{AdcUnit::UNIT_1, AdcChannel::CHANNEL_0, AdcAttenuation::ATTEN_11dB, AdcBitwidth::WIDTH_12BIT},
{AdcUnit::UNIT_1, AdcChannel::CHANNEL_1, AdcAttenuation::ATTEN_6dB, AdcBitwidth::WIDTH_12BIT},
{AdcUnit::UNIT_2, AdcChannel::CHANNEL_0, AdcAttenuation::ATTEN_11dB, AdcBitwidth::WIDTH_12BIT}
};
// Initialize units
adc.initializeUnit(AdcUnit::UNIT_1, AdcBitwidth::WIDTH_12BIT);
adc.initializeUnit(AdcUnit::UNIT_2, AdcBitwidth::WIDTH_12BIT);
// Configure all channels
for (const auto& config : configs) {
adc.configureChannel(config);
}
// Read from all channels
for (const auto& config : configs) {
int32_t voltage = adc.readVoltage(config.unit, config.channel);
printf("Unit %d Channel %d: %ld mV\n",
static_cast<int>(config.unit),
static_cast<int>(config.channel),
voltage);
}
```
### Noise Reduction with Averaging
```cpp
// Read 10 samples and return average
int32_t avgVoltage = adc.readVoltageAverage(AdcUnit::UNIT_1, AdcChannel::CHANNEL_0, 10);
printf("Average voltage: %ld mV\n", avgVoltage);
// Read 50 raw samples and return average
int32_t avgRaw = adc.readRawAverage(AdcUnit::UNIT_1, AdcChannel::CHANNEL_0, 50);
printf("Average raw: %ld\n", avgRaw);
```
### Different Voltage Ranges
```cpp
// Low voltage sensor (0-950mV)
AdcChannelConfig lowVoltageConfig = {};
lowVoltageConfig.unit = AdcUnit::UNIT_1;
lowVoltageConfig.channel = AdcChannel::CHANNEL_0;
lowVoltageConfig.atten = AdcAttenuation::ATTEN_0dB; // 0-950mV
lowVoltageConfig.bitwidth = AdcBitwidth::WIDTH_12BIT;
// Battery voltage (0-3.1V)
AdcChannelConfig batteryConfig = {};
batteryConfig.unit = AdcUnit::UNIT_1;
batteryConfig.channel = AdcChannel::CHANNEL_1;
batteryConfig.atten = AdcAttenuation::ATTEN_11dB; // 0-3100mV
batteryConfig.bitwidth = AdcBitwidth::WIDTH_12BIT;
adc.configureChannel(lowVoltageConfig);
adc.configureChannel(batteryConfig);
```
## API Reference
### Constructor/Destructor
- **Adc()**: Initialize ADC wrapper instance
- **~Adc()**: Clean up resources and deinitialize all units
### Unit Management
- **initializeUnit(unit, bitwidth)**: Initialize ADC unit with resolution
- **deinitializeUnit(unit)**: Deinitialize ADC unit
- **isUnitInitialized(unit)**: Check if unit is initialized
### Channel Configuration
- **configureChannel(config)**: Configure ADC channel
- **isChannelConfigured(unit, channel)**: Check if channel is configured
### Reading Methods
- **readVoltage(unit, channel)**: Read calibrated voltage in mV
- **readRaw(unit, channel)**: Read raw ADC value
- **readVoltageAverage(unit, channel, samples)**: Read averaged voltage
- **readRawAverage(unit, channel, samples)**: Read averaged raw value
### Utility Methods
- **getMaxRawValue(bitwidth)**: Get maximum raw value for resolution
- **getDefaultChannelConfig()**: Get default configuration
## Voltage Ranges by Attenuation
| Attenuation | Voltage Range | Typical Use Case |
|-------------|---------------|------------------|
| 0dB | 0 - 950mV | Low voltage sensors |
| 2.5dB | 0 - 1250mV | 1.2V references |
| 6dB | 0 - 1750mV | 1.8V logic levels |
| 11dB | 0 - 3100mV | 3.3V logic, battery voltage |
## ADC Resolution and Range
| Bitwidth | Resolution | Max Raw Value | LSB (at 3.1V) |
|----------|------------|---------------|----------------|
| 9-bit | 512 steps | 511 | ~6.1 mV |
| 10-bit | 1024 steps | 1023 | ~3.0 mV |
| 11-bit | 2048 steps | 2047 | ~1.5 mV |
| 12-bit | 4096 steps | 4095 | ~0.76 mV |
| 13-bit | 8192 steps | 8191 | ~0.38 mV |
## Error Handling
The module provides comprehensive error handling:
- Unit and channel validation
- Initialization status checks
- ESP-IDF error codes are caught and logged
- Return values indicate success/failure for all operations
- Graceful fallback when calibration is unavailable
## Dependencies
- ESP-IDF ADC driver (`esp_adc/adc_oneshot.h`)
- ESP-IDF ADC calibration (`esp_adc/adc_cali.h`)
- ESP-IDF error handling (`esp_err.h`)
- ESP-IDF logging (`esp_log.h`)
## Thread Safety
The ADC wrapper uses ESP-IDF's thread-safe ADC driver. Multiple tasks can safely read from different channels simultaneously.
## Memory Usage
- Fixed memory footprint per instance
- Calibration handles stored per channel
- No dynamic memory allocation in wrapper layer
## Performance Considerations
- Direct ESP-IDF function calls for optimal performance
- Calibration lookup tables for fast voltage conversion
- Averaging reduces noise but increases read time
- Higher resolution increases conversion time
## Calibration
The module automatically initializes calibration for each configured channel:
- Uses ESP-IDF's curve fitting calibration scheme
- Provides accurate voltage readings across temperature range
- Falls back to linear estimation if calibration fails
- Calibration data stored in eFuse (factory calibrated)
## Limitations
- ADC2 cannot be used when WiFi is active
- Some channels may not be available on all ESP32 variants
- Maximum sampling rate depends on resolution and calibration
- Input impedance affects accuracy for high-impedance sources

View File

@@ -1,363 +0,0 @@
/**
* @file adc.cpp
* @brief ADC wrapper component implementation
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "adc.hpp"
#include "logger.hpp"
#include <cstring>
static const char* TAG = "ADC_WRAPPER";
Adc::Adc()
{
// Initialize handles to NULL
memset(m_adcHandle_, 0, sizeof(m_adcHandle_));
memset(m_caliHandle_, 0, sizeof(m_caliHandle_));
memset(m_unitInitialized_, false, sizeof(m_unitInitialized_));
memset(m_channelConfigured_, false, sizeof(m_channelConfigured_));
m_unitBitwidth_[0] = AdcBitwidth::WIDTH_12BIT;
m_unitBitwidth_[1] = AdcBitwidth::WIDTH_12BIT;
ASF_LOGI(TAG, 2000, asf::logger::Criticality::LOW, "ADC wrapper initialized");
}
Adc::~Adc()
{
// Deinitialize all units
for (int i = 0; i < 2; i++) {
if (m_unitInitialized_[i]) {
deinitializeUnit(static_cast<AdcUnit>(i + 1));
}
}
ASF_LOGI(TAG, 2001, asf::logger::Criticality::LOW, "ADC wrapper destroyed");
}
bool Adc::initialize()
{
ASF_LOGI(TAG, 2002, asf::logger::Criticality::LOW, "ADC initialized successfully");
return true;
}
bool Adc::initializeUnit(AdcUnit unit, AdcBitwidth bitwidth)
{
uint8_t unitIdx = getUnitIndex(unit);
if (m_unitInitialized_[unitIdx]) {
ASF_LOGW(TAG, 2003, asf::logger::Criticality::MEDIUM, "ADC unit %d already initialized", unitIdx + 1);
return true;
}
// Configure ADC unit
adc_oneshot_unit_init_cfg_t initConfig = {};
initConfig.unit_id = convertUnit(unit);
esp_err_t ret = adc_oneshot_new_unit(&initConfig, &m_adcHandle_[unitIdx]);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2004, asf::logger::Criticality::HIGH, "Failed to initialize ADC unit %d: %s", unitIdx + 1, esp_err_to_name(ret));
return false;
}
m_unitInitialized_[unitIdx] = true;
m_unitBitwidth_[unitIdx] = bitwidth;
ASF_LOGI(TAG, 2005, asf::logger::Criticality::LOW, "ADC unit %d initialized successfully", unitIdx + 1);
return true;
}
bool Adc::deinitializeUnit(AdcUnit unit)
{
uint8_t unitIdx = getUnitIndex(unit);
if (!m_unitInitialized_[unitIdx]) {
ASF_LOGW(TAG, 2006, asf::logger::Criticality::MEDIUM, "ADC unit %d not initialized", unitIdx + 1);
return true;
}
// Deinitialize all calibrations for this unit
for (int ch = 0; ch < 10; ch++) {
if (m_channelConfigured_[unitIdx][ch]) {
deinitializeCalibration(unit, static_cast<AdcChannel>(ch));
m_channelConfigured_[unitIdx][ch] = false;
}
}
esp_err_t ret = adc_oneshot_del_unit(m_adcHandle_[unitIdx]);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2007, asf::logger::Criticality::HIGH, "Failed to deinitialize ADC unit %d: %s", unitIdx + 1, esp_err_to_name(ret));
return false;
}
m_adcHandle_[unitIdx] = nullptr;
m_unitInitialized_[unitIdx] = false;
ASF_LOGI(TAG, 2008, asf::logger::Criticality::LOW, "ADC unit %d deinitialized", unitIdx + 1);
return true;
}
bool Adc::configureChannel(const AdcChannelConfig& config)
{
uint8_t unitIdx = getUnitIndex(config.unit);
uint8_t channelIdx = getChannelIndex(config.channel);
if (!m_unitInitialized_[unitIdx]) {
ASF_LOGE(TAG, 2009, asf::logger::Criticality::HIGH, "ADC unit %d not initialized", unitIdx + 1);
return false;
}
// Configure channel
adc_oneshot_chan_cfg_t chanConfig = {};
chanConfig.atten = convertAttenuation(config.atten);
chanConfig.bitwidth = convertBitwidth(config.bitwidth);
esp_err_t ret = adc_oneshot_config_channel(m_adcHandle_[unitIdx],
convertChannel(config.channel), &chanConfig);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2010, asf::logger::Criticality::HIGH, "Failed to configure ADC channel %d on unit %d: %s",
channelIdx, unitIdx + 1, esp_err_to_name(ret));
return false;
}
// Initialize calibration
if (!initializeCalibration(config.unit, config.channel, config.atten)) {
ASF_LOGW(TAG, 2011, asf::logger::Criticality::MEDIUM, "Failed to initialize calibration for channel %d on unit %d",
channelIdx, unitIdx + 1);
}
m_channelConfigured_[unitIdx][channelIdx] = true;
ASF_LOGI(TAG, 2012, asf::logger::Criticality::LOW, "ADC channel %d configured on unit %d", channelIdx, unitIdx + 1);
return true;
}
int32_t Adc::readVoltage(AdcUnit unit, AdcChannel channel)
{
uint8_t unitIdx = getUnitIndex(unit);
uint8_t channelIdx = getChannelIndex(channel);
if (!m_unitInitialized_[unitIdx]) {
ASF_LOGE(TAG, 2013, asf::logger::Criticality::HIGH, "ADC unit %d not initialized", unitIdx + 1);
return -1;
}
if (!m_channelConfigured_[unitIdx][channelIdx]) {
ASF_LOGE(TAG, 2014, asf::logger::Criticality::HIGH, "ADC channel %d not configured on unit %d", channelIdx, unitIdx + 1);
return -1;
}
// Read raw value
int rawValue = 0;
esp_err_t ret = adc_oneshot_read(m_adcHandle_[unitIdx], convertChannel(channel), &rawValue);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2015, asf::logger::Criticality::HIGH, "Failed to read ADC channel %d on unit %d: %s",
channelIdx, unitIdx + 1, esp_err_to_name(ret));
return -1;
}
// Convert to voltage if calibration is available
if (m_caliHandle_[unitIdx][channelIdx] != nullptr) {
int voltage = 0;
ret = adc_cali_raw_to_voltage(m_caliHandle_[unitIdx][channelIdx], rawValue, &voltage);
if (ret == ESP_OK) {
return voltage;
} else {
ASF_LOGW(TAG, 2016, asf::logger::Criticality::MEDIUM, "Failed to convert raw to voltage: %s", esp_err_to_name(ret));
}
}
// Fallback: estimate voltage based on attenuation and raw value
uint32_t maxRaw = getMaxRawValue(m_unitBitwidth_[unitIdx]);
return (rawValue * 3300) / maxRaw; // Rough estimation
}
int32_t Adc::readRaw(AdcUnit unit, AdcChannel channel)
{
uint8_t unitIdx = getUnitIndex(unit);
uint8_t channelIdx = getChannelIndex(channel);
if (!m_unitInitialized_[unitIdx]) {
ASF_LOGE(TAG, 2017, asf::logger::Criticality::HIGH, "ADC unit %d not initialized", unitIdx + 1);
return -1;
}
if (!m_channelConfigured_[unitIdx][channelIdx]) {
ASF_LOGE(TAG, 2018, asf::logger::Criticality::HIGH, "ADC channel %d not configured on unit %d", channelIdx, unitIdx + 1);
return -1;
}
int rawValue = 0;
esp_err_t ret = adc_oneshot_read(m_adcHandle_[unitIdx], convertChannel(channel), &rawValue);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2019, asf::logger::Criticality::HIGH, "Failed to read ADC channel %d on unit %d: %s",
channelIdx, unitIdx + 1, esp_err_to_name(ret));
return -1;
}
return rawValue;
}
int32_t Adc::readVoltageAverage(AdcUnit unit, AdcChannel channel, uint32_t samples)
{
if (samples == 0) {
ASF_LOGE(TAG, 2020, asf::logger::Criticality::HIGH, "Invalid sample count");
return -1;
}
int64_t sum = 0;
uint32_t validSamples = 0;
for (uint32_t i = 0; i < samples; i++) {
int32_t voltage = readVoltage(unit, channel);
if (voltage >= 0) {
sum += voltage;
validSamples++;
}
}
if (validSamples == 0) {
ASF_LOGE(TAG, 2021, asf::logger::Criticality::HIGH, "No valid samples obtained");
return -1;
}
return static_cast<int32_t>(sum / validSamples);
}
int32_t Adc::readRawAverage(AdcUnit unit, AdcChannel channel, uint32_t samples)
{
if (samples == 0) {
ASF_LOGE(TAG, 2022, asf::logger::Criticality::HIGH, "Invalid sample count");
return -1;
}
int64_t sum = 0;
uint32_t validSamples = 0;
for (uint32_t i = 0; i < samples; i++) {
int32_t raw = readRaw(unit, channel);
if (raw >= 0) {
sum += raw;
validSamples++;
}
}
if (validSamples == 0) {
ASF_LOGE(TAG, 2023, asf::logger::Criticality::HIGH, "No valid samples obtained");
return -1;
}
return static_cast<int32_t>(sum / validSamples);
}
bool Adc::isUnitInitialized(AdcUnit unit) const
{
uint8_t unitIdx = getUnitIndex(unit);
return m_unitInitialized_[unitIdx];
}
bool Adc::isChannelConfigured(AdcUnit unit, AdcChannel channel) const
{
uint8_t unitIdx = getUnitIndex(unit);
uint8_t channelIdx = getChannelIndex(channel);
return m_channelConfigured_[unitIdx][channelIdx];
}
uint32_t Adc::getMaxRawValue(AdcBitwidth bitwidth)
{
switch (bitwidth) {
case AdcBitwidth::WIDTH_9BIT:
return 511;
case AdcBitwidth::WIDTH_10BIT:
return 1023;
case AdcBitwidth::WIDTH_11BIT:
return 2047;
case AdcBitwidth::WIDTH_12BIT:
return 4095;
case AdcBitwidth::WIDTH_13BIT:
return 8191;
default:
return 4095;
}
}
AdcChannelConfig Adc::getDefaultChannelConfig()
{
AdcChannelConfig config = {};
config.unit = AdcUnit::UNIT_1;
config.channel = AdcChannel::CHANNEL_0;
config.atten = AdcAttenuation::ATTEN_11dB;
config.bitwidth = AdcBitwidth::WIDTH_12BIT;
return config;
}
adc_unit_t Adc::convertUnit(AdcUnit unit)
{
return static_cast<adc_unit_t>(unit);
}
adc_channel_t Adc::convertChannel(AdcChannel channel)
{
return static_cast<adc_channel_t>(channel);
}
adc_atten_t Adc::convertAttenuation(AdcAttenuation atten)
{
return static_cast<adc_atten_t>(atten);
}
adc_bitwidth_t Adc::convertBitwidth(AdcBitwidth bitwidth)
{
return static_cast<adc_bitwidth_t>(bitwidth);
}
bool Adc::initializeCalibration(AdcUnit unit, AdcChannel channel, AdcAttenuation atten)
{
uint8_t unitIdx = getUnitIndex(unit);
uint8_t channelIdx = getChannelIndex(channel);
adc_cali_line_fitting_config_t caliConfig = {};
caliConfig.unit_id = convertUnit(unit);
caliConfig.atten = convertAttenuation(atten);
caliConfig.bitwidth = convertBitwidth(m_unitBitwidth_[unitIdx]);
#if CONFIG_IDF_TARGET_ESP32
caliConfig.default_vref = 1100; // Default Vref for ESP32
#endif
esp_err_t ret = adc_cali_create_scheme_line_fitting(&caliConfig, &m_caliHandle_[unitIdx][channelIdx]);
if (ret == ESP_OK) {
ASF_LOGI(TAG, 2024, asf::logger::Criticality::LOW, "Calibration initialized for unit %d channel %d", unitIdx + 1, channelIdx);
return true;
} else {
ASF_LOGW(TAG, 2025, asf::logger::Criticality::MEDIUM, "Failed to initialize calibration for unit %d channel %d: %s",
unitIdx + 1, channelIdx, esp_err_to_name(ret));
m_caliHandle_[unitIdx][channelIdx] = nullptr;
return false;
}
}
bool Adc::deinitializeCalibration(AdcUnit unit, AdcChannel channel)
{
uint8_t unitIdx = getUnitIndex(unit);
uint8_t channelIdx = getChannelIndex(channel);
if (m_caliHandle_[unitIdx][channelIdx] != nullptr) {
esp_err_t ret = adc_cali_delete_scheme_line_fitting(m_caliHandle_[unitIdx][channelIdx]);
if (ret != ESP_OK) {
ASF_LOGW(TAG, 2026, asf::logger::Criticality::MEDIUM, "Failed to deinitialize calibration: %s", esp_err_to_name(ret));
return false;
}
m_caliHandle_[unitIdx][channelIdx] = nullptr;
}
return true;
}
uint8_t Adc::getUnitIndex(AdcUnit unit) const
{
return static_cast<uint8_t>(unit) - 1;
}
uint8_t Adc::getChannelIndex(AdcChannel channel) const
{
return static_cast<uint8_t>(channel);
}

View File

@@ -1,261 +0,0 @@
/**
* @file adc.hpp
* @brief ADC wrapper component header - Wrapper for ESP-IDF ADC functionality
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#ifndef ADC_HPP
#define ADC_HPP
#include <cstdint>
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include "esp_err.h"
/**
* @brief ADC unit enumeration
*/
enum class AdcUnit
{
UNIT_1 = ADC_UNIT_1,
UNIT_2 = ADC_UNIT_2
};
/**
* @brief ADC channel enumeration
*/
enum class AdcChannel
{
CHANNEL_0 = ADC_CHANNEL_0,
CHANNEL_1 = ADC_CHANNEL_1,
CHANNEL_2 = ADC_CHANNEL_2,
CHANNEL_3 = ADC_CHANNEL_3,
CHANNEL_4 = ADC_CHANNEL_4,
CHANNEL_5 = ADC_CHANNEL_5,
CHANNEL_6 = ADC_CHANNEL_6,
CHANNEL_7 = ADC_CHANNEL_7,
CHANNEL_8 = ADC_CHANNEL_8,
CHANNEL_9 = ADC_CHANNEL_9
};
/**
* @brief ADC attenuation enumeration
*/
enum class AdcAttenuation
{
ATTEN_0dB = ADC_ATTEN_DB_0, ///< 0dB attenuation (0-950mV)
ATTEN_2_5dB = ADC_ATTEN_DB_2_5, ///< 2.5dB attenuation (0-1250mV)
ATTEN_6dB = ADC_ATTEN_DB_6, ///< 6dB attenuation (0-1750mV)
ATTEN_11dB = ADC_ATTEN_DB_11 ///< 11dB attenuation (0-3100mV)
};
/**
* @brief ADC bitwidth enumeration
*/
enum class AdcBitwidth
{
WIDTH_9BIT = ADC_BITWIDTH_9,
WIDTH_10BIT = ADC_BITWIDTH_10,
WIDTH_11BIT = ADC_BITWIDTH_11,
WIDTH_12BIT = ADC_BITWIDTH_12,
WIDTH_13BIT = ADC_BITWIDTH_13
};
/**
* @brief ADC channel configuration structure
*/
struct AdcChannelConfig
{
AdcUnit unit; ///< ADC unit
AdcChannel channel; ///< ADC channel
AdcAttenuation atten; ///< Attenuation level
AdcBitwidth bitwidth; ///< ADC resolution
};
/**
* @brief ADC wrapper class
*
* Provides a C++ wrapper for ESP-IDF ADC functionality
* with simplified interface and error handling.
* This class encapsulates ESP-IDF ADC oneshot driver functions in an object-oriented interface.
*/
class Adc
{
public:
/**
* @brief Constructor
* @details Initializes the ADC wrapper instance
*/
Adc();
/**
* @brief Destructor
* @details Cleans up resources and deinitializes ADC units
*/
~Adc();
/**
* @brief Initialize ADC component
* @return true if initialized successfully, false otherwise
*/
bool initialize();
/**
* @brief Initialize ADC unit
* @param unit ADC unit to initialize
* @param bitwidth ADC resolution
* @return true if initialized successfully, false otherwise
*/
bool initializeUnit(AdcUnit unit, AdcBitwidth bitwidth);
/**
* @brief Deinitialize ADC unit
* @param unit ADC unit to deinitialize
* @return true if deinitialized successfully, false otherwise
*/
bool deinitializeUnit(AdcUnit unit);
/**
* @brief Configure ADC channel
* @param config Channel configuration
* @return true if configured successfully, false otherwise
* @note Unit must be initialized before configuring channels
*/
bool configureChannel(const AdcChannelConfig& config);
/**
* @brief Read voltage from a channel
* @param unit ADC unit
* @param channel ADC channel to read
* @return Voltage in millivolts, or -1 on error
* @note Channel must be configured before reading
*/
int32_t readVoltage(AdcUnit unit, AdcChannel channel);
/**
* @brief Read raw value from a channel
* @param unit ADC unit
* @param channel ADC channel to read
* @return Raw ADC value, or -1 on error
* @note Channel must be configured before reading
*/
int32_t readRaw(AdcUnit unit, AdcChannel channel);
/**
* @brief Read multiple samples and return average
* @param unit ADC unit
* @param channel ADC channel to read
* @param samples Number of samples to average
* @return Average voltage in millivolts, or -1 on error
*/
int32_t readVoltageAverage(AdcUnit unit, AdcChannel channel, uint32_t samples);
/**
* @brief Read multiple raw samples and return average
* @param unit ADC unit
* @param channel ADC channel to read
* @param samples Number of samples to average
* @return Average raw ADC value, or -1 on error
*/
int32_t readRawAverage(AdcUnit unit, AdcChannel channel, uint32_t samples);
/**
* @brief Check if ADC unit is initialized
* @param unit ADC unit to check
* @return true if initialized, false otherwise
*/
bool isUnitInitialized(AdcUnit unit) const;
/**
* @brief Check if channel is configured
* @param unit ADC unit
* @param channel ADC channel
* @return true if configured, false otherwise
*/
bool isChannelConfigured(AdcUnit unit, AdcChannel channel) const;
/**
* @brief Get maximum raw value for given bitwidth
* @param bitwidth ADC bitwidth
* @return Maximum raw value
*/
static uint32_t getMaxRawValue(AdcBitwidth bitwidth);
/**
* @brief Get default channel configuration
* @return Default ADC channel configuration
*/
static AdcChannelConfig getDefaultChannelConfig();
private:
adc_oneshot_unit_handle_t m_adcHandle_[2]; ///< ADC unit handles
adc_cali_handle_t m_caliHandle_[2][10]; ///< Calibration handles [unit][channel]
bool m_unitInitialized_[2]; ///< Unit initialization status
bool m_channelConfigured_[2][10]; ///< Channel configuration status [unit][channel]
AdcBitwidth m_unitBitwidth_[2]; ///< Bitwidth for each unit
/**
* @brief Convert AdcUnit to ESP-IDF adc_unit_t
* @param unit ADC unit
* @return ESP-IDF adc_unit_t
*/
adc_unit_t convertUnit(AdcUnit unit);
/**
* @brief Convert AdcChannel to ESP-IDF adc_channel_t
* @param channel ADC channel
* @return ESP-IDF adc_channel_t
*/
adc_channel_t convertChannel(AdcChannel channel);
/**
* @brief Convert AdcAttenuation to ESP-IDF adc_atten_t
* @param atten ADC attenuation
* @return ESP-IDF adc_atten_t
*/
adc_atten_t convertAttenuation(AdcAttenuation atten);
/**
* @brief Convert AdcBitwidth to ESP-IDF adc_bitwidth_t
* @param bitwidth ADC bitwidth
* @return ESP-IDF adc_bitwidth_t
*/
adc_bitwidth_t convertBitwidth(AdcBitwidth bitwidth);
/**
* @brief Initialize calibration for channel
* @param unit ADC unit
* @param channel ADC channel
* @param atten Attenuation level
* @return true if calibration initialized, false otherwise
*/
bool initializeCalibration(AdcUnit unit, AdcChannel channel, AdcAttenuation atten);
/**
* @brief Deinitialize calibration for channel
* @param unit ADC unit
* @param channel ADC channel
* @return true if calibration deinitialized, false otherwise
*/
bool deinitializeCalibration(AdcUnit unit, AdcChannel channel);
/**
* @brief Get unit index from AdcUnit
* @param unit ADC unit
* @return Unit index (0 or 1)
*/
uint8_t getUnitIndex(AdcUnit unit) const;
/**
* @brief Get channel index from AdcChannel
* @param channel ADC channel
* @return Channel index (0-9)
*/
uint8_t getChannelIndex(AdcChannel channel) const;
};
#endif // ADC_HPP

View File

@@ -1,28 +0,0 @@
ID,Component,Level,Criticality,Message
2000,ADC,INFO,Low,ADC wrapper initialized
2001,ADC,INFO,Low,ADC wrapper destroyed
2002,ADC,INFO,Low,ADC initialized successfully
2003,ADC,WARNING,Medium,ADC unit %d already initialized
2004,ADC,ERROR,High,Failed to initialize ADC unit %d: %s
2005,ADC,INFO,Low,ADC unit %d initialized successfully
2006,ADC,WARNING,Medium,ADC unit %d not initialized
2007,ADC,ERROR,High,Failed to deinitialize ADC unit %d: %s
2008,ADC,INFO,Low,ADC unit %d deinitialized
2009,ADC,ERROR,High,ADC unit %d not initialized
2010,ADC,ERROR,High,Failed to configure ADC channel %d on unit %d: %s
2011,ADC,WARNING,Medium,Failed to initialize calibration for channel %d on unit %d
2012,ADC,INFO,Low,ADC channel %d configured on unit %d
2013,ADC,ERROR,High,ADC unit %d not initialized
2014,ADC,ERROR,High,ADC channel %d not configured on unit %d
2015,ADC,ERROR,High,Failed to read ADC channel %d on unit %d: %s
2016,ADC,WARNING,Medium,Failed to convert raw to voltage: %s
2017,ADC,ERROR,High,ADC unit %d not initialized
2018,ADC,ERROR,High,ADC channel %d not configured on unit %d
2019,ADC,ERROR,High,Failed to read ADC channel %d on unit %d: %s
2020,ADC,ERROR,High,Invalid sample count
2021,ADC,ERROR,High,No valid samples obtained
2022,ADC,ERROR,High,Invalid sample count
2023,ADC,ERROR,High,No valid samples obtained
2024,ADC,INFO,Low,Calibration initialized for unit %d channel %d
2025,ADC,WARNING,Medium,Failed to initialize calibration for unit %d channel %d: %s
2026,ADC,WARNING,Medium,Failed to deinitialize calibration: %s
1 ID Component Level Criticality Message
2 2000 ADC INFO Low ADC wrapper initialized
3 2001 ADC INFO Low ADC wrapper destroyed
4 2002 ADC INFO Low ADC initialized successfully
5 2003 ADC WARNING Medium ADC unit %d already initialized
6 2004 ADC ERROR High Failed to initialize ADC unit %d: %s
7 2005 ADC INFO Low ADC unit %d initialized successfully
8 2006 ADC WARNING Medium ADC unit %d not initialized
9 2007 ADC ERROR High Failed to deinitialize ADC unit %d: %s
10 2008 ADC INFO Low ADC unit %d deinitialized
11 2009 ADC ERROR High ADC unit %d not initialized
12 2010 ADC ERROR High Failed to configure ADC channel %d on unit %d: %s
13 2011 ADC WARNING Medium Failed to initialize calibration for channel %d on unit %d
14 2012 ADC INFO Low ADC channel %d configured on unit %d
15 2013 ADC ERROR High ADC unit %d not initialized
16 2014 ADC ERROR High ADC channel %d not configured on unit %d
17 2015 ADC ERROR High Failed to read ADC channel %d on unit %d: %s
18 2016 ADC WARNING Medium Failed to convert raw to voltage: %s
19 2017 ADC ERROR High ADC unit %d not initialized
20 2018 ADC ERROR High ADC channel %d not configured on unit %d
21 2019 ADC ERROR High Failed to read ADC channel %d on unit %d: %s
22 2020 ADC ERROR High Invalid sample count
23 2021 ADC ERROR High No valid samples obtained
24 2022 ADC ERROR High Invalid sample count
25 2023 ADC ERROR High No valid samples obtained
26 2024 ADC INFO Low Calibration initialized for unit %d channel %d
27 2025 ADC WARNING Medium Failed to initialize calibration for unit %d channel %d: %s
28 2026 ADC WARNING Medium Failed to deinitialize calibration: %s

View File

@@ -1,34 +0,0 @@
import sys
import os
import time
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
if folder_path not in sys.path:
sys.path.append(folder_path)
from scan_serial import ESP32Runner
def test_adc_initialize():
runner = ESP32Runner(mode="SIM", port="COM9")
runner.start()
print("--- QEMU Runner Started ---", flush=True)
try:
start_time = time.time()
while time.time() - start_time < 30:
line = runner.get_line(timeout=1.0)
if line:
print(line, flush=True)
if "ADC wrapper initialized" in line or "ADC initialized successfully" in line:
print("SUCCESS CRITERIA MET!", flush=True)
return 0
if runner.process.poll() is not None:
print(f"Process exited with code: {runner.process.returncode}", flush=True)
return 1
finally:
runner.stop()
print("Done.", flush=True)
return 1
if __name__ == "__main__":
exit_code = test_adc_initialize()
sys.exit(exit_code)

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<test_scenario>
<!-- The configuration for the test environment. -->
<!-- Available configurations: SIMULATE, HIL -->
<config>SIMULATE</config>
<test_case>
<test_case_id>ADC_INIT_TEST</test_case_id>
<!-- The main command that executes the test itself. -->
<test_exec>python components/ESP_IDF_FW_wrappers/adc/test/adc_init_test.py</test_exec>
</test_case>
</test_scenario>

View File

@@ -1,69 +0,0 @@
/**
* @file test_adc.cpp
* @brief Unit tests for ADC wrapper component
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "unity.h"
#include "adc.hpp"
extern "C" {
void setUp(void)
{
// Set up test fixtures before each test
}
void tearDown(void)
{
// Clean up test fixtures after each test
}
/**
* @brief Test ADC initialization
*/
void test_adc_initialize(void)
{
Adc adc;
bool result = adc.initialize();
TEST_ASSERT_TRUE(result);
TEST_ASSERT_TRUE(adc.isInitialized());
}
/**
* @brief Test ADC deinitialize
*/
void test_adc_deinitialize(void)
{
Adc adc;
adc.initialize();
bool result = adc.deinitialize();
TEST_ASSERT_TRUE(result);
TEST_ASSERT_FALSE(adc.isInitialized());
}
/**
* @brief Test ADC read voltage without initialization
*/
void test_adc_read_voltage_not_initialized(void)
{
Adc adc;
int32_t result = adc.readVoltage(AdcChannel::CHANNEL_0, AdcAttenuation::ATTEN_0dB);
TEST_ASSERT_EQUAL(-1, result);
}
/**
* @brief Test ADC read raw without initialization
*/
void test_adc_read_raw_not_initialized(void)
{
Adc adc;
int32_t result = adc.readRaw(AdcChannel::CHANNEL_0, AdcAttenuation::ATTEN_0dB);
TEST_ASSERT_EQUAL(-1, result);
}
} // extern "C"

View File

@@ -1,5 +0,0 @@
idf_component_register(
SRCS "com/bt.cpp"
INCLUDE_DIRS "com"
REQUIRES bt nvs_flash logger
)

View File

@@ -1,502 +0,0 @@
# Bluetooth Wrapper Module
## Overview
The Bluetooth wrapper module provides a C++ object-oriented interface for ESP-IDF Bluetooth functionality. This module encapsulates the ESP-IDF Bluetooth and BLE (Bluetooth Low Energy) driver functions and provides a clean, easy-to-use API for Bluetooth Classic, BLE, and dual-mode operations.
## Features
- **Multiple Modes**: Bluetooth Classic, BLE, and dual-mode support
- **BLE Advertising**: Configurable advertising parameters and data
- **BLE Scanning**: Network discovery and device scanning
- **GATT Server**: Create services and characteristics
- **GATT Client**: Connect to and interact with remote devices
- **Event Handling**: Comprehensive event callbacks for all operations
- **Security Support**: Pairing, bonding, and encryption
- **Power Management**: Configurable TX power levels
## Architecture
### Class Diagram
```
┌─────────────────────────────────────┐
│ Bluetooth │
├─────────────────────────────────────┤
│ - m_isInitialized_: bool │
│ - m_mode_: BtMode │
│ - m_connectionState_: State │
│ - m_connectedDeviceCount_: uint8_t │
│ - m_gattsIf_: esp_gatt_if_t │
│ - m_gattcIf_: esp_gatt_if_t │
│ - m_appId_: uint16_t │
│ - m_gapCallback_: Callback │
│ - m_gattsCallback_: Callback │
│ - m_gattcCallback_: Callback │
├─────────────────────────────────────┤
│ + Bluetooth() │
│ + ~Bluetooth() │
│ + initialize(mode): bool │
│ + deinitialize(): bool │
│ + setDeviceName(name): bool │
│ + getDeviceName(name, len): bool │
│ + setAdvParams(params): bool │
│ + setAdvData(data): bool │
│ + startAdvertising(): bool │
│ + stopAdvertising(): bool │
│ + startScanning(duration): bool │
│ + stopScanning(): bool │
│ + createGattService(svc): uint16_t │
│ + addCharacteristic(...): uint16_t │
│ + startGattService(handle): bool │
│ + stopGattService(handle): bool │
│ + sendNotification(...): bool │
│ + sendIndication(...): bool │
│ + connectToDevice(...): bool │
│ + disconnectDevice(connId): bool │
│ + getConnectionState(): State │
│ + isConnected(): bool │
│ + getConnectedDeviceCount(): uint8_t│
│ + setGapEventCallback(...): void │
│ + setGattsEventCallback(...): void │
│ + setGattcEventCallback(...): void │
│ + getMacAddress(mac): bool │
│ + setTxPower(type, level): bool │
│ + getTxPower(type): esp_power_level │
│ + isInitialized(): bool │
│ + getDefaultAdvParams(): Params │
│ + getDefaultAdvData(): Data │
│ + getDefaultGattService(): Service │
│ + getDefaultGattChar(): Char │
│ - gapEventHandler(...): void │
│ - gattsEventHandler(...): void │
│ - gattcEventHandler(...): void │
│ - convertMode(mode): esp_bt_mode_t │
│ - convertAdvType(type): esp_adv_t │
└─────────────────────────────────────┘
```
### Enumerations
#### BtMode
- `CLASSIC`: Bluetooth Classic mode
- `BLE`: Bluetooth Low Energy mode
- `DUAL`: Dual mode (Classic + BLE)
#### BleAdvType
- `ADV_IND`: Connectable undirected advertising
- `ADV_DIRECT_IND_HIGH`: Connectable directed advertising (high duty cycle)
- `ADV_SCAN_IND`: Scannable undirected advertising
- `ADV_NONCONN_IND`: Non-connectable undirected advertising
- `ADV_DIRECT_IND_LOW`: Connectable directed advertising (low duty cycle)
#### BleConnectionState
- `DISCONNECTED`: Not connected
- `CONNECTING`: Connection in progress
- `CONNECTED`: Successfully connected
- `DISCONNECTING`: Disconnection in progress
### Configuration Structures
#### BleAdvParams
```cpp
struct BleAdvParams {
uint16_t advIntMin; // Minimum advertising interval
uint16_t advIntMax; // Maximum advertising interval
BleAdvType advType; // Advertising type
uint8_t ownAddrType; // Own address type
uint8_t peerAddrType; // Peer address type
uint8_t peerAddr[6]; // Peer address
uint8_t channelMap; // Channel map
uint8_t advFilterPolicy; // Advertising filter policy
};
```
#### BleAdvData
```cpp
struct BleAdvData {
bool setName; // Set device name in advertising data
bool setTxPower; // Set TX power in advertising data
bool includeUuid; // Include service UUID
bool setManufacturerData; // Set manufacturer data
uint16_t appearance; // Device appearance
uint16_t manufacturerId; // Manufacturer ID
uint8_t manufacturerDataLen; // Manufacturer data length
uint8_t* manufacturerData; // Manufacturer data
uint8_t serviceUuidLen; // Service UUID length
uint8_t* serviceUuid; // Service UUID
char* deviceName; // Device name
};
```
#### GattService
```cpp
struct GattService {
uint16_t serviceId; // Service ID
uint16_t serviceUuid; // Service UUID
uint16_t numHandles; // Number of handles
bool isPrimary; // Primary service flag
};
```
#### GattCharacteristic
```cpp
struct GattCharacteristic {
uint16_t charUuid; // Characteristic UUID
uint8_t properties; // Characteristic properties
uint8_t permissions; // Characteristic permissions
uint16_t maxLen; // Maximum value length
bool autoRsp; // Auto response flag
};
```
## Usage Examples
### Basic BLE Advertising
```cpp
#include "bt.hpp"
// BLE event callback
void bleGapCallback(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param, void* userData) {
switch (event) {
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
printf("Advertising started\n");
break;
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
printf("Advertising stopped\n");
break;
default:
break;
}
}
// Create Bluetooth instance
Bluetooth bt;
// Initialize BLE
bt.initialize(BtMode::BLE);
// Set device name
bt.setDeviceName("ESP32-BLE-Device");
// Set event callback
bt.setGapEventCallback(bleGapCallback, nullptr);
// Configure advertising
BleAdvParams advParams = Bluetooth::getDefaultAdvParams();
advParams.advIntMin = 0x20; // 20ms
advParams.advIntMax = 0x40; // 40ms
bt.setAdvParams(advParams);
// Set advertising data
BleAdvData advData = Bluetooth::getDefaultAdvData();
advData.setName = true;
advData.setTxPower = true;
bt.setAdvData(advData);
// Start advertising
bt.startAdvertising();
```
### BLE GATT Server
```cpp
// GATT server event callback
void bleGattsCallback(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param, void* userData) {
switch (event) {
case ESP_GATTS_CONNECT_EVT:
printf("Client connected\n");
break;
case ESP_GATTS_DISCONNECT_EVT:
printf("Client disconnected\n");
break;
case ESP_GATTS_READ_EVT:
printf("Characteristic read request\n");
break;
case ESP_GATTS_WRITE_EVT:
printf("Characteristic write request\n");
break;
default:
break;
}
}
// Set GATT server callback
bt.setGattsEventCallback(bleGattsCallback, nullptr);
// Create a service
GattService service = Bluetooth::getDefaultGattService();
service.serviceUuid = 0x180F; // Battery Service
service.numHandles = 4;
uint16_t serviceHandle = bt.createGattService(service);
// Add characteristic
GattCharacteristic characteristic = Bluetooth::getDefaultGattCharacteristic();
characteristic.charUuid = 0x2A19; // Battery Level
characteristic.properties = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_NOTIFY;
uint16_t charHandle = bt.addCharacteristic(serviceHandle, characteristic);
// Start the service
bt.startGattService(serviceHandle);
```
### BLE Scanning
```cpp
// Scanning callback
void bleScanCallback(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param, void* userData) {
switch (event) {
case ESP_GAP_BLE_SCAN_RESULT_EVT: {
esp_ble_gap_cb_param_t* scanResult = param;
if (scanResult->scan_rst.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) {
printf("Found device: ");
for (int i = 0; i < 6; i++) {
printf("%02X", scanResult->scan_rst.bda[i]);
if (i < 5) printf(":");
}
printf(" RSSI: %d\n", scanResult->scan_rst.rssi);
}
break;
}
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
printf("Scan started\n");
break;
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
printf("Scan stopped\n");
break;
default:
break;
}
}
// Set scan callback
bt.setGapEventCallback(bleScanCallback, nullptr);
// Start scanning for 10 seconds
bt.startScanning(10);
```
### BLE Client Connection
```cpp
// GATT client callback
void bleGattcCallback(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t* param, void* userData) {
switch (event) {
case ESP_GATTC_CONNECT_EVT:
printf("Connected to server\n");
break;
case ESP_GATTC_DISCONNECT_EVT:
printf("Disconnected from server\n");
break;
case ESP_GATTC_SEARCH_CMPL_EVT:
printf("Service discovery complete\n");
break;
default:
break;
}
}
// Set GATT client callback
bt.setGattcEventCallback(bleGattcCallback, nullptr);
// Connect to remote device
uint8_t remoteAddr[6] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
bt.connectToDevice(remoteAddr, BLE_ADDR_TYPE_PUBLIC);
```
### Dual Mode Operation
```cpp
// Initialize in dual mode
bt.initialize(BtMode::DUAL);
// Set up BLE advertising
bt.setDeviceName("ESP32-Dual-Mode");
bt.startAdvertising();
// Also available for Bluetooth Classic operations
// (Classic Bluetooth APIs would be used here)
```
## API Reference
### Constructor/Destructor
- **Bluetooth()**: Initialize Bluetooth wrapper instance
- **~Bluetooth()**: Clean up resources and deinitialize Bluetooth
### Initialization Methods
- **initialize(mode)**: Initialize Bluetooth stack with specified mode
- **deinitialize()**: Deinitialize Bluetooth stack
- **isInitialized()**: Check if Bluetooth is initialized
### Device Configuration
- **setDeviceName(name)**: Set Bluetooth device name
- **getDeviceName(name, maxLen)**: Get current device name
- **getMacAddress(mac)**: Get Bluetooth MAC address
### BLE Advertising
- **setAdvParams(params)**: Set advertising parameters
- **setAdvData(advData)**: Set advertising data
- **startAdvertising()**: Start BLE advertising
- **stopAdvertising()**: Stop BLE advertising
### BLE Scanning
- **startScanning(duration)**: Start BLE scanning
- **stopScanning()**: Stop BLE scanning
### GATT Server
- **createGattService(service)**: Create GATT service
- **addCharacteristic(serviceHandle, characteristic)**: Add characteristic to service
- **startGattService(serviceHandle)**: Start GATT service
- **stopGattService(serviceHandle)**: Stop GATT service
- **sendNotification(connId, attrHandle, data, dataLen)**: Send notification
- **sendIndication(connId, attrHandle, data, dataLen)**: Send indication
### GATT Client
- **connectToDevice(remoteAddr, addrType)**: Connect to remote device
- **disconnectDevice(connId)**: Disconnect from device
### Connection Management
- **getConnectionState()**: Get current connection state
- **isConnected()**: Check if any device is connected
- **getConnectedDeviceCount()**: Get number of connected devices
### Event Handling
- **setGapEventCallback(callback, userData)**: Set GAP event callback
- **setGattsEventCallback(callback, userData)**: Set GATT server callback
- **setGattcEventCallback(callback, userData)**: Set GATT client callback
### Power Management
- **setTxPower(powerType, powerLevel)**: Set TX power level
- **getTxPower(powerType)**: Get current TX power level
### Configuration Methods
- **getDefaultAdvParams()**: Get default advertising parameters
- **getDefaultAdvData()**: Get default advertising data
- **getDefaultGattService()**: Get default GATT service configuration
- **getDefaultGattCharacteristic()**: Get default characteristic configuration
## Error Handling
The module provides comprehensive error handling:
- Initialization status checks
- ESP-IDF error codes are caught and logged
- Return values indicate success/failure for all operations
- Event callbacks for asynchronous status updates
- Automatic cleanup of resources on errors
## Dependencies
- ESP-IDF Bluetooth stack (`esp_bt.h`, `esp_bt_main.h`)
- ESP-IDF BLE GAP API (`esp_gap_ble_api.h`)
- ESP-IDF GATT server API (`esp_gatts_api.h`)
- ESP-IDF GATT client API (`esp_gattc_api.h`)
- NVS flash storage (`nvs_flash.h`)
- ESP-IDF error handling (`esp_err.h`)
- ESP-IDF logging (`esp_log.h`)
## Thread Safety
The Bluetooth wrapper uses ESP-IDF's thread-safe Bluetooth stack. Event callbacks are called from the Bluetooth task context and should be kept short.
## Memory Usage
- Fixed memory footprint per instance
- Bluetooth stack uses significant RAM (configurable)
- GATT database stored in flash/RAM
- Event callbacks use minimal stack space
## Performance Considerations
- Bluetooth operations are asynchronous with event callbacks
- GATT operations have latency depending on connection interval
- Multiple simultaneous connections increase memory usage
- Advertising and scanning affect power consumption
## Security Features
### Pairing and Bonding
- Support for various pairing methods
- Secure Simple Pairing (SSP)
- Out-of-Band (OOB) authentication
- Passkey entry and numeric comparison
### Encryption
- AES-128 encryption for BLE
- Link-level security
- Application-level encryption support
## Power Management
### BLE Power Optimization
- Configurable advertising intervals
- Connection interval optimization
- Sleep mode support
- TX power control
### Connection Parameters
- Minimum/maximum connection intervals
- Slave latency configuration
- Supervision timeout settings
## Common Use Cases
### IoT Sensor Node
- BLE advertising with sensor data
- GATT server for configuration
- Low power operation
- Mobile app connectivity
### BLE Gateway
- Multiple device connections
- Data aggregation and forwarding
- WiFi + BLE dual connectivity
- Cloud integration
### HID Device
- Keyboard/mouse emulation
- Custom HID reports
- Pairing and bonding
- Battery level reporting
### Beacon Applications
- iBeacon/Eddystone protocols
- Proximity detection
- Asset tracking
- Location services
## Troubleshooting
### Common Issues
1. **Initialization Failures**: Check NVS partition and Bluetooth configuration
2. **Connection Issues**: Verify advertising parameters and device compatibility
3. **GATT Errors**: Check service/characteristic UUIDs and permissions
4. **Memory Issues**: Monitor heap usage, especially with multiple connections
5. **Range Problems**: Check TX power settings and antenna configuration
### Debug Tips
1. Enable Bluetooth logging in menuconfig
2. Use ESP-IDF Bluetooth tools for analysis
3. Monitor connection parameters
4. Check for interference on 2.4GHz band
5. Verify security requirements and capabilities

View File

@@ -1,621 +0,0 @@
/**
* @file bt.cpp
* @brief Bluetooth wrapper component implementation
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "bt.hpp"
#include "logger.hpp"
#include <cstring>
static const char* TAG = "BT_WRAPPER";
// Static instance for callbacks
Bluetooth* Bluetooth::s_instance_ = nullptr;
Bluetooth::Bluetooth()
: m_isInitialized_(false)
, m_mode_(BtMode::BLE)
, m_connectionState_(BleConnectionState::DISCONNECTED)
, m_connectedDeviceCount_(0)
#ifdef CONFIG_BT_ENABLED
, m_gattsIf_(ESP_GATT_IF_NONE)
, m_gattcIf_(ESP_GATT_IF_NONE)
#else
, m_gattsIf_(0)
, m_gattcIf_(0)
#endif
, m_appId_(0)
, m_gapCallback_(nullptr)
, m_gapUserData_(nullptr)
, m_gattsCallback_(nullptr)
, m_gattsUserData_(nullptr)
, m_gattcCallback_(nullptr)
, m_gattcUserData_(nullptr)
{
s_instance_ = this;
ASF_LOGI(TAG, 2100, asf::logger::Criticality::LOW, "Bluetooth wrapper initialized");
}
Bluetooth::~Bluetooth()
{
deinitialize();
s_instance_ = nullptr;
ASF_LOGI(TAG, 2101, asf::logger::Criticality::LOW, "Bluetooth wrapper destroyed");
}
bool Bluetooth::initialize(BtMode mode)
{
#ifdef CONFIG_BT_ENABLED
if (m_isInitialized_) {
ASF_LOGW(TAG, 2102, asf::logger::Criticality::MEDIUM, "Bluetooth already initialized");
return true;
}
// Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// Release Bluetooth controller memory if not needed
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
// Initialize Bluetooth controller
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&bt_cfg);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2103, asf::logger::Criticality::HIGH, "Failed to initialize BT controller: %s", esp_err_to_name(ret));
return false;
}
// Enable Bluetooth controller
ret = esp_bt_controller_enable(convertMode(mode));
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2104, asf::logger::Criticality::HIGH, "Failed to enable BT controller: %s", esp_err_to_name(ret));
return false;
}
// Initialize Bluedroid stack
ret = esp_bluedroid_init();
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2105, asf::logger::Criticality::HIGH, "Failed to initialize Bluedroid: %s", esp_err_to_name(ret));
return false;
}
// Enable Bluedroid stack
ret = esp_bluedroid_enable();
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2106, asf::logger::Criticality::HIGH, "Failed to enable Bluedroid: %s", esp_err_to_name(ret));
return false;
}
// Register GAP callback
ret = esp_ble_gap_register_callback(gapEventHandler);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2107, asf::logger::Criticality::HIGH, "Failed to register GAP callback: %s", esp_err_to_name(ret));
return false;
}
// Register GATTS callback
ret = esp_ble_gatts_register_callback(gattsEventHandler);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2108, asf::logger::Criticality::HIGH, "Failed to register GATTS callback: %s", esp_err_to_name(ret));
return false;
}
// Register GATTC callback
ret = esp_ble_gattc_register_callback(gattcEventHandler);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2109, asf::logger::Criticality::HIGH, "Failed to register GATTC callback: %s", esp_err_to_name(ret));
return false;
}
m_mode_ = mode;
m_isInitialized_ = true;
ASF_LOGI(TAG, 2110, asf::logger::Criticality::LOW, "Bluetooth initialized successfully");
return true;
#else
ASF_LOGW(TAG, 2111, asf::logger::Criticality::MEDIUM, "Bluetooth disabled in sdkconfig");
return false;
#endif
}
bool Bluetooth::deinitialize()
{
#ifdef CONFIG_BT_ENABLED
if (!m_isInitialized_) {
ASF_LOGW(TAG, 2112, asf::logger::Criticality::MEDIUM, "Bluetooth not initialized");
return true;
}
stopAdvertising();
stopScanning();
// Disable Bluedroid stack
esp_err_t ret = esp_bluedroid_disable();
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2113, asf::logger::Criticality::HIGH, "Failed to disable Bluedroid: %s", esp_err_to_name(ret));
}
// Deinitialize Bluedroid stack
ret = esp_bluedroid_deinit();
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2114, asf::logger::Criticality::HIGH, "Failed to deinitialize Bluedroid: %s", esp_err_to_name(ret));
}
// Disable Bluetooth controller
ret = esp_bt_controller_disable();
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2115, asf::logger::Criticality::HIGH, "Failed to disable BT controller: %s", esp_err_to_name(ret));
}
// Deinitialize Bluetooth controller
ret = esp_bt_controller_deinit();
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2116, asf::logger::Criticality::HIGH, "Failed to deinitialize BT controller: %s", esp_err_to_name(ret));
}
m_isInitialized_ = false;
m_connectionState_ = BleConnectionState::DISCONNECTED;
m_connectedDeviceCount_ = 0;
ASF_LOGI(TAG, 2117, asf::logger::Criticality::LOW, "Bluetooth deinitialized");
return true;
#else
return true;
#endif
}
bool Bluetooth::setDeviceName(const char* name)
{
#ifdef CONFIG_BT_ENABLED
if (!m_isInitialized_ || name == nullptr) {
ASF_LOGE(TAG, 2118, asf::logger::Criticality::HIGH, "Bluetooth not initialized or invalid name");
return false;
}
esp_err_t ret = esp_ble_gap_set_device_name(name);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2119, asf::logger::Criticality::HIGH, "Failed to set device name: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2120, asf::logger::Criticality::LOW, "Device name set to: %s", name);
return true;
#else
return false;
#endif
}
bool Bluetooth::getDeviceName(char* name, size_t maxLen)
{
if (name == nullptr || maxLen == 0) return false;
strncpy(name, "ESP32-BT", maxLen - 1);
name[maxLen - 1] = '\0';
return true;
}
bool Bluetooth::setAdvParams(const BleAdvParams& params)
{
#ifdef CONFIG_BT_ENABLED
if (!m_isInitialized_) {
ASF_LOGE(TAG, 2121, asf::logger::Criticality::HIGH, "Bluetooth not initialized");
return false;
}
esp_ble_adv_params_t advParams = {};
advParams.adv_int_min = params.advIntMin;
advParams.adv_int_max = params.advIntMax;
advParams.adv_type = convertAdvType(params.advType);
advParams.own_addr_type = static_cast<esp_ble_addr_type_t>(params.ownAddrType);
advParams.peer_addr_type = static_cast<esp_ble_addr_type_t>(params.peerAddrType);
memcpy(advParams.peer_addr, params.peerAddr, 6);
advParams.channel_map = static_cast<esp_ble_adv_channel_t>(params.channelMap);
advParams.adv_filter_policy = static_cast<esp_ble_adv_filter_t>(params.advFilterPolicy);
esp_err_t ret = esp_ble_gap_config_adv_data_raw(nullptr, 0);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2122, asf::logger::Criticality::HIGH, "Failed to set advertising parameters: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2123, asf::logger::Criticality::LOW, "Advertising parameters set successfully");
return true;
#else
return false;
#endif
}
bool Bluetooth::setAdvData(const BleAdvData& advData)
{
#ifdef CONFIG_BT_ENABLED
if (!m_isInitialized_) {
ASF_LOGE(TAG, 2124, asf::logger::Criticality::HIGH, "Bluetooth not initialized");
return false;
}
esp_ble_adv_data_t bleAdvData = {};
bleAdvData.set_scan_rsp = false;
bleAdvData.include_name = advData.setName;
bleAdvData.include_txpower = advData.setTxPower;
bleAdvData.min_interval = 0x0006;
bleAdvData.max_interval = 0x0010;
bleAdvData.appearance = advData.appearance;
bleAdvData.manufacturer_len = advData.manufacturerDataLen;
bleAdvData.p_manufacturer_data = advData.manufacturerData;
bleAdvData.service_data_len = 0;
bleAdvData.p_service_data = nullptr;
bleAdvData.service_uuid_len = advData.serviceUuidLen;
bleAdvData.p_service_uuid = advData.serviceUuid;
bleAdvData.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT);
esp_err_t ret = esp_ble_gap_config_adv_data(&bleAdvData);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2125, asf::logger::Criticality::HIGH, "Failed to set advertising data: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2126, asf::logger::Criticality::LOW, "Advertising data set successfully");
return true;
#else
return false;
#endif
}
bool Bluetooth::startAdvertising()
{
#ifdef CONFIG_BT_ENABLED
if (!m_isInitialized_) {
ASF_LOGE(TAG, 2127, asf::logger::Criticality::HIGH, "Bluetooth not initialized");
return false;
}
esp_ble_adv_params_t params = {};
params.adv_int_min = 0x20;
params.adv_int_max = 0x40;
params.adv_type = ADV_TYPE_IND;
params.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
params.channel_map = ADV_CHNL_ALL;
params.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY;
esp_err_t ret = esp_ble_gap_start_advertising(&params);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2128, asf::logger::Criticality::HIGH, "Failed to start advertising: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2129, asf::logger::Criticality::LOW, "BLE advertising started");
return true;
#else
return false;
#endif
}
bool Bluetooth::stopAdvertising()
{
#ifdef CONFIG_BT_ENABLED
if (!m_isInitialized_) {
ASF_LOGW(TAG, 2130, asf::logger::Criticality::MEDIUM, "Bluetooth not initialized");
return true;
}
esp_err_t ret = esp_ble_gap_stop_advertising();
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2131, asf::logger::Criticality::HIGH, "Failed to stop advertising: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2132, asf::logger::Criticality::LOW, "BLE advertising stopped");
return true;
#else
return true;
#endif
}
bool Bluetooth::startScanning(uint32_t duration)
{
#ifdef CONFIG_BT_ENABLED
if (!m_isInitialized_) {
ASF_LOGE(TAG, 2133, asf::logger::Criticality::HIGH, "Bluetooth not initialized");
return false;
}
esp_ble_scan_params_t scanParams = {};
scanParams.scan_type = BLE_SCAN_TYPE_ACTIVE;
scanParams.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
scanParams.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL;
scanParams.scan_interval = 0x50;
scanParams.scan_window = 0x30;
scanParams.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE;
esp_err_t ret = esp_ble_gap_set_scan_params(&scanParams);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2134, asf::logger::Criticality::HIGH, "Failed to set scan parameters: %s", esp_err_to_name(ret));
return false;
}
ret = esp_ble_gap_start_scanning(duration);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2135, asf::logger::Criticality::HIGH, "Failed to start scanning: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2136, asf::logger::Criticality::LOW, "BLE scanning started (duration: %lu seconds)", duration);
return true;
#else
return false;
#endif
}
bool Bluetooth::stopScanning()
{
#ifdef CONFIG_BT_ENABLED
if (!m_isInitialized_) {
ASF_LOGW(TAG, 2137, asf::logger::Criticality::MEDIUM, "Bluetooth not initialized");
return true;
}
esp_err_t ret = esp_ble_gap_stop_scanning();
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2138, asf::logger::Criticality::HIGH, "Failed to stop scanning: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2139, asf::logger::Criticality::LOW, "BLE scanning stopped");
return true;
#else
return true;
#endif
}
uint16_t Bluetooth::createGattService(const GattService& service)
{
#ifdef CONFIG_BT_ENABLED
if (!m_isInitialized_) {
ASF_LOGE(TAG, 2140, asf::logger::Criticality::HIGH, "Bluetooth not initialized");
return 0;
}
esp_gatt_srvc_id_t serviceId = {};
serviceId.is_primary = service.isPrimary;
serviceId.id.inst_id = service.serviceId;
serviceId.id.uuid.len = ESP_UUID_LEN_16;
serviceId.id.uuid.uuid.uuid16 = service.serviceUuid;
esp_err_t ret = esp_ble_gatts_create_service(m_gattsIf_, &serviceId, service.numHandles);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2141, asf::logger::Criticality::HIGH, "Failed to create GATT service: %s", esp_err_to_name(ret));
return 0;
}
ASF_LOGI(TAG, 2142, asf::logger::Criticality::LOW, "GATT service created successfully");
return service.serviceId;
#else
return 0;
#endif
}
bool Bluetooth::startGattService(uint16_t serviceHandle)
{
#ifdef CONFIG_BT_ENABLED
if (!m_isInitialized_) {
ASF_LOGE(TAG, 2143, asf::logger::Criticality::HIGH, "Bluetooth not initialized");
return false;
}
esp_err_t ret = esp_ble_gatts_start_service(serviceHandle);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2144, asf::logger::Criticality::HIGH, "Failed to start GATT service: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2145, asf::logger::Criticality::LOW, "GATT service started successfully");
return true;
#else
return false;
#endif
}
BleConnectionState Bluetooth::getConnectionState() const
{
return m_connectionState_;
}
bool Bluetooth::isConnected() const
{
return m_connectionState_ == BleConnectionState::CONNECTED;
}
uint8_t Bluetooth::getConnectedDeviceCount() const
{
return m_connectedDeviceCount_;
}
void Bluetooth::setGapEventCallback(BtGapEventCallback callback, void* userData)
{
m_gapCallback_ = callback;
m_gapUserData_ = userData;
}
void Bluetooth::setGattsEventCallback(BtGattsEventCallback callback, void* userData)
{
m_gattsCallback_ = callback;
m_gattsUserData_ = userData;
}
void Bluetooth::setGattcEventCallback(BtGattcEventCallback callback, void* userData)
{
m_gattcCallback_ = callback;
m_gattcUserData_ = userData;
}
bool Bluetooth::getMacAddress(uint8_t* mac)
{
#ifdef CONFIG_BT_ENABLED
if (mac == nullptr) {
ASF_LOGE(TAG, 2146, asf::logger::Criticality::HIGH, "Invalid MAC address buffer");
return false;
}
memcpy(mac, esp_bt_dev_get_address(), 6);
return true;
#else
return false;
#endif
}
bool Bluetooth::isInitialized() const
{
return m_isInitialized_;
}
BleAdvParams Bluetooth::getDefaultAdvParams()
{
BleAdvParams params = {};
params.advIntMin = 0x20;
params.advIntMax = 0x40;
params.advType = BleAdvType::ADV_IND;
params.ownAddrType = 0;
params.peerAddrType = 0;
memset(params.peerAddr, 0, 6);
params.channelMap = 7;
params.advFilterPolicy = 0;
return params;
}
BleAdvData Bluetooth::getDefaultAdvData()
{
BleAdvData advData = {};
advData.setName = true;
advData.setTxPower = true;
advData.includeUuid = false;
advData.setManufacturerData = false;
advData.appearance = 0;
advData.manufacturerId = 0;
advData.manufacturerDataLen = 0;
advData.manufacturerData = nullptr;
advData.serviceUuidLen = 0;
advData.serviceUuid = nullptr;
advData.deviceName = nullptr;
return advData;
}
GattService Bluetooth::getDefaultGattService()
{
GattService service = {};
service.serviceId = 0;
service.serviceUuid = 0x180F; // Battery Service UUID
service.numHandles = 4;
service.isPrimary = true;
return service;
}
GattCharacteristic Bluetooth::getDefaultGattCharacteristic()
{
GattCharacteristic characteristic = {};
characteristic.charUuid = 0x2A19; // Battery Level Characteristic UUID
characteristic.properties = 0x12; // Read | Notify
characteristic.permissions = 0x01; // Read
characteristic.maxLen = 1;
characteristic.autoRsp = true;
return characteristic;
}
#ifdef CONFIG_BT_ENABLED
void Bluetooth::gapEventHandler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param)
{
if (s_instance_ && s_instance_->m_gapCallback_) {
s_instance_->m_gapCallback_(event, param, s_instance_->m_gapUserData_);
}
switch (event) {
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
ASF_LOGI(TAG, 2147, asf::logger::Criticality::LOW, "GAP: Advertising data set complete");
break;
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
ASF_LOGI(TAG, 2148, asf::logger::Criticality::LOW, "GAP: Advertising start complete");
break;
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
ASF_LOGI(TAG, 2149, asf::logger::Criticality::LOW, "GAP: Advertising stop complete");
break;
default:
break;
}
}
void Bluetooth::gattsEventHandler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param)
{
if (s_instance_ && s_instance_->m_gattsCallback_) {
s_instance_->m_gattsCallback_(event, gatts_if, param, s_instance_->m_gattsUserData_);
}
switch (event) {
case ESP_GATTS_REG_EVT:
ASF_LOGI(TAG, 2150, asf::logger::Criticality::LOW, "GATTS: Register complete");
if (s_instance_) {
s_instance_->m_gattsIf_ = gatts_if;
}
break;
case ESP_GATTS_CONNECT_EVT:
ASF_LOGI(TAG, 2151, asf::logger::Criticality::LOW, "GATTS: Device connected");
if (s_instance_) {
s_instance_->m_connectionState_ = BleConnectionState::CONNECTED;
s_instance_->m_connectedDeviceCount_++;
}
break;
case ESP_GATTS_DISCONNECT_EVT:
ASF_LOGI(TAG, 2152, asf::logger::Criticality::LOW, "GATTS: Device disconnected");
if (s_instance_) {
s_instance_->m_connectionState_ = BleConnectionState::DISCONNECTED;
if (s_instance_->m_connectedDeviceCount_ > 0) {
s_instance_->m_connectedDeviceCount_--;
}
}
break;
default:
break;
}
}
void Bluetooth::gattcEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param)
{
if (s_instance_ && s_instance_->m_gattcCallback_) {
s_instance_->m_gattcCallback_(event, gattc_if, param, s_instance_->m_gattcUserData_);
}
switch (event) {
case ESP_GATTC_REG_EVT:
ASF_LOGI(TAG, 2153, asf::logger::Criticality::LOW, "GATTC: Register complete");
if (s_instance_) {
s_instance_->m_gattcIf_ = gattc_if;
}
break;
default:
break;
}
}
esp_bt_mode_t Bluetooth::convertMode(BtMode mode)
{
return static_cast<esp_bt_mode_t>(mode);
}
esp_ble_adv_type_t Bluetooth::convertAdvType(BleAdvType advType)
{
return static_cast<esp_ble_adv_type_t>(advType);
}
#endif
bool Bluetooth::stopGattService(uint16_t serviceHandle) { return true; }
bool Bluetooth::sendNotification(uint16_t connId, uint16_t attrHandle, const uint8_t* data, size_t dataLen) { return true; }
bool Bluetooth::sendIndication(uint16_t connId, uint16_t attrHandle, const uint8_t* data, size_t dataLen) { return true; }
bool Bluetooth::connectToDevice(const uint8_t* remoteAddr, uint8_t addrType) { return true; }
bool Bluetooth::disconnectDevice(uint16_t connId) { return true; }
#ifdef CONFIG_BT_ENABLED
bool Bluetooth::setTxPower(esp_ble_power_type_t powerType, esp_power_level_t powerLevel) { return true; }
esp_power_level_t Bluetooth::getTxPower(esp_ble_power_type_t powerType) { return ESP_PWR_LVL_N0; }
#endif

View File

@@ -1,457 +0,0 @@
/**
* @file bt.hpp
* @brief Bluetooth wrapper component header - Wrapper for ESP-IDF Bluetooth functionality
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#ifndef BT_HPP
#define BT_HPP
#include <cstdint>
#include <cstring>
#include "esp_err.h"
#include "nvs_flash.h"
#ifdef CONFIG_BT_ENABLED
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_gattc_api.h"
#include "esp_bt_defs.h"
#endif
/**
* @brief Bluetooth mode enumeration
*/
enum class BtMode
{
#ifdef CONFIG_BT_ENABLED
CLASSIC = ESP_BT_MODE_CLASSIC_BT,
BLE = ESP_BT_MODE_BLE,
DUAL = ESP_BT_MODE_BTDM
#else
CLASSIC,
BLE,
DUAL
#endif
};
/**
* @brief BLE advertising type enumeration
*/
enum class BleAdvType
{
#ifdef CONFIG_BT_ENABLED
ADV_IND = ADV_TYPE_IND,
ADV_DIRECT_IND_HIGH = ADV_TYPE_DIRECT_IND_HIGH,
ADV_SCAN_IND = ADV_TYPE_SCAN_IND,
ADV_NONCONN_IND = ADV_TYPE_NONCONN_IND,
ADV_DIRECT_IND_LOW = ADV_TYPE_DIRECT_IND_LOW
#else
ADV_IND,
ADV_DIRECT_IND_HIGH,
ADV_SCAN_IND,
ADV_NONCONN_IND,
ADV_DIRECT_IND_LOW
#endif
};
/**
* @brief BLE connection state enumeration
*/
enum class BleConnectionState
{
DISCONNECTED,
CONNECTING,
CONNECTED,
DISCONNECTING
};
/**
* @brief BLE advertising parameters structure
*/
struct BleAdvParams
{
uint16_t advIntMin; ///< Minimum advertising interval
uint16_t advIntMax; ///< Maximum advertising interval
BleAdvType advType; ///< Advertising type
uint8_t ownAddrType; ///< Own address type
uint8_t peerAddrType; ///< Peer address type
uint8_t peerAddr[6]; ///< Peer address
uint8_t channelMap; ///< Channel map
uint8_t advFilterPolicy; ///< Advertising filter policy
};
/**
* @brief BLE advertising data structure
*/
struct BleAdvData
{
bool setName; ///< Set device name in advertising data
bool setTxPower; ///< Set TX power in advertising data
bool includeUuid; ///< Include service UUID
bool setManufacturerData; ///< Set manufacturer data
uint16_t appearance; ///< Device appearance
uint16_t manufacturerId; ///< Manufacturer ID
uint8_t manufacturerDataLen; ///< Manufacturer data length
uint8_t* manufacturerData; ///< Manufacturer data
uint8_t serviceUuidLen; ///< Service UUID length
uint8_t* serviceUuid; ///< Service UUID
char* deviceName; ///< Device name
};
/**
* @brief GATT service structure
*/
struct GattService
{
uint16_t serviceId; ///< Service ID
uint16_t serviceUuid; ///< Service UUID
uint16_t numHandles; ///< Number of handles
bool isPrimary; ///< Primary service flag
};
/**
* @brief GATT characteristic structure
*/
struct GattCharacteristic
{
uint16_t charUuid; ///< Characteristic UUID
uint8_t properties; ///< Characteristic properties
uint8_t permissions; ///< Characteristic permissions
uint16_t maxLen; ///< Maximum value length
bool autoRsp; ///< Auto response flag
};
/**
* @brief Bluetooth event callback function types
*/
#ifdef CONFIG_BT_ENABLED
using BtGapEventCallback = void (*)(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param, void* userData);
using BtGattsEventCallback = void (*)(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param, void* userData);
using BtGattcEventCallback = void (*)(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param, void* userData);
#else
using BtGapEventCallback = void (*)(int event, void* param, void* userData);
using BtGattsEventCallback = void (*)(int event, int gatts_if, void* param, void* userData);
using BtGattcEventCallback = void (*)(int event, int gattc_if, void* param, void* userData);
#endif
/**
* @brief Bluetooth wrapper class
*
* Provides a C++ wrapper for ESP-IDF Bluetooth functionality.
* This class encapsulates ESP-IDF Bluetooth and BLE driver functions in an object-oriented interface.
*/
class Bluetooth
{
public:
/**
* @brief Constructor
* @details Initializes the Bluetooth wrapper instance
*/
Bluetooth();
/**
* @brief Destructor
* @details Cleans up resources and deinitializes Bluetooth
*/
~Bluetooth();
/**
* @brief Initialize Bluetooth stack
* @param mode Bluetooth mode (Classic, BLE, or Dual)
* @return true if initialized successfully, false otherwise
*/
bool initialize(BtMode mode);
/**
* @brief Deinitialize Bluetooth stack
* @return true if deinitialized successfully, false otherwise
*/
bool deinitialize();
/**
* @brief Set device name
* @param name Device name (max 248 characters)
* @return true if set successfully, false otherwise
*/
bool setDeviceName(const char* name);
/**
* @brief Get device name
* @param name Buffer to store device name
* @param maxLen Maximum length of name buffer
* @return true if retrieved successfully, false otherwise
*/
bool getDeviceName(char* name, size_t maxLen);
/**
* @brief Set advertising parameters
* @param params Advertising parameters
* @return true if set successfully, false otherwise
*/
bool setAdvParams(const BleAdvParams& params);
/**
* @brief Set advertising data
* @param advData Advertising data
* @return true if set successfully, false otherwise
*/
bool setAdvData(const BleAdvData& advData);
/**
* @brief Start BLE advertising
* @return true if started successfully, false otherwise
*/
bool startAdvertising();
/**
* @brief Stop BLE advertising
* @return true if stopped successfully, false otherwise
*/
bool stopAdvertising();
/**
* @brief Start BLE scanning
* @param duration Scan duration in seconds (0 = continuous)
* @return true if started successfully, false otherwise
*/
bool startScanning(uint32_t duration = 0);
/**
* @brief Stop BLE scanning
* @return true if stopped successfully, false otherwise
*/
bool stopScanning();
/**
* @brief Create GATT service
* @param service Service configuration
* @return Service handle, or 0 on failure
*/
uint16_t createGattService(const GattService& service);
/**
* @brief Add characteristic to service
* @param serviceHandle Service handle
* @param characteristic Characteristic configuration
* @return Characteristic handle, or 0 on failure
*/
uint16_t addCharacteristic(uint16_t serviceHandle, const GattCharacteristic& characteristic);
/**
* @brief Start GATT service
* @param serviceHandle Service handle
* @return true if started successfully, false otherwise
*/
bool startGattService(uint16_t serviceHandle);
/**
* @brief Stop GATT service
* @param serviceHandle Service handle
* @return true if stopped successfully, false otherwise
*/
bool stopGattService(uint16_t serviceHandle);
/**
* @brief Send notification to connected client
* @param connId Connection ID
* @param attrHandle Attribute handle
* @param data Data to send
* @param dataLen Length of data
* @return true if sent successfully, false otherwise
*/
bool sendNotification(uint16_t connId, uint16_t attrHandle, const uint8_t* data, size_t dataLen);
/**
* @brief Send indication to connected client
* @param connId Connection ID
* @param attrHandle Attribute handle
* @param data Data to send
* @param dataLen Length of data
* @return true if sent successfully, false otherwise
*/
bool sendIndication(uint16_t connId, uint16_t attrHandle, const uint8_t* data, size_t dataLen);
/**
* @brief Connect to remote device
* @param remoteAddr Remote device address
* @param addrType Address type
* @return true if connection initiated successfully, false otherwise
*/
bool connectToDevice(const uint8_t* remoteAddr, uint8_t addrType);
/**
* @brief Disconnect from remote device
* @param connId Connection ID
* @return true if disconnection initiated successfully, false otherwise
*/
bool disconnectDevice(uint16_t connId);
/**
* @brief Get connection state
* @return Current connection state
*/
BleConnectionState getConnectionState() const;
/**
* @brief Check if device is connected
* @return true if connected, false otherwise
*/
bool isConnected() const;
/**
* @brief Get number of connected devices
* @return Number of connected devices
*/
uint8_t getConnectedDeviceCount() const;
/**
* @brief Set GAP event callback
* @param callback Callback function
* @param userData User data passed to callback
*/
void setGapEventCallback(BtGapEventCallback callback, void* userData);
/**
* @brief Set GATTS event callback
* @param callback Callback function
* @param userData User data passed to callback
*/
void setGattsEventCallback(BtGattsEventCallback callback, void* userData);
/**
* @brief Set GATTC event callback
* @param callback Callback function
* @param userData User data passed to callback
*/
void setGattcEventCallback(BtGattcEventCallback callback, void* userData);
/**
* @brief Get MAC address
* @param mac Buffer to store MAC address (6 bytes)
* @return true if retrieved successfully, false otherwise
*/
bool getMacAddress(uint8_t* mac);
/**
* @brief Set TX power
* @param powerType Power type
* @param powerLevel Power level
* @return true if set successfully, false otherwise
*/
#ifdef CONFIG_BT_ENABLED
bool setTxPower(esp_ble_power_type_t powerType, esp_power_level_t powerLevel);
#endif
/**
* @brief Get TX power
* @param powerType Power type
* @return Current power level, or ESP_PWR_LVL_INVALID on error
*/
#ifdef CONFIG_BT_ENABLED
esp_power_level_t getTxPower(esp_ble_power_type_t powerType);
#endif
/**
* @brief Check if Bluetooth is initialized
* @return true if initialized, false otherwise
*/
bool isInitialized() const;
/**
* @brief Get default advertising parameters
* @return Default BLE advertising parameters
*/
static BleAdvParams getDefaultAdvParams();
/**
* @brief Get default advertising data
* @return Default BLE advertising data
*/
static BleAdvData getDefaultAdvData();
/**
* @brief Get default GATT service configuration
* @return Default GATT service configuration
*/
static GattService getDefaultGattService();
/**
* @brief Get default GATT characteristic configuration
* @return Default GATT characteristic configuration
*/
static GattCharacteristic getDefaultGattCharacteristic();
private:
bool m_isInitialized_; ///< Initialization status
BtMode m_mode_; ///< Bluetooth mode
BleConnectionState m_connectionState_; ///< Current connection state
uint8_t m_connectedDeviceCount_; ///< Number of connected devices
#ifdef CONFIG_BT_ENABLED
esp_gatt_if_t m_gattsIf_; ///< GATTS interface
esp_gatt_if_t m_gattcIf_; ///< GATTC interface
#else
int m_gattsIf_;
int m_gattcIf_;
#endif
uint16_t m_appId_; ///< Application ID
// Callback functions and user data
BtGapEventCallback m_gapCallback_; ///< GAP event callback
void* m_gapUserData_; ///< GAP callback user data
BtGattsEventCallback m_gattsCallback_; ///< GATTS event callback
void* m_gattsUserData_; ///< GATTS callback user data
BtGattcEventCallback m_gattcCallback_; ///< GATTC event callback
void* m_gattcUserData_; ///< GATTC callback user data
#ifdef CONFIG_BT_ENABLED
/**
* @brief GAP event handler
* @param event GAP event
* @param param Event parameters
*/
static void gapEventHandler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param);
/**
* @brief GATTS event handler
* @param event GATTS event
* @param gatts_if GATTS interface
* @param param Event parameters
*/
static void gattsEventHandler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param);
/**
* @brief GATTC event handler
* @param event GATTC event
* @param gattc_if GATTC interface
* @param param Event parameters
*/
static void gattcEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param);
#endif
/**
* @brief Convert BtMode to ESP-IDF esp_bt_mode_t
* @param mode Bluetooth mode
* @return ESP-IDF bluetooth mode
*/
#ifdef CONFIG_BT_ENABLED
esp_bt_mode_t convertMode(BtMode mode);
#endif
/**
* @brief Convert BleAdvType to ESP-IDF esp_ble_adv_type_t
* @param advType Advertising type
* @return ESP-IDF advertising type
*/
#ifdef CONFIG_BT_ENABLED
esp_ble_adv_type_t convertAdvType(BleAdvType advType);
#endif
static Bluetooth* s_instance_; ///< Static instance for callbacks
};
#endif // BT_HPP

View File

@@ -1,55 +0,0 @@
ID,Component,Level,Criticality,Message
2100,BT,INFO,Low,Bluetooth wrapper initialized
2101,BT,INFO,Low,Bluetooth wrapper destroyed
2102,BT,WARNING,Medium,Bluetooth already initialized
2103,BT,ERROR,High,Failed to initialize BT controller: %s
2104,BT,ERROR,High,Failed to enable BT controller: %s
2105,BT,ERROR,High,Failed to initialize Bluedroid: %s
2106,BT,ERROR,High,Failed to enable Bluedroid: %s
2107,BT,ERROR,High,Failed to register GAP callback: %s
2108,BT,ERROR,High,Failed to register GATTS callback: %s
2109,BT,ERROR,High,Failed to register GATTC callback: %s
2110,BT,INFO,Low,Bluetooth initialized successfully
2111,BT,WARNING,Medium,Bluetooth disabled in sdkconfig
2112,BT,WARNING,Medium,Bluetooth not initialized
2113,BT,ERROR,High,Failed to disable Bluedroid: %s
2114,BT,ERROR,High,Failed to deinitialize Bluedroid: %s
2115,BT,ERROR,High,Failed to disable BT controller: %s
2116,BT,ERROR,High,Failed to deinitialize BT controller: %s
2117,BT,INFO,Low,Bluetooth deinitialized
2118,BT,ERROR,High,Bluetooth not initialized or invalid name
2119,BT,ERROR,High,Failed to set device name: %s
2120,BT,INFO,Low,Device name set to: %s
2121,BT,ERROR,High,Bluetooth not initialized
2122,BT,ERROR,High,Failed to set advertising parameters: %s
2123,BT,INFO,Low,Advertising parameters set successfully
2124,BT,ERROR,High,Bluetooth not initialized
2125,BT,ERROR,High,Failed to set advertising data: %s
2126,BT,INFO,Low,Advertising data set successfully
2127,BT,ERROR,High,Bluetooth not initialized
2128,BT,ERROR,High,Failed to start advertising: %s
2129,BT,INFO,Low,BLE advertising started
2130,BT,WARNING,Medium,Bluetooth not initialized
2131,BT,ERROR,High,Failed to stop advertising: %s
2132,BT,INFO,Low,BLE advertising stopped
2133,BT,ERROR,High,Bluetooth not initialized
2134,BT,ERROR,High,Failed to set scan parameters: %s
2135,BT,ERROR,High,Failed to start scanning: %s
2136,BT,INFO,Low,BLE scanning started (duration: %lu seconds)
2137,BT,WARNING,Medium,Bluetooth not initialized
2138,BT,ERROR,High,Failed to stop scanning: %s
2139,BT,INFO,Low,BLE scanning stopped
2140,BT,ERROR,High,Bluetooth not initialized
2141,BT,ERROR,High,Failed to create GATT service: %s
2142,BT,INFO,Low,GATT service created successfully
2143,BT,ERROR,High,Bluetooth not initialized
2144,BT,ERROR,High,Failed to start GATT service: %s
2145,BT,INFO,Low,GATT service started successfully
2146,BT,ERROR,High,Invalid MAC address buffer
2147,BT,INFO,Low,GAP: Advertising data set complete
2148,BT,INFO,Low,GAP: Advertising start complete
2149,BT,INFO,Low,GAP: Advertising stop complete
2150,BT,INFO,Low,GATTS: Register complete
2151,BT,INFO,Low,GATTS: Device connected
2152,BT,INFO,Low,GATTS: Device disconnected
2153,BT,INFO,Low,GATTC: Register complete
1 ID Component Level Criticality Message
2 2100 BT INFO Low Bluetooth wrapper initialized
3 2101 BT INFO Low Bluetooth wrapper destroyed
4 2102 BT WARNING Medium Bluetooth already initialized
5 2103 BT ERROR High Failed to initialize BT controller: %s
6 2104 BT ERROR High Failed to enable BT controller: %s
7 2105 BT ERROR High Failed to initialize Bluedroid: %s
8 2106 BT ERROR High Failed to enable Bluedroid: %s
9 2107 BT ERROR High Failed to register GAP callback: %s
10 2108 BT ERROR High Failed to register GATTS callback: %s
11 2109 BT ERROR High Failed to register GATTC callback: %s
12 2110 BT INFO Low Bluetooth initialized successfully
13 2111 BT WARNING Medium Bluetooth disabled in sdkconfig
14 2112 BT WARNING Medium Bluetooth not initialized
15 2113 BT ERROR High Failed to disable Bluedroid: %s
16 2114 BT ERROR High Failed to deinitialize Bluedroid: %s
17 2115 BT ERROR High Failed to disable BT controller: %s
18 2116 BT ERROR High Failed to deinitialize BT controller: %s
19 2117 BT INFO Low Bluetooth deinitialized
20 2118 BT ERROR High Bluetooth not initialized or invalid name
21 2119 BT ERROR High Failed to set device name: %s
22 2120 BT INFO Low Device name set to: %s
23 2121 BT ERROR High Bluetooth not initialized
24 2122 BT ERROR High Failed to set advertising parameters: %s
25 2123 BT INFO Low Advertising parameters set successfully
26 2124 BT ERROR High Bluetooth not initialized
27 2125 BT ERROR High Failed to set advertising data: %s
28 2126 BT INFO Low Advertising data set successfully
29 2127 BT ERROR High Bluetooth not initialized
30 2128 BT ERROR High Failed to start advertising: %s
31 2129 BT INFO Low BLE advertising started
32 2130 BT WARNING Medium Bluetooth not initialized
33 2131 BT ERROR High Failed to stop advertising: %s
34 2132 BT INFO Low BLE advertising stopped
35 2133 BT ERROR High Bluetooth not initialized
36 2134 BT ERROR High Failed to set scan parameters: %s
37 2135 BT ERROR High Failed to start scanning: %s
38 2136 BT INFO Low BLE scanning started (duration: %lu seconds)
39 2137 BT WARNING Medium Bluetooth not initialized
40 2138 BT ERROR High Failed to stop scanning: %s
41 2139 BT INFO Low BLE scanning stopped
42 2140 BT ERROR High Bluetooth not initialized
43 2141 BT ERROR High Failed to create GATT service: %s
44 2142 BT INFO Low GATT service created successfully
45 2143 BT ERROR High Bluetooth not initialized
46 2144 BT ERROR High Failed to start GATT service: %s
47 2145 BT INFO Low GATT service started successfully
48 2146 BT ERROR High Invalid MAC address buffer
49 2147 BT INFO Low GAP: Advertising data set complete
50 2148 BT INFO Low GAP: Advertising start complete
51 2149 BT INFO Low GAP: Advertising stop complete
52 2150 BT INFO Low GATTS: Register complete
53 2151 BT INFO Low GATTS: Device connected
54 2152 BT INFO Low GATTS: Device disconnected
55 2153 BT INFO Low GATTC: Register complete

View File

@@ -1,34 +0,0 @@
import sys
import os
import time
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
if folder_path not in sys.path:
sys.path.append(folder_path)
from scan_serial import ESP32Runner
def test_bt_initialize():
runner = ESP32Runner(mode="SIM", port="COM9")
runner.start()
print("--- QEMU Runner Started ---", flush=True)
try:
start_time = time.time()
while time.time() - start_time < 30:
line = runner.get_line(timeout=1.0)
if line:
print(line, flush=True)
if "Bluetooth initialized successfully" in line:
print("SUCCESS CRITERIA MET!", flush=True)
return 0
if runner.process.poll() is not None:
print(f"Process exited with code: {runner.process.returncode}", flush=True)
return 1
finally:
runner.stop()
print("Done.", flush=True)
return 1
if __name__ == "__main__":
exit_code = test_bt_initialize()
sys.exit(exit_code)

View File

@@ -1,39 +0,0 @@
/**
* @file test_bt.cpp
* @brief Unit tests for Bluetooth wrapper component
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "unity.h"
#include "bt.hpp"
extern "C" {
void setUp(void)
{
}
void tearDown(void)
{
}
void test_bt_initialize(void)
{
Bluetooth bt;
bool result = bt.initialize(BtMode::BLE);
TEST_ASSERT_TRUE(result);
}
void test_bt_start_advertising(void)
{
Bluetooth bt;
bt.initialize(BtMode::BLE);
bool result = bt.startAdvertising();
TEST_ASSERT_TRUE(result);
}
} // extern "C"

View File

@@ -1,5 +0,0 @@
idf_component_register(
SRCS "com/dma.cpp"
INCLUDE_DIRS "com"
REQUIRES driver esp_hw_support logger
)

View File

@@ -1,396 +0,0 @@
# DMA Wrapper Module
## Overview
The DMA wrapper module provides a C++ object-oriented interface for ESP-IDF GDMA (General DMA) functionality. This module encapsulates the ESP-IDF GDMA driver functions and provides a clean, easy-to-use API for high-performance data transfers between memory and peripherals.
## Features
- **Channel Management**: Dynamic allocation and deallocation of DMA channels
- **Memory Management**: DMA-capable memory allocation and management
- **Descriptor Management**: Creation and linking of DMA descriptors
- **Transfer Control**: Start, stop, and reset DMA transfers
- **Callback Support**: Event-driven callbacks for transfer completion
- **Memory Copy**: High-performance DMA-based memory copy operations
- **Error Handling**: Comprehensive error checking and logging
## Architecture
### Class Diagram
```
┌─────────────────────────────────────┐
│ Dma │
├─────────────────────────────────────┤
│ - m_channels_[8]: gdma_handle_t │
│ - m_channelAllocated_[8]: bool │
├─────────────────────────────────────┤
│ + Dma() │
│ + ~Dma() │
│ + allocateChannel(cfg, hdl): bool │
│ + deallocateChannel(hdl): bool │
│ + configureTransfer(hdl, cfg): bool │
│ + startTransfer(hdl): bool │
│ + stopTransfer(hdl): bool │
│ + resetChannel(hdl): bool │
│ + allocateDmaMemory(size): void* │
│ + freeDmaMemory(ptr): void │
│ + createDescriptor(...): Desc* │
│ + linkDescriptors(d1, d2): bool │
│ + freeDescriptor(desc): void │
│ + setCallback(hdl, cb, arg): bool │
│ + enableChannel(hdl): bool │
│ + disableChannel(hdl): bool │
│ + getChannelState(hdl): State │
│ + memcpy(dst, src, size): bool │
│ + getDefaultChannelConfig(): Config │
│ + getDefaultTransferConfig(): Config│
│ + isDmaCapable(addr): bool │
│ - findFreeChannelSlot(): int │
│ - findChannelSlot(hdl): int │
│ - convertDirection(dir): gdma_dir │
│ - convertPriority(pri): int │
└─────────────────────────────────────┘
```
### Enumerations
#### DmaDirection
- `MEM_TO_MEM`: Memory to memory transfer
- `MEM_TO_PERIPH`: Memory to peripheral transfer
- `PERIPH_TO_MEM`: Peripheral to memory transfer
- `PERIPH_TO_PERIPH`: Peripheral to peripheral transfer
#### DmaTransferType
- `SINGLE`: Single transfer
- `BLOCK`: Block transfer
- `LINKED_LIST`: Linked list transfer
#### DmaPriority
- `LOW`: Low priority (0)
- `MEDIUM`: Medium priority (1)
- `HIGH`: High priority (2)
- `HIGHEST`: Highest priority (3)
### Configuration Structures
#### DmaChannelConfig
```cpp
struct DmaChannelConfig {
DmaPriority priority; // Channel priority
uint32_t flags; // Configuration flags
};
```
#### DmaTransferConfig
```cpp
struct DmaTransferConfig {
void* srcAddr; // Source address
void* dstAddr; // Destination address
size_t dataSize; // Size of data to transfer
DmaDirection direction; // Transfer direction
DmaTransferType type; // Transfer type
DmaPriority priority; // Transfer priority
bool autoReload; // Auto-reload mode
bool enableInterrupt; // Enable transfer complete interrupt
};
```
#### DmaDescriptor
```cpp
struct DmaDescriptor {
uint32_t size; // Size of data to transfer
uint32_t length; // Actual length of valid data
uint32_t sosf : 1; // Start of sub-frame
uint32_t eof : 1; // End of frame
uint32_t owner : 1; // Owner (0: CPU, 1: DMA)
uint32_t reserved : 29; // Reserved bits
void* buffer; // Pointer to data buffer
DmaDescriptor* next; // Pointer to next descriptor
};
```
## Usage Examples
### Basic DMA Channel Allocation
```cpp
#include "dma.hpp"
// DMA transfer completion callback
void dmaCallback(gdma_channel_handle_t channel, gdma_event_data_t* eventData, void* userData) {
printf("DMA transfer completed!\n");
}
// Create DMA instance
Dma dma;
// Allocate DMA channel
DmaChannelConfig config = Dma::getDefaultChannelConfig();
config.priority = DmaPriority::HIGH;
gdma_channel_handle_t channel;
dma.allocateChannel(config, &channel);
// Set callback
dma.setCallback(channel, dmaCallback, nullptr);
```
### Memory-to-Memory Transfer
```cpp
// Allocate DMA-capable memory
size_t dataSize = 1024;
void* srcBuffer = dma.allocateDmaMemory(dataSize);
void* dstBuffer = dma.allocateDmaMemory(dataSize);
// Fill source buffer with test data
memset(srcBuffer, 0xAA, dataSize);
// Perform DMA copy
bool success = dma.memcpy(dstBuffer, srcBuffer, dataSize, channel);
if (success) {
printf("DMA copy completed successfully\n");
}
// Clean up
dma.freeDmaMemory(srcBuffer);
dma.freeDmaMemory(dstBuffer);
dma.deallocateChannel(channel);
```
### Descriptor-Based Transfer
```cpp
// Create source and destination buffers
uint8_t* srcData = (uint8_t*)dma.allocateDmaMemory(512);
uint8_t* dstData = (uint8_t*)dma.allocateDmaMemory(512);
// Fill source with test pattern
for (int i = 0; i < 512; i++) {
srcData[i] = i & 0xFF;
}
// Create DMA descriptors
DmaDescriptor* desc1 = dma.createDescriptor(srcData, 256, false);
DmaDescriptor* desc2 = dma.createDescriptor(srcData + 256, 256, true);
// Link descriptors for chained transfer
dma.linkDescriptors(desc1, desc2);
// Configure and start transfer
DmaTransferConfig transferConfig = Dma::getDefaultTransferConfig();
transferConfig.srcAddr = srcData;
transferConfig.dstAddr = dstData;
transferConfig.dataSize = 512;
transferConfig.direction = DmaDirection::MEM_TO_MEM;
dma.configureTransfer(channel, transferConfig);
dma.startTransfer(channel);
// Wait for completion (in real application, use callback)
// ...
// Clean up descriptors
dma.freeDescriptor(desc1);
dma.freeDescriptor(desc2);
```
### Large Data Transfer with Chunking
```cpp
void transferLargeData(Dma& dma, void* src, void* dst, size_t totalSize) {
const size_t chunkSize = 4096; // 4KB chunks
size_t remaining = totalSize;
uint8_t* srcPtr = (uint8_t*)src;
uint8_t* dstPtr = (uint8_t*)dst;
gdma_channel_handle_t channel;
DmaChannelConfig config = Dma::getDefaultChannelConfig();
dma.allocateChannel(config, &channel);
while (remaining > 0) {
size_t currentChunk = (remaining > chunkSize) ? chunkSize : remaining;
// Perform DMA transfer for current chunk
bool success = dma.memcpy(dstPtr, srcPtr, currentChunk, channel);
if (!success) {
printf("DMA transfer failed for chunk\n");
break;
}
srcPtr += currentChunk;
dstPtr += currentChunk;
remaining -= currentChunk;
printf("Transferred %zu bytes, %zu remaining\n", currentChunk, remaining);
}
dma.deallocateChannel(channel);
}
```
### Peripheral-to-Memory Transfer
```cpp
// Example: DMA transfer from SPI peripheral to memory
void setupSpiDmaTransfer(Dma& dma) {
// Allocate receive buffer
uint8_t* rxBuffer = (uint8_t*)dma.allocateDmaMemory(1024);
// Allocate DMA channel
gdma_channel_handle_t channel;
DmaChannelConfig config = Dma::getDefaultChannelConfig();
dma.allocateChannel(config, &channel);
// Configure transfer from SPI to memory
DmaTransferConfig transferConfig = {};
transferConfig.srcAddr = nullptr; // Will be set by SPI driver
transferConfig.dstAddr = rxBuffer;
transferConfig.dataSize = 1024;
transferConfig.direction = DmaDirection::PERIPH_TO_MEM;
transferConfig.enableInterrupt = true;
dma.configureTransfer(channel, transferConfig);
// SPI driver would typically handle the actual peripheral configuration
// and trigger the DMA transfer
// Clean up when done
dma.freeDmaMemory(rxBuffer);
dma.deallocateChannel(channel);
}
```
## API Reference
### Constructor/Destructor
- **Dma()**: Initialize DMA wrapper instance
- **~Dma()**: Clean up resources and deallocate all channels
### Channel Management
- **allocateChannel(config, handle)**: Allocate DMA channel
- **deallocateChannel(handle)**: Deallocate DMA channel
- **enableChannel(handle)**: Enable DMA channel
- **disableChannel(handle)**: Disable DMA channel
- **resetChannel(handle)**: Reset DMA channel
- **getChannelState(handle)**: Get current channel state
### Transfer Control
- **configureTransfer(handle, config)**: Configure DMA transfer
- **startTransfer(handle)**: Start DMA transfer
- **stopTransfer(handle)**: Stop DMA transfer
### Memory Management
- **allocateDmaMemory(size, caps)**: Allocate DMA-capable memory
- **freeDmaMemory(ptr)**: Free DMA-capable memory
- **isDmaCapable(addr)**: Check if address is DMA-capable
### Descriptor Management
- **createDescriptor(buffer, size, eof)**: Create DMA descriptor
- **linkDescriptors(desc1, desc2)**: Link two descriptors
- **freeDescriptor(descriptor)**: Free DMA descriptor
### Callback and Events
- **setCallback(handle, callback, userData)**: Set transfer completion callback
### Utility Methods
- **memcpy(dst, src, size, handle)**: High-performance DMA memory copy
- **getDefaultChannelConfig()**: Get default channel configuration
- **getDefaultTransferConfig()**: Get default transfer configuration
## Error Handling
The module provides comprehensive error handling:
- Channel handle validation
- Memory allocation checks
- ESP-IDF error codes are caught and logged
- Return values indicate success/failure for all operations
- Automatic cleanup of allocated resources
## Dependencies
- ESP-IDF GDMA driver (`driver/gdma.h`)
- ESP-IDF DMA utilities (`esp_dma_utils.h`)
- ESP-IDF hardware support (`esp_hw_support`)
- ESP-IDF error handling (`esp_err.h`)
- ESP-IDF logging (`esp_log.h`)
- ESP-IDF heap capabilities (`esp_heap_caps.h`)
## Thread Safety
The DMA wrapper uses ESP-IDF's thread-safe GDMA driver. Multiple tasks can safely use different DMA channels simultaneously.
## Memory Usage
- Fixed memory footprint per instance
- Channel handles stored in fixed-size array
- DMA descriptors allocated from DMA-capable memory
- No hidden dynamic allocations
## Performance Considerations
- Direct ESP-IDF GDMA calls for optimal performance
- DMA transfers are asynchronous and non-blocking
- Use callbacks for transfer completion notification
- DMA-capable memory required for source/destination buffers
- Descriptor chaining for large transfers
## DMA Memory Requirements
### DMA-Capable Memory
- Source and destination buffers must be in DMA-capable memory
- Use `allocateDmaMemory()` or `MALLOC_CAP_DMA` flag
- Check memory capability with `isDmaCapable()`
### Memory Alignment
- Some transfers may require specific alignment
- Descriptors must be in DMA-capable memory
- Buffer addresses should be word-aligned for best performance
## Limitations
- Maximum 8 DMA channels can be managed simultaneously
- Memory buffers must be DMA-capable
- Transfer size limitations depend on ESP32 variant
- Some peripherals have specific DMA requirements
- Descriptor chains limited by available DMA memory
## Use Cases
### High-Speed Data Transfer
- Large memory copies
- Image processing
- Audio/video streaming
- Network packet processing
### Peripheral Integration
- SPI high-speed transfers
- I2S audio streaming
- UART bulk data transfer
- ADC continuous sampling
### Background Processing
- Asynchronous data movement
- Buffer management
- Real-time data streaming
- Multi-buffer ping-pong operations
## Troubleshooting
### Common Issues
1. **Memory Allocation Failures**: Ensure sufficient DMA-capable memory
2. **Transfer Failures**: Check buffer alignment and DMA capability
3. **Performance Issues**: Use appropriate transfer sizes and descriptor chaining
4. **Channel Exhaustion**: Monitor channel allocation and deallocation
5. **Callback Issues**: Ensure callback functions are in IRAM for interrupt context

View File

@@ -1,512 +0,0 @@
/**
* @file dma.cpp
* @brief DMA wrapper component implementation
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "dma.hpp"
#include "logger.hpp"
#include "esp_memory_utils.h"
#include <cstring>
static const char* TAG = "DMA_WRAPPER";
Dma::Dma()
{
// Initialize channel handles and allocation status
memset(m_channels_, 0, sizeof(m_channels_));
memset(m_channelAllocated_, false, sizeof(m_channelAllocated_));
ASF_LOGI(TAG, 2200, asf::logger::Criticality::LOW, "DMA wrapper initialized");
}
Dma::~Dma()
{
// Deallocate all channels
for (int i = 0; i < MAX_CHANNELS; i++) {
if (m_channelAllocated_[i]) {
deallocateChannel(m_channels_[i]);
}
}
ASF_LOGI(TAG, 2201, asf::logger::Criticality::LOW, "DMA wrapper destroyed");
}
bool Dma::initialize(DmaChannel channel, const DmaConfig& config)
{
ASF_LOGI(TAG, 2202, asf::logger::Criticality::LOW, "DMA initialized successfully");
return true;
}
#if SOC_GDMA_SUPPORTED
bool Dma::allocateChannel(const DmaChannelConfig& config, gdma_channel_handle_t* channelHandle)
#else
bool Dma::allocateChannel(const DmaChannelConfig& config, void** channelHandle)
#endif
{
if (channelHandle == nullptr) {
ASF_LOGE(TAG, 2203, asf::logger::Criticality::HIGH, "Invalid channel handle pointer");
return false;
}
int slotIndex = findFreeChannelSlot();
if (slotIndex < 0) {
ASF_LOGE(TAG, 2204, asf::logger::Criticality::HIGH, "No free channel slots available");
return false;
}
#if SOC_GDMA_SUPPORTED
gdma_channel_alloc_config_t allocConfig = {};
allocConfig.direction = GDMA_CHANNEL_DIRECTION_TX; // Default to TX, can be changed later
allocConfig.flags.reserve_sibling = 0;
esp_err_t ret = gdma_new_channel(&allocConfig, channelHandle);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2205, asf::logger::Criticality::HIGH, "Failed to allocate DMA channel: %s", esp_err_to_name(ret));
return false;
}
m_channels_[slotIndex] = *channelHandle;
m_channelAllocated_[slotIndex] = true;
ASF_LOGI(TAG, 2206, asf::logger::Criticality::LOW, "DMA channel allocated successfully (slot %d)", slotIndex);
return true;
#else
ASF_LOGW(TAG, 2207, asf::logger::Criticality::MEDIUM, "GDMA not supported on this target");
return false;
#endif
}
#if SOC_GDMA_SUPPORTED
bool Dma::deallocateChannel(gdma_channel_handle_t channelHandle)
#else
bool Dma::deallocateChannel(void* channelHandle)
#endif
{
if (channelHandle == nullptr) {
ASF_LOGE(TAG, 2208, asf::logger::Criticality::HIGH, "Invalid channel handle");
return false;
}
int slotIndex = findChannelSlot(channelHandle);
if (slotIndex < 0) {
ASF_LOGE(TAG, 2209, asf::logger::Criticality::HIGH, "Channel handle not found");
return false;
}
#if SOC_GDMA_SUPPORTED
esp_err_t ret = gdma_del_channel(channelHandle);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2210, asf::logger::Criticality::HIGH, "Failed to deallocate DMA channel: %s", esp_err_to_name(ret));
return false;
}
m_channels_[slotIndex] = nullptr;
m_channelAllocated_[slotIndex] = false;
ASF_LOGI(TAG, 2211, asf::logger::Criticality::LOW, "DMA channel deallocated successfully (slot %d)", slotIndex);
return true;
#else
return false;
#endif
}
#if SOC_GDMA_SUPPORTED
bool Dma::configureTransfer(gdma_channel_handle_t channelHandle, const DmaTransferConfig& config)
#else
bool Dma::configureTransfer(void* channelHandle, const DmaTransferConfig& config)
#endif
{
if (channelHandle == nullptr) {
ASF_LOGE(TAG, 2212, asf::logger::Criticality::HIGH, "Invalid channel handle");
return false;
}
// Configure transfer based on direction
switch (config.direction) {
case DmaDirection::MEM_TO_MEM:
// Memory to memory transfer configuration
break;
case DmaDirection::MEM_TO_PERIPH:
// Memory to peripheral transfer configuration
break;
case DmaDirection::PERIPH_TO_MEM:
// Peripheral to memory transfer configuration
break;
case DmaDirection::PERIPH_TO_PERIPH:
// Peripheral to peripheral transfer configuration
break;
}
ASF_LOGI(TAG, 2213, asf::logger::Criticality::LOW, "DMA transfer configured successfully");
return true;
}
#if SOC_GDMA_SUPPORTED
bool Dma::startTransfer(gdma_channel_handle_t channelHandle)
#else
bool Dma::startTransfer(void* channelHandle)
#endif
{
if (channelHandle == nullptr) {
ASF_LOGE(TAG, 2214, asf::logger::Criticality::HIGH, "Invalid channel handle");
return false;
}
#if SOC_GDMA_SUPPORTED
esp_err_t ret = gdma_start(channelHandle);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2215, asf::logger::Criticality::HIGH, "Failed to start DMA transfer: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2216, asf::logger::Criticality::LOW, "DMA transfer started successfully");
return true;
#else
return false;
#endif
}
#if SOC_GDMA_SUPPORTED
bool Dma::stopTransfer(gdma_channel_handle_t channelHandle)
#else
bool Dma::stopTransfer(void* channelHandle)
#endif
{
if (channelHandle == nullptr) {
ASF_LOGE(TAG, 2217, asf::logger::Criticality::HIGH, "Invalid channel handle");
return false;
}
#if SOC_GDMA_SUPPORTED
esp_err_t ret = gdma_stop(channelHandle);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2218, asf::logger::Criticality::HIGH, "Failed to stop DMA transfer: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2219, asf::logger::Criticality::LOW, "DMA transfer stopped successfully");
return true;
#else
return false;
#endif
}
#if SOC_GDMA_SUPPORTED
bool Dma::resetChannel(gdma_channel_handle_t channelHandle)
#else
bool Dma::resetChannel(void* channelHandle)
#endif
{
if (channelHandle == nullptr) {
ASF_LOGE(TAG, 2220, asf::logger::Criticality::HIGH, "Invalid channel handle");
return false;
}
#if SOC_GDMA_SUPPORTED
esp_err_t ret = gdma_reset(channelHandle);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2221, asf::logger::Criticality::HIGH, "Failed to reset DMA channel: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2222, asf::logger::Criticality::LOW, "DMA channel reset successfully");
return true;
#else
return false;
#endif
}
void* Dma::allocateDmaMemory(size_t size, uint32_t caps)
{
void* ptr = heap_caps_malloc(size, caps);
if (ptr == nullptr) {
ASF_LOGE(TAG, 2223, asf::logger::Criticality::HIGH, "Failed to allocate DMA memory of size %zu", size);
return nullptr;
}
ASF_LOGI(TAG, 2224, asf::logger::Criticality::LOW, "DMA memory allocated: %zu bytes at %p", size, ptr);
return ptr;
}
void Dma::freeDmaMemory(void* ptr)
{
if (ptr != nullptr) {
heap_caps_free(ptr);
ASF_LOGI(TAG, 2225, asf::logger::Criticality::LOW, "DMA memory freed at %p", ptr);
}
}
DmaDescriptor* Dma::createDescriptor(void* buffer, size_t size, bool eof)
{
if (buffer == nullptr || size == 0) {
ASF_LOGE(TAG, 2226, asf::logger::Criticality::HIGH, "Invalid buffer or size for descriptor");
return nullptr;
}
DmaDescriptor* desc = static_cast<DmaDescriptor*>(
heap_caps_malloc(sizeof(DmaDescriptor), MALLOC_CAP_DMA));
if (desc == nullptr) {
ASF_LOGE(TAG, 2227, asf::logger::Criticality::HIGH, "Failed to allocate DMA descriptor");
return nullptr;
}
desc->size = size;
desc->length = size;
desc->sosf = 0;
desc->eof = eof ? 1 : 0;
desc->owner = 1; // DMA owns the descriptor
desc->reserved = 0;
desc->buffer = buffer;
desc->next = nullptr;
ASF_LOGI(TAG, 2228, asf::logger::Criticality::LOW, "DMA descriptor created: size=%zu, eof=%d", size, eof);
return desc;
}
bool Dma::linkDescriptors(DmaDescriptor* desc1, DmaDescriptor* desc2)
{
if (desc1 == nullptr || desc2 == nullptr) {
ASF_LOGE(TAG, 2229, asf::logger::Criticality::HIGH, "Invalid descriptors for linking");
return false;
}
desc1->next = desc2;
desc1->eof = 0; // Not end of frame since there's a next descriptor
ASF_LOGI(TAG, 2230, asf::logger::Criticality::LOW, "DMA descriptors linked successfully");
return true;
}
void Dma::freeDescriptor(DmaDescriptor* descriptor)
{
if (descriptor != nullptr) {
heap_caps_free(descriptor);
ASF_LOGI(TAG, 2231, asf::logger::Criticality::LOW, "DMA descriptor freed");
}
}
#if SOC_GDMA_SUPPORTED
bool Dma::setCallback(gdma_channel_handle_t channelHandle, DmaCallback callback, void* userData)
#else
bool Dma::setCallback(void* channelHandle, DmaCallback callback, void* userData)
#endif
{
if (channelHandle == nullptr || callback == nullptr) {
ASF_LOGE(TAG, 2232, asf::logger::Criticality::HIGH, "Invalid channel handle or callback");
return false;
}
#if SOC_GDMA_SUPPORTED
gdma_event_callbacks_t callbacks = {};
callbacks.on_trans_eof = callback;
esp_err_t ret = gdma_register_event_callbacks(channelHandle, &callbacks, userData);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2233, asf::logger::Criticality::HIGH, "Failed to set DMA callback: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2234, asf::logger::Criticality::LOW, "DMA callback set successfully");
return true;
#else
return false;
#endif
}
#if SOC_GDMA_SUPPORTED
bool Dma::enableChannel(gdma_channel_handle_t channelHandle)
#else
bool Dma::enableChannel(void* channelHandle)
#endif
{
if (channelHandle == nullptr) {
ASF_LOGE(TAG, 2235, asf::logger::Criticality::HIGH, "Invalid channel handle");
return false;
}
#if SOC_GDMA_SUPPORTED
esp_err_t ret = gdma_start(channelHandle);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2236, asf::logger::Criticality::HIGH, "Failed to enable DMA channel: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2237, asf::logger::Criticality::LOW, "DMA channel enabled successfully");
return true;
#else
return false;
#endif
}
#if SOC_GDMA_SUPPORTED
bool Dma::disableChannel(gdma_channel_handle_t channelHandle)
#else
bool Dma::disableChannel(void* channelHandle)
#endif
{
if (channelHandle == nullptr) {
ASF_LOGE(TAG, 2238, asf::logger::Criticality::HIGH, "Invalid channel handle");
return false;
}
#if SOC_GDMA_SUPPORTED
esp_err_t ret = gdma_stop(channelHandle);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2239, asf::logger::Criticality::HIGH, "Failed to disable DMA channel: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2240, asf::logger::Criticality::LOW, "DMA channel disabled successfully");
return true;
#else
return false;
#endif
}
#if SOC_GDMA_SUPPORTED
gdma_channel_state_t Dma::getChannelState(gdma_channel_handle_t channelHandle)
#else
int Dma::getChannelState(void* channelHandle)
#endif
{
if (channelHandle == nullptr) {
ASF_LOGE(TAG, 2241, asf::logger::Criticality::HIGH, "Invalid channel handle");
#if SOC_GDMA_SUPPORTED
return GDMA_CHANNEL_STATE_INVALID;
#else
return -1;
#endif
}
// Note: ESP-IDF doesn't provide a direct API to get channel state
// This would need to be implemented by reading hardware registers
#if SOC_GDMA_SUPPORTED
return GDMA_CHANNEL_STATE_IDLE;
#else
return 0;
#endif
}
#if SOC_GDMA_SUPPORTED
bool Dma::memcpy(void* dst, const void* src, size_t size, gdma_channel_handle_t channelHandle)
#else
bool Dma::memcpy(void* dst, const void* src, size_t size, void* channelHandle)
#endif
{
if (dst == nullptr || src == nullptr || size == 0) {
ASF_LOGE(TAG, 2242, asf::logger::Criticality::HIGH, "Invalid parameters for DMA memcpy");
return false;
}
#if SOC_GDMA_SUPPORTED
bool allocatedChannel = false;
gdma_channel_handle_t channel = channelHandle;
// Allocate channel if not provided
if (channel == nullptr) {
DmaChannelConfig config = getDefaultChannelConfig();
if (!allocateChannel(config, &channel)) {
return false;
}
allocatedChannel = true;
}
// Perform DMA copy using ESP-IDF DMA utilities
// Note: This implementation is a placeholder, actual GDMA memcpy requires descriptors
::memcpy(dst, src, size);
esp_err_t ret = ESP_OK;
// Clean up if we allocated the channel
if (allocatedChannel) {
deallocateChannel(channel);
}
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2243, asf::logger::Criticality::HIGH, "DMA memcpy failed: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2244, asf::logger::Criticality::LOW, "DMA memcpy completed: %zu bytes", size);
return true;
#else
// Fallback to standard memcpy if GDMA not supported
::memcpy(dst, src, size);
return true;
#endif
}
DmaChannelConfig Dma::getDefaultChannelConfig()
{
DmaChannelConfig config = {};
config.priority = DmaPriority::MEDIUM;
config.flags = 0;
return config;
}
DmaTransferConfig Dma::getDefaultTransferConfig()
{
DmaTransferConfig config = {};
config.srcAddr = nullptr;
config.dstAddr = nullptr;
config.dataSize = 0;
config.direction = DmaDirection::MEM_TO_MEM;
config.type = DmaTransferType::SINGLE;
config.priority = DmaPriority::MEDIUM;
config.autoReload = false;
config.enableInterrupt = false;
return config;
}
bool Dma::isDmaCapable(const void* addr)
{
return esp_ptr_dma_capable(addr);
}
int Dma::findFreeChannelSlot()
{
for (int i = 0; i < MAX_CHANNELS; i++) {
if (!m_channelAllocated_[i]) {
return i;
}
}
return -1;
}
#if SOC_GDMA_SUPPORTED
int Dma::findChannelSlot(gdma_channel_handle_t channelHandle)
#else
int Dma::findChannelSlot(void* channelHandle)
#endif
{
for (int i = 0; i < MAX_CHANNELS; i++) {
if (m_channelAllocated_[i] && m_channels_[i] == channelHandle) {
return i;
}
}
return -1;
}
#if SOC_GDMA_SUPPORTED
gdma_transfer_ability_t Dma::convertDirection(DmaDirection direction)
{
switch (direction) {
case DmaDirection::MEM_TO_MEM:
return GDMA_TRANSFER_ABILITY_MEM;
case DmaDirection::MEM_TO_PERIPH:
return GDMA_TRANSFER_ABILITY_MEM;
case DmaDirection::PERIPH_TO_MEM:
return GDMA_TRANSFER_ABILITY_MEM;
case DmaDirection::PERIPH_TO_PERIPH:
return GDMA_TRANSFER_ABILITY_MEM;
default:
return GDMA_TRANSFER_ABILITY_MEM;
}
}
#endif
int Dma::convertPriority(DmaPriority priority)
{
return static_cast<int>(priority);
}

View File

@@ -1,386 +0,0 @@
/**
* @file dma.hpp
* @brief DMA wrapper component header - Wrapper for ESP-IDF DMA functionality
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#ifndef DMA_HPP
#define DMA_HPP
#include <cstdint>
#include "esp_dma_utils.h"
#include "esp_err.h"
#include "esp_heap_caps.h"
#include "soc/soc_caps.h"
#if SOC_GDMA_SUPPORTED
#include "soc/gdma_struct.h"
#include "hal/gdma_ll.h"
#include "driver/gdma.h"
#endif
/**
* @brief DMA transfer direction enumeration
*/
enum class DmaDirection
{
MEM_TO_MEM, ///< Memory to memory transfer
MEM_TO_PERIPH, ///< Memory to peripheral transfer
PERIPH_TO_MEM, ///< Peripheral to memory transfer
PERIPH_TO_PERIPH ///< Peripheral to peripheral transfer
};
/**
* @brief DMA transfer type enumeration
*/
enum class DmaTransferType
{
SINGLE, ///< Single transfer
BLOCK, ///< Block transfer
LINKED_LIST ///< Linked list transfer
};
/**
* @brief DMA priority enumeration
*/
enum class DmaPriority
{
LOW = 0,
MEDIUM = 1,
HIGH = 2,
HIGHEST = 3
};
/**
* @brief DMA channel enumeration
*/
enum class DmaChannel
{
CHANNEL_0,
CHANNEL_1,
CHANNEL_2,
CHANNEL_3,
CHANNEL_4,
CHANNEL_5,
CHANNEL_6,
CHANNEL_7
};
/**
* @brief DMA configuration structure
*/
struct DmaConfig
{
DmaPriority priority;
uint32_t flags;
};
/**
* @brief DMA descriptor structure
*/
struct DmaDescriptor
{
uint32_t size; ///< Size of data to transfer
uint32_t length; ///< Actual length of valid data
uint32_t sosf : 1; ///< Start of sub-frame
uint32_t eof : 1; ///< End of frame
uint32_t owner : 1; ///< Owner (0: CPU, 1: DMA)
uint32_t reserved : 29; ///< Reserved bits
void* buffer; ///< Pointer to data buffer
DmaDescriptor* next; ///< Pointer to next descriptor
};
/**
* @brief DMA transfer configuration structure
*/
struct DmaTransferConfig
{
void* srcAddr; ///< Source address
void* dstAddr; ///< Destination address
size_t dataSize; ///< Size of data to transfer
DmaDirection direction; ///< Transfer direction
DmaTransferType type; ///< Transfer type
DmaPriority priority; ///< Transfer priority
bool autoReload; ///< Auto-reload mode
bool enableInterrupt; ///< Enable transfer complete interrupt
};
/**
* @brief DMA channel configuration structure
*/
struct DmaChannelConfig
{
DmaPriority priority; ///< Channel priority
uint32_t flags; ///< Configuration flags
};
/**
* @brief DMA callback function type
*/
#if SOC_GDMA_SUPPORTED
using DmaCallback = void (*)(gdma_channel_handle_t dmaChannel, gdma_event_data_t* eventData, void* userData);
#else
using DmaCallback = void (*)(void* dmaChannel, void* eventData, void* userData);
#endif
/**
* @brief DMA wrapper class
*
* Provides a C++ wrapper for ESP-IDF GDMA functionality.
* This class encapsulates ESP-IDF GDMA driver functions in an object-oriented interface.
*/
class Dma
{
public:
/**
* @brief Constructor
* @details Initializes the DMA wrapper instance
*/
Dma();
/**
* @brief Destructor
* @details Cleans up resources and deinitializes all DMA channels
*/
~Dma();
/**
* @brief Initialize DMA component
* @param channel DMA channel
* @param config DMA configuration
* @return true if initialized successfully, false otherwise
*/
bool initialize(DmaChannel channel, const DmaConfig& config);
/**
* @brief Allocate DMA channel
* @param config Channel configuration
* @param channelHandle Pointer to store channel handle
* @return true if allocated successfully, false otherwise
*/
#if SOC_GDMA_SUPPORTED
bool allocateChannel(const DmaChannelConfig& config, gdma_channel_handle_t* channelHandle);
#else
bool allocateChannel(const DmaChannelConfig& config, void** channelHandle);
#endif
/**
* @brief Deallocate DMA channel
* @param channelHandle Channel handle to deallocate
* @return true if deallocated successfully, false otherwise
*/
#if SOC_GDMA_SUPPORTED
bool deallocateChannel(gdma_channel_handle_t channelHandle);
#else
bool deallocateChannel(void* channelHandle);
#endif
/**
* @brief Configure DMA transfer
* @param channelHandle Channel handle
* @param config Transfer configuration
* @return true if configured successfully, false otherwise
*/
#if SOC_GDMA_SUPPORTED
bool configureTransfer(gdma_channel_handle_t channelHandle, const DmaTransferConfig& config);
#else
bool configureTransfer(void* channelHandle, const DmaTransferConfig& config);
#endif
/**
* @brief Start DMA transfer
* @param channelHandle Channel handle
* @return true if started successfully, false otherwise
*/
#if SOC_GDMA_SUPPORTED
bool startTransfer(gdma_channel_handle_t channelHandle);
#else
bool startTransfer(void* channelHandle);
#endif
/**
* @brief Stop DMA transfer
* @param channelHandle Channel handle
* @return true if stopped successfully, false otherwise
*/
#if SOC_GDMA_SUPPORTED
bool stopTransfer(gdma_channel_handle_t channelHandle);
#else
bool stopTransfer(void* channelHandle);
#endif
/**
* @brief Reset DMA channel
* @param channelHandle Channel handle
* @return true if reset successfully, false otherwise
*/
#if SOC_GDMA_SUPPORTED
bool resetChannel(gdma_channel_handle_t channelHandle);
#else
bool resetChannel(void* channelHandle);
#endif
/**
* @brief Allocate DMA-capable memory
* @param size Size of memory to allocate
* @param caps Memory capabilities (default: DMA capable)
* @return Pointer to allocated memory, or nullptr on failure
*/
void* allocateDmaMemory(size_t size, uint32_t caps = MALLOC_CAP_DMA);
/**
* @brief Free DMA-capable memory
* @param ptr Pointer to memory to free
*/
void freeDmaMemory(void* ptr);
/**
* @brief Create DMA descriptor
* @param buffer Pointer to data buffer
* @param size Size of data
* @param eof End of frame flag
* @return Pointer to created descriptor, or nullptr on failure
*/
DmaDescriptor* createDescriptor(void* buffer, size_t size, bool eof = true);
/**
* @brief Link DMA descriptors
* @param desc1 First descriptor
* @param desc2 Second descriptor
* @return true if linked successfully, false otherwise
*/
bool linkDescriptors(DmaDescriptor* desc1, DmaDescriptor* desc2);
/**
* @brief Free DMA descriptor
* @param descriptor Pointer to descriptor to free
*/
void freeDescriptor(DmaDescriptor* descriptor);
/**
* @brief Set DMA callback
* @param channelHandle Channel handle
* @param callback Callback function
* @param userData User data passed to callback
* @return true if set successfully, false otherwise
*/
#if SOC_GDMA_SUPPORTED
bool setCallback(gdma_channel_handle_t channelHandle, DmaCallback callback, void* userData);
#else
bool setCallback(void* channelHandle, DmaCallback callback, void* userData);
#endif
/**
* @brief Enable DMA channel
* @param channelHandle Channel handle
* @return true if enabled successfully, false otherwise
*/
#if SOC_GDMA_SUPPORTED
bool enableChannel(gdma_channel_handle_t channelHandle);
#else
bool enableChannel(void* channelHandle);
#endif
/**
* @brief Disable DMA channel
* @param channelHandle Channel handle
* @return true if disabled successfully, false otherwise
*/
#if SOC_GDMA_SUPPORTED
bool disableChannel(gdma_channel_handle_t channelHandle);
#else
bool disableChannel(void* channelHandle);
#endif
/**
* @brief Get DMA channel state
* @param channelHandle Channel handle
* @return Current channel state
*/
#if SOC_GDMA_SUPPORTED
gdma_channel_state_t getChannelState(gdma_channel_handle_t channelHandle);
#else
int getChannelState(void* channelHandle);
#endif
/**
* @brief Perform memory-to-memory copy using DMA
* @param dst Destination address
* @param src Source address
* @param size Size of data to copy
* @param channelHandle Channel handle (optional, will allocate if nullptr)
* @return true if copy completed successfully, false otherwise
*/
#if SOC_GDMA_SUPPORTED
bool memcpy(void* dst, const void* src, size_t size, gdma_channel_handle_t channelHandle = nullptr);
#else
bool memcpy(void* dst, const void* src, size_t size, void* channelHandle = nullptr);
#endif
/**
* @brief Get default channel configuration
* @return Default DMA channel configuration
*/
static DmaChannelConfig getDefaultChannelConfig();
/**
* @brief Get default transfer configuration
* @return Default DMA transfer configuration
*/
static DmaTransferConfig getDefaultTransferConfig();
/**
* @brief Check if address is DMA capable
* @param addr Address to check
* @return true if DMA capable, false otherwise
*/
static bool isDmaCapable(const void* addr);
private:
static constexpr size_t MAX_CHANNELS = 8; ///< Maximum number of DMA channels
#if SOC_GDMA_SUPPORTED
gdma_channel_handle_t m_channels_[MAX_CHANNELS]; ///< Channel handles
#else
void* m_channels_[MAX_CHANNELS];
#endif
bool m_channelAllocated_[MAX_CHANNELS]; ///< Channel allocation status
/**
* @brief Find free channel slot
* @return Index of free slot, or -1 if none available
*/
int findFreeChannelSlot();
/**
* @brief Find channel slot by handle
* @param channelHandle Channel handle to find
* @return Index of channel slot, or -1 if not found
*/
#if SOC_GDMA_SUPPORTED
int findChannelSlot(gdma_channel_handle_t channelHandle);
#else
int findChannelSlot(void* channelHandle);
#endif
/**
* @brief Convert DmaDirection to GDMA direction
* @param direction DMA direction
* @return GDMA direction
*/
#if SOC_GDMA_SUPPORTED
gdma_transfer_ability_t convertDirection(DmaDirection direction);
#endif
/**
* @brief Convert DmaPriority to GDMA priority
* @param priority DMA priority
* @return GDMA priority
*/
int convertPriority(DmaPriority priority);
};
#endif // DMA_HPP

View File

@@ -1,46 +0,0 @@
ID,Component,Level,Criticality,Message
2200,DMA,INFO,Low,DMA wrapper initialized
2201,DMA,INFO,Low,DMA wrapper destroyed
2202,DMA,INFO,Low,DMA initialized successfully
2203,DMA,ERROR,High,Invalid channel handle pointer
2204,DMA,ERROR,High,No free channel slots available
2205,DMA,ERROR,High,Failed to allocate DMA channel: %s
2206,DMA,INFO,Low,DMA channel allocated successfully (slot %d)
2207,DMA,WARNING,Medium,GDMA not supported on this target
2208,DMA,ERROR,High,Invalid channel handle
2209,DMA,ERROR,High,Channel handle not found
2210,DMA,ERROR,High,Failed to deallocate DMA channel: %s
2211,DMA,INFO,Low,DMA channel deallocated successfully (slot %d)
2212,DMA,ERROR,High,Invalid channel handle
2213,DMA,INFO,Low,DMA transfer configured successfully
2214,DMA,ERROR,High,Invalid channel handle
2215,DMA,ERROR,High,Failed to start DMA transfer: %s
2216,DMA,INFO,Low,DMA transfer started successfully
2217,DMA,ERROR,High,Invalid channel handle
2218,DMA,ERROR,High,Failed to stop DMA transfer: %s
2219,DMA,INFO,Low,DMA transfer stopped successfully
2220,DMA,ERROR,High,Invalid channel handle
2221,DMA,ERROR,High,Failed to reset DMA channel: %s
2222,DMA,INFO,Low,DMA channel reset successfully
2223,DMA,ERROR,High,Failed to allocate DMA memory of size %zu
2224,DMA,INFO,Low,DMA memory allocated: %zu bytes at %p
2225,DMA,INFO,Low,DMA memory freed at %p
2226,DMA,ERROR,High,Invalid buffer or size for descriptor
2227,DMA,ERROR,High,Failed to allocate DMA descriptor
2228,DMA,INFO,Low,DMA descriptor created: size=%zu, eof=%d
2229,DMA,ERROR,High,Invalid descriptors for linking
2230,DMA,INFO,Low,DMA descriptors linked successfully
2231,DMA,INFO,Low,DMA descriptor freed
2232,DMA,ERROR,High,Invalid channel handle or callback
2233,DMA,ERROR,High,Failed to set DMA callback: %s
2234,DMA,INFO,Low,DMA callback set successfully
2235,DMA,ERROR,High,Invalid channel handle
2236,DMA,ERROR,High,Failed to enable DMA channel: %s
2237,DMA,INFO,Low,DMA channel enabled successfully
2238,DMA,ERROR,High,Invalid channel handle
2239,DMA,ERROR,High,Failed to disable DMA channel: %s
2240,DMA,INFO,Low,DMA channel disabled successfully
2241,DMA,ERROR,High,Invalid channel handle
2242,DMA,ERROR,High,Invalid parameters for DMA memcpy
2243,DMA,ERROR,High,DMA memcpy failed: %s
2244,DMA,INFO,Low,DMA memcpy completed: %zu bytes
Can't render this file because it has a wrong number of fields in line 30.

View File

@@ -1,34 +0,0 @@
import sys
import os
import time
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
if folder_path not in sys.path:
sys.path.append(folder_path)
from scan_serial import ESP32Runner
def test_dma_initialize():
runner = ESP32Runner(mode="SIM", port="COM9")
runner.start()
print("--- QEMU Runner Started ---", flush=True)
try:
start_time = time.time()
while time.time() - start_time < 30:
line = runner.get_line(timeout=1.0)
if line:
print(line, flush=True)
if "DMA initialized successfully" in line:
print("SUCCESS CRITERIA MET!", flush=True)
return 0
if runner.process.poll() is not None:
print(f"Process exited with code: {runner.process.returncode}", flush=True)
return 1
finally:
runner.stop()
print("Done.", flush=True)
return 1
if __name__ == "__main__":
exit_code = test_dma_initialize()
sys.exit(exit_code)

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<test_scenario>
<!-- The configuration for the test environment. -->
<!-- Available configurations: SIMULATE, HIL -->
<config>SIMULATE</config>
<test_case>
<test_case_id>DMA_INIT_TEST</test_case_id>
<!-- The main command that executes the test itself. -->
<test_exec>python components/ESP_IDF_FW_wrappers/dma/test/dma_init_test.py</test_exec>
</test_case>
</test_scenario>

View File

@@ -1,31 +0,0 @@
/**
* @file test_dma.cpp
* @brief Unit tests for DMA wrapper component
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "unity.h"
#include "dma.hpp"
extern "C" {
void setUp(void)
{
}
void tearDown(void)
{
}
void test_dma_initialize(void)
{
Dma dma;
DmaConfig config = {64, 1024};
bool result = dma.initialize(DmaChannel::CHANNEL_0, config);
TEST_ASSERT_TRUE(result);
}
} // extern "C"

View File

@@ -1,5 +0,0 @@
idf_component_register(
SRCS "com/gpio.cpp"
INCLUDE_DIRS "com"
REQUIRES esp_driver_gpio logger
)

View File

@@ -1,154 +0,0 @@
# GPIO Wrapper Module
## Overview
The GPIO wrapper module provides a C++ object-oriented interface for ESP-IDF GPIO functionality. This module encapsulates the ESP-IDF GPIO driver functions and provides a clean, easy-to-use API for GPIO operations.
## Features
- **Pin Configuration**: Configure GPIO pins as input, output, or open-drain
- **Digital I/O**: Read and write digital values to GPIO pins
- **Pull Resistors**: Configure internal pull-up and pull-down resistors
- **Interrupt Handling**: Attach interrupt handlers to GPIO pins
- **Pin Validation**: Automatic validation of GPIO pin numbers
- **Error Handling**: Comprehensive error checking and logging
## Architecture
### Class Diagram
```
┌─────────────────────────────────────┐
│ Gpio │
├─────────────────────────────────────┤
│ - m_isrInstalled_: bool │
├─────────────────────────────────────┤
│ + Gpio() │
│ + ~Gpio() │
│ + configure(pin, mode): bool │
│ + setLevel(pin, level): bool │
│ + getLevel(pin): int32_t │
│ + toggleLevel(pin): bool │
│ + installIsr(flags): bool │
│ + uninstallIsr(): bool │
│ + attachInterrupt(...): bool │
│ + detachInterrupt(pin): bool │
│ + enableInterrupt(pin): bool │
│ + disableInterrupt(pin): bool │
│ + isValidPin(pin): bool [static] │
│ - convertMode(mode): gpio_mode_t │
│ - convertIntType(type): gpio_int_t │
└─────────────────────────────────────┘
```
### Enumerations
#### GpioMode
- `INPUT`: Standard input mode
- `OUTPUT`: Standard output mode
- `INPUT_PULLUP`: Input with internal pull-up resistor
- `INPUT_PULLDOWN`: Input with internal pull-down resistor
- `OUTPUT_OD`: Open-drain output mode
#### GpioIntType
- `DISABLE`: Disable interrupt
- `RISING_EDGE`: Trigger on rising edge
- `FALLING_EDGE`: Trigger on falling edge
- `ANY_EDGE`: Trigger on any edge
- `LOW_LEVEL`: Trigger on low level
- `HIGH_LEVEL`: Trigger on high level
## Usage Examples
### Basic GPIO Operations
```cpp
#include "gpio.hpp"
// Create GPIO instance
Gpio gpio;
// Configure pin 2 as output
gpio.configure(2, GpioMode::OUTPUT);
// Set pin high
gpio.setLevel(2, 1);
// Configure pin 4 as input with pull-up
gpio.configure(4, GpioMode::INPUT_PULLUP);
// Read pin level
int level = gpio.getLevel(4);
```
### Interrupt Handling
```cpp
// Interrupt callback function
void IRAM_ATTR gpioInterruptHandler(uint32_t pin, void* arg) {
// Handle interrupt
printf("Interrupt on pin %lu\n", pin);
}
// Setup interrupt
gpio.configure(5, GpioMode::INPUT);
gpio.attachInterrupt(5, GpioIntType::FALLING_EDGE, gpioInterruptHandler, nullptr);
```
## API Reference
### Constructor/Destructor
- **Gpio()**: Initialize GPIO wrapper instance
- **~Gpio()**: Clean up resources and uninstall ISR if needed
### Configuration Methods
- **configure(pin, mode)**: Configure GPIO pin mode and pull resistors
- **isValidPin(pin)**: Check if GPIO pin number is valid
### Digital I/O Methods
- **setLevel(pin, level)**: Set output pin level (0 or 1)
- **getLevel(pin)**: Read pin level, returns 0, 1, or -1 on error
- **toggleLevel(pin)**: Toggle output pin level
### Interrupt Methods
- **installIsr(flags)**: Install GPIO interrupt service
- **uninstallIsr()**: Uninstall GPIO interrupt service
- **attachInterrupt(pin, type, callback, arg)**: Attach interrupt handler
- **detachInterrupt(pin)**: Detach interrupt handler
- **enableInterrupt(pin)**: Enable interrupt for pin
- **disableInterrupt(pin)**: Disable interrupt for pin
## Error Handling
The module provides comprehensive error handling:
- Invalid pin numbers are checked and logged
- ESP-IDF errors are caught and logged with descriptive messages
- Return values indicate success/failure for all operations
- ESP_LOG is used for debugging and error reporting
## Dependencies
- ESP-IDF GPIO driver (`driver/gpio.h`)
- ESP-IDF error handling (`esp_err.h`)
- ESP-IDF logging (`esp_log.h`)
## Thread Safety
The GPIO wrapper is not inherently thread-safe. If used in multi-threaded applications, external synchronization mechanisms should be employed.
## Memory Usage
The GPIO wrapper has minimal memory footprint:
- Single boolean flag for ISR installation status
- No dynamic memory allocation
- Stack-based configuration structures
## Performance Considerations
- Direct ESP-IDF function calls for optimal performance
- Minimal overhead over raw ESP-IDF calls
- Interrupt handlers should be kept short and use IRAM_ATTR

View File

@@ -1,271 +0,0 @@
/**
* @file gpio.cpp
* @brief GPIO wrapper component implementation
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "gpio.hpp"
#include "logger.hpp"
static const char* TAG = "GPIO_WRAPPER";
/**
* @brief Constructor - Initialize GPIO wrapper instance
* @details Initializes the GPIO wrapper with default settings
*/
Gpio::Gpio()
: m_isrInstalled_(false)
{
ASF_LOGI(TAG, 2300, asf::logger::Criticality::LOW, "GPIO wrapper initialized");
}
/**
* @brief Destructor - Clean up GPIO wrapper resources
* @details Uninstalls ISR service if it was installed
*/
Gpio::~Gpio()
{
if (m_isrInstalled_) {
uninstallIsr();
}
ASF_LOGI(TAG, 2301, asf::logger::Criticality::LOW, "GPIO wrapper destroyed");
}
bool Gpio::configure(uint32_t pin, GpioMode mode)
{
if (!isValidPin(pin)) {
ASF_LOGE(TAG, 2302, asf::logger::Criticality::HIGH, "Invalid GPIO pin: %lu", pin);
return false;
}
gpio_config_t config = {};
config.pin_bit_mask = (1ULL << pin);
config.mode = convertMode(mode);
config.intr_type = GPIO_INTR_DISABLE;
// Configure pull-up/down based on mode
switch (mode) {
case GpioMode::INPUT_PULLUP:
config.pull_up_en = GPIO_PULLUP_ENABLE;
config.pull_down_en = GPIO_PULLDOWN_DISABLE;
break;
case GpioMode::INPUT_PULLDOWN:
config.pull_up_en = GPIO_PULLUP_DISABLE;
config.pull_down_en = GPIO_PULLDOWN_ENABLE;
break;
default:
config.pull_up_en = GPIO_PULLUP_DISABLE;
config.pull_down_en = GPIO_PULLDOWN_DISABLE;
break;
}
esp_err_t ret = gpio_config(&config);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2303, asf::logger::Criticality::HIGH, "Failed to configure GPIO pin %lu: %s", pin, esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2304, asf::logger::Criticality::LOW, "GPIO pin %lu configured successfully", pin);
return true;
}
bool Gpio::setLevel(uint32_t pin, uint32_t level)
{
if (!isValidPin(pin)) {
ASF_LOGE(TAG, 2302, asf::logger::Criticality::HIGH, "Invalid GPIO pin: %lu", pin);
return false;
}
esp_err_t ret = gpio_set_level(static_cast<gpio_num_t>(pin), level);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2305, asf::logger::Criticality::HIGH, "Failed to set GPIO pin %lu level: %s", pin, esp_err_to_name(ret));
return false;
}
return true;
}
int32_t Gpio::getLevel(uint32_t pin)
{
if (!isValidPin(pin)) {
ASF_LOGE(TAG, 2302, asf::logger::Criticality::HIGH, "Invalid GPIO pin: %lu", pin);
return -1;
}
int level = gpio_get_level(static_cast<gpio_num_t>(pin));
return level;
}
bool Gpio::toggleLevel(uint32_t pin)
{
int32_t currentLevel = getLevel(pin);
if (currentLevel < 0) {
return false;
}
return setLevel(pin, currentLevel == 0 ? 1 : 0);
}
bool Gpio::installIsr(int flags)
{
if (m_isrInstalled_) {
ASF_LOGW(TAG, 2306, asf::logger::Criticality::MEDIUM, "GPIO ISR already installed");
return true;
}
esp_err_t ret = gpio_install_isr_service(flags);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2307, asf::logger::Criticality::HIGH, "Failed to install GPIO ISR service: %s", esp_err_to_name(ret));
return false;
}
m_isrInstalled_ = true;
ASF_LOGI(TAG, 2308, asf::logger::Criticality::LOW, "GPIO initialized successfully");
return true;
}
bool Gpio::uninstallIsr()
{
if (!m_isrInstalled_) {
ASF_LOGW(TAG, 2309, asf::logger::Criticality::MEDIUM, "GPIO ISR not installed");
return true;
}
gpio_uninstall_isr_service();
m_isrInstalled_ = false;
ASF_LOGI(TAG, 2310, asf::logger::Criticality::LOW, "GPIO ISR service uninstalled");
return true;
}
bool Gpio::attachInterrupt(uint32_t pin, GpioIntType intType, GpioCallback callback, void* arg)
{
if (!isValidPin(pin)) {
ASF_LOGE(TAG, 2302, asf::logger::Criticality::HIGH, "Invalid GPIO pin: %lu", pin);
return false;
}
if (!m_isrInstalled_) {
ASF_LOGW(TAG, 2311, asf::logger::Criticality::MEDIUM, "GPIO ISR not installed, installing now");
if (!installIsr()) {
return false;
}
}
// Set interrupt type
esp_err_t ret = gpio_set_intr_type(static_cast<gpio_num_t>(pin), convertIntType(intType));
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2312, asf::logger::Criticality::HIGH, "Failed to set interrupt type for pin %lu: %s", pin, esp_err_to_name(ret));
return false;
}
// Add ISR handler
ret = gpio_isr_handler_add(static_cast<gpio_num_t>(pin),
reinterpret_cast<gpio_isr_t>(callback), arg);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2313, asf::logger::Criticality::HIGH, "Failed to add ISR handler for pin %lu: %s", pin, esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2314, asf::logger::Criticality::LOW, "Interrupt attached to GPIO pin %lu", pin);
return true;
}
bool Gpio::detachInterrupt(uint32_t pin)
{
if (!isValidPin(pin)) {
ASF_LOGE(TAG, 2302, asf::logger::Criticality::HIGH, "Invalid GPIO pin: %lu", pin);
return false;
}
// Disable interrupt
esp_err_t ret = gpio_set_intr_type(static_cast<gpio_num_t>(pin), GPIO_INTR_DISABLE);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2315, asf::logger::Criticality::HIGH, "Failed to disable interrupt for pin %lu: %s", pin, esp_err_to_name(ret));
return false;
}
// Remove ISR handler
ret = gpio_isr_handler_remove(static_cast<gpio_num_t>(pin));
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2316, asf::logger::Criticality::HIGH, "Failed to remove ISR handler for pin %lu: %s", pin, esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2317, asf::logger::Criticality::LOW, "Interrupt detached from GPIO pin %lu", pin);
return true;
}
bool Gpio::enableInterrupt(uint32_t pin)
{
if (!isValidPin(pin)) {
ASF_LOGE(TAG, 2302, asf::logger::Criticality::HIGH, "Invalid GPIO pin: %lu", pin);
return false;
}
esp_err_t ret = gpio_intr_enable(static_cast<gpio_num_t>(pin));
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2318, asf::logger::Criticality::HIGH, "Failed to enable interrupt for pin %lu: %s", pin, esp_err_to_name(ret));
return false;
}
return true;
}
bool Gpio::disableInterrupt(uint32_t pin)
{
if (!isValidPin(pin)) {
ASF_LOGE(TAG, 2302, asf::logger::Criticality::HIGH, "Invalid GPIO pin: %lu", pin);
return false;
}
esp_err_t ret = gpio_intr_disable(static_cast<gpio_num_t>(pin));
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2319, asf::logger::Criticality::HIGH, "Failed to disable interrupt for pin %lu: %s", pin, esp_err_to_name(ret));
return false;
}
return true;
}
bool Gpio::isValidPin(uint32_t pin)
{
return GPIO_IS_VALID_GPIO(pin);
}
gpio_mode_t Gpio::convertMode(GpioMode mode)
{
switch (mode) {
case GpioMode::INPUT:
case GpioMode::INPUT_PULLUP:
case GpioMode::INPUT_PULLDOWN:
return GPIO_MODE_INPUT;
case GpioMode::OUTPUT:
return GPIO_MODE_OUTPUT;
case GpioMode::OUTPUT_OD:
return GPIO_MODE_OUTPUT_OD;
default:
return GPIO_MODE_INPUT;
}
}
gpio_int_type_t Gpio::convertIntType(GpioIntType intType)
{
switch (intType) {
case GpioIntType::DISABLE:
return GPIO_INTR_DISABLE;
case GpioIntType::RISING_EDGE:
return GPIO_INTR_POSEDGE;
case GpioIntType::FALLING_EDGE:
return GPIO_INTR_NEGEDGE;
case GpioIntType::ANY_EDGE:
return GPIO_INTR_ANYEDGE;
case GpioIntType::LOW_LEVEL:
return GPIO_INTR_LOW_LEVEL;
case GpioIntType::HIGH_LEVEL:
return GPIO_INTR_HIGH_LEVEL;
default:
return GPIO_INTR_DISABLE;
}
}

View File

@@ -1,173 +0,0 @@
/**
* @file gpio.hpp
* @brief GPIO wrapper component header - Wrapper for ESP-IDF GPIO functionality
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#ifndef GPIO_HPP
#define GPIO_HPP
#include <cstdint>
#include "driver/gpio.h"
#include "esp_err.h"
/**
* @brief GPIO pin mode enumeration
*/
enum class GpioMode
{
INPUT,
OUTPUT,
INPUT_PULLUP,
INPUT_PULLDOWN,
OUTPUT_OD
};
/**
* @brief GPIO interrupt type enumeration
*/
enum class GpioIntType
{
DISABLE,
RISING_EDGE,
FALLING_EDGE,
ANY_EDGE,
LOW_LEVEL,
HIGH_LEVEL
};
/**
* @brief GPIO callback function type
*/
using GpioCallback = void (*)(uint32_t pin, void* arg);
/**
* @brief GPIO wrapper class
*
* Provides a C++ wrapper for ESP-IDF GPIO functionality.
* This class encapsulates ESP-IDF GPIO driver functions in an object-oriented interface.
*/
class Gpio
{
public:
/**
* @brief Constructor
* @details Initializes the GPIO wrapper instance
*/
Gpio();
/**
* @brief Destructor
* @details Cleans up resources and uninstalls ISR if installed
*/
~Gpio();
/**
* @brief Configure a GPIO pin
* @param pin GPIO pin number (0-39 for ESP32)
* @param mode GPIO mode configuration
* @return true if configured successfully, false otherwise
* @note This function configures the pin direction, pull-up/down resistors
*/
bool configure(uint32_t pin, GpioMode mode);
/**
* @brief Set GPIO pin level
* @param pin GPIO pin number
* @param level Pin level (0 for low, 1 for high)
* @return true if set successfully, false otherwise
* @note Only works for output pins
*/
bool setLevel(uint32_t pin, uint32_t level);
/**
* @brief Get GPIO pin level
* @param pin GPIO pin number
* @return Pin level (0 for low, 1 for high), or -1 on error
* @note Works for both input and output pins
*/
int32_t getLevel(uint32_t pin);
/**
* @brief Toggle GPIO pin level
* @param pin GPIO pin number
* @return true if toggled successfully, false otherwise
* @note Only works for output pins
*/
bool toggleLevel(uint32_t pin);
/**
* @brief Install GPIO interrupt service
* @param flags Allocation flags for interrupt service
* @return true if installed successfully, false otherwise
* @note Must be called before attaching interrupts
*/
bool installIsr(int flags = 0);
/**
* @brief Uninstall GPIO interrupt service
* @return true if uninstalled successfully, false otherwise
*/
bool uninstallIsr();
/**
* @brief Attach interrupt to a GPIO pin
* @param pin GPIO pin number
* @param intType Interrupt type
* @param callback Callback function
* @param arg Argument passed to callback
* @return true if attached successfully, false otherwise
* @note ISR must be installed before calling this function
*/
bool attachInterrupt(uint32_t pin, GpioIntType intType, GpioCallback callback, void* arg);
/**
* @brief Detach interrupt from a GPIO pin
* @param pin GPIO pin number
* @return true if detached successfully, false otherwise
*/
bool detachInterrupt(uint32_t pin);
/**
* @brief Enable interrupt for a GPIO pin
* @param pin GPIO pin number
* @return true if enabled successfully, false otherwise
*/
bool enableInterrupt(uint32_t pin);
/**
* @brief Disable interrupt for a GPIO pin
* @param pin GPIO pin number
* @return true if disabled successfully, false otherwise
*/
bool disableInterrupt(uint32_t pin);
/**
* @brief Check if GPIO pin is valid
* @param pin GPIO pin number
* @return true if pin is valid, false otherwise
*/
static bool isValidPin(uint32_t pin);
private:
bool m_isrInstalled_; ///< Flag indicating if ISR is installed
/**
* @brief Convert GpioMode to ESP-IDF gpio_mode_t
* @param mode GPIO mode
* @return ESP-IDF gpio_mode_t
*/
gpio_mode_t convertMode(GpioMode mode);
/**
* @brief Convert GpioIntType to ESP-IDF gpio_int_type_t
* @param intType Interrupt type
* @return ESP-IDF gpio_int_type_t
*/
gpio_int_type_t convertIntType(GpioIntType intType);
};
#endif // GPIO_HPP

View File

@@ -1,21 +0,0 @@
ID,Component,Level,Criticality,Message
2300,GPIO,INFO,Low,GPIO wrapper initialized
2301,GPIO,INFO,Low,GPIO wrapper destroyed
2302,GPIO,ERROR,High,Invalid GPIO pin: %lu
2303,GPIO,ERROR,High,Failed to configure GPIO pin %lu: %s
2304,GPIO,INFO,Low,GPIO pin %lu configured successfully
2305,GPIO,ERROR,High,Failed to set GPIO pin %lu level: %s
2306,GPIO,WARNING,Medium,GPIO ISR already installed
2307,GPIO,ERROR,High,Failed to install GPIO ISR service: %s
2308,GPIO,INFO,Low,GPIO initialized successfully
2309,GPIO,WARNING,Medium,GPIO ISR not installed
2310,GPIO,INFO,Low,GPIO ISR service uninstalled
2311,GPIO,WARNING,Medium,GPIO ISR not installed, installing now
2312,GPIO,ERROR,High,Failed to set interrupt type for pin %lu: %s
2313,GPIO,ERROR,High,Failed to add ISR handler for pin %lu: %s
2314,GPIO,INFO,Low,Interrupt attached to GPIO pin %lu
2315,GPIO,ERROR,High,Failed to disable interrupt for pin %lu: %s
2316,GPIO,ERROR,High,Failed to remove ISR handler for pin %lu: %s
2317,GPIO,INFO,Low,Interrupt detached from GPIO pin %lu
2318,GPIO,ERROR,High,Failed to enable interrupt for pin %lu: %s
2319,GPIO,ERROR,High,Failed to disable interrupt for pin %lu: %s
Can't render this file because it has a wrong number of fields in line 13.

View File

@@ -1,34 +0,0 @@
import sys
import os
import time
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
if folder_path not in sys.path:
sys.path.append(folder_path)
from scan_serial import ESP32Runner
def test_gpio_initialize():
runner = ESP32Runner(mode="SIM", port="COM9")
runner.start()
print("--- QEMU Runner Started ---", flush=True)
try:
start_time = time.time()
while time.time() - start_time < 30:
line = runner.get_line(timeout=1.0)
if line:
print(line, flush=True)
if "GPIO wrapper initialized" in line or "GPIO ISR service installed successfully" in line:
print("SUCCESS CRITERIA MET!", flush=True)
return 0
if runner.process.poll() is not None:
print(f"Process exited with code: {runner.process.returncode}", flush=True)
return 1
finally:
runner.stop()
print("Done.", flush=True)
return 1
if __name__ == "__main__":
exit_code = test_gpio_initialize()
sys.exit(exit_code)

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<test_scenario>
<!-- The configuration for the test environment. -->
<!-- Available configurations: SIMULATE, HIL -->
<config>SIMULATE</config>
<test_case>
<test_case_id>GPIO_INIT_TEST</test_case_id>
<!-- The main command that executes the test itself. -->
<test_exec>python components/ESP_IDF_FW_wrappers/gpio/test/gpio_init_test.py</test_exec>
</test_case>
</test_scenario>

View File

@@ -1,65 +0,0 @@
/**
* @file test_gpio.cpp
* @brief Unit tests for GPIO wrapper component
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "unity.h"
#include "gpio.hpp"
extern "C" {
void setUp(void)
{
}
void tearDown(void)
{
}
/**
* @brief Test GPIO configuration
*/
void test_gpio_configure(void)
{
Gpio gpio;
bool result = gpio.configure(2, GpioMode::OUTPUT);
TEST_ASSERT_TRUE(result);
}
/**
* @brief Test GPIO set level
*/
void test_gpio_set_level(void)
{
Gpio gpio;
gpio.configure(2, GpioMode::OUTPUT);
bool result = gpio.setLevel(2, 1);
TEST_ASSERT_TRUE(result);
}
/**
* @brief Test GPIO get level
*/
void test_gpio_get_level(void)
{
Gpio gpio;
gpio.configure(2, GpioMode::INPUT);
int32_t result = gpio.getLevel(2);
TEST_ASSERT_TRUE(result >= 0);
}
/**
* @brief Test GPIO ISR installation
*/
void test_gpio_install_isr(void)
{
Gpio gpio;
bool result = gpio.installIsr();
TEST_ASSERT_TRUE(result);
}
} // extern "C"

View File

@@ -1,6 +0,0 @@
idf_component_register(
SRCS "com/i2c.cpp"
INCLUDE_DIRS "com"
REQUIRES driver logger
)

View File

@@ -1,216 +0,0 @@
# I2C Wrapper Module
## Overview
The I2C wrapper module provides a C++ object-oriented interface for ESP-IDF I2C functionality. This module encapsulates the ESP-IDF I2C driver functions and provides a clean, easy-to-use API for I2C communication with peripheral devices.
## Features
- **Multi-Port Support**: Support for I2C0 and I2C1
- **Master/Slave Mode**: Configurable as master or slave device
- **Register Operations**: Convenient register read/write functions
- **Bus Scanning**: Automatic device discovery on I2C bus
- **Timeout Support**: Configurable timeouts for all operations
- **Pull-up Configuration**: Internal pull-up resistor control
- **Error Handling**: Comprehensive error checking and logging
## Architecture
### Class Diagram
```
┌─────────────────────────────────────┐
│ I2c │
├─────────────────────────────────────┤
│ - m_isInitialized_[2]: bool │
├─────────────────────────────────────┤
│ + I2c() │
│ + ~I2c() │
│ + initialize(port, config): bool │
│ + deinitialize(port): bool │
│ + write(...): int32_t │
│ + read(...): int32_t │
│ + writeRead(...): int32_t │
│ + writeRegister(...): bool │
│ + readRegister(...): int32_t │
│ + scanBus(...): int32_t │
│ + isInitialized(port): bool │
│ + getDefaultConfig(): I2cConfig │
│ - convertPort(port): i2c_port_t │
│ - convertConfig(cfg): i2c_config_t │
└─────────────────────────────────────┘
```
### Enumerations
#### I2cPort
- `PORT_0`: I2C port 0
- `PORT_1`: I2C port 1
#### I2cMode
- `MASTER`: Master mode
- `SLAVE`: Slave mode
### Configuration Structure
```cpp
struct I2cConfig {
I2cMode mode; // I2C mode (master/slave)
uint32_t sdaPin; // SDA pin number
uint32_t sclPin; // SCL pin number
uint32_t clkSpeedHz; // Clock speed in Hz (for master mode)
bool sdaPullupEnable; // Enable SDA pull-up resistor
bool sclPullupEnable; // Enable SCL pull-up resistor
uint8_t slaveAddress; // Slave address (for slave mode)
uint32_t rxBufferLen; // RX buffer length (for slave mode)
uint32_t txBufferLen; // TX buffer length (for slave mode)
};
```
## Usage Examples
### Basic I2C Master Communication
```cpp
#include "i2c.hpp"
// Create I2C instance
I2c i2c;
// Get default configuration
I2cConfig config = I2c::getDefaultConfig();
config.sdaPin = 21;
config.sclPin = 22;
config.clkSpeedHz = 400000; // 400kHz
// Initialize I2C
i2c.initialize(I2cPort::PORT_0, config);
// Write data to device
uint8_t data[] = {0x01, 0x02, 0x03};
i2c.write(I2cPort::PORT_0, 0x48, data, sizeof(data));
// Read data from device
uint8_t buffer[10];
int32_t bytesRead = i2c.read(I2cPort::PORT_0, 0x48, buffer, sizeof(buffer));
```
### Register Operations
```cpp
// Write to register
i2c.writeRegister(I2cPort::PORT_0, 0x48, 0x10, 0xFF);
// Read from register
int32_t regValue = i2c.readRegister(I2cPort::PORT_0, 0x48, 0x10);
// Write register address and read multiple bytes
uint8_t regData[4];
i2c.writeRead(I2cPort::PORT_0, 0x48, 0x10, regData, sizeof(regData));
```
### Bus Scanning
```cpp
// Scan for devices on the bus
uint8_t devices[20];
int32_t deviceCount = i2c.scanBus(I2cPort::PORT_0, devices, sizeof(devices));
printf("Found %ld devices:\n", deviceCount);
for (int i = 0; i < deviceCount; i++) {
printf("Device at address: 0x%02X\n", devices[i]);
}
```
### Slave Mode Configuration
```cpp
I2cConfig slaveConfig = {};
slaveConfig.mode = I2cMode::SLAVE;
slaveConfig.sdaPin = 21;
slaveConfig.sclPin = 22;
slaveConfig.slaveAddress = 0x28;
slaveConfig.rxBufferLen = 256;
slaveConfig.txBufferLen = 256;
slaveConfig.sdaPullupEnable = true;
slaveConfig.sclPullupEnable = true;
i2c.initialize(I2cPort::PORT_1, slaveConfig);
```
## API Reference
### Constructor/Destructor
- **I2c()**: Initialize I2C wrapper instance
- **~I2c()**: Clean up resources and deinitialize all ports
### Configuration Methods
- **initialize(port, config)**: Initialize I2C port with configuration
- **deinitialize(port)**: Deinitialize I2C port
- **isInitialized(port)**: Check if port is initialized
- **getDefaultConfig()**: Get default configuration structure
### Communication Methods
- **write(port, address, data, length, timeout)**: Write data to device
- **read(port, address, buffer, length, timeout)**: Read data from device
- **writeRead(port, address, regAddr, buffer, length, timeout)**: Write register address and read data
### Register Operations
- **writeRegister(port, address, regAddr, value, timeout)**: Write single register
- **readRegister(port, address, regAddr, timeout)**: Read single register
### Utility Methods
- **scanBus(port, devices, maxDevices)**: Scan bus for connected devices
## Error Handling
The module provides comprehensive error handling:
- Port validation and initialization checks
- Device address validation (0x08-0x77 range)
- ESP-IDF error codes are caught and logged
- Return values indicate success/failure for all operations
- Detailed logging for debugging and troubleshooting
## Dependencies
- ESP-IDF I2C driver (`driver/i2c.h`)
- ESP-IDF error handling (`esp_err.h`)
- ESP-IDF logging (`esp_log.h`)
- FreeRTOS (`freertos/FreeRTOS.h`, `freertos/task.h`)
## Thread Safety
The I2C wrapper uses ESP-IDF's thread-safe I2C driver. Multiple tasks can safely use different I2C ports simultaneously, but access to the same port should be synchronized.
## Memory Usage
- Fixed memory footprint per instance
- Configurable buffer sizes for slave mode
- No dynamic memory allocation in wrapper layer
## Performance Considerations
- Direct ESP-IDF function calls for optimal performance
- Minimal overhead over raw ESP-IDF calls
- Configurable clock speeds (100kHz, 400kHz, 1MHz)
- Bus scanning can be time-consuming for large address ranges
## Common I2C Speeds
- **Standard Mode**: 100 kHz
- **Fast Mode**: 400 kHz
- **Fast Mode Plus**: 1 MHz
## Troubleshooting
### Common Issues
1. **Pull-up Resistors**: Ensure proper pull-up resistors on SDA/SCL lines
2. **Address Conflicts**: Check for address conflicts when using multiple devices
3. **Clock Stretching**: Some devices require clock stretching support
4. **Bus Capacitance**: Long wires or many devices can affect signal integrity

View File

@@ -1,273 +0,0 @@
/**
* @file i2c.cpp
* @brief I2C wrapper component implementation
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "i2c.hpp"
#include "logger.hpp"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static const char* TAG = "I2C_WRAPPER";
I2c::I2c()
{
m_isInitialized_[0] = false;
m_isInitialized_[1] = false;
ASF_LOGI(TAG, 2400, asf::logger::Criticality::LOW, "I2C wrapper initialized");
}
I2c::~I2c()
{
// Deinitialize all ports
for (int i = 0; i < 2; i++) {
if (m_isInitialized_[i]) {
deinitialize(static_cast<I2cPort>(i));
}
}
ASF_LOGI(TAG, 2401, asf::logger::Criticality::LOW, "I2C wrapper destroyed");
}
bool I2c::initialize(I2cPort port, const I2cConfig& config)
{
uint8_t portIdx = static_cast<uint8_t>(port);
i2c_port_t i2cPort = convertPort(port);
if (m_isInitialized_[portIdx]) {
ASF_LOGW(TAG, 2402, asf::logger::Criticality::MEDIUM, "I2C port %d already initialized", portIdx);
return true;
}
// Configure I2C parameters
i2c_config_t i2cConfig = convertConfig(config);
esp_err_t ret = i2c_param_config(i2cPort, &i2cConfig);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2403, asf::logger::Criticality::HIGH, "Failed to configure I2C port %d: %s", portIdx, esp_err_to_name(ret));
return false;
}
// Install I2C driver
ret = i2c_driver_install(i2cPort, i2cConfig.mode, config.rxBufferLen, config.txBufferLen, 0);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2404, asf::logger::Criticality::HIGH, "Failed to install I2C driver for port %d: %s", portIdx, esp_err_to_name(ret));
return false;
}
m_isInitialized_[portIdx] = true;
ASF_LOGI(TAG, 2405, asf::logger::Criticality::LOW, "I2C initialized successfully");
return true;
}
bool I2c::deinitialize(I2cPort port)
{
uint8_t portIdx = static_cast<uint8_t>(port);
i2c_port_t i2cPort = convertPort(port);
if (!m_isInitialized_[portIdx]) {
ASF_LOGW(TAG, 2406, asf::logger::Criticality::MEDIUM, "I2C port %d not initialized", portIdx);
return true;
}
esp_err_t ret = i2c_driver_delete(i2cPort);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2407, asf::logger::Criticality::HIGH, "Failed to delete I2C driver for port %d: %s", portIdx, esp_err_to_name(ret));
return false;
}
m_isInitialized_[portIdx] = false;
ASF_LOGI(TAG, 2408, asf::logger::Criticality::LOW, "I2C port %d deinitialized", portIdx);
return true;
}
int32_t I2c::write(I2cPort port, uint8_t deviceAddress, const uint8_t* data, size_t dataLen, uint32_t timeoutMs)
{
uint8_t portIdx = static_cast<uint8_t>(port);
i2c_port_t i2cPort = convertPort(port);
if (!m_isInitialized_[portIdx]) {
ASF_LOGE(TAG, 2409, asf::logger::Criticality::HIGH, "I2C port %d not initialized", portIdx);
return -1;
}
if (data == nullptr || dataLen == 0) {
ASF_LOGE(TAG, 2410, asf::logger::Criticality::HIGH, "Invalid data or length");
return -1;
}
TickType_t timeout = pdMS_TO_TICKS(timeoutMs);
esp_err_t ret = i2c_master_write_to_device(i2cPort, deviceAddress, data, dataLen, timeout);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2411, asf::logger::Criticality::HIGH, "Failed to write to device 0x%02X on port %d: %s",
deviceAddress, portIdx, esp_err_to_name(ret));
return -1;
}
return static_cast<int32_t>(dataLen);
}
int32_t I2c::read(I2cPort port, uint8_t deviceAddress, uint8_t* data, size_t dataLen, uint32_t timeoutMs)
{
uint8_t portIdx = static_cast<uint8_t>(port);
i2c_port_t i2cPort = convertPort(port);
if (!m_isInitialized_[portIdx]) {
ASF_LOGE(TAG, 2412, asf::logger::Criticality::HIGH, "I2C port %d not initialized", portIdx);
return -1;
}
if (data == nullptr || dataLen == 0) {
ASF_LOGE(TAG, 2413, asf::logger::Criticality::HIGH, "Invalid data buffer or length");
return -1;
}
TickType_t timeout = pdMS_TO_TICKS(timeoutMs);
esp_err_t ret = i2c_master_read_from_device(i2cPort, deviceAddress, data, dataLen, timeout);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2414, asf::logger::Criticality::HIGH, "Failed to read from device 0x%02X on port %d: %s",
deviceAddress, portIdx, esp_err_to_name(ret));
return -1;
}
return static_cast<int32_t>(dataLen);
}
int32_t I2c::writeRead(I2cPort port, uint8_t deviceAddress, uint8_t regAddress, uint8_t* data, size_t dataLen, uint32_t timeoutMs)
{
uint8_t portIdx = static_cast<uint8_t>(port);
i2c_port_t i2cPort = convertPort(port);
if (!m_isInitialized_[portIdx]) {
ASF_LOGE(TAG, 2415, asf::logger::Criticality::HIGH, "I2C port %d not initialized", portIdx);
return -1;
}
if (data == nullptr || dataLen == 0) {
ASF_LOGE(TAG, 2416, asf::logger::Criticality::HIGH, "Invalid data buffer or length");
return -1;
}
TickType_t timeout = pdMS_TO_TICKS(timeoutMs);
esp_err_t ret = i2c_master_write_read_device(i2cPort, deviceAddress,
&regAddress, 1, data, dataLen, timeout);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2417, asf::logger::Criticality::HIGH, "Failed to write-read device 0x%02X on port %d: %s",
deviceAddress, portIdx, esp_err_to_name(ret));
return -1;
}
return static_cast<int32_t>(dataLen);
}
bool I2c::writeRegister(I2cPort port, uint8_t deviceAddress, uint8_t regAddress, uint8_t regValue, uint32_t timeoutMs)
{
uint8_t writeData[2] = {regAddress, regValue};
int32_t result = write(port, deviceAddress, writeData, 2, timeoutMs);
return result == 2;
}
int32_t I2c::readRegister(I2cPort port, uint8_t deviceAddress, uint8_t regAddress, uint32_t timeoutMs)
{
uint8_t regValue = 0;
int32_t result = writeRead(port, deviceAddress, regAddress, &regValue, 1, timeoutMs);
if (result == 1) {
return regValue;
}
return -1;
}
int32_t I2c::scanBus(I2cPort port, uint8_t* devices, size_t maxDevices)
{
uint8_t portIdx = static_cast<uint8_t>(port);
i2c_port_t i2cPort = convertPort(port);
if (!m_isInitialized_[portIdx]) {
ASF_LOGE(TAG, 2418, asf::logger::Criticality::HIGH, "I2C port %d not initialized", portIdx);
return -1;
}
if (devices == nullptr || maxDevices == 0) {
ASF_LOGE(TAG, 2419, asf::logger::Criticality::HIGH, "Invalid devices buffer or size");
return -1;
}
int32_t deviceCount = 0;
uint8_t testData = 0;
ASF_LOGI(TAG, 2420, asf::logger::Criticality::LOW, "Scanning I2C bus on port %d...", portIdx);
for (uint8_t addr = 0x08; addr < 0x78 && deviceCount < maxDevices; addr++) {
esp_err_t ret = i2c_master_write_to_device(i2cPort, addr, &testData, 0, pdMS_TO_TICKS(100));
if (ret == ESP_OK) {
devices[deviceCount] = addr;
deviceCount++;
ASF_LOGI(TAG, 2421, asf::logger::Criticality::LOW, "Found device at address 0x%02X", addr);
}
}
ASF_LOGI(TAG, 2422, asf::logger::Criticality::LOW, "I2C scan complete. Found %ld devices", deviceCount);
return deviceCount;
}
bool I2c::isInitialized(I2cPort port) const
{
uint8_t portIdx = static_cast<uint8_t>(port);
return m_isInitialized_[portIdx];
}
I2cConfig I2c::getDefaultConfig()
{
I2cConfig config = {};
config.mode = I2cMode::MASTER;
config.sdaPin = GPIO_NUM_21;
config.sclPin = GPIO_NUM_22;
config.clkSpeedHz = 100000; // 100kHz
config.sdaPullupEnable = true;
config.sclPullupEnable = true;
config.slaveAddress = 0;
config.rxBufferLen = 0; // Not used in master mode
config.txBufferLen = 0; // Not used in master mode
return config;
}
i2c_port_t I2c::convertPort(I2cPort port)
{
switch (port) {
case I2cPort::PORT_0:
return I2C_NUM_0;
case I2cPort::PORT_1:
return I2C_NUM_1;
default:
return I2C_NUM_0;
}
}
i2c_config_t I2c::convertConfig(const I2cConfig& config)
{
i2c_config_t i2cConfig = {};
i2cConfig.mode = static_cast<i2c_mode_t>(config.mode);
i2cConfig.sda_io_num = static_cast<gpio_num_t>(config.sdaPin);
i2cConfig.scl_io_num = static_cast<gpio_num_t>(config.sclPin);
i2cConfig.sda_pullup_en = config.sdaPullupEnable;
i2cConfig.scl_pullup_en = config.sclPullupEnable;
if (config.mode == I2cMode::MASTER) {
i2cConfig.master.clk_speed = config.clkSpeedHz;
} else {
i2cConfig.slave.addr_10bit_en = 0;
i2cConfig.slave.slave_addr = config.slaveAddress;
i2cConfig.slave.maximum_speed = config.clkSpeedHz;
}
i2cConfig.clk_flags = 0;
return i2cConfig;
}

View File

@@ -1,183 +0,0 @@
/**
* @file i2c.hpp
* @brief I2C wrapper component header - Wrapper for ESP-IDF I2C functionality
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#ifndef I2C_HPP
#define I2C_HPP
#include <cstdint>
#include "driver/i2c.h"
#include "esp_err.h"
/**
* @brief I2C port enumeration
*/
enum class I2cPort
{
PORT_0,
PORT_1
};
/**
* @brief I2C mode enumeration
*/
enum class I2cMode
{
MASTER = I2C_MODE_MASTER,
SLAVE = I2C_MODE_SLAVE
};
/**
* @brief I2C configuration structure
*/
struct I2cConfig
{
I2cMode mode; ///< I2C mode (master/slave)
uint32_t sdaPin; ///< SDA pin number
uint32_t sclPin; ///< SCL pin number
uint32_t clkSpeedHz; ///< Clock speed in Hz (for master mode)
bool sdaPullupEnable; ///< Enable SDA pull-up resistor
bool sclPullupEnable; ///< Enable SCL pull-up resistor
uint8_t slaveAddress; ///< Slave address (for slave mode)
uint32_t rxBufferLen; ///< RX buffer length (for slave mode)
uint32_t txBufferLen; ///< TX buffer length (for slave mode)
};
/**
* @brief I2C wrapper class
*
* Provides a C++ wrapper for ESP-IDF I2C functionality.
* This class encapsulates ESP-IDF I2C driver functions in an object-oriented interface.
*/
class I2c
{
public:
/**
* @brief Constructor
* @details Initializes the I2C wrapper instance
*/
I2c();
/**
* @brief Destructor
* @details Cleans up resources and deinitializes all I2C ports
*/
~I2c();
/**
* @brief Initialize I2C bus
* @param port I2C port number
* @param config I2C configuration parameters
* @return true if initialized successfully, false otherwise
* @note This function configures pins, clock speed, and mode
*/
bool initialize(I2cPort port, const I2cConfig& config);
/**
* @brief Deinitialize I2C bus
* @param port I2C port number
* @return true if deinitialized successfully, false otherwise
*/
bool deinitialize(I2cPort port);
/**
* @brief Write data to I2C device
* @param port I2C port number
* @param deviceAddress 7-bit device address
* @param data Pointer to data buffer
* @param dataLen Number of bytes to write
* @param timeoutMs Timeout in milliseconds (default: 1000ms)
* @return Number of bytes written, or -1 on error
*/
int32_t write(I2cPort port, uint8_t deviceAddress, const uint8_t* data, size_t dataLen, uint32_t timeoutMs = 1000);
/**
* @brief Read data from I2C device
* @param port I2C port number
* @param deviceAddress 7-bit device address
* @param data Pointer to receive buffer
* @param dataLen Number of bytes to read
* @param timeoutMs Timeout in milliseconds (default: 1000ms)
* @return Number of bytes read, or -1 on error
*/
int32_t read(I2cPort port, uint8_t deviceAddress, uint8_t* data, size_t dataLen, uint32_t timeoutMs = 1000);
/**
* @brief Write to register and read from I2C device
* @param port I2C port number
* @param deviceAddress 7-bit device address
* @param regAddress Register address to write
* @param data Pointer to receive buffer
* @param dataLen Number of bytes to read
* @param timeoutMs Timeout in milliseconds (default: 1000ms)
* @return Number of bytes read, or -1 on error
*/
int32_t writeRead(I2cPort port, uint8_t deviceAddress, uint8_t regAddress, uint8_t* data, size_t dataLen, uint32_t timeoutMs = 1000);
/**
* @brief Write register value to I2C device
* @param port I2C port number
* @param deviceAddress 7-bit device address
* @param regAddress Register address
* @param regValue Register value
* @param timeoutMs Timeout in milliseconds (default: 1000ms)
* @return true if written successfully, false otherwise
*/
bool writeRegister(I2cPort port, uint8_t deviceAddress, uint8_t regAddress, uint8_t regValue, uint32_t timeoutMs = 1000);
/**
* @brief Read register value from I2C device
* @param port I2C port number
* @param deviceAddress 7-bit device address
* @param regAddress Register address
* @param timeoutMs Timeout in milliseconds (default: 1000ms)
* @return Register value, or -1 on error
*/
int32_t readRegister(I2cPort port, uint8_t deviceAddress, uint8_t regAddress, uint32_t timeoutMs = 1000);
/**
* @brief Scan I2C bus for devices
* @param port I2C port number
* @param devices Array to store found device addresses
* @param maxDevices Maximum number of devices to find
* @return Number of devices found
*/
int32_t scanBus(I2cPort port, uint8_t* devices, size_t maxDevices);
/**
* @brief Check if I2C port is initialized
* @param port I2C port number
* @return true if initialized, false otherwise
*/
bool isInitialized(I2cPort port) const;
/**
* @brief Get default I2C configuration
* @return Default I2C configuration structure
*/
static I2cConfig getDefaultConfig();
private:
bool m_isInitialized_[2]; ///< Initialization status for each port
/**
* @brief Convert I2cPort to ESP-IDF i2c_port_t
* @param port I2C port
* @return ESP-IDF i2c_port_t
*/
i2c_port_t convertPort(I2cPort port);
/**
* @brief Convert I2cConfig to ESP-IDF i2c_config_t
* @param config I2C configuration
* @return ESP-IDF i2c_config_t
*/
i2c_config_t convertConfig(const I2cConfig& config);
};
#endif // I2C_HPP

View File

@@ -1,24 +0,0 @@
ID,Component,Level,Criticality,Message
2400,I2C,INFO,Low,I2C wrapper initialized
2401,I2C,INFO,Low,I2C wrapper destroyed
2402,I2C,WARNING,Medium,I2C port %d already initialized
2403,I2C,ERROR,High,Failed to configure I2C port %d: %s
2404,I2C,ERROR,High,Failed to install I2C driver for port %d: %s
2405,I2C,INFO,Low,I2C initialized successfully
2406,I2C,WARNING,Medium,I2C port %d not initialized
2407,I2C,ERROR,High,Failed to delete I2C driver for port %d: %s
2408,I2C,INFO,Low,I2C port %d deinitialized
2409,I2C,ERROR,High,I2C port %d not initialized
2410,I2C,ERROR,High,Invalid data or length
2411,I2C,ERROR,High,Failed to write to device 0x%02X on port %d: %s
2412,I2C,ERROR,High,I2C port %d not initialized
2413,I2C,ERROR,High,Invalid data buffer or length
2414,I2C,ERROR,High,Failed to read from device 0x%02X on port %d: %s
2415,I2C,ERROR,High,I2C port %d not initialized
2416,I2C,ERROR,High,Invalid data buffer or length
2417,I2C,ERROR,High,Failed to write-read device 0x%02X on port %d: %s
2418,I2C,ERROR,High,I2C port %d not initialized
2419,I2C,ERROR,High,Invalid devices buffer or size
2420,I2C,INFO,Low,Scanning I2C bus on port %d...
2421,I2C,INFO,Low,Found device at address 0x%02X
2422,I2C,INFO,Low,I2C scan complete. Found %ld devices
1 ID Component Level Criticality Message
2 2400 I2C INFO Low I2C wrapper initialized
3 2401 I2C INFO Low I2C wrapper destroyed
4 2402 I2C WARNING Medium I2C port %d already initialized
5 2403 I2C ERROR High Failed to configure I2C port %d: %s
6 2404 I2C ERROR High Failed to install I2C driver for port %d: %s
7 2405 I2C INFO Low I2C initialized successfully
8 2406 I2C WARNING Medium I2C port %d not initialized
9 2407 I2C ERROR High Failed to delete I2C driver for port %d: %s
10 2408 I2C INFO Low I2C port %d deinitialized
11 2409 I2C ERROR High I2C port %d not initialized
12 2410 I2C ERROR High Invalid data or length
13 2411 I2C ERROR High Failed to write to device 0x%02X on port %d: %s
14 2412 I2C ERROR High I2C port %d not initialized
15 2413 I2C ERROR High Invalid data buffer or length
16 2414 I2C ERROR High Failed to read from device 0x%02X on port %d: %s
17 2415 I2C ERROR High I2C port %d not initialized
18 2416 I2C ERROR High Invalid data buffer or length
19 2417 I2C ERROR High Failed to write-read device 0x%02X on port %d: %s
20 2418 I2C ERROR High I2C port %d not initialized
21 2419 I2C ERROR High Invalid devices buffer or size
22 2420 I2C INFO Low Scanning I2C bus on port %d...
23 2421 I2C INFO Low Found device at address 0x%02X
24 2422 I2C INFO Low I2C scan complete. Found %ld devices

View File

@@ -1,34 +0,0 @@
import sys
import os
import time
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
if folder_path not in sys.path:
sys.path.append(folder_path)
from scan_serial import ESP32Runner
def test_i2c_initialize():
runner = ESP32Runner(mode="SIM", port="COM9")
runner.start()
print("--- QEMU Runner Started ---", flush=True)
try:
start_time = time.time()
while time.time() - start_time < 30:
line = runner.get_line(timeout=1.0)
if line:
print(line, flush=True)
if "I2C initialized successfully" in line:
print("SUCCESS CRITERIA MET!", flush=True)
return 0
if runner.process.poll() is not None:
print(f"Process exited with code: {runner.process.returncode}", flush=True)
return 1
finally:
runner.stop()
print("Done.", flush=True)
return 1
if __name__ == "__main__":
exit_code = test_i2c_initialize()
sys.exit(exit_code)

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<test_scenario>
<!-- The configuration for the test environment. -->
<!-- Available configurations: SIMULATE, HIL -->
<config>SIMULATE</config>
<test_case>
<test_case_id>I2C_INIT_TEST</test_case_id>
<!-- The main command that executes the test itself. -->
<test_exec>python components/ESP_IDF_FW_wrappers/i2c/test/i2c_init_test.py</test_exec>
</test_case>
</test_scenario>

View File

@@ -1,47 +0,0 @@
/**
* @file test_i2c.cpp
* @brief Unit tests for I2C wrapper component
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "unity.h"
#include "i2c.hpp"
extern "C" {
void setUp(void)
{
}
void tearDown(void)
{
}
/**
* @brief Test I2C initialization
*/
void test_i2c_initialize(void)
{
I2c i2c;
I2cConfig config = {21, 22, 100000};
bool result = i2c.initialize(I2cPort::PORT_0, config);
TEST_ASSERT_TRUE(result);
}
/**
* @brief Test I2C deinitialize
*/
void test_i2c_deinitialize(void)
{
I2c i2c;
I2cConfig config = {21, 22, 100000};
i2c.initialize(I2cPort::PORT_0, config);
bool result = i2c.deinitialize(I2cPort::PORT_0);
TEST_ASSERT_TRUE(result);
}
} // extern "C"

View File

@@ -1,5 +0,0 @@
idf_component_register(
SRCS "com/spi.cpp"
INCLUDE_DIRS "com"
REQUIRES esp_driver_spi esp_driver_gpio logger
)

View File

@@ -1,279 +0,0 @@
# SPI Wrapper Module
## Overview
The SPI wrapper module provides a C++ object-oriented interface for ESP-IDF SPI master functionality. This module encapsulates the ESP-IDF SPI master driver functions and provides a clean, easy-to-use API for SPI communication with peripheral devices.
## Features
- **Multi-Host Support**: Support for SPI1, SPI2, and SPI3 hosts
- **Device Management**: Add/remove multiple devices per SPI bus
- **Flexible Transfers**: Support for TX-only, RX-only, and full-duplex transfers
- **Command/Address Phases**: Support for command and address phases
- **DMA Support**: Automatic DMA channel allocation for high-speed transfers
- **Multiple SPI Modes**: Support for all 4 SPI modes (0-3)
- **Error Handling**: Comprehensive error checking and logging
## Architecture
### Class Diagram
```
┌─────────────────────────────────────┐
│ Spi │
├─────────────────────────────────────┤
│ - m_isInitialized_[3]: bool │
├─────────────────────────────────────┤
│ + Spi() │
│ + ~Spi() │
│ + initializeBus(host, cfg): bool │
│ + deinitializeBus(host): bool │
│ + addDevice(host, cfg, hdl): bool │
│ + removeDevice(handle): bool │
│ + transmit(...): int32_t │
│ + transmitOnly(...): int32_t │
│ + receiveOnly(...): int32_t │
│ + transmitWithCmdAddr(...): int32_t │
│ + isInitialized(host): bool │
│ + getDefaultBusConfig(): SpiBusCfg │
│ + getDefaultDeviceConfig(): SpiDev │
│ - convertHost(host): spi_host_t │
│ - convertBusConfig(cfg): spi_bus_t │
│ - convertDeviceConfig(cfg): spi_dev │
│ - convertMode(mode): uint32_t │
└─────────────────────────────────────┘
```
### Enumerations
#### SpiHost
- `SPI1_HOST`: SPI1 host (typically used for flash)
- `SPI2_HOST`: SPI2 host (HSPI)
- `SPI3_HOST`: SPI3 host (VSPI)
#### SpiMode
- `MODE_0`: CPOL=0, CPHA=0 (Clock idle low, data sampled on rising edge)
- `MODE_1`: CPOL=0, CPHA=1 (Clock idle low, data sampled on falling edge)
- `MODE_2`: CPOL=1, CPHA=0 (Clock idle high, data sampled on falling edge)
- `MODE_3`: CPOL=1, CPHA=1 (Clock idle high, data sampled on rising edge)
### Configuration Structures
#### SpiBusConfig
```cpp
struct SpiBusConfig {
uint32_t mosiPin; // MOSI pin number
uint32_t misoPin; // MISO pin number
uint32_t sclkPin; // SCLK pin number
uint32_t quadwpPin; // Quad SPI write protect pin (optional)
uint32_t quadhdPin; // Quad SPI hold pin (optional)
uint32_t maxTransferSize; // Maximum transfer size in bytes
};
```
#### SpiDeviceConfig
```cpp
struct SpiDeviceConfig {
uint32_t csPin; // Chip select pin number
uint32_t clockSpeedHz; // Clock speed in Hz
SpiMode mode; // SPI mode (0-3)
uint32_t queueSize; // Transaction queue size
uint32_t commandBits; // Command phase bits
uint32_t addressBits; // Address phase bits
uint32_t dummyBits; // Dummy phase bits
bool csEnaPretrans; // CS setup time
bool csEnaPosttrans; // CS hold time
};
```
## Usage Examples
### Basic SPI Communication
```cpp
#include "spi.hpp"
// Create SPI instance
Spi spi;
// Get default bus configuration
SpiBusConfig busConfig = Spi::getDefaultBusConfig();
busConfig.mosiPin = 23;
busConfig.misoPin = 19;
busConfig.sclkPin = 18;
// Initialize SPI bus
spi.initializeBus(SpiHost::SPI2_HOST, busConfig);
// Get default device configuration
SpiDeviceConfig deviceConfig = Spi::getDefaultDeviceConfig();
deviceConfig.csPin = 5;
deviceConfig.clockSpeedHz = 1000000; // 1MHz
deviceConfig.mode = SpiMode::MODE_0;
// Add device to bus
spi_device_handle_t deviceHandle;
spi.addDevice(SpiHost::SPI2_HOST, deviceConfig, &deviceHandle);
// Transmit and receive data
uint8_t txData[] = {0x01, 0x02, 0x03};
uint8_t rxData[3];
spi.transmit(deviceHandle, txData, rxData, sizeof(txData));
```
### Transmit-Only Operation
```cpp
// Send data without receiving
uint8_t command[] = {0xAA, 0x55, 0xFF};
spi.transmitOnly(deviceHandle, command, sizeof(command));
```
### Receive-Only Operation
```cpp
// Receive data without sending
uint8_t buffer[10];
spi.receiveOnly(deviceHandle, buffer, sizeof(buffer));
```
### Command and Address Phases
```cpp
// Configure device with command and address bits
SpiDeviceConfig config = Spi::getDefaultDeviceConfig();
config.commandBits = 8;
config.addressBits = 24;
config.csPin = 5;
spi_device_handle_t device;
spi.addDevice(SpiHost::SPI2_HOST, config, &device);
// Send command and address with data
uint8_t data[4];
spi.transmitWithCmdAddr(device, 0x03, 0x000000, nullptr, data, sizeof(data));
```
### Multiple Devices on Same Bus
```cpp
// Device 1 configuration
SpiDeviceConfig device1Config = Spi::getDefaultDeviceConfig();
device1Config.csPin = 5;
device1Config.clockSpeedHz = 1000000;
// Device 2 configuration
SpiDeviceConfig device2Config = Spi::getDefaultDeviceConfig();
device2Config.csPin = 15;
device2Config.clockSpeedHz = 5000000;
device2Config.mode = SpiMode::MODE_3;
// Add both devices
spi_device_handle_t device1, device2;
spi.addDevice(SpiHost::SPI2_HOST, device1Config, &device1);
spi.addDevice(SpiHost::SPI2_HOST, device2Config, &device2);
// Use devices independently
uint8_t data1[] = {0x01, 0x02};
uint8_t data2[] = {0xAA, 0xBB};
spi.transmitOnly(device1, data1, sizeof(data1));
spi.transmitOnly(device2, data2, sizeof(data2));
```
## API Reference
### Constructor/Destructor
- **Spi()**: Initialize SPI wrapper instance
- **~Spi()**: Clean up resources and deinitialize all hosts
### Bus Management
- **initializeBus(host, config)**: Initialize SPI bus with configuration
- **deinitializeBus(host)**: Deinitialize SPI bus
- **isInitialized(host)**: Check if host is initialized
### Device Management
- **addDevice(host, config, handle)**: Add device to SPI bus
- **removeDevice(handle)**: Remove device from SPI bus
### Data Transfer Methods
- **transmit(handle, txData, rxData, length)**: Full-duplex transfer
- **transmitOnly(handle, txData, length)**: Transmit-only transfer
- **receiveOnly(handle, rxData, length)**: Receive-only transfer
- **transmitWithCmdAddr(handle, cmd, addr, txData, rxData, length)**: Transfer with command/address
### Configuration Methods
- **getDefaultBusConfig()**: Get default bus configuration
- **getDefaultDeviceConfig()**: Get default device configuration
## Error Handling
The module provides comprehensive error handling:
- Host validation and initialization checks
- Device handle validation
- ESP-IDF error codes are caught and logged
- Return values indicate success/failure for all operations
- Detailed logging for debugging and troubleshooting
## Dependencies
- ESP-IDF SPI master driver (`driver/spi_master.h`)
- ESP-IDF error handling (`esp_err.h`)
- ESP-IDF logging (`esp_log.h`)
## Thread Safety
The SPI wrapper uses ESP-IDF's thread-safe SPI master driver. Multiple tasks can safely use different devices on the same bus simultaneously.
## Memory Usage
- Fixed memory footprint per instance
- DMA buffers allocated automatically by ESP-IDF
- Transaction queues configurable per device
## Performance Considerations
- Direct ESP-IDF function calls for optimal performance
- DMA support for high-speed transfers
- Configurable transaction queue sizes
- Clock speeds up to 80MHz (depending on ESP32 variant)
## SPI Timing Modes
| Mode | CPOL | CPHA | Clock Idle | Data Sample Edge |
|------|------|------|------------|------------------|
| 0 | 0 | 0 | Low | Rising |
| 1 | 0 | 1 | Low | Falling |
| 2 | 1 | 0 | High | Falling |
| 3 | 1 | 1 | High | Rising |
## Common Use Cases
### Flash Memory
- Command/address phases for read/write operations
- High-speed transfers with DMA
- Mode 0 or Mode 3 typically used
### Display Controllers
- Command/data distinction using DC pin
- High-speed pixel data transfers
- Various modes depending on controller
### Sensors
- Register-based communication
- Lower speed requirements
- Mode 0 most common
## Troubleshooting
### Common Issues
1. **Clock Speed**: Ensure clock speed is within device specifications
2. **SPI Mode**: Verify correct CPOL/CPHA settings for device
3. **CS Timing**: Some devices require specific CS setup/hold times
4. **Wire Length**: Long wires can cause signal integrity issues
5. **Pull-up Resistors**: MISO line may need pull-up resistor

View File

@@ -1,282 +0,0 @@
/**
* @file spi.cpp
* @brief SPI wrapper component implementation
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "spi.hpp"
#include "logger.hpp"
#include "driver/gpio.h"
#include "driver/spi_common.h"
#include "driver/spi_master.h"
#include <cstring>
static const char* TAG = "SPI_WRAPPER";
Spi::Spi()
{
m_isInitialized_[0] = false;
m_isInitialized_[1] = false;
m_isInitialized_[2] = false;
ASF_LOGI(TAG, 2500, asf::logger::Criticality::LOW, "SPI wrapper initialized");
}
Spi::~Spi()
{
// Deinitialize all hosts
for (int i = 0; i < 3; i++) {
if (m_isInitialized_[i]) {
deinitializeBus(static_cast<SpiHost>(i));
}
}
ASF_LOGI(TAG, 2501, asf::logger::Criticality::LOW, "SPI wrapper destroyed");
}
bool Spi::initializeBus(SpiHost host, const SpiBusConfig& config)
{
uint8_t hostIdx = static_cast<uint8_t>(host);
spi_host_device_t spiHost = convertHost(host);
if (m_isInitialized_[hostIdx]) {
ASF_LOGW(TAG, 2502, asf::logger::Criticality::MEDIUM, "SPI host %d already initialized", hostIdx);
return true;
}
// Configure SPI bus
spi_bus_config_t busConfig = convertBusConfig(config);
esp_err_t ret = spi_bus_initialize(spiHost, &busConfig, SPI_DMA_CH_AUTO);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2503, asf::logger::Criticality::HIGH, "Failed to initialize SPI bus %d: %s", hostIdx, esp_err_to_name(ret));
return false;
}
// Enable pull-ups on all SPI lines to ensure signal stability
if (config.misoPin >= 0) {
gpio_set_pull_mode(static_cast<gpio_num_t>(config.misoPin), GPIO_PULLUP_ONLY);
}
if (config.mosiPin >= 0) {
gpio_set_pull_mode(static_cast<gpio_num_t>(config.mosiPin), GPIO_PULLUP_ONLY);
}
if (config.sclkPin >= 0) {
gpio_set_pull_mode(static_cast<gpio_num_t>(config.sclkPin), GPIO_PULLUP_ONLY);
}
m_isInitialized_[hostIdx] = true;
ASF_LOGI(TAG, 2504, asf::logger::Criticality::LOW, "SPI initialized successfully");
return true;
}
bool Spi::deinitializeBus(SpiHost host)
{
uint8_t hostIdx = static_cast<uint8_t>(host);
spi_host_device_t spiHost = convertHost(host);
if (!m_isInitialized_[hostIdx]) {
ASF_LOGW(TAG, 2505, asf::logger::Criticality::MEDIUM, "SPI host %d not initialized", hostIdx);
return true;
}
esp_err_t ret = spi_bus_free(spiHost);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2506, asf::logger::Criticality::HIGH, "Failed to free SPI bus %d: %s", hostIdx, esp_err_to_name(ret));
return false;
}
m_isInitialized_[hostIdx] = false;
ASF_LOGI(TAG, 2507, asf::logger::Criticality::LOW, "SPI bus %d deinitialized", hostIdx);
return true;
}
bool Spi::addDevice(SpiHost host, const SpiDeviceConfig& config, spi_device_handle_t* deviceHandle)
{
uint8_t hostIdx = static_cast<uint8_t>(host);
spi_host_device_t spiHost = convertHost(host);
if (!m_isInitialized_[hostIdx]) {
ASF_LOGE(TAG, 2508, asf::logger::Criticality::HIGH, "SPI host %d not initialized", hostIdx);
return false;
}
if (deviceHandle == nullptr) {
ASF_LOGE(TAG, 2509, asf::logger::Criticality::HIGH, "Invalid device handle pointer");
return false;
}
// Configure SPI device
spi_device_interface_config_t deviceConfig = convertDeviceConfig(config);
esp_err_t ret = spi_bus_add_device(spiHost, &deviceConfig, deviceHandle);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2510, asf::logger::Criticality::HIGH, "Failed to add SPI device to bus %d: %s", hostIdx, esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2511, asf::logger::Criticality::LOW, "SPI device added to bus %d successfully", hostIdx);
return true;
}
bool Spi::removeDevice(spi_device_handle_t deviceHandle)
{
if (deviceHandle == nullptr) {
ASF_LOGE(TAG, 2512, asf::logger::Criticality::HIGH, "Invalid device handle");
return false;
}
esp_err_t ret = spi_bus_remove_device(deviceHandle);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2513, asf::logger::Criticality::HIGH, "Failed to remove SPI device: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2514, asf::logger::Criticality::LOW, "SPI device removed successfully");
return true;
}
int32_t Spi::transmit(spi_device_handle_t deviceHandle, const uint8_t* txData, uint8_t* rxData, size_t length)
{
if (deviceHandle == nullptr) {
ASF_LOGE(TAG, 2515, asf::logger::Criticality::HIGH, "Invalid device handle");
return -1;
}
if (length == 0) {
ASF_LOGW(TAG, 2516, asf::logger::Criticality::MEDIUM, "Zero length transfer");
return 0;
}
spi_transaction_t transaction = {};
transaction.length = length * 8; // Length in bits
transaction.tx_buffer = txData;
transaction.rx_buffer = rxData;
esp_err_t ret = spi_device_transmit(deviceHandle, &transaction);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2517, asf::logger::Criticality::HIGH, "Failed to transmit SPI data: %s", esp_err_to_name(ret));
return -1;
}
return static_cast<int32_t>(length);
}
int32_t Spi::transmitOnly(spi_device_handle_t deviceHandle, const uint8_t* txData, size_t length)
{
return transmit(deviceHandle, txData, nullptr, length);
}
int32_t Spi::receiveOnly(spi_device_handle_t deviceHandle, uint8_t* rxData, size_t length)
{
return transmit(deviceHandle, nullptr, rxData, length);
}
int32_t Spi::transmitWithCmdAddr(spi_device_handle_t deviceHandle, uint32_t command, uint32_t address,
const uint8_t* txData, uint8_t* rxData, size_t length)
{
if (deviceHandle == nullptr) {
ASF_LOGE(TAG, 2518, asf::logger::Criticality::HIGH, "Invalid device handle");
return -1;
}
spi_transaction_t transaction = {};
transaction.cmd = command;
transaction.addr = address;
transaction.length = length * 8; // Length in bits
transaction.tx_buffer = txData;
transaction.rx_buffer = rxData;
esp_err_t ret = spi_device_transmit(deviceHandle, &transaction);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2519, asf::logger::Criticality::HIGH, "Failed to transmit SPI data with cmd/addr: %s", esp_err_to_name(ret));
return -1;
}
return static_cast<int32_t>(length);
}
bool Spi::isInitialized(SpiHost host) const
{
uint8_t hostIdx = static_cast<uint8_t>(host);
return m_isInitialized_[hostIdx];
}
SpiBusConfig Spi::getDefaultBusConfig()
{
SpiBusConfig config = {};
config.mosiPin = GPIO_NUM_23;
config.misoPin = GPIO_NUM_19;
config.sclkPin = GPIO_NUM_18;
config.quadwpPin = -1;
config.quadhdPin = -1;
config.maxTransferSize = 4096;
return config;
}
SpiDeviceConfig Spi::getDefaultDeviceConfig()
{
SpiDeviceConfig config = {};
config.csPin = GPIO_NUM_5;
config.clockSpeedHz = 1000000; // 1MHz
config.mode = SpiMode::MODE_0;
config.queueSize = 7;
config.commandBits = 0;
config.addressBits = 0;
config.dummyBits = 0;
config.csEnaPretrans = false;
config.csEnaPosttrans = false;
return config;
}
spi_host_device_t Spi::convertHost(SpiHost host)
{
switch (host) {
case SpiHost::SPI1_HOST:
return SPI1_HOST;
case SpiHost::SPI2_HOST:
return SPI2_HOST;
case SpiHost::SPI3_HOST:
return SPI3_HOST;
default:
return SPI2_HOST;
}
}
spi_bus_config_t Spi::convertBusConfig(const SpiBusConfig& config)
{
spi_bus_config_t busConfig = {};
busConfig.mosi_io_num = config.mosiPin;
busConfig.miso_io_num = config.misoPin;
busConfig.sclk_io_num = config.sclkPin;
busConfig.quadwp_io_num = config.quadwpPin;
busConfig.quadhd_io_num = config.quadhdPin;
busConfig.max_transfer_sz = config.maxTransferSize;
busConfig.flags = 0;
busConfig.intr_flags = 0;
return busConfig;
}
spi_device_interface_config_t Spi::convertDeviceConfig(const SpiDeviceConfig& config)
{
spi_device_interface_config_t deviceConfig = {};
deviceConfig.command_bits = config.commandBits;
deviceConfig.address_bits = config.addressBits;
deviceConfig.dummy_bits = config.dummyBits;
deviceConfig.mode = convertMode(config.mode);
deviceConfig.duty_cycle_pos = 0;
deviceConfig.cs_ena_pretrans = config.csEnaPretrans ? 1 : 0;
deviceConfig.cs_ena_posttrans = config.csEnaPosttrans ? 1 : 0;
deviceConfig.clock_speed_hz = config.clockSpeedHz;
deviceConfig.input_delay_ns = 0;
deviceConfig.spics_io_num = config.csPin;
deviceConfig.flags = 0;
deviceConfig.queue_size = config.queueSize;
deviceConfig.pre_cb = nullptr;
deviceConfig.post_cb = nullptr;
return deviceConfig;
}
uint32_t Spi::convertMode(SpiMode mode)
{
return static_cast<uint32_t>(mode);
}

View File

@@ -1,212 +0,0 @@
/**
* @file spi.hpp
* @brief SPI wrapper component header - Wrapper for ESP-IDF SPI functionality
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#ifndef SPI_HPP
#define SPI_HPP
#include <cstdint>
#include "driver/spi_master.h"
#include "esp_err.h"
/**
* @brief SPI host enumeration
*/
enum class SpiHost
{
SPI1_HOST,
SPI2_HOST,
SPI3_HOST
};
/**
* @brief SPI mode enumeration
*/
enum class SpiMode
{
MODE_0,
MODE_1,
MODE_2,
MODE_3
};
/**
* @brief SPI bus configuration structure
*/
struct SpiBusConfig
{
uint32_t mosiPin; ///< MOSI pin number
uint32_t misoPin; ///< MISO pin number
uint32_t sclkPin; ///< SCLK pin number
uint32_t quadwpPin; ///< Quad SPI write protect pin (optional)
uint32_t quadhdPin; ///< Quad SPI hold pin (optional)
uint32_t maxTransferSize; ///< Maximum transfer size in bytes
};
/**
* @brief SPI device configuration structure
*/
struct SpiDeviceConfig
{
uint32_t csPin; ///< Chip select pin number
uint32_t clockSpeedHz; ///< Clock speed in Hz
SpiMode mode; ///< SPI mode (0-3)
uint32_t queueSize; ///< Transaction queue size
uint32_t commandBits; ///< Command phase bits
uint32_t addressBits; ///< Address phase bits
uint32_t dummyBits; ///< Dummy phase bits
bool csEnaPretrans; ///< CS setup time
bool csEnaPosttrans; ///< CS hold time
};
/**
* @brief SPI wrapper class
*
* Provides a C++ wrapper for ESP-IDF SPI functionality.
* This class encapsulates ESP-IDF SPI master driver functions in an object-oriented interface.
*/
class Spi
{
public:
/**
* @brief Constructor
* @details Initializes the SPI wrapper instance
*/
Spi();
/**
* @brief Destructor
* @details Cleans up resources and deinitializes all SPI hosts
*/
~Spi();
/**
* @brief Initialize SPI bus
* @param host SPI host number
* @param config SPI bus configuration
* @return true if initialized successfully, false otherwise
* @note This function configures the SPI bus pins and parameters
*/
bool initializeBus(SpiHost host, const SpiBusConfig& config);
/**
* @brief Deinitialize SPI bus
* @param host SPI host number
* @return true if deinitialized successfully, false otherwise
*/
bool deinitializeBus(SpiHost host);
/**
* @brief Add SPI device to bus
* @param host SPI host number
* @param config SPI device configuration
* @param deviceHandle Pointer to store device handle
* @return true if device added successfully, false otherwise
*/
bool addDevice(SpiHost host, const SpiDeviceConfig& config, spi_device_handle_t* deviceHandle);
/**
* @brief Remove SPI device from bus
* @param deviceHandle Device handle to remove
* @return true if device removed successfully, false otherwise
*/
bool removeDevice(spi_device_handle_t deviceHandle);
/**
* @brief Transmit and receive data
* @param deviceHandle Device handle
* @param txData Pointer to transmit data
* @param rxData Pointer to receive buffer
* @param length Number of bytes to transfer
* @return Number of bytes transferred, or -1 on error
*/
int32_t transmit(spi_device_handle_t deviceHandle, const uint8_t* txData, uint8_t* rxData, size_t length);
/**
* @brief Transmit data only
* @param deviceHandle Device handle
* @param txData Pointer to transmit data
* @param length Number of bytes to transmit
* @return Number of bytes transmitted, or -1 on error
*/
int32_t transmitOnly(spi_device_handle_t deviceHandle, const uint8_t* txData, size_t length);
/**
* @brief Receive data only
* @param deviceHandle Device handle
* @param rxData Pointer to receive buffer
* @param length Number of bytes to receive
* @return Number of bytes received, or -1 on error
*/
int32_t receiveOnly(spi_device_handle_t deviceHandle, uint8_t* rxData, size_t length);
/**
* @brief Transmit with command and address phases
* @param deviceHandle Device handle
* @param command Command to send
* @param address Address to send
* @param txData Pointer to transmit data
* @param rxData Pointer to receive buffer
* @param length Number of bytes to transfer
* @return Number of bytes transferred, or -1 on error
*/
int32_t transmitWithCmdAddr(spi_device_handle_t deviceHandle, uint32_t command, uint32_t address,
const uint8_t* txData, uint8_t* rxData, size_t length);
/**
* @brief Check if SPI host is initialized
* @param host SPI host number
* @return true if initialized, false otherwise
*/
bool isInitialized(SpiHost host) const;
/**
* @brief Get default bus configuration
* @return Default SPI bus configuration structure
*/
static SpiBusConfig getDefaultBusConfig();
/**
* @brief Get default device configuration
* @return Default SPI device configuration structure
*/
static SpiDeviceConfig getDefaultDeviceConfig();
private:
bool m_isInitialized_[3]; ///< Initialization status for each host
/**
* @brief Convert SpiHost to ESP-IDF spi_host_device_t
* @param host SPI host
* @return ESP-IDF spi_host_device_t
*/
spi_host_device_t convertHost(SpiHost host);
/**
* @brief Convert SpiBusConfig to ESP-IDF spi_bus_config_t
* @param config SPI bus configuration
* @return ESP-IDF spi_bus_config_t
*/
spi_bus_config_t convertBusConfig(const SpiBusConfig& config);
/**
* @brief Convert SpiDeviceConfig to ESP-IDF spi_device_interface_config_t
* @param config SPI device configuration
* @return ESP-IDF spi_device_interface_config_t
*/
spi_device_interface_config_t convertDeviceConfig(const SpiDeviceConfig& config);
/**
* @brief Convert SpiMode to ESP-IDF mode flags
* @param mode SPI mode
* @return ESP-IDF mode flags
*/
uint32_t convertMode(SpiMode mode);
};
#endif // SPI_HPP

View File

@@ -1,21 +0,0 @@
ID,Component,Level,Criticality,Message
2500,SPI,INFO,Low,SPI wrapper initialized
2501,SPI,INFO,Low,SPI wrapper destroyed
2502,SPI,WARNING,Medium,SPI host %d already initialized
2503,SPI,ERROR,High,Failed to initialize SPI bus %d: %s
2504,SPI,INFO,Low,SPI initialized successfully
2505,SPI,WARNING,Medium,SPI host %d not initialized
2506,SPI,ERROR,High,Failed to free SPI bus %d: %s
2507,SPI,INFO,Low,SPI bus %d deinitialized
2508,SPI,ERROR,High,SPI host %d not initialized
2509,SPI,ERROR,High,Invalid device handle pointer
2510,SPI,ERROR,High,Failed to add SPI device to bus %d: %s
2511,SPI,INFO,Low,SPI device added to bus %d successfully
2512,SPI,ERROR,High,Invalid device handle
2513,SPI,ERROR,High,Failed to remove SPI device: %s
2514,SPI,INFO,Low,SPI device removed successfully
2515,SPI,ERROR,High,Invalid device handle
2516,SPI,WARNING,Medium,Zero length transfer
2517,SPI,ERROR,High,Failed to transmit SPI data: %s
2518,SPI,ERROR,High,Invalid device handle
2519,SPI,ERROR,High,Failed to transmit SPI data with cmd/addr: %s
1 ID Component Level Criticality Message
2 2500 SPI INFO Low SPI wrapper initialized
3 2501 SPI INFO Low SPI wrapper destroyed
4 2502 SPI WARNING Medium SPI host %d already initialized
5 2503 SPI ERROR High Failed to initialize SPI bus %d: %s
6 2504 SPI INFO Low SPI initialized successfully
7 2505 SPI WARNING Medium SPI host %d not initialized
8 2506 SPI ERROR High Failed to free SPI bus %d: %s
9 2507 SPI INFO Low SPI bus %d deinitialized
10 2508 SPI ERROR High SPI host %d not initialized
11 2509 SPI ERROR High Invalid device handle pointer
12 2510 SPI ERROR High Failed to add SPI device to bus %d: %s
13 2511 SPI INFO Low SPI device added to bus %d successfully
14 2512 SPI ERROR High Invalid device handle
15 2513 SPI ERROR High Failed to remove SPI device: %s
16 2514 SPI INFO Low SPI device removed successfully
17 2515 SPI ERROR High Invalid device handle
18 2516 SPI WARNING Medium Zero length transfer
19 2517 SPI ERROR High Failed to transmit SPI data: %s
20 2518 SPI ERROR High Invalid device handle
21 2519 SPI ERROR High Failed to transmit SPI data with cmd/addr: %s

View File

@@ -1,34 +0,0 @@
import sys
import os
import time
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
if folder_path not in sys.path:
sys.path.append(folder_path)
from scan_serial import ESP32Runner
def test_spi_initialize():
runner = ESP32Runner(mode="SIM", port="COM9")
runner.start()
print("--- QEMU Runner Started ---", flush=True)
try:
start_time = time.time()
while time.time() - start_time < 30:
line = runner.get_line(timeout=1.0)
if line:
print(line, flush=True)
if "SPI initialized successfully" in line:
print("SUCCESS CRITERIA MET!", flush=True)
return 0
if runner.process.poll() is not None:
print(f"Process exited with code: {runner.process.returncode}", flush=True)
return 1
finally:
runner.stop()
print("Done.", flush=True)
return 1
if __name__ == "__main__":
exit_code = test_spi_initialize()
sys.exit(exit_code)

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<test_scenario>
<!-- The configuration for the test environment. -->
<!-- Available configurations: SIMULATE, HIL -->
<config>SIMULATE</config>
<test_case>
<test_case_id>SPI_INIT_TEST</test_case_id>
<!-- The main command that executes the test itself. -->
<test_exec>python components/ESP_IDF_FW_wrappers/spi/test/spi_init_test.py</test_exec>
</test_case>
</test_scenario>

View File

@@ -1,31 +0,0 @@
/**
* @file test_spi.cpp
* @brief Unit tests for SPI wrapper component
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "unity.h"
#include "spi.hpp"
extern "C" {
void setUp(void)
{
}
void tearDown(void)
{
}
void test_spi_initialize(void)
{
Spi spi;
SpiConfig config = {23, 19, 18, 5, 1000000, SpiMode::MODE_0};
bool result = spi.initialize(SpiHost::SPI2_HOST, config);
TEST_ASSERT_TRUE(result);
}
} // extern "C"

View File

@@ -1,5 +0,0 @@
idf_component_register(
SRCS "com/uart.cpp"
INCLUDE_DIRS "com"
REQUIRES esp_driver_uart logger
)

View File

@@ -1,222 +0,0 @@
# UART Wrapper Module
## Overview
The UART wrapper module provides a C++ object-oriented interface for ESP-IDF UART functionality. This module encapsulates the ESP-IDF UART driver functions and provides a clean, easy-to-use API for serial communication.
## Features
- **Multi-Port Support**: Support for UART0, UART1, and UART2
- **Configurable Parameters**: Baudrate, data bits, parity, stop bits, flow control
- **Buffer Management**: Configurable TX/RX buffer sizes
- **Timeout Support**: Configurable timeouts for read/write operations
- **Flow Control**: Hardware flow control support (RTS/CTS)
- **Error Handling**: Comprehensive error checking and logging
## Architecture
### Class Diagram
```
┌─────────────────────────────────────┐
│ Uart │
├─────────────────────────────────────┤
│ - m_isInitialized_[3]: bool │
├─────────────────────────────────────┤
│ + Uart() │
│ + ~Uart() │
│ + initialize(port, config): bool │
│ + deinitialize(port): bool │
│ + transmit(...): int32_t │
│ + receive(...): int32_t │
│ + getBytesAvailable(port): int32_t │
│ + flushTx(port): bool │
│ + flushRx(port): bool │
│ + setBaudrate(port, baud): bool │
│ + isInitialized(port): bool │
│ + getDefaultConfig(): UartConfig │
│ - convertPort(port): uart_port_t │
│ - convertConfig(cfg): uart_config_t │
└─────────────────────────────────────┘
```
### Enumerations
#### UartPort
- `PORT_0`: UART port 0
- `PORT_1`: UART port 1
- `PORT_2`: UART port 2
#### UartBaudrate
- `BAUD_9600`: 9600 bps
- `BAUD_19200`: 19200 bps
- `BAUD_38400`: 38400 bps
- `BAUD_57600`: 57600 bps
- `BAUD_115200`: 115200 bps
- `BAUD_230400`: 230400 bps
- `BAUD_460800`: 460800 bps
- `BAUD_921600`: 921600 bps
#### UartDataBits
- `DATA_5_BITS`: 5 data bits
- `DATA_6_BITS`: 6 data bits
- `DATA_7_BITS`: 7 data bits
- `DATA_8_BITS`: 8 data bits
#### UartParity
- `PARITY_DISABLE`: No parity
- `PARITY_EVEN`: Even parity
- `PARITY_ODD`: Odd parity
#### UartStopBits
- `STOP_BITS_1`: 1 stop bit
- `STOP_BITS_1_5`: 1.5 stop bits
- `STOP_BITS_2`: 2 stop bits
#### UartFlowControl
- `FLOW_CTRL_DISABLE`: No flow control
- `FLOW_CTRL_RTS`: RTS flow control
- `FLOW_CTRL_CTS`: CTS flow control
- `FLOW_CTRL_CTS_RTS`: RTS/CTS flow control
### Configuration Structure
```cpp
struct UartConfig {
UartBaudrate baudrate; // UART baudrate
uint32_t txPin; // TX pin number
uint32_t rxPin; // RX pin number
uint32_t rtsPin; // RTS pin number (optional)
uint32_t ctsPin; // CTS pin number (optional)
UartDataBits dataBits; // Number of data bits
UartParity parity; // Parity setting
UartStopBits stopBits; // Number of stop bits
UartFlowControl flowControl; // Flow control setting
uint32_t rxBufferSize; // RX buffer size
uint32_t txBufferSize; // TX buffer size
uint32_t eventQueueSize; // Event queue size
};
```
## Usage Examples
### Basic UART Communication
```cpp
#include "uart.hpp"
// Create UART instance
Uart uart;
// Get default configuration
UartConfig config = Uart::getDefaultConfig();
config.baudrate = UartBaudrate::BAUD_115200;
config.txPin = 1;
config.rxPin = 3;
// Initialize UART
uart.initialize(UartPort::PORT_0, config);
// Transmit data
const char* message = "Hello World!";
uart.transmit(UartPort::PORT_0, (uint8_t*)message, strlen(message));
// Receive data
uint8_t buffer[100];
int32_t bytesReceived = uart.receive(UartPort::PORT_0, buffer, sizeof(buffer), 1000);
```
### Advanced Configuration
```cpp
// Configure UART with flow control
UartConfig config = {};
config.baudrate = UartBaudrate::BAUD_921600;
config.txPin = 4;
config.rxPin = 5;
config.rtsPin = 18;
config.ctsPin = 19;
config.dataBits = UartDataBits::DATA_8_BITS;
config.parity = UartParity::PARITY_EVEN;
config.stopBits = UartStopBits::STOP_BITS_2;
config.flowControl = UartFlowControl::FLOW_CTRL_CTS_RTS;
config.rxBufferSize = 2048;
config.txBufferSize = 2048;
uart.initialize(UartPort::PORT_1, config);
```
### Buffer Management
```cpp
// Check available bytes
int32_t available = uart.getBytesAvailable(UartPort::PORT_0);
// Flush buffers
uart.flushRx(UartPort::PORT_0);
uart.flushTx(UartPort::PORT_0);
// Change baudrate at runtime
uart.setBaudrate(UartPort::PORT_0, UartBaudrate::BAUD_460800);
```
## API Reference
### Constructor/Destructor
- **Uart()**: Initialize UART wrapper instance
- **~Uart()**: Clean up resources and deinitialize all ports
### Configuration Methods
- **initialize(port, config)**: Initialize UART port with configuration
- **deinitialize(port)**: Deinitialize UART port
- **isInitialized(port)**: Check if port is initialized
- **getDefaultConfig()**: Get default configuration structure
### Communication Methods
- **transmit(port, data, length, timeout)**: Transmit data with timeout
- **receive(port, buffer, maxLength, timeout)**: Receive data with timeout
- **getBytesAvailable(port)**: Get number of bytes in RX buffer
### Buffer Management
- **flushTx(port)**: Flush TX buffer
- **flushRx(port)**: Flush RX buffer
### Runtime Configuration
- **setBaudrate(port, baudrate)**: Change baudrate at runtime
## Error Handling
The module provides comprehensive error handling:
- Port validation and initialization checks
- ESP-IDF error codes are caught and logged
- Return values indicate success/failure for all operations
- Detailed logging for debugging and troubleshooting
## Dependencies
- ESP-IDF UART driver (`driver/uart.h`)
- ESP-IDF error handling (`esp_err.h`)
- ESP-IDF logging (`esp_log.h`)
- FreeRTOS (`freertos/FreeRTOS.h`, `freertos/task.h`)
## Thread Safety
The UART wrapper uses ESP-IDF's thread-safe UART driver. Multiple tasks can safely use different UART ports simultaneously.
## Memory Usage
- Fixed memory footprint per instance
- Configurable buffer sizes for each port
- No dynamic memory allocation in wrapper layer
## Performance Considerations
- Direct ESP-IDF function calls for optimal performance
- Minimal overhead over raw ESP-IDF calls
- Configurable buffer sizes for throughput optimization
- Hardware flow control support for high-speed communication

View File

@@ -1,293 +0,0 @@
/**
* @file uart.cpp
* @brief UART wrapper component implementation
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "uart.hpp"
#include "logger.hpp"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <cstring>
#include <inttypes.h>
static const char* TAG = "UART_WRAPPER";
/**
* @brief Constructor - Initialize UART wrapper instance
* @details Initializes all UART ports as not initialized
*/
Uart::Uart()
{
m_isInitialized_[0] = false;
m_isInitialized_[1] = false;
m_isInitialized_[2] = false;
ASF_LOGI(TAG, 2600, asf::logger::Criticality::LOW, "UART wrapper initialized");
}
/**
* @brief Destructor - Clean up UART wrapper resources
* @details Deinitializes all active UART ports
*/
Uart::~Uart()
{
// Deinitialize all ports
for (int i = 0; i < 3; i++) {
if (m_isInitialized_[i]) {
deinitialize(static_cast<UartPort>(i));
}
}
ASF_LOGI(TAG, 2601, asf::logger::Criticality::LOW, "UART wrapper destroyed");
}
bool Uart::initialize(UartPort port, const UartConfig& config)
{
uint8_t portIdx = static_cast<uint8_t>(port);
uart_port_t uartPort = convertPort(port);
if (m_isInitialized_[portIdx]) {
ASF_LOGW(TAG, 2602, asf::logger::Criticality::MEDIUM, "UART port %d already initialized", portIdx);
return true;
}
// Configure UART parameters
uart_config_t uartConfig = convertConfig(config);
esp_err_t ret = uart_param_config(uartPort, &uartConfig);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2603, asf::logger::Criticality::HIGH, "Failed to configure UART port %d: %s", portIdx, esp_err_to_name(ret));
return false;
}
// Set UART pins
ret = uart_set_pin(uartPort, config.txPin, config.rxPin,
config.rtsPin, config.ctsPin);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2604, asf::logger::Criticality::HIGH, "Failed to set UART pins for port %d: %s", portIdx, esp_err_to_name(ret));
return false;
}
// Install UART driver
ret = uart_driver_install(uartPort, config.rxBufferSize, config.txBufferSize,
config.eventQueueSize, nullptr, 0);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2605, asf::logger::Criticality::HIGH, "Failed to install UART driver for port %d: %s", portIdx, esp_err_to_name(ret));
return false;
}
m_isInitialized_[portIdx] = true;
ASF_LOGI(TAG, 2606, asf::logger::Criticality::LOW, "UART initialized successfully");
return true;
}
bool Uart::deinitialize(UartPort port)
{
uint8_t portIdx = static_cast<uint8_t>(port);
uart_port_t uartPort = convertPort(port);
if (!m_isInitialized_[portIdx]) {
ASF_LOGW(TAG, 2607, asf::logger::Criticality::MEDIUM, "UART port %d not initialized", portIdx);
return true;
}
esp_err_t ret = uart_driver_delete(uartPort);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2608, asf::logger::Criticality::HIGH, "Failed to delete UART driver for port %d: %s", portIdx, esp_err_to_name(ret));
return false;
}
m_isInitialized_[portIdx] = false;
ASF_LOGI(TAG, 2609, asf::logger::Criticality::LOW, "UART port %d deinitialized", portIdx);
return true;
}
int32_t Uart::transmit(UartPort port, const uint8_t* data, size_t length, uint32_t timeoutMs)
{
uint8_t portIdx = static_cast<uint8_t>(port);
uart_port_t uartPort = convertPort(port);
if (!m_isInitialized_[portIdx]) {
ASF_LOGE(TAG, 2610, asf::logger::Criticality::HIGH, "UART port %d not initialized", portIdx);
return -1;
}
if (data == nullptr || length == 0) {
ASF_LOGE(TAG, 2611, asf::logger::Criticality::HIGH, "Invalid data or length");
return -1;
}
TickType_t timeout = pdMS_TO_TICKS(timeoutMs);
int bytesWritten = uart_write_bytes(uartPort, data, length);
if (bytesWritten < 0) {
ASF_LOGE(TAG, 2612, asf::logger::Criticality::HIGH, "Failed to write to UART port %d", portIdx);
return -1;
}
// Wait for transmission to complete
esp_err_t ret = uart_wait_tx_done(uartPort, timeout);
if (ret != ESP_OK) {
ASF_LOGW(TAG, 2613, asf::logger::Criticality::MEDIUM, "TX timeout for UART port %d", portIdx);
}
return bytesWritten;
}
int32_t Uart::receive(UartPort port, uint8_t* data, size_t maxLength, uint32_t timeoutMs)
{
uint8_t portIdx = static_cast<uint8_t>(port);
uart_port_t uartPort = convertPort(port);
if (!m_isInitialized_[portIdx]) {
ASF_LOGE(TAG, 2614, asf::logger::Criticality::HIGH, "UART port %d not initialized", portIdx);
return -1;
}
if (data == nullptr || maxLength == 0) {
ASF_LOGE(TAG, 2615, asf::logger::Criticality::HIGH, "Invalid data buffer or length");
return -1;
}
TickType_t timeout = pdMS_TO_TICKS(timeoutMs);
int bytesRead = uart_read_bytes(uartPort, data, maxLength, timeout);
if (bytesRead < 0) {
ASF_LOGE(TAG, 2616, asf::logger::Criticality::HIGH, "Failed to read from UART port %d", portIdx);
return -1;
}
return bytesRead;
}
int32_t Uart::getBytesAvailable(UartPort port)
{
uint8_t portIdx = static_cast<uint8_t>(port);
uart_port_t uartPort = convertPort(port);
if (!m_isInitialized_[portIdx]) {
ASF_LOGE(TAG, 2617, asf::logger::Criticality::HIGH, "UART port %d not initialized", portIdx);
return -1;
}
size_t bytesAvailable = 0;
esp_err_t ret = uart_get_buffered_data_len(uartPort, &bytesAvailable);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2618, asf::logger::Criticality::HIGH, "Failed to get buffered data length for port %d: %s",
portIdx, esp_err_to_name(ret));
return -1;
}
return static_cast<int32_t>(bytesAvailable);
}
bool Uart::flushTx(UartPort port)
{
uint8_t portIdx = static_cast<uint8_t>(port);
uart_port_t uartPort = convertPort(port);
if (!m_isInitialized_[portIdx]) {
ASF_LOGE(TAG, 2619, asf::logger::Criticality::HIGH, "UART port %d not initialized", portIdx);
return false;
}
esp_err_t ret = uart_flush_input(uartPort);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2620, asf::logger::Criticality::HIGH, "Failed to flush TX for port %d: %s", portIdx, esp_err_to_name(ret));
return false;
}
return true;
}
bool Uart::flushRx(UartPort port)
{
uint8_t portIdx = static_cast<uint8_t>(port);
uart_port_t uartPort = convertPort(port);
if (!m_isInitialized_[portIdx]) {
ASF_LOGE(TAG, 2621, asf::logger::Criticality::HIGH, "UART port %d not initialized", portIdx);
return false;
}
esp_err_t ret = uart_flush_input(uartPort);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2622, asf::logger::Criticality::HIGH, "Failed to flush RX for port %d: %s", portIdx, esp_err_to_name(ret));
return false;
}
return true;
}
bool Uart::setBaudrate(UartPort port, UartBaudrate baudrate)
{
uint8_t portIdx = static_cast<uint8_t>(port);
uart_port_t uartPort = convertPort(port);
if (!m_isInitialized_[portIdx]) {
ASF_LOGE(TAG, 2623, asf::logger::Criticality::HIGH, "UART port %d not initialized", portIdx);
return false;
}
esp_err_t ret = uart_set_baudrate(uartPort, static_cast<uint32_t>(baudrate));
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2624, asf::logger::Criticality::HIGH, "Failed to set baudrate for port %d: %s", portIdx, esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2625, asf::logger::Criticality::LOW, "Baudrate set to %" PRIu32 " for port %d", static_cast<uint32_t>(baudrate), portIdx);
return true;
}
bool Uart::isInitialized(UartPort port) const
{
uint8_t portIdx = static_cast<uint8_t>(port);
return m_isInitialized_[portIdx];
}
UartConfig Uart::getDefaultConfig()
{
UartConfig config = {};
config.baudrate = UartBaudrate::BAUD_115200;
config.txPin = UART_PIN_NO_CHANGE;
config.rxPin = UART_PIN_NO_CHANGE;
config.rtsPin = UART_PIN_NO_CHANGE;
config.ctsPin = UART_PIN_NO_CHANGE;
config.dataBits = UartDataBits::DATA_8_BITS;
config.parity = UartParity::PARITY_DISABLE;
config.stopBits = UartStopBits::STOP_BITS_1;
config.flowControl = UartFlowControl::FLOW_CTRL_DISABLE;
config.rxBufferSize = 1024;
config.txBufferSize = 1024;
config.eventQueueSize = 10;
return config;
}
uart_port_t Uart::convertPort(UartPort port)
{
switch (port) {
case UartPort::PORT_0:
return UART_NUM_0;
case UartPort::PORT_1:
return UART_NUM_1;
case UartPort::PORT_2:
return UART_NUM_2;
default:
return UART_NUM_0;
}
}
uart_config_t Uart::convertConfig(const UartConfig& config)
{
uart_config_t uartConfig = {};
uartConfig.baud_rate = static_cast<int>(config.baudrate);
uartConfig.data_bits = static_cast<uart_word_length_t>(config.dataBits);
uartConfig.parity = static_cast<uart_parity_t>(config.parity);
uartConfig.stop_bits = static_cast<uart_stop_bits_t>(config.stopBits);
uartConfig.flow_ctrl = static_cast<uart_hw_flowcontrol_t>(config.flowControl);
uartConfig.rx_flow_ctrl_thresh = 122;
uartConfig.source_clk = UART_SCLK_DEFAULT;
return uartConfig;
}

View File

@@ -1,220 +0,0 @@
/**
* @file uart.hpp
* @brief UART wrapper component header - Wrapper for ESP-IDF UART functionality
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#ifndef UART_HPP
#define UART_HPP
#include <cstdint>
#include "driver/uart.h"
#include "esp_err.h"
/**
* @brief UART port enumeration
*/
enum class UartPort
{
PORT_0,
PORT_1,
PORT_2
};
/**
* @brief UART baudrate enumeration
*/
enum class UartBaudrate
{
BAUD_9600 = 9600,
BAUD_19200 = 19200,
BAUD_38400 = 38400,
BAUD_57600 = 57600,
BAUD_115200 = 115200,
BAUD_230400 = 230400,
BAUD_460800 = 460800,
BAUD_921600 = 921600
};
/**
* @brief UART data bits enumeration
*/
enum class UartDataBits
{
DATA_5_BITS = UART_DATA_5_BITS,
DATA_6_BITS = UART_DATA_6_BITS,
DATA_7_BITS = UART_DATA_7_BITS,
DATA_8_BITS = UART_DATA_8_BITS
};
/**
* @brief UART parity enumeration
*/
enum class UartParity
{
PARITY_DISABLE = UART_PARITY_DISABLE,
PARITY_EVEN = UART_PARITY_EVEN,
PARITY_ODD = UART_PARITY_ODD
};
/**
* @brief UART stop bits enumeration
*/
enum class UartStopBits
{
STOP_BITS_1 = UART_STOP_BITS_1,
STOP_BITS_1_5 = UART_STOP_BITS_1_5,
STOP_BITS_2 = UART_STOP_BITS_2
};
/**
* @brief UART flow control enumeration
*/
enum class UartFlowControl
{
FLOW_CTRL_DISABLE = UART_HW_FLOWCTRL_DISABLE,
FLOW_CTRL_RTS = UART_HW_FLOWCTRL_RTS,
FLOW_CTRL_CTS = UART_HW_FLOWCTRL_CTS,
FLOW_CTRL_CTS_RTS = UART_HW_FLOWCTRL_CTS_RTS
};
/**
* @brief UART configuration structure
*/
struct UartConfig
{
UartBaudrate baudrate; ///< UART baudrate
uint32_t txPin; ///< TX pin number
uint32_t rxPin; ///< RX pin number
uint32_t rtsPin; ///< RTS pin number (optional)
uint32_t ctsPin; ///< CTS pin number (optional)
UartDataBits dataBits; ///< Number of data bits
UartParity parity; ///< Parity setting
UartStopBits stopBits; ///< Number of stop bits
UartFlowControl flowControl; ///< Flow control setting
uint32_t rxBufferSize; ///< RX buffer size
uint32_t txBufferSize; ///< TX buffer size
uint32_t eventQueueSize; ///< Event queue size
};
/**
* @brief UART wrapper class
*
* Provides a C++ wrapper for ESP-IDF UART functionality.
* This class encapsulates ESP-IDF UART driver functions in an object-oriented interface.
*/
class Uart
{
public:
/**
* @brief Constructor
* @details Initializes the UART wrapper instance
*/
Uart();
/**
* @brief Destructor
* @details Cleans up resources and deinitializes all UART ports
*/
~Uart();
/**
* @brief Initialize UART port with configuration
* @param port UART port number
* @param config UART configuration parameters
* @return true if initialized successfully, false otherwise
* @note This function configures pins, baudrate, and buffer sizes
*/
bool initialize(UartPort port, const UartConfig& config);
/**
* @brief Deinitialize UART port
* @param port UART port number
* @return true if deinitialized successfully, false otherwise
*/
bool deinitialize(UartPort port);
/**
* @brief Transmit data over UART
* @param port UART port number
* @param data Pointer to data buffer
* @param length Number of bytes to transmit
* @param timeoutMs Timeout in milliseconds (default: 1000ms)
* @return Number of bytes transmitted, or -1 on error
*/
int32_t transmit(UartPort port, const uint8_t* data, size_t length, uint32_t timeoutMs = 1000);
/**
* @brief Receive data from UART
* @param port UART port number
* @param data Pointer to receive buffer
* @param maxLength Maximum number of bytes to receive
* @param timeoutMs Timeout in milliseconds
* @return Number of bytes received, or -1 on error
*/
int32_t receive(UartPort port, uint8_t* data, size_t maxLength, uint32_t timeoutMs);
/**
* @brief Get number of bytes available in RX buffer
* @param port UART port number
* @return Number of bytes available, or -1 on error
*/
int32_t getBytesAvailable(UartPort port);
/**
* @brief Flush UART TX buffer
* @param port UART port number
* @return true if flushed successfully, false otherwise
*/
bool flushTx(UartPort port);
/**
* @brief Flush UART RX buffer
* @param port UART port number
* @return true if flushed successfully, false otherwise
*/
bool flushRx(UartPort port);
/**
* @brief Set UART baudrate
* @param port UART port number
* @param baudrate New baudrate
* @return true if set successfully, false otherwise
*/
bool setBaudrate(UartPort port, UartBaudrate baudrate);
/**
* @brief Check if UART port is initialized
* @param port UART port number
* @return true if initialized, false otherwise
*/
bool isInitialized(UartPort port) const;
/**
* @brief Get default UART configuration
* @return Default UART configuration structure
*/
static UartConfig getDefaultConfig();
private:
bool m_isInitialized_[3]; ///< Initialization status for each port
/**
* @brief Convert UartPort to ESP-IDF uart_port_t
* @param port UART port
* @return ESP-IDF uart_port_t
*/
uart_port_t convertPort(UartPort port);
/**
* @brief Convert UartConfig to ESP-IDF uart_config_t
* @param config UART configuration
* @return ESP-IDF uart_config_t
*/
uart_config_t convertConfig(const UartConfig& config);
};
#endif // UART_HPP

View File

@@ -1,27 +0,0 @@
ID,Component,Level,Criticality,Message
2600,UART,INFO,Low,UART wrapper initialized
2601,UART,INFO,Low,UART wrapper destroyed
2602,UART,WARNING,Medium,UART port %d already initialized
2603,UART,ERROR,High,Failed to configure UART port %d: %s
2604,UART,ERROR,High,Failed to set UART pins for port %d: %s
2605,UART,ERROR,High,Failed to install UART driver for port %d: %s
2606,UART,INFO,Low,UART initialized successfully
2607,UART,WARNING,Medium,UART port %d not initialized
2608,UART,ERROR,High,Failed to delete UART driver for port %d: %s
2609,UART,INFO,Low,UART port %d deinitialized
2610,UART,ERROR,High,UART port %d not initialized
2611,UART,ERROR,High,Invalid data or length
2612,UART,ERROR,High,Failed to write to UART port %d
2613,UART,WARNING,Medium,TX timeout for UART port %d
2614,UART,ERROR,High,UART port %d not initialized
2615,UART,ERROR,High,Invalid data buffer or length
2616,UART,ERROR,High,Failed to read from UART port %d
2617,UART,ERROR,High,UART port %d not initialized
2618,UART,ERROR,High,Failed to get buffered data length for port %d: %s
2619,UART,ERROR,High,UART port %d not initialized
2620,UART,ERROR,High,Failed to flush TX for port %d: %s
2621,UART,ERROR,High,UART port %d not initialized
2622,UART,ERROR,High,Failed to flush RX for port %d: %s
2623,UART,ERROR,High,UART port %d not initialized
2624,UART,ERROR,High,Failed to set baudrate for port %d: %s
2625,UART,INFO,Low,Baudrate set to %" PRIu32 " for port %d
Can't render this file because it contains an unexpected character in line 27 and column 37.

View File

@@ -1,31 +0,0 @@
/**
* @file test_uart.cpp
* @brief Unit tests for UART wrapper component
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "unity.h"
#include "uart.hpp"
extern "C" {
void setUp(void)
{
}
void tearDown(void)
{
}
void test_uart_initialize(void)
{
Uart uart;
UartConfig config = {UartBaudrate::BAUD_115200, 17, 16, 8, 0, 1, 0};
bool result = uart.initialize(UartPort::PORT_0, config);
TEST_ASSERT_TRUE(result);
}
} // extern "C"

View File

@@ -1,34 +0,0 @@
import sys
import os
import time
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
if folder_path not in sys.path:
sys.path.append(folder_path)
from scan_serial import ESP32Runner
def test_UART_initialize():
runner = ESP32Runner(mode="SIM", port="COM9")
runner.start()
print("--- QEMU Runner Started ---", flush=True)
try:
start_time = time.time()
while time.time() - start_time < 30:
line = runner.get_line(timeout=1.0)
if line:
print(line, flush=True)
if "UART initialized successfully" in line:
print("SUCCESS CRITERIA MET!", flush=True)
return 0
if runner.process.poll() is not None:
print(f"Process exited with code: {runner.process.returncode}", flush=True)
return 1
finally:
runner.stop()
print("Done.", flush=True)
return 1
if __name__ == "__main__":
exit_code = test_UART_initialize()
sys.exit(exit_code)

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<test_scenario>
<!-- The configuration for the test environment. -->
<!-- Available configurations: SIMULATE, HIL -->
<config>SIMULATE</config>
<test_case>
<test_case_id>UART_INIT_TEST</test_case_id>
<!-- The main command that executes the test itself. -->
<test_exec>python components/ESP_IDF_FW_wrappers/uart/test/uart_init_test.py</test_exec>
</test_case>
</test_scenario>

View File

@@ -1,5 +0,0 @@
idf_component_register(
SRCS "com/wifi.cpp"
INCLUDE_DIRS "com"
REQUIRES esp_wifi esp_netif nvs_flash logger
)

View File

@@ -1,323 +0,0 @@
# WiFi Wrapper Module
## Overview
The WiFi wrapper module provides a C++ object-oriented interface for ESP-IDF WiFi functionality. This module encapsulates the ESP-IDF WiFi driver functions and provides a clean, easy-to-use API for WiFi station and access point operations.
## Features
- **Multiple Modes**: Station (STA), Access Point (AP), and combined (AP+STA) modes
- **Network Scanning**: Scan for available WiFi networks
- **Event Handling**: Asynchronous event callbacks for connection status
- **Security Support**: Multiple authentication modes (Open, WEP, WPA/WPA2/WPA3)
- **Network Information**: IP address, MAC address, RSSI retrieval
- **AP Management**: Connected stations monitoring in AP mode
- **Error Handling**: Comprehensive error checking and logging
## Architecture
### Class Diagram
```
┌─────────────────────────────────────┐
│ Wifi │
├─────────────────────────────────────┤
│ - m_isInitialized_: bool │
│ - m_connectionStatus_: Status │
│ - m_config_: WifiConfig │
│ - m_netifSta_: esp_netif_t* │
│ - m_netifAp_: esp_netif_t* │
│ - m_eventCallback_: Callback │
│ - m_eventCallbackArg_: void* │
├─────────────────────────────────────┤
│ + Wifi() │
│ + ~Wifi() │
│ + initialize(config): bool │
│ + deinitialize(): bool │
│ + start(): bool │
│ + stop(): bool │
│ + connect(): bool │
│ + disconnect(): bool │
│ + scan(...): int32_t │
│ + getConnectionStatus(): Status │
│ + isConnected(): bool │
│ + getRssi(): int32_t │
│ + getIpAddress(ip, len): bool │
│ + getMacAddress(mac, mode): bool │
│ + setEventCallback(cb, arg): void │
│ + getConnectedStations(): int32_t │
│ + isInitialized(): bool │
│ + getDefaultStaConfig(): StaConfig │
│ + getDefaultApConfig(): ApConfig │
│ - convertMode(mode): wifi_mode_t │
│ - convertAuthMode(auth): wifi_auth │
│ - wifiEventHandler(...): void │
│ - ipEventHandler(...): void │
└─────────────────────────────────────┘
```
### Enumerations
#### WifiMode
- `NONE`: WiFi disabled
- `STA`: Station mode (client)
- `AP`: Access Point mode (server)
- `APSTA`: Combined AP + STA mode
#### WifiAuthMode
- `OPEN`: No authentication
- `WEP`: WEP encryption
- `WPA_PSK`: WPA with pre-shared key
- `WPA2_PSK`: WPA2 with pre-shared key
- `WPA_WPA2_PSK`: WPA/WPA2 mixed mode
- `WPA3_PSK`: WPA3 with pre-shared key
- `WPA2_WPA3_PSK`: WPA2/WPA3 mixed mode
#### WifiConnectionStatus
- `DISCONNECTED`: Not connected
- `CONNECTING`: Connection in progress
- `CONNECTED`: Successfully connected
- `FAILED`: Connection failed
### Configuration Structures
#### WifiStaConfig
```cpp
struct WifiStaConfig {
char ssid[32]; // SSID of target AP
char password[64]; // Password of target AP
WifiAuthMode authMode; // Authentication mode
bool pmfRequired; // Protected Management Frame required
uint8_t channel; // Channel of target AP (0 = auto)
int8_t rssiThreshold; // Minimum RSSI threshold
};
```
#### WifiApConfig
```cpp
struct WifiApConfig {
char ssid[32]; // SSID of AP
char password[64]; // Password of AP
WifiAuthMode authMode; // Authentication mode
uint8_t channel; // Channel number
uint8_t maxConnections; // Maximum number of connections
bool ssidHidden; // Hide SSID
};
```
#### WifiConfig
```cpp
struct WifiConfig {
WifiMode mode; // WiFi mode
WifiStaConfig staConfig; // Station configuration
WifiApConfig apConfig; // Access Point configuration
};
```
## Usage Examples
### Station Mode (Client)
```cpp
#include "wifi.hpp"
// WiFi event callback
void wifiEventCallback(WifiConnectionStatus status, void* arg) {
switch (status) {
case WifiConnectionStatus::CONNECTED:
printf("WiFi connected!\n");
break;
case WifiConnectionStatus::DISCONNECTED:
printf("WiFi disconnected!\n");
break;
default:
break;
}
}
// Create WiFi instance
Wifi wifi;
// Configure WiFi
WifiConfig config = {};
config.mode = WifiMode::STA;
config.staConfig = Wifi::getDefaultStaConfig();
strcpy(config.staConfig.ssid, "MyWiFiNetwork");
strcpy(config.staConfig.password, "MyPassword");
config.staConfig.authMode = WifiAuthMode::WPA2_PSK;
// Set event callback
wifi.setEventCallback(wifiEventCallback, nullptr);
// Initialize and start WiFi
wifi.initialize(config);
wifi.start();
wifi.connect();
// Get IP address when connected
char ip[16];
if (wifi.getIpAddress(ip, sizeof(ip))) {
printf("IP Address: %s\n", ip);
}
```
### Access Point Mode (Server)
```cpp
// Configure as Access Point
WifiConfig apConfig = {};
apConfig.mode = WifiMode::AP;
apConfig.apConfig = Wifi::getDefaultApConfig();
strcpy(apConfig.apConfig.ssid, "ESP32-Hotspot");
strcpy(apConfig.apConfig.password, "12345678");
apConfig.apConfig.authMode = WifiAuthMode::WPA2_PSK;
apConfig.apConfig.channel = 6;
apConfig.apConfig.maxConnections = 4;
wifi.initialize(apConfig);
wifi.start();
// Monitor connected stations
int32_t connectedStations = wifi.getConnectedStations();
printf("Connected stations: %ld\n", connectedStations);
```
### Network Scanning
```cpp
// Scan for available networks
WifiScanResult results[20];
int32_t networkCount = wifi.scan(results, 20, 5000); // 5 second scan
printf("Found %ld networks:\n", networkCount);
for (int i = 0; i < networkCount; i++) {
printf("SSID: %s, RSSI: %d dBm, Channel: %d\n",
results[i].ssid, results[i].rssi, results[i].channel);
}
```
### Combined AP+STA Mode
```cpp
WifiConfig combinedConfig = {};
combinedConfig.mode = WifiMode::APSTA;
// Configure STA
combinedConfig.staConfig = Wifi::getDefaultStaConfig();
strcpy(combinedConfig.staConfig.ssid, "InternetRouter");
strcpy(combinedConfig.staConfig.password, "RouterPassword");
// Configure AP
combinedConfig.apConfig = Wifi::getDefaultApConfig();
strcpy(combinedConfig.apConfig.ssid, "ESP32-Bridge");
strcpy(combinedConfig.apConfig.password, "BridgePassword");
wifi.initialize(combinedConfig);
wifi.start();
wifi.connect(); // Connect to internet router
```
## API Reference
### Constructor/Destructor
- **Wifi()**: Initialize WiFi wrapper instance
- **~Wifi()**: Clean up resources and deinitialize WiFi
### Initialization Methods
- **initialize(config)**: Initialize WiFi with configuration
- **deinitialize()**: Deinitialize WiFi and clean up resources
- **isInitialized()**: Check if WiFi is initialized
### Control Methods
- **start()**: Start WiFi (required after initialization)
- **stop()**: Stop WiFi operations
- **connect()**: Connect to network (STA mode)
- **disconnect()**: Disconnect from network
### Information Methods
- **getConnectionStatus()**: Get current connection status
- **isConnected()**: Check if connected to network
- **getRssi()**: Get signal strength of current connection
- **getIpAddress(ip, maxLen)**: Get IP address string
- **getMacAddress(mac, mode)**: Get MAC address
### Scanning Methods
- **scan(results, maxResults, scanTimeMs)**: Scan for available networks
### Event Handling
- **setEventCallback(callback, arg)**: Set connection event callback
### Access Point Methods
- **getConnectedStations()**: Get number of connected stations (AP mode)
### Configuration Methods
- **getDefaultStaConfig()**: Get default station configuration
- **getDefaultApConfig()**: Get default AP configuration
## Error Handling
The module provides comprehensive error handling:
- Initialization status checks
- ESP-IDF error codes are caught and logged
- Return values indicate success/failure for all operations
- Event callbacks for asynchronous status updates
- Detailed logging for debugging and troubleshooting
## Dependencies
- ESP-IDF WiFi driver (`esp_wifi.h`)
- ESP-IDF network interface (`esp_netif.h`)
- ESP-IDF event system (`esp_event.h`)
- NVS flash storage (`nvs_flash.h`)
- ESP-IDF error handling (`esp_err.h`)
- ESP-IDF logging (`esp_log.h`)
## Thread Safety
The WiFi wrapper uses ESP-IDF's thread-safe WiFi driver and event system. Multiple tasks can safely call WiFi functions simultaneously.
## Memory Usage
- Fixed memory footprint per instance
- Network interface handles managed by ESP-IDF
- Event system uses minimal memory for callbacks
## Performance Considerations
- Asynchronous connection process with event callbacks
- Network scanning can take several seconds
- WiFi operations may affect other radio functions (Bluetooth)
- Power management considerations for battery-powered devices
## Security Considerations
- Use WPA2 or WPA3 for secure connections
- Avoid WEP encryption (deprecated and insecure)
- Use strong passwords (minimum 8 characters)
- Consider hiding SSID for AP mode (security through obscurity)
- Enable PMF (Protected Management Frames) when supported
## Power Management
- WiFi can be configured for power saving modes
- AP mode typically consumes more power than STA mode
- Consider WiFi sleep modes for battery-powered applications
- Monitor power consumption in different WiFi modes
## Troubleshooting
### Common Issues
1. **Connection Failures**: Check SSID, password, and authentication mode
2. **Weak Signal**: Monitor RSSI values and consider antenna placement
3. **IP Assignment**: Ensure DHCP is working or configure static IP
4. **Channel Conflicts**: Use WiFi analyzer to find less congested channels
5. **Memory Issues**: Monitor heap usage, especially with many connections

View File

@@ -1,501 +0,0 @@
/**
* @file wifi.cpp
* @brief WiFi wrapper component implementation
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "wifi.hpp"
#include "logger.hpp"
#include "nvs_flash.h"
#include <cstring>
static const char* TAG = "WIFI_WRAPPER";
Wifi::Wifi()
: m_isInitialized_(false)
, m_connectionStatus_(WifiConnectionStatus::DISCONNECTED)
, m_netifSta_(nullptr)
, m_netifAp_(nullptr)
, m_eventCallback_(nullptr)
, m_eventCallbackArg_(nullptr)
{
memset(&m_config_, 0, sizeof(m_config_));
ASF_LOGI(TAG, 2700, asf::logger::Criticality::LOW, "WiFi wrapper initialized");
}
Wifi::~Wifi()
{
deinitialize();
ASF_LOGI(TAG, 2701, asf::logger::Criticality::LOW, "WiFi wrapper destroyed");
}
bool Wifi::initialize(const WifiConfig& config)
{
#ifdef CONFIG_ESP_WIFI_ENABLED
if (m_isInitialized_) {
ASF_LOGW(TAG, 2702, asf::logger::Criticality::MEDIUM, "WiFi already initialized");
return true;
}
// Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// Initialize TCP/IP stack
ESP_ERROR_CHECK(esp_netif_init());
// Create default event loop
ESP_ERROR_CHECK(esp_event_loop_create_default());
// Create network interfaces
if (config.mode == WifiMode::STA || config.mode == WifiMode::APSTA) {
m_netifSta_ = esp_netif_create_default_wifi_sta();
}
if (config.mode == WifiMode::AP || config.mode == WifiMode::APSTA) {
m_netifAp_ = esp_netif_create_default_wifi_ap();
}
// Initialize WiFi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// Register event handlers
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
&wifiEventHandler, this));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
&ipEventHandler, this));
// Set WiFi mode
ESP_ERROR_CHECK(esp_wifi_set_mode(convertMode(config.mode)));
// Configure WiFi
if (config.mode == WifiMode::STA || config.mode == WifiMode::APSTA) {
wifi_config_t staConfig = {};
strncpy((char*)staConfig.sta.ssid, config.staConfig.ssid, sizeof(staConfig.sta.ssid) - 1);
strncpy((char*)staConfig.sta.password, config.staConfig.password, sizeof(staConfig.sta.password) - 1);
staConfig.sta.threshold.authmode = convertAuthMode(config.staConfig.authMode);
staConfig.sta.pmf_cfg.required = config.staConfig.pmfRequired;
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &staConfig));
}
if (config.mode == WifiMode::AP || config.mode == WifiMode::APSTA) {
wifi_config_t apConfig = {};
strncpy((char*)apConfig.ap.ssid, config.apConfig.ssid, sizeof(apConfig.ap.ssid) - 1);
strncpy((char*)apConfig.ap.password, config.apConfig.password, sizeof(apConfig.ap.password) - 1);
apConfig.ap.ssid_len = strlen(config.apConfig.ssid);
apConfig.ap.channel = config.apConfig.channel;
apConfig.ap.max_connection = config.apConfig.maxConnections;
apConfig.ap.authmode = convertAuthMode(config.apConfig.authMode);
apConfig.ap.ssid_hidden = config.apConfig.ssidHidden ? 1 : 0;
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &apConfig));
}
m_config_ = config;
m_isInitialized_ = true;
ASF_LOGI(TAG, 2703, asf::logger::Criticality::LOW, "WiFi initialized successfully");
return true;
#else
ASF_LOGW(TAG, 2704, asf::logger::Criticality::MEDIUM, "WiFi disabled in sdkconfig");
return false;
#endif
}
bool Wifi::deinitialize()
{
#ifdef CONFIG_ESP_WIFI_ENABLED
if (!m_isInitialized_) {
ASF_LOGW(TAG, 2705, asf::logger::Criticality::MEDIUM, "WiFi not initialized");
return true;
}
stop();
// Unregister event handlers
esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifiEventHandler);
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &ipEventHandler);
// Deinitialize WiFi
esp_wifi_deinit();
// Destroy network interfaces
if (m_netifSta_) {
esp_netif_destroy_default_wifi(m_netifSta_);
m_netifSta_ = nullptr;
}
if (m_netifAp_) {
esp_netif_destroy_default_wifi(m_netifAp_);
m_netifAp_ = nullptr;
}
m_isInitialized_ = false;
m_connectionStatus_ = WifiConnectionStatus::DISCONNECTED;
ASF_LOGI(TAG, 2706, asf::logger::Criticality::LOW, "WiFi deinitialized");
return true;
#else
return true;
#endif
}
bool Wifi::start()
{
#ifdef CONFIG_ESP_WIFI_ENABLED
if (!m_isInitialized_) {
ASF_LOGE(TAG, 2707, asf::logger::Criticality::HIGH, "WiFi not initialized");
return false;
}
esp_err_t ret = esp_wifi_start();
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2708, asf::logger::Criticality::HIGH, "Failed to start WiFi: %s", esp_err_to_name(ret));
return false;
}
ASF_LOGI(TAG, 2709, asf::logger::Criticality::LOW, "WiFi started");
return true;
#else
return false;
#endif
}
bool Wifi::stop()
{
#ifdef CONFIG_ESP_WIFI_ENABLED
if (!m_isInitialized_) {
ASF_LOGW(TAG, 2710, asf::logger::Criticality::MEDIUM, "WiFi not initialized");
return true;
}
esp_err_t ret = esp_wifi_stop();
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2711, asf::logger::Criticality::HIGH, "Failed to stop WiFi: %s", esp_err_to_name(ret));
return false;
}
m_connectionStatus_ = WifiConnectionStatus::DISCONNECTED;
ASF_LOGI(TAG, 2712, asf::logger::Criticality::LOW, "WiFi stopped");
return true;
#else
return true;
#endif
}
bool Wifi::connect()
{
#ifdef CONFIG_ESP_WIFI_ENABLED
if (!m_isInitialized_) {
ASF_LOGE(TAG, 2713, asf::logger::Criticality::HIGH, "WiFi not initialized");
return false;
}
if (m_config_.mode != WifiMode::STA && m_config_.mode != WifiMode::APSTA) {
ASF_LOGE(TAG, 2714, asf::logger::Criticality::HIGH, "WiFi not in STA mode");
return false;
}
m_connectionStatus_ = WifiConnectionStatus::CONNECTING;
esp_err_t ret = esp_wifi_connect();
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2715, asf::logger::Criticality::HIGH, "Failed to connect to WiFi: %s", esp_err_to_name(ret));
m_connectionStatus_ = WifiConnectionStatus::FAILED;
return false;
}
ASF_LOGI(TAG, 2716, asf::logger::Criticality::LOW, "WiFi connection initiated");
return true;
#else
return false;
#endif
}
bool Wifi::disconnect()
{
#ifdef CONFIG_ESP_WIFI_ENABLED
if (!m_isInitialized_) {
ASF_LOGW(TAG, 2717, asf::logger::Criticality::MEDIUM, "WiFi not initialized");
return true;
}
esp_err_t ret = esp_wifi_disconnect();
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2718, asf::logger::Criticality::HIGH, "Failed to disconnect WiFi: %s", esp_err_to_name(ret));
return false;
}
m_connectionStatus_ = WifiConnectionStatus::DISCONNECTED;
ASF_LOGI(TAG, 2719, asf::logger::Criticality::LOW, "WiFi disconnected");
return true;
#else
return true;
#endif
}
int32_t Wifi::scan(WifiScanResult* results, size_t maxResults, uint32_t scanTimeMs)
{
#ifdef CONFIG_ESP_WIFI_ENABLED
if (!m_isInitialized_ || results == nullptr || maxResults == 0) {
ASF_LOGE(TAG, 2720, asf::logger::Criticality::HIGH, "Invalid scan parameters");
return -1;
}
wifi_scan_config_t scanConfig = {};
scanConfig.ssid = nullptr;
scanConfig.bssid = nullptr;
scanConfig.channel = 0;
scanConfig.show_hidden = true;
scanConfig.scan_type = WIFI_SCAN_TYPE_ACTIVE;
scanConfig.scan_time.active.min = scanTimeMs / 2;
scanConfig.scan_time.active.max = scanTimeMs;
esp_err_t ret = esp_wifi_scan_start(&scanConfig, true);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2721, asf::logger::Criticality::HIGH, "Failed to start WiFi scan: %s", esp_err_to_name(ret));
return -1;
}
uint16_t apCount = 0;
esp_wifi_scan_get_ap_num(&apCount);
if (apCount == 0) {
ASF_LOGI(TAG, 2722, asf::logger::Criticality::LOW, "No access points found");
return 0;
}
uint16_t actualCount = (apCount > maxResults) ? maxResults : apCount;
wifi_ap_record_t* apRecords = new wifi_ap_record_t[actualCount];
ret = esp_wifi_scan_get_ap_records(&actualCount, apRecords);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2723, asf::logger::Criticality::HIGH, "Failed to get scan results: %s", esp_err_to_name(ret));
delete[] apRecords;
return -1;
}
// Convert results
for (uint16_t i = 0; i < actualCount; i++) {
strncpy(results[i].ssid, (char*)apRecords[i].ssid, sizeof(results[i].ssid) - 1);
results[i].ssid[sizeof(results[i].ssid) - 1] = '\0';
memcpy(results[i].bssid, apRecords[i].bssid, 6);
results[i].channel = apRecords[i].primary;
results[i].rssi = apRecords[i].rssi;
results[i].authMode = static_cast<WifiAuthMode>(apRecords[i].authmode);
}
delete[] apRecords;
ASF_LOGI(TAG, 2724, asf::logger::Criticality::LOW, "WiFi scan completed, found %d networks", actualCount);
return actualCount;
#else
return -1;
#endif
}
WifiConnectionStatus Wifi::getConnectionStatus() const
{
return m_connectionStatus_;
}
bool Wifi::isConnected() const
{
return m_connectionStatus_ == WifiConnectionStatus::CONNECTED;
}
int32_t Wifi::getRssi()
{
#ifdef CONFIG_ESP_WIFI_ENABLED
if (!isConnected()) {
ASF_LOGW(TAG, 2725, asf::logger::Criticality::MEDIUM, "WiFi not connected");
return 0;
}
wifi_ap_record_t apInfo;
esp_err_t ret = esp_wifi_sta_get_ap_info(&apInfo);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2726, asf::logger::Criticality::HIGH, "Failed to get AP info: %s", esp_err_to_name(ret));
return 0;
}
return apInfo.rssi;
#else
return 0;
#endif
}
bool Wifi::getIpAddress(char* ip, size_t maxLen)
{
#ifdef CONFIG_ESP_WIFI_ENABLED
if (!isConnected() || ip == nullptr || maxLen == 0) {
return false;
}
esp_netif_ip_info_t ipInfo;
esp_err_t ret = esp_netif_get_ip_info(m_netifSta_, &ipInfo);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2727, asf::logger::Criticality::HIGH, "Failed to get IP info: %s", esp_err_to_name(ret));
return false;
}
snprintf(ip, maxLen, IPSTR, IP2STR(&ipInfo.ip));
return true;
#else
return false;
#endif
}
bool Wifi::getMacAddress(uint8_t* mac, WifiMode mode)
{
#ifdef CONFIG_ESP_WIFI_ENABLED
if (mac == nullptr) {
return false;
}
wifi_interface_t interface = (mode == WifiMode::AP) ? WIFI_IF_AP : WIFI_IF_STA;
esp_err_t ret = esp_wifi_get_mac(interface, mac);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2728, asf::logger::Criticality::HIGH, "Failed to get MAC address: %s", esp_err_to_name(ret));
return false;
}
return true;
#else
return false;
#endif
}
void Wifi::setEventCallback(WifiEventCallback callback, void* arg)
{
m_eventCallback_ = callback;
m_eventCallbackArg_ = arg;
}
int32_t Wifi::getConnectedStations()
{
#ifdef CONFIG_ESP_WIFI_ENABLED
if (m_config_.mode != WifiMode::AP && m_config_.mode != WifiMode::APSTA) {
ASF_LOGE(TAG, 2729, asf::logger::Criticality::HIGH, "WiFi not in AP mode");
return -1;
}
wifi_sta_list_t staList;
esp_err_t ret = esp_wifi_ap_get_sta_list(&staList);
if (ret != ESP_OK) {
ASF_LOGE(TAG, 2730, asf::logger::Criticality::HIGH, "Failed to get connected stations: %s", esp_err_to_name(ret));
return -1;
}
return staList.num;
#else
return -1;
#endif
}
bool Wifi::isInitialized() const
{
return m_isInitialized_;
}
WifiStaConfig Wifi::getDefaultStaConfig()
{
WifiStaConfig config = {};
strcpy(config.ssid, "");
strcpy(config.password, "");
config.authMode = WifiAuthMode::WPA2_PSK;
config.pmfRequired = false;
config.channel = 0;
config.rssiThreshold = -127;
return config;
}
WifiApConfig Wifi::getDefaultApConfig()
{
WifiApConfig config = {};
strcpy(config.ssid, "ESP32-AP");
strcpy(config.password, "12345678");
config.authMode = WifiAuthMode::WPA2_PSK;
config.channel = 1;
config.maxConnections = 4;
config.ssidHidden = false;
return config;
}
#ifdef CONFIG_ESP_WIFI_ENABLED
wifi_mode_t Wifi::convertMode(WifiMode mode)
{
switch (mode) {
case WifiMode::NONE:
return WIFI_MODE_NULL;
case WifiMode::STA:
return WIFI_MODE_STA;
case WifiMode::AP:
return WIFI_MODE_AP;
case WifiMode::APSTA:
return WIFI_MODE_APSTA;
default:
return WIFI_MODE_NULL;
}
}
wifi_auth_mode_t Wifi::convertAuthMode(WifiAuthMode authMode)
{
return static_cast<wifi_auth_mode_t>(authMode);
}
void Wifi::wifiEventHandler(void* arg, esp_event_base_t eventBase, int32_t eventId, void* eventData)
{
Wifi* wifi = static_cast<Wifi*>(arg);
switch (eventId) {
case WIFI_EVENT_STA_START:
ASF_LOGI(TAG, 2731, asf::logger::Criticality::LOW, "WiFi STA started");
break;
case WIFI_EVENT_STA_CONNECTED:
ASF_LOGI(TAG, 2732, asf::logger::Criticality::LOW, "WiFi STA connected");
wifi->m_connectionStatus_ = WifiConnectionStatus::CONNECTED;
break;
case WIFI_EVENT_STA_DISCONNECTED:
ASF_LOGI(TAG, 2733, asf::logger::Criticality::LOW, "WiFi STA disconnected");
wifi->m_connectionStatus_ = WifiConnectionStatus::DISCONNECTED;
if (wifi->m_eventCallback_) {
wifi->m_eventCallback_(WifiConnectionStatus::DISCONNECTED, wifi->m_eventCallbackArg_);
}
break;
case WIFI_EVENT_AP_START:
ASF_LOGI(TAG, 2734, asf::logger::Criticality::LOW, "WiFi AP started");
break;
case WIFI_EVENT_AP_STOP:
ASF_LOGI(TAG, 2735, asf::logger::Criticality::LOW, "WiFi AP stopped");
break;
default:
break;
}
}
void Wifi::ipEventHandler(void* arg, esp_event_base_t eventBase, int32_t eventId, void* eventData)
{
Wifi* wifi = static_cast<Wifi*>(arg);
if (eventId == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) eventData;
ASF_LOGI(TAG, 2736, asf::logger::Criticality::LOW, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
wifi->m_connectionStatus_ = WifiConnectionStatus::CONNECTED;
if (wifi->m_eventCallback_) {
wifi->m_eventCallback_(WifiConnectionStatus::CONNECTED, wifi->m_eventCallbackArg_);
}
}
}
#endif

View File

@@ -1,306 +0,0 @@
/**
* @file wifi.hpp
* @brief WiFi wrapper component header - Wrapper for ESP-IDF WiFi functionality
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#ifndef WIFI_HPP
#define WIFI_HPP
#include <cstdint>
#include <cstring>
#include "esp_err.h"
#ifdef CONFIG_ESP_WIFI_ENABLED
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_netif.h"
#endif
/**
* @brief WiFi mode enumeration
*/
enum class WifiMode
{
NONE,
STA, ///< Station mode
AP, ///< Access Point mode
APSTA ///< AP + STA mode
};
/**
* @brief WiFi authentication mode enumeration
*/
enum class WifiAuthMode
{
#ifdef CONFIG_ESP_WIFI_ENABLED
OPEN = WIFI_AUTH_OPEN,
WEP = WIFI_AUTH_WEP,
WPA_PSK = WIFI_AUTH_WPA_PSK,
WPA2_PSK = WIFI_AUTH_WPA2_PSK,
WPA_WPA2_PSK = WIFI_AUTH_WPA_WPA2_PSK,
WPA3_PSK = WIFI_AUTH_WPA3_PSK,
WPA2_WPA3_PSK = WIFI_AUTH_WPA2_WPA3_PSK
#else
OPEN,
WEP,
WPA_PSK,
WPA2_PSK,
WPA_WPA2_PSK,
WPA3_PSK,
WPA2_WPA3_PSK
#endif
};
/**
* @brief WiFi station configuration structure
*/
struct WifiStaConfig
{
char ssid[32]; ///< SSID of target AP
char password[64]; ///< Password of target AP
WifiAuthMode authMode; ///< Authentication mode
bool pmfRequired; ///< Protected Management Frame required
uint8_t channel; ///< Channel of target AP (0 = auto)
int8_t rssiThreshold; ///< Minimum RSSI threshold
};
/**
* @brief WiFi access point configuration structure
*/
struct WifiApConfig
{
char ssid[32]; ///< SSID of AP
char password[64]; ///< Password of AP
WifiAuthMode authMode; ///< Authentication mode
uint8_t channel; ///< Channel number
uint8_t maxConnections; ///< Maximum number of connections
bool ssidHidden; ///< Hide SSID
};
/**
* @brief WiFi configuration structure
*/
struct WifiConfig
{
WifiMode mode; ///< WiFi mode
WifiStaConfig staConfig; ///< Station configuration
WifiApConfig apConfig; ///< Access Point configuration
};
/**
* @brief WiFi connection status enumeration
*/
enum class WifiConnectionStatus
{
DISCONNECTED,
CONNECTING,
CONNECTED,
FAILED
};
/**
* @brief WiFi scan result structure
*/
struct WifiScanResult
{
char ssid[33]; ///< SSID
uint8_t bssid[6]; ///< BSSID
uint8_t channel; ///< Channel
int8_t rssi; ///< RSSI
WifiAuthMode authMode; ///< Authentication mode
};
/**
* @brief WiFi event callback function type
*/
using WifiEventCallback = void (*)(WifiConnectionStatus status, void* arg);
/**
* @brief WiFi wrapper class
*
* Provides a C++ wrapper for ESP-IDF WiFi functionality.
* This class encapsulates ESP-IDF WiFi driver functions in an object-oriented interface.
*/
class Wifi
{
public:
/**
* @brief Constructor
* @details Initializes the WiFi wrapper instance
*/
Wifi();
/**
* @brief Destructor
* @details Cleans up resources and deinitializes WiFi
*/
~Wifi();
/**
* @brief Initialize WiFi with configuration
* @param config WiFi configuration
* @return true if initialized successfully, false otherwise
* @note This function initializes the WiFi stack and event loop
*/
bool initialize(const WifiConfig& config);
/**
* @brief Deinitialize WiFi
* @return true if deinitialized successfully, false otherwise
*/
bool deinitialize();
/**
* @brief Start WiFi (connect in STA mode or start AP)
* @return true if started successfully, false otherwise
*/
bool start();
/**
* @brief Stop WiFi
* @return true if stopped successfully, false otherwise
*/
bool stop();
/**
* @brief Connect to WiFi network (STA mode)
* @return true if connection initiated successfully, false otherwise
* @note This is asynchronous, use event callback to get connection status
*/
bool connect();
/**
* @brief Disconnect from WiFi network
* @return true if disconnected successfully, false otherwise
*/
bool disconnect();
/**
* @brief Scan for available WiFi networks
* @param results Array to store scan results
* @param maxResults Maximum number of results to return
* @param scanTimeMs Scan time in milliseconds
* @return Number of networks found, or -1 on error
*/
int32_t scan(WifiScanResult* results, size_t maxResults, uint32_t scanTimeMs = 3000);
/**
* @brief Get current connection status
* @return Current WiFi connection status
*/
WifiConnectionStatus getConnectionStatus() const;
/**
* @brief Check if WiFi is connected
* @return true if connected, false otherwise
*/
bool isConnected() const;
/**
* @brief Get RSSI of current connection
* @return RSSI in dBm, or 0 if not connected
*/
int32_t getRssi();
/**
* @brief Get IP address (STA mode)
* @param ip Buffer to store IP address string
* @param maxLen Maximum length of IP string
* @return true if IP retrieved successfully, false otherwise
*/
bool getIpAddress(char* ip, size_t maxLen);
/**
* @brief Get MAC address
* @param mac Buffer to store MAC address (6 bytes)
* @param mode WiFi mode to get MAC for
* @return true if MAC retrieved successfully, false otherwise
*/
bool getMacAddress(uint8_t* mac, WifiMode mode);
/**
* @brief Set event callback
* @param callback Callback function
* @param arg Argument passed to callback
*/
void setEventCallback(WifiEventCallback callback, void* arg);
/**
* @brief Get number of connected stations (AP mode)
* @return Number of connected stations, or -1 on error
*/
int32_t getConnectedStations();
/**
* @brief Check if WiFi is initialized
* @return true if initialized, false otherwise
*/
bool isInitialized() const;
/**
* @brief Get default station configuration
* @return Default WiFi station configuration
*/
static WifiStaConfig getDefaultStaConfig();
/**
* @brief Get default access point configuration
* @return Default WiFi access point configuration
*/
static WifiApConfig getDefaultApConfig();
private:
bool m_isInitialized_; ///< Initialization status
WifiConnectionStatus m_connectionStatus_; ///< Current connection status
WifiConfig m_config_; ///< WiFi configuration
#ifdef CONFIG_ESP_WIFI_ENABLED
esp_netif_t* m_netifSta_; ///< Station netif handle
esp_netif_t* m_netifAp_; ///< AP netif handle
#else
void* m_netifSta_;
void* m_netifAp_;
#endif
WifiEventCallback m_eventCallback_; ///< Event callback function
void* m_eventCallbackArg_; ///< Event callback argument
#ifdef CONFIG_ESP_WIFI_ENABLED
/**
* @brief Convert WifiMode to ESP-IDF wifi_mode_t
* @param mode WiFi mode
* @return ESP-IDF wifi_mode_t
*/
wifi_mode_t convertMode(WifiMode mode);
/**
* @brief Convert WifiAuthMode to ESP-IDF wifi_auth_mode_t
* @param authMode Authentication mode
* @return ESP-IDF wifi_auth_mode_t
*/
wifi_auth_mode_t convertAuthMode(WifiAuthMode authMode);
/**
* @brief WiFi event handler
* @param arg Event handler argument
* @param eventBase Event base
* @param eventId Event ID
* @param eventData Event data
*/
static void wifiEventHandler(void* arg, esp_event_base_t eventBase,
int32_t eventId, void* eventData);
/**
* @brief IP event handler
* @param arg Event handler argument
* @param eventBase Event base
* @param eventId Event ID
* @param eventData Event data
*/
static void ipEventHandler(void* arg, esp_event_base_t eventBase,
int32_t eventId, void* eventData);
#endif
};
#endif // WIFI_HPP

View File

@@ -1,38 +0,0 @@
ID,Component,Level,Criticality,Message
2700,WIFI,INFO,Low,WiFi wrapper initialized
2701,WIFI,INFO,Low,WiFi wrapper destroyed
2702,WIFI,WARNING,Medium,WiFi already initialized
2703,WIFI,INFO,Low,WiFi initialized successfully
2704,WIFI,WARNING,Medium,WiFi disabled in sdkconfig
2705,WIFI,WARNING,Medium,WiFi not initialized
2706,WIFI,INFO,Low,WiFi deinitialized
2707,WIFI,ERROR,High,WiFi not initialized
2708,WIFI,ERROR,High,Failed to start WiFi: %s
2709,WIFI,INFO,Low,WiFi started
2710,WIFI,WARNING,Medium,WiFi not initialized
2711,WIFI,ERROR,High,Failed to stop WiFi: %s
2712,WIFI,INFO,Low,WiFi stopped
2713,WIFI,ERROR,High,WiFi not initialized
2714,WIFI,ERROR,High,WiFi not in STA mode
2715,WIFI,ERROR,High,Failed to connect to WiFi: %s
2716,WIFI,INFO,Low,WiFi connection initiated
2717,WIFI,WARNING,Medium,WiFi not initialized
2718,WIFI,ERROR,High,Failed to disconnect WiFi: %s
2719,WIFI,INFO,Low,WiFi disconnected
2720,WIFI,ERROR,High,Invalid scan parameters
2721,WIFI,ERROR,High,Failed to start WiFi scan: %s
2722,WIFI,INFO,Low,No access points found
2723,WIFI,ERROR,High,Failed to get scan results: %s
2724,WIFI,INFO,Low,WiFi scan completed, found %d networks
2725,WIFI,WARNING,Medium,WiFi not connected
2726,WIFI,ERROR,High,Failed to get AP info: %s
2727,WIFI,ERROR,High,Failed to get IP info: %s
2728,WIFI,ERROR,High,Failed to get MAC address: %s
2729,WIFI,ERROR,High,WiFi not in AP mode
2730,WIFI,ERROR,High,Failed to get connected stations: %s
2731,WIFI,INFO,Low,WiFi STA started
2732,WIFI,INFO,Low,WiFi STA connected
2733,WIFI,INFO,Low,WiFi STA disconnected
2734,WIFI,INFO,Low,WiFi AP started
2735,WIFI,INFO,Low,WiFi AP stopped
2736,WIFI,INFO,Low,Got IP: %s
Can't render this file because it has a wrong number of fields in line 26.

View File

@@ -1,42 +0,0 @@
/**
* @file test_wifi.cpp
* @brief Unit tests for WiFi wrapper component
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "unity.h"
#include "wifi.hpp"
extern "C" {
void setUp(void)
{
}
void tearDown(void)
{
}
void test_wifi_initialize(void)
{
Wifi wifi;
WifiConfig config = {"test_ssid", "test_password", WifiMode::STA};
bool result = wifi.initialize(config);
TEST_ASSERT_TRUE(result);
}
void test_wifi_connect(void)
{
Wifi wifi;
WifiConfig config = {"test_ssid", "test_password", WifiMode::STA};
wifi.initialize(config);
bool result = wifi.connect();
TEST_ASSERT_TRUE(result);
TEST_ASSERT_TRUE(wifi.isConnected());
}
} // extern "C"

View File

@@ -1,34 +0,0 @@
import sys
import os
import time
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
if folder_path not in sys.path:
sys.path.append(folder_path)
from scan_serial import ESP32Runner
def test_wifi_initialize():
runner = ESP32Runner(mode="SIM", port="COM9")
runner.start()
print("--- QEMU Runner Started ---", flush=True)
try:
start_time = time.time()
while time.time() - start_time < 30:
line = runner.get_line(timeout=1.0)
if line:
print(line, flush=True)
if "WiFi initialized successfully" in line:
print("SUCCESS CRITERIA MET!", flush=True)
return 0
if runner.process.poll() is not None:
print(f"Process exited with code: {runner.process.returncode}", flush=True)
return 1
finally:
runner.stop()
print("Done.", flush=True)
return 1
if __name__ == "__main__":
exit_code = test_wifi_initialize()
sys.exit(exit_code)

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<test_scenario>
<!-- The configuration for the test environment. -->
<!-- Available configurations: SIMULATE, HIL -->
<config>SIMULATE</config>
<test_case>
<test_case_id>WIFI_INIT_TEST</test_case_id>
<!-- The main command that executes the test itself. -->
<test_exec>python components/ESP_IDF_FW_wrappers/wifi/test/wifi_init_test.py</test_exec>
</test_case>
</test_scenario>

View File

@@ -1,5 +0,0 @@
idf_component_register(
SRCS "com/data_pool.cpp"
INCLUDE_DIRS "com"
REQUIRES logger
)

View File

@@ -1,667 +0,0 @@
# Data Pool Component Specification
**Component ID:** COMP-DATA-POOL
**Version:** 1.0
**Date:** 2025-01-19
**Location:** `application_layer/DP_stack/data_pool/`
## 1. Purpose
The Data Pool component provides centralized, thread-safe storage for runtime system data including latest sensor values, system state, diagnostic information, and configuration data. It serves as the single source of truth for all runtime data and provides fast access for components that need current system information.
## 2. Responsibilities
### 2.1 Primary Responsibilities
- **Runtime Data Storage:** Maintain latest sensor data, system state, and diagnostic information
- **Thread-Safe Access:** Provide concurrent read/write access with appropriate synchronization
- **Data Consistency:** Ensure data integrity across multiple readers and writers
- **Fast Data Access:** Provide low-latency access to frequently accessed data
- **Data Validation:** Validate data integrity and consistency on updates
- **Memory Management:** Efficient memory usage with bounded storage requirements
### 2.2 Non-Responsibilities
- **Data Persistence:** Delegated to Persistence component (long-term storage)
- **Data Processing:** Components handle their own data processing logic
- **Network Communication:** Delegated to Communication components
- **Hardware Access:** No direct hardware interface
## 3. Public API
### 3.1 Sensor Data Management
```c
/**
* @brief Update sensor data record
* @param sensor_id Sensor identifier (0-6)
* @param record Sensor data record to store
* @return true if update successful, false on error
*/
bool dataPool_updateSensorData(uint8_t sensor_id, const sensor_data_record_t* record);
/**
* @brief Get latest sensor data record
* @param sensor_id Sensor identifier (0-6)
* @param record Output buffer for sensor data record
* @return true if data retrieved, false on error
*/
bool dataPool_getSensorData(uint8_t sensor_id, sensor_data_record_t* record);
/**
* @brief Get all sensor data records
* @param records Output buffer for sensor data records
* @param count Input: buffer size, Output: number of records filled
* @return true if data retrieved, false on error
*/
bool dataPool_getAllSensorData(sensor_data_record_t* records, size_t* count);
/**
* @brief Check if sensor data is available and valid
* @param sensor_id Sensor identifier (0-6)
* @return true if valid data available, false otherwise
*/
bool dataPool_isSensorDataValid(uint8_t sensor_id);
/**
* @brief Get timestamp of last sensor data update
* @param sensor_id Sensor identifier (0-6)
* @return Timestamp of last update, 0 if no data available
*/
uint64_t dataPool_getSensorDataTimestamp(uint8_t sensor_id);
```
### 3.2 System State Management
```c
/**
* @brief Update system state information
* @param state_info System state information structure
* @return true if update successful, false on error
*/
bool dataPool_updateSystemState(const system_state_info_t* state_info);
/**
* @brief Get current system state information
* @param state_info Output buffer for system state information
* @return true if data retrieved, false on error
*/
bool dataPool_getSystemState(system_state_info_t* state_info);
/**
* @brief Update system health metrics
* @param health_metrics System health metrics structure
* @return true if update successful, false on error
*/
bool dataPool_updateHealthMetrics(const system_health_metrics_t* health_metrics);
/**
* @brief Get system health metrics
* @param health_metrics Output buffer for health metrics
* @return true if data retrieved, false on error
*/
bool dataPool_getHealthMetrics(system_health_metrics_t* health_metrics);
```
### 3.3 Diagnostic Data Management
```c
/**
* @brief Add diagnostic event to pool
* @param event Diagnostic event structure
* @return true if event added, false on error
*/
bool dataPool_addDiagnosticEvent(const diagnostic_event_t* event);
/**
* @brief Get recent diagnostic events
* @param events Output buffer for diagnostic events
* @param count Input: buffer size, Output: number of events filled
* @return true if events retrieved, false on error
*/
bool dataPool_getRecentDiagnostics(diagnostic_event_t* events, size_t* count);
/**
* @brief Get diagnostic summary (counts by severity)
* @param summary Output buffer for diagnostic summary
* @return true if summary retrieved, false on error
*/
bool dataPool_getDiagnosticSummary(diagnostic_summary_t* summary);
/**
* @brief Clear diagnostic events from pool
* @param severity Severity level to clear (DIAG_SEVERITY_ALL for all)
* @return Number of events cleared
*/
size_t dataPool_clearDiagnostics(diagnostic_severity_t severity);
```
### 3.4 Communication Status Management
```c
/**
* @brief Update communication link status
* @param link_type Communication link type
* @param status Link status information
* @return true if update successful, false on error
*/
bool dataPool_updateLinkStatus(comm_link_type_t link_type, const comm_link_status_t* status);
/**
* @brief Get communication link status
* @param link_type Communication link type
* @param status Output buffer for link status
* @return true if status retrieved, false on error
*/
bool dataPool_getLinkStatus(comm_link_type_t link_type, comm_link_status_t* status);
/**
* @brief Get overall communication status
* @param comm_status Output buffer for communication status
* @return true if status retrieved, false on error
*/
bool dataPool_getCommunicationStatus(communication_status_t* comm_status);
```
### 3.5 Configuration Data Management
```c
/**
* @brief Update runtime configuration
* @param config_type Configuration type
* @param config_data Configuration data
* @param data_size Size of configuration data
* @return true if update successful, false on error
*/
bool dataPool_updateConfiguration(config_type_t config_type, const void* config_data, size_t data_size);
/**
* @brief Get runtime configuration
* @param config_type Configuration type
* @param config_data Output buffer for configuration data
* @param data_size Input: buffer size, Output: actual data size
* @return true if configuration retrieved, false on error
*/
bool dataPool_getConfiguration(config_type_t config_type, void* config_data, size_t* data_size);
/**
* @brief Check if configuration is valid
* @param config_type Configuration type
* @return true if configuration is valid, false otherwise
*/
bool dataPool_isConfigurationValid(config_type_t config_type);
```
### 3.6 Data Pool Management
```c
/**
* @brief Initialize Data Pool component
* @return true if initialization successful, false otherwise
*/
bool dataPool_initialize(void);
/**
* @brief Get Data Pool statistics
* @param stats Output buffer for statistics
* @return true if statistics retrieved, false on error
*/
bool dataPool_getStatistics(data_pool_stats_t* stats);
/**
* @brief Reset Data Pool statistics
* @return true if statistics reset, false on error
*/
bool dataPool_resetStatistics(void);
/**
* @brief Validate Data Pool integrity
* @return true if integrity check passed, false if corruption detected
*/
bool dataPool_validateIntegrity(void);
/**
* @brief Create snapshot of current data pool state
* @param snapshot Output buffer for snapshot
* @return true if snapshot created, false on error
*/
bool dataPool_createSnapshot(data_pool_snapshot_t* snapshot);
```
## 4. Data Types
### 4.1 Sensor Data Record (Extended)
```c
typedef struct {
uint8_t sensor_id; // Sensor identifier (0-6)
sensor_type_t sensor_type; // Type of sensor
float filtered_value; // Processed sensor value
char unit[8]; // Unit of measurement
uint64_t timestamp_ms; // Timestamp in milliseconds
data_validity_t validity; // Data validity status
uint16_t sample_count; // Number of samples used
float raw_min, raw_max; // Min/max of raw samples
float raw_stddev; // Standard deviation
uint32_t acquisition_time_us; // Acquisition time
uint32_t sequence_number; // Monotonic sequence number
uint16_t checksum; // Data integrity checksum
} sensor_data_record_t;
```
### 4.2 System State Information
```c
typedef struct {
system_state_t current_state; // Current system state
system_state_t previous_state; // Previous system state
transition_reason_t last_reason; // Last transition reason
uint64_t state_entry_time; // Time when current state was entered
uint64_t state_duration_ms; // Duration in current state
uint32_t state_transition_count; // Total number of state transitions
bool teardown_in_progress; // Teardown sequence active
uint8_t teardown_progress; // Teardown progress (0-100%)
} system_state_info_t;
```
### 4.3 System Health Metrics
```c
typedef struct {
// CPU and Memory
float cpu_usage_percent; // Current CPU usage
uint32_t free_heap_bytes; // Available heap memory
uint32_t min_free_heap_bytes; // Minimum free heap recorded
uint32_t heap_fragmentation; // Heap fragmentation percentage
// Task Information
uint32_t task_count; // Number of active tasks
uint32_t stack_high_water_mark; // Minimum remaining stack
// System Uptime
uint64_t uptime_ms; // System uptime in milliseconds
uint32_t boot_count; // Number of system boots
// Storage
uint64_t sd_free_bytes; // SD card free space
uint64_t sd_total_bytes; // SD card total space
uint32_t nvm_free_entries; // NVM free entries
// Communication
uint32_t wifi_rssi; // WiFi signal strength
uint32_t packets_sent; // Total packets sent
uint32_t packets_received; // Total packets received
uint32_t communication_errors; // Communication error count
// Sensors
uint8_t sensors_active; // Number of active sensors
uint8_t sensors_faulty; // Number of faulty sensors
uint32_t total_acquisitions; // Total sensor acquisitions
// Diagnostics
uint32_t warning_count; // Active warning count
uint32_t error_count; // Active error count
uint32_t fatal_count; // Fatal error count
uint64_t last_update_time; // Last metrics update time
} system_health_metrics_t;
```
### 4.4 Diagnostic Event
```c
typedef struct {
uint16_t diagnostic_code; // Diagnostic code (0xSCCC format)
diagnostic_severity_t severity; // Severity level
uint64_t timestamp_ms; // Event timestamp
uint32_t occurrence_count; // Number of occurrences
char description[64]; // Human-readable description
uint8_t context_data[32]; // Context-specific data
uint32_t sequence_number; // Event sequence number
} diagnostic_event_t;
typedef struct {
uint32_t info_count; // Number of INFO events
uint32_t warning_count; // Number of WARNING events
uint32_t error_count; // Number of ERROR events
uint32_t fatal_count; // Number of FATAL events
uint64_t last_event_time; // Timestamp of last event
uint16_t most_recent_code; // Most recent diagnostic code
} diagnostic_summary_t;
```
### 4.5 Communication Status
```c
typedef enum {
COMM_LINK_MAIN_HUB = 0, // Main Hub communication
COMM_LINK_PEER_HUB, // Peer Hub communication
COMM_LINK_DIAGNOSTIC, // Diagnostic communication
COMM_LINK_COUNT
} comm_link_type_t;
typedef struct {
bool is_connected; // Connection status
uint64_t last_activity_time; // Last communication activity
uint32_t bytes_sent; // Bytes sent
uint32_t bytes_received; // Bytes received
uint32_t error_count; // Communication errors
int32_t signal_strength; // Signal strength (RSSI)
uint32_t round_trip_time_ms; // Average round-trip time
} comm_link_status_t;
typedef struct {
comm_link_status_t links[COMM_LINK_COUNT]; // Individual link status
bool overall_connectivity; // Overall connectivity status
uint64_t last_successful_comm; // Last successful communication
uint32_t total_comm_failures; // Total communication failures
} communication_status_t;
```
### 4.6 Data Pool Statistics
```c
typedef struct {
// Access Statistics
uint64_t total_reads; // Total read operations
uint64_t total_writes; // Total write operations
uint64_t read_errors; // Read operation errors
uint64_t write_errors; // Write operation errors
// Performance Metrics
uint32_t avg_read_time_us; // Average read time
uint32_t avg_write_time_us; // Average write time
uint32_t max_read_time_us; // Maximum read time
uint32_t max_write_time_us; // Maximum write time
// Memory Usage
uint32_t memory_used_bytes; // Current memory usage
uint32_t max_memory_used_bytes; // Peak memory usage
// Data Integrity
uint32_t checksum_failures; // Checksum validation failures
uint32_t integrity_checks; // Total integrity checks performed
// Concurrency
uint32_t concurrent_readers; // Current concurrent readers
uint32_t max_concurrent_readers; // Maximum concurrent readers
uint32_t lock_contentions; // Lock contention events
uint64_t statistics_reset_time; // Last statistics reset time
} data_pool_stats_t;
```
## 5. Internal Architecture
### 5.1 Data Pool Structure
```mermaid
graph TB
subgraph "Data Pool Component"
subgraph "Sensor Data Storage"
SD0[Sensor 0 Data]
SD1[Sensor 1 Data]
SD2[Sensor 2 Data]
SD3[Sensor 3 Data]
SD4[Sensor 4 Data]
SD5[Sensor 5 Data]
SD6[Sensor 6 Data]
end
subgraph "System Data Storage"
SS[System State Info]
HM[Health Metrics]
CS[Communication Status]
CF[Configuration Data]
end
subgraph "Diagnostic Data Storage"
DE[Diagnostic Events]
DS[Diagnostic Summary]
end
subgraph "Access Control"
RM[Reader-Writer Mutex]
IC[Integrity Checker]
ST[Statistics Tracker]
end
end
subgraph "External Components"
SM[Sensor Manager]
STM[State Manager]
COMM[Communication]
DIAG[Diagnostics]
HMI[HMI]
end
SM -->|Update| SD0
SM -->|Update| SD1
STM -->|Update| SS
COMM -->|Update| CS
DIAG -->|Update| DE
HMI -->|Read| SS
HMI -->|Read| HM
COMM -->|Read| SD0
COMM -->|Read| SD1
RM -.->|Protects| SD0
RM -.->|Protects| SS
IC -.->|Validates| DE
ST -.->|Tracks| RM
```
### 5.2 Thread Safety Model
```mermaid
sequenceDiagram
participant W1 as Writer 1
participant W2 as Writer 2
participant R1 as Reader 1
participant R2 as Reader 2
participant DP as Data Pool
participant Mutex as RW Mutex
Note over W1,Mutex: Concurrent Access Scenario
W1->>Mutex: acquireWriteLock()
Mutex-->>W1: lockAcquired
W1->>DP: updateSensorData()
R1->>Mutex: acquireReadLock()
Note over R1,Mutex: Blocked until write complete
W2->>Mutex: acquireWriteLock()
Note over W2,Mutex: Blocked until write complete
W1->>DP: dataUpdateComplete
W1->>Mutex: releaseWriteLock()
Mutex-->>R1: readLockAcquired
R1->>DP: getSensorData()
DP-->>R1: sensorData
R2->>Mutex: acquireReadLock()
Mutex-->>R2: readLockAcquired
R2->>DP: getSensorData()
DP-->>R2: sensorData
R1->>Mutex: releaseReadLock()
R2->>Mutex: releaseReadLock()
Mutex-->>W2: writeLockAcquired
W2->>DP: updateSystemState()
W2->>Mutex: releaseWriteLock()
```
## 6. Threading Model
- **Access Model:** Multi-reader, single-writer with reader-writer mutex
- **Thread Safety:** Fully thread-safe for all operations
- **Blocking Operations:** Read operations may block on write operations (bounded)
- **ISR Access:** Limited read-only access for critical data (lock-free atomic reads)
- **Priority Inheritance:** Mutex supports priority inheritance to prevent priority inversion
## 7. Resource Ownership
- **Data Storage:** All runtime data owned exclusively by Data Pool
- **Access Control:** Reader-writer mutex owned by Data Pool
- **Memory Management:** Static allocation for all data structures (no dynamic allocation)
- **Integrity Checking:** Checksum validation owned by Data Pool
## 8. Error Model
### 8.1 Error Conditions
| Error | Condition | Response |
|-------|-----------|----------|
| `DATAPOOL_ERR_INVALID_SENSOR_ID` | Invalid sensor ID (>6) | Return false, log warning |
| `DATAPOOL_ERR_NULL_POINTER` | NULL pointer parameter | Return false, log error |
| `DATAPOOL_ERR_BUFFER_TOO_SMALL` | Output buffer too small | Return false, set required size |
| `DATAPOOL_ERR_DATA_STALE` | Data older than threshold | Return false, log info |
| `DATAPOOL_ERR_CHECKSUM_FAILURE` | Data integrity check failed | Return false, log error |
| `DATAPOOL_ERR_LOCK_TIMEOUT` | Failed to acquire lock | Return false, log warning |
| `DATAPOOL_ERR_MEMORY_FULL` | Storage capacity exceeded | Overwrite oldest, log warning |
### 8.2 Diagnostics Emitted
- `DIAG-DP-POOL-0001`: Data integrity check failed (ERROR)
- `DIAG-DP-POOL-0002`: Lock contention detected (WARNING)
- `DIAG-DP-POOL-0003`: Memory usage high (WARNING)
- `DIAG-DP-POOL-0004`: Stale data detected (INFO)
## 9. State-Dependent Behavior
| System State | Data Pool Behavior |
|-------------|-------------------|
| **INIT** | Initialize data structures, clear all data |
| **RUNNING** | Normal operation, full read/write access |
| **WARNING** | Continue operation, enhanced integrity checking |
| **FAULT** | Read-only mode, preserve data integrity |
| **OTA_UPDATE** | Read-only mode, prepare for snapshot |
| **MC_UPDATE** | Limited updates, configuration reload |
| **TEARDOWN** | Read-only mode, prepare for shutdown |
| **SERVICE** | Full access, enhanced diagnostics |
| **SD_DEGRADED** | Normal operation, no persistence coordination |
## 10. Dependencies
### 10.1 Required Components
- **Logger:** Debug and diagnostic logging
- **Time Utils:** Timestamp generation for data records
- **Error Handler:** Error reporting and escalation
### 10.2 Required Interfaces
- Logger interface for diagnostic output
- Time Utils interface for timestamp generation
- Error Handler interface for fault reporting
## 11. Performance Requirements
### 11.1 Access Performance
- **Read Operations:** Maximum 10μs for single sensor data read
- **Write Operations:** Maximum 50μs for single sensor data write
- **Bulk Operations:** Maximum 500μs for all sensor data read
- **Lock Acquisition:** Maximum 1ms timeout for lock acquisition
### 11.2 Memory Requirements
- **Static Memory:** Maximum 64KB for all data structures
- **Per-Sensor Data:** Maximum 1KB per sensor (including history)
- **Diagnostic Storage:** Maximum 8KB for recent diagnostic events
- **Configuration Storage:** Maximum 4KB for runtime configuration
## 12. Acceptance Tests
### 12.1 Functional Tests
- **T-DATAPOOL-001:** Sensor data storage and retrieval works correctly
- **T-DATAPOOL-002:** System state information maintained accurately
- **T-DATAPOOL-003:** Diagnostic events stored and retrieved correctly
- **T-DATAPOOL-004:** Communication status tracking works
- **T-DATAPOOL-005:** Configuration data management works
### 12.2 Concurrency Tests
- **T-DATAPOOL-006:** Multiple readers can access data simultaneously
- **T-DATAPOOL-007:** Writers have exclusive access during updates
- **T-DATAPOOL-008:** Reader-writer priority handling works correctly
- **T-DATAPOOL-009:** Lock contention handled gracefully
### 12.3 Performance Tests
- **T-DATAPOOL-010:** Read operations complete within 10μs
- **T-DATAPOOL-011:** Write operations complete within 50μs
- **T-DATAPOOL-012:** Memory usage stays within 64KB limit
- **T-DATAPOOL-013:** No memory leaks during continuous operation
### 12.4 Integrity Tests
- **T-DATAPOOL-014:** Data integrity checks detect corruption
- **T-DATAPOOL-015:** Checksum validation works correctly
- **T-DATAPOOL-016:** Stale data detection works
- **T-DATAPOOL-017:** Statistics tracking is accurate
## 13. Traceability
### 13.1 System Requirements
- **SR-DATA-002:** Data Persistence Abstraction (runtime data management)
- **SR-PERF-003:** Memory usage constraints
- **SR-REL-004:** Data integrity requirements
### 13.2 Software Requirements
- **SWR-DATA-004:** DP component API definition
- **SWR-DATA-005:** Storage media abstraction
- **SWR-DATA-006:** Unified data access interface
- **SWR-REL-010:** Error detection implementation
### 13.3 Features
- **F-DATA-002:** Data Persistence Abstraction (runtime component)
- **F-DIAG-002:** Diagnostic Data Storage (runtime component)
## 14. Implementation Notes
### 14.1 Design Patterns
- **Singleton Pattern:** Single Data Pool instance per system
- **Reader-Writer Lock Pattern:** Concurrent access control
- **Observer Pattern:** Data change notifications (via Event System)
- **Template Method Pattern:** Generic data access operations
### 14.2 Key Implementation Details
- All data structures SHALL use static allocation (no malloc/free)
- Reader-writer mutex SHALL support priority inheritance
- Data integrity SHALL be verified using checksums
- Access statistics SHALL be maintained for performance monitoring
- Atomic operations SHALL be used for lock-free ISR access
### 14.3 Memory Layout
```c
// Static memory allocation structure
typedef struct {
sensor_data_record_t sensor_data[SENSOR_TYPE_COUNT];
system_state_info_t system_state;
system_health_metrics_t health_metrics;
diagnostic_event_t diagnostic_events[MAX_DIAGNOSTIC_EVENTS];
comm_link_status_t comm_links[COMM_LINK_COUNT];
uint8_t configuration_data[MAX_CONFIG_SIZE];
data_pool_stats_t statistics;
pthread_rwlock_t access_lock;
} data_pool_storage_t;
```
---
**Document Status:** Final for Implementation Phase
**Component Dependencies:** Verified against architecture
**Requirements Traceability:** Complete (SR-DATA, SWR-DATA)
**Next Review:** After implementation and testing

View File

@@ -1,47 +0,0 @@
/**
* @file data_pool.cpp
* @brief DataPool component implementation
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "data_pool.hpp"
#include "logger.hpp"
static const char* TAG = "DataPool";
DataPool::DataPool()
: m_isInitialized(false)
{
}
DataPool::~DataPool()
{
deinitialize();
}
bool DataPool::initialize()
{
// TODO: Implement initialization
m_isInitialized = true;
ASF_LOGI(TAG, 4900, asf::logger::Criticality::LOW, "DataPool initialized successfully");
return true;
}
bool DataPool::deinitialize()
{
if (!m_isInitialized)
{
return false;
}
// TODO: Implement deinitialization
m_isInitialized = false;
return true;
}
bool DataPool::isInitialized() const
{
return m_isInitialized;
}

View File

@@ -1,33 +0,0 @@
/**
* @file data_pool.hpp
* @brief DataPool component header
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#ifndef DATA_POOL_HPP
#define DATA_POOL_HPP
#include <cstdint>
/**
* @brief DataPool class
*
* Component description goes here.
*/
class DataPool
{
public:
DataPool();
~DataPool();
bool initialize();
bool deinitialize();
bool isInitialized() const;
private:
bool m_isInitialized;
};
#endif // DATA_POOL_HPP

View File

@@ -1,2 +0,0 @@
ID,Component,Level,Criticality,Message
4900,DataPool,INFO,Low,DataPool initialized successfully
1 ID Component Level Criticality Message
2 4900 DataPool INFO Low DataPool initialized successfully

View File

@@ -1,34 +0,0 @@
import sys
import os
import time
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
if folder_path not in sys.path:
sys.path.append(folder_path)
from scan_serial import ESP32Runner
def test_data_pool_initialize():
runner = ESP32Runner(mode="SIM", port="COM9")
runner.start()
print("--- QEMU Runner Started ---", flush=True)
try:
start_time = time.time()
while time.time() - start_time < 30:
line = runner.get_line(timeout=1.0)
if line:
print(line, flush=True)
if "DataPool initialized successfully" in line:
print("SUCCESS CRITERIA MET!", flush=True)
return 0
if runner.process.poll() is not None:
print(f"Process exited with code: {runner.process.returncode}", flush=True)
return 1
finally:
runner.stop()
print("Done.", flush=True)
return 1
if __name__ == "__main__":
exit_code = test_data_pool_initialize()
sys.exit(exit_code)

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<test_scenario>
<!-- The configuration for the test environment. -->
<!-- Available configurations: SIMULATE, HIL -->
<config>SIMULATE</config>
<test_case>
<test_case_id>DATA_POOL_INIT_TEST</test_case_id>
<!-- The main command that executes the test itself. -->
<test_exec>python components/application_layer/DP_stack/data_pool/test/data_pool_init_test.py</test_exec>
</test_case>
</test_scenario>

View File

@@ -1,40 +0,0 @@
/**
* @file test_data_pool.cpp
* @brief Unit tests for DataPool component
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "unity.h"
#include "data_pool.hpp"
extern "C" {
void setUp(void)
{
}
void tearDown(void)
{
}
void test_data_pool_initialize(void)
{
DataPool comp;
bool result = comp.initialize();
TEST_ASSERT_TRUE(result);
TEST_ASSERT_TRUE(comp.isInitialized());
}
void test_data_pool_deinitialize(void)
{
DataPool comp;
comp.initialize();
bool result = comp.deinitialize();
TEST_ASSERT_TRUE(result);
TEST_ASSERT_FALSE(comp.isInitialized());
}
} // extern "C"

View File

@@ -1,5 +0,0 @@
idf_component_register(
SRCS "com/persistence.cpp"
INCLUDE_DIRS "com"
REQUIRES logger
)

View File

@@ -1,257 +0,0 @@
# Persistence Component Specification
**Component ID:** COMP-PERSIST
**Version:** 1.0
**Date:** 2025-01-19
**Location:** `application_layer/DP_stack/persistence/`
## 1. Purpose
The Persistence component provides the sole interface for persistent storage access. It abstracts storage media (SD card, NVM), manages serialization/deserialization, implements wear-aware storage, and ensures data integrity.
## 2. Responsibilities
### 2.1 Primary Responsibilities
- Abstract storage media (SD card, NVM)
- Serialize/deserialize structured data
- Manage wear-aware storage (SD card)
- Ensure data integrity
- Coordinate data flush operations
### 2.2 Non-Responsibilities
- Business logic (data semantics owned by components)
- Hardware access (delegated to storage drivers)
- Data validation (components validate before persistence)
## 3. Public API
### 3.1 Sensor Data Persistence
```c
/**
* @brief Write sensor data record
* @param record Sensor data record
* @return true if written, false on error
*/
bool persistence_writeSensorData(const sensor_data_record_t* record);
/**
* @brief Read sensor data records
* @param records Output buffer
* @param count Input: buffer size, Output: records read
* @param start_time Start timestamp filter (0 for all)
* @param end_time End timestamp filter (0 for all)
* @return true if read successful, false on error
*/
bool persistence_readSensorData(sensor_data_record_t* records, size_t* count, uint64_t start_time, uint64_t end_time);
```
### 3.2 Diagnostic Persistence
```c
/**
* @brief Write diagnostic event
* @param event Diagnostic event
* @return true if written, false on error
*/
bool persistence_writeDiagnostic(const diagnostic_event_t* event);
/**
* @brief Read diagnostic events
* @param events Output buffer
* @param count Input: buffer size, Output: events read
* @param filter Filter criteria (severity, component, etc.)
* @return true if read successful, false on error
*/
bool persistence_readDiagnostics(diagnostic_event_t* events, size_t* count, const diag_filter_t* filter);
/**
* @brief Clear diagnostic log
* @return true if cleared, false on error
*/
bool persistence_clearDiagnostics(void);
```
### 3.3 Machine Constants Persistence
```c
/**
* @brief Write machine constants
* @param mc Machine constants structure
* @return true if written, false on error
*/
bool persistence_writeMachineConstants(const machine_constants_t* mc);
/**
* @brief Read machine constants
* @param mc Output buffer
* @return true if read successful, false on error
*/
bool persistence_readMachineConstants(machine_constants_t* mc);
```
### 3.4 Flush Operations
```c
/**
* @brief Flush all critical data to storage
* @return true if flushed, false on error
*/
bool persistence_flushCriticalData(void);
/**
* @brief Check if flush is complete
* @return true if flush complete, false otherwise
*/
bool persistence_isFlushComplete(void);
/**
* @brief Get flush progress (0-100%)
* @return Flush progress percentage
*/
uint8_t persistence_getFlushProgress(void);
```
### 3.5 Storage Status
```c
/**
* @brief Check storage availability
* @param storage_type Storage type (SD_CARD, NVM)
* @return true if available, false otherwise
*/
bool persistence_isStorageAvailable(storage_type_t storage_type);
/**
* @brief Get storage usage statistics
* @param storage_type Storage type
* @param stats Output statistics
* @return true if retrieved, false on error
*/
bool persistence_getStorageStats(storage_type_t storage_type, storage_stats_t* stats);
```
## 4. Data Types
### 4.1 Storage Types
```c
typedef enum {
STORAGE_TYPE_SD_CARD = 0,
STORAGE_TYPE_NVM,
STORAGE_TYPE_COUNT
} storage_type_t;
```
### 4.2 Storage Statistics
```c
typedef struct {
uint64_t total_bytes;
uint64_t used_bytes;
uint64_t free_bytes;
uint32_t write_count;
bool is_healthy;
} storage_stats_t;
```
## 5. Threading Model
- **Owner Task:** Persistence Task (MEDIUM priority)
- **Thread Safety:** Thread-safe (all operations protected by mutex)
- **Blocking Operations:** Storage I/O operations (bounded by timing requirements)
- **ISR Access:** Not allowed (blocking operations)
## 6. Resource Ownership
- **SD Card Driver:** Owned exclusively by Persistence component
- **NVM Driver:** Owned exclusively by Persistence component
- **Storage Mutex:** Protects concurrent access
- **Write Buffers:** Pre-allocated static buffers
## 7. Error Model
### 7.1 Error Conditions
| Error | Condition | Response |
|-------|-----------|----------|
| `PERSIST_ERR_STORAGE_FULL` | Storage full | Trigger retention policy, log warning |
| `PERSIST_ERR_STORAGE_FAILED` | Storage failure | Enter SD_DEGRADED state, log error |
| `PERSIST_ERR_SERIALIZATION` | Serialization failure | Return false, log error |
| `PERSIST_ERR_INTEGRITY_CHECK` | Integrity check failed | Return false, log error, attempt recovery |
### 7.2 Diagnostics Emitted
- `DIAG-ST-PERSIST-0001`: Storage write failure (WARNING)
- `DIAG-ST-PERSIST-0002`: Storage full (WARNING)
- `DIAG-ST-PERSIST-0003`: Storage corruption detected (ERROR)
- `DIAG-ST-PERSIST-0004`: Storage failure (FATAL)
## 8. State-Dependent Behavior
### 8.1 All States
- Read operations allowed
- Write operations allowed (with restrictions)
### 8.2 TEARDOWN State
- Only authorized writes allowed (critical data flush)
- Read operations allowed
### 8.3 SD_DEGRADED State
- SD card writes disabled
- NVM writes allowed
- Read operations allowed (if storage accessible)
## 9. Dependencies
### 9.1 Required Components
- **SD Card Driver:** For SD card access
- **NVM Driver:** For NVM access
- **STM:** For state queries and transitions
- **Logger:** For diagnostic logging
### 9.2 Required Interfaces
- SD Card Driver interface
- NVM Driver interface
- STM state query interface
- Logger interface
## 10. Acceptance Tests
- **T-PERSIST-001:** Sensor data write/read operations
- **T-PERSIST-002:** Diagnostic event write/read operations
- **T-PERSIST-003:** Machine constants write/read operations
- **T-PERSIST-004:** Data flush completes within 200ms
- **T-PERSIST-005:** Storage failure handling (SD_DEGRADED state)
- **T-PERSIST-006:** Data integrity verification
- **T-PERSIST-007:** Wear-aware storage management
## 11. Traceability
- **SWR-DATA-001:** Persistent timestamped sensor data
- **SWR-DATA-004:** DP component as sole persistence interface
- **SWR-DATA-005:** Storage access isolation
- **SWR-DATA-006:** Structured data serialization
- **SWR-DATA-007:** Data flush before teardown
- **SWR-DATA-008:** Data integrity during updates
- **SWR-DATA-009:** Persistence verification
- **SWR-DATA-012:** SD card failure handling
- **SWR-DATA-013:** Wear-aware storage management
- **CFC-ARCH-01:** Hardware access via drivers only
- **CFC-DATA-01:** Single source of truth
- **CFC-DATA-02:** Data consistency during transitions
## 12. Implementation Notes
- Serialization SHALL use a well-defined format (e.g., CBOR, MessagePack, or custom binary)
- SD card writes SHALL be wear-aware (wear leveling, write frequency limits)
- Data integrity SHALL be verified using checksums or hashes
- Flush operations SHALL be coordinated with STM for state transitions
- Storage failures SHALL trigger state transitions (SD_DEGRADED)

View File

@@ -1,47 +0,0 @@
/**
* @file persistence.cpp
* @brief Persistence component implementation
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "persistence.hpp"
#include "logger.hpp"
static const char* TAG = "Persistence";
Persistence::Persistence()
: m_isInitialized(false)
{
}
Persistence::~Persistence()
{
deinitialize();
}
bool Persistence::initialize()
{
// TODO: Implement initialization
m_isInitialized = true;
ASF_LOGI(TAG, 5000, asf::logger::Criticality::LOW, "Persistence initialized successfully");
return true;
}
bool Persistence::deinitialize()
{
if (!m_isInitialized)
{
return false;
}
// TODO: Implement deinitialization
m_isInitialized = false;
return true;
}
bool Persistence::isInitialized() const
{
return m_isInitialized;
}

View File

@@ -1,33 +0,0 @@
/**
* @file persistence.hpp
* @brief Persistence component header
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#ifndef PERSISTENCE_HPP
#define PERSISTENCE_HPP
#include <cstdint>
/**
* @brief Persistence class
*
* Component description goes here.
*/
class Persistence
{
public:
Persistence();
~Persistence();
bool initialize();
bool deinitialize();
bool isInitialized() const;
private:
bool m_isInitialized;
};
#endif // PERSISTENCE_HPP

View File

@@ -1,2 +0,0 @@
ID,Component,Level,Criticality,Message
5000,Persistence,INFO,Low,Persistence initialized successfully
1 ID Component Level Criticality Message
2 5000 Persistence INFO Low Persistence initialized successfully

View File

@@ -1,34 +0,0 @@
import sys
import os
import time
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
if folder_path not in sys.path:
sys.path.append(folder_path)
from scan_serial import ESP32Runner
def test_persistence_initialize():
runner = ESP32Runner(mode="SIM", port="COM9")
runner.start()
print("--- QEMU Runner Started ---", flush=True)
try:
start_time = time.time()
while time.time() - start_time < 30:
line = runner.get_line(timeout=1.0)
if line:
print(line, flush=True)
if "Persistence initialized successfully" in line:
print("SUCCESS CRITERIA MET!", flush=True)
return 0
if runner.process.poll() is not None:
print(f"Process exited with code: {runner.process.returncode}", flush=True)
return 1
finally:
runner.stop()
print("Done.", flush=True)
return 1
if __name__ == "__main__":
exit_code = test_persistence_initialize()
sys.exit(exit_code)

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<test_scenario>
<!-- The configuration for the test environment. -->
<!-- Available configurations: SIMULATE, HIL -->
<config>SIMULATE</config>
<test_case>
<test_case_id>PERSISTENCE_INIT_TEST</test_case_id>
<!-- The main command that executes the test itself. -->
<test_exec>python components/application_layer/DP_stack/persistence/test/persistence_init_test.py</test_exec>
</test_case>
</test_scenario>

View File

@@ -1,40 +0,0 @@
/**
* @file test_persistence.cpp
* @brief Unit tests for Persistence component
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "unity.h"
#include "persistence.hpp"
extern "C" {
void setUp(void)
{
}
void tearDown(void)
{
}
void test_persistence_initialize(void)
{
Persistence comp;
bool result = comp.initialize();
TEST_ASSERT_TRUE(result);
TEST_ASSERT_TRUE(comp.isInitialized());
}
void test_persistence_deinitialize(void)
{
Persistence comp;
comp.initialize();
bool result = comp.deinitialize();
TEST_ASSERT_TRUE(result);
TEST_ASSERT_FALSE(comp.isInitialized());
}
} // extern "C"

View File

@@ -1,5 +0,0 @@
idf_component_register(
SRCS "com/stm.cpp"
INCLUDE_DIRS "com"
REQUIRES logger
)

View File

@@ -1,291 +0,0 @@
# State Manager (STM) Component Specification
**Component ID:** COMP-STM
**Version:** 1.0
**Date:** 2025-01-19
**Location:** `application_layer/business_stack/STM/`
## 1. Purpose
The State Manager (STM) component implements the system finite state machine (FSM) as defined in the System State Machine Specification. It manages system operational states, enforces valid state transitions, coordinates teardown sequences, and notifies components of state changes.
## 2. Responsibilities
### 2.1 Primary Responsibilities
- Implement system FSM with 11 states (INIT, BOOT_FAILURE, RUNNING, WARNING, FAULT, OTA_PREP, OTA_UPDATE, MC_UPDATE, TEARDOWN, SERVICE, SD_DEGRADED)
- Enforce valid state transitions according to transition table
- Coordinate controlled teardown sequences
- Notify registered components of state changes via Event System
- Provide state query interface for components
### 2.2 Non-Responsibilities
- Feature logic (sensor acquisition, communication, etc.)
- Hardware access (delegated to drivers)
- Fault detection (delegated to Error Handler)
- Diagnostic logging (delegated to Diagnostics Task)
## 3. Public API
### 3.1 State Query Functions
```c
/**
* @brief Get current system state
* @return Current system state
*/
system_state_t stm_getCurrentState(void);
/**
* @brief Check if a state is valid
* @param state State to validate
* @return true if state is valid, false otherwise
*/
bool stm_isStateValid(system_state_t state);
/**
* @brief Check if system is in a specific state
* @param state State to check
* @return true if current state matches, false otherwise
*/
bool stm_isInState(system_state_t state);
```
### 3.2 State Transition Functions
```c
/**
* @brief Request a state transition
* @param target_state Target state
* @param reason Transition reason
* @return true if transition initiated, false if rejected
*/
bool stm_requestTransition(system_state_t target_state, transition_reason_t reason);
/**
* @brief Validate if a state transition is allowed
* @param from Source state
* @param to Target state
* @return true if transition is valid, false otherwise
*/
bool stm_validateTransition(system_state_t target_state, transition_reason_t reason);
/**
* @brief Get transition reason for last transition
* @return Transition reason
*/
transition_reason_t stm_getLastTransitionReason(void);
```
### 3.3 Teardown Functions
```c
/**
* @brief Initiate controlled teardown sequence
* @param reason Teardown reason
* @return true if teardown initiated, false if rejected
*/
bool stm_initiateTeardown(teardown_reason_t reason);
/**
* @brief Check if teardown is complete
* @return true if teardown complete, false otherwise
*/
bool stm_isTeardownComplete(void);
/**
* @brief Get teardown progress (0-100%)
* @return Teardown progress percentage
*/
uint8_t stm_getTeardownProgress(void);
```
### 3.4 Component Registration
```c
/**
* @brief State listener callback type
* @param old_state Previous state
* @param new_state New state
* @param reason Transition reason
*/
typedef void (*state_listener_t)(system_state_t old_state, system_state_t new_state, transition_reason_t reason);
/**
* @brief Register a state change listener
* @param listener Callback function
* @return true if registered, false on error
*/
bool stm_registerStateListener(state_listener_t listener);
/**
* @brief Unregister a state change listener
* @param listener Callback function to remove
* @return true if unregistered, false if not found
*/
bool stm_unregisterStateListener(state_listener_t listener);
```
## 4. Data Types
### 4.1 System States
```c
typedef enum {
SYSTEM_STATE_INIT = 0,
SYSTEM_STATE_BOOT_FAILURE,
SYSTEM_STATE_RUNNING,
SYSTEM_STATE_WARNING,
SYSTEM_STATE_FAULT,
SYSTEM_STATE_OTA_PREP,
SYSTEM_STATE_OTA_UPDATE,
SYSTEM_STATE_MC_UPDATE,
SYSTEM_STATE_TEARDOWN,
SYSTEM_STATE_SERVICE,
SYSTEM_STATE_SD_DEGRADED,
SYSTEM_STATE_COUNT
} system_state_t;
```
### 4.2 Transition Reasons
```c
typedef enum {
TRANSITION_REASON_INIT_SUCCESS,
TRANSITION_REASON_SECURE_BOOT_FAIL,
TRANSITION_REASON_NON_FATAL_FAULT,
TRANSITION_REASON_FATAL_FAULT,
TRANSITION_REASON_FAULT_CLEARED,
TRANSITION_REASON_FAULT_ESCALATED,
TRANSITION_REASON_OTA_REQUEST,
TRANSITION_REASON_MC_UPDATE_REQUEST,
TRANSITION_REASON_DEBUG_SESSION,
TRANSITION_REASON_SD_FAILURE,
TRANSITION_REASON_SD_RECOVERED,
TRANSITION_REASON_RECOVERY_ATTEMPT,
TRANSITION_REASON_MANUAL_RESET,
TRANSITION_REASON_OTA_READY,
TRANSITION_REASON_OTA_REJECTED,
TRANSITION_REASON_TEARDOWN_COMPLETE,
TRANSITION_REASON_OTA_SUCCESS,
TRANSITION_REASON_OTA_FAILURE,
TRANSITION_REASON_MC_UPDATE_COMPLETE,
TRANSITION_REASON_SESSION_CLOSED,
TRANSITION_REASON_MANUAL_INTERVENTION
} transition_reason_t;
```
### 4.3 Teardown Reasons
```c
typedef enum {
TEARDOWN_REASON_OTA,
TEARDOWN_REASON_MC_UPDATE,
TEARDOWN_REASON_FATAL_FAULT,
TEARDOWN_REASON_MANUAL_COMMAND,
TEARDOWN_REASON_SYSTEM_RESET
} teardown_reason_t;
```
## 5. Threading Model
- **Owner Task:** System Management Task (HIGH priority)
- **Thread Safety:** Thread-safe (protected by mutex for state transitions)
- **Blocking Operations:** None (all operations are non-blocking)
- **ISR Access:** State queries allowed from ISR (read-only, lock-free)
## 6. Resource Ownership
- **State Machine State:** Owned exclusively by STM component
- **State Transition Lock:** Mutex-protected (prevents concurrent transitions)
- **State Listeners:** List maintained by STM (protected by mutex)
## 7. Error Model
### 7.1 Error Conditions
| Error | Condition | Response |
|-------|-----------|----------|
| `STM_ERR_INVALID_STATE` | Invalid state parameter | Return false, log warning |
| `STM_ERR_INVALID_TRANSITION` | Invalid transition requested | Return false, log warning |
| `STM_ERR_TEARDOWN_IN_PROGRESS` | Teardown already in progress | Return false, log info |
| `STM_ERR_LISTENER_FULL` | Too many listeners registered | Return false, log error |
### 7.2 Diagnostics Emitted
- `DIAG-SY-STM-0001`: Invalid state transition attempted (WARNING)
- `DIAG-SY-STM-0002`: State transition timeout (ERROR)
- `DIAG-SY-STM-0003`: Teardown sequence failure (FATAL)
## 8. State-Dependent Behavior
### 8.1 INIT State
- **Allowed Operations:** State queries, listener registration
- **Forbidden Operations:** State transitions (except to RUNNING or BOOT_FAILURE)
- **Duration:** Bounded (max 5 seconds)
### 8.2 RUNNING State
- **Allowed Operations:** All operations
- **Forbidden Operations:** None
- **Duration:** Indefinite (normal operation)
### 8.3 TEARDOWN State
- **Allowed Operations:** State queries, teardown progress queries
- **Forbidden Operations:** New state transitions (except completion transitions)
- **Duration:** Bounded (max 500ms)
## 9. Dependencies
### 9.1 Required Components
- **Event System:** For state change notifications
- **Error Handler:** For fault-triggered transitions
- **Persistence:** For data flush during teardown
- **Logger:** For diagnostic logging
### 9.2 Required Interfaces
- Event System publish interface
- Persistence flush interface
- Logger interface
## 10. Acceptance Tests
### 10.1 State Transition Tests
- **T-STM-001:** Valid state transitions succeed
- **T-STM-002:** Invalid state transitions are rejected
- **T-STM-003:** State transition timing meets requirements (≤50ms)
### 10.2 Teardown Tests
- **T-STM-004:** Teardown sequence completes within 500ms
- **T-STM-005:** Teardown coordinates data flush
- **T-STM-006:** Teardown prevents new transitions
### 10.3 Notification Tests
- **T-STM-007:** State change notifications are sent to all listeners
- **T-STM-008:** State change notifications are non-blocking
## 11. Traceability
- **SWR-SYS-001:** FSM implementation
- **SWR-SYS-002:** State transition enforcement
- **SWR-SYS-003:** State-based operation restriction
- **SWR-SYS-004:** State transition notification
- **SWR-SYS-005:** Controlled teardown execution
- **SWR-SYS-006:** Critical data persistence before teardown
- **SWR-SYS-007:** Data integrity protection during shutdown
## 12. Implementation Notes
- State machine SHALL be implemented as a table-driven FSM
- State transitions SHALL be atomic (protected by mutex)
- State listeners SHALL be called asynchronously via Event System
- Teardown sequence SHALL coordinate with Persistence component for data flush
- State queries SHALL be lock-free for ISR access (read-only)

View File

@@ -1,47 +0,0 @@
/**
* @file stm.cpp
* @brief Stm component implementation
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "stm.hpp"
#include "logger.hpp"
static const char* TAG = "Stm";
Stm::Stm()
: m_isInitialized(false)
{
}
Stm::~Stm()
{
deinitialize();
}
bool Stm::initialize()
{
// TODO: Implement initialization
m_isInitialized = true;
ASF_LOGI(TAG, 4600, asf::logger::Criticality::LOW, "Stm initialized successfully");
return true;
}
bool Stm::deinitialize()
{
if (!m_isInitialized)
{
return false;
}
// TODO: Implement deinitialization
m_isInitialized = false;
return true;
}
bool Stm::isInitialized() const
{
return m_isInitialized;
}

View File

@@ -1,33 +0,0 @@
/**
* @file stm.hpp
* @brief Stm component header
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#ifndef STM_HPP
#define STM_HPP
#include <cstdint>
/**
* @brief Stm class
*
* Component description goes here.
*/
class Stm
{
public:
Stm();
~Stm();
bool initialize();
bool deinitialize();
bool isInitialized() const;
private:
bool m_isInitialized;
};
#endif // STM_HPP

View File

@@ -1,2 +0,0 @@
ID,Component,Level,Criticality,Message
4600,STM,INFO,Low,Stm initialized successfully
1 ID Component Level Criticality Message
2 4600 STM INFO Low Stm initialized successfully

View File

@@ -1,34 +0,0 @@
import sys
import os
import time
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
if folder_path not in sys.path:
sys.path.append(folder_path)
from scan_serial import ESP32Runner
def test_stm_initialize():
runner = ESP32Runner(mode="SIM", port="COM9")
runner.start()
print("--- QEMU Runner Started ---", flush=True)
try:
start_time = time.time()
while time.time() - start_time < 30:
line = runner.get_line(timeout=1.0)
if line:
print(line, flush=True)
if "Stm initialized successfully" in line:
print("SUCCESS CRITERIA MET!", flush=True)
return 0
if runner.process.poll() is not None:
print(f"Process exited with code: {runner.process.returncode}", flush=True)
return 1
finally:
runner.stop()
print("Done.", flush=True)
return 1
if __name__ == "__main__":
exit_code = test_stm_initialize()
sys.exit(exit_code)

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<test_scenario>
<!-- The configuration for the test environment. -->
<!-- Available configurations: SIMULATE, HIL -->
<config>SIMULATE</config>
<test_case>
<test_case_id>STM_INIT_TEST</test_case_id>
<!-- The main command that executes the test itself. -->
<test_exec>python components/application_layer/business_stack/STM/test/stm_init_test.py</test_exec>
</test_case>
</test_scenario>

View File

@@ -1,40 +0,0 @@
/**
* @file test_stm.cpp
* @brief Unit tests for Stm component
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "unity.h"
#include "stm.hpp"
extern "C" {
void setUp(void)
{
}
void tearDown(void)
{
}
void test_stm_initialize(void)
{
Stm comp;
bool result = comp.initialize();
TEST_ASSERT_TRUE(result);
TEST_ASSERT_TRUE(comp.isInitialized());
}
void test_stm_deinitialize(void)
{
Stm comp;
comp.initialize();
bool result = comp.deinitialize();
TEST_ASSERT_TRUE(result);
TEST_ASSERT_FALSE(comp.isInitialized());
}
} // extern "C"

View File

@@ -1,5 +0,0 @@
idf_component_register(
SRCS "com/actuator_manager.cpp"
INCLUDE_DIRS "com"
REQUIRES logger
)

View File

@@ -1,46 +0,0 @@
/**
* @file actuator_manager.cpp
* @brief ActuatorManager component implementation
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "logger.hpp"
#include "actuator_manager.hpp"
static const char* TAG = "actuator_manager";
ActuatorManager::ActuatorManager()
: m_isInitialized(false)
{
}
ActuatorManager::~ActuatorManager()
{
deinitialize();
}
bool ActuatorManager::initialize()
{
// TODO: Implement initialization
m_isInitialized = true;
ASF_LOGI(TAG, 4000, asf::logger::Criticality::LOW, "actuator manager initialized successfully");
return true;
}
bool ActuatorManager::deinitialize()
{
if (!m_isInitialized)
{
return false;
}
// TODO: Implement deinitialization
m_isInitialized = false;
return true;
}
bool ActuatorManager::isInitialized() const
{
return m_isInitialized;
}

View File

@@ -1,33 +0,0 @@
/**
* @file actuator_manager.hpp
* @brief ActuatorManager component header
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#ifndef ACTUATOR_MANAGER_HPP
#define ACTUATOR_MANAGER_HPP
#include <cstdint>
/**
* @brief ActuatorManager class
*
* Component description goes here.
*/
class ActuatorManager
{
public:
ActuatorManager();
~ActuatorManager();
bool initialize();
bool deinitialize();
bool isInitialized() const;
private:
bool m_isInitialized;
};
#endif // ACTUATOR_MANAGER_HPP

View File

@@ -1,2 +0,0 @@
ID,Component,Level,Criticality,Message
4000,ActuatorManager,INFO,Low,actuator manager initialized successfully
1 ID Component Level Criticality Message
2 4000 ActuatorManager INFO Low actuator manager initialized successfully

View File

@@ -1,34 +0,0 @@
import sys
import os
import time
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
if folder_path not in sys.path:
sys.path.append(folder_path)
from scan_serial import ESP32Runner
def test_actuator_manager_initialize():
runner = ESP32Runner(mode="SIM", port="COM9")
runner.start()
print("--- QEMU Runner Started ---", flush=True)
try:
start_time = time.time()
while time.time() - start_time < 30:
line = runner.get_line(timeout=1.0)
if line:
print(line, flush=True)
if "Actuator Manager initialized successfully" in line or "actuator manager initialized successfully" in line.lower():
print("SUCCESS CRITERIA MET!", flush=True)
return 0
if runner.process.poll() is not None:
print(f"Process exited with code: {runner.process.returncode}", flush=True)
return 1
finally:
runner.stop()
print("Done.", flush=True)
return 1
if __name__ == "__main__":
exit_code = test_actuator_manager_initialize()
sys.exit(exit_code)

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<test_scenario>
<!-- The configuration for the test environment. -->
<!-- Available configurations: SIMULATE, HIL -->
<config>SIMULATE</config>
<test_case>
<test_case_id>ACTUATOR_MANAGER_INIT_TEST</test_case_id>
<!-- The main command that executes the test itself. -->
<test_exec>python components/application_layer/business_stack/actuator_manager/test/actuator_manager_init_test.py</test_exec>
</test_case>
</test_scenario>

View File

@@ -1,56 +0,0 @@
import sys
import os
# Get the absolute path to the folder you want to add
# Example: Adding a folder named 'my_components' located in the current directory
folder_path = os.path.abspath("components/system_tests")
if folder_path not in sys.path:
sys.path.append(folder_path)
from scan_serial import ESP32Runner
import time
# --- Main Logic ---
if __name__ == "__main__":
runner = ESP32Runner(mode="SIM", port="COM9")
runner.start()
keyword_found = False
# Force print to terminal immediately
print("--- QEMU Runner Started ---", flush=True)
try:
start_time = time.time()
import sys
import os
import time
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
if folder_path not in sys.path:
sys.path.append(folder_path)
from scan_serial import ESP32Runner
def test_actuator_manager_initialize():
runner = ESP32Runner(mode="SIM", port="COM9")
runner.start()
print("--- QEMU Runner Started ---", flush=True)
try:
start_time = time.time()
while time.time() - start_time < 30:
line = runner.get_line(timeout=1.0)
if line:
print(line, flush=True)
if "actuator manager initialized successfully" in line.lower() or "Actuator Manager initialized successfully" in line:
print("SUCCESS CRITERIA MET!", flush=True)
return 0
if runner.process.poll() is not None:
print(f"Process exited with code: {runner.process.returncode}", flush=True)
return 1
finally:
runner.stop()
print("Done.", flush=True)
return 1
if __name__ == "__main__":
exit_code = test_actuator_manager_initialize()
sys.exit(exit_code)

View File

@@ -1,40 +0,0 @@
/**
* @file test_actuator_manager.cpp
* @brief Unit tests for ActuatorManager component
* @author Mahmoud Elmohtady
* @company Nabd solutions - ASF
* @copyright Copyright (c) 2025
*/
#include "unity.h"
#include "actuator_manager.hpp"
extern "C" {
void setUp(void)
{
}
void tearDown(void)
{
}
void test_actuator_manager_initialize(void)
{
ActuatorManager comp;
bool result = comp.initialize();
TEST_ASSERT_TRUE(result);
TEST_ASSERT_TRUE(comp.isInitialized());
}
void test_actuator_manager_deinitialize(void)
{
ActuatorManager comp;
comp.initialize();
bool result = comp.deinitialize();
TEST_ASSERT_TRUE(result);
TEST_ASSERT_FALSE(comp.isInitialized());
}
} // extern "C"

View File

@@ -1,5 +0,0 @@
idf_component_register(
SRCS "com/event_system.cpp"
INCLUDE_DIRS "com"
REQUIRES logger
)

View File

@@ -1,200 +0,0 @@
# Event System Component Specification
**Component ID:** COMP-EVENT
**Version:** 1.0
**Date:** 2025-01-19
**Location:** `application_layer/business_stack/event_system/`
## 1. Purpose
The Event System provides a publish/subscribe event bus for cross-component communication. It decouples components, enables asynchronous event delivery, and ensures non-blocking operation.
## 2. Responsibilities
### 2.1 Primary Responsibilities
- Provide publish/subscribe event bus
- Decouple components via events
- Ensure non-blocking event delivery
- Support multiple subscribers per event type
- Queue management for event delivery
### 2.2 Non-Responsibilities
- Business logic (components handle events)
- Event payload validation (components validate)
- Event ordering guarantees (best-effort)
## 3. Public API
### 3.1 Event Publishing
```c
/**
* @brief Publish an event
* @param type Event type
* @param payload Event payload (may be NULL)
* @param payload_size Payload size in bytes (0 if payload is NULL)
* @return true if published, false on error
*/
bool event_publish(event_type_t type, const void* payload, size_t payload_size);
/**
* @brief Publish an event with timestamp
* @param type Event type
* @param payload Event payload
* @param payload_size Payload size
* @param timestamp Event timestamp
* @return true if published, false on error
*/
bool event_publishWithTimestamp(event_type_t type, const void* payload, size_t payload_size, uint64_t timestamp);
```
### 3.2 Event Subscription
```c
/**
* @brief Event handler callback type
* @param type Event type
* @param payload Event payload
* @param payload_size Payload size
* @param timestamp Event timestamp
*/
typedef void (*event_handler_t)(event_type_t type, const void* payload, size_t payload_size, uint64_t timestamp);
/**
* @brief Subscribe to an event type
* @param type Event type to subscribe to
* @param handler Callback function
* @return true if subscribed, false on error
*/
bool event_subscribe(event_type_t type, event_handler_t handler);
/**
* @brief Unsubscribe from an event type
* @param type Event type to unsubscribe from
* @param handler Callback function to remove
* @return true if unsubscribed, false if not found
*/
bool event_unsubscribe(event_type_t type, event_handler_t handler);
```
### 3.3 Event Queue Management
```c
/**
* @brief Get number of pending events
* @param type Event type (EVENT_TYPE_ALL for all types)
* @return Number of pending events
*/
size_t event_getPendingCount(event_type_t type);
/**
* @brief Clear pending events
* @param type Event type (EVENT_TYPE_ALL for all types)
* @return Number of events cleared
*/
size_t event_clearPending(event_type_t type);
```
## 4. Data Types
### 4.1 Event Types
```c
typedef enum {
EVENT_SENSOR_DATA_UPDATE = 0,
EVENT_DIAGNOSTIC_EVENT,
EVENT_STATE_CHANGED,
EVENT_OTA_REQUEST,
EVENT_OTA_STATUS,
EVENT_MC_UPDATE_REQUEST,
EVENT_COMMUNICATION_LINK_STATUS,
EVENT_STORAGE_STATUS,
EVENT_SYSTEM_HEALTH_UPDATE,
EVENT_TYPE_ALL = 0xFF,
EVENT_TYPE_COUNT
} event_type_t;
```
### 4.2 Event Structure
```c
typedef struct {
event_type_t type;
uint64_t timestamp;
size_t payload_size;
uint8_t payload[]; // Variable-length payload
} event_t;
```
## 5. Threading Model
- **Owner Task:** Event System Task (MEDIUM priority) or shared with components
- **Thread Safety:** Thread-safe (lock-free queue for publishing, mutex for subscription management)
- **Blocking Operations:** None (all operations are non-blocking)
- **ISR Access:** Publish allowed from ISR (lock-free queue)
## 6. Resource Ownership
- **Event Queue:** Owned by Event System (lock-free ring buffer)
- **Subscriber List:** Protected by mutex
- **Event Buffers:** Pre-allocated static buffers (no dynamic allocation)
## 7. Error Model
### 7.1 Error Conditions
| Error | Condition | Response |
|-------|-----------|----------|
| `EVENT_ERR_QUEUE_FULL` | Event queue full | Drop oldest event, log warning |
| `EVENT_ERR_INVALID_TYPE` | Invalid event type | Return false, log warning |
| `EVENT_ERR_PAYLOAD_TOO_LARGE` | Payload exceeds maximum | Return false, log error |
| `EVENT_ERR_SUBSCRIBER_FULL` | Too many subscribers | Return false, log error |
### 7.2 Diagnostics Emitted
- `DIAG-SY-EVENT-0001`: Event queue full (WARNING)
- `DIAG-SY-EVENT-0002`: Event dropped due to queue full (WARNING)
- `DIAG-SY-EVENT-0003`: Subscriber callback failure (ERROR)
## 8. State-Dependent Behavior
- **All States:** Event System operates in all states
- **TEARDOWN State:** Event publishing limited to teardown-related events
- **FAULT State:** Event publishing limited to diagnostic events
## 9. Dependencies
### 9.1 Required Components
- **Logger:** For diagnostic logging
- **Time Utils:** For timestamp generation
### 9.2 Required Interfaces
- Logger interface
- Time Utils interface
## 10. Acceptance Tests
- **T-EVENT-001:** Events are published successfully
- **T-EVENT-002:** Subscribers receive events
- **T-EVENT-003:** Multiple subscribers receive same event
- **T-EVENT-004:** Event publishing is non-blocking
- **T-EVENT-005:** Event queue overflow handling
- **T-EVENT-006:** Event unsubscription works correctly
## 11. Traceability
- **SWR-DESIGN-006:** Event System for cross-component communication
- **CFC-TIME-01:** Non-blocking operation
- **Architecture Requirement:** Event-driven communication
## 12. Implementation Notes
- Event queue SHALL be implemented as lock-free ring buffer
- Maximum queue size: 100 events
- Maximum payload size: 256 bytes
- Maximum subscribers per event type: 10
- Event handlers SHALL be called synchronously (in publisher's context) or asynchronously (in event task context) based on configuration

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