Merge pull request #118 from bobbycar-graz/multiple_compile_units
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>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace {
|
|
||||||
#ifdef FEATURE_BLUETOOTH
|
#ifdef FEATURE_BLUETOOTH
|
||||||
enum class BluetoothMode : uint8_t
|
enum class BluetoothMode : uint8_t
|
||||||
{
|
{
|
||||||
@ -11,4 +10,3 @@ enum class BluetoothMode : uint8_t
|
|||||||
Slave
|
Slave
|
||||||
};
|
};
|
||||||
#endif
|
#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
|
#pragma once
|
||||||
|
|
||||||
#include <cstring>
|
// system includes
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#include <driver/gpio.h>
|
// 3rdparty lib includes
|
||||||
#include <driver/twai.h>
|
|
||||||
#include <esp_log.h>
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#include <espchrono.h>
|
#include <espchrono.h>
|
||||||
#include <tickchrono.h>
|
|
||||||
|
|
||||||
#include "bobbycar-can.h"
|
|
||||||
|
|
||||||
#include "globals.h"
|
|
||||||
#include "buttons.h"
|
|
||||||
|
|
||||||
#ifdef CAN_PLUGIN
|
#ifdef CAN_PLUGIN
|
||||||
#pragma message "Activating Can Plugin"
|
#pragma message "Activating Can Plugin"
|
||||||
@ -24,9 +14,8 @@
|
|||||||
|
|
||||||
namespace can {
|
namespace can {
|
||||||
|
|
||||||
namespace {
|
extern std::optional<int16_t> can_gas, can_brems;
|
||||||
std::optional<int16_t> can_gas, can_brems;
|
extern espchrono::millis_clock::time_point last_can_gas, last_can_brems;
|
||||||
espchrono::millis_clock::time_point last_can_gas{}, last_can_brems{};
|
|
||||||
|
|
||||||
struct CanButtonsState
|
struct CanButtonsState
|
||||||
{
|
{
|
||||||
@ -39,428 +28,10 @@ struct CanButtonsState
|
|||||||
bool profile2{};
|
bool profile2{};
|
||||||
bool profile3{};
|
bool profile3{};
|
||||||
};
|
};
|
||||||
CanButtonsState lastButtonsState;
|
extern CanButtonsState lastButtonsState;
|
||||||
|
|
||||||
void initCan()
|
void initCan();
|
||||||
{
|
bool tryParseCanInput();
|
||||||
ESP_LOGI(TAG, "called");
|
void parseCanInput();
|
||||||
|
void sendCanCommands();
|
||||||
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
|
|
||||||
} // namespace can
|
} // namespace can
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <espwifistack.h>
|
#include <espwifistack.h>
|
||||||
#include <esphttpdutils.h>
|
#include <esphttpdutils.h>
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
#include <tickchrono.h>
|
||||||
|
|
||||||
// local includes
|
// local includes
|
||||||
#include "globals.h"
|
#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;
|
class HardwareSerial;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace {
|
struct Controller
|
||||||
|
{
|
||||||
struct Controller {
|
|
||||||
Controller(
|
Controller(
|
||||||
#ifdef FEATURE_SERIAL
|
#ifdef FEATURE_SERIAL
|
||||||
HardwareSerial &serial,
|
HardwareSerial &serial,
|
||||||
#endif
|
#endif
|
||||||
bool &enableLeft, bool &enableRight, bool &invertLeft, bool &invertRight,
|
bool &enableLeft, bool &enableRight, bool &invertLeft, bool &invertRight,
|
||||||
int16_t &voltageCalib30V, int16_t &voltageCalib50V) :
|
int16_t &voltageCalib30V, int16_t &voltageCalib50V);
|
||||||
#ifdef FEATURE_SERIAL
|
|
||||||
serial{serial},
|
|
||||||
#endif
|
|
||||||
enableLeft{enableLeft}, enableRight{enableRight}, invertLeft{invertLeft}, invertRight{invertRight},
|
|
||||||
voltageCalib30V{voltageCalib30V}, voltageCalib50V{voltageCalib50V}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
// Controller(const Controller &) = delete;
|
// Controller(const Controller &) = delete;
|
||||||
// Controller &operator=(const Controller &) = delete;
|
// Controller &operator=(const Controller &) = delete;
|
||||||
|
|
||||||
@ -61,18 +53,5 @@ struct Controller {
|
|||||||
FeedbackParser parser{serial, feedbackValid, feedback};
|
FeedbackParser parser{serial, feedbackValid, feedback};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
float getCalibratedVoltage(bool applyCalibration) const
|
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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
@ -9,35 +9,34 @@
|
|||||||
// local includes
|
// local includes
|
||||||
#include "textinterface.h"
|
#include "textinterface.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
//template<const char *Ttext, typename TreturnType, TreturnType (EspClass::*Tmethod)()>
|
//template<const char *Ttext, typename TreturnType, TreturnType (EspClass::*Tmethod)()>
|
||||||
//using EspStatusTextHelper = StatusTextHelper<Ttext, EspClass, &ESP, TreturnType, 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)); }};
|
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)); }};
|
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)); }};
|
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)); }};
|
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)); }};
|
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)); }};
|
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)); }};
|
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)); }};
|
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;
|
std::string reset_reason_string;
|
||||||
switch (esp_reset_reason()) {
|
switch (esp_reset_reason()) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -80,32 +79,32 @@ class LastRebootReasonText : public virtual TextInterface { public: std::string
|
|||||||
return fmt::format("Last Reboot Reason: {}", reset_reason_string); }};
|
return fmt::format("Last Reboot Reason: {}", reset_reason_string); }};
|
||||||
|
|
||||||
constexpr char TEXT_ESPCHIPREVISION[] = "Chip revision: ";
|
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: ";
|
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: ";
|
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: ";
|
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: ";
|
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: ";
|
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: ";
|
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: ";
|
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: ";
|
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: ";
|
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 "settingspersister.h"
|
||||||
#include "macros_bobbycar.h"
|
#include "macros_bobbycar.h"
|
||||||
|
|
||||||
using namespace espgui;
|
extern std::optional<int16_t> raw_gas, raw_brems;
|
||||||
|
extern std::optional<float> gas, brems;
|
||||||
namespace {
|
|
||||||
std::optional<int16_t> raw_gas, raw_brems;
|
|
||||||
std::optional<float> gas, brems;
|
|
||||||
|
|
||||||
#ifdef FEATURE_GAMETRAK
|
#ifdef FEATURE_GAMETRAK
|
||||||
int16_t raw_gametrakX, raw_gametrakY, raw_gametrakDist;
|
extern int16_t raw_gametrakX;
|
||||||
float gametrakX, gametrakY, gametrakDist;
|
extern int16_t raw_gametrakY;
|
||||||
|
extern int16_t raw_gametrakDist;
|
||||||
|
extern float gametrakX;
|
||||||
|
extern float gametrakY;
|
||||||
|
extern float gametrakDist;
|
||||||
#endif
|
#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
|
#ifdef GLOBALS_PLUGIN
|
||||||
#include GLOBALS_PLUGIN
|
#include GLOBALS_PLUGIN
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAS_SIMPLIFIED)
|
extern bool simplified;
|
||||||
bool simplified = true;
|
|
||||||
#else
|
|
||||||
bool simplified = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Settings settings;
|
extern Settings settings;
|
||||||
StringSettings stringSettings;
|
extern StringSettings stringSettings;
|
||||||
SettingsPersister settingsPersister;
|
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>
|
class Controllers : public std::array<Controller, 2>
|
||||||
{
|
{
|
||||||
@ -86,22 +85,23 @@ public:
|
|||||||
Controller &back{operator[](1)};
|
Controller &back{operator[](1)};
|
||||||
};
|
};
|
||||||
|
|
||||||
Controllers controllers;
|
extern Controllers controllers;
|
||||||
struct FrontControllerGetter { static Controller &get() { return controllers.front; }};
|
struct FrontControllerGetter { static Controller &get() { return controllers.front; }};
|
||||||
struct BackControllerGetter { static Controller &get() { return controllers.back; }};
|
struct BackControllerGetter { static Controller &get() { return controllers.back; }};
|
||||||
|
|
||||||
struct {
|
struct Performance {
|
||||||
espchrono::millis_clock::time_point lastTime;
|
espchrono::millis_clock::time_point lastTime;
|
||||||
int current{};
|
int current{};
|
||||||
int last{};
|
int last{};
|
||||||
} performance;
|
};
|
||||||
|
extern Performance performance;
|
||||||
|
|
||||||
#ifdef FEATURE_BLUETOOTH
|
#ifdef FEATURE_BLUETOOTH
|
||||||
BluetoothSerial bluetoothSerial;
|
extern BluetoothSerial bluetoothSerial;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ModeInterface *lastMode{};
|
extern ModeInterface *lastMode;
|
||||||
ModeInterface *currentMode{};
|
extern ModeInterface *currentMode;
|
||||||
|
|
||||||
#ifdef FEATURE_LEDBACKLIGHT
|
#ifdef FEATURE_LEDBACKLIGHT
|
||||||
constexpr const bool ledBacklightInverted =
|
constexpr const bool ledBacklightInverted =
|
||||||
@ -112,4 +112,3 @@ constexpr const bool ledBacklightInverted =
|
|||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
class ModeInterface
|
||||||
|
{
|
||||||
namespace {
|
|
||||||
class ModeInterface {
|
|
||||||
public:
|
public:
|
||||||
virtual ~ModeInterface() = default;
|
virtual ~ModeInterface() = default;
|
||||||
|
|
||||||
@ -13,4 +11,4 @@ public:
|
|||||||
|
|
||||||
virtual const char *displayName() const = 0;
|
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
|
#pragma once
|
||||||
|
|
||||||
|
// system includes
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include <Arduino.h>
|
// 3rdparty lib includes
|
||||||
|
|
||||||
#include <espchrono.h>
|
#include <espchrono.h>
|
||||||
|
|
||||||
|
// local includes
|
||||||
#include "modeinterface.h"
|
#include "modeinterface.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
class DefaultMode : public ModeInterface
|
class DefaultMode : public ModeInterface
|
||||||
{
|
{
|
||||||
using Base = ModeInterface;
|
using Base = ModeInterface;
|
||||||
@ -31,154 +31,5 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
namespace modes {
|
namespace modes {
|
||||||
DefaultMode defaultMode;
|
extern 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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
|
#pragma once
|
||||||
|
|
||||||
namespace {
|
// local includes
|
||||||
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"
|
|
||||||
|
|
||||||
#include "bobbycar-common.h"
|
#include "bobbycar-common.h"
|
||||||
|
#include "modeinterface.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
#ifdef FEATURE_GAMETRAK
|
#ifdef FEATURE_GAMETRAK
|
||||||
class GametrakMode : public ModeInterface
|
class GametrakMode : public ModeInterface
|
||||||
{
|
{
|
||||||
@ -33,70 +20,6 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
namespace modes {
|
namespace modes {
|
||||||
GametrakMode gametrakMode;
|
extern 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
|
#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
|
#pragma once
|
||||||
|
|
||||||
#include "modeinterface.h"
|
// local includes
|
||||||
#include "globals.h"
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
#include "bobbycar-common.h"
|
#include "bobbycar-common.h"
|
||||||
|
#include "modeinterface.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
class IgnoreInputMode : public ModeInterface
|
class IgnoreInputMode : public ModeInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -24,20 +21,3 @@ private:
|
|||||||
const bobbycar::protocol::ControlType m_ctrlTyp;
|
const bobbycar::protocol::ControlType m_ctrlTyp;
|
||||||
const bobbycar::protocol::ControlMode m_ctrlMod;
|
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
|
#pragma once
|
||||||
|
|
||||||
#include "modeinterface.h"
|
// local includes
|
||||||
#include "globals.h"
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
#include "bobbycar-common.h"
|
#include "bobbycar-common.h"
|
||||||
|
#include "modeinterface.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
class LarsmMode : public ModeInterface
|
class LarsmMode : public ModeInterface
|
||||||
{
|
{
|
||||||
using Base = ModeInterface;
|
using Base = ModeInterface;
|
||||||
@ -25,109 +22,5 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
namespace modes {
|
namespace modes {
|
||||||
LarsmMode larsmMode;
|
extern 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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
|
// local includes
|
||||||
#include "bobbycar-common.h"
|
#include "bobbycar-common.h"
|
||||||
|
|
||||||
#include "modeinterface.h"
|
#include "modeinterface.h"
|
||||||
#include "globals.h"
|
|
||||||
#include "utils.h"
|
|
||||||
#include "defaultmode.h"
|
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
struct RemoteCommand {
|
struct RemoteCommand {
|
||||||
int16_t frontLeft{};
|
int16_t frontLeft{};
|
||||||
int16_t frontRight{};
|
int16_t frontRight{};
|
||||||
@ -41,48 +34,5 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
namespace modes {
|
namespace modes {
|
||||||
RemoteControlMode remoteControlMode;
|
extern 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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
|
#pragma once
|
||||||
|
|
||||||
|
// system includes
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
// local includes
|
||||||
#include "bobbycar-common.h"
|
#include "bobbycar-common.h"
|
||||||
|
|
||||||
#include "modeinterface.h"
|
#include "modeinterface.h"
|
||||||
#include "globals.h"
|
|
||||||
#include "utils.h"
|
|
||||||
#include "defaultmode.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
class TempomatMode : public ModeInterface
|
class TempomatMode : public ModeInterface
|
||||||
{
|
{
|
||||||
using Base = ModeInterface;
|
using Base = ModeInterface;
|
||||||
@ -24,56 +21,5 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
namespace modes {
|
namespace modes {
|
||||||
TempomatMode tempomatMode;
|
extern 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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
|
.lockscreen = defaultLockscreen
|
||||||
};
|
};
|
||||||
|
|
||||||
StringSettings makeDefaultStringSettings()
|
StringSettings makeDefaultStringSettings();
|
||||||
{
|
} // namespace presets
|
||||||
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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "unifiedmodelmode.h"
|
#include "unifiedmodelmode.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
enum class LarsmModeMode : uint8_t { Mode1, Mode2, Mode3, Mode4 };
|
enum class LarsmModeMode : uint8_t { Mode1, Mode2, Mode3, Mode4 };
|
||||||
|
|
||||||
struct Settings
|
struct Settings
|
||||||
@ -358,4 +357,3 @@ void Settings::executeForEveryProfileSetting(T &&callable)
|
|||||||
callable("larsm.mode", larsmMode.mode);
|
callable("larsm.mode", larsmMode.mode);
|
||||||
callable("larsm.iters", larsmMode.iterations);
|
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
|
#pragma once
|
||||||
|
|
||||||
// system includes
|
// system includes
|
||||||
#include <type_traits>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
// esp-idf includes
|
|
||||||
#include <esp_log.h>
|
|
||||||
#include <nvs_flash.h>
|
|
||||||
#include <nvs.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
|
class SettingsPersister
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -55,409 +32,3 @@ private:
|
|||||||
};
|
};
|
||||||
std::optional<CurrentlyOpenProfile> m_profile;
|
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 <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace {
|
|
||||||
struct StringSettings
|
struct StringSettings
|
||||||
{
|
{
|
||||||
struct ConfiguredWifi {
|
struct ConfiguredWifi {
|
||||||
@ -30,7 +29,6 @@ struct StringSettings
|
|||||||
std::string timeServer;
|
std::string timeServer;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void executeForEveryCommonSetting(T &&callable);
|
void executeForEveryCommonSetting(T &&callable);
|
||||||
|
|
||||||
@ -96,17 +94,17 @@ void StringSettings::executeForEveryCommonSetting(T &&callable)
|
|||||||
callable("otaName3", otaServers[3].name);
|
callable("otaName3", otaServers[3].name);
|
||||||
callable("otaUrl3", otaServers[3].url);
|
callable("otaUrl3", otaServers[3].url);
|
||||||
callable("otaName4", otaServers[4].name);
|
callable("otaName4", otaServers[4].name);
|
||||||
callable("otaUrl4", otaServers[4].url);/*
|
callable("otaUrl4", otaServers[4].url);
|
||||||
callable("otaName5", otaServers[5].name);
|
// callable("otaName5", otaServers[5].name);
|
||||||
callable("otaUrl5", otaServers[5].url);
|
// callable("otaUrl5", otaServers[5].url);
|
||||||
callable("otaName6", otaServers[6].name);
|
// callable("otaName6", otaServers[6].name);
|
||||||
callable("otaUrl6", otaServers[6].url);
|
// callable("otaUrl6", otaServers[6].url);
|
||||||
callable("otaName7", otaServers[7].name);
|
// callable("otaName7", otaServers[7].name);
|
||||||
callable("otaUrl7", otaServers[7].url);
|
// callable("otaUrl7", otaServers[7].url);
|
||||||
callable("otaName8", otaServers[8].name);
|
// callable("otaName8", otaServers[8].name);
|
||||||
callable("otaUrl8", otaServers[8].url);
|
// callable("otaUrl8", otaServers[8].url);
|
||||||
callable("otaName9", otaServers[9].name);
|
// callable("otaName9", otaServers[9].name);
|
||||||
callable("otaUrl9", otaServers[9].url);*/
|
// callable("otaUrl9", otaServers[9].url);
|
||||||
|
|
||||||
callable("otaserver", otaServerUrl);
|
callable("otaserver", otaServerUrl);
|
||||||
#endif
|
#endif
|
||||||
@ -116,4 +114,3 @@ template<typename T>
|
|||||||
void StringSettings::executeForEveryProfileSetting(T &&callable)
|
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
|
#pragma once
|
||||||
|
|
||||||
|
// system includes
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
// local includes
|
||||||
#include "bobbycar-common.h"
|
#include "bobbycar-common.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
enum class UnifiedModelMode : uint8_t
|
enum class UnifiedModelMode : uint8_t
|
||||||
{
|
{
|
||||||
Commutation,
|
Commutation,
|
||||||
@ -14,22 +15,4 @@ enum class UnifiedModelMode : uint8_t
|
|||||||
FocTorque
|
FocTorque
|
||||||
};
|
};
|
||||||
|
|
||||||
std::pair<bobbycar::protocol::ControlType, bobbycar::protocol::ControlMode> split(UnifiedModelMode mode)
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
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"
|
#include "can.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace {
|
extern bool currentlyReverseBeeping;
|
||||||
bool currentlyReverseBeeping;
|
extern bool reverseBeepToggle;
|
||||||
bool reverseBeepToggle;
|
extern espchrono::millis_clock::time_point lastReverseBeepToggle;
|
||||||
espchrono::millis_clock::time_point lastReverseBeepToggle;
|
|
||||||
|
|
||||||
float convertToKmh(float val)
|
float convertToKmh(float val);
|
||||||
{
|
float convertFromKmh(float val);
|
||||||
return val /* / settings.controllerHardware.numMagnetPoles */ / 60.f * settings.controllerHardware.wheelDiameter / 1000.f * 3.14159265359f * 3.6f;
|
float convertToInch(float val);
|
||||||
}
|
float convertFromInch(float val);
|
||||||
|
float fixCurrent(int16_t value);
|
||||||
float convertFromKmh(float val)
|
float fixBoardTemp(int16_t value);
|
||||||
{
|
std::string hallString(const bobbycar::protocol::serial::MotorFeedback &motor);
|
||||||
return val /* * settings.controllerHardware.numMagnetPoles */ * 60.f / settings.controllerHardware.wheelDiameter * 1000.f / 3.14159265359f / 3.6f;
|
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);
|
||||||
float convertToInch(float val)
|
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);
|
||||||
return val / 25.4f;
|
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();
|
||||||
float convertFromInch(float val)
|
void sendCommands();
|
||||||
{
|
|
||||||
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
|
#ifdef FEATURE_SERIAL
|
||||||
using namespace bobbycar::protocol::serial;
|
void updateSwapFrontBack();
|
||||||
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
|
#endif
|
||||||
#ifdef FEATURE_CAN
|
bool loadSettings();
|
||||||
can::sendCanCommands();
|
bool saveSettings();
|
||||||
#endif
|
void updateAccumulators();
|
||||||
}
|
void readPotis();
|
||||||
|
|
||||||
#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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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
|
#pragma once
|
||||||
|
|
||||||
// esp-idf includes
|
// esp-idf includes
|
||||||
#include <esp_log.h>
|
#include <esp_err.h>
|
||||||
|
|
||||||
// 3rdparty lib includes
|
void wifi_begin();
|
||||||
#include <espwifistack.h>
|
|
||||||
|
|
||||||
// local includes
|
void wifi_update();
|
||||||
#include "globals.h"
|
|
||||||
|
|
||||||
namespace {
|
esp_err_t wifi_scan();
|
||||||
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
|
|
||||||
|
Reference in New Issue
Block a user