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