diff --git a/.gitmodules b/.gitmodules index 60ccfed9e..f3c35ed31 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "code/components/esp-tflite-micro"] path = code/components/esp-tflite-micro url = https://github.com/espressif/esp-tflite-micro.git +[submodule "code/components/smartleds"] + path = code/components/smartleds + url = https://github.com/RoboticsBrno/SmartLeds.git diff --git a/code/components/jomjol_controlGPIO/CMakeLists.txt b/code/components/jomjol_controlGPIO/CMakeLists.txt index b287de81e..8c7cebabc 100644 --- a/code/components/jomjol_controlGPIO/CMakeLists.txt +++ b/code/components/jomjol_controlGPIO/CMakeLists.txt @@ -1,9 +1,7 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) -list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) - idf_component_register(SRCS ${app_sources} INCLUDE_DIRS "." "../../include" - REQUIRES esp_http_server jomjol_logfile jomjol_configfile jomjol_mqtt jomjol_flowcontroll) + REQUIRES smartleds esp_http_server jomjol_logfile jomjol_configfile jomjol_mqtt jomjol_flowcontroll) diff --git a/code/components/jomjol_controlGPIO/Color.cpp b/code/components/jomjol_controlGPIO/Color.cpp deleted file mode 100644 index e1d1eda4b..000000000 --- a/code/components/jomjol_controlGPIO/Color.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "Color.h" -#include -#include -#include - -namespace { - -// Int -> fixed point -int up( int x ) { return x * 255; } - -} // namespace - -int iRgbSqrt( int num ) { - // https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_.28base_2.29 - assert( "sqrt input should be non-negative" && num >= 0 ); - assert( "sqrt input should no exceed 16 bits" && num <= 0xFFFF ); - int res = 0; - int bit = 1 << 16; - while ( bit > num ) - bit >>= 2; - while ( bit != 0 ) { - if ( num >= res + bit ) { - num -= res + bit; - res = ( res >> 1 ) + bit; - } else - res >>= 1; - bit >>= 2; - } - return res; -} - -Rgb::Rgb(const Hsv& y ) { - // https://stackoverflow.com/questions/24152553/hsv-to-rgb-and-back-without-floating-point-math-in-python - // greyscale - if( y.s == 0 ) { - r = g = b = y.v; - return; - } - - const int region = y.h / 43; - const int remainder = ( y.h - ( region * 43 ) ) * 6; - - const int p = ( y.v * ( 255 - y.s ) ) >> 8; - const int q = ( y.v * ( 255 - ( ( y.s * remainder ) >> 8 ) ) ) >> 8; - const int t = ( y.v * ( 255 - ( ( y.s * (255 -remainder ) ) >> 8 ) ) ) >> 8; - - switch( region ) { - case 0: r = y.v; g = t; b = p; break; - case 1: r = q; g = y.v; b = p; break; - case 2: r = p; g = y.v; b = t; break; - case 3: r = p; g = q; b = y.v; break; - case 4: r = t; g = p; b = y.v; break; - case 5: r = y.v; g = p; b = q; break; - default: __builtin_trap(); - } - - a = y.a; -} - -Rgb& Rgb::operator=( const Hsv& hsv ) { - Rgb r{ hsv }; - swap( r ); - return *this; -} - -Rgb Rgb::operator+( const Rgb& in ) const { - auto copy = *this; - copy += in; - return copy; -} - -Rgb& Rgb::operator+=( const Rgb& in ) { - unsigned int red = r + in.r; - r = ( red < 255 ) ? red : 255; - unsigned int green = g + in.g; - g = ( green < 255 ) ? green : 255; - unsigned int blue = b + in.b; - b = ( blue < 255 ) ? blue : 255; - return *this; -} - -Rgb Rgb::operator-( const Rgb& in ) const { - auto copy = *this; - copy -= in; - return copy; -} - -Rgb& Rgb::operator-=( const Rgb& in ) { - r = ( in.r > r ) ? 0 : r - in.r; - g = ( in.g > g ) ? 0 : g - in.g; - b = ( in.b > b ) ? 0 : b - in.b; - return *this; -} - -Rgb& Rgb::blend( const Rgb& in ) { - unsigned int inAlpha = in.a * ( 255 - a ); - unsigned int alpha = a + inAlpha; - r = iRgbSqrt( ( ( r * r * a ) + ( in.r * in.r * inAlpha ) ) / alpha ); - g = iRgbSqrt( ( ( g * g * a ) + ( in.g * in.g * inAlpha ) ) / alpha ); - b = iRgbSqrt( ( ( b * b * a ) + ( in.b * in.b * inAlpha ) ) / alpha ); - a = alpha; - return *this; -} - -uint8_t Rgb::getGrb( int idx ) const { - switch ( idx ) { - case 0: return g; - case 1: return r; - case 2: return b; - } - __builtin_unreachable(); -} - -Hsv::Hsv( const Rgb& r ) { - int min = std::min( r.r, std::min( r.g, r.b ) ); - int max = std::max( r.r, std::max( r.g, r.b ) ); - int chroma = max - min; - - v = max; - if ( chroma == 0 ) { - h = s = 0; - return; - } - - s = up( chroma ) / max; - int hh; - if ( max == r.r ) - hh = ( up( int( r.g ) - int( r.b ) ) ) / chroma / 6; - else if ( max == r.g ) - hh = 255 / 3 + ( up( int( r.b ) - int( r.r ) ) ) / chroma / 6; - else - hh = 2 * 255 / 3 + ( up( int( r.r ) - int( r.g ) ) ) / chroma / 6; - - if ( hh < 0 ) - hh += 255; - h = hh; - - a = r.a; -} - -Hsv& Hsv::operator=( const Rgb& rgb ) { - Hsv h{ rgb }; - swap( h ); - return *this; -} diff --git a/code/components/jomjol_controlGPIO/Color.h b/code/components/jomjol_controlGPIO/Color.h deleted file mode 100644 index d6aaeaa64..000000000 --- a/code/components/jomjol_controlGPIO/Color.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include -#include "esp_attr.h" -union Hsv; - -union Rgb { - struct __attribute__ ((packed)) { - uint8_t g, r, b, a; - }; - uint32_t value; - - Rgb( uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 255 ) : g( g ), r( r ), b( b ), a( a ) {} - Rgb( const Hsv& c ); - Rgb(const Rgb&) = default; - Rgb& operator=( const Rgb& rgb ) { swap( rgb ); return *this; } - Rgb& operator=( const Hsv& hsv ); - Rgb operator+( const Rgb& in ) const; - Rgb& operator+=( const Rgb& in ); - Rgb operator-(const Rgb& in) const; - Rgb &operator-=(const Rgb& in); - bool operator==(const Rgb& in ) const { return in.value == value; } - Rgb& blend( const Rgb& in ); - void swap( const Rgb& o ) { value = o.value; } - void linearize() { - r = channelGamma(r); - g = channelGamma(g); - b = channelGamma(b); - } - - uint8_t getGrb( int idx ) const; - - void stretchChannels( uint8_t maxR, uint8_t maxG, uint8_t maxB ) { - r = stretch( r, maxR ); - g = stretch( g, maxG ); - b = stretch( b, maxB ); - } - - void stretchChannelsEvenly( uint8_t max ) { - stretchChannels( max, max, max ); - } - -private: - uint8_t stretch( int value, uint8_t max ) { - return ( value * max ) >> 8; - } - - uint8_t channelGamma( int channel ) { - /* The optimal gamma correction is x^2.8. However, this is expensive to - * compute. Therefore, we use x^3 for gamma correction. Also, we add a - * bias as the WS2812 LEDs do not turn on for values less than 4. */ - if (channel == 0) - return channel; - channel = channel * channel * channel * 251; - channel >>= 24; - return static_cast< uint8_t >( 4 + channel ); - } -}; - -union Hsv { - struct __attribute__ ((packed)) { - uint8_t h, s, v, a; - }; - uint32_t value; - - Hsv( uint8_t h, uint8_t s = 0, uint8_t v = 0, uint8_t a = 255 ) : h( h ), s( s ), v( v ), a( a ) {} - Hsv( const Rgb& r ); - Hsv& operator=( const Hsv& h ) { swap( h ); return *this; } - Hsv& operator=( const Rgb& rgb ); - bool operator==( const Hsv& in ) const { return in.value == value; } - void swap( const Hsv& o ) { value = o.value; } -}; diff --git a/code/components/jomjol_controlGPIO/GpioControl.cpp b/code/components/jomjol_controlGPIO/GpioControl.cpp new file mode 100644 index 000000000..890082b3b --- /dev/null +++ b/code/components/jomjol_controlGPIO/GpioControl.cpp @@ -0,0 +1,924 @@ +#include "GpioControl.h" + +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" + +#include "esp_log.h" +#include "cJSON.h" + +#include +#include + +#include "ClassLogFile.h" +#include "configFile.h" +#include "Helper.h" + +#ifdef ENABLE_MQTT +#include "interface_mqtt.h" +#include "server_mqtt.h" +#endif //ENABLE_MQTT + + +static const char *TAG = "GPIOCTRL"; + +static GpioHandler *gpioHandler = NULL; +QueueHandle_t gpio_queue_handle = NULL; + +static const gpio_num_t gpio_spare[] {GPIO_SPARE_1, GPIO_SPARE_2, GPIO_SPARE_3, GPIO_SPARE_4, GPIO_SPARE_5, GPIO_SPARE_6}; +static const char* gpio_spare_usage[] {GPIO_SPARE_1_USAGE, GPIO_SPARE_2_USAGE, GPIO_SPARE_3_USAGE, + GPIO_SPARE_4_USAGE, GPIO_SPARE_5_USAGE, GPIO_SPARE_6_USAGE}; + + +GpioHandler::GpioHandler(std::string _configFileName, httpd_handle_t _httpServer) +{ + configFileName = _configFileName; + httpServer = _httpServer; + + registerGpioUri(); +} + + +GpioHandler::~GpioHandler() +{ + gpioFlashlightControl(false, 0); // Flashlight off + + if (gpioMap != NULL) { + clear(); + delete gpioMap; + } +} + + +void GpioHandler::gpioPinInterrupt(GpioResult* gpioResult) +{ + if ((gpioMap != NULL) && (gpioMap->find(gpioResult->gpio) != gpioMap->end())) { + (*gpioMap)[gpioResult->gpio]->updatePinState(gpioResult->state); + } +} + + +static void gpioHandlerTask(void *arg) +{ + while(1) { + if(uxQueueMessagesWaiting(gpio_queue_handle)) { + while(uxQueueMessagesWaiting(gpio_queue_handle)) { + GpioResult gpioResult; + xQueueReceive(gpio_queue_handle, (void*)&gpioResult, 5); + //LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Pin interrupt: GPIO" + std::to_string((int)gpioResult.gpio) + + // ", State: " + std::to_string(gpioResult.state)); + ((GpioHandler*)arg)->gpioPinInterrupt(&gpioResult); + } + } + + ((GpioHandler*)arg)->gpioInputStatePolling(); + vTaskDelay(pdMS_TO_TICKS(1000)); + } +} + + +void GpioHandler::gpioInputStatePolling() +{ + if (gpioMap != NULL) { + for(std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) { + if (it->second->getMode() == GPIO_PIN_MODE_INPUT || it->second->getMode() == GPIO_PIN_MODE_INPUT_PULLUP || + it->second->getMode() == GPIO_PIN_MODE_INPUT_PULLDOWN || it->second->getMode() == GPIO_PIN_MODE_TRIGGER_CYCLE_START) + { + it->second->updatePinState(); + } + } + } +} + + +void GpioHandler::ledcInitGpio(ledc_timer_t _timer, ledc_channel_t _channel, int _gpioNum, int _frequency) +{ + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Init LEDC timer " + std::to_string((int)_timer) + + ", Frequency: " + std::to_string(_frequency) + + ", Duty Resolution: " + std::to_string((int)calcDutyResolution(_frequency))); + + // Prepare and then apply the LEDC PWM timer configuration + ledc_timer_config_t ledc_timer = { }; + + ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE; + ledc_timer.timer_num = _timer; + ledc_timer.duty_resolution = calcDutyResolution(_frequency); + ledc_timer.freq_hz = _frequency; + ledc_timer.clk_cfg = LEDC_USE_APB_CLK; + + esp_err_t retVal = ledc_timer_config(&ledc_timer); + + if (retVal != ESP_OK) + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to init LEDC timer " + + std::to_string((int)_timer) + ", Error: " +intToHexString(retVal)); + + // Prepare and then apply the LEDC PWM channel configuration + ledc_channel_config_t ledc_channel = { }; + + ledc_channel.speed_mode = LEDC_LOW_SPEED_MODE; + ledc_channel.channel = _channel; + ledc_channel.timer_sel = _timer; + ledc_channel.intr_type = LEDC_INTR_DISABLE; + ledc_channel.gpio_num = _gpioNum; + ledc_channel.duty = 0; // Set duty to 0% + ledc_channel.hpoint = 0; + + retVal = ledc_channel_config(&ledc_channel); + + if (retVal != ESP_OK) + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to init LEDC channel " + + std::to_string((int)_channel) + ", Error: " +intToHexString(retVal)); +} + + +bool GpioHandler::init() +{ + if (gpioMap == NULL) { + gpioMap = new std::map(); + } + else { + clear(); + } + + int retVal = readConfig(); + + if (retVal == -1) { // Error state + clear(); + delete gpioMap; + gpioMap = NULL; + return false; + } + else if (retVal == 0) { // GPIO disabled + return true; + } + + int smartLedChannel = 0; // max. 8 channels + int ledcChannel = 1; // max 8 channels (CH0: camera, CH1 - CH7: spare) + bool initHandlerTask = false; + + for(std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) { + it->second->init(); + + if (it->second->getMode() == GPIO_PIN_MODE_FLASHLIGHT_SMARTLED) { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Init SmartLED (Flashlight): GPIO" + std::to_string((int)it->second->getGPIO())); + it->second->setSmartLed(new SmartLed(it->second->getLEDType(), it->second->getLEDQuantity(), + it->second->getGPIO(), smartLedChannel, DoubleBuffer)); + smartLedChannel++; + if (smartLedChannel == detail::CHANNEL_COUNT) { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Insufficient SmartLED channels"); + return false; + } + } + else if (it->second->getMode() == GPIO_PIN_MODE_FLASHLIGHT_PWM) { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Init PWM (Flashlight): GPIO" + std::to_string((int)it->second->getGPIO())); + + ledc_timer_t timer = getFreeTimer(it->second->getFrequency()); + if (timer == LEDC_TIMER_MAX) { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Insufficient LEDC timer"); + return false; + } + + ledcInitGpio(timer, (ledc_channel_t)ledcChannel, it->second->getGPIO(), it->second->getFrequency()); + it->second->setLedcChannel(static_cast(ledcChannel)); + ledcChannel++; + if (ledcChannel == LEDC_CHANNEL_MAX) { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Insufficient LEDC channels"); + return false; + } + } + else if (it->second->getMode() == GPIO_PIN_MODE_OUTPUT_PWM) { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Init PWM (GPIO output): GPIO" + std::to_string((int)it->second->getGPIO())); + + ledc_timer_t timer = getFreeTimer(it->second->getFrequency()); + if (timer == LEDC_TIMER_MAX) { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Insufficient LEDC timer"); + return false; + } + + ledcInitGpio(timer, (ledc_channel_t)ledcChannel, it->second->getGPIO(), it->second->getFrequency()); + it->second->setLedcChannel(static_cast(ledcChannel)); + ledcChannel++; + if (ledcChannel == LEDC_CHANNEL_MAX) { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Insufficient LEDC channels"); + return false; + } + } + + // Handler task is only needed to maintain input pin state (interrupt or polling) + if (it->second->getMode() == GPIO_PIN_MODE_INPUT || it->second->getMode() == GPIO_PIN_MODE_INPUT_PULLUP || + it->second->getMode() == GPIO_PIN_MODE_INPUT_PULLDOWN || it->second->getMode() == GPIO_PIN_MODE_TRIGGER_CYCLE_START) + { + initHandlerTask = true; + } + + } + + #ifdef ENABLE_MQTT + std::function f = std::bind(&GpioHandler::handleMQTTconnect, this); + MQTTregisterConnectFunction("gpioHandler", f); + #endif //ENABLE_MQTT + + // Handler task is only needed to maintain input pin state (interrupt or polling) + if (initHandlerTask && xHandleTaskGpio == NULL) { + gpio_queue_handle = xQueueCreate(10, sizeof(GpioResult)); + BaseType_t xReturned = xTaskCreate(&gpioHandlerTask, "gpioHandlerTask", 3 * 1024, (void *)this, + tskIDLE_PRIORITY + 4, &xHandleTaskGpio); + + if (xReturned != pdPASS ) { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create gpioHandlerTask"); + return false; + } + } + + return true; +} + + +int GpioHandler::readConfig() +{ + ConfigFile configFile = ConfigFile(configFileName); + std::string line = ""; + bool disabledLine = false; + bool eof = false; + + #ifdef ENABLE_MQTT + const std::string gpioMQTTMainTopic = mqttServer_getMainTopic() + "/device/gpio"; + #endif // ENABLE_MQTT + + isEnabled = false; + + while ((!configFile.GetNextParagraph(line, disabledLine, eof) || (line.compare("[GPIO]") != 0)) && !eof) {} + + if (disabledLine || eof) { + #if defined(GPIO_FLASHLIGHT_DEFAULT_USE_SMARTLED) + // Special case: Flashlight default uses SmartLED functionality -> init smartLED functionality only + for (int i= 0; i < GPIO_SPARE_PIN_COUNT; ++i) { + if (strcmp(gpio_spare_usage[i], FLASHLIGHT_SMARTLED) == 0) { + GpioPin* gpioPin = new GpioPin((gpio_num_t)gpio_spare[i], ("gpio" + std::to_string((int)gpio_spare[i])).c_str(), + GPIO_PIN_MODE_FLASHLIGHT_SMARTLED, GPIO_INTR_DISABLE, 200, 5000, false, false, "", + GPIO_FLASHLIGHT_DEFAULT_SMARTLED_TYPE, GPIO_FLASHLIGHT_DEFAULT_SMARTLED_QUANTITY, + Rgb{255,255,255}, 100); + (*gpioMap)[(gpio_num_t)gpio_spare[i]] = gpioPin; + return 1; + } + } + + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Default flashlight configured as SmartLED, but no valid GPIO config found"); + return -1; + #endif + + return 0; + } + + std::vector splitted; + bool gpioInstallISR = false; + + while (configFile.getNextLine(&line, disabledLine, eof) && !configFile.isNewParagraph(line)) { + splitted = ZerlegeZeile(line); + + if (splitted[0].find_first_of("IO") > 0) // Parameter disabled + continue; + + if (splitted.size() >= 13) { // Parameter enabled and enough parameter detected + gpio_num_t gpioNr = (gpio_num_t)std::stoi(splitted[0].substr(2, 2).c_str()); + + gpio_pin_mode_t pinMode = resolvePinMode(toLower(splitted[1])); + if (pinMode == GPIO_PIN_MODE_DISABLED) { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "readConfig: Invalid GPIO pin mode"); + return -1; + } + + gpio_int_type_t intType = resolveIntType(toLower(splitted[2])); + if (intType != GPIO_INTR_DISABLE) { + gpioInstallISR = true; + } + + int debounceTime = std::stoi(splitted[3].c_str()); + if (debounceTime < 0 || debounceTime > 5000) { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "readConfig: Debounce time out of range (0 .. 5000ms)"); + return -1; + } + + int frequency = std::stoi(splitted[4].c_str()); + if (frequency < 5 || frequency > 1000000) { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "readConfig: Frequency out of range (5Hz .. 1MHz)"); + return -1; + } + + bool httpAccess = (toUpper(splitted[6]) == "TRUE"); + + LedType LEDType = LED_WS2812; + if (splitted[7] == "WS2812") + LEDType = LED_WS2812; + else if (splitted[7] == "WS2812B") + LEDType = LED_WS2812B; + else if (splitted[7] == "WS2812B-NEW") + LEDType = LED_WS2812B_NEWVARIANT; + else if (splitted[7] == "WS2812B-OLD") + LEDType = LED_WS2812B_OLDVARIANT; + else if (splitted[7] == "SK6812") + LEDType = LED_SK6812; + else if (splitted[7] == "WS2813") + LEDType = LED_WS2813; + else { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "readConfig: Unknown LED type"); + return -1; + } + + int LEDQuantity = std::stoi(splitted[8]); + if (LEDQuantity < 1 ) { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "readConfig: LED quantity out of range (> 1)"); + return -1; + } + Rgb LEDColor = Rgb{(uint8_t)std::stoi(splitted[9]), (uint8_t)std::stoi(splitted[10]), (uint8_t)std::stoi(splitted[11])}; + + int intensityCorrection = std::stoi(splitted[12]); + if (intensityCorrection < 1 || intensityCorrection > 100) { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "readConfig: LED intensity correction out of range (1 .. 100)"); + return -1; + } + + char gpioName[32]; + if (splitted.size() >= 14) + strcpy(gpioName, trim(splitted[13]).c_str()); + else + sprintf(gpioName, "gpio%d", gpioNr); + + #ifdef ENABLE_MQTT + bool mqttAccess = (toUpper(splitted[5]) == "TRUE"); + std::string mqttTopic = mqttAccess ? (gpioMQTTMainTopic + "/" + gpioName) : ""; + #else + bool mqttAccess = false; + std::string mqttTopic = ""; + #endif + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Pin Config: GPIO" + std::to_string((int)gpioNr) + + ", Name: " + std::string(gpioName) + ", Mode: " + splitted[1] + ", Interrupt Type: " + + splitted[2] + ", Debounce Time: " + std::to_string(debounceTime) + ", Frequency: " + + std::to_string(frequency) + ", HTTP Access: " + std::to_string(httpAccess) + + ", MQTT Access: " + std::to_string(mqttAccess) +", MQTT Topic: " + mqttTopic + + ", LED Type: " + splitted[7] + ", LED Quantity: " + std::to_string(LEDQuantity) + + ", 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(intensityCorrection)); + + GpioPin* gpioPin = new GpioPin(gpioNr, gpioName, pinMode, intType, debounceTime, frequency, httpAccess, + mqttAccess, mqttTopic, LEDType, LEDQuantity, LEDColor, intensityCorrection); + (*gpioMap)[gpioNr] = gpioPin; + } + else { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "readConfig: " + splitted[0] + ": Parameter set incomplete"); + return -1; + } + } + + if (gpioInstallISR) + gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM); + + isEnabled = true; + return 1; +} + + +void GpioHandler::clear() +{ + if (gpioMap != NULL) { + for(std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) { + if (it->second->getSmartLed() != NULL) { + delete it->second->getSmartLed(); + it->second->setSmartLed(NULL); + } + + delete it->second; + } + gpioMap->clear(); + } + + frequencyTable.clear(); + + // gpio_uninstall_isr_service(); can't uninstall, ISR service is also used by camera +} + + +void GpioHandler::deinit() +{ + #ifdef ENABLE_MQTT + MQTTunregisterConnectFunction("gpioHandler"); + #endif //ENABLE_MQTT + + clear(); + + if (xHandleTaskGpio != NULL) { + vTaskDelete(xHandleTaskGpio); + xHandleTaskGpio = NULL; + } +} + + +void GpioHandler::gpioFlashlightControl(bool _state, int _intensity) +{ + if (gpioMap == NULL) + return; + + for (std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) { + if (it->second->getMode() == GPIO_PIN_MODE_FLASHLIGHT_PWM) { + int dutyResultionMaxValue = calcDutyResultionMaxValue(it->second->getFrequency()); + 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); + + if (retVal != ESP_OK) { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Flashlight PWM: GPIO" + std::to_string((int)it->first) + + " failed to set state | Error: " + intToHexString(retVal)); + return; + } + + if (_state) { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Flashlight PWM: GPIO" + std::to_string((int)it->first) + + ", State: " + std::to_string(_state) + ", Intensity: " + std::to_string(intensityValueCorrected) + + "/" + std::to_string(dutyResultionMaxValue)); + } + else { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Flashlight PWM: GPIO" + std::to_string((int)it->first) + + ", State: " + std::to_string(_state)); + } + } + else if (it->second->getMode() == GPIO_PIN_MODE_FLASHLIGHT_SMARTLED) { + if (_state) { + int intensityValueCorrected = it->second->getIntensityCorrection() * _intensity * 8191 / 10000; + + Rgb LEDColorIntensityCorrected = it->second->getLEDColor(); + LEDColorIntensityCorrected.r = std::min(std::max(0, (LEDColorIntensityCorrected.r * intensityValueCorrected) / 8191), 255); + LEDColorIntensityCorrected.g = std::min(std::max(0, (LEDColorIntensityCorrected.g * intensityValueCorrected) / 8191), 255); + LEDColorIntensityCorrected.b = std::min(std::max(0, (LEDColorIntensityCorrected.b * intensityValueCorrected) / 8191), 255); + + for (int i = 0; i < it->second->getLEDQuantity(); ++i) { + (*it->second->getSmartLed())[i] = LEDColorIntensityCorrected; + } + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Flashlight SmartLED: GPIO" + std::to_string((int)it->first) + + ", State: " + std::to_string(_state) + ", Intensity: " + std::to_string(intensityValueCorrected) + + "/8191, | R: " + std::to_string(LEDColorIntensityCorrected.r) + + ", G:" + std::to_string(LEDColorIntensityCorrected.g) + + ", B:" + std::to_string(LEDColorIntensityCorrected.b)); + } + else { + for (int i = 0; i < it->second->getLEDQuantity(); ++i) { + (*it->second->getSmartLed())[i] = Rgb{0, 0, 0}; + } + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Flashlight SmartLED: GPIO" + std::to_string((int)it->first) + + ", State: " + std::to_string(_state)); + } + + esp_err_t retVal = it->second->getSmartLed()->show(); + it->second->updatePinState(_state ? 1 : 0); + + if (retVal != ESP_OK) { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Flashlight SmartLED: GPIO" + std::to_string((int)it->first) + + " failed to set state | Error: " + intToHexString(retVal)); + } + } + else if (it->second->getMode() == GPIO_PIN_MODE_FLASHLIGHT_DIGITAL) { + esp_err_t retVal = it->second->setPinState(_state, GPIO_SET_SOURCE_INTERNAL); + + 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)); + } + } + } +} + + +gpio_num_t GpioHandler::resolveSparePinNr(uint8_t _sparePinNr) +{ + for (int i = 0; i < GPIO_SPARE_PIN_COUNT; ++i) { + if (gpio_spare[i] == _sparePinNr) + return gpio_spare[i]; + } + return GPIO_NUM_NC; +} + + +gpio_pin_mode_t GpioHandler::resolvePinMode(std::string input) +{ + if (input == "disabled") + return GPIO_PIN_MODE_DISABLED; + else if (input == "input") + return GPIO_PIN_MODE_INPUT; + if (input == "input-pullup") + return GPIO_PIN_MODE_INPUT_PULLUP; + else if (input == "input-pulldown") + return GPIO_PIN_MODE_INPUT_PULLDOWN; + else if (input == "output") + return GPIO_PIN_MODE_OUTPUT; + else if (input == "output-pwm") + return GPIO_PIN_MODE_OUTPUT_PWM; + else if (input == FLASHLIGHT_PWM) + return GPIO_PIN_MODE_FLASHLIGHT_PWM; + else if (input == FLASHLIGHT_SMARTLED) + return GPIO_PIN_MODE_FLASHLIGHT_SMARTLED; + else if (input == FLASHLIGHT_DIGITAL) + return GPIO_PIN_MODE_FLASHLIGHT_DIGITAL; + else if (input == FLASHLIGHT_DEFAULT) { + #if defined(GPIO_FLASHLIGHT_DEFAULT_USE_PWM) + return GPIO_PIN_MODE_FLASHLIGHT_PWM; + #elif defined(GPIO_FLASHLIGHT_DEFAULT_USE_SMARTLED) + return GPIO_PIN_MODE_FLASHLIGHT_SMARTLED; + #else + return GPIO_PIN_MODE_FLASHLIGHT_DIGITAL; + #endif + } + else if (input == "trigger-cycle-start") + return GPIO_PIN_MODE_TRIGGER_CYCLE_START; + + return GPIO_PIN_MODE_DISABLED; +} + + +std::string GpioHandler::getPinModeDecription(gpio_pin_mode_t _mode) +{ + switch(_mode) { + case 0: + return "disabled"; + case 1: + return "input"; + case 2: + return "input-pullup"; + case 3: + return "input-pulldown"; + case 4: + return "output"; + case 5: + return "output-pwm"; + case 6: + return FLASHLIGHT_PWM; + case 7: + return FLASHLIGHT_SMARTLED; + case 8: + return FLASHLIGHT_DIGITAL; + case 9: + return "trigger-cycle-start"; + default: + return "disabled"; + } +} + + +gpio_int_type_t GpioHandler::resolveIntType(std::string input) +{ + if ( input == "cyclic-polling" ) + return GPIO_INTR_DISABLE; + else if ( input == "interrupt-rising-edge" ) + return GPIO_INTR_POSEDGE; + else if ( input == "interrupt-falling-edge" ) + return GPIO_INTR_NEGEDGE; + else if ( input == "interrupt-rising-falling" ) + return GPIO_INTR_ANYEDGE ; + else if ( input == "interrupt-low-level" ) + return GPIO_INTR_LOW_LEVEL; + else if ( input == "interrupt-high-level" ) + return GPIO_INTR_HIGH_LEVEL; + + return GPIO_INTR_DISABLE; +} + + +std::string GpioHandler::getPinInterruptDecription(gpio_int_type_t _type) +{ + switch(_type) { + case 0: + return "cyclic-polling"; + case 1: + return "interrupt-rising-edge"; + case 2: + return "interrupt-falling-edge"; + case 3: + return "interrupt-rising-falling"; + case 4: + return "interrupt-low-level"; + case 5: + return "interrupt-high-level"; + default: + return "cyclic-polling"; + } +} + + +int GpioHandler::calcDutyResultionMaxValue(int frequency) +{ + return ((1 << calcDutyResolution(frequency)) - 1); +} + + +ledc_timer_bit_t GpioHandler::calcDutyResolution(int frequency) +{ + // Calculate max duty resultion derived from device clock (LEDC_USE_APB_CLK == 80Mhz) + // Limit max duty resolution to 14 bit (due to compability with ESP32S3) + return static_cast(std::min((int)log2(80000000/frequency), 14)); +} + + +ledc_timer_t GpioHandler::getFreeTimer(int _frequency) +{ + auto it = frequencyTable.find(_frequency); + + // Return timer related to already registered frequency + if (it != frequencyTable.end()) + return it->second; + + // Insert new frequency and return timer + if (frequencyTable.size() == 0) { + frequencyTable.insert({_frequency, LEDC_TIMER_1}); + return LEDC_TIMER_1; + } + else if (frequencyTable.size() == 1) { + frequencyTable.insert({_frequency, LEDC_TIMER_2}); + return LEDC_TIMER_2; + } + else if (frequencyTable.size() == 2) { + frequencyTable.insert({_frequency, LEDC_TIMER_3}); + return LEDC_TIMER_3; + } + else { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Insufficient LEDC timer"); + return LEDC_TIMER_MAX; + } +} + + + +// MQTT GPIO state publish +// *********************************** +#ifdef ENABLE_MQTT +void GpioHandler::handleMQTTconnect() +{ + if (gpioMap != NULL) { + for(std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) { + it->second->mqttPublishPinState(); + } + } +} +#endif //ENABLE_MQTT + + +// Handle HTTP GPIO request +// *********************************** +esp_err_t callHandleHttpRequest(httpd_req_t *req) +{ + GpioHandler *gpioHandler = (GpioHandler*)req->user_ctx; + return gpioHandler->handleHttpRequest(req); +} + + +esp_err_t GpioHandler::handleHttpRequest(httpd_req_t *req) +{ + char query[200]; + char value[30]; + std::string respStr = ""; + std::string task = ""; + std::string requestedGpioState = ""; + int requestedGpioNum = -1; + int requestedPWMDuty = -1; + + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + + if (httpd_req_get_url_query_str(req, query, 200) == ESP_OK) { + if (httpd_query_key_value(query, "task", value, sizeof(value)) == ESP_OK) { + task = std::string(value); + } + + if (httpd_query_key_value(query, "gpio", value, sizeof(value)) == ESP_OK) { + requestedGpioNum = std::stoi(std::string(value)); + } + + if (httpd_query_key_value(query, "state", value, sizeof(value)) == ESP_OK) { + requestedGpioState = std::string(value); + } + + if (httpd_query_key_value(query, "pwm_duty", value, sizeof(value)) == ESP_OK) { + requestedPWMDuty = std::stoi(value); + } + } + else { + httpd_resp_set_type(req, "text/html"); + httpd_resp_sendstr(req, "Error in call. Use /gpio?gpio=12&state=1"); + return ESP_OK; + } + + if (task.compare("gpio_config") == 0) { + // Prepare JSON object + esp_err_t retVal = ESP_OK; + cJSON *cJSONObject, *pins, *pin, *mods, *default_config, *pinModes, *pinInterrupt; + + cJSONObject = cJSON_CreateObject(); + if (cJSONObject == NULL) { + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "E91: Error, JSON object cannot be created"); + return ESP_FAIL; + } + + if (!cJSON_AddItemToObject(cJSONObject, "default_config", default_config = cJSON_CreateObject())) + retVal = ESP_FAIL; + + if ((pinModes = cJSON_AddArrayToObject(default_config, "gpio_modes")) == NULL) + retVal = ESP_FAIL; + for (int i = 1; i < GPIO_PIN_MODE_MAX; ++i) { + if (!cJSON_AddItemToArray(pinModes, cJSON_CreateString(getPinModeDecription((gpio_pin_mode_t)i).c_str()))) + retVal = ESP_FAIL; + } + + if ((pinInterrupt = cJSON_AddArrayToObject(default_config, "gpio_interrupt")) == NULL) + retVal = ESP_FAIL; + for (int i = 0; i <= 3; ++i) { + if (!cJSON_AddItemToArray(pinInterrupt, cJSON_CreateString(getPinInterruptDecription((gpio_int_type_t)i).c_str()))) + retVal = ESP_FAIL; + } + + if (!cJSON_AddItemToObject(cJSONObject, "gpio", pins = cJSON_CreateArray())) + retVal = ESP_FAIL; + + for (int i = 0; i < GPIO_SPARE_PIN_COUNT; ++i) { + if (gpio_spare[i] == GPIO_NUM_NC) // Skip not usable GPIOs + continue; + + if (!cJSON_AddItemToArray(pins, pin = cJSON_CreateObject())) + retVal = ESP_FAIL; + if (!cJSON_AddItemToObject(pin, "name", cJSON_CreateNumber(gpio_spare[i]))) + retVal = ESP_FAIL; + if (cJSON_AddStringToObject(pin, "usage", gpio_spare_usage[i]) == NULL) + retVal = ESP_FAIL; + } + + char *jsonString = cJSON_PrintBuffered(cJSONObject, 1024, true); // Print to predefined buffer, avoid dynamic allocations + std::string sReturnMessage = std::string(jsonString); + cJSON_free(jsonString); + cJSON_Delete(cJSONObject); + + if (retVal == ESP_OK) { + httpd_resp_set_type(req, "application/json"); + httpd_resp_sendstr(req, sReturnMessage.c_str()); + return ESP_OK; + } + else { + httpd_resp_set_type(req, "text/plain"); + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "E92: Error while adding JSON elements"); + return ESP_FAIL; + } + } + else if (task.compare("get_state") == 0 || task.compare("set_state") == 0) { + if (gpioMap == NULL || !isEnabled) { + httpd_resp_set_type(req, "text/plain"); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "GPIO handler not initialized or disabled"); + return ESP_FAIL; + } + + gpio_num_t gpioNum = (gpio_num_t)resolveSparePinNr(requestedGpioNum); + if (gpioMap->count(gpioNum) == 0) { + respStr = "Skip request, GPIO not enabled: " + std::to_string(requestedGpioNum); + httpd_resp_set_type(req, "text/plain"); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, respStr.c_str()); + return ESP_FAIL; + } + + if (gpioNum == GPIO_NUM_NC) { + respStr = "Skip request, GPIO number unknown: " + std::to_string(requestedGpioNum); + httpd_resp_set_type(req, "text/plain"); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, respStr.c_str()); + return ESP_OK; + } + + if (!(*gpioMap)[gpioNum]->getHttpAccess()) { + httpd_resp_set_type(req, "text/plain"); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Skip request, HTTP access disabled"); + return ESP_FAIL; + } + + if (task.compare("get_state") == 0) { + requestedGpioState = "{ \"state\": " + std::to_string((*gpioMap)[gpioNum]->getPinState()); + + if ((*gpioMap)[gpioNum]->getMode() == GPIO_PIN_MODE_OUTPUT_PWM || + (*gpioMap)[gpioNum]->getMode() == GPIO_PIN_MODE_FLASHLIGHT_PWM) + { + requestedGpioState += ", \"pwm_duty\": " + std::to_string(ledc_get_duty(LEDC_LOW_SPEED_MODE, + (*gpioMap)[gpioNum]->getLedcChannel())); + } + + requestedGpioState += " }"; + + httpd_resp_set_type(req, "text/plain"); + httpd_resp_sendstr(req, requestedGpioState.c_str()); + return ESP_OK; + } + else if (task.compare("set_state") == 0) { + requestedGpioState = toUpper(requestedGpioState); + if (requestedGpioState != "0" && requestedGpioState != "1") + { + respStr = "Skip request, invalid state: " + requestedGpioState; + httpd_resp_set_type(req, "text/plain"); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, respStr.c_str()); + return ESP_FAIL; + } + + esp_err_t retVal = ESP_OK; + if (requestedPWMDuty == -1) { // Use ON/OFF + retVal = (*gpioMap)[gpioNum]->setPinState(requestedGpioState == "1", GPIO_SET_SOURCE_HTTP); + respStr = "GPIO" + std::to_string(requestedGpioNum) + + ", State: " + std::to_string((*gpioMap)[gpioNum]->getPinState()); + } + else { // Use PWM + requestedPWMDuty = std::min(std::max(0, requestedPWMDuty), calcDutyResultionMaxValue((*gpioMap)[gpioNum]->getFrequency())); + retVal = (*gpioMap)[gpioNum]->setPinState(requestedGpioState == "1", requestedPWMDuty, GPIO_SET_SOURCE_HTTP); + respStr = "GPIO" + std::to_string(requestedGpioNum) + + ", State: " + std::to_string((*gpioMap)[gpioNum]->getPinState()) + + ", PWM Duty: " + std::to_string(requestedPWMDuty); + } + + if (retVal != ESP_OK) { + httpd_resp_set_type(req, "text/plain"); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Skip request, wrong GPIO pin mode"); + return ESP_FAIL; + } + + httpd_resp_set_type(req, "text/plain"); + httpd_resp_sendstr(req, respStr.c_str()); + return ESP_OK; + } + } + else { + httpd_resp_set_type(req, "text/plain"); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "E90: Task not found"); + return ESP_FAIL; + } + + return ESP_OK; +} + + +void GpioHandler::registerGpioUri() +{ + ESP_LOGI(TAG, "Registering URI handlers"); + + httpd_uri_t camuri = { }; + camuri.method = HTTP_GET; + camuri.uri = "/gpio"; + camuri.handler = callHandleHttpRequest; + camuri.user_ctx = (void*)this; + httpd_register_uri_handler(httpServer, &camuri); +} + + +// GPIO handler interface +// *********************************** +void gpio_handler_create(httpd_handle_t _server) +{ + if (gpioHandler == NULL) + gpioHandler = new GpioHandler(CONFIG_FILE, _server); +} + + +bool gpio_handler_init() +{ + if (gpioHandler != NULL) { + return (gpioHandler->init()); + } + return false; +} + + +void gpio_handler_deinit() +{ + if (gpioHandler != NULL) { + gpioHandler->deinit(); + } +} + + +void gpio_handler_destroy() +{ + if (gpioHandler != NULL) { + gpio_handler_deinit(); + delete gpioHandler; + gpioHandler = NULL; + } +} + + +GpioHandler* gpio_handler_get() +{ + return gpioHandler; +} diff --git a/code/components/jomjol_controlGPIO/GpioControl.h b/code/components/jomjol_controlGPIO/GpioControl.h new file mode 100644 index 000000000..bc2aed62a --- /dev/null +++ b/code/components/jomjol_controlGPIO/GpioControl.h @@ -0,0 +1,69 @@ +#ifndef GPIO_CONTROL_H +#define GPIO_CONTROL_H + +#include "../../include/defines.h" + +#include +#include "hal/gpio_types.h" +#include +#include +#include "driver/gpio.h" +#include "driver/ledc.h" + +#include "GpioPin.h" + + +class GpioHandler +{ + private: + std::string configFileName; + httpd_handle_t httpServer; + std::map *gpioMap = NULL; + TaskHandle_t xHandleTaskGpio = NULL; + bool isEnabled = false; + + std::map frequencyTable; + + int readConfig(); + void clear(); + + gpio_num_t resolveSparePinNr(uint8_t _sparePinNr); + gpio_pin_mode_t resolvePinMode(std::string input); + std::string getPinModeDecription(gpio_pin_mode_t _mode); + gpio_int_type_t resolveIntType(std::string input); + std::string getPinInterruptDecription(gpio_int_type_t _type); + + public: + GpioHandler(std::string _configFileName, httpd_handle_t _httpServer); + ~GpioHandler(); + bool init(); + void deinit(); + void ledcInitGpio(ledc_timer_t _timer, ledc_channel_t _channel, int _gpioNum, int _frequency); + bool gpioHandlerIsEnabled() { return isEnabled; }; + + int calcDutyResultionMaxValue(int frequency); + ledc_timer_bit_t calcDutyResolution(int frequency); + ledc_timer_t getFreeTimer(int _frequency); + + void gpioPinInterrupt(GpioResult* gpioResult); + void gpioInputStatePolling(); + + void gpioFlashlightControl(bool _state, int _intensity); + + #ifdef ENABLE_MQTT + void handleMQTTconnect(); + #endif //ENABLE_MQTT + + void registerGpioUri(); + esp_err_t handleHttpRequest(httpd_req_t *req); +}; + +esp_err_t callHandleHttpRequest(httpd_req_t *req); + +void gpio_handler_create(httpd_handle_t server); +bool gpio_handler_init(); +void gpio_handler_deinit(); +void gpio_handler_destroy(); +GpioHandler* gpio_handler_get(); + +#endif //GPIO_CONTROL_H diff --git a/code/components/jomjol_controlGPIO/GpioPin.cpp b/code/components/jomjol_controlGPIO/GpioPin.cpp new file mode 100644 index 000000000..341b20aa9 --- /dev/null +++ b/code/components/jomjol_controlGPIO/GpioPin.cpp @@ -0,0 +1,300 @@ +#include "GpioPin.h" + +#include +#include "freertos/queue.h" +#include "cJSON.h" + +#include "GpioControl.h" +#include "ClassLogFile.h" +#include "Helper.h" +#include "MainFlowControl.h" + +#ifdef ENABLE_MQTT +#include "interface_mqtt.h" +#endif //ENABLE_MQTT + + +static const char *TAG = "GPIOPIN"; + +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) +{ + gpioISR.gpio = gpio = _gpio; + name = _name; + mode = _mode; + interruptType = mode == GPIO_PIN_MODE_TRIGGER_CYCLE_START ? GPIO_INTR_ANYEDGE : _interruptType; + gpioISR.debounceTime = mode == GPIO_PIN_MODE_TRIGGER_CYCLE_START ? 1000 : _debounceTime; + frequency = _frequency; + + httpAccess = _httpAccess; + mqttAccess = _mqttAccess; + mqttTopic = _mqttTopic; + + LEDType = _LEDType; + LEDQuantity = _LEDQuantity; + LEDColor = _LEDColor; + + intensityCorrection = _intensityCorrection; +} + + +GpioPin::~GpioPin() +{ + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Reset GPIO" + std::to_string((int)gpio)); + + if (interruptType != GPIO_INTR_DISABLE) { + gpio_isr_handler_remove(gpio); + } + + gpio_reset_pin(gpio); + + if (gpio == GPIO_FLASHLIGHT_DEFAULT) + gpio_set_direction((gpio_num_t)GPIO_FLASHLIGHT_DEFAULT, GPIO_MODE_OUTPUT); +} + + +static void IRAM_ATTR gpioPinISRHandler(void* arg) +{ + static TickType_t lastInterruptTime = 0; + TickType_t interruptTime = xTaskGetTickCountFromISR(); // Depending on CONFIG_FREERTOS_HZ (100Hz) + + // If interrupts come faster than debounceTime, assume it's a bounce and ignore + if (interruptTime - lastInterruptTime > pdMS_TO_TICKS(((struct GpioISR*)arg)->debounceTime)) { + GpioResult gpioResult; + gpioResult.gpio = ((struct GpioISR*)arg)->gpio; + gpioResult.state = gpio_get_level(gpioResult.gpio); + BaseType_t ContextSwitchRequest = pdFALSE; + + xQueueSendToBackFromISR(gpio_queue_handle, (void*)&gpioResult, &ContextSwitchRequest); + + if (ContextSwitchRequest) + taskYIELD(); + } + + lastInterruptTime = interruptTime; +} + + +void GpioPin::init() +{ + gpio_config_t io_conf; + + //set interrupt + io_conf.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 ? + interruptType : GPIO_INTR_DISABLE; + + //set input / output mode + io_conf.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); + + //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; + + //set pull-up mode + io_conf.pull_up_en = mode == GPIO_PIN_MODE_INPUT_PULLUP || mode == GPIO_PIN_MODE_TRIGGER_CYCLE_START ? + gpio_pullup_t::GPIO_PULLUP_ENABLE : gpio_pullup_t::GPIO_PULLUP_DISABLE; + + //configure GPIO with the given settings + gpio_config(&io_conf); + + if (io_conf.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; + + #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 func = std::bind(&GpioPin::mqttControlPinState, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + MQTTregisterSubscribeFunction(mqttTopic + "/ctrl", func); + } + #endif //ENABLE_MQTT +} + + +void GpioPin::updatePinState(int _state) +{ + int newState = _state; + if (_state == -1) // If no pin state provided, read pin state + newState = gpio_get_level(gpio); + + if (newState != pinState) { + pinState = newState; + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "updatePinState: GPIO" + std::to_string((int)gpio) + + ", State: " + std::to_string(pinState)); + + if (mode == GPIO_PIN_MODE_TRIGGER_CYCLE_START && pinState == 0) // Pullup enabled, trigger with falling edge / low level + triggerFlowStartByGpio(); + + #ifdef ENABLE_MQTT + mqttPublishPinState(); + #endif + } +} + + +esp_err_t GpioPin::setPinState(bool _value, gpio_set_source _setSource) +{ + if (mode != GPIO_PIN_MODE_OUTPUT && mode != GPIO_PIN_MODE_FLASHLIGHT_DIGITAL) { + return ESP_ERR_NOT_SUPPORTED; + } + + pinState = _value; + esp_err_t retVal = gpio_set_level(gpio, _value); + + #ifdef ENABLE_MQTT + mqttPublishPinState(); + #endif //ENABLE_MQTT + + return retVal; +} + + +esp_err_t GpioPin::setPinState(bool _value, int _intensity, gpio_set_source _setSource) +{ + if (mode != GPIO_PIN_MODE_OUTPUT_PWM && mode != GPIO_PIN_MODE_FLASHLIGHT_PWM) { + return ESP_ERR_NOT_SUPPORTED; + } + + pinState = _value; + + if (_value) { + ledc_set_duty(LEDC_LOW_SPEED_MODE, ledcChannel, _intensity); + ledc_update_duty(LEDC_LOW_SPEED_MODE, ledcChannel); // Apply the new value + } + else { + ledc_set_duty(LEDC_LOW_SPEED_MODE, ledcChannel, 0); + ledc_update_duty(LEDC_LOW_SPEED_MODE, ledcChannel); // Apply the new value + } + + #ifdef ENABLE_MQTT + mqttPublishPinState(_intensity); + #endif //ENABLE_MQTT + + return ESP_OK; +} + + +int GpioPin::getPinState() +{ + return pinState; +} + + +#ifdef ENABLE_MQTT +bool GpioPin::mqttPublishPinState(int _pwmDuty) +{ + if (mqttAccess) { + cJSON *cJSONObject = cJSON_CreateObject(); + if (cJSONObject == NULL) { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create JSON object"); + return false; + } + + bool retVal = true; + + if (cJSON_AddNumberToObject(cJSONObject, "state", pinState) == NULL) + retVal = false; + + if (mode == GPIO_PIN_MODE_OUTPUT_PWM || mode == GPIO_PIN_MODE_FLASHLIGHT_PWM) { + if (cJSON_AddNumberToObject(cJSONObject, "pwm_duty", _pwmDuty) == NULL) + retVal = false; + } + + char *jsonString = cJSON_PrintBuffered(cJSONObject, 256, 1); // Print to predefined buffer, avoid dynamic allocations + std::string jsonData = std::string(jsonString); + cJSON_free(jsonString); + cJSON_Delete(cJSONObject); + + retVal &= MQTTPublish(mqttTopic + "/state", jsonData, 1); + + if (!retVal) { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "GPIO" + std::to_string((int)gpio) + ": Failed to publish state to MQTT broker"); + return false; + } + } + return true; +} + + +bool GpioPin::mqttControlPinState(std::string _topic, char* _data, int _data_len) +{ + //ESP_LOGI(TAG, "mqttControlPinStateHandler: topic %s, data %.*s", _topic.c_str(), _data_len, _data); + //example: {"state": 1, "pwm_duty": 1024} + + if (_data_len == 0) { // Check if data length > 0 + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "mqttControlPinStateHandler: Handler called, but no data received"); + return false; + } + + cJSON *jsonData = cJSON_Parse(_data); + cJSON *pinState = cJSON_GetObjectItemCaseSensitive(jsonData, "state"); + + if (mode == GPIO_PIN_MODE_OUTPUT) { + if (cJSON_IsNumber(pinState)) { // Check if pinState is a number + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "mqttControlPinStateHandler: state: " + std::to_string(pinState->valueint)); + esp_err_t retVal = setPinState(pinState->valueint, GPIO_SET_SOURCE_MQTT); + if (retVal == ESP_OK) { + cJSON_Delete(jsonData); + return true; + } + else { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mqttControlPinStateHandler: GPIO" + std::to_string((int)gpio) + + " failed to set state | Error: " + intToHexString(retVal)); + } + } + else { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "mqttControlPinStateHandler: state not a valid number (\"state\": 1)"); + } + } + else if (mode == GPIO_PIN_MODE_OUTPUT_PWM) { + if (cJSON_IsNumber(pinState)) { // Check if pinState is a number + cJSON *pwmDuty = cJSON_GetObjectItemCaseSensitive(jsonData, "pwm_duty"); + if (cJSON_IsNumber(pwmDuty)) { // Check if pwmDuty is a number + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "mqttControlPinStateHandler: state: " + std::to_string(pinState->valueint) + + ", pwm_duty: " + std::to_string(pwmDuty->valueint)); + esp_err_t retVal = setPinState(pinState->valueint, pwmDuty->valueint, GPIO_SET_SOURCE_MQTT); + if (retVal == ESP_OK) { + cJSON_Delete(jsonData); + return true; + } + else { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mqttControlPinStateHandler: GPIO" + std::to_string((int)gpio) + + " failed to set state | Error: " + intToHexString(retVal)); + } + } + else { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "mqttControlPinStateHandler: pwm_duty not a valid number (\"pwm_duty\": 1024)"); + } + } + else { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "mqttControlPinStateHandler: state not a valid number (\"state\": 1)"); + } + + } + else { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "mqttControlPinStateHandler: Wrong pin mode, GPIO cannot be controlled)"); + } + + cJSON_Delete(jsonData); + return false; +} +#endif //ENABLE_MQTT diff --git a/code/components/jomjol_controlGPIO/GpioPin.h b/code/components/jomjol_controlGPIO/GpioPin.h new file mode 100644 index 000000000..50a449eb8 --- /dev/null +++ b/code/components/jomjol_controlGPIO/GpioPin.h @@ -0,0 +1,105 @@ +#ifndef GPIO_PIN_H +#define GPIO_PIN_H + +#include "../../include/defines.h" + +#include "hal/gpio_types.h" +#include +#include "driver/ledc.h" +#include "SmartLeds.h" + + +typedef enum { + GPIO_PIN_MODE_DISABLED = 0, + GPIO_PIN_MODE_INPUT = 1, + GPIO_PIN_MODE_INPUT_PULLUP = 2, + GPIO_PIN_MODE_INPUT_PULLDOWN = 3, + GPIO_PIN_MODE_OUTPUT = 4, + GPIO_PIN_MODE_OUTPUT_PWM = 5, + GPIO_PIN_MODE_FLASHLIGHT_PWM = 6, + GPIO_PIN_MODE_FLASHLIGHT_SMARTLED = 7, + GPIO_PIN_MODE_FLASHLIGHT_DIGITAL = 8, + GPIO_PIN_MODE_TRIGGER_CYCLE_START = 9, + GPIO_PIN_MODE_MAX = 10 +} gpio_pin_mode_t; + + +typedef enum { + GPIO_SET_SOURCE_INTERNAL = 0, + GPIO_SET_SOURCE_MQTT = 1, + GPIO_SET_SOURCE_HTTP = 2, + GPIO_SET_SOURCE_MAX = 3 +} gpio_set_source; + + +struct GpioResult { + gpio_num_t gpio; + int state; +}; + + +struct GpioISR { + gpio_num_t gpio; + int debounceTime; +}; + + +class GpioPin +{ + private: + int pinState = -1; + + gpio_num_t gpio; + const char* name; + gpio_pin_mode_t mode; + gpio_int_type_t interruptType; + int frequency; + GpioISR gpioISR; + + bool httpAccess = false; + bool mqttAccess = false; + std::string mqttTopic; + + ledc_channel_t ledcChannel = LEDC_CHANNEL_MAX; + SmartLed *smartLed = NULL; + LedType LEDType; + int LEDQuantity; + Rgb LEDColor; + + int intensityCorrection; + + 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); + ~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); + int getPinState(); + + #ifdef ENABLE_MQTT + bool mqttPublishPinState(int _pwmDuty = 0); + bool mqttControlPinState(std::string _topic, char* data, int data_len); + #endif + + gpio_num_t getGPIO() { return gpio; }; + gpio_pin_mode_t getMode() { return mode; }; + gpio_int_type_t getInterruptType() { return interruptType; }; + int getFrequency() { return frequency; }; + bool getHttpAccess() { return httpAccess; }; + + void setLedcChannel (ledc_channel_t _ledcChannel) { ledcChannel = _ledcChannel; }; + ledc_channel_t getLedcChannel () { return ledcChannel; }; + void setSmartLed(SmartLed* _smartLed) { smartLed = _smartLed; }; + SmartLed* getSmartLed() { return smartLed; }; + LedType getLEDType() { return LEDType; }; + int getLEDQuantity() { return LEDQuantity; }; + Rgb getLEDColor() { return LEDColor; }; + + int getIntensityCorrection() { return intensityCorrection; }; +}; + +#endif //GPIO_PIN_H diff --git a/code/components/jomjol_controlGPIO/SmartLeds.cpp b/code/components/jomjol_controlGPIO/SmartLeds.cpp deleted file mode 100644 index 5a22e932e..000000000 --- a/code/components/jomjol_controlGPIO/SmartLeds.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "SmartLeds.h" - -IsrCore SmartLed::_interruptCore = CoreCurrent; - -SmartLed*& IRAM_ATTR SmartLed::ledForChannel( int channel ) { - static SmartLed* table[8] = { nullptr }; - assert( channel < 8 ); - return table[ channel ]; -} - -void IRAM_ATTR SmartLed::txEndCallback(rmt_channel_t channel, void *arg) { - xSemaphoreGiveFromISR(ledForChannel(channel)->_finishedFlag, nullptr); -} - - -void IRAM_ATTR SmartLed::translateForRmt(const void *src, rmt_item32_t *dest, size_t src_size, - size_t wanted_rmt_items_num, size_t *out_consumed_src_bytes, size_t *out_used_rmt_items) { - SmartLed *self; - ESP_ERROR_CHECK(rmt_translator_get_context(out_used_rmt_items, (void**)&self)); - - const auto& _bitToRmt = self->_bitToRmt; - const auto src_offset = self->_translatorSourceOffset; - - auto *src_components = (const uint8_t *)src; - size_t consumed_src_bytes = 0; - size_t used_rmt_items = 0; - - while (consumed_src_bytes < src_size && used_rmt_items + 7 < wanted_rmt_items_num) { - uint8_t val = *src_components; - - // each bit, from highest to lowest - for ( uint8_t j = 0; j != 8; j++, val <<= 1 ) { - dest->val = _bitToRmt[ val >> 7 ].val; - ++dest; - } - - used_rmt_items += 8; - ++src_components; - ++consumed_src_bytes; - - // skip alpha byte - if(((src_offset + consumed_src_bytes) % 4) == 3) { - ++src_components; - ++consumed_src_bytes; - - // TRST delay after last pixel in strip - if(consumed_src_bytes == src_size) { - (dest-1)->duration1 = self->_timing.TRS / ( detail::RMT_DURATION_NS * detail::DIVIDER ); - } - } - } - - self->_translatorSourceOffset = src_offset + consumed_src_bytes; - *out_consumed_src_bytes = consumed_src_bytes; - *out_used_rmt_items = used_rmt_items; -} diff --git a/code/components/jomjol_controlGPIO/SmartLeds.h b/code/components/jomjol_controlGPIO/SmartLeds.h deleted file mode 100644 index e654aeb89..000000000 --- a/code/components/jomjol_controlGPIO/SmartLeds.h +++ /dev/null @@ -1,489 +0,0 @@ -#pragma once - -/* - * A C++ driver for the WS2812 LEDs using the RMT peripheral on the ESP32. - * - * Jan "yaqwsx" Mrázek - * - * Based on the work by Martin F. Falatic - https://github.com/FozzTexx/ws2812-demo - */ - -/* - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include "Color.h" - -namespace detail { - -struct TimingParams { - uint32_t T0H; - uint32_t T1H; - uint32_t T0L; - uint32_t T1L; - uint32_t TRS; -}; - -static const int DIVIDER = 4; // 8 still seems to work, but timings become marginal -static const double RMT_DURATION_NS = 12.5; // minimum time of a single RMT duration based on clock ns - -} // namespace detail - -using LedType = detail::TimingParams; - -static const LedType LED_WS2812 = { 350, 700, 800, 600, 50000 }; -static const LedType LED_WS2812B = { 400, 850, 850, 400, 50100 }; -static const LedType LED_SK6812 = { 300, 600, 900, 600, 80000 }; -static const LedType LED_WS2813 = { 350, 800, 350, 350, 300000 }; - -// Single buffer == can't touch the Rgbs between show() and wait() -enum BufferType { SingleBuffer = 0, DoubleBuffer }; - -enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2}; - -class SmartLed { -public: - // The RMT interrupt must not run on the same core as WiFi interrupts, otherwise SmartLeds - // can't fill the RMT buffer fast enough, resulting in rendering artifacts. - // Usually, that means you have to set isrCore == CoreSecond. - // - // If you use anything other than CoreCurrent, the FreeRTOS scheduler MUST be already running, - // so you can't use it if you define SmartLed as global variable. - SmartLed(const LedType &type, int count, int pin, int channel = 0, BufferType doubleBuffer = DoubleBuffer, IsrCore isrCore = CoreCurrent) - : _timing(type), - _channel((rmt_channel_t)channel), - _count(count), - _firstBuffer(new Rgb[count]), - _secondBuffer( doubleBuffer ? new Rgb[ count ] : nullptr ), - _finishedFlag(xSemaphoreCreateBinary()) - { - assert( channel >= 0 && channel < RMT_CHANNEL_MAX ); - assert( ledForChannel( channel ) == nullptr ); - - xSemaphoreGive( _finishedFlag ); - - _bitToRmt[ 0 ].level0 = 1; - _bitToRmt[ 0 ].level1 = 0; - _bitToRmt[ 0 ].duration0 = _timing.T0H / ( detail::RMT_DURATION_NS * detail::DIVIDER ); - _bitToRmt[ 0 ].duration1 = _timing.T0L / ( detail::RMT_DURATION_NS * detail::DIVIDER ); - - _bitToRmt[ 1 ].level0 = 1; - _bitToRmt[ 1 ].level1 = 0; - _bitToRmt[ 1 ].duration0 = _timing.T1H / ( detail::RMT_DURATION_NS * detail::DIVIDER ); - _bitToRmt[ 1 ].duration1 = _timing.T1L / ( detail::RMT_DURATION_NS * detail::DIVIDER ); - - rmt_config_t config = RMT_DEFAULT_CONFIG_TX((gpio_num_t)pin, _channel); - config.rmt_mode = RMT_MODE_TX; - config.clk_div = detail::DIVIDER; - config.mem_block_num = 1; - config.tx_config.idle_output_en = true; - - ESP_ERROR_CHECK(rmt_config(&config)); - - const auto anyAliveCached = anyAlive(); - if (!anyAliveCached && isrCore != CoreCurrent) { - _interruptCore = isrCore; - ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, (void*)((intptr_t)_channel))); - } else { - registerInterrupt((void*)((intptr_t)_channel)); - } - - if(!anyAliveCached) { - rmt_register_tx_end_callback(txEndCallback, NULL); - } - - ESP_ERROR_CHECK(rmt_translator_init(_channel, translateForRmt)); - ESP_ERROR_CHECK(rmt_translator_set_context(_channel, this)); - - ledForChannel( channel ) = this; - } - - ~SmartLed() { - ledForChannel( _channel ) = nullptr; - if ( !anyAlive() && _interruptCore != CoreCurrent ) { - ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, (void*)((intptr_t)_channel))); - } else { - unregisterInterrupt((void*)((intptr_t)_channel)); - } - vSemaphoreDelete( _finishedFlag ); - } - - Rgb& operator[]( int idx ) { - return _firstBuffer[ idx ]; - } - - const Rgb& operator[]( int idx ) const { - return _firstBuffer[ idx ]; - } - - esp_err_t show() { - esp_err_t err = startTransmission(); - swapBuffers(); - return err; - } - - bool wait( TickType_t timeout = portMAX_DELAY ) { - if( xSemaphoreTake( _finishedFlag, timeout ) == pdTRUE ) { - xSemaphoreGive( _finishedFlag ); - return true; - } - return false; - } - - int size() const { - return _count; - } - - Rgb *begin() { return _firstBuffer.get(); } - const Rgb *begin() const { return _firstBuffer.get(); } - const Rgb *cbegin() const { return _firstBuffer.get(); } - - Rgb *end() { return _firstBuffer.get() + _count; } - const Rgb *end() const { return _firstBuffer.get() + _count; } - const Rgb *cend() const { return _firstBuffer.get() + _count; } - -private: - static IsrCore _interruptCore; - - static void registerInterrupt(void *channelVoid) { - ESP_ERROR_CHECK(rmt_driver_install((rmt_channel_t)((intptr_t)channelVoid), 0, ESP_INTR_FLAG_IRAM)); - } - - static void unregisterInterrupt(void *channelVoid) { - ESP_ERROR_CHECK(rmt_driver_uninstall((rmt_channel_t)((intptr_t)channelVoid))); - } - - static SmartLed*& IRAM_ATTR ledForChannel( int channel ); - - static bool anyAlive() { - for ( int i = 0; i != 8; i++ ) - if ( ledForChannel( i ) != nullptr ) return true; - return false; - } - - static void IRAM_ATTR txEndCallback(rmt_channel_t channel, void *arg); - - static void IRAM_ATTR translateForRmt(const void *src, rmt_item32_t *dest, size_t src_size, - size_t wanted_rmt_items_num, size_t *out_consumed_src_bytes, size_t *out_used_rmt_items); - - void swapBuffers() { - if (_secondBuffer) - _firstBuffer.swap(_secondBuffer); - } - - esp_err_t startTransmission() { - // Invalid use of the library, you must wait() fir previous frame to get processed first - if( xSemaphoreTake( _finishedFlag, 0 ) != pdTRUE ) - abort(); - - _translatorSourceOffset = 0; - - auto err = rmt_write_sample(_channel, (const uint8_t *)_firstBuffer.get(), _count * 4, false); - if(err != ESP_OK) { - return err; - } - - return ESP_OK; - } - - const LedType& _timing; - rmt_channel_t _channel; - rmt_item32_t _bitToRmt[ 2 ]; - int _count; - std::unique_ptr _firstBuffer; - std::unique_ptr _secondBuffer; - - size_t _translatorSourceOffset; - - SemaphoreHandle_t _finishedFlag; -}; - -#ifdef CONFIG_IDF_TARGET_ESP32 -#define _SMARTLEDS_SPI_HOST HSPI_HOST -#elif defined(CONFIG_IDF_TARGET_ESP32S3) -#define _SMARTLEDS_SPI_HOST SPI2_HOST -#else -#error "SmartLeds SPI host not defined for this chip/esp-idf version." -#endif - -class Apa102 { -public: - struct ApaRgb { - ApaRgb( uint8_t r = 0, uint8_t g = 0, uint32_t b = 0, uint32_t v = 0xFF ) - : v( 0xE0 | v ), b( b ), g( g ), r( r ) - {} - - ApaRgb& operator=( const Rgb& o ) { - r = o.r; - g = o.g; - b = o.b; - return *this; - } - - ApaRgb& operator=( const Hsv& o ) { - *this = Rgb{ o }; - return *this; - } - - uint8_t v, b, g, r; - }; - - static const int FINAL_FRAME_SIZE = 4; - static const int TRANS_COUNT = 2 + 8; - - Apa102( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer ) - : _count( count ), - _firstBuffer( new ApaRgb[ count ] ), - _secondBuffer( doubleBuffer ? new ApaRgb[ count ] : nullptr ), - _initFrame( 0 ) - { - spi_bus_config_t buscfg; - memset( &buscfg, 0, sizeof( buscfg ) ); - buscfg.mosi_io_num = datapin; - buscfg.miso_io_num = -1; - buscfg.sclk_io_num = clkpin; - buscfg.quadwp_io_num = -1; - buscfg.quadhd_io_num = -1; - buscfg.max_transfer_sz = 65535; - - spi_device_interface_config_t devcfg; - memset( &devcfg, 0, sizeof( devcfg ) ); - devcfg.clock_speed_hz = 1000000; - devcfg.mode = 0; - devcfg.spics_io_num = -1; - devcfg.queue_size = TRANS_COUNT; - devcfg.pre_cb = nullptr; - - auto ret = spi_bus_initialize( _SMARTLEDS_SPI_HOST, &buscfg, 1 ); - assert( ret == ESP_OK ); - - ret = spi_bus_add_device( _SMARTLEDS_SPI_HOST, &devcfg, &_spi ); - assert( ret == ESP_OK ); - - std::fill_n( _finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF ); - } - - ~Apa102() { - // ToDo - } - - ApaRgb& operator[]( int idx ) { - return _firstBuffer[ idx ]; - } - - const ApaRgb& operator[]( int idx ) const { - return _firstBuffer[ idx ]; - } - - void show() { - _buffer = _firstBuffer.get(); - startTransmission(); - swapBuffers(); - } - - void wait() { - for ( int i = 0; i != _transCount; i++ ) { - spi_transaction_t *t; - spi_device_get_trans_result( _spi, &t, portMAX_DELAY ); - } - } -private: - void swapBuffers() { - if ( _secondBuffer ) - _firstBuffer.swap( _secondBuffer ); - } - - void startTransmission() { - for ( int i = 0; i != TRANS_COUNT; i++ ) { - _transactions[ i ].cmd = 0; - _transactions[ i ].addr = 0; - _transactions[ i ].flags = 0; - _transactions[ i ].rxlength = 0; - _transactions[ i ].rx_buffer = nullptr; - } - // Init frame - _transactions[ 0 ].length = 32; - _transactions[ 0 ].tx_buffer = &_initFrame; - spi_device_queue_trans( _spi, _transactions + 0, portMAX_DELAY ); - // Data - _transactions[ 1 ].length = 32 * _count; - _transactions[ 1 ].tx_buffer = _buffer; - spi_device_queue_trans( _spi, _transactions + 1, portMAX_DELAY ); - _transCount = 2; - // End frame - for ( int i = 0; i != 1 + _count / 32 / FINAL_FRAME_SIZE; i++ ) { - _transactions[ 2 + i ].length = 32 * FINAL_FRAME_SIZE; - _transactions[ 2 + i ].tx_buffer = _finalFrame; - spi_device_queue_trans( _spi, _transactions + 2 + i, portMAX_DELAY ); - _transCount++; - } - } - - spi_device_handle_t _spi; - int _count; - std::unique_ptr< ApaRgb[] > _firstBuffer, _secondBuffer; - ApaRgb *_buffer; - - spi_transaction_t _transactions[ TRANS_COUNT ]; - int _transCount; - - uint32_t _initFrame; - uint32_t _finalFrame[ FINAL_FRAME_SIZE ]; -}; - -class LDP8806 { -public: - struct LDP8806_GRB { - - LDP8806_GRB( uint8_t g_7bit = 0, uint8_t r_7bit = 0, uint32_t b_7bit = 0 ) - : g( g_7bit ), r( r_7bit ), b( b_7bit ) - { - } - - LDP8806_GRB& operator=( const Rgb& o ) { - //Convert 8->7bit colour - r = ( o.r * 127 / 256 ) | 0x80; - g = ( o.g * 127 / 256 ) | 0x80; - b = ( o.b * 127 / 256 ) | 0x80; - return *this; - } - - LDP8806_GRB& operator=( const Hsv& o ) { - *this = Rgb{ o }; - return *this; - } - - uint8_t g, r, b; - }; - - static const int LED_FRAME_SIZE_BYTES = sizeof( LDP8806_GRB ); - static const int LATCH_FRAME_SIZE_BYTES = 3; - static const int TRANS_COUNT_MAX = 20;//Arbitrary, supports up to 600 LED - - LDP8806( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000 ) - : _count( count ), - _firstBuffer( new LDP8806_GRB[ count ] ), - _secondBuffer( doubleBuffer ? new LDP8806_GRB[ count ] : nullptr ), - // one 'latch'/start-of-data mark frame for every 32 leds - _latchFrames( ( count + 31 ) / 32 ) - { - spi_bus_config_t buscfg; - memset( &buscfg, 0, sizeof( buscfg ) ); - buscfg.mosi_io_num = datapin; - buscfg.miso_io_num = -1; - buscfg.sclk_io_num = clkpin; - buscfg.quadwp_io_num = -1; - buscfg.quadhd_io_num = -1; - buscfg.max_transfer_sz = 65535; - - spi_device_interface_config_t devcfg; - memset( &devcfg, 0, sizeof( devcfg ) ); - devcfg.clock_speed_hz = clock_speed_hz; - devcfg.mode = 0; - devcfg.spics_io_num = -1; - devcfg.queue_size = TRANS_COUNT_MAX; - devcfg.pre_cb = nullptr; - - auto ret = spi_bus_initialize( _SMARTLEDS_SPI_HOST, &buscfg, 1 ); - assert( ret == ESP_OK ); - - ret = spi_bus_add_device( _SMARTLEDS_SPI_HOST, &devcfg, &_spi ); - assert( ret == ESP_OK ); - - std::fill_n( _latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0 ); - } - - ~LDP8806() { - // noop - } - - LDP8806_GRB& operator[]( int idx ) { - return _firstBuffer[ idx ]; - } - - const LDP8806_GRB& operator[]( int idx ) const { - return _firstBuffer[ idx ]; - } - - void show() { - _buffer = _firstBuffer.get(); - startTransmission(); - swapBuffers(); - } - - void wait() { - while ( _transCount-- ) { - spi_transaction_t *t; - spi_device_get_trans_result( _spi, &t, portMAX_DELAY ); - } - } -private: - void swapBuffers() { - if ( _secondBuffer ) - _firstBuffer.swap( _secondBuffer ); - } - - void startTransmission() { - _transCount = 0; - for ( int i = 0; i != TRANS_COUNT_MAX; i++ ) { - _transactions[ i ].cmd = 0; - _transactions[ i ].addr = 0; - _transactions[ i ].flags = 0; - _transactions[ i ].rxlength = 0; - _transactions[ i ].rx_buffer = nullptr; - } - // LED Data - _transactions[ 0 ].length = ( LED_FRAME_SIZE_BYTES * 8 ) * _count; - _transactions[ 0 ].tx_buffer = _buffer; - spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY ); - _transCount++; - - // 'latch'/start-of-data marker frames - for ( int i = 0; i < _latchFrames; i++ ) { - _transactions[ _transCount ].length = ( LATCH_FRAME_SIZE_BYTES * 8 ); - _transactions[ _transCount ].tx_buffer = _latchBuffer; - spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY ); - _transCount++; - } - } - - spi_device_handle_t _spi; - int _count; - std::unique_ptr< LDP8806_GRB[] > _firstBuffer, _secondBuffer; - LDP8806_GRB *_buffer; - - spi_transaction_t _transactions[ TRANS_COUNT_MAX ]; - int _transCount; - - int _latchFrames; - uint8_t _latchBuffer[ LATCH_FRAME_SIZE_BYTES ]; -}; diff --git a/code/components/jomjol_controlGPIO/server_GPIO.cpp b/code/components/jomjol_controlGPIO/server_GPIO.cpp deleted file mode 100644 index 8f47aae4e..000000000 --- a/code/components/jomjol_controlGPIO/server_GPIO.cpp +++ /dev/null @@ -1,692 +0,0 @@ -#include "server_GPIO.h" - -#include -#include -#include -#include - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" - -#include "esp_system.h" -#include "esp_log.h" - -#include "ClassLogFile.h" -#include "configFile.h" -#include "Helper.h" - -#ifdef ENABLE_MQTT -#include "interface_mqtt.h" -#include "server_mqtt.h" -#endif //ENABLE_MQTT - - -static const char *TAG = "GPIO"; - -QueueHandle_t gpio_queue_handle = NULL; - - -GpioPin::GpioPin(gpio_num_t gpio, const char* name, gpio_pin_mode_t mode, gpio_int_type_t interruptType, uint8_t dutyResolution, std::string mqttTopic, bool httpEnable) -{ - _gpio = gpio; - _name = name; - _mode = mode; - _interruptType = interruptType; - _mqttTopic = mqttTopic; -} - -GpioPin::~GpioPin() -{ - ESP_LOGD(TAG,"reset GPIO pin %d", _gpio); - if (_interruptType != GPIO_INTR_DISABLE) { - //hook isr handler for specific gpio pin - gpio_isr_handler_remove(_gpio); - } - gpio_reset_pin(_gpio); -} - -static void IRAM_ATTR gpio_isr_handler(void* arg) -{ - GpioResult gpioResult; - gpioResult.gpio = *(gpio_num_t*) arg; - gpioResult.value = gpio_get_level(gpioResult.gpio); - BaseType_t ContextSwitchRequest = pdFALSE; - - xQueueSendToBackFromISR(gpio_queue_handle,(void*)&gpioResult,&ContextSwitchRequest); - - if(ContextSwitchRequest){ - taskYIELD(); - } -} - -static void gpioHandlerTask(void *arg) { - ESP_LOGD(TAG,"start interrupt task"); - while(1){ - if(uxQueueMessagesWaiting(gpio_queue_handle)){ - while(uxQueueMessagesWaiting(gpio_queue_handle)){ - GpioResult gpioResult; - xQueueReceive(gpio_queue_handle,(void*)&gpioResult,10); - ESP_LOGD(TAG,"gpio: %d state: %d", gpioResult.gpio, gpioResult.value); - ((GpioHandler*)arg)->gpioInterrupt(&gpioResult); - } - } - - ((GpioHandler*)arg)->taskHandler(); - vTaskDelay(pdMS_TO_TICKS(1000)); - } -} - -void GpioPin::gpioInterrupt(int value) { -#ifdef ENABLE_MQTT - if (_mqttTopic.compare("") != 0) { - ESP_LOGD(TAG, "gpioInterrupt %s %d", _mqttTopic.c_str(), value); - - MQTTPublish(_mqttTopic, value ? "true" : "false", 1); - } -#endif //ENABLE_MQTT - currentState = value; -} - -void GpioPin::init() -{ - gpio_config_t io_conf; - //set interrupt - io_conf.intr_type = _interruptType; - //set as output mode - io_conf.mode = (_mode == GPIO_PIN_MODE_OUTPUT) || (_mode == GPIO_PIN_MODE_BUILT_IN_FLASH_LED) ? gpio_mode_t::GPIO_MODE_OUTPUT : gpio_mode_t::GPIO_MODE_INPUT; - //bit mask of the pins that you want to set,e.g.GPIO18/19 - io_conf.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; - //set pull-up mode - io_conf.pull_up_en = _mode == GPIO_PIN_MODE_INPUT_PULLDOWN ? gpio_pullup_t::GPIO_PULLUP_ENABLE : gpio_pullup_t::GPIO_PULLUP_DISABLE; - //configure GPIO with the given settings - gpio_config(&io_conf); - -// if (_interruptType != GPIO_INTR_DISABLE) { // ohne GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X, wenn das genutzt wird, dann soll auch der Handler hier nicht initialisiert werden, da das dann über SmartLED erfolgt. - if ((_interruptType != GPIO_INTR_DISABLE) && (_mode != GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)) { - //hook isr handler for specific gpio pin - ESP_LOGD(TAG, "GpioPin::init add isr handler for GPIO %d", _gpio); - gpio_isr_handler_add(_gpio, gpio_isr_handler, (void*)&_gpio); - } - -#ifdef ENABLE_MQTT - if ((_mqttTopic.compare("") != 0) && ((_mode == GPIO_PIN_MODE_OUTPUT) || (_mode == GPIO_PIN_MODE_OUTPUT_PWM) || (_mode == GPIO_PIN_MODE_BUILT_IN_FLASH_LED))) { - std::function f = std::bind(&GpioPin::handleMQTT, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); - MQTTregisterSubscribeFunction(_mqttTopic, f); - } -#endif //ENABLE_MQTT -} - -bool GpioPin::getValue(std::string* errorText) -{ - if ((_mode != GPIO_PIN_MODE_INPUT) && (_mode != GPIO_PIN_MODE_INPUT_PULLUP) && (_mode != GPIO_PIN_MODE_INPUT_PULLDOWN)) { - (*errorText) = "GPIO is not in input mode"; - } - - return gpio_get_level(_gpio) == 1; -} - -void GpioPin::setValue(bool value, gpio_set_source setSource, std::string* errorText) -{ - ESP_LOGD(TAG, "GpioPin::setValue %d", value); - - if ((_mode != GPIO_PIN_MODE_OUTPUT) && (_mode != GPIO_PIN_MODE_OUTPUT_PWM) && (_mode != GPIO_PIN_MODE_BUILT_IN_FLASH_LED)) { - (*errorText) = "GPIO is not in output mode"; - } else { - gpio_set_level(_gpio, value); - -#ifdef ENABLE_MQTT - if ((_mqttTopic.compare("") != 0) && (setSource != GPIO_SET_SOURCE_MQTT)) { - MQTTPublish(_mqttTopic, value ? "true" : "false", 1); - } -#endif //ENABLE_MQTT - } -} - -void GpioPin::publishState() { - int newState = gpio_get_level(_gpio); - if (newState != currentState) { - ESP_LOGD(TAG,"publish state of GPIO %d new state %d", _gpio, newState); -#ifdef ENABLE_MQTT - if (_mqttTopic.compare("") != 0) - MQTTPublish(_mqttTopic, newState ? "true" : "false", 1); -#endif //ENABLE_MQTT - currentState = newState; - } -} - -#ifdef ENABLE_MQTT -bool GpioPin::handleMQTT(std::string, char* data, int data_len) { - ESP_LOGD(TAG, "GpioPin::handleMQTT data %.*s", data_len, data); - - std::string dataStr(data, data_len); - dataStr = toLower(dataStr); - std::string errorText = ""; - if ((dataStr == "true") || (dataStr == "1")) { - setValue(true, GPIO_SET_SOURCE_MQTT, &errorText); - } else if ((dataStr == "false") || (dataStr == "0")) { - setValue(false, GPIO_SET_SOURCE_MQTT, &errorText); - } else { - errorText = "wrong value "; - errorText.append(data, data_len); - } - - if (errorText != "") { - ESP_LOGE(TAG, "%s", errorText.c_str()); - } - - return (errorText == ""); -} -#endif //ENABLE_MQTT - -esp_err_t callHandleHttpRequest(httpd_req_t *req) -{ - ESP_LOGD(TAG,"callHandleHttpRequest"); - - GpioHandler *gpioHandler = (GpioHandler*)req->user_ctx; - return gpioHandler->handleHttpRequest(req); -} - -void taskGpioHandler(void *pvParameter) -{ - ESP_LOGD(TAG,"taskGpioHandler"); - ((GpioHandler*)pvParameter)->init(); -} - -GpioHandler::GpioHandler(std::string configFile, httpd_handle_t httpServer) -{ - ESP_LOGD(TAG,"start GpioHandler"); - _configFile = configFile; - _httpServer = httpServer; - registerGpioUri(); -} - -GpioHandler::~GpioHandler() { - if (gpioMap != NULL) { - clear(); - delete gpioMap; - } -} - -void GpioHandler::init() -{ - // TickType_t xDelay = 60000 / portTICK_PERIOD_MS; - // ESP_LOGD(TAG, "wait before start %ldms", (long) xDelay); - // vTaskDelay( xDelay ); - - ESP_LOGD(TAG, "*************** Start GPIOHandler_Init *****************"); - - if (gpioMap == NULL) { - gpioMap = new std::map(); - } else { - clear(); - } - - - ESP_LOGI(TAG, "read GPIO config and init GPIO"); - if (!readConfig()) { - clear(); - delete gpioMap; - gpioMap = NULL; - ESP_LOGI(TAG, "GPIO init completed, handler is disabled"); - return; - } - - - for(std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) { - it->second->init(); - } - -#ifdef ENABLE_MQTT - std::function f = std::bind(&GpioHandler::handleMQTTconnect, this); - MQTTregisterConnectFunction("gpio-handler", f); -#endif //ENABLE_MQTT - - if (xHandleTaskGpio == NULL) { - gpio_queue_handle = xQueueCreate(10,sizeof(GpioResult)); - BaseType_t xReturned = xTaskCreate(&gpioHandlerTask, "gpio_int", 3 * 1024, (void *)this, tskIDLE_PRIORITY + 4, &xHandleTaskGpio); - if(xReturned == pdPASS ) { - ESP_LOGD(TAG, "xHandletaskGpioHandler started"); - } else { - ESP_LOGD(TAG, "xHandletaskGpioHandler not started %d ", (int)xHandleTaskGpio); - } - } - - ESP_LOGI(TAG, "GPIO init completed, is enabled"); -} - -void GpioHandler::taskHandler() { - if (gpioMap != NULL) { - for(std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) { - if ((it->second->getInterruptType() == GPIO_INTR_DISABLE)) - it->second->publishState(); - } - } -} - -#ifdef ENABLE_MQTT -void GpioHandler::handleMQTTconnect() -{ - if (gpioMap != NULL) { - for(std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) { - if ((it->second->getMode() == GPIO_PIN_MODE_INPUT) || (it->second->getMode() == GPIO_PIN_MODE_INPUT_PULLDOWN) || (it->second->getMode() == GPIO_PIN_MODE_INPUT_PULLUP)) - it->second->publishState(); - } - } -} -#endif //ENABLE_MQTT - -void GpioHandler::deinit() { -#ifdef ENABLE_MQTT - MQTTunregisterConnectFunction("gpio-handler"); -#endif //ENABLE_MQTT - clear(); - if (xHandleTaskGpio != NULL) { - vTaskDelete(xHandleTaskGpio); - xHandleTaskGpio = NULL; - } -} - -void GpioHandler::gpioInterrupt(GpioResult* gpioResult) { - if ((gpioMap != NULL) && (gpioMap->find(gpioResult->gpio) != gpioMap->end())) { - (*gpioMap)[gpioResult->gpio]->gpioInterrupt(gpioResult->value); - } -} - -bool GpioHandler::readConfig() -{ - if (!gpioMap->empty()) - clear(); - - ConfigFile configFile = ConfigFile(_configFile); - - std::vector splitted; - std::string line = ""; - bool disabledLine = false; - bool eof = false; - gpio_num_t gpioExtLED = (gpio_num_t) 0; - - while ((!configFile.GetNextParagraph(line, disabledLine, eof) || (line.compare("[GPIO]") != 0)) && !eof) {} - - if (disabledLine || eof) { - ESP_LOGI(TAG, "GPIO handler disabled"); - _isEnabled = false; - return false; - } - - #ifdef ENABLE_MQTT - std::string mainTopicMQTT = mqttServer_getMainTopic(); - if (mainTopicMQTT.length() > 0) - mainTopicMQTT = mainTopicMQTT + "/device/gpio"; - #endif // ENABLE_MQTT - - bool registerISR = false; - while (configFile.getNextLine(&line, disabledLine, eof) && !configFile.isNewParagraph(line)) - { - splitted = ZerlegeZeile(line); - // const std::regex pieces_regex("IO([0-9]{1,2})"); - // std::smatch pieces_match; - // if (std::regex_match(splitted[0], pieces_match, pieces_regex) && (pieces_match.size() == 2)) - // { - // std::string gpioStr = pieces_match[1]; - ESP_LOGD(TAG, "conf param %s", toUpper(splitted[0]).c_str()); - - if ((splitted[0].rfind("IO", 0) == 0) && (splitted.size() >= 6)) - { - ESP_LOGI(TAG,"Enable GP%s in %s mode", splitted[0].c_str(), splitted[1].c_str()); - std::string gpioStr = splitted[0].substr(2, 2); - gpio_num_t gpioNr = (gpio_num_t)atoi(gpioStr.c_str()); - gpio_pin_mode_t pinMode = resolvePinMode(toLower(splitted[1])); - gpio_int_type_t intType = resolveIntType(toLower(splitted[2])); - uint16_t dutyResolution = (uint8_t)atoi(splitted[3].c_str()); -#ifdef ENABLE_MQTT - bool mqttEnabled = (toLower(splitted[4]) == "true"); -#endif // ENABLE_MQTT - bool httpEnabled = (toLower(splitted[5]) == "true"); - char gpioName[100]; - if (splitted.size() >= 7) { - strcpy(gpioName, trim(splitted[6]).c_str()); - } else { - sprintf(gpioName, "GPIO%d", gpioNr); - } -#ifdef ENABLE_MQTT - std::string mqttTopic = mqttEnabled ? (mainTopicMQTT + "/" + gpioName) : ""; -#else // ENABLE_MQTT - std::string mqttTopic = ""; -#endif // ENABLE_MQTT - GpioPin* gpioPin = new GpioPin(gpioNr, gpioName, pinMode, intType,dutyResolution, mqttTopic, httpEnabled); - (*gpioMap)[gpioNr] = gpioPin; - - if (pinMode == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X) - { - ESP_LOGD(TAG, "Set WS2812 to GPIO %d", gpioNr); - gpioExtLED = gpioNr; - } - - if (intType != GPIO_INTR_DISABLE) { - registerISR = true; - } - } - - if (toUpper(splitted[0]) == "LEDTYPE") - { - if (splitted[1] == "WS2812") - LEDType = LED_WS2812; - if (splitted[1] == "WS2812B") - LEDType = LED_WS2812B; - if (splitted[1] == "SK6812") - LEDType = LED_SK6812; - if (splitted[1] == "WS2813") - LEDType = LED_WS2813; - } - - if (toUpper(splitted[0]) == "LEDNUMBERS") - { - LEDNumbers = stoi(splitted[1]); - } - - if (toUpper(splitted[0]) == "LEDCOLOR") - { - uint8_t _r, _g, _b; - _r = stoi(splitted[1]); - _g = stoi(splitted[2]); - _b = stoi(splitted[3]); - - LEDColor = Rgb{_r, _g, _b}; - } - - } - - if (registerISR) { - //install gpio isr service - gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM); - } - - if (gpioExtLED > 0) - { - // LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Startsequence 06"); // Nremove -// vTaskDelay( xDelay ); -// xDelay = 5000 / portTICK_PERIOD_MS; -// ESP_LOGD(TAG, "main: sleep for: %ldms", (long) xDelay); - -// SmartLed leds( LED_WS2812, 2, GPIO_NUM_12, 0, DoubleBuffer ); - - -// leds[ 0 ] = Rgb{ 255, 0, 0 }; -// leds[ 1 ] = Rgb{ 255, 255, 255 }; -// leds.show(); -// SmartLed leds = new SmartLed(LEDType, LEDNumbers, gpioExtLED, 0, DoubleBuffer); -// _SmartLED = new SmartLed( LED_WS2812, 2, GPIO_NUM_12, 0, DoubleBuffer ); - } - - _isEnabled = true; - return true; -} - -void GpioHandler::clear() -{ - ESP_LOGD(TAG, "GpioHandler::clear"); - - if (gpioMap != NULL) { - for(std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) { - delete it->second; - } - gpioMap->clear(); - } - - // gpio_uninstall_isr_service(); can't uninstall, isr service is used by camera -} - -void GpioHandler::registerGpioUri() -{ - ESP_LOGI(TAG, "Registering URI handlers"); - - httpd_uri_t camuri = { }; - camuri.method = HTTP_GET; - camuri.uri = "/gpio"; - camuri.handler = callHandleHttpRequest; - camuri.user_ctx = (void*)this; - httpd_register_uri_handler(_httpServer, &camuri); -} - -esp_err_t GpioHandler::handleHttpRequest(httpd_req_t *req) -{ - ESP_LOGD(TAG, "handleHttpRequest"); - - if (gpioMap == NULL) { - std::string resp_str = "GPIO handler not initialized"; - httpd_resp_send(req, resp_str.c_str(), resp_str.length()); - return ESP_OK; - } - -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_switch_GPIO - Start"); -#endif - - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_switch_GPIO"); - char _query[200]; - char _valueGPIO[30]; - char _valueStatus[30]; - std::string gpio, status; - - if (httpd_req_get_url_query_str(req, _query, sizeof(_query)) == ESP_OK) { - ESP_LOGD(TAG, "Query: %s", _query); - - if (httpd_query_key_value(_query, "GPIO", _valueGPIO, sizeof(_valueGPIO)) == ESP_OK) - { - ESP_LOGD(TAG, "GPIO is found %s", _valueGPIO); - gpio = std::string(_valueGPIO); - } else { - std::string resp_str = "GPIO No is not defined"; - httpd_resp_send(req, resp_str.c_str(), resp_str.length()); - return ESP_OK; - } - if (httpd_query_key_value(_query, "Status", _valueStatus, sizeof(_valueStatus)) == ESP_OK) - { - ESP_LOGD(TAG, "Status is found %s", _valueStatus); - status = std::string(_valueStatus); - } - } else { - const char* resp_str = "Error in call. Use /GPIO?GPIO=12&Status=high"; - httpd_resp_send(req, resp_str, strlen(resp_str)); - return ESP_OK; - } - - status = toUpper(status); - if ((status != "HIGH") && (status != "LOW") && (status != "TRUE") && (status != "FALSE") && (status != "0") && (status != "1") && (status != "")) - { - std::string zw = "Status not valid: " + status; - httpd_resp_sendstr_chunk(req, zw.c_str()); - httpd_resp_sendstr_chunk(req, NULL); - return ESP_OK; - } - - int gpionum = stoi(gpio); - - // frei: 16; 12-15; 2; 4 // nur 12 und 13 funktionieren 2: reboot, 4: BlitzLED, 15: PSRAM, 14/15: DMA für SDKarte ??? - gpio_num_t gpio_num = resolvePinNr(gpionum); - if (gpio_num == GPIO_NUM_NC) - { - std::string zw = "GPIO" + std::to_string(gpionum) + " unsupported - only 12 & 13 free"; - httpd_resp_sendstr_chunk(req, zw.c_str()); - httpd_resp_sendstr_chunk(req, NULL); - return ESP_OK; - } - - if (gpioMap->count(gpio_num) == 0) { - char resp_str [30]; - sprintf(resp_str, "GPIO%d is not registred", gpio_num); - httpd_resp_send(req, resp_str, strlen(resp_str)); - return ESP_OK; - } - - if (status == "") - { - std::string resp_str = ""; - status = (*gpioMap)[gpio_num]->getValue(&resp_str) ? "HIGH" : "LOW"; - if (resp_str == "") { - resp_str = status; - } - httpd_resp_sendstr_chunk(req, resp_str.c_str()); - httpd_resp_sendstr_chunk(req, NULL); - } - else - { - std::string resp_str = ""; - (*gpioMap)[gpio_num]->setValue((status == "HIGH") || (status == "TRUE") || (status == "1"), GPIO_SET_SOURCE_HTTP, &resp_str); - if (resp_str == "") { - resp_str = "GPIO" + std::to_string(gpionum) + " switched to " + status; - } - httpd_resp_sendstr_chunk(req, resp_str.c_str()); - httpd_resp_sendstr_chunk(req, NULL); - } - - return ESP_OK; -}; - -void GpioHandler::flashLightEnable(bool value) -{ - ESP_LOGD(TAG, "GpioHandler::flashLightEnable %s", value ? "true" : "false"); - - if (gpioMap != NULL) { - for(std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) - { - if (it->second->getMode() == GPIO_PIN_MODE_BUILT_IN_FLASH_LED) //|| (it->second->getMode() == GPIO_PIN_MODE_EXTERNAL_FLASH_PWM) || (it->second->getMode() == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)) - { - std::string resp_str = ""; - it->second->setValue(value, GPIO_SET_SOURCE_INTERNAL, &resp_str); - - if (resp_str == "") { - ESP_LOGD(TAG, "Flash light pin GPIO %d switched to %s", (int)it->first, (value ? "on" : "off")); - } else { - ESP_LOGE(TAG, "Can't set flash light pin GPIO %d. Error: %s", (int)it->first, resp_str.c_str()); - } - } else - { - if (it->second->getMode() == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X) - { -#ifdef __LEDGLOBAL - if (leds_global == NULL) { - ESP_LOGI(TAG, "init SmartLed: LEDNumber=%d, GPIO=%d", LEDNumbers, (int)it->second->getGPIO()); - leds_global = new SmartLed( LEDType, LEDNumbers, it->second->getGPIO(), 0, DoubleBuffer ); - } else { - // wait until we can update: https://github.com/RoboticsBrno/SmartLeds/issues/10#issuecomment-386921623 - leds_global->wait(); - } -#else - SmartLed leds( LEDType, LEDNumbers, it->second->getGPIO(), 0, DoubleBuffer ); -#endif - - if (value) - { - for (int i = 0; i < LEDNumbers; ++i) -#ifdef __LEDGLOBAL - (*leds_global)[i] = LEDColor; -#else - leds[i] = LEDColor; -#endif - } - else - { - for (int i = 0; i < LEDNumbers; ++i) -#ifdef __LEDGLOBAL - (*leds_global)[i] = Rgb{0, 0, 0}; -#else - leds[i] = Rgb{0, 0, 0}; -#endif - } -#ifdef __LEDGLOBAL - leds_global->show(); -#else - leds.show(); -#endif - } - } - } - } -} - -gpio_num_t GpioHandler::resolvePinNr(uint8_t pinNr) -{ - switch(pinNr) { - case 0: - return GPIO_NUM_0; - case 1: - return GPIO_NUM_1; - case 3: - return GPIO_NUM_3; - case 4: - return GPIO_NUM_4; - case 12: - return GPIO_NUM_12; - case 13: - return GPIO_NUM_13; - default: - return GPIO_NUM_NC; - } -} - -gpio_pin_mode_t GpioHandler::resolvePinMode(std::string input) -{ - if( input == "disabled" ) return GPIO_PIN_MODE_DISABLED; - if( input == "input" ) return GPIO_PIN_MODE_INPUT; - if( input == "input-pullup" ) return GPIO_PIN_MODE_INPUT_PULLUP; - if( input == "input-pulldown" ) return GPIO_PIN_MODE_INPUT_PULLDOWN; - if( input == "output" ) return GPIO_PIN_MODE_OUTPUT; - if( input == "built-in-led" ) return GPIO_PIN_MODE_BUILT_IN_FLASH_LED; - if( input == "output-pwm" ) return GPIO_PIN_MODE_OUTPUT_PWM; - if( input == "external-flash-pwm" ) return GPIO_PIN_MODE_EXTERNAL_FLASH_PWM; - if( input == "external-flash-ws281x" ) return GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X; - - return GPIO_PIN_MODE_DISABLED; -} - -gpio_int_type_t GpioHandler::resolveIntType(std::string input) -{ - if( input == "disabled" ) return GPIO_INTR_DISABLE; - if( input == "rising-edge" ) return GPIO_INTR_POSEDGE; - if( input == "falling-edge" ) return GPIO_INTR_NEGEDGE; - if( input == "rising-and-falling" ) return GPIO_INTR_ANYEDGE ; - if( input == "low-level-trigger" ) return GPIO_INTR_LOW_LEVEL; - if( input == "high-level-trigger" ) return GPIO_INTR_HIGH_LEVEL; - - - return GPIO_INTR_DISABLE; -} - -static GpioHandler *gpioHandler = NULL; - -void gpio_handler_create(httpd_handle_t server) -{ - if (gpioHandler == NULL) - gpioHandler = new GpioHandler(CONFIG_FILE, server); -} - -void gpio_handler_init() -{ - if (gpioHandler != NULL) { - gpioHandler->init(); - } -} - -void gpio_handler_deinit() { - if (gpioHandler != NULL) { - gpioHandler->deinit(); - } -} - -void gpio_handler_destroy() -{ - if (gpioHandler != NULL) { - gpio_handler_deinit(); - delete gpioHandler; - gpioHandler = NULL; - } -} - -GpioHandler* gpio_handler_get() -{ - return gpioHandler; -} - diff --git a/code/components/jomjol_controlGPIO/server_GPIO.h b/code/components/jomjol_controlGPIO/server_GPIO.h deleted file mode 100644 index 55cc7b884..000000000 --- a/code/components/jomjol_controlGPIO/server_GPIO.h +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef SERVER_GPIO_H -#define SERVER_GPIO_H - -#include "../../include/defines.h" - -#include - -#include -#include - -#include "driver/gpio.h" -#include "SmartLeds.h" - - -typedef enum { - GPIO_PIN_MODE_DISABLED = 0x0, - GPIO_PIN_MODE_INPUT = 0x1, - GPIO_PIN_MODE_INPUT_PULLUP = 0x2, - GPIO_PIN_MODE_INPUT_PULLDOWN = 0x3, - GPIO_PIN_MODE_OUTPUT = 0x4, - GPIO_PIN_MODE_BUILT_IN_FLASH_LED = 0x5, - GPIO_PIN_MODE_OUTPUT_PWM = 0x6, - GPIO_PIN_MODE_EXTERNAL_FLASH_PWM = 0x7, - GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X = 0x8, -} gpio_pin_mode_t; - -struct GpioResult { - gpio_num_t gpio; - int value; -}; - -typedef enum { - GPIO_SET_SOURCE_INTERNAL = 0, - GPIO_SET_SOURCE_MQTT = 1, - GPIO_SET_SOURCE_HTTP = 2, -} gpio_set_source; - - -class GpioPin -{ - public: - GpioPin(gpio_num_t gpio, const char* name, gpio_pin_mode_t mode, gpio_int_type_t interruptType, uint8_t dutyResolution, std::string mqttTopic, bool httpEnable); - ~GpioPin(); - - void init(); - bool getValue(std::string* errorText); - void setValue(bool value, gpio_set_source setSource, std::string* errorText); - #ifdef ENABLE_MQTT - bool handleMQTT(std::string, char* data, int data_len); - #endif //ENABLE_MQTT - void publishState(); - void gpioInterrupt(int value); - gpio_int_type_t getInterruptType() { return _interruptType; } - gpio_pin_mode_t getMode() { return _mode; } - gpio_num_t getGPIO(){return _gpio;}; - - private: - gpio_num_t _gpio; - const char* _name; - gpio_pin_mode_t _mode; - gpio_int_type_t _interruptType; - std::string _mqttTopic; - int currentState = -1; -}; - -esp_err_t callHandleHttpRequest(httpd_req_t *req); -void taskGpioHandler(void *pvParameter); - - -class GpioHandler -{ - public: - GpioHandler(std::string configFile, httpd_handle_t httpServer); - ~GpioHandler(); - - void init(); - void deinit(); - void registerGpioUri(); - esp_err_t handleHttpRequest(httpd_req_t *req); - void taskHandler(); - void gpioInterrupt(GpioResult* gpioResult); - void flashLightEnable(bool value); - bool isEnabled() { return _isEnabled; } - #ifdef ENABLE_MQTT - void handleMQTTconnect(); - #endif //ENABLE_MQTT - - private: - std::string _configFile; - httpd_handle_t _httpServer; - std::map *gpioMap = NULL; - TaskHandle_t xHandleTaskGpio = NULL; - bool _isEnabled = false; - - int LEDNumbers = 2; - Rgb LEDColor = Rgb{ 255, 255, 255 }; - LedType LEDType = LED_WS2812; - #ifdef __LEDGLOBAL - SmartLed *leds_global = NULL; - #endif - - bool readConfig(); - void clear(); - - gpio_num_t resolvePinNr(uint8_t pinNr); - gpio_pin_mode_t resolvePinMode(std::string input); - gpio_int_type_t resolveIntType(std::string input); -}; - -void gpio_handler_create(httpd_handle_t server); -void gpio_handler_init(); -void gpio_handler_deinit(); -void gpio_handler_destroy(); -GpioHandler* gpio_handler_get(); - -#endif //SERVER_GPIO_H diff --git a/code/components/jomjol_controlcamera/ClassControllCamera.cpp b/code/components/jomjol_controlcamera/ClassControllCamera.cpp index 3f938fe2a..433aeaab2 100644 --- a/code/components/jomjol_controlcamera/ClassControllCamera.cpp +++ b/code/components/jomjol_controlcamera/ClassControllCamera.cpp @@ -25,7 +25,7 @@ #include "CImageBasis.h" #include "ClassLogFile.h" #include "server_ota.h" -#include "server_GPIO.h" +#include "GpioControl.h" #include "MainFlowControl.h" @@ -75,40 +75,56 @@ static camera_config_t camera_config = { }; -void CCamera::ledc_init(void) +#ifdef GPIO_FLASHLIGHT_DEFAULT_USE_PWM +void CCamera::ledcInitFlashlightDefault(void) { - // Prepare and then apply the LEDC PWM timer configuration + // Prepare GPIO for flashlight default + gpio_config_t conf = { }; + conf.pin_bit_mask = 1LL << GPIO_FLASHLIGHT_DEFAULT; + conf.mode = GPIO_MODE_OUTPUT; + gpio_config(&conf); + + // Prepare LEDC PWM timer configuration ledc_timer_config_t ledc_timer = { }; - ledc_timer.speed_mode = LEDC_MODE; - ledc_timer.timer_num = LEDC_TIMER; - ledc_timer.duty_resolution = LEDC_DUTY_RES; - ledc_timer.freq_hz = LEDC_FREQUENCY; // Set output frequency at 5 kHz - ledc_timer.clk_cfg = LEDC_AUTO_CLK; + ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE; + ledc_timer.timer_num = FLASHLIGHT_DEFAULT_LEDC_TIMER; // Use TIMER 1 (TIMER0: camera) + ledc_timer.duty_resolution = FLASHLIGHT_DEFAULT_DUTY_RESOLUTION; // 13 bit + ledc_timer.freq_hz = FLASHLIGHT_DEFAULT_FREQUENCY; // Use output frequency at 5 kHz + ledc_timer.clk_cfg = LEDC_USE_APB_CLK; - ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); + esp_err_t retVal = ledc_timer_config(&ledc_timer); - // Prepare and then apply the LEDC PWM channel configuration + if (retVal != ESP_OK) + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to init LEDC timer " + + std::to_string((int)FLASHLIGHT_DEFAULT_LEDC_TIMER) + ", Error: " +intToHexString(retVal)); + + // Prepare LEDC PWM channel configuration ledc_channel_config_t ledc_channel = { }; - ledc_channel.speed_mode = LEDC_MODE; - ledc_channel.channel = LEDC_CHANNEL; - ledc_channel.timer_sel = LEDC_TIMER; + ledc_channel.speed_mode = LEDC_LOW_SPEED_MODE; + ledc_channel.channel = FLASHLIGHT_DEFAULT_LEDC_CHANNEL; // CH0: Camera, CH2 - CH7: GPIO + ledc_channel.timer_sel = FLASHLIGHT_DEFAULT_LEDC_TIMER; // Use TIMER 1 (TIMER0: camera) ledc_channel.intr_type = LEDC_INTR_DISABLE; - ledc_channel.gpio_num = LEDC_OUTPUT_IO; + ledc_channel.gpio_num = GPIO_FLASHLIGHT_DEFAULT; // Use default flashlight GPIO pin ledc_channel.duty = 0; // Set duty to 0% ledc_channel.hpoint = 0; - ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); + retVal = ledc_channel_config(&ledc_channel); + + if (retVal != ESP_OK) + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to init LEDC channel " + + std::to_string((int)FLASHLIGHT_DEFAULT_LEDC_CHANNEL) + ", Error: " +intToHexString(retVal)); } +#endif CCamera::CCamera() { cameraInitSuccessful = false; - camParameter.flashTime = 2000; // flashTime in ms - camParameter.flashIntensity = 4095; + camParameter.flashTime = 2000; // Flash time in ms + camParameter.flashIntensity = 0; // Flash intensity [0 .. 100%] camParameter.actualResolution = FRAMESIZE_VGA; camParameter.actualQuality = 12; @@ -129,8 +145,8 @@ CCamera::CCamera() demoMode = false; - #ifdef GPIO_FLASHLIGHT_DEFAULT_USE_LEDC - ledc_init(); + #ifdef GPIO_FLASHLIGHT_DEFAULT_USE_PWM + ledcInitFlashlightDefault(); #endif } @@ -582,18 +598,14 @@ bool CCamera::setMirrorFlip(bool _mirror, bool _flip) void CCamera::setFlashIntensity(int _flashIntensity) { - _flashIntensity = std::min(_flashIntensity, 100); - _flashIntensity = std::max(_flashIntensity, 0); - camParameter.flashIntensity = ((_flashIntensity * LEDC_RESOLUTION) / 100); - ESP_LOGD(TAG, "Set flashIntensity to %d of %d", camParameter.flashIntensity, LEDC_RESOLUTION); // @TODO: LOGD + camParameter.flashIntensity = std::min(std::max(0, _flashIntensity), 100); } /* Set flash time in milliseconds */ void CCamera::setFlashTime(int _flashTime) { - camParameter.flashTime = std::max(_flashTime, 0); - ESP_LOGD(TAG, "Set flashTime to %d", camParameter.flashTime); // @TODO: LOGD + camParameter.flashTime = std::max(0, _flashTime); } @@ -903,35 +915,43 @@ esp_err_t CCamera::captureToStream(httpd_req_t *_req, bool _flashlightOn) void CCamera::setFlashlight(bool _status) { GpioHandler* gpioHandler = gpio_handler_get(); - if ((gpioHandler != NULL) && (gpioHandler->isEnabled())) { - ESP_LOGD(TAG, "GPIO handler enabled: Trigger flashlight by GPIO handler"); - gpioHandler->flashLightEnable(_status); + #ifdef GPIO_FLASHLIGHT_DEFAULT_USE_SMARTLED + if (gpioHandler != NULL) { + gpioHandler->gpioFlashlightControl(_status, camParameter.flashIntensity); } - else { - #ifdef GPIO_FLASHLIGHT_DEFAULT_USE_LEDC - if (_status) { - ESP_LOGD(TAG, "Default flashlight turn on with PWM %d", camParameter.flashIntensity); - ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, camParameter.flashIntensity)); - // Update duty to apply the new value - ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL)); - } - else { - ESP_LOGD(TAG, "Default flashlight turn off PWM"); - ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0)); - ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL)); - } #else - // Init the GPIO - esp_rom_gpio_pad_select_gpio(FLASH_GPIO); - // Set the GPIO as a push/pull output - gpio_set_direction(GPIO_FLASHLIGHT_DEFAULT, GPIO_MODE_OUTPUT); - - if (_status) - gpio_set_level(GPIO_FLASHLIGHT_DEFAULT, 1); - else - gpio_set_level(GPIO_FLASHLIGHT_DEFAULT, 0); - #endif + if (gpioHandler != NULL && gpioHandler->gpioHandlerIsEnabled()) { + gpioHandler->gpioFlashlightControl(_status, camParameter.flashIntensity); } + else { + #ifdef GPIO_FLASHLIGHT_DEFAULT_USE_PWM + if (_status) { + int intensityValue = (camParameter.flashIntensity * FLASHLIGHT_DEFAULT_RESOLUTION_RANGE) / 100; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Default flashlight PWM: GPIO" + + std::to_string((int)GPIO_FLASHLIGHT_DEFAULT) + ", State: 1, Intensity: " + + std::to_string(intensityValue) + "/" + std::to_string(FLASHLIGHT_DEFAULT_RESOLUTION_RANGE)); + + ledc_set_duty(LEDC_LOW_SPEED_MODE, FLASHLIGHT_DEFAULT_LEDC_CHANNEL, intensityValue); + ledc_update_duty(LEDC_LOW_SPEED_MODE, FLASHLIGHT_DEFAULT_LEDC_CHANNEL); // Update duty to apply the new value + } + else { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Default flashlight PWM: GPIO" + + std::to_string((int)GPIO_FLASHLIGHT_DEFAULT) + ", State: 0"); + + ledc_set_duty(LEDC_LOW_SPEED_MODE, FLASHLIGHT_DEFAULT_LEDC_CHANNEL, 0); + ledc_update_duty(LEDC_LOW_SPEED_MODE, FLASHLIGHT_DEFAULT_LEDC_CHANNEL); + } + #else + esp_rom_gpio_pad_select_gpio(GPIO_FLASHLIGHT_DEFAULT); // Init the GPIO + gpio_set_direction(GPIO_FLASHLIGHT_DEFAULT, GPIO_MODE_OUTPUT); // Set the GPIO as a push/pull output + + if (_status) + gpio_set_level(GPIO_FLASHLIGHT_DEFAULT, 1); + else + gpio_set_level(GPIO_FLASHLIGHT_DEFAULT, 0); + #endif + } + #endif } diff --git a/code/components/jomjol_controlcamera/ClassControllCamera.h b/code/components/jomjol_controlcamera/ClassControllCamera.h index 2f029b2e9..589ccdaf6 100644 --- a/code/components/jomjol_controlcamera/ClassControllCamera.h +++ b/code/components/jomjol_controlcamera/ClassControllCamera.h @@ -72,7 +72,10 @@ class CCamera esp_err_t captureToHTTP(httpd_req_t *_req); esp_err_t captureToStream(httpd_req_t *_req, bool _flashlightOn); - void ledc_init(void); + #ifdef GPIO_FLASHLIGHT_DEFAULT_USE_PWM + void ledcInitFlashlightDefault(void); + #endif + void setFlashIntensity(int _flashIntensity); void setFlashTime(int _flashTime); int getFlashTime(); diff --git a/code/components/jomjol_fileserver_ota/server_file.cpp b/code/components/jomjol_fileserver_ota/server_file.cpp index cbbbf6bd1..ea1e24482 100644 --- a/code/components/jomjol_fileserver_ota/server_file.cpp +++ b/code/components/jomjol_fileserver_ota/server_file.cpp @@ -27,7 +27,7 @@ extern "C" { #include "ClassLogFile.h" #include "MainFlowControl.h" #include "server_help.h" -#include "server_GPIO.h" +#include "GpioControl.h" #include "Helper.h" #ifdef ENABLE_MQTT diff --git a/code/components/jomjol_fileserver_ota/server_ota.cpp b/code/components/jomjol_fileserver_ota/server_ota.cpp index 67c36e9fd..04294fad9 100644 --- a/code/components/jomjol_fileserver_ota/server_ota.cpp +++ b/code/components/jomjol_fileserver_ota/server_ota.cpp @@ -27,7 +27,7 @@ #endif //ENABLE_MQTT #include "MainFlowControl.h" -#include "server_GPIO.h" +#include "GpioControl.h" #include "ClassControllCamera.h" #include "connect_wlan.h" #include "ClassLogFile.h" diff --git a/code/components/jomjol_flowcontroll/ClassFlowControll.cpp b/code/components/jomjol_flowcontroll/ClassFlowControll.cpp index 72cc355c3..52f162f07 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowControll.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowControll.cpp @@ -22,7 +22,7 @@ extern "C" { #include "server_ota.h" #include "server_help.h" #include "MainFlowControl.h" -#include "server_GPIO.h" +#include "GpioControl.h" #ifdef ENABLE_MQTT #include "interface_mqtt.h" @@ -854,8 +854,8 @@ void ClassFlowControll::AnalogDrawROI(CImageBasis *_zw) #ifdef ENABLE_MQTT bool ClassFlowControll::StartMQTTService() { - if (flowMQTT == NULL) - return false; + if (flowMQTT == NULL) // Service disabled + return true; return flowMQTT->Start(AutoInterval); } diff --git a/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp b/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp index 281e09ee8..ae4be0c40 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp @@ -183,7 +183,9 @@ bool ClassFlowTakeImage::ReadParameter(FILE* pfile, std::string& aktparamgraph) } } - Camera.ledc_init(); // PWM init needs to be done here due to parameter reload (camera class not to be deleted completely) + #ifdef GPIO_FLASHLIGHT_DEFAULT_USE_PWM + Camera.ledcInitFlashlightDefault(); // PWM init needs to be done here due to parameter reload (camera class not to be deleted completely) + #endif Camera.setFlashIntensity(flashIntensity); Camera.setFlashTime(flashTime); Camera.setCameraFrequency(cameraFrequency); diff --git a/code/components/jomjol_flowcontroll/MainFlowControl.cpp b/code/components/jomjol_flowcontroll/MainFlowControl.cpp index 9ea52e858..a6ea1bf24 100644 --- a/code/components/jomjol_flowcontroll/MainFlowControl.cpp +++ b/code/components/jomjol_flowcontroll/MainFlowControl.cpp @@ -18,7 +18,7 @@ #include "time_sntp.h" #include "ClassControllCamera.h" #include "ClassLogFile.h" -#include "server_GPIO.h" +#include "GpioControl.h" #include "server_file.h" #include "read_wlanini.h" #include "connect_wlan.h" @@ -75,11 +75,13 @@ bool doInit(void) flowctrl.DeinitFlow(); bRetVal = false; } - + // Init GPIO handler - // Note: GPIO handler has to be initialized before MQTT init to ensure proper topic subscription + // Note: It has to be initialized before MQTT (topic subscription) + // and after flow init (MQTT main topic parameter) // ******************************************** - gpio_handler_init(); + if (!gpio_handler_init()) + bRetVal = false; // Init MQTT service // ******************************************** @@ -96,12 +98,12 @@ bool doInit(void) #ifdef ENABLE_MQTT -esp_err_t MQTTCtrlFlowStart(std::string _topic) +esp_err_t triggerFlowStartByMqtt(std::string _topic) { if (taskAutoFlowState == FLOW_TASK_STATE_IDLE_NO_AUTOSTART || taskAutoFlowState == FLOW_TASK_STATE_IDLE_AUTOSTART) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Flow start triggered by MQTT topic " + _topic); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Cycle start triggered by MQTT topic " + _topic); manualFlowStart = true; if (taskAutoFlowState == FLOW_TASK_STATE_IDLE_AUTOSTART) @@ -111,11 +113,11 @@ esp_err_t MQTTCtrlFlowStart(std::string _topic) taskAutoFlowState == FLOW_TASK_STATE_PUBLISH_DATA || taskAutoFlowState == FLOW_TASK_STATE_ADDITIONAL_TASKS) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Flow start triggered by MQTT topic "+ _topic + " got scheduled"); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Cycle start triggered by MQTT topic "+ _topic + " got scheduled"); manualFlowStart = true; } else { - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Flow start triggered by MQTT topic " + _topic + ". Flow not initialized. Request rejected"); + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Cycle start triggered by MQTT topic " + _topic + ". Main task not initialized. Request rejected"); } return ESP_OK; @@ -123,6 +125,32 @@ esp_err_t MQTTCtrlFlowStart(std::string _topic) #endif //ENABLE_MQTT + +void triggerFlowStartByGpio() +{ + if (taskAutoFlowState == FLOW_TASK_STATE_IDLE_NO_AUTOSTART || + taskAutoFlowState == FLOW_TASK_STATE_IDLE_AUTOSTART) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Cycle start triggered by GPIO"); + manualFlowStart = true; + + if (taskAutoFlowState == FLOW_TASK_STATE_IDLE_AUTOSTART) + xTaskAbortDelay(xHandletask_autodoFlow); // Delay will be aborted if task is in blocked (waiting) state + } + else if (taskAutoFlowState == FLOW_TASK_STATE_IMG_PROCESSING || + taskAutoFlowState == FLOW_TASK_STATE_PUBLISH_DATA || + taskAutoFlowState == FLOW_TASK_STATE_ADDITIONAL_TASKS) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Cycle start triggered by GPIO got scheduled"); + manualFlowStart = true; + } + else { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Cycle start triggered by GPIO. Main task not initialized. Request rejected"); + } +} + + + esp_err_t handler_cycle_start(httpd_req_t *req) { httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); diff --git a/code/components/jomjol_flowcontroll/MainFlowControl.h b/code/components/jomjol_flowcontroll/MainFlowControl.h index 499dfd725..2135e13ef 100644 --- a/code/components/jomjol_flowcontroll/MainFlowControl.h +++ b/code/components/jomjol_flowcontroll/MainFlowControl.h @@ -14,8 +14,9 @@ extern ClassFlowControll flowctrl; #ifdef ENABLE_MQTT -esp_err_t MQTTCtrlFlowStart(std::string _topic); +esp_err_t triggerFlowStartByMqtt(std::string _topic); #endif //ENABLE_MQTT +void triggerFlowStartByGpio(); void setTaskAutoFlowState(int _value); diff --git a/code/components/jomjol_mqtt/interface_mqtt.cpp b/code/components/jomjol_mqtt/interface_mqtt.cpp index 35eebfdf6..fdff1b2f0 100644 --- a/code/components/jomjol_mqtt/interface_mqtt.cpp +++ b/code/components/jomjol_mqtt/interface_mqtt.cpp @@ -411,7 +411,7 @@ bool mqtt_handler_flow_start(std::string _topic, char* _data, int _data_len) //ESP_LOGD(TAG, "Handler called: topic %s, data %.*s", _topic.c_str(), _data_len, _data); if (_data_len > 0) { - MQTTCtrlFlowStart(_topic); + triggerFlowStartByMqtt(_topic); } else { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_flow_start: handler called, but no data"); diff --git a/code/components/smartleds b/code/components/smartleds new file mode 160000 index 000000000..ede05a641 --- /dev/null +++ b/code/components/smartleds @@ -0,0 +1 @@ +Subproject commit ede05a64142a75e71d7dcd4a321e045ea53e0c5a diff --git a/code/include/defines.h b/code/include/defines.h index d94bec1d2..c9b1b8024 100644 --- a/code/include/defines.h +++ b/code/include/defines.h @@ -6,7 +6,7 @@ // Needs to be increased whenever config.ini gets modified and migration is necessary // Add migration routine in main.cpp --> migrateConfiguration() //************************************************************************************** -#define CONFIG_FILE_VERSION 1 +#define CONFIG_FILE_VERSION 2 //************************************************************************************** @@ -103,19 +103,13 @@ //compiler optimization for tflite-micro-esp-examples #define XTENSA -//#define CONFIG_IDF_TARGET_ARCH_XTENSA //not needed with platformio/espressif32 @ 5.2.0 +//#define CONFIG_IDF_TARGET_ARCH_XTENSA // not needed with platformio/espressif32 @ 5.2.0 //ClassControllCamera -#define CAM_LIVESTREAM_REFRESHRATE 500 // Camera livestream feature: Waiting time in milliseconds to refresh image - - -//ClassControllCamera + ClassFlowTakeImage +#define CAM_LIVESTREAM_REFRESHRATE 500 // Camera livestream feature: Waiting time in milliseconds to refresh image #define DEMO_IMAGE_SIZE 30000 // Max size of demo image in bytes -//server_GPIO -#define __LEDGLOBAL - //server_GPIO + server_file + SoftAP #define CONFIG_FILE "/sdcard/config/config.ini" @@ -127,7 +121,7 @@ // server_file + Helper - #define FILE_PATH_MAX (255) //Max length a file path can have on storage +#define FILE_PATH_MAX (255) //Max length a file path can have on storage //server_file +(ota_page.html + upload_script.html) @@ -300,6 +294,18 @@ CONFIG_WPA_11R_SUPPORT=n #endif // ENABLE_SOFTAP +// Global flashlight definitions +#define FLASHLIGHT_DEFAULT_LEDC_TIMER LEDC_TIMER_1 +#define FLASHLIGHT_DEFAULT_LEDC_CHANNEL LEDC_CHANNEL_1 +#define FLASHLIGHT_DEFAULT_FREQUENCY (5000) // 5kHz +#define FLASHLIGHT_DEFAULT_DUTY_RESOLUTION LEDC_TIMER_13_BIT // 13 bit resolution --> 8192: 0 .. 8191 +#define FLASHLIGHT_DEFAULT_RESOLUTION_RANGE ((1 << FLASHLIGHT_DEFAULT_DUTY_RESOLUTION) - 1) // 13 bit resolution --> 8192: 0 .. 8191 +#define FLASHLIGHT_DEFAULT "flashlight-default" +#define FLASHLIGHT_PWM "flashlight-pwm" +#define FLASHLIGHT_SMARTLED "flashlight-smartled" +#define FLASHLIGHT_DIGITAL "flashlight-digital" + + //************************************************************************* // HARDWARE RELATED DEFINITIONS //************************************************************************* @@ -328,9 +334,10 @@ CONFIG_WPA_11R_SUPPORT=n // Board types //************************************ #ifdef BOARD_AITHINKER_ESP32CAM - #define BOARD_SDCARD_SDMMC_BUS_WIDTH_1 // Only 1 line SD card operation is supported (hardware related) + #define BOARD_SDCARD_SDMMC_BUS_WIDTH_1 // Set 1 line SD card operation // SD card (operated with SDMMC peripheral) + //------------------------------------------------- #define GPIO_SDCARD_CLK GPIO_NUM_14 #define GPIO_SDCARD_CMD GPIO_NUM_15 #define GPIO_SDCARD_D0 GPIO_NUM_2 @@ -340,17 +347,68 @@ CONFIG_WPA_11R_SUPPORT=n #endif #define GPIO_SDCARD_D3 GPIO_NUM_13 // Needs to be high to init SD in MMC mode. After init GPIO can be used as spare GPIO + // LEDs + //------------------------------------------------- #define GPIO_STATUS_LED_ONBOARD GPIO_NUM_33 // Onboard red status LED #define GPIO_FLASHLIGHT_ONBOARD GPIO_NUM_4 // Onboard flashlight LED - #define GPIO_FLASHLIGHT_DEFAULT_USE_LEDC // Activate LEDC peripheral for PWM control - #ifdef BOARD_SDCARD_SDMMC_BUS_WIDTH_1 #define GPIO_FLASHLIGHT_DEFAULT GPIO_FLASHLIGHT_ONBOARD // Use onboard flashlight as default flashlight #else - #define GPIO_FLASHLIGHT_DEFAULT GPIO_NUM_13 // Onboard flashlight cannot be used if SD card operated in 4-line mode -> Define GPIO13 + #define GPIO_FLASHLIGHT_DEFAULT GPIO_NUM_13 // Onboard flashlight cannot be used if SD card operated in 4-line mode -> Define e.g. GPIO13 + #endif + + #define GPIO_FLASHLIGHT_DEFAULT_USE_PWM // Default flashlight LED (.e.g onboard LED) is PWM controlled + //#define GPIO_FLASHLIGHT_DEFAULT_USE_SMARTLED // Default flashlight SmartLED (e.g. onboard WS2812X) controlled + + #ifdef GPIO_FLASHLIGHT_DEFAULT_USE_SMARTLED + #define GPIO_FLASHLIGHT_DEFAULT_SMARTLED_TYPE LED_WS2812 // Flashlight default: SmartLED type + #define GPIO_FLASHLIGHT_DEFAULT_SMARTLED_QUANTITY 1 // Flashlight default: SmartLED Quantity #endif + + + // Spare GPIO + //------------------------------------------------- + // Options for usage defintion: + // - 'spare': Free to use + // - 'restricted: usage': Restricted usable (WebUI expert view) + // - 'flashlight-pwm' or 'flashlight-smartled' or 'flashlight-digital' (ON/OFF) -> Map to 'flashlight-default' + // --> ESP32CAM: flashlight-default -> flashlight-pwm (Onboard LED, PWM controlled) + //------------------------------------------------- + #define GPIO_SPARE_PIN_COUNT 6 + + #define GPIO_SPARE_1 GPIO_NUM_1 // Use carefully: UART pin for debug/logging + #define GPIO_SPARE_1_USAGE "restricted: uart0-tx" // Only visible when expert mode is activated + + #define GPIO_SPARE_2 GPIO_NUM_3 // Use carefully: UART pin for debug/logging + #define GPIO_SPARE_2_USAGE "restricted: uart0-rx" // Only visible when expert mode is activated + + #ifdef BOARD_SDCARD_SDMMC_BUS_WIDTH_1 + #define GPIO_SPARE_3 GPIO_FLASHLIGHT_DEFAULT // Use carefully: flashlight-default + #if defined(GPIO_FLASHLIGHT_DEFAULT_USE_PWM) + #define GPIO_SPARE_3_USAGE FLASHLIGHT_PWM // Define flashlight-default as ... + #elif defined(GPIO_FLASHLIGHT_DEFAULT_USE_SMARTLED) + #define GPIO_SPARE_3_USAGE FLASHLIGHT_SMARTLED // Define flashlight-default as ... + #else + #define GPIO_SPARE_3_USAGE FLASHLIGHT_DIGITAL // Define flashlight-default as ... + #endif + + #define GPIO_SPARE_4 GPIO_NUM_12 + #define GPIO_SPARE_4_USAGE "spare" + #else + #define GPIO_SPARE_3 GPIO_NUM_NC // Not usable, in use for 'SD-card' + #define GPIO_SPARE_3_USAGE "" + + #define GPIO_SPARE_4 GPIO_NUM_NC // Not usable, in use for 'SD-card' + #define GPIO_SPARE_4_USAGE "" + #endif + + #define GPIO_SPARE_5 GPIO_NUM_13 + #define GPIO_SPARE_5_USAGE "spare" + + #define GPIO_SPARE_6 GPIO_NUM_NC // Not defined spare position + #define GPIO_SPARE_6_USAGE "" #else #error "define.h: No board type defined or type unknown" #endif //Board types @@ -381,16 +439,4 @@ CONFIG_WPA_11R_SUPPORT=n #error "define.h: No camera model defined or model unknown" #endif //Camera models - -// GPIO_FLASHLIGHT_DEFAULT PWM definitions -#ifdef GPIO_FLASHLIGHT_DEFAULT_USE_LEDC - #define LEDC_TIMER LEDC_TIMER_1 // LEDC_TIMER_0 is used for camera - #define LEDC_MODE LEDC_LOW_SPEED_MODE - #define LEDC_OUTPUT_IO GPIO_FLASHLIGHT_DEFAULT // Define the output GPIO of default flashlight - #define LEDC_CHANNEL LEDC_CHANNEL_1 // LEDC_CHANNEL_0 is used for camera - #define LEDC_DUTY_RES LEDC_TIMER_13_BIT // Set duty resolution to 13 bits - #define LEDC_RESOLUTION (1 << LEDC_TIMER_13_BIT) -1 // 13 bit resolution --> 8192: 0 .. 8191 - #define LEDC_FREQUENCY (5000) // Frequency in hertz. Set frequency at 5 kHz -#endif //GPIO_FLASHLIGHT_DEFAULT_USE_LEDC - -#endif //DEFINES_H +#endif //DEFINES_H \ No newline at end of file diff --git a/code/main/main.cpp b/code/main/main.cpp index 52ce4a893..f7573e854 100644 --- a/code/main/main.cpp +++ b/code/main/main.cpp @@ -38,7 +38,7 @@ #include "server_ota.h" #include "time_sntp.h" #include "configFile.h" -#include "server_GPIO.h" +#include "GpioControl.h" #include "server_camera.h" #ifdef ENABLE_MQTT @@ -508,11 +508,39 @@ void migrateConfiguration(void) * - Only one whitespace before/after the equal sign */ + //************************************************************************************************* + // Migrate from version 1 to version 2 + // Migrate GPIO section due to PR#154 (complete refactoring of GPIO) which is part of v17.x + //************************************************************************************************* + if (configFileVersion == 1) { + // Update config version + // --------------------- + if (section == sectionConfigFile) { + if(replaceString(configLines[i], "Version = " + std::to_string(configFileVersion), + "Version = " + std::to_string(configFileVersion+1))) { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Config.ini: Migrate v" + std::to_string(configFileVersion) + + " > v" + std::to_string(configFileVersion+1)); + migrated = true; + } + } + + // Migrate parameter + // --------------------- + if (section == "[GPIO]") { + // Erase complete section content due to major change in parameter usage + // Section will be filled again by WebUI after save config initially + if (configLines[i].find("[GPIO]") == std::string::npos && !configLines[i].empty()) { + configLines.erase(configLines.begin()+i); + i--; // One element removed, check same position again + } + } + } + //************************************************************************************************* // Migrate from version 0 to version 1 // Version 0: All config file versions before 17.x //************************************************************************************************* - if (configFileVersion == 0) { + else if (configFileVersion == 0) { // Update config version // --------------------- if (section == sectionConfigFile) { diff --git a/code/platformio.ini b/code/platformio.ini index 1bae9b5af..6ef8b251c 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -83,7 +83,14 @@ monitor_filters = default, esp32_exception_decoder extends = env:esp32cam extra_scripts = post:scripts/localbuild.py # Add parameter tooltips to HTML page # and hashes to all cached HTML files - +build_flags = + ; ### Common flags: + ${common:esp32-idf.build_flags} + ${flags:runtime.build_flags} + ; ### Hardware: Define board type + camera model + ; ### (see 'include/defines.h' for definitions) + -D ENV_BOARD_TYPE=1 + -D ENV_CAMERA_MODEL=1 ; +++++++++++++++++++++++++++++++++++++++++++ ; Use this environment for task analysis (PR #1751) @@ -93,6 +100,11 @@ extends = env:esp32cam ; sdkconfig.esp32cam-task-analysis.defaults override some sdkconfig.defaults ; and enable debug analysis options build_flags = + ; ### Hardware: Define board type + camera model + ; ### (see 'include/defines.h' for definitions) + -D ENV_BOARD_TYPE=1 + -D ENV_CAMERA_MODEL=1 + ; ### Debug options : -D TASK_ANALYSIS_ON ;-D DEBUG_DETAIL_ON ;please use only one HEAP tracing at time. @@ -112,6 +124,10 @@ extends = env:esp32cam build_flags = ; ### clangtidy build flags: ${flags:clangtidy.build_flags} + ; ### Hardware: Define board type + camera model + ; ### (see 'include/defines.h' for definitions) + -D ENV_BOARD_TYPE=1 + -D ENV_CAMERA_MODEL=1 ; ### Debug options : -D DEBUG_DETAIL_ON ;-D DEBUG_DISABLE_BROWNOUT_DETECTOR @@ -128,5 +144,10 @@ lib_ldf_mode = deep+ [env:esp32cam-himem] extends = env:esp32cam ; sdkconfig.esp32cam-dev-himem.defaults override some sdkconfig.defaults -build_flags = +build_flags = + ; ### Hardware: Define board type + camera model + ; ### (see 'include/defines.h' for definitions) + -D ENV_BOARD_TYPE=1 + -D ENV_CAMERA_MODEL=1 + ; ### Debug options : ;-D DEBUG_HIMEM_MEMORY_CHECK diff --git a/code/sdkconfig.defaults b/code/sdkconfig.defaults index 0912d5d1c..0ba52400c 100644 --- a/code/sdkconfig.defaults +++ b/code/sdkconfig.defaults @@ -57,7 +57,7 @@ CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y # RMT Configuration # CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y - +CONFIG_RMT_ISR_IRAM_SAFE=y # # ESP-TLS diff --git a/docs/API/MQTT/gpio.md b/docs/API/MQTT/gpio.md index 2953ed426..937d49c8f 100644 --- a/docs/API/MQTT/gpio.md +++ b/docs/API/MQTT/gpio.md @@ -4,28 +4,59 @@ !!! __Note__: The respective GPIO (General Purpose Input / Output) pin needs to be enabled in configuration - (`Settings` > `Configuration` > section `GPIO`). + (`Settings` > `Configuration` > section `GPIO` > `GPIO Expose to MQTT`). + ### GPIO Control GPIO pins can be controlled by publishing data to the following topic. -- Format: `[MainTopic]/device/gpio/[GPIOName]` -- Example: `watermeter/device/gpio/GPIO12` - -| Topic | Description | Payload -|:-------------------------------------|:----------------------------|:-------------- -|`[MainTopic]/device/gpio/[GPIO Name]` | Control GPIO Pin State | `HIGH`: `true` or `1`
`LOW`: `false` or `0` - +- Format: `[MainTopic]/device/gpio/[GPIOName]/ctrl` +- Example: `watermeter/device/gpio/gpio4/ctrl` + +| Topic | Description | Payload +|:-----------------------------------------|:----------------------------|:-------------- +|`[MainTopic]/device/gpio/[GPIOName]/ctrl` | Control GPIO Pin State

Valid for mode:
-`Output` | see Usage Details 1 +|`[MainTopic]/device/gpio/[GPIOName]/ctrl` | Control GPIO Pin State

Valid for mode:
-`Output PWM` | see Usage Details 2 + + +Usage Details +1. `[MainTopic]/device/gpio/[GPIOName]/ctrl`: Set GPIO output state (only applicaple for mode `Output`)
+ Payload (needs to be provided in JSON notation): + - `state:` Desired state of GPIO output [`1` / `0`] + - Example: + ``` + { + "state": 0 + } + ``` + +2. `[MainTopic]/device/gpio/[GPIOName]/ctrl`: Set GPIO output state and PWM duty (only applicaple for mode `Output PWM`)
+ Payload (needs to be provided in JSON notation): + - `state:` GPIO State [`1` or `0`] + - `pwm_duty:` GPIO PWM Duty [0 .. 2^Duty Resolution - 1] + - Duty Resolution is derived from configured PWM frequency, e.g. 5Khz frequency -> 13 Bit
+ - Formula: log2(APB CLK Frequency / Desired Frequency) = log2(80000000 / 5000) = 13.966
+ - Maximum resolution is limited to 14 Bit due to compability reasons (e.g. ESP32S3) + - Example: + ``` + { + "state": 1, + "pwm_duty": 1024 + } + ``` --- + ### GPIO Status GPIO pin status gets published to the following topic. -- Format: `[MainTopic]/device/gpio/[GPIOName]` -- Example: `watermeter/device/gpio/GPIO12` +- Format: `[MainTopic]/device/gpio/[GPIOName]/state` +- Example: `watermeter/device/gpio/GPIO4/state` + +| Topic | Description | Output +|:-------------------------------------------|:----------------------------|:-------------- +| `[MainTopic]/device/gpio/[GPIOName]/state` | Actual GPIO Pin State

Valid for modes:
-`Input`
-`Input Pullup`
-`Input Pulldown`
-`Output`
-`Flashlight default`
-`Flashlight smartled`
-`Flashlight digital` | `{"state": 0}` +| `[MainTopic]/device/gpio/[GPIOName]/state` | Actual GPIO Pin State

Valid for modes:
-`Output PWM`
-`Flashlight PWM`
-`Flashlight default` | `{"state": 0, "pwm_duty": 1024}` -| Topic | Description | Output -|:-------------------------------------|:----------------------------|:-------------- -| `[MainTopic]/device/gpio/[GPIOName]` | GPIO Pin State

Possible States:
- `HIGH`: `true`
- `LOW`: `false` | `false` diff --git a/docs/API/REST/gpio.md b/docs/API/REST/gpio.md index 0f675c4f9..58df0e48c 100644 --- a/docs/API/REST/gpio.md +++ b/docs/API/REST/gpio.md @@ -5,19 +5,100 @@ `http://IP-ADDRESS/gpio` -Interact with GPIO pins +Interact with GPIO pins and retrieve GPIO configuration Payload: -1. Control a GPIO output - - `/gpio?GPIO=[PinNumber]&Status=high` - - `/gpio?GPIO=[PinNumber]&Status=low` - - Example: `/gpio?GPIO=12&Status=high` - -2. Read a GPIO input - - `/gpio?GPIO=[PinNumber]` - - Example: `/gpio?GPIO=12` - -Response: -- Content type: `HTML` -- Content: Query response text +1. Control a GPIO output (GPIO mode: `Output`) + - Format: `/gpio?task=set_state&gpio=[PinNumber]&state=1` + - Example: `/gpio?task=set_state&gpio=4&state=0` + - Response: + - Content type: `HTML` + - Content: `GPIO4, State: 0` + +2. Control a GPIO PWM output (GPIO mode: `Output PWM`) + - Format: `/gpio?task=set_state&gpio=[PinNumber]&state=1&pwm_duty=1024` + - Example: `/gpio?task=set_state&gpio=4&state=0&pwm_duty=1024` + - Response: + - Content type: `HTML` + - Content: `GPIO4, State: 0, PWM Duty: 1024` + +3. Read a digital GPIO + - Valid for GPIO modes + - `Input` + - `Input Pullup` + - `Input Pulldown` + - `Output` + - `Flashlight Default` (depending on configuration) + - `Flashlight Smartled` + - `Flashlight Digital` + - `Trigger Cycle Start` + - Format: `/gpio?task=get_state&gpio=[PinNumber]` + - Example: `/gpio?task=get_state&gpio=4` + - Response: + - Content type: `JSON` + - Content: `{ "state": 1 }` + +4. Read a PWM controlled GPIO + - Valid for GPIO modes + - `Output PWM` + - `Flashlight PWM` + - `Flashlight Default` (depending on configuration) + - Format: `/gpio?task=get_state&gpio=[PinNumber]` + - Example: `/gpio?task=get_state&gpio=4` + - Response: + - Content type: `JSON` + - Content: `{ "state": 1, "pwm_duty": 1024 }` + +5. Get GPIO pin configuration + - This is used to configure WebUI GPIO section based on board configuration defined in firmware (includes/defines.h) + - Format: `/gpio?task=gpio_config` + - Response: + - Content type: `JSON` + - Content: JSON response + - Example: + ``` + { + "default_config": { + "gpio_modes": [ + "input", + "input-pullup", + "input-pulldown", + "output", + "output-pwm", + "flashlight-pwm", + "flashlight-smartled", + "flashlight-digital", + "trigger-cycle-start" + ], + "gpio_interrupt": [ + "cyclic-polling", + "interrupt-rising-edge", + "interrupt-falling-edge", + "interrupt-rising-falling" + ] + }, + "gpio": [ + { + "name": 1, + "usage": "restricted: uart0-tx" + }, + { + "name": 3, + "usage": "restricted: uart0-rx" + }, + { + "name": 4, + "usage": "flashlight-pwm" + }, + { + "name": 12, + "usage": "spare" + }, + { + "name": 13, + "usage": "spare" + } + ] + } + ``` \ No newline at end of file diff --git a/docs/Configuration/Parameter/GPIO/GPIOCaptureMode.md b/docs/Configuration/Parameter/GPIO/GPIOCaptureMode.md new file mode 100644 index 000000000..3a7dd33be --- /dev/null +++ b/docs/Configuration/Parameter/GPIO/GPIOCaptureMode.md @@ -0,0 +1,28 @@ +# Parameter: GPIO Capture Mode + +| | WebUI | Config.ini +|:--- |:--- |:---- +| Parameter Name | GPIO Capture Mode | IOx: 2. parameter +| Default Value | `cyclic polling` | `cyclic-polling` +| Input Options | `cyclic polling`
`interrupt rising edge`
`interrupt falling edge`
`interrupt rising falling` | `cyclic-polling`
`interrupt-rising-edge`
`interrupt-falling-edge`
`interrupt-rising-falling` + + + +## Description + +GPIO capture mode (only for GPIO input modes).
+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 + + +!!! Tip + To debounce the GPIO input, use any interrupt capture mode. + Debounce time can be defined with parameter `GPIO Debounce Time`. + diff --git a/docs/Configuration/Parameter/GPIO/GPIODebounceTime.md b/docs/Configuration/Parameter/GPIO/GPIODebounceTime.md new file mode 100644 index 000000000..c263bf81b --- /dev/null +++ b/docs/Configuration/Parameter/GPIO/GPIODebounceTime.md @@ -0,0 +1,19 @@ +# Parameter: GPIO Debounce Time + +| | WebUI | Config.ini +|:--- |:--- |:---- +| Parameter Name | GPIO PWM Frequency | IOx: 3. parameter +| Default Value | `200` | `200` +| Input Options | `0 .. 5000` | `0 .. 5000` +| Unit | Milliseconds | Milliseconds + + + +## Description + +GPIO debounce time to avoid to many capture events due to any signal flicker +of e.g. while pressing a push button (only for GPIO input modes) + + +!!! Note + Debouncing is removing unwanted input noise from buttons, switches or other user input. \ No newline at end of file diff --git a/docs/Configuration/Parameter/GPIO/GPIOEnabled.md b/docs/Configuration/Parameter/GPIO/GPIOEnabled.md new file mode 100644 index 000000000..c8ce9c452 --- /dev/null +++ b/docs/Configuration/Parameter/GPIO/GPIOEnabled.md @@ -0,0 +1,13 @@ +# Parameter: GPIO Enabled + +| | WebUI | Config.ini +|:--- |:--- |:---- +| Parameter Name | GPIO Enabled | IOx +| Default Value | `false` | `disabled` -> ; +| Input Options | `false`
`true` | `false`
`true` + + + +## Description + +Enable selected GPIO diff --git a/docs/Configuration/Parameter/GPIO/GPIOExposeToMQTT.md b/docs/Configuration/Parameter/GPIO/GPIOExposeToMQTT.md new file mode 100644 index 000000000..024bd26cf --- /dev/null +++ b/docs/Configuration/Parameter/GPIO/GPIOExposeToMQTT.md @@ -0,0 +1,22 @@ +# Parameter: GPIO Expose To MQTT + +| | WebUI | Config.ini +|:--- |:--- |:---- +| Parameter Name | GPIO Expose To MQTT | IOx: 5. parameter +| Default Value | `false` | `false` +| Input Options | `false`
`true` | `false`
`true` + + +## Description + +Enable publishing GPIO state to MQTT broker
+- GPIO pin state (and PWM duty)gets published to GPIO specific topic.
+- GPIO output / GPIO Output PWM are controlable via GPIO specific topic. + + +!!! Tip + Overview of MQTT topics -> see MQTT API description + + +!!! Note + MQTT functionality has to be enabled -> see section `MQTT` \ No newline at end of file diff --git a/docs/Configuration/Parameter/GPIO/GPIOExposeToREST.md b/docs/Configuration/Parameter/GPIO/GPIOExposeToREST.md new file mode 100644 index 000000000..e0bcc2b08 --- /dev/null +++ b/docs/Configuration/Parameter/GPIO/GPIOExposeToREST.md @@ -0,0 +1,18 @@ +# Parameter: GPIO Expose To REST + +| | WebUI | Config.ini +|:--- |:--- |:---- +| Parameter Name | GPIO Expose To REST | IOx: 6. parameter +| Default Value | `false` | `false` +| Input Options | `false`
`true` | `false`
`true` + + +## Description + +Enable GPIO state request by REST API calls
+- GPIO pin state (and PWM duty) can be requested by REST API
+- GPIO output / GPIO Output PWM are controlable by REST API. + + +!!! Tip + Syntax of REST API requests -> see REST API description diff --git a/docs/Configuration/Parameter/GPIO/GPIOMode.md b/docs/Configuration/Parameter/GPIO/GPIOMode.md new file mode 100644 index 000000000..68948a7bd --- /dev/null +++ b/docs/Configuration/Parameter/GPIO/GPIOMode.md @@ -0,0 +1,38 @@ +# Parameter: GPIO Mode + +| | WebUI | Config.ini +|:--- |:--- |:---- +| Parameter Name | GPIO Mode | IOx: 1. parameter +| Default Value | `input-pullup` | `input-pullup` +| Input Options | `input`
`input pullup`
`input pulldown`
`output`
`output pwm`
`flashlight default`
`flashlight pwm`
`flashlight smartled`
`flashlight digital`
`trigger cycle start` | `input`
`input-pullup`
`input-pulldown`
`output`
`output-pwm`
`flashlight-default`
`flashlight-pwm`
`flashlight-smartled`
`flashlight-digital`
`trigger-cycle-start` + + + +## Description + +GPIO operation mode + +### Input Options + +| Input Option | Direction | Description +|:--- |:--- |:--- +| `input` | input | Use as input, internal pullup and pulldown resistor is disabled +| `input pullup` | input | Use as input, internal pullup resistor is enabled +| `input pulldown` | input | Use as input, internal pulldown resistor is enabled +| `output` | output | Use as output (digital states: 0, 1)
-> controlable by REST API and/or MQTT +| `output pwm` | output | Use as output which controlable by PWM duty (duty cycle: 0 .. Max duty resolution depending on PWM frequency)
-> controlable by REST API and/or MQTT +| `flashlight default` | output | This mode represents the board's default flashlight configuration, e.g. Default for board `ESP32CAM` -> GPIO4 as PWM controlled output. This mode is only visible on respective GPIO which is defined as default in firmware +| `flashlight pwm` | output | Use for flashlight operation with regular LEDs (PWM controlled intensity) +| `flashlight smartled` | output | Use for flashlight operation with smartLEDs +| `flashlight digital` | output | Use for flashlight operation with regular LEDs (digital states: 0, 1) +| `trigger cycle start` | input
(pullup enabled) | Trigger a cycle start (by pulling signal to low) + + +!!! Tip + All flashlight modes are fully controlled by process cycle, no external manipulation can be done. + + +!!! Tip + `Flashlight digital` / `Flashlight pwm` act like an output and are activated while + flashlight is requested by process (before image gets taken). This could potentially + be used to control any mechanism to activate display before image gets taken. \ No newline at end of file diff --git a/docs/Configuration/Parameter/GPIO/GPIOName.md b/docs/Configuration/Parameter/GPIO/GPIOName.md new file mode 100644 index 000000000..fa38ebec7 --- /dev/null +++ b/docs/Configuration/Parameter/GPIO/GPIOName.md @@ -0,0 +1,15 @@ +# Parameter: GPIO Name + +| | WebUI | Config.ini +|:--- |:--- |:---- +| Parameter Name | GPIO Name | IOx: 13. parameter +| Default Value | empty -> `gpioX` | empty + + +## Description + +Name which is used as MQTT topic name + + +!!! Note + If parameter is empty, default name (gpioX) is used, e.g. gpio1 diff --git a/docs/Configuration/Parameter/GPIO/GPIOPwmFrequency.md b/docs/Configuration/Parameter/GPIO/GPIOPwmFrequency.md new file mode 100644 index 000000000..0267d3740 --- /dev/null +++ b/docs/Configuration/Parameter/GPIO/GPIOPwmFrequency.md @@ -0,0 +1,20 @@ +# Parameter: GPIO PWM Frequency + +| | WebUI | Config.ini +|:--- |:--- |:---- +| Parameter Name | GPIO PWM Frequency | IOx: 4. parameter +| Default Value | `5000` | `5000` +| Input Options | `5 .. 1000000` | `5 .. 1000000` +| Unit | Hertz | Hertz + + + +## Description + +GPIO PWM frequency (only for PWM controlled GPIO modes) + + +!!! Note + Maximum duty resolution is derived from configured PWM frequency (e.g. 5Khz PWM frequency -> 13 Bit)
+ - Formula: log2(APB CLK Frequency / Desired Frequency) = log2(80000000 / 5000) = 13.966
+ - Maximum resolution is limited to 14 Bit due to compability reasons (e.g. ESP32S3) \ No newline at end of file diff --git a/docs/Configuration/Parameter/GPIO/GPIOSelection.md b/docs/Configuration/Parameter/GPIO/GPIOSelection.md new file mode 100644 index 000000000..e55f8ef14 --- /dev/null +++ b/docs/Configuration/Parameter/GPIO/GPIOSelection.md @@ -0,0 +1,9 @@ +# GPIO Selection + +## Description + +With this drop down the respective GPIO can be selected. + +!!! Note + The GPIO selection is pre-filled automatically based on hardware defintion + in source code (includes/defines.h) diff --git a/docs/Configuration/Parameter/GPIO/IO0.md b/docs/Configuration/Parameter/GPIO/IO0.md deleted file mode 100644 index 594e3e3ff..000000000 --- a/docs/Configuration/Parameter/GPIO/IO0.md +++ /dev/null @@ -1,38 +0,0 @@ -# Parameter group: GPIO0 - -| | WebUI | Config.ini -|:--- |:--- |:---- -| Parameter Group Name | GPIO0 | IO0 -| Default State | `disabled` -> unselected | `disabled` -> ; -| Default Value | Configuration: `input`
Use Interrupt: `disabled`
PWM Duty Cycle Resolution: `10` Bit
Enable MQTT: `false`
Enable REST API: `false`
Name: ` ` | `input disabled 10 false false ` -| Input Options: Configuration | `input`
`input-pullup`
`input-pulldown`
`output` | `input`
`input-pullup`
`input-pulldown`
`output` -| Input Options: Use Interrupt | `disabled`
`rising edge`
`falling edge`
`rising and falling`
`low level trigger`
`high level trigger` | `disabled`
`rising-edge`
`falling-edge`
`rising-and-falling`
`low-level-trigger`
`high-level-trigger` -| Input Options: PWM Duty Cycle Resolution | `1` .. `20` Bit | `1` .. `20` Bit -| Input Options: Enable MQTT | `false`
`true` | `false`
`true` -| Input Options: Enable REST API | `false`
`true` | `false`
`true` -| Input Options: Name | Empty = `GPIO0`
Any name with `a-z, A-Z, 0-9, _, -`. | Empty = `GPIO0`
Any name with `a-z, A-Z, 0-9, _, -`. - - -!!! Warning - This is an **Expert Parameter**! Only change it if you understand what it does! - - -## Description - -This parameter group can be used to configure the GPIO `IO0` pin. - -!!! Warning - This pin is only usable with some restrictions. It must be disabled when the camera module is used. - Additionally, it is used to activate bootloader mode and must therefore be HIGH after a reset! - - -### Parameter - -| Parameter | Description -|:--- |:--- -| Configuration | Set the pin configuration -| Use Interrupt | Enable interrupt for GPIO pin -| PWM Duty Cycle Resolution | Set PWM duty resolution for the internal LED controller -| Enable MQTT | If enabled the pin sate is provided by MQTT and pin can be controlled by MQTT (output only) -| Enable REST API | If enabled the pin sate is provided by REST API and pin can be controlled by REST API (output only) -| Name | Name which is used for this pin (MQTT topic, REST API) diff --git a/docs/Configuration/Parameter/GPIO/IO1.md b/docs/Configuration/Parameter/GPIO/IO1.md deleted file mode 100644 index 4b0e44a11..000000000 --- a/docs/Configuration/Parameter/GPIO/IO1.md +++ /dev/null @@ -1,37 +0,0 @@ -# Parameter group: GPIO1 - -| | WebUI | Config.ini -|:--- |:--- |:---- -| Parameter Group Name | GPIO1 | IO1 -| Default State | `disabled` -> unselected | `disabled` -> ; -| Default Value | Configuration: `input`
Use Interrupt: `disabled`
PWM Duty Cycle Resolution: `10` Bit
Enable MQTT: `false`
Enable REST API: `false`
Name: ` ` | `input disabled 10 false false ` -| Input Options: Configuration | `input`
`input-pullup`
`input-pulldown`
`output` | `input`
`input-pullup`
`input-pulldown`
`output` -| Input Options: Use Interrupt | `disabled`
`rising edge`
`falling edge`
`rising and falling`
`low level trigger`
`high level trigger` | `disabled`
`rising-edge`
`falling-edge`
`rising-and-falling`
`low-level-trigger`
`high-level-trigger` -| Input Options: PWM Duty Cycle Resolution | `1` .. `20` Bit | `1` .. `20` Bit -| Input Options: Enable MQTT | `false`
`true` | `false`
`true` -| Input Options: Enable REST API | `false`
`true` | `false`
`true` -| Input Options: Name | Empty = `GPIO0`
Any name with `a-z, A-Z, 0-9, _, -`. | Empty = `GPIO0`
Any name with `a-z, A-Z, 0-9, _, -`. - - -!!! Warning - This is an **Expert Parameter**! Only change it if you understand what it does! - - -## Description - -This parameter group can be used to configure the GPIO `IO1` pin. - -!!! Warning - This pin is by default used for the serial communication as TX pin (USB logging)! - - -### Parameter - -| Parameter | Description -|:--- |:--- -| Configuration | Set the pin configuration -| Use Interrupt | Enable interrupt for GPIO pin -| PWM Duty Cycle Resolution | Set PWM duty resolution for the internal LED controller -| Enable MQTT | If enabled the pin sate is provided by MQTT and pin can be controlled by MQTT (output only) -| Enable REST API | If enabled the pin sate is provided by REST API and pin can be controlled by REST API (output only) -| Name | Name which is used for this pin (MQTT topic, REST API) diff --git a/docs/Configuration/Parameter/GPIO/IO12.md b/docs/Configuration/Parameter/GPIO/IO12.md deleted file mode 100644 index 49d38bd99..000000000 --- a/docs/Configuration/Parameter/GPIO/IO12.md +++ /dev/null @@ -1,39 +0,0 @@ -# Parameter group: GPIO12 - -| | WebUI | Config.ini -|:--- |:--- |:---- -| Parameter Group Name | GPIO12 | IO12 -| Default State | `disabled` -> unselected | `disabled` -> ; -| Default Value | Configuration: `external flash light ws281x controlled`
Use Interrupt: `disabled`
PWM Duty Cycle Resolution: `10` Bit
Enable MQTT: `false`
Enable REST API: `false`
Name: ` ` | `external-flash-ws281x disabled 10 false false ` -| Input Options: Configuration | `input`
`input-pullup`
`input-pulldown`
`output`
`external flash light ws281x controlled` | `input`
`input-pullup`
`input-pulldown`
`output`
`external-flash-ws281x` -| Input Options: Use Interrupt | `disabled`
`rising edge`
`falling edge`
`rising and falling`
`low level trigger`
`high level trigger` | `disabled`
`rising-edge`
`falling-edge`
`rising-and-falling`
`low-level-trigger`
`high-level-trigger` -| Input Options: PWM Duty Cycle Resolution | `1` .. `20` Bit | `1` .. `20` Bit -| Input Options: Enable MQTT | `false`
`true` | `false`
`true` -| Input Options: Enable REST API | `false`
`true` | `false`
`true` -| Input Options: Name | Empty = `GPIO0`
Any name with `a-z, A-Z, 0-9, _, -`. | Empty = `GPIO0`
Any name with `a-z, A-Z, 0-9, _, -`. - - -!!! Warning - This is an **Expert Parameter**! Only change it if you understand what it does! - - -## Description - -This parameter group can be used to configure the GPIO `IO12` pin.
-As a special function with this pin an external flash light can be controlled by using the `external flash light ws281x controlled` configuration. Further configuration is necessary. Please check parameter 'LED Type', 'Numbers of LEDs` and `LED Color`. - - -!!! Note - This pin is usable without any known restrictions. - - -### Parameter - -| Parameter | Description -|:--- |:--- -| Configuration | Set the pin configuration -| Use Interrupt | Enable interrupt for GPIO pin -| PWM Duty Cycle Resolution | Set PWM duty resolution for the internal LED controller -| Enable MQTT | If enabled the pin sate is provided by MQTT and pin can be controlled by MQTT (output only) -| Enable REST API | If enabled the pin sate is provided by REST API and pin can be controlled by REST API (output only) -| Name | Name which is used for this pin (MQTT topic, REST API) diff --git a/docs/Configuration/Parameter/GPIO/IO13.md b/docs/Configuration/Parameter/GPIO/IO13.md deleted file mode 100644 index e891a2c4d..000000000 --- a/docs/Configuration/Parameter/GPIO/IO13.md +++ /dev/null @@ -1,37 +0,0 @@ -# Parameter group: GPIO13 - -| | WebUI | Config.ini -|:--- |:--- |:---- -| Parameter Group Name | GPIO13 | IO13 -| Default State | `disabled` -> unselected | `disabled` -> ; -| Default Value | Configuration: `input`
Use Interrupt: `disabled`
PWM Duty Cycle Resolution: `10` Bit
Enable MQTT: `false`
Enable REST API: `false`
Name: ` ` | `input disabled 10 false false ` -| Input Options: Configuration | `input`
`input-pullup`
`input-pulldown`
`output` | `input`
`input-pullup`
`input-pulldown`
`output` -| Input Options: Use Interrupt | `disabled`
`rising edge`
`falling edge`
`rising and falling`
`low level trigger`
`high level trigger` | `disabled`
`rising-edge`
`falling-edge`
`rising-and-falling`
`low-level-trigger`
`high-level-trigger` -| Input Options: PWM Duty Cycle Resolution | `1` .. `20` Bit | `1` .. `20` Bit -| Input Options: Enable MQTT | `false`
`true` | `false`
`true` -| Input Options: Enable REST API | `false`
`true` | `false`
`true` -| Input Options: Name | Empty = `GPIO0`
Any name with `a-z, A-Z, 0-9, _, -`. | Empty = `GPIO0`
Any name with `a-z, A-Z, 0-9, _, -`. - - -!!! Warning - This is an **Expert Parameter**! Only change it if you understand what it does! - - -## Description - -This parameter group can be used to configure the GPIO `IO13` pin. - -!!! Note - This pin is usable without any known restrictions. - - -### Parameter - -| Parameter | Description -|:--- |:--- -| Configuration | Set the pin configuration -| Use Interrupt | Enable interrupt for GPIO pin -| PWM Duty Cycle Resolution | Set PWM duty resolution for the internal LED controller -| Enable MQTT | If enabled the pin sate is provided by MQTT and pin can be controlled by MQTT (output only) -| Enable REST API | If enabled the pin sate is provided by REST API and pin can be controlled by REST API (output only) -| Name | Name which is used for this pin (MQTT topic, REST API) diff --git a/docs/Configuration/Parameter/GPIO/IO3.md b/docs/Configuration/Parameter/GPIO/IO3.md deleted file mode 100644 index f529f90c1..000000000 --- a/docs/Configuration/Parameter/GPIO/IO3.md +++ /dev/null @@ -1,38 +0,0 @@ -# Parameter group: GPIO3 - -| | WebUI | Config.ini -|:--- |:--- |:---- -| Parameter Group Name | GPIO3 | IO3 -| Default State | `disabled` -> unselected | `disabled` -> ; -| Default Value | Configuration: `input`
Use Interrupt: `disabled`
PWM Duty Cycle Resolution: `10` Bit
Enable MQTT: `false`
Enable REST API: `false`
Name: ` ` | `input disabled 10 false false ` -| Input Options: Configuration | `input`
`input-pullup`
`input-pulldown`
`output` | `input`
`input-pullup`
`input-pulldown`
`output` -| Input Options: Use Interrupt | `disabled`
`rising edge`
`falling edge`
`rising and falling`
`low level trigger`
`high level trigger` | `disabled`
`rising-edge`
`falling-edge`
`rising-and-falling`
`low-level-trigger`
`high-level-trigger` -| Input Options: PWM Duty Cycle Resolution | `1` .. `20` Bit | `1` .. `20` Bit -| Input Options: Enable MQTT | `false`
`true` | `false`
`true` -| Input Options: Enable REST API | `false`
`true` | `false`
`true` -| Input Options: Name | Empty = `GPIO0`
Any name with `a-z, A-Z, 0-9, _, -`. | Empty = `GPIO0`
Any name with `a-z, A-Z, 0-9, _, -`. - - -!!! Warning - This is an **Expert Parameter**! Only change it if you understand what it does! - - -## Description - -This parameter group can be used to configure the GPIO `IO3` pin. - - -!!! Warning - This pin is used for the serial communication as RX pin (USB logging) by default! - - -### Parameter - -| Parameter | Description -|:--- |:--- -| Configuration | Set the pin configuration -| Use Interrupt | Enable interrupt for GPIO pin -| PWM Duty Cycle Resolution | Set PWM duty resolution for the interval LED controller -| Enable MQTT | If enabled the pin sate is provided by MQTT and pin can be controlled by MQTT (output only) -| Enable REST API | If enabled the pin sate is provided by REST API and pin can be controlled by REST API (output only) -| Name | Name which is used for this pin (MQTT topic, REST API) diff --git a/docs/Configuration/Parameter/GPIO/IO4.md b/docs/Configuration/Parameter/GPIO/IO4.md deleted file mode 100644 index 1ede7234c..000000000 --- a/docs/Configuration/Parameter/GPIO/IO4.md +++ /dev/null @@ -1,38 +0,0 @@ -# Parameter group: GPIO4 - -| | WebUI | Config.ini -|:--- |:--- |:---- -| Parameter Group Name | GPIO4 | IO4 -| Default State | `disabled` -> unselected | `disabled` -> ; -| Default Value | Configuration: `built-in led flash light`
Use Interrupt: `disabled`
PWM Duty Cycle Resolution: `10` Bit
Enable MQTT: `false`
Enable REST API: `false`
Name: ` ` | `built-in-led disabled 10 false false ` -| Input Options: Configuration | `input`
`input-pullup`
`input-pulldown`
`output`
`built-in led flash light` | `input`
`input-pullup`
`input-pulldown`
`output`
`built-in-led` -| Input Options: Use Interrupt | `disabled`
`rising edge`
`falling edge`
`rising and falling`
`low level trigger`
`high level trigger` | `disabled`
`rising-edge`
`falling-edge`
`rising-and-falling`
`low-level-trigger`
`high-level-trigger` -| Input Options: PWM Duty Cycle Resolution | `1` .. `20` Bit | `1` .. `20` Bit -| Input Options: Enable MQTT | `false`
`true` | `false`
`true` -| Input Options: Enable REST API | `false`
`true` | `false`
`true` -| Input Options: Name | Empty = `GPIO0`
Any name with `a-z, A-Z, 0-9, _, -`. | Empty = `GPIO0`
Any name with `a-z, A-Z, 0-9, _, -`. - - -!!! Warning - This is an **Expert Parameter**! Only change it if you understand what it does! - - -## Description - -This parameter group can be used to configure the GPIO `IO4` pin. - - -!!! Warning - This pin is only usable with restrictions. This pin is used for build-in flash light (onboard LED) by default. - - -### Parameter - -| Parameter | Description -|:--- |:--- -| Configuration | Set the pin configuration -| Use Interrupt | Enable interrupt for GPIO pin -| PWM Duty Cycle Resolution | Set PWM duty resolution for the internal LED controller -| Enable MQTT | If enabled the pin sate is provided by MQTT and pin can be controlled by MQTT (output only) -| Enable REST API | If enabled the pin sate is provided by REST API and pin can be controlled by REST API (output only) -| Name | Name which is used for this pin (MQTT topic, REST API) diff --git a/docs/Configuration/Parameter/GPIO/LEDColor.md b/docs/Configuration/Parameter/GPIO/LEDColor.md index d0e993a93..210bc4358 100644 --- a/docs/Configuration/Parameter/GPIO/LEDColor.md +++ b/docs/Configuration/Parameter/GPIO/LEDColor.md @@ -2,15 +2,20 @@ | | WebUI | Config.ini |:--- |:--- |:---- -| Parameter Name | LED Color | LEDColor -| Default Value | Red: `150`
Green: `150`
Blue: `150` | `150 150 150` -| Input Options | Red: `0` (0%) .. `255` (100%)
Green: `0` (0%) .. `255` (100%)
Blue: `0` (0%) .. `255` (100%) | Red: `0` .. `255`
Green: `0` .. `255`
Blue: `0` .. `255` +| Parameter Name | LED Color | IOx: 9-11. parameter (RGB) +| Default Value | Red: `255`
Green: `255`
Blue: `255` | `255 255 255` +| Input Options | Red: `0` .. `255`
Green: `0` .. `255`
Blue: `0` .. `255` | Red: `0` .. `255`
Green: `0` .. `255`
Blue: `0` .. `255` ## Description -Set color of the external LEDs connected to GPIO12. +Define desired color of the smart LEDs (RGB: additive color model) -!!! Note - In parameter group GPIO12, configuartion needs to be set to `external flash light ws281x controlled`. +!!! Tip + White color: Set all three components to 255 (Red: 255 | Green: 255 | Blue: 255) + + +!!! Tip + Adjust to the desired color only. Intensity adjustments can be controlled by + parameter `Take Image -> Flash Intensity` and `LED Intensity Correction Factor`. diff --git a/docs/Configuration/Parameter/GPIO/LEDIntensityCorrection.md b/docs/Configuration/Parameter/GPIO/LEDIntensityCorrection.md new file mode 100644 index 000000000..dbbc90eb7 --- /dev/null +++ b/docs/Configuration/Parameter/GPIO/LEDIntensityCorrection.md @@ -0,0 +1,21 @@ +# Parameter: LED Intensity Correction + +| | WebUI | Config.ini +|:--- |:--- |:---- +| Parameter Name | LED Intensity Correction | IOx: 12. parameter +| Default Value | `100` | `100` +| Input Options | `1 .. 100` | `1 .. 100` +| Unit | % | % + + + +## Description + +Apply output specific intensity correction factor (only for flashlight modes). +This potentially helps to align different intensity levels if multiple LED types are in use. + + +!!! Note + Generally, flashlight intensity is controlled with global `Flash Intensity` parameter in + section `Take Image`.
+ Formula: Resulting intensity = Global `Flash Intensity` * Output specific `LED Intensity Correction` \ No newline at end of file diff --git a/docs/Configuration/Parameter/GPIO/LEDNumbers.md b/docs/Configuration/Parameter/GPIO/LEDNumbers.md deleted file mode 100644 index a1ebfd94a..000000000 --- a/docs/Configuration/Parameter/GPIO/LEDNumbers.md +++ /dev/null @@ -1,16 +0,0 @@ -# Parameter: Number of LEDs - -| | WebUI | Config.ini -|:--- |:--- |:---- -| Parameter Name | Number of LEDs | LEDNumbers -| Default Value | `2` | `2` -| Input Range | `1` .. ∞ | `1` .. ∞ - - -## Description - -Set number of external LEDs (LED stripe) which are connected to GPIO12. - - -!!! Note - In parameter group GPIO12, configuartion needs to be set to `external flash light ws281x controlled`. diff --git a/docs/Configuration/Parameter/GPIO/LEDQuantity.md b/docs/Configuration/Parameter/GPIO/LEDQuantity.md new file mode 100644 index 000000000..258952f6f --- /dev/null +++ b/docs/Configuration/Parameter/GPIO/LEDQuantity.md @@ -0,0 +1,12 @@ +# Parameter: LED Quantity + +| | WebUI | Config.ini +|:--- |:--- |:---- +| Parameter Name | LED Quantity | IOx: 8. parameter +| Default Value | `1` | `1` +| Input Range | `1` .. ∞ | `1` .. ∞ + + +## Description + +Quantity of smart LEDs which are connected as daisy chain (e.g. LED stripe) diff --git a/docs/Configuration/Parameter/GPIO/LEDType.md b/docs/Configuration/Parameter/GPIO/LEDType.md index 432ca11df..317e8633c 100644 --- a/docs/Configuration/Parameter/GPIO/LEDType.md +++ b/docs/Configuration/Parameter/GPIO/LEDType.md @@ -1,16 +1,12 @@ -# Parameter: LED Type (NeoPixel) +# Parameter: LED Type | | WebUI | Config.ini |:--- |:--- |:---- -| Parameter Name | LED Type (NeoPixel) | LEDType +| Parameter Name | LED Type | IOx: 7. parameter | Default Value | `WS2812` | `WS2812` -| Input Options | `WS2812`
`WS2812B`
`SK6812`
`WS2813` | `WS2812`
`WS2812B`
`SK6812`
`WS2813` +| Input Options | `WS2812`
`WS2812B (universal)`
`WS2812B (newer variant)`
`WS2812B (older variant)`
`SK6812`
`WS2813` | `WS2812`
`WS2812B (universal)`
`WS2812B (newer variant)`
`WS2812B (older variant)`
`SK6812`
`WS2813` ## Description -Set the type of external LEDs (LED stripe) which are connected to GPIO12. - - -!!! Note - In parameter group GPIO12, configuartion needs to be set to `external flash light ws281x controlled`. +LED type of smartLEDs diff --git a/docs/Configuration/Parameter/TakeImage/FlashlightGPIOMapping.md b/docs/Configuration/Parameter/TakeImage/FlashlightGPIOMapping.md new file mode 100644 index 000000000..434477261 --- /dev/null +++ b/docs/Configuration/Parameter/TakeImage/FlashlightGPIOMapping.md @@ -0,0 +1,9 @@ +# Parameter: Flashlight GPIO Mapping + +## Description + +Show flashlight GPIO mapping configuration + + +!!! Note + This is not a parameter. This is only for visualization purpose. diff --git a/sd-card/config/config.ini b/sd-card/config/config.ini index 83b882afa..013dbd822 100644 --- a/sd-card/config/config.ini +++ b/sd-card/config/config.ini @@ -1,5 +1,5 @@ [ConfigFile] -Version = 1 +Version = 2 [TakeImage] ;RawImagesLocation = /log/source @@ -110,15 +110,7 @@ main.Measurement = undefined main.Field = undefined ;[GPIO] -;IO0 = input disabled 10 false false -;IO1 = input disabled 10 false false -;IO3 = input disabled 10 false false -;IO4 = built-in-led disabled 10 false false -;IO12 = input-pullup disabled 10 false false -;IO13 = input-pullup disabled 10 false false -LEDType = WS2812 -LEDNumbers = 2 -LEDColor = 150 150 150 +;IOx = input-pullup cyclic-polling 200 5000 false false WS2812 1 255 255 255 100 gpioX [AutoTimer] AutoStart = true diff --git a/sd-card/html/edit_config_param.html b/sd-card/html/edit_config_param.html index f82c21f44..5c84488ee 100644 --- a/sd-card/html/edit_config_param.html +++ b/sd-card/html/edit_config_param.html @@ -105,11 +105,6 @@ color:rgb(122, 122, 122); } - #GPIO_LEDColor_value1 #GPIO_LEDColor_value2 #GPIO_LEDColor_value3 { - width: 30px; - font-size: 1em; - } - .invalid-input { background-color: #FFAA00; } @@ -124,6 +119,11 @@ border: 1px solid red; } + select:invalid { + background-color: rgba(255, 0, 0, 0.25); + border: 1px solid red; + } + .hidden { display: none; } @@ -219,9 +219,9 @@ } details { - text-align: justify; - font-size: 1em; - margin-right: 10px; + text-align: justify; + font-size: 1em; + margin-right: 10px; } @@ -318,7 +318,7 @@

Configuration

+ Raw Images Retention Configuration $TOOLTIP_TakeImage_RawImagesRetention + + + Flashlight GPIO Mapping + + + + + $TOOLTIP_TakeImage_FlashlightGPIOMapping + + Flash Time @@ -428,7 +438,7 @@

Configuration

- Sharpness + Sharpness Configuration - Auto Exposure Level + Auto Exposure Level Configuration - Manual Exposure Value + Manual Exposure Value Configuration - Gain Control Mode + Gain Control Mode Configuration - Special Effect + Special Effect @@ -538,7 +548,7 @@

Configuration

- Flip Image + Flip Image Configuration - Zoom Offset X + Zoom Offset X Configuration - Zoom Offset Y + Zoom Offset Y Configuration - - - - - - - - - - $TOOLTIP_GPIO_IO0 - - - - - GPIO0 Use Interrupt - - - - - - - - - - GPIO0 PWM Duty Cycle Resolution - - - Bits - - - - - - - GPIO0 Enable MQTT - - - - - - - - GPIO0 Enable REST API - - - - - - - - GPIO0 Name - - - - - - - - + - - + GPIO Selection - - - $TOOLTIP_GPIO_IO1 - - - - - GPIO1 Use Interrupt - - - - - - - - - GPIO1 PWM Duty Cycle Resolution - - Bits - - - - - - - GPIO1 Enable MQTT - - - + $TOOLTIP_GPIO_GPIOSelection - - - GPIO1 Enable REST API - - - - - - + - GPIO1 Name - - - - - - - - - - - + GPIO Enabled - + + - $TOOLTIP_GPIO_IO3 + $TOOLTIP_GPIO_GPIOEnabled - + - GPIO3 Use Interrupt + GPIO Mode + - - - - - - - GPIO3 PWM Duty Cycle Resolution - - Bits - - - - - - - GPIO3 Enable MQTT - - - - - - - - GPIO3 Enable REST API - - - + $TOOLTIP_GPIO_GPIOMode - + - GPIO3 Name - - - - - - - - - - - + GPIO Capture Mode - - $TOOLTIP_GPIO_IO4 + $TOOLTIP_GPIO_GPIOCaptureMode - - - IMPORTANT NOTE:
- If you'd like to use the built-in flash LED in parallel with other GPIO functionality, - you have to explicitely activate the "built-in LED flash light" option on GPIO4. The light - intensity control (PWM) of the LED flash light is not functional anymore (only 100%). - - - - + - GPIO4 Use Interrupt + GPIO Debounce Time - + + ms - + $TOOLTIP_GPIO_GPIODebounceTime - + - GPIO4 PWM Duty Cycle Resolution - - Bits + GPIO PWM Frequency - - - - - - GPIO4 Enable MQTT - - - - - - - - GPIO4 Enable REST API + + + Hz - - + $TOOLTIP_GPIO_GPIOPwmFrequency - + - GPIO4 Name - - - - - - - - - - - + GPIO Expose To MQTT - + + - $TOOLTIP_GPIO_IO12 + $TOOLTIP_GPIO_GPIOExposeToMQTT - + - GPIO12 Use Interrupt + GPIO Expose To REST - - - - - - - - GPIO12 PWM Duty Cycle Resolution - - Bits - - - - - - - GPIO12 Enable MQTT + - - + $TOOLTIP_GPIO_GPIOExposeToREST - + - GPIO12 Enable REST API + GPIO Name - - + + $TOOLTIP_GPIO_GPIOName - + - GPIO12 Name + LED Type - - - - - - - LED Type (NeoPixel) - - - - + + + @@ -1845,104 +1603,45 @@

Configuration

$TOOLTIP_GPIO_LEDType - - - Numbers of LEDs + + + LED Quantity - - $TOOLTIP_GPIO_LEDNumbers + $TOOLTIP_GPIO_LEDQuantity - - - LED Color + + + LED Color - - R + R - G - B $TOOLTIP_GPIO_LEDColor - - - - - - - - - - - - $TOOLTIP_GPIO_IO13 - - - - - GPIO13 Use Interrupt - - - - - - - - - - GPIO13 PWM Duty Cycle Resolution - - Bits - - - + - GPIO13 Enable MQTT + LED Intensity Correction Factor - - - - - - - GPIO13 Enable REST API - - - - - - - - GPIO13 Name + + % - - + $TOOLTIP_GPIO_LEDIntensityCorrection - @@ -2239,7 +1938,8 @@

Configuration

param, category, NUNBERSAkt = -1, - NUMBERS; + NUMBERS, + GpioSelected = -1; function InitIndivParameter() @@ -2346,6 +2046,93 @@

Configuration

} } } + else if (_cat == "GPIO") { + document.getElementById("GPIO_EnablePin_value1").value = _param[_cat][_name]["enabled"] ? "true" : "false"; + setEnabled("GPIO_EnablePin_parameter", document.getElementById("GPIO_EnablePin_value1").value == "true" ? true : false); + + for (var j = 1; j <= anzpara; ++j) { + let element = document.getElementById("GPIO_IO_value"+j); + if (element.tagName.toLowerCase() == "select") { + var textToFind = _param[_cat][_name]["value"+j]; + if (textToFind == undefined) + continue; + + _isFound = false; + element.selectedIndex = -1; + for (var i = 0; i < element.options.length; i++) { + if (element.options[i].value.toLowerCase() === textToFind.toLowerCase()) { + element.selectedIndex = i; + _isFound = true; + break; + } + } + + if (!_isFound) { + _zw_txt = "Section: " + _cat + ", Parameter: " + _name + ": Invalid parametrization. " + + "Automatic preset with default values. Please verify parameter configuration."; + firework.launch(_zw_txt, 'warning', 10000); + ParamResetGpioValuesToDefault(param, _cat, _name); + GpioConfigCheck(); + break; + } + } + else { + element.value = _param[_cat][_name]["value"+j]; + } + } + + if (_category[_cat]["enabled"] == true) { + let pwm = false; + let smartled = false; + for (let i = 0 ; i < pinConfig.gpio.length; ++i) { + if (pinConfig.gpio[i].usage == "flashlight-pwm") + pwm = true; + if (pinConfig.gpio[i].usage == "flashlight-smartled") + smartled = true; + } + + if ((pwm && _param[_cat][_name]["value1"] == "flashlight-default") || + _param[_cat][_name]["value1"] == "flashlight-pwm" || + _param[_cat][_name]["value1"] == "output-pwm" + ) + setVisible("GPIO_ShowPWMFrequency", true); + else + setVisible("GPIO_ShowPWMFrequency", false); + + if (_param[_cat][_name]["value1"] == "input" || + _param[_cat][_name]["value1"] == "input-pullup" || + _param[_cat][_name]["value1"] == "input-pulldown") + { + setVisible("GPIO_ShowGPIOCaptureMode", true); + + if (_param[_cat][_name]["value2"] != "cyclic-polling") + setVisible("GPIO_ShowGPIODebounceTime", true); + else + setVisible("GPIO_ShowGPIODebounceTime", false); + } + else { + setVisible("GPIO_ShowGPIOCaptureMode", false); + setVisible("GPIO_ShowGPIODebounceTime", false); + } + + if (_param[_cat][_name]["value1"] == "flashlight-smartled" || + (smartled && _param[_cat][_name]["value1"]["value1"] == "flashlight-default") + ) + setVisible("GPIO_ShowSmartLed", true); + else + setVisible("GPIO_ShowSmartLed", false); + + + if (_param[_cat][_name]["value1"].substring(0,10) == "flashlight" && + _param[_cat][_name]["value1"] != "flashlight-digital" + ) + setVisible("GPIO_ShowIntersityCorrection", true); + else + setVisible("GPIO_ShowIntersityCorrection", false); + } + + return; + } else { if (_optional) { document.getElementById(_cat+"_"+_name+"_enabled").checked = _param[_cat][_name]["enabled"]; @@ -2562,6 +2349,29 @@

Configuration

} } } + else if (_cat == "GPIO") { + _param[_cat][_name]["enabled"] = document.getElementById("GPIO_EnablePin_value1").value == "true" ? true : false; + + for (var j = 1; j <= _param[_cat][_name]["anzParam"]; ++j) { + let element = document.getElementById("GPIO_IO_value"+j); + if (element.tagName.toLowerCase() == "select") { + _param[_cat][_name]["value"+j] = element.selectedIndex > -1 ? element.options[element.selectedIndex].value : ""; + } + else if ((element.getAttribute("type") != null) && (element.getAttribute("type").toLowerCase() == "checkbox")) { + _param[_cat][_name]["value"+j] = element.checked; + } + else { + if ((_param[_cat][_name].checkRegExList != null) && (_param[_cat][_name].checkRegExList[j-1] != null)) { + if (!element.value.match(_param[_cat][_name].checkRegExList[j-1])) { + element.classList.add("invalid-input"); + } else { + element.classList.remove("invalid-input"); + } + } + _param[_cat][_name]["value"+j] = element.value; + } + } + } else { if (_optional) { // If checkbox is avaiable -> set by selection @@ -2640,15 +2450,15 @@

Configuration

document.getElementById("Category_MQTT_enabled").checked = category["MQTT"]["enabled"]; setVisible("MQTTItem", category["MQTT"]["enabled"]); - document.getElementById("Category_GPIO_enabled").checked = category["GPIO"]["enabled"]; - setVisible("GPIO_item", category["GPIO"]["enabled"]); - document.getElementById("Category_InfluxDB_enabled").checked = category["InfluxDB"]["enabled"]; setVisible("InfluxDBv1Item", category["InfluxDB"]["enabled"]); document.getElementById("Category_InfluxDBv2_enabled").checked = category["InfluxDBv2"]["enabled"]; setVisible("InfluxDBv2Item", category["InfluxDBv2"]["enabled"]); + document.getElementById("Category_GPIO_enabled").checked = category["GPIO"]["enabled"]; + setVisible("GPIO_item", category["GPIO"]["enabled"]); + WriteParameter(param, category, "TakeImage", "RawImagesLocation", true); WriteParameter(param, category, "TakeImage", "RawImagesRetention", true); WriteParameter(param, category, "TakeImage", "FlashTime", false); @@ -2725,17 +2535,9 @@

Configuration

WriteParameter(param, category, "InfluxDBv2", "TLSEncryption", false); WriteParameter(param, category, "InfluxDBv2", "TLSCACert", false); WriteParameter(param, category, "InfluxDBv2", "TLSClientCert", false); - WriteParameter(param, category, "InfluxDBv2", "TLSClientKey", false); - - WriteParameter(param, category, "GPIO", "IO0", true); - WriteParameter(param, category, "GPIO", "IO1", true); - WriteParameter(param, category, "GPIO", "IO3", true); - WriteParameter(param, category, "GPIO", "IO4", true); - WriteParameter(param, category, "GPIO", "IO12", true); - WriteParameter(param, category, "GPIO", "IO13", true); - WriteParameter(param, category, "GPIO", "LEDType", false); - WriteParameter(param, category, "GPIO", "LEDNumbers", false); - WriteParameter(param, category, "GPIO", "LEDColor", false); + WriteParameter(param, category, "InfluxDBv2", "TLSClientKey", false); + + // GPIO values are getting updated by GpioPinSelectAfterChange() WriteParameter(param, category, "AutoTimer", "AutoStart", false); WriteParameter(param, category, "AutoTimer", "Interval", false); @@ -2767,6 +2569,7 @@

Configuration

setEnabled("TakeImage_Zoom_parameter", document.getElementById("TakeImage_ZoomMode_value1").value > 0 ? true : false); setEnabled("MQTT_TLSEncryption_parameter", document.getElementById("MQTT_TLSEncryption_value1").value == "true" ? true : false); setEnabled("MQTT_HomeassistantDiscovery_parameter", document.getElementById("MQTT_HomeassistantDiscovery_value1").value == "true" ? true : false); + setEnabled("GPIO_EnablePin_parameter", document.getElementById("GPIO_EnablePin_value1").value == "true" ? true : false); setEnabled("InfluxDB_TLSEncryption_parameter", document.getElementById("InfluxDB_TLSEncryption_value1").value == "true" ? true : false); setEnabled("InfluxDBv2_TLSEncryption_parameter", document.getElementById("InfluxDBv2_TLSEncryption_value1").value == "true" ? true : false); setEnabled("WebUI_OverviewAutoRefresh_parameter", document.getElementById("WebUI_OverviewAutoRefresh_value1").value == "true" ? true : false); @@ -2901,22 +2704,7 @@

Configuration

ReadParameter(param, "InfluxDBv2", "TLSClientCert", false); ReadParameter(param, "InfluxDBv2", "TLSClientKey", false); - ReadParameter(param, "GPIO", "IO0", true); - ReadParameter(param, "GPIO", "IO1", true); - ReadParameter(param, "GPIO", "IO3", true); - ReadParameter(param, "GPIO", "IO4", true); - ReadParameter(param, "GPIO", "IO12", true); - ReadParameter(param, "GPIO", "IO13", true); - ReadParameter(param, "GPIO", "LEDType", false); - ReadParameter(param, "GPIO", "LEDNumbers", false); - ReadParameter(param, "GPIO", "LEDColor", false); - // Folgende Zeilen sind für Abwärtskompatibität < v9.0.0 notwendig (manchmal parameter auskommentiert) - param["GPIO"]["LEDType"]["enabled"] = true; - param["GPIO"]["LEDNumbers"]["enabled"] = true; - param["GPIO"]["LEDColor"]["enabled"] = true; - param["GPIO"]["LEDType"]["found"] = true; - param["GPIO"]["LEDNumbers"]["found"] = true; - param["GPIO"]["LEDColor"]["found"] = true; + ReadParameter(param, "GPIO", "IO" + GpioSelected, false); // Update last selected pin before saving ReadParameter(param, "AutoTimer", "AutoStart", false); ReadParameter(param, "AutoTimer", "Interval", false); @@ -2963,8 +2751,12 @@

Configuration

category["GPIO"]["enabled"] = document.getElementById("Category_GPIO_enabled").checked; UpdateInput(); + var sel = document.getElementById("Numbers_value1"); UpdateInputIndividual(sel); + + sel = document.getElementById("GPIO_PinSelect_value1"); + updateInputGpio(sel); } @@ -2985,22 +2777,14 @@

Configuration

document.getElementById("Edit_Config_Direct").style.display = "none"; } + getGPIOPins(); + GpioPinSelectAfterChange(); + const expert = document.querySelectorAll(".expert"); for (var i = 0; i < expert.length; i++) { expert[i].style.display = _style_pur; - // document.getElementById(expert[i].id).style = _style; + //document.getElementById(expert[i].id).style = _style; } - - // Enable / Disable die Optionen in den Menues für die Auswahl. Falls kein Expertenmodus soll nur ein Wert (built-in-led oder externan-flash-ws281x) möglich sein - Array.from(document.querySelector("#GPIO_IO4_value1").options).forEach(function(option_element) { - if (option_element.value != "built-in-led") - option_element.hidden = _hidden; - }); - - Array.from(document.querySelector("#GPIO_IO12_value1").options).forEach(function(option_element) { - if (option_element.value != "external-flash-ws281x") - option_element.hidden = _hidden; - }); } @@ -3060,6 +2844,162 @@

Configuration

} + function updateInputGpio(sel) + { + if (GpioSelected != -1) + ReadParameter(param, "GPIO", "IO" + sel.value, false); + + GpioSelected = sel.value; + WriteParameter(param, category, "GPIO", "IO" + sel.value, false); + } + + + function GpioPinSelectBeforeChange() + { + let sel = document.getElementById("GPIO_PinSelect_value1"); + ReadParameter(param, "GPIO", "IO" + sel.value, false); + } + + + function GpioPinSelectAfterChange() + { + let sel = document.getElementById("GPIO_PinSelect_value1"); + GpioSelected = sel.value; + getGPIOPinMode(GpioSelected); + updateGPIOExpertView(GpioSelected); + WriteParameter(param, category, "GPIO", "IO" + GpioSelected, false); + } + + + function updateGPIOExpertView(selected_pin) + { + let elements = document.getElementsByClassName("GPIO_item"); + for (var i = 0; i < pinConfig.gpio.length; ++i) { + if (pinConfig.gpio[i].name == selected_pin) { + for (j = 0; j < elements.length; ++j) { + if (pinConfig.gpio[i].usage.substring(0,10) == "restricted") { + elements[j].classList.add("expert"); + document.getElementById("GPIO_RestrictedUsageText").style.display = ""; + document.getElementById("GPIO_RestrictedUsageText").innerText = "[Default: " + + pinConfig.gpio[i].usage.substring(12).replace(/\-/g, " ") + "]"; + } + else { + elements[j].classList.remove("expert"); + document.getElementById("GPIO_RestrictedUsageText").style.display = "none"; + } + } + break; // If selected pin is found, stop further searching + } + } + } + + + function GpioCaptureModeChange() + { + if (document.getElementById("GPIO_IO_value1").value == "input" || + document.getElementById("GPIO_IO_value1").value == "input-pullup" || + document.getElementById("GPIO_IO_value1").value == "input-pulldown") + { + if (document.getElementById("GPIO_IO_value2").value != "cyclic-polling") + setVisible("GPIO_ShowGPIODebounceTime", true); + else + setVisible("GPIO_ShowGPIODebounceTime", false); + } + } + + + function getGPIOPins() + { + let _indexGpio = document.getElementById("GPIO_PinSelect_value1"); + while (_indexGpio.length) + _indexGpio.remove(0); + + for (let i = 0; i < pinConfig.gpio.length; ++i) { + if (pinConfig.gpio[i].usage.substring(0,10) !== "restricted" || document.getElementById("ExpertModus_enabled").checked) { + let option = document.createElement("option"); + option.text = "GPIO" + pinConfig.gpio[i].name; + option.value = pinConfig.gpio[i].name; + _indexGpio.add(option); + } + } + _indexGpio.selectedIndex = 0; + } + + + function getGPIOPinMode(selected_pin) + { + let _indexGpioMode = document.getElementById("GPIO_IO_value1"); + while (_indexGpioMode.length) + _indexGpioMode.remove(0); + + // Check all pins to find selected pin + for (var i = 0; i < pinConfig.gpio.length; ++i) { + if (pinConfig.gpio[i].name == selected_pin) { + if (pinConfig.gpio[i].usage.substring(0,10) == "flashlight") { // Special case: Default flashlight GPIO + // Add mode flashlight-default + let option = document.createElement("option"); + option.text = "flashlight default"; + option.value = "flashlight-default"; + _indexGpioMode.add(option); + + // Add all modes exluding option which is mapped to flashlight-default (depending on defined GPIO usage) + for (let pinMode = 0; pinMode < pinConfig.default_config.gpio_modes.length; ++pinMode) { + if (pinConfig.gpio[i].usage != pinConfig.default_config.gpio_modes[pinMode]) { + let option = document.createElement("option"); + option.text = pinConfig.default_config.gpio_modes[pinMode].replace(/\-/g, " "); + option.value = pinConfig.default_config.gpio_modes[pinMode]; + _indexGpioMode.add(option); + } + } + + if (category["GPIO"]["enabled"] == false) + document.getElementById("TakeImage_FlashlightGPIOMapping_value").innerText = + "Default [GPIO" + pinConfig.gpio[i].name + "]"; + else + document.getElementById("TakeImage_FlashlightGPIOMapping_value").innerText = "Customized [GPIO Section]"; + } + else { + // Add all modes without option which is mapped to flashlight-default + for (let pinMode = 0; pinMode < pinConfig.default_config.gpio_modes.length; ++pinMode) { + let option = document.createElement("option"); + option.text = pinConfig.default_config.gpio_modes[pinMode].replace(/\-/g, " "); + option.value = pinConfig.default_config.gpio_modes[pinMode]; + _indexGpioMode.add(option); + } + } + break; + } + } + + getGPIOPinInterrupt(selected_pin) + } + + + function getGPIOPinInterrupt(selected_pin) + { + let _indexGpioIntr = document.getElementById("GPIO_IO_value2"); + while (_indexGpioIntr.length) + _indexGpioIntr.remove(0); + + // Add all interrupt modes which are listed as defaults + for (let i = 0; i < pinConfig.default_config.gpio_interrupt.length; ++i) { + let option = document.createElement("option"); + option.text = pinConfig.default_config.gpio_interrupt[i].replace(/\-/g, " "); + option.value = pinConfig.default_config.gpio_interrupt[i]; + _indexGpioIntr.add(option); + } + } + + + function GpioConfigCheck() + { + for (let i = 0; i < pinConfig.gpio.length; ++i) { + getGPIOPinMode(pinConfig.gpio[i].name); + WriteParameter(param, category, "GPIO", "IO" + pinConfig.gpio[i].name, false); + } + } + + /* hash #description open the details part of the page */ function openDescription() { @@ -3080,6 +3020,7 @@

Configuration

UpdateInput(); UpdateInputIndividual(); + GpioConfigCheck(); UpdateExpertModus(); document.getElementById("divloading").style.display = 'none'; // Hide loading indicator diff --git a/sd-card/html/index.html b/sd-card/html/index.html index 12cad8633..75e743b9d 100644 --- a/sd-card/html/index.html +++ b/sd-card/html/index.html @@ -116,7 +116,7 @@

A Neural Network Recognition Sy
  • Livestream (Flash on)
  • Reboot
  • -
  • Info
  • +
  • System Info
  • Documentation
    • REST API
    • diff --git a/sd-card/html/readconfigparam.js b/sd-card/html/readconfigparam.js index f43833558..edb5c0001 100644 --- a/sd-card/html/readconfigparam.js +++ b/sd-card/html/readconfigparam.js @@ -5,7 +5,7 @@ var category = {}; // Configuration category obejct var NUMBERS = []; // Number sequences let REFERENCES = []; // Alignment marker let tflite_list = ""; // TFLite model files as tab separated list - +let pinConfig = {}; // Board pin configuration async function getDataFileList() { @@ -69,7 +69,35 @@ async function fetchTFLITEList() } -async function loadConfig() +function loadPinConfig() +{ + return new Promise(function (resolve, reject) { + var url = getDomainname() + '/gpio?task=gpio_config'; + + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4) { + if (this.status >= 200 && this.status < 300) { + pinConfig = JSON.parse(xhttp.responseText); + return resolve(pinConfig); + } + else { + firework.launch("Loading GPIO pin config failed (Response status: " + this.status + + "). Repeat action or check logs.", 'danger', 30000); + console.error("Loading GPIO pin config failed. Response status: " + this.status); + return reject("Loading GPIO pin config failed"); + } + } + }; + + xhttp.timeout = 10000; // 10 seconds + xhttp.open("GET", url, true); + xhttp.send(); + }); +} + + +async function loadConfigIni() { return new Promise(function (resolve, reject) { var url = getDomainname() + '/fileserver/config/config.ini'; @@ -97,13 +125,21 @@ async function loadConfig() } +async function loadConfig() +{ + // Ensure GPIO pin config and config.ini is loaded before continue + return Promise.all([loadPinConfig(), loadConfigIni()]); +} + + function getConfig() { return config_gesamt; } -function ParseConfig() { +function ParseConfig() +{ config_split = config_gesamt.split("\n"); var aktline = 0; @@ -250,18 +286,8 @@ function ParseConfig() { category[catname]["enabled"] = false; category[catname]["found"] = true; param[catname] = new Object(); - ParamAddValue(param, catname, "IO0", 6, false, "", [null, null, /^[0-9]*$/, null, null, /^[a-zA-Z0-9_-]*$/]); - ParamAddValue(param, catname, "IO1", 6, false, "", [null, null, /^[0-9]*$/, null, null, /^[a-zA-Z0-9_-]*$/]); - ParamAddValue(param, catname, "IO3", 6, false, "", [null, null, /^[0-9]*$/, null, null, /^[a-zA-Z0-9_-]*$/]); - ParamAddValue(param, catname, "IO4", 6, false, "", [null, null, /^[0-9]*$/, null, null, /^[a-zA-Z0-9_-]*$/]); - ParamAddValue(param, catname, "IO12", 6, false, "", [null, null, /^[0-9]*$/, null, null, /^[a-zA-Z0-9_-]*$/]); - ParamAddValue(param, catname, "IO13", 6, false, "", [null, null, /^[0-9]*$/, null, null, /^[a-zA-Z0-9_-]*$/]); - ParamAddSingleValueWithPreset(param, catname, "LEDType", true, "WS2812"); - ParamAddSingleValueWithPreset(param, catname, "LEDNumbers", true, "2"); - ParamAddValue(param, catname, "LEDColor", 3); - param[catname]["LEDColor"]["value1"] = "50"; - param[catname]["LEDColor"]["value2"] = "50"; - param[catname]["LEDColor"]["value3"] = "50"; + ParamAddGpioWithPreset(param, catname, [null, null, /^[0-9]*$/, null, null, null, + /^[0-9]*$/, /^[0-9]*$/, /^[0-9]*$/, /^[a-zA-Z0-9_-]*$/]); var catname = "AutoTimer"; category[catname] = new Object(); @@ -494,6 +520,55 @@ function ParamAddModelWithPreset(param, _cat, _param, _enabled) } +/* Add a model parameter (no parameter which is used in a number sequence) and set to default value */ +function ParamAddGpioWithPreset(param, _cat, _checkRegExList = null) +{ + for (let i = 0; i < pinConfig.gpio.length; ++i) { + let _name = "IO" + pinConfig.gpio[i].name; + if (param[_cat][_name] == null || param[_cat][_name]["value1"] == "") { + param[_cat][_name] = new Object(); + param[_cat][_name]["found"] = true; + param[_cat][_name]["enabled"] = false; + param[_cat][_name]["line"] = -1; + param[_cat][_name]["anzParam"] = 13; + param[_cat][_name]["value1"] = "input-pullup"; + param[_cat][_name]["value2"] = "cyclic-polling"; + param[_cat][_name]["value3"] = "200"; + param[_cat][_name]["value4"] = "5000"; + param[_cat][_name]["value5"] = "false"; + param[_cat][_name]["value6"] = "false"; + param[_cat][_name]["value7"] = "WS2812"; + param[_cat][_name]["value8"] = "1"; + param[_cat][_name]["value9"] = "255"; + param[_cat][_name]["value10"] = "255"; + param[_cat][_name]["value11"] = "255"; + param[_cat][_name]["value12"] = "100"; + param[_cat][_name]["value13"] = ""; + param[_cat][_name].checkRegExList = _checkRegExList; + } + } +} + + +/* Add a model parameter (no parameter which is used in a number sequence) and set to default value */ +function ParamResetGpioValuesToDefault(param, _cat, _name) +{ + param[_cat][_name]["value1"] = "input-pullup"; + param[_cat][_name]["value2"] = "cyclic-polling"; + param[_cat][_name]["value3"] = "200"; + param[_cat][_name]["value4"] = "5000"; + param[_cat][_name]["value5"] = "false"; + param[_cat][_name]["value6"] = "false"; + param[_cat][_name]["value7"] = "WS2812"; + param[_cat][_name]["value8"] = "1"; + param[_cat][_name]["value9"] = "255"; + param[_cat][_name]["value10"] = "255"; + param[_cat][_name]["value11"] = "255"; + param[_cat][_name]["value12"] = "100"; + param[_cat][_name]["value13"] = ""; +} + + function ParseConfigParamAll(_aktline, _catname){ ++_aktline; @@ -531,7 +606,7 @@ function ParamExtractValue(_param, _linesplit, _catname, _paramname, _aktline, _ _param[_catname][_paramname]["anzpara"] = _anzvalue; for (var j = 1; j <= _anzvalue; ++j) { _param[_catname][_paramname]["value"+j] = _linesplit[j]; - } + } } } @@ -563,7 +638,7 @@ function ParamExtractValueAll(_param, _linesplit, _catname, _aktline, _iscom){ for (var j = 1; j <= _param[_catname][paramname]["anzParam"]; ++j) { abc[_catname][paramname]["value"+j] = _linesplit[j]; - } + } if (abc["name"] == "default") { for (_num in NUMBERS) // wert mit Default belegen