Challenger - ZMOD4510 - Outdoor Air Quality Sensor
Hello element14 Family,
Please find the code for the device constructed as a part of the Summer of Sensors Design Challenge.
Since the Renesas code was proprietary and was downloaded through a request on their portal, the license agreements will not allow me post it publicly on the forum.
However, I have indicated the sections where the downloaded library code can be directly included for reconstructing the results of this exercise.
Hardware:
- Renesas ZMOD4510 EVK Outdoor Air Quality Sensor
- Adafruit Feather M0 WiFi - ATSAMD21 + ATWINC1500
- Adafruit FeatherWing OLED - 128x32 OLED Add-on For Feather
- Adafruit Si7021 Temperature & Humidity Sensor Breakout Board
- Adafruit Electret Microphone Amplifier - MAX4466 with Adjustable Gain
Software:
- Arduino IDE
- ZMOD4510 - OAQ 2nd Gen Firmware - Selective Ozone and Ultra-Low Power (request for secure access)
- Library: Arduino SAMD Boards
- Library: Adafruit SAMD Boards
- Library: Adafruit SSD1306 (and associated dependencies)
- Library: Adafruit Si7021 Library
- Library: Blynk
Arduino Code:
/* Peronalized Air Quality & Sound Measurement Device
* This file is part of the element14 Summer of Sensors Challenge
* Category : In The Air Tonight
* Hardware : Renesas ZMOD4510 Outdoor Air Quality Sensor
* Due Date : 17th of November, 2022
* Authors : Renesas Electronics Corporation, Ladyada, Abhay Joshi
* Usage : Search For Fields Labelled {USER INPUT:} & Input Relevant Data
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* USER INPUT: Template ID, Device Name, Auth Token Provided By Blynk.Cloud */
#define BLYNK_TEMPLATE_ID ""
#define BLYNK_DEVICE_NAME ""
#define BLYNK_AUTH_TOKEN ""
/* Comment This To Save Space */
#define BLYNK_PRINT Serial
/*******************************************************************/
/* INCLUDES */
/*******************************************************************/
/* Includes For Adafruit OLED Featherwing */
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
/* Includes For Si7021 Temperature & Humidity Sensor */
#include "Adafruit_Si7021.h"
/* USER INPUT: Includes For ZMOD4510 Gas Sensor */
/* Includes For Blynk IoT Cloud Reporting */
#include <WiFi101.h>
#include <BlynkSimpleWiFiShield101.h>
/*******************************************************************/
/* MACROS */
/*******************************************************************/
/* OLED Display Width (Pixels) */
#define SCREEN_WIDTH 128
/* OLED Display Heighth (Pixels) */
#define SCREEN_HEIGHT 32
/* OLED Reset Pin (-1 If Sharing Arduino Reset Pin) */
#define OLED_RESET -1
/* OLED I2C Address (Datasheet: 0x3C for 128x32) */
#define SCREEN_ADDRESS 0x3C
/* Read Battery Level */
/* Ref: https://learn.adafruit.com/adafruit-feather-m0-adalogger/power-management */
#define VBATPIN A7
/* Read Microphone Level */
/* Ref: https://learn.adafruit.com/adafruit-microphone-amplifier-breakout/measuring-sound-levels */
#define MICROPHONE_PIN A5
/*******************************************************************/
/* HANDLERS */
/*******************************************************************/
/* OLED Display */
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void error_handle();
/* Temperature & Humidity Sensor */
Adafruit_Si7021 sensor = Adafruit_Si7021();
/*******************************************************************/
/* GLOBAL VARIABLES */
/*******************************************************************/
/* USER INPUT: Variables Specific To ZMOD4510 Gas Sensor*/
/* Variables Specific To Environmental Sensor */
bool enableHeater = false;
/* Variables Specific To Electret Microphone */
const int sampleWindow = 50; // Sample window width in mS (50 mS = 20Hz)
unsigned int sample;
/* Blynk.Cloud Authorization Token */
char auth[] = BLYNK_AUTH_TOKEN;
/* USER INPUT: WiFi Credentials */
char ssid[] = "";
char pass[] = "";
/*******************************************************************/
/* FUNCTIONS */
/*******************************************************************/
/*******************************************************************/
/* SETUP */
/*******************************************************************/
void setup()
{
/* Initialize The Serial Port */
Serial.begin(115200);
/* Setup WiFi & Blynk.Cloud Interface */
/* Ref: https://github.com/blynkkk/blynk-library/blob/master/examples/Boards_WiFi/Adafruit_Feather_M0_WiFi/Adafruit_Feather_M0_WiFi.ino */
WiFi.setPins(8, 7, 4, 2);
Blynk.begin(auth, ssid, pass);
/* Delay For System Stability */
delay(2000);
/* Initialize The Temperature & Humidity Sensor */
if (!sensor.begin())
{
Serial.println("Si7021 Sensor Error!");
for(;;); // Don't Proceed, Loop Forever
}
/* Initialize The OLED Display */
/* SSD1306_SWITCHCAPVCC - Generate Display Voltage From 3.3V Internally */
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
{
Serial.println(F("SSD1306 OLED Display Error"));
for(;;); // Don't Proceed, Loop Forever
}
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 Pixel Scale
display.setTextColor(SSD1306_WHITE); // Draw White Text
display.setCursor(0, 0); // Start At Top-left Corner
display.cp437(true); // Use Full 256 Char 'Code Page 437' Font
/* Print A Welcome Message */
display.println(F("Welcome To"));
display.println(F("element14"));
display.println(F("Summer of Sensors"));
display.println(F("Design Challenge!\n"));
display.display();
delay(5000);
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 10);
display.println(F("In The Air Tonight..."));
display.display();
delay(100);
/* Additional Delay For System Stability */
delay(2000);
Serial.println(F("Starting The ZMOD4510 Gas Sensor!"));
/* USER INPUT: Enter The Setup Section Code Obtained From The Renesas Library */
}
/*******************************************************************/
/* LOOP */
/*******************************************************************/
void loop()
{
Blynk.run();
/* USER INPUT: Enter The Loop Section Code Obtained From The Renesas Library Download */
/* Use sensor.readHumidity() & sensor.readTemperature() for algo_input */
/* Report Battery Level */
float measuredvbat = analogRead(VBATPIN);
measuredvbat *= 2; // we divided by 2, so multiply back
measuredvbat *= 3.3; // Multiply by 3.3V, our reference voltage
measuredvbat /= 1024; // convert to voltage
Serial.print(F("VBat = "));
Serial.print(measuredvbat);
unsigned long startMillis = millis(); // Start of sample window
unsigned int peakToPeak = 0; // peak-to-peak level
unsigned int signalMax = 0;
unsigned int signalMin = 1024;
/* Collect Data For 50 Milliseconds */
while (millis() - startMillis < sampleWindow) {
sample = analogRead(MICROPHONE_PIN);
if (sample < 1024) {
if (sample > signalMax) {
signalMax = sample; // save just the max levels
}
else if (sample < signalMin) {
signalMin = sample; // save just the min levels
}
}
}
/* Ref: https://forums.adafruit.com/viewtopic.php?f=8&t=100462&p=503661 */
peakToPeak = signalMax - signalMin; // max - min = peak-peak amplitude
double volts = ((peakToPeak * 3.3) / 1024) * 0.707; // convert to RMS voltage
double first = log10(volts/0.00631)*20;
double decibels = first + 94 - 44 - 25;
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(F("In The Air Tonight"));
/* Display & Report Temperature */
display.print(F("T:"));
display.print(sensor.readTemperature(), 2);
Blynk.virtualWrite(V5, sensor.readTemperature());
display.print(F("C "));
/* Display & Report Relative Humidity */
display.print(F("RH:"));
display.print(sensor.readHumidity(), 2);
Blynk.virtualWrite(V6, sensor.readHumidity());
display.print(F("% "));
/* Display & Report AQI */
display.print(F(" AQI:"));
if (lib_ret == OAQ_2ND_GEN_STABILIZATION) {
display.print(F("Warming Up... "));
/* Convey That The Sensor Is In Warm Up State */
Blynk.virtualWrite(V7, -1);
}
else {
display.print(algo_results.EPA_AQI);
Blynk.virtualWrite(V7, algo_results.EPA_AQI);
}
display.println();
/* Display & Report Noise Level (dB) */
display.print(F("Noise:"));
display.print(decibels);
Blynk.virtualWrite(V8, decibels);
display.print(F("dB "));
/* Call The Drawing Function */
display.display();
delay(100);
/* Issue A Low Battery Warning (400 mAh LiPo) */
if(measuredvbat < 3.7) {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setCursor(10, 10);
display.println(F("CHARGE BATTERY NOW!"));
display.display();
/* Hold The Message On The Display For Some Time */
delay(2000);
}
}
void error_handle()
{
/* Implement Specific Error Handling Routines */
while(1);
}