Hi all !
Hope everyone is safe and well.
In my last post about the data collecting, I've spoken about the battery issues and wonder why it only lasts half a day.
I then wonder if using a MOSFET could solve the problem, by putting all the sensors in a subcircuit and activate it - letting the current on and off to the sensors by means of a MOSFET.
I did try it. Here are some photos of the testing:
But, because my electronic skills are limited and even with some google searches, I ended up losing the AM2302 sensor - not enough current for it, I guess. I did try with a TIP120 and a 1K resistor to the Arduino PIN, but no luck.
So, I've ended up with the same circuit I had before, already discussed here.
Code
The code is very straightforward, being mostly collecting data from the sensors, creating a package and sending it away, to the wind.
#include <SPI.h> #include <LoRa.h> #include <OneWire.h> #include <DallasTemperature.h> #include <pins_arduino.h> #include "DHT.h" #include "ArduinoLowPower.h" #define debug 0 /* * DS18B20 probe */ #define ONE_WIRE_BUS 2 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature ds18b20sensor(&oneWire); /* * AM2302 */ #define DHTPIN 5 #define DHTTYPE DHT22 //AM2302 DHT dht(DHTPIN, DHTTYPE); /* * TDS */ #define TdsSensorPin A2 #define VREF 3.3 #define SCOUNT 30 //sum of sample point int analogBuffer[SCOUNT]; // store the analog value in the array, read from ADC int analogBufferTemp[SCOUNT]; int analogBufferIndex = 0,copyIndex = 0; float averageVoltage = 0, tdsValue = 0, temperature = 14.20; //temperature will be dynamic /* * Battery */ float battery_voltage = 3.037; // use multimeter /* remove all references to Serial */ void setup() { if (debug) { Serial.begin(115200); } ds18b20sensor.begin(); dht.begin(); pinMode (TdsSensorPin, INPUT); if (!LoRa.begin (868E6)) { if (debug) { Serial.println("Starting LoRa failed!"); } while (1); } delay (3000); //we'll have the LED on when sending and off after pinMode(LED_BUILTIN, OUTPUT); //saving battery USBDevice.detach(); } /* * Get AM2302 temperature */ float *getInsideTemperature() { static float measurements[2]; measurements[0] = dht.readHumidity(); measurements[1] = dht.readTemperature(); return measurements; } /* * DS18b20 values */ float getOutsideTemperature() { //static float waterTemperature; ds18b20sensor.requestTemperatures(); return ds18b20sensor.getTempCByIndex(0); //default value is celcius } /* * get battery values */ float getBatteryStatus() { analogReadResolution(10); analogReference(AR_INTERNAL1V0); // put your main code here, to run repeatedly: int batteryLevel = analogRead(ADC_BATTERY); float voltage = batteryLevel * (battery_voltage / 1023.0); return ((voltage * 100) / battery_voltage); } /* * Get TDS values */ int getTDSmeasurement(float temp) { /* * There's a need to define the analogReference because * this is the default - 3.3v and the TDS sensor depends on it * On the function above, we're changing the reference to be able to, * accurately, measure the battery voltage. A pain this was to find out * what was going on with the TDS values almost 500ppm */ analogReference(AR_DEFAULT); int toExit = 0; while (1) { static unsigned long analogSampleTimepoint = millis(); if (millis()-analogSampleTimepoint > 40U) { //every 40 milliseconds,read the analog value from the ADC analogSampleTimepoint = millis(); analogBuffer[analogBufferIndex] = analogRead(TdsSensorPin); //read the analog value and store into the buffer analogBufferIndex++; if (analogBufferIndex == SCOUNT) analogBufferIndex = 0; } static unsigned long printTimepoint = millis(); if (millis()-printTimepoint > 800U) { printTimepoint = millis(); for (copyIndex=0;copyIndex<SCOUNT;copyIndex++) analogBufferTemp[copyIndex]= analogBuffer[copyIndex]; averageVoltage = getMedianNum(analogBufferTemp,SCOUNT) * (float)VREF / 1024.0; // read the analog value more stable by the median filtering algorithm, and convert to voltage value float compensationCoefficient=1.0+0.02*(temp-25.0); //temperature compensation formula: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.02*(fTP-25.0)); float compensationVolatge=averageVoltage/compensationCoefficient; //temperature compensation tdsValue = (133.42*compensationVolatge*compensationVolatge*compensationVolatge - 255.86*compensationVolatge*compensationVolatge + 857.39*compensationVolatge)*0.5; //convert voltage value to tds value if (toExit == 3) { //we need this because we must wait for values to setle.. return tdsValue; } toExit++; } } } int getMedianNum(int bArray[], int iFilterLen) { int bTab[iFilterLen]; for (byte i = 0; i<iFilterLen; i++) bTab[i] = bArray[i]; int i, j, bTemp; for (j = 0; j < iFilterLen - 1; j++) { for (i = 0; i < iFilterLen - j - 1; i++) { if (bTab[i] > bTab[i + 1]) { bTemp = bTab[i]; bTab[i] = bTab[i + 1]; bTab[i + 1] = bTemp; } } } if ((iFilterLen & 1) > 0) bTemp = bTab[(iFilterLen - 1) / 2]; else bTemp = (bTab[iFilterLen / 2] + bTab[iFilterLen / 2 - 1]) / 2; return bTemp; } void loop() { // turn the LED on when awake: digitalWrite(LED_BUILTIN, HIGH); float *insideValues; //hold am2302 values float outsideValues; //Water temperature float batteryVoltage; //battery float tdsValues; insideValues = getInsideTemperature(); if (debug) { Serial.println ("Inside Temperatures"); Serial.print ("Humidity: "); Serial.println (insideValues[0]); Serial.print ("Temperature: "); Serial.println (insideValues[1]); } outsideValues = getOutsideTemperature(); if (debug) { Serial.print ("Water Temperature: "); Serial.println (outsideValues); } tdsValues = getTDSmeasurement (float(outsideValues)); if (debug) { Serial.print ("TDS: "); Serial.println (tdsValues); } batteryVoltage = getBatteryStatus(); if (debug) { Serial.print("Batery Percentage: "); Serial.println (batteryVoltage); } //let's create the string to send via LoRa /* * It will be like this * insideTemp | insideHum | outsideTemp | TDS values | batteryVoltage */ String LoRaPacket = String(insideValues[1]) + "|" + String(insideValues[0]) + "|" + String(outsideValues) + "|" + String(tdsValues) + "|" + String(batteryVoltage); //send package if (debug) { Serial.println("Sending package..."); } LoRa.beginPacket(); LoRa.print(LoRaPacket); LoRa.endPacket(); // turn the LED off: digitalWrite(LED_BUILTIN, LOW); //for testing, just 5 minutes //LowPower.sleep (20000); LowPower.deepSleep (1800000); }
We start by including all the libraries needed .
Setting the debug flag - testing
#define debug 0
Next, it's declaring all the sensors variables and definitions needed .
Adafruit's High temperature probe
/*
* DS18B20 probe
*/
#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature ds18b20sensor(&oneWire);
AM2302 sensor (DHT22)
/*
* AM2302
*/
#define DHTPIN 5
#define DHTTYPE DHT22 //AM2302
DHT dht(DHTPIN, DHTTYPE);
TDS sensor
/*
* TDS
*/
#define TdsSensorPin A2
#define VREF 3.3
#define SCOUNT 30 //sum of sample point
int analogBuffer[SCOUNT]; // store the analog value in the array, read from ADC
int analogBufferTemp[SCOUNT];
int analogBufferIndex = 0,copyIndex = 0;
float averageVoltage = 0, tdsValue = 0, temperature = 14.20; //temperature will be dynamic
And the battery voltage. This one is the be able to accurately measure the percentage left.
/*
* Battery
*/
float battery_voltage = 3.037; // use multimeter
setup()
The setup() function will run once, every time the Arduino boots
Only start the serial console if debuging
if (debug) {
Serial.begin(115200);
}
Initialize the high temperature probe, the DHT22 sensor and the TDS sensor pin to input
ds18b20sensor.begin();
dht.begin();
pinMode (TdsSensorPin, INPUT);
Next we start the LoRa communications. If not, it just hangs there.
if (!LoRa.begin (868E6)) {
if (debug) {
Serial.println("Starting LoRa failed!");
}
while (1);
}
We wait 3s for things to settle.
We define the BUILT IN LED PIN to output. We will use it to indicate that we are collecting data and sending it.
pinMode(LED_BUILTIN, OUTPUT);
In an effort to save battery, I detach the USB device. Remotely, I don't need it and it saves battery.
USBDevice.detach();
Next, are the functions definitions used to gather all the sensors data.
AM2302
This function will read the sensor and return the data in an array
float *getInsideTemperature() {
static float measurements[2];
measurements[0] = dht.readHumidity();
measurements[1] = dht.readTemperature();
return measurements;
}
DS18b20
This one will read the high temperature probe and return it's value
float getOutsideTemperature() {
//static float waterTemperature;
ds18b20sensor.requestTemperatures();
return ds18b20sensor.getTempCByIndex(0); //default value is celcius
}
Battery
Now, we have a function to read the battery values. This is read the 2xAA batteries connected to the Arduino.
float getBatteryStatus() {
analogReadResolution(10);
analogReference(AR_INTERNAL1V0);
// put your main code here, to run repeatedly:
int batteryLevel = analogRead(ADC_BATTERY);
float voltage = batteryLevel * (battery_voltage / 1023.0);
return ((voltage * 100) / battery_voltage);
}
I've already discussed this one here (battery voltage reading). It was a tricky one, because how the analogReference can mess up the readings of the other sensors. Solution here. It will return the battery voltage percentage.
TDS values
This function will read the TDS values of the water. This was returning crazy values because of the changes on the analogReference. Had to be defined here to work.
/*
* Get TDS values
*/
int getTDSmeasurement(float temp) {
/*
* There's a need to define the analogReference because
* this is the default - 3.3v and the TDS sensor depends on it
* On the function above, we're changing the reference to be able to,
* accurately, measure the battery voltage. A pain this was to find out
* what was going on with the TDS values almost 500ppm
*/
analogReference(AR_DEFAULT);
int toExit = 0;
while (1) {
static unsigned long analogSampleTimepoint = millis();
if (millis()-analogSampleTimepoint > 40U) { //every 40 milliseconds,read the analog value from the ADC
analogSampleTimepoint = millis();
analogBuffer[analogBufferIndex] = analogRead(TdsSensorPin); //read the analog value and store into the buffer
analogBufferIndex++;
if (analogBufferIndex == SCOUNT)
analogBufferIndex = 0;
}
static unsigned long printTimepoint = millis();
if (millis()-printTimepoint > 800U) {
printTimepoint = millis();
for (copyIndex=0;copyIndex<SCOUNT;copyIndex++)
analogBufferTemp[copyIndex]= analogBuffer[copyIndex];
averageVoltage = getMedianNum(analogBufferTemp,SCOUNT) * (float)VREF / 1024.0; // read the analog value more stable by the median filtering algorithm, and convert to voltage value
float compensationCoefficient=1.0+0.02*(temp-25.0); //temperature compensation formula: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.02*(fTP-25.0));
float compensationVolatge=averageVoltage/compensationCoefficient; //temperature compensation
tdsValue = (133.42*compensationVolatge*compensationVolatge*compensationVolatge - 255.86*compensationVolatge*compensationVolatge + 857.39*compensationVolatge)*0.5; //convert voltage value to tds value
if (toExit == 3) { //we need this because we must wait for values to setle..
return tdsValue;
}
toExit++;
}
}
}
This function will take several measurements of the TDS and return the average value. Because the temperature also has influence in the TDS values, this function is called after the water temperature value is taken.
This function uses another one for the average.
Now, we get to the loop function.
loop()
This function will run forever, when the Arduino is running.
First, we turn the BUILT INT LED on to indicate that we're going to start measuring and sending.
digitalWrite(LED_BUILTIN, HIGH);
Next, set some local variables to hold the return values from the several functions.
float *insideValues; //hold am2302 values
float outsideValues; //Water temperature
float batteryVoltage; //battery
float tdsValues;
We start by measuring the enclosure ambience and store them the variable insideValues
If we have debugging active, we show them
insideValues = getInsideTemperature();
if (debug) {
Serial.println ("Inside Temperatures");
Serial.print ("Humidity: ");
Serial.println (insideValues[0]);
Serial.print ("Temperature: ");
Serial.println (insideValues[1]);
}
Next, the water temperature - Adafruit's ds18b20
outsideValues = getOutsideTemperature();
if (debug) {
Serial.print ("Water Temperature: ");
Serial.println (outsideValues);
}
The TDS values and battery voltage
tdsValues = getTDSmeasurement (float(outsideValues));
if (debug) {
Serial.print ("TDS: ");
Serial.println (tdsValues);
}
batteryVoltage = getBatteryStatus();
if (debug) {
Serial.print("Batery Percentage: ");
Serial.println (batteryVoltage);
}
After we have all the values, let's construct the package (a string) to send to the other Arduino MKR 1300 WAN.
We will create a string by concatnating all the values, with the vertical bar - | - as a seperator.
/*
* It will be like this
* insideTemp | insideHum | outsideTemp | TDS values | batteryVoltage
*/
String LoRaPacket = String(insideValues[1]) + "|"
+ String(insideValues[0]) + "|"
+ String(outsideValues) + "|"
+ String(tdsValues) + "|"
+ String(batteryVoltage);
Next, we send the packet.
We start the sequence by using beginPackaget().
LoRa.beginPacket();
We send the string
LoRa.print(LoRaPacket);
And we signal the end of it by using endPacket().
LoRa.endPacket();
We turn the BUILT IN LED off
digitalWrite(LED_BUILTIN, LOW);
and sleep for 30minutes. The deepSleep function with turn everything off, except RTC. The trade off is that it takes the most time to wake up.
LowPower.deepSleep (1800000);
And here we have it. This is the remote MKR 1300 WAN that collects and sends the data.
Next post, my conclusions