Skip to content
Open
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
207 changes: 83 additions & 124 deletions examples/UARTRead/UARTRead.ino
Original file line number Diff line number Diff line change
Expand Up @@ -26,153 +26,112 @@
*
*/

#include <map>
#include <vector>
#include <tuple>

constexpr char DEFAULT_DELIMITER = ',';

std::map<u_int8_t, std::tuple<String, String>> csvFieldMapping = {
{0, {"HS4001 sample counter", "uint32"}},
{1, {"HS4001 temperature (degC)", "float"}},
{2, {"HS4001 humidity (%RH)", "float"}},
{3, {"ZMOD4510 status", "uint8"}},
{4, {"ZMOD4510 sample counter", "uint32"}},
{5, {"ZMOD4510 EPA AQI", "uint16"}},
{6, {"ZMOD4510 Fast AQI", "uint16"}},
{7, {"ZMOD4510 O3 (ppb)", "float"}},
{8, {"ZMOD4510 NO2 (ppb)", "float"}},
{9, {"ZMOD4510 Rmox[0]", "float"}},
{10, {"ZMOD4510 Rmox[1]", "float"}},
{11, {"ZMOD4510 Rmox[2]", "float"}},
{12, {"ZMOD4510 Rmox[3]", "float"}},
{13, {"ZMOD4510 Rmox[4]", "float"}},
{14, {"ZMOD4510 Rmox[5]", "float"}},
{15, {"ZMOD4510 Rmox[6]", "float"}},
{16, {"ZMOD4510 Rmox[7]", "float"}},
{17, {"ZMOD4510 Rmox[8]", "float"}},
{18, {"ZMOD4510 Rmox[9]", "float"}},
{19, {"ZMOD4510 Rmox[10]", "float"}},
{20, {"ZMOD4510 Rmox[11]", "float"}},
{21, {"ZMOD4510 Rmox[12]", "float"}},
{22, {"ZMOD4410 status", "uint8"}},
{23, {"ZMD4410 sample counter", "uint32"}},
{24, {"ZMOD4410 IAQ", "float"}},
{25, {"ZMOD4410 TVOC (mg/m^3)", "float"}},
{26, {"ZMOD4410 eCO2 (ppm)", "float"}},
{27, {"ZMOD4410 Rel IAQ", "float"}},
{28, {"ZMOD4410 EtOH (ppm)", "float"}},
{29, {"ZMOD4410 Rmox[0]", "float"}},
{30, {"ZMOD4410 Rmox[1]", "float"}},
{31, {"ZMOD4410 Rmox[2]", "float"}},
{32, {"ZMOD4410 Rmox[3]", "float"}},
{33, {"ZMOD4410 Rmox[4]", "float"}},
{34, {"ZMOD4410 Rmox[5]", "float"}},
{35, {"ZMOD4410 Rmox[6]", "float"}},
{36, {"ZMOD4410 Rmox[7]", "float"}},
{37, {"ZMOD4410 Rmox[8]", "float"}},
{38, {"ZMOD4410 Rmox[9]", "float"}},
{39, {"ZMOD4410 Rmox[10]", "float"}},
{40, {"ZMOD4410 Rmox[11]", "float"}},
{41, {"ZMOD4410 Rmox[12]", "float"}},
{42, {"ZMOD4410 Rcda[0]", "float"}},
{43, {"ZMOD4410 Rcda[1]", "float"}},
{44, {"ZMOD4410 Rcda[2]", "float"}},
{45, {"ZMOD4410 Rhtr", "float"}},
{46, {"ZMOD4410 Temp", "float"}},
{47, {"ZMOD4410 intensity", "float"}},
{48, {"ZMOD4410 odor", "uint8"}}
};

std::map<String, String> parsedValuesMap;

// Function to convert a string to a float, handling exponents
float parseFloatWithExponent(const String &str) {
// Convert the string to a double
double value = str.toDouble();

// Convert the double to a float
return static_cast<float>(value);
}
#include "NiclaSenseEnvSerial.h"

// Function to process a CSV line
void processCSVLine(String data, char delimiter, std::map<String, String> &targetMap) {
// Skip lines that start with INFO: or WARNING:
if (data.startsWith("INFO:") || data.startsWith("WARNING:")) {
return;
NiclaSenseEnvSerial niclaSerial(Serial1);

void setup() {
Serial.begin(115200);
niclaSerial.begin();

while (!Serial) {
delay(100);
}

// Print the error message if the line starts with ERROR:
if (data.startsWith("ERROR:")) {
Serial.println(data);
Serial.println("Serial ports initialized");
}

void loop() {
bool updated = niclaSerial.update();

if (!updated) {
String err = niclaSerial.lastErrorMessage();
if (err.length() > 0) {
Serial.print("Error: ");
Serial.println(err);
}
delay(100);
return;
}

// Split CSV line into fields
std::vector<String> fields;
size_t pos = 0;
while ((pos = data.indexOf(delimiter)) != -1) {
fields.push_back(data.substring(0, pos));
data = data.substring(pos + 1);
float temperature = niclaSerial.temperature();
if (!isnan(temperature)) {
Serial.print("🌡 Temperature (°C): ");
Serial.println(temperature);
}
fields.push_back(data); // Last field

// Map fields to their corresponding names and store in parsedValuesMap
for (size_t i = 0; i < fields.size(); ++i) {
// Use index as key to get tuple (name, type)
auto [name, type] = csvFieldMapping[i];
String fieldValue = fields[i];
float humidity = niclaSerial.humidity();
if (!isnan(humidity)) {
Serial.print("💧 Humidity (%RH): ");
Serial.println(humidity);
}

// Check if the field is empty
if (fieldValue == "") {
continue;
}
int epaAqi = niclaSerial.outdoorAirQualityIndex();
if (epaAqi >= 0) {
Serial.print("🏭 Outdoor EPA AQI: ");
Serial.println(epaAqi);
Serial.print("🏭 Outdoor EPA AQI interpreted: ");
Serial.println(niclaSerial.outdoorAirQualityIndexInterpreted());
}

// Check if the field is a float based on the "type" property
if (type == "float") {
float floatValue = parseFloatWithExponent(fieldValue);
targetMap[name] = String(floatValue);
} else {
targetMap[name] = fieldValue;
}
int fastAqi = niclaSerial.outdoorFastAirQualityIndex();
if (fastAqi >= 0) {
Serial.print("🏭 Outdoor Fast AQI: ");
Serial.println(fastAqi);
}
}

void setup(){
Serial.begin(115200);
Serial1.begin(38400, SERIAL_8N1);
float o3 = niclaSerial.O3();
if (!isnan(o3)) {
Serial.print("🌬 Outdoor O3 (ppb): ");
Serial.println(o3);
}

while (!Serial || !Serial1) {
delay(100);
float no2 = niclaSerial.NO2();
if (!isnan(no2)) {
Serial.print("🌬 Outdoor NO2 (ppb): ");
Serial.println(no2);
}

Serial.println("Serial ports initialized");
}
float iaq = niclaSerial.indoorAirQuality();
if (!isnan(iaq)) {
Serial.print("🏠 Indoor IAQ: ");
Serial.println(iaq);
Serial.print("🏠 Indoor IAQ interpreted: ");
Serial.println(niclaSerial.indoorAirQualityInterpreted());
}

float relIaq = niclaSerial.indoorRelativeAirQuality();
if (!isnan(relIaq)) {
Serial.print("🏠 Indoor relative IAQ: ");
Serial.println(relIaq);
}

void loop() {
if (!Serial1.available()) {
delay(100);
return;
float co2 = niclaSerial.CO2();
if (!isnan(co2)) {
Serial.print("🌬 Indoor eCO2 (ppm): ");
Serial.println(co2);
}

String csvLine = Serial1.readStringUntil('\n');
processCSVLine(csvLine, DEFAULT_DELIMITER, parsedValuesMap);
float tvoc = niclaSerial.TVOC();
if (!isnan(tvoc)) {
Serial.print("🌬 Indoor TVOC (mg/m^3): ");
Serial.println(tvoc);
}

// If map is empty, there was no data to parse
if (parsedValuesMap.empty()) {
Serial.println("No data to parse.");
return;
float ethanol = niclaSerial.ethanol();
if (!isnan(ethanol)) {
Serial.print("🍺 Ethanol (ppm): ");
Serial.println(ethanol);
}

// Print parsed values in the loop
for (const auto &entry : parsedValuesMap) {
Serial.print(entry.first + ": ");
Serial.println(entry.second);
float odorIntensity = niclaSerial.odorIntensity();
if (!isnan(odorIntensity)) {
Serial.print("👃 Odor intensity: ");
Serial.println(odorIntensity);
}

Serial.println();
Serial.print("👃 Sulfur odor: ");
Serial.println(niclaSerial.sulfurOdor() ? "detected" : "not detected");

// Clear the map for the next iteration
parsedValuesMap.clear();
Serial.println();
}
Loading