12 Commits

5 changed files with 506 additions and 180 deletions

31
CMakeLists.txt Normal file
View File

@ -0,0 +1,31 @@
set(headers
bobbycar-can.h
bobbycar-common.h
bobbycar-serial.h
)
set(sources
)
set(dependencies
)
idf_component_register(
INCLUDE_DIRS
.
SRCS
${headers}
${sources}
REQUIRES
${dependencies}
)
target_compile_options(${COMPONENT_TARGET}
PRIVATE
-fstack-reuse=all
-fstack-protector-all
-Wno-unused-function
-Wno-deprecated-declarations
-Wno-missing-field-initializers
-Wno-parentheses
)

View File

@ -3,92 +3,177 @@
#include <cstdint>
namespace bobbycar {
namespace protocol {
namespace can {
enum : uint16_t { // vvv'
DeviceTypeMotorController = 0b000'00000000,
DeviceTypeBoardcomputer = 0b001'00000000
};
template<bool isBack, bool isRight>
class MotorController
{
private:
enum { // vvv
DeviceTypeMotorController = 0b00000000000
enum : uint16_t { // ...v
MotorControllerCommand = 0b00000000000, // send only TO the motor controller
MotorControllerFeedback = 0b00010000000, // send only FROM the motor controller
};
enum { // ...v
MotorControllerCommand = 0b00000000000,
MotorControllerFeedback = 0b00010000000,
};
enum { // .........v
enum : uint16_t { // .........v
MotorControllerFront = 0b00000000000,
MotorControllerBack = 0b00000000010,
};
enum { // ..........v
enum : uint16_t { // ..........v
MotorControllerLeft = 0b00000000000,
MotorControllerRight = 0b00000000001,
};
enum {
enum : uint16_t {
MotorControllerMask =
DeviceTypeMotorController |
(isBack ? MotorControllerBack : MotorControllerFront) |
(isRight ? MotorControllerRight : MotorControllerLeft)
uint16_t(DeviceTypeMotorController) |
uint16_t(isBack ? MotorControllerBack : MotorControllerFront) |
uint16_t(isRight ? MotorControllerRight : MotorControllerLeft)
};
public:
MotorController() = delete;
~MotorController() = delete;
class Command {
// send only TO the motor controller
class Command
{
private:
enum {
MotorControllerCommandMask = MotorControllerMask | MotorControllerCommand
enum : uint16_t {
MotorControllerCommandMask =
uint16_t(MotorControllerMask) |
uint16_t(MotorControllerCommand)
};
public:
Command() = delete;
~Command() = delete;
enum { // ....vvvvv
Enable = 0b00000000000 | MotorControllerCommandMask,
InpTgt = 0b00000000100 | MotorControllerCommandMask,
CtrlTyp = 0b00000001000 | MotorControllerCommandMask,
CtrlMod = 0b00000001100 | MotorControllerCommandMask,
IMotMax = 0b00000010000 | MotorControllerCommandMask,
IDcMax = 0b00000010100 | MotorControllerCommandMask,
NMotMax = 0b00000011000 | MotorControllerCommandMask,
FieldWeakMax = 0b00000011100 | MotorControllerCommandMask,
PhaseAdvMax = 0b00000100000 | MotorControllerCommandMask,
CruiseCtrlEna = 0b00000100100 | MotorControllerCommandMask,
CruiseMotTgt = 0b00000101000 | MotorControllerCommandMask,
BuzzerFreq = 0b00000101100 | MotorControllerCommandMask,
BuzzerPattern = 0b00000110000 | MotorControllerCommandMask,
Led = 0b00000110100 | MotorControllerCommandMask,
Poweroff = 0b00000111000 | MotorControllerCommandMask
enum : uint16_t { // ....'vvvvv'..
Enable = 0b0000'00000'00 | MotorControllerCommandMask,
InpTgt = 0b0000'00001'00 | MotorControllerCommandMask,
CtrlTyp = 0b0000'00010'00 | MotorControllerCommandMask,
CtrlMod = 0b0000'00011'00 | MotorControllerCommandMask,
IMotMax = 0b0000'00100'00 | MotorControllerCommandMask,
IDcMax = 0b0000'00101'00 | MotorControllerCommandMask,
NMotMax = 0b0000'00110'00 | MotorControllerCommandMask,
FieldWeakMax = 0b0000'00111'00 | MotorControllerCommandMask,
PhaseAdvMax = 0b0000'01000'00 | MotorControllerCommandMask,
CruiseCtrlEna = 0b0000'01001'00 | MotorControllerCommandMask,
CruiseMotTgt = 0b0000'01010'00 | MotorControllerCommandMask,
BuzzerFreq = 0b0000'01011'00 | MotorControllerCommandMask,
BuzzerPattern = 0b0000'01100'00 | MotorControllerCommandMask,
Led = 0b0000'01101'00 | MotorControllerCommandMask,
Poweroff = 0b0000'01110'00 | MotorControllerCommandMask
};
};
class Feedback {
// send only FROM the motor controller
class Feedback
{
private:
enum {
MotorControllerFeedbackMask = MotorControllerMask | MotorControllerFeedback
enum : uint16_t {
MotorControllerFeedbackMask =
uint16_t(MotorControllerMask) |
uint16_t(MotorControllerFeedback)
};
public:
Feedback() = delete;
~Feedback() = delete;
enum { // ....vvvvv
DcLink = 0b00000000000 | MotorControllerFeedbackMask,
Speed = 0b00000000100 | MotorControllerFeedbackMask,
Error = 0b00000001000 | MotorControllerFeedbackMask,
Angle = 0b00000001100 | MotorControllerFeedbackMask,
DcPhaA = 0b00000010000 | MotorControllerFeedbackMask,
DcPhaB = 0b00000010100 | MotorControllerFeedbackMask,
DcPhaC = 0b00000011000 | MotorControllerFeedbackMask,
Chops = 0b00000011100 | MotorControllerFeedbackMask,
Hall = 0b00000100000 | MotorControllerFeedbackMask,
Voltage = 0b00000100100 | MotorControllerFeedbackMask,
Temp = 0b00000101000 | MotorControllerFeedbackMask
enum : uint16_t { // ....'vvvvv'..
DcLink = 0b0000'00000'00 | MotorControllerFeedbackMask,
Speed = 0b0000'00001'00 | MotorControllerFeedbackMask,
Error = 0b0000'00010'00 | MotorControllerFeedbackMask,
Angle = 0b0000'00011'00 | MotorControllerFeedbackMask,
DcPhaA = 0b0000'00100'00 | MotorControllerFeedbackMask, // unused
DcPhaB = 0b0000'00101'00 | MotorControllerFeedbackMask, // unused
DcPhaC = 0b0000'00110'00 | MotorControllerFeedbackMask, // unused
Chops = 0b0000'00111'00 | MotorControllerFeedbackMask,
Hall = 0b0000'01000'00 | MotorControllerFeedbackMask,
Voltage = 0b0000'01001'00 | MotorControllerFeedbackMask,
Temp = 0b0000'01010'00 | MotorControllerFeedbackMask,
Id = 0b0000'01011'00 | MotorControllerFeedbackMask,
Iq = 0b0000'01100'00 | MotorControllerFeedbackMask,
};
};
};
class Boardcomputer
{
private:
enum : uint16_t { // ...v
BoardcomputerCommand = 0b00000000000, // send only TO the boardcomputer
BoardcomputerFeedback = 0b00010000000, // send only FROM the boardcomputer
};
public:
Boardcomputer() = delete;
~Boardcomputer() = delete;
// used with ButtonPressed and ButtonReleased
enum class Button {
Profile0 = 1,
Profile1 = 2,
Profile2 = 4,
Profile3 = 8,
Left = 16,
Right = 32,
Up = 64,
Down = 128,
Left2 = 256,
Right2 = 512,
Up2 = 1024,
Down2 = 2048,
};
// send only TO the boardcomputer
class Command
{
private:
enum : uint16_t {
BoardcomputerCommandMask =
uint16_t(DeviceTypeBoardcomputer) |
uint16_t(BoardcomputerCommand)
};
public:
Command() = delete;
~Command() = delete;
enum : uint16_t { // ....'vvvvvvv
RawButtonPressed = 0b0000'0000000 | BoardcomputerCommandMask,
RawButtonReleased = 0b0000'0000001 | BoardcomputerCommandMask,
ButtonPressed = 0b0000'0000010 | BoardcomputerCommandMask,
ButtonReleased = 0b0000'0000011 | BoardcomputerCommandMask,
RawGas = 0b0000'0000100 | BoardcomputerCommandMask,
RawBrems = 0b0000'0000101 | BoardcomputerCommandMask
};
};
// send only FROM the boardcomputer
class Feedback
{
private:
enum : uint16_t {
BoardcomputerFeedbackMask =
uint16_t(DeviceTypeBoardcomputer) |
uint16_t(BoardcomputerFeedback)
};
public:
Feedback() = delete;
~Feedback() = delete;
enum : uint16_t { // ....'vvvvvvv
ButtonLeds = 0b0000'0000000 | BoardcomputerFeedbackMask,
};
};
};
@ -97,104 +182,113 @@ inline const char *bobbycarCanIdDesc(uint16_t id)
{
switch (id)
{
case MotorController<false, false>::Command::Enable: return "Enable (Command, Front, Left)";
case MotorController<false, true>::Command::Enable: return "Enable (Command, Front, Right)";
case MotorController<true, false>::Command::Enable: return "Enable (Command, Back, Left)";
case MotorController<true, true>::Command::Enable: return "Enable (Command, Back, Right)";
case MotorController<false, false>::Command::InpTgt: return "InpTgt (Command, Front, Left)";
case MotorController<false, true>::Command::InpTgt: return "InpTgt (Command, Front, Right)";
case MotorController<true, false>::Command::InpTgt: return "InpTgt (Command, Back, Left)";
case MotorController<true, true>::Command::InpTgt: return "InpTgt (Command, Back, Right)";
case MotorController<false, false>::Command::CtrlTyp: return "CtrlTyp (Command, Front, Left)";
case MotorController<false, true>::Command::CtrlTyp: return "CtrlTyp (Command, Front, Right)";
case MotorController<true, false>::Command::CtrlTyp: return "CtrlTyp (Command, Back, Left)";
case MotorController<true, true>::Command::CtrlTyp: return "CtrlTyp (Command, Back, Right)";
case MotorController<false, false>::Command::CtrlMod: return "CtrlMod (Command, Front, Left)";
case MotorController<false, true>::Command::CtrlMod: return "CtrlMod (Command, Front, Right)";
case MotorController<true, false>::Command::CtrlMod: return "CtrlMod (Command, Back, Left)";
case MotorController<true, true>::Command::CtrlMod: return "CtrlMod (Command, Back, Right)";
case MotorController<false, false>::Command::IMotMax: return "IMotMax (Command, Front, Left)";
case MotorController<false, true>::Command::IMotMax: return "IMotMax (Command, Front, Right)";
case MotorController<true, false>::Command::IMotMax: return "IMotMax (Command, Back, Left)";
case MotorController<true, true>::Command::IMotMax: return "IMotMax (Command, Back, Right)";
case MotorController<false, false>::Command::IDcMax: return "IDcMax (Command, Front, Left)";
case MotorController<false, true>::Command::IDcMax: return "IDcMax (Command, Front, Right)";
case MotorController<true, false>::Command::IDcMax: return "IDcMax (Command, Back, Left)";
case MotorController<true, true>::Command::IDcMax: return "IDcMax (Command, Back, Right)";
case MotorController<false, false>::Command::NMotMax: return "NMotMax (Command, Front, Left)";
case MotorController<false, true>::Command::NMotMax: return "NMotMax (Command, Front, Right)";
case MotorController<true, false>::Command::NMotMax: return "NMotMax (Command, Back, Left)";
case MotorController<true, true>::Command::NMotMax: return "NMotMax (Command, Back, Right)";
case MotorController<false, false>::Command::FieldWeakMax: return "FieldWeakMax (Command, Front, Left)";
case MotorController<false, true>::Command::FieldWeakMax: return "FieldWeakMax (Command, Front, Right)";
case MotorController<true, false>::Command::FieldWeakMax: return "FieldWeakMax (Command, Back, Left)";
case MotorController<true, true>::Command::FieldWeakMax: return "FieldWeakMax (Command, Back, Right)";
case MotorController<false, false>::Command::PhaseAdvMax: return "PhaseAdvMax (Command, Front, Left)";
case MotorController<false, true>::Command::PhaseAdvMax: return "PhaseAdvMax (Command, Front, Right)";
case MotorController<true, false>::Command::PhaseAdvMax: return "PhaseAdvMax (Command, Back, Left)";
case MotorController<true, true>::Command::PhaseAdvMax: return "PhaseAdvMax (Command, Back, Right)";
case MotorController<false, false>::Command::BuzzerFreq: return "BuzzerFreq (Command, Front, Left)";
case MotorController<false, true>::Command::BuzzerFreq: return "BuzzerFreq (Command, Front, Right)";
case MotorController<true, false>::Command::BuzzerFreq: return "BuzzerFreq (Command, Back, Left)";
case MotorController<true, true>::Command::BuzzerFreq: return "BuzzerFreq (Command, Back, Right)";
case MotorController<false, false>::Command::BuzzerPattern: return "BuzzerPattern (Command, Front, Left)";
case MotorController<false, true>::Command::BuzzerPattern: return "BuzzerPattern (Command, Front, Right)";
case MotorController<true, false>::Command::BuzzerPattern: return "BuzzerPattern (Command, Back, Left)";
case MotorController<true, true>::Command::BuzzerPattern: return "BuzzerPattern (Command, Back, Right)";
case MotorController<false, false>::Command::Led: return "Led (Command, Front, Left)";
case MotorController<false, true>::Command::Led: return "Led (Command, Front, Right)";
case MotorController<true, false>::Command::Led: return "Led (Command, Back, Left)";
case MotorController<true, true>::Command::Led: return "Led (Command, Back, Right)";
case MotorController<false, false>::Command::Poweroff: return "Poweroff (Command, Front, Left)";
case MotorController<false, true>::Command::Poweroff: return "Poweroff (Command, Front, Right)";
case MotorController<true, false>::Command::Poweroff: return "Poweroff (Command, Back, Left)";
case MotorController<true, true>::Command::Poweroff: return "Poweroff (Command, Back, Right)";
case MotorController<false, false>::Feedback::DcLink: return "DcLink (Feedback, Front, Left)";
case MotorController<false, true>::Feedback::DcLink: return "DcLink (Feedback, Front, Right)";
case MotorController<true, false>::Feedback::DcLink: return "DcLink (Feedback, Back, Left)";
case MotorController<true, true>::Feedback::DcLink: return "DcLink (Feedback, Back, Right)";
case MotorController<false, false>::Feedback::Speed: return "Speed (Feedback, Front, Left)";
case MotorController<false, true>::Feedback::Speed: return "Speed (Feedback, Front, Right)";
case MotorController<true, false>::Feedback::Speed: return "Speed (Feedback, Back, Left)";
case MotorController<true, true>::Feedback::Speed: return "Speed (Feedback, Back, Right)";
case MotorController<false, false>::Feedback::Error: return "Error (Feedback, Front, Left)";
case MotorController<false, true>::Feedback::Error: return "Error (Feedback, Front, Right)";
case MotorController<true, false>::Feedback::Error: return "Error (Feedback, Back, Left)";
case MotorController<true, true>::Feedback::Error: return "Error (Feedback, Back, Right)";
case MotorController<false, false>::Feedback::Angle: return "Angle (Feedback, Front, Left)";
case MotorController<false, true>::Feedback::Angle: return "Angle (Feedback, Front, Right)";
case MotorController<true, false>::Feedback::Angle: return "Angle (Feedback, Back, Left)";
case MotorController<true, true>::Feedback::Angle: return "Angle (Feedback, Back, Right)";
case MotorController<false, false>::Feedback::DcPhaA: return "DcPhaA (Feedback, Front, Left)";
case MotorController<false, true>::Feedback::DcPhaA: return "DcPhaA (Feedback, Front, Right)";
case MotorController<true, false>::Feedback::DcPhaA: return "DcPhaA (Feedback, Back, Left)";
case MotorController<true, true>::Feedback::DcPhaA: return "DcPhaA (Feedback, Back, Right)";
case MotorController<false, false>::Feedback::DcPhaB: return "DcPhaB (Feedback, Front, Left)";
case MotorController<false, true>::Feedback::DcPhaB: return "DcPhaB (Feedback, Front, Right)";
case MotorController<true, false>::Feedback::DcPhaB: return "DcPhaB (Feedback, Back, Left)";
case MotorController<true, true>::Feedback::DcPhaB: return "DcPhaB (Feedback, Back, Right)";
case MotorController<false, false>::Feedback::DcPhaC: return "DcPhaC (Feedback, Front, Left)";
case MotorController<false, true>::Feedback::DcPhaC: return "DcPhaC (Feedback, Front, Right)";
case MotorController<true, false>::Feedback::DcPhaC: return "DcPhaC (Feedback, Back, Left)";
case MotorController<true, true>::Feedback::DcPhaC: return "DcPhaC (Feedback, Back, Right)";
case MotorController<false, false>::Feedback::Chops: return "Chops (Feedback, Front, Left)";
case MotorController<false, true>::Feedback::Chops: return "Chops (Feedback, Front, Right)";
case MotorController<true, false>::Feedback::Chops: return "Chops (Feedback, Back, Left)";
case MotorController<true, true>::Feedback::Chops: return "Chops (Feedback, Back, Right)";
case MotorController<false, false>::Feedback::Hall: return "Hall (Feedback, Front, Left)";
case MotorController<false, true>::Feedback::Hall: return "Hall (Feedback, Front, Right)";
case MotorController<true, false>::Feedback::Hall: return "Hall (Feedback, Back, Left)";
case MotorController<true, true>::Feedback::Hall: return "Hall (Feedback, Back, Right)";
case MotorController<false, false>::Feedback::Voltage: return "Voltage (Feedback, Front, Left)";
case MotorController<false, true>::Feedback::Voltage: return "Voltage (Feedback, Front, Right)";
case MotorController<true, false>::Feedback::Voltage: return "Voltage (Feedback, Back, Left)";
case MotorController<true, true>::Feedback::Voltage: return "Voltage (Feedback, Back, Right)";
case MotorController<false, false>::Feedback::Temp: return "Temp (Feedback, Front, Left)";
case MotorController<false, true>::Feedback::Temp: return "Temp (Feedback, Front, Right)";
case MotorController<true, false>::Feedback::Temp: return "Temp (Feedback, Back, Left)";
case MotorController<true, true>::Feedback::Temp: return "Temp (Feedback, Back, Right)";
case MotorController<false, false>::Command::Enable: return "Enable (MotorController, Command, Front, Left)";
case MotorController<false, true>::Command::Enable: return "Enable (MotorController, Command, Front, Right)";
case MotorController<true, false>::Command::Enable: return "Enable (MotorController, Command, Back, Left)";
case MotorController<true, true>::Command::Enable: return "Enable (MotorController, Command, Back, Right)";
case MotorController<false, false>::Command::InpTgt: return "InpTgt (MotorController, Command, Front, Left)";
case MotorController<false, true>::Command::InpTgt: return "InpTgt (MotorController, Command, Front, Right)";
case MotorController<true, false>::Command::InpTgt: return "InpTgt (MotorController, Command, Back, Left)";
case MotorController<true, true>::Command::InpTgt: return "InpTgt (MotorController, Command, Back, Right)";
case MotorController<false, false>::Command::CtrlTyp: return "CtrlTyp (MotorController, Command, Front, Left)";
case MotorController<false, true>::Command::CtrlTyp: return "CtrlTyp (MotorController, Command, Front, Right)";
case MotorController<true, false>::Command::CtrlTyp: return "CtrlTyp (MotorController, Command, Back, Left)";
case MotorController<true, true>::Command::CtrlTyp: return "CtrlTyp (MotorController, Command, Back, Right)";
case MotorController<false, false>::Command::CtrlMod: return "CtrlMod (MotorController, Command, Front, Left)";
case MotorController<false, true>::Command::CtrlMod: return "CtrlMod (MotorController, Command, Front, Right)";
case MotorController<true, false>::Command::CtrlMod: return "CtrlMod (MotorController, Command, Back, Left)";
case MotorController<true, true>::Command::CtrlMod: return "CtrlMod (MotorController, Command, Back, Right)";
case MotorController<false, false>::Command::IMotMax: return "IMotMax (MotorController, Command, Front, Left)";
case MotorController<false, true>::Command::IMotMax: return "IMotMax (MotorController, Command, Front, Right)";
case MotorController<true, false>::Command::IMotMax: return "IMotMax (MotorController, Command, Back, Left)";
case MotorController<true, true>::Command::IMotMax: return "IMotMax (MotorController, Command, Back, Right)";
case MotorController<false, false>::Command::IDcMax: return "IDcMax (MotorController, Command, Front, Left)";
case MotorController<false, true>::Command::IDcMax: return "IDcMax (MotorController, Command, Front, Right)";
case MotorController<true, false>::Command::IDcMax: return "IDcMax (MotorController, Command, Back, Left)";
case MotorController<true, true>::Command::IDcMax: return "IDcMax (MotorController, Command, Back, Right)";
case MotorController<false, false>::Command::NMotMax: return "NMotMax (MotorController, Command, Front, Left)";
case MotorController<false, true>::Command::NMotMax: return "NMotMax (MotorController, Command, Front, Right)";
case MotorController<true, false>::Command::NMotMax: return "NMotMax (MotorController, Command, Back, Left)";
case MotorController<true, true>::Command::NMotMax: return "NMotMax (MotorController, Command, Back, Right)";
case MotorController<false, false>::Command::FieldWeakMax: return "FieldWeakMax (MotorController, Command, Front, Left)";
case MotorController<false, true>::Command::FieldWeakMax: return "FieldWeakMax (MotorController, Command, Front, Right)";
case MotorController<true, false>::Command::FieldWeakMax: return "FieldWeakMax (MotorController, Command, Back, Left)";
case MotorController<true, true>::Command::FieldWeakMax: return "FieldWeakMax (MotorController, Command, Back, Right)";
case MotorController<false, false>::Command::PhaseAdvMax: return "PhaseAdvMax (MotorController, Command, Front, Left)";
case MotorController<false, true>::Command::PhaseAdvMax: return "PhaseAdvMax (MotorController, Command, Front, Right)";
case MotorController<true, false>::Command::PhaseAdvMax: return "PhaseAdvMax (MotorController, Command, Back, Left)";
case MotorController<true, true>::Command::PhaseAdvMax: return "PhaseAdvMax (MotorController, Command, Back, Right)";
case MotorController<false, false>::Command::BuzzerFreq: return "BuzzerFreq (MotorController, Command, Front, Left)";
case MotorController<false, true>::Command::BuzzerFreq: return "BuzzerFreq (MotorController, Command, Front, Right)";
case MotorController<true, false>::Command::BuzzerFreq: return "BuzzerFreq (MotorController, Command, Back, Left)";
case MotorController<true, true>::Command::BuzzerFreq: return "BuzzerFreq (MotorController, Command, Back, Right)";
case MotorController<false, false>::Command::BuzzerPattern: return "BuzzerPattern (MotorController, Command, Front, Left)";
case MotorController<false, true>::Command::BuzzerPattern: return "BuzzerPattern (MotorController, Command, Front, Right)";
case MotorController<true, false>::Command::BuzzerPattern: return "BuzzerPattern (MotorController, Command, Back, Left)";
case MotorController<true, true>::Command::BuzzerPattern: return "BuzzerPattern (MotorController, Command, Back, Right)";
case MotorController<false, false>::Command::Led: return "Led (MotorController, Command, Front, Left)";
case MotorController<false, true>::Command::Led: return "Led (MotorController, Command, Front, Right)";
case MotorController<true, false>::Command::Led: return "Led (MotorController, Command, Back, Left)";
case MotorController<true, true>::Command::Led: return "Led (MotorController, Command, Back, Right)";
case MotorController<false, false>::Command::Poweroff: return "Poweroff (MotorController, Command, Front, Left)";
case MotorController<false, true>::Command::Poweroff: return "Poweroff (MotorController, Command, Front, Right)";
case MotorController<true, false>::Command::Poweroff: return "Poweroff (MotorController, Command, Back, Left)";
case MotorController<true, true>::Command::Poweroff: return "Poweroff (MotorController, Command, Back, Right)";
case MotorController<false, false>::Feedback::DcLink: return "DcLink (MotorController, Feedback, Front, Left)";
case MotorController<false, true>::Feedback::DcLink: return "DcLink (MotorController, Feedback, Front, Right)";
case MotorController<true, false>::Feedback::DcLink: return "DcLink (MotorController, Feedback, Back, Left)";
case MotorController<true, true>::Feedback::DcLink: return "DcLink (MotorController, Feedback, Back, Right)";
case MotorController<false, false>::Feedback::Speed: return "Speed (MotorController, Feedback, Front, Left)";
case MotorController<false, true>::Feedback::Speed: return "Speed (MotorController, Feedback, Front, Right)";
case MotorController<true, false>::Feedback::Speed: return "Speed (MotorController, Feedback, Back, Left)";
case MotorController<true, true>::Feedback::Speed: return "Speed (MotorController, Feedback, Back, Right)";
case MotorController<false, false>::Feedback::Error: return "Error (MotorController, Feedback, Front, Left)";
case MotorController<false, true>::Feedback::Error: return "Error (MotorController, Feedback, Front, Right)";
case MotorController<true, false>::Feedback::Error: return "Error (MotorController, Feedback, Back, Left)";
case MotorController<true, true>::Feedback::Error: return "Error (MotorController, Feedback, Back, Right)";
case MotorController<false, false>::Feedback::Angle: return "Angle (MotorController, Feedback, Front, Left)";
case MotorController<false, true>::Feedback::Angle: return "Angle (MotorController, Feedback, Front, Right)";
case MotorController<true, false>::Feedback::Angle: return "Angle (MotorController, Feedback, Back, Left)";
case MotorController<true, true>::Feedback::Angle: return "Angle (MotorController, Feedback, Back, Right)";
case MotorController<false, false>::Feedback::DcPhaA: return "DcPhaA (MotorController, Feedback, Front, Left)";
case MotorController<false, true>::Feedback::DcPhaA: return "DcPhaA (MotorController, Feedback, Front, Right)";
case MotorController<true, false>::Feedback::DcPhaA: return "DcPhaA (MotorController, Feedback, Back, Left)";
case MotorController<true, true>::Feedback::DcPhaA: return "DcPhaA (MotorController, Feedback, Back, Right)";
case MotorController<false, false>::Feedback::DcPhaB: return "DcPhaB (MotorController, Feedback, Front, Left)";
case MotorController<false, true>::Feedback::DcPhaB: return "DcPhaB (MotorController, Feedback, Front, Right)";
case MotorController<true, false>::Feedback::DcPhaB: return "DcPhaB (MotorController, Feedback, Back, Left)";
case MotorController<true, true>::Feedback::DcPhaB: return "DcPhaB (MotorController, Feedback, Back, Right)";
case MotorController<false, false>::Feedback::DcPhaC: return "DcPhaC (MotorController, Feedback, Front, Left)";
case MotorController<false, true>::Feedback::DcPhaC: return "DcPhaC (MotorController, Feedback, Front, Right)";
case MotorController<true, false>::Feedback::DcPhaC: return "DcPhaC (MotorController, Feedback, Back, Left)";
case MotorController<true, true>::Feedback::DcPhaC: return "DcPhaC (MotorController, Feedback, Back, Right)";
case MotorController<false, false>::Feedback::Chops: return "Chops (MotorController, Feedback, Front, Left)";
case MotorController<false, true>::Feedback::Chops: return "Chops (MotorController, Feedback, Front, Right)";
case MotorController<true, false>::Feedback::Chops: return "Chops (MotorController, Feedback, Back, Left)";
case MotorController<true, true>::Feedback::Chops: return "Chops (MotorController, Feedback, Back, Right)";
case MotorController<false, false>::Feedback::Hall: return "Hall (MotorController, Feedback, Front, Left)";
case MotorController<false, true>::Feedback::Hall: return "Hall (MotorController, Feedback, Front, Right)";
case MotorController<true, false>::Feedback::Hall: return "Hall (MotorController, Feedback, Back, Left)";
case MotorController<true, true>::Feedback::Hall: return "Hall (MotorController, Feedback, Back, Right)";
case MotorController<false, false>::Feedback::Voltage: return "Voltage (MotorController, Feedback, Front, Left)";
case MotorController<false, true>::Feedback::Voltage: return "Voltage (MotorController, Feedback, Front, Right)";
case MotorController<true, false>::Feedback::Voltage: return "Voltage (MotorController, Feedback, Back, Left)";
case MotorController<true, true>::Feedback::Voltage: return "Voltage (MotorController, Feedback, Back, Right)";
case MotorController<false, false>::Feedback::Temp: return "Temp (MotorController, Feedback, Front, Left)";
case MotorController<false, true>::Feedback::Temp: return "Temp (MotorController, Feedback, Front, Right)";
case MotorController<true, false>::Feedback::Temp: return "Temp (MotorController, Feedback, Back, Left)";
case MotorController<true, true>::Feedback::Temp: return "Temp (MotorController, Feedback, Back, Right)";
case Boardcomputer::Command::RawButtonPressed: return "RawButtonPressed (Boardcomputer, Command)";
case Boardcomputer::Command::RawButtonReleased: return "RawButtonReleased (Boardcomputer, Command)";
case Boardcomputer::Command::ButtonPressed: return "ButtonPressed (Boardcomputer, Command)";
case Boardcomputer::Command::ButtonReleased: return "ButtonReleased (Boardcomputer, Command)";
case Boardcomputer::Command::RawGas: return "RawGas (Boardcomputer, Command)";
case Boardcomputer::Command::RawBrems: return "RawBrems (Boardcomputer, Command)";
case Boardcomputer::Feedback::ButtonLeds: return "ButtonLeds (Boardcomputer, Feedback)";
}
return "Unknown";
}
}
}
} // namespace can
} // namespace protocol
} // namespace bobbycar

View File

@ -2,6 +2,9 @@
#include <stdint.h>
namespace bobbycar {
namespace protocol {
enum class ControlType : uint8_t {
Commutation,
Sinusoidal,
@ -14,3 +17,6 @@ enum class ControlMode : uint8_t {
Speed, // Only with FieldOrientedControl
Torque // Only with FieldOrientedControl
};
} // namespace protocol
} // namespace bobbycar

View File

@ -4,6 +4,10 @@
#include "bobbycar-common.h"
namespace bobbycar {
namespace protocol {
namespace serial {
struct MotorState {
bool enable = false;
int16_t pwm = 0;
@ -14,6 +18,8 @@ struct MotorState {
uint16_t nMotMax = 1000; // [rpm] Maximum motor speed limit
uint8_t fieldWeakMax = 10; // [A] Maximum Field Weakening D axis current (only for FOC). Higher current results in higher maximum speed.
uint8_t phaseAdvMax = 40; // [deg] Maximum Phase Advance angle (only for SIN). Higher angle results in higher maximum speed.
bool cruiseCtrlEna = false;
int16_t nCruiseMotTgt = 0;
};
inline uint16_t calculateChecksum(MotorState state) {
@ -26,7 +32,9 @@ inline uint16_t calculateChecksum(MotorState state) {
state.iDcMax ^
state.nMotMax ^
state.fieldWeakMax ^
state.phaseAdvMax;
state.phaseAdvMax ^
uint16_t(state.cruiseCtrlEna) ^
state.nCruiseMotTgt;
}
struct BuzzerState {
@ -67,8 +75,13 @@ struct MotorFeedback {
int16_t angle = 0;
int16_t speed = 0;
uint8_t error = 0;
int16_t current = 0;
int16_t dcLink = 0;
int16_t dcPhaA = 0;
int16_t dcPhaB = 0;
int16_t dcPhaC = 0;
uint16_t chops = 0;
int16_t id = 0;
int16_t iq = 0;
bool hallA = false;
bool hallB = false;
bool hallC = false;
@ -76,8 +89,9 @@ struct MotorFeedback {
inline uint16_t calculateChecksum(MotorFeedback feedback) {
return feedback.angle ^ feedback.speed ^
feedback.error ^ feedback.current ^
feedback.chops ^
feedback.error ^ feedback.dcLink ^
feedback.dcPhaA ^ feedback.dcPhaB ^
feedback.dcPhaC ^ feedback.chops ^
feedback.hallA ^ feedback.hallB ^ feedback.hallC;
}
@ -108,35 +122,41 @@ inline uint16_t calculateChecksum(Feedback feedback) {
#define ASSERT_LAYOUT(st, memb, off) \
static_assert(offsetof(st, memb) == off, "struct layout wrong");
//static_assert(offsetof(st, memb) == off, "struct layout wrong");
ASSERT_LAYOUT(Feedback, start, 0);
ASSERT_LAYOUT(Feedback, left, 2);
ASSERT_LAYOUT(Feedback, left.angle, 2);
ASSERT_LAYOUT(Feedback, left.speed, 4);
ASSERT_LAYOUT(Feedback, left.error, 6);
ASSERT_LAYOUT(Feedback, left.current, 8);
ASSERT_LAYOUT(Feedback, left.chops, 10);
ASSERT_LAYOUT(Feedback, left.hallA, 12);
ASSERT_LAYOUT(Feedback, left.hallB, 13);
ASSERT_LAYOUT(Feedback, left.hallC, 14);
ASSERT_LAYOUT(Feedback, left.dcLink, 8);
ASSERT_LAYOUT(Feedback, left.dcPhaA, 10);
ASSERT_LAYOUT(Feedback, left.dcPhaB, 12);
ASSERT_LAYOUT(Feedback, left.dcPhaC, 14);
ASSERT_LAYOUT(Feedback, left.chops, 16);
ASSERT_LAYOUT(Feedback, left.hallA, 18);
ASSERT_LAYOUT(Feedback, left.hallB, 19);
ASSERT_LAYOUT(Feedback, left.hallC, 20);
ASSERT_LAYOUT(Feedback, right, 16);
ASSERT_LAYOUT(Feedback, right.angle, 16);
ASSERT_LAYOUT(Feedback, right.speed, 18);
ASSERT_LAYOUT(Feedback, right.error, 20);
ASSERT_LAYOUT(Feedback, right.current, 22);
ASSERT_LAYOUT(Feedback, right.chops, 24);
ASSERT_LAYOUT(Feedback, right.hallA, 26);
ASSERT_LAYOUT(Feedback, right.hallB, 27);
ASSERT_LAYOUT(Feedback, right.hallC, 28);
ASSERT_LAYOUT(Feedback, right, 22);
ASSERT_LAYOUT(Feedback, right.angle, 22);
ASSERT_LAYOUT(Feedback, right.speed, 24);
ASSERT_LAYOUT(Feedback, right.error, 26);
ASSERT_LAYOUT(Feedback, right.dcLink, 28);
ASSERT_LAYOUT(Feedback, right.dcPhaA, 30);
ASSERT_LAYOUT(Feedback, right.dcPhaB, 32);
ASSERT_LAYOUT(Feedback, right.dcPhaC, 34);
ASSERT_LAYOUT(Feedback, right.chops, 36);
ASSERT_LAYOUT(Feedback, right.hallA, 38);
ASSERT_LAYOUT(Feedback, right.hallB, 39);
ASSERT_LAYOUT(Feedback, right.hallC, 40);
ASSERT_LAYOUT(Feedback, batVoltage, 30);
ASSERT_LAYOUT(Feedback, boardTemp, 32);
ASSERT_LAYOUT(Feedback, timeoutCntSerial, 34);
ASSERT_LAYOUT(Feedback, checksum, 36);
ASSERT_LAYOUT(Feedback, batVoltage, 42);
ASSERT_LAYOUT(Feedback, boardTemp, 44);
ASSERT_LAYOUT(Feedback, timeoutCntSerial, 46);
ASSERT_LAYOUT(Feedback, checksum, 48);
static_assert(sizeof(Command) == 32, "sizeof(Command) wrong");
//static_assert(sizeof(Command) == 38, "sizeof(Command) wrong");
ASSERT_LAYOUT(Command, start, 0);
@ -150,21 +170,29 @@ ASSERT_LAYOUT(Command, left.iDcMax, 9);
ASSERT_LAYOUT(Command, left.nMotMax, 10);
ASSERT_LAYOUT(Command, left.fieldWeakMax, 12);
ASSERT_LAYOUT(Command, left.phaseAdvMax, 13);
ASSERT_LAYOUT(Command, left.cruiseCtrlEna, 14);
ASSERT_LAYOUT(Command, left.nCruiseMotTgt, 15);
ASSERT_LAYOUT(Command, right.enable, 14);
ASSERT_LAYOUT(Command, right.pwm, 16);
ASSERT_LAYOUT(Command, right.ctrlTyp, 18);
ASSERT_LAYOUT(Command, right.ctrlMod, 19);
ASSERT_LAYOUT(Command, right.iMotMax, 20);
ASSERT_LAYOUT(Command, right.iDcMax, 21);
ASSERT_LAYOUT(Command, right.nMotMax, 22);
ASSERT_LAYOUT(Command, right.fieldWeakMax, 24);
ASSERT_LAYOUT(Command, right.phaseAdvMax, 25);
ASSERT_LAYOUT(Command, right.enable, 18);
ASSERT_LAYOUT(Command, right.pwm, 19);
ASSERT_LAYOUT(Command, right.ctrlTyp, 21);
ASSERT_LAYOUT(Command, right.ctrlMod, 22);
ASSERT_LAYOUT(Command, right.iMotMax, 23);
ASSERT_LAYOUT(Command, right.iDcMax, 24);
ASSERT_LAYOUT(Command, right.nMotMax, 25);
ASSERT_LAYOUT(Command, right.fieldWeakMax, 27);
ASSERT_LAYOUT(Command, right.phaseAdvMax, 28);
ASSERT_LAYOUT(Command, right.cruiseCtrlEna, 29);
ASSERT_LAYOUT(Command, right.nCruiseMotTgt, 31);
ASSERT_LAYOUT(Command, buzzer, 26);
ASSERT_LAYOUT(Command, buzzer.freq, 26);
ASSERT_LAYOUT(Command, buzzer, 33);
ASSERT_LAYOUT(Command, buzzer.freq, 33);
ASSERT_LAYOUT(Command, buzzer.pattern, 27);
ASSERT_LAYOUT(Command, poweroff, 28);
ASSERT_LAYOUT(Command, led, 29);
ASSERT_LAYOUT(Command, checksum, 30);
} // namespace serial
} // namespace protocol
} // namespace bobbycar

167
utils/bobbycan.lua Normal file
View File

@ -0,0 +1,167 @@
local p_bobbycan = Proto("bobbycan", "BobbyCAN")
p_bobbycan.fields = {}
local function format_voltage(val)
return string.format("%.2f V", val / 100)
end
local function format_cmd_current(val)
return string.format("%d A", val)
end
local function format_fb_current(val)
return string.format("%.2f A", -val / 50)
end
local function format_angle(val)
return string.format("%d°", val)
end
local function format_temperature(val)
return string.format("%.1f °C", val / 10)
end
local function format_speed(val)
return string.format("%d RPM", val)
end
local motor_cmds = {
[ 0] = { len = 1, abbr = "enable", name = "Enable", type = ftypes.BOOLEAN },
[ 1] = { len = 2, abbr = "inp_tgt", name = "Input target", type = ftypes.INT16 },
[ 2] = { len = 1, abbr = "ctrl_typ", name = "Control type", type = ftypes.UINT8,
valuestring = {[0] = "Commutation", [1] = "Sinusoidal", [2] = "FOC"} },
[ 3] = { len = 1, abbr = "ctrl_mod", name = "Control mode", type = ftypes.UINT8,
valuestring = {[0] = "Open", [1] = "Voltage", [2] = "Speed", [3] = "Torque"} },
[ 4] = { len = 1, abbr = "imotmax", name = "Maximum motor current", type = ftypes.UINT8, format = format_cmd_current },
[ 5] = { len = 1, abbr = "idcmax", name = "Maximum DC link current", type = ftypes.UINT8, format = format_cmd_current },
[ 6] = { len = 2, abbr = "nmotmax", name = "Maximum speed", type = ftypes.UINT16, format = format_speed },
[ 7] = { len = 1, abbr = "fieldweakmax", name = "Maximum field weakening current", type = ftypes.UINT8, format = format_cmd_current },
[ 8] = { len = 1, abbr = "phaseadvmax", name = "Maximum phase advance angle", type = ftypes.UINT16, format = format_angle },
[ 9] = { len = 1, abbr = "cruisectrlena", name = "Enable cruise control", type = ftypes.BOOLEAN },
[10] = { len = 1, abbr = "cruisemottgt", name = "Cruise control target", type = ftypes.INT16, format = format_speed },
[11] = { len = 1, abbr = "buzzerfreq", name = "Buzzer freq. divider", type = ftypes.UINT8 },
[12] = { len = 1, abbr = "buzzerpattern", name = "Buzzer pattern", type = ftypes.UINT8 },
[13] = { len = 1, abbr = "led", name = "LED state", type = ftypes.BOOLEAN },
[14] = { len = 1, abbr = "poweroff", name = "Power off", type = ftypes.BOOLEAN },
}
local motor_errors = {
[0] = "None",
[1] = "Hall sensor not connected",
[2] = "Hall sensor short circuit",
[4] = "Motor NOT able to spin"
}
local motor_fbs = {
[ 0] = { len = 2, abbr = "dc_link", name = "DC Link Current", type = ftypes.INT16, format = format_fb_current },
[ 1] = { len = 2, abbr = "speed", name = "Speed", type = ftypes.INT16 },
[ 2] = { len = 1, abbr = "error", name = "Error", type = ftypes.UINT8, valuestring = motor_errors },
[ 3] = { len = 2, abbr = "angle", name = "Electrical angle", type = ftypes.INT16, format = format_angle },
[ 4] = { len = 2, abbr = "dc_a", name = "Phase A Current", type = ftypes.INT16, format = format_fb_current },
[ 5] = { len = 2, abbr = "dc_b", name = "Phase B Current", type = ftypes.INT16, format = format_fb_current },
[ 6] = { len = 2, abbr = "dc_c", name = "Phase C Current", type = ftypes.INT16, format = format_fb_current },
[ 7] = { len = 4, abbr = "chops", name = "Chops since last update", type = ftypes.UINT32 },
[ 8] = { len = 1, abbr = "hall", name = "Hall Sensors", type = ftypes.UINT8 },
[ 9] = { len = 2, abbr = "voltage", name = "Supply Voltage", type = ftypes.INT16, format = format_voltage },
[10] = { len = 2, abbr = "temp", name = "MCU Temperature", type = ftypes.INT16, format = format_temperature },
[11] = { len = 2, abbr = "curr_id", name = "Id current", type = ftypes.INT16, format = format_fb_current },
[12] = { len = 2, abbr = "curr_iq", name = "Iq current", type = ftypes.INT16, format = format_fb_current },
}
local motor_generated = {
left = { abbr = "left", name = "Is left", type = ftypes.BOOLEAN },
right = { abbr = "right", name = "Is right", type = ftypes.BOOLEAN },
front = { abbr = "front", name = "Is front", type = ftypes.BOOLEAN },
back = { abbr = "back", name = "Is back", type = ftypes.BOOLEAN },
cmd = { abbr = "cmd", name = "Is command", type = ftypes.BOOLEAN },
fb = { abbr = "fb", name = "Is feedback", type = ftypes.BOOLEAN },
}
local function create_fields(prefix, proto)
for k,p in pairs(proto) do
p.field = ProtoField.new(p.name, prefix .. p.abbr, p.type, p.valuestring, p.base, p.mask, p.descr)
table.insert(p_bobbycan.fields, p.field)
end
end
create_fields("bobbycan.motor.", motor_cmds)
create_fields("bobbycan.motor.", motor_fbs)
create_fields("bobbycan.motor.", motor_generated)
local f_can_id = Field.new("can.id")
function to_int(t, tvsr)
if t == ftypes.UINT8 or t == ftypes.UINT16 or t == ftypes.UINT32 then
return tvsr:le_uint()
else
return tvsr:le_int()
end
end
function p_bobbycan.dissector(buf, pkt, tree)
pkt.cols.protocol:set("BobbyCAN")
local can_id = f_can_id()()
pkt.cols.info:set("test " .. can_id)
local devtype = bit32.rshift(bit32.band(can_id, 0x700), 8)
if devtype == 0 then
local command = bit32.band(can_id, 0x80) == 0
local subid = bit32.rshift(bit32.band(can_id, 0x7c), 2)
local tbl
local prefix
local generated_fields = { nil, nil, nil }
if command then
tbl = motor_cmds
prefix = " CMD "
generated_fields[1] = motor_generated.cmd.field
else
tbl = motor_fbs
prefix = " FB "
generated_fields[1] = motor_generated.fb.field
end
local front = bit32.band(can_id, 0x02) == 0
if front then
prefix = prefix .. "F"
generated_fields[2] = motor_generated.front.field
else
prefix = prefix .. "B"
generated_fields[2] = motor_generated.back.field
end
local left = bit32.band(can_id, 0x01) == 0
if left then
prefix = prefix .. "L "
generated_fields[3] = motor_generated.left.field
else
prefix = prefix .. "R "
generated_fields[3] = motor_generated.right.field
end
local meta = tbl[subid]
local tvsr = buf(0,meta.len)
local subtree = tree:add(p_bobbycan, tvsr)
local field_val = to_int(meta.type, tvsr)
local ti = subtree:add_packet_field(meta.field, tvsr, ENC_LITTLE_ENDIAN)
local val_fmt
if meta.format then
val_fmt = meta.format(field_val)
elseif meta.valuestring and meta.valuestring[field_val] then
val_fmt = string.format("%s (%d)", meta.valuestring[field_val], field_val)
else
val_fmt = tostring(field_val)
end
ti.text = meta.name..": "..val_fmt
pkt.cols.info:set(prefix..meta.name.." = "..val_fmt)
-- Add generated fields (front, back, left, right, cmd, fb)
for k,f in ipairs(generated_fields) do
subtree:add(f, true):set_generated()
end
end
end
can_table = DissectorTable.get("can.subdissector")
can_table:add_for_decode_as(p_bobbycan)