Moved sources into separate .cpp files

This commit is contained in:
2021-11-01 20:44:57 +01:00
parent fd87c82580
commit a3970514b8
38 changed files with 2061 additions and 1881 deletions

View File

@ -2,7 +2,6 @@
#include <cstdint>
namespace {
#ifdef FEATURE_BLUETOOTH
enum class BluetoothMode : uint8_t
{
@ -11,4 +10,3 @@ enum class BluetoothMode : uint8_t
Slave
};
#endif
}

View File

@ -0,0 +1,455 @@
#include "can.h"
// system includes
#include <cstring>
#include <optional>
// esp-idf
#include <driver/gpio.h>
#include <driver/twai.h>
#include <esp_log.h>
// 3rdparty lib includes
#include <espchrono.h>
#include <tickchrono.h>
// local includes
#include "bobbycar-can.h"
#include "globals.h"
#include "buttons.h"
namespace can {
namespace {
constexpr const char * const TAG = "BOBBYCAN";
} // namespace
std::optional<int16_t> can_gas, can_brems;
espchrono::millis_clock::time_point last_can_gas{}, last_can_brems{};
CanButtonsState lastButtonsState;
void initCan()
{
ESP_LOGI(TAG, "called");
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_21, GPIO_NUM_22, TWAI_MODE_NORMAL);
twai_timing_config_t t_config TWAI_TIMING_CONFIG_250KBITS();
twai_filter_config_t f_config TWAI_FILTER_CONFIG_ACCEPT_ALL();
// {
// //
// .acceptance_code = 0b00000000000,
// .acceptance_mask = 0b00001111111,
// .single_filter = true
// };
if (const auto result = twai_driver_install(&g_config, &t_config, &f_config); result == ESP_OK)
{
ESP_LOGI(TAG, "twai_driver_install() succeeded");
}
else
{
ESP_LOGE(TAG, "twai_driver_install() failed with %s", esp_err_to_name(result));
return;
}
if (const auto result = twai_start(); result == ESP_OK)
{
ESP_LOGI(TAG, "twai_start() succeeded");
}
else
{
ESP_LOGE(TAG, "twai_start() failed with %s", esp_err_to_name(result));
if (const auto result = twai_driver_uninstall(); result == ESP_OK)
{
ESP_LOGI(TAG, "twai_driver_uninstall() succeeded");
}
else
{
ESP_LOGE(TAG, "twai_driver_uninstall() failed with %s", esp_err_to_name(result));
}
return;
}
}
namespace {
template<bool isBack>
bool parseMotorControllerCanMessage(const twai_message_t &message, Controller &controller)
{
switch (message.identifier)
{
using namespace bobbycar::protocol::can;
case MotorController<isBack, false>::Feedback::DcLink:
controller.feedback.left.dcLink = *((int16_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::DcLink:
controller.feedback.right.dcLink = *((int16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::Speed:
controller.feedback.left.speed = *((int16_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::Speed:
controller.feedback.right.speed = *((int16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::Error:
controller.feedback.left.error = *((int8_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::Error:
controller.feedback.right.error = *((int8_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::Angle:
controller.feedback.left.angle = *((int16_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::Angle:
controller.feedback.right.angle = *((int16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::DcPhaA:
controller.feedback.left.dcPhaA = *((int16_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::DcPhaA:
controller.feedback.right.dcPhaA = *((int16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::DcPhaB:
controller.feedback.left.dcPhaB = *((int16_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::DcPhaB:
controller.feedback.right.dcPhaB = *((int16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::DcPhaC:
controller.feedback.left.dcPhaC = *((int16_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::DcPhaC:
controller.feedback.right.dcPhaC = *((int16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::Chops:
controller.feedback.left.chops = *((uint16_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::Chops:
controller.feedback.right.chops = *((uint16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::Hall:
controller.feedback.left.hallA = *((uint8_t*)message.data) & 1;
controller.feedback.left.hallB = *((uint8_t*)message.data) & 2;
controller.feedback.left.hallC = *((uint8_t*)message.data) & 4;
return true;
case MotorController<isBack, true>::Feedback::Hall:
controller.feedback.right.hallA = *((uint8_t*)message.data) & 1;
controller.feedback.right.hallB = *((uint8_t*)message.data) & 2;
controller.feedback.right.hallC = *((uint8_t*)message.data) & 4;
return true;
case MotorController<isBack, false>::Feedback::Voltage:
case MotorController<isBack, true>::Feedback::Voltage:
controller.feedback.batVoltage = *((int16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::Temp:
case MotorController<isBack, true>::Feedback::Temp:
controller.feedback.boardTemp = *((int16_t*)message.data);
return true;
}
return false;
}
bool parseBoardcomputerCanMessage(const twai_message_t &message)
{
switch (message.identifier)
{
using namespace bobbycar::protocol::can;
case Boardcomputer::Command::ButtonPress:
{
const auto canButtonBits = *((uint16_t*)message.data);
CanButtonsState newState {
.up = bool(canButtonBits & Boardcomputer::ButtonUp),
.down = bool(canButtonBits & Boardcomputer::ButtonDown),
.confirm = bool(canButtonBits & Boardcomputer::ButtonConfirm),
.back = bool(canButtonBits & Boardcomputer::ButtonBack),
.profile0 = bool(canButtonBits & Boardcomputer::ButtonProfile0),
.profile1 = bool(canButtonBits & Boardcomputer::ButtonProfile1),
.profile2 = bool(canButtonBits & Boardcomputer::ButtonProfile2),
.profile3 = bool(canButtonBits & Boardcomputer::ButtonProfile3),
};
if (lastButtonsState.up != newState.up)
InputDispatcher::upButton(newState.up);
if (lastButtonsState.down != newState.down)
InputDispatcher::downButton(newState.down);
if (lastButtonsState.confirm != newState.confirm)
InputDispatcher::confirmButton(newState.confirm);
if (lastButtonsState.back != newState.back)
InputDispatcher::backButton(newState.back);
if (lastButtonsState.profile0 != newState.profile0)
InputDispatcher::profileButton(0, newState.profile0);
if (lastButtonsState.profile1 != newState.profile1)
InputDispatcher::profileButton(1, newState.profile1);
if (lastButtonsState.profile2 != newState.profile2)
InputDispatcher::profileButton(2, newState.profile2);
if (lastButtonsState.profile3 != newState.profile3)
InputDispatcher::profileButton(3, newState.profile3);
lastButtonsState = newState;
break;
}
case Boardcomputer::Command::RawGas:
can_gas = *((int16_t*)message.data);
last_can_gas = espchrono::millis_clock::now();
break;
case Boardcomputer::Command::RawBrems:
can_brems = *((int16_t*)message.data);
last_can_brems = espchrono::millis_clock::now();
break;
}
return false;
}
} // namespace
bool tryParseCanInput()
{
twai_message_t message;
const auto timeout = std::chrono::ceil<espcpputils::ticks>(espchrono::milliseconds32{settings.controllerHardware.canReceiveTimeout}).count();
if (const auto result = twai_receive(&message, timeout); result != ESP_OK)
{
if (result != ESP_ERR_TIMEOUT)
{
ESP_LOGE(TAG, "twai_receive() failed with %s", esp_err_to_name(result));
}
if (espchrono::millis_clock::now() - controllers.front.lastCanFeedback > 100ms)
controllers.front.feedbackValid = false;
if (espchrono::millis_clock::now() - controllers.back.lastCanFeedback > 100ms)
controllers.back.feedbackValid = false;
return false;
}
Controller &front = settings.controllerHardware.swapFrontBack ? controllers.back : controllers.front;
Controller &back = settings.controllerHardware.swapFrontBack ? controllers.front : controllers.back;
if (parseMotorControllerCanMessage<false>(message, front))
{
if (espchrono::millis_clock::now() - back.lastCanFeedback > 100ms)
back.feedbackValid = false;
front.lastCanFeedback = espchrono::millis_clock::now();
front.feedbackValid = true;
return true;
}
else
{
if (espchrono::millis_clock::now() - front.lastCanFeedback > 100ms)
front.feedbackValid = false;
}
if (parseMotorControllerCanMessage<true>(message, back))
{
back.lastCanFeedback = espchrono::millis_clock::now();
back.feedbackValid = true;
return true;
}
else
{
if (espchrono::millis_clock::now() - back.lastCanFeedback > 100ms)
back.feedbackValid = false;
}
if (parseBoardcomputerCanMessage(message))
return true;
ESP_LOGW(TAG, "Unknown CAN info received .identifier = %u", message.identifier);
return true;
}
void parseCanInput()
{
for (int i = 0; i < 4; i++)
if (!tryParseCanInput())
break;
}
void sendCanCommands()
{
constexpr auto send = [](uint32_t addr, auto value){
twai_message_t message;
message.identifier = addr;
message.flags = TWAI_MSG_FLAG_SS;
message.data_length_code = sizeof(value);
std::fill(std::begin(message.data), std::end(message.data), 0);
std::memcpy(message.data, &value, sizeof(value));
const auto timeout = std::chrono::ceil<espcpputils::ticks>(espchrono::milliseconds32{settings.controllerHardware.canTransmitTimeout}).count();
const auto result = twai_transmit(&message, timeout);
if (result != ESP_OK && result != ESP_ERR_TIMEOUT)
{
ESP_LOGE(TAG, "ERROR: twai_transmit() failed with %s", esp_err_to_name(result));
}
return result;
};
const bool swap = settings.controllerHardware.swapFrontBack;
const Controller *front =
(swap ? settings.controllerHardware.sendBackCanCmd : settings.controllerHardware.sendFrontCanCmd ) ?
(swap ? &controllers.back : &controllers.front) :
nullptr;
const Controller *back =
(swap ? settings.controllerHardware.sendFrontCanCmd : settings.controllerHardware.sendBackCanCmd ) ?
(swap ? &controllers.front : &controllers.back) :
nullptr;
using namespace bobbycar::protocol::can;
if (front) send(MotorController<false, false>::Command::InpTgt, front->command.left.pwm);
if (front) send(MotorController<false, true>::Command::InpTgt, front->command.right.pwm);
if (back) send(MotorController<true, false>::Command::InpTgt, back->command.left.pwm);
if (back) send(MotorController<true, true>::Command::InpTgt, back->command.right.pwm);
uint16_t buttonLeds{};
if (const auto index = settingsPersister.currentlyOpenProfileIndex())
switch (*index)
{
case 0: buttonLeds |= Boardcomputer::ButtonProfile0; break;
case 1: buttonLeds |= Boardcomputer::ButtonProfile1; break;
case 2: buttonLeds |= Boardcomputer::ButtonProfile2; break;
case 3: buttonLeds |= Boardcomputer::ButtonProfile3; break;
}
static struct {
struct {
uint8_t freq = 0;
uint8_t pattern = 0;
} front, back;
uint16_t buttonLeds{};
} lastValues;
static int i{};
if ((front && front->command.buzzer.freq != lastValues.front.freq ) ||
(front && front->command.buzzer.pattern != lastValues.front.pattern ) ||
(back && back->command.buzzer.freq != lastValues.back.freq) ||
(back && back->command.buzzer.pattern != lastValues.back.pattern))
i = 10;
else if (buttonLeds != lastValues.buttonLeds)
i = 12;
switch (i++)
{
case 0:
if (front) send(MotorController<false, false>::Command::Enable, front->command.left.enable);
if (front) send(MotorController<false, true>::Command::Enable, front->command.right.enable);
if (back) send(MotorController<true, false>::Command::Enable, back->command.left.enable);
if (back) send(MotorController<true, true>::Command::Enable, back->command.right.enable);
break;
case 1:
if (front) send(MotorController<false, false>::Command::CtrlTyp, front->command.left.ctrlTyp);
if (front) send(MotorController<false, true>::Command::CtrlTyp, front->command.right.ctrlTyp);
if (back) send(MotorController<true, false>::Command::CtrlTyp, back->command.left.ctrlTyp);
if (back) send(MotorController<true, true>::Command::CtrlTyp, back->command.right.ctrlTyp);
break;
case 2:
if (front) send(MotorController<false, false>::Command::CtrlMod, front->command.left.ctrlMod);
if (front) send(MotorController<false, true>::Command::CtrlMod, front->command.right.ctrlMod);
if (back) send(MotorController<true, false>::Command::CtrlMod, back->command.left.ctrlMod);
if (back) send(MotorController<true, true>::Command::CtrlMod, back->command.right.ctrlMod);
break;
case 3:
#if defined(HAS_SIMPLIFIED)
SIMPLIFIED_IMOTMAX
#endif
if (front) send(MotorController<false, false>::Command::IMotMax, front->command.left.iMotMax);
if (front) send(MotorController<false, true>::Command::IMotMax, front->command.right.iMotMax);
if (back) send(MotorController<true, false>::Command::IMotMax, back->command.left.iMotMax);
if (back) send(MotorController<true, true>::Command::IMotMax, back->command.right.iMotMax);
break;
case 4:
#if defined(HAS_SIMPLIFIED)
SIMPLIFIED_IDCMAX
#endif
if (front) send(MotorController<false, false>::Command::IDcMax, front->command.left.iDcMax);
if (front) send(MotorController<false, true>::Command::IDcMax, front->command.right.iDcMax);
if (back) send(MotorController<true, false>::Command::IDcMax, back->command.left.iDcMax);
if (back) send(MotorController<true, true>::Command::IDcMax, back->command.right.iDcMax);
break;
case 5:
#if defined(HAS_SIMPLIFIED)
SIMPLIFIED_NMOTMAX
#endif
if (front) send(MotorController<false, false>::Command::NMotMax, front->command.left.nMotMax);
if (front) send(MotorController<false, true>::Command::NMotMax, front->command.right.nMotMax);
if (back) send(MotorController<true, false>::Command::NMotMax, back->command.left.nMotMax);
if (back) send(MotorController<true, true>::Command::NMotMax, back->command.right.nMotMax);
break;
case 6:
#if defined(HAS_SIMPLIFIED)
SIMPLIFIED_FIELDWEAKMAX
#endif
if (front) send(MotorController<false, false>::Command::FieldWeakMax, front->command.left.fieldWeakMax);
if (front) send(MotorController<false, true>::Command::FieldWeakMax, front->command.right.fieldWeakMax);
if (back) send(MotorController<true, false>::Command::FieldWeakMax, back->command.left.fieldWeakMax);
if (back) send(MotorController<true, true>::Command::FieldWeakMax, back->command.right.fieldWeakMax);
break;
case 7:
if (front) send(MotorController<false, false>::Command::PhaseAdvMax, front->command.left.phaseAdvMax);
if (front) send(MotorController<false, true>::Command::PhaseAdvMax, front->command.right.phaseAdvMax);
if (back) send(MotorController<true, false>::Command::PhaseAdvMax, back->command.left.phaseAdvMax);
if (back) send(MotorController<true, true>::Command::PhaseAdvMax, back->command.right.phaseAdvMax);
break;
case 8:
if (front) send(MotorController<false, false>::Command::CruiseCtrlEna, front->command.left.cruiseCtrlEna);
if (front) send(MotorController<false, true>::Command::CruiseCtrlEna, front->command.right.cruiseCtrlEna);
if (back) send(MotorController<true, false>::Command::CruiseCtrlEna, back->command.left.cruiseCtrlEna);
if (back) send(MotorController<true, true>::Command::CruiseCtrlEna, back->command.right.cruiseCtrlEna);
break;
case 9:
if (front) send(MotorController<false, false>::Command::CruiseMotTgt, front->command.left.nCruiseMotTgt);
if (front) send(MotorController<false, true>::Command::CruiseMotTgt, front->command.right.nCruiseMotTgt);
if (back) send(MotorController<true, false>::Command::CruiseMotTgt, back->command.left.nCruiseMotTgt);
if (back) send(MotorController<true, true>::Command::CruiseMotTgt, back->command.right.nCruiseMotTgt);
break;
case 10:
if (front && send(MotorController<false, false>::Command::BuzzerFreq, front->command.buzzer.freq) == ESP_OK)
lastValues.front.freq = front->command.buzzer.freq;
// if (front && send(MotorController<false, true>::Command::BuzzerFreq, front->command.buzzer.freq) == ESP_OK)
// lastValues.front.freq = front->command.buzzer.freq;
if (back && send(MotorController<true, false>::Command::BuzzerFreq, back->command.buzzer.freq) == ESP_OK)
lastValues.back.freq = back->command.buzzer.freq;
// if (back && send(MotorController<true, true>::Command::BuzzerFreq, back->command.buzzer.freq) == ESP_OK)
// lastValues.back.freq = back->command.buzzer.freq;
if (front && send(MotorController<false, false>::Command::BuzzerPattern, front->command.buzzer.pattern) == ESP_OK)
lastValues.front.pattern = front->command.buzzer.pattern;
// if (front && send(MotorController<false, true>::Command::BuzzerPattern, front->command.buzzer.pattern) == ESP_OK)
// lastValues.front.pattern = front->command.buzzer.pattern;
if (back && send(MotorController<true, false>::Command::BuzzerPattern, back->command.buzzer.pattern) == ESP_OK)
lastValues.back.pattern = back->command.buzzer.pattern;
// if (back && send(MotorController<true, true>::Command::BuzzerPattern, back->command.buzzer.pattern) == ESP_OK)
// lastValues.back.pattern = back->command.buzzer.pattern;
break;
case 11:
if (front) send(MotorController<false, false>::Command::Led, front->command.led);
//if (front) send(MotorController<false, true>::Command::Led, front->command.led);
if (back) send(MotorController<true, false>::Command::Led, back->command.led);
//if (back) send(MotorController<true, true>::Command::Led, back->command.led);
if (front) send(MotorController<false, false>::Command::Poweroff, front->command.poweroff);
//if (front) send(MotorController<false, true>::Command::Poweroff, front->command.poweroff);
if (back) send(MotorController<true, false>::Command::Poweroff, back->command.poweroff);
//if (back) send(MotorController<true, true>::Command::Poweroff, back->command.poweroff);
break;
case 12:
if (send(Boardcomputer::Feedback::ButtonLeds, buttonLeds) == ESP_OK)
lastValues.buttonLeds = buttonLeds;
[[fallthrough]];
default:
i=0;
break;
}
}
} // namespace can

View File

@ -1,21 +1,11 @@
#pragma once
#include <cstring>
// system includes
#include <optional>
#include <cstdint>
#include <driver/gpio.h>
#include <driver/twai.h>
#include <esp_log.h>
#include <Arduino.h>
// 3rdparty lib includes
#include <espchrono.h>
#include <tickchrono.h>
#include "bobbycar-can.h"
#include "globals.h"
#include "buttons.h"
#ifdef CAN_PLUGIN
#pragma message "Activating Can Plugin"
@ -24,9 +14,8 @@
namespace can {
namespace {
std::optional<int16_t> can_gas, can_brems;
espchrono::millis_clock::time_point last_can_gas{}, last_can_brems{};
extern std::optional<int16_t> can_gas, can_brems;
extern espchrono::millis_clock::time_point last_can_gas, last_can_brems;
struct CanButtonsState
{
@ -39,428 +28,10 @@ struct CanButtonsState
bool profile2{};
bool profile3{};
};
CanButtonsState lastButtonsState;
extern CanButtonsState lastButtonsState;
void initCan()
{
ESP_LOGI(TAG, "called");
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_21, GPIO_NUM_22, TWAI_MODE_NORMAL);
twai_timing_config_t t_config TWAI_TIMING_CONFIG_250KBITS();
twai_filter_config_t f_config TWAI_FILTER_CONFIG_ACCEPT_ALL();
// {
// //
// .acceptance_code = 0b00000000000,
// .acceptance_mask = 0b00001111111,
// .single_filter = true
// };
if (const auto result = twai_driver_install(&g_config, &t_config, &f_config); result == ESP_OK)
{
ESP_LOGI(TAG, "twai_driver_install() succeeded");
}
else
{
ESP_LOGE(TAG, "twai_driver_install() failed with %s", esp_err_to_name(result));
return;
}
if (const auto result = twai_start(); result == ESP_OK)
{
ESP_LOGI(TAG, "twai_start() succeeded");
}
else
{
ESP_LOGE(TAG, "twai_start() failed with %s", esp_err_to_name(result));
if (const auto result = twai_driver_uninstall(); result == ESP_OK)
{
ESP_LOGI(TAG, "twai_driver_uninstall() succeeded");
}
else
{
ESP_LOGE(TAG, "twai_driver_uninstall() failed with %s", esp_err_to_name(result));
}
return;
}
}
template<bool isBack>
bool parseMotorControllerCanMessage(const twai_message_t &message, Controller &controller)
{
switch (message.identifier)
{
using namespace bobbycar::protocol::can;
case MotorController<isBack, false>::Feedback::DcLink:
controller.feedback.left.dcLink = *((int16_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::DcLink:
controller.feedback.right.dcLink = *((int16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::Speed:
controller.feedback.left.speed = *((int16_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::Speed:
controller.feedback.right.speed = *((int16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::Error:
controller.feedback.left.error = *((int8_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::Error:
controller.feedback.right.error = *((int8_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::Angle:
controller.feedback.left.angle = *((int16_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::Angle:
controller.feedback.right.angle = *((int16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::DcPhaA:
controller.feedback.left.dcPhaA = *((int16_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::DcPhaA:
controller.feedback.right.dcPhaA = *((int16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::DcPhaB:
controller.feedback.left.dcPhaB = *((int16_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::DcPhaB:
controller.feedback.right.dcPhaB = *((int16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::DcPhaC:
controller.feedback.left.dcPhaC = *((int16_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::DcPhaC:
controller.feedback.right.dcPhaC = *((int16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::Chops:
controller.feedback.left.chops = *((uint16_t*)message.data);
return true;
case MotorController<isBack, true>::Feedback::Chops:
controller.feedback.right.chops = *((uint16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::Hall:
controller.feedback.left.hallA = *((uint8_t*)message.data) & 1;
controller.feedback.left.hallB = *((uint8_t*)message.data) & 2;
controller.feedback.left.hallC = *((uint8_t*)message.data) & 4;
return true;
case MotorController<isBack, true>::Feedback::Hall:
controller.feedback.right.hallA = *((uint8_t*)message.data) & 1;
controller.feedback.right.hallB = *((uint8_t*)message.data) & 2;
controller.feedback.right.hallC = *((uint8_t*)message.data) & 4;
return true;
case MotorController<isBack, false>::Feedback::Voltage:
case MotorController<isBack, true>::Feedback::Voltage:
controller.feedback.batVoltage = *((int16_t*)message.data);
return true;
case MotorController<isBack, false>::Feedback::Temp:
case MotorController<isBack, true>::Feedback::Temp:
controller.feedback.boardTemp = *((int16_t*)message.data);
return true;
}
return false;
}
bool parseBoardcomputerCanMessage(const twai_message_t &message)
{
switch (message.identifier)
{
using namespace bobbycar::protocol::can;
case Boardcomputer::Command::ButtonPress:
{
const auto canButtonBits = *((uint16_t*)message.data);
CanButtonsState newState {
.up = bool(canButtonBits & Boardcomputer::ButtonUp),
.down = bool(canButtonBits & Boardcomputer::ButtonDown),
.confirm = bool(canButtonBits & Boardcomputer::ButtonConfirm),
.back = bool(canButtonBits & Boardcomputer::ButtonBack),
.profile0 = bool(canButtonBits & Boardcomputer::ButtonProfile0),
.profile1 = bool(canButtonBits & Boardcomputer::ButtonProfile1),
.profile2 = bool(canButtonBits & Boardcomputer::ButtonProfile2),
.profile3 = bool(canButtonBits & Boardcomputer::ButtonProfile3),
};
if (lastButtonsState.up != newState.up)
InputDispatcher::upButton(newState.up);
if (lastButtonsState.down != newState.down)
InputDispatcher::downButton(newState.down);
if (lastButtonsState.confirm != newState.confirm)
InputDispatcher::confirmButton(newState.confirm);
if (lastButtonsState.back != newState.back)
InputDispatcher::backButton(newState.back);
if (lastButtonsState.profile0 != newState.profile0)
InputDispatcher::profileButton(0, newState.profile0);
if (lastButtonsState.profile1 != newState.profile1)
InputDispatcher::profileButton(1, newState.profile1);
if (lastButtonsState.profile2 != newState.profile2)
InputDispatcher::profileButton(2, newState.profile2);
if (lastButtonsState.profile3 != newState.profile3)
InputDispatcher::profileButton(3, newState.profile3);
lastButtonsState = newState;
break;
}
case Boardcomputer::Command::RawGas:
can_gas = *((int16_t*)message.data);
last_can_gas = espchrono::millis_clock::now();
break;
case Boardcomputer::Command::RawBrems:
can_brems = *((int16_t*)message.data);
last_can_brems = espchrono::millis_clock::now();
break;
}
return false;
}
bool tryParseCanInput()
{
twai_message_t message;
const auto timeout = std::chrono::ceil<espcpputils::ticks>(espchrono::milliseconds32{settings.controllerHardware.canReceiveTimeout}).count();
if (const auto result = twai_receive(&message, timeout); result != ESP_OK)
{
if (result != ESP_ERR_TIMEOUT)
{
ESP_LOGE(TAG, "twai_receive() failed with %s", esp_err_to_name(result));
}
if (espchrono::millis_clock::now() - controllers.front.lastCanFeedback > 100ms)
controllers.front.feedbackValid = false;
if (espchrono::millis_clock::now() - controllers.back.lastCanFeedback > 100ms)
controllers.back.feedbackValid = false;
return false;
}
Controller &front = settings.controllerHardware.swapFrontBack ? controllers.back : controllers.front;
Controller &back = settings.controllerHardware.swapFrontBack ? controllers.front : controllers.back;
if (parseMotorControllerCanMessage<false>(message, front))
{
if (espchrono::millis_clock::now() - back.lastCanFeedback > 100ms)
back.feedbackValid = false;
front.lastCanFeedback = espchrono::millis_clock::now();
front.feedbackValid = true;
return true;
}
else
{
if (espchrono::millis_clock::now() - front.lastCanFeedback > 100ms)
front.feedbackValid = false;
}
if (parseMotorControllerCanMessage<true>(message, back))
{
back.lastCanFeedback = espchrono::millis_clock::now();
back.feedbackValid = true;
return true;
}
else
{
if (espchrono::millis_clock::now() - back.lastCanFeedback > 100ms)
back.feedbackValid = false;
}
if (parseBoardcomputerCanMessage(message))
return true;
ESP_LOGW(TAG, "Unknown CAN info received .identifier = %u", message.identifier);
return true;
}
void parseCanInput()
{
for (int i = 0; i < 4; i++)
if (!tryParseCanInput())
break;
}
void sendCanCommands()
{
constexpr auto send = [](uint32_t addr, auto value){
twai_message_t message;
message.identifier = addr;
message.flags = TWAI_MSG_FLAG_SS;
message.data_length_code = sizeof(value);
std::fill(std::begin(message.data), std::end(message.data), 0);
std::memcpy(message.data, &value, sizeof(value));
const auto timeout = std::chrono::ceil<espcpputils::ticks>(espchrono::milliseconds32{settings.controllerHardware.canTransmitTimeout}).count();
const auto result = twai_transmit(&message, timeout);
if (result != ESP_OK && result != ESP_ERR_TIMEOUT)
{
ESP_LOGE(TAG, "ERROR: twai_transmit() failed with %s", esp_err_to_name(result));
}
return result;
};
const bool swap = settings.controllerHardware.swapFrontBack;
const Controller *front =
(swap ? settings.controllerHardware.sendBackCanCmd : settings.controllerHardware.sendFrontCanCmd ) ?
(swap ? &controllers.back : &controllers.front) :
nullptr;
const Controller *back =
(swap ? settings.controllerHardware.sendFrontCanCmd : settings.controllerHardware.sendBackCanCmd ) ?
(swap ? &controllers.front : &controllers.back) :
nullptr;
using namespace bobbycar::protocol::can;
if (front) send(MotorController<false, false>::Command::InpTgt, front->command.left.pwm);
if (front) send(MotorController<false, true>::Command::InpTgt, front->command.right.pwm);
if (back) send(MotorController<true, false>::Command::InpTgt, back->command.left.pwm);
if (back) send(MotorController<true, true>::Command::InpTgt, back->command.right.pwm);
uint16_t buttonLeds{};
if (const auto index = settingsPersister.currentlyOpenProfileIndex())
switch (*index)
{
case 0: buttonLeds |= Boardcomputer::ButtonProfile0; break;
case 1: buttonLeds |= Boardcomputer::ButtonProfile1; break;
case 2: buttonLeds |= Boardcomputer::ButtonProfile2; break;
case 3: buttonLeds |= Boardcomputer::ButtonProfile3; break;
}
static struct {
struct {
uint8_t freq = 0;
uint8_t pattern = 0;
} front, back;
uint16_t buttonLeds{};
} lastValues;
static int i{};
if ((front && front->command.buzzer.freq != lastValues.front.freq ) ||
(front && front->command.buzzer.pattern != lastValues.front.pattern ) ||
(back && back->command.buzzer.freq != lastValues.back.freq) ||
(back && back->command.buzzer.pattern != lastValues.back.pattern))
i = 10;
else if (buttonLeds != lastValues.buttonLeds)
i = 12;
switch (i++)
{
case 0:
if (front) send(MotorController<false, false>::Command::Enable, front->command.left.enable);
if (front) send(MotorController<false, true>::Command::Enable, front->command.right.enable);
if (back) send(MotorController<true, false>::Command::Enable, back->command.left.enable);
if (back) send(MotorController<true, true>::Command::Enable, back->command.right.enable);
break;
case 1:
if (front) send(MotorController<false, false>::Command::CtrlTyp, front->command.left.ctrlTyp);
if (front) send(MotorController<false, true>::Command::CtrlTyp, front->command.right.ctrlTyp);
if (back) send(MotorController<true, false>::Command::CtrlTyp, back->command.left.ctrlTyp);
if (back) send(MotorController<true, true>::Command::CtrlTyp, back->command.right.ctrlTyp);
break;
case 2:
if (front) send(MotorController<false, false>::Command::CtrlMod, front->command.left.ctrlMod);
if (front) send(MotorController<false, true>::Command::CtrlMod, front->command.right.ctrlMod);
if (back) send(MotorController<true, false>::Command::CtrlMod, back->command.left.ctrlMod);
if (back) send(MotorController<true, true>::Command::CtrlMod, back->command.right.ctrlMod);
break;
case 3:
#if defined(HAS_SIMPLIFIED)
SIMPLIFIED_IMOTMAX
#endif
if (front) send(MotorController<false, false>::Command::IMotMax, front->command.left.iMotMax);
if (front) send(MotorController<false, true>::Command::IMotMax, front->command.right.iMotMax);
if (back) send(MotorController<true, false>::Command::IMotMax, back->command.left.iMotMax);
if (back) send(MotorController<true, true>::Command::IMotMax, back->command.right.iMotMax);
break;
case 4:
#if defined(HAS_SIMPLIFIED)
SIMPLIFIED_IDCMAX
#endif
if (front) send(MotorController<false, false>::Command::IDcMax, front->command.left.iDcMax);
if (front) send(MotorController<false, true>::Command::IDcMax, front->command.right.iDcMax);
if (back) send(MotorController<true, false>::Command::IDcMax, back->command.left.iDcMax);
if (back) send(MotorController<true, true>::Command::IDcMax, back->command.right.iDcMax);
break;
case 5:
#if defined(HAS_SIMPLIFIED)
SIMPLIFIED_NMOTMAX
#endif
if (front) send(MotorController<false, false>::Command::NMotMax, front->command.left.nMotMax);
if (front) send(MotorController<false, true>::Command::NMotMax, front->command.right.nMotMax);
if (back) send(MotorController<true, false>::Command::NMotMax, back->command.left.nMotMax);
if (back) send(MotorController<true, true>::Command::NMotMax, back->command.right.nMotMax);
break;
case 6:
#if defined(HAS_SIMPLIFIED)
SIMPLIFIED_FIELDWEAKMAX
#endif
if (front) send(MotorController<false, false>::Command::FieldWeakMax, front->command.left.fieldWeakMax);
if (front) send(MotorController<false, true>::Command::FieldWeakMax, front->command.right.fieldWeakMax);
if (back) send(MotorController<true, false>::Command::FieldWeakMax, back->command.left.fieldWeakMax);
if (back) send(MotorController<true, true>::Command::FieldWeakMax, back->command.right.fieldWeakMax);
break;
case 7:
if (front) send(MotorController<false, false>::Command::PhaseAdvMax, front->command.left.phaseAdvMax);
if (front) send(MotorController<false, true>::Command::PhaseAdvMax, front->command.right.phaseAdvMax);
if (back) send(MotorController<true, false>::Command::PhaseAdvMax, back->command.left.phaseAdvMax);
if (back) send(MotorController<true, true>::Command::PhaseAdvMax, back->command.right.phaseAdvMax);
break;
case 8:
if (front) send(MotorController<false, false>::Command::CruiseCtrlEna, front->command.left.cruiseCtrlEna);
if (front) send(MotorController<false, true>::Command::CruiseCtrlEna, front->command.right.cruiseCtrlEna);
if (back) send(MotorController<true, false>::Command::CruiseCtrlEna, back->command.left.cruiseCtrlEna);
if (back) send(MotorController<true, true>::Command::CruiseCtrlEna, back->command.right.cruiseCtrlEna);
break;
case 9:
if (front) send(MotorController<false, false>::Command::CruiseMotTgt, front->command.left.nCruiseMotTgt);
if (front) send(MotorController<false, true>::Command::CruiseMotTgt, front->command.right.nCruiseMotTgt);
if (back) send(MotorController<true, false>::Command::CruiseMotTgt, back->command.left.nCruiseMotTgt);
if (back) send(MotorController<true, true>::Command::CruiseMotTgt, back->command.right.nCruiseMotTgt);
break;
case 10:
if (front && send(MotorController<false, false>::Command::BuzzerFreq, front->command.buzzer.freq) == ESP_OK)
lastValues.front.freq = front->command.buzzer.freq;
// if (front && send(MotorController<false, true>::Command::BuzzerFreq, front->command.buzzer.freq) == ESP_OK)
// lastValues.front.freq = front->command.buzzer.freq;
if (back && send(MotorController<true, false>::Command::BuzzerFreq, back->command.buzzer.freq) == ESP_OK)
lastValues.back.freq = back->command.buzzer.freq;
// if (back && send(MotorController<true, true>::Command::BuzzerFreq, back->command.buzzer.freq) == ESP_OK)
// lastValues.back.freq = back->command.buzzer.freq;
if (front && send(MotorController<false, false>::Command::BuzzerPattern, front->command.buzzer.pattern) == ESP_OK)
lastValues.front.pattern = front->command.buzzer.pattern;
// if (front && send(MotorController<false, true>::Command::BuzzerPattern, front->command.buzzer.pattern) == ESP_OK)
// lastValues.front.pattern = front->command.buzzer.pattern;
if (back && send(MotorController<true, false>::Command::BuzzerPattern, back->command.buzzer.pattern) == ESP_OK)
lastValues.back.pattern = back->command.buzzer.pattern;
// if (back && send(MotorController<true, true>::Command::BuzzerPattern, back->command.buzzer.pattern) == ESP_OK)
// lastValues.back.pattern = back->command.buzzer.pattern;
break;
case 11:
if (front) send(MotorController<false, false>::Command::Led, front->command.led);
//if (front) send(MotorController<false, true>::Command::Led, front->command.led);
if (back) send(MotorController<true, false>::Command::Led, back->command.led);
//if (back) send(MotorController<true, true>::Command::Led, back->command.led);
if (front) send(MotorController<false, false>::Command::Poweroff, front->command.poweroff);
//if (front) send(MotorController<false, true>::Command::Poweroff, front->command.poweroff);
if (back) send(MotorController<true, false>::Command::Poweroff, back->command.poweroff);
//if (back) send(MotorController<true, true>::Command::Poweroff, back->command.poweroff);
break;
case 12:
if (send(Boardcomputer::Feedback::ButtonLeds, buttonLeds) == ESP_OK)
lastValues.buttonLeds = buttonLeds;
[[fallthrough]];
default:
i=0;
break;
}
}
} // namespace
void initCan();
bool tryParseCanInput();
void parseCanInput();
void sendCanCommands();
} // namespace can

View File

@ -8,6 +8,7 @@
#include <espwifistack.h>
#include <esphttpdutils.h>
#include <fmt/core.h>
#include <tickchrono.h>
// local includes
#include "globals.h"

View File

@ -0,0 +1,30 @@
#include "controller.h"
Controller::Controller(
#ifdef FEATURE_SERIAL
HardwareSerial &serial,
#endif
bool &enableLeft, bool &enableRight, bool &invertLeft, bool &invertRight,
int16_t &voltageCalib30V, int16_t &voltageCalib50V
) :
#ifdef FEATURE_SERIAL
serial{serial},
#endif
enableLeft{enableLeft}, enableRight{enableRight}, invertLeft{invertLeft}, invertRight{invertRight},
voltageCalib30V{voltageCalib30V}, voltageCalib50V{voltageCalib50V}
{
}
float Controller::getCalibratedVoltage(bool applyCalibration) const
{
float voltage = feedback.batVoltage;
if (applyCalibration)
{
voltage = ((voltage - float(voltageCalib30V)) * (20.f / (float(voltageCalib50V) - float(voltageCalib30V))) + 30.f);
}
else
{
voltage = voltage / 100.;
}
return voltage;
}

View File

@ -23,22 +23,14 @@
class HardwareSerial;
#endif
namespace {
struct Controller {
struct Controller
{
Controller(
#ifdef FEATURE_SERIAL
HardwareSerial &serial,
#endif
bool &enableLeft, bool &enableRight, bool &invertLeft, bool &invertRight,
int16_t &voltageCalib30V, int16_t &voltageCalib50V) :
#ifdef FEATURE_SERIAL
serial{serial},
#endif
enableLeft{enableLeft}, enableRight{enableRight}, invertLeft{invertLeft}, invertRight{invertRight},
voltageCalib30V{voltageCalib30V}, voltageCalib50V{voltageCalib50V}
{
}
int16_t &voltageCalib30V, int16_t &voltageCalib50V);
// Controller(const Controller &) = delete;
// Controller &operator=(const Controller &) = delete;
@ -61,18 +53,5 @@ struct Controller {
FeedbackParser parser{serial, feedbackValid, feedback};
#endif
float getCalibratedVoltage(bool applyCalibration) const
{
float voltage = feedback.batVoltage;
if (applyCalibration)
{
voltage = ((voltage - float(voltageCalib30V)) * (20.f / (float(voltageCalib50V) - float(voltageCalib30V))) + 30.f);
}
else
{
voltage = voltage / 100.;
}
return voltage;
}
float getCalibratedVoltage(bool applyCalibration) const;
};
}

View File

@ -9,35 +9,34 @@
// local includes
#include "textinterface.h"
namespace {
//template<const char *Ttext, typename TreturnType, TreturnType (EspClass::*Tmethod)()>
//using EspStatusTextHelper = StatusTextHelper<Ttext, EspClass, &ESP, TreturnType, Tmethod>;
class HeapTotal8Text : public virtual TextInterface { public: std::string text() const override {
class HeapTotal8Text : public virtual espgui::TextInterface { public: std::string text() const override {
return fmt::format("HeapTotal8: {}", heap_caps_get_total_size(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); }};
class HeapFree8Text : public virtual TextInterface { public: std::string text() const override {
class HeapFree8Text : public virtual espgui::TextInterface { public: std::string text() const override {
return fmt::format("HeapFree8: {}", heap_caps_get_free_size(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); }};
class HeapMinFree8Text : public virtual TextInterface { public: std::string text() const override {
class HeapMinFree8Text : public virtual espgui::TextInterface { public: std::string text() const override {
return fmt::format("HeapMinFree8: {}", heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); }};
class HeapLargest8Text : public virtual TextInterface { public: std::string text() const override {
class HeapLargest8Text : public virtual espgui::TextInterface { public: std::string text() const override {
return fmt::format("HeapLargest8: {}", heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); }};
class HeapTotal32Text : public virtual TextInterface { public: std::string text() const override {
class HeapTotal32Text : public virtual espgui::TextInterface { public: std::string text() const override {
return fmt::format("HeapTotal32: {}", heap_caps_get_total_size(MALLOC_CAP_INTERNAL|MALLOC_CAP_32BIT)); }};
class HeapFree32Text : public virtual TextInterface { public: std::string text() const override {
class HeapFree32Text : public virtual espgui::TextInterface { public: std::string text() const override {
return fmt::format("HeapFree32: {}", heap_caps_get_free_size(MALLOC_CAP_INTERNAL|MALLOC_CAP_32BIT)); }};
class HeapMinFree32Text : public virtual TextInterface { public: std::string text() const override {
class HeapMinFree32Text : public virtual espgui::TextInterface { public: std::string text() const override {
return fmt::format("HeapMinFree32: {}", heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL|MALLOC_CAP_32BIT)); }};
class HeapLargest32Text : public virtual TextInterface { public: std::string text() const override {
class HeapLargest32Text : public virtual espgui::TextInterface { public: std::string text() const override {
return fmt::format("HeapLargest32: {}", heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL|MALLOC_CAP_32BIT)); }};
class LastRebootReasonText : public virtual TextInterface { public: std::string text() const override {
class LastRebootReasonText : public virtual espgui::TextInterface { public: std::string text() const override {
std::string reset_reason_string;
switch (esp_reset_reason()) {
case 0:
@ -80,32 +79,32 @@ class LastRebootReasonText : public virtual TextInterface { public: std::string
return fmt::format("Last Reboot Reason: {}", reset_reason_string); }};
constexpr char TEXT_ESPCHIPREVISION[] = "Chip revision: ";
using EspChipRevisionText = StaticText<TEXT_ESPCHIPREVISION>; //EspStatusTextHelper<TEXT_ESPCHIPREVISION, uint8_t, &EspClass::getChipRevision>;
using EspChipRevisionText = espgui::StaticText<TEXT_ESPCHIPREVISION>; //EspStatusTextHelper<TEXT_ESPCHIPREVISION, uint8_t, &EspClass::getChipRevision>;
constexpr char TEXT_ESPCPUFREQMHZ[] = "Cpu freq MHz: ";
using EspCpuFreqMHzText = StaticText<TEXT_ESPCPUFREQMHZ>; //EspStatusTextHelper<TEXT_ESPCPUFREQMHZ, uint32_t, &EspClass::getCpuFreqMHz>;
using EspCpuFreqMHzText = espgui::StaticText<TEXT_ESPCPUFREQMHZ>; //EspStatusTextHelper<TEXT_ESPCPUFREQMHZ, uint32_t, &EspClass::getCpuFreqMHz>;
constexpr char TEXT_ESPCYCLECOUNT[] = "Cycle count: ";
using EspCycleCountText = StaticText<TEXT_ESPCYCLECOUNT>; //EspStatusTextHelper<TEXT_ESPCYCLECOUNT, uint32_t, &EspClass::getCycleCount>;
using EspCycleCountText = espgui::StaticText<TEXT_ESPCYCLECOUNT>; //EspStatusTextHelper<TEXT_ESPCYCLECOUNT, uint32_t, &EspClass::getCycleCount>;
constexpr char TEXT_ESPSDKVERSION[] = "Sdk version: ";
using EspSdkVersionText = StaticText<TEXT_ESPSDKVERSION>; //EspStatusTextHelper<TEXT_ESPSDKVERSION, const char *, &EspClass::getSdkVersion>;
using EspSdkVersionText = espgui::StaticText<TEXT_ESPSDKVERSION>; //EspStatusTextHelper<TEXT_ESPSDKVERSION, const char *, &EspClass::getSdkVersion>;
constexpr char TEXT_ESPFLASHCHIPSIZE[] = "Flash chip size: ";
using EspFlashChipSizeText = StaticText<TEXT_ESPFLASHCHIPSIZE>; //EspStatusTextHelper<TEXT_ESPFLASHCHIPSIZE, uint32_t, &EspClass::getFlashChipSize>;
using EspFlashChipSizeText = espgui::StaticText<TEXT_ESPFLASHCHIPSIZE>; //EspStatusTextHelper<TEXT_ESPFLASHCHIPSIZE, uint32_t, &EspClass::getFlashChipSize>;
constexpr char TEXT_ESPFLASHCHIPSPEED[] = "Flash chip speed: ";
using EspFlashChipSpeedText = StaticText<TEXT_ESPFLASHCHIPSPEED>; //EspStatusTextHelper<TEXT_ESPFLASHCHIPSPEED, uint32_t, &EspClass::getFlashChipSpeed>;
using EspFlashChipSpeedText = espgui::StaticText<TEXT_ESPFLASHCHIPSPEED>; //EspStatusTextHelper<TEXT_ESPFLASHCHIPSPEED, uint32_t, &EspClass::getFlashChipSpeed>;
constexpr char TEXT_ESPFLASHCHIPMODE[] = "Flash chip mode: ";
using EspFlashChipModeText = StaticText<TEXT_ESPFLASHCHIPMODE>; //EspStatusTextHelper<TEXT_ESPFLASHCHIPMODE, FlashMode_t, &EspClass::getFlashChipMode>; // TODO: improve stringifying
using EspFlashChipModeText = espgui::StaticText<TEXT_ESPFLASHCHIPMODE>; //EspStatusTextHelper<TEXT_ESPFLASHCHIPMODE, FlashMode_t, &EspClass::getFlashChipMode>; // TODO: improve stringifying
constexpr char TEXT_ESPSKETCHSIZE[] = "Sketch size: ";
using EspSketchSizeText = StaticText<TEXT_ESPSKETCHSIZE>; //StaticallyCachedText<EspStatusTextHelper<TEXT_ESPSKETCHSIZE, uint32_t, &EspClass::getSketchSize>>; // caching because of slow
using EspSketchSizeText = espgui::StaticText<TEXT_ESPSKETCHSIZE>; //StaticallyCachedText<EspStatusTextHelper<TEXT_ESPSKETCHSIZE, uint32_t, &EspClass::getSketchSize>>; // caching because of slow
constexpr char TEXT_ESPSKETCHMD5[] = "Sketch MD5: ";
using EspSketchMd5Text = StaticText<TEXT_ESPSKETCHMD5>; //StaticallyCachedText<EspStatusTextHelper<TEXT_ESPSKETCHMD5, String, &EspClass::getSketchMD5>>; // caching because of slow
using EspSketchMd5Text = espgui::StaticText<TEXT_ESPSKETCHMD5>; //StaticallyCachedText<EspStatusTextHelper<TEXT_ESPSKETCHMD5, String, &EspClass::getSketchMD5>>; // caching because of slow
constexpr char TEXT_ESPFREESKETCHSPACE[] = "Free sketch space: ";
using EspFreeSketchSpaceText = StaticText<TEXT_ESPFREESKETCHSPACE>; //EspStatusTextHelper<TEXT_ESPFREESKETCHSPACE, uint32_t, &EspClass::getFreeSketchSpace>;
}
using EspFreeSketchSpaceText = espgui::StaticText<TEXT_ESPFREESKETCHSPACE>; //EspStatusTextHelper<TEXT_ESPFREESKETCHSPACE, uint32_t, &EspClass::getFreeSketchSpace>;

View File

@ -0,0 +1,43 @@
#include "globals.h"
std::optional<int16_t> raw_gas, raw_brems;
std::optional<float> gas, brems;
#ifdef FEATURE_GAMETRAK
int16_t raw_gametrakX{};
int16_t raw_gametrakY{};
int16_t raw_gametrakDist{};
float gametrakX;
float gametrakY;
float gametrakDist;
#endif
float avgSpeed{};
float avgSpeedKmh{};
float sumCurrent{};
char deviceName[32] = STRING(DEVICE_PREFIX) "_ERR";
bool simplified =
#if defined(HAS_SIMPLIFIED)
true
#else
false
#endif
;
Settings settings;
StringSettings stringSettings;
SettingsPersister settingsPersister;
std::array<CRGB, 8> ledstrip_custom_colors;
Controllers controllers;
Performance performance;
#ifdef FEATURE_BLUETOOTH
BluetoothSerial bluetoothSerial;
#endif
ModeInterface *lastMode{};
ModeInterface *currentMode{};

View File

@ -27,35 +27,34 @@
#include "settingspersister.h"
#include "macros_bobbycar.h"
using namespace espgui;
namespace {
std::optional<int16_t> raw_gas, raw_brems;
std::optional<float> gas, brems;
extern std::optional<int16_t> raw_gas, raw_brems;
extern std::optional<float> gas, brems;
#ifdef FEATURE_GAMETRAK
int16_t raw_gametrakX, raw_gametrakY, raw_gametrakDist;
float gametrakX, gametrakY, gametrakDist;
extern int16_t raw_gametrakX;
extern int16_t raw_gametrakY;
extern int16_t raw_gametrakDist;
extern float gametrakX;
extern float gametrakY;
extern float gametrakDist;
#endif
float avgSpeed, avgSpeedKmh, sumCurrent;
extern float avgSpeed;
extern float avgSpeedKmh;
extern float sumCurrent;
char deviceName[32] = STRING(DEVICE_PREFIX) "_ERR";
extern char deviceName[32];
#ifdef GLOBALS_PLUGIN
#include GLOBALS_PLUGIN
#endif
#if defined(HAS_SIMPLIFIED)
bool simplified = true;
#else
bool simplified = false;
#endif
extern bool simplified;
Settings settings;
StringSettings stringSettings;
SettingsPersister settingsPersister;
extern Settings settings;
extern StringSettings stringSettings;
extern SettingsPersister settingsPersister;
std::array<CRGB, 8> ledstrip_custom_colors = {};
extern std::array<CRGB, 8> ledstrip_custom_colors;
class Controllers : public std::array<Controller, 2>
{
@ -86,22 +85,23 @@ public:
Controller &back{operator[](1)};
};
Controllers controllers;
extern Controllers controllers;
struct FrontControllerGetter { static Controller &get() { return controllers.front; }};
struct BackControllerGetter { static Controller &get() { return controllers.back; }};
struct {
struct Performance {
espchrono::millis_clock::time_point lastTime;
int current{};
int last{};
} performance;
};
extern Performance performance;
#ifdef FEATURE_BLUETOOTH
BluetoothSerial bluetoothSerial;
extern BluetoothSerial bluetoothSerial;
#endif
ModeInterface *lastMode{};
ModeInterface *currentMode{};
extern ModeInterface *lastMode;
extern ModeInterface *currentMode;
#ifdef FEATURE_LEDBACKLIGHT
constexpr const bool ledBacklightInverted =
@ -112,4 +112,3 @@ constexpr const bool ledBacklightInverted =
#endif
;
#endif
}

View File

@ -1,9 +1,7 @@
#pragma once
#include <string>
namespace {
class ModeInterface {
class ModeInterface
{
public:
virtual ~ModeInterface() = default;
@ -13,4 +11,4 @@ public:
virtual const char *displayName() const = 0;
};
}

View File

@ -0,0 +1,153 @@
#include "defaultmode.h"
namespace modes {
DefaultMode defaultMode;
} // namespace modes
void DefaultMode::start()
{
Base::start();
}
void DefaultMode::update()
{
if (!gas || !brems)
{
start();
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::OpenMode;
motor.pwm = 0;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
else
{
float local_gas = *gas;
float local_brems = *brems;
if (waitForGasLoslass)
{
if (local_gas < 50)
waitForGasLoslass = false;
else
local_gas = 0;
}
if (waitForBremsLoslass)
{
if (local_brems < 50)
waitForBremsLoslass = false;
else
local_brems = 0;
}
const auto gas_processed = settings.defaultMode.squareGas ? (local_gas * local_gas) / 1000.f : local_gas;
const auto brems_processed = settings.defaultMode.squareBrems ? (local_brems * local_brems) / 1000 : local_brems;
const auto now = espchrono::millis_clock::now();
float pwm;
if (gas_processed >= settings.defaultMode.add_schwelle)
{
pwm = (gas_processed/1000.*settings.defaultMode.gas1_wert) + (brems_processed/1000.*settings.defaultMode.brems1_wert);
if ((settings.defaultMode.enableSmoothingUp || settings.defaultMode.enableSmoothingDown) && (pwm > 1000. || lastPwm > 1000.))
{
if (lastPwm < pwm && settings.defaultMode.enableSmoothingUp)
{
pwm = std::min(pwm, lastPwm + (settings.defaultMode.smoothing * std::chrono::milliseconds{now - lastTime}.count() / 100.f));
if (pwm < 1000.)
pwm = 1000.;
}
else if (lastPwm > pwm && settings.defaultMode.enableSmoothingDown)
{
pwm = std::max(pwm, lastPwm - (settings.defaultMode.smoothing * std::chrono::milliseconds{now - lastTime}.count() / 100.f));
}
}
}
else
{
pwm = (gas_processed/1000.*settings.defaultMode.gas2_wert) - (brems_processed/1000.*settings.defaultMode.brems2_wert);
if (
(settings.defaultMode.enableFieldWeakSmoothingUp || settings.defaultMode.enableFieldWeakSmoothingDown) &&
(lastPwm > settings.defaultMode.fwSmoothLowerLimit) &&
brems_processed > 0)
{
if (lastPwm < pwm && settings.defaultMode.enableFieldWeakSmoothingUp)
{
auto effective_smoothing = settings.defaultMode.smoothing;
auto difference_to_target = std::abs(pwm-lastPwm);
effective_smoothing *= std::max((difference_to_target / 500),0.5f);
pwm = std::min(pwm, lastPwm + (effective_smoothing * std::chrono::milliseconds{now - lastTime}.count() / 100.f));
}
else if (lastPwm > pwm && settings.defaultMode.enableFieldWeakSmoothingDown)
{
auto effective_smoothing = settings.defaultMode.smoothing;
auto difference_to_target = std::abs(pwm-lastPwm);
effective_smoothing *= std::max((difference_to_target / 500),0.5f);
pwm = std::max(pwm, lastPwm - (effective_smoothing * std::chrono::milliseconds{now - lastTime}.count() / 100.f));
}
}
}
lastPwm = pwm;
lastTime = now;
auto pair = split(settings.defaultMode.modelMode);
if (settings.hybrid.enable)
{
auto activationLimit = settings.hybrid.activationLimit;
auto deactivationLimit = settings.hybrid.deactivationLimit;
auto diff = std::abs(activationLimit - deactivationLimit);
if (diff < 20)
{
int half = (diff / 2) + 0.5;
deactivationLimit -= half;
activationLimit += half;
}
if (!hybridModeActivated && (pwm > activationLimit))
{
hybridModeActivated = true;
}
else if (hybridModeActivated && (pwm < deactivationLimit))
{
hybridModeActivated = false;
}
if (hybridModeActivated)
{
pair = split(settings.hybrid.hybridMode);
}
}
for (bobbycar::protocol::serial::MotorState &motor : motorsInController(controllers.front))
{
motor.ctrlTyp = pair.first;
motor.ctrlMod = pair.second;
motor.pwm = pwm / 100. * settings.defaultMode.frontPercentage;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
for (bobbycar::protocol::serial::MotorState &motor : motorsInController(controllers.back))
{
motor.ctrlTyp = pair.first;
motor.ctrlMod = pair.second;
motor.pwm = pwm / 100. * settings.defaultMode.backPercentage;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
fixCommonParams();
sendCommands();
}

View File

@ -1,16 +1,16 @@
#pragma once
// system includes
#include <cstdint>
#include <Arduino.h>
// 3rdparty lib includes
#include <espchrono.h>
// local includes
#include "modeinterface.h"
#include "globals.h"
#include "utils.h"
namespace {
class DefaultMode : public ModeInterface
{
using Base = ModeInterface;
@ -31,154 +31,5 @@ private:
};
namespace modes {
DefaultMode defaultMode;
}
void DefaultMode::start()
{
Base::start();
}
void DefaultMode::update()
{
if (!gas || !brems)
{
start();
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::OpenMode;
motor.pwm = 0;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
else
{
float local_gas = *gas;
float local_brems = *brems;
if (waitForGasLoslass)
{
if (local_gas < 50)
waitForGasLoslass = false;
else
local_gas = 0;
}
if (waitForBremsLoslass)
{
if (local_brems < 50)
waitForBremsLoslass = false;
else
local_brems = 0;
}
const auto gas_processed = settings.defaultMode.squareGas ? (local_gas * local_gas) / 1000.f : local_gas;
const auto brems_processed = settings.defaultMode.squareBrems ? (local_brems * local_brems) / 1000 : local_brems;
const auto now = espchrono::millis_clock::now();
float pwm;
if (gas_processed >= settings.defaultMode.add_schwelle)
{
pwm = (gas_processed/1000.*settings.defaultMode.gas1_wert) + (brems_processed/1000.*settings.defaultMode.brems1_wert);
if ((settings.defaultMode.enableSmoothingUp || settings.defaultMode.enableSmoothingDown) && (pwm > 1000. || lastPwm > 1000.))
{
if (lastPwm < pwm && settings.defaultMode.enableSmoothingUp)
{
pwm = std::min(pwm, lastPwm + (settings.defaultMode.smoothing * std::chrono::milliseconds{now - lastTime}.count() / 100.f));
if (pwm < 1000.)
pwm = 1000.;
}
else if (lastPwm > pwm && settings.defaultMode.enableSmoothingDown)
{
pwm = std::max(pwm, lastPwm - (settings.defaultMode.smoothing * std::chrono::milliseconds{now - lastTime}.count() / 100.f));
}
}
}
else
{
pwm = (gas_processed/1000.*settings.defaultMode.gas2_wert) - (brems_processed/1000.*settings.defaultMode.brems2_wert);
if (
(settings.defaultMode.enableFieldWeakSmoothingUp || settings.defaultMode.enableFieldWeakSmoothingDown) &&
(lastPwm > settings.defaultMode.fwSmoothLowerLimit) &&
brems_processed > 0)
{
if (lastPwm < pwm && settings.defaultMode.enableFieldWeakSmoothingUp)
{
auto effective_smoothing = settings.defaultMode.smoothing;
auto difference_to_target = std::abs(pwm-lastPwm);
effective_smoothing *= std::max((difference_to_target / 500),0.5f);
pwm = std::min(pwm, lastPwm + (effective_smoothing * std::chrono::milliseconds{now - lastTime}.count() / 100.f));
}
else if (lastPwm > pwm && settings.defaultMode.enableFieldWeakSmoothingDown)
{
auto effective_smoothing = settings.defaultMode.smoothing;
auto difference_to_target = std::abs(pwm-lastPwm);
effective_smoothing *= std::max((difference_to_target / 500),0.5f);
pwm = std::max(pwm, lastPwm - (effective_smoothing * std::chrono::milliseconds{now - lastTime}.count() / 100.f));
}
}
}
lastPwm = pwm;
lastTime = now;
auto pair = split(settings.defaultMode.modelMode);
if (settings.hybrid.enable)
{
auto activationLimit = settings.hybrid.activationLimit;
auto deactivationLimit = settings.hybrid.deactivationLimit;
auto diff = std::abs(activationLimit - deactivationLimit);
if (diff < 20)
{
int half = (diff / 2) + 0.5;
deactivationLimit -= half;
activationLimit += half;
}
if (!hybridModeActivated && (pwm > activationLimit))
{
hybridModeActivated = true;
}
else if (hybridModeActivated && (pwm < deactivationLimit))
{
hybridModeActivated = false;
}
if (hybridModeActivated)
{
pair = split(settings.hybrid.hybridMode);
}
}
for (bobbycar::protocol::serial::MotorState &motor : motorsInController(controllers.front))
{
motor.ctrlTyp = pair.first;
motor.ctrlMod = pair.second;
motor.pwm = pwm / 100. * settings.defaultMode.frontPercentage;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
for (bobbycar::protocol::serial::MotorState &motor : motorsInController(controllers.back))
{
motor.ctrlTyp = pair.first;
motor.ctrlMod = pair.second;
motor.pwm = pwm / 100. * settings.defaultMode.backPercentage;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
fixCommonParams();
sendCommands();
}
}
extern DefaultMode defaultMode;
} // namespace modes

View File

@ -0,0 +1,84 @@
#include "gametrakmode.h"
// local includes
#include "globals.h"
#include "utils.h"
#include "defaultmode.h"
namespace {
template<class T>
constexpr const T& clamp( const T& v, const T& lo, const T& hi )
{
assert( !(hi < lo) );
return (v < lo) ? lo : (hi < v) ? hi : v;
}
} // namespace
#ifdef FEATURE_GAMETRAK
namespace modes {
GametrakMode gametrakMode;
} // namespace modes
void GametrakMode::start()
{
Base::start();
m_flag = false;
}
void GametrakMode::update()
{
if (!gas || !brems)
{
start();
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::OpenMode;
motor.pwm = 0;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
else
{
if (*gas > 500. || *brems > 500.)
{
modes::defaultMode.waitForGasLoslass = true;
modes::defaultMode.waitForBremsLoslass = true;
currentMode = &modes::defaultMode;
return;
}
int16_t pwm;
if (gametrakDist < 150)
{
pwm = 0;
m_flag = false;
}
else
{
if (m_flag || gametrakDist >= 400)
{
m_flag = true;
pwm = clamp<int>((gametrakDist - 400) / 2, -200, 200);
}
else
pwm = 0;
}
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::Speed;
motor.pwm = pwm;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
fixCommonParams();
sendCommands();
}
#endif

View File

@ -1,22 +1,9 @@
#pragma once
namespace {
template<class T>
constexpr const T& clamp( const T& v, const T& lo, const T& hi )
{
assert( !(hi < lo) );
return (v < lo) ? lo : (hi < v) ? hi : v;
}
}
#include "modeinterface.h"
#include "globals.h"
#include "utils.h"
#include "defaultmode.h"
// local includes
#include "bobbycar-common.h"
#include "modeinterface.h"
namespace {
#ifdef FEATURE_GAMETRAK
class GametrakMode : public ModeInterface
{
@ -33,70 +20,6 @@ private:
};
namespace modes {
GametrakMode gametrakMode;
}
void GametrakMode::start()
{
Base::start();
m_flag = false;
}
void GametrakMode::update()
{
if (!gas || !brems)
{
start();
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::OpenMode;
motor.pwm = 0;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
else
{
if (*gas > 500. || *brems > 500.)
{
modes::defaultMode.waitForGasLoslass = true;
modes::defaultMode.waitForBremsLoslass = true;
currentMode = &modes::defaultMode;
return;
}
int16_t pwm;
if (gametrakDist < 150)
{
pwm = 0;
m_flag = false;
}
else
{
if (m_flag || gametrakDist >= 400)
{
m_flag = true;
pwm = clamp<int>((gametrakDist - 400) / 2, -200, 200);
}
else
pwm = 0;
}
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::Speed;
motor.pwm = pwm;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
fixCommonParams();
sendCommands();
}
extern GametrakMode gametrakMode;
} // namespace modes
#endif
}

View File

@ -0,0 +1,21 @@
#include "ignoreinputmode.h"
// local includes
#include "globals.h"
#include "utils.h"
void IgnoreInputMode::update()
{
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = m_ctrlTyp;
motor.ctrlMod = m_ctrlMod;
motor.pwm = m_pwm;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
fixCommonParams();
sendCommands();
}

View File

@ -1,12 +1,9 @@
#pragma once
#include "modeinterface.h"
#include "globals.h"
#include "utils.h"
// local includes
#include "bobbycar-common.h"
#include "modeinterface.h"
namespace {
class IgnoreInputMode : public ModeInterface
{
public:
@ -24,20 +21,3 @@ private:
const bobbycar::protocol::ControlType m_ctrlTyp;
const bobbycar::protocol::ControlMode m_ctrlMod;
};
void IgnoreInputMode::update()
{
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = m_ctrlTyp;
motor.ctrlMod = m_ctrlMod;
motor.pwm = m_pwm;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
fixCommonParams();
sendCommands();
}
}

View File

@ -0,0 +1,112 @@
#include "larsmmode.h"
// local includes
#include "globals.h"
#include "utils.h"
namespace modes {
LarsmMode larsmMode;
} // namespace modes
void LarsmMode::start()
{
Base::start();
adc1_filtered = 0.f;
adc2_filtered = 0.f;
speed = 0;
weak = 0.f;
}
void LarsmMode::update()
{
if (!gas || !brems)
{
start();
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::OpenMode;
motor.pwm = 0;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
else
{
for (uint8_t i = 0; i < settings.larsmMode.iterations; i++) // run multiple times to emulate higher refreshrate
{
// ####### larsm's bobby car code #######
// LOW-PASS FILTER (fliessender Mittelwert)
adc1_filtered = adc1_filtered * 0.9 + *gas * 0.1; // ADC1, TX, rechts, vorwaerts, blau
adc2_filtered = adc2_filtered * 0.9 + *brems * 0.1; // ADC2, RX, links, rueckwearts, gruen
// magic numbers die ich nicht mehr nachvollziehen kann, faehrt sich aber gut ;-)
#define LOSLASS_BREMS_ACC 0.996f // naeher an 1 = gemaechlicher
#define DRUECK_ACC2 (1.0f - LOSLASS_BREMS_ACC + 0.001f) // naeher an 0 = gemaechlicher
#define DRUECK_ACC1 (1.0f - LOSLASS_BREMS_ACC + 0.001f) // naeher an 0 = gemaechlicher
//die + 0.001f gleichen float ungenauigkeiten aus.
#define ADC1_MIN 0
#define ADC2_MIN 0
#define ADC1_MAX 1000
#define ADC2_MAX 1000
#define ADC2_DELTA (ADC2_MAX - ADC2_MIN)
#define ADC1_DELTA (ADC1_MAX - ADC1_MIN)
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
if (settings.larsmMode.mode == LarsmModeMode::Mode1) { // Mode 1, links: 3 kmh
speed = (float)speed * LOSLASS_BREMS_ACC // bremsen wenn kein poti gedrueckt
- (CLAMP(*brems - ADC2_MIN, 0, ADC2_DELTA) / (ADC2_DELTA / 280.0f)) * DRUECK_ACC2 // links gedrueckt = zusatzbremsen oder rueckwaertsfahren
+ (CLAMP(*gas - ADC1_MIN, 0, ADC1_DELTA) / (ADC1_DELTA / 350.0f)) * DRUECK_ACC1; // vorwaerts gedrueckt = beschleunigen 12s: 350=3kmh
weak = 0;
} else if (settings.larsmMode.mode == LarsmModeMode::Mode2) { // Mode 2, default: 6 kmh
speed = (float)speed * LOSLASS_BREMS_ACC
- (CLAMP(*brems - ADC2_MIN, 0, ADC2_DELTA) / (ADC2_DELTA / 310.0f)) * DRUECK_ACC2
+ (CLAMP(*gas - ADC1_MIN, 0, ADC1_DELTA) / (ADC1_DELTA / 420.0f)) * DRUECK_ACC1; // 12s: 400=5-6kmh 450=7kmh
weak = 0;
} else if (settings.larsmMode.mode == LarsmModeMode::Mode3) { // Mode 3, rechts: 12 kmh
speed = (float)speed * LOSLASS_BREMS_ACC
- (CLAMP(*brems - ADC2_MIN, 0, ADC2_DELTA) / (ADC2_DELTA / 340.0f)) * DRUECK_ACC2
+ (CLAMP(*gas - ADC1_MIN, 0, ADC1_DELTA) / (ADC1_DELTA / 600.0f)) * DRUECK_ACC1; // 12s: 600=12kmh
weak = 0;
} else if (settings.larsmMode.mode == LarsmModeMode::Mode4) { // Mode 4, l + r: full kmh
// Feldschwaechung wird nur aktiviert wenn man schon sehr schnell ist. So gehts: Rechts voll druecken und warten bis man schnell ist, dann zusaetzlich links schnell voll druecken.
if (adc2_filtered > (ADC2_MAX - 450) && speed > 800) { // field weakening at high speeds
speed = (float)speed * LOSLASS_BREMS_ACC
+ (CLAMP(*gas - ADC1_MIN, 0, ADC1_DELTA) / (ADC1_DELTA / 1000.0f)) * DRUECK_ACC1;
weak = weak * 0.95 + 400.0 * 0.05; // sanftes hinzuschalten des turbos, 12s: 400=29kmh
} else { //normale fahrt ohne feldschwaechung
speed = (float)speed * LOSLASS_BREMS_ACC
- (CLAMP(*brems - ADC2_MIN, 0, ADC2_DELTA) / (ADC2_DELTA / 340.0f)) * DRUECK_ACC2
+ (CLAMP(*gas - ADC1_MIN, 0, ADC1_DELTA) / (ADC1_DELTA / 1000.0f)) * DRUECK_ACC1; // 12s: 1000=22kmh
weak = weak * 0.95; // sanftes abschalten des turbos
}
// weak should never exceed 400 or 450 MAX!!
}
speed = CLAMP(speed, -1000, 1000); // clamp output
}
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
const auto pair = split(settings.larsmMode.modelMode);
motor.ctrlTyp = pair.first;
motor.ctrlMod = pair.second;
motor.pwm = speed + weak;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
fixCommonParams();
sendCommands();
}

View File

@ -1,12 +1,9 @@
#pragma once
#include "modeinterface.h"
#include "globals.h"
#include "utils.h"
// local includes
#include "bobbycar-common.h"
#include "modeinterface.h"
namespace {
class LarsmMode : public ModeInterface
{
using Base = ModeInterface;
@ -25,109 +22,5 @@ private:
};
namespace modes {
LarsmMode larsmMode;
}
void LarsmMode::start()
{
Base::start();
adc1_filtered = 0.f;
adc2_filtered = 0.f;
speed = 0;
weak = 0.f;
}
void LarsmMode::update()
{
if (!gas || !brems)
{
start();
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::OpenMode;
motor.pwm = 0;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
else
{
for (uint8_t i = 0; i < settings.larsmMode.iterations; i++) // run multiple times to emulate higher refreshrate
{
// ####### larsm's bobby car code #######
// LOW-PASS FILTER (fliessender Mittelwert)
adc1_filtered = adc1_filtered * 0.9 + *gas * 0.1; // ADC1, TX, rechts, vorwaerts, blau
adc2_filtered = adc2_filtered * 0.9 + *brems * 0.1; // ADC2, RX, links, rueckwearts, gruen
// magic numbers die ich nicht mehr nachvollziehen kann, faehrt sich aber gut ;-)
#define LOSLASS_BREMS_ACC 0.996f // naeher an 1 = gemaechlicher
#define DRUECK_ACC2 (1.0f - LOSLASS_BREMS_ACC + 0.001f) // naeher an 0 = gemaechlicher
#define DRUECK_ACC1 (1.0f - LOSLASS_BREMS_ACC + 0.001f) // naeher an 0 = gemaechlicher
//die + 0.001f gleichen float ungenauigkeiten aus.
#define ADC1_MIN 0
#define ADC2_MIN 0
#define ADC1_MAX 1000
#define ADC2_MAX 1000
#define ADC2_DELTA (ADC2_MAX - ADC2_MIN)
#define ADC1_DELTA (ADC1_MAX - ADC1_MIN)
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
if (settings.larsmMode.mode == LarsmModeMode::Mode1) { // Mode 1, links: 3 kmh
speed = (float)speed * LOSLASS_BREMS_ACC // bremsen wenn kein poti gedrueckt
- (CLAMP(*brems - ADC2_MIN, 0, ADC2_DELTA) / (ADC2_DELTA / 280.0f)) * DRUECK_ACC2 // links gedrueckt = zusatzbremsen oder rueckwaertsfahren
+ (CLAMP(*gas - ADC1_MIN, 0, ADC1_DELTA) / (ADC1_DELTA / 350.0f)) * DRUECK_ACC1; // vorwaerts gedrueckt = beschleunigen 12s: 350=3kmh
weak = 0;
} else if (settings.larsmMode.mode == LarsmModeMode::Mode2) { // Mode 2, default: 6 kmh
speed = (float)speed * LOSLASS_BREMS_ACC
- (CLAMP(*brems - ADC2_MIN, 0, ADC2_DELTA) / (ADC2_DELTA / 310.0f)) * DRUECK_ACC2
+ (CLAMP(*gas - ADC1_MIN, 0, ADC1_DELTA) / (ADC1_DELTA / 420.0f)) * DRUECK_ACC1; // 12s: 400=5-6kmh 450=7kmh
weak = 0;
} else if (settings.larsmMode.mode == LarsmModeMode::Mode3) { // Mode 3, rechts: 12 kmh
speed = (float)speed * LOSLASS_BREMS_ACC
- (CLAMP(*brems - ADC2_MIN, 0, ADC2_DELTA) / (ADC2_DELTA / 340.0f)) * DRUECK_ACC2
+ (CLAMP(*gas - ADC1_MIN, 0, ADC1_DELTA) / (ADC1_DELTA / 600.0f)) * DRUECK_ACC1; // 12s: 600=12kmh
weak = 0;
} else if (settings.larsmMode.mode == LarsmModeMode::Mode4) { // Mode 4, l + r: full kmh
// Feldschwaechung wird nur aktiviert wenn man schon sehr schnell ist. So gehts: Rechts voll druecken und warten bis man schnell ist, dann zusaetzlich links schnell voll druecken.
if (adc2_filtered > (ADC2_MAX - 450) && speed > 800) { // field weakening at high speeds
speed = (float)speed * LOSLASS_BREMS_ACC
+ (CLAMP(*gas - ADC1_MIN, 0, ADC1_DELTA) / (ADC1_DELTA / 1000.0f)) * DRUECK_ACC1;
weak = weak * 0.95 + 400.0 * 0.05; // sanftes hinzuschalten des turbos, 12s: 400=29kmh
} else { //normale fahrt ohne feldschwaechung
speed = (float)speed * LOSLASS_BREMS_ACC
- (CLAMP(*brems - ADC2_MIN, 0, ADC2_DELTA) / (ADC2_DELTA / 340.0f)) * DRUECK_ACC2
+ (CLAMP(*gas - ADC1_MIN, 0, ADC1_DELTA) / (ADC1_DELTA / 1000.0f)) * DRUECK_ACC1; // 12s: 1000=22kmh
weak = weak * 0.95; // sanftes abschalten des turbos
}
// weak should never exceed 400 or 450 MAX!!
}
speed = CLAMP(speed, -1000, 1000); // clamp output
}
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
const auto pair = split(settings.larsmMode.modelMode);
motor.ctrlTyp = pair.first;
motor.ctrlMod = pair.second;
motor.pwm = speed + weak;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
fixCommonParams();
sendCommands();
}
}
extern LarsmMode larsmMode;
} // namespace modes

View File

@ -0,0 +1,54 @@
#include "remotecontrolmode.h"
// local includes
#include "globals.h"
#include "utils.h"
#include "defaultmode.h"
using namespace std::chrono_literals;
namespace modes {
RemoteControlMode remoteControlMode;
} // namespace modes
void RemoteControlMode::update()
{
if (!m_remoteCommand || espchrono::ago(m_timestamp) > 500ms)
{
start();
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::OpenMode;
motor.pwm = 0;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
else
{
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::Torque;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
controllers.front.command.left.pwm = m_remoteCommand->frontLeft;
controllers.front.command.right.pwm = m_remoteCommand->frontRight;
controllers.back.command.left.pwm = m_remoteCommand->backLeft;
controllers.back.command.right.pwm = m_remoteCommand->backRight;
}
fixCommonParams();
sendCommands();
}
void RemoteControlMode::setCommand(const RemoteCommand &command)
{
m_remoteCommand = command;
m_timestamp = espchrono::millis_clock::now();
}

View File

@ -9,15 +9,8 @@
// local includes
#include "bobbycar-common.h"
#include "modeinterface.h"
#include "globals.h"
#include "utils.h"
#include "defaultmode.h"
using namespace std::chrono_literals;
namespace {
struct RemoteCommand {
int16_t frontLeft{};
int16_t frontRight{};
@ -41,48 +34,5 @@ public:
};
namespace modes {
RemoteControlMode remoteControlMode;
}
void RemoteControlMode::update()
{
if (!m_remoteCommand || espchrono::ago(m_timestamp) > 500ms)
{
start();
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::OpenMode;
motor.pwm = 0;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
else
{
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::Torque;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
controllers.front.command.left.pwm = m_remoteCommand->frontLeft;
controllers.front.command.right.pwm = m_remoteCommand->frontRight;
controllers.back.command.left.pwm = m_remoteCommand->backLeft;
controllers.back.command.right.pwm = m_remoteCommand->backRight;
}
fixCommonParams();
sendCommands();
}
void RemoteControlMode::setCommand(const RemoteCommand &command)
{
m_remoteCommand = command;
m_timestamp = espchrono::millis_clock::now();
}
}
extern RemoteControlMode remoteControlMode;
} // namespace modes

View File

@ -0,0 +1,60 @@
#include "tempomatmode.h"
// local includes
#include "globals.h"
#include "utils.h"
#include "defaultmode.h"
namespace modes {
TempomatMode tempomatMode;
} // namespace modes
void TempomatMode::start()
{
Base::start();
nCruiseMotTgt = avgSpeed;
}
void TempomatMode::update()
{
if (!gas || !brems)
{
start();
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::OpenMode;
motor.pwm = 0;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
else
{
if (*gas > 500. && *brems > 500.)
{
nCruiseMotTgt = 0;
modes::defaultMode.waitForGasLoslass = true;
modes::defaultMode.waitForBremsLoslass = true;
currentMode = &modes::defaultMode;
return;
}
nCruiseMotTgt += (*gas/1000.) - (*brems/1000.);
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
const auto pair = split(settings.tempomatMode.modelMode);
motor.ctrlTyp = pair.first;
motor.ctrlMod = pair.second;
motor.pwm = 0;
motor.cruiseCtrlEna = true;
motor.nCruiseMotTgt = nCruiseMotTgt;
}
}
fixCommonParams();
sendCommands();
}

View File

@ -1,15 +1,12 @@
#pragma once
// system includes
#include <cstdint>
// local includes
#include "bobbycar-common.h"
#include "modeinterface.h"
#include "globals.h"
#include "utils.h"
#include "defaultmode.h"
namespace {
class TempomatMode : public ModeInterface
{
using Base = ModeInterface;
@ -24,56 +21,5 @@ public:
};
namespace modes {
TempomatMode tempomatMode;
}
void TempomatMode::start()
{
Base::start();
nCruiseMotTgt = avgSpeed;
}
void TempomatMode::update()
{
if (!gas || !brems)
{
start();
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::OpenMode;
motor.pwm = 0;
motor.cruiseCtrlEna = false;
motor.nCruiseMotTgt = 0;
}
}
else
{
if (*gas > 500. && *brems > 500.)
{
nCruiseMotTgt = 0;
modes::defaultMode.waitForGasLoslass = true;
modes::defaultMode.waitForBremsLoslass = true;
currentMode = &modes::defaultMode;
return;
}
nCruiseMotTgt += (*gas/1000.) - (*brems/1000.);
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
const auto pair = split(settings.tempomatMode.modelMode);
motor.ctrlTyp = pair.first;
motor.ctrlMod = pair.second;
motor.pwm = 0;
motor.cruiseCtrlEna = true;
motor.nCruiseMotTgt = nCruiseMotTgt;
}
}
fixCommonParams();
sendCommands();
}
}
extern TempomatMode tempomatMode;
} // namespace modes

View File

@ -0,0 +1,53 @@
#include "presets.h"
namespace presets {
StringSettings makeDefaultStringSettings()
{
using ConfiguredWifi = StringSettings::ConfiguredWifi;
#ifdef FEATURE_OTA
using ConfiguredOtaServer = StringSettings::ConfiguredOtaServer;
#endif
return {
.wifis = std::array<ConfiguredWifi, 10> {
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} }
},
#ifdef FEATURE_CLOUD
.cloudUrl = {},
#endif
#ifdef FEATURE_OTA
.otaUrl = {},
#endif
#ifdef FEATURE_GARAGE
.garageUrl = {},
#endif
#ifdef FEATURE_NTP
.timeServer = "europe.pool.ntp.org",
#endif
#ifdef FEATURE_OTA
.otaServers = std::array<ConfiguredOtaServer, 5> {
ConfiguredOtaServer { .name = {}, .url = {} },
ConfiguredOtaServer { .name = {}, .url = {} },
ConfiguredOtaServer { .name = {}, .url = {} },
ConfiguredOtaServer { .name = {}, .url = {} },
ConfiguredOtaServer { .name = {}, .url = {} },
// ConfiguredOtaServer { .name = {}, .url = {} },
// ConfiguredOtaServer { .name = {}, .url = {} },
// ConfiguredOtaServer { .name = {}, .url = {} },
// ConfiguredOtaServer { .name = {}, .url = {} },
// ConfiguredOtaServer { .name = {}, .url = {} },
},
.otaServerUrl = {},
#endif
};
}
} // namespace presets

View File

@ -302,53 +302,5 @@ constexpr Settings defaultSettings {
.lockscreen = defaultLockscreen
};
StringSettings makeDefaultStringSettings()
{
using ConfiguredWifi = StringSettings::ConfiguredWifi;
#ifdef FEATURE_OTA
using ConfiguredOtaServer = StringSettings::ConfiguredOtaServer;
#endif
return {
.wifis = std::array<ConfiguredWifi, 10> {
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} },
ConfiguredWifi { .ssid = {}, .key = {} }
},
#ifdef FEATURE_CLOUD
.cloudUrl = {},
#endif
#ifdef FEATURE_OTA
.otaUrl = {},
#endif
#ifdef FEATURE_GARAGE
.garageUrl = {},
#endif
#ifdef FEATURE_NTP
.timeServer = "europe.pool.ntp.org",
#endif
#ifdef FEATURE_OTA
.otaServers = std::array<ConfiguredOtaServer, 5> {
ConfiguredOtaServer { .name = {}, .url = {} },
ConfiguredOtaServer { .name = {}, .url = {} },
ConfiguredOtaServer { .name = {}, .url = {} },
ConfiguredOtaServer { .name = {}, .url = {} },
ConfiguredOtaServer { .name = {}, .url = {} },/*
ConfiguredOtaServer { .name = {}, .url = {} },
ConfiguredOtaServer { .name = {}, .url = {} },
ConfiguredOtaServer { .name = {}, .url = {} },
ConfiguredOtaServer { .name = {}, .url = {} },
ConfiguredOtaServer { .name = {}, .url = {} },*/
},
.otaServerUrl = {},
#endif
};
}
}
StringSettings makeDefaultStringSettings();
} // namespace presets

View File

@ -22,7 +22,6 @@
#endif
#include "unifiedmodelmode.h"
namespace {
enum class LarsmModeMode : uint8_t { Mode1, Mode2, Mode3, Mode4 };
struct Settings
@ -358,4 +357,3 @@ void Settings::executeForEveryProfileSetting(T &&callable)
callable("larsm.mode", larsmMode.mode);
callable("larsm.iters", larsmMode.iterations);
}
}

View File

@ -0,0 +1,438 @@
#include "settingspersister.h"
// system includes
#include <type_traits>
// esp-idf includes
#include <esp_log.h>
#include <nvs_flash.h>
#ifdef FEATURE_NTP
#include <lwip/apps/snmp.h>
#include <esp_sntp.h>
#endif
// 3rdparty lib includes
#include <fmt/core.h>
#include <cpputils.h>
#include <espchrono.h>
#include <futurecpp.h>
// local includes
#include "settings.h"
#ifdef FEATURE_BLUETOOTH
#include "bluetoothmode.h"
#endif
#include "unifiedmodelmode.h"
#include "settings.h"
#include "stringsettings.h"
bool SettingsPersister::init()
{
if (esp_err_t result = nvs_flash_init();
cpputils::is_in(result, ESP_ERR_NVS_NO_FREE_PAGES, ESP_ERR_NVS_NEW_VERSION_FOUND))
{
ESP_LOGE("BOBBY", "nvs_flash_init() failed with %s, trying to erase...", esp_err_to_name(result));
return erase();
}
else if (result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_flash_init() failed with %s", esp_err_to_name(result));
return false;
}
return true;
}
bool SettingsPersister::erase()
{
closeProfile();
closeCommon();
bool result{true};
if (esp_err_t result = nvs_flash_erase(); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_flash_erase() failed with %s", esp_err_to_name(result));
result = false;
}
if (esp_err_t result = nvs_flash_init(); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_flash_init() failed with %s", esp_err_to_name(result));
result = false;
}
return result;
}
bool SettingsPersister::openCommon()
{
closeCommon();
nvs_handle handle;
if (esp_err_t result = nvs_open("bobbycar", NVS_READWRITE, &handle); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_open() COMMON %s failed with %s", "bobbycar", esp_err_to_name(result));
return false;
}
m_handle = handle;
return true;
}
void SettingsPersister::closeCommon()
{
if (!m_handle)
return;
nvs_close(m_handle);
m_handle = {};
}
bool SettingsPersister::openProfile(uint8_t index)
{
closeProfile();
nvs_handle handle;
const auto name = fmt::format("bobbycar{}", index);
if (esp_err_t result = nvs_open(name.c_str(), NVS_READWRITE, &handle); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_open() PROFILE %s failed with %s", name.c_str(), esp_err_to_name(result));
return false;
}
m_profile = {handle, index};
return true;
}
void SettingsPersister::closeProfile()
{
if (!m_profile)
return;
nvs_close(m_profile->handle);
m_profile = std::nullopt;
}
template<typename T> struct nvsGetterHelper;
template<> struct nvsGetterHelper<int8_t> { static constexpr auto nvs_get = &nvs_get_i8; };
template<> struct nvsGetterHelper<uint8_t> { static constexpr auto nvs_get = &nvs_get_u8; };
template<> struct nvsGetterHelper<int16_t> { static constexpr auto nvs_get = &nvs_get_i16; };
template<> struct nvsGetterHelper<uint16_t> { static constexpr auto nvs_get = &nvs_get_u16; };
template<> struct nvsGetterHelper<int32_t> { static constexpr auto nvs_get = &nvs_get_i32; };
template<> struct nvsGetterHelper<uint32_t> { static constexpr auto nvs_get = &nvs_get_u32; };
template<> struct nvsGetterHelper<std::string> { static esp_err_t nvs_get(nvs_handle handle, const char* key, std::string* out_value)
{
size_t length;
if (const esp_err_t result = nvs_get_str(handle, key, nullptr, &length); result != ESP_OK)
return result;
char buf[length];
if (const esp_err_t result = nvs_get_str(handle, key, buf, &length); result != ESP_OK)
return result;
*out_value = buf;
return ESP_OK;
}};
template<> struct nvsGetterHelper<bool> { static esp_err_t nvs_get(nvs_handle handle, const char* key, bool* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = tempValue;
return err;
}};
template<> struct nvsGetterHelper<bobbycar::protocol::ControlType> { static esp_err_t nvs_get(nvs_handle handle, const char* key, bobbycar::protocol::ControlType* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = bobbycar::protocol::ControlType(tempValue);
return err;
}};
template<> struct nvsGetterHelper<bobbycar::protocol::ControlMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, bobbycar::protocol::ControlMode* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = bobbycar::protocol::ControlMode(tempValue);
return err;
}};
template<> struct nvsGetterHelper<LarsmModeMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, LarsmModeMode* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = LarsmModeMode(tempValue);
return err;
}};
#ifdef FEATURE_BLUETOOTH
template<> struct nvsGetterHelper<BluetoothMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, BluetoothMode* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = BluetoothMode(tempValue);
return err;
}};
#endif
template<> struct nvsGetterHelper<UnifiedModelMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, UnifiedModelMode* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = UnifiedModelMode(tempValue);
return err;
}};
template<> struct nvsGetterHelper<wifi_mode_t> { static esp_err_t nvs_get(nvs_handle handle, const char* key, wifi_mode_t* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = wifi_mode_t(tempValue);
return err;
}};
template<> struct nvsGetterHelper<espchrono::DayLightSavingMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::DayLightSavingMode* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = espchrono::DayLightSavingMode(tempValue);
return err;
}};
template<> struct nvsGetterHelper<espchrono::milliseconds32> { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::milliseconds32* out_value)
{
int32_t tempValue;
esp_err_t err = nvs_get_i32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = espchrono::milliseconds32(tempValue);
return err;
}};
template<> struct nvsGetterHelper<espchrono::seconds32> { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::seconds32* out_value)
{
int32_t tempValue;
esp_err_t err = nvs_get_i32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = espchrono::seconds32(tempValue);
return err;
}};
template<> struct nvsGetterHelper<espchrono::minutes32> { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::minutes32* out_value)
{
int32_t tempValue;
esp_err_t err = nvs_get_i32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = espchrono::minutes32(tempValue);
return err;
}};
template<> struct nvsGetterHelper<espchrono::hours32> { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::hours32* out_value)
{
int32_t tempValue;
esp_err_t err = nvs_get_i32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = espchrono::hours32(tempValue);
return err;
}};
#ifdef FEATURE_NTP
template<> struct nvsGetterHelper<sntp_sync_mode_t> { static esp_err_t nvs_get(nvs_handle handle, const char* key, sntp_sync_mode_t* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = sntp_sync_mode_t(tempValue);
return err;
}};
#endif
template<> struct nvsGetterHelper<std::array<int8_t, 4>> { static esp_err_t nvs_get(nvs_handle handle, const char* key, std::array<int8_t, 4>* out_value)
{
uint32_t tempValue;
esp_err_t err = nvs_get_u32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = std::bit_cast<std::array<int8_t, 4>>(tempValue);
return err;
}};
template<> struct nvsGetterHelper<std::array<uint8_t, 4>> { static esp_err_t nvs_get(nvs_handle handle, const char* key, std::array<uint8_t, 4>* out_value)
{
uint32_t tempValue;
esp_err_t err = nvs_get_u32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = std::bit_cast<std::array<uint8_t, 4>>(tempValue);
return err;
}};
template<typename T>
bool SettingsPersister::load(T &settings)
{
bool result{true};
if (m_handle)
{
settings.executeForEveryCommonSetting([&](const char *key, auto &value)
{
if (esp_err_t result = nvsGetterHelper<std::decay_t<decltype(value)>>::nvs_get(m_handle, key, &value); result != ESP_OK)
{
if (result != ESP_ERR_NVS_NOT_FOUND)
ESP_LOGE("BOBBY", "nvs_get() COMMON %s failed with %s", key, esp_err_to_name(result));
result = false;
}
});
}
else
{
ESP_LOGW("BOBBY", "common nvs handle not valid!");
result = false;
}
if (m_profile)
{
settings.executeForEveryProfileSetting([&](const char *key, auto &value)
{
if (esp_err_t result = nvsGetterHelper<std::decay_t<decltype(value)>>::nvs_get(m_profile->handle, key, &value); result != ESP_OK)
{
if (result != ESP_ERR_NVS_NOT_FOUND)
ESP_LOGE("BOBBY", "nvs_get() PROFILE %s failed with %s", key, esp_err_to_name(result));
result = false;
}
});
}
else
{
ESP_LOGW("BOBBY", "no profile open currently!");
result = false;
}
return result;
}
template bool SettingsPersister::load<Settings>(Settings &settings);
template bool SettingsPersister::load<StringSettings>(StringSettings &settings);
template<typename T> struct nvsSetterHelper;
template<> struct nvsSetterHelper<int8_t> { static constexpr auto nvs_set = &nvs_set_i8; };
template<> struct nvsSetterHelper<uint8_t> { static constexpr auto nvs_set = &nvs_set_u8; };
template<> struct nvsSetterHelper<int16_t> { static constexpr auto nvs_set = &nvs_set_i16; };
template<> struct nvsSetterHelper<uint16_t> { static constexpr auto nvs_set = &nvs_set_u16; };
template<> struct nvsSetterHelper<int32_t> { static constexpr auto nvs_set = &nvs_set_i32; };
template<> struct nvsSetterHelper<uint32_t> { static constexpr auto nvs_set = &nvs_set_u32; };
template<> struct nvsSetterHelper<bool> { static constexpr auto nvs_set = &nvs_set_u8; };
template<> struct nvsSetterHelper<std::string> { static esp_err_t nvs_set(nvs_handle handle, const char* key, const std::string &value)
{
return nvs_set_str(handle, key, value.c_str());
}};
template<> struct nvsSetterHelper<bobbycar::protocol::ControlType> { static esp_err_t nvs_set(nvs_handle handle, const char* key, bobbycar::protocol::ControlType value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<bobbycar::protocol::ControlMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, bobbycar::protocol::ControlMode value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<LarsmModeMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, LarsmModeMode value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
#ifdef FEATURE_BLUETOOTH
template<> struct nvsSetterHelper<BluetoothMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, BluetoothMode value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
#endif
template<> struct nvsSetterHelper<UnifiedModelMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, UnifiedModelMode value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<wifi_mode_t> { static esp_err_t nvs_set(nvs_handle handle, const char* key, wifi_mode_t value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<espchrono::DayLightSavingMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::DayLightSavingMode value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<espchrono::milliseconds32> { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::milliseconds32 value)
{
return nvs_set_i32(handle, key, value.count());
}};
template<> struct nvsSetterHelper<espchrono::seconds32> { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::seconds32 value)
{
return nvs_set_i32(handle, key, value.count());
}};
template<> struct nvsSetterHelper<espchrono::minutes32> { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::minutes32 value)
{
return nvs_set_i32(handle, key, value.count());
}};
template<> struct nvsSetterHelper<espchrono::hours32> { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::hours32 value)
{
return nvs_set_i32(handle, key, value.count());
}};
#ifdef FEATURE_NTP
template<> struct nvsSetterHelper<sntp_sync_mode_t> { static esp_err_t nvs_set(nvs_handle handle, const char* key, sntp_sync_mode_t value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
#endif
template<> struct nvsSetterHelper<std::array<int8_t, 4>> { static esp_err_t nvs_set(nvs_handle handle, const char* key, std::array<int8_t, 4> value)
{
return nvs_set_u32(handle, key, std::bit_cast<uint32_t>(value));
}};
template<> struct nvsSetterHelper<std::array<uint8_t, 4>> { static esp_err_t nvs_set(nvs_handle handle, const char* key, std::array<uint8_t, 4> value)
{
return nvs_set_u32(handle, key, std::bit_cast<uint32_t>(value));
}};
template<typename T>
bool SettingsPersister::save(T &settings)
{
bool result{true};
if (m_handle)
{
settings.executeForEveryCommonSetting([&](const char *key, const auto &value)
{
if (esp_err_t result = nvsSetterHelper<std::decay_t<decltype(value)>>::nvs_set(m_handle, key, value); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_set() COMMON %s failed with %s", key, esp_err_to_name(result));
result = false;
}
});
}
else
{
ESP_LOGW("BOBBY", "common nvs handle not valid!");
result = false;
}
if (m_profile)
{
settings.executeForEveryProfileSetting([&](const char *key, const auto &value)
{
if (esp_err_t result = nvsSetterHelper<std::decay_t<decltype(value)>>::nvs_set(m_profile->handle, key, value); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_set() PROFILE %s failed with %s", key, esp_err_to_name(result));
result = false;
}
});
}
else
{
ESP_LOGW("BOBBY", "no profile open currently!");
result = false;
}
return result;
}
template bool SettingsPersister::save<Settings>(Settings &settings);
template bool SettingsPersister::save<StringSettings>(StringSettings &settings);
std::optional<uint8_t> SettingsPersister::currentlyOpenProfileIndex() const
{
if (m_profile)
return m_profile->profileIndex;
return std::nullopt;
}

View File

@ -1,32 +1,9 @@
#pragma once
// system includes
#include <type_traits>
#include <optional>
// esp-idf includes
#include <esp_log.h>
#include <nvs_flash.h>
#include <nvs.h>
#ifdef FEATURE_NTP
#include <lwip/apps/snmp.h>
#include <esp_sntp.h>
#endif
// 3rdparty lib includes
#include <fmt/core.h>
#include <cpputils.h>
#include <espchrono.h>
#include <futurecpp.h>
// local includes
#include "settings.h"
#ifdef FEATURE_BLUETOOTH
#include "bluetoothmode.h"
#endif
#include "unifiedmodelmode.h"
namespace {
class SettingsPersister
{
public:
@ -55,409 +32,3 @@ private:
};
std::optional<CurrentlyOpenProfile> m_profile;
};
bool SettingsPersister::init()
{
if (esp_err_t result = nvs_flash_init();
cpputils::is_in(result, ESP_ERR_NVS_NO_FREE_PAGES, ESP_ERR_NVS_NEW_VERSION_FOUND))
{
ESP_LOGE("BOBBY", "nvs_flash_init() failed with %s, trying to erase...", esp_err_to_name(result));
return erase();
}
else if (result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_flash_init() failed with %s", esp_err_to_name(result));
return false;
}
return true;
}
bool SettingsPersister::erase()
{
closeProfile();
closeCommon();
bool result{true};
if (esp_err_t result = nvs_flash_erase(); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_flash_erase() failed with %s", esp_err_to_name(result));
result = false;
}
if (esp_err_t result = nvs_flash_init(); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_flash_init() failed with %s", esp_err_to_name(result));
result = false;
}
return result;
}
bool SettingsPersister::openCommon()
{
closeCommon();
nvs_handle handle;
if (esp_err_t result = nvs_open("bobbycar", NVS_READWRITE, &handle); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_open() COMMON %s failed with %s", "bobbycar", esp_err_to_name(result));
return false;
}
m_handle = handle;
return true;
}
void SettingsPersister::closeCommon()
{
if (!m_handle)
return;
nvs_close(m_handle);
m_handle = {};
}
bool SettingsPersister::openProfile(uint8_t index)
{
closeProfile();
nvs_handle handle;
const auto name = fmt::format("bobbycar{}", index);
if (esp_err_t result = nvs_open(name.c_str(), NVS_READWRITE, &handle); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_open() PROFILE %s failed with %s", name.c_str(), esp_err_to_name(result));
return false;
}
m_profile = {handle, index};
return true;
}
void SettingsPersister::closeProfile()
{
if (!m_profile)
return;
nvs_close(m_profile->handle);
m_profile = std::nullopt;
}
template<typename T> struct nvsGetterHelper;
template<> struct nvsGetterHelper<int8_t> { static constexpr auto nvs_get = &nvs_get_i8; };
template<> struct nvsGetterHelper<uint8_t> { static constexpr auto nvs_get = &nvs_get_u8; };
template<> struct nvsGetterHelper<int16_t> { static constexpr auto nvs_get = &nvs_get_i16; };
template<> struct nvsGetterHelper<uint16_t> { static constexpr auto nvs_get = &nvs_get_u16; };
template<> struct nvsGetterHelper<int32_t> { static constexpr auto nvs_get = &nvs_get_i32; };
template<> struct nvsGetterHelper<uint32_t> { static constexpr auto nvs_get = &nvs_get_u32; };
template<> struct nvsGetterHelper<std::string> { static esp_err_t nvs_get(nvs_handle handle, const char* key, std::string* out_value)
{
size_t length;
if (const esp_err_t result = nvs_get_str(handle, key, nullptr, &length); result != ESP_OK)
return result;
char buf[length];
if (const esp_err_t result = nvs_get_str(handle, key, buf, &length); result != ESP_OK)
return result;
*out_value = buf;
return ESP_OK;
}};
template<> struct nvsGetterHelper<bool> { static esp_err_t nvs_get(nvs_handle handle, const char* key, bool* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = tempValue;
return err;
}};
template<> struct nvsGetterHelper<bobbycar::protocol::ControlType> { static esp_err_t nvs_get(nvs_handle handle, const char* key, bobbycar::protocol::ControlType* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = bobbycar::protocol::ControlType(tempValue);
return err;
}};
template<> struct nvsGetterHelper<bobbycar::protocol::ControlMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, bobbycar::protocol::ControlMode* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = bobbycar::protocol::ControlMode(tempValue);
return err;
}};
template<> struct nvsGetterHelper<LarsmModeMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, LarsmModeMode* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = LarsmModeMode(tempValue);
return err;
}};
#ifdef FEATURE_BLUETOOTH
template<> struct nvsGetterHelper<BluetoothMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, BluetoothMode* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = BluetoothMode(tempValue);
return err;
}};
#endif
template<> struct nvsGetterHelper<UnifiedModelMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, UnifiedModelMode* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = UnifiedModelMode(tempValue);
return err;
}};
template<> struct nvsGetterHelper<wifi_mode_t> { static esp_err_t nvs_get(nvs_handle handle, const char* key, wifi_mode_t* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = wifi_mode_t(tempValue);
return err;
}};
template<> struct nvsGetterHelper<espchrono::DayLightSavingMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::DayLightSavingMode* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = espchrono::DayLightSavingMode(tempValue);
return err;
}};
template<> struct nvsGetterHelper<espchrono::milliseconds32> { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::milliseconds32* out_value)
{
int32_t tempValue;
esp_err_t err = nvs_get_i32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = espchrono::milliseconds32(tempValue);
return err;
}};
template<> struct nvsGetterHelper<espchrono::seconds32> { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::seconds32* out_value)
{
int32_t tempValue;
esp_err_t err = nvs_get_i32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = espchrono::seconds32(tempValue);
return err;
}};
template<> struct nvsGetterHelper<espchrono::minutes32> { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::minutes32* out_value)
{
int32_t tempValue;
esp_err_t err = nvs_get_i32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = espchrono::minutes32(tempValue);
return err;
}};
template<> struct nvsGetterHelper<espchrono::hours32> { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::hours32* out_value)
{
int32_t tempValue;
esp_err_t err = nvs_get_i32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = espchrono::hours32(tempValue);
return err;
}};
#ifdef FEATURE_NTP
template<> struct nvsGetterHelper<sntp_sync_mode_t> { static esp_err_t nvs_get(nvs_handle handle, const char* key, sntp_sync_mode_t* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = sntp_sync_mode_t(tempValue);
return err;
}};
#endif
template<> struct nvsGetterHelper<std::array<int8_t, 4>> { static esp_err_t nvs_get(nvs_handle handle, const char* key, std::array<int8_t, 4>* out_value)
{
uint32_t tempValue;
esp_err_t err = nvs_get_u32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = std::bit_cast<std::array<int8_t, 4>>(tempValue);
return err;
}};
template<> struct nvsGetterHelper<std::array<uint8_t, 4>> { static esp_err_t nvs_get(nvs_handle handle, const char* key, std::array<uint8_t, 4>* out_value)
{
uint32_t tempValue;
esp_err_t err = nvs_get_u32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = std::bit_cast<std::array<uint8_t, 4>>(tempValue);
return err;
}};
template<typename T>
bool SettingsPersister::load(T &settings)
{
bool result{true};
if (m_handle)
{
settings.executeForEveryCommonSetting([&](const char *key, auto &value)
{
if (esp_err_t result = nvsGetterHelper<std::decay_t<decltype(value)>>::nvs_get(m_handle, key, &value); result != ESP_OK)
{
if (result != ESP_ERR_NVS_NOT_FOUND)
ESP_LOGE("BOBBY", "nvs_get() COMMON %s failed with %s", key, esp_err_to_name(result));
result = false;
}
});
}
else
{
ESP_LOGW("BOBBY", "common nvs handle not valid!");
result = false;
}
if (m_profile)
{
settings.executeForEveryProfileSetting([&](const char *key, auto &value)
{
if (esp_err_t result = nvsGetterHelper<std::decay_t<decltype(value)>>::nvs_get(m_profile->handle, key, &value); result != ESP_OK)
{
if (result != ESP_ERR_NVS_NOT_FOUND)
ESP_LOGE("BOBBY", "nvs_get() PROFILE %s failed with %s", key, esp_err_to_name(result));
result = false;
}
});
}
else
{
ESP_LOGW("BOBBY", "no profile open currently!");
result = false;
}
return result;
}
template<typename T> struct nvsSetterHelper;
template<> struct nvsSetterHelper<int8_t> { static constexpr auto nvs_set = &nvs_set_i8; };
template<> struct nvsSetterHelper<uint8_t> { static constexpr auto nvs_set = &nvs_set_u8; };
template<> struct nvsSetterHelper<int16_t> { static constexpr auto nvs_set = &nvs_set_i16; };
template<> struct nvsSetterHelper<uint16_t> { static constexpr auto nvs_set = &nvs_set_u16; };
template<> struct nvsSetterHelper<int32_t> { static constexpr auto nvs_set = &nvs_set_i32; };
template<> struct nvsSetterHelper<uint32_t> { static constexpr auto nvs_set = &nvs_set_u32; };
template<> struct nvsSetterHelper<bool> { static constexpr auto nvs_set = &nvs_set_u8; };
template<> struct nvsSetterHelper<std::string> { static esp_err_t nvs_set(nvs_handle handle, const char* key, const std::string &value)
{
return nvs_set_str(handle, key, value.c_str());
}};
template<> struct nvsSetterHelper<bobbycar::protocol::ControlType> { static esp_err_t nvs_set(nvs_handle handle, const char* key, bobbycar::protocol::ControlType value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<bobbycar::protocol::ControlMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, bobbycar::protocol::ControlMode value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<LarsmModeMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, LarsmModeMode value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
#ifdef FEATURE_BLUETOOTH
template<> struct nvsSetterHelper<BluetoothMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, BluetoothMode value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
#endif
template<> struct nvsSetterHelper<UnifiedModelMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, UnifiedModelMode value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<wifi_mode_t> { static esp_err_t nvs_set(nvs_handle handle, const char* key, wifi_mode_t value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<espchrono::DayLightSavingMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::DayLightSavingMode value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<espchrono::milliseconds32> { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::milliseconds32 value)
{
return nvs_set_i32(handle, key, value.count());
}};
template<> struct nvsSetterHelper<espchrono::seconds32> { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::seconds32 value)
{
return nvs_set_i32(handle, key, value.count());
}};
template<> struct nvsSetterHelper<espchrono::minutes32> { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::minutes32 value)
{
return nvs_set_i32(handle, key, value.count());
}};
template<> struct nvsSetterHelper<espchrono::hours32> { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::hours32 value)
{
return nvs_set_i32(handle, key, value.count());
}};
#ifdef FEATURE_NTP
template<> struct nvsSetterHelper<sntp_sync_mode_t> { static esp_err_t nvs_set(nvs_handle handle, const char* key, sntp_sync_mode_t value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
#endif
template<> struct nvsSetterHelper<std::array<int8_t, 4>> { static esp_err_t nvs_set(nvs_handle handle, const char* key, std::array<int8_t, 4> value)
{
return nvs_set_u32(handle, key, std::bit_cast<uint32_t>(value));
}};
template<> struct nvsSetterHelper<std::array<uint8_t, 4>> { static esp_err_t nvs_set(nvs_handle handle, const char* key, std::array<uint8_t, 4> value)
{
return nvs_set_u32(handle, key, std::bit_cast<uint32_t>(value));
}};
template<typename T>
bool SettingsPersister::save(T &settings)
{
bool result{true};
if (m_handle)
{
settings.executeForEveryCommonSetting([&](const char *key, const auto &value)
{
if (esp_err_t result = nvsSetterHelper<std::decay_t<decltype(value)>>::nvs_set(m_handle, key, value); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_set() COMMON %s failed with %s", key, esp_err_to_name(result));
result = false;
}
});
}
else
{
ESP_LOGW("BOBBY", "common nvs handle not valid!");
result = false;
}
if (m_profile)
{
settings.executeForEveryProfileSetting([&](const char *key, const auto &value)
{
if (esp_err_t result = nvsSetterHelper<std::decay_t<decltype(value)>>::nvs_set(m_profile->handle, key, value); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_set() PROFILE %s failed with %s", key, esp_err_to_name(result));
result = false;
}
});
}
else
{
ESP_LOGW("BOBBY", "no profile open currently!");
result = false;
}
return result;
}
std::optional<uint8_t> SettingsPersister::currentlyOpenProfileIndex() const
{
if (m_profile)
return m_profile->profileIndex;
return std::nullopt;
}
}

View File

@ -4,7 +4,6 @@
#include <array>
#include <string>
namespace {
struct StringSettings
{
struct ConfiguredWifi {
@ -30,7 +29,6 @@ struct StringSettings
std::string timeServer;
#endif
template<typename T>
void executeForEveryCommonSetting(T &&callable);
@ -96,17 +94,17 @@ void StringSettings::executeForEveryCommonSetting(T &&callable)
callable("otaName3", otaServers[3].name);
callable("otaUrl3", otaServers[3].url);
callable("otaName4", otaServers[4].name);
callable("otaUrl4", otaServers[4].url);/*
callable("otaName5", otaServers[5].name);
callable("otaUrl5", otaServers[5].url);
callable("otaName6", otaServers[6].name);
callable("otaUrl6", otaServers[6].url);
callable("otaName7", otaServers[7].name);
callable("otaUrl7", otaServers[7].url);
callable("otaName8", otaServers[8].name);
callable("otaUrl8", otaServers[8].url);
callable("otaName9", otaServers[9].name);
callable("otaUrl9", otaServers[9].url);*/
callable("otaUrl4", otaServers[4].url);
// callable("otaName5", otaServers[5].name);
// callable("otaUrl5", otaServers[5].url);
// callable("otaName6", otaServers[6].name);
// callable("otaUrl6", otaServers[6].url);
// callable("otaName7", otaServers[7].name);
// callable("otaUrl7", otaServers[7].url);
// callable("otaName8", otaServers[8].name);
// callable("otaUrl8", otaServers[8].url);
// callable("otaName9", otaServers[9].name);
// callable("otaUrl9", otaServers[9].url);
callable("otaserver", otaServerUrl);
#endif
@ -116,4 +114,3 @@ template<typename T>
void StringSettings::executeForEveryProfileSetting(T &&callable)
{
}
}

View File

@ -0,0 +1,20 @@
#include "unifiedmodelmode.h"
std::pair<bobbycar::protocol::ControlType, bobbycar::protocol::ControlMode> split(UnifiedModelMode mode)
{
using bobbycar::protocol::ControlType;
using bobbycar::protocol::ControlMode;
switch (mode)
{
case UnifiedModelMode::Commutation: return std::make_pair(ControlType::Commutation, ControlMode::Voltage);
case UnifiedModelMode::Sinusoidal: return std::make_pair(ControlType::Sinusoidal, ControlMode::Voltage);
case UnifiedModelMode::FocVoltage: return std::make_pair(ControlType::FieldOrientedControl, ControlMode::Voltage);
case UnifiedModelMode::FocSpeed: return std::make_pair(ControlType::FieldOrientedControl, ControlMode::Speed);
case UnifiedModelMode::FocTorque: return std::make_pair(ControlType::FieldOrientedControl, ControlMode::Torque);
}
//Serial.printf("Unknown UnifiedModelMode: %i\r\n", int(mode));
return std::make_pair(ControlType::FieldOrientedControl, ControlMode::OpenMode);
}

View File

@ -1,10 +1,11 @@
#pragma once
// system includes
#include <utility>
// local includes
#include "bobbycar-common.h"
namespace {
enum class UnifiedModelMode : uint8_t
{
Commutation,
@ -14,22 +15,4 @@ enum class UnifiedModelMode : uint8_t
FocTorque
};
std::pair<bobbycar::protocol::ControlType, bobbycar::protocol::ControlMode> split(UnifiedModelMode mode)
{
using bobbycar::protocol::ControlType;
using bobbycar::protocol::ControlMode;
switch (mode)
{
case UnifiedModelMode::Commutation: return std::make_pair(ControlType::Commutation, ControlMode::Voltage);
case UnifiedModelMode::Sinusoidal: return std::make_pair(ControlType::Sinusoidal, ControlMode::Voltage);
case UnifiedModelMode::FocVoltage: return std::make_pair(ControlType::FieldOrientedControl, ControlMode::Voltage);
case UnifiedModelMode::FocSpeed: return std::make_pair(ControlType::FieldOrientedControl, ControlMode::Speed);
case UnifiedModelMode::FocTorque: return std::make_pair(ControlType::FieldOrientedControl, ControlMode::Torque);
}
//Serial.printf("Unknown UnifiedModelMode: %i\r\n", int(mode));
return std::make_pair(ControlType::FieldOrientedControl, ControlMode::OpenMode);
}
}
std::pair<bobbycar::protocol::ControlType, bobbycar::protocol::ControlMode> split(UnifiedModelMode mode);

View File

@ -0,0 +1,308 @@
#include "utils.h"
using namespace std::chrono_literals;
bool currentlyReverseBeeping;
bool reverseBeepToggle;
espchrono::millis_clock::time_point lastReverseBeepToggle;
float convertToKmh(float val)
{
return val /* / settings.controllerHardware.numMagnetPoles */ / 60.f * settings.controllerHardware.wheelDiameter / 1000.f * 3.14159265359f * 3.6f;
}
float convertFromKmh(float val)
{
return val /* * settings.controllerHardware.numMagnetPoles */ * 60.f / settings.controllerHardware.wheelDiameter * 1000.f / 3.14159265359f / 3.6f;
}
float convertToInch(float val)
{
return val / 25.4f;
}
float convertFromInch(float val)
{
return val * 25.4f;
}
float fixCurrent(int16_t value)
{
return -value/50.;
}
float fixBoardTemp(int16_t value)
{
return value/10.;
}
std::string hallString(const bobbycar::protocol::serial::MotorFeedback &motor)
{
return std::string{} + (motor.hallA ? '1' : '0') + (motor.hallB ? '1' : '0') + (motor.hallC ? '1' : '0');
}
std::string to_string(bobbycar::protocol::ControlType value)
{
switch (value)
{
using namespace bobbycar::protocol;
case ControlType::Commutation: return "Commutation";
case ControlType::Sinusoidal: return "Sinusoidal";
case ControlType::FieldOrientedControl: return "FieldOrientedControl";
}
return fmt::format("Unknown ControlType({})", std::to_underlying(value));
}
std::string to_string(bobbycar::protocol::ControlMode value)
{
switch (value)
{
using namespace bobbycar::protocol;
case ControlMode::OpenMode: return "OpenMode";
case ControlMode::Voltage: return "Voltage";
case ControlMode::Speed: return "Speed";
case ControlMode::Torque: return "Torque";
}
return fmt::format("Unknown ControlMode({})", std::to_underlying(value));
}
std::array<std::reference_wrapper<bobbycar::protocol::serial::MotorState>, 2> motorsInController(Controller &controller)
{
return {std::ref(controller.command.left), std::ref(controller.command.right)};
}
std::array<std::reference_wrapper<const bobbycar::protocol::serial::MotorState>, 2> motorsInController(const Controller &controller)
{
return {std::ref(controller.command.left), std::ref(controller.command.right)};
}
std::array<std::reference_wrapper<bobbycar::protocol::serial::MotorFeedback>, 2> motorFeedbacksInController(Controller &controller)
{
return {std::ref(controller.feedback.left), std::ref(controller.feedback.right)};
}
std::array<std::reference_wrapper<const bobbycar::protocol::serial::MotorFeedback>, 2> motorFeedbacksInController(const Controller &controller)
{
return {std::ref(controller.feedback.left), std::ref(controller.feedback.right)};
}
std::array<std::reference_wrapper<bobbycar::protocol::serial::MotorState>, 4> motors()
{
return {
std::ref(controllers.front.command.left), std::ref(controllers.front.command.right),
std::ref(controllers.back.command.left), std::ref(controllers.back.command.right)
};
}
void fixCommonParams()
{
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.iMotMax = settings.limits.iMotMax;
motor.iDcMax = settings.limits.iDcMax;
motor.nMotMax = settings.limits.nMotMax;
motor.fieldWeakMax = settings.limits.fieldWeakMax;
motor.phaseAdvMax = settings.limits.phaseAdvMax;
}
if (settings.buzzer.reverseBeep)
{
const auto x = motors();
const auto shouldBeep = std::all_of(std::begin(x), std::end(x), [](const bobbycar::protocol::serial::MotorState &motor){ return motor.pwm < 0; });
if (shouldBeep != currentlyReverseBeeping)
{
if (shouldBeep)
{
reverseBeepToggle = true;
lastReverseBeepToggle = espchrono::millis_clock::now();
for (auto &controller : controllers)
controller.command.buzzer = {.freq=settings.buzzer.reverseBeepFreq0, .pattern=0};
}
else
for (auto &controller : controllers)
controller.command.buzzer = {};
currentlyReverseBeeping = shouldBeep;
}
else if (shouldBeep && espchrono::millis_clock::now() - lastReverseBeepToggle >= std::chrono::milliseconds{reverseBeepToggle?settings.buzzer.reverseBeepDuration0:settings.buzzer.reverseBeepDuration1})
{
reverseBeepToggle = !reverseBeepToggle;
for (auto &controller : controllers)
controller.command.buzzer = {.freq=uint8_t(reverseBeepToggle?settings.buzzer.reverseBeepFreq0:settings.buzzer.reverseBeepFreq1), .pattern=0};
lastReverseBeepToggle = espchrono::millis_clock::now();
}
}
else if (currentlyReverseBeeping)
{
for (auto &controller : controllers)
controller.command.buzzer = {};
currentlyReverseBeeping = false;
}
controllers.front.command.left.enable = settings.controllerHardware.enableFrontLeft;
controllers.front.command.right.enable = settings.controllerHardware.enableFrontRight;
controllers.back.command.left.enable = settings.controllerHardware.enableBackLeft;
controllers.back.command.right.enable = settings.controllerHardware.enableBackRight;
if (settings.controllerHardware.invertFrontLeft)
{
controllers.front.command.left.pwm = -controllers.front.command.left.pwm;
controllers.front.command.left.nCruiseMotTgt = -controllers.front.command.left.nCruiseMotTgt;
}
if (settings.controllerHardware.invertFrontRight)
{
controllers.front.command.right.pwm = -controllers.front.command.right.pwm;
controllers.front.command.right.nCruiseMotTgt = -controllers.front.command.right.nCruiseMotTgt;
}
if (settings.controllerHardware.invertBackLeft)
{
controllers.back.command.left.pwm = -controllers.back.command.left.pwm;
controllers.back.command.left.nCruiseMotTgt = -controllers.back.command.left.nCruiseMotTgt;
}
if (settings.controllerHardware.invertBackRight)
{
controllers.back.command.right.pwm = -controllers.back.command.right.pwm;
controllers.back.command.right.nCruiseMotTgt = -controllers.back.command.right.nCruiseMotTgt;
}
}
void sendCommands()
{
#ifdef FEATURE_SERIAL
using namespace bobbycar::protocol::serial;
for (Controller &controller : controllers)
{
controller.command.start = Command::VALID_HEADER;
controller.command.checksum = calculateChecksum(controller.command);
controller.serial.get().write((uint8_t *) &controller.command, sizeof(controller.command));
}
#endif
#ifdef FEATURE_CAN
can::sendCanCommands();
#endif
}
#ifdef FEATURE_SERIAL
void updateSwapFrontBack()
{
controllers.front.serial = settings.controllerHardware.swapFrontBack ? Serial2 : Serial1;
controllers.back.serial = settings.controllerHardware.swapFrontBack ? Serial1 : Serial2;
}
#endif
bool loadSettings()
{
bool result{true};
if (!settingsPersister.load(settings))
result = false;
if (!settingsPersister.load(stringSettings))
result = false;
return result;
}
bool saveSettings()
{
if (simplified) return true;
bool result{true};
if (!settingsPersister.save(settings))
result = false;
if (!settingsPersister.save(stringSettings))
result = false;
return result;
}
void updateAccumulators()
{
avgSpeed = 0.f;
sumCurrent = 0.f;
uint8_t count{0};
for (const Controller &controller : controllers)
{
if (!controller.feedbackValid)
continue;
avgSpeed +=
controller.feedback.left.speed * (controller.invertLeft ? -1 : 1) +
controller.feedback.right.speed * (controller.invertRight ? -1 : 1);
sumCurrent +=
controller.feedback.left.dcLink +
controller.feedback.right.dcLink;
count +=2;
}
if (count)
avgSpeed /= count;
sumCurrent = fixCurrent(sumCurrent);
avgSpeedKmh = convertToKmh(avgSpeed);
}
void readPotis()
{
[[maybe_unused]]
constexpr auto sampleMultipleTimes = [](int pin){
analogRead(pin);
double sum{};
const auto sampleCount = settings.boardcomputerHardware.sampleCount;
for (int i = 0; i < sampleCount; i++)
sum += analogRead(pin);
return sum / sampleCount;
};
raw_gas = std::nullopt;
raw_brems = std::nullopt;
#ifdef FEATURE_CAN
const auto now = espchrono::millis_clock::now();
if (can::can_gas)
{
if (now - can::last_can_gas < 100ms)
raw_gas = *can::can_gas;
else
can::can_gas = std::nullopt;
}
if (can::can_brems)
{
if (now - can::last_can_brems < 100ms)
raw_brems = *can::can_brems;
else
can::can_brems = std::nullopt;
}
#endif
#ifdef FEATURE_ADC_IN
if (!raw_gas)
raw_gas = sampleMultipleTimes(PINS_GAS);
if (!raw_brems)
raw_brems = sampleMultipleTimes(PINS_BREMS);
#endif
if (raw_gas)
gas = cpputils::mapValueClamped<float>(*raw_gas, settings.boardcomputerHardware.gasMin, settings.boardcomputerHardware.gasMax, 0., 1000.);
else
gas = std::nullopt;
if (raw_brems)
brems = cpputils::mapValueClamped<float>(*raw_brems, settings.boardcomputerHardware.bremsMin, settings.boardcomputerHardware.bremsMax, 0., 1000.);
else
brems = std::nullopt;
#ifdef FEATURE_GAMETRAK
raw_gametrakX = sampleMultipleTimes(PINS_GAMETRAKX);
gametrakX = cpputils::mapValueClamped<float>(raw_gametrakX, settings.boardcomputerHardware.gametrakXMin, settings.boardcomputerHardware.gametrakXMax, 0., 1000.);
raw_gametrakY = sampleMultipleTimes(PINS_GAMETRAKY);
gametrakY = cpputils::mapValueClamped<float>(raw_gametrakY, settings.boardcomputerHardware.gametrakYMin, settings.boardcomputerHardware.gametrakYMax, 0., 1000.);
raw_gametrakDist = sampleMultipleTimes(PINS_GAMETRAKDIST);
gametrakDist = cpputils::mapValueClamped<float>(raw_gametrakDist, settings.boardcomputerHardware.gametrakDistMin, settings.boardcomputerHardware.gametrakDistMax, 0., 1000.);
#endif
}

View File

@ -29,309 +29,30 @@
#include "can.h"
#endif
namespace {
bool currentlyReverseBeeping;
bool reverseBeepToggle;
espchrono::millis_clock::time_point lastReverseBeepToggle;
extern bool currentlyReverseBeeping;
extern bool reverseBeepToggle;
extern espchrono::millis_clock::time_point lastReverseBeepToggle;
float convertToKmh(float val)
{
return val /* / settings.controllerHardware.numMagnetPoles */ / 60.f * settings.controllerHardware.wheelDiameter / 1000.f * 3.14159265359f * 3.6f;
}
float convertFromKmh(float val)
{
return val /* * settings.controllerHardware.numMagnetPoles */ * 60.f / settings.controllerHardware.wheelDiameter * 1000.f / 3.14159265359f / 3.6f;
}
float convertToInch(float val)
{
return val / 25.4f;
}
float convertFromInch(float val)
{
return val * 25.4f;
}
float fixCurrent(int16_t value)
{
return -value/50.;
}
float fixBoardTemp(int16_t value)
{
return value/10.;
}
std::string hallString(const bobbycar::protocol::serial::MotorFeedback &motor)
{
return std::string{} + (motor.hallA ? '1' : '0') + (motor.hallB ? '1' : '0') + (motor.hallC ? '1' : '0');
}
std::string to_string(bobbycar::protocol::ControlType value)
{
switch (value)
{
using namespace bobbycar::protocol;
case ControlType::Commutation: return "Commutation";
case ControlType::Sinusoidal: return "Sinusoidal";
case ControlType::FieldOrientedControl: return "FieldOrientedControl";
}
return fmt::format("Unknown ControlType({})", std::to_underlying(value));
}
std::string to_string(bobbycar::protocol::ControlMode value)
{
switch (value)
{
using namespace bobbycar::protocol;
case ControlMode::OpenMode: return "OpenMode";
case ControlMode::Voltage: return "Voltage";
case ControlMode::Speed: return "Speed";
case ControlMode::Torque: return "Torque";
}
return fmt::format("Unknown ControlMode({})", std::to_underlying(value));
}
std::array<std::reference_wrapper<bobbycar::protocol::serial::MotorState>, 2> motorsInController(Controller &controller)
{
return {std::ref(controller.command.left), std::ref(controller.command.right)};
}
std::array<std::reference_wrapper<const bobbycar::protocol::serial::MotorState>, 2> motorsInController(const Controller &controller)
{
return {std::ref(controller.command.left), std::ref(controller.command.right)};
}
std::array<std::reference_wrapper<bobbycar::protocol::serial::MotorFeedback>, 2> motorFeedbacksInController(Controller &controller)
{
return {std::ref(controller.feedback.left), std::ref(controller.feedback.right)};
}
std::array<std::reference_wrapper<const bobbycar::protocol::serial::MotorFeedback>, 2> motorFeedbacksInController(const Controller &controller)
{
return {std::ref(controller.feedback.left), std::ref(controller.feedback.right)};
}
std::array<std::reference_wrapper<bobbycar::protocol::serial::MotorState>, 4> motors()
{
return {
std::ref(controllers.front.command.left), std::ref(controllers.front.command.right),
std::ref(controllers.back.command.left), std::ref(controllers.back.command.right)
};
}
void fixCommonParams()
{
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.iMotMax = settings.limits.iMotMax;
motor.iDcMax = settings.limits.iDcMax;
motor.nMotMax = settings.limits.nMotMax;
motor.fieldWeakMax = settings.limits.fieldWeakMax;
motor.phaseAdvMax = settings.limits.phaseAdvMax;
}
if (settings.buzzer.reverseBeep)
{
const auto x = motors();
const auto shouldBeep = std::all_of(std::begin(x), std::end(x), [](const bobbycar::protocol::serial::MotorState &motor){ return motor.pwm < 0; });
if (shouldBeep != currentlyReverseBeeping)
{
if (shouldBeep)
{
reverseBeepToggle = true;
lastReverseBeepToggle = espchrono::millis_clock::now();
for (auto &controller : controllers)
controller.command.buzzer = {.freq=settings.buzzer.reverseBeepFreq0, .pattern=0};
}
else
for (auto &controller : controllers)
controller.command.buzzer = {};
currentlyReverseBeeping = shouldBeep;
}
else if (shouldBeep && espchrono::millis_clock::now() - lastReverseBeepToggle >= std::chrono::milliseconds{reverseBeepToggle?settings.buzzer.reverseBeepDuration0:settings.buzzer.reverseBeepDuration1})
{
reverseBeepToggle = !reverseBeepToggle;
for (auto &controller : controllers)
controller.command.buzzer = {.freq=uint8_t(reverseBeepToggle?settings.buzzer.reverseBeepFreq0:settings.buzzer.reverseBeepFreq1), .pattern=0};
lastReverseBeepToggle = espchrono::millis_clock::now();
}
}
else if (currentlyReverseBeeping)
{
for (auto &controller : controllers)
controller.command.buzzer = {};
currentlyReverseBeeping = false;
}
controllers.front.command.left.enable = settings.controllerHardware.enableFrontLeft;
controllers.front.command.right.enable = settings.controllerHardware.enableFrontRight;
controllers.back.command.left.enable = settings.controllerHardware.enableBackLeft;
controllers.back.command.right.enable = settings.controllerHardware.enableBackRight;
if (settings.controllerHardware.invertFrontLeft)
{
controllers.front.command.left.pwm = -controllers.front.command.left.pwm;
controllers.front.command.left.nCruiseMotTgt = -controllers.front.command.left.nCruiseMotTgt;
}
if (settings.controllerHardware.invertFrontRight)
{
controllers.front.command.right.pwm = -controllers.front.command.right.pwm;
controllers.front.command.right.nCruiseMotTgt = -controllers.front.command.right.nCruiseMotTgt;
}
if (settings.controllerHardware.invertBackLeft)
{
controllers.back.command.left.pwm = -controllers.back.command.left.pwm;
controllers.back.command.left.nCruiseMotTgt = -controllers.back.command.left.nCruiseMotTgt;
}
if (settings.controllerHardware.invertBackRight)
{
controllers.back.command.right.pwm = -controllers.back.command.right.pwm;
controllers.back.command.right.nCruiseMotTgt = -controllers.back.command.right.nCruiseMotTgt;
}
}
void sendCommands()
{
float convertToKmh(float val);
float convertFromKmh(float val);
float convertToInch(float val);
float convertFromInch(float val);
float fixCurrent(int16_t value);
float fixBoardTemp(int16_t value);
std::string hallString(const bobbycar::protocol::serial::MotorFeedback &motor);
std::string to_string(bobbycar::protocol::ControlType value);
std::string to_string(bobbycar::protocol::ControlMode value);
std::array<std::reference_wrapper<bobbycar::protocol::serial::MotorState>, 2> motorsInController(Controller &controller);
std::array<std::reference_wrapper<const bobbycar::protocol::serial::MotorState>, 2> motorsInController(const Controller &controller);
std::array<std::reference_wrapper<bobbycar::protocol::serial::MotorFeedback>, 2> motorFeedbacksInController(Controller &controller);
std::array<std::reference_wrapper<const bobbycar::protocol::serial::MotorFeedback>, 2> motorFeedbacksInController(const Controller &controller);
std::array<std::reference_wrapper<bobbycar::protocol::serial::MotorState>, 4> motors();
void fixCommonParams();
void sendCommands();
#ifdef FEATURE_SERIAL
using namespace bobbycar::protocol::serial;
for (Controller &controller : controllers)
{
controller.command.start = Command::VALID_HEADER;
controller.command.checksum = calculateChecksum(controller.command);
controller.serial.get().write((uint8_t *) &controller.command, sizeof(controller.command));
}
void updateSwapFrontBack();
#endif
#ifdef FEATURE_CAN
can::sendCanCommands();
#endif
}
#ifdef FEATURE_SERIAL
void updateSwapFrontBack()
{
controllers.front.serial = settings.controllerHardware.swapFrontBack ? Serial2 : Serial1;
controllers.back.serial = settings.controllerHardware.swapFrontBack ? Serial1 : Serial2;
}
#endif
bool loadSettings()
{
bool result{true};
if (!settingsPersister.load(settings))
result = false;
if (!settingsPersister.load(stringSettings))
result = false;
return result;
}
bool saveSettings()
{
if (simplified) return true;
bool result{true};
if (!settingsPersister.save(settings))
result = false;
if (!settingsPersister.save(stringSettings))
result = false;
return result;
}
void updateAccumulators()
{
avgSpeed = 0.f;
sumCurrent = 0.f;
uint8_t count{0};
for (const Controller &controller : controllers)
{
if (!controller.feedbackValid)
continue;
avgSpeed +=
controller.feedback.left.speed * (controller.invertLeft ? -1 : 1) +
controller.feedback.right.speed * (controller.invertRight ? -1 : 1);
sumCurrent +=
controller.feedback.left.dcLink +
controller.feedback.right.dcLink;
count +=2;
}
if (count)
avgSpeed /= count;
sumCurrent = fixCurrent(sumCurrent);
avgSpeedKmh = convertToKmh(avgSpeed);
}
void readPotis()
{
[[maybe_unused]]
constexpr auto sampleMultipleTimes = [](int pin){
analogRead(pin);
double sum{};
const auto sampleCount = settings.boardcomputerHardware.sampleCount;
for (int i = 0; i < sampleCount; i++)
sum += analogRead(pin);
return sum / sampleCount;
};
raw_gas = std::nullopt;
raw_brems = std::nullopt;
#ifdef FEATURE_CAN
const auto now = espchrono::millis_clock::now();
if (can::can_gas)
{
if (now - can::last_can_gas < 100ms)
raw_gas = *can::can_gas;
else
can::can_gas = std::nullopt;
}
if (can::can_brems)
{
if (now - can::last_can_brems < 100ms)
raw_brems = *can::can_brems;
else
can::can_brems = std::nullopt;
}
#endif
#ifdef FEATURE_ADC_IN
if (!raw_gas)
raw_gas = sampleMultipleTimes(PINS_GAS);
if (!raw_brems)
raw_brems = sampleMultipleTimes(PINS_BREMS);
#endif
if (raw_gas)
gas = cpputils::mapValueClamped<float>(*raw_gas, settings.boardcomputerHardware.gasMin, settings.boardcomputerHardware.gasMax, 0., 1000.);
else
gas = std::nullopt;
if (raw_brems)
brems = cpputils::mapValueClamped<float>(*raw_brems, settings.boardcomputerHardware.bremsMin, settings.boardcomputerHardware.bremsMax, 0., 1000.);
else
brems = std::nullopt;
#ifdef FEATURE_GAMETRAK
raw_gametrakX = sampleMultipleTimes(PINS_GAMETRAKX);
gametrakX = cpputils::mapValueClamped<float>(raw_gametrakX, settings.boardcomputerHardware.gametrakXMin, settings.boardcomputerHardware.gametrakXMax, 0., 1000.);
raw_gametrakY = sampleMultipleTimes(PINS_GAMETRAKY);
gametrakY = cpputils::mapValueClamped<float>(raw_gametrakY, settings.boardcomputerHardware.gametrakYMin, settings.boardcomputerHardware.gametrakYMax, 0., 1000.);
raw_gametrakDist = sampleMultipleTimes(PINS_GAMETRAKDIST);
gametrakDist = cpputils::mapValueClamped<float>(raw_gametrakDist, settings.boardcomputerHardware.gametrakDistMin, settings.boardcomputerHardware.gametrakDistMax, 0., 1000.);
#endif
}
}
bool loadSettings();
bool saveSettings();
void updateAccumulators();
void readPotis();

View File

@ -0,0 +1,99 @@
#include "wifi_bobbycar.h"
// system includes
#include <optional>
// esp-idf includes
#include <esp_log.h>
// 3rdparty lib includes
#include <espwifistack.h>
// local includes
#include "globals.h"
namespace {
wifi_stack::config wifi_create_config();
std::optional<wifi_stack::sta_config> wifi_create_sta_config();
std::optional<wifi_stack::ap_config> wifi_create_ap_config();
}
void wifi_begin()
{
wifi_stack::init(wifi_create_config());
}
void wifi_update()
{
wifi_stack::update(wifi_create_config());
}
esp_err_t wifi_scan()
{
const auto &sta_config = wifi_create_sta_config();
if (!sta_config)
{
ESP_LOGE("BOBBY", "no sta enabled");
return ESP_FAIL;
}
if (const auto result = wifi_stack::begin_scan(*sta_config); !result)
{
ESP_LOGE("BOBBY", "begin_scan() failed with %.*s", result.error().size(), result.error().data());
return ESP_FAIL;
}
return ESP_OK;
}
namespace {
wifi_stack::config wifi_create_config()
{
return wifi_stack::config {
.sta = wifi_create_sta_config(),
.ap = wifi_create_ap_config()
};
}
std::optional<wifi_stack::sta_config> wifi_create_sta_config()
{
if (!settings.wifiSettings.wifiEnabled)
return std::nullopt;
return wifi_stack::sta_config {
.hostname = deviceName,
.wifis = std::array<wifi_stack::wifi_entry, 10> {
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[0].ssid, .key = stringSettings.wifis[0].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[1].ssid, .key = stringSettings.wifis[1].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[2].ssid, .key = stringSettings.wifis[2].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[3].ssid, .key = stringSettings.wifis[3].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[4].ssid, .key = stringSettings.wifis[4].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[5].ssid, .key = stringSettings.wifis[5].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[6].ssid, .key = stringSettings.wifis[6].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[7].ssid, .key = stringSettings.wifis[7].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[8].ssid, .key = stringSettings.wifis[8].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[9].ssid, .key = stringSettings.wifis[9].key }
},
.min_rssi = -90
};
}
std::optional<wifi_stack::ap_config> wifi_create_ap_config()
{
return wifi_stack::ap_config {
.hostname = deviceName,
.ssid = deviceName,
.key = STRING(AP_PASSWORD),
.static_ip = {
.ip = {10, 0, 0, 1},
.subnet = {255, 255, 255, 0},
.gateway = {10, 0, 0, 1},
},
.channel = 1,
.authmode = WIFI_AUTH_WPA2_PSK,
.ssid_hidden = false,
.max_connection = 4,
.beacon_interval = 100
};
}
} // namespace

View File

@ -1,70 +1,10 @@
#pragma once
// esp-idf includes
#include <esp_log.h>
#include <esp_err.h>
// 3rdparty lib includes
#include <espwifistack.h>
void wifi_begin();
// local includes
#include "globals.h"
void wifi_update();
namespace {
wifi_stack::config wifi_create_config()
{
return wifi_stack::config {
.hostname = deviceName,
.sta = {
.enabled = settings.wifiSettings.wifiEnabled,
.wifis = std::array<wifi_stack::wifi_entry, 10> {
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[0].ssid, .key = stringSettings.wifis[0].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[1].ssid, .key = stringSettings.wifis[1].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[2].ssid, .key = stringSettings.wifis[2].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[3].ssid, .key = stringSettings.wifis[3].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[4].ssid, .key = stringSettings.wifis[4].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[5].ssid, .key = stringSettings.wifis[5].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[6].ssid, .key = stringSettings.wifis[6].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[7].ssid, .key = stringSettings.wifis[7].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[8].ssid, .key = stringSettings.wifis[8].key },
wifi_stack::wifi_entry { .ssid = stringSettings.wifis[9].ssid, .key = stringSettings.wifis[9].key }
},
.min_rssi = -90
},
.ap = {
.ssid = deviceName,
.key = STRING(AP_PASSWORD),
.static_ip = {
.ip = {10, 0, 0, 1},
.subnet = {255, 255, 255, 0},
.gateway = {10, 0, 0, 1},
},
.channel = 1,
.authmode = WIFI_AUTH_WPA2_PSK,
.ssid_hidden = false,
.max_connection = 4,
.beacon_interval = 100
}
};
}
void wifi_begin()
{
wifi_stack::init(wifi_create_config());
}
void wifi_update()
{
wifi_stack::update(wifi_create_config());
}
esp_err_t wifi_scan()
{
if (const auto result = wifi_stack::begin_scan(wifi_create_config()); result != ESP_OK)
{
ESP_LOGE("BOBBY", "begin_scan() failed with %s", esp_err_to_name(result));
return result;
}
return ESP_OK;
}
} // namespace
esp_err_t wifi_scan();