One V9 Compile Issue

Hello,

Using Chrome I’ve updated the V9 firmware to the latest version. Everything is working as it should. I’m trying to push the example firmware ONE_V9 using the Arduino IDE on Windows. My intent it to modify it to allow pull requests. But I’m running into an issue with the example version.

AppData\Local\Temp.arduinoIDE-unsaved20231125-3080-ke0sh9.wqc1\ONE_V9\ONE_V9.ino:34:10: fatal error: HTTPClient.h: No such file or directory
34 | #include <HTTPClient.h>
| ^~~~~~~~~~~~~~
compilation terminated.

exit status 1

Compilation error: HTTPClient.h: No such file or directory

I’ve tried adding various libraries using Manage Libraries with no luck. Would anyone know what library I should add? I’ve already added the listed libraries mentioned in the firmware comments.

Thank you

Hi everyone,
I am using Arduino IDE to compile the firmware of v9.ino and get the below error message.
Does anyone have any idea to fix it?

Thanks!

/private/var/folders/6_/05frmsx38d7jxd95rp2d1300000gn/T/.arduinoIDE-unsaved202404-2762-tntdgk.msom/sketch_jan4a/sketch_jan4a.ino: In function ‘void updatePm()’:
/private/var/folders/6
/_05frmsx38d7jxd95rp2d1300000gn/T/.arduinoIDE-unsaved202404-2762-tntdgk.msom/sketch_jan4a/sketch_jan4a.ino:275:26: error: ‘struct PMS::DATA’ has no member named ‘PM_RAW_0_3’
275 | pm03PCount = data1.PM_RAW_0_3;
| ^~~~~~~~~~

exit status 1

Compilation error: ‘struct PMS::DATA’ has no member named ‘PM_RAW_0_3’

Here is the screen capture of the error message that I got.

Make sure you installed all of the libraries/dependencies that are needed

Hi @MallocArray ,
Thanks for your comment, I already installed the all library of One v9.ino suggested. please refer to the capture screen. With the complied error of PMS, I already installed the "PMS Library by Mariusz Kacki
version 1.1.0 " I am not sure this library is correct for V9.ino or it is not needed which “Airgradient Air Quality Sensor V 2.4.12” is included.

Do you have all the library version needed for v9.ino?

Thanks!

We’ll need Achim to reply on the versions needed, but I saw another thread that talked a kit the same errors but this was for the Open Air Outdoor unit

Might try installing an older version of the PMS library just for fun

Edit: that post was about the indoor ONE v9 board, but the replies were relevant to the Outdoor unit, so probably not a relevant fix, but a user with the same error message you are getting

Hi @MallocArray , thanks for your updates. I agree your comments.
Yes, I am also compiling DIY in-door v3.7.ino and found the board library is not compatible of the latest Board library of ESP8266 that it cannot be use the version latest then v3.0.2
I may be ask Achim for that issue.

I followed these instructions to make it compile:

@bekoeppel
Those instructions are the for the Outdoor model which uses the PMS5003T

The original poster has an Indoor model with a PMS5003 without the Temp sensors built-in, so not sure how the instructions are related?

On the example code on the top in the comment section should be written all libraries that need to be installed.

So if
arduino/examples/ONE_V9/ONE_V9.ino at master · airgradienthq/arduino (github.com)
doesn’t have any library mentioned that looks to be related to the PMS sensor, then should any installed libraries be removed?

My bad, it’s missing there.

It should be:

I will push an update.

So the ‘pms’ library needs to be installed additionally.

So the original poster here had that installed, but still had the error
Compilation error: ‘struct PMS::DATA’ has no member named ‘PM_RAW_0_3’

Another user had the same issue, so something seems to be off about the current code and libraries, but I haven’t checked any of my Arduino stuff

Hi @Achim_AirGradient @MallocArray ,

I have installed the PMS library which Achim have told before this error occurred.
At my findings it seems that this error “PM_RAW_0_3” is not related there is no PMS library installed.

Ok, I could reproduce the error. It seems I did some more patching on my local PMS library. To keep things simnple, I temporarily removed the PM03PCount from the example code. Now it compiles with the default PMS library.
I am also pushing the update to the Arduino library but it normally takes a day or two until it’s updated. In the meantime, you can use below script that compiles.

/*
Important: This code is only for the DIY PRO / AirGradient ONE PCB Version 9 with the ESP-C3 MCU.

It is a high quality sensor showing PM2.5, CO2, TVOC, NOx, Temperature and Humidity on a small display and LEDbar and can send data over Wifi.

Build Instructions: https://www.airgradient.com/open-airgradient/instructions/

Kits (including a pre-soldered version) are available: https://www.airgradient.com/indoor/

The codes needs the following libraries installed:
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
“U8g2” by oliver tested with version 2.32.15
"Sensirion I2C SGP41" by Sensation Version 0.1.0
"Sensirion Gas Index Algorithm" by Sensation Version 3.2.1
“pms” by Markusz Kakl version 1.1.0
"Arduino-SHT" by Johannes Winkelmann Version 1.2.2
"Adafruit NeoPixel" by Adafruit Version 1.11.0

Configuration:
Please set in the code below the configuration parameters.

If you have any questions please visit our forum at https://forum.airgradient.com/

If you are a school or university contact us for a free trial on the AirGradient platform.
https://www.airgradient.com/

CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License

*/

#include "PMS.h"

#include <HardwareSerial.h>

#include <Wire.h>

#include "s8_uart.h"

#include <HTTPClient.h>

#include <WiFiManager.h>

#include <Adafruit_NeoPixel.h>

#include <EEPROM.h>

#include "SHTSensor.h"

#include <SensirionI2CSgp41.h>

#include <NOxGasIndexAlgorithm.h>

#include <VOCGasIndexAlgorithm.h>

#include <U8g2lib.h>

#define DEBUG true

#define I2C_SDA 7
#define I2C_SCL 6

HTTPClient client;

Adafruit_NeoPixel pixels(11, 10, NEO_GRB + NEO_KHZ800);
SensirionI2CSgp41 sgp41;
VOCGasIndexAlgorithm voc_algorithm;
NOxGasIndexAlgorithm nox_algorithm;
SHTSensor sht;

PMS pms1(Serial0);

PMS::DATA data1;

S8_UART * sensor_S8;
S8_sensor sensor;

// time in seconds needed for NOx conditioning
uint16_t conditioning_s = 10;

// for peristent saving and loading
int addr = 4;
byte value;

// Display bottom right
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

String APIROOT = "http://hw.airgradient.com/";

// set to true to switch from Celcius to Fahrenheit
boolean inF = false;

// PM2.5 in US AQI (default ug/m3)
boolean inUSAQI = false;

// Display Position
boolean displayTop = true;

// use RGB LED Bar
boolean useRGBledBar = true;

// set to true if you want to connect to wifi. You have 60 seconds to connect. Then it will go into an offline mode.
boolean connectWIFI = true;

int loopCount = 0;

unsigned long currentMillis = 0;

const int oledInterval = 5000;
unsigned long previousOled = 0;

const int sendToServerInterval = 10000;
unsigned long previoussendToServer = 0;

const int tvocInterval = 1000;
unsigned long previousTVOC = 0;
int TVOC = -1;
int NOX = -1;

const int co2Interval = 5000;
unsigned long previousCo2 = 0;
int Co2 = 0;

const int pmInterval = 5000;
unsigned long previousPm = 0;
int pm25 = -1;
int pm01 = -1;
int pm10 = -1;
//int pm03PCount = -1;

const int tempHumInterval = 5000;
unsigned long previousTempHum = 0;
float temp;
int hum;

int buttonConfig = 0;
int lastState = LOW;
int currentState;
unsigned long pressedTime = 0;
unsigned long releasedTime = 0;

void setup() {
  if (DEBUG) {
    Serial.begin(115200);
    // see https://github.com/espressif/arduino-esp32/issues/6983
    Serial.setTxTimeoutMs(0); // <<<====== solves the delay issue
  }

  Wire.begin(I2C_SDA, I2C_SCL);
  pixels.begin();
  pixels.clear();

  Serial1.begin(9600, SERIAL_8N1, 0, 1);
  Serial0.begin(9600);
  u8g2.begin();

  updateOLED2("Warming Up", "Serial Number:", String(getNormalizedMac()));
  sgp41.begin(Wire);
  delay(300);

  sht.init(Wire);
  //sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM);
  delay(300);

  //init Watchdog
  pinMode(2, OUTPUT);
  digitalWrite(2, LOW);

  sensor_S8 = new S8_UART(Serial1);

  EEPROM.begin(512);
  delay(500);

  // push button
  pinMode(9, INPUT_PULLUP);

  buttonConfig = String(EEPROM.read(addr)).toInt();
  if (buttonConfig > 7) buttonConfig = 0;
  delay(400);
  setConfig();
  Serial.println("buttonConfig: " + String(buttonConfig));

  updateOLED2("Press Button", "for LED test &", "offline mode");
  delay(2000);
  currentState = digitalRead(9);
  if (currentState == LOW) {
    ledTest();
    return;
  }

  updateOLED2("Press Button", "Now for", "Config Menu");
  delay(2000);
  currentState = digitalRead(9);
  if (currentState == LOW) {
    updateOLED2("Entering", "Config Menu", "");
    delay(3000);
    lastState = HIGH;
    setConfig();
    inConf();
  }

   if (connectWIFI) connectToWifi();
    if (WiFi.status() == WL_CONNECTED) {
      sendPing();
      Serial.println(F("WiFi connected!"));
      Serial.println("IP address: ");
      Serial.println(WiFi.localIP());
    }
  updateOLED2("Warming Up", "Serial Number:", String(getNormalizedMac()));
}

void loop() {
  currentMillis = millis();
  updateTVOC();
  updateOLED();
  updateCo2();
  updatePm();
  updateTempHum();
  sendToServer();
}

void updateTVOC() {
  uint16_t error;
  char errorMessage[256];
  uint16_t defaultRh = 0x8000;
  uint16_t defaultT = 0x6666;
  uint16_t srawVoc = 0;
  uint16_t srawNox = 0;
  uint16_t defaultCompenstaionRh = 0x8000; // in ticks as defined by SGP41
  uint16_t defaultCompenstaionT = 0x6666; // in ticks as defined by SGP41
  uint16_t compensationRh = 0; // in ticks as defined by SGP41
  uint16_t compensationT = 0; // in ticks as defined by SGP41

  delay(1000);

  compensationT = static_cast < uint16_t > ((temp + 45) * 65535 / 175);
  compensationRh = static_cast < uint16_t > (hum * 65535 / 100);

  if (conditioning_s > 0) {
    error = sgp41.executeConditioning(compensationRh, compensationT, srawVoc);
    conditioning_s--;
  } else {
    error = sgp41.measureRawSignals(compensationRh, compensationT, srawVoc,
      srawNox);
  }

  if (currentMillis - previousTVOC >= tvocInterval) {
    previousTVOC += tvocInterval;
    if (error) {
      TVOC = -1;
      NOX = -1;
      Serial.println(String(TVOC));
    } else {
      TVOC = voc_algorithm.process(srawVoc);
      NOX = nox_algorithm.process(srawNox);
      Serial.println(String(TVOC));
    }

  }
}

void updateCo2() {
  if (currentMillis - previousCo2 >= co2Interval) {
    previousCo2 += co2Interval;
    Co2 = sensor_S8 -> get_co2();
    Serial.println(String(Co2));
  }
}

void updatePm() {
  if (currentMillis - previousPm >= pmInterval) {
    previousPm += pmInterval;
    if (pms1.readUntil(data1, 2000)) {
      pm01 = data1.PM_AE_UG_1_0;
      pm25 = data1.PM_AE_UG_2_5;
      pm10 = data1.PM_AE_UG_10_0;
//      pm03PCount = data1.PM_RAW_0_3;
    } else {
      pm01 = -1;
      pm25 = -1;
      pm10 = -1;
//      pm03PCount = -1;
    }
  }
}

void updateTempHum() {
  if (currentMillis - previousTempHum >= tempHumInterval) {
    previousTempHum += tempHumInterval;

    if (sht.readSample()) {
      temp = sht.getTemperature();
      hum = sht.getHumidity();
    } else {
      Serial.print("Error in readSample()\n");
      temp = -10001;
      hum = -10001;
    }
  }
}

void updateOLED() {
  if (currentMillis - previousOled >= oledInterval) {
    previousOled += oledInterval;

    String ln3;
    String ln1;

    if (inUSAQI) {
      ln1 = "AQI:" + String(PM_TO_AQI_US(pm25)) + " CO2:" + String(Co2);
    } else {
      ln1 = "PM:" + String(pm25) + " CO2:" + String(Co2);
    }

    String ln2 = "TVOC:" + String(TVOC) + " NOX:" + String(NOX);

    if (inF) {
      ln3 = "F:" + String((temp * 9 / 5) + 32) + " H:" + String(hum) + "%";
    } else {
      ln3 = "C:" + String(temp) + " H:" + String(hum) + "%";
    }
    //updateOLED2(ln1, ln2, ln3);
    updateOLED3();
    setRGBledCO2color(Co2);
  }
}

void inConf() {
  setConfig();
  currentState = digitalRead(9);

  if (currentState) {
    Serial.println("currentState: high");
  } else {
    Serial.println("currentState: low");
  }

  if (lastState == HIGH && currentState == LOW) {
    pressedTime = millis();
  } else if (lastState == LOW && currentState == HIGH) {
    releasedTime = millis();
    long pressDuration = releasedTime - pressedTime;
    if (pressDuration < 1000) {
      buttonConfig = buttonConfig + 1;
      if (buttonConfig > 7) buttonConfig = 0;
    }
  }

  if (lastState == LOW && currentState == LOW) {
    long passedDuration = millis() - pressedTime;
    if (passedDuration > 4000) {
      updateOLED2("Saved", "Release", "Button Now");
      delay(1000);
      updateOLED2("Rebooting", "in", "5 seconds");
      delay(5000);
      EEPROM.write(addr, char(buttonConfig));
      EEPROM.commit();
      delay(1000);
      ESP.restart();
    }

  }
  lastState = currentState;
  delay(100);
  inConf();
}

void setConfig() {
  Serial.println("in setConfig");
  if (buttonConfig == 0) {
    u8g2.setDisplayRotation(U8G2_R0);
    updateOLED2("T:C, PM:ug/m3", "LED Bar: on", "Long Press Saves");
    inF = false;
    inUSAQI = false;
    useRGBledBar = true;
  } else if (buttonConfig == 1) {
    u8g2.setDisplayRotation(U8G2_R0);
    updateOLED2("T:C, PM:US AQI", "LED Bar: on", "Long Press Saves");
    inF = false;
    inUSAQI = true;
    useRGBledBar = true;
  } else if (buttonConfig == 2) {
    u8g2.setDisplayRotation(U8G2_R0);
    updateOLED2("T:F PM:ug/m3", "LED Bar: on", "Long Press Saves");
    inF = true;
    inUSAQI = false;
    useRGBledBar = true;
  } else if (buttonConfig == 3) {
    u8g2.setDisplayRotation(U8G2_R0);
    updateOLED2("T:F PM:US AQI", "LED Bar: on", "Long Press Saves");
    inF = true;
    inUSAQI = true;
    useRGBledBar = true;
  } else  if (buttonConfig == 4) {
    updateOLED2("T:C, PM:ug/m3", "LED Bar: off", "Long Press Saves");
    inF = false;
    inUSAQI = false;
    useRGBledBar = false;
  } else if (buttonConfig == 5) {
    u8g2.setDisplayRotation(U8G2_R0);
    updateOLED2("T:C, PM:US AQI", "LED Bar: off", "Long Press Saves");
    inF = false;
    inUSAQI = true;
    useRGBledBar = false;
  } else if (buttonConfig == 6) {
    u8g2.setDisplayRotation(U8G2_R0);
    updateOLED2("T:F PM:ug/m3", "LED Bar: off", "Long Press Saves");
    inF = true;
    inUSAQI = false;
    useRGBledBar = false;
  } else if (buttonConfig == 7) {
    u8g2.setDisplayRotation(U8G2_R0);
    updateOLED2("T:F PM:US AQI", "LED Bar: off", "Long Press Saves");
    inF = true;
    inUSAQI = true;
    useRGBledBar = false;
  }
}

void sendPing() {
  String payload = "{\"wifi\":" + String(WiFi.RSSI()) +
    ", \"boot\":" + loopCount +
    "}";
}

void updateOLED2(String ln1, String ln2, String ln3) {
  char buf[9];
  u8g2.firstPage();
  u8g2.firstPage();
  do {
    u8g2.setFont(u8g2_font_t0_16_tf);
    u8g2.drawStr(1, 10, String(ln1).c_str());
    u8g2.drawStr(1, 30, String(ln2).c_str());
    u8g2.drawStr(1, 50, String(ln3).c_str());
  } while (u8g2.nextPage());
}

void updateOLED3() {
  char buf[9];
  u8g2.firstPage();
  u8g2.firstPage();
  do {

    u8g2.setFont(u8g2_font_t0_16_tf);

    if (inF) {
      if (temp > -10001) {
        float tempF = (temp * 9 / 5) + 32;
        sprintf(buf, "%.1f°F", tempF);
      } else {
        sprintf(buf, "-°F");
      }
      u8g2.drawUTF8(1, 10, buf);
    } else {
      if (temp > -10001) {
        sprintf(buf, "%.1f°C", temp);
      } else {
        sprintf(buf, "-°C");
      }
      u8g2.drawUTF8(1, 10, buf);
    }

    if (hum >= 0) {
      sprintf(buf, "%d%%", hum);
    } else {
      sprintf(buf, " -%%");
    }
    if (hum > 99) {
      u8g2.drawStr(97, 10, buf);
    } else {
      u8g2.drawStr(105, 10, buf);
      // there might also be single digits, not considered, sprintf might actually support a leading space
    }

    u8g2.drawLine(1, 13, 128, 13);
    u8g2.setFont(u8g2_font_t0_12_tf);
    u8g2.drawUTF8(1, 27, "CO2");
    u8g2.setFont(u8g2_font_t0_22b_tf);
    if (Co2 > 0) {
      sprintf(buf, "%d", Co2);
    } else {
      sprintf(buf, "%s", "-");
    }
    u8g2.drawStr(1, 48, buf);
    u8g2.setFont(u8g2_font_t0_12_tf);
    u8g2.drawStr(1, 61, "ppm");
    u8g2.drawLine(45, 15, 45, 64);
    u8g2.setFont(u8g2_font_t0_12_tf);
    u8g2.drawStr(48, 27, "PM2.5");
    u8g2.setFont(u8g2_font_t0_22b_tf);

    if (inUSAQI) {
      if (pm25 >= 0) {
        sprintf(buf, "%d", PM_TO_AQI_US(pm25));
      } else {
        sprintf(buf, "%s", "-");
      }
      u8g2.drawStr(48, 48, buf);
      u8g2.setFont(u8g2_font_t0_12_tf);
      u8g2.drawUTF8(48, 61, "AQI");
    } else {
      if (pm25 >= 0) {
        sprintf(buf, "%d", pm25);
      } else {
        sprintf(buf, "%s", "-");
      }
      u8g2.drawStr(48, 48, buf);
      u8g2.setFont(u8g2_font_t0_12_tf);
      u8g2.drawUTF8(48, 61, "ug/m³");
    }

    u8g2.drawLine(82, 15, 82, 64);
    u8g2.setFont(u8g2_font_t0_12_tf);
    u8g2.drawStr(85, 27, "TVOC:");
    if (TVOC >= 0) {
      sprintf(buf, "%d", TVOC);
    } else {
      sprintf(buf, "%s", "-");
    }
    u8g2.drawStr(85, 39, buf);
    u8g2.drawStr(85, 53, "NOx:");
    if (NOX >= 0) {
      sprintf(buf, "%d", NOX);
    } else {
      sprintf(buf, "%s", "-");
    }
    u8g2.drawStr(85, 63, buf);

  } while (u8g2.nextPage());
}

void sendToServer() {
  if (currentMillis - previoussendToServer >= sendToServerInterval) {
    previoussendToServer += sendToServerInterval;
    String payload = "{\"wifi\":" + String(WiFi.RSSI()) +
      (Co2 < 0 ? "" : ", \"rco2\":" + String(Co2)) +
      (pm01 < 0 ? "" : ", \"pm01\":" + String(pm01)) +
      (pm25 < 0 ? "" : ", \"pm02\":" + String(pm25)) +
      (pm10 < 0 ? "" : ", \"pm10\":" + String(pm10)) +
//      (pm03PCount < 0 ? "" : ", \"pm003_count\":" + String(pm03PCount)) +
      (TVOC < 0 ? "" : ", \"tvoc_index\":" + String(TVOC)) +
      (NOX < 0 ? "" : ", \"nox_index\":" + String(NOX)) +
      ", \"atmp\":" + String(temp) +
      (hum < 0 ? "" : ", \"rhum\":" + String(hum)) +
      ", \"boot\":" + loopCount +
      "}";

    if (WiFi.status() == WL_CONNECTED) {
      Serial.println(payload);
      String POSTURL = APIROOT + "sensors/airgradient:" + String(getNormalizedMac()) + "/measures";
      Serial.println(POSTURL);
      WiFiClient client;
      HTTPClient http;
      http.begin(client, POSTURL);
      http.addHeader("content-type", "application/json");
      int httpCode = http.POST(payload);
      String response = http.getString();
      Serial.println(httpCode);
      Serial.println(response);
      http.end();
      resetWatchdog();
      loopCount++;
    } else {
      Serial.println("WiFi Disconnected");
    }
  }
}

void countdown(int from) {
  debug("\n");
  while (from > 0) {
    debug(String(from--));
    debug(" ");
    delay(1000);
  }
  debug("\n");
}

void resetWatchdog() {
  Serial.println("Watchdog reset");
  digitalWrite(2, HIGH);
  delay(20);
  digitalWrite(2, LOW);
}

// Wifi Manager
void connectToWifi() {
  WiFiManager wifiManager;
  //WiFi.disconnect(); //to delete previous saved hotspot
  String HOTSPOT = "AG-" + String(getNormalizedMac());
  updateOLED2("180s to connect", "to Wifi Hotspot", HOTSPOT);
  wifiManager.setTimeout(180);
  if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
    Serial.println("failed to connect and hit timeout");
    delay(6000);
  }

}

void debug(String msg) {
  if (DEBUG)
    Serial.print(msg);
}

void debug(int msg) {
  if (DEBUG)
    Serial.print(msg);
}

void debugln(String msg) {
  if (DEBUG)
    Serial.println(msg);
}

void debugln(int msg) {
  if (DEBUG)
    Serial.println(msg);
}

String getNormalizedMac() {
  String mac = WiFi.macAddress();
  mac.replace(":", "");
  mac.toLowerCase();
  return mac;
}

void setRGBledCO2color(int co2Value) {
  if (co2Value >= 300 && co2Value < 800) setRGBledColor('g');
  if (co2Value >= 800 && co2Value < 1000) setRGBledColor('y');
  if (co2Value >= 1000 && co2Value < 1500) setRGBledColor('o');
  if (co2Value >= 1500 && co2Value < 2000) setRGBledColor('r');
  if (co2Value >= 2000 && co2Value < 3000) setRGBledColor('p');
  if (co2Value >= 3000 && co2Value < 10000) setRGBledColor('z');
}

void setRGBledColor(char color) {
  if (useRGBledBar) {
    //pixels.clear();
    switch (color) {
    case 'g':
      for (int i = 0; i < 11; i++) {
        pixels.setPixelColor(i, pixels.Color(0, 255, 0));
        delay(30);
        pixels.show();
      }
      break;
    case 'y':
      for (int i = 0; i < 11; i++) {
        pixels.setPixelColor(i, pixels.Color(255, 255, 0));
        delay(30);
        pixels.show();
      }
      break;
    case 'o':
      for (int i = 0; i < 11; i++) {
        pixels.setPixelColor(i, pixels.Color(255, 128, 0));
        delay(30);
        pixels.show();
      }
      break;
    case 'r':
      for (int i = 0; i < 11; i++) {
        pixels.setPixelColor(i, pixels.Color(255, 0, 0));
        delay(30);
        pixels.show();
      }
      break;
    case 'b':
      for (int i = 0; i < 11; i++) {
        pixels.setPixelColor(i, pixels.Color(0, 0, 255));
        delay(30);
        pixels.show();
      }
      break;
    case 'w':
      for (int i = 0; i < 11; i++) {
        pixels.setPixelColor(i, pixels.Color(255, 255, 255));
        delay(30);
        pixels.show();
      }
      break;
    case 'p':
      for (int i = 0; i < 11; i++) {
        pixels.setPixelColor(i, pixels.Color(153, 0, 153));
        delay(30);
        pixels.show();
      }
      break;
    case 'z':
      for (int i = 0; i < 11; i++) {
        pixels.setPixelColor(i, pixels.Color(102, 0, 0));
        delay(30);
        pixels.show();
      }
      break;
    case 'n':
      for (int i = 0; i < 11; i++) {
        pixels.setPixelColor(i, pixels.Color(0, 0, 0));
        delay(30);
        pixels.show();
      }
      break;
    default:
      // if nothing else matches, do the default
      // default is optional
      break;
    }
  }
}

void ledTest() {
  updateOLED2("LED Test", "running", ".....");
  for (int i = 0; i < 11; i++) {
        pixels.setPixelColor(i, pixels.Color(255, 0, 0));
        delay(30);
        pixels.show();
      }
  delay(500);
  for (int i = 0; i < 11; i++) {
        pixels.setPixelColor(i, pixels.Color(0, 255, 0));
        delay(30);
        pixels.show();
      }
  delay(500);
  for (int i = 0; i < 11; i++) {
        pixels.setPixelColor(i, pixels.Color(0, 0, 255));
        delay(30);
        pixels.show();
      }
  delay(500);
  for (int i = 0; i < 11; i++) {
        pixels.setPixelColor(i, pixels.Color(255, 255, 255));
        delay(30);
        pixels.show();
      }
  delay(500);
  for (int i = 0; i < 11; i++) {
        pixels.setPixelColor(i, pixels.Color(0, 0, 0));
        delay(30);
        pixels.show();
      }
  delay(500);
}

// Calculate PM2.5 US AQI
int PM_TO_AQI_US(int pm02) {
  if (pm02 <= 12.0) return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0);
  else if (pm02 <= 35.4) return ((100 - 50) / (35.4 - 12.0) * (pm02 - 12.0) + 50);
  else if (pm02 <= 55.4) return ((150 - 100) / (55.4 - 35.4) * (pm02 - 35.4) + 100);
  else if (pm02 <= 150.4) return ((200 - 150) / (150.4 - 55.4) * (pm02 - 55.4) + 150);
  else if (pm02 <= 250.4) return ((300 - 200) / (250.4 - 150.4) * (pm02 - 150.4) + 200);
  else if (pm02 <= 350.4) return ((400 - 300) / (350.4 - 250.4) * (pm02 - 250.4) + 300);
  else if (pm02 <= 500.4) return ((500 - 400) / (500.4 - 350.4) * (pm02 - 350.4) + 400);
  else return 500;
};

I had pms 1.1.0 installed

My intention was to redirect the telemetry updates to a local server. I’ve developed a simpler solution that uses PiHole and PHP to capture those stats. If anyone is interested:

https://hub.docker.com/repository/docker/bachsolo/air_gradient_local/general

I have a Raspberry Pi running docker with the following containers: Graphana, Prometheus, and my AirGradient Proxy script. Works great. And I didn’t have to push new firmware.

Hi @Achim_AirGradient ,
I haved updated Airgradient Air Quality Sensor t o v2.4.13 to get the same error of PMS.

I used your posted script then the compelling is completed with no errors reported.

I would like to known the version of Web install of V9 is not same as Example of AirGradient library?
By the simple speaking the latest and stable version of all AirGradient sensor software which flashed from Web is the best way to update the sensor board if you do not need to modified the original script posted on Example under AirGradient Air Quality Sensor?

Is S8_UART also needed?

I was trying to compile the example code to help out and kept getting a message that I didn’t have s8_uart.h but once I installed the library, I was able to get further. I ran into several other Arudino related errors, but I’m not so concerned with them at this time, but wanted to point out that it kind of looks like the S8 library is missing.

Yes correct. I am on it.

Sorry for the mess.

By the way, we have a dedicated developer completely redoing the Arduino library. So hopefully we will have something much better and stable soon.

Yes, flashing through the web is the easiest way and currently the most stable version.

I tired to flash through the web without any luck and figured I would try to flash with Arduino.
I’m getting the following compile error:


Any suggestions?