Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(gpio): Support pin logic invertion (active high / low) for selected pin modes #193

Merged
merged 1 commit into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions code/components/config_handling/cfgDataStruct.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ struct GpioElement {
std::string captureMode = "cyclic-polling";
int inputDebounceTime = 200;
int PwmFrequency = 5000;
bool logicActiveLow = false;
bool exposeToMqtt = false;
bool exposeToRest = false;
struct SmartLed {
Expand Down
6 changes: 6 additions & 0 deletions code/components/config_handling/configClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,10 @@ esp_err_t ConfigClass::parseConfig(httpd_req_t *req, bool init, bool unityTest)
if (cJSON_IsNumber(arrEl))
gpioElTemp->PwmFrequency = std::clamp(arrEl->valueint, 5, 1000000); // Hertz

arrEl = cJSON_GetObjectItem(objArrEl, "logicactivelow");
if (cJSON_IsBool(arrEl))
gpioElTemp->logicActiveLow = arrEl->valueint;

arrEl = cJSON_GetObjectItem(objArrEl, "exposetomqtt");
if (cJSON_IsBool(arrEl))
gpioElTemp->exposeToMqtt = arrEl->valueint;
Expand Down Expand Up @@ -1932,6 +1936,8 @@ esp_err_t ConfigClass::serializeConfig(bool unityTest)
retVal = ESP_FAIL;
if (cJSON_AddNumberToObject(gpiopinEl, "pwmfrequency", cfgDataTemp.sectionGpio.gpioPin[i].PwmFrequency) == NULL)
retVal = ESP_FAIL;
if (cJSON_AddBoolToObject(gpiopinEl, "logicactivelow", cfgDataTemp.sectionGpio.gpioPin[i].logicActiveLow) == NULL)
retVal = ESP_FAIL;
if (cJSON_AddBoolToObject(gpiopinEl, "exposetomqtt", cfgDataTemp.sectionGpio.gpioPin[i].exposeToMqtt) == NULL)
retVal = ESP_FAIL;
if (cJSON_AddBoolToObject(gpiopinEl, "exposetorest", cfgDataTemp.sectionGpio.gpioPin[i].exposeToRest) == NULL)
Expand Down
24 changes: 10 additions & 14 deletions code/components/gpio_ctrl/gpioControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,15 +324,17 @@ esp_err_t GpioHandler::loadParameter()
LogFile.writeToFile(ESP_LOG_DEBUG, TAG, "Pin Config: GPIO" + std::to_string((int)gpioNr) +
", Name: " + std::string(gpioName) + ", Mode: " + pin.pinMode + ", Interrupt Type: " +
pin.captureMode + ", Debounce Time: " + std::to_string(pin.inputDebounceTime) + ", Frequency: " +
std::to_string(pin.PwmFrequency) + ", HTTP Access: " + std::to_string(pin.exposeToRest) +
", MQTT Access: " + std::to_string(mqttAccess) +", MQTT Topic: " + mqttTopic +
std::to_string(pin.PwmFrequency) + ", Logic Active Low: " + std::to_string(pin.logicActiveLow) +
", HTTP Access: " + std::to_string(pin.exposeToRest) +
", MQTT Access: " + std::to_string(mqttAccess) + ", MQTT Topic: " + mqttTopic +
", LED Type: " + std::to_string(pin.smartLed.type) + ", LED Quantity: " + std::to_string(pin.smartLed.quantity) +
", LED Color: R:" + std::to_string(LEDColor.r) + " | G:" + std::to_string(LEDColor.g) +
" | B:" + std::to_string(LEDColor.b) + ", LED Intensity Correction: " +
std::to_string(pin.intensityCorrectionFactor));

GpioPin* gpioPin = new GpioPin(gpioNr, gpioName, pinMode, captureMode, pin.inputDebounceTime, pin.PwmFrequency, pin.exposeToRest,
mqttAccess, mqttTopic, LEDType, pin.smartLed.quantity, LEDColor, pin.intensityCorrectionFactor);
GpioPin* gpioPin = new GpioPin(gpioNr, gpioName, pinMode, captureMode, pin.inputDebounceTime, pin.PwmFrequency,
pin.logicActiveLow, pin.exposeToRest, mqttAccess, mqttTopic, LEDType, pin.smartLed.quantity,
LEDColor, pin.intensityCorrectionFactor);
(*gpioMap)[gpioNr] = gpioPin;
}

Expand Down Expand Up @@ -391,7 +393,7 @@ void GpioHandler::gpioFlashlightControl(bool _state, int _intensity)
int intensityValueCorrected = std::min(std::max(0, it->second->getIntensityCorrection() *
_intensity * dutyResultionMaxValue / 10000), dutyResultionMaxValue);

esp_err_t retVal = it->second->setPinState(_state, intensityValueCorrected, GPIO_SET_SOURCE_INTERNAL);
esp_err_t retVal = it->second->setPinState(_state, intensityValueCorrected);

if (retVal != ESP_OK) {
LogFile.writeToFile(ESP_LOG_DEBUG, TAG, "Flashlight PWM: GPIO" + std::to_string((int)it->first) +
Expand Down Expand Up @@ -446,22 +448,16 @@ void GpioHandler::gpioFlashlightControl(bool _state, int _intensity)
}
}
else if (it->second->getMode() == GPIO_PIN_MODE_FLASHLIGHT_DIGITAL) {
esp_err_t retVal = it->second->setPinState(_state, GPIO_SET_SOURCE_INTERNAL);
esp_err_t retVal = it->second->setPinState(it->second->getLogicLevelActiveLow() ? !_state : _state);

if (retVal != ESP_OK) {
LogFile.writeToFile(ESP_LOG_DEBUG, TAG, "Flashlight Digital: GPIO" + std::to_string((int)it->first) +
" failed to set state | Error: " + intToHexString(retVal));
return;
}

if (_state) {
LogFile.writeToFile(ESP_LOG_DEBUG, TAG, "Flashlight Digital: GPIO" + std::to_string((int)it->first) +
", State: " + std::to_string(_state));
}
else {
LogFile.writeToFile(ESP_LOG_DEBUG, TAG, "Flashlight Digital: GPIO" + std::to_string((int)it->first) +
", State: " + std::to_string(_state));
}
LogFile.writeToFile(ESP_LOG_DEBUG, TAG, "Flashlight Digital: GPIO" + std::to_string((int)it->first) +
", State: " + std::to_string(it->second->getLogicLevelActiveLow() ? !_state : _state));
}
}
}
Expand Down
60 changes: 34 additions & 26 deletions code/components/gpio_ctrl/gpioPin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ extern QueueHandle_t gpio_queue_handle;
// GPIO Pin
//********************************************************************************
GpioPin::GpioPin(gpio_num_t _gpio, const char* _name, gpio_pin_mode_t _mode, gpio_int_type_t _interruptType,
int _debounceTime, int _frequency, bool _httpAccess, bool _mqttAccess, std::string _mqttTopic,
LedType _LEDType, int _LEDQuantity, Rgb _LEDColor, int _intensityCorrection)
int _debounceTime, int _frequency, bool _logicLevelActiveLow, bool _httpAccess, bool _mqttAccess,
std::string _mqttTopic, LedType _LEDType, int _LEDQuantity, Rgb _LEDColor, int _intensityCorrection)
{
gpioISR.gpio = gpio = _gpio;
name = _name;
mode = _mode;
interruptType = mode == GPIO_PIN_MODE_TRIGGER_CYCLE_START ||
mode == GPIO_PIN_MODE_RESUME_WLAN_CONNECTION ? GPIO_INTR_ANYEDGE : _interruptType;
gpioISR.debounceTime = mode == GPIO_PIN_MODE_TRIGGER_CYCLE_START ||
mode == GPIO_PIN_MODE_RESUME_WLAN_CONNECTION? 1000 : _debounceTime;
gpioISR.debounceTime = _debounceTime;
frequency = _frequency;
logicLevelActiveLow = _logicLevelActiveLow;

httpAccess = _httpAccess;
mqttAccess = _mqttAccess;
Expand All @@ -58,8 +58,9 @@ GpioPin::~GpioPin()

gpio_reset_pin(gpio);

if (gpio == GPIO_FLASHLIGHT_DEFAULT)
if (gpio == GPIO_FLASHLIGHT_DEFAULT) {
gpio_set_direction((gpio_num_t)GPIO_FLASHLIGHT_DEFAULT, GPIO_MODE_OUTPUT);
}
}


Expand All @@ -77,8 +78,9 @@ static void IRAM_ATTR gpioPinISRHandler(void* arg)

xQueueSendToBackFromISR(gpio_queue_handle, (void*)&gpioResult, &ContextSwitchRequest);

if (ContextSwitchRequest)
if (ContextSwitchRequest) {
taskYIELD();
}
}

lastInterruptTime = interruptTime;
Expand All @@ -87,48 +89,54 @@ static void IRAM_ATTR gpioPinISRHandler(void* arg)

void GpioPin::init()
{
gpio_config_t io_conf;
gpio_config_t gpioConfig;

//set interrupt
io_conf.intr_type = mode == GPIO_PIN_MODE_INPUT || mode == GPIO_PIN_MODE_INPUT_PULLUP ||
gpioConfig.intr_type = mode == GPIO_PIN_MODE_INPUT || mode == GPIO_PIN_MODE_INPUT_PULLUP ||
mode == GPIO_PIN_MODE_INPUT_PULLDOWN || mode == GPIO_PIN_MODE_TRIGGER_CYCLE_START ||
mode == GPIO_PIN_MODE_RESUME_WLAN_CONNECTION ? interruptType : GPIO_INTR_DISABLE;

//set input / output mode
io_conf.mode = mode == GPIO_PIN_MODE_OUTPUT || mode == GPIO_PIN_MODE_OUTPUT_PWM ||
gpioConfig.mode = mode == GPIO_PIN_MODE_OUTPUT || mode == GPIO_PIN_MODE_OUTPUT_PWM ||
mode == GPIO_PIN_MODE_FLASHLIGHT_PWM || mode == GPIO_PIN_MODE_FLASHLIGHT_SMARTLED ||
mode == GPIO_PIN_MODE_FLASHLIGHT_DIGITAL ? gpio_mode_t::GPIO_MODE_OUTPUT : gpio_mode_t::GPIO_MODE_INPUT;

//bit mask of the pins that you want to set, e.g. GPIO12
io_conf.pin_bit_mask = (1ULL << gpio);
gpioConfig.pin_bit_mask = (1ULL << gpio);

//set pull-down mode
io_conf.pull_down_en = mode == GPIO_PIN_MODE_INPUT_PULLDOWN ?
gpio_pulldown_t::GPIO_PULLDOWN_ENABLE : gpio_pulldown_t::GPIO_PULLDOWN_DISABLE;
gpioConfig.pull_down_en = mode == GPIO_PIN_MODE_INPUT_PULLDOWN || (!logicLevelActiveLow && (mode == GPIO_PIN_MODE_TRIGGER_CYCLE_START ||
mode == GPIO_PIN_MODE_RESUME_WLAN_CONNECTION)) ? gpio_pulldown_t::GPIO_PULLDOWN_ENABLE : gpio_pulldown_t::GPIO_PULLDOWN_DISABLE;

//set pull-up mode
io_conf.pull_up_en = mode == GPIO_PIN_MODE_INPUT_PULLUP || mode == GPIO_PIN_MODE_TRIGGER_CYCLE_START ||
mode == GPIO_PIN_MODE_RESUME_WLAN_CONNECTION ? gpio_pullup_t::GPIO_PULLUP_ENABLE : gpio_pullup_t::GPIO_PULLUP_DISABLE;
gpioConfig.pull_up_en = mode == GPIO_PIN_MODE_INPUT_PULLUP || (logicLevelActiveLow && (mode == GPIO_PIN_MODE_TRIGGER_CYCLE_START ||
mode == GPIO_PIN_MODE_RESUME_WLAN_CONNECTION)) ? gpio_pullup_t::GPIO_PULLUP_ENABLE : gpio_pullup_t::GPIO_PULLUP_DISABLE;

//configure GPIO with the given settings
gpio_config(&io_conf);
gpio_config(&gpioConfig);

if (io_conf.intr_type != GPIO_INTR_DISABLE) {
if (gpioConfig.intr_type != GPIO_INTR_DISABLE) {
LogFile.writeToFile(ESP_LOG_DEBUG, TAG, "Register ISR handler for GPIO" + std::to_string((int)gpioISR.gpio) +
", Debounce time: " + std::to_string(gpioISR.debounceTime));
gpio_isr_handler_add(gpio, gpioPinISRHandler, (void*)&gpioISR); // Hook ISR handler for specific gpio pin
}

pinState = (io_conf.pull_up_en == gpio_pullup_t::GPIO_PULLUP_ENABLE) ? 1 : 0;
// Set initial pin state (inputs: read actual pin state, outputs: 0)
updatePinState();

// Preset flashlight output if active low is activated
if ((mode == GPIO_PIN_MODE_FLASHLIGHT_DIGITAL) && logicLevelActiveLow) {
setPinState(true);
}

#ifdef ENABLE_MQTT
#ifdef ENABLE_MQTT
if (mqttAccess && (mode == GPIO_PIN_MODE_OUTPUT || mode == GPIO_PIN_MODE_OUTPUT_PWM)) {
// Subcribe to [mainTopic]/device/gpio/[GpioName]/ctrl
std::function<bool(std::string, char*, int)> func = std::bind(&GpioPin::mqttControlPinState, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3);
registerMqttSubscribeFunction(mqttTopic + "/ctrl", func);
}
#endif //ENABLE_MQTT
#endif //ENABLE_MQTT
}


Expand All @@ -145,7 +153,7 @@ void GpioPin::updatePinState(int _state)
", State: " + std::to_string(pinState));

// Handle special modes
if (pinState == 0) { // Pullup enabled, trigger with falling edge / low level
if ((logicLevelActiveLow && pinState == 0) || (!logicLevelActiveLow && pinState == 1)) {
if (mode == GPIO_PIN_MODE_TRIGGER_CYCLE_START) {
triggerFlowStartByGpio();
}
Expand All @@ -154,9 +162,9 @@ void GpioPin::updatePinState(int _state)
}
}

#ifdef ENABLE_MQTT
#ifdef ENABLE_MQTT
mqttPublishPinState();
#endif
#endif
}
}

Expand All @@ -170,9 +178,9 @@ esp_err_t GpioPin::setPinState(bool _value, gpio_set_source _setSource)
pinState = _value;
esp_err_t retVal = gpio_set_level(gpio, _value);

#ifdef ENABLE_MQTT
#ifdef ENABLE_MQTT
mqttPublishPinState();
#endif //ENABLE_MQTT
#endif //ENABLE_MQTT

return retVal;
}
Expand All @@ -195,9 +203,9 @@ esp_err_t GpioPin::setPinState(bool _value, int _intensity, gpio_set_source _set
ledc_update_duty(LEDC_LOW_SPEED_MODE, ledcChannel); // Apply the new value
}

#ifdef ENABLE_MQTT
#ifdef ENABLE_MQTT
mqttPublishPinState(_intensity);
#endif //ENABLE_MQTT
#endif //ENABLE_MQTT

return ESP_OK;
}
Expand Down
10 changes: 6 additions & 4 deletions code/components/gpio_ctrl/gpioPin.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class GpioPin
gpio_int_type_t interruptType;
int frequency;
GpioISR gpioISR;
bool logicLevelActiveLow;

bool httpAccess = false;
bool mqttAccess = false;
Expand All @@ -71,14 +72,14 @@ class GpioPin

public:
GpioPin(gpio_num_t _gpio, const char* _name, gpio_pin_mode_t _mode, gpio_int_type_t _interruptType,
int _debounceTime, int _frequency, bool _httpAccess, bool _mqttAccess, std::string _mqttTopic,
LedType _LEDType, int _LEDQuantity, Rgb _LEDColor, int _intensityCorrection);
int _debounceTime, int _frequency, bool _logicLevelActiveLow, bool _httpAccess, bool _mqttAccess,
std::string _mqttTopic, LedType _LEDType, int _LEDQuantity, Rgb _LEDColor, int _intensityCorrection);
~GpioPin();
void init();

void updatePinState(int state = -1);
esp_err_t setPinState(bool _state, gpio_set_source _setSource);
esp_err_t setPinState(bool _state, int _ledIntensity, gpio_set_source _setSource);
esp_err_t setPinState(bool _state, gpio_set_source _setSource = GPIO_SET_SOURCE_INTERNAL);
esp_err_t setPinState(bool _state, int _ledIntensity, gpio_set_source _setSource = GPIO_SET_SOURCE_INTERNAL);
int getPinState();

#ifdef ENABLE_MQTT
Expand All @@ -88,6 +89,7 @@ class GpioPin

gpio_num_t getGPIO() { return gpio; };
gpio_pin_mode_t getMode() { return mode; };
bool getLogicLevelActiveLow() { return logicLevelActiveLow; };
gpio_int_type_t getInterruptType() { return interruptType; };
int getFrequency() { return frequency; };
bool getHttpAccess() { return httpAccess; };
Expand Down
12 changes: 6 additions & 6 deletions docs/Configuration/Parameter/GPIO/GPIOPin_CaptureMode.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
| | WebUI | REST API
|:--- |:--- |:----
| Parameter Name | Pin Capture Mode | capturemode
| Default Value | `cyclic polling` | `cyclic-polling`
| Input Options | `cyclic polling`<br>`interrupt rising edge`<br>`interrupt falling edge`<br>`interrupt rising falling` | `cyclic-polling`<br>`interrupt-rising-edge`<br>`interrupt-falling-edge`<br>`interrupt-rising-falling`
| Default Value | `Cyclic Polling` | `cyclic-polling`
| Input Options | `Cyclic Polling`<br>`Interrupt Rising Edge`<br>`Interrupt Falling Edge`<br>`Interrupt Rising+Falling` | `cyclic-polling`<br>`interrupt-rising-edge`<br>`interrupt-falling-edge`<br>`interrupt-rising-falling`



Expand All @@ -16,10 +16,10 @@ This defines how the selected GPIO input is captured internally.

| Input Option | Description
|:--- |:---
| `cyclic polling` | Poll GPIO input state in a predefined interval of 1 second
| `interrupt rising edge` | Capture GPIO input state when a rising edge of signal is detected
| `interrupt falling edge` | Capture GPIO input state when a falling edge of signal is detected
| `interrupt rising falling` | Capture GPIO input state when a rising or falling edge of signal is detected
| `Cyclic Polling` | Poll GPIO input state in a predefined interval of 1 second
| `Interrupt Rising Edge` | Capture GPIO input state when a rising edge of signal is detected
| `Interrupt Falling Edge` | Capture GPIO input state when a falling edge of signal is detected
| `Interrupt Rising+Falling` | Capture GPIO input state when a rising or falling edge of signal is detected


!!! Tip
Expand Down
30 changes: 30 additions & 0 deletions docs/Configuration/Parameter/GPIO/GPIOPin_LogicActiveLow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Parameter: Pin Logic level

| | WebUI | REST API
|:--- |:--- |:----
| Parameter Name | Pin Logic Level | logicactivelow
| Default Value | `Active High` | `false`
| Input Options | `Active High`<br>`Active Low` | `false`<br>`true`



## Description

Defines the pin logic level configuration.


| Input Option | Description
|:--- |:---
| `Active High` | Active state when signal level is high (positive logic, activate by pulling pin to supply level (0 -> 1))
| `Active Low` | Active state when signal level is low (negative logic, activate by pulling pin to ground level (1 -> 0))


!!! Note
This option is only available for pin modes `Flashlight Digital`,
`Trigger Cycle Start` and `Resume WLAN Connection`.


!!! Note
External wiring of the respective gpio pin requires to match internal signal level
configuration. Especially verifiy the external pullup or pulldown setup.

Loading