514 lines
15 KiB
C++
514 lines
15 KiB
C++
/**
|
|
* @file config.cpp
|
|
* @brief Implementation of EEPROM and configuration-related functions.
|
|
*
|
|
* This file contains functions for managing EEPROM storage and handling configuration data.
|
|
* It includes the definitions of configuration structures, EEPROM access, and utility functions.
|
|
*/
|
|
|
|
#include "eeprom.h"
|
|
#include "debugger.h"
|
|
#include "globals.h"
|
|
|
|
// Instance of I2C_eeprom for EEPROM access
|
|
I2C_eeprom ee(I2C_EE_ADDRESS, EEPROM_SIZE_BYTES);
|
|
|
|
// Configuration and persistence data structures
|
|
configData_t ConfigData;
|
|
persistenceData_t PersistenceData;
|
|
|
|
// EEPROM version identifier
|
|
const uint16_t eeVersion = EEPROM_STRUCTURE_REVISION;
|
|
|
|
// Flag indicating whether EEPROM is available
|
|
boolean eeAvailable = false;
|
|
|
|
// Offsets within EEPROM for ConfigData and PersistenceData
|
|
const uint16_t startofConfigData = 16;
|
|
const uint16_t startofPersistence = 16 + sizeof(ConfigData) + (sizeof(ConfigData) % 16);
|
|
|
|
// Function prototype to check EEPROM availability
|
|
boolean checkEEPROMavailable();
|
|
|
|
/**
|
|
* @brief Initializes EEPROM and checks its availability.
|
|
*
|
|
* This function initializes the EEPROM using the I2C_eeprom instance and checks if it's available.
|
|
*/
|
|
void InitEEPROM()
|
|
{
|
|
ConfigData = ConfigData_defaults;
|
|
PersistenceData = {0};
|
|
ee.begin();
|
|
checkEEPROMavailable();
|
|
}
|
|
|
|
/**
|
|
* @brief Processes EEPROM actions based on the request from the global state.
|
|
*
|
|
* This function processes EEPROM actions based on the request from the global state.
|
|
* It performs actions such as saving, loading, and formatting EEPROM data for both configuration and persistence.
|
|
*/
|
|
void EEPROM_Process()
|
|
{
|
|
switch (globals.requestEEAction)
|
|
{
|
|
case EE_CFG_SAVE:
|
|
StoreConfig_EEPROM();
|
|
globals.requestEEAction = EE_IDLE;
|
|
Debug_pushMessage("Stored EEPROM CFG\n");
|
|
break;
|
|
case EE_CFG_LOAD:
|
|
GetConfig_EEPROM();
|
|
globals.requestEEAction = EE_IDLE;
|
|
Debug_pushMessage("Loaded EEPROM CFG\n");
|
|
break;
|
|
case EE_CFG_FORMAT:
|
|
FormatConfig_EEPROM();
|
|
globals.requestEEAction = EE_IDLE;
|
|
GetConfig_EEPROM();
|
|
Debug_pushMessage("Formatted EEPROM CFG\n");
|
|
break;
|
|
case EE_PDS_SAVE:
|
|
StorePersistence_EEPROM();
|
|
globals.requestEEAction = EE_IDLE;
|
|
Debug_pushMessage("Stored EEPROM PDS\n");
|
|
break;
|
|
case EE_PDS_LOAD:
|
|
GetPersistence_EEPROM();
|
|
globals.requestEEAction = EE_IDLE;
|
|
Debug_pushMessage("Loaded EEPROM PDS\n");
|
|
break;
|
|
case EE_PDS_FORMAT:
|
|
FormatPersistence_EEPROM();
|
|
globals.requestEEAction = EE_IDLE;
|
|
GetPersistence_EEPROM();
|
|
Debug_pushMessage("Formatted EEPROM PDS\n");
|
|
break;
|
|
case EE_FORMAT_ALL:
|
|
FormatConfig_EEPROM();
|
|
FormatPersistence_EEPROM();
|
|
GetConfig_EEPROM();
|
|
GetPersistence_EEPROM();
|
|
globals.requestEEAction = EE_IDLE;
|
|
Debug_pushMessage("Formatted EEPROM ALL\n");
|
|
break;
|
|
case EE_ALL_SAVE:
|
|
StorePersistence_EEPROM();
|
|
StoreConfig_EEPROM();
|
|
globals.requestEEAction = EE_IDLE;
|
|
Debug_pushMessage("Stored EEPROM ALL\n");
|
|
break;
|
|
case EE_IDLE:
|
|
default:
|
|
globals.requestEEAction = EE_IDLE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Stores the configuration data in EEPROM.
|
|
*
|
|
* This function calculates the checksum for the configuration data, updates it, and stores it in EEPROM.
|
|
* It also performs a sanity check on the configuration and raises a diagnostic trouble code (DTC) if needed.
|
|
*/
|
|
void StoreConfig_EEPROM()
|
|
{
|
|
// Berechnung der Prüfsumme
|
|
ConfigData.checksum = 0;
|
|
ConfigData.checksum = Checksum_EEPROM((uint8_t *)&ConfigData, sizeof(ConfigData));
|
|
|
|
// Überprüfung, ob der EEPROM verfügbar ist
|
|
if (!checkEEPROMavailable())
|
|
return;
|
|
|
|
// Byteweise in den EEPROM schreiben
|
|
uint8_t *dataPtr = (uint8_t *)&ConfigData;
|
|
for (uint16_t i = 0; i < sizeof(ConfigData); i++)
|
|
{
|
|
ee.writeByte(startofConfigData + i, dataPtr[i]);
|
|
}
|
|
|
|
// Sanity Check der Konfiguration
|
|
uint32_t ConfigSanityCheckResult = ConfigSanityCheck(false);
|
|
if (ConfigSanityCheckResult > 0)
|
|
{
|
|
MaintainDTC(DTC_EEPROM_CFG_SANITY, true, ConfigSanityCheckResult);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Retrieves the configuration data from EEPROM.
|
|
*
|
|
* This function reads the configuration data from EEPROM, performs a checksum validation,
|
|
* and conducts a sanity check on the configuration. It raises a diagnostic trouble code (DTC) if needed.
|
|
*/
|
|
void GetConfig_EEPROM()
|
|
{
|
|
if (!checkEEPROMavailable())
|
|
return;
|
|
|
|
ee.readBlock(startofConfigData, (uint8_t *)&ConfigData, sizeof(ConfigData));
|
|
|
|
uint32_t checksum = ConfigData.checksum;
|
|
ConfigData.checksum = 0;
|
|
|
|
MaintainDTC(DTC_EEPROM_CFG_BAD, (Checksum_EEPROM((uint8_t *)&ConfigData, sizeof(ConfigData)) != checksum));
|
|
|
|
ConfigData.checksum = checksum;
|
|
|
|
uint32_t ConfigSanityCheckResult = ConfigSanityCheck(false);
|
|
|
|
MaintainDTC(DTC_EEPROM_CFG_SANITY, (ConfigSanityCheckResult > 0), ConfigSanityCheckResult);
|
|
}
|
|
|
|
/**
|
|
* @brief Stores the persistence data in EEPROM.
|
|
*
|
|
* This function increments the write cycle counter, performs a checksum calculation on the persistence data,
|
|
* and stores it in EEPROM. It also handles EEPROM page movement when needed.
|
|
*/
|
|
void StorePersistence_EEPROM()
|
|
{
|
|
if (PersistenceData.writeCycleCounter >= 0xFFF0)
|
|
MovePersistencePage_EEPROM(false);
|
|
else
|
|
PersistenceData.writeCycleCounter++;
|
|
|
|
PersistenceData.checksum = 0;
|
|
PersistenceData.checksum = Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData));
|
|
|
|
if (!checkEEPROMavailable())
|
|
return;
|
|
|
|
// Byteweise in den EEPROM schreiben
|
|
uint8_t *dataPtr = (uint8_t *)&PersistenceData;
|
|
for (uint16_t i = 0; i < sizeof(PersistenceData); i++)
|
|
{
|
|
ee.writeByte(globals.eePersistanceAdress + i, dataPtr[i]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Retrieves the persistence data from EEPROM.
|
|
*
|
|
* This function reads the EEPROM to get the start address of the persistence data.
|
|
* If the start address is out of range, it resets and stores defaults. Otherwise,
|
|
* it reads from EEPROM and checks if the data is correct.
|
|
*/
|
|
void GetPersistence_EEPROM()
|
|
{
|
|
if (!checkEEPROMavailable())
|
|
return;
|
|
|
|
ee.readBlock(0, (uint8_t *)&globals.eePersistanceAdress, sizeof(globals.eePersistanceAdress));
|
|
// if we got the StartAdress of Persistance and it's out of Range - we Reset it and store defaults
|
|
// otherwise we Read from eeprom and check if everything is correct
|
|
if (globals.eePersistanceAdress < startofPersistence || globals.eePersistanceAdress > ee.getDeviceSize())
|
|
{
|
|
MovePersistencePage_EEPROM(true);
|
|
FormatPersistence_EEPROM();
|
|
MaintainDTC(DTC_EEPROM_PDSADRESS_BAD, true);
|
|
}
|
|
else
|
|
{
|
|
ee.readBlock(globals.eePersistanceAdress, (uint8_t *)&PersistenceData, sizeof(PersistenceData));
|
|
|
|
uint32_t checksum = PersistenceData.checksum;
|
|
PersistenceData.checksum = 0;
|
|
|
|
MaintainDTC(DTC_EEPROM_PDS_BAD, (Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData)) != checksum));
|
|
|
|
PersistenceData.checksum = checksum;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Formats the configuration partition in EEPROM.
|
|
*
|
|
* This function resets the configuration data to defaults and stores it in EEPROM.
|
|
*/
|
|
void FormatConfig_EEPROM()
|
|
{
|
|
Debug_pushMessage("Formatting Config-Partition\n");
|
|
ConfigData = ConfigData_defaults;
|
|
ConfigData.EEPROM_Version = eeVersion;
|
|
StoreConfig_EEPROM();
|
|
}
|
|
|
|
/**
|
|
* @brief Formats the persistence partition in EEPROM.
|
|
*
|
|
* This function resets the persistence data to defaults and stores it in EEPROM.
|
|
*/
|
|
void FormatPersistence_EEPROM()
|
|
{
|
|
Debug_pushMessage("Formatting Persistance-Partition\n");
|
|
PersistenceData = {0};
|
|
// memset(&PersistenceData, 0, sizeof(PersistenceData));
|
|
StorePersistence_EEPROM();
|
|
}
|
|
/**
|
|
* @brief Moves the persistence page in EEPROM.
|
|
*
|
|
* This function adjusts the persistence page address and resets the write cycle counter.
|
|
*
|
|
* @param reset If true, the function resets the persistence page address to the start of the partition.
|
|
*/
|
|
void MovePersistencePage_EEPROM(boolean reset)
|
|
{
|
|
if (!checkEEPROMavailable())
|
|
return;
|
|
|
|
globals.eePersistanceAdress += sizeof(PersistenceData);
|
|
PersistenceData.writeCycleCounter = 0;
|
|
|
|
// Check if we reached the end of the EEPROM and start over at the beginning
|
|
if ((globals.eePersistanceAdress + sizeof(PersistenceData)) > ee.getDeviceSize() || reset)
|
|
{
|
|
globals.eePersistanceAdress = startofPersistence;
|
|
}
|
|
|
|
ee.updateBlock(0, (uint8_t *)&globals.eePersistanceAdress, sizeof(globals.eePersistanceAdress));
|
|
}
|
|
|
|
/**
|
|
* @brief Calculate CRC-32 checksum for a block of data.
|
|
*
|
|
* This function implements the CRC-32 algorithm.
|
|
*
|
|
* @param data Pointer to the data block.
|
|
* @param len Length of the data block in bytes.
|
|
* @return CRC-32 checksum.
|
|
*/
|
|
uint32_t Checksum_EEPROM(uint8_t const *data, size_t len)
|
|
{
|
|
if (data == NULL)
|
|
return 0;
|
|
|
|
uint32_t crc = 0xFFFFFFFF;
|
|
uint32_t mask;
|
|
|
|
while (len--)
|
|
{
|
|
crc ^= *data++;
|
|
|
|
for (uint8_t k = 0; k < 8; k++)
|
|
{
|
|
mask = -(crc & 1);
|
|
crc = (crc >> 1) ^ (0xEDB88320 & mask);
|
|
}
|
|
}
|
|
|
|
return ~crc;
|
|
}
|
|
|
|
/**
|
|
* @brief Dump a portion of EEPROM contents for debugging.
|
|
*
|
|
* This function prints the contents of a specified portion of EEPROM in a formatted way.
|
|
*
|
|
* @param memoryAddress Starting address in EEPROM.
|
|
* @param length Number of bytes to dump.
|
|
*/
|
|
void dumpEEPROM(uint16_t memoryAddress, uint16_t length)
|
|
{
|
|
#define BLOCK_TO_LENGTH 16
|
|
|
|
if (!checkEEPROMavailable())
|
|
return;
|
|
|
|
char ascii_buf[BLOCK_TO_LENGTH + 1] = {0};
|
|
sprintf(ascii_buf, "%*s", BLOCK_TO_LENGTH, "ASCII");
|
|
|
|
// Print column headers
|
|
Debug_pushMessage(PSTR("\nAddress "));
|
|
for (int x = 0; x < BLOCK_TO_LENGTH; x++)
|
|
Debug_pushMessage("%3d", x);
|
|
|
|
// Align address and length to BLOCK_TO_LENGTH boundaries
|
|
memoryAddress = memoryAddress / BLOCK_TO_LENGTH * BLOCK_TO_LENGTH;
|
|
length = (length + BLOCK_TO_LENGTH - 1) / BLOCK_TO_LENGTH * BLOCK_TO_LENGTH;
|
|
|
|
// Iterate through the specified portion of EEPROM
|
|
for (unsigned int i = 0; i < length; i++)
|
|
{
|
|
int blockpoint = memoryAddress % BLOCK_TO_LENGTH;
|
|
|
|
// Print ASCII representation header for each block
|
|
if (blockpoint == 0)
|
|
{
|
|
if (i > 0) // Ensure we don't print an empty ASCII buffer on the first iteration
|
|
{
|
|
ascii_buf[BLOCK_TO_LENGTH] = 0;
|
|
Debug_pushMessage(" %s", ascii_buf);
|
|
}
|
|
Debug_pushMessage("\n0x%05X:", memoryAddress);
|
|
memset(ascii_buf, ' ', BLOCK_TO_LENGTH); // Clear the ASCII buffer with spaces
|
|
}
|
|
|
|
// Read and print each byte
|
|
uint8_t byte = ee.readByte(memoryAddress);
|
|
ascii_buf[blockpoint] = (byte >= 0x20 && byte <= 0x7E) ? byte : '.';
|
|
Debug_pushMessage(" %02X", byte);
|
|
|
|
memoryAddress++;
|
|
}
|
|
|
|
// Print remaining ASCII buffer
|
|
ascii_buf[BLOCK_TO_LENGTH] = 0;
|
|
Debug_pushMessage(" %s\n", ascii_buf); // Final ASCII line
|
|
}
|
|
|
|
/**
|
|
* @brief Check if EEPROM is available and connected.
|
|
*
|
|
* This function checks if the EEPROM is available and connected. If not, it triggers
|
|
* a diagnostic trouble code (DTC) indicating the absence of EEPROM.
|
|
*
|
|
* @return true if EEPROM is available, false otherwise.
|
|
*/
|
|
boolean checkEEPROMavailable()
|
|
{
|
|
// Check if EEPROM is connected
|
|
if (!ee.isConnected())
|
|
{
|
|
// Trigger DTC for no EEPROM found
|
|
MaintainDTC(DTC_NO_EEPROM_FOUND, true);
|
|
return false;
|
|
}
|
|
|
|
// Clear DTC for no EEPROM found since it's available now
|
|
MaintainDTC(DTC_NO_EEPROM_FOUND, false);
|
|
|
|
// EEPROM is available
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Perform sanity check on configuration settings.
|
|
*
|
|
* This function checks the validity of various configuration settings and returns a bitmask
|
|
* indicating which settings need to be reset. If autocorrect is enabled, it resets the settings
|
|
* to their default values.
|
|
*
|
|
* @param autocorrect If true, automatically correct invalid settings by resetting to defaults.
|
|
* @return A bitmask indicating which settings need to be reset.
|
|
*/
|
|
uint32_t ConfigSanityCheck(bool autocorrect)
|
|
{
|
|
uint32_t setting_reset_bits = 0;
|
|
|
|
if (!validateWiFiString(ConfigData.wifi_ap_ssid, sizeof(ConfigData.wifi_ap_ssid)))
|
|
{
|
|
SET_BIT(setting_reset_bits, 1);
|
|
if (autocorrect)
|
|
strncpy(ConfigData.wifi_ap_ssid, ConfigData_defaults.wifi_ap_ssid, sizeof(ConfigData.wifi_ap_ssid));
|
|
}
|
|
|
|
if (!validateWiFiString(ConfigData.wifi_ap_password, sizeof(ConfigData.wifi_ap_password)))
|
|
{
|
|
SET_BIT(setting_reset_bits, 2);
|
|
if (autocorrect)
|
|
strncpy(ConfigData.wifi_ap_password, ConfigData_defaults.wifi_ap_password, sizeof(ConfigData.wifi_ap_password));
|
|
}
|
|
|
|
if (!validateWiFiString(ConfigData.wifi_client_ssid, sizeof(ConfigData.wifi_client_ssid)))
|
|
{
|
|
SET_BIT(setting_reset_bits, 3);
|
|
if (autocorrect)
|
|
strncpy(ConfigData.wifi_client_ssid, ConfigData_defaults.wifi_client_ssid, sizeof(ConfigData.wifi_client_ssid));
|
|
}
|
|
|
|
if (!validateWiFiString(ConfigData.wifi_client_password, sizeof(ConfigData.wifi_client_password)))
|
|
{
|
|
SET_BIT(setting_reset_bits, 4);
|
|
if (autocorrect)
|
|
strncpy(ConfigData.wifi_client_password, ConfigData_defaults.wifi_client_password, sizeof(ConfigData.wifi_client_password));
|
|
}
|
|
// Return the bitmask indicating which settings need to be reset
|
|
return setting_reset_bits;
|
|
}
|
|
|
|
/**
|
|
* @brief Validates whether a given string contains only characters allowed in WiFi SSIDs and passwords.
|
|
*
|
|
* This function checks each character in the provided string to ensure
|
|
* that it contains only characters allowed in WiFi SSIDs and passwords.
|
|
* It considers characters from 'A' to 'Z', 'a' to 'z', '0' to '9', as well as
|
|
* the following special characters: ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
|
|
*
|
|
* @param string Pointer to the string to be validated.
|
|
* @param size Size of the string including the null-terminator.
|
|
* @return true if the string contains only allowed characters or is NULL,
|
|
* false otherwise.
|
|
*/
|
|
bool validateWiFiString(char *string, size_t size)
|
|
{
|
|
if (string == NULL)
|
|
return false;
|
|
|
|
for (size_t i = 0; i < size; i++)
|
|
{
|
|
char c = string[i];
|
|
if (c == '\0')
|
|
{
|
|
// Reached the end of the string, all characters were valid WiFi characters.
|
|
return true;
|
|
}
|
|
if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
|
(c >= '0' && c <= '9') || c == '!' || c == '"' || c == '#' ||
|
|
c == '$' || c == '%' || c == '&' || c == '\'' || c == '(' ||
|
|
c == ')' || c == '*' || c == '+' || c == ',' || c == '-' ||
|
|
c == '.' || c == '/' || c == ':' || c == ';' || c == '<' ||
|
|
c == '=' || c == '>' || c == '?' || c == '@' || c == '[' ||
|
|
c == '\\' || c == ']' || c == '^' || c == '_' || c == '`' ||
|
|
c == '{' || c == '|' || c == '}' || c == '~'))
|
|
{
|
|
// Found a character that is not a valid WiFi character.
|
|
return false;
|
|
}
|
|
}
|
|
// If the loop completes without finding a null terminator, the string is invalid.
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Write sequential numbers to a portion of EEPROM.
|
|
*
|
|
* This function writes sequential numbers starting from 0 to a specified portion of EEPROM.
|
|
* If the number reaches 255, it wraps around and starts again from 1.
|
|
*
|
|
* @param memoryAddress Starting address in EEPROM.
|
|
* @param length Number of bytes to write.
|
|
*/
|
|
void writeSequentialToEEPROM(uint16_t memoryAddress, uint16_t length)
|
|
{
|
|
if (!checkEEPROMavailable())
|
|
return;
|
|
|
|
uint8_t value = 0;
|
|
for (uint16_t i = 0; i < length; i++)
|
|
{
|
|
ee.writeByte(memoryAddress + i, value);
|
|
value = (value == 255) ? 1 : value + 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Write 0 to a portion of EEPROM.
|
|
*
|
|
* This function writes 0 to a specified portion of EEPROM.
|
|
*
|
|
* @param memoryAddress Starting address in EEPROM.
|
|
* @param length Number of bytes to write.
|
|
*/
|
|
void writeZeroToEEPROM(uint16_t memoryAddress, uint16_t length)
|
|
{
|
|
if (!checkEEPROMavailable())
|
|
return;
|
|
|
|
for (uint16_t i = 0; i < length; i++)
|
|
{
|
|
ee.writeByte(memoryAddress + i, 0);
|
|
}
|
|
} |