Maker.io main logo

Smart Air Quality Monitor for 3D Printers Using Arduino Uno R4 WiFi

143

2025-07-09 | By Nate_Larson

License: See Original Project Air Quality Carbon Dioxide (CO2) Environmental Gas Arduino

As a Home Assistant user, one of the things I like to track in my home is air quality. I wrote previously about this, using the Sensirion SCD30 carbon dioxide sensor to monitor CO2 levels and automate ventilation. However, CO2 is only one aspect of air quality – there are other factors to track as well, such as dust (aka particulate), volatile organic compounds (VOCs), and more. Recently, Sensirion released their new SEN6x series of sensors, which combines sensors for all of this, and more, into a single unit, perfect for home automation!

home_1

table_2

The SEN6x series of sensors communicate via I2C, making it easy to work with. While most of my connected homebrew sensor devices are created using ESPHome, as of this time, this sensor is not yet officially supported, so I decided to create my own using Arduino, MQTT, and Sensirion’s Arduino library for the SEN66 sensor.

While working on this project, I came across the popular HALO project, which is a smart air quality monitor for 3D printing areas. Inspired by this project, I realized I could achieve similar results with a simpler BOM (bill of materials) by adding the DFRobot MiCS-4514 Gravity board to my build. This would be perfect for my home lab, where I work on my electronics projects, since they often involve soldering, 3D printing, or the use of adhesives or other chemicals. Plus, with the boards and sensor selected, this project is completely solder-free.

sensors_3

I also found a great resource for adding Home Assistant discovery to DIY MQTT devices – this allows the server to automatically discover the device and set up the device in Home Assistant, which is a great alternative to having to manually set up the device and all its entities in Home Assistant’s configuration.YAML file!

resource_4

Follow along to create your own smart air quality monitor designed to oversee your 3D printer workspace. This project measures particles, gases, and overall air quality, with the data published via MQTT to your Home Assistant server using native auto-discovery. This feature allows you to monitor your environment without additional integration steps, providing opportunities to automate your air quality control.

monitor_5

Features

  • Uses WiFi and MQTT to communicate with Home Assistant

  • Tracks PM1.0, PM2.5, PM4.0, PM10, Temperature, Humidity, VOC, NOx, CO2, and multiple gas types

  • Automatically integrates into Home Assistant (HA) via MQTT discovery

  • Built-in watchdog timer to ensure system reliability

Parts List

Wiring Instructions

1. SEN66 Sensor:

  • Using the ACES 51452-006H0H0-001 to JST SHR-04V-S cable included in the SEK-SEN66 eval kit, plug the cable into the sensor and the Uno R4 WiFi's onboard Qwiic port.

wiring_6

2. MiCS-4514 Sensor (SEN0377):

  • Connect the included Gravity cable to the SEN0377 board.

  • Use male-to-male jumper wires or male headers to connect to the Uno's I2C headers:

    • SDA to SDA

    • SCL to SCL

    • VCC to 5V

    • GND to GND

sensor_7

Software Setup

1. Libraries Required (Install via Library Manager or GitHub):

2. Create arduino_secrets.h File

This file holds your WiFi and MQTT credentials securely. Place it in the same directory as your .ino file.

#define SECRET_SSID "YourWiFiSSID"

#define SECRET_WIFI_PASSWORD "YourWiFiPassword"

#define SECRET_MQTT_BROKER_ADDRESS "192.168.1.X" // Your HA IP if running Mosquitto on your HA server. Note: Local domain names (e.g., "Computer.local" on OSX) are not supported by Arduino. You need to set the IP address directly.

#define SECRET_MQTT_USERNAME "mqtt_user"

#define SECRET_MQTT_PASSWORD "mqtt_pass"

#define SECRET_MQTT_CLIENT_ID "YourDeviceName"

3. MQTT Broker Setup in HA

Ensure the Mosquitto broker is installed via the Home Assistant add-ons store, properly configured, and running. No manual MQTT sensor configuration is needed due to the project’s use of Home Assistant auto-discovery.

setup_8

Uploading and Running

1. Plug your Uno R4 WiFi into your PC.

2. Open the project in the Arduino IDE.

3. Ensure the correct board (Uno R4 WiFi) and port are selected.

4. Upload the code found at the end of this project.

5. Open Serial Monitor for debug output (baud: 115200). If you don’t see anything in the Serial Monitor, ensure you have enabled debugging in the configuration section of the code.

run_9

Home Assistant Integration

Once the device is online and MQTT is working, you should see sensors appear in Home Assistant automatically. No YAML editing or MQTT sensor creation is required.

assist_10

Calibration Tips (MiCS Sensor)

  • The MiCS-4514 requires a 3-minute warm-up on power-up. During this warmup period, you will see the Arduino’s onboard user LED blink on and off once a second.

  • Run the board for 24–48 hours for the best baseline calibration.

  • For environments with fluctuating air quality, allow longer stabilization periods before trusting readings.

Troubleshooting

WiFi Issues:

  • Ensure SSID/password are correct.

  • Retry limit is built-in; check debug output via Serial Monitor.

MQTT Issues:

  • Confirm MQTT broker is reachable from Uno (check IP/firewall).

  • Use MQTT Explorer or similar to verify topic publishing.

Sensor Issues:

  • Ensure correct I2C wiring.

  • Check address conflicts on the I2C bus.

HA Discovery Fails:

  • Restart MQTT and HA server.

  • Ensure auto_discovery flag in the code is set to true.

Code Concepts Overview

State Machine

The code uses a clean finite state machine (FSM) for reliability. Each critical step (WiFi connection, MQTT setup, sensor initialization, discovery, and normal operation) is managed sequentially. You can learn more about this approach to coding here: How to Program an Arduino Finite State Machine.

Watchdog Timer

A watchdog timer resets the board if any part of the FSM hangs. The Uno R4 has a max watchdog interval of ~5.5 seconds, so the refreshWatchdog() function is used in long loops and retries. Note that due to the time required for the board to make the initial connection to the MQTT broker and the library’s blocking function during this process, the watchdog timer is not started until after the WiFi and MQTT connections are made.

Debug Macro

The code uses a macro for serial debug messages that can easily be disabled once debugging is complete, allowing for more efficient, streamlined code execution. You can learn more about this here: How to Streamline Your Arduino Code: Avoid Using Serial Print Commands.

Next Steps

While this project is functionally complete, I still plan to utilize the Arduino board’s LED matrix for a local snapshot view of air quality. Additionally, I will be designing and printing an enclosure for this project.

Final Thoughts

This project offers a flexible and HA-friendly, extensive air quality monitor that can integrate with your home automation setup.

Project Code:

Copy Code
#include <Arduino.h>

#include <SensirionI2cSen66.h> // For SEN66 air quality sensor: https://github.com/Sensirion/arduino-i2c-sen66

#include <Wire.h> // I2C communication

#include <MQTT.h> // Gilberto Conti's MQTT Library: https://github.com/256dpi/arduino-mqtt

#include "arduino_secrets.h" // WiFi/MQTT credentials stored separately

#include <ArduinoJson.h> // JSON handling (for Home Assistant discovery)

#include <WiFi.h> // WiFi Library (auto-selects appropriate functions for the board being used)

#include "DFRobot_MICS.h" // DFRobot MiCS gas sensor support Library: https://github.com/DFRobot/DFRobot_MICS

#include <WDT.h> // Watchdog Timer library

//---------------- Configuration ----------------

bool auto_discovery = true; // Enable/disable Home Assistant auto-discovery (Courtesy of https://resinchemtech.blogspot.com/2023/12/mqtt-auto-discovery.html)

const long interval = 5000; // Sensor polling interval (milliseconds)

#define DEBUG 0 // Enable debug output (set to 1 to enable, 0 to disable)

//-----------------------------------------------

// Device state machine

enum DeviceState {

STATE_INIT,

STATE_WIFI_CONNECT,

STATE_MQTT_CONNECT,

STATE_WDT_START,

STATE_DISCOVERY,

STATE_SENSOR_SETUP,

STATE_RUNNING,

STATE_ERROR

};

DeviceState currentState = STATE_INIT;

//------------- Debug Macros ---------------

#if DEBUG

#define outputDebug(x) Serial.print(x)

#define outputDebugLine(x) Serial.println(x)

#define outputDebugF(...) { char debugbuf[80]; snprintf(_debug_buf, sizeof(_debug_buf), __VA_ARGS__); Serial.print(_debug_buf); }

#define outputDebugLineF(...) { char debugbuf[80]; snprintf(_debug_buf, sizeof(_debug_buf), __VA_ARGS__); Serial.println(_debug_buf); }

#else

#define outputDebug(x)

#define outputDebugLine(x)

#define outputDebugF(...)

#define outputDebugLineF(...)

#endif

// Constants

#define NO_ERROR 0

#define MAX_RETRY_ATTEMPTS 5

#define CALIBRATION_TIME 3 // MiCS-4514 calibration time in minutes (default: 3 minutes)

// Sensor validation ranges

#define PM_MIN 0.0

#define PM_MAX 1000.0

#define HUMIDITY_MIN 0.0

#define HUMIDITY_MAX 100.0

#define TEMP_MIN -10.0

#define TEMP_MAX 40.0

#define VOC_MIN 0.0

#define VOC_MAX 500.0

#define NOX_MIN 0.0

#define NOX_MAX 500.0

#define CO2_MIN 0

#define CO2_MAX 40000

#define CO_MIN 0.0

#define CO_MAX 1000.0

#define CH4_MIN 0.0

#define CH4_MAX 25000.0

#define C2H5OH_MIN 0.0

#define C2H5OH_MAX 500.0

#define H2_MIN 0.0

#define H2_MAX 1000.0

#define NH3_MIN 0.0

#define NH3_MAX 500.0

#define NO2_MIN 0.0

#define NO2_MAX 10.0

// MiCS gas sensor I2C address

#define Mics_I2C_ADDRESS MICS_ADDRESS_0

DFRobot_MICS_I2C mics(&Wire, Mics_I2C_ADDRESS);

// Global instances

WiFiClient wifiClient;

MQTTClient client(512); // MQTT buffer size

SensirionI2cSen66 sensor; // SEN66 sensor instance

// Sensor metadata arrays

const char* sensorName[] = {

"PM1.0", "PM2.5", "PM4.0", "PM10", "Humidity", "Temperature",

"VOC Index", "NOx Index", "Methane", "Ethanol", "Hydrogen",

"Ammonia", "Carbon Monoxide", "Nitrogen Dioxide", "CO2"

};

const char* deviceClass[] = { // https://www.home-assistant.io/integrations/sensor#device-class

"pm1", "pm25", "", "pm10", "humidity", "temperature",

"", "", "", "", "",

"", "carbon_monoxide", "nitrogen_dioxide", "carbon_dioxide"

};

const char* UofM[] = {

"µg/m³", "µg/m³", "µg/m³", "µg/m³", "%", "°C",

"", "", "ppm", "ppm", "ppm",

"ppm", "ppm", "ppm", "ppm"

};

const float minRanges[] = {

PM_MIN, PM_MIN, PM_MIN, PM_MIN, HUMIDITY_MIN, TEMP_MIN,

VOC_MIN, NOX_MIN, CH4_MIN, C2H5OH_MIN, H2_MIN,

NH3_MIN, CO_MIN, NO2_MIN

};

const float maxRanges[] = {

PM_MAX, PM_MAX, PM_MAX, PM_MAX, HUMIDITY_MAX, TEMP_MAX,

VOC_MAX, NOX_MAX, CH4_MAX, C2H5OH_MAX, H2_MAX,

NH3_MAX, CO_MAX, NO2_MAX

};

// MiCS gas sensor codes

const uint8_t MiCSgases[] = {0x01, 0x02, 0x03, 0x06, 0x08, 0x0A};

// Time and state tracking

unsigned long previousMillis = 0;

static char devUniqueID[30]; // Unique MQTT discovery device ID

static int16_t error; // Sensor error tracking

uint8_t errorCount = 0;

byte mac[6]; // MAC address for unique ID

const long wdtInterval = 5592; // Watchdog interval (Uno R4 max is 5592)

bool isWatchdogActive = false; // Flag for watchdog status

// Sensor value buffers

float SensorVal[14]; // All values except CO2

uint16_t CO2sensorVal; // CO2 requires uint16_t

// Credentials (stored separately)

const char ssid[] = SECRET_SSID;

const char pass[] = SECRET_WIFI_PASSWORD;

const char broker[] = SECRET_MQTT_BROKER_ADDRESS;

const char mqttClientId[] = SECRET_MQTT_CLIENT_ID;

const char mqttUsername[] = SECRET_MQTT_USERNAME;

const char mqttPassword[] = SECRET_MQTT_PASSWORD;

//--------------- WiFi Setup ---------------

/**

* Connect to WiFi with retries and backoff

* @return true if connected, false otherwise

*/

bool connectWiFi() {

outputDebugLine("Connecting to WiFi...");

WiFi.disconnect(); // Disconnect if currently connected to avoid hanging

delay(100);

int retryCount = 0;

while (WiFi.status() != WL_CONNECTED && retryCount < MAX_RETRY_ATTEMPTS) {

WiFi.begin(ssid, pass);

unsigned long connectTimeout = millis() + 10000; // Wait up to 10 seconds for connection

while (WiFi.status() != WL_CONNECTED && millis() < connectTimeout) {

outputDebug(".");

delay(500);

refreshWatchdog();

}

if (WiFi.status() != WL_CONNECTED) {

retryCount++;

int backoffTime = min(1000 * retryCount, 5000); // Exponential backoff capped at 5 seconds

outputDebugF("WiFi connection attempt %d failed. Retrying in %d ms...\n", retryCount, backoffTime);

delay(backoffTime);

refreshWatchdog();

}

}

if (WiFi.status() == WL_CONNECTED) {

outputDebugF("WiFi connected. IP: %s, RSSI: %d dBm\n",

WiFi.localIP().toString().c_str(), WiFi.RSSI());

WiFi.macAddress(mac);

return true;

} else {

outputDebugLine("WiFi connection failed after multiple attempts.");

return false;

}

}

//--------------- MQTT Setup ---------------

/**

* Connect to the MQTT broker with retry

* @return true if connected, false otherwise

*/

bool connectMQTT() {

client.setKeepAlive(60); // Ping the broker every 60 seconds

client.onMessage(messageReceived);

client.begin(broker, wifiClient); // Initialize the MQTT client with the WiFi connection

outputDebug("Connecting to MQTT...");

int retryCount = 0;

refreshWatchdog();

while (!client.connect(mqttClientId, mqttUsername, mqttPassword) &&

retryCount < MAX_RETRY_ATTEMPTS) {

outputDebug(".");

retryCount++;

int backoffTime = min(1000 * retryCount, 5000); // Exponential backoff capped at 5 seconds

delay(backoffTime);

refreshWatchdog();

}

if (client.connected()) {

outputDebugLine("MQTT connected.");

return true;

} else {

outputDebugLine("MQTT connection failed after multiple attempts.");

return false;

}

}

//--------- Connection Management ----------

/**

* Checks connection status of WiFi and MQTT

* and attempts to reconnect if needed

* @return true if connected, false otherwise

*/

bool checkConnections() {

static unsigned long lastReconnectAttempt = 0;

bool reconnected = false;

// Check WiFi connection

if (WiFi.status() != WL_CONNECTED || WiFi.RSSI() < -80) {

outputDebugLine("WiFi connection lost or poor signal. Reconnecting...");

if (connectWiFi()) {

reconnected = true;

} else {

return false;

}

}

// Check MQTT connection

if (!client.connected()) {

outputDebugLine("MQTT connection lost. Reconnecting...");

if (connectMQTT()) {

reconnected = true;

} else {

return false;

}

}

// If we reconnected, reinitiate discovery

if (reconnected) {

haDiscovery();

}

client.loop(); // Process any incoming messages

return true;

}

//--------- MQTT Message Callback ----------

/**

* Handles incoming MQTT messages

*/

void messageReceived(String &topic, String &payload) {

outputDebugF("Incoming: %s - %s\n", topic.c_str(), payload.c_str());

// Parse commands if needed

if (topic.endsWith("/command")) {

if (payload == "reset") {

outputDebugLine("Reset command received. Restarting device...");

delay(100);

NVIC_SystemReset();

}

}

}

//----------- Unique ID Creation -----------

/**

* Generates the unique device ID for MQTT discovery topics

*/

void createDiscoveryUniqueID() {

strcpy(devUniqueID, mqttClientId);

for (int i = 0; i < 3; i++) {

sprintf(&devUniqueID[strlen(devUniqueID)], "%02X", mac[5 - i]);

}

outputDebugF("Generated Unique ID: %s\n", devUniqueID);

}

//------- Home Assistant Discovery ---------

/**

* Publishes MQTT discovery config payloads for Home Assistant auto-discovery

*/

void haDiscovery() {

char disc_topic[128];

char buffer[512];

for (int i = 0; i < 15; i++) {

snprintf(disc_topic, sizeof(disc_topic), "homeassistant/sensor/%s%d/config", devUniqueID, i);

if (auto_discovery) {

outputDebugF("Adding %s Sensor\n", sensorName[i]);

// Use StaticJsonDocument instead of DynamicJsonDocument to avoid heap fragmentation

StaticJsonDocument<512> doc;

doc["name"] = sensorName[i];

doc["unique_id"] = String(devUniqueID) + String(i);

if (strlen(deviceClass[i]) > 0) {

doc["device_class"] = deviceClass[i];

}

// State topic

char stateTopic[80];

snprintf(stateTopic, sizeof(stateTopic), "%s/%s", devUniqueID, sensorName[i]);

doc["state_topic"] = stateTopic;

doc["unit_of_measurement"] = UofM[i];

// Add the device object

JsonObject device = doc.createNestedObject("device");

device["identifiers"] = devUniqueID;

device["name"] = mqttClientId;

device["manufacturer"] = "DigiKey";

device["model"] = "Arduino Uno R4 + SEN66";

// Serialize and publish

size_t len = serializeJson(doc, buffer);

if (!client.publish(disc_topic, buffer, len)) {

outputDebugF("Failed to publish discovery for %s\n", sensorName[i]);

}

} else {

client.publish(disc_topic, ""); // Clear discovery

}

delay(50); // Small delay between discovery messages

refreshWatchdog();

}

outputDebugLine("Device discovery complete.");

}

//------------ Sensor Reading ---------------

/**

* Reads air quality data from the SEN66 and MiCS sensors.

* @return true if all data successfully read and sensor

* readings are in correct range, false if an error occurred.

*/

bool readSensorData() {

bool success = true;

// Read SEN66 sensor values

error = sensor.readMeasuredValues(

SensorVal[0], SensorVal[1], SensorVal[2], SensorVal[3],

SensorVal[4], SensorVal[5], SensorVal[6], SensorVal[7],

CO2sensorVal

);

if (error != NO_ERROR) {

outputDebugLine("Error reading sensor values.");

success = false;

}

// Read MiCS-4514 sensor values

for (int i = 8; i < 14; i++) {

int j = (i-8);

SensorVal[i] = mics.getGasData(MiCSgases[j]);

}

// Validate Sesnor readings

for (int i = 0; i < 14; i++) {

if (SensorVal[i] < minRanges[i] || SensorVal[i] > maxRanges[i]) {

outputDebugF("Invalid %s reading: %.2f (outside range)\n", sensorName[i], SensorVal[i]);

success = false;

}

}

// Validate CO2 reading

if (CO2sensorVal < CO2_MIN || CO2sensorVal > CO2_MAX) {

outputDebugF("Invalid CO2 reading: %d (outside range)\n", CO2sensorVal);

success = false;

}

return success;

}

//---------- MQTT Publishing ---------------

/**

* Publishes sensor readings to MQTT topics

*/

void publishSensorData() {

char topic[80];

char valueStr[20];

// Publish float values (SEN66 + MiCS gases)

for (int i = 0; i < 14; i++) {

if (!isnan(SensorVal[i])) {

dtostrf(SensorVal[i], 1, 2, valueStr); // Format value string

snprintf(topic, sizeof(topic), "%s/%s", devUniqueID, sensorName[i]); // Format topic string

outputDebugF("%s: %s %s ", sensorName[i], valueStr, UofM[i]); // Debug output

if (!client.publish(topic, valueStr)) {

outputDebugF("Failed to publish %s\n", sensorName[i]);

}

}

delay(10); // Avoid flooding the broker

refreshWatchdog();

}

// Publish CO2 separately (uint16_t)

snprintf(topic, sizeof(topic), "%s/%s", devUniqueID, sensorName[14]);

snprintf(valueStr, sizeof(valueStr), "%d", CO2sensorVal);

outputDebugF("%s: %s %s\n", sensorName[14], valueStr, UofM[14]);

if (!client.publish(topic, valueStr)) {

outputDebugLine("Failed to publish CO2 data");

}

}

//--------- Watchdog Handling --------------

/**

* Initializes the watchdog timer

*/

void setupWatchdog() {

if (!isWatchdogActive) {

outputDebugLine("Starting watchdog...");

WDT.begin(wdtInterval);

isWatchdogActive = true;

}

}

/**

* Refreshes (resets) the watchdog timer

* to prevent unwanted device reset

*/

void refreshWatchdog() {

if (isWatchdogActive) {

WDT.refresh();

}

}

//-------- MiCS-4514 Initialization --------

/**

* Initializes the MiCS sensor

* @return true if successful, false if sensor fails to initialize

*/

bool initializeMiCS() {

int retryCount = 0;

// Attempt to connect to MiCS sensor

while (!mics.begin() && retryCount < MAX_RETRY_ATTEMPTS) {

outputDebugLine("NO MiCS-4514 detected!");

retryCount++;

delay(1000);

refreshWatchdog();

}

if (retryCount >= MAX_RETRY_ATTEMPTS) {

outputDebugLine("Failed to connect to MiCS-4514 after multiple attempts");

return false;

}

outputDebugLine("MiCS-4514 connected successfully!");

// Wake up the sensor if it's in sleep mode

uint8_t mode = mics.getPowerState();

if (mode == SLEEP_MODE) {

mics.wakeUpMode();

outputDebugLine("Wake up MiCS-4514 success!");

} else {

outputDebugLine("The MiCS-4514 is already awake");

}

/**

* Do not touch the sensor probe when preheating the sensor.

* Place the sensor in clean air.

* The default calibration time is 3 minutes.

*/

outputDebugLine("Please wait until the warm-up time is over!");

while(!mics.warmUpTime(CALIBRATION_TIME)){

// Perform warm-up with visual feedback

for (int minute = 1; minute <= CALIBRATION_TIME; minute++) {

outputDebugF("Warm-up: %d of %d minutes\n", minute, CALIBRATION_TIME);

// 60 second loop with LED blinks and WDT refreshes

for (int second = 0; second < 60; second++) {

digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // Blink LED during warm-up

delay(500);

digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // Blink LED during warm-up

delay(500);

refreshWatchdog();

}

}

}

digitalWrite(LED_BUILTIN, LOW); // Turn off LED after warm-up

return true;

}

//---------- SEN66 Initialization ----------

/**

* Initializes the SEN66 sensor

* @return true if successful, false if sensor fails to initialize

*/

bool initializeSEN66() {

Wire1.begin();

sensor.begin(Wire1, SEN66_I2C_ADDR_6B);

if (sensor.deviceReset() != NO_ERROR || sensor.startContinuousMeasurement() != NO_ERROR) {

outputDebugLine("SEN66 initialization failed.");

return false;

}

delay(100); // Give the sensor time to finish start up before taking any readings

refreshWatchdog();

outputDebugLine("SEN66 initialized successfully");

return true;

}

//------- State Machine Processing ---------

void processStateMachine() {

static unsigned long stateEnterTime = 0;

refreshWatchdog();

// Record time of state entry for timeout purposes

if (currentState != STATE_RUNNING) {

unsigned long now = millis();

if (stateEnterTime == 0) {

stateEnterTime = now;

} else if (now - stateEnterTime > 210000) { // 3.5 minute allowance for sensor initialization and connection

outputDebugLine("State timeout - restarting setup process");

currentState = STATE_INIT;

stateEnterTime = now;

}

}

// Process current state

switch (currentState) {

case STATE_INIT: {

outputDebugLine("State: INIT");

stateEnterTime = millis();

currentState = STATE_WIFI_CONNECT;

break;

}

case STATE_SENSOR_SETUP: { // Initialize sensors

outputDebugLine("State: SENSOR_SETUP");

if (!initializeSEN66() || !initializeMiCS()) {

outputDebugLine("Sensor initialization failed");

errorCount++;

if (errorCount > 3) {

currentState = STATE_ERROR;

}

delay(5000); // Wait before retry

refreshWatchdog();

} else {

errorCount = 0;

refreshWatchdog();

currentState = STATE_RUNNING;

stateEnterTime = 0; // Clear timeout when entering running state

}

break;

}

case STATE_WIFI_CONNECT: {

outputDebugLine("State: WIFI_CONNECT");

refreshWatchdog();

if (connectWiFi()) {

outputDebugLine("Wifi connection successful");

errorCount = 0;

currentState = STATE_MQTT_CONNECT;

} else {

outputDebugLine("WiFi connection failed");

errorCount++;

if (errorCount > 3) {

currentState = STATE_ERROR;

}

refreshWatchdog();

delay(1000); // Wait before retry

}

break;

}

case STATE_MQTT_CONNECT: {

outputDebugLine("State: MQTT_CONNECT");

createDiscoveryUniqueID();

if (connectMQTT()) {

errorCount = 0;

refreshWatchdog();

currentState = STATE_WDT_START;

} else {

outputDebugLine("MQTT connection failed");

errorCount++;

if (errorCount > 3) {

currentState = STATE_ERROR;

}

refreshWatchdog();

delay(1000); // Wait before retry

}

break;

}

case STATE_WDT_START: { // Initialize watchdog timer - this must be done after MQTT client connection due to library function being a long, blocking process

outputDebugLine("State: WDT START");

setupWatchdog();

currentState = STATE_DISCOVERY;

break;

}

case STATE_DISCOVERY: {

outputDebugLine("State: DISCOVERY");

haDiscovery();

errorCount = 0;

currentState = STATE_SENSOR_SETUP;

break;

}

case STATE_RUNNING: { // Main operational state

if (!checkConnections()) {

outputDebugLine("Connection check failed");

errorCount++;

if (errorCount > 3) {

currentState = STATE_WIFI_CONNECT;

stateEnterTime = millis();

}

delay(1000); // Wait before retry

refreshWatchdog();

break;

}

unsigned long currentMillis = millis();

if (currentMillis - previousMillis >= interval) {

previousMillis = currentMillis;

digitalWrite(LED_BUILTIN, HIGH); // Visual heartbeat

if (readSensorData()) {

publishSensorData();

errorCount = 0; // Reset error count on success

} else {

outputDebugLine("Sensor read failed");

errorCount++;

delay(500);

if (errorCount > 5) {

currentState = STATE_SENSOR_SETUP;

stateEnterTime = millis();

}

}

digitalWrite(LED_BUILTIN, LOW);

}

break;

}

case STATE_ERROR: { // Try to recover by resetting everything

outputDebugLine("State: ERROR - Performing recovery");

errorCount = 0;

currentState = STATE_INIT;

stateEnterTime = millis();

break;

}

}

}

//------------------ Setup -----------------

void setup() {

// Initialize onboard LED

pinMode(LED_BUILTIN, OUTPUT);

digitalWrite(LED_BUILTIN, HIGH);

// Initialize Serial for debugging

if (DEBUG) {

Serial.begin(115200);

while (!Serial && millis() < 3000); // Wait for serial port with timeout

delay(5000); // Wait a few seconds for PC to display serial connection

}

outputDebugLine("\n\n===== Environmental Monitor Starting =====");

currentState = STATE_INIT;

}

//----------------- Main Loop --------------

void loop() {

processStateMachine();

}
制造商零件编号 ABX00087
ARDUINO UNO R4 WIFI
Arduino
制造商零件编号 SEK-SEN66
EVALUATION KIT FOR THE SEN6X FAM
Sensirion AG
制造商零件编号 SEN0377
GRAVITY MEMS GAS SENSOR BOARD
DFRobot
Add all DigiKey Parts to Cart
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.