diff --git a/components/gpio/CMakeLists.txt b/components/gpio/CMakeLists.txt new file mode 100644 index 0000000..386d539 --- /dev/null +++ b/components/gpio/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "gpio_evaluateSwitch.cpp" + INCLUDE_DIRS "." +) diff --git a/components/gpio/gpio_evaluateSwitch.cpp b/components/gpio/gpio_evaluateSwitch.cpp new file mode 100644 index 0000000..b33e9a2 --- /dev/null +++ b/components/gpio/gpio_evaluateSwitch.cpp @@ -0,0 +1,171 @@ +#include "gpio_evaluateSwitch.hpp" + +static const char *TAG = "evaluateSwitch"; + + +gpio_evaluatedSwitch::gpio_evaluatedSwitch( //minimal (use default values) + gpio_num_t gpio_num_declare + ){ + gpio_num = gpio_num_declare; + pullup = true; + inverted = false; + + init(); +}; + + + +gpio_evaluatedSwitch::gpio_evaluatedSwitch( //optional parameters given + gpio_num_t gpio_num_declare, + bool pullup_declare, + bool inverted_declare + ){ + gpio_num = gpio_num_declare; + pullup = pullup_declare; + inverted = inverted_declare; + + init(); +}; + + + +void gpio_evaluatedSwitch::init(){ + ESP_LOGI(TAG, "initializing gpio pin %d", (int)gpio_num); + + //define gpio pin as input + gpio_pad_select_gpio(gpio_num); + gpio_set_direction(gpio_num, GPIO_MODE_INPUT); + + if (pullup == true){ //enable pullup if desired (default) + gpio_pad_select_gpio(gpio_num); + gpio_set_pull_mode(gpio_num, GPIO_PULLUP_ONLY); + }else{ + gpio_set_pull_mode(gpio_num, GPIO_FLOATING); + gpio_pad_select_gpio(gpio_num); + } + //TODO add pulldown option + //gpio_set_pull_mode(gpio_num, GPIO_PULLDOWN_ONLY); +}; + + +void gpio_evaluatedSwitch::handle(){ //Statemachine for debouncing and edge detection + if (inverted == true){ + //========================================================= + //=========== Statemachine for inverted switch ============ + //=================== (switch to VCC) ===================== + //========================================================= + switch (p_state){ + default: + p_state = switchState::FALSE; + break; + + case switchState::FALSE: //input confirmed high (released) + fallingEdge = false; //reset edge event + if (gpio_get_level(gpio_num) == 1){ //pin high (on) + p_state = switchState::HIGH; + timestampHigh = esp_log_timestamp(); //save timestamp switched from low to high + } else { + msReleased = esp_log_timestamp() - timestampLow; //update duration released + } + break; + + case switchState::HIGH: //input recently switched to high (pressed) + if (gpio_get_level(gpio_num) == 1){ //pin still high (on) + if (esp_log_timestamp() - timestampHigh > minOnMs){ //pin in same state long enough + p_state = switchState::TRUE; + state = true; + risingEdge = true; + msReleased = timestampHigh - timestampLow; //calculate duration the button was released + } + }else{ + p_state = switchState::FALSE; + } + break; + + case switchState::TRUE: //input confirmed high (pressed) + risingEdge = false; //reset edge event + if (gpio_get_level(gpio_num) == 0){ //pin low (off) + timestampLow = esp_log_timestamp(); + p_state = switchState::LOW; + } else { + msPressed = esp_log_timestamp() - timestampHigh; //update duration pressed + } + + break; + + case switchState::LOW: //input recently switched to low (released) + if (gpio_get_level(gpio_num) == 0){ //pin still low (off) + if (esp_log_timestamp() - timestampLow > minOffMs){ //pin in same state long enough + p_state = switchState::FALSE; + msPressed = timestampLow - timestampHigh; //calculate duration the button was pressed + state=false; + fallingEdge=true; + } + }else{ + p_state = switchState::TRUE; + } + break; + } + + }else{ + //========================================================= + //========= Statemachine for not inverted switch ========== + //=================== (switch to GND) ===================== + //========================================================= + switch (p_state){ + default: + p_state = switchState::FALSE; + break; + + case switchState::FALSE: //input confirmed high (released) + fallingEdge = false; //reset edge event + if (gpio_get_level(gpio_num) == 0){ //pin low (on) + p_state = switchState::LOW; + timestampLow = esp_log_timestamp(); //save timestamp switched from high to low + } else { + msReleased = esp_log_timestamp() - timestampHigh; //update duration released + } + break; + + case switchState::LOW: //input recently switched to low (pressed) + if (gpio_get_level(gpio_num) == 0){ //pin still low (on) + if (esp_log_timestamp() - timestampLow > minOnMs){ //pin in same state long enough + p_state = switchState::TRUE; + state = true; + risingEdge = true; + msReleased = timestampLow - timestampHigh; //calculate duration the button was released + } + }else{ + p_state = switchState::FALSE; + } + break; + + case switchState::TRUE: //input confirmed low (pressed) + risingEdge = false; //reset edge event + if (gpio_get_level(gpio_num) == 1){ //pin high (off) + timestampHigh = esp_log_timestamp(); + p_state = switchState::HIGH; + } else { + msPressed = esp_log_timestamp() - timestampLow; //update duration pressed + } + + break; + + case switchState::HIGH: //input recently switched to high (released) + if (gpio_get_level(gpio_num) == 1){ //pin still high (off) + if (esp_log_timestamp() - timestampHigh > minOffMs){ //pin in same state long enough + p_state = switchState::FALSE; + msPressed = timestampHigh - timestampLow; //calculate duration the button was pressed + state=false; + fallingEdge=true; + } + }else{ + p_state = switchState::TRUE; + } + break; + } + } + +} + + diff --git a/components/gpio/gpio_evaluateSwitch.hpp b/components/gpio/gpio_evaluateSwitch.hpp new file mode 100644 index 0000000..c4a27e4 --- /dev/null +++ b/components/gpio/gpio_evaluateSwitch.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include + +extern "C" +{ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "esp_log.h" +} + +//constructor examples: +//switch to gnd and us internal pullup: +//gpio_evaluatedSwitch s3(GPIO_NUM_14); +//switch to gnd dont use internal pullup: +//gpio_evaluatedSwitch s3(GPIO_NUM_14 false); +//switch to VCC (inverted) and dont use internal pullup: +//gpio_evaluatedSwitch s3(GPIO_NUM_14 false, true); + + +class gpio_evaluatedSwitch { + public: + //--- input --- + uint32_t minOnMs = 30; + uint32_t minOffMs = 30; + gpio_evaluatedSwitch( //constructor minimal (default parameters pullup=true, inverted=false) + gpio_num_t gpio_num_declare + ); + gpio_evaluatedSwitch( //constructor with optional parameters + gpio_num_t gpio_num_declare, + bool pullup_declare, + bool inverted_declare=false + ); + + //--- output --- TODO make readonly? (e.g. public section: const int& x = m_x;) + bool state = false; + bool risingEdge = false; + bool fallingEdge = false; + uint32_t msPressed = 0; + uint32_t msReleased = 0; + + //--- functions --- + void handle(); //Statemachine for debouncing and edge detection + + private: + gpio_num_t gpio_num; + bool pullup; + bool inverted; + + enum class switchState {TRUE, FALSE, LOW, HIGH}; + switchState p_state = switchState::FALSE; + uint32_t timestampLow = 0; + uint32_t timestampHigh = 0; + void init(); + +}; + + diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index cf2c455..c2918bd 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,2 +1,9 @@ -idf_component_register(SRCS "main.c" - INCLUDE_DIRS ".") +idf_component_register( + SRCS + "main.cpp" + "config.cpp" + "control.cpp" + "buzzer.cpp" + INCLUDE_DIRS + "." + ) diff --git a/main/buzzer.cpp b/main/buzzer.cpp new file mode 100644 index 0000000..9046cd1 --- /dev/null +++ b/main/buzzer.cpp @@ -0,0 +1,95 @@ +#include "buzzer.hpp" +#include "config.hpp" + +static const char *TAG_BUZZER = "buzzer"; + +//============================ +//========== init ============ +//============================ +//define gpio pin as output, initialize queue +void buzzer_t::init(){ + //define buzzer pin as output + gpio_pad_select_gpio(gpio_pin); + gpio_set_direction(gpio_pin, GPIO_MODE_OUTPUT); + //create queue + beepQueue = xQueueCreate( 20, sizeof( struct beepEntry ) ); +} + + +//============================= +//======== constructor ======== +//============================= +//copy provided config parameters to private variables, run init function +buzzer_t::buzzer_t(gpio_num_t gpio_pin_f, uint16_t msGap_f){ + ESP_LOGI(TAG_BUZZER, "Initializing buzzer"); + //copy configuration parameters to variables + gpio_pin = gpio_pin_f; + msGap = msGap_f; + //run init function to initialize gpio and queue + init(); +}; + + +//============================ +//=========== beep =========== +//============================ +//function to add a beep command to the queue +void buzzer_t::beep(uint8_t count, uint16_t msOn, uint16_t msOff){ + //create entry struct with provided data + struct beepEntry entryInsert = { + count = count, + msOn = msOn, + msOff = msOff + }; + + // Send a pointer to a struct AMessage object. Don't block if the + // queue is already full. + //struct beepEntry *entryInsertPointer; + //entryInsertPointer = &entryInsertData; + ESP_LOGW(TAG_BUZZER, "Inserted object to queue - count=%d, msOn=%d, msOff=%d", entryInsert.count, entryInsert.msOn, entryInsert.msOff); + //xQueueGenericSend( beepQueue, ( void * ) &entryInsertPointer, ( TickType_t ) 0, queueSEND_TO_BACK ); + xQueueSend( beepQueue, ( void * )&entryInsert, ( TickType_t ) 0 ); +} + + +//============================== +//======== processQueue ======== +//============================== +void buzzer_t::processQueue(){ + //struct for receiving incomming events + struct beepEntry entryRead = { }; + + //loop forever + while(1){ + ESP_LOGD(TAG_BUZZER, "processQueue: waiting for beep command"); + + //if queue is ready + if( beepQueue != 0 ) + { + // wait for a queue entry to be available indefinetely if INCLUDE_vTaskSuspend is enabled in the FreeRTOS config + // otherwise waits for at least 7 weeks + if( xQueueReceive( beepQueue, &entryRead, portMAX_DELAY ) ) + { + ESP_LOGW(TAG_BUZZER, "Read entry from queue: count=%d, msOn=%d, msOff=%d", entryRead.count, entryRead.msOn, entryRead.msOff); + + //beep requested count with requested delays + for (int i = entryRead.count; i--;){ + //turn on + ESP_LOGD(TAG_BUZZER, "turning buzzer on"); + gpio_set_level(gpio_pin, 1); + vTaskDelay(entryRead.msOn / portTICK_PERIOD_MS); + //turn off + ESP_LOGD(TAG_BUZZER, "turning buzzer off"); + gpio_set_level(gpio_pin, 0); + vTaskDelay(entryRead.msOff / portTICK_PERIOD_MS); + } + //wait for minimum gap between beep events + vTaskDelay(msGap / portTICK_PERIOD_MS); + } + }else{ //wait for queue to become available + vTaskDelay(50 / portTICK_PERIOD_MS); + } + } +} + + diff --git a/main/buzzer.hpp b/main/buzzer.hpp new file mode 100644 index 0000000..ce6c2c6 --- /dev/null +++ b/main/buzzer.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include + +extern "C" +{ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "esp_log.h" +} + +#include "freertos/queue.h" + + + +//=================================== +//========= buzzer_t class ========== +//=================================== +//class which blinks a gpio pin for the provided count and durations. +//- 'processQueue' has to be run in a separate task +//- uses a queue to queue up multiple beep commands +class buzzer_t { + public: + //--- constructor --- + buzzer_t(gpio_num_t gpio_pin_f, uint16_t msGap_f = 200); + + //--- functions --- + void processQueue(); //has to be run once in a separate task, waits for and processes queued events + void beep(uint8_t count, uint16_t msOn, uint16_t msOff); + //void clear(); (TODO - not implemented yet) + //void createTask(); (TODO - not implemented yet) + + //--- variables --- + uint16_t msGap; //gap between beep entries (when multiple queued) + + private: + //--- functions --- + void init(); + + //--- variables --- + gpio_num_t gpio_pin; + + struct beepEntry { + uint8_t count; + uint16_t msOn; + uint16_t msOff; + }; + + //queue for queueing up multiple events while one is still processing + QueueHandle_t beepQueue = NULL; + +}; + + + diff --git a/main/config.cpp b/main/config.cpp new file mode 100644 index 0000000..6f27b28 --- /dev/null +++ b/main/config.cpp @@ -0,0 +1,15 @@ +#include "config.hpp" + + +//--- inputs --- +//create objects for switches at bottom screw temerinals +gpio_evaluatedSwitch SW_START(GPIO_SW_START, true, false); //pullup true, not inverted (switch to GND, pullup on receiver pcb) +gpio_evaluatedSwitch SW_RESET(GPIO_SW_RESET, true, false); //pullup true, not inverted (switch to GND, pullup on receiver pcb) +gpio_evaluatedSwitch SW_SET(GPIO_SW_SET, true, false); //pullup true, not inverted (switch to GND, pullup on receiver pcb) +gpio_evaluatedSwitch SW_PRESET1(GPIO_SW_PRESET1, true, false); //pullup true, not inverted (switch to GND, pullup on receiver pcb) +gpio_evaluatedSwitch SW_PRESET2(GPIO_SW_PRESET2, true, false); //pullup true, not inverted (switch to GND, pullup on receiver pcb) +gpio_evaluatedSwitch SW_PRESET3(GPIO_SW_PRESET3, true, false); //pullup true, not inverted (switch to GND, pullup on receiver pcb) + + +//create buzzer object with gap between queued events of 100ms +buzzer_t buzzer(GPIO_BUZZER, 100); diff --git a/main/config.hpp b/main/config.hpp new file mode 100644 index 0000000..aef20ed --- /dev/null +++ b/main/config.hpp @@ -0,0 +1,74 @@ +#pragma once +#include "gpio_evaluateSwitch.hpp" +#include "buzzer.hpp" + +//=================================== +//===== define output gpio pins ===== +//=================================== +//4x stepper mosfet outputs for VFD +#define GPIO_VFD_FWD GPIO_NUM_4 +#define GPIO_VFD_D0 GPIO_NUM_16 +#define GPIO_VFD_D1 GPIO_NUM_2 +#define GPIO_VFD_D2 GPIO_NUM_15 + +#define GPIO_MOS1 GPIO_NUM_18 +#define GPIO_MOS2 GPIO_NUM_5 +#define GPIO_RELAY GPIO_NUM_13 +#define GPIO_BUZZER GPIO_NUM_12 + + +//================================== +//===== define input gpio pins ===== +//================================== +#define GPIO_SW_START GPIO_NUM_26 +#define GPIO_SW_RESET GPIO_NUM_25 +#define GPIO_SW_SET GPIO_NUM_33 +#define GPIO_SW_PRESET1 GPIO_NUM_32 +#define GPIO_SW_PRESET2 GPIO_NUM_34 +#define GPIO_SW_PRESET3 GPIO_NUM_39 + +#define GPIO_POTI GPIO_NUM_36 + + +//-------------------------- +//----- display config ----- +//-------------------------- +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0) +#define HOST HSPI_HOST +#else +#define HOST SPI2_HOST +#endif +#define DISPLAY_PIN_NUM_MOSI GPIO_NUM_23 +#define DISPLAY_PIN_NUM_CLK GPIO_NUM_22 +#define DISPLAY_PIN_NUM_CS GPIO_NUM_27 +#define DISPLAY_DELAY 2000 + +//-------------------------- +//----- encoder config ----- +//-------------------------- +#define TAG "app" +#define ROT_ENC_A_GPIO GPIO_NUM_19 +#define ROT_ENC_B_GPIO GPIO_NUM_21 +#define ENABLE_HALF_STEPS false // Set to true to enable tracking of rotary encoder at half step resolution +#define FLIP_DIRECTION false // Set to true to reverse the clockwise/counterclockwise sense + +#define MEASURING_ROLL_DIAMETER 44 +#define PI 3.14159265358979323846 + + + +//============================ +//===== global variables ===== +//============================ +//create global evaluated switch objects +//--- inputs --- +//create objects for switches at bottom screw temerinals +extern gpio_evaluatedSwitch SW_START; +extern gpio_evaluatedSwitch SW_RESET; +extern gpio_evaluatedSwitch SW_SET; +extern gpio_evaluatedSwitch SW_PRESET1; +extern gpio_evaluatedSwitch SW_PRESET2; +extern gpio_evaluatedSwitch SW_PRESET3; + +//create global buzzer object +extern buzzer_t buzzer; diff --git a/main/control.cpp b/main/control.cpp new file mode 100644 index 0000000..8dc513c --- /dev/null +++ b/main/control.cpp @@ -0,0 +1,10 @@ +#include "control.hpp" + + +void task_control(void *pvParameter) +{ + while(1){ + vTaskDelay(500 / portTICK_PERIOD_MS); + } + +} diff --git a/main/control.hpp b/main/control.hpp new file mode 100644 index 0000000..2a7be80 --- /dev/null +++ b/main/control.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include +#include +#include "freertos/queue.h" +#include "esp_system.h" +#include "esp_log.h" + +#include "config.hpp" + + +void task_control(void *pvParameter); diff --git a/main/main.c b/main/main.cpp similarity index 64% rename from main/main.c rename to main/main.cpp index 69b04fb..205a4a0 100644 --- a/main/main.c +++ b/main/main.cpp @@ -1,3 +1,5 @@ +extern "C" +{ #include #include #include @@ -8,36 +10,43 @@ #include #include "rotary_encoder.h" +} -//-------------------------- -//----- display config ----- -//-------------------------- -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0) -#define HOST HSPI_HOST -#else -#define HOST SPI2_HOST -#endif -#define DISPLAY_PIN_NUM_MOSI 23 -#define DISPLAY_PIN_NUM_CLK 18 -#define DISPLAY_PIN_NUM_CS 21 -#define DISPLAY_DELAY 2000 - -//-------------------------- -//----- encoder config ----- -//-------------------------- -#define TAG "app" -#define ROT_ENC_A_GPIO 16 -#define ROT_ENC_B_GPIO 17 -#define ENABLE_HALF_STEPS false // Set to true to enable tracking of rotary encoder at half step resolution -#define FLIP_DIRECTION false // Set to true to reverse the clockwise/counterclockwise sense - -#define MEASURING_ROLL_DIAMETER 44 -#define PI 3.14159265358979323846 +#include "gpio_evaluateSwitch.hpp" +#include "config.hpp" +#include "control.hpp" +#include "buzzer.hpp" +//function to configure gpio pin as output +void gpio_configure_output(gpio_num_t gpio_pin){ + gpio_pad_select_gpio(gpio_pin); + gpio_set_direction(gpio_pin, GPIO_MODE_OUTPUT); +} -void task(void *pvParameter) + +void init_gpios(){ + //initialize all outputs + //4x stepper mosfets + gpio_configure_output(GPIO_VFD_FWD); + gpio_configure_output(GPIO_VFD_D0); + gpio_configure_output(GPIO_VFD_D1); + gpio_configure_output(GPIO_VFD_D2); + //2x power mosfets + gpio_configure_output(GPIO_MOS1); + gpio_configure_output(GPIO_MOS2); + //onboard relay and buzzer + gpio_configure_output(GPIO_RELAY); + gpio_configure_output(GPIO_BUZZER); + //5v regulator + gpio_configure_output(GPIO_NUM_17); +} + + + +//task for testing the encoder and display +void task_testing(void *pvParameter) { //======================== @@ -47,7 +56,7 @@ void task(void *pvParameter) ESP_ERROR_CHECK(gpio_install_isr_service(0)); // Initialise the rotary encoder device with the GPIOs for A and B signals - rotary_encoder_info_t info = { 0 }; + rotary_encoder_info_t info; ESP_ERROR_CHECK(rotary_encoder_init(&info, ROT_ENC_A_GPIO, ROT_ENC_B_GPIO)); ESP_ERROR_CHECK(rotary_encoder_enable_half_steps(&info, ENABLE_HALF_STEPS)); #ifdef FLIP_DIRECTION @@ -66,23 +75,21 @@ void task(void *pvParameter) //===== init display ===== //======================== // Configure SPI bus - spi_bus_config_t cfg = { - .mosi_io_num = DISPLAY_PIN_NUM_MOSI, - .miso_io_num = -1, - .sclk_io_num = DISPLAY_PIN_NUM_CLK, - .quadwp_io_num = -1, - .quadhd_io_num = -1, - .max_transfer_sz = 0, - .flags = 0 - }; + spi_bus_config_t cfg; + cfg.mosi_io_num = DISPLAY_PIN_NUM_MOSI; + cfg.miso_io_num = -1; + cfg.sclk_io_num = DISPLAY_PIN_NUM_CLK; + cfg.quadwp_io_num = -1; + cfg.quadhd_io_num = -1; + cfg.max_transfer_sz = 0; + cfg.flags = 0; ESP_ERROR_CHECK(spi_bus_initialize(HOST, &cfg, 1)); // Configure device - max7219_t dev = { - .cascade_size = 1, - .digits = 8, - .mirrored = true - }; + max7219_t dev; + dev.cascade_size = 1; + dev.digits = 8; + dev.mirrored = true; ESP_ERROR_CHECK(max7219_init_desc(&dev, HOST, MAX7219_MAX_CLOCK_SPEED_HZ, DISPLAY_PIN_NUM_CS)); ESP_ERROR_CHECK(max7219_init(&dev)); @@ -111,7 +118,7 @@ void task(void *pvParameter) // Wait for incoming events on the event queue. - rotary_encoder_event_t event = { 0 }; + rotary_encoder_event_t event; if (xQueueReceive(event_queue, &event, 1000 / portTICK_PERIOD_MS) == pdTRUE) { ESP_LOGI(TAG, "Event: position %d, direction %s", event.state.position, @@ -131,7 +138,7 @@ void task(void *pvParameter) else //no event for 1s { // Poll current position and direction - rotary_encoder_state_t state = { 0 }; + rotary_encoder_state_t state; ESP_ERROR_CHECK(rotary_encoder_get_state(&info, &state)); ESP_LOGI(TAG, "Poll: position %d, direction %s", state.position, state.direction ? (state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET"); @@ -143,8 +150,6 @@ void task(void *pvParameter) - - //display test //float count = 0; @@ -183,9 +188,40 @@ void task(void *pvParameter) // } //} -void app_main() -{ - xTaskCreate(task, "task", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL); + +//====================================== +//============ buzzer task ============= +//====================================== +//TODO: move the task creation to buzzer class (buzzer.cpp) +//e.g. only have function buzzer.createTask() in app_main +void task_buzzer( void * pvParameters ){ + ESP_LOGI("task_buzzer", "Start of buzzer task..."); + //run function that waits for a beep events to arrive in the queue + //and processes them + buzzer.processQueue(); } + + + + +extern "C" void app_main() +{ + + //init outputs + init_gpios(); + //enable 5V volate regulator + gpio_set_level(GPIO_NUM_17, 1); + + + //--- create task testing --- + xTaskCreate(task_testing, "task_testing", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL); + //--- create task control --- + xTaskCreate(task_control, "task_control", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL); + //--- create task for buzzer --- + xTaskCreate(&task_buzzer, "task_buzzer", 2048, NULL, 2, NULL); + + //beep at startup + buzzer.beep(3, 70, 50); +}