hello and thank you in advance for any suggestions offered.
I have a system setup with three sensors and I want to poll the sensors every minute for 10 minutes and then present the average to the Raspberry Pi for insertion into a MySQL database. I had everything working when my cycle was just a minute or two but when I started a long term test none of the measurements were performed. What I found to be the issue is that the delay() function, though the documentation states it can handle an unsigned long, craps out somewhere between 25,000 and 50,000. Has anyone seen an issue like this before? I’ve solved my problem by doing 36 iterations of 25s which gets me to my 15 minute cycle of records.
I know the code is ugly and there is a lot of clean up to do and if you have any suggestions on my technique I’ll take that also.
/******************************************************************************
Sketch to read from the following sensors for the purpose of monitoring
bee hives.
SEN-14348 (CCS811/BME280 Combo)
SEN-15892 (Loudness Sensor)
WRL-13287 (WiFi Shield)
SEN-15242 (NAU7802 Load Sensor)
Pulled from multiple examples found on the internet
Author: David Boyd
File Name: BeeHive_Master
Date Created: 24MAY2019
Date Modified: 27JAN2020
******************************************************************************/
#include <Wire.h>
#include <SparkFunBME280.h> //library for Sparkfun SEN-14348
#include <SparkFunCCS811.h> //library for Sparkfun SEN-14348
#include <EEPROM.h> //Needed to record user settings
#include "SparkFun_Qwiic_Scale_NAU7802_Arduino_Library.h" //library for Sparkfun SEN-15242
// #include <SparkFun_RV1805.h> //library for Sparkfun BOB-14558
// #include <SparkFunESP8266WiFi.h> //library for ESP8266 WiFi
#define COMMAND_LED_OFF 0x00
#define COMMAND_LED_ON 0x01
#define COMMAND_GET_VALUE 0x05
#define COMMAND_NOTHING_NEW 0x99
#define CCS811_ADDR 0x5B //Default I2C Address
//#define CCS811_ADDR 0x5A //Alternate I2C Address
#define AVG_SIZE 4
#define LOCATION_CALIBRATION_FACTOR 0 //Float, requires 4 bytes of EEPROM
#define LOCATION_ZERO_OFFSET 10 //Must be more than 4 away from previous spot. Long, requires 4 bytes of EEPROM
//Global environmental sensor objects
CCS811 myCCS811(CCS811_ADDR); //initialize CCS811
BME280 myBME280; //initialize BME280
NAU7802 myScale; //initialize NAU7802 class
// RV1805 rtc; //initialize RV1805 RTC
//EEPROM locations to store 4-byte variables for NAU7802 Scale - Do I still need this?
bool settingsDetected = false; //Used to prompt user to calibrate their scale
// SETTINGS FOR AVERAGING
const int numReadings = 36; // number of readings for the average
const int sketch_delay = 25000; // delay between readings
int readings[numReadings]; // the readings from the analog input
int readIndex = 0; // the index of the current reading
unsigned int ADC_t = 0; // running total for ADC
unsigned int CO2_t = 0; // running total for CO2
unsigned int TVOC_t = 0; // running total for TVOC
unsigned int TEMP_t = 0; // running total for TEMP
unsigned int H2O_t = 0; // running total for H2O
unsigned int PRES_t = 0; // running total for Pressure
unsigned int ALT_t = 0; // running total for Altitude
unsigned int WGT_t = 0; // running total for Scale
int ADC_a = 0; // ADC the average (noise)
int CO2_a = 0; // CO2 average
int TVOC_a = 0; // TVOC average
int TEMP_a = 0; // Temperature average
int H2O_a = 0; // Humidity average
int PRES_a = 0; // Barometric pressure average
int ALT_a = 0; // Altitude average in ft.
int WGT_a = 0; // Scale average
const byte qwiicAddress = 0x38; //Default Address
uint16_t ADC_VALUE = 0;
//The below variables control what the date will be set to
int hund = 50;
int sec = 2;
int minute = 55;
int hour = 20;
int date = 30;
int month = 2;
int year = 2020;
int day = 5;
// WiFi Settings
// password of your WiFi network.
/*
const char mySSID[] = "ORBI59";
const char myPSK[] = "cleverplanet441";
char destServer[] = "www.google.com";
*/
void setup() {
Serial.begin(9600);
Wire.setClock(100000); //Qwiic Scale is capable of running at 400kHz if desired
testForConnectivity();
// for (int thisReading = 0; thisReading < numReadings; thisReading++) {
// readings[thisReading] = 0;
// }
//Initialize BME280
//For I2C, enable the following and disable the SPI section
myBME280.settings.commInterface = I2C_MODE;
myBME280.settings.I2CAddress = 0x77;
myBME280.settings.runMode = 3; //Normal mode
myBME280.settings.tStandby = 0;
myBME280.settings.filter = 4;
myBME280.settings.tempOverSample = 5;
myBME280.settings.pressOverSample = 5;
myBME280.settings.humidOverSample = 5;
//Calling .begin() causes the settings to be loaded
delay(20); //Make sure sensor had enough time to turn on. BME280 requires 2ms to start up.
byte id = myBME280.begin(); //Returns ID of 0x60 if successful
if (id != 0x60)
{
Serial.println("Problem with BME280");
}
else
{
// Serial.println("BME280 online");
}
if (myScale.begin() == false)
{
Serial.println("Scale not detected. Please check wiring. Freezing...");
while (1);
}
//Serial.println("Scale detected!");
readSystemSettings(); //Load zeroOffset and calibrationFactor from EEPROM
myScale.setSampleRate(NAU7802_SPS_320); //Increase to max sample rate
myScale.calibrateAFE(); //Re-cal analog front end when we change gain, sample rate, or channel
//Serial.print("Arduino Sensors Present");
/* Serial.print("Zero offset: ");
Serial.println(myScale.getZeroOffset());
Serial.print("Calibration factor: ");
Serial.println(myScale.getCalibrationFactor());
*/
/*
Serial.println("Read Time from RTC Example");
if (rtc.begin() == false) {
Serial.println("Something went wrong, check wiring");
}
//Use the time from the Arduino compiler (build time) to set the RTC
//Keep in mind that Arduino does not get the new compiler time every time it compiles. to ensure the proper time is loaded, open up a fresh version of the IDE and load the sketch.
if (rtc.setToCompilerTime() == false) {
Serial.println("Something went wrong setting the time");
}
//Uncomment the below code to set the RTC to your own time
if (rtc.setTime(hund, sec, minute, hour, date, month, year, day) == false) {
// if (rtc.setTime(00, 00, 31, 21, date, month, year, day) == false) {
Serial.println("Something went wrong setting the time");
}
Serial.println("RTC online!");
// Check status of ESP8266
if (esp8266.status() <= 0)
{
while (esp8266.connect(mySSID, myPSK) < 0)
delay(1000);
}
delay(1000);
Serial.println("Wireless Connected!");
Serial.print("Pinging: ");
Serial.println(destServer);
*/
//end setup
}
void loop() {
get_value();
delay(100);
}
void get_value() {
// Check EEPROM to verify that the scale is calibrated
if(settingsDetected == false)
{
Serial.print("\tScale not calibrated. Press 'c'.");
}
//scale calibration / tare checks
if (Serial.available())
{
byte incoming = Serial.read();
if (incoming == 't') //Tare the scale
myScale.calculateZeroOffset();
else if (incoming == 'c') //Calibrate
{
calibrateScale();
}
}
unsigned long currentTime = millis();
myScale.powerUp();
Wire.beginTransmission(qwiicAddress);
Wire.write(COMMAND_GET_VALUE); // command for status
Wire.endTransmission(); // stop transmitting //this looks like it was essential.
Wire.requestFrom(qwiicAddress, 2); // request 1 bytes from slave device qwiicAddress
while (Wire.available()) { // slave may send less than requested
uint8_t ADC_VALUE_L = Wire.read();
uint8_t ADC_VALUE_H = Wire.read();
//lines are needed but I don't know why
ADC_VALUE=ADC_VALUE_H;
ADC_VALUE<<=8;
ADC_VALUE|=ADC_VALUE_L;
// read various sensors
ADC_t = ADC_t + ADC_VALUE;
CO2_t = CO2_t + (myCCS811.getCO2());
TEMP_t = TEMP_t + (myBME280.readTempF());
TVOC_t = TVOC_t + (myCCS811.getTVOC());
PRES_t = PRES_t + (myBME280.readFloatPressure());
ALT_t = ALT_t + (myBME280.readFloatAltitudeFeet());
H2O_t = H2O_t + (myBME280.readFloatHumidity());
WGT_t = WGT_t + myScale.getWeight();
readIndex = readIndex +1;
// if we're at the end of the array...
if (readIndex >= numReadings) {
// calculate the averages:
ADC_a = ADC_t / numReadings;
CO2_a = CO2_t / numReadings;
TEMP_a = TEMP_t / numReadings;
TVOC_a = TVOC_t / numReadings;
PRES_a = PRES_t / numReadings;
ALT_a = ALT_t / numReadings;
H2O_a = H2O_t / numReadings;
WGT_a = WGT_t / numReadings;
// Print function to print data to serial port
printData();
// Reset counters to 0
readIndex = 0;
ADC_t = 0;
CO2_t = 0;
TEMP_t = 0;
TVOC_t = 0;
PRES_t = 0;
ALT_t = 0;
H2O_t = 0;
WGT_t = 0;
}
delay(sketch_delay); // delay in between reads for stability
// Ping WebSite
// Serial.println();
// Serial.println(esp8266.ping(destServer));
}
// uint16_t x=Wire.read();
}
// testForConnectivity() checks for an ACK from an Sensor. If no ACK
// program freezes and notifies user.
void testForConnectivity() {
Wire.beginTransmission(qwiicAddress);
//check here for an ACK from the slave, if no ACK don't allow change?
if (Wire.endTransmission() != 0) {
Serial.println("Check connections. No slave attached.");
while (1);
}
}
void printData()
{
/*
Serial.print("\t Avg. ADC Value:\t\t");
Serial.print(ADC_a, DEC);
Serial.println();
Serial.print("\t Total ADC Value:\t\t");
Serial.print(ADC_t, DEC);
Serial.println();
Serial.println();
Serial.print("\t Avg. CO2 Value:\t\t");
Serial.print(CO2_a, DEC);
Serial.println();
Serial.print("\t Total CO2 Value:\t\t");
Serial.print(CO2_t, DEC);
Serial.println();
Serial.println();
Serial.print("\t Avg. Temp Value:\t\t");
Serial.print(TEMP_a, DEC);
Serial.println();
Serial.print("\t Total Temp Value:\t\t");
Serial.print(TEMP_t, DEC);
Serial.println();
Serial.println();
Serial.print("\t Avg. TVOC Value:\t\t");
Serial.print(TVOC_a, DEC);
Serial.println();
Serial.print("\t Total TVOC Value:\t\t");
Serial.print(TVOC_t, DEC);
Serial.println();
Serial.println();
Serial.print("\t Avg. Pressure Value:\t\t");
Serial.print(PRES_a, DEC);
Serial.println();
Serial.print("\t Total Pressure Value:\t\t");
Serial.print(PRES_t, DEC);
Serial.println();
Serial.println();
Serial.print("\t Avg. Humidity Value:\t\t");
Serial.print(H2O_a, DEC);
Serial.println();
Serial.print("\t Total Humidity Value:\t\t");
Serial.print(H2O_t, DEC);
Serial.println();
Serial.println();
Serial.print("\t Avg. Altitude Value:\t\t");
Serial.print(ALT_a, DEC);
Serial.println();
Serial.print("\t Total Altitude Value:\t\t");
Serial.print(ALT_t, DEC);
Serial.println();
Serial.println();
Serial.print("\t Avg. Weight Value:\t\t");
Serial.print(WGT_a, DEC);
Serial.println();
Serial.print("\t Total Weight Value:\t\t");
Serial.print(WGT_t, 3);
Serial.println();
Serial.println();
Serial.print("\t Number of Readings:\t\t");
Serial.print(numReadings, DEC);
Serial.println();
if (rtc.updateTime() == false) //Updates the time variables from RTC
{
Serial.print("RTC failed to update");
}
String currentTime = rtc.stringTime();
String currentDate = rtc.stringDate();
// Serial.print(currentDate);
// Serial.print(" ");
// Serial.println(currentTime);
*/
// output as CSV format for reading by linux box
// Serial.println(currentDate+" "+currentTime+", "+(String)ADC_a+","+CO2_a+","+TEMP_a+","+TVOC_a+","+PRES_a+","+H2O_a+","+ALT_a+","+WGT_a);
Serial.println((String)ADC_a+","+CO2_a+","+TEMP_a+","+TVOC_a+","+PRES_a+","+H2O_a+","+ALT_a+","+WGT_a);
// Serial.println();
// Serial.println();
}
void printDriverError( CCS811Core::status errorCode )
{
switch ( errorCode )
{
case CCS811Core::SENSOR_SUCCESS:
Serial.print("SUCCESS");
break;
case CCS811Core::SENSOR_ID_ERROR:
Serial.print("ID_ERROR");
break;
case CCS811Core::SENSOR_I2C_ERROR:
Serial.print("I2C_ERROR");
break;
case CCS811Core::SENSOR_INTERNAL_ERROR:
Serial.print("INTERNAL_ERROR");
break;
case CCS811Core::SENSOR_GENERIC_ERROR:
Serial.print("GENERIC_ERROR");
break;
default:
Serial.print("Unspecified error.");
}
}
//Gives user the ability to set a known weight on the scale and calculate a calibration factor
void calibrateScale(void)
{
Serial.println();
Serial.println();
Serial.println(F("Scale calibration"));
Serial.println(F("Setup scale with no weight on it. Press a key when ready."));
while (Serial.available()) Serial.read(); //Clear anything in RX buffer
while (Serial.available() == 0) delay(1000); //Wait for user to press key
myScale.calculateZeroOffset(64); //Zero or Tare the scale. Average over 64 readings.
Serial.print(F("New zero offset: "));
Serial.println(myScale.getZeroOffset());
Serial.println(F("Place known weight on scale. Press a key when weight is in place and stable."));
while (Serial.available()) Serial.read(); //Clear anything in RX buffer
while (Serial.available() == 0) delay(1000); //Wait for user to press key
Serial.print(F("Please enter the weight, without units, currently sitting on the scale (for example '4.25'): "));
while (Serial.available()) Serial.read(); //Clear anything in RX buffer
while (Serial.available() == 0) delay(1000); //Wait for user to press key
//Read user input
float weightOnScale = Serial.parseFloat();
Serial.println();
myScale.calculateCalibrationFactor(weightOnScale, 64); //Tell the library how much weight is currently on it
Serial.print(F("New cal factor: "));
Serial.println(myScale.getCalibrationFactor(), 2);
Serial.print(F("New Scale Reading: "));
Serial.println(myScale.getWeight(), 2);
recordSystemSettings(); //Commit these values to EEPROM
}
void recordSystemSettings(void)
{
//Get various values from the library and commit them to NVM
EEPROM.put(LOCATION_CALIBRATION_FACTOR, myScale.getCalibrationFactor());
EEPROM.put(LOCATION_ZERO_OFFSET, myScale.getZeroOffset());
}
//Reads the current system settings from EEPROM
//If anything looks weird, reset setting to default value
void readSystemSettings(void)
{
float settingCalibrationFactor; //Value used to convert the load cell reading to lbs or kg
long settingZeroOffset; //Zero value that is found when scale is tared
//Look up the calibration factor
EEPROM.get(LOCATION_CALIBRATION_FACTOR, settingCalibrationFactor);
if (settingCalibrationFactor == 0xFFFFFFFF)
{
settingCalibrationFactor = 0; //Default to 0
EEPROM.put(LOCATION_CALIBRATION_FACTOR, settingCalibrationFactor);
}
//Look up the zero tare point
EEPROM.get(LOCATION_ZERO_OFFSET, settingZeroOffset);
if (settingZeroOffset == 0xFFFFFFFF)
{
settingZeroOffset = 1000L; //Default to 1000 so we don't get inf
EEPROM.put(LOCATION_ZERO_OFFSET, settingZeroOffset);
}
//Pass these values to the library
myScale.setCalibrationFactor(settingCalibrationFactor);
myScale.setZeroOffset(settingZeroOffset);
settingsDetected = true; //Assume for the moment that there are good cal values
if (settingCalibrationFactor < 0.1 || settingZeroOffset == 1000)
settingsDetected = false; //Defaults detected. Prompt user to cal scale.
}