2424#include < globals.hpp>
2525
2626#include " ../../src/filesystem/versioned_struct.hpp"
27+ #include " GpsServiceBase.hpp"
2728
2829namespace xbot ::driver::sabo {
2930
3031// Main types and enums
3132namespace types {
3233// Hardware versions are "as of" versions
33- enum class HardwareVersion : uint8_t { V0_1 = 0 , V0_2 , V0_3 };
34+ enum class HardwareVersion : uint8_t { V0_1 = 0 , V0_2_0, V0_2_1 , V0_3 };
3435
3536enum class SeriesType { Series1, Series2 };
3637
@@ -74,43 +75,49 @@ enum class TempCompensation : uint8_t {
7475// Hardware configurations
7576namespace config {
7677struct Sensor {
77- ioline_t line;
78- bool invert = false ;
78+ const ioline_t line;
79+ const bool invert = false ;
7980};
8081
8182struct Spi {
8283 SPIDriver* instance;
8384 struct {
84- ioline_t sck;
85- ioline_t miso;
86- ioline_t mosi;
87- ioline_t cs = PAL_NOLINE;
85+ const ioline_t sck;
86+ const ioline_t miso;
87+ const ioline_t mosi;
88+ const ioline_t cs = PAL_NOLINE;
8889 } pins;
8990};
9091
9192struct CoverUi {
9293 Spi spi;
9394 struct {
94- ioline_t latch_load;
95- ioline_t oe = PAL_NOLINE;
96- ioline_t btn_cs = PAL_NOLINE;
97- ioline_t inp_cs = PAL_NOLINE;
95+ const ioline_t latch_load;
96+ const ioline_t oe = PAL_NOLINE;
97+ const ioline_t btn_cs = PAL_NOLINE;
98+ const ioline_t inp_cs = PAL_NOLINE;
9899 } pins;
99100};
100101
101102struct Lcd {
102- Spi spi;
103+ const Spi spi;
103104 struct {
104- ioline_t dc;
105- ioline_t rst;
106- ioline_t backlight;
105+ const ioline_t dc;
106+ const ioline_t rst;
107+ const ioline_t backlight;
107108 } pins;
108109};
109110
110111struct Bms {
111112 I2CDriver* i2c;
112113};
113114
115+ struct Adc {
116+ const float charger_voltage_scale_factor;
117+ const float battery_voltage_scale_factor;
118+ const float dcdc_in_current_scale_factor;
119+ };
120+
114121// Individual hardware configurations
115122inline const Sensor SENSORS_V0_1[] = {
116123 {LINE_GPIO13}, // LIFT_FL
@@ -143,12 +150,17 @@ inline const Lcd LCD_V0_2 = {.spi = {&SPID1, {LINE_SPI1_SCK, LINE_SPI1_MISO, LIN
143150
144151inline const Bms BMS_V0_2 = {.i2c = &I2CD2};
145152
153+ inline const Adc ADC_V0_2_1 = {.charger_voltage_scale_factor = 16 .3846f , // (200k Rtop + 13k Rbot)/13k Rbot
154+ .battery_voltage_scale_factor = 16 .3846f , // (200k Rtop + 13k Rbot)/13k Rbot
155+ .dcdc_in_current_scale_factor = 1 .0f }; // 1/(20gain * Rshunt 0.05)
156+
146157// Hardware configuration references
147158struct HardwareConfig {
148159 etl::array_view<const Sensor> sensors;
149160 const CoverUi* cover_ui;
150161 const Lcd* lcd;
151162 const Bms* bms;
163+ const Adc* adc;
152164};
153165
154166// Hardware version to configuration array which need to be in sync with HardwareVersion enum
@@ -159,22 +171,26 @@ inline constexpr HardwareConfig HARDWARE_CONFIGS[] = {
159171 .cover_ui = &COVER_UI_V0_1,
160172 .lcd = nullptr , // No LCD for V0_1
161173 .bms = nullptr , // No BMS for V0_1
174+ .adc = nullptr // No ADC for V0_1
162175 },
163- // V0_2
164- {
165- .sensors = etl::array_view<const Sensor>(SENSORS_V0_1),
166- .cover_ui = &COVER_UI_V0_2,
167- .lcd = &LCD_V0_2,
168- .bms = &BMS_V0_2,
169- },
176+ // V0_2_0
177+ {.sensors = etl::array_view<const Sensor>(SENSORS_V0_1),
178+ .cover_ui = &COVER_UI_V0_2,
179+ .lcd = &LCD_V0_2,
180+ .bms = &BMS_V0_2,
181+ .adc = nullptr },
182+ // V0_2_1
183+ {.sensors = etl::array_view<const Sensor>(SENSORS_V0_1),
184+ .cover_ui = &COVER_UI_V0_2,
185+ .lcd = &LCD_V0_2,
186+ .bms = &BMS_V0_2,
187+ .adc = &ADC_V0_2_1},
170188 // V0_3
171- {
172- .sensors = etl::array_view<const Sensor>(SENSORS_V0_3),
173- .cover_ui = &COVER_UI_V0_2,
174- .lcd = &LCD_V0_2,
175- .bms = nullptr , // No BMS for V0_3 yet
176- },
177- };
189+ {.sensors = etl::array_view<const Sensor>(SENSORS_V0_3),
190+ .cover_ui = &COVER_UI_V0_2,
191+ .lcd = &LCD_V0_2,
192+ .bms = nullptr , // No BMS for V0_3 yet
193+ .adc = &ADC_V0_2_1}};
178194} // namespace config
179195
180196// Constants and definitions
@@ -190,6 +206,7 @@ inline constexpr size_t NUM_BUTTONS = sizeof(ALL_BUTTONS) / sizeof(ALL_BUTTONS[0
190206inline constexpr uint16_t LCD_WIDTH = 240 ; // ATTENTION: LVGL I1 mode requires a multiple of 8 width
191207inline constexpr uint16_t LCD_HEIGHT = 160 ;
192208inline constexpr uint8_t BUFFER_FRACTION = 10 ; // 1/10 screen size for buffers
209+
193210} // namespace defs
194211
195212// Settings namespace for persistent configuration
@@ -210,7 +227,7 @@ namespace settings {
210227 * Migration is handled automatically by VersionedStruct base class.
211228 */
212229#pragma pack(push, 1)
213- struct LCDSettings : public xbot ::driver::filesystem::VersionedStruct<xbot::driver::sabo::settings:: LCDSettings> {
230+ struct LCDSettings : public xbot ::driver::filesystem::VersionedStruct<LCDSettings> {
214231 VERSIONED_STRUCT_FIELDS (1 ); // Version 1 - automatically defines VERSION constant and version field
215232 static constexpr const char * PATH = " /cfg/sabo/lcd.bin" ;
216233
@@ -220,8 +237,36 @@ struct LCDSettings : public xbot::driver::filesystem::VersionedStruct<xbot::driv
220237};
221238#pragma pack(pop)
222239
223- static_assert (sizeof (LCDSettings) == 5 ,
224- " LCDSettings must be 5 bytes (2 version + 3 data)" ); // Protect against thoughtless changes
240+ // Protect against thoughtless changes
241+ static_assert (sizeof (LCDSettings) == 5 , " LCDSettings must be 5 bytes (2 version + 3 data)" );
242+
243+ /* *
244+ * @brief GPS persistent settings stored in LittleFS
245+ *
246+ * This struct is serialized directly to flash as binary data.
247+ * Evolution strategy: version field + append-only new fields.
248+ *
249+ * Rules for evolution:
250+ * - NEVER change existing field types or order
251+ * - ALWAYS increment VERSION when adding fields
252+ * - ONLY append new fields at the end
253+ * - Use padding to maintain alignment if needed
254+ *
255+ * Migration is handled automatically by VersionedStruct base class.
256+ */
257+ #pragma pack(push, 1)
258+ struct GPSSettings : public xbot ::driver::filesystem::VersionedStruct<GPSSettings> {
259+ VERSIONED_STRUCT_FIELDS (1 ); // Version 1 - automatically defines VERSION constant and version field
260+ static constexpr const char * PATH = " /cfg/sabo/gps.bin" ;
261+
262+ ProtocolType protocol = ProtocolType::NMEA; // GPS protocol (UBX or NMEA)
263+ uint8_t uart = 0 ; // UART index (0 = default robot port)
264+ uint32_t baudrate = 921600 ; // Baudrate
265+ };
266+ #pragma pack(pop)
267+
268+ // Protect against thoughtless changes
269+ static_assert (sizeof (GPSSettings) == 8 , " GPSSettings must be 8 bytes (2 version + 1 protocol + 1 uart + 4 baudrate)" );
225270
226271} // namespace settings
227272
@@ -236,7 +281,12 @@ inline types::HardwareVersion GetHardwareVersion(const struct carrier_board_info
236281 if (board_info.version_major == 0 ) {
237282 switch (board_info.version_minor ) {
238283 case 1 : version = types::HardwareVersion::V0_1; break ;
239- case 2 : version = types::HardwareVersion::V0_2; break ;
284+ case 2 :
285+ switch (board_info.version_patch ) {
286+ case 0 : version = types::HardwareVersion::V0_2_0; break ;
287+ case 1 : version = types::HardwareVersion::V0_2_1; break ;
288+ }
289+ break ;
240290 case 3 : version = types::HardwareVersion::V0_3; break ;
241291 }
242292 }
0 commit comments