//
#include <Arduino.h>
// Math library
#include <math.h>
// I2C Communication
#include <Wire.h>
// SGP30 - CO2 Sensor
#include "Adafruit_SGP30.h"
Adafruit_SGP30 sgp;
//BlueTooth MESH
#include "painlessMesh.h"
//JSON file creation & Reading
#include <Arduino_JSON.h>
//#include <ArduinoJson.h>
//RGB LED
#include <FastLED.h>
// ---------- Function declaration ----------
// VMA320 - TempSensor
void TempS_VMA320(int); //Temperature Sensor, Pin required, To measure temperature
int VMS320_T;
#define VMA320_PIN A0
// SGP30 - CO2 Sensor
void CO2S_SGP30_START(bool); //function to initialize the CO2 Sensor - SGP30
void CO2S_SGP30(); //CO2 Sensor - SGP30
int SGP30_TVOC;
int SGP30_eCO2;
int counter = 0; // Counter for CO2 Sensor
uint32_t getAbsoluteHumidity(float, float); // Absolute humidity correction for CO2 Sensor
// I2C Scanner -----------
void I2C_Scan(); //I2C Scanner, no Pin required, To check I2C devices connected to the board
//LES SETUP
#define LED_PIN D9 //GPIO //D0 //or D9
#define NUM_LEDS 1
#define BRIGHTNESS 32
#define LED_TYPE WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
#define UPDATES_PER_SECOND 100
CRGBPalette16 currentPalette;
TBlendType currentBlending;
extern CRGBPalette16 myRedWhiteBluePalette;
extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM;
int R;
int G;
int B;
void RGB_Send_OFF();
void RGB_Send_ON();
//Button
#define Button_PIN1 43 //GPIO //D6
#define Button_PIN2 7 //GPIO //D8
//#define Button_PIN2 D6 //or D8
// MESH -----------------
//the name of the MESH network
#define MESH_PREFIX "whateverYouLike"
//password of the MESH network
#define MESH_PASSWORD "somethingSneaky"
// This refers to the the TCP port that you want the mesh server to run on. The default is 5555
#define MESH_PORT 5555
//Number for this node
int nodeNumber = 3;
//Node ID to send a specific message
int Node_ID = 1;
//String to send to other nodes with sensor readings
String final_message;
//It is recommended to avoid using delay() in the mesh network code.
//To maintain the mesh, some tasks need to be performed in the background.
//Using delay() will stop these tasks from happening and can cause the mesh to lose stability/fall apart.
//The following line creates a new Scheduler called userScheduler
Scheduler userScheduler; // to control your personal task
//Create a painlessMesh object called mesh to handle the mesh network
painlessMesh mesh;
// User stub
void sendMessage() ; // Prototype so PlatformIO doesn't complain
String Message(); // Prototype for sending message
//Create a task called taskSendMessage responsible for calling the sendMessage() function every X (the number) second as long as the program is running.
Task taskSendMessage( TASK_SECOND * 5 , TASK_FOREVER, &sendMessage );
String Message() {
JSONVar jsonReadings;
jsonReadings["node"] = nodeNumber;
jsonReadings["temperature"] = VMS320_T;
jsonReadings["humidity"] = NAN;
jsonReadings["CO2"] = SGP30_eCO2;
jsonReadings["TVOC"] = SGP30_TVOC;
// Add more sensor readings as needed
final_message = JSON.stringify(jsonReadings);
return final_message;
}
//The sendMessage() function sends a message to all nodes in the message network (broadcast).
void sendMessage() {
RGB_Send_ON();
String msg = Message();
//board chip ID
//msg += mesh.getNodeId();
//Replaced by nodeNumber
//To broadcast a message, simply use the sendBroadcast() method on the mesh object and pass as argument the message (msg) you want to send.
mesh.sendBroadcast(msg);
//Send to specific Node
//mesh.sendSingle(Node_ID, msg);
//Every time a new message is sent, the code changes the interval between messages (one to five seconds).
//taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 5 ));
RGB_Send_OFF();
Serial.println("Message Sent");
}
//several callback functions are created that will be called when specific events happen on the mesh.
//The receivedCallback() function prints the message sender (from) and the content of the message (msg.c_str()).
void receivedCallback( uint32_t from, String &msg ) {
//Serial.printf("Received from %u msg=%s\n", from, msg.c_str());
//The message comes in JSON format, so, we can access the variables as follows:
JSONVar myObject = JSON.parse(msg.c_str());
if (JSON.typeof(myObject) == "undefined") {
Serial.println("Parsing input failed!");
return;
}
int node = myObject["node"];
int temperature_in = myObject["temperature"];
int humidity_in = myObject["humidity"];
int CO2_in = myObject["CO2"];
int TVOC_in = myObject["TVOC"];
//Serial.print(M1_I);
}
//The newConnectionCallback() function runs whenever a new node joins the network.
//This function simply prints the chip ID of the new node.
//You can modify the function to do any other task.
void newConnectionCallback(uint32_t nodeId) {
Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
}
//The changedConnectionCallback() function runs whenever a connection changes on the network (when a node joins or leaves the network).
void changedConnectionCallback() {
Serial.printf("Changed connections\n");
}
//The nodeTimeAdjustedCallback() function runs when the network adjusts the time, so that all nodes are synchronized.
//It prints the offset.
void nodeTimeAdjustedCallback(int32_t offset) {
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}
// ---------- Setup ----------
void setup() {
// put your setup code here, to run once:
Wire.begin(); // Initialize I2C communication
Serial.begin(115200); // Initialize Serial Monitor
while (!Serial) { delay(10); } // Wait for serial console to open!
pinMode(LED_BUILTIN, OUTPUT); // Set the built-in LED as output
//LED
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
FastLED.setBrightness( BRIGHTNESS );
currentPalette = RainbowColors_p;
currentBlending = LINEARBLEND;
pinMode(VMA320_PIN, INPUT); // Set the pin input for VMA320 Thermistor / Temp Sensor
// Initialize the CO2 Sensor - SGP30
CO2S_SGP30_START(true);
//MESH
mesh.setDebugMsgTypes( ERROR | STARTUP ); // set before init() so that you can see startup messages
//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
//Initialize the mesh with the details defined earlier.
mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
//Assign all the callback functions to their corresponding events.
mesh.onReceive(&receivedCallback);
mesh.onNewConnection(&newConnectionCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
//Finally, add the taskSendMessage function to the userScheduler.
//The scheduler is responsible for handling and running the tasks at the right time.
userScheduler.addTask( taskSendMessage );
//Finally, enable the taskSendMessage, so that the program starts sending the messages to the mesh.
taskSendMessage.enable();
//Upload Check
int time = 0;
R = random(0,255);
G = random(0,255);
B = random(0,255);
while (time <= 100){
for(int i = 0; i < NUM_LEDS; i++ ){
leds[i].setRGB(R,G,B); // Set Color HERE!!!
}
FastLED.show();
FastLED.delay(1000 / UPDATES_PER_SECOND);
time++;
}
R = 0;
G = 0;
B = 0;
for(int i = 0; i < NUM_LEDS; i++ ){
leds[i].setRGB(R,G,B); // Set Color HERE!!!
}
FastLED.show();
FastLED.delay(1000 / UPDATES_PER_SECOND);
}
// ---------- Loop ----------
void loop() {
//I2C_Scan(); // Uncomment to scan I2C devices
//RGB_Send_OFF()
digitalWrite(LED_BUILTIN, HIGH); //high turn off the led ...
TempS_VMA320(VMA320_PIN);
CO2S_SGP30();
mesh.update();
//RGB_Send_ON()
}
// ---------- Function definitions ----------
// ---------- CO2 Sensor - Sensirion SGP30 eCO2 & TVOC Sensor
void CO2S_SGP30_START(bool initialize){
if (!initialize) return; // If false, exit function
// Initialize I2C communication for the SGP30
Serial.println("SGP30 test");
if (! sgp.begin()){
Serial.println("Sensor not found :(");
while (1);
}
Serial.print("Found SGP30 serial #");
Serial.print(sgp.serialnumber[0], HEX);
Serial.print(sgp.serialnumber[1], HEX);
Serial.println(sgp.serialnumber[2], HEX);
// If you have a baseline measurement from before you can assign it to start, to 'self-calibrate'
//sgp.setIAQBaseline(0x8E68, 0x8F41); // Will vary for each sensor!
}
/* return absolute humidity [mg/m^3] with approximation formula
* @param temperature [°C]
* @param humidity [%RH]
*/
uint32_t getAbsoluteHumidity(float temperature, float humidity) {
// approximation formula from Sensirion SGP30 Driver Integration chapter 3.15
const float absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f * exp((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature)); // [g/m^3]
const uint32_t absoluteHumidityScaled = static_cast<uint32_t>(1000.0f * absoluteHumidity); // [mg/m^3]
return absoluteHumidityScaled;
}
void CO2S_SGP30(){
// If you have a temperature / humidity sensor, you can set the absolute humidity to enable the humidity compensation for the air quality signals
//float temperature = 22.1; // [°C]
//float humidity = 45.2; // [%RH]
//sgp.setHumidity(getAbsoluteHumidity(temperature, humidity));
if (! sgp.IAQmeasure()) {
Serial.println("Measurement failed");
//return;
}
Serial.print("TVOC ");
float TVOC = sgp.TVOC;
Serial.print(TVOC);
Serial.print(" ppb\t");
Serial.print("eCO2 ");
float eCO2 = sgp.eCO2;
Serial.print(eCO2);
Serial.println(" ppm");
/*
if (! sgp.IAQmeasureRaw()) {
Serial.println("Raw Measurement failed");
//return;
}
Serial.print("Raw H2 ");
Serial.print(sgp.rawH2);
Serial.print(" \t");
Serial.print("Raw Ethanol ");
Serial.print(sgp.rawEthanol);
Serial.println("");
*/
delay(1000);
/*
counter++;
if (counter == 30) {
counter = 0;
uint16_t TVOC_base, eCO2_base;
if (! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) {
Serial.println("Failed to get baseline readings");
return;
}
Serial.print("****Baseline values: eCO2: 0x");
Serial.print(eCO2_base, HEX);
Serial.print(" & TVOC: 0x");
Serial.println(TVOC_base, HEX);
}
*/
SGP30_eCO2 = eCO2;
SGP30_TVOC = TVOC;
//Code modified from Adafruit SGP30 example
}
// ---------- Temperature Sensor - VMA 320
void TempS_VMA320(int pin){
float reading;
// The beta coefficient of the thermistor (usually 3000-4000)
#define BCOEFFICIENT 3950
// the value of the 'other' resistor
#define SERIESRESISTOR 10000
// resistance at 25 degrees C
#define THERMISTORNOMINAL 10000
// temp. for nominal resistance (almost always 25 C)
#define TEMPERATURENOMINAL 25
reading = analogRead(pin);
reading = 4095 / reading - 1;
reading = SERIESRESISTOR / reading;
//Serial.print("Thermistor resistance ");
//Serial.println(reading);
float steinhart;
steinhart = reading / THERMISTORNOMINAL; // (R/Ro)
steinhart = log(steinhart); // ln(R/Ro)
steinhart /= BCOEFFICIENT; // 1/B * ln(R/Ro)
steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To)
steinhart = 1.0 / steinhart; // Invert
steinhart -= 273.15; // convert absolute temp to C
//Serial.print("Temperature ");
//Serial.print(steinhart);
//Serial.println(" *C");
VMS320_T = steinhart;
//Modified from: https://learn.adafruit.com/thermistor/using-a-thermistor
}
// ---------- Integrated LED Blinking
void BlinkLED(int time) {
digitalWrite(LED_BUILTIN, HIGH);
Serial.println("ON");
delay(time);
digitalWrite(LED_BUILTIN, LOW);
delay(time);
Serial.println("OFF");
}
// ---------- SEND RGB LED Blinking
void RGB_Send_ON(){
int time = 0;
R = random(0,255);
G = random(0,255);
B = random(0,255);
while (time <= 100){
for(int i = 0; i < NUM_LEDS; i++ ){
leds[i].setRGB(R,G,B); // Set Color HERE!!!
}
FastLED.show();
FastLED.delay(1000 / UPDATES_PER_SECOND);
time++;
}}
void RGB_Send_OFF(){
R = 0;
G = 0;
B = 0;
for(int i = 0; i < NUM_LEDS; i++ ){
leds[i].setRGB(R,G,B); // Set Color HERE!!!
}
FastLED.show();
FastLED.delay(1000 / UPDATES_PER_SECOND);
}
// ---------- I2C Scanner
void I2C_Scan() {
byte error, address;
int nDevices;
Serial.println("Scanning...");
nDevices = 0;
for(address = 1; address < 127; address++ ) {
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("I2C device found at address 0x");
if (address<16) {
Serial.print("0");
}
Serial.println(address,HEX);
nDevices++;
}
else if (error==4) {
Serial.print("Unknown error at address 0x");
if (address<16) {
Serial.print("0");
}
Serial.println(address,HEX);
}
}
if (nDevices == 0) {
Serial.println("No I2C devices found\n");
}
else {
Serial.println("done\n");
}
delay(5000);
}