diff --git a/src/base/task.h b/src/base/task.h index 0b31fd3..9266661 100644 --- a/src/base/task.h +++ b/src/base/task.h @@ -38,6 +38,7 @@ enum Type : uint8_t { DisplayMsgEngineTempHigh, DisplayMsgLapCounterStart, DisplayMsgLapCounterLapTime, + DisplayMsgCorruptedConfig, ConfigTrackDetect, ConfigWriteTempTrack, ConfigTrackDelete, @@ -46,6 +47,7 @@ enum Type : uint8_t { ConfigVbatSetLow, ConfigTengSetLow, ConfigTengSetHigh, + ConfigTengSetOffset, BatteryCal, TelemetrySendLapPacket, AllConfigUpdated, diff --git a/src/custom_types.h b/src/custom_types.h index e4c3f40..e5ff958 100644 --- a/src/custom_types.h +++ b/src/custom_types.h @@ -16,6 +16,7 @@ struct VehicleConfig { float vbat_low_ = 0; float teng_low_ = 0; float teng_high_ = 0; + float teng_offset_ = 0; }; struct LatLng { diff --git a/src/data/eeprom_layout.h b/src/data/eeprom_layout.h index 9ddf4ae..66f7588 100644 --- a/src/data/eeprom_layout.h +++ b/src/data/eeprom_layout.h @@ -15,11 +15,25 @@ static const uint16_t TRACK_SLOT_BYTES = 128; static const uint16_t TRACK_BASE_ADDR = CONFIG_ADDR + CONFIG_RESERVED_BYTES; static const uint16_t TRACK_END_ADDR = TRACK_BASE_ADDR + (TRACK_SLOTS * TRACK_SLOT_BYTES); static const uint16_t FREE_AFTER_TRACKS = TOTAL_BYTES - TRACK_END_ADDR; +static const uint16_t CRC_BASE_OFFSET = 3072; +static const uint16_t CRC_SIZE = sizeof(uint16_t); +static const uint16_t CRC_SLOTS = 9; +static const uint16_t CRC_END_ADDR = CRC_BASE_OFFSET + (CRC_SLOTS * CRC_SIZE); -static_assert(TRACK_END_ADDR <= TOTAL_BYTES, "EEPROM layout exceeds physical storage"); + +static_assert(TRACK_END_ADDR <= CRC_BASE_OFFSET, "EEPROM layout overlap between tracks and CRC sectors"); +static_assert(TRACK_END_ADDR <= TOTAL_BYTES || CRC_END_ADDR <= TOTAL_BYTES, "EEPROM layout exceeds physical storage"); inline uint16_t trackSlotAddr(uint8_t idx) { return TRACK_BASE_ADDR + ((idx - 1) * TRACK_SLOT_BYTES); } +inline uint16_t configCRCAddr() { + return CRC_BASE_OFFSET; +} + +inline uint16_t trackCRCAddr(uint8_t idx) { + return CRC_BASE_OFFSET + (idx * CRC_SIZE); +} + } // namespace eeprom_layout diff --git a/src/modules/cmd/cmd.cpp b/src/modules/cmd/cmd.cpp index 85c1fa3..669f7e4 100644 --- a/src/modules/cmd/cmd.cpp +++ b/src/modules/cmd/cmd.cpp @@ -133,6 +133,10 @@ Cmd::CommandId Cmd::parseCommandName(const char *input) { return ThermoSetHigh; } + if (strcmp(input, "THERMO_SET_OFFSET") == 0) { + return ThermoSetOffset; + } + if (strcmp(input, "DEBUG_UNLOCK") == 0) { return DebugUnlock; } @@ -524,6 +528,33 @@ int Cmd::handleThermoSetHigh(unsigned short argc, char* argv[]) { return result; } + +int Cmd::handleThermoSetOffset(unsigned short argc, char* argv[]) { + if (argc != 2) { +#ifdef ERROR + if (logger_ != nullptr) { + logger_->error("THERMO_SET_OFFSET expects 1 argument"); + } +#endif + return 1; + } + float offset = strtod(argv[1], nullptr); + uint32_t task_data; + memcpy(&task_data, &offset, sizeof(uint32_t)); +#ifdef INFO + if (logger_ != nullptr) { + logger_->info("Setting offset for TENG"); + } +#endif + int result = router::send(module::Config, task::ConfigTengSetOffset, task_data); +#ifdef ERROR + if (result != 0 && logger_ != nullptr) { + logger_->error("Failed to queue THERMO_SET_OFFSET config update"); + } +#endif + return result; +} + int Cmd::handleHelpGlobal(unsigned short argc) { if (argc != 1) { #ifdef ERROR @@ -703,6 +734,9 @@ int Cmd::dispatchCommand(CommandId command, unsigned short argc, char *argv[]) { case ThermoSetHigh: return this->handleThermoSetHigh(argc, argv); + case ThermoSetOffset: + return this->handleThermoSetOffset(argc, argv); + case DebugUnlock: return this->handleDebugUnlock(argc); diff --git a/src/modules/cmd/cmd.h b/src/modules/cmd/cmd.h index 3e6f2c3..d039c6b 100644 --- a/src/modules/cmd/cmd.h +++ b/src/modules/cmd/cmd.h @@ -30,6 +30,7 @@ private: BatterySetLow, ThermoSetLow, ThermoSetHigh, + ThermoSetOffset, DebugUnlock, DebugLock, DbgSendBlankLap, @@ -67,6 +68,7 @@ private: int handleBatterySetLow(unsigned short argc, char *argv[]); int handleThermoSetLow(unsigned short argc, char *argv[]); int handleThermoSetHigh(unsigned short argc, char *argv[]); + int handleThermoSetOffset(unsigned short argc, char *argv[]); int handleHelpGlobal(unsigned short argc); int handleUnknownCommand(unsigned short argc, char *argv[]); int handleDebugUnlock(unsigned short argc); diff --git a/src/modules/config/config.cpp b/src/modules/config/config.cpp index a3d02d3..550045f 100644 --- a/src/modules/config/config.cpp +++ b/src/modules/config/config.cpp @@ -4,6 +4,7 @@ #include "config.h" #include "data/track_store.h" +#include "telemetry_common/telemetry_common.h" #include #include @@ -125,6 +126,11 @@ int Config::writeTengHigh(float value) { return this->writeConfig(); } +int Config::writeTengOffset(float value) { + config_.teng_offset_ = value; + return this->writeConfig(); +} + Config::Config() : logger_(nullptr), valid_config_(true) {} Config::Config(SystemLogger *logger) : logger_(logger), valid_config_(true) {} @@ -132,13 +138,28 @@ Config::Config(SystemLogger *logger) : logger_(logger), valid_config_(true) {} Config::~Config() {} int Config::readConfig() { + uint16_t crc; EEPROM.get(eeprom_layout::CONFIG_ADDR, config_); + EEPROM.get(eeprom_layout::configCRCAddr(), crc); + int res = crc16_ccitt_check((uint8_t*)&config_, sizeof(config_), crc); + if (res != 0) { + #ifdef ERROR + if (logger_ != nullptr) { + logger_->error("Corrupted config, reset and regenerate config required"); + } + #endif + router::send(module::Lcd, task::DisplayMsgCorruptedConfig, 5000); + return 1; + } configGlobalWrite(config_); + router::sendAll(module::Config, task::AllConfigUpdated); return 0; } int Config::writeConfig() { + uint16_t crc = crc16_ccitt((uint8_t*)&config_, sizeof(config_)); EEPROM.put(eeprom_layout::CONFIG_ADDR, config_); + EEPROM.put(eeprom_layout::configCRCAddr(), crc); configGlobalWrite(config_); #ifdef INFO if (logger_ != nullptr) { @@ -301,6 +322,14 @@ int Config::handleActiveTask(unsigned long timeout_ms) { this->taskComplete(); return result; } + + case task::ConfigTengSetOffset: { + float offset_value; + memcpy(&offset_value, &active_task_.data_, sizeof(float)); + int result = this->writeTengOffset(offset_value); + this->taskComplete(); + return result; + } default: // Broadcasts such as AllConfigUpdated may be queued back into Config. diff --git a/src/modules/config/config.h b/src/modules/config/config.h index 2d94b30..922b718 100644 --- a/src/modules/config/config.h +++ b/src/modules/config/config.h @@ -51,6 +51,7 @@ private: int writeVbatLow(float value); int writeTengLow(float value); int writeTengHigh(float value); + int writeTengOffset(float value); public: int push(const Task &task) override; diff --git a/src/modules/lcd/lcd.cpp b/src/modules/lcd/lcd.cpp index 6e4cbfd..946410d 100644 --- a/src/modules/lcd/lcd.cpp +++ b/src/modules/lcd/lcd.cpp @@ -378,6 +378,18 @@ int Lcd::renderMsgLapCounterLapTime() { return 0; } +int Lcd::renderMsgCorruptedConfig() { + if (!base_rendered_) { + this->clear(); + display_->setCursor(3, 1); + this->print("CRITICAL ERROR"); + display_->setCursor(2, 2); + this->print("CONFIG CORRUPTED"); + base_rendered_ = true; + } + return 0; +} + int Lcd::push(const Task &task) { return queue_.push(task); } Lcd::Lcd() @@ -593,6 +605,10 @@ int Lcd::loop(unsigned long timeout_ms) { case task::DisplayMsgLapCounterLapTime: base_rendered_ = false; activateMessage(screen::MsgLapCounterLapTime, next_task.data_); + + case task::DisplayMsgCorruptedConfig: + base_rendered_ = false; + activateMessage(screen::MsgCorruptedConfig, next_task.data_); default: break; @@ -667,6 +683,10 @@ int Lcd::loop(unsigned long timeout_ms) { case screen::MsgLapCounterLapTime: this->renderMsgLapCounterLapTime(); break; + + case screen::MsgCorruptedConfig: + this->renderMsgCorruptedConfig(); + break; default: break; diff --git a/src/modules/lcd/lcd.h b/src/modules/lcd/lcd.h index d11c501..62c2594 100644 --- a/src/modules/lcd/lcd.h +++ b/src/modules/lcd/lcd.h @@ -31,6 +31,7 @@ enum LcdScreen : uint8_t { MsgEngineTempHigh, MsgLapCounterStart, MsgLapCounterLapTime, + MsgCorruptedConfig, }; } // namespace screen @@ -79,6 +80,7 @@ private: int renderMsgEngineTempHigh(); int renderMsgLapCounterStart(); int renderMsgLapCounterLapTime(); + int renderMsgCorruptedConfig(); public: int push(const Task &task) override; diff --git a/src/modules/thermocouple/thermocouple.cpp b/src/modules/thermocouple/thermocouple.cpp index 76f5f37..49ec40a 100644 --- a/src/modules/thermocouple/thermocouple.cpp +++ b/src/modules/thermocouple/thermocouple.cpp @@ -36,6 +36,7 @@ int Thermocouple::loop(unsigned long timeout_ms) { configGlobalRead(config); low_ = config.teng_low_; high_ = config.teng_high_; + offset_ = config.teng_offset_; break; } @@ -46,7 +47,7 @@ int Thermocouple::loop(unsigned long timeout_ms) { } if (millis() > last_read_at_ + update_interval_) { - temperature_ = thermocouple_->readCelsius(); + temperature_ = thermocouple_->readCelsius() + offset_; tengGlobalWrite(temperature_); last_read_at_ = millis(); if (temperature_ > high_) { diff --git a/src/modules/thermocouple/thermocouple.h b/src/modules/thermocouple/thermocouple.h index 5d85aa1..f144608 100644 --- a/src/modules/thermocouple/thermocouple.h +++ b/src/modules/thermocouple/thermocouple.h @@ -20,6 +20,7 @@ private: float temperature_; float low_; float high_; + float offset_; unsigned long update_interval_ = 1000; unsigned long last_read_at_ = 0; unsigned long warning_sent_at_ = 0;