diff --git a/board_motorctl/main/CMakeLists.txt b/board_motorctl/main/CMakeLists.txt index 4b2b42c..f6c8f85 100644 --- a/board_motorctl/main/CMakeLists.txt +++ b/board_motorctl/main/CMakeLists.txt @@ -1,11 +1,8 @@ idf_component_register( SRCS "main.cpp" - "motordrivers.cpp" - "motorctl.cpp" "config.cpp" "fan.cpp" - "currentsensor.cpp" "uart.cpp" INCLUDE_DIRS "." diff --git a/board_single/CMakeLists.txt b/board_single/CMakeLists.txt index cd14e0b..e827903 100644 --- a/board_single/CMakeLists.txt +++ b/board_single/CMakeLists.txt @@ -5,4 +5,5 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(armchair) +set(EXTRA_COMPONENT_DIRS "../components ../common") +project(armchair-singleBoard) diff --git a/board_single/main/CMakeLists.txt b/board_single/main/CMakeLists.txt index 92ed0e1..6a60a1a 100644 --- a/board_single/main/CMakeLists.txt +++ b/board_single/main/CMakeLists.txt @@ -1,18 +1,13 @@ idf_component_register( SRCS "main.cpp" - "motordrivers.cpp" - "motorctl.cpp" "config.cpp" "joystick.cpp" - "buzzer.cpp" "control.cpp" "button.cpp" "fan.cpp" - "wifi.c" "http.cpp" "auto.cpp" - "currentsensor.cpp" INCLUDE_DIRS "." ) diff --git a/board_single/main/buzzer.cpp b/board_single/main/buzzer.cpp deleted file mode 100644 index 9046cd1..0000000 --- a/board_single/main/buzzer.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#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/board_single/main/buzzer.hpp b/board_single/main/buzzer.hpp deleted file mode 100644 index ce6c2c6..0000000 --- a/board_single/main/buzzer.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#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/board_single/main/currentsensor.cpp b/board_single/main/currentsensor.cpp deleted file mode 100644 index 2569c6d..0000000 --- a/board_single/main/currentsensor.cpp +++ /dev/null @@ -1,75 +0,0 @@ -extern "C" { -#include "hal/timer_types.h" -#include "esp_log.h" -} - -#include "currentsensor.hpp" - -//tag for logging -static const char * TAG = "current-sensors"; - - - -//-------------------------- -//------- getVoltage ------- -//-------------------------- -//local function to get average voltage from adc -float getVoltage(adc1_channel_t adc, uint32_t samples){ - //measure voltage - int measure = 0; - for (int j=0; j centerVoltage){ - current = (voltage - centerVoltage) / (3.3 - centerVoltage) * ratedCurrent; - }else { - current = 0; - } - - ESP_LOGI(TAG, "read sensor adc=%d: voltage=%.3fV, centerVoltage=%.3fV => current=%.3fA", (int)adcChannel, voltage, centerVoltage, current); - return current; -} - - - -//=============================== -//===== calibrateZeroAmpere ===== -//=============================== -void currentSensor::calibrateZeroAmpere(void){ - //measure voltage - float prev = centerVoltage; - centerVoltage = getVoltage(adcChannel, 100); - ESP_LOGW(TAG, "defined centerVoltage (0A) to %.3f (previous %.3f)", centerVoltage, prev); -} diff --git a/board_single/main/currentsensor.hpp b/board_single/main/currentsensor.hpp deleted file mode 100644 index f62fa34..0000000 --- a/board_single/main/currentsensor.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#include - -//supported current sensor working method: - //0V = -ratedCurrent - //centerVoltage = 0A - //3.3V = ratedCurrent - -class currentSensor{ - public: - currentSensor (adc1_channel_t adcChannel_f, float ratedCurrent); - void calibrateZeroAmpere(void); //set current voltage to voltage representing 0A - float read(void); //get current ampere - private: - adc1_channel_t adcChannel; - float ratedCurrent; - uint32_t measure; - float voltage; - float current; - float centerVoltage = 3.3/2; -}; diff --git a/board_single/main/motorctl.cpp b/board_single/main/motorctl.cpp deleted file mode 100644 index df544ea..0000000 --- a/board_single/main/motorctl.cpp +++ /dev/null @@ -1,330 +0,0 @@ -#include "motorctl.hpp" - -//tag for logging -static const char * TAG = "motor-control"; - - -//============================= -//======== constructor ======== -//============================= -//constructor, simultaniously initialize instance of motor driver 'motor' and current sensor 'cSensor' with provided config (see below lines after ':') -controlledMotor::controlledMotor(single100a_config_t config_driver, motorctl_config_t config_control): - motor(config_driver), - cSensor(config_control.currentSensor_adc, config_control.currentSensor_ratedCurrent) { - //copy parameters for controlling the motor - config = config_control; - //copy configured default fading durations to actually used variables - msFadeAccel = config.msFadeAccel; - msFadeDecel = config.msFadeDecel; - - init(); - //TODO: add currentsensor object here - //currentSensor cSensor(config.currentSensor_adc, config.currentSensor_ratedCurrent); - } - - - -//============================ -//========== init ============ -//============================ -void controlledMotor::init(){ - commandQueue = xQueueCreate( 1, sizeof( struct motorCommand_t ) ); - //cSensor.calibrateZeroAmpere(); //currently done in currentsensor constructor TODO do this regularly e.g. in idle? -} - - - -//---------------- -//----- fade ----- -//---------------- -//local function that fades a variable -//- increments a variable (pointer) by given value -//- sets to target if already closer than increment -//TODO this needs testing -void fade(float * dutyNow, float dutyTarget, float dutyIncrement){ - float dutyDelta = dutyTarget - *dutyNow; - if ( fabs(dutyDelta) > fabs(dutyIncrement) ) { //check if already close to target - *dutyNow = *dutyNow + dutyIncrement; - } - //already closer to target than increment - else { - *dutyNow = dutyTarget; - } -} - - - -//---------------------------- -//----- getStateFromDuty ----- -//---------------------------- -//local function that determines motor the direction from duty range -100 to 100 -motorstate_t getStateFromDuty(float duty){ - if(duty > 0) return motorstate_t::FWD; - if (duty < 0) return motorstate_t::REV; - return motorstate_t::IDLE; -} - - - -//============================== -//=========== handle =========== -//============================== -//function that controls the motor driver and handles fading/ramp, current limit and deadtime -void controlledMotor::handle(){ - - //TODO: History: skip fading when motor was running fast recently / alternatively add rot-speed sensor - - //--- receive commands from queue --- - if( xQueueReceive( commandQueue, &commandReceive, ( TickType_t ) 0 ) ) - { - ESP_LOGD(TAG, "Read command from queue: state=%s, duty=%.2f", motorstateStr[(int)commandReceive.state], commandReceive.duty); - state = commandReceive.state; - dutyTarget = commandReceive.duty; - - //--- convert duty --- - //define target duty (-100 to 100) from provided duty and motorstate - //this value is more suitable for the fading algorithm - switch(commandReceive.state){ - case motorstate_t::BRAKE: - //update state - state = motorstate_t::BRAKE; - dutyTarget = 0; - break; - case motorstate_t::IDLE: - dutyTarget = 0; - break; - case motorstate_t::FWD: - dutyTarget = fabs(commandReceive.duty); - break; - case motorstate_t::REV: - dutyTarget = - fabs(commandReceive.duty); - break; - } - } - - - //--- calculate increment --- - //calculate increment for fading UP with passed time since last run and configured fade time - int64_t usPassed = esp_timer_get_time() - timestampLastRunUs; - if (msFadeAccel > 0){ - dutyIncrementAccel = ( usPassed / ((float)msFadeAccel * 1000) ) * 100; //TODO define maximum increment - first run after startup (or long) pause can cause a very large increment - } else { - dutyIncrementAccel = 100; - } - - //calculate increment for fading DOWN with passed time since last run and configured fade time - if (msFadeDecel > 0){ - dutyIncrementDecel = ( usPassed / ((float)msFadeDecel * 1000) ) * 100; - } else { - dutyIncrementDecel = 100; - } - - - //--- BRAKE --- - //brake immediately, update state, duty and exit this cycle of handle function - if (state == motorstate_t::BRAKE){ - motor.set(motorstate_t::BRAKE, 0); - dutyNow = 0; - return; //no need to run the fade algorithm - } - - - //--- calculate difference --- - dutyDelta = dutyTarget - dutyNow; - //positive: need to increase by that value - //negative: need to decrease - - - //----- FADING ----- - //fade duty to target (up and down) - //TODO: this needs optimization (can be more clear and/or simpler) - if (dutyDelta > 0) { //difference positive -> increasing duty (-100 -> 100) - if (dutyNow < 0) { //reverse, decelerating - fade(&dutyNow, dutyTarget, dutyIncrementDecel); - } - else if (dutyNow >= 0) { //forward, accelerating - fade(&dutyNow, dutyTarget, dutyIncrementAccel); - } - } - else if (dutyDelta < 0) { //difference negative -> decreasing duty (100 -> -100) - if (dutyNow <= 0) { //reverse, accelerating - fade(&dutyNow, dutyTarget, - dutyIncrementAccel); - } - else if (dutyNow > 0) { //forward, decelerating - fade(&dutyNow, dutyTarget, - dutyIncrementDecel); - } - } - - - //----- CURRENT LIMIT ----- - if ((config.currentLimitEnabled) && (dutyDelta != 0)){ - currentNow = cSensor.read(); - if (fabs(currentNow) > config.currentMax){ - float dutyOld = dutyNow; - //adaptive decrement: - //Note current exceeded twice -> twice as much decrement: TODO: decrement calc needs finetuning, currently random values - dutyIncrementDecel = (currentNow/config.currentMax) * ( usPassed / ((float)msFadeDecel * 1500) ) * 100; - float currentLimitDecrement = ( (float)usPassed / ((float)1000 * 1000) ) * 100; //1000ms from 100 to 0 - if (dutyNow < -currentLimitDecrement) { - dutyNow += currentLimitDecrement; - } else if (dutyNow > currentLimitDecrement) { - dutyNow -= currentLimitDecrement; - } - ESP_LOGW(TAG, "current limit exceeded! now=%.3fA max=%.1fA => decreased duty from %.3f to %.3f", currentNow, config.currentMax, dutyOld, dutyNow); - } - } - - - //--- define new motorstate --- (-100 to 100 => direction) - state=getStateFromDuty(dutyNow); - - - //--- DEAD TIME ---- - //ensure minimum idle time between direction change to prevent driver overload - //FWD -> IDLE -> FWD continue without waiting - //FWD -> IDLE -> REV wait for dead-time in IDLE - //TODO check when changed only? - if ( //not enough time between last direction state - ( state == motorstate_t::FWD && (esp_log_timestamp() - timestampsModeLastActive[(int)motorstate_t::REV] < config.deadTimeMs)) - || (state == motorstate_t::REV && (esp_log_timestamp() - timestampsModeLastActive[(int)motorstate_t::FWD] < config.deadTimeMs)) - ){ - ESP_LOGD(TAG, "waiting dead-time... dir change %s -> %s", motorstateStr[(int)statePrev], motorstateStr[(int)state]); - if (!deadTimeWaiting){ //log start - deadTimeWaiting = true; - ESP_LOGW(TAG, "starting dead-time... %s -> %s", motorstateStr[(int)statePrev], motorstateStr[(int)state]); - } - //force IDLE state during wait - state = motorstate_t::IDLE; - dutyNow = 0; - } else { - if (deadTimeWaiting){ //log end - deadTimeWaiting = false; - ESP_LOGW(TAG, "dead-time ended - continue with %s", motorstateStr[(int)state]); - } - ESP_LOGV(TAG, "deadtime: no change below deadtime detected... dir=%s, duty=%.1f", motorstateStr[(int)state], dutyNow); - } - - - //--- save current actual motorstate and timestamp --- - //needed for deadtime - timestampsModeLastActive[(int)getStateFromDuty(dutyNow)] = esp_log_timestamp(); - //(-100 to 100 => direction) - statePrev = getStateFromDuty(dutyNow); - - - //--- apply new target to motor --- - motor.set(state, fabs(dutyNow)); - //note: BRAKE state is handled earlier - - - //--- update timestamp --- - timestampLastRunUs = esp_timer_get_time(); //update timestamp last run with current timestamp in microseconds -} - - - -//=============================== -//========== setTarget ========== -//=============================== -//function to set the target mode and duty of a motor -//puts the provided command in a queue for the handle function running in another task -void controlledMotor::setTarget(motorstate_t state_f, float duty_f){ - commandSend = { - .state = state_f, - .duty = duty_f - }; - - ESP_LOGD(TAG, "Inserted command to queue: state=%s, duty=%.2f", motorstateStr[(int)commandSend.state], commandSend.duty); - //send command to queue (overwrite if an old command is still in the queue and not processed) - xQueueOverwrite( commandQueue, ( void * )&commandSend); - //xQueueSend( commandQueue, ( void * )&commandSend, ( TickType_t ) 0 ); - -} - - - -//=============================== -//========== getStatus ========== -//=============================== -//function which returns the current status of the motor in a motorCommand_t struct -motorCommand_t controlledMotor::getStatus(){ - motorCommand_t status = { - .state = state, - .duty = dutyNow - }; - //TODO: mutex - return status; -}; - - - -//=============================== -//=========== setFade =========== -//=============================== -//function for editing or enabling the fading/ramp of the motor control - -//set/update fading duration/amount -void controlledMotor::setFade(fadeType_t fadeType, uint32_t msFadeNew){ - //TODO: mutex for msFade variable also used in handle function - switch(fadeType){ - case fadeType_t::ACCEL: - msFadeAccel = msFadeNew; - break; - case fadeType_t::DECEL: - msFadeDecel = msFadeNew; - break; - } -} - -//enable (set to default value) or disable fading -void controlledMotor::setFade(fadeType_t fadeType, bool enabled){ - uint32_t msFadeNew = 0; //define new fade time as default disabled - if(enabled){ //enable - //set to default/configured value - switch(fadeType){ - case fadeType_t::ACCEL: - msFadeNew = config.msFadeAccel; - break; - case fadeType_t::DECEL: - msFadeNew = config.msFadeDecel; - break; - } - } - //apply new Fade value - setFade(fadeType, msFadeNew); -} - - - -//================================== -//=========== toggleFade =========== -//================================== -//toggle fading between OFF and default value -bool controlledMotor::toggleFade(fadeType_t fadeType){ - uint32_t msFadeNew = 0; - bool enabled = false; - switch(fadeType){ - case fadeType_t::ACCEL: - if (msFadeAccel == 0){ - msFadeNew = config.msFadeAccel; - enabled = true; - } else { - msFadeNew = 0; - } - break; - case fadeType_t::DECEL: - if (msFadeDecel == 0){ - msFadeNew = config.msFadeAccel; - enabled = true; - } else { - msFadeNew = 0; - } - break; - } - //apply new Fade value - setFade(fadeType, msFadeNew); - - //return new state (fading enabled/disabled) - return enabled; -} - diff --git a/board_single/main/motorctl.hpp b/board_single/main/motorctl.hpp deleted file mode 100644 index c4bc19e..0000000 --- a/board_single/main/motorctl.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -extern "C" -{ -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "esp_log.h" -#include "esp_timer.h" -} - -#include "motordrivers.hpp" -#include "currentsensor.hpp" - - -//======================================= -//====== struct/type declarations ====== -//======================================= - -//struct for sending command for one motor in the queue -struct motorCommand_t { - motorstate_t state; - float duty; -}; - -//struct containing commands for two motors -typedef struct motorCommands_t { - motorCommand_t left; - motorCommand_t right; -} motorCommands_t; - -//struct with all config parameters for a motor regarding ramp and current limit -typedef struct motorctl_config_t { - uint32_t msFadeAccel; //acceleration of the motor (ms it takes from 0% to 100%) - uint32_t msFadeDecel; //deceleration of the motor (ms it takes from 100% to 0%) - bool currentLimitEnabled; - adc1_channel_t currentSensor_adc; - float currentSensor_ratedCurrent; - float currentMax; - uint32_t deadTimeMs; //time motor stays in IDLE before direction change -} motorctl_config_t; - -//enum fade type (acceleration, deceleration) -//e.g. used for specifying which fading should be modified with setFade, togleFade functions -enum class fadeType_t {ACCEL, DECEL}; - - - -//=================================== -//====== controlledMotor class ====== -//=================================== -class controlledMotor { - public: - //--- functions --- - controlledMotor(single100a_config_t config_driver, motorctl_config_t config_control); //constructor with structs for configuring motordriver and parameters for control TODO: add configuration for currentsensor - void handle(); //controls motor duty with fade and current limiting feature (has to be run frequently by another task) - void setTarget(motorstate_t state_f, float duty_f = 0); //adds target command to queue for handle function - motorCommand_t getStatus(); //get current status of the motor (returns struct with state and duty) - - void setFade(fadeType_t fadeType, bool enabled); //enable/disable acceleration or deceleration fading - void setFade(fadeType_t fadeType, uint32_t msFadeNew); //set acceleration or deceleration fade time - bool toggleFade(fadeType_t fadeType); //toggle acceleration or deceleration on/off - - //TODO set current limit method - - private: - //--- functions --- - void init(); //creates currentsensor objects, motordriver objects and queue - - //--- objects --- - //motor driver - single100a motor; - //queue for sending commands to the separate task running the handle() function very fast - QueueHandle_t commandQueue = NULL; - //current sensor - currentSensor cSensor; - - //--- variables --- - //struct for storing control specific parameters - motorctl_config_t config; - motorstate_t state = motorstate_t::IDLE; - - float currentMax; - float currentNow; - - float dutyTarget; - float dutyNow; - float dutyIncrementAccel; - float dutyIncrementDecel; - float dutyDelta; - - uint32_t msFadeAccel; - uint32_t msFadeDecel; - - uint32_t ramp; - int64_t timestampLastRunUs; - - bool deadTimeWaiting = false; - uint32_t timestampsModeLastActive[4] = {}; - motorstate_t statePrev = motorstate_t::FWD; - - struct motorCommand_t commandReceive = {}; - struct motorCommand_t commandSend = {}; -}; diff --git a/board_single/main/motordrivers.cpp b/board_single/main/motordrivers.cpp deleted file mode 100644 index e15b4e6..0000000 --- a/board_single/main/motordrivers.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include "motordrivers.hpp" - -//TODO: move from ledc to mcpwm? -//https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32/api-reference/peripherals/mcpwm.html# -//https://github.com/espressif/esp-idf/tree/v4.3/examples/peripherals/mcpwm/mcpwm_basic_config - -//Note fade functionality provided by LEDC would be very useful but unfortunately is not usable here because: -//"Due to hardware limitations, there is no way to stop a fade before it reaches its target duty." - -//definition of string array to be able to convert state enum to readable string -const char* motorstateStr[4] = {"IDLE", "FWD", "REV", "BRAKE"}; - -//tag for logging -static const char * TAG = "motordriver"; - - - -//==================================== -//===== single100a motor driver ====== -//==================================== - -//----------------------------- -//-------- constructor -------- -//----------------------------- -//copy provided struct with all configuration and run init function -single100a::single100a(single100a_config_t config_f){ - config = config_f; - init(); -} - - - -//---------------------------- -//---------- init ------------ -//---------------------------- -//function to initialize pwm output, gpio pins and calculate maxDuty -void single100a::init(){ - - //--- configure ledc timer --- - ledc_timer_config_t ledc_timer; - ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE; - ledc_timer.timer_num = config.ledc_timer; - ledc_timer.duty_resolution = config.resolution; //13bit gives max 5khz freq - ledc_timer.freq_hz = config.pwmFreq; - ledc_timer.clk_cfg = LEDC_AUTO_CLK; - //apply configuration - ledc_timer_config(&ledc_timer); - - //--- configure ledc channel --- - ledc_channel_config_t ledc_channel; - ledc_channel.channel = config.ledc_channel; - ledc_channel.duty = 0; - ledc_channel.gpio_num = config.gpio_pwm; - ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE; - ledc_channel.hpoint = 0; - ledc_channel.timer_sel = config.ledc_timer; - ledc_channel.intr_type = LEDC_INTR_DISABLE; - ledc_channel.flags.output_invert = 0; //TODO: add config option to invert the pwm output? - //apply configuration - ledc_channel_config(&ledc_channel); - - //--- define gpio pins as outputs --- - gpio_pad_select_gpio(config.gpio_a); - gpio_set_direction(config.gpio_a, GPIO_MODE_OUTPUT); - gpio_pad_select_gpio(config.gpio_b); - gpio_set_direction(config.gpio_b, GPIO_MODE_OUTPUT); - - //--- calculate max duty according to selected resolution --- - dutyMax = pow(2, ledc_timer.duty_resolution) -1; - ESP_LOGI(TAG, "initialized single100a driver"); - ESP_LOGI(TAG, "resolution=%dbit, dutyMax value=%d, resolution=%.4f %%", ledc_timer.duty_resolution, dutyMax, 100/(float)dutyMax); -} - - - -//--------------------------- -//----------- set ----------- -//--------------------------- -//function to put the h-bridge module in the desired state and duty cycle -void single100a::set(motorstate_t state_f, float duty_f){ - - //scale provided target duty in percent to available resolution for ledc - uint32_t dutyScaled; - if (duty_f > 100) { //target duty above 100% - dutyScaled = dutyMax; - } else if (duty_f <= 0) { //target at or below 0% - state_f = motorstate_t::IDLE; - dutyScaled = 0; - } else { //target duty 0-100% - //scale duty to available resolution - dutyScaled = duty_f / 100 * dutyMax; - } - - //put the single100a h-bridge module in the desired state update duty-cycle - switch (state_f){ - case motorstate_t::IDLE: - ledc_set_duty(LEDC_HIGH_SPEED_MODE, config.ledc_channel, dutyScaled); - ledc_update_duty(LEDC_HIGH_SPEED_MODE, config.ledc_channel); - //TODO: to fix bugged state of h-bridge module when idle and start again, maybe try to leave pwm signal on for some time before updating a/b pins? - //no brake: (freewheel) - //gpio_set_level(config.gpio_a, config.aEnabledPinState); - //gpio_set_level(config.gpio_b, !config.bEnabledPinState); - gpio_set_level(config.gpio_a, config.aEnabledPinState); - gpio_set_level(config.gpio_b, config.bEnabledPinState); - break; - case motorstate_t::BRAKE: - ledc_set_duty(LEDC_HIGH_SPEED_MODE, config.ledc_channel, 0); - ledc_update_duty(LEDC_HIGH_SPEED_MODE, config.ledc_channel); - //brake: - gpio_set_level(config.gpio_a, !config.aEnabledPinState); - gpio_set_level(config.gpio_b, !config.bEnabledPinState); - break; - case motorstate_t::FWD: - ledc_set_duty(LEDC_HIGH_SPEED_MODE, config.ledc_channel, dutyScaled); - ledc_update_duty(LEDC_HIGH_SPEED_MODE, config.ledc_channel); - //forward: - gpio_set_level(config.gpio_a, config.aEnabledPinState); - gpio_set_level(config.gpio_b, !config.bEnabledPinState); - break; - case motorstate_t::REV: - ledc_set_duty(LEDC_HIGH_SPEED_MODE, config.ledc_channel, dutyScaled); - ledc_update_duty(LEDC_HIGH_SPEED_MODE, config.ledc_channel); - //reverse: - gpio_set_level(config.gpio_a, !config.aEnabledPinState); - gpio_set_level(config.gpio_b, config.bEnabledPinState); - break; - } - ESP_LOGD(TAG, "set module to state=%s, duty=%d/%d, duty_input=%.3f%%", motorstateStr[(int)state_f], dutyScaled, dutyMax, duty_f); -} diff --git a/board_single/main/motordrivers.hpp b/board_single/main/motordrivers.hpp deleted file mode 100644 index 2cc3c75..0000000 --- a/board_single/main/motordrivers.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -extern "C" -{ -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "driver/gpio.h" -#include "esp_log.h" - -#include "driver/ledc.h" -#include "esp_err.h" -} - -#include - - -//==================================== -//===== single100a motor driver ====== -//==================================== - -//-------------------------------------------- -//---- struct, enum, variable declarations --- -//-------------------------------------------- - -//class which controls a motor using a 'single100a' h-bridge module -enum class motorstate_t {IDLE, FWD, REV, BRAKE}; -//definition of string array to be able to convert state enum to readable string (defined in motordrivers.cpp) -extern const char* motorstateStr[4]; - -//struct with all config parameters for single100a motor driver -typedef struct single100a_config_t { - gpio_num_t gpio_pwm; - gpio_num_t gpio_a; - gpio_num_t gpio_b; - ledc_timer_t ledc_timer; - ledc_channel_t ledc_channel; - bool aEnabledPinState; - bool bEnabledPinState; - ledc_timer_bit_t resolution; - int pwmFreq; -} single100a_config_t; - - - -//-------------------------------- -//------- single100a class ------- -//-------------------------------- -class single100a { - public: - //--- constructor --- - single100a(single100a_config_t config_f); //provide config struct (see above) - - //--- functions --- - void set(motorstate_t state, float duty_f = 0); //set mode and duty of the motor (see motorstate_t above) - //TODO: add functions to get the current state and duty - - private: - //--- functions --- - void init(); //initialize pwm and gpio outputs, calculate maxDuty - - //--- variables --- - single100a_config_t config; - uint32_t dutyMax; - motorstate_t state = motorstate_t::IDLE; -}; diff --git a/board_single/main/wifi.c b/board_single/main/wifi.c deleted file mode 100644 index 27eb3db..0000000 --- a/board_single/main/wifi.c +++ /dev/null @@ -1,265 +0,0 @@ -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" -#include "esp_system.h" -#include "esp_mac.h" -#include "esp_wifi.h" -#include "esp_event.h" -#include "esp_log.h" -#include "nvs_flash.h" - -#include "lwip/err.h" -#include "lwip/sys.h" - -#include "wifi.h" - - - -//--- variables used for ap and wifi --- -static const char *TAG = "wifi"; -static esp_event_handler_instance_t instance_any_id; - - -//============================================ -//============ init nvs and netif ============ -//============================================ -//initialize nvs-flash and netif (needed for both AP and CLIENT) -//has to be run once at startup -void wifi_initNvs_initNetif(){ - //Initialize NVS (needed for wifi) - esp_err_t ret = nvs_flash_init(); - if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { - ESP_ERROR_CHECK(nvs_flash_erase()); - ret = nvs_flash_init(); - } - ESP_ERROR_CHECK(esp_netif_init()); - ESP_ERROR_CHECK(esp_event_loop_create_default()); -} - - -//=========================================== -//============ init access point ============ -//=========================================== - -//-------------------------------------------- -//------ configuration / declarations -------- -//-------------------------------------------- -#define EXAMPLE_ESP_WIFI_SSID_AP "armchair" -#define EXAMPLE_ESP_WIFI_PASS_AP "" -#define EXAMPLE_ESP_WIFI_CHANNEL_AP 1 -#define EXAMPLE_MAX_STA_CONN_AP 4 - -static esp_netif_t *ap; - -static void wifi_event_handler_ap(void* arg, esp_event_base_t event_base, - int32_t event_id, void* event_data) -{ - if (event_id == WIFI_EVENT_AP_STACONNECTED) { - wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data; - ESP_LOGI(TAG, "station "MACSTR" join, AID=%d", - MAC2STR(event->mac), event->aid); - } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) { - wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data; - ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d", - MAC2STR(event->mac), event->aid); - } -} - -//----------------------- -//------ init ap -------- -//----------------------- -void wifi_init_ap(void) -{ - ap = esp_netif_create_default_wifi_ap(); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - - ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, - ESP_EVENT_ANY_ID, - &wifi_event_handler_ap, - NULL, - &instance_any_id)); - - wifi_config_t wifi_config = { - .ap = { - .ssid = EXAMPLE_ESP_WIFI_SSID_AP, - .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID_AP), - .channel = EXAMPLE_ESP_WIFI_CHANNEL_AP, - .password = EXAMPLE_ESP_WIFI_PASS_AP, - .max_connection = EXAMPLE_MAX_STA_CONN_AP, - .authmode = WIFI_AUTH_WPA_WPA2_PSK - }, - }; - if (strlen(EXAMPLE_ESP_WIFI_PASS_AP) == 0) { - wifi_config.ap.authmode = WIFI_AUTH_OPEN; - } - - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config)); - ESP_ERROR_CHECK(esp_wifi_start()); - - ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d", - EXAMPLE_ESP_WIFI_SSID_AP, EXAMPLE_ESP_WIFI_PASS_AP, EXAMPLE_ESP_WIFI_CHANNEL_AP); -} - - - -//============================= -//========= deinit AP ========= -//============================= -void wifi_deinit_ap(void) -{ - ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); - ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); - esp_wifi_stop(); - esp_wifi_deinit(); - esp_netif_destroy_default_wifi(ap); - -} - - - - - -//=========================================== -//=============== init client =============== -//=========================================== - -//-------------------------------------------- -//------ configuration / declarations -------- -//-------------------------------------------- -#define EXAMPLE_ESP_WIFI_SSID_CLIENT "BKA-network" -#define EXAMPLE_ESP_WIFI_PASS_CLIENT "airwaveslogitech410" -#define EXAMPLE_ESP_MAXIMUM_RETRY_CLIENT 10 - -static esp_netif_t *sta; -static esp_event_handler_instance_t instance_got_ip; - -/* FreeRTOS event group to signal when we are connected*/ -static EventGroupHandle_t s_wifi_event_group; - -/* The event group allows multiple bits for each event, but we only care about two events: - * - we are connected to the AP with an IP - * - we failed to connect after the maximum amount of retries */ -#define WIFI_CONNECTED_BIT BIT0 -#define WIFI_FAIL_BIT BIT1 - -static int s_retry_num = 0; -static void event_handler(void* arg, esp_event_base_t event_base, - int32_t event_id, void* event_data) -{ - if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { - esp_wifi_connect(); - } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { - if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY_CLIENT) { - esp_wifi_connect(); - s_retry_num++; - ESP_LOGI(TAG, "retry to connect to the AP"); - } else { - xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); - } - ESP_LOGI(TAG,"connect to the AP fail"); - } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { - ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; - ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); - s_retry_num = 0; - xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); - } -} -//--------------------------- -//------ init client -------- -//--------------------------- -void wifi_init_client(void) -{ - s_wifi_event_group = xEventGroupCreate(); - sta = esp_netif_create_default_wifi_sta(); - - - - //set static ip - esp_netif_dhcpc_stop(sta); - - esp_netif_ip_info_t ip_info; - IP4_ADDR(&ip_info.ip, 10, 0, 0, 66); - IP4_ADDR(&ip_info.gw, 10, 0, 0, 1); - IP4_ADDR(&ip_info.netmask, 255, 255, 0, 0); - - esp_netif_set_ip_info(sta, &ip_info); - - - - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - - ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, - ESP_EVENT_ANY_ID, - &event_handler, - NULL, - &instance_any_id)); - ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, - IP_EVENT_STA_GOT_IP, - &event_handler, - NULL, - &instance_got_ip)); - - wifi_config_t wifi_config = { - .sta = { - .ssid = EXAMPLE_ESP_WIFI_SSID_CLIENT, - .password = EXAMPLE_ESP_WIFI_PASS_CLIENT, - /* Setting a password implies station will connect to all security modes including WEP/WPA. - * However these modes are deprecated and not advisable to be used. Incase your Access point - * doesn't support WPA2, these mode can be enabled by commenting below line */ - .threshold.authmode = WIFI_AUTH_WPA2_PSK, - }, - }; - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); - ESP_ERROR_CHECK(esp_wifi_start() ); - - ESP_LOGI(TAG, "wifi_init_sta finished."); - - /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum - * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ - EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, - WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, - pdFALSE, - pdFALSE, - portMAX_DELAY); - - /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually - * happened. */ - if (bits & WIFI_CONNECTED_BIT) { - ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", - EXAMPLE_ESP_WIFI_SSID_CLIENT, EXAMPLE_ESP_WIFI_PASS_CLIENT); - } else if (bits & WIFI_FAIL_BIT) { - ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", - EXAMPLE_ESP_WIFI_SSID_CLIENT, EXAMPLE_ESP_WIFI_PASS_CLIENT); - } else { - ESP_LOGE(TAG, "UNEXPECTED EVENT"); - } - - // /* The event will not be processed after unregister */ - // ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); - // ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); - // vEventGroupDelete(s_wifi_event_group); -} - - - -//================================= -//========= deinit client ========= -//================================= -void wifi_deinit_client(void) -{ - /* The event will not be processed after unregister */ - ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); - ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); - vEventGroupDelete(s_wifi_event_group); - esp_wifi_stop(); - esp_wifi_deinit(); - esp_netif_destroy_default_wifi(sta); -} - diff --git a/board_single/main/wifi.h b/board_single/main/wifi.h deleted file mode 100644 index 39eecee..0000000 --- a/board_single/main/wifi.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -//TODO: currently wifi names and passwords are configured in wifi.c -> move this to config? - -//initialize nvs-flash and netif (needed for both AP and CLIENT) -//has to be run once at startup -//Note: this cant be put in wifi_init functions because this may not be in deinit functions -void wifi_initNvs_initNetif(); - - -//function to start an access point -void wifi_init_ap(void); -//function to disable/deinit access point -void wifi_deinit_ap(void); - -//function to connect to existing wifi network -void wifi_init_client(void); -//function to disable/deinit client -void wifi_deinit_client(void); - - - diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index a49da26..a00b27e 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -4,6 +4,9 @@ idf_component_register( "buzzer.cpp" "uart_common.cpp" "types.cpp" + "motordrivers.cpp" + "motorctl.cpp" + "currentsensor.cpp" INCLUDE_DIRS "." PRIV_REQUIRES nvs_flash diff --git a/board_motorctl/main/currentsensor.cpp b/common/currentsensor.cpp similarity index 100% rename from board_motorctl/main/currentsensor.cpp rename to common/currentsensor.cpp diff --git a/board_motorctl/main/currentsensor.hpp b/common/currentsensor.hpp similarity index 100% rename from board_motorctl/main/currentsensor.hpp rename to common/currentsensor.hpp diff --git a/board_motorctl/main/motorctl.cpp b/common/motorctl.cpp similarity index 100% rename from board_motorctl/main/motorctl.cpp rename to common/motorctl.cpp diff --git a/board_motorctl/main/motorctl.hpp b/common/motorctl.hpp similarity index 100% rename from board_motorctl/main/motorctl.hpp rename to common/motorctl.hpp diff --git a/board_motorctl/main/motordrivers.cpp b/common/motordrivers.cpp similarity index 100% rename from board_motorctl/main/motordrivers.cpp rename to common/motordrivers.cpp diff --git a/board_motorctl/main/motordrivers.hpp b/common/motordrivers.hpp similarity index 100% rename from board_motorctl/main/motordrivers.hpp rename to common/motordrivers.hpp