Merge pull request #79 from bobbycar-graz/feature_can

Feature can (and std::string)
This commit is contained in:
2021-06-28 14:40:26 +02:00
committed by GitHub
56 changed files with 1413 additions and 627 deletions

View File

@ -63,10 +63,12 @@ build_flags =
-DTFT_RST=2
-DSPI_FREQUENCY=27000000
-DDEFAULT_SWAPSCREENBYTES=false
-DFEATURE_SERIAL
-DPINS_RX1=4
-DPINS_TX1=5
-DPINS_RX2=25
-DPINS_TX2=26
-DFEATURE_ADC_IN
-DFEATURE_MOSFETS
-DPINS_MOSFET0=18
-DPINS_MOSFET1=19
@ -96,16 +98,18 @@ platform = ${common_env_data.platform}
board = ${common_env_data.board}
framework = ${common_env_data.framework}
platform_packages = ${common_env_data.platform_packages}
board_build.partitions = partitions_4M_ota.csv
board_build.partitions = partitions_16M_ota.csv
lib_deps =
${common_env_data.lib_deps}
${webserver_common.lib_deps}
; ${webserver_common.lib_deps}
lib_compat_mode = ${common_env_data.lib_compat_mode}
build_unflags = ${common_env_data.build_unflags}
build_flags =
${common_env_data.build_flags}
-DPINS_GAS=33
-DFEATURE_ADC_IN
-DPINS_GAS=34
-DPINS_BREMS=35
; -DPINS_LED=23
-DILI9341_DRIVER=1
-DTFT_MOSI=13
-DTFT_SCLK=15
@ -114,10 +118,12 @@ build_flags =
-DTFT_RST=2
-DSPI_FREQUENCY=27000000
-DDEFAULT_SWAPSCREENBYTES=false
-DPINS_RX1=4
-DPINS_TX1=5
-DPINS_RX2=22
-DPINS_TX2=23
-DFEATURE_CAN
; -DFEATURE_SERIAL
; -DPINS_RX1=4
; -DPINS_TX1=5
; -DPINS_RX2=22
; -DPINS_TX2=23
${default_wheels_inverted.build_flags}
-DDEFAULT_WHEELDIAMETER=255
; -DFEATURE_MOSFETS
@ -127,30 +133,30 @@ build_flags =
${default_limits.build_flags}
-DDEVICE_PREFIX=bobbyquad
-DAP_PASSWORD=Passwort_123
${webserver_common.build_flags}
; ${webserver_common.build_flags}
; -DFEATURE_ARDUINOOTA
; -DFEATURE_WEBOTA
-DFEATURE_DPAD_5WIRESW
-DPINS_DPAD_5WIRESW_OUT=32
-DPINS_DPAD_5WIRESW_IN1=25
-DPINS_DPAD_5WIRESW_IN2=26
-DPINS_DPAD_5WIRESW_IN3=27
-DPINS_DPAD_5WIRESW_IN4=21
-DDPAD_5WIRESW_UP=4
-DPINS_DPAD_5WIRESW_OUT=18
-DPINS_DPAD_5WIRESW_IN1=19
-DPINS_DPAD_5WIRESW_IN2=27
-DPINS_DPAD_5WIRESW_IN3=32
-DPINS_DPAD_5WIRESW_IN4=33
-DDPAD_5WIRESW_UP=6
-DDPAD_5WIRESW_DOWN=3
-DDPAD_5WIRESW_CONFIRM=7
-DDPAD_5WIRESW_BACK=0
-DDPAD_5WIRESW_BACK=2
-DDPAD_5WIRESW_PROFILE0=1
-DDPAD_5WIRESW_PROFILE1=5
-DDPAD_5WIRESW_PROFILE2=2
-DDPAD_5WIRESW_PROFILE3=6
-DDPAD_5WIRESW_PROFILE2=0
-DDPAD_5WIRESW_PROFILE3=4
; -DDPAD_5WIRESW_DEBUG
-DDEFAULT_GASMIN=750
-DDEFAULT_GASMAX=3700
-DDEFAULT_BREMSMIN=880
-DDEFAULT_BREMSMAX=4000
; -DFEATURE_BLUETOOTH
; -DFEATURE_BMS
-DDEFAULT_GASMIN=150
-DDEFAULT_GASMAX=1300
-DDEFAULT_BREMSMIN=200
-DDEFAULT_BREMSMAX=1500
-DFEATURE_BLUETOOTH
-DFEATURE_BMS
; -DFEATURE_GAMETRAK
; -DPINS_GAMETRAKX=34
; -DPINS_GAMETRAKY=39
@ -161,7 +167,7 @@ build_flags =
; -DDEFAULT_GAMETRAKYMAX=4095
; -DDEFAULT_GAMETRAKDISTMIN=0
; -DDEFAULT_GAMETRAKDISTMAX=4095
-DFEATURE_CLOUD
; -DFEATURE_CLOUD
[env:feedc0de_usb]
platform = ${feedc0de.platform}
@ -330,10 +336,12 @@ build_flags =
${default_wheels_inverted.build_flags}
-DDEFAULT_WHEELDIAMETER=165
; TODO: actually assign pins
-DFEATURE_SERIAL
-DPINS_RX1=22
-DPINS_TX1=25
-DPINS_RX2=23
-DPINS_TX2=34
-DFEATURE_ADC_IN
-DPINS_GAS=35
-DPINS_BREMS=33
-DDEFAULT_GASMIN=0
@ -409,6 +417,7 @@ lib_compat_mode = ${common_env_data.lib_compat_mode}
build_unflags = ${common_env_data.build_unflags}
build_flags =
${common_env_data.build_flags}
-DFEATURE_ADC_IN
-DPINS_GAS=33
-DPINS_BREMS=35
-DILI9341_DRIVER=1
@ -419,6 +428,7 @@ build_flags =
-DTFT_RST=2
-DSPI_FREQUENCY=27000000
-DDEFAULT_SWAPSCREENBYTES=false
-DFEATURE_SERIAL
-DPINS_RX1=4
-DPINS_TX1=5
-DPINS_RX2=25

View File

@ -3,25 +3,16 @@
#include <HardwareSerial.h>
#include "actioninterface.h"
#include "globals.h"
#include "presets.h"
#include "settingsutils.h"
namespace {
template<uint8_t profile>
template<uint8_t index>
class SwitchProfileAction : public virtual ActionInterface
{
public:
void triggered() override
{
settings = presets::defaultSettings;
if (settingsPersister.openProfile(profile))
{
if (!settingsPersister.load(settings))
Serial.println("SwitchProfileAction::triggered() load failed");
}
else
Serial.println("SwitchProfileAction::triggered() openProfile failed");
switchProfile(index);
}
};
}

View File

@ -4,9 +4,11 @@
#include "utils.h"
namespace {
#ifdef FEATURE_SERIAL
class UpdateSwapFrontBackAction : public virtual ActionInterface
{
public:
void triggered() override { updateSwapFrontBack(); }
};
#endif
}

View File

@ -19,21 +19,21 @@ using BluetoothHasClientText = BluetoothStatusTextHelper<TEXT_BLUETOOTHHASCLIENT
//using BluetoothConnectedText = BluetoothStatusTextHelper<TEXT_BLUETOOTHCONNECTED, bool, &BluetoothSerial::connected>;
struct BluetoothConnectedText : public virtual TextInterface {
public:
String text() const override { return String{"connected: "} + toString(bluetoothSerial.connected()); }
std::string text() const override { return std::string{"connected: "} + to_string(bluetoothSerial.connected()); }
};
//constexpr char TEXT_BLUETOOTHISREADY[] = "Is ready: ";
//using BluetoothIsReadyText = BluetoothStatusTextHelper<TEXT_BLUETOOTHISREADY, bool, &BluetoothSerial::isReady>;
struct BluetoothIsReadyText : public virtual TextInterface {
public:
String text() const override { return String{"isReady: "} + toString(bluetoothSerial.isReady()); }
std::string text() const override { return std::string{"isReady: "} + to_string(bluetoothSerial.isReady()); }
};
//constexpr char TEXT_BLUETOOTHISREADYMASTER[] = "Is ready (M): ";
//using BluetoothIsReadyMasterText = BluetoothStatusTextHelper<TEXT_BLUETOOTHISREADYMASTER, bool, &BluetoothSerial::isReady>;
class BluetoothIsReadyMasterText : public virtual TextInterface {
public:
String text() const override { return String{"isReady (M): "} + toString(bluetoothSerial.isReady(true)); }
std::string text() const override { return std::string{"isReady (M): "} + to_string(bluetoothSerial.isReady(true)); }
};
#endif
}

View File

@ -10,6 +10,7 @@
#include "changevaluedisplay.h"
#include "displays/updatedisplay.h"
//#include "esputils.h"
#include "buttons.h"
namespace {
#ifdef FEATURE_WEBSERVER
@ -80,7 +81,7 @@ void initWebserver()
if (const auto *textInterface = constCurrentDisplay->asTextInterface())
{
HtmlTag h2Tag{"h2", response};
response->print(textInterface->text());
response->print(textInterface->text().c_str());
}
if (const auto *menuDisplay = constCurrentDisplay->asMenuDisplay())
@ -97,7 +98,7 @@ void initWebserver()
response->print("><a href=\"/triggerItem?index=");
response->print(i);
response->print("\">");
response->print(menuItem.text());
response->print(menuItem.text().c_str());
response->print("</a></li>");
i++;
@ -106,7 +107,7 @@ void initWebserver()
else if (const auto *changeValueDisplay = constCurrentDisplay->asChangeValueDisplayInterface())
{
response->print("<form action=\"/setValue\" method=\"GET\">");
response->print("<input type=\"number\" name=\"value\" value=\"" + String{changeValueDisplay->shownValue()} + "\" />");
response->print(("<input type=\"number\" name=\"value\" value=\"" + std::to_string(changeValueDisplay->shownValue()) + "\" />").c_str());
response->print("<button type=\"submit\">Update</button>");
response->print("</form>");
}
@ -276,7 +277,7 @@ void initWebserver()
if (!Update.begin(size, command))
Update.printError(Serial);
String type;
std::string type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else if (ArduinoOTA.getCommand() == U_SPIFFS) // U_SPIFFS

80
src/buttons.h Normal file
View File

@ -0,0 +1,80 @@
#pragma once
// Arduino includes
#include <Arduino.h>
// local includes
#include "types.h"
#include "settingsutils.h"
namespace {
int rotated{};
bool requestFullRedraw{};
bool confirmButtonPressed{};
bool confirmButtonLongPressed{};
bool backButtonPressed{};
bool backButtonLongPressed{};
class InputDispatcher
{
public:
static void rotate(int offset)
{
rotated += offset;
}
static void confirmButton(bool pressed)
{
static millis_t pressBegin = 0;
const auto now = millis();
if (pressed)
pressBegin = now;
else
{
const auto duration = now - pressBegin;
if (duration < 500)
confirmButtonPressed = true;
else if (duration < 2000)
confirmButtonLongPressed = true;
else
requestFullRedraw = true;
pressBegin = 0;
}
}
static void backButton(bool pressed)
{
static millis_t pressBegin = 0;
const auto now = millis();
if (pressed)
pressBegin = now;
else
{
const auto duration = now - pressBegin;
if (duration < 500)
backButtonPressed = true;
else
backButtonLongPressed = true;
pressBegin = 0;
}
}
static void profileButton(uint8_t index, bool pressed)
{
if (!pressed)
return;
switchProfile(index);
}
};
}

421
src/can.h Normal file
View File

@ -0,0 +1,421 @@
#pragma once
#include <cstring>
#include <optional>
#include <driver/gpio.h>
#include <driver/can.h>
#include <Arduino.h>
#include "bobbycar-protocol/bobbycar-can.h"
#include "types.h"
#include "globals.h"
#include "buttons.h"
namespace can {
namespace {
std::optional<int16_t> can_gas, can_brems;
millis_t last_can_gas{}, last_can_brems{};
struct CanButtonsState
{
bool up{};
bool down{};
bool confirm{};
bool back{};
bool profile0{};
bool profile1{};
bool profile2{};
bool profile3{};
};
CanButtonsState lastButtonsState;
void initCan()
{
Serial.println("initCan()");
can_general_config_t g_config = CAN_GENERAL_CONFIG_DEFAULT(GPIO_NUM_21, GPIO_NUM_22, CAN_MODE_NORMAL);
can_timing_config_t t_config CAN_TIMING_CONFIG_250KBITS();
can_filter_config_t f_config CAN_FILTER_CONFIG_ACCEPT_ALL();
// {
// //
// .acceptance_code = 0b00000000000,
// .acceptance_mask = 0b00001111111,
// .single_filter = true
// };
if (const auto result = can_driver_install(&g_config, &t_config, &f_config); result == ESP_OK)
{
Serial.printf("CAN info can_driver_install() succeeded\r\n");
}
else
{
Serial.printf("CAN err can_driver_install() failed with %s\r\n", esp_err_to_name(result));
return;
}
if (const auto result = can_start(); result == ESP_OK)
{
Serial.printf("CAN info can_start() succeeded\r\n");
}
else
{
Serial.printf("CAN err can_start() failed with %s\r\n", esp_err_to_name(result));
if (const auto result = can_driver_uninstall(); result == ESP_OK)
{
Serial.printf("CAN info can_driver_uninstall() succeeded\r\n");
}
else
{
Serial.printf("CAN err can_driver_uninstall() failed with %s\r\n", esp_err_to_name(result));
}
return;
}
}
template<bool isBack>
bool parseMotorControllerCanMessage(const can_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 can_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)
if (newState.up)
InputDispatcher::rotate(-1);
if (lastButtonsState.down != newState.down)
if (newState.down)
InputDispatcher::rotate(1);
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 = millis();
break;
case Boardcomputer::Command::RawBrems:
can_brems = *((int16_t*)message.data);
last_can_brems = millis();
break;
}
return false;
}
bool tryParseCanInput()
{
can_message_t message;
if (const auto result = can_receive(&message, pdMS_TO_TICKS(50)); result != ESP_OK)
{
if (millis() - controllers.front.lastCanFeedback > 100)
controllers.front.feedbackValid = false;
if (millis() - controllers.back.lastCanFeedback > 100)
controllers.back.feedbackValid = false;
if (result != ESP_ERR_TIMEOUT)
Serial.printf("CAN err can_receive() failed with %s\r\n", esp_err_to_name(result));
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 (millis() - back.lastCanFeedback > 100)
back.feedbackValid = false;
front.lastCanFeedback = millis();
front.feedbackValid = true;
return true;
}
else
{
if (millis() - front.lastCanFeedback > 100)
front.feedbackValid = false;
}
if (parseMotorControllerCanMessage<true>(message, back))
{
back.lastCanFeedback = millis();
back.feedbackValid = true;
return true;
}
else
{
if (millis() - back.lastCanFeedback > 100)
back.feedbackValid = false;
}
if (parseBoardcomputerCanMessage(message))
return true;
//Serial.printf("WARNING Unknown CAN info received .identifier = %u\r\n", 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){
can_message_t message;
message.identifier = addr;
message.flags = CAN_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 result = can_transmit(&message, pdMS_TO_TICKS(200));
if (result != ESP_OK && result != ESP_ERR_TIMEOUT)
Serial.printf("ERROR: can_transmit() failed with %s\r\n", esp_err_to_name(result));
return result;
};
const Controller &front = settings.controllerHardware.swapFrontBack ? controllers.back : controllers.front;
const Controller &back = settings.controllerHardware.swapFrontBack ? controllers.front : controllers.back;
using namespace bobbycar::protocol::can;
send(MotorController<false, false>::Command::InpTgt, front.command.left.pwm);
send(MotorController<false, true>::Command::InpTgt, front.command.right.pwm);
send(MotorController<true, false>::Command::InpTgt, back.command.left.pwm);
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.command.buzzer.freq != lastValues.front.freq ||
front.command.buzzer.pattern != lastValues.front.pattern ||
back.command.buzzer.freq != lastValues.back.freq ||
back.command.buzzer.pattern != lastValues.back.pattern)
i = 8;
else if (buttonLeds != lastValues.buttonLeds)
i = 10;
switch (i++)
{
case 0:
send(MotorController<false, false>::Command::Enable, front.command.left.enable);
send(MotorController<false, true>::Command::Enable, front.command.right.enable);
send(MotorController<true, false>::Command::Enable, back.command.left.enable);
send(MotorController<true, true>::Command::Enable, back.command.right.enable);
break;
case 1:
send(MotorController<false, false>::Command::CtrlTyp, front.command.left.ctrlTyp);
send(MotorController<false, true>::Command::CtrlTyp, front.command.right.ctrlTyp);
send(MotorController<true, false>::Command::CtrlTyp, back.command.left.ctrlTyp);
send(MotorController<true, true>::Command::CtrlTyp, back.command.right.ctrlTyp);
break;
case 2:
send(MotorController<false, false>::Command::CtrlMod, front.command.left.ctrlMod);
send(MotorController<false, true>::Command::CtrlMod, front.command.right.ctrlMod);
send(MotorController<true, false>::Command::CtrlMod, back.command.left.ctrlMod);
send(MotorController<true, true>::Command::CtrlMod, back.command.right.ctrlMod);
break;
case 3:
send(MotorController<false, false>::Command::IMotMax, front.command.left.iMotMax);
send(MotorController<false, true>::Command::IMotMax, front.command.right.iMotMax);
send(MotorController<true, false>::Command::IMotMax, back.command.left.iMotMax);
send(MotorController<true, true>::Command::IMotMax, back.command.right.iMotMax);
break;
case 4:
send(MotorController<false, false>::Command::IDcMax, front.command.left.iDcMax);
send(MotorController<false, true>::Command::IDcMax, front.command.right.iDcMax);
send(MotorController<true, false>::Command::IDcMax, back.command.left.iDcMax);
send(MotorController<true, true>::Command::IDcMax, back.command.right.iDcMax);
break;
case 5:
send(MotorController<false, false>::Command::NMotMax, front.command.left.nMotMax);
send(MotorController<false, true>::Command::NMotMax, front.command.right.nMotMax);
send(MotorController<true, false>::Command::NMotMax, back.command.left.nMotMax);
send(MotorController<true, true>::Command::NMotMax, back.command.right.nMotMax);
break;
case 6:
send(MotorController<false, false>::Command::FieldWeakMax, front.command.left.fieldWeakMax);
send(MotorController<false, true>::Command::FieldWeakMax, front.command.right.fieldWeakMax);
send(MotorController<true, false>::Command::FieldWeakMax, back.command.left.fieldWeakMax);
send(MotorController<true, true>::Command::FieldWeakMax, back.command.right.fieldWeakMax);
break;
case 7:
send(MotorController<false, false>::Command::PhaseAdvMax, front.command.left.phaseAdvMax);
send(MotorController<false, true>::Command::PhaseAdvMax, front.command.right.phaseAdvMax);
send(MotorController<true, false>::Command::PhaseAdvMax, back.command.left.phaseAdvMax);
send(MotorController<true, true>::Command::PhaseAdvMax, back.command.right.phaseAdvMax);
break;
case 8:
if (send(MotorController<false, false>::Command::BuzzerFreq, front.command.buzzer.freq) == ESP_OK)
lastValues.front.freq = front.command.buzzer.freq;
// if (send(MotorController<false, true>::Command::BuzzerFreq, front.command.buzzer.freq) == ESP_OK)
// lastValues.front.freq = front.command.buzzer.freq;
if (send(MotorController<true, false>::Command::BuzzerFreq, back.command.buzzer.freq) == ESP_OK)
lastValues.back.freq = back.command.buzzer.freq;
// if (send(MotorController<true, true>::Command::BuzzerFreq, back.command.buzzer.freq) == ESP_OK)
// lastValues.back.freq = back.command.buzzer.freq;
if (send(MotorController<false, false>::Command::BuzzerPattern, front.command.buzzer.pattern) == ESP_OK)
lastValues.front.pattern = front.command.buzzer.pattern;
// if (send(MotorController<false, true>::Command::BuzzerPattern, front.command.buzzer.pattern) == ESP_OK)
// lastValues.front.pattern = front.command.buzzer.pattern;
if (send(MotorController<true, false>::Command::BuzzerPattern, back.command.buzzer.pattern) == ESP_OK)
lastValues.back.pattern = back.command.buzzer.pattern;
// if (send(MotorController<true, true>::Command::BuzzerPattern, back.command.buzzer.pattern) == ESP_OK)
// lastValues.back.pattern = back.command.buzzer.pattern;
break;
case 9:
send(MotorController<false, false>::Command::Led, front.command.led);
//send(MotorController<false, true>::Command::Led, front.command.led);
send(MotorController<true, false>::Command::Led, back.command.led);
//send(MotorController<true, true>::Command::Led, back.command.led);
send(MotorController<false, false>::Command::Poweroff, front.command.poweroff);
//send(MotorController<false, true>::Command::Poweroff, front.command.poweroff);
send(MotorController<true, false>::Command::Poweroff, back.command.poweroff);
//send(MotorController<true, true>::Command::Poweroff, back.command.poweroff);
break;
case 10:
if (send(Boardcomputer::Feedback::ButtonLeds, buttonLeds) == ESP_OK)
lastValues.buttonLeds = buttonLeds;
default:
i=0;
break;
}
}
} // namespace
} // namespace can

View File

@ -119,7 +119,7 @@ void ChangeValueDisplay<Tvalue>::redraw()
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextFont(7);
m_valueLabel.redraw(String{m_value});
m_valueLabel.redraw(std::to_string(m_value));
}
template<typename Tvalue>

View File

@ -10,9 +10,9 @@
namespace {
template<>
class ChangeValueDisplay<ControlMode> :
class ChangeValueDisplay<bobbycar::protocol::ControlMode> :
public MenuDisplay,
public virtual AccessorInterface<ControlMode>,
public virtual AccessorInterface<bobbycar::protocol::ControlMode>,
public virtual ActionInterface
{
using Base = MenuDisplay;
@ -23,8 +23,9 @@ public:
void start() override;
};
ChangeValueDisplay<ControlMode>::ChangeValueDisplay()
ChangeValueDisplay<bobbycar::protocol::ControlMode>::ChangeValueDisplay()
{
using bobbycar::protocol::ControlMode;
constructMenuItem<makeComponentArgs<MenuItem, SetValueAction<ControlMode>, StaticText<TEXT_OPENMODE>>>(ControlMode::OpenMode, *this, *this);
constructMenuItem<makeComponentArgs<MenuItem, SetValueAction<ControlMode>, StaticText<TEXT_VOLTAGE>>>(ControlMode::Voltage, *this, *this);
constructMenuItem<makeComponentArgs<MenuItem, SetValueAction<ControlMode>, StaticText<TEXT_SPEED>>>(ControlMode::Speed, *this, *this);
@ -32,12 +33,13 @@ ChangeValueDisplay<ControlMode>::ChangeValueDisplay()
constructMenuItem<makeComponentArgs<MenuItem, BackProxyAction, StaticText<TEXT_BACK>, StaticMenuItemIcon<&icons::back>>>(*this);
}
void ChangeValueDisplay<ControlMode>::start()
void ChangeValueDisplay<bobbycar::protocol::ControlMode>::start()
{
Base::start();
switch (const auto value = getValue())
{
using bobbycar::protocol::ControlMode;
case ControlMode::OpenMode: setSelectedIndex(0); break;
case ControlMode::Voltage: setSelectedIndex(1); break;
case ControlMode::Speed: setSelectedIndex(2); break;

View File

@ -10,9 +10,9 @@
namespace {
template<>
class ChangeValueDisplay<ControlType> :
class ChangeValueDisplay<bobbycar::protocol::ControlType> :
public MenuDisplay,
public virtual AccessorInterface<ControlType>,
public virtual AccessorInterface<bobbycar::protocol::ControlType>,
public virtual ActionInterface
{
using Base = MenuDisplay;
@ -23,20 +23,22 @@ public:
void start() override;
};
ChangeValueDisplay<ControlType>::ChangeValueDisplay()
ChangeValueDisplay<bobbycar::protocol::ControlType>::ChangeValueDisplay()
{
using bobbycar::protocol::ControlType;
constructMenuItem<makeComponentArgs<MenuItem, SetValueAction<ControlType>, StaticText<TEXT_COMMUTATION>>>(ControlType::Commutation, *this, *this);
constructMenuItem<makeComponentArgs<MenuItem, SetValueAction<ControlType>, StaticText<TEXT_SINUSOIDAL>>>(ControlType::Sinusoidal, *this, *this);
constructMenuItem<makeComponentArgs<MenuItem, SetValueAction<ControlType>, StaticText<TEXT_FIELDORIENTEDCONTROL>>>(ControlType::FieldOrientedControl, *this, *this);
constructMenuItem<makeComponentArgs<MenuItem, BackProxyAction, StaticText<TEXT_BACK>, StaticMenuItemIcon<&icons::back>>>(*this);
}
void ChangeValueDisplay<ControlType>::start()
void ChangeValueDisplay<bobbycar::protocol::ControlType>::start()
{
Base::start();
switch (const auto value = getValue())
{
using bobbycar::protocol::ControlType;
case ControlType::Commutation: setSelectedIndex(0); break;
case ControlType::Sinusoidal: setSelectedIndex(1); break;
case ControlType::FieldOrientedControl: setSelectedIndex(2); break;

View File

@ -2,29 +2,50 @@
#include <functional>
#include "bobbycar-protocol/protocol.h"
#include "bobbycar-protocol/bobbycar-common.h"
#include "bobbycar-protocol/bobbycar-serial.h"
#include "types.h"
#ifdef FEATURE_SERIAL
#include "feedbackparser.h"
#endif
#ifdef FEATURE_SERIAL
class HardwareSerial;
#endif
namespace {
struct Controller {
Controller(HardwareSerial &serial, bool &enableLeft, bool &enableRight, bool &invertLeft, bool &invertRight) :
serial{serial}, enableLeft{enableLeft}, enableRight{enableRight}, invertLeft{invertLeft}, invertRight{invertRight}
Controller(
#ifdef FEATURE_SERIAL
HardwareSerial &serial,
#endif
bool &enableLeft, bool &enableRight, bool &invertLeft, bool &invertRight) :
#ifdef FEATURE_SERIAL
serial{serial},
#endif
enableLeft{enableLeft}, enableRight{enableRight}, invertLeft{invertLeft}, invertRight{invertRight}
{
}
// Controller(const Controller &) = delete;
// Controller &operator=(const Controller &) = delete;
#ifdef FEATURE_SERIAL
std::reference_wrapper<HardwareSerial> serial;
#endif
bool &enableLeft, &enableRight, &invertLeft, &invertRight;
Command command{};
bobbycar::protocol::serial::Command command{};
#ifdef FEATURE_CAN
millis_t lastCanFeedback{};
#endif
bool feedbackValid{};
Feedback feedback{};
bobbycar::protocol::serial::Feedback feedback{};
#ifdef FEATURE_SERIAL
FeedbackParser parser{serial, feedbackValid, feedback};
#endif
};
}

View File

@ -11,14 +11,14 @@ struct ControllerTexts
ControllerTexts() = delete;
~ControllerTexts() = delete;
struct BuzzerFreqText : public virtual TextInterface { public: String text() const override { return String{"buzzerFreq: "} + toString(controller::get().command.buzzer.freq); } };
struct BuzzerPatternText : public virtual TextInterface { public: String text() const override { return String{"buzzerPattern: "} + toString(controller::get().command.buzzer.pattern); } };
struct PoweroffText : public virtual TextInterface { public: String text() const override { return String{"poweroff: "} + toString(controller::get().command.poweroff); } };
struct LedText : public virtual TextInterface { public: String text() const override { return String{"led: "} + toString(controller::get().command.led); } };
struct BuzzerFreqText : public virtual TextInterface { public: std::string text() const override { return "buzzerFreq: " + std::to_string(controller::get().command.buzzer.freq); } };
struct BuzzerPatternText : public virtual TextInterface { public: std::string text() const override { return "buzzerPattern: " + std::to_string(controller::get().command.buzzer.pattern); } };
struct PoweroffText : public virtual TextInterface { public: std::string text() const override { return "poweroff: " + std::to_string(controller::get().command.poweroff); } };
struct LedText : public virtual TextInterface { public: std::string text() const override { return "led: " + std::to_string(controller::get().command.led); } };
private:
struct LeftCommandGetter { static const MotorState &get() { return controller::get().command.left; } };
struct RightCommandGetter { static const MotorState &get() { return controller::get().command.right; } };
struct LeftCommandGetter { static const bobbycar::protocol::serial::MotorState &get() { return controller::get().command.left; } };
struct RightCommandGetter { static const bobbycar::protocol::serial::MotorState &get() { return controller::get().command.right; } };
template<typename MotorStateGetter>
struct CommandTexts
@ -26,30 +26,30 @@ private:
CommandTexts() = delete;
~CommandTexts() = delete;
struct EnableText : public virtual TextInterface { public: String text() const override { return String{"enable: "} + toString(MotorStateGetter::get().enable); } };
struct PwmText : public virtual TextInterface { public: String text() const override { return String{"pwm: "} + toString(MotorStateGetter::get().pwm); } };
struct CtrlTypText : public virtual TextInterface { public: String text() const override { return String{"ctrlTyp: "} + toString(MotorStateGetter::get().ctrlTyp); } };
struct CtrlModText : public virtual TextInterface { public: String text() const override { return String{"ctrlMod: "} + toString(MotorStateGetter::get().ctrlMod); } };
struct IMotMaxText : public virtual TextInterface { public: String text() const override { return String{"iMotMax: "} + toString(MotorStateGetter::get().iMotMax); } };
struct IDcMaxText : public virtual TextInterface { public: String text() const override { return String{"iDcMax: "} + toString(MotorStateGetter::get().iDcMax); } };
struct NMotMaxText : public virtual TextInterface { public: String text() const override { return String{"nMotMax: "} + toString(MotorStateGetter::get().nMotMax); } };
struct FieldWeakMaxText : public virtual TextInterface { public: String text() const override { return String{"fieldWeakMax: "} + toString(MotorStateGetter::get().fieldWeakMax); } };
struct PhaseAdvMaxText : public virtual TextInterface { public: String text() const override { return String{"phaseAdvMax: "} + toString(MotorStateGetter::get().phaseAdvMax); } };
struct EnableText : public virtual TextInterface { public: std::string text() const override { return "enable: " + std::to_string(MotorStateGetter::get().enable); } };
struct PwmText : public virtual TextInterface { public: std::string text() const override { return "pwm: " + std::to_string(MotorStateGetter::get().pwm); } };
struct CtrlTypText : public virtual TextInterface { public: std::string text() const override { return "ctrlTyp: " + to_string(MotorStateGetter::get().ctrlTyp); } };
struct CtrlModText : public virtual TextInterface { public: std::string text() const override { return "ctrlMod: " + to_string(MotorStateGetter::get().ctrlMod); } };
struct IMotMaxText : public virtual TextInterface { public: std::string text() const override { return "iMotMax: " + std::to_string(MotorStateGetter::get().iMotMax); } };
struct IDcMaxText : public virtual TextInterface { public: std::string text() const override { return "iDcMax: " + std::to_string(MotorStateGetter::get().iDcMax); } };
struct NMotMaxText : public virtual TextInterface { public: std::string text() const override { return "nMotMax: " + std::to_string(MotorStateGetter::get().nMotMax); } };
struct FieldWeakMaxText : public virtual TextInterface { public: std::string text() const override { return "fieldWeakMax: " + std::to_string(MotorStateGetter::get().fieldWeakMax); } };
struct PhaseAdvMaxText : public virtual TextInterface { public: std::string text() const override { return "phaseAdvMax: " + std::to_string(MotorStateGetter::get().phaseAdvMax); } };
};
public:
using LeftCommand = CommandTexts<LeftCommandGetter>;
using RightCommand = CommandTexts<LeftCommandGetter>;
struct BatVoltageText : public virtual TextInterface { public: String text() const override { auto line = String{"batVoltage: "}; if (controller::get().feedbackValid) line += toString(controller::get().feedback.batVoltage); return line; } };
struct BatVoltageFixedText : public virtual TextInterface { public: String text() const override { auto line = String{"batVoltage: "}; if (controller::get().feedbackValid) line += toString(fixBatVoltage(controller::get().feedback.batVoltage)) + 'V'; return line; } };
struct BoardTempText : public virtual TextInterface { public: String text() const override { auto line = String{"boardTemp: "}; if (controller::get().feedbackValid) line += toString(controller::get().feedback.boardTemp); return line; } };
struct BoardTempFixedText : public virtual TextInterface { public: String text() const override { auto line = String{"boardTemp: "}; if (controller::get().feedbackValid) line += toString(fixBoardTemp(controller::get().feedback.boardTemp)) + 'C'; return line; } };
struct TimeoutCntSerialText : public virtual TextInterface { public: String text() const override { auto line = String{"timeoutCntSerial: "}; if (controller::get().feedbackValid) line += toString(controller::get().feedback.timeoutCntSerial); return line; } };
struct BatVoltageText : public virtual TextInterface { public: std::string text() const override { std::string line{"batVoltage: "}; if (controller::get().feedbackValid) line += std::to_string(controller::get().feedback.batVoltage); return line; } };
struct BatVoltageFixedText : public virtual TextInterface { public: std::string text() const override { std::string line{"batVoltage: "}; if (controller::get().feedbackValid) line += std::to_string(fixBatVoltage(controller::get().feedback.batVoltage)) + 'V'; return line; } };
struct BoardTempText : public virtual TextInterface { public: std::string text() const override { std::string line{"boardTemp: "}; if (controller::get().feedbackValid) line += std::to_string(controller::get().feedback.boardTemp); return line; } };
struct BoardTempFixedText : public virtual TextInterface { public: std::string text() const override { std::string line{"boardTemp: "}; if (controller::get().feedbackValid) line += std::to_string(fixBoardTemp(controller::get().feedback.boardTemp)) + 'C'; return line; } };
struct TimeoutCntSerialText : public virtual TextInterface { public: std::string text() const override { std::string line{"timeoutCntSerial: "}; if (controller::get().feedbackValid) line += std::to_string(controller::get().feedback.timeoutCntSerial); return line; } };
private:
struct LeftFeedbackGetter { static const MotorFeedback &get() { return controller::get().feedback.left; } };
struct RightFeedbackGetter { static const MotorFeedback &get() { return controller::get().feedback.right; } };
struct LeftFeedbackGetter { static const bobbycar::protocol::serial::MotorFeedback &get() { return controller::get().feedback.left; } };
struct RightFeedbackGetter { static const bobbycar::protocol::serial::MotorFeedback &get() { return controller::get().feedback.right; } };
template<typename MotorFeedbackGetter>
struct FeedbackTexts
@ -57,14 +57,20 @@ private:
FeedbackTexts() = delete;
~FeedbackTexts() = delete;
struct AngleText : public virtual TextInterface { public: String text() const override { auto line = String{"angle: "}; if (controller::get().feedbackValid) line += toString(MotorFeedbackGetter::get().angle); return line; } };
struct SpeedText : public virtual TextInterface { public: String text() const override { auto line = String{"speed: "}; if (controller::get().feedbackValid) line += toString(MotorFeedbackGetter::get().speed); return line; } };
struct SpeedKmhText : public virtual TextInterface { public: String text() const override { auto line = String{"speed kmh: "}; if (controller::get().feedbackValid) line += toString(convertToKmh(MotorFeedbackGetter::get().speed)); return line; } };
struct ErrorText : public virtual TextInterface { public: String text() const override { auto line = String{"error: "}; if (controller::get().feedbackValid) line += toString(MotorFeedbackGetter::get().error); return line; } };
struct CurrentText : public virtual TextInterface { public: String text() const override { auto line = String{"current: "}; if (controller::get().feedbackValid) line += toString(MotorFeedbackGetter::get().current); return line; } };
struct CurrentFixedText : public virtual TextInterface { public: String text() const override { auto line = String{"current: "}; if (controller::get().feedbackValid) line += toString(fixCurrent(MotorFeedbackGetter::get().current)) + 'A'; return line; } };
struct ChopsText : public virtual TextInterface { public: String text() const override { auto line = String{"chops: "}; if (controller::get().feedbackValid) line += toString(MotorFeedbackGetter::get().chops); return line; } };
struct HallText : public virtual TextInterface { public: String text() const override { auto line = String{"hall: "}; if (controller::get().feedbackValid) line += hallString(MotorFeedbackGetter::get()); return line; } };
struct AngleText : public virtual TextInterface { public: std::string text() const override { std::string line{"angle: "}; if (controller::get().feedbackValid) line += std::to_string(MotorFeedbackGetter::get().angle); return line; } };
struct SpeedText : public virtual TextInterface { public: std::string text() const override { std::string line{"speed: "}; if (controller::get().feedbackValid) line += std::to_string(MotorFeedbackGetter::get().speed); return line; } };
struct SpeedKmhText : public virtual TextInterface { public: std::string text() const override { std::string line{"speed kmh: "}; if (controller::get().feedbackValid) line += std::to_string(convertToKmh(MotorFeedbackGetter::get().speed)); return line; } };
struct ErrorText : public virtual TextInterface { public: std::string text() const override { std::string line{"error: "}; if (controller::get().feedbackValid) line += std::to_string(MotorFeedbackGetter::get().error); return line; } };
struct DcLinkText : public virtual TextInterface { public: std::string text() const override { std::string line{"dcLink: "}; if (controller::get().feedbackValid) line += std::to_string(MotorFeedbackGetter::get().dcLink); return line; } };
struct DcLinkFixedText : public virtual TextInterface { public: std::string text() const override { std::string line{"dcLink: "}; if (controller::get().feedbackValid) line += std::to_string(fixCurrent(MotorFeedbackGetter::get().dcLink)) + 'A'; return line; } };
struct DcPhaAText : public virtual TextInterface { public: std::string text() const override { std::string line{"dcPhaA: "}; if (controller::get().feedbackValid) line += std::to_string(MotorFeedbackGetter::get().dcPhaA); return line; } };
struct DcPhaAFixedText : public virtual TextInterface { public: std::string text() const override { std::string line{"dcPhaA: "}; if (controller::get().feedbackValid) line += std::to_string(fixCurrent(MotorFeedbackGetter::get().dcPhaA)) + 'A'; return line; } };
struct DcPhaBText : public virtual TextInterface { public: std::string text() const override { std::string line{"dcPhaB: "}; if (controller::get().feedbackValid) line += std::to_string(MotorFeedbackGetter::get().dcPhaB); return line; } };
struct DcPhaBFixedText : public virtual TextInterface { public: std::string text() const override { std::string line{"dcPhaB: "}; if (controller::get().feedbackValid) line += std::to_string(fixCurrent(MotorFeedbackGetter::get().dcPhaB)) + 'A'; return line; } };
struct DcPhaCText : public virtual TextInterface { public: std::string text() const override { std::string line{"dcPhaC: "}; if (controller::get().feedbackValid) line += std::to_string(MotorFeedbackGetter::get().dcPhaC); return line; } };
struct DcPhaCFixedText : public virtual TextInterface { public: std::string text() const override { std::string line{"dcPhaC: "}; if (controller::get().feedbackValid) line += std::to_string(fixCurrent(MotorFeedbackGetter::get().dcPhaC)) + 'A'; return line; } };
struct ChopsText : public virtual TextInterface { public: std::string text() const override { std::string line{"chops: "}; if (controller::get().feedbackValid) line += std::to_string(MotorFeedbackGetter::get().chops); return line; } };
struct HallText : public virtual TextInterface { public: std::string text() const override { std::string line{"hall: "}; if (controller::get().feedbackValid) line += hallString(MotorFeedbackGetter::get()); return line; } };
};
public:

View File

@ -1,6 +1,6 @@
#pragma once
#include <WString.h>
#include <string>
namespace {
class TextInterface;

View File

@ -92,11 +92,11 @@ void BmsDisplay::redraw()
if (bluetoothSerial.hasClient())
{
m_voltageLabel.redraw(String{bms::voltage} + 'V');
m_capacityLabel.redraw(String{int(bms::capacity)} + "mAh");
m_socLabel.redraw(String{bms::soc} + '%');
m_powerLabel.redraw(String{bms::power} + 'W');
m_currentLabel.redraw(String{bms::current} + 'A');
m_voltageLabel.redraw(std::to_string(bms::voltage) + 'V');
m_capacityLabel.redraw(std::to_string(int(bms::capacity)) + "mAh");
m_socLabel.redraw(std::to_string(bms::soc) + '%');
m_powerLabel.redraw(std::to_string(bms::power) + 'W');
m_currentLabel.redraw(std::to_string(bms::current) + 'A');
}
else
{
@ -107,18 +107,18 @@ void BmsDisplay::redraw()
m_currentLabel.clear();
}
m_speedLabel.redraw(String{avgSpeedKmh} + "kmh");
m_speedLabel.redraw(std::to_string(avgSpeedKmh) + "kmh");
if (bluetoothSerial.hasClient())
m_powerPerSpeedLabel.redraw(String{avgSpeedKmh < 1 ? 0 : bms::power / avgSpeedKmh} + "W/kmh");
m_powerPerSpeedLabel.redraw(std::to_string(avgSpeedKmh < 1 ? 0 : bms::power / avgSpeedKmh) + "W/kmh");
else
m_powerPerSpeedLabel.clear();
for (int i = 0; i < 12; i++)
m_battLabels[i].redraw(String{bms::batt[i]});
m_battLabels[i].redraw(std::to_string(bms::batt[i]));
if (bluetoothSerial.hasClient())
m_cycleLabel.redraw(String{bms::cycle} + "AH");
m_cycleLabel.redraw(std::to_string(bms::cycle) + "AH");
else
m_cycleLabel.clear();
}

View File

@ -1,8 +1,7 @@
#pragma once
#include <array>
#include <WString.h>
#include <string>
#include "display.h"
#include "actions/switchscreenaction.h"
@ -43,7 +42,7 @@ private:
const bool m_bootup{false};
ModeInterface *m_oldMode;
IgnoreInputMode m_mode{0, ControlType::FieldOrientedControl, ControlMode::Torque};
IgnoreInputMode m_mode{0, bobbycar::protocol::ControlType::FieldOrientedControl, bobbycar::protocol::ControlMode::Torque};
std::array<Label, 11> m_labels {{
Label{25, 72}, // 100, 23
@ -80,7 +79,7 @@ private:
Status m_status;
int16_t m_gasMin, m_gasMax, m_bremsMin, m_bremsMax;
float m_gas, m_brems;
std::optional<float> m_gas, m_brems;
};
CalibrateDisplay::CalibrateDisplay(bool bootup) :
@ -95,8 +94,8 @@ void CalibrateDisplay::start()
m_selectedButton = 0;
m_status = Status::Begin;
copyFromSettings();
m_gas = 0.f;
m_brems = 0.f;
m_gas = std::nullopt;
m_brems = std::nullopt;
}
void CalibrateDisplay::initScreen()
@ -125,41 +124,48 @@ void CalibrateDisplay::initScreen()
void CalibrateDisplay::update()
{
m_gas = scaleBetween<float>(raw_gas, m_gasMin, m_gasMax, 0., 1000.);
m_brems = scaleBetween<float>(raw_brems, m_bremsMin, m_bremsMax, 0., 1000.);
if (raw_gas)
m_gas = scaleBetween<float>(*raw_gas, m_gasMin, m_gasMax, 0., 1000.);
else
m_gas = std::nullopt;
if (raw_brems)
m_brems = scaleBetween<float>(*raw_brems, m_bremsMin, m_bremsMax, 0., 1000.);
else
m_brems = std::nullopt;
}
void CalibrateDisplay::redraw()
{
m_labels[0].redraw(toString(m_gas));
m_labels[1].redraw(toString(raw_gas));
m_labels[0].redraw(m_gas ? std::to_string(*m_gas) : "?");
m_labels[1].redraw(raw_gas ? std::to_string(*raw_gas) : "?");
if (m_status == Status::GasMin)
tft.setTextColor(TFT_RED, TFT_BLACK);
m_labels[2].redraw(toString(m_gasMin));
m_labels[2].redraw(std::to_string(m_gasMin));
if (m_status == Status::GasMin)
tft.setTextColor(TFT_WHITE, TFT_BLACK);
if (m_status == Status::GasMax)
tft.setTextColor(TFT_RED, TFT_BLACK);
m_labels[3].redraw(toString(m_gasMax));
m_labels[3].redraw(std::to_string(m_gasMax));
if (m_status == Status::GasMax)
tft.setTextColor(TFT_WHITE, TFT_BLACK);
m_progressBars[0].redraw(m_gas);
m_progressBars[0].redraw(m_gas ? *m_gas : 0);
m_labels[4].redraw(toString(m_brems));
m_labels[5].redraw(toString(raw_brems));
m_labels[4].redraw(m_brems ? std::to_string(*m_brems) : "?");
m_labels[5].redraw(raw_brems ? std::to_string(*raw_brems) : "?");
if (m_status == Status::BremsMin)
tft.setTextColor(TFT_RED, TFT_BLACK);
m_labels[6].redraw(toString(m_bremsMin));
m_labels[6].redraw(std::to_string(m_bremsMin));
if (m_status == Status::BremsMin)
tft.setTextColor(TFT_WHITE, TFT_BLACK);
if (m_status == Status::BremsMax)
tft.setTextColor(TFT_RED, TFT_BLACK);
m_labels[7].redraw(toString(m_bremsMax));
m_labels[7].redraw(std::to_string(m_bremsMax));
if (m_status == Status::BremsMax)
tft.setTextColor(TFT_WHITE, TFT_BLACK);
m_progressBars[1].redraw(m_brems);
m_progressBars[1].redraw(m_brems ? *m_brems : 0);
m_labels[8].redraw([&](){
switch (m_status)
@ -175,7 +181,8 @@ void CalibrateDisplay::redraw()
}());
{
const auto color = m_status == Status::Confirm && (m_gas > 100 || m_brems > 100) ? TFT_DARKGREY : TFT_WHITE;
const auto failed = !m_gas || !m_brems || (m_status == Status::Confirm && (*m_gas > 100 || *m_brems > 100));
const auto color = failed ? TFT_DARKGREY : TFT_WHITE;
tft.setTextColor(color, TFT_BLACK);
m_labels[9].redraw([&](){
switch (m_status)
@ -256,17 +263,20 @@ void CalibrateDisplay::confirm()
switch (m_selectedButton)
{
case 0: // left button pressed
if (!raw_gas || !raw_brems || !m_gas || !m_brems)
return;
switch (m_status)
{
case Status::Begin:
m_status = Status::GasMin;
break;
case Status::GasMin:
m_gasMin = raw_gas;
m_gasMin = *raw_gas;
m_status = Status::GasMax;
break;
case Status::GasMax:
m_gasMax = raw_gas;
m_gasMax = *raw_gas;
m_status = Status::BremsMin;
{
const auto dead = (m_gasMax - m_gasMin)/20;
@ -275,11 +285,11 @@ void CalibrateDisplay::confirm()
}
break;
case Status::BremsMin:
m_bremsMin = raw_brems;
m_bremsMin = *raw_brems;
m_status = Status::BremsMax;
break;
case Status::BremsMax:
m_bremsMax = raw_brems;
m_bremsMax = *raw_brems;
m_status = Status::Confirm;
{
const auto dead = (m_bremsMax - m_bremsMin)/20;
@ -288,7 +298,7 @@ void CalibrateDisplay::confirm()
}
break;
case Status::Confirm:
if (m_gas > 100 || m_brems > 100)
if (*m_gas > 100 || *m_brems > 100)
return;
copyToSettings();
saveSettings();

View File

@ -65,7 +65,7 @@ void DPad5WireDebugDisplay::initScreen()
void DPad5WireDebugDisplay::redraw()
{
m_labelRaw.redraw(String{} +
m_labelRaw.redraw(std::string{} +
(std::get<0>(dpad5wire::lastState) ? '1' : '0') + ' ' +
(std::get<1>(dpad5wire::lastState) ? '1' : '0') + ' ' +
(std::get<2>(dpad5wire::lastState) ? '1' : '0') + ' ' +
@ -83,8 +83,8 @@ void DPad5WireDebugDisplay::redraw()
m_labelProfile1.redraw(std::get<DPAD_5WIRESW_PROFILE1>(dpad5wire::lastState) ? "1" : "0");
m_labelProfile2.redraw(std::get<DPAD_5WIRESW_PROFILE2>(dpad5wire::lastState) ? "1" : "0");
m_labelProfile3.redraw(std::get<DPAD_5WIRESW_PROFILE3>(dpad5wire::lastState) ? "1" : "0");
m_labelGas.redraw(String{raw_gas});
m_labelBrems.redraw(String{raw_brems});
m_labelGas.redraw(raw_gas ? std::to_string(*raw_gas) : "?");
m_labelBrems.redraw(raw_brems ? std::to_string(*raw_brems) : "?");
}
#endif
}

View File

@ -1,8 +1,7 @@
#pragma once
#include <array>
#include <WString.h>
#include <string>
#include "display.h"
#include "actions/switchscreenaction.h"
@ -63,14 +62,14 @@ void GametrakCalibrateDisplay::initScreen()
void GametrakCalibrateDisplay::redraw()
{
m_labels[0].redraw(String{gametrakX});
m_labels[1].redraw(String{raw_gametrakX});
m_labels[0].redraw(std::to_string(gametrakX));
m_labels[1].redraw(std::to_string(raw_gametrakX));
m_labels[2].redraw(String{gametrakY});
m_labels[3].redraw(String{raw_gametrakY});
m_labels[2].redraw(std::to_string(gametrakY));
m_labels[3].redraw(std::to_string(raw_gametrakY));
m_labels[4].redraw(String{gametrakDist});
m_labels[5].redraw(String{raw_gametrakDist});
m_labels[4].redraw(std::to_string(gametrakDist));
m_labels[5].redraw(std::to_string(raw_gametrakDist));
m_progressBars[0].redraw(gametrakX);
m_progressBars[1].redraw(gametrakY);

View File

@ -50,7 +50,7 @@ private:
int m_rotated;
ModeInterface *m_oldMode;
IgnoreInputMode m_mode{0, ControlType::FieldOrientedControl, ControlMode::Speed};
IgnoreInputMode m_mode{0, bobbycar::protocol::ControlType::FieldOrientedControl, bobbycar::protocol::ControlMode::Speed};
};
void Lockscreen::start()
@ -92,7 +92,7 @@ void Lockscreen::initScreen()
drawRect(0, 1, TFT_YELLOW);
drawRect(0, 2, TFT_YELLOW);
m_labels[0].redraw(String(m_numbers[0]));
m_labels[0].redraw(std::to_string(m_numbers[0]));
}
void Lockscreen::redraw()
@ -115,7 +115,7 @@ void Lockscreen::redraw()
std::for_each(std::begin(m_labels) + 1, std::end(m_labels), [](auto &label){ label.redraw({}); });
}
m_labels[m_currentIndex].redraw(String{m_numbers[m_currentIndex]});
m_labels[m_currentIndex].redraw(std::to_string(m_numbers[m_currentIndex]));
drawRect(m_currentIndex, 1, TFT_YELLOW);
drawRect(m_currentIndex, 2, TFT_YELLOW);
@ -132,7 +132,7 @@ void Lockscreen::redraw()
else if (m_numbers[m_currentIndex] > 9)
m_numbers[m_currentIndex]-=10;
m_labels[m_currentIndex].redraw(String(m_numbers[m_currentIndex]));
m_labels[m_currentIndex].redraw(std::to_string(m_numbers[m_currentIndex]));
m_rotated = 0;
}

View File

@ -24,11 +24,23 @@ class SettingsMenu;
namespace {
struct GasText : public virtual TextInterface {
public:
String text() const override { return String{"gas: "} + raw_gas + ": " + gas; }
std::string text() const override
{
return std::string{"gas: "} +
(raw_gas ? std::to_string(*raw_gas) : "?") +
": " +
(gas ? std::to_string(*gas) : "?");
}
};
struct BremsText : public virtual TextInterface {
public:
String text() const override { return String{"brems: "} + raw_brems + ": " + brems; }
std::string text() const override
{
return std::string{"brems: "} +
(raw_brems ? std::to_string(*raw_brems) : "?") +
": " +
(brems ? std::to_string(*brems) : "?");
}
};
using SampleCountChangeScreen = makeComponent<
@ -80,15 +92,15 @@ using DPadDebounceChangeScreen = makeComponent<
#ifdef FEATURE_GAMETRAK
struct GametrakXText : public virtual TextInterface {
public:
String text() const override { return String{"gametrakX: "} + raw_gametrakX + ": " + gametrakX; }
std::string text() const override { return std::string{"gametrakX: "} + std::to_string(raw_gametrakX) + ": " + std::to_string(gametrakX); }
};
struct GametrakYText : public virtual TextInterface {
public:
String text() const override { return String{"gametrakY: "} + raw_gametrakY + ": " + gametrakY; }
std::string text() const override { return std::string{"gametrakY: "} + std::to_string(raw_gametrakY) + ": " + std::to_string(gametrakY); }
};
struct GametrakDistText : public virtual TextInterface {
public:
String text() const override { return String{"gametrakDist: "} + raw_gametrakDist + ": " + gametrakDist; }
std::string text() const override { return std::string{"gametrakDist: "} + std::to_string(raw_gametrakDist) + ": " + std::to_string(gametrakDist); }
};
using GametrakXMinChangeScreen = makeComponent<

View File

@ -2,7 +2,7 @@
// Arduino includes
#include <Arduino.h>
#include <WString.h>
#include <string>
// local includes
#include "menudisplay.h"
@ -26,12 +26,12 @@ namespace {
class RandomText : public virtual TextInterface
{
public:
String text() const override
std::string text() const override
{
const auto now = millis();
if (!m_nextUpdate || now >= m_nextUpdate)
{
m_title = String{"Dynamic text: "} + random(0, 100);
m_title = std::string{"Dynamic text: "} + std::to_string(random(0, 100));
m_nextUpdate = now + random(0, 1000);
}
@ -40,7 +40,7 @@ public:
private:
mutable millis_t m_nextUpdate{};
mutable String m_title;
mutable std::string m_title;
};
class RandomColor : public virtual ColorInterface

View File

@ -17,44 +17,50 @@ class SettingsMenu;
} // namespace
namespace {
template<const char *Tprefix, typename Taccessor>
struct TextWithValueHelper : public virtual TextInterface
{
std::string text() const override { return Tprefix + (' ' + std::to_string(Taccessor{}.getValue())); }
};
using IMotMaxChangeScreen = makeComponent<
ChangeValueDisplay<int16_t>,
StaticText<TEXT_SETIMOTMAX>,
StaticText<TEXT_IMOTMAX>,
IMotMaxAccessor,
BackActionInterface<SwitchScreenAction<LimitsSettingsMenu>>,
SwitchScreenAction<LimitsSettingsMenu>
>;
using IDcMaxChangeScreen = makeComponent<
ChangeValueDisplay<int16_t>,
StaticText<TEXT_SETIDCMAX>,
StaticText<TEXT_IDCMAX>,
IDcMaxAccessor,
BackActionInterface<SwitchScreenAction<LimitsSettingsMenu>>,
SwitchScreenAction<LimitsSettingsMenu>
>;
using NMotMaxKmhChangeScreen = makeComponent<
ChangeValueDisplay<int16_t>,
StaticText<TEXT_SETNMOTMAXKMH>,
StaticText<TEXT_NMOTMAXKMH>,
NMotMaxKmhAccessor,
BackActionInterface<SwitchScreenAction<LimitsSettingsMenu>>,
SwitchScreenAction<LimitsSettingsMenu>
>;
using NMotMaxRpmChangeScreen = makeComponent<
ChangeValueDisplay<int16_t>,
StaticText<TEXT_SETNMOTMAX>,
StaticText<TEXT_NMOTMAX>,
NMotMaxRpmAccessor,
BackActionInterface<SwitchScreenAction<LimitsSettingsMenu>>,
SwitchScreenAction<LimitsSettingsMenu>
>;
using FieldWeakMaxChangeScreen = makeComponent<
ChangeValueDisplay<int16_t>,
StaticText<TEXT_SETFIELDWEAKMAX>,
StaticText<TEXT_FIELDWEAKMAX>,
FieldWeakMaxAccessor,
BackActionInterface<SwitchScreenAction<LimitsSettingsMenu>>,
SwitchScreenAction<LimitsSettingsMenu>
>;
using PhaseAdvMaxChangeScreen = makeComponent<
ChangeValueDisplay<int16_t>,
StaticText<TEXT_SETPHASEADVMAX>,
StaticText<TEXT_PHASEADVMAX>,
PhaseAdvMaxAccessor,
BackActionInterface<SwitchScreenAction<LimitsSettingsMenu>>,
SwitchScreenAction<LimitsSettingsMenu>
@ -68,13 +74,13 @@ class LimitsSettingsMenu :
public:
LimitsSettingsMenu()
{
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_SETIMOTMAX>, SwitchScreenAction<IMotMaxChangeScreen>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_SETIDCMAX>, SwitchScreenAction<IDcMaxChangeScreen>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_SETNMOTMAXKMH>, SwitchScreenAction<NMotMaxKmhChangeScreen>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_SETNMOTMAX>, SwitchScreenAction<NMotMaxRpmChangeScreen>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_SETFIELDWEAKMAX>, SwitchScreenAction<FieldWeakMaxChangeScreen>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_SETPHASEADVMAX>, SwitchScreenAction<PhaseAdvMaxChangeScreen>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<SettingsMenu>, StaticMenuItemIcon<&icons::back>>>();
constructMenuItem<makeComponent<MenuItem, TextWithValueHelper<TEXT_IMOTMAX, IMotMaxAccessor>, SwitchScreenAction<IMotMaxChangeScreen>>>();
constructMenuItem<makeComponent<MenuItem, TextWithValueHelper<TEXT_IDCMAX, IDcMaxAccessor>, SwitchScreenAction<IDcMaxChangeScreen>>>();
constructMenuItem<makeComponent<MenuItem, TextWithValueHelper<TEXT_NMOTMAXKMH, NMotMaxKmhAccessor>, SwitchScreenAction<NMotMaxKmhChangeScreen>>>();
constructMenuItem<makeComponent<MenuItem, TextWithValueHelper<TEXT_NMOTMAX, NMotMaxRpmAccessor>, SwitchScreenAction<NMotMaxRpmChangeScreen>>>();
constructMenuItem<makeComponent<MenuItem, TextWithValueHelper<TEXT_FIELDWEAKMAX, FieldWeakMaxAccessor>, SwitchScreenAction<FieldWeakMaxChangeScreen>>>();
constructMenuItem<makeComponent<MenuItem, TextWithValueHelper<TEXT_PHASEADVMAX, PhaseAdvMaxAccessor>, SwitchScreenAction<PhaseAdvMaxChangeScreen>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<SettingsMenu>, StaticMenuItemIcon<&icons::back>>>();
}
};
} // namespace

View File

@ -25,6 +25,7 @@ class SelectModeMenu;
class ProfilesMenu;
class PresetsMenu;
class GraphsMenu;
class PowerSupplyDisplay;
class BmsMenu;
class SettingsMenu;
class Lockscreen;
@ -49,6 +50,9 @@ public:
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_PRESETS>, SwitchScreenAction<PresetsMenu>, StaticMenuItemIcon<&icons::presets>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_PROFILES>, SwitchScreenAction<ProfilesMenu>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_GRAPHS>, SwitchScreenAction<GraphsMenu>, StaticMenuItemIcon<&icons::graph>>>();
#ifdef FEATURE_CAN
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_POWERSUPPLY>, SwitchScreenAction<PowerSupplyDisplay>>>();
#endif
#ifdef FEATURE_BMS
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BMS>, SwitchScreenAction<BmsMenu>, StaticMenuItemIcon<&icons::bms>>>();
#endif

View File

@ -30,8 +30,14 @@ public:
constructMenuItem<makeComponent<MenuItem, typename Ttexts::SpeedText, ColorInterface<TFT_DARKGREY>, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, typename Ttexts::SpeedKmhText, ColorInterface<TFT_DARKGREY>, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, typename Ttexts::ErrorText, ColorInterface<TFT_DARKGREY>, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, typename Ttexts::CurrentText, ColorInterface<TFT_DARKGREY>, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, typename Ttexts::CurrentFixedText, ColorInterface<TFT_DARKGREY>, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, typename Ttexts::DcLinkText, ColorInterface<TFT_DARKGREY>, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, typename Ttexts::DcLinkFixedText, ColorInterface<TFT_DARKGREY>, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, typename Ttexts::DcPhaAText, ColorInterface<TFT_DARKGREY>, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, typename Ttexts::DcPhaAFixedText, ColorInterface<TFT_DARKGREY>, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, typename Ttexts::DcPhaBText, ColorInterface<TFT_DARKGREY>, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, typename Ttexts::DcPhaBFixedText, ColorInterface<TFT_DARKGREY>, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, typename Ttexts::DcPhaCText, ColorInterface<TFT_DARKGREY>, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, typename Ttexts::DcPhaCFixedText, ColorInterface<TFT_DARKGREY>, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, typename Ttexts::ChopsText, ColorInterface<TFT_DARKGREY>, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, typename Ttexts::HallText, ColorInterface<TFT_DARKGREY>, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<DebugMenu>, StaticMenuItemIcon<&icons::back>>>();

View File

@ -27,7 +27,7 @@ class WifiScanMenu : public MenuDisplay, public BackActionInterface<SwitchScreen
public:
WifiScanMenu();
String text() const override;
std::string text() const override;
void start() override;
void update() override;
@ -44,9 +44,9 @@ WifiScanMenu::WifiScanMenu()
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<WifiSettingsMenu>, StaticMenuItemIcon<&icons::back>>>();
}
String WifiScanMenu::text() const
std::string WifiScanMenu::text() const
{
auto text = String{menuItemCount()-1} + " found";
auto text = std::to_string(menuItemCount()-1) + " found";
switch (WiFi.scanComplete())
{
case WIFI_SCAN_RUNNING: text += " (scanning)"; break;
@ -76,7 +76,7 @@ void WifiScanMenu::update()
for (std::size_t i = 0; i < n; i++)
{
const auto ssid = WiFi.SSID(i);
const auto ssid = to_string(WiFi.SSID(i));
if (menuItemCount() <= i)
{
if (m_reusableItems.empty())

View File

@ -72,14 +72,14 @@ void MetersDisplay::redraw()
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextFont(2);
m_sumCurrentLabel.redraw(toString(sumCurrent) + 'A');
m_sumCurrentLabel.redraw(std::to_string(sumCurrent) + 'A');
meters[0].redraw(fixBatVoltage(controllers.front.feedback.batVoltage), 35, 50);
meters[1].redraw(fixBatVoltage(controllers.back.feedback.batVoltage), 35, 50);
meters[2].redraw(fixCurrent(controllers.front.feedback.left.current), -10, 10);
meters[3].redraw(fixCurrent(controllers.front.feedback.right.current), -10, 10);
meters[4].redraw(fixCurrent(controllers.back.feedback.left.current), -10, 10);
meters[5].redraw(fixCurrent(controllers.back.feedback.right.current), -10, 10);
meters[2].redraw(fixCurrent(controllers.front.feedback.left.dcLink), -10, 10);
meters[3].redraw(fixCurrent(controllers.front.feedback.right.dcLink), -10, 10);
meters[4].redraw(fixCurrent(controllers.back.feedback.left.dcLink), -10, 10);
meters[5].redraw(fixCurrent(controllers.back.feedback.right.dcLink), -10, 10);
}
void MetersDisplay::rotate(int offset)

View File

@ -0,0 +1,68 @@
#pragma once
#include <Arduino.h>
#include "display.h"
#include "actions/switchscreenaction.h"
#include "globals.h"
#include "widgets/label.h"
namespace {
class MainMenu;
class MetersDisplay;
class StatusDisplay;
}
namespace {
#ifdef FEATURE_CAN
class PowerSupplyDisplay : public Display
{
public:
void initScreen() override;
void redraw() override;
void confirm() override;
void back() override;
void rotate(int offset) override;
Label m_voltageLabel{120, 50};
Label m_currentLabel{120, 75};
};
void PowerSupplyDisplay::initScreen()
{
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextFont(4);
tft.drawString("Voltage:", 0, m_voltageLabel.y());
m_voltageLabel.start();
tft.drawString("Current:", 0, m_currentLabel.y());
m_currentLabel.start();
}
void PowerSupplyDisplay::redraw()
{
m_voltageLabel.redraw(std::to_string(50.4) + 'V');
m_currentLabel.redraw(std::to_string(15.1) + 'A');
}
void PowerSupplyDisplay::confirm()
{
}
void PowerSupplyDisplay::back()
{
}
void PowerSupplyDisplay::rotate(int offset)
{
// if (offset < 0)
// switchScreen<MetersDisplay>();
// else if (offset > 0)
// switchScreen<StatusDisplay>();
}
#endif
}

View File

@ -57,7 +57,7 @@ private:
{}
void start();
void redraw(const MotorFeedback &motor);
void redraw(const bobbycar::protocol::serial::MotorFeedback &motor);
private:
Label m_labelError;
@ -145,26 +145,26 @@ void StatusDisplay::initScreen()
void StatusDisplay::redraw()
{
tft.setTextFont(2);
m_labelRawGas.redraw(String{raw_gas});
m_labelGas.redraw(String{gas});
m_progressBarGas.redraw(gas);
m_labelRawBrems.redraw(String{raw_brems});
m_labelBrems.redraw(String{brems});
m_progressBarBrems.redraw(brems);
m_labelRawGas.redraw(raw_gas ? std::to_string(*raw_gas) : "?");
m_labelGas.redraw(gas ? std::to_string(*gas) : "?");
m_progressBarGas.redraw(gas ? *gas : 0);
m_labelRawBrems.redraw(raw_brems ? std::to_string(*raw_brems) : "?");
m_labelBrems.redraw(brems ? std::to_string(*brems) : "?");
m_progressBarBrems.redraw(brems ? *brems : 0);
m_frontStatus.redraw(controllers.front);
m_backStatus.redraw(controllers.back);
tft.setTextFont(2);
m_labelWifiStatus.redraw(toString(WiFi.status()));
m_labelLimit0.redraw(String{controllers.front.command.left.iMotMax} + "A");
m_labelIpAddress.redraw(WiFi.localIP().toString());
m_labelLimit1.redraw(String{controllers.front.command.left.iDcMax} + "A");
m_labelPerformance.redraw(String{performance.last});
m_labelWifiStatus.redraw(to_string(WiFi.status()));
m_labelLimit0.redraw(std::to_string(controllers.front.command.left.iMotMax) + "A");
m_labelIpAddress.redraw(to_string(WiFi.localIP()));
m_labelLimit1.redraw(std::to_string(controllers.front.command.left.iDcMax) + "A");
m_labelPerformance.redraw(std::to_string(performance.last));
m_labelMode.redraw(currentMode->displayName());
m_labelName.redraw(&deviceName[0]);
const auto profile = settingsPersister.currentlyOpenProfileIndex();
m_labelProfile.redraw(profile?String{*profile}:"-");
m_labelProfile.redraw(profile ? std::to_string(*profile) : "-");
}
void StatusDisplay::rotate(int offset)
@ -192,8 +192,8 @@ void StatusDisplay::BoardStatus::redraw(const Controller &controller)
{
tft.setTextFont(4);
m_labelLeftPwm.redraw(String{controller.command.left.pwm});
m_labelRightPwm.redraw(String{controller.command.right.pwm});
m_labelLeftPwm.redraw(std::to_string(controller.command.left.pwm));
m_labelRightPwm.redraw(std::to_string(controller.command.right.pwm));
if (controller.feedbackValid != m_lastFeedbackValid || m_initialRedraw)
{
@ -231,8 +231,8 @@ void StatusDisplay::BoardStatus::redraw(const Controller &controller)
if (controller.feedbackValid)
{
m_labelVoltage.redraw(String{fixBatVoltage(controller.feedback.batVoltage)} + 'V');
m_labelTemperature.redraw(String{fixBoardTemp(controller.feedback.boardTemp)} + 'C');
m_labelVoltage.redraw(std::to_string(fixBatVoltage(controller.feedback.batVoltage)) + 'V');
m_labelTemperature.redraw(std::to_string(fixBoardTemp(controller.feedback.boardTemp)) + 'C');
m_leftMotor.redraw(controller.feedback.left);
m_rightMotor.redraw(controller.feedback.right);
}
@ -246,15 +246,15 @@ void StatusDisplay::BoardStatus::MotorStatus::start()
m_labelHallSensors.start();
}
void StatusDisplay::BoardStatus::MotorStatus::redraw(const MotorFeedback &motor)
void StatusDisplay::BoardStatus::MotorStatus::redraw(const bobbycar::protocol::serial::MotorFeedback &motor)
{
tft.setTextFont(4);
tft.setTextColor(motor.error?TFT_RED:TFT_GREEN, TFT_BLACK);
m_labelError.redraw(String{motor.error});
m_labelError.redraw(std::to_string(motor.error));
tft.setTextColor(TFT_WHITE, TFT_BLACK);
m_labelCurrent.redraw(String{fixCurrent(motor.current)} + 'A');
m_labelSpeed.redraw(String{convertToKmh(motor.speed)});
m_labelCurrent.redraw(std::to_string(fixCurrent(motor.dcLink)) + 'A');
m_labelSpeed.redraw(std::to_string(convertToKmh(motor.speed)));
tft.setTextFont(2);
m_labelHallSensors.redraw(hallString(motor));

View File

@ -1,9 +1,9 @@
#pragma once
#include <array>
#include <string>
#include <ArduinoOTA.h>
#include <WString.h>
#include "display.h"
#include "actions/switchscreenaction.h"
@ -18,12 +18,12 @@ class StatusDisplay;
}
namespace {
#ifdef FEATURE_ARDUINOOTA
#if defined(FEATURE_ARDUINOOTA) || defined(FEATURE_WEBOTA)
class UpdateDisplay : public Display, public DummyBack
{
public:
UpdateDisplay(const String &title);
UpdateDisplay(String &&title);
UpdateDisplay(const std::string &title);
UpdateDisplay(std::string &&title);
void start() override;
void initScreen() override;
@ -38,18 +38,18 @@ public:
bool m_errorValid;
private:
const String m_title;
const std::string m_title;
Label m_progressLabel{20, 150};
ProgressBar m_progressBar{20, 200, 200, 10, 0, 100};
};
UpdateDisplay::UpdateDisplay(const String &title) :
UpdateDisplay::UpdateDisplay(const std::string &title) :
m_title{title}
{}
UpdateDisplay::UpdateDisplay(String &&title) :
UpdateDisplay::UpdateDisplay(std::string &&title) :
m_title{std::move(title)}
{}
@ -67,7 +67,7 @@ void UpdateDisplay::initScreen()
tft.setTextFont(4);
tft.setTextColor(TFT_YELLOW);
tft.drawString(m_title, 5, 5, 4);
tft.drawString(m_title.c_str(), 5, 5, 4);
tft.fillRect(0, 34, tft.width(), 3, TFT_WHITE);
@ -81,7 +81,7 @@ void UpdateDisplay::initScreen()
void UpdateDisplay::redraw()
{
m_progressLabel.redraw(String{} + m_progress + '/' + m_total);
m_progressLabel.redraw(std::to_string(m_progress) + '/' + std::to_string(m_total));
m_progressBar.redraw(float(m_progress) / m_total * 100.f);
}

View File

@ -4,8 +4,8 @@
#include <Arduino.h>
#include "globals.h"
#include "types.h"
#include "buttons.h"
namespace {
namespace dpad

View File

@ -2,11 +2,9 @@
#include <Arduino.h>
#include "globals.h"
#include "dpad.h"
#include "globals.h"
#include "types.h"
#include "buttons.h"
namespace {
namespace dpad3wire

View File

@ -1,17 +1,42 @@
#pragma once
#include <tuple>
#include <array>
#include <Arduino.h>
#include "globals.h"
#include "types.h"
#include "actions/switchprofileaction.h"
#include "buttons.h"
namespace {
namespace dpad5wire
namespace dpad5wire {
#ifdef FEATURE_DPAD_5WIRESW
class State : public std::array<bool, 8>
{
using State = std::tuple<bool, bool, bool, bool, bool, bool, bool, bool>;
public:
State() : std::array<bool, 8>{false, false, false, false, false, false, false, false} {}
State(const std::array<bool, 8> &other) : std::array<bool, 8>{} {}
State &operator=(const std::array<bool, 8> &other)
{
std::array<bool, 8>::operator=(other);
return *this;
}
State &operator=(const State &other)
{
std::array<bool, 8>::operator=(other);
return *this;
}
bool &up{this->at(DPAD_5WIRESW_UP)};
bool &down{this->at(DPAD_5WIRESW_DOWN)};
bool &confirm{this->at(DPAD_5WIRESW_CONFIRM)};
bool &back{this->at(DPAD_5WIRESW_BACK)};
bool &profile0{this->at(DPAD_5WIRESW_PROFILE0)};
bool &profile1{this->at(DPAD_5WIRESW_PROFILE1)};
bool &profile2{this->at(DPAD_5WIRESW_PROFILE2)};
bool &profile3{this->at(DPAD_5WIRESW_PROFILE3)};
};
template<pin_t OUT, pin_t IN1, pin_t IN2, pin_t IN3, pin_t IN4>
class Helper
@ -37,6 +62,8 @@ void Helper<OUT, IN1, IN2, IN3, IN4>::begin()
template<pin_t OUT, pin_t IN1, pin_t IN2, pin_t IN3, pin_t IN4>
State Helper<OUT, IN1, IN2, IN3, IN4>::read()
{
State result;
digitalWrite(OUT, LOW);
pinMode(IN1, INPUT_PULLUP);
@ -46,10 +73,10 @@ State Helper<OUT, IN1, IN2, IN3, IN4>::read()
delay(1);
const bool result0 = digitalRead(IN1)==LOW;
const bool result1 = digitalRead(IN2)==LOW;
const bool result2 = digitalRead(IN3)==LOW;
const bool result3 = digitalRead(IN4)==LOW;
result[0] = digitalRead(IN1)==LOW;
result[1] = digitalRead(IN2)==LOW;
result[2] = digitalRead(IN3)==LOW;
result[3] = digitalRead(IN4)==LOW;
digitalWrite(OUT, HIGH);
@ -60,15 +87,14 @@ State Helper<OUT, IN1, IN2, IN3, IN4>::read()
delay(1);
const bool result4 = digitalRead(IN1);
const bool result5 = digitalRead(IN2);
const bool result6 = digitalRead(IN3);
const bool result7 = digitalRead(IN4);
result[4] = digitalRead(IN1);
result[5] = digitalRead(IN2);
result[6] = digitalRead(IN3);
result[7] = digitalRead(IN4);
return std::make_tuple(result0, result1, result2, result3, result4, result5, result6, result7);
return result;
}
#ifdef FEATURE_DPAD_5WIRESW
Helper<PINS_DPAD_5WIRESW_OUT, PINS_DPAD_5WIRESW_IN1, PINS_DPAD_5WIRESW_IN2, PINS_DPAD_5WIRESW_IN3, PINS_DPAD_5WIRESW_IN4> helper;
State lastState;
millis_t debounceUp, debounceDown, debounceConfirm, debounceBack, debounceProfile0, debounceProfile1, debounceProfile2, debounceProfile3;
@ -81,78 +107,68 @@ void init()
void update()
{
const auto state = helper.read();
const auto newState = helper.read();
#ifdef DPAD_5WIRESW_DEBUG
lastState = state;
lastState = newState;
return;
#endif
const auto now = millis();
if (std::get<DPAD_5WIRESW_UP>(lastState) != std::get<DPAD_5WIRESW_UP>(state) && now-debounceUp > settings.boardcomputerHardware.dpadDebounce)
if (lastState.up != newState.up && now-debounceUp > settings.boardcomputerHardware.dpadDebounce)
{
if (std::get<DPAD_5WIRESW_UP>(state))
if (newState.up)
InputDispatcher::rotate(-1);
std::get<DPAD_5WIRESW_UP>(lastState) = std::get<DPAD_5WIRESW_UP>(state);
debounceUp = now;
}
if (std::get<DPAD_5WIRESW_DOWN>(lastState) != std::get<DPAD_5WIRESW_DOWN>(state) && now-debounceDown > settings.boardcomputerHardware.dpadDebounce)
if (lastState.down != newState.down && now-debounceDown > settings.boardcomputerHardware.dpadDebounce)
{
if (std::get<DPAD_5WIRESW_DOWN>(state))
if (newState.down)
InputDispatcher::rotate(1);
std::get<DPAD_5WIRESW_DOWN>(lastState) = std::get<DPAD_5WIRESW_DOWN>(state);
debounceDown = now;
}
if (std::get<DPAD_5WIRESW_CONFIRM>(lastState) != std::get<DPAD_5WIRESW_CONFIRM>(state) && now-debounceConfirm > settings.boardcomputerHardware.dpadDebounce)
if (lastState.confirm != newState.confirm && now-debounceConfirm > settings.boardcomputerHardware.dpadDebounce)
{
InputDispatcher::confirmButton(std::get<DPAD_5WIRESW_CONFIRM>(state));
std::get<DPAD_5WIRESW_CONFIRM>(lastState) = std::get<DPAD_5WIRESW_CONFIRM>(state);
InputDispatcher::confirmButton(std::get<DPAD_5WIRESW_CONFIRM>(newState));
debounceConfirm = now;
}
if (std::get<DPAD_5WIRESW_BACK>(lastState) != std::get<DPAD_5WIRESW_BACK>(state) && now-debounceBack > settings.boardcomputerHardware.dpadDebounce)
if (lastState.back != newState.back && now-debounceBack > settings.boardcomputerHardware.dpadDebounce)
{
InputDispatcher::backButton(std::get<DPAD_5WIRESW_BACK>(state));
std::get<DPAD_5WIRESW_BACK>(lastState) = std::get<DPAD_5WIRESW_BACK>(state);
InputDispatcher::backButton(std::get<DPAD_5WIRESW_BACK>(newState));
debounceBack = now;
}
if (std::get<DPAD_5WIRESW_PROFILE0>(lastState) != std::get<DPAD_5WIRESW_PROFILE0>(state) && now-debounceProfile0 > settings.boardcomputerHardware.dpadDebounce)
if (lastState.profile0 != newState.profile0 && now-debounceProfile0 > settings.boardcomputerHardware.dpadDebounce)
{
if (std::get<DPAD_5WIRESW_PROFILE0>(state))
{
SwitchProfileAction<0>{}.triggered();
}
std::get<DPAD_5WIRESW_PROFILE0>(lastState) = std::get<DPAD_5WIRESW_PROFILE0>(state);
InputDispatcher::profileButton(0, std::get<DPAD_5WIRESW_PROFILE0>(newState));
debounceProfile0 = now;
}
if (std::get<DPAD_5WIRESW_PROFILE1>(lastState) != std::get<DPAD_5WIRESW_PROFILE1>(state) && now-debounceProfile1 > settings.boardcomputerHardware.dpadDebounce)
if (lastState.profile1 != newState.profile1 && now-debounceProfile1 > settings.boardcomputerHardware.dpadDebounce)
{
if (std::get<DPAD_5WIRESW_PROFILE1>(state))
{
SwitchProfileAction<1>{}.triggered();
}
std::get<DPAD_5WIRESW_PROFILE1>(lastState) = std::get<DPAD_5WIRESW_PROFILE1>(state);
InputDispatcher::profileButton(1, std::get<DPAD_5WIRESW_PROFILE1>(newState));
debounceProfile1 = now;
}
if (std::get<DPAD_5WIRESW_PROFILE2>(lastState) != std::get<DPAD_5WIRESW_PROFILE2>(state) && now-debounceProfile2 > settings.boardcomputerHardware.dpadDebounce)
if (lastState.profile2 != newState.profile2 && now-debounceProfile2 > settings.boardcomputerHardware.dpadDebounce)
{
if (std::get<DPAD_5WIRESW_PROFILE2>(state))
{
SwitchProfileAction<2>{}.triggered();
}
std::get<DPAD_5WIRESW_PROFILE2>(lastState) = std::get<DPAD_5WIRESW_PROFILE2>(state);
InputDispatcher::profileButton(2, std::get<DPAD_5WIRESW_PROFILE2>(newState));
debounceProfile2 = now;
}
if (std::get<DPAD_5WIRESW_PROFILE3>(lastState) != std::get<DPAD_5WIRESW_PROFILE3>(state) && now-debounceProfile3 > settings.boardcomputerHardware.dpadDebounce)
if (lastState.profile3 != newState.profile3 && now-debounceProfile3 > settings.boardcomputerHardware.dpadDebounce)
{
if (std::get<DPAD_5WIRESW_PROFILE3>(state))
{
SwitchProfileAction<3>{}.triggered();
}
std::get<DPAD_5WIRESW_PROFILE3>(lastState) = std::get<DPAD_5WIRESW_PROFILE3>(state);
InputDispatcher::profileButton(3, std::get<DPAD_5WIRESW_PROFILE3>(newState));
debounceProfile3 = now;
}
lastState = newState;
}
#endif
}
}
} // namespace dpad5wire
} // namespace

View File

@ -5,7 +5,7 @@
#include <HardwareSerial.h>
#include "bobbycar-protocol/protocol.h"
#include "bobbycar-protocol/bobbycar-serial.h"
#include "types.h"
@ -13,13 +13,15 @@ namespace {
class FeedbackParser
{
public:
FeedbackParser(const std::reference_wrapper<HardwareSerial> &serial, bool &feedbackValid, Feedback &feedback) :
FeedbackParser(const std::reference_wrapper<HardwareSerial> &serial, bool &feedbackValid, bobbycar::protocol::serial::Feedback &feedback) :
m_serial{serial}, m_feedbackValid{feedbackValid}, m_feedback{feedback}
{
}
void update()
{
using namespace bobbycar::protocol::serial;
// Check for new data availability in the Serial buffer
while (m_serial.get().available())
{
@ -83,6 +85,6 @@ private:
millis_t m_lastFeedback{millis()};
const std::reference_wrapper<HardwareSerial> &m_serial;
bool &m_feedbackValid;
Feedback &m_feedback, m_newFeedback;
bobbycar::protocol::serial::Feedback &m_feedback, m_newFeedback;
};
}

View File

@ -3,6 +3,7 @@
// system includes
#include <array>
#include <memory>
#include <optional>
// Arduino includes
#ifdef FEATURE_BLUETOOTH
@ -21,8 +22,9 @@
#include "types.h"
namespace {
int16_t raw_gas, raw_brems;
float gas, brems;
std::optional<int16_t> raw_gas, raw_brems;
std::optional<float> gas, brems;
#ifdef FEATURE_GAMETRAK
int16_t raw_gametrakX, raw_gametrakY, raw_gametrakDist;
float gametrakX, gametrakY, gametrakDist;
@ -41,8 +43,18 @@ class Controllers : public std::array<Controller, 2>
public:
explicit Controllers() :
std::array<Controller, 2>{{
Controller{Serial1, settings.controllerHardware.enableFrontLeft, settings.controllerHardware.enableFrontRight, settings.controllerHardware.invertFrontLeft, settings.controllerHardware.invertFrontRight},
Controller{Serial2, settings.controllerHardware.enableBackLeft, settings.controllerHardware.enableBackRight, settings.controllerHardware.invertBackLeft, settings.controllerHardware.invertBackRight}
Controller {
#ifdef FEATURE_SERIAL
Serial1,
#endif
settings.controllerHardware.enableFrontLeft, settings.controllerHardware.enableFrontRight, settings.controllerHardware.invertFrontLeft, settings.controllerHardware.invertFrontRight
},
Controller {
#ifdef FEATURE_SERIAL
Serial2,
#endif
settings.controllerHardware.enableBackLeft, settings.controllerHardware.enableBackRight, settings.controllerHardware.invertBackLeft, settings.controllerHardware.invertBackRight
}
}}
{}
@ -72,64 +84,4 @@ TFT_eSPI tft = TFT_eSPI();
ModeInterface *currentMode{};
std::unique_ptr<Display> currentDisplay;
int rotated{};
bool requestFullRedraw{};
bool confirmButtonPressed{};
bool confirmButtonLongPressed{};
bool backButtonPressed{};
bool backButtonLongPressed{};
class InputDispatcher
{
public:
static void rotate(int offset)
{
rotated += offset;
}
static void confirmButton(bool pressed)
{
static millis_t pressBegin = 0;
const auto now = millis();
if (pressed)
pressBegin = now;
else
{
const auto duration = now - pressBegin;
if (duration < 500)
confirmButtonPressed = true;
else if (duration < 2000)
confirmButtonLongPressed = true;
else
requestFullRedraw = true;
pressBegin = 0;
}
}
static void backButton(bool pressed)
{
static millis_t pressBegin = 0;
const auto now = millis();
if (pressed)
pressBegin = now;
else
{
const auto duration = now - pressBegin;
if (duration < 500)
backButtonPressed = true;
else
backButtonLongPressed = true;
pressBegin = 0;
}
}
};
}

View File

@ -10,7 +10,7 @@ void breakLine(AsyncResponseStream &stream)
void label(AsyncResponseStream &stream, const char *name, const char *text)
{
HtmlTag label(stream, "label", String(" for=\"") + name + "\"");
HtmlTag label(stream, "label", std::string(" for=\"") + name + "\"");
stream.print(text);
}
@ -78,7 +78,7 @@ void checkboxInput(AsyncResponseStream &stream, bool value, const char *name, co
void selectOption(AsyncResponseStream &stream, const char *value, const char *text, bool selected)
{
String str{" value=\""};
std::string str{" value=\""};
str += value;
str += "\"";

View File

@ -12,7 +12,8 @@
#include <WiFi.h>
#include <WiFiMulti.h>
#include "bobbycar-protocol/protocol.h"
#include "bobbycar-protocol/bobbycar-common.h"
#include "bobbycar-protocol/bobbycar-serial.h"
#include "globals.h"
#include "modes/defaultmode.h"
@ -62,6 +63,7 @@
#include "displays/metersdisplay.h"
#include "displays/pingpongdisplay.h"
#include "displays/poweroffdisplay.h"
#include "displays/powersupplydisplay.h"
#include "displays/spirodisplay.h"
#include "displays/starfielddisplay.h"
#include "displays/statusdisplay.h"
@ -83,6 +85,9 @@
#endif
#include "bobby_webserver.h"
#include "types.h"
#ifdef FEATURE_CAN
#include "can.h"
#endif
namespace {
ModeInterface *lastMode{};
@ -105,7 +110,7 @@ void printMemoryStats(const char *s)
void cloudTask(void*)
{
const esp_websocket_client_config_t config = {
.uri = "--REMOVED--",
.uri = "ws://iot.wattpilot.io:8080/charger/bobbycar1",
};
esp_websocket_client_handle_t handle = esp_websocket_client_init(&config);
@ -121,35 +126,35 @@ void cloudTask(void*)
{
if (esp_websocket_client_is_connected(handle))
{
String msg = "{"
std::string msg = "{"
"\"type\": \"fullStatus\","
"\"partial\": false, "
"\"status\": {"
"\"millis\":" + String{millis()} + ","
"\"millis\":" + std::to_string(millis()) + ","
"\"front.valid\":" + (controllers.front.feedbackValid?"true":"false") + ","
"\"back.valid\":" + (controllers.back.feedbackValid?"true":"false") + ","
"\"front.left.pwm\":" + String(controllers.front.command.left.pwm) + ","
"\"front.right.pwm\":" + String(controllers.front.command.right.pwm) + ","
"\"back.left.pwm\":" + String(controllers.back.command.left.pwm) + ","
"\"back.right.pwm\":" + String(controllers.back.command.right.pwm) + ","
"\"front.volt\":" + String(controllers.front.feedback.batVoltage) + ","
"\"back.volt\":" + String(controllers.back.feedback.batVoltage) + ","
"\"front.temp\":" + String(controllers.front.feedback.boardTemp) + ","
"\"back.temp\":" + String(controllers.back.feedback.boardTemp) + ","
"\"front.bad\":" + String(controllers.front.feedback.timeoutCntSerial) + ","
"\"back.bad\":" + String(controllers.back.feedback.timeoutCntSerial) + ","
"\"front.left.speed\":" + String(controllers.front.feedback.left.speed) + ","
"\"front.right.speed\":" + String(controllers.front.feedback.right.speed) + ","
"\"back.left.speed\":" + String(controllers.back.feedback.left.speed) + ","
"\"back.right.speed\":" + String(controllers.back.feedback.right.speed) + ","
"\"front.left.current\":" + String(controllers.front.feedback.left.current) + ","
"\"front.right.current\":" + String(controllers.front.feedback.right.current) + ","
"\"back.left.current\":" + String(controllers.back.feedback.left.current) + ","
"\"back.right.current\":" + String(controllers.back.feedback.right.current) + ","
"\"front.left.error\":" + String(controllers.front.feedback.left.error) + ","
"\"front.right.error\":" + String(controllers.front.feedback.right.error) + ","
"\"back.left.error\":" + String(controllers.back.feedback.left.error) + ","
"\"back.right.error\":" + String(controllers.back.feedback.right.error) +
"\"front.left.pwm\":" + std::to_string(controllers.front.command.left.pwm) + ","
"\"front.right.pwm\":" + std::to_string(controllers.front.command.right.pwm) + ","
"\"back.left.pwm\":" + std::to_string(controllers.back.command.left.pwm) + ","
"\"back.right.pwm\":" + std::to_string(controllers.back.command.right.pwm) + ","
"\"front.volt\":" + std::to_string(controllers.front.feedback.batVoltage) + ","
"\"back.volt\":" + std::to_string(controllers.back.feedback.batVoltage) + ","
"\"front.temp\":" + std::to_string(controllers.front.feedback.boardTemp) + ","
"\"back.temp\":" + std::to_string(controllers.back.feedback.boardTemp) + ","
"\"front.bad\":" + std::to_string(controllers.front.feedback.timeoutCntSerial) + ","
"\"back.bad\":" + std::to_string(controllers.back.feedback.timeoutCntSerial) + ","
"\"front.left.speed\":" + std::to_string(controllers.front.feedback.left.speed) + ","
"\"front.right.speed\":" + std::to_string(controllers.front.feedback.right.speed) + ","
"\"back.left.speed\":" + std::to_string(controllers.back.feedback.left.speed) + ","
"\"back.right.speed\":" + std::to_string(controllers.back.feedback.right.speed) + ","
"\"front.left.current\":" + std::to_string(controllers.front.feedback.left.current) + ","
"\"front.right.current\":" + std::to_string(controllers.front.feedback.right.current) + ","
"\"back.left.current\":" + std::to_string(controllers.back.feedback.left.current) + ","
"\"back.right.current\":" + std::to_string(controllers.back.feedback.right.current) + ","
"\"front.left.error\":" + std::to_string(controllers.front.feedback.left.error) + ","
"\"front.right.error\":" + std::to_string(controllers.front.feedback.right.error) + ","
"\"back.left.error\":" + std::to_string(controllers.back.feedback.left.error) + ","
"\"back.right.error\":" + std::to_string(controllers.back.feedback.right.error) +
"}"
"}";
@ -162,7 +167,7 @@ void cloudTask(void*)
else
Serial.println("Not sending cloud because not connected");
delay(1000);
delay(100);
}
}
else
@ -181,6 +186,11 @@ void setup()
Serial.setDebugOutput(true);
//Serial.println("setup()");
#ifdef PINS_LED
pinMode(PINS_LED, OUTPUT);
digitalWrite(PINS_LED, LOW);
#endif
printMemoryStats("setup()");
pinMode(3, INPUT_PULLUP);
@ -233,9 +243,11 @@ void setup()
}
printMemoryStats("loadSettings()");
#ifdef FEATURE_SERIAL
bootLabel.redraw("swap front back");
updateSwapFrontBack();
printMemoryStats("swapFronBack()");
#endif
bootLabel.redraw("deviceName");
{
@ -295,16 +307,22 @@ void setup()
}
#endif
#ifdef FEATURE_CAN
can::initCan();
#endif
#ifdef FEATURE_SERIAL
bootLabel.redraw("front Serial begin");
controllers.front.serial.get().begin(38400, SERIAL_8N1, PINS_RX1, PINS_TX1);
bootLabel.redraw("back Serial begin");
controllers.back.serial.get().begin(38400, SERIAL_8N1, PINS_RX2, PINS_TX2);
#endif
raw_gas = 0;
raw_brems = 0;
gas = 0;
brems = 0;
raw_gas = std::nullopt;
raw_brems = std::nullopt;
gas = std::nullopt;
brems = std::nullopt;
for (Controller &controller : controllers)
controller.command.buzzer = {};
@ -317,9 +335,11 @@ void setup()
printMemoryStats("initOta()");
#endif
#ifdef FEATURE_WEBSERVER
bootLabel.redraw("webserver");
initWebserver();
printMemoryStats("initWebserver()");
#endif
bootLabel.redraw("potis");
readPotis();
@ -337,7 +357,7 @@ void setup()
return;
#endif
if (gas > 200.f || brems > 200.f)
if (!gas || !brems || *gas > 200.f || *brems > 200.f)
switchScreen<CalibrateDisplay>(true);
else
switchScreen<StatusDisplay>();
@ -418,8 +438,14 @@ void loop()
performance.lastTime = now;
}
#ifdef FEATURE_CAN
can::parseCanInput();
#endif
#ifdef FEATURE_SERIAL
for (Controller &controller : controllers)
controller.parser.update();
#endif
handleSerial();

View File

@ -1,6 +1,6 @@
#pragma once
#include <WString.h>
#include <string>
namespace {
class ModeInterface {

View File

@ -31,64 +31,81 @@ DefaultMode defaultMode;
void DefaultMode::update()
{
if (waitForGasLoslass)
if (!gas || !brems)
{
if (gas < 50)
waitForGasLoslass = false;
else
gas = 0;
}
start();
if (waitForBremsLoslass)
{
if (brems < 50)
waitForBremsLoslass = false;
else
brems = 0;
}
const auto gas_processed = settings.defaultMode.squareGas ? (gas * gas) / 1000.f : gas;
const auto brems_processed = settings.defaultMode.squareBrems ? (brems * brems) / 1000 : brems;
const auto now = millis();
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.enableSmoothing && (pwm > 1000. || lastPwm > 1000.))
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
if (lastPwm < pwm)
{
pwm = std::min(pwm, lastPwm+(settings.defaultMode.smoothing*(now-lastTime)/100.f));
if (pwm < 1000.)
pwm = 1000.;
}
else if (lastPwm > pwm)
{
pwm = std::max(pwm, lastPwm-(settings.defaultMode.smoothing*(now-lastTime)/100.f));
}
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::OpenMode;
motor.pwm = 0;
}
}
else
pwm = (gas_processed/1000.*settings.defaultMode.gas2_wert) - (brems_processed/1000.*settings.defaultMode.brems2_wert);
lastPwm = pwm;
lastTime = now;
const auto pair = split(settings.defaultMode.modelMode);
for (MotorState &motor : motorsInController(controllers.front))
{
motor.ctrlTyp = pair.first;
motor.ctrlMod = pair.second;
motor.pwm = pwm / 100. * settings.defaultMode.frontPercentage;
}
for (MotorState &motor : motorsInController(controllers.back))
{
motor.ctrlTyp = pair.first;
motor.ctrlMod = pair.second;
motor.pwm = pwm / 100. * settings.defaultMode.backPercentage;
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 = millis();
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.enableSmoothing && (pwm > 1000. || lastPwm > 1000.))
{
if (lastPwm < pwm)
{
pwm = std::min(pwm, lastPwm+(settings.defaultMode.smoothing*(now-lastTime)/100.f));
if (pwm < 1000.)
pwm = 1000.;
}
else if (lastPwm > pwm)
{
pwm = std::max(pwm, lastPwm-(settings.defaultMode.smoothing*(now-lastTime)/100.f));
}
}
}
else
pwm = (gas_processed/1000.*settings.defaultMode.gas2_wert) - (brems_processed/1000.*settings.defaultMode.brems2_wert);
lastPwm = pwm;
lastTime = now;
const auto pair = split(settings.defaultMode.modelMode);
for (bobbycar::protocol::serial::MotorState &motor : motorsInController(controllers.front))
{
motor.ctrlTyp = pair.first;
motor.ctrlMod = pair.second;
motor.pwm = pwm / 100. * settings.defaultMode.frontPercentage;
}
for (bobbycar::protocol::serial::MotorState &motor : motorsInController(controllers.back))
{
motor.ctrlTyp = pair.first;
motor.ctrlMod = pair.second;
motor.pwm = pwm / 100. * settings.defaultMode.backPercentage;
}
}
fixCommonParams();

View File

@ -14,7 +14,7 @@ constexpr const T& clamp( const T& v, const T& lo, const T& hi )
#include "utils.h"
#include "defaultmode.h"
#include "bobbycar-protocol/protocol.h"
#include "bobbycar-protocol/bobbycar-common.h"
namespace {
#ifdef FEATURE_GAMETRAK
@ -41,36 +41,50 @@ void GametrakMode::start()
void GametrakMode::update()
{
if (gas > 500. || brems > 500.)
if (!gas || !brems)
{
modes::defaultMode.waitForGasLoslass = true;
modes::defaultMode.waitForBremsLoslass = true;
currentMode = &modes::defaultMode;
return;
}
start();
int16_t pwm;
if (gametrakDist < 150)
{
pwm = 0;
m_flag = false;
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::OpenMode;
motor.pwm = 0;
}
}
else
{
if (m_flag || gametrakDist >= 400)
if (*gas > 500. || *brems > 500.)
{
m_flag = true;
pwm = clamp<int>((gametrakDist - 400) / 2, -200, 200);
modes::defaultMode.waitForGasLoslass = true;
modes::defaultMode.waitForBremsLoslass = true;
currentMode = &modes::defaultMode;
return;
}
int16_t pwm;
if (gametrakDist < 150)
{
pwm = 0;
m_flag = false;
}
else
pwm = 0;
}
{
if (m_flag || gametrakDist >= 400)
{
m_flag = true;
pwm = clamp<int>((gametrakDist - 400) / 2, -200, 200);
}
else
pwm = 0;
}
for (MotorState &motor : motors())
{
motor.ctrlTyp = ControlType::FieldOrientedControl;
motor.ctrlMod = ControlMode::Speed;
motor.pwm = pwm;
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::Speed;
motor.pwm = pwm;
}
}
fixCommonParams();

View File

@ -4,13 +4,13 @@
#include "globals.h"
#include "utils.h"
#include "bobbycar-protocol/protocol.h"
#include "bobbycar-protocol/bobbycar-common.h"
namespace {
class IgnoreInputMode : public ModeInterface
{
public:
IgnoreInputMode(int16_t pwm, ControlType ctrlTyp, ControlMode ctrlMod) :
IgnoreInputMode(int16_t pwm, bobbycar::protocol::ControlType ctrlTyp, bobbycar::protocol::ControlMode ctrlMod) :
m_pwm{pwm}, m_ctrlTyp{ctrlTyp}, m_ctrlMod{ctrlMod}
{
}
@ -21,13 +21,13 @@ public:
private:
const int16_t m_pwm;
const ControlType m_ctrlTyp;
const ControlMode m_ctrlMod;
const bobbycar::protocol::ControlType m_ctrlTyp;
const bobbycar::protocol::ControlMode m_ctrlMod;
};
void IgnoreInputMode::update()
{
for (MotorState &motor : motors())
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = m_ctrlTyp;
motor.ctrlMod = m_ctrlMod;

View File

@ -4,7 +4,7 @@
#include "globals.h"
#include "utils.h"
#include "bobbycar-protocol/protocol.h"
#include "bobbycar-protocol/bobbycar-common.h"
namespace {
class LarsmMode : public ModeInterface
@ -36,72 +36,86 @@ void LarsmMode::start()
void LarsmMode::update()
{
for (uint8_t i = 0; i < settings.larsmMode.iterations; i++) // run multiple times to emulate higher refreshrate
if (!gas || !brems)
{
// ####### larsm's bobby car code #######
start();
// 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
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::OpenMode;
motor.pwm = 0;
}
}
else
{
for (uint8_t i = 0; i < settings.larsmMode.iterations; i++) // run multiple times to emulate higher refreshrate
{
// ####### larsm's bobby car code #######
// 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.
// 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
#define ADC1_MIN 0
#define ADC2_MIN 0
#define ADC1_MAX 1000
#define ADC2_MAX 1000
// 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 ADC2_DELTA (ADC2_MAX - ADC2_MIN)
#define ADC1_DELTA (ADC1_MAX - ADC1_MIN)
#define ADC1_MIN 0
#define ADC2_MIN 0
#define ADC1_MAX 1000
#define ADC2_MAX 1000
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
#define ADC2_DELTA (ADC2_MAX - ADC2_MIN)
#define ADC1_DELTA (ADC1_MAX - ADC1_MIN)
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;
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
} 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;
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::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::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::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!!
} 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
}
speed = CLAMP(speed, -1000, 1000); // clamp output
}
for (MotorState &motor : motors())
{
const auto pair = split(settings.larsmMode.modelMode);
motor.ctrlTyp = pair.first;
motor.ctrlMod = pair.second;
motor.pwm = speed + weak;
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;
}
}
fixCommonParams();

View File

@ -2,7 +2,7 @@
#include <cstdint>
#include "bobbycar-protocol/protocol.h"
#include "bobbycar-protocol/bobbycar-common.h"
#include "modeinterface.h"
#include "globals.h"
@ -32,23 +32,37 @@ void TempomatMode::start()
void TempomatMode::update()
{
if (gas > 500. && brems > 500.)
if (!gas || !brems)
{
pwm = 0;
modes::defaultMode.waitForGasLoslass = true;
modes::defaultMode.waitForBremsLoslass = true;
currentMode = &modes::defaultMode;
return;
start();
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
motor.ctrlMod = bobbycar::protocol::ControlMode::OpenMode;
motor.pwm = 0;
}
}
pwm += (gas/1000.) - (brems/1000.);
for (MotorState &motor : motors())
else
{
const auto pair = split(settings.tempomatMode.modelMode);
motor.ctrlTyp = pair.first;
motor.ctrlMod = pair.second;
motor.pwm = pwm;
if (*gas > 500. && *brems > 500.)
{
pwm = 0;
modes::defaultMode.waitForGasLoslass = true;
modes::defaultMode.waitForBremsLoslass = true;
currentMode = &modes::defaultMode;
return;
}
pwm += (*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 = pwm;
}
}
fixCommonParams();

View File

@ -14,14 +14,13 @@ void initOta()
{
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else if (ArduinoOTA.getCommand() == U_SPIFFS) // U_SPIFFS
type = "filesystem";
else
type = "unknown";
std::string type;
switch (ArduinoOTA.getCommand())
{
case U_FLASH: type = "sketch"; break;
case U_SPIFFS: type = "filesystem"; break;
default: type = "unknown";
}
switchScreenImpl<UpdateDisplay>("Updating " + type);
})
.onEnd([]() {

View File

@ -17,7 +17,7 @@ void handleSerial()
if (last_status != status)
{
Serial.print("Status changed to: ");
Serial.println(toString(status));
Serial.println(to_string(status).c_str());
last_status = status;
}
@ -25,7 +25,7 @@ void handleSerial()
if (last_ip != ip)
{
Serial.print("IP changed to: ");
Serial.println(ip.toString());
Serial.println(to_string(ip).c_str());
last_ip = ip;
}

View File

@ -4,7 +4,7 @@
#include <esp_wifi_types.h>
#include "bobbycar-protocol/protocol.h"
#include "bobbycar-protocol/bobbycar-common.h"
#ifdef FEATURE_BLUETOOTH
#include "bluetoothmode.h"

View File

@ -58,7 +58,9 @@ struct WheelDiameterInchAccessor : public virtual AccessorInterface<float>
struct NumMagnetPolesAccessor : public RefAccessorSaveSettings<int16_t> { int16_t &getRef() const override { return settings.controllerHardware.numMagnetPoles; } };
struct SwapFrontBackAccessor : public RefAccessorSaveSettings<bool> {
bool &getRef() const override { return settings.controllerHardware.swapFrontBack; }
#ifdef FEATURE_SERIAL
void setValue(bool value) override { RefAccessorSaveSettings<bool>::setValue(value); updateSwapFrontBack(); };
#endif
};
struct SampleCountAccessor : public RefAccessorSaveSettings<int16_t> { int16_t &getRef() const override { return settings.boardcomputerHardware.sampleCount; } };

View File

@ -1,13 +1,12 @@
#pragma once
#include <type_traits>
#include <optional>
#include <HardwareSerial.h>
#include <nvs_flash.h>
#include <nvs.h>
#include <optional>
#include "settings.h"
#ifdef FEATURE_BLUETOOTH
#include "bluetoothmode.h"
@ -78,7 +77,7 @@ bool SettingsPersister::openProfile(uint8_t index)
closeProfile();
nvs_handle handle;
esp_err_t err = nvs_open((String{"bobbycar"}+index).c_str(), NVS_READWRITE, &handle);
esp_err_t err = nvs_open(("bobbycar"+std::to_string(index)).c_str(), NVS_READWRITE, &handle);
if (err != ESP_OK)
{
Serial.printf("nvs_open() returned: %s\r\n", esp_err_to_name(err));
@ -115,20 +114,20 @@ template<> struct nvsGetterHelper<bool> { static esp_err_t nvs_get(nvs_handle ha
*out_value = tempValue;
return err;
}};
template<> struct nvsGetterHelper<ControlType> { static esp_err_t nvs_get(nvs_handle handle, const char* key, ControlType* out_value)
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 = ControlType(tempValue);
*out_value = bobbycar::protocol::ControlType(tempValue);
return err;
}};
template<> struct nvsGetterHelper<ControlMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, ControlMode* out_value)
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 = ControlMode(tempValue);
*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)
@ -197,11 +196,11 @@ template<> struct nvsSetterHelper<uint16_t> { static constexpr auto nvs_set = &n
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<ControlType> { static esp_err_t nvs_set(nvs_handle handle, const char* key, ControlType value)
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<ControlMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, ControlMode 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));
}};

23
src/settingsutils.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
// Arduino includes
#include <HardwareSerial.h>
// local includes
#include "globals.h"
#include "presets.h"
namespace {
void switchProfile(uint8_t index)
{
settings = presets::defaultSettings;
if (settingsPersister.openProfile(index))
{
if (!settingsPersister.load(settings))
Serial.println("switchProfile() load failed");
}
else
Serial.println("switchProfile() openProfile failed");
}
}

View File

@ -18,22 +18,24 @@ ContainerType gas, brems, avgSpeed, avgSpeedKmh, sumCurrent, frontVoltage, backV
void pushStats()
{
statistics::gas.push_back(gas);
statistics::brems.push_back(brems);
if (gas)
statistics::gas.push_back(*gas);
if (brems)
statistics::brems.push_back(*brems);
statistics::avgSpeed.push_back(avgSpeed);
statistics::avgSpeedKmh.push_back(avgSpeedKmh);
statistics::sumCurrent.push_back(sumCurrent);
if (controllers.front.feedbackValid)
{
statistics::frontVoltage.push_back(fixBatVoltage(controllers.front.feedback.batVoltage));
statistics::frontLeftCurrent.push_back(fixCurrent(controllers.front.feedback.left.current));
statistics::frontRightCurrent.push_back(fixCurrent(controllers.front.feedback.right.current));
statistics::frontLeftCurrent.push_back(fixCurrent(controllers.front.feedback.left.dcLink));
statistics::frontRightCurrent.push_back(fixCurrent(controllers.front.feedback.right.dcLink));
}
if (controllers.back.feedbackValid)
{
statistics::backVoltage.push_back(fixBatVoltage(controllers.back.feedback.batVoltage));
statistics::backLeftCurrent.push_back(fixCurrent(controllers.back.feedback.left.current));
statistics::backRightCurrent.push_back(fixCurrent(controllers.back.feedback.right.current));
statistics::backLeftCurrent.push_back(fixCurrent(controllers.back.feedback.left.dcLink));
statistics::backRightCurrent.push_back(fixCurrent(controllers.back.feedback.right.dcLink));
}
#ifdef FEATURE_BMS
statistics::bmsVoltage.push_back(bms::voltage);

View File

@ -1,11 +1,13 @@
#pragma once
#include <WString.h>
#include <string>
#include "utils.h"
namespace {
class TextInterface {
public:
virtual String text() const = 0;
virtual std::string text() const = 0;
};
template<const char *Ttext>
@ -14,31 +16,32 @@ class StaticText : public virtual TextInterface
public:
static constexpr const char *STATIC_TEXT = Ttext;
String text() const override { return Ttext; }
std::string text() const override { return Ttext; }
};
class ChangeableText : public virtual TextInterface
{
public:
String text() const override { return m_title; }
void setTitle(const String &title) { m_title = title; }
std::string text() const override { return m_title; }
void setTitle(std::string &&title) { m_title = std::move(title); }
void setTitle(const std::string &title) { m_title = title; }
private:
String m_title;
std::string m_title;
};
template<const char *Ttext, typename Ttype, Ttype *Tptr, typename TreturnType, TreturnType (Ttype::*Tmethod)()>
class StatusTextHelper : public virtual TextInterface
{
public:
String text() const override { return String{Ttext} + (Tptr->*Tmethod)(); }
std::string text() const override { using std::to_string; using ::to_string; return Ttext + to_string((Tptr->*Tmethod)()); }
};
template<typename T>
class CachedText : public virtual T
{
public:
String text() const override
std::string text() const override
{
if (!m_loaded)
{
@ -51,14 +54,14 @@ public:
private:
mutable bool m_loaded{};
mutable String m_text;
mutable std::string m_text;
};
template<typename T>
class StaticallyCachedText : public virtual T
{
public:
String text() const override
std::string text() const override
{
static const auto text = T::text();
return text;

View File

@ -130,13 +130,13 @@ constexpr char TEXT_REVERSEBEEPDURATION1[] = "Reverse beep duration1";
//constexpr char TEXT_BACK[] = "Back";
//LimitsSettingsMenu
//constexpr char TEXT_LIMITSSETTINGS[] = "Common settings";
constexpr char TEXT_SETIMOTMAX[] = "Set iMotMax";
constexpr char TEXT_SETIDCMAX[] = "Set iDcMax";
constexpr char TEXT_SETNMOTMAXKMH[] = "Set nMotMax (kmh)";
constexpr char TEXT_SETNMOTMAX[] = "Set nMotMax";
constexpr char TEXT_SETFIELDWEAKMAX[] = "Set fieldWeakMax";
constexpr char TEXT_SETPHASEADVMAX[] = "Set phaseAdvMax";
//constexpr char TEXT_LIMITSSETTINGS[] = "Limit settings";
constexpr char TEXT_IMOTMAX[] = "iMotMax";
constexpr char TEXT_IDCMAX[] = "iDcMax";
constexpr char TEXT_NMOTMAXKMH[] = "nMotMaxKmh";
constexpr char TEXT_NMOTMAX[] = "nMotMax";
constexpr char TEXT_FIELDWEAKMAX[] = "fldWkMax";
constexpr char TEXT_PHASEADVMAX[] = "phsAdvMax";
//constexpr char TEXT_BACK[] = "Back";
//DebugMenu
@ -363,4 +363,8 @@ constexpr char TEXT_WIFI_POWER_5dBm[] = "5dBm";
constexpr char TEXT_WIFI_POWER_2dBm[] = "2dBm";
constexpr char TEXT_WIFI_POWER_MINUS_1dBm[] = "-1dBm";
//constexpr char TEXT_BACK[] = "Back";
#ifdef FEATURE_CAN
constexpr char TEXT_POWERSUPPLY[] = "Powersupply";
#endif
}

View File

@ -4,7 +4,7 @@
#include <HardwareSerial.h>
#include "bobbycar-protocol/protocol.h"
#include "bobbycar-protocol/bobbycar-common.h"
namespace {
enum class UnifiedModelMode : uint8_t
@ -16,8 +16,11 @@ enum class UnifiedModelMode : uint8_t
FocTorque
};
std::pair<ControlType, 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);

View File

@ -2,13 +2,26 @@
#include <algorithm>
#include <utility>
#include <string>
#include <driver/can.h>
#include <ArduinoOTA.h>
#include <WString.h>
#include <WiFi.h>
#include <WString.h>
#ifdef FEATURE_CAN
#include "bobbycar-protocol/bobbycar-can.h"
#endif
#ifdef FEATURE_SERIAL
#include "bobbycar-protocol/bobbycar-serial.h"
#endif
#include "display.h"
#include "globals.h"
#ifdef FEATURE_CAN
#include "can.h"
#endif
// macros are a shit piece of software
#define STRING2(s) #s
@ -79,50 +92,42 @@ float fixBoardTemp(int16_t value)
return value/10.;
}
String hallString(const MotorFeedback &motor)
std::string hallString(const bobbycar::protocol::serial::MotorFeedback &motor)
{
return String{} + (motor.hallA ? '1' : '0') + (motor.hallB ? '1' : '0') + (motor.hallC ? '1' : '0');
return std::string{} + (motor.hallA ? '1' : '0') + (motor.hallB ? '1' : '0') + (motor.hallC ? '1' : '0');
}
template<typename T>
String toString(T value)
std::string to_string(const String &value)
{
return String{} + value;
return std::string{value.c_str(), value.length()};
}
template<>
String toString<bool>(bool value)
{
return value ? "true" : "false";
}
template<>
String toString<ControlType>(ControlType value)
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 String("Unknown: ") + int(value);
return "Unknown ControlType(" + std::to_string(int(value)) + ')';
}
template<>
String toString<ControlMode>(ControlMode 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 String("Unknown: ") + int(value);
return "Unknown ControlMode(" + std::to_string(int(value)) + ')';
}
template<>
String toString<wl_status_t>(wl_status_t value)
std::string to_string(wl_status_t value)
{
switch (value)
{
@ -136,11 +141,10 @@ String toString<wl_status_t>(wl_status_t value)
case WL_DISCONNECTED: return "WL_DISCONNECTED";
}
return String("Unknown: ") + int(value);
return "Unknown wl_status_t(" + std::to_string(int(value)) + ')';
}
template<>
String toString<ota_error_t>(ota_error_t value)
std::string to_string(ota_error_t value)
{
switch (value)
{
@ -151,30 +155,40 @@ String toString<ota_error_t>(ota_error_t value)
case OTA_END_ERROR: return "OTA_END_ERROR";
}
return String("Unknown: ") + int(value);
return "Unknown ota_error_t(" + std::to_string(int(value)) + ')';
}
std::array<std::reference_wrapper<MotorState>, 2> motorsInController(Controller &controller)
std::string to_string(IPAddress value)
{
return to_string(value.toString());
}
std::string to_string(IPv6Address value)
{
return to_string(value.toString());
}
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 MotorState>, 2> motorsInController(const Controller &controller)
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<MotorFeedback>, 2> motorFeedbacksInController(Controller &controller)
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 MotorFeedback>, 2> motorFeedbacksInController(const Controller &controller)
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<MotorState>, 4> motors()
std::array<std::reference_wrapper<bobbycar::protocol::serial::MotorState>, 4> motors()
{
return {
std::ref(controllers.front.command.left), std::ref(controllers.front.command.right),
@ -184,7 +198,7 @@ std::array<std::reference_wrapper<MotorState>, 4> motors()
void fixCommonParams()
{
for (MotorState &motor : motors())
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.iMotMax = settings.limits.iMotMax;
motor.iDcMax = settings.limits.iDcMax;
@ -196,7 +210,7 @@ void fixCommonParams()
if (settings.reverseBeep)
{
const auto x = motors();
const auto shouldBeep = std::all_of(std::begin(x), std::end(x), [](const MotorState &motor){ return motor.pwm < 0; });
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)
{
@ -247,22 +261,30 @@ void fixCommonParams()
void sendCommands()
{
#ifdef FEATURE_SERIAL
using namespace bobbycar::protocol::serial;
for (Controller &controller : controllers)
{
controller.command.start = Command::VALID_HEADER;
controller.command.checksum = calculateChecksum(controller.command);
controller.serial.get().write((uint8_t *) &controller.command, sizeof(controller.command));
}
#endif
#ifdef FEATURE_CAN
can::sendCanCommands();
#endif
}
template<typename T, typename... Args>
void switchScreen(Args&&... args);
#ifdef FEATURE_SERIAL
void updateSwapFrontBack()
{
controllers.front.serial = settings.controllerHardware.swapFrontBack ? Serial2 : Serial1;
controllers.back.serial = settings.controllerHardware.swapFrontBack ? Serial1 : Serial2;
}
#endif
void loadSettings()
{
@ -290,8 +312,8 @@ void updateAccumulators()
controller.feedback.right.speed * (controller.invertRight ? -1 : 1);
sumCurrent +=
controller.feedback.left.current +
controller.feedback.right.current;
controller.feedback.left.dcLink +
controller.feedback.right.dcLink;
count +=2;
}
@ -305,20 +327,55 @@ void updateAccumulators()
}
void readPotis()
{
const auto sampleMultipleTimes = [](int pin){
{
[[maybe_unused]]
constexpr auto sampleMultipleTimes = [](int pin){
analogRead(pin);
double sum{};
for (int i = 0; i < settings.boardcomputerHardware.sampleCount; i++)
const auto sampleCount = settings.boardcomputerHardware.sampleCount;
for (int i = 0; i < sampleCount; i++)
sum += analogRead(pin);
return sum/settings.boardcomputerHardware.sampleCount;
return sum / sampleCount;
};
raw_gas = sampleMultipleTimes(PINS_GAS);
gas = scaleBetween<float>(raw_gas, settings.boardcomputerHardware.gasMin, settings.boardcomputerHardware.gasMax, 0., 1000.);
raw_gas = std::nullopt;
raw_brems = std::nullopt;
raw_brems = sampleMultipleTimes(PINS_BREMS);
brems = scaleBetween<float>(raw_brems, settings.boardcomputerHardware.bremsMin, settings.boardcomputerHardware.bremsMax, 0., 1000.);
#ifdef FEATURE_CAN
const auto now = millis();
if (can::can_gas)
{
if (now - can::last_can_gas < 100)
raw_gas = *can::can_gas;
else
can::can_gas = std::nullopt;
}
if (can::can_brems)
{
if (now - can::last_can_brems < 100)
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 = scaleBetween<float>(*raw_gas, settings.boardcomputerHardware.gasMin, settings.boardcomputerHardware.gasMax, 0., 1000.);
else
gas = std::nullopt;
if (raw_brems)
brems = scaleBetween<float>(*raw_brems, settings.boardcomputerHardware.bremsMin, settings.boardcomputerHardware.bremsMax, 0., 1000.);
else
brems = std::nullopt;
#ifdef FEATURE_GAMETRAK
raw_gametrakX = sampleMultipleTimes(PINS_GAMETRAKX);

View File

@ -112,7 +112,7 @@ void Graph<LENGTH, COUNT>::render(const Container &buffers, bool delta)
tft.setTextFont(2);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
for (auto iter = std::begin(m_labels); iter != std::end(m_labels); iter++)
iter->redraw(String(int(m_max+((m_min-m_max)/(m_labels.size()-1)*std::distance(std::begin(m_labels), iter)))));
iter->redraw(std::to_string(int(m_max+((m_min-m_max)/(m_labels.size()-1)*std::distance(std::begin(m_labels), iter)))));
int x{leftMargin};
for (auto pixelsIter = std::begin(m_lastPixels); pixelsIter!=std::end(m_lastPixels); pixelsIter++)

View File

@ -1,6 +1,6 @@
#pragma once
#include <WString.h>
#include <string>
#include "globals.h"
@ -14,14 +14,14 @@ public:
int y() const { return m_y; };
void start();
void redraw(const String &str, bool forceRedraw = false);
void redraw(const std::string &str, bool forceRedraw = false);
void clear();
private:
const int m_x;
const int m_y;
String m_lastStr;
std::string m_lastStr;
int m_lastFont;
int m_lastColor;
@ -45,7 +45,7 @@ void Label::start()
m_lastHeight = 0;
}
void Label::redraw(const String &str, bool forceRedraw)
void Label::redraw(const std::string &str, bool forceRedraw)
{
if (m_lastStr == str &&
m_lastFont == tft.textfont &&
@ -53,7 +53,7 @@ void Label::redraw(const String &str, bool forceRedraw)
!forceRedraw)
return;
const auto renderedWidth = tft.drawString(str, m_x, m_y);
const auto renderedWidth = tft.drawString(str.c_str(), m_x, m_y);
const auto renderedHeight = tft.fontHeight();
if (renderedWidth < m_lastWidth)

View File

@ -8,110 +8,110 @@
namespace {
struct WifiStatusBitsText : public virtual TextInterface {
public:
String text() const override { return String{"statusBits: "} + WiFi.getStatusBits(); }
std::string text() const override { return "statusBits: " + std::to_string(WiFi.getStatusBits()); }
};
struct WifiChannelText : public virtual TextInterface {
public:
String text() const override { return String{"channel: "} + WiFi.channel(); }
std::string text() const override { return "channel: " + std::to_string(WiFi.channel()); }
};
struct WifiIsConnectedText : public virtual TextInterface {
public:
String text() const override { return String{"isConnected: "} + toString(WiFi.isConnected()); }
std::string text() const override { return "isConnected: " + std::to_string(WiFi.isConnected()); }
};
struct WifiLocalIpText : public virtual TextInterface {
public:
String text() const override { return String{"localIP: "} + WiFi.localIP().toString(); }
std::string text() const override { return "localIP: " + to_string(WiFi.localIP()); }
};
struct WifiMacAddressText : public virtual TextInterface {
public:
String text() const override { return String{"macAddress: "} + WiFi.macAddress(); }
std::string text() const override { return "macAddress: " + to_string(WiFi.macAddress()); }
};
struct WifiSubnetMaskText : public virtual TextInterface {
public:
String text() const override { return String{"subnetMask: "} + WiFi.subnetMask().toString(); }
std::string text() const override { return "subnetMask: " + to_string(WiFi.subnetMask()); }
};
struct WifiGatewayIpText : public virtual TextInterface {
public:
String text() const override { return String{"gatewayIP: "} + WiFi.gatewayIP().toString(); }
std::string text() const override { return "gatewayIP: " + to_string(WiFi.gatewayIP()); }
};
struct WifiDnsIpText : public virtual TextInterface {
public:
String text() const override { return String{"dnsIP: "} + WiFi.dnsIP().toString(); }
std::string text() const override { return "dnsIP: " + to_string(WiFi.dnsIP()); }
};
struct WifiBroadcastIpText : public virtual TextInterface {
public:
String text() const override { return String{"broadcastIP: "} + WiFi.broadcastIP().toString(); }
std::string text() const override { return "broadcastIP: " + to_string(WiFi.broadcastIP()); }
};
struct WifiNetworkIdText : public virtual TextInterface {
public:
String text() const override { return String{"networkID: "} + WiFi.networkID().toString(); }
std::string text() const override { return "networkID: " + to_string(WiFi.networkID()); }
};
struct WifiSubnetCIDRText : public virtual TextInterface {
public:
String text() const override { return String{"subnetCIDR: "} + WiFi.subnetCIDR(); }
std::string text() const override { return "subnetCIDR: " + to_string(WiFi.subnetCIDR()); }
};
struct WifiLocalIpV6Text : public virtual TextInterface {
public:
String text() const override { return String{"localIPv6: "} + WiFi.localIPv6().toString(); }
std::string text() const override { return "localIPv6: " + to_string(WiFi.localIPv6()); }
};
struct WifiHostnameText : public virtual TextInterface {
public:
String text() const override { return String{"hostname: "} + WiFi.getHostname(); }
std::string text() const override { return "hostname: " + to_string(WiFi.getHostname()); }
};
struct WifiStatusText : public virtual TextInterface {
public:
String text() const override { return String{"status: "} + toString(WiFi.status()); }
std::string text() const override { return "status: " + to_string(WiFi.status()); }
};
struct WifiSsidText : public virtual TextInterface {
public:
String text() const override { return String{"SSID: "} + WiFi.SSID(); }
std::string text() const override { return "SSID: " + to_string(WiFi.SSID()); }
};
struct WifiPskText : public virtual TextInterface {
public:
String text() const override { return String{"psk: "} + WiFi.psk(); }
std::string text() const override { return "psk: " + to_string(WiFi.psk()); }
};
struct WifiBssidText : public virtual TextInterface {
public:
String text() const override { return String{"BSSID: "} + WiFi.BSSIDstr(); }
std::string text() const override { return "BSSID: " + to_string(WiFi.BSSIDstr()); }
};
struct WifiRssiText : public virtual TextInterface {
public:
String text() const override { return String{"RSSI: "} + WiFi.RSSI(); }
std::string text() const override { return "RSSI: " + to_string(WiFi.RSSI()); }
};
class WifiSoftApGetStationNumText : public virtual TextInterface {
public:
String text() const override { return String{"softAPgetStationNum: "} + WiFi.softAPgetStationNum(); }
std::string text() const override { return "softAPgetStationNum: " + to_string(WiFi.softAPgetStationNum()); }
};
class WifiSoftApIpText : public virtual TextInterface {
public:
String text() const override { return String{"softAPIP: "} + WiFi.softAPIP().toString(); }
std::string text() const override { return "softAPIP: " + to_string(WiFi.softAPIP()); }
};
class WifiSoftApBroadcastIpText : public virtual TextInterface {
public:
String text() const override { return String{"softAPBroadcastIP: "} + WiFi.softAPBroadcastIP().toString(); }
std::string text() const override { return "softAPBroadcastIP: " + to_string(WiFi.softAPBroadcastIP()); }
};
class WifiSoftApNetworkIdText : public virtual TextInterface {
public:
String text() const override { return String{"softAPNetworkID: "} + WiFi.softAPNetworkID().toString(); }
std::string text() const override { return "softAPNetworkID: " + to_string(WiFi.softAPNetworkID()); }
};
class WifiSoftApSubnetCidrText : public virtual TextInterface {
public:
String text() const override { return String{"softAPSubnetCIDR: "} + WiFi.softAPSubnetCIDR(); }
std::string text() const override { return "softAPSubnetCIDR: " + std::to_string(WiFi.softAPSubnetCIDR()); }
};
class WifiSoftApIpV6Text : public virtual TextInterface {
public:
String text() const override { return String{"softAPIPv6: "} + WiFi.softAPIPv6().toString(); }
std::string text() const override { return "softAPIPv6: " + to_string(WiFi.softAPIPv6()); }
};
class WifiSoftApHostnameText : public virtual TextInterface {
public:
String text() const override { return String{"softAPgetHostname: "} + WiFi.softAPgetHostname(); }
std::string text() const override { return "softAPgetHostname: " + to_string(WiFi.softAPgetHostname()); }
};
class WifiSoftApMacAddressText : public virtual TextInterface {
public:
String text() const override { return String{"softAPmacAddress: "} + WiFi.softAPmacAddress(); }
std::string text() const override { return "softAPmacAddress: " + to_string(WiFi.softAPmacAddress()); }
};
}