Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for new defaults register #3

Merged
merged 8 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading