Skip to content

Commit

Permalink
Merge pull request #3 from arduino-libraries/persistence
Browse files Browse the repository at this point in the history
Add support for new defaults register
  • Loading branch information
sebromero authored Jun 14, 2024
2 parents 778a4e7 + db4c882 commit 6b52463
Show file tree
Hide file tree
Showing 19 changed files with 240 additions and 138 deletions.
7 changes: 3 additions & 4 deletions examples/ChangeI2CAddress/ChangeI2CAddress.ino
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,11 @@ void setup() {
Serial.print("🔧 Changing device address to 0x");
Serial.print(customI2CAddress, HEX);
Serial.println("...");
device.setDeviceAddress(customI2CAddress);

// Setting the second parameter to true makes the change persistent
device.setDeviceAddress(customI2CAddress, true);
checkConnection(device);

// Store the new address in flash
device.storeSettingsInFlash();

Serial.println("🔄 Resetting device to check if change is persistent...");
device.reset();
delay(2000); // Wait for the device to reset
Expand Down
3 changes: 2 additions & 1 deletion examples/OutdoorAirQuality/OutdoorAirQuality.ino
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ void setup() {

// Enable outdoor air quality sensor (disabled by default)
// Please note that it may take some time for the sensor to deliver the first data
outdoorAirQualitySensor.setMode(OutdoorAirQualitySensorMode::outdoorAirQuality);
// Use setEnabled(true, true) make the change persistent
outdoorAirQualitySensor.setEnabled(true);
displaySensorData(outdoorAirQualitySensor);

// Optionally disable the sensor
Expand Down
3 changes: 1 addition & 2 deletions examples/RGBLED/RGBLED.ino
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ void pulseLED(RGBLED &led) {
}

void pulseColors(RGBLED &led) {
// Brightness can also be set via 4th struct element
led.setColor({255, 0, 0}, 255); // Red
led.setColor({255, 0, 0}); // Red
pulseLED(led);

// Color can be set via Color struct or 3 separate uint8_t values
Expand Down
4 changes: 2 additions & 2 deletions examples/UARTRead/UARTRead.ino
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
*
* NiclaSenseEnv device;
* device.begin();
* device.setUARTCSVOutputEnabled(true);
* device.storeSettingsInFlash() # Store the settings so they are not lost after a reset
* The second parameter ensures that the settings are not lost after a reset
* device.setUARTCSVOutputEnabled(true, true);
*
* Initial author: Sebastian Romero ([email protected])
*
Expand Down
19 changes: 19 additions & 0 deletions src/I2CDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,25 @@ I2CDevice::I2CDevice(TwoWire& bus, uint8_t deviceAddress)
I2CDevice::I2CDevice(uint8_t deviceAddress)
: bus(Wire), i2cDeviceAddress(deviceAddress) {}

bool I2CDevice::persistRegister(RegisterInfo registerInfo){
writeToRegister(DEFAULTS_REGISTER_INFO, registerInfo.address | (1 << 7));

// Read bit 7 to check if the write is complete. When the write is complete, bit 7 will be 0.
// Try 10 times with increasing delay between each try
for (int i = 0; i < 10; ++i) {
uint8_t defaultsRegisterData = readFromRegister<uint8_t>(DEFAULTS_REGISTER_INFO);
if (!(defaultsRegisterData & (1 << 7))) {
return true;
}
// Even a value of 1 us seems to work, but we start with 100 us to be safe.
Serial.println("⌛️ Waiting for flash write to complete...");
// Exponential sleep duration
delayMicroseconds(100 * (2 << i));
}

return false;
}

bool I2CDevice::connected() {
bus.beginTransmission(i2cDeviceAddress);
return bus.endTransmission() == 0;
Expand Down
7 changes: 7 additions & 0 deletions src/I2CDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,13 @@ class I2CDevice {
return bus.endTransmission() == 0;
}

/**
* @brief Makes the value of a given register persistent.
* @param registerInfo The register to make persistent.
* @return Whether or not the register value was successfully persisted.
*/
bool persistRegister(RegisterInfo registerInfo);

/**
* @brief Reference to the I2C bus used by the device.
*/
Expand Down
26 changes: 16 additions & 10 deletions src/IndoorAirQualitySensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,23 @@ IndoorAirQualitySensorMode IndoorAirQualitySensor::mode() {
return IndoorAirQualitySensorMode((data >> 1) & 7);
}

void IndoorAirQualitySensor::setMode(IndoorAirQualitySensorMode sensorMode) {
bool IndoorAirQualitySensor::setMode(IndoorAirQualitySensorMode sensorMode, bool persist) {
uint8_t currentRegisterData = readFromRegister<uint8_t>(STATUS_REGISTER_INFO);
uint8_t mode = static_cast<uint8_t>(sensorMode); // convert to numeric type

// Check if the existing value is already the same
if ((currentRegisterData & (7 << 1)) == (mode << 1)) {
return;
return true;
}
writeToRegister(STATUS_REGISTER_INFO, (currentRegisterData & ~(7 << 1)) | (mode << 1));
if(!writeToRegister(STATUS_REGISTER_INFO, (currentRegisterData & ~(7 << 1)) | (mode << 1))){
return false;
}

if(persist){
return persistRegister(STATUS_REGISTER_INFO);
}

return true;
}

String IndoorAirQualitySensor::modeString() {
Expand All @@ -85,13 +93,11 @@ bool IndoorAirQualitySensor::enabled() {
return mode() != IndoorAirQualitySensorMode::powerDown;
}

void IndoorAirQualitySensor::setEnabled(bool isEnabled) {
bool IndoorAirQualitySensor::setEnabled(bool isEnabled, bool persist) {
if (isEnabled == enabled()) {
return;
}
if (isEnabled) {
setMode(IndoorAirQualitySensorMode::defaultMode);
} else {
setMode(IndoorAirQualitySensorMode::powerDown);
return true;
}

auto mode = isEnabled ? IndoorAirQualitySensorMode::indoorAirQuality : IndoorAirQualitySensorMode::powerDown;
return setMode(mode, persist);
}
13 changes: 9 additions & 4 deletions src/IndoorAirQualitySensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ class IndoorAirQualitySensor : public I2CDevice {

/**
* @brief Set the mode of the IndoorAirQualitySensor.
* Call storeSettingsInFlash() on NiclaSenseEnv instance after changing the mode to make the change persistent.
*
* Note on cleaning mode:
* The cleaning mode performs a thermal cleaning cycle of the MOx element. It can eliminate some light pollution
Expand All @@ -117,8 +116,11 @@ class IndoorAirQualitySensor : public I2CDevice {
* For more accurate readings, use the default indoor air quality mode.
*
* @param sensorMode The mode to set. See the IndoorAirQualitySensorMode enum class for possible values.
* @param persist If true, the change will be saved to flash memory.
* When persist is true, the mode setting of OutdoorAirQualitySensor and TemperatureHumiditySensor will also be persisted.
* @return True if the mode was set successfully, false otherwise.
*/
void setMode(IndoorAirQualitySensorMode sensorMode);
bool setMode(IndoorAirQualitySensorMode sensorMode, bool persist = false);

/**
* @brief Get the mode as a string.
Expand All @@ -136,10 +138,13 @@ class IndoorAirQualitySensor : public I2CDevice {

/**
* @brief Set the sensor enabled or disabled.
* Call storeSettingsInFlash() on NiclaSenseEnv instance after changing the enabled state to make the change persistent.
* When the sensor is enabled after being disabled, the sensor will go back to the indoorAirQuality mode.
* @param isEnabled True to enable the sensor, false to disable it.
* @param persist If true, the change will be saved to flash memory.
* When persist is true, the mode setting of IndoorAirQualitySensor and TemperatureHumiditySensor will also be persisted.
* @return True if the the sensor was enabled successfully.
*/
void setEnabled(bool isEnabled);
bool setEnabled(bool isEnabled, bool persist = false);
};

#endif
81 changes: 61 additions & 20 deletions src/NiclaSenseEnv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ void NiclaSenseEnv::end() {
}
}

bool NiclaSenseEnv::storeSettingsInFlash() {
bool NiclaSenseEnv::persistSettings() {
uint8_t controlRegisterData = readFromRegister<uint8_t>(CONTROL_REGISTER_INFO);

writeToRegister(CONTROL_REGISTER_INFO, controlRegisterData | (1 << 7));
Expand Down Expand Up @@ -137,7 +137,7 @@ bool NiclaSenseEnv::restoreFactorySettings() {
boardControlRegisterData = readFromRegister<uint8_t>(CONTROL_REGISTER_INFO);

if ((boardControlRegisterData & (1 << 5)) == 0) {
return storeSettingsInFlash();
return persistSettings();
}
Serial.println("⌛️ Waiting for factory reset to complete...");
// Exponential sleep duration
Expand All @@ -152,81 +152,122 @@ int NiclaSenseEnv::UARTBaudRate() {
return baudRateMap[uartControlRegisterData];
}

void NiclaSenseEnv::setUARTBaudRate(int baudRate) {
bool NiclaSenseEnv::setUARTBaudRate(int baudRate, bool persist) {
int baudRateIndex = baudRateNativeValue(baudRate);
if (baudRateIndex == -1) {
return; // Baud rate not found
return false; // Baud rate not found
}

uint8_t uartControlRegisterData = readFromRegister<uint8_t>(UART_CONTROL_REGISTER_INFO);
if ((uartControlRegisterData & 7) == baudRateIndex) {
return; // Value is already the same
return true; // Value is already the same
}
writeToRegister(UART_CONTROL_REGISTER_INFO, (uartControlRegisterData & ~7) | baudRateIndex);
if(!writeToRegister(UART_CONTROL_REGISTER_INFO, (uartControlRegisterData & ~7) | baudRateIndex)){
return false;
}

if (persist) {
return persistRegister(UART_CONTROL_REGISTER_INFO);
}

return true;
}

bool NiclaSenseEnv::isUARTCSVOutputEnabled() {
uint8_t boardControlRegisterData = readFromRegister<uint8_t>(CONTROL_REGISTER_INFO);
return (boardControlRegisterData & (1 << 1)) != 0;
}

void NiclaSenseEnv::setUARTCSVOutputEnabled(bool enabled) {
bool NiclaSenseEnv::setUARTCSVOutputEnabled(bool enabled, bool persist) {
uint8_t boardControlRegisterData = readFromRegister<uint8_t>(CONTROL_REGISTER_INFO);
if ((boardControlRegisterData & 2) == static_cast<int>(enabled)) {
return; // Value is already the same
return true; // Value is already the same
}
if(!writeToRegister(CONTROL_REGISTER_INFO, (boardControlRegisterData & ~2) | (enabled << 1))){
return false;
}
writeToRegister(CONTROL_REGISTER_INFO, (boardControlRegisterData & ~2) | (enabled << 1));

if (persist) {
return persistRegister(CONTROL_REGISTER_INFO);
}

return true;
}

char NiclaSenseEnv::CSVDelimiter() {
uint8_t csvDelimiterRegisterData = readFromRegister<uint8_t>(CSV_DELIMITER_REGISTER_INFO);
return static_cast<char>(csvDelimiterRegisterData);
}

void NiclaSenseEnv::setCSVDelimiter(char delimiter) {
bool NiclaSenseEnv::setCSVDelimiter(char delimiter, bool persist) {
char currentDelimiter = CSVDelimiter();
if (currentDelimiter == delimiter) {
return; // Value is already the same
return true; // Value is already the same
}

// Define prohibited delimiters
const char prohibitedDelimiters[] = {'\r', '\n', '\\', '"', '\''};

for (auto prohibitedDelimiter : prohibitedDelimiters) {
if (delimiter == prohibitedDelimiter) {
return; // Delimiter is prohibited
return false; // Delimiter is prohibited
}
}

// Use ASCII code of the delimiter character
writeToRegister(CSV_DELIMITER_REGISTER_INFO, static_cast<uint8_t>(delimiter));
if(!writeToRegister(CSV_DELIMITER_REGISTER_INFO, static_cast<uint8_t>(delimiter))){
return false;
}

if (persist) {
return persistRegister(CSV_DELIMITER_REGISTER_INFO);
}

return true;
}

bool NiclaSenseEnv::isDebuggingEnabled() {
uint8_t boardControlRegisterData = readFromRegister<uint8_t>(CONTROL_REGISTER_INFO);
return (boardControlRegisterData & 1) != 0;
}

void NiclaSenseEnv::setDebuggingEnabled(bool enabled) {
bool NiclaSenseEnv::setDebuggingEnabled(bool enabled, bool persist) {
uint8_t boardControlRegisterData = readFromRegister<uint8_t>(CONTROL_REGISTER_INFO);
if ((boardControlRegisterData & 1) == static_cast<int>(enabled)) {
return; // Value is already the same
return true; // Value is already the same
}
if(!writeToRegister(CONTROL_REGISTER_INFO, (boardControlRegisterData & ~1) | enabled)){
return false;
}
writeToRegister(CONTROL_REGISTER_INFO, (boardControlRegisterData & ~1) | enabled);

if(persist){
return persistRegister(CONTROL_REGISTER_INFO);
}

return true;
}

void NiclaSenseEnv::setDeviceAddress(int address) {
bool NiclaSenseEnv::setDeviceAddress(int address, bool persist) {
if (address < 0 || address > 127) {
return; // Invalid address
return false; // Invalid address
}
uint8_t addressRegisterData = readFromRegister<uint8_t>(SLAVE_ADDRESS_REGISTER_INFO);
// Check bits 0 - 6
if ((addressRegisterData & 127) == address) {
return; // Value is already the same
return true; // Value is already the same
}
writeToRegister(SLAVE_ADDRESS_REGISTER_INFO, (addressRegisterData & ~127) | address);
if(!writeToRegister(SLAVE_ADDRESS_REGISTER_INFO, (addressRegisterData & ~127) | address)){
return false;
}

delayMicroseconds(100); // Wait for the new address to take effect
this->i2cDeviceAddress = address;

if (persist) {
return persistRegister(SLAVE_ADDRESS_REGISTER_INFO);
}

return true;
}

// Function to get the index for a given baud rate
Expand Down
Loading

0 comments on commit 6b52463

Please sign in to comment.