Moved sources into separate .cpp files
This commit is contained in:
Submodule components/cpputils updated: 82498617f2...0b4ba83742
Submodule components/espasynchttpreq updated: 8b05591702...ba852bbbf4
Submodule components/espcpputils updated: 3a3e4bc044...850a42adc4
Submodule components/espwifistack updated: 34792268a8...6eb911c8ea
@ -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
|
||||
}
|
||||
|
455
main/can.cpp
455
main/can.cpp
@ -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
|
||||
|
449
main/can.h
449
main/can.h
@ -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
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <espwifistack.h>
|
||||
#include <esphttpdutils.h>
|
||||
#include <fmt/core.h>
|
||||
#include <tickchrono.h>
|
||||
|
||||
// local includes
|
||||
#include "globals.h"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
@ -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>;
|
||||
|
||||
|
@ -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{};
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
308
main/utils.cpp
308
main/utils.cpp
@ -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
|
||||
}
|
||||
|
327
main/utils.h
327
main/utils.h
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
Reference in New Issue
Block a user