GCC Code Coverage Report


Directory: ./
File: components/parking_system/src/hal/EspGpioInput.cpp
Date: 2025-12-16 19:41:02
Exec Total Coverage
Lines: 0 66 0.0%
Functions: 0 9 0.0%
Branches: 0 26 0.0%

Line Branch Exec Source
1 #include "EspGpioInput.h"
2 #include "esp_log.h"
3 #include "esp_timer.h"
4 #include <cstring>
5 #include <atomic>
6
7 static const char* TAG = "EspGpioInput";
8
9 ✗ EspGpioInput::EspGpioInput(gpio_num_t pin, uint32_t debounceMs)
10 ✗ : m_pin(pin)
11 ✗ , m_debounceMs(debounceMs)
12 ✗ , m_handler(nullptr)
13 ✗ , m_lastInterruptTime(0) {
14 // Configure GPIO: enable internal pull-up by default for stable input
15 ✗ gpio_config_t io_conf = {};
16 ✗ io_conf.pin_bit_mask = (1ULL << pin);
17 ✗ io_conf.mode = GPIO_MODE_INPUT;
18 ✗ io_conf.pull_up_en = GPIO_PULLUP_ENABLE; // Use internal pull-up to avoid floating
19 ✗ io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; // Adjust per sensor if needed
20 ✗ io_conf.intr_type = GPIO_INTR_ANYEDGE; // Interrupt on both edges
21
22 ✗ esp_err_t ret = gpio_config(&io_conf);
23 ✗ if (ret != ESP_OK) {
24 ✗ ESP_LOGE(TAG, "Failed to configure GPIO %d: %s", pin, esp_err_to_name(ret));
25 } else {
26 // Read initial level immediately after configuration
27 ✗ int initial_level = gpio_get_level(pin);
28 ✗ ESP_LOGW(TAG, "*** GPIO %d configured, pull: PULLUP, initial level: %d ***", pin, initial_level);
29 }
30 ✗ }
31
32 ✗ EspGpioInput::~EspGpioInput() {
33 ✗ disableInterrupt();
34 ✗ }
35
36 ✗ bool EspGpioInput::getLevel() const {
37 ✗ return gpio_get_level(m_pin) != 0;
38 }
39
40 ✗ void EspGpioInput::setInterruptHandler(std::function<void(bool level)> handler) {
41 ✗ m_handler = std::move(handler);
42 ✗ }
43
44 ✗ void EspGpioInput::enableInterrupt() {
45 ✗ if (!m_handler) {
46 ✗ ESP_LOGW(TAG, "Cannot enable interrupt on GPIO %d: no handler set", m_pin);
47 ✗ return;
48 }
49
50 // Install ISR service if not already installed (done once globally)
51 static bool isr_service_installed = false;
52 ✗ if (!isr_service_installed) {
53 ✗ esp_err_t ret = gpio_install_isr_service(0);
54 ✗ if (ret == ESP_OK) {
55 ✗ isr_service_installed = true;
56 ✗ ESP_LOGI(TAG, "GPIO ISR service installed");
57 ✗ } else if (ret != ESP_ERR_INVALID_STATE) { // Already installed
58 ✗ ESP_LOGE(TAG, "Failed to install ISR service: %s", esp_err_to_name(ret));
59 ✗ return;
60 }
61 }
62
63 // Add ISR handler
64 ✗ esp_err_t ret = gpio_isr_handler_add(m_pin, gpioIsrHandler, this);
65 ✗ if (ret != ESP_OK) {
66 ✗ ESP_LOGE(TAG, "Failed to add ISR handler for GPIO %d: %s", m_pin, esp_err_to_name(ret));
67 } else {
68 ✗ int current_level = gpio_get_level(m_pin);
69 ✗ ESP_LOGW(TAG, "*** ISR enabled GPIO %d, level: %d, debounce: %lu ms ***",
70 m_pin, current_level, m_debounceMs);
71 }
72 }
73
74 ✗ void EspGpioInput::disableInterrupt() {
75 ✗ gpio_isr_handler_remove(m_pin);
76 ✗ ESP_LOGI(TAG, "Interrupt disabled on GPIO %d", m_pin);
77 ✗ }
78
79 ✗ void EspGpioInput::simulateInterrupt(bool level) {
80 ✗ ESP_LOGI(TAG, "Simulating interrupt on GPIO %d with level %d", m_pin, level);
81 ✗ if (m_handler) {
82 ✗ m_handler(level);
83 }
84 ✗ }
85
86 ✗ void IRAM_ATTR EspGpioInput::gpioIsrHandler(void* arg) {
87 ✗ auto* input = static_cast<EspGpioInput*>(arg);
88 ✗ if (input) {
89 // Simple debug: count interrupts (visible in debugger)
90 static std::atomic<uint32_t> isr_count{0};
91 isr_count.fetch_add(1, std::memory_order_relaxed);
92 ✗ input->handleInterrupt();
93 }
94 ✗ }
95
96 ✗ void IRAM_ATTR EspGpioInput::handleInterrupt() {
97 // Debug counter
98 static std::atomic<uint32_t> handle_count{0};
99 handle_count.fetch_add(1, std::memory_order_relaxed);
100
101 // Read current level immediately
102 ✗ bool level = gpio_get_level(m_pin) != 0;
103
104 // Debouncing: check if enough time has passed since last interrupt
105 ✗ if (m_debounceMs > 0) {
106 ✗ int64_t now = esp_timer_get_time();
107 ✗ int64_t elapsed_ms = (now - m_lastInterruptTime) / 1000;
108
109 ✗ if (elapsed_ms < m_debounceMs) {
110 // Debug: count debounced interrupts
111 static std::atomic<uint32_t> debounce_blocked{0};
112 debounce_blocked.fetch_add(1, std::memory_order_relaxed);
113 ✗ return; // Ignore this interrupt (debounce period)
114 }
115
116 ✗ m_lastInterruptTime = now;
117 }
118
119 // Call handler with current level
120 ✗ if (m_handler) {
121 ✗ m_handler(level);
122 }
123 }
124