2 boards: split code, outsource common, remove duplicate files

- outsoruce common files and separate common types from source files (new
  common/types.hpp)
- split source files to 2 board folders (relevant only, no duplicate)
- currently boards compile successfull but not functional at all
- comment out currently incompatible code
This commit is contained in:
jonny_jr9
2023-08-29 08:50:49 +02:00
parent 8ef71082dc
commit 76e8bac113
53 changed files with 916 additions and 33817 deletions

View File

@@ -1,18 +1,12 @@
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"
"uart.cpp"
INCLUDE_DIRS
"."

View File

@@ -14,75 +14,76 @@ automatedArmchair::automatedArmchair(void) {
}
//FIXME motorLeft, Right not available, needs rework to work with uart only
//==============================
//====== generateCommands ======
//==============================
motorCommands_t automatedArmchair::generateCommands(auto_instruction_t * instruction) {
//reset instruction
*instruction = auto_instruction_t::NONE;
//check if previous command is finished
if ( esp_log_timestamp() > timestampCmdFinished ) {
//get next command from queue
if( xQueueReceive( commandQueue, &cmdCurrent, pdMS_TO_TICKS(500) ) ) {
ESP_LOGI(TAG, "running next command from queue...");
//copy instruction to be provided to control task
*instruction = cmdCurrent.instruction;
//set acceleration / fading parameters according to command
motorLeft.setFade(fadeType_t::DECEL, cmdCurrent.fadeDecel);
motorRight.setFade(fadeType_t::DECEL, cmdCurrent.fadeDecel);
motorLeft.setFade(fadeType_t::ACCEL, cmdCurrent.fadeAccel);
motorRight.setFade(fadeType_t::ACCEL, cmdCurrent.fadeAccel);
//calculate timestamp the command is finished
timestampCmdFinished = esp_log_timestamp() + cmdCurrent.msDuration;
//copy the new commands
motorCommands = cmdCurrent.motorCmds;
} else { //queue empty
ESP_LOGD(TAG, "no new command in queue -> set motors to IDLE");
motorCommands = motorCmds_bothMotorsIdle;
}
} else { //previous command still running
ESP_LOGD(TAG, "command still running -> no change");
}
//TODO also return instructions via call by reference
return motorCommands;
}
//============================
//======== addCommand ========
//============================
//function that adds a basic command to the queue
void automatedArmchair::addCommand(commandSimple_t command) {
//add command to queue
if ( xQueueSend( commandQueue, ( void * )&command, ( TickType_t ) 0 ) ){
ESP_LOGI(TAG, "Successfully inserted command to queue");
} else {
ESP_LOGE(TAG, "Failed to insert new command to queue");
}
}
void automatedArmchair::addCommands(commandSimple_t commands[], size_t count) {
for (int i = 0; i < count; i++) {
ESP_LOGI(TAG, "Reading command no. %d from provided array", i);
addCommand(commands[i]);
}
}
//===============================
//======== clearCommands ========
//===============================
//function that deletes all pending/queued commands
//e.g. when switching modes
motorCommands_t automatedArmchair::clearCommands() {
//clear command queue
xQueueReset( commandQueue );
ESP_LOGW(TAG, "command queue was successfully emptied");
//return commands for idling both motors
motorCommands = motorCmds_bothMotorsIdle;
return motorCmds_bothMotorsIdle;
}
// //==============================
// //====== generateCommands ======
// //==============================
// motorCommands_t automatedArmchair::generateCommands(auto_instruction_t * instruction) {
// //reset instruction
// *instruction = auto_instruction_t::NONE;
// //check if previous command is finished
// if ( esp_log_timestamp() > timestampCmdFinished ) {
// //get next command from queue
// if( xQueueReceive( commandQueue, &cmdCurrent, pdMS_TO_TICKS(500) ) ) {
// ESP_LOGI(TAG, "running next command from queue...");
// //copy instruction to be provided to control task
// *instruction = cmdCurrent.instruction;
// //set acceleration / fading parameters according to command
// motorLeft.setFade(fadeType_t::DECEL, cmdCurrent.fadeDecel);
// motorRight.setFade(fadeType_t::DECEL, cmdCurrent.fadeDecel);
// motorLeft.setFade(fadeType_t::ACCEL, cmdCurrent.fadeAccel);
// motorRight.setFade(fadeType_t::ACCEL, cmdCurrent.fadeAccel);
// //calculate timestamp the command is finished
// timestampCmdFinished = esp_log_timestamp() + cmdCurrent.msDuration;
// //copy the new commands
// motorCommands = cmdCurrent.motorCmds;
// } else { //queue empty
// ESP_LOGD(TAG, "no new command in queue -> set motors to IDLE");
// motorCommands = motorCmds_bothMotorsIdle;
// }
// } else { //previous command still running
// ESP_LOGD(TAG, "command still running -> no change");
// }
//
// //TODO also return instructions via call by reference
// return motorCommands;
// }
//
//
//
// //============================
// //======== addCommand ========
// //============================
// //function that adds a basic command to the queue
// void automatedArmchair::addCommand(commandSimple_t command) {
// //add command to queue
// if ( xQueueSend( commandQueue, ( void * )&command, ( TickType_t ) 0 ) ){
// ESP_LOGI(TAG, "Successfully inserted command to queue");
// } else {
// ESP_LOGE(TAG, "Failed to insert new command to queue");
// }
// }
//
// void automatedArmchair::addCommands(commandSimple_t commands[], size_t count) {
// for (int i = 0; i < count; i++) {
// ESP_LOGI(TAG, "Reading command no. %d from provided array", i);
// addCommand(commands[i]);
// }
// }
//
//
// //===============================
// //======== clearCommands ========
// //===============================
// //function that deletes all pending/queued commands
// //e.g. when switching modes
// motorCommands_t automatedArmchair::clearCommands() {
// //clear command queue
// xQueueReset( commandQueue );
// ESP_LOGW(TAG, "command queue was successfully emptied");
// //return commands for idling both motors
// motorCommands = motorCmds_bothMotorsIdle;
// return motorCmds_bothMotorsIdle;
// }
//

View File

@@ -10,7 +10,7 @@ extern "C"
#include "freertos/queue.h"
#include <cmath>
#include "motorctl.hpp"
#include "types.hpp"

View File

@@ -16,200 +16,201 @@ static const char * TAG = "button";
//-----------------------------
//-------- constructor --------
//-----------------------------
buttonCommands::buttonCommands(gpio_evaluatedSwitch * button_f, evaluatedJoystick * joystick_f, controlledArmchair * control_f, buzzer_t * buzzer_f, controlledMotor * motorLeft_f, controlledMotor * motorRight_f){
//copy object pointers
button = button_f;
joystick = joystick_f;
control = control_f;
buzzer = buzzer_f;
motorLeft = motorLeft_f;
motorRight = motorRight_f;
//TODO declare / configure evaluatedSwitch here instead of config (unnecessary that button object is globally available - only used here)?
}
//----------------------------
//--------- action -----------
//----------------------------
//function that runs commands depending on a count value
void buttonCommands::action (uint8_t count, bool lastPressLong){
//--- variable declarations ---
bool decelEnabled; //for different beeping when toggling
commandSimple_t cmds[8]; //array for commands for automatedArmchair
//--- get joystick position ---
//joystickData_t stickData = joystick->getData();
//--- actions based on count ---
switch (count){
//no such command
default:
ESP_LOGE(TAG, "no command for count=%d defined", count);
buzzer->beep(3, 400, 100);
break;
case 1:
//restart contoller when 1x long pressed
if (lastPressLong){
ESP_LOGW(TAG, "RESTART");
buzzer->beep(1,1000,1);
vTaskDelay(1000 / portTICK_PERIOD_MS);
esp_restart();
return;
}
ESP_LOGW(TAG, "cmd %d: sending button event to control task", count);
//-> define joystick center or toggle freeze input (executed in control task)
control->sendButtonEvent(count); //TODO: always send button event to control task (not just at count=1) -> control.cpp has to be changed
break;
case 2:
//run automatic commands to lift leg support when pressed 1x short 1x long
if (lastPressLong){
//define commands
cmds[0] =
{
.motorCmds = {
.left = {motorstate_t::REV, 90},
.right = {motorstate_t::REV, 90}
},
.msDuration = 1200,
.fadeDecel = 800,
.fadeAccel = 1300,
.instruction = auto_instruction_t::NONE
};
cmds[1] =
{
.motorCmds = {
.left = {motorstate_t::FWD, 70},
.right = {motorstate_t::FWD, 70}
},
.msDuration = 70,
.fadeDecel = 0,
.fadeAccel = 300,
.instruction = auto_instruction_t::NONE
};
cmds[2] =
{
.motorCmds = {
.left = {motorstate_t::IDLE, 0},
.right = {motorstate_t::IDLE, 0}
},
.msDuration = 10,
.fadeDecel = 800,
.fadeAccel = 1300,
.instruction = auto_instruction_t::SWITCH_JOYSTICK_MODE
};
//send commands to automatedArmchair command queue
armchair.addCommands(cmds, 3);
//change mode to AUTO
control->changeMode(controlMode_t::AUTO);
return;
}
//toggle idle when 2x pressed
ESP_LOGW(TAG, "cmd %d: toggle IDLE", count);
control->toggleIdle(); //toggle between idle and previous/default mode
break;
case 3:
ESP_LOGW(TAG, "cmd %d: switch to JOYSTICK", count);
control->changeMode(controlMode_t::JOYSTICK); //switch to JOYSTICK mode
break;
case 4:
ESP_LOGW(TAG, "cmd %d: toggle between HTTP and JOYSTICK", count);
control->toggleModes(controlMode_t::HTTP, controlMode_t::JOYSTICK); //toggle between HTTP and JOYSTICK mode
break;
case 6:
ESP_LOGW(TAG, "cmd %d: toggle between MASSAGE and JOYSTICK", count);
control->toggleModes(controlMode_t::MASSAGE, controlMode_t::JOYSTICK); //toggle between MASSAGE and JOYSTICK mode
break;
case 8:
//toggle deceleration fading between on and off
decelEnabled = motorLeft->toggleFade(fadeType_t::DECEL);
motorRight->toggleFade(fadeType_t::DECEL);
ESP_LOGW(TAG, "cmd %d: toggle deceleration fading to: %d", count, (int)decelEnabled);
if (decelEnabled){
buzzer->beep(3, 60, 50);
} else {
buzzer->beep(1, 1000, 1);
}
break;
case 12:
ESP_LOGW(TAG, "cmd %d: sending button event to control task", count);
//-> toggle altStickMapping (executed in control task)
control->sendButtonEvent(count); //TODO: always send button event to control task (not just at count=1)?
break;
}
}
//-----------------------------
//------ startHandleLoop ------
//-----------------------------
//this function has to be started once in a separate task
//repeatedly evaluates and processes button events then takes the corresponding action
void buttonCommands::startHandleLoop() {
while(1) {
vTaskDelay(20 / portTICK_PERIOD_MS);
//run handle function of evaluatedSwitch object
button->handle();
//--- count button presses and run action ---
switch(state) {
case inputState_t::IDLE: //wait for initial button press
if (button->risingEdge) {
count = 1;
buzzer->beep(1, 65, 0);
timestamp_lastAction = esp_log_timestamp();
state = inputState_t::WAIT_FOR_INPUT;
ESP_LOGI(TAG, "first button press detected -> waiting for further events");
}
break;
case inputState_t::WAIT_FOR_INPUT: //wait for further presses
//button pressed again
if (button->risingEdge){
count++;
buzzer->beep(1, 65, 0);
timestamp_lastAction = esp_log_timestamp();
ESP_LOGI(TAG, "another press detected -> count=%d -> waiting for further events", count);
}
//timeout
else if (esp_log_timestamp() - timestamp_lastAction > 1000) {
state = inputState_t::IDLE;
buzzer->beep(count, 50, 50);
//TODO: add optional "bool wait" parameter to beep function to delay until finished beeping
ESP_LOGI(TAG, "timeout - running action function for count=%d", count);
//--- run action function ---
//check if still pressed
bool lastPressLong = false;
if (button->state == true){
//run special case when last press was longer than timeout
lastPressLong = true;
}
//run action function with current count of button presses
action(count, lastPressLong);
}
break;
}
}
}
//FIXME needs rework, motorleft/right objectn not available anymore
// //-----------------------------
// //-------- constructor --------
// //-----------------------------
// buttonCommands::buttonCommands(gpio_evaluatedSwitch * button_f, evaluatedJoystick * joystick_f, controlledArmchair * control_f, buzzer_t * buzzer_f, controlledMotor * motorLeft_f, controlledMotor * motorRight_f){
// //copy object pointers
// button = button_f;
// joystick = joystick_f;
// control = control_f;
// buzzer = buzzer_f;
// motorLeft = motorLeft_f;
// motorRight = motorRight_f;
// //TODO declare / configure evaluatedSwitch here instead of config (unnecessary that button object is globally available - only used here)?
// }
//
//
//
// //----------------------------
// //--------- action -----------
// //----------------------------
// //function that runs commands depending on a count value
// void buttonCommands::action (uint8_t count, bool lastPressLong){
// //--- variable declarations ---
// bool decelEnabled; //for different beeping when toggling
// commandSimple_t cmds[8]; //array for commands for automatedArmchair
//
// //--- get joystick position ---
// //joystickData_t stickData = joystick->getData();
//
// //--- actions based on count ---
// switch (count){
// //no such command
// default:
// ESP_LOGE(TAG, "no command for count=%d defined", count);
// buzzer->beep(3, 400, 100);
// break;
//
// case 1:
// //restart contoller when 1x long pressed
// if (lastPressLong){
// ESP_LOGW(TAG, "RESTART");
// buzzer->beep(1,1000,1);
// vTaskDelay(1000 / portTICK_PERIOD_MS);
// esp_restart();
// return;
// }
//
// ESP_LOGW(TAG, "cmd %d: sending button event to control task", count);
// //-> define joystick center or toggle freeze input (executed in control task)
// control->sendButtonEvent(count); //TODO: always send button event to control task (not just at count=1) -> control.cpp has to be changed
// break;
// case 2:
// //run automatic commands to lift leg support when pressed 1x short 1x long
// if (lastPressLong){
// //define commands
// cmds[0] =
// {
// .motorCmds = {
// .left = {motorstate_t::REV, 90},
// .right = {motorstate_t::REV, 90}
// },
// .msDuration = 1200,
// .fadeDecel = 800,
// .fadeAccel = 1300,
// .instruction = auto_instruction_t::NONE
// };
// cmds[1] =
// {
// .motorCmds = {
// .left = {motorstate_t::FWD, 70},
// .right = {motorstate_t::FWD, 70}
// },
// .msDuration = 70,
// .fadeDecel = 0,
// .fadeAccel = 300,
// .instruction = auto_instruction_t::NONE
// };
// cmds[2] =
// {
// .motorCmds = {
// .left = {motorstate_t::IDLE, 0},
// .right = {motorstate_t::IDLE, 0}
// },
// .msDuration = 10,
// .fadeDecel = 800,
// .fadeAccel = 1300,
// .instruction = auto_instruction_t::SWITCH_JOYSTICK_MODE
// };
//
// //send commands to automatedArmchair command queue
// armchair.addCommands(cmds, 3);
//
// //change mode to AUTO
// control->changeMode(controlMode_t::AUTO);
// return;
// }
//
// //toggle idle when 2x pressed
// ESP_LOGW(TAG, "cmd %d: toggle IDLE", count);
// control->toggleIdle(); //toggle between idle and previous/default mode
// break;
//
//
// case 3:
// ESP_LOGW(TAG, "cmd %d: switch to JOYSTICK", count);
// control->changeMode(controlMode_t::JOYSTICK); //switch to JOYSTICK mode
// break;
//
// case 4:
// ESP_LOGW(TAG, "cmd %d: toggle between HTTP and JOYSTICK", count);
// control->toggleModes(controlMode_t::HTTP, controlMode_t::JOYSTICK); //toggle between HTTP and JOYSTICK mode
// break;
//
// case 6:
// ESP_LOGW(TAG, "cmd %d: toggle between MASSAGE and JOYSTICK", count);
// control->toggleModes(controlMode_t::MASSAGE, controlMode_t::JOYSTICK); //toggle between MASSAGE and JOYSTICK mode
// break;
//
// case 8:
// //toggle deceleration fading between on and off
// decelEnabled = motorLeft->toggleFade(fadeType_t::DECEL);
// motorRight->toggleFade(fadeType_t::DECEL);
// ESP_LOGW(TAG, "cmd %d: toggle deceleration fading to: %d", count, (int)decelEnabled);
// if (decelEnabled){
// buzzer->beep(3, 60, 50);
// } else {
// buzzer->beep(1, 1000, 1);
// }
// break;
//
// case 12:
// ESP_LOGW(TAG, "cmd %d: sending button event to control task", count);
// //-> toggle altStickMapping (executed in control task)
// control->sendButtonEvent(count); //TODO: always send button event to control task (not just at count=1)?
// break;
//
// }
// }
//
//
//
//
// //-----------------------------
// //------ startHandleLoop ------
// //-----------------------------
// //this function has to be started once in a separate task
// //repeatedly evaluates and processes button events then takes the corresponding action
// void buttonCommands::startHandleLoop() {
//
// while(1) {
// vTaskDelay(20 / portTICK_PERIOD_MS);
// //run handle function of evaluatedSwitch object
// button->handle();
//
// //--- count button presses and run action ---
// switch(state) {
// case inputState_t::IDLE: //wait for initial button press
// if (button->risingEdge) {
// count = 1;
// buzzer->beep(1, 65, 0);
// timestamp_lastAction = esp_log_timestamp();
// state = inputState_t::WAIT_FOR_INPUT;
// ESP_LOGI(TAG, "first button press detected -> waiting for further events");
// }
// break;
//
// case inputState_t::WAIT_FOR_INPUT: //wait for further presses
// //button pressed again
// if (button->risingEdge){
// count++;
// buzzer->beep(1, 65, 0);
// timestamp_lastAction = esp_log_timestamp();
// ESP_LOGI(TAG, "another press detected -> count=%d -> waiting for further events", count);
// }
// //timeout
// else if (esp_log_timestamp() - timestamp_lastAction > 1000) {
// state = inputState_t::IDLE;
// buzzer->beep(count, 50, 50);
// //TODO: add optional "bool wait" parameter to beep function to delay until finished beeping
// ESP_LOGI(TAG, "timeout - running action function for count=%d", count);
// //--- run action function ---
// //check if still pressed
// bool lastPressLong = false;
// if (button->state == true){
// //run special case when last press was longer than timeout
// lastPressLong = true;
// }
// //run action function with current count of button presses
// action(count, lastPressLong);
// }
// break;
// }
// }
// }
//
//
//
//

View File

@@ -3,51 +3,52 @@
#include "gpio_evaluateSwitch.hpp"
#include "buzzer.hpp"
#include "control.hpp"
#include "motorctl.hpp"
#include "auto.hpp"
#include "config.hpp"
#include "joystick.hpp"
#include "types.hpp"
//===================================
//====== buttonCommands class =======
//===================================
//class which runs commands depending on the count a button was pressed
class buttonCommands {
public:
//--- constructor ---
buttonCommands (
gpio_evaluatedSwitch * button_f,
evaluatedJoystick * joystick_f,
controlledArmchair * control_f,
buzzer_t * buzzer_f,
controlledMotor * motorLeft_f,
controlledMotor * motorRight_f
);
//--- functions ---
//the following function has to be started once in a separate task.
//repeatedly evaluates and processes button events then takes the corresponding action
void startHandleLoop();
private:
//--- functions ---
void action(uint8_t count, bool lastPressLong);
//--- objects ---
gpio_evaluatedSwitch* button;
evaluatedJoystick* joystick;
controlledArmchair * control;
buzzer_t* buzzer;
controlledMotor * motorLeft;
controlledMotor * motorRight;
//--- variables ---
uint8_t count = 0;
uint32_t timestamp_lastAction = 0;
enum class inputState_t {IDLE, WAIT_FOR_INPUT};
inputState_t state = inputState_t::IDLE;
};
// //===================================
// //====== buttonCommands class =======
// //===================================
// //class which runs commands depending on the count a button was pressed
// class buttonCommands {
// public:
// //--- constructor ---
// buttonCommands (
// gpio_evaluatedSwitch * button_f,
// evaluatedJoystick * joystick_f,
// controlledArmchair * control_f,
// buzzer_t * buzzer_f,
// controlledMotor * motorLeft_f,
// controlledMotor * motorRight_f
// );
//
// //--- functions ---
// //the following function has to be started once in a separate task.
// //repeatedly evaluates and processes button events then takes the corresponding action
// void startHandleLoop();
//
// private:
// //--- functions ---
// void action(uint8_t count, bool lastPressLong);
//
// //--- objects ---
// gpio_evaluatedSwitch* button;
// evaluatedJoystick* joystick;
// controlledArmchair * control;
// buzzer_t* buzzer;
// controlledMotor * motorLeft;
// controlledMotor * motorRight;
//
// //--- variables ---
// uint8_t count = 0;
// uint32_t timestamp_lastAction = 0;
// enum class inputState_t {IDLE, WAIT_FOR_INPUT};
// inputState_t state = inputState_t::IDLE;
//
// };

View File

@@ -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);
}
}
}

View File

@@ -1,56 +0,0 @@
#pragma once
#include <stdio.h>
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;
};

View File

@@ -1,60 +1,5 @@
#include "config.hpp"
//===================================
//======= motor configuration =======
//===================================
//--- configure left motor (hardware) ---
single100a_config_t configDriverLeft = {
.gpio_pwm = GPIO_NUM_26,
.gpio_a = GPIO_NUM_16,
.gpio_b = GPIO_NUM_4,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.aEnabledPinState = false, //-> pins inverted (mosfets)
.bEnabledPinState = false,
.resolution = LEDC_TIMER_11_BIT,
.pwmFreq = 10000
};
//--- configure right motor (hardware) ---
single100a_config_t configDriverRight = {
.gpio_pwm = GPIO_NUM_27,
.gpio_a = GPIO_NUM_2,
.gpio_b = GPIO_NUM_14,
.ledc_timer = LEDC_TIMER_1,
.ledc_channel = LEDC_CHANNEL_1,
.aEnabledPinState = false, //-> pin inverted (mosfet)
.bEnabledPinState = true, //-> not inverted (direct)
.resolution = LEDC_TIMER_11_BIT,
.pwmFreq = 10000
};
//TODO add motor name string -> then use as log tag?
//--- configure left motor (contol) ---
motorctl_config_t configMotorControlLeft = {
.msFadeAccel = 1900, //acceleration of the motor (ms it takes from 0% to 100%)
.msFadeDecel = 1000, //deceleration of the motor (ms it takes from 100% to 0%)
.currentLimitEnabled = true,
.currentSensor_adc = ADC1_CHANNEL_6, //GPIO34
.currentSensor_ratedCurrent = 50,
.currentMax = 30,
.deadTimeMs = 900 //minimum time motor is off between direction change
};
//--- configure right motor (contol) ---
motorctl_config_t configMotorControlRight = {
.msFadeAccel = 1900, //acceleration of the motor (ms it takes from 0% to 100%)
.msFadeDecel = 1000, //deceleration of the motor (ms it takes from 100% to 0%)
.currentLimitEnabled = true,
.currentSensor_adc = ADC1_CHANNEL_4, //GPIO32
.currentSensor_ratedCurrent = 50,
.currentMax = 30,
.deadTimeMs = 900 //minimum time motor is off between direction change
};
//==============================
//======= control config =======
//==============================
@@ -107,29 +52,12 @@ joystick_config_t configJoystick = {
//============================
//=== configure fan contol ===
//============================
fan_config_t configCooling = {
.gpio_fan = GPIO_NUM_13,
.dutyThreshold = 40,
.minOnMs = 1500,
.minOffMs = 3000,
.turnOffDelayMs = 5000,
};
//=================================
//===== create global objects =====
//=================================
//TODO outsource global variables to e.g. global.cpp and only config options here?
//create controlled motor instances (motorctl.hpp)
controlledMotor motorLeft(configDriverLeft, configMotorControlLeft);
controlledMotor motorRight(configDriverRight, configMotorControlRight);
//create global joystic instance (joystick.hpp)
evaluatedJoystick joystick(configJoystick);
@@ -143,7 +71,7 @@ buzzer_t buzzer(GPIO_NUM_12, 100);
httpJoystick httpJoystickMain(configHttpJoystickMain);
//create global control object (control.hpp)
controlledArmchair control(configControl, &buzzer, &motorLeft, &motorRight, &joystick, &httpJoystickMain);
//controlledArmchair control(configControl, &buzzer, &motorLeft, &motorRight, &joystick, &httpJoystickMain);
//create global automatedArmchair object (for auto-mode) (auto.hpp)
automatedArmchair armchair;

View File

@@ -1,13 +1,10 @@
#pragma once
#include "motordrivers.hpp"
#include "motorctl.hpp"
#include "joystick.hpp"
#include "gpio_evaluateSwitch.hpp"
#include "buzzer.hpp"
#include "control.hpp"
#include "fan.hpp"
#include "http.hpp"
#include "auto.hpp"
@@ -19,10 +16,6 @@
//TODO outsource global variables to e.g. global.cpp and only config options here?
//create global controlledMotor instances for both motors
extern controlledMotor motorLeft;
extern controlledMotor motorRight;
//create global joystic instance
extern evaluatedJoystick joystick;
@@ -33,14 +26,10 @@ extern gpio_evaluatedSwitch buttonJoystick;
extern buzzer_t buzzer;
//create global control object
extern controlledArmchair control;
//extern controlledArmchair control;
//create global automatedArmchair object (for auto-mode)
extern automatedArmchair armchair;
//create global httpJoystick object
extern httpJoystick httpJoystickMain;
//configuration for fans / cooling
extern fan_config_t configCooling;

View File

@@ -22,458 +22,459 @@ static const char * TAG = "control";
const char* controlModeStr[7] = {"IDLE", "JOYSTICK", "MASSAGE", "HTTP", "MQTT", "BLUETOOTH", "AUTO"};
//-----------------------------
//-------- constructor --------
//-----------------------------
controlledArmchair::controlledArmchair (
control_config_t config_f,
buzzer_t * buzzer_f,
controlledMotor* motorLeft_f,
controlledMotor* motorRight_f,
evaluatedJoystick* joystick_f,
httpJoystick* httpJoystick_f
){
//copy configuration
config = config_f;
//copy object pointers
buzzer = buzzer_f;
motorLeft = motorLeft_f;
motorRight = motorRight_f;
joystick_l = joystick_f,
httpJoystickMain_l = httpJoystick_f;
//set default mode from config
modePrevious = config.defaultMode;
//TODO declare / configure controlled motors here instead of config (unnecessary that button object is globally available - only used here)?
}
//----------------------------------
//---------- Handle loop -----------
//----------------------------------
//function that repeatedly generates motor commands depending on the current mode
//also handles fading and current-limit
void controlledArmchair::startHandleLoop() {
while (1){
ESP_LOGV(TAG, "control task executing... mode=%s", controlModeStr[(int)mode]);
switch(mode) {
default:
mode = controlMode_t::IDLE;
break;
case controlMode_t::IDLE:
//copy preset commands for idling both motors
commands = cmds_bothMotorsIdle;
motorRight->setTarget(commands.right.state, commands.right.duty);
motorLeft->setTarget(commands.left.state, commands.left.duty);
vTaskDelay(200 / portTICK_PERIOD_MS);
#ifdef JOYSTICK_LOG_IN_IDLE
//get joystick data here (without using it)
//since loglevel is DEBUG, calculateion details is output
joystick_l->getData(); //get joystick data here
#endif
break;
case controlMode_t::JOYSTICK:
vTaskDelay(20 / portTICK_PERIOD_MS);
//get current joystick data with getData method of evaluatedJoystick
stickData = joystick_l->getData();
//additionaly scale coordinates (more detail in slower area)
joystick_scaleCoordinatesLinear(&stickData, 0.6, 0.35); //TODO: add scaling parameters to config
//generate motor commands
commands = joystick_generateCommandsDriving(stickData, altStickMapping);
//apply motor commands
motorRight->setTarget(commands.right.state, commands.right.duty);
motorLeft->setTarget(commands.left.state, commands.left.duty);
//TODO make motorctl.setTarget also accept motorcommand struct directly
break;
case controlMode_t::MASSAGE:
vTaskDelay(10 / portTICK_PERIOD_MS);
//--- read joystick ---
//only update joystick data when input not frozen
if (!freezeInput){
stickData = joystick_l->getData();
}
//--- generate motor commands ---
//pass joystick data from getData method of evaluatedJoystick to generateCommandsShaking function
commands = joystick_generateCommandsShaking(stickData);
//apply motor commands
motorRight->setTarget(commands.right.state, commands.right.duty);
motorLeft->setTarget(commands.left.state, commands.left.duty);
break;
case controlMode_t::HTTP:
//--- get joystick data from queue ---
//Note this function waits several seconds (httpconfig.timeoutMs) for data to arrive, otherwise Center data or NULL is returned
//TODO: as described above, when changing modes it might delay a few seconds for the change to apply
stickData = httpJoystickMain_l->getData();
//scale coordinates additionally (more detail in slower area)
joystick_scaleCoordinatesLinear(&stickData, 0.6, 0.4); //TODO: add scaling parameters to config
ESP_LOGD(TAG, "generating commands from x=%.3f y=%.3f radius=%.3f angle=%.3f", stickData.x, stickData.y, stickData.radius, stickData.angle);
//--- generate motor commands ---
//Note: timeout (no data received) is handled in getData method
commands = joystick_generateCommandsDriving(stickData, altStickMapping);
//--- apply commands to motors ---
//TODO make motorctl.setTarget also accept motorcommand struct directly
motorRight->setTarget(commands.right.state, commands.right.duty);
motorLeft->setTarget(commands.left.state, commands.left.duty);
break;
case controlMode_t::AUTO:
vTaskDelay(20 / portTICK_PERIOD_MS);
//generate commands
commands = armchair.generateCommands(&instruction);
//--- apply commands to motors ---
//TODO make motorctl.setTarget also accept motorcommand struct directly
motorRight->setTarget(commands.right.state, commands.right.duty);
motorLeft->setTarget(commands.left.state, commands.left.duty);
//process received instruction
switch (instruction) {
case auto_instruction_t::NONE:
break;
case auto_instruction_t::SWITCH_PREV_MODE:
toggleMode(controlMode_t::AUTO);
break;
case auto_instruction_t::SWITCH_JOYSTICK_MODE:
changeMode(controlMode_t::JOYSTICK);
break;
case auto_instruction_t::RESET_ACCEL_DECEL:
//enable downfading (set to default value)
motorLeft->setFade(fadeType_t::DECEL, true);
motorRight->setFade(fadeType_t::DECEL, true);
//set upfading to default value
motorLeft->setFade(fadeType_t::ACCEL, true);
motorRight->setFade(fadeType_t::ACCEL, true);
break;
case auto_instruction_t::RESET_ACCEL:
//set upfading to default value
motorLeft->setFade(fadeType_t::ACCEL, true);
motorRight->setFade(fadeType_t::ACCEL, true);
break;
case auto_instruction_t::RESET_DECEL:
//enable downfading (set to default value)
motorLeft->setFade(fadeType_t::DECEL, true);
motorRight->setFade(fadeType_t::DECEL, true);
break;
}
break;
//TODO: add other modes here
}
//--- run actions based on received button button event ---
//note: buttonCount received by sendButtonEvent method called from button.cpp
//TODO: what if variable gets set from other task during this code? -> mutex around this code
switch (buttonCount) {
case 1: //define joystick center or freeze input
if (mode == controlMode_t::JOYSTICK){
//joystick mode: calibrate joystick
joystick_l->defineCenter();
} else if (mode == controlMode_t::MASSAGE){
//massage mode: toggle freeze of input (lock joystick at current values)
freezeInput = !freezeInput;
if (freezeInput){
buzzer->beep(5, 40, 25);
} else {
buzzer->beep(1, 300, 100);
}
}
break;
case 12: //toggle alternative joystick mapping (reverse swapped)
altStickMapping = !altStickMapping;
if (altStickMapping){
buzzer->beep(6, 70, 50);
} else {
buzzer->beep(1, 500, 100);
}
break;
}
//--- reset button event --- (only one action per run)
if (buttonCount > 0){
ESP_LOGI(TAG, "resetting button event/count");
buttonCount = 0;
}
//-----------------------
//------ slow loop ------
//-----------------------
//this section is run about every 5s (+500ms)
if (esp_log_timestamp() - timestamp_SlowLoopLastRun > 5000) {
ESP_LOGV(TAG, "running slow loop... time since last run: %.1fs", (float)(esp_log_timestamp() - timestamp_SlowLoopLastRun)/1000);
timestamp_SlowLoopLastRun = esp_log_timestamp();
//run function which detects timeout (switch to idle)
handleTimeout();
}
}//end while(1)
}//end startHandleLoop
//-----------------------------------
//---------- resetTimeout -----------
//-----------------------------------
void controlledArmchair::resetTimeout(){
//TODO mutex
timestamp_lastActivity = esp_log_timestamp();
}
//------------------------------------
//--------- sendButtonEvent ----------
//------------------------------------
void controlledArmchair::sendButtonEvent(uint8_t count){
//TODO mutex - if not replaced with queue
ESP_LOGI(TAG, "setting button event");
buttonCount = count;
}
//------------------------------------
//---------- handleTimeout -----------
//------------------------------------
//percentage the duty can vary since last timeout check and still counts as incative
//TODO: add this to config
float inactivityTolerance = 10;
//local function that checks whether two values differ more than a given tolerance
bool validateActivity(float dutyOld, float dutyNow, float tolerance){
float dutyDelta = dutyNow - dutyOld;
if (fabs(dutyDelta) < tolerance) {
return false; //no significant activity detected
} else {
return true; //there was activity
}
}
//function that evaluates whether there is no activity/change on the motor duty for a certain time. If so, a switch to IDLE is issued. - has to be run repeatedly in a slow interval
void controlledArmchair::handleTimeout(){
//check for timeout only when not idling already
if (mode != controlMode_t::IDLE) {
//get current duty from controlled motor objects
float dutyLeftNow = motorLeft->getStatus().duty;
float dutyRightNow = motorRight->getStatus().duty;
//activity detected on any of the two motors
if (validateActivity(dutyLeft_lastActivity, dutyLeftNow, inactivityTolerance)
|| validateActivity(dutyRight_lastActivity, dutyRightNow, inactivityTolerance)
){
ESP_LOGD(TAG, "timeout check: [activity] detected since last check -> reset");
//reset last duty and timestamp
dutyLeft_lastActivity = dutyLeftNow;
dutyRight_lastActivity = dutyRightNow;
resetTimeout();
}
//no activity on any motor and msTimeout exceeded
else if (esp_log_timestamp() - timestamp_lastActivity > config.timeoutMs){
ESP_LOGI(TAG, "timeout check: [TIMEOUT], no activity for more than %.ds -> switch to idle", config.timeoutMs/1000);
//toggle to idle mode
toggleIdle();
}
else {
ESP_LOGD(TAG, "timeout check: [inactive], last activity %.1f s ago, timeout after %d s", (float)(esp_log_timestamp() - timestamp_lastActivity)/1000, config.timeoutMs/1000);
}
}
}
//-----------------------------------
//----------- changeMode ------------
//-----------------------------------
//function to change to a specified control mode
void controlledArmchair::changeMode(controlMode_t modeNew) {
//reset timeout timer
resetTimeout();
//exit if target mode is already active
if (mode == modeNew) {
ESP_LOGE(TAG, "changeMode: Already in target mode '%s' -> nothing to change", controlModeStr[(int)mode]);
return;
}
//copy previous mode
modePrevious = mode;
ESP_LOGW(TAG, "=== changing mode from %s to %s ===", controlModeStr[(int)mode], controlModeStr[(int)modeNew]);
//========== commands change FROM mode ==========
//run functions when changing FROM certain mode
switch(modePrevious){
default:
ESP_LOGI(TAG, "noting to execute when changing FROM this mode");
break;
#ifdef JOYSTICK_LOG_IN_IDLE
case controlMode_t::IDLE:
ESP_LOGI(TAG, "disabling debug output for 'evaluatedJoystick'");
esp_log_level_set("evaluatedJoystick", ESP_LOG_WARN); //FIXME: loglevel from config
break;
#endif
case controlMode_t::HTTP:
ESP_LOGW(TAG, "switching from http mode -> disabling http and wifi");
//stop http server
ESP_LOGI(TAG, "disabling http server...");
http_stop_server();
//FIXME: make wifi function work here - currently starting wifi at startup (see notes main.cpp)
//stop wifi
//TODO: decide whether ap or client is currently used - which has to be disabled?
//ESP_LOGI(TAG, "deinit wifi...");
//wifi_deinit_client();
//wifi_deinit_ap();
ESP_LOGI(TAG, "done stopping http mode");
break;
case controlMode_t::MASSAGE:
ESP_LOGW(TAG, "switching from MASSAGE mode -> restoring fading, reset frozen input");
//TODO: fix issue when downfading was disabled before switching to massage mode - currently it gets enabled again here...
//enable downfading (set to default value)
motorLeft->setFade(fadeType_t::DECEL, true);
motorRight->setFade(fadeType_t::DECEL, true);
//set upfading to default value
motorLeft->setFade(fadeType_t::ACCEL, true);
motorRight->setFade(fadeType_t::ACCEL, true);
//reset frozen input state
freezeInput = false;
break;
case controlMode_t::AUTO:
ESP_LOGW(TAG, "switching from AUTO mode -> restoring fading to default");
//TODO: fix issue when downfading was disabled before switching to auto mode - currently it gets enabled again here...
//enable downfading (set to default value)
motorLeft->setFade(fadeType_t::DECEL, true);
motorRight->setFade(fadeType_t::DECEL, true);
//set upfading to default value
motorLeft->setFade(fadeType_t::ACCEL, true);
motorRight->setFade(fadeType_t::ACCEL, true);
break;
}
//========== commands change TO mode ==========
//run functions when changing TO certain mode
switch(modeNew){
default:
ESP_LOGI(TAG, "noting to execute when changing TO this mode");
break;
case controlMode_t::IDLE:
buzzer->beep(1, 1500, 0);
#ifdef JOYSTICK_LOG_IN_IDLE
esp_log_level_set("evaluatedJoystick", ESP_LOG_DEBUG);
#endif
break;
case controlMode_t::HTTP:
ESP_LOGW(TAG, "switching to http mode -> enabling http and wifi");
//start wifi
//TODO: decide wether ap or client should be started
ESP_LOGI(TAG, "init wifi...");
//FIXME: make wifi function work here - currently starting wifi at startup (see notes main.cpp)
//wifi_init_client();
//wifi_init_ap();
//wait for wifi
//ESP_LOGI(TAG, "waiting for wifi...");
//vTaskDelay(1000 / portTICK_PERIOD_MS);
//start http server
ESP_LOGI(TAG, "init http server...");
http_init_server();
ESP_LOGI(TAG, "done initializing http mode");
break;
case controlMode_t::MASSAGE:
ESP_LOGW(TAG, "switching to MASSAGE mode -> reducing fading");
uint32_t shake_msFadeAccel = 500; //TODO: move this to config
//disable downfading (max. deceleration)
motorLeft->setFade(fadeType_t::DECEL, false);
motorRight->setFade(fadeType_t::DECEL, false);
//reduce upfading (increase acceleration)
motorLeft->setFade(fadeType_t::ACCEL, shake_msFadeAccel);
motorRight->setFade(fadeType_t::ACCEL, shake_msFadeAccel);
break;
}
//--- update mode to new mode ---
//TODO: add mutex
mode = modeNew;
}
//TODO simplify the following 3 functions? can be replaced by one?
//-----------------------------------
//----------- toggleIdle ------------
//-----------------------------------
//function to toggle between IDLE and previous active mode
void controlledArmchair::toggleIdle() {
//toggle between IDLE and previous mode
toggleMode(controlMode_t::IDLE);
}
//------------------------------------
//----------- toggleModes ------------
//------------------------------------
//function to toggle between two modes, but prefer first argument if entirely different mode is currently active
void controlledArmchair::toggleModes(controlMode_t modePrimary, controlMode_t modeSecondary) {
//switch to secondary mode when primary is already active
if (mode == modePrimary){
ESP_LOGW(TAG, "toggleModes: switching from primaryMode %s to secondarMode %s", controlModeStr[(int)mode], controlModeStr[(int)modeSecondary]);
buzzer->beep(2,200,100);
changeMode(modeSecondary); //switch to secondary mode
}
//switch to primary mode when any other mode is active
else {
ESP_LOGW(TAG, "toggleModes: switching from %s to primary mode %s", controlModeStr[(int)mode], controlModeStr[(int)modePrimary]);
buzzer->beep(4,200,100);
changeMode(modePrimary);
}
}
//-----------------------------------
//----------- toggleMode ------------
//-----------------------------------
//function that toggles between certain mode and previous mode
void controlledArmchair::toggleMode(controlMode_t modePrimary){
//switch to previous mode when primary is already active
if (mode == modePrimary){
ESP_LOGW(TAG, "toggleMode: switching from primaryMode %s to previousMode %s", controlModeStr[(int)mode], controlModeStr[(int)modePrevious]);
//buzzer->beep(2,200,100);
changeMode(modePrevious); //switch to previous mode
}
//switch to primary mode when any other mode is active
else {
ESP_LOGW(TAG, "toggleModes: switching from %s to primary mode %s", controlModeStr[(int)mode], controlModeStr[(int)modePrimary]);
//buzzer->beep(4,200,100);
changeMode(modePrimary);
}
}
//FIXME controlledMotor class not available for this pcb, rework
// //-----------------------------
// //-------- constructor --------
// //-----------------------------
// controlledArmchair::controlledArmchair (
// control_config_t config_f,
// buzzer_t * buzzer_f,
// controlledMotor* motorLeft_f,
// controlledMotor* motorRight_f,
// evaluatedJoystick* joystick_f,
// httpJoystick* httpJoystick_f
// ){
//
// //copy configuration
// config = config_f;
// //copy object pointers
// buzzer = buzzer_f;
// motorLeft = motorLeft_f;
// motorRight = motorRight_f;
// joystick_l = joystick_f,
// httpJoystickMain_l = httpJoystick_f;
// //set default mode from config
// modePrevious = config.defaultMode;
//
// //TODO declare / configure controlled motors here instead of config (unnecessary that button object is globally available - only used here)?
// }
//
//
//
// //----------------------------------
// //---------- Handle loop -----------
// //----------------------------------
// //function that repeatedly generates motor commands depending on the current mode
// //also handles fading and current-limit
// void controlledArmchair::startHandleLoop() {
// while (1){
// ESP_LOGV(TAG, "control task executing... mode=%s", controlModeStr[(int)mode]);
//
// switch(mode) {
// default:
// mode = controlMode_t::IDLE;
// break;
//
// case controlMode_t::IDLE:
// //copy preset commands for idling both motors
// commands = cmds_bothMotorsIdle;
// motorRight->setTarget(commands.right.state, commands.right.duty);
// motorLeft->setTarget(commands.left.state, commands.left.duty);
// vTaskDelay(200 / portTICK_PERIOD_MS);
// #ifdef JOYSTICK_LOG_IN_IDLE
// //get joystick data here (without using it)
// //since loglevel is DEBUG, calculateion details is output
// joystick_l->getData(); //get joystick data here
// #endif
// break;
//
//
// case controlMode_t::JOYSTICK:
// vTaskDelay(20 / portTICK_PERIOD_MS);
// //get current joystick data with getData method of evaluatedJoystick
// stickData = joystick_l->getData();
// //additionaly scale coordinates (more detail in slower area)
// joystick_scaleCoordinatesLinear(&stickData, 0.6, 0.35); //TODO: add scaling parameters to config
// //generate motor commands
// commands = joystick_generateCommandsDriving(stickData, altStickMapping);
// //apply motor commands
// motorRight->setTarget(commands.right.state, commands.right.duty);
// motorLeft->setTarget(commands.left.state, commands.left.duty);
// //TODO make motorctl.setTarget also accept motorcommand struct directly
// break;
//
//
// case controlMode_t::MASSAGE:
// vTaskDelay(10 / portTICK_PERIOD_MS);
// //--- read joystick ---
// //only update joystick data when input not frozen
// if (!freezeInput){
// stickData = joystick_l->getData();
// }
// //--- generate motor commands ---
// //pass joystick data from getData method of evaluatedJoystick to generateCommandsShaking function
// commands = joystick_generateCommandsShaking(stickData);
// //apply motor commands
// motorRight->setTarget(commands.right.state, commands.right.duty);
// motorLeft->setTarget(commands.left.state, commands.left.duty);
// break;
//
//
// case controlMode_t::HTTP:
// //--- get joystick data from queue ---
// //Note this function waits several seconds (httpconfig.timeoutMs) for data to arrive, otherwise Center data or NULL is returned
// //TODO: as described above, when changing modes it might delay a few seconds for the change to apply
// stickData = httpJoystickMain_l->getData();
// //scale coordinates additionally (more detail in slower area)
// joystick_scaleCoordinatesLinear(&stickData, 0.6, 0.4); //TODO: add scaling parameters to config
// ESP_LOGD(TAG, "generating commands from x=%.3f y=%.3f radius=%.3f angle=%.3f", stickData.x, stickData.y, stickData.radius, stickData.angle);
// //--- generate motor commands ---
// //Note: timeout (no data received) is handled in getData method
// commands = joystick_generateCommandsDriving(stickData, altStickMapping);
//
// //--- apply commands to motors ---
// //TODO make motorctl.setTarget also accept motorcommand struct directly
// motorRight->setTarget(commands.right.state, commands.right.duty);
// motorLeft->setTarget(commands.left.state, commands.left.duty);
// break;
//
//
// case controlMode_t::AUTO:
// vTaskDelay(20 / portTICK_PERIOD_MS);
// //generate commands
// commands = armchair.generateCommands(&instruction);
// //--- apply commands to motors ---
// //TODO make motorctl.setTarget also accept motorcommand struct directly
// motorRight->setTarget(commands.right.state, commands.right.duty);
// motorLeft->setTarget(commands.left.state, commands.left.duty);
//
// //process received instruction
// switch (instruction) {
// case auto_instruction_t::NONE:
// break;
// case auto_instruction_t::SWITCH_PREV_MODE:
// toggleMode(controlMode_t::AUTO);
// break;
// case auto_instruction_t::SWITCH_JOYSTICK_MODE:
// changeMode(controlMode_t::JOYSTICK);
// break;
// case auto_instruction_t::RESET_ACCEL_DECEL:
// //enable downfading (set to default value)
// motorLeft->setFade(fadeType_t::DECEL, true);
// motorRight->setFade(fadeType_t::DECEL, true);
// //set upfading to default value
// motorLeft->setFade(fadeType_t::ACCEL, true);
// motorRight->setFade(fadeType_t::ACCEL, true);
// break;
// case auto_instruction_t::RESET_ACCEL:
// //set upfading to default value
// motorLeft->setFade(fadeType_t::ACCEL, true);
// motorRight->setFade(fadeType_t::ACCEL, true);
// break;
// case auto_instruction_t::RESET_DECEL:
// //enable downfading (set to default value)
// motorLeft->setFade(fadeType_t::DECEL, true);
// motorRight->setFade(fadeType_t::DECEL, true);
// break;
// }
// break;
//
//
// //TODO: add other modes here
// }
//
//
// //--- run actions based on received button button event ---
// //note: buttonCount received by sendButtonEvent method called from button.cpp
// //TODO: what if variable gets set from other task during this code? -> mutex around this code
// switch (buttonCount) {
// case 1: //define joystick center or freeze input
// if (mode == controlMode_t::JOYSTICK){
// //joystick mode: calibrate joystick
// joystick_l->defineCenter();
// } else if (mode == controlMode_t::MASSAGE){
// //massage mode: toggle freeze of input (lock joystick at current values)
// freezeInput = !freezeInput;
// if (freezeInput){
// buzzer->beep(5, 40, 25);
// } else {
// buzzer->beep(1, 300, 100);
// }
// }
// break;
//
// case 12: //toggle alternative joystick mapping (reverse swapped)
// altStickMapping = !altStickMapping;
// if (altStickMapping){
// buzzer->beep(6, 70, 50);
// } else {
// buzzer->beep(1, 500, 100);
// }
// break;
// }
// //--- reset button event --- (only one action per run)
// if (buttonCount > 0){
// ESP_LOGI(TAG, "resetting button event/count");
// buttonCount = 0;
// }
//
//
//
// //-----------------------
// //------ slow loop ------
// //-----------------------
// //this section is run about every 5s (+500ms)
// if (esp_log_timestamp() - timestamp_SlowLoopLastRun > 5000) {
// ESP_LOGV(TAG, "running slow loop... time since last run: %.1fs", (float)(esp_log_timestamp() - timestamp_SlowLoopLastRun)/1000);
// timestamp_SlowLoopLastRun = esp_log_timestamp();
//
// //run function which detects timeout (switch to idle)
// handleTimeout();
// }
//
// }//end while(1)
// }//end startHandleLoop
//
//
//
// //-----------------------------------
// //---------- resetTimeout -----------
// //-----------------------------------
// void controlledArmchair::resetTimeout(){
// //TODO mutex
// timestamp_lastActivity = esp_log_timestamp();
// }
//
//
//
// //------------------------------------
// //--------- sendButtonEvent ----------
// //------------------------------------
// void controlledArmchair::sendButtonEvent(uint8_t count){
// //TODO mutex - if not replaced with queue
// ESP_LOGI(TAG, "setting button event");
// buttonCount = count;
// }
//
//
//
// //------------------------------------
// //---------- handleTimeout -----------
// //------------------------------------
// //percentage the duty can vary since last timeout check and still counts as incative
// //TODO: add this to config
// float inactivityTolerance = 10;
//
// //local function that checks whether two values differ more than a given tolerance
// bool validateActivity(float dutyOld, float dutyNow, float tolerance){
// float dutyDelta = dutyNow - dutyOld;
// if (fabs(dutyDelta) < tolerance) {
// return false; //no significant activity detected
// } else {
// return true; //there was activity
// }
// }
//
// //function that evaluates whether there is no activity/change on the motor duty for a certain time. If so, a switch to IDLE is issued. - has to be run repeatedly in a slow interval
// void controlledArmchair::handleTimeout(){
// //check for timeout only when not idling already
// if (mode != controlMode_t::IDLE) {
// //get current duty from controlled motor objects
// float dutyLeftNow = motorLeft->getStatus().duty;
// float dutyRightNow = motorRight->getStatus().duty;
//
// //activity detected on any of the two motors
// if (validateActivity(dutyLeft_lastActivity, dutyLeftNow, inactivityTolerance)
// || validateActivity(dutyRight_lastActivity, dutyRightNow, inactivityTolerance)
// ){
// ESP_LOGD(TAG, "timeout check: [activity] detected since last check -> reset");
// //reset last duty and timestamp
// dutyLeft_lastActivity = dutyLeftNow;
// dutyRight_lastActivity = dutyRightNow;
// resetTimeout();
// }
// //no activity on any motor and msTimeout exceeded
// else if (esp_log_timestamp() - timestamp_lastActivity > config.timeoutMs){
// ESP_LOGI(TAG, "timeout check: [TIMEOUT], no activity for more than %.ds -> switch to idle", config.timeoutMs/1000);
// //toggle to idle mode
// toggleIdle();
// }
// else {
// ESP_LOGD(TAG, "timeout check: [inactive], last activity %.1f s ago, timeout after %d s", (float)(esp_log_timestamp() - timestamp_lastActivity)/1000, config.timeoutMs/1000);
// }
// }
// }
//
//
//
// //-----------------------------------
// //----------- changeMode ------------
// //-----------------------------------
// //function to change to a specified control mode
// void controlledArmchair::changeMode(controlMode_t modeNew) {
// //reset timeout timer
// resetTimeout();
//
// //exit if target mode is already active
// if (mode == modeNew) {
// ESP_LOGE(TAG, "changeMode: Already in target mode '%s' -> nothing to change", controlModeStr[(int)mode]);
// return;
// }
//
// //copy previous mode
// modePrevious = mode;
//
// ESP_LOGW(TAG, "=== changing mode from %s to %s ===", controlModeStr[(int)mode], controlModeStr[(int)modeNew]);
//
// //========== commands change FROM mode ==========
// //run functions when changing FROM certain mode
// switch(modePrevious){
// default:
// ESP_LOGI(TAG, "noting to execute when changing FROM this mode");
// break;
//
// #ifdef JOYSTICK_LOG_IN_IDLE
// case controlMode_t::IDLE:
// ESP_LOGI(TAG, "disabling debug output for 'evaluatedJoystick'");
// esp_log_level_set("evaluatedJoystick", ESP_LOG_WARN); //FIXME: loglevel from config
// break;
// #endif
//
// case controlMode_t::HTTP:
// ESP_LOGW(TAG, "switching from http mode -> disabling http and wifi");
// //stop http server
// ESP_LOGI(TAG, "disabling http server...");
// http_stop_server();
//
// //FIXME: make wifi function work here - currently starting wifi at startup (see notes main.cpp)
// //stop wifi
// //TODO: decide whether ap or client is currently used - which has to be disabled?
// //ESP_LOGI(TAG, "deinit wifi...");
// //wifi_deinit_client();
// //wifi_deinit_ap();
// ESP_LOGI(TAG, "done stopping http mode");
// break;
//
// case controlMode_t::MASSAGE:
// ESP_LOGW(TAG, "switching from MASSAGE mode -> restoring fading, reset frozen input");
// //TODO: fix issue when downfading was disabled before switching to massage mode - currently it gets enabled again here...
// //enable downfading (set to default value)
// motorLeft->setFade(fadeType_t::DECEL, true);
// motorRight->setFade(fadeType_t::DECEL, true);
// //set upfading to default value
// motorLeft->setFade(fadeType_t::ACCEL, true);
// motorRight->setFade(fadeType_t::ACCEL, true);
// //reset frozen input state
// freezeInput = false;
// break;
//
// case controlMode_t::AUTO:
// ESP_LOGW(TAG, "switching from AUTO mode -> restoring fading to default");
// //TODO: fix issue when downfading was disabled before switching to auto mode - currently it gets enabled again here...
// //enable downfading (set to default value)
// motorLeft->setFade(fadeType_t::DECEL, true);
// motorRight->setFade(fadeType_t::DECEL, true);
// //set upfading to default value
// motorLeft->setFade(fadeType_t::ACCEL, true);
// motorRight->setFade(fadeType_t::ACCEL, true);
// break;
// }
//
//
// //========== commands change TO mode ==========
// //run functions when changing TO certain mode
// switch(modeNew){
// default:
// ESP_LOGI(TAG, "noting to execute when changing TO this mode");
// break;
//
// case controlMode_t::IDLE:
// buzzer->beep(1, 1500, 0);
// #ifdef JOYSTICK_LOG_IN_IDLE
// esp_log_level_set("evaluatedJoystick", ESP_LOG_DEBUG);
// #endif
// break;
//
// case controlMode_t::HTTP:
// ESP_LOGW(TAG, "switching to http mode -> enabling http and wifi");
// //start wifi
// //TODO: decide wether ap or client should be started
// ESP_LOGI(TAG, "init wifi...");
//
// //FIXME: make wifi function work here - currently starting wifi at startup (see notes main.cpp)
// //wifi_init_client();
// //wifi_init_ap();
//
// //wait for wifi
// //ESP_LOGI(TAG, "waiting for wifi...");
// //vTaskDelay(1000 / portTICK_PERIOD_MS);
//
// //start http server
// ESP_LOGI(TAG, "init http server...");
// http_init_server();
// ESP_LOGI(TAG, "done initializing http mode");
// break;
//
// case controlMode_t::MASSAGE:
// ESP_LOGW(TAG, "switching to MASSAGE mode -> reducing fading");
// uint32_t shake_msFadeAccel = 500; //TODO: move this to config
//
// //disable downfading (max. deceleration)
// motorLeft->setFade(fadeType_t::DECEL, false);
// motorRight->setFade(fadeType_t::DECEL, false);
// //reduce upfading (increase acceleration)
// motorLeft->setFade(fadeType_t::ACCEL, shake_msFadeAccel);
// motorRight->setFade(fadeType_t::ACCEL, shake_msFadeAccel);
// break;
//
// }
//
// //--- update mode to new mode ---
// //TODO: add mutex
// mode = modeNew;
// }
//
//
// //TODO simplify the following 3 functions? can be replaced by one?
//
// //-----------------------------------
// //----------- toggleIdle ------------
// //-----------------------------------
// //function to toggle between IDLE and previous active mode
// void controlledArmchair::toggleIdle() {
// //toggle between IDLE and previous mode
// toggleMode(controlMode_t::IDLE);
// }
//
//
//
// //------------------------------------
// //----------- toggleModes ------------
// //------------------------------------
// //function to toggle between two modes, but prefer first argument if entirely different mode is currently active
// void controlledArmchair::toggleModes(controlMode_t modePrimary, controlMode_t modeSecondary) {
// //switch to secondary mode when primary is already active
// if (mode == modePrimary){
// ESP_LOGW(TAG, "toggleModes: switching from primaryMode %s to secondarMode %s", controlModeStr[(int)mode], controlModeStr[(int)modeSecondary]);
// buzzer->beep(2,200,100);
// changeMode(modeSecondary); //switch to secondary mode
// }
// //switch to primary mode when any other mode is active
// else {
// ESP_LOGW(TAG, "toggleModes: switching from %s to primary mode %s", controlModeStr[(int)mode], controlModeStr[(int)modePrimary]);
// buzzer->beep(4,200,100);
// changeMode(modePrimary);
// }
// }
//
//
//
// //-----------------------------------
// //----------- toggleMode ------------
// //-----------------------------------
// //function that toggles between certain mode and previous mode
// void controlledArmchair::toggleMode(controlMode_t modePrimary){
//
// //switch to previous mode when primary is already active
// if (mode == modePrimary){
// ESP_LOGW(TAG, "toggleMode: switching from primaryMode %s to previousMode %s", controlModeStr[(int)mode], controlModeStr[(int)modePrevious]);
// //buzzer->beep(2,200,100);
// changeMode(modePrevious); //switch to previous mode
// }
// //switch to primary mode when any other mode is active
// else {
// ESP_LOGW(TAG, "toggleModes: switching from %s to primary mode %s", controlModeStr[(int)mode], controlModeStr[(int)modePrimary]);
// //buzzer->beep(4,200,100);
// changeMode(modePrimary);
// }
// }

View File

@@ -1,130 +1,130 @@
#pragma once
#include "motordrivers.hpp"
#include "motorctl.hpp"
#include "buzzer.hpp"
#include "http.hpp"
#include "auto.hpp"
#include "types.hpp"
//--------------------------------------------
//---- struct, enum, variable declarations ---
//--------------------------------------------
//enum that decides how the motors get controlled
enum class controlMode_t {IDLE, JOYSTICK, MASSAGE, HTTP, MQTT, BLUETOOTH, AUTO};
//string array representing the mode enum (for printing the state as string)
extern const char* controlModeStr[7];
//--- control_config_t ---
//struct with config parameters
typedef struct control_config_t {
controlMode_t defaultMode; //default mode after startup and toggling IDLE
//timeout options
uint32_t timeoutMs; //time of inactivity after which the mode gets switched to IDLE
float timeoutTolerancePer; //percentage the duty can vary between timeout checks considered still inactive
} control_config_t;
//==================================
//========= control class ==========
//==================================
//controls the mode the armchair operates
//repeatedly generates the motor commands corresponding to current mode and sends those to motorcontrol
class controlledArmchair {
public:
//--- constructor ---
controlledArmchair (
control_config_t config_f,
buzzer_t* buzzer_f,
controlledMotor* motorLeft_f,
controlledMotor* motorRight_f,
evaluatedJoystick* joystick_f,
httpJoystick* httpJoystick_f
);
//--- functions ---
//task that repeatedly generates motor commands depending on the current mode
void startHandleLoop();
//function that changes to a specified control mode
void changeMode(controlMode_t modeNew);
//function that toggle between IDLE and previous active mode (or default if not switched to certain mode yet)
void toggleIdle();
//function that toggles between two modes, but prefers first argument if entirely different mode is currently active
void toggleModes(controlMode_t modePrimary, controlMode_t modeSecondary);
//toggle between certain mode and previous mode
void toggleMode(controlMode_t modePrimary);
//function that restarts timer which initiates the automatic timeout (switch to IDLE) after certain time of inactivity
void resetTimeout();
//function for sending a button event (e.g. from button task at event) to control task
//TODO: use queue instead?
void sendButtonEvent(uint8_t count);
private:
//--- functions ---
//function that evaluates whether there is no activity/change on the motor duty for a certain time, if so a switch to IDLE is issued. - has to be run repeatedly in a slow interval
void handleTimeout();
//--- objects ---
buzzer_t* buzzer;
controlledMotor* motorLeft;
controlledMotor* motorRight;
httpJoystick* httpJoystickMain_l;
evaluatedJoystick* joystick_l;
//---variables ---
//struct for motor commands returned by generate functions of each mode
motorCommands_t commands;
//struct with config parameters
control_config_t config;
//store joystick data
joystickData_t stickData;
bool altStickMapping; //alternative joystick mapping (reverse mapped differently)
//variables for http mode
uint32_t http_timestamp_lastData = 0;
//variables for MASSAGE mode
bool freezeInput = false;
//variables for AUTO mode
auto_instruction_t instruction = auto_instruction_t::NONE; //variable to receive instructions from automatedArmchair
//variable to store button event
uint8_t buttonCount = 0;
//definition of mode enum
controlMode_t mode = controlMode_t::IDLE;
//variable to store mode when toggling IDLE mode
controlMode_t modePrevious; //default mode
//command preset for idling motors
const motorCommand_t cmd_motorIdle = {
.state = motorstate_t::IDLE,
.duty = 0
};
const motorCommands_t cmds_bothMotorsIdle = {
.left = cmd_motorIdle,
.right = cmd_motorIdle
};
//variable for slow loop
uint32_t timestamp_SlowLoopLastRun = 0;
//variables for detecting timeout (switch to idle, after inactivity)
float dutyLeft_lastActivity = 0;
float dutyRight_lastActivity = 0;
uint32_t timestamp_lastActivity = 0;
};
//FIXME controlledMotor class not available for this pcb, rework
//--------------------------------------------
//---- struct, enum, variable declarations ---
//--------------------------------------------
//enum that decides how the motors get controlled
enum class controlMode_t {IDLE, JOYSTICK, MASSAGE, HTTP, MQTT, BLUETOOTH, AUTO};
//string array representing the mode enum (for printing the state as string)
extern const char* controlModeStr[7];
//--- control_config_t ---
//struct with config parameters
typedef struct control_config_t {
controlMode_t defaultMode; //default mode after startup and toggling IDLE
//timeout options
uint32_t timeoutMs; //time of inactivity after which the mode gets switched to IDLE
float timeoutTolerancePer; //percentage the duty can vary between timeout checks considered still inactive
} control_config_t;
// //==================================
// //========= control class ==========
// //==================================
// //controls the mode the armchair operates
// //repeatedly generates the motor commands corresponding to current mode and sends those to motorcontrol
// class controlledArmchair {
// public:
// //--- constructor ---
// controlledArmchair (
// control_config_t config_f,
// buzzer_t* buzzer_f,
// controlledMotor* motorLeft_f,
// controlledMotor* motorRight_f,
// evaluatedJoystick* joystick_f,
// httpJoystick* httpJoystick_f
// );
//
// //--- functions ---
// //task that repeatedly generates motor commands depending on the current mode
// void startHandleLoop();
//
// //function that changes to a specified control mode
// void changeMode(controlMode_t modeNew);
//
// //function that toggle between IDLE and previous active mode (or default if not switched to certain mode yet)
// void toggleIdle();
//
// //function that toggles between two modes, but prefers first argument if entirely different mode is currently active
// void toggleModes(controlMode_t modePrimary, controlMode_t modeSecondary);
//
// //toggle between certain mode and previous mode
// void toggleMode(controlMode_t modePrimary);
//
// //function that restarts timer which initiates the automatic timeout (switch to IDLE) after certain time of inactivity
// void resetTimeout();
//
// //function for sending a button event (e.g. from button task at event) to control task
// //TODO: use queue instead?
// void sendButtonEvent(uint8_t count);
//
// private:
//
// //--- functions ---
// //function that evaluates whether there is no activity/change on the motor duty for a certain time, if so a switch to IDLE is issued. - has to be run repeatedly in a slow interval
// void handleTimeout();
//
// //--- objects ---
// buzzer_t* buzzer;
// controlledMotor* motorLeft;
// controlledMotor* motorRight;
// httpJoystick* httpJoystickMain_l;
// evaluatedJoystick* joystick_l;
//
// //---variables ---
// //struct for motor commands returned by generate functions of each mode
// motorCommands_t commands;
// //struct with config parameters
// control_config_t config;
//
// //store joystick data
// joystickData_t stickData;
// bool altStickMapping; //alternative joystick mapping (reverse mapped differently)
//
// //variables for http mode
// uint32_t http_timestamp_lastData = 0;
//
// //variables for MASSAGE mode
// bool freezeInput = false;
//
// //variables for AUTO mode
// auto_instruction_t instruction = auto_instruction_t::NONE; //variable to receive instructions from automatedArmchair
//
// //variable to store button event
// uint8_t buttonCount = 0;
//
// //definition of mode enum
// controlMode_t mode = controlMode_t::IDLE;
//
// //variable to store mode when toggling IDLE mode
// controlMode_t modePrevious; //default mode
//
// //command preset for idling motors
// const motorCommand_t cmd_motorIdle = {
// .state = motorstate_t::IDLE,
// .duty = 0
// };
// const motorCommands_t cmds_bothMotorsIdle = {
// .left = cmd_motorIdle,
// .right = cmd_motorIdle
// };
//
// //variable for slow loop
// uint32_t timestamp_SlowLoopLastRun = 0;
//
// //variables for detecting timeout (switch to idle, after inactivity)
// float dutyLeft_lastActivity = 0;
// float dutyRight_lastActivity = 0;
// uint32_t timestamp_lastActivity = 0;
// };
//
//

View File

@@ -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<samples; j++){
measure += adc1_get_raw(adc);
ets_delay_us(50);
}
return (float)measure / samples / 4096 * 3.3;
}
//=============================
//======== constructor ========
//=============================
currentSensor::currentSensor (adc1_channel_t adcChannel_f, float ratedCurrent_f){
//copy config
adcChannel = adcChannel_f;
ratedCurrent = ratedCurrent_f;
//init adc
adc1_config_width(ADC_WIDTH_BIT_12); //max resolution 4096
adc1_config_channel_atten(adcChannel, ADC_ATTEN_DB_11); //max voltage
//calibrate
calibrateZeroAmpere();
}
//============================
//=========== read ===========
//============================
float currentSensor::read(void){
//measure voltage
voltage = getVoltage(adcChannel, 30);
//scale voltage to current
if (voltage < centerVoltage){
current = (1 - voltage / centerVoltage) * -ratedCurrent;
} else if (voltage > 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);
}

View File

@@ -1,20 +0,0 @@
#include <driver/adc.h>
//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;
};

View File

@@ -1,82 +0,0 @@
extern "C"
{
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
}
#include "fan.hpp"
//tag for logging
static const char * TAG = "fan-control";
//-----------------------------
//-------- constructor --------
//-----------------------------
controlledFan::controlledFan (fan_config_t config_f, controlledMotor* motor1_f, controlledMotor* motor2_f ){
//copy config
config = config_f;
//copy pointer to motor objects
motor1 = motor1_f;
motor2 = motor2_f;
//initialize gpio pin
gpio_pad_select_gpio(config.gpio_fan);
gpio_set_direction(config.gpio_fan, GPIO_MODE_OUTPUT);
}
//--------------------------
//--------- handle ---------
//--------------------------
void controlledFan::handle(){
//get current state of the motor (motorctl.cpp)
motor1Status = motor1->getStatus();
motor2Status = motor2->getStatus();
//--- handle duty threshold ---
//update timestamp if any threshold exceeded
if (motor1Status.duty > config.dutyThreshold
|| motor2Status.duty > config.dutyThreshold){ //TODO add temperature threshold
if (!needsCooling){
timestamp_needsCoolingSet = esp_log_timestamp();
needsCooling = true;
}
timestamp_lastThreshold = esp_log_timestamp();
} else {
needsCooling = false;
}
//--- turn off condition ---
if (fanRunning
&& !needsCooling //no more cooling required
&& (motor1Status.duty == 0) && (motor2Status.duty == 0) //both motors are off
//-> keeps fans running even when lower than threshold already, however turnOffDelay already started TODO: start turn off delay after motor stop only?
&& (esp_log_timestamp() - timestamp_lastThreshold) > config.turnOffDelayMs){ //turn off delay passed
fanRunning = false;
gpio_set_level(config.gpio_fan, 0);
timestamp_turnedOff = esp_log_timestamp();
ESP_LOGI(TAG, "turned fan OFF gpio=%d, minOnMs=%d, WasOnMs=%d", (int)config.gpio_fan, config.minOnMs, esp_log_timestamp()-timestamp_turnedOn);
}
//--- turn on condition ---
if (!fanRunning
&& needsCooling
&& ((esp_log_timestamp() - timestamp_turnedOff) > config.minOffMs) //fans off long enough
&& ((esp_log_timestamp() - timestamp_needsCoolingSet) > config.minOnMs)){ //motors on long enough
fanRunning = true;
gpio_set_level(config.gpio_fan, 1);
timestamp_turnedOn = esp_log_timestamp();
ESP_LOGI(TAG, "turned fan ON gpio=%d, minOffMs=%d, WasOffMs=%d", (int)config.gpio_fan, config.minOffMs, esp_log_timestamp()-timestamp_turnedOff);
}
//TODO Add statemachine for more specific control? Exponential average?
//TODO idea: try other aproach? increment a variable with certain weights e.g. integrate over duty, then turn fans on and decrement the variable again
ESP_LOGD(TAG, "fanState=%d, duty1=%f, duty2=%f, needsCooling=%d", fanRunning, motor1Status.duty, motor2Status.duty, needsCooling);
}

View File

@@ -1,49 +0,0 @@
#pragma once
extern "C"
{
#include "driver/gpio.h"
}
#include "motorctl.hpp"
//--- fan_config_t ---
//struct with all config parameters for a fan
typedef struct fan_config_t {
gpio_num_t gpio_fan;
float dutyThreshold;
uint32_t minOnMs;
uint32_t minOffMs;
uint32_t turnOffDelayMs;
} fan_config;
//==================================
//====== controlledFan class =======
//==================================
class controlledFan {
public:
//--- constructor ---
controlledFan (fan_config_t config_f, controlledMotor* motor1_f, controlledMotor* motor2_f );
//--- functions ---
void handle(); //has to be run repeatedly in a slow loop
private:
//--- variables ---
bool fanRunning = false;
bool needsCooling = false;
uint32_t timestamp_needsCoolingSet;
uint32_t timestamp_lastThreshold = 0;
uint32_t timestamp_turnedOn = 0;
uint32_t timestamp_turnedOff = 0;
fan_config_t config;
controlledMotor * motor1;
controlledMotor * motor2;
motorCommand_t motor1Status;
motorCommand_t motor2Status;
};

View File

@@ -11,7 +11,7 @@ extern "C"
}
#include <cmath>
#include "motorctl.hpp" //for declaration of motorCommands_t struct
#include "types.hpp"
//======================================

View File

@@ -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;
}

View File

@@ -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 = {};
};

View File

@@ -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);
}

View File

@@ -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 <cmath>
//====================================
//===== 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;
};

View File

@@ -20,23 +20,6 @@ extern "C"
static const char * TAG = "uart";
void uart_init(void){
uart_config_t uart1_config = {
.baud_rate = 115198,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_EVEN,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
};
ESP_LOGW(TAG, "config...");
ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart1_config));
ESP_LOGW(TAG, "setpins...");
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, 23, 22, 0, 0));
ESP_LOGW(TAG, "init...");
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, 1024, 1024, 10, NULL, 0));
}
void uart_task_testing(void *arg){
//repeatedly send 8 bit count and log received 1 byte
@@ -100,11 +83,6 @@ void task_uartReceiveQueue(void *arg){
// }
typedef struct {
uint32_t timestamp;
int id;
float value;
} uartDataStruct;
//send struct
void task_uartSend(void *arg){

View File

@@ -1,5 +1,15 @@
#pragma once
#include "uart_common.hpp"
void uart_init(void);
void uart_task_testing(void *arg);
void task_uartReceive(void *arg);
void task_uartSend(void *arg);
typedef struct {
uint32_t timestamp;
int id;
float value;
} uartDataStruct;

View File

@@ -1,265 +0,0 @@
#include <string.h>
#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);
}

View File

@@ -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);