Skip to content

Commit

Permalink
Enlargeable Config Packet (#110)
Browse files Browse the repository at this point in the history
* Initial proposal

* Add missing files

* Abort one large config struct consideration

* Finish flexible length config packet

* Cleanup, add and rename config struct member

* refactor: create applyConfig()

* PoC for LittleFS

* Implement review hints of rovo89

* Don't write config to flash if it hasn't changed

* Implement review hints of rovo89

* Minor LittleFS implementation changes

* Intermediate result after protocol implementation with ROS

* Missing commit

* Intermediate commit #2

* Adjust v_charge_cutoff to MAX20405

* Change config_bitmask to option bitfield

* Make IGNORE_CHARGING_CURRENT configurable via LL/HL config `options.ignore_charging_current`

* Finalize LittleFS

* Finish new flexible hall implementation

* Remove IGNORE_CHARGING_CURRENT builds from artifacts

* Make halls const

* Apply received packet on default-config (instead of live-config)

* Add configurable (Stock-CoverUI) rain threshold

* Change config packet IDs

* Cleanup

* Cosmetic changes

* Store config packet instead of effective config

* Optimize hall handling with etl::vector

* Change ConfigOptions from boolean to tree-state enums

* Change shutdown_esc_max_pitch to uint8_t

* Add new/missing config values to applyConfig()

* Fix hall handling

---------

Co-authored-by: Robert Vollmer <dev@robv.de>
  • Loading branch information
Apehaenger and rovo89 authored Nov 15, 2024
1 parent 97629f8 commit c9d557b
Show file tree
Hide file tree
Showing 4 changed files with 282 additions and 188 deletions.
21 changes: 0 additions & 21 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -116,27 +116,6 @@ jobs:
cp Firmware/LowLevel/.pio/build/0_9_X_WT901_INSTEAD_OF_SOUND/firmware.elf ./artifacts/0_9_X_WT901_INSTEAD_OF_SOUND
cp Firmware/LowLevel/.pio/build/0_9_X_WT901_INSTEAD_OF_SOUND/firmware.uf2 ./artifacts/0_9_X_WT901_INSTEAD_OF_SOUND
mkdir ./artifacts/0_13_X_IGNORE_CHARGING_CURRENT
cp Firmware/LowLevel/.pio/build/0_13_X_IGNORE_CHARGING_CURRENT/firmware.elf ./artifacts/0_13_X_IGNORE_CHARGING_CURRENT
cp Firmware/LowLevel/.pio/build/0_13_X_IGNORE_CHARGING_CURRENT/firmware.uf2 ./artifacts/0_13_X_IGNORE_CHARGING_CURRENT
mkdir ./artifacts/0_12_X_IGNORE_CHARGING_CURRENT
cp Firmware/LowLevel/.pio/build/0_12_X_IGNORE_CHARGING_CURRENT/firmware.elf ./artifacts/0_12_X_IGNORE_CHARGING_CURRENT
cp Firmware/LowLevel/.pio/build/0_12_X_IGNORE_CHARGING_CURRENT/firmware.uf2 ./artifacts/0_12_X_IGNORE_CHARGING_CURRENT
mkdir ./artifacts/0_11_X_WT901_IGNORE_CHARGING_CURRENT
cp Firmware/LowLevel/.pio/build/0_11_X_WT901_IGNORE_CHARGING_CURRENT/firmware.elf ./artifacts/0_11_X_WT901_IGNORE_CHARGING_CURRENT
cp Firmware/LowLevel/.pio/build/0_11_X_WT901_IGNORE_CHARGING_CURRENT/firmware.uf2 ./artifacts/0_11_X_WT901_IGNORE_CHARGING_CURRENT
mkdir ./artifacts/0_10_X_WT901_IGNORE_CHARGING_CURRENT
cp Firmware/LowLevel/.pio/build/0_10_X_WT901_IGNORE_CHARGING_CURRENT/firmware.elf ./artifacts/0_10_X_WT901_IGNORE_CHARGING_CURRENT
cp Firmware/LowLevel/.pio/build/0_10_X_WT901_IGNORE_CHARGING_CURRENT/firmware.uf2 ./artifacts/0_10_X_WT901_IGNORE_CHARGING_CURRENT
mkdir ./artifacts/0_9_X_WT901_IGNORE_CHARGING_CURRENT
cp Firmware/LowLevel/.pio/build/0_9_X_WT901_IGNORE_CHARGING_CURRENT/firmware.elf ./artifacts/0_9_X_WT901_IGNORE_CHARGING_CURRENT
cp Firmware/LowLevel/.pio/build/0_9_X_WT901_IGNORE_CHARGING_CURRENT/firmware.uf2 ./artifacts/0_9_X_WT901_IGNORE_CHARGING_CURRENT
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
Expand Down
45 changes: 3 additions & 42 deletions Firmware/LowLevel/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ platform = https://github.com/maxgerhardt/platform-raspberrypi.git
board = pico
framework = arduino
board_build.core = earlephilhower
board_build.filesystem_size = 64k

lib_deps =
Wire
SPI
FastCRC
LittleFS
bakercp/PacketSerial@^1.4.0
powerbroker2/FireTimer@^1.0.5
https://github.com/ClemensElflein/NeoPixelConnect.git

etlcpp/Embedded Template Library @ ^20.39.4

debug_tool = custom
debug_init_break =
Expand All @@ -50,14 +52,6 @@ lib_deps = ${env.lib_deps}
build_src_filter = ${env.build_src_filter} +<imu/LSM6DSO/>
build_flags = ${env.build_flags} -DHW_0_13_X

[env:0_13_X_IGNORE_CHARGING_CURRENT]
lib_ignore = JY901_SERIAL,JY901_I2C
lib_deps = ${env.lib_deps}
stm32duino/STM32duino LSM6DSO@^2.0.3
powerbroker2/DFPlayerMini_Fast@^1.2.4
build_src_filter = ${env.build_src_filter} +<imu/LSM6DSO/>
build_flags = ${env.build_flags} -DHW_0_13_X -DIGNORE_CHARGING_CURRENT

[env:0_12_X]
lib_ignore = JY901_SERIAL,JY901_I2C
lib_deps = ${env.lib_deps}
Expand All @@ -67,15 +61,6 @@ lib_deps = ${env.lib_deps}
build_src_filter = ${env.build_src_filter} +<imu/LSM6DSO/> +<soundsystem.cpp>
build_flags = ${env.build_flags} -DHW_0_12_X -DENABLE_SOUND_MODULE

[env:0_12_X_IGNORE_CHARGING_CURRENT]
lib_ignore = JY901_SERIAL,JY901_I2C
lib_deps = ${env.lib_deps}
stm32duino/STM32duino LSM6DSO@^2.0.3
jpiat/PioSPI@^0.0.1
powerbroker2/DFPlayerMini_Fast@^1.2.4
build_src_filter = ${env.build_src_filter} +<imu/LSM6DSO/> +<soundsystem.cpp>
build_flags = ${env.build_flags} -DHW_0_12_X -DENABLE_SOUND_MODULE -DIGNORE_CHARGING_CURRENT


[env:0_11_X_MPU9250]
lib_ignore = JY901_SERIAL,JY901_I2C
Expand All @@ -93,14 +78,6 @@ lib_deps = ${env.lib_deps}
JY901_I2C
build_flags = ${env.build_flags} -DWT901_I2C -DHW_0_11_X -DENABLE_SOUND_MODULE

[env:0_11_X_WT901_IGNORE_CHARGING_CURRENT]
build_src_filter = ${env.build_src_filter} +<imu/WT901_I2C/> +<soundsystem.cpp>
lib_ignore = JY901_SERIAL
lib_deps = ${env.lib_deps}
powerbroker2/DFPlayerMini_Fast@^1.2.4
JY901_I2C
build_flags = ${env.build_flags} -DWT901_I2C -DHW_0_11_X -DENABLE_SOUND_MODULE -DIGNORE_CHARGING_CURRENT


[env:0_10_X_MPU9250]
lib_ignore = JY901_SERIAL,JY901_I2C
Expand All @@ -118,13 +95,6 @@ lib_deps = ${env.lib_deps}
JY901_I2C
build_flags = ${env.build_flags} -DWT901_I2C -DHW_0_10_X -DENABLE_SOUND_MODULE

[env:0_10_X_WT901_IGNORE_CHARGING_CURRENT]
build_src_filter = ${env.build_src_filter} +<imu/WT901_I2C/> +<soundsystem.cpp>
lib_ignore = JY901_SERIAL
lib_deps = ${env.lib_deps}
powerbroker2/DFPlayerMini_Fast@^1.2.4
JY901_I2C
build_flags = ${env.build_flags} -DWT901_I2C -DHW_0_10_X -DENABLE_SOUND_MODULE -DIGNORE_CHARGING_CURRENT

[env:0_9_X_MPU9250]
lib_ignore = JY901_SERIAL,JY901_I2C
Expand All @@ -141,15 +111,6 @@ lib_deps = ${env.lib_deps}
JY901_SERIAL
build_flags = ${env.build_flags} -DWT901_INSTEAD_OF_SOUND -DHW_0_9_X


[env:0_9_X_WT901_IGNORE_CHARGING_CURRENT]
lib_ignore = JY901_I2C
build_src_filter = ${env.build_src_filter} +<imu/WT901_SERIAL/> +<soundsystem.cpp>
lib_deps = ${env.lib_deps}
JY901_SERIAL
powerbroker2/DFPlayerMini_Fast@^1.2.4
build_flags = ${env.build_flags} -DWT901 -DHW_0_9_X -DENABLE_SOUND_MODULE -DIGNORE_CHARGING_CURRENT

[env:0_9_X_WT901]
lib_ignore = JY901_I2C
build_src_filter = ${env.build_src_filter} +<imu/WT901_SERIAL/> +<soundsystem.cpp>
Expand Down
127 changes: 94 additions & 33 deletions Firmware/LowLevel/src/datatypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
#define PACKET_ID_LL_STATUS 1
#define PACKET_ID_LL_IMU 2
#define PACKET_ID_LL_UI_EVENT 3
#define PACKET_ID_LL_HIGH_LEVEL_CONFIG_REQ 0x21 // ll_high_level_config and request config from receiver
#define PACKET_ID_LL_HIGH_LEVEL_CONFIG_RSP 0x22 // ll_high_level_config response
#define PACKET_ID_LL_HIGH_LEVEL_CONFIG_REQ 0x11 // ll_high_level_config and request config from receiver
#define PACKET_ID_LL_HIGH_LEVEL_CONFIG_RSP 0x12 // ll_high_level_config response
#define PACKET_ID_LL_HEARTBEAT 0x42
#define PACKET_ID_LL_HIGH_LEVEL_STATE 0x43

Expand All @@ -34,23 +34,22 @@ enum HighLevelMode {
MODE_RECORDING = 3 // ROS connected, Manual mode during recording etc
};

#define LL_EMERGENCY_BIT_LATCH 0b00000001
#define LL_EMERGENCY_BIT_HALL1 0b00001000 // Lift1
#define LL_EMERGENCY_BIT_HALL2 0b00010000 // Lift2
#define LL_EMERGENCY_BIT_HALL3 0b00000010 // Stop1
#define LL_EMERGENCY_BIT_HALL4 0b00000100 // Stop2
// clang-format off
#define LL_EMERGENCY_BIT_LATCH (1 << 0) // Any emergency latch
#define LL_EMERGENCY_BIT_STOP (1 << 1) // Stop
#define LL_EMERGENCY_BIT_LIFT (1 << 2) // Lift (or tilt)

#define LL_EMERGENCY_BIT_LIFT1 LL_EMERGENCY_BIT_HALL1
#define LL_EMERGENCY_BIT_LIFT2 LL_EMERGENCY_BIT_HALL2
#define LL_EMERGENCY_BITS_LIFT (LL_EMERGENCY_BIT_LIFT1 | LL_EMERGENCY_BIT_LIFT2)
#define LL_EMERGENCY_BIT_STOP1 LL_EMERGENCY_BIT_HALL3
#define LL_EMERGENCY_BIT_STOP2 LL_EMERGENCY_BIT_HALL4
#define LL_EMERGENCY_BITS_STOP (LL_EMERGENCY_BIT_STOP1 | LL_EMERGENCY_BIT_STOP2)
// CoverUI will stay with the old emergency_bitmask definition
#define LL_EMERGENCY_BIT_CU_LATCH (1 << 0) // Any emergency latch
#define LL_EMERGENCY_BIT_CU_STOP1 (1 << 1) // Stop1
#define LL_EMERGENCY_BIT_CU_STOP2 (1 << 2) // Stop2
#define LL_EMERGENCY_BIT_CU_LIFT (1 << 3) // LIFT | LIFTX (up to CoverUI FW 2.0x)
#define LL_EMERGENCY_BIT_CU_BUMP (1 << 4) // LBUMP | RBUMP (up to CoverUI FW 2.0x)
#define LL_EMERGENCY_BIT_CU_LIFTX (1 << 5) // CoverUI-LIFTX (as of CoverUI FW 2.1x)
#define LL_EMERGENCY_BIT_CU_RBUMP (1 << 6) // CoverUI-RBUMP (as of CoverUI FW 2.1x)

#define LIFT1_IS_INVERTED 0
#define LIFT2_IS_INVERTED 0

#define LL_STATUS_BIT_UI_AVAIL 0b10000000
#define LL_STATUS_BIT_UI_AVAIL (1 << 7)
// clang-format on

#pragma pack(push, 1)
struct ll_status {
Expand All @@ -60,7 +59,7 @@ struct ll_status {
// Bit 0: Initialized (i.e. setup() was a success). If this is 0, all other bits are meaningless.
// Bit 1: Raspberry Power
// Bit 2: Charging enabled
// Bit 3: don't care
// Bit 3: don't care (reserved for ESC shutdown PR)
// Bit 4: Rain detected
// Bit 5: Sound available
// Bit 6: Sound busy
Expand All @@ -70,10 +69,8 @@ struct ll_status {
float uss_ranges_m[5];
// Emergency bitmask:
// Bit 0: Emergency latch
// Bit 1: Emergency/Hall 3 (Stop1) active
// Bit 2: Emergency/Hall 4 (Stop2) active
// Bit 3: Emergency/Hall 1 (Lift1) active
// Bit 4: Emergency/Hall 2 (Lift2) active
// Bit 1: Emergency/Lift (or tilt)
// Bit 2: Emergency/Stop
uint8_t emergency_bitmask;
// Charge voltage
float v_charge;
Expand Down Expand Up @@ -135,22 +132,86 @@ struct ll_ui_event {
} __attribute__((packed));
#pragma pack(pop)

#define LL_HIGH_LEVEL_CONFIG_MAX_COMMS_VERSION 1 // Max. comms packet version supported by this open_mower LL FW
#define LL_HIGH_LEVEL_CONFIG_BIT_DFPIS5V 1 << 0 // Enable full sound via mower_config env var "OM_DFP_IS_5V"
#define LL_HIGH_LEVEL_CONFIG_BIT_EMERGENCY_INVERSE 1 << 1 // Sample, for possible future usage, i.e. for SA-Type emergency
enum class OptionState : unsigned int {
OFF = 0,
ON,
UNDEFINED
};

#pragma pack(push, 1)
struct ConfigOptions {
OptionState dfp_is_5v : 2;
OptionState background_sounds : 2;
OptionState ignore_charging_current : 2;
// Need to block/waster the bits now, to be prepared for future enhancements
OptionState reserved_for_future_use1 : 2;
OptionState reserved_for_future_use2 : 2;
OptionState reserved_for_future_use3 : 2;
OptionState reserved_for_future_use4 : 2;
OptionState reserved_for_future_use5 : 2;
} __attribute__((packed));
#pragma pack(pop)
static_assert(sizeof(ConfigOptions) == 2, "Changing size of ConfigOption != 2 will break packet compatibilty");

typedef char iso639_1[2]; // Two char ISO 639-1 language code

enum class HallMode : unsigned int {
OFF = 0,
LIFT_TILT, // Wheel lifted and/or wheels tilted functionality
STOP, // Stop mower
UNDEFINED // This is used by foreign side to inform that it doesn't has a configuration for this sensor
};

#pragma pack(push, 1)
struct HallConfig {
HallMode mode : 3; // 1 bit reserved
bool active_low : 1;
} __attribute__((packed));
#pragma pack(pop)

// For each active hall, we've a handle of it for quick and direct access
struct HallHandle {
HallConfig config;
std::function<bool()> get_value;
};

#define MAX_HALL_INPUTS 10 // How much Hall-inputs we support. 4 * OM + 6 * Stock-CoverUI + 0 spare (because not yet required to make it fixed)

// LL/HL config packet, bi-directional, flexible-length, with defaults for YF-C500.
#pragma pack(push, 1)
struct ll_high_level_config {
uint8_t type;
uint8_t comms_version = LL_HIGH_LEVEL_CONFIG_MAX_COMMS_VERSION; // Increasing comms packet-version number for packet compatibility (n > 0)
uint8_t config_bitmask = 0; // See LL_HIGH_LEVEL_CONFIG_BIT_*
int8_t volume; // Volume (0-100%) feedback (if directly changed via CoverUI)
iso639_1 language; // ISO 639-1 (2-char) language code (en, de, ...)
uint16_t spare1 = 0; // Spare for future use
uint16_t spare2 = 0; // Spare for future use
uint16_t crc;
// ATTENTION: This is a flexible length struct. It is allowed to grow independently to HL without loosing compatibility,
// but never change or restructure already published member, except you really know their consequences.

// uint8_t type; Just for illustration. Get set in wire buffer with type PACKET_ID_LL_HIGH_LEVEL_CONFIG_REQ or PACKET_ID_LL_HIGH_LEVEL_CONFIG_RSP

ConfigOptions options = {.dfp_is_5v = OptionState::OFF, .background_sounds = OptionState::OFF, .ignore_charging_current = OptionState::OFF};
uint16_t rain_threshold = 700; // If (stock CoverUI) rain value < rain_threshold then it rains. Expected to differ between C500, SA and SC types (0xFFFF = unknown)
float v_charge_cutoff = 30.0f; // Protective max. charging voltage before charging get switched off (-1 = unknown)
float i_charge_cutoff = 1.5f; // Protective max. charging current before charging get switched off (-1 = unknown)
float v_battery_cutoff = 29.0f; // Protective max. battery voltage before charging get switched off (-1 = unknown)
float v_battery_empty = 21.7f + 0.3f; // Empty battery voltage used for % calc of capacity (-1 = unknown)
float v_battery_full = 28.7f - 0.3f; // Full battery voltage used for % calc of capacity (-1 = unknown)
uint16_t lift_period = 100; // Period (ms) for >=2 wheels to be lifted in order to count as emergency (0 = disable, 0xFFFF = unknown). This is to filter uneven ground
uint16_t tilt_period = 2500; // Period (ms) for a single wheel to be lifted in order to count as emergency (0 = disable, 0xFFFF = unknown). This is to filter uneven ground
uint8_t shutdown_esc_max_pitch = 0; // Do not shutdown ESCs if absolute pitch angle is greater than this (0 = disable, 0xffff = unknown) (to be implemented, see PR #97)
iso639_1 language = {'e', 'n'}; // ISO 639-1 (2-char) language code (en, de, ...)
uint8_t volume = 80; // Volume (0-100%) feedback (if directly changed i.e. via CoverUI or WebApp) (0xff = do not change)
HallConfig hall_configs[MAX_HALL_INPUTS] = {
{HallMode::LIFT_TILT, true}, // [0] OM Hall-1 input (Lift1) = YF-C500 default
{HallMode::LIFT_TILT, true}, // [1] OM Hall-2 input (Lift2) = YF-C500 default
{HallMode::STOP, true}, // [2] OM Hall-3 input (Stop1) = YF-C500 default
{HallMode::STOP, true}, // [3] OM Hall-4 input (Stop2) = YF-C500 default
//{HallMode::LIFT_TILT, false}, // [4] CoverUI-LIFT | LIFTX (up to CoverUI FW 2.0x)
//{HallMode::LIFT_TILT, false}, // [5] CoverUI-LIFTX (as of CoverUI FW 2.1x)
//{HallMode::LIFT_TILT, false}, // [6] CoverUI-LBUMP | RBUMP (up to CoverUI FW 2.0x)
//{HallMode::LIFT_TILT, false}, // [7] CoverUI-RBUMP (as of CoverUI FW 2.1x)
//{HallMode::STOP, false}, // [8] CoverUI-Stop1
//{HallMode::STOP, false}, // [9] CoverUI-Stop2
};
// INFO: Before adding a new member here: Decide if and how much hall_configs spares do we like to have

// uint16_t crc; Just for illustration, that it get appended, but only within the wire buffer
} __attribute__((packed));
#pragma pack(pop)

Expand Down
Loading

0 comments on commit c9d557b

Please sign in to comment.