Files
2023-08-13 20:26:51 +02:00

462 lines
14 KiB
C++

#include "utils.h"
// system includes
#include <utility>
// local includes
#include "globals.h"
#include "newsettings.h"
using namespace std::chrono_literals;
bool currentlyReverseBeeping{};
bool reverseBeepToggle;
espchrono::millis_clock::time_point lastReverseBeepToggle;
float convertToKmh(float val)
{
return val /* / profileSettings.controllerHardware.numMagnetPoles */ / 60.f * configs.controllerHardware.wheelDiameter.value() / 1000.f * 3.14159265359f * 3.6f;
}
float convertFromKmh(float val)
{
return val /* * profileSettings.controllerHardware.numMagnetPoles */ * 60.f / configs.controllerHardware.wheelDiameter.value() * 1000.f / 3.14159265359f / 3.6f;
}
float convertToInch(float val)
{
return val / 25.4f;
}
float convertFromInch(float val)
{
return val * 25.4f;
}
float fixCurrent(int16_t value)
{
return -value/50.f;
}
float fixBoardTemp(int16_t value)
{
return value/10.f;
}
std::string hallString(const bobbycar::protocol::serial::MotorFeedback &motor)
{
return std::string{} + (motor.hallA ? '1' : '0') + (motor.hallB ? '1' : '0') + (motor.hallC ? '1' : '0');
}
std::string to_string(bobbycar::protocol::ControlType value)
{
switch (value)
{
using namespace bobbycar::protocol;
case ControlType::Commutation: return "Commutation";
case ControlType::Sinusoidal: return "Sinusoidal";
case ControlType::FieldOrientedControl: return "FieldOrientedControl";
}
return fmt::format("Unknown ControlType({})", std::to_underlying(value));
}
std::string to_string(bobbycar::protocol::ControlMode value)
{
switch (value)
{
using namespace bobbycar::protocol;
case ControlMode::OpenMode: return "OpenMode";
case ControlMode::Voltage: return "Voltage";
case ControlMode::Speed: return "Speed";
case ControlMode::Torque: return "Torque";
}
return fmt::format("Unknown ControlMode({})", std::to_underlying(value));
}
std::array<std::reference_wrapper<bobbycar::protocol::serial::MotorState>, 2> motorsInController(Controller &controller)
{
return {std::ref(controller.command.left), std::ref(controller.command.right)};
}
std::array<std::reference_wrapper<const bobbycar::protocol::serial::MotorState>, 2> motorsInController(const Controller &controller)
{
return {std::ref(controller.command.left), std::ref(controller.command.right)};
}
std::array<std::reference_wrapper<bobbycar::protocol::serial::MotorFeedback>, 2> motorFeedbacksInController(Controller &controller)
{
return {std::ref(controller.feedback.left), std::ref(controller.feedback.right)};
}
std::array<std::reference_wrapper<const bobbycar::protocol::serial::MotorFeedback>, 2> motorFeedbacksInController(const Controller &controller)
{
return {std::ref(controller.feedback.left), std::ref(controller.feedback.right)};
}
std::array<std::reference_wrapper<bobbycar::protocol::serial::MotorState>, 4> motors()
{
return {
std::ref(controllers.front.command.left), std::ref(controllers.front.command.right),
std::ref(controllers.back.command.left), std::ref(controllers.back.command.right)
};
}
void fixCommonParams()
{
for (bobbycar::protocol::serial::MotorState &motor : motors())
{
motor.iMotMax = profileSettings.limits.iMotMax;
motor.iDcMax = profileSettings.limits.iDcMax;
motor.nMotMax = profileSettings.limits.nMotMax;
motor.fieldWeakMax = profileSettings.limits.fieldWeakMax;
motor.phaseAdvMax = profileSettings.limits.phaseAdvMax;
}
if (configs.reverseBeep.value())
{
const auto x = motors();
const auto shouldBeep = std::all_of(std::begin(x), std::end(x), [](const bobbycar::protocol::serial::MotorState &motor){ return motor.pwm < 0; });
if (shouldBeep != currentlyReverseBeeping)
{
if (shouldBeep)
{
reverseBeepToggle = true;
lastReverseBeepToggle = espchrono::millis_clock::now();
for (auto &controller : controllers)
controller.command.buzzer = {.freq=configs.reverseBeepFreq0.value(), .pattern=0};
}
else
for (auto &controller : controllers)
controller.command.buzzer = {};
currentlyReverseBeeping = shouldBeep;
}
else if (shouldBeep && espchrono::millis_clock::now() - lastReverseBeepToggle >= std::chrono::milliseconds{reverseBeepToggle?configs.reverseBeepDuration0.value():configs.reverseBeepDuration1.value()})
{
reverseBeepToggle = !reverseBeepToggle;
for (auto &controller : controllers)
controller.command.buzzer = {.freq=uint8_t(reverseBeepToggle?configs.reverseBeepFreq0.value():configs.reverseBeepFreq1.value()), .pattern=0};
lastReverseBeepToggle = espchrono::millis_clock::now();
}
}
else if (currentlyReverseBeeping)
{
for (auto &controller : controllers)
controller.command.buzzer = {};
currentlyReverseBeeping = false;
}
controllers.front.command.left.enable = profileSettings.controllerHardware.enableFrontLeft;
controllers.front.command.right.enable = profileSettings.controllerHardware.enableFrontRight;
controllers.back.command.left.enable = profileSettings.controllerHardware.enableBackLeft;
controllers.back.command.right.enable = profileSettings.controllerHardware.enableBackRight;
if (profileSettings.controllerHardware.invertFrontLeft)
{
controllers.front.command.left.pwm = -controllers.front.command.left.pwm;
controllers.front.command.left.nCruiseMotTgt = -controllers.front.command.left.nCruiseMotTgt;
}
if (profileSettings.controllerHardware.invertFrontRight)
{
controllers.front.command.right.pwm = -controllers.front.command.right.pwm;
controllers.front.command.right.nCruiseMotTgt = -controllers.front.command.right.nCruiseMotTgt;
}
if (profileSettings.controllerHardware.invertBackLeft)
{
controllers.back.command.left.pwm = -controllers.back.command.left.pwm;
controllers.back.command.left.nCruiseMotTgt = -controllers.back.command.left.nCruiseMotTgt;
}
if (profileSettings.controllerHardware.invertBackRight)
{
controllers.back.command.right.pwm = -controllers.back.command.right.pwm;
controllers.back.command.right.nCruiseMotTgt = -controllers.back.command.right.nCruiseMotTgt;
}
}
void sendCommands()
{
#ifdef FEATURE_SERIAL
using namespace bobbycar::protocol::serial;
for (Controller &controller : controllers)
{
controller.command.start = Command::VALID_HEADER;
controller.command.checksum = calculateChecksum(controller.command);
controller.serial.get().write((uint8_t *) &controller.command, sizeof(controller.command));
}
#endif
#ifdef FEATURE_CAN
can::sendCanCommands();
#endif
}
#ifdef FEATURE_SERIAL
void updateSwapFrontBack()
{
controllers.front.serial = configs.controllerHardware.swapFrontBack.value ? Serial2 : Serial1;
controllers.back.serial = configs.controllerHardware.swapFrontBack.value ? Serial1 : Serial2;
}
#endif
bool loadProfileSettings()
{
bool result{true};
if (!settingsPersister.load(profileSettings))
result = false;
return result;
}
bool saveProfileSettings()
{
if (simplified) return true;
bool result{true};
if (!settingsPersister.save(profileSettings))
result = false;
return result;
}
void updateAccumulators()
{
avgSpeed = 0.f;
sumCurrent = 0.f;
uint8_t count{0};
for (const Controller &controller : controllers)
{
if (!controller.feedbackValid)
continue;
avgSpeed +=
controller.feedback.left.speed * (controller.invertLeft ? -1 : 1) +
controller.feedback.right.speed * (controller.invertRight ? -1 : 1);
sumCurrent +=
controller.feedback.left.dcLink +
controller.feedback.right.dcLink;
count +=2;
}
if (count)
avgSpeed /= count;
sumCurrent = fixCurrent(sumCurrent);
avgSpeedKmh = convertToKmh(avgSpeed);
// accel
EVERY_N_MILLIS(100)
{
const auto now = espchrono::millis_clock::now();
const auto delta = now - lastAvgSpeedKmhTs;
const auto deltaSpeedKmh = avgSpeedKmh - lastAvgSpeedKmh;
// ESP_LOGI("utils.cpp", "delta: %lli ms, deltaSpeedKmh: %f", delta.count() / 1000, deltaSpeedKmh);
const auto deltaMilliseconds = delta.count() / 1000.f;
const auto kmh_s = deltaSpeedKmh / deltaMilliseconds; // km/h / s
const auto m_s2 = kmh_s * 1000.f / 3600.f; // m/s^2
// ESP_LOGI("utils.cpp", "m_s2: %f", m_s2);
avgAccel = avgAccel * 0.3f + m_s2 * 0.7f;
lastAvgSpeedKmh = avgSpeedKmh;
lastAvgSpeedKmhTs = espchrono::millis_clock::now();
}
}
float wattToAmpere(float watt) {
float voltage = std::max(controllers.front.feedback.batVoltage, controllers.back.feedback.batVoltage);
if (voltage > 50) voltage = 50;
if (voltage < 30) voltage = 30;
return watt / voltage;
}
float wattToMotorCurrent(float watt)
{
return wattToAmpere(watt) / 4;
}
uint8_t time_to_percent(espchrono::milliseconds32 repeat, espchrono::milliseconds32 riseTime, espchrono::milliseconds32 fullTime, size_t numLeds, bool invert)
{
const auto now = espchrono::millis_clock::now().time_since_epoch() % repeat;
if (now <= riseTime)
{
if (invert)
return numLeds - ((now*numLeds) / riseTime);
else
return (now*numLeds) / riseTime;
}
else if (now < riseTime + fullTime)
return invert ? 0 : numLeds;
else
return invert ? numLeds : 0;
}
std::string local_clock_string()
{
#ifdef CONFIG_ESPCHRONO_SUPPORT_DEFAULT_TIMEZONE
const auto now = espchrono::local_clock::now();
#else // mir egal ob die lokalzeit richtig is
const auto now = espchrono::utc_clock::now() + configs.timezoneOffset.value();
#endif
const auto dt = espchrono::toDateTime(now);
return fmt::format("{:02d}:{:02d}:{:02d}", dt.hour, dt.minute, dt.second);
}
int16_t map_analog_stick(uint16_t middle, uint16_t start, uint16_t end, uint16_t deadband, uint16_t raw)
{
if (abs(raw - middle) < configs.deadband.value())
{
return 0;
}
if (start < end)
{
if (raw < middle)
{
raw += configs.deadband.value();
start += configs.deadband.value();
const auto return_val = cpputils::mapValueClamped<float>(raw, start, middle, -1000, 0);
return return_val;
}
else
{
raw -= configs.deadband.value();
end -= configs.deadband.value();
const auto return_val = cpputils::mapValueClamped<float>(raw, middle, end, 0, 1000);
return return_val;
}
}
else
{
if (raw < middle)
{
raw += configs.deadband.value();
end += configs.deadband.value();
const auto return_val = cpputils::mapValueClamped<float>(raw, end, middle, 1000, 0);
return return_val;
}
else
{
raw -= configs.deadband.value();
start -= configs.deadband.value();
const auto return_val = cpputils::mapValueClamped<float>(raw, middle, start, 0, -1000);
return return_val;
}
}
}
std::string get_wifi_security_string(wifi_auth_mode_t authMode)
{ // use for qr code
switch (authMode)
{
case WIFI_AUTH_OPEN:
return "";
case WIFI_AUTH_WEP:
return "WEP";
case WIFI_AUTH_WPA_PSK:
case WIFI_AUTH_WPA2_PSK:
case WIFI_AUTH_WPA_WPA2_PSK:
return "WPA";
default:
return "";
}
}
float float_map(float x, float in_min, float in_max, float out_min, float out_max) {
const float dividend = out_max - out_min;
const float divisor = in_max - in_min;
const float delta = x - in_min;
return (delta * dividend + (divisor / 2.f)) / divisor + out_min;
}
bool is_valid_timestamp(espchrono::utc_clock::time_point timestamp)
{
using namespace date;
return timestamp.time_since_epoch() > sys_seconds{sys_days{1_d/January/2000}}.time_since_epoch();
}
std::string toString(esp_chip_model_t esp_chip_model)
{
switch (esp_chip_model)
{
case CHIP_ESP32:
return "ESP32";
case CHIP_ESP32S2:
return "ESP32S2";
case CHIP_ESP32S3:
return "ESP32S3";
case CHIP_ESP32C3:
return "ESP32C3";
case CHIP_ESP32H4:
return "ESP32H4";
case CHIP_ESP32C2:
return "ESP32C2";
case CHIP_ESP32C6:
return "ESP32C6";
default:
return "invalid";
}
}
std::optional<SetupStep> checkIfInCalibration()
{
if (!configs.boardcomputerHardware.setupFinished.value())
{
return SetupStep::INFORMATION;
}
else if (
configs.dpadMappingLeft.value() == INPUT_MAPPING_NONE ||
configs.dpadMappingRight.value() == INPUT_MAPPING_NONE ||
configs.dpadMappingUp.value() == INPUT_MAPPING_NONE ||
configs.dpadMappingDown.value() == INPUT_MAPPING_NONE
)
{
return SetupStep::BASIC_BUTTONS;
}
else if (!gas || !brems || *gas > 200.f || *brems > 200.f)
{
return SetupStep::CALIBRATE_POTIS;
}
return std::nullopt;
}
void drawLargeText(const std::string&& text)
{
using namespace espgui;
const auto topMargin = 50;
const uint8_t leftMargin = 8;
const auto rightMargin = leftMargin;
const auto bottomMargin = leftMargin;
int x = leftMargin + 5;
int y = topMargin + 5;
tft.setTextColor(TFT_WHITE);
for (char c : text)
{
if (c == '\n' || x > tft.width() - rightMargin - 10)
{
x = leftMargin + 5;
y += tft.fontHeight(2);
}
if (c != '\n')
{
const auto addedWidth = tft.drawChar(tft.decodeUTF8(c), x, y, 2);
x += addedWidth;
}
if (y >= tft.height() - bottomMargin)
break;
}
}