Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 2 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
|
This project began shortly after I put my buddy in the crawlspace. Now before you start freaking out and calling law enforcement, my "Crawlspace Buddy" is 4 temperature sensors-1 outdoors, and 3 in the crawlspace at critical locations. Readings from these are used to control a supplemental heater when temps get too cold, preventing frozen pipes in my 110 year old house. There is also an ultrasonic distance sensor mounted to a joist that measures the water level if flooding occurs. That project reignited a long term dream of building my own weather station. It has now become rather complex system, with 3 separate microcontrollers communicating, and finally sending the data to the internet.
Step 1I started with a DHT22 temp/humidity sensor that I already had on hand, and an Arduino Nano.(For the record, I am not a big fan of the DHT sensors, and will eventually be upgrading.) I then built a "Tipping Bucket" rain gauge, immediately followed by an anemometer. Both the anemometer and the rain gauge use Hall effect sensors and magnets for detection. I also added a barometric pressure module and real-time clock. Next, I built a vented box to house the DHT and the weather station circuitry, and mounted all on a fence about 100 feet from my house. All data was only sent by bluetooth to my phone at this stage, so I also added a RF transmitter (433Mhz).
I then built the data receiver. I took a Superheterodyne RF Receiver module, built a parabolic antenna for it, and connected it to to the Arduino MEGA type board that was already on my desk, monitoring my "Crawlspace Buddy", and parsing/processing the data. I decided at that point that the MEGA would become the data/control center for several of my planned home monitoring projects. The MEGA is in the photo below, buried under a MEGA proto shield, and a LCD which is mounted on a UNO shield.
Finally, I needed to implement the last link in the chain of the project-connecting it to the internet, so I could monitor the weather at my house from anywhere in the world, because... well, who doesn't need that capability? After several futile hours with an ESP8266 module and breakout board, I consulted the Arduino Forums, and decided I should seek out a separate ESP8266 based Development board. Based upon several posts I had read, I decided a board by NodeMCU sounded like my best choice, and a search on Amazon found a "Weather Station Kit" that included a NodeMCU ESP8266 board, a DHT11 temp/humidity sensor, and a.96" OLED Display. It would retrieve weather and forecast data from "Weather Underground", correct time from a time server. It would then send temperature and humidity reading from the DHT to the "Thingspeak" website, where it would display on graphs. ("Thingspeak" is a site that allows you to track data over time, and display it on graphs. It is free for up to 3 MILLION data points per month. It also has tools you can use to visualize/analyse your data.) The time/weather data retrieved by the NodeMCU are displayed on a series of 4 scrolling screens on the OLED. This was perfect, as at this point, I did not know how/where I was going to use the data, and this kit showed me much of what I needed to know to get it going. I liked the little weather station kit so well, I decided to just use it as a network interface for the Ardino MEGA, and otherwise keep it doing it's original job as a nifty little weather IOT device. So I established a serial connection between it and the MEGA, mounted it in a little box, and added the necessary code to the NodeMCU program. Once every 10 minutes, it receives a data summary from the MEGA, and submits it to the "Thingspeak" website. Here is a link to my public channel with my live weather data. https://thingspeak.com/channels/463187
As is typical of any complex project, there were/are issues that must be addressed during development. For this project project, power for the outdoor station is a main issue. I am using 3.7v 18650 type batteries (2) to supply power to the monitoring station. I eventually want to incorporate solar charging, but that will come later. For now, I just monitor the voltage, and change the batteries when needed. I have several sets so I always keep a set charged. These batteries have proved to have a mostly linear operating range for my application of 8.4 volts at full charge, to discharged at 7.4v, the point at which the voltage begins dropping at a much quicker rate. I use a buck converter to drop that to a regulated 5v for the system, and this 5 volts is also connected through an OptoMOS relay to the input of a boost regulator to output 9.6v to the transmitter. This supply is only switched on during actual transmission of data packets once every minute. (Each data packet is sent 2 times to help prevent data loss, as this is only a one way RF link.) To maximize battery life, I also implemented sleep/power saving, disconnected the resistors to the LEDs on the Nano, and eliminated the on board regulator, since I am delivering a regulated 5 to the board. This has my power consumption down to about 11 mA when in power save mode, 16 mA during normal operation, and about 24 mA during RF transmission. Since both wind speed and rain readings are generated by interrupts, to keep wind measurement accurate, the Nano only goes into power save when there are no input pulses from the anemometer for a 10 full seconds. With this current setup, I get about 48-60 hours on a set of fully charged batteries before I must change them out. Wind makes a significant difference in the potential battery life, thus the wide range.
ConclusionsThis project has already taken about 3 months of my spare time, and will probably take several more to fully implement. I want to add a weather vane to monitor wind direction. Schematics and code to be posted when time allows. Oh, and the reason for the "Rube Goldberg" title? My son called the weather station a "crazy contraption", and it really is a hodge-podge of circuits/devices, so the name just seemed to fit. One other "feature" that it has, although I rarely turn it on due to massive current draw, is a set of Ultraviolet LEDs. They shine on the anemometer, and one cup of the anemometer is painted with green fluorescent paint. When this is turned on at night, you see a little green ball of light floating back and forth. The purpose of this is to give neighbors and passers-by something to think about!
#include <AltSoftSerial.h>
#include <Adafruit_Sensor.h>
#include <VirtualWire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>
#define TIMEOUT 5000
//SoftwareSerial dataSerial(39, 38); // RX, TX
const int rs = 2, en = 3, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
const String THINGSPEAK_API_WRITE_KEY = "PWLOULJXD5GO8RWP";
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
int alarmLevel = 7000;
int devCount;
int swPin = 11;
int RlyPin = 8;
const int receive_pin = 9;// Pin for RF receiver for weather data from transmitter in yard
//bool OKtoPost;
bool HeatControl; //ON status means temperature controlling is active
bool SuspendData;
float t1, t2, t3, t4;
/********************************************************************/
// Data wire is plugged into pin 12 on the Arduino
#define ONE_WIRE_BUS 12
/********************************************************************/
// Setup a oneWire instance to communicate with any OneWire devices
// (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
/********************************************************************/
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
/********************************************************************/
DeviceAddress Probe01 = { 0x28, 0xFF, 0x67, 0x18, 0x23, 0x17, 0x04, 0xBF }; //Outdoor Sensor
DeviceAddress Probe02 = { 0x28, 0xFF, 0xF5, 0x22, 0x23, 0x17, 0x04, 0xD8 }; //Under Stairs Sensor
DeviceAddress Probe03 = { 0x28, 0xFF, 0xA9, 0xE7, 0x22, 0x17, 0x04, 0x7F }; // Utility Room
DeviceAddress Probe04 = { 0x28, 0xFF, 0x7D, 0x18, 0x23, 0x17, 0x04, 0x7E };// Under Kitchen
long delayMS;
long OutdoorTrig = 25; // Trigger Relay if outdoor temp is below
long OutdoorCut = 26; // Cutoff Relay if outdoor temp is above
long SinkTrig = 35;
long SinkCut = 38;
struct package
{
float temperature = 0;
float humidity = 0;
float barometer = 0;
byte wind = 0;
byte maxWind = 0;
byte windPacketPeak = 0;
float rain = 0;
float rainRate = 0;
float randDate = 0;
float randTime = 0;
float battery = 0;
long packetID = 0;
float CKSum = 1;
};
typedef struct package Package;
Package data;
Package lastData;
struct postingPackage{
float temperature = 0;
float barometer = 0;
float humidity = 0;
uint8_t avgWind = 0;
uint8_t peakWindThisRead = 0;
float rainThisRead = 0;
float rainRate = 0;
float battV = 0;
};
typedef struct postingPackage PostingPackage;
PostingPackage dataToPost;
bool wxMode;
void setup() {
pinMode(13, OUTPUT);
pinMode(RlyPin, OUTPUT);
pinMode(swPin, INPUT);
Serial.begin(115200);
Serial1.begin(57600);
HeatControl = false;
SuspendData = false;
//OKtoPost = false;// becomes true after first temperature readings
//Serial.println("Dallas Temperature IC Control Library Demo");
// Start up the library
sensors.begin();
lcd.begin(16, 2);
// Print a message to the LCD.
lcd.print("Pipe Protector 2");
lcd.setCursor(0, 1);
devCount = sensors.getDeviceCount();
Serial.print("Device Count: ");
Serial.println(devCount);
delayMS = 60000;// WIll increase to 60000 ms when finished. One reading per minute is plenty
randomSeed(analogRead(0));
vw_set_rx_pin(receive_pin);
vw_setup(2000); // Bits per sec
vw_rx_start(); // Start the receiver PLL running
wxMode = true;// for now, we'll start in weather mode
//lastData.
//sensors.requestTemperatures();
}// End Setup
void stateSwitch(void) {
/* if(delayMS >=50000){
delayMS=10000;
}
else{
delayMS=60000;
}*/
}
bool SuspendToggle(void) {
SuspendData = !SuspendData;
return (SuspendData);
}
void sendStatus(void) {
String myStr;// Variable to hold all values as text string for sending over serial port
myStr = OutdoorTrig;
myStr.concat(",");
myStr.concat(OutdoorCut);
myStr.concat(",");
myStr.concat(SinkTrig);
myStr.concat(",");
myStr.concat(SinkCut);
myStr.concat(",");
myStr.concat(delayMS);
myStr.concat(",");
myStr.concat(HeatControl);
myStr.concat(",");
myStr.concat(SuspendData);
Serial.println(myStr);
}
int HighestTemp() {
int retVal;
for (int cnt = 0; cnt < devCount; cnt++) {
//int trVal = GetFTempDec(cnt);
int trVal = sensors.getTempFByIndex(cnt);
if (cnt == 0) {
retVal = trVal;
}
//Serial.println(sensors.getTempFByIndex(cnt));
if (retVal < trVal) {
retVal = trVal;
}
}
// checkRange(retVal);
return (retVal);
}
float LowestTemp() {
float retVal;
for (int cnt = 0; cnt < devCount; cnt++) {
//int trVal = GetFTempDec(cnt);
int trVal = sensors.getTempFByIndex(cnt);
if (cnt == 0) {
retVal = trVal;
}
//Serial.println(sensors.getTempFByIndex(cnt));
if (retVal > trVal) {
retVal = trVal;
}
}
// checkRange(retVal);
return (retVal);
}
float printTemperature(DeviceAddress deviceAddress)
{
float retVal = sensors.getTempF(deviceAddress);
if (retVal == -196.60)
{
Serial.print(F("<Error>"));
}
else
{
Serial.print(retVal);
Serial.print(F(" F "));
return (retVal);
//Serial.print(DallasTemperature::toFahrenheit(tempC));
}
}// End printTemperature
void serial1Check(){
String myCmd;
if(Serial1.available() > 0){
Serial.println("You are there");
myCmd = Serial1.readString();
Serial.println(myCmd);
}
Serial.println("You ARE LEAVING!!");
}
void serialCheck(){
if (Serial.available() >> 0) {
String NewVal = Serial.readString();
lcd.clear();
if (NewVal == "+") {
//lcd.print("HEAT ON");
// delay( 2000);
HeatControl = true;
}
else if (NewVal == "-") {
lcd.print(F("HEAT OFF"));
HeatControl = false;
//delay (2000);
}
else if (NewVal == "S") {
sendStatus;
//delay (2000);
}
else {
long secs = NewVal.toInt();
delayMS = secs * 1000;
//lcd.clear();
//lcd.print(delayMS);
//delay (2000);
}
// digitalWrite(13,HeatControl);
}
}
void pipeProtect(){
sensors.requestTemperatures(); // Send the command to get temperature readings
/********************************************************************/
bool RelayOn;
RelayOn = digitalRead(RlyPin);
digitalWrite(13, HeatControl); // Board LED indicates Heat Control Status
if (HeatControl == true) {
//if (!(t1> OutdoorTrig && t4>SinkTrig)){
//if (digitalRead(RlyPin)==false){// Heat status is OFF
if (t1 > OutdoorCut) {
RelayOn = false;
}
if (t4 > SinkCut) {
RelayOn = false;
}
if (t1 < OutdoorTrig) {
RelayOn = true;
}
if (t4 > 41) { //Absolute cutoff if temp at sink is above 41. No need to heat more that?
RelayOn = false;
}
if (t4 < SinkTrig) {
RelayOn = true;
}
digitalWrite(RlyPin, RelayOn);
// }
}
else {
digitalWrite(RlyPin, false);
}
if (SuspendData == false) {
Serial.println(F(" ** Temperatures Read **"));
Serial.print(F("Outdoor temp. is: "));
t1 = printTemperature(Probe01);
Serial.println();
Serial.print(F("Near Furnace temp. is: "));
t2 = printTemperature(Probe02);
Serial.println();
Serial.print(F("Under Utility Room temp. is: "));
t3 = printTemperature(Probe03);
Serial.println();
Serial.print(F("Under Sink temp. is: "));
t4 = printTemperature(Probe04);
String OutLogic;
OutLogic = "*&*";
if (digitalRead(RlyPin) == HIGH) {
OutLogic.concat("+");
}
else {
OutLogic.concat("-");
}
Serial.println();
Serial.println();
Serial.println(OutLogic);
Serial.println();
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(F("O="));
lcd.print(t1);
lcd.print(F(" F="));
lcd.print(t2);
lcd.setCursor(0, 1);
lcd.print(F("U="));
lcd.print(t3);
lcd.print(F(" K="));
lcd.print(t4);
}
void loop(void)
{
static bool notFirstIteration;
static long nextOneWireMillis;
if (!notFirstIteration) {// Occurs on first run.
nextOneWireMillis = millis() + delayMS;
pipeProtect();
notFirstIteration = true;
}
if (wxMode == true) {// If monitoring weather station
//Serial.println("WXMODE TRUE");
rxSubLoop();
delay(100);
if(Serial1.available()>0){
Serial.println("Something Returned I think");
while(Serial1.available()){
char c = Serial1.read();
Serial.print(c);
}
Serial.println("\nDDDD\n");
}
}
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
/********************************************************************/
//Serial.print("**Requesting temperatures...");
serialCheck();// Check serial ports for incoming commands
//serial1Check();
//Serial.println("Out of Serial1Check");
if (nextOneWireMillis <= millis()) { // This section is for pipe protector portion of program
nextOneWireMillis = millis() + delayMS;
pipeProtect();
// You can have more than one DS18B20 on the same bus.
// 0 refers to the first IC on the wire
// delay(delayMS+200);
}
}
bool CheckData() {
float CkVal = data.humidity + data.temperature + data.barometer + data.wind + data.rain + data.maxWind + data.rainRate + data.packetID + data.windPacketPeak + data.randDate + data.randTime + data.battery;
bool ckB = ((CkVal == data.CKSum) && (data.CKSum > 30000));
if (ckB == false) {
data.temperature = 0;
data.humidity = 0;
data.barometer = 0;
data.wind = 0;
data.rain = 0;
data.CKSum = 1;
}
return (ckB);
}
float rainFallRate() {
}// END rainFallRate() **********************
float windChill(float mTempF, float mMPH) {// Calculate wind chill if temp <50, wind >3
float Twc;
if ((mTempF < 50.0) && (mMPH > 3.0)) {
Twc = 35.74 + 0.6215 * mTempF - 35.75 * pow(mMPH, 0.16) + 0.4275 * mTempF * pow(mMPH, 0.16);
}
else
{
Twc = mTempF;// return temp unchanged if no wind chill
}
return (Twc);
}// End windChill()---------------------------------------------
float RandianDate(byte dayOfMonth, byte month, byte year) { // Date to integer format - 01-01-1980 becomes 111180
String myStr;
byte myDay = dayOfMonth + 10;// Adding 10 to values to make all 2 digit. No need to do the years.
byte myMonth = month + 10;
byte myYear = year + 10;
//month += 10;
myStr.concat(myMonth);
myStr.concat(myDay);
myStr.concat(myYear);
return (myStr.toFloat());
//return(0);
}
float RandianTime(byte hours, byte minutes, byte seconds) { // Time to integer format - 14:56:03 becomes 145603
String myStr;
hours += 10; // Adding 10 to these values to ensure all are 2 digits.
minutes += 10; // Will subtract 10 again when decoded
seconds += 10;
myStr.concat(hours);
myStr.concat(minutes);
myStr.concat(seconds);
return (myStr.toFloat());
//return(0);
}
String RandianToDate(float RandDate) {
String msg = "";
msg.concat(RandDate);
String token;
String myDt;
int trans;
for ( int cnt = 0; cnt < 6 ; cnt++) {
token.concat(msg.charAt(cnt));
if ((cnt == 1) || (cnt == 3)) {
trans = token.toInt() - 10;
if (trans < 10) {
myDt.concat("0");
}
myDt.concat(trans);
myDt.concat("-");
token = "";
}
else if (cnt == 5) {
trans = token.toInt() - 10;
myDt.concat("20");
myDt.concat(trans);
}
}
return (myDt);
}
String RandianToTime(float RandTime) {
String msg = "";
msg.concat(RandTime);
String token;
String myDt;
int trans;
for ( int cnt = 0; cnt < 6 ; cnt++) {
token.concat(msg.charAt(cnt));
if ((cnt == 1) || (cnt == 3) || (cnt == 5)) {
trans = token.toInt() - 10;
if (trans < 10) {
myDt.concat("0");
}
myDt.concat(trans);
if (cnt < 5) {
myDt.concat(":");
}
token = "";
}
}
return (myDt);
}
void showReport(void) {
Serial.print(F("\nTemperature: "));
Serial.print(data.temperature);
Serial.print(F(" Degrees F\nHumidity: "));
Serial.print(data.humidity);
Serial.print(F("% \nBarometer: "));
Serial.print(data.barometer);
Serial.print(F(" Inches Hg \nWind (current): "));
Serial.print(data.wind);
Serial.print(F(" Mph\n Feels like "));
Serial.print(windChill(data.temperature, data.wind));
Serial.print(F(" Wind Chill\nPeak Wind this Read: "));
Serial.print(data.windPacketPeak);
Serial.print(F(" Mph\nPeak Wind Today: "));
Serial.print(data.maxWind);
Serial.print(F(" Mph\nRainfall: "));
Serial.print(data.rain);
Serial.print(F(" Inches\nRain Rate: "));
Serial.print(data.rainRate);
Serial.print(F(" Inches per Hour\n\nPacket ID: "));
Serial.print(data.packetID);
//Serial.print("\nCheck Sum: ");
//Serial.println(data.CKSum);
Serial.print(F("\nDate: "));
Serial.println(RandianToDate(data.randDate));
Serial.print(F("Time: "));
Serial.println(RandianToTime(data.randTime));
Serial.println("");
Serial.print(F("Battery: "));
Serial.print(data.battery);
Serial.println(F(" Volts\n\n****************************************\n"));
}
void dataPrep() { // Process incoming data, prepare dataToPost for transmission to network interface. Average some values
//static bool firstSeries;
static int avgCounter;
static float tempAvg;
static int windAvg;
static int windPeak;
static int windRing[10];
static float tempRing[10];// the "Ring arrays keep the last 10 readings for averaging, constatly rotating through
static float rainVals[10];
static float barVals[10];
static float humVals[10];
float tempTot;
float batt;
float rainfallRate;
static int clearCount;
if (avgCounter == 0) { // Reset some vals for next post
windPeak = 0;
for (int ct; ct < 10 ; ct++) {
rainVals[ct] = 0;
tempRing[ct] = 0;
tempTot = 0;
}
}
windRing[avgCounter] = data.wind;
if (data.windPacketPeak > windPeak) {
windPeak = data.windPacketPeak;
}
tempRing[avgCounter] = data.temperature;
humVals[avgCounter] = data.humidity;
barVals[avgCounter] = data.barometer;
rainVals[avgCounter] = data.rain;// - lastData.rain;// Rain measurement from station is a daily total-we need to calculate rain per package
String trTime = RandianToTime(data.randTime);
bool saveNOW = (trTime.charAt(4) == '0') && (avgCounter > 0);// will cause update at least every 10 packets, but should keep it at 00 minutes
if ((avgCounter == 9)|| (saveNOW)) { //Last of data packets. Prepare values, insert into dataToPost, then send to Network Interface
float rainTot;
float windTot;
float humTot;
float barTot;
// int tempTot;
for (int ct = 0; ct < avgCounter+1 ; ct++) {
rainTot = rainVals[ct];// for rain, just use last reading sent. No need to average, will send as total for day
windTot += windRing[ct];
tempTot += tempRing[ct];
barTot += barVals[ct]-28.00;// Subtracting out lowest likely reading. Averaging difference to increase accuracy in the event of errors
humTot += humVals[ct];
/* Serial.println();
Serial.print(tempRing[ct]);
Serial.print(" ");
Serial.println(tempTot);
//Serial.println();
*/
}
avgCounter ++;// increment for calculating averages (due to zero based array counter)
dataToPost.battV = data.battery;
dataToPost.temperature = tempTot / float(avgCounter);
dataToPost.barometer = (barTot / float(avgCounter))+28.00;
dataToPost.humidity = humTot / float(avgCounter);
dataToPost.avgWind = windTot / (avgCounter);
dataToPost.peakWindThisRead = windPeak;
dataToPost.rainThisRead = rainTot;
dataToPost.rainRate = data.rainRate;
Serial.println(F("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"));
Serial.print(F("\nData to Post:\n"));
Serial.println(dataToPost.temperature);
Serial.println(dataToPost.barometer);
Serial.println(dataToPost.humidity);
Serial.println(dataToPost.avgWind);
Serial.println(dataToPost.peakWindThisRead);
Serial.println(dataToPost.rainThisRead);
Serial.println(F("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"));
delay (2000);
String url = "@";// add start character
url += String(dataToPost.temperature);
url += ",2=";
url += String(dataToPost.humidity);
url += ",3=";
url += String(dataToPost.avgWind);
url += ",4=";
url += String(dataToPost.peakWindThisRead);
url += ",5=";
url += String(dataToPost.rainThisRead);
url += ",6=";
url += String(dataToPost.barometer);
url += ",7=";
url += String(dataToPost.rainRate);
url += ",8=";
url += String(dataToPost.battV);
url += "~";// add end character
String datStr;
datStr.concat(dataToPost.temperature);
datStr.concat(",");
datStr.concat(dataToPost.humidity);
datStr.concat(",");
datStr.concat(dataToPost.avgWind);
datStr.concat(",");
datStr.concat(dataToPost.peakWindThisRead);
datStr.concat(",");
datStr.concat(dataToPost.rainThisRead);
datStr.concat(",");
datStr.concat(dataToPost.barometer);
Serial.println (datStr);
Serial1.print(url);// This line is where data is submitted to Network Interface (NodeMCU)
delay(10000); // Lets wait a little while then
Serial1.print(url);// Send again to minimize data gaps due to errors in transmission
avgCounter = -1;
// Serial.print("IN the Test");
}
avgCounter ++;
if (avgCounter > 9) {
avgCounter = 0;
}
}
void simulateData() {
static long cnt;
data.temperature = t1;
data.humidity = t2;
data.barometer = t3;
data.wind = random(t4);
data.maxWind = t4 + random(0, 21);
data.windPacketPeak = t4 + random(0, 21);
data.rain = t1;
data.rainRate = t2;
data.randDate = 101218;
data.randTime = 131313;
data.packetID = cnt;
cnt ++;
}
void rxSubLoop(void) {
// Serial.println("In the rxSubLoop");
uint8_t buf[sizeof(data)];
uint8_t buflen = sizeof(data);
//Serial.println("in Loop");
//vw_wait_message();
if (vw_have_message()) // Is there a packet for us?
{
vw_get_message(buf, &buflen);
memcpy(&data, &buf, buflen);
// delay(500);
bool myCkV = CheckData();// Check for valid Packet-return true if valid
if (myCkV == true) {
if (data.packetID != lastData.packetID) {
dataPrep();
showReport();
lastData = data;
}
}
else {
Serial.print(F("Bad Packet"));
Serial.print(F("\n\n****************************************\n\n"));
}
}
/*else{//A place to add a line for data simlation-for devel purposes
// This ELSE clause will be commented out after new transmitter comes in
simulateData();
dataPrep();
showReport();
lastData = data;
delay(30000);// want data packets simulated every 1 minute
//Serial.println("Exiting RX_Lib");
}*/
}
#include <VirtualWire.h>
#include <Time.h>
#include <TimeLib.h>
#include <dht.h>
#include <Adafruit_MPL3115A2.h>
#include <Wire.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#define DS3231_I2C_ADDRESS 0x68
#define OFF false
#define ON true
#define TOGGLE 2// Use if you want to toggle a value instead of just ON/OFF/TRUE/FALSE-for radio function
dht myDHT; // create instance of DHT sensor
Adafruit_MPL3115A2 baro = Adafruit_MPL3115A2();// Create instance of Barometric sensor
time_t myTime;// Is System Time Object
time_t upTime;// Contains time System Timer was set
const uint8_t ledPin = 13;
const uint8_t transmit_pin = 11;
const uint8_t rainPin = 2;// Wind and rain readings handled by interrupts
const uint8_t windPin = 3; // Wind and rain readings handled by interrupts
const uint8_t dhtPin = 4;
const uint8_t rfPower = 5;// control line for RF Power Relay
const float rainPerTip = .0202;// Specific to dimensions of rain gauge built
const int timeComp = -1516;// Value for periodic time compensation. After this many seconds,
// 1 second adjustment is added or subtracted for on the fly Sys Clock a Correction
// Value was computed after 24 hours running, to determine approximate adjustment value.
// This may be further refined as I use unit for longer periods of time.
float rainInches;
volatile long rainLastMillis; // Millis of previous rain "tip"
volatile long rainMillis; // Millis of current rain tip
float windAdj = 9.5; //default value for wind speed (Anemometer) adjustment
volatile long windLastMillis; // last millis reading from anemometer
volatile long revTime; // time in millis of current anemometer revolution
volatile int f_wdt = 1;
volatile long nextSleepMillis;
volatile long wokeUpMillis;
long xmitRate = 60000; // xmitRate is how often packets will be transmitted, in milliseconds.
int tipCount; // Rain gauge bucket "tips", .0202 inch per tip for my gauge. Resets at midnight
float tempDHT; // Temperature from DHT
float humDHT; // Humidity from DHT
float curWindMPH; // Calculated wind speed
float topWind;
bool needsReset; // Set to true at 11:59 PM, to trigger reset at midnight
bool baroSensorPresent; // Set to true if barometric pressure sensor is detected at startup
long timeCompTrigger;// value of next time compensation in millis
bool serialDump;
bool testMode;
byte testMinutes = 2;
byte ckHour;// ckHour and ckMinute are used to see if time to reset (at midnight)
byte ckMinute;
byte ckSecond;
volatile bool beenAsleep;
struct package
{
float temperature ;
float humidity ;
float barometer ;
byte wind ;
byte maxWind;
byte windPacketPeak;
float rain ;
float rainRate;
float randDate;
float randTime;
float battery;
long packetID;
float CKSum ;
};
typedef struct package Package;
Package data;
//DHT dht(DHTPIN, DHTTYPE);
void setup()
{
testMode = true; // Will start in test mode, this will time out in 5 minutes. Helps aim antenna
beenAsleep = false;
serialDump = testMode;
// Initialise the IO and ISR
vw_set_tx_pin(transmit_pin);
vw_set_ptt_inverted(true); // Required for DR3100
vw_setup(2000); // Bits per sec
Serial.begin(9600);
Serial.println(F("Working to setup"));
Wire.begin();
//serialDump = false;
//serialDump = true;
baroSensorPresent = true;
if (! baro.begin()) {
Serial.println(F("Couldnt find sensor"));
baroSensorPresent = false;
//return;
}
else {
baro.write8(0x2D, 0x23);// calibration for altitude
}
pinMode (rainPin, INPUT_PULLUP);
pinMode (dhtPin, INPUT);
pinMode(ledPin, OUTPUT);
pinMode (windPin, INPUT_PULLUP);
pinMode(rfPower, OUTPUT);
attachInterrupt(0, rain_Count, FALLING);// Define interrupt pins and processes required
attachInterrupt(1, wind_Count, FALLING);
//needsReset = true;// used in testing only
tipCount = 0;// Initial value 0 "tips", 0 inches of rain
rainInches = 0;
revTime = 0;
readDHT();
/*** Setup the WDT ***/
/* Clear the reset flag. */
MCUSR &= ~(1 << WDRF);
/* In order to change WDE or the prescaler, we need to
set WDCE (This will allow updates for 4 clock cycles).
*/
WDTCSR |= (1 << WDCE) | (1 << WDE);
/* set new watchdog timeout prescaler value */
WDTCSR = 1 << WDP0 | 1 << WDP3; /* 8.0 seconds */
/* Enable the WD interrupt (note no reset). */
WDTCSR |= _BV(WDIE);
delay(2000);// Delay to prevent re-read of DHT prematurely after startup
radio(OFF);
} // END Setup() *******************
//!!!!!!!!!!!!!!!!!! The following 3 functions are used by RTC. DO NOT CHANGE !!!!!!!!!!!!!!!!!!!
byte decToBcd(byte val)
{
return ( (val / 10 * 16) + (val % 10) );
}
// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
return ( (val / 16 * 10) + (val % 16) );
}
void readRTCTime(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0); // set DS3231 register pointer to 00h
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
// request seven bytes of data from DS3231 starting from register 00h
*second = bcdToDec(Wire.read() & 0x7f);
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f);
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());
}
// !!!!!!!!!!!!!!!! END OF DO NOT CHANGE SECTION !!!!!!!!!!!!!!!!!!!!!!!!!!!
// ** INTERRUPT PROCESSOR SECTION **
//*************************************************************
void rain_Count() { // Interrupt Process for Rain Gauge Tip Counting
rainLastMillis = rainMillis;
rainMillis = millis();
tipCount ++; // All we aneed to do here is increment the tip counter
} // END of Interrupt Processor rain_Count --------------------
// ************************************************************
//*************************************************************
void wind_Count() { // Interrupt Process for Wind measuring
if (beenAsleep) {
/*
nextSleepMillis = millis() + 10000;// wait 10 seconds to see if another wind pulse comes in before sleeping again.
windLastMillis = nextSleepMillis - 10000;
*/
//long adj = 15;
long myVal = millis();
//long newRevTime = myVal - windLastMillis;
nextSleepMillis = myVal + 9000;// stay awake for a little while to wait for more wind pulses.
if (1) { //(((curWindMPH > 8) && (newRevTime < revTime/2)) || ((curWindMPH <8) && (newRevTime < revTime/3))) {// Probably Noise-ignore pulse
}
else {
revTime = myVal - windLastMillis;
windLastMillis = myVal;
nextSleepMillis = myVal + 12000;
}
}
else {
long myVal = millis();
long newRevTime = myVal - windLastMillis;
if (((curWindMPH > 8) && (newRevTime < revTime/2)) || ((curWindMPH <8) && (newRevTime < revTime/3))) {// Probably Noise-ignore pulse
}
else {
revTime = myVal - windLastMillis;
windLastMillis = myVal;
}
}
//Serial.println("TRIGGERED");
//}
} // END of Interrupt Processor wind_Count
ISR(WDT_vect)// Watchdog timer interrupt routine
{
if (f_wdt == 0)
{
f_wdt = 1;
}
else
{
//Serial.println("WDT Overrun!!!");
}
}
// ************************************************************
void sleepNow()
{
Serial.println(F("Going to sleep now"));
// Choose our preferred sleep mode:
set_sleep_mode(SLEEP_MODE_IDLE);
// Set sleep enable (SE) bit:
sleep_enable();
power_adc_disable();
power_spi_disable();
power_timer0_disable();
power_timer2_disable();
power_twi_disable();
// Put the device to sleep:
sleep_mode();
// Upon waking up, sketch continues from this point.
sleep_disable();
Serial.println(F("GOOD MORNING VIET NAM!"));
power_all_enable();
}
void radio(uint8_t action) {
int pwrOnDly = 500;
if (action < 2) {
digitalWrite(rfPower, action); // If action = ON or OFF, comply
if (action == ON) {
delay(pwrOnDly);// Give radio a moment to power up before using0
}
}
else {
digitalWrite(rfPower, !digitalRead(rfPower)); // Otherwise Toggle
delay(pwrOnDly);// see above
}
}
float readBattery(void) {
float chipVoltage = 5.096;// Set this value to the measured voltage on 5 v pin on Arduino, USING EXPECTED POWER SUPPLY!!
int sum = 0; // sum of samples taken
unsigned char sample_count = 0; // current sample number
// take a number of analog samples and add them up
while (sample_count < 10) {
sum += analogRead(A1);
sample_count++;
delay(10);
}
float voltage = ((float)sum / 10.0 * chipVoltage ) / 1024.0;
// Serial.println(voltage * 1.922);
return (voltage * 2);
}
void readDHT() {
static long nextReadMillis;
if (millis() >= nextReadMillis) {
int readData = myDHT.read22(dhtPin);
tempDHT = myDHT.temperature * 1.8 + 32;
humDHT = myDHT.humidity;
nextReadMillis = millis() + 2000;// DHT can only be read every 2 seconds. This variable prevents premature readings
}
}// END of readDHT()-------------------------------------------
void rain_Set(float myVal) {// Set rain to a specific value, used if restarting during day with rain
rainInches = myVal;
tipCount = rainInches / rainPerTip;
rainLastMillis = 0;
Serial.println("");
Serial.print(F("Rainfall Measurement SET to "));
Serial.print(myVal);
Serial.println(F(" Imches\n"));
}// END of rain_Set()-----------------------------------------
void rain_Reset() {
tipCount = 0;
rainInches = 0;
rainLastMillis = 0;
Serial.println("");
Serial.println(F("Rainfall Measurement RESET!\n"));
Serial.println(F(""));
}// END of rain_Reset()-----------------------------------------
void wind_Reset() {
topWind = 0;
Serial.println();
Serial.println(F("Maximum Wind RESET!\n"));
Serial.println();
}// END of wind_Reset()-----------------------------------------
void all_Reset() {
wind_Reset();
rain_Reset();
}// END of all()-----------------------------------------
float RandianDate(byte dayOfMonth, byte month, byte year) { // Date to integer format - 01-01-1980 becomes 111180
String myStr;
byte myDay = dayOfMonth + 10;// Adding 10 to values to make all 2 digit. No need to do the years.
byte myMonth = month + 10;
byte myYear = year + 10;
//month += 10;
myStr.concat(myMonth);
myStr.concat(myDay);
myStr.concat(myYear);
return (myStr.toFloat());
//return(0);
}
float RandianTime(byte hours, byte minutes, byte seconds) { // Time to integer format - 14:56:03 becomes 145603
String myStr;
hours += 10; // Adding 10 to these values to ensure all are 2 digits.
minutes += 10; // Will subtract 10 again when decoded
seconds += 10;
myStr.concat(hours);
myStr.concat(minutes);
myStr.concat(seconds);
return (myStr.toFloat());
//return(0);
}
String RandianToDate(float RandDate) {
String msg = "";
msg.concat(RandDate);
String token;
String myDt;
int trans;
for ( int cnt = 0; cnt < 6 ; cnt++) {
token.concat(msg.charAt(cnt));
if ((cnt == 1) || (cnt == 3)) {
trans = token.toInt() - 10;
if (trans < 10) {
myDt.concat("0");
}
myDt.concat(trans);
myDt.concat("-");
token = "";
}
else if (cnt == 5) {
trans = token.toInt() - 10;
myDt.concat("20");
myDt.concat(trans);
}
}
return (myDt);
}
String RandianToTime(float RandTime) {
String msg = "";
msg.concat(RandTime);
String token;
String myDt;
int trans;
for ( int cnt = 0; cnt < 6 ; cnt++) {
token.concat(msg.charAt(cnt));
if ((cnt == 1) || (cnt == 3) || (cnt == 5)) {
trans = token.toInt() - 10;
if (trans < 10) {
myDt.concat("0");
}
myDt.concat(trans);
if (cnt < 5) {
myDt.concat(":");
}
token = "";
}
}
return (myDt);
}
void stampTime(void) {
String dateStr;
String timeStr;
byte second;
byte minute;
byte hour;
byte dayOfWeek;
byte dayOfMonth;
byte month;
byte year;
readRTCTime(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month,
&year);
ckMinute = minute;
ckHour = hour;
ckSecond = second;
data.randDate = RandianDate(dayOfMonth, month, year);
data.randTime = RandianTime(hour, minute, second);
/*
Serial.print("Hour: ");
Serial.print(hour);
Serial.print(" Minute: ");
Serial.print(minute);
Serial.print(" Second ");
Serial.print(second);
Serial.print("\n\n");
Serial.println(data.randTime);
//Serial.println(data.randTime);
Serial.println(data.randDate);
*/
}
void serialReport(void) {
Serial.print("\Temperature: ");
Serial.print(data.temperature);
Serial.print(F(" Degrees F\nHumidity: "));
Serial.print(data.humidity);
Serial.print(F("% \nBarometer: "));
Serial.print(data.barometer);
Serial.print(F(" Inches Hg \nWind (current): "));
Serial.print(data.wind);
Serial.print(F(" Mph\nPeak Wind this Read: "));
Serial.print(data.windPacketPeak);
Serial.print(F(" Mph\nPeak Wind Today: "));
Serial.print(data.maxWind);
Serial.print(F(" Mph\nRainfall: "));
Serial.print(data.rain);
Serial.print(F(" Inches\nRain Rate: "));
Serial.print(data.rainRate);
Serial.print(F(" Inches per Hour\nPacket ID: "));
Serial.print(data.packetID);
Serial.print(F("\nCheck Sum: "));
Serial.println(data.CKSum);
Serial.print(F("Date: "));
Serial.print(RandianToDate(data.randDate));
Serial.print(F("\nTime: "));
Serial.println(RandianToTime(data.randTime));
Serial.print(F("Battery Voltage: "));
Serial.print(data.battery);
Serial.print(F(" Vdc\n\n"));
if (testMode == true) {
Serial.print(F("** TEST MODE ** TEST MODE **\n\n"));
}
}
float CheckSum() { // A Check to ensure valid packet received. Basic Check Sum (Sum of all fields-all are numeric)
return (data.temperature + data.humidity + data.barometer + data.wind + data.rain + data.rainRate + data.maxWind + data.packetID + data.windPacketPeak + data.randDate + data.randTime + data.battery);
} // END CheckSum **************************
void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte
dayOfMonth, byte month, byte year)
{
// sets time and date data to DS3231
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0); // set next input to start at the seconds register
Wire.write(decToBcd(second)); // set seconds
Wire.write(decToBcd(minute)); // set minutes
Wire.write(decToBcd(hour)); // set hours
Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday)
Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31)
Wire.write(decToBcd(month)); // set month
Wire.write(decToBcd(year)); // set year (0 to 99)
Wire.endTransmission();
}
void sendData() { // Send data packet by RF transmitter
radio(ON);
digitalWrite(ledPin, HIGH); // Flash a light to show transmitting
//readSensors(); now doing in loop, not here - prevents differences in "same" packet sends
vw_send((uint8_t *)&data, sizeof(data));
vw_wait_tx(); // Wait until the whole message is gone
digitalWrite(ledPin, LOW);
// delay(2000);
radio(OFF);
}// END sendData *******************
void readSensors()// Read all sensors and build data package
{
readDHT();
data.humidity = humDHT;
data.temperature = tempDHT;
data.barometer = baroReading();
data.wind = Wind_Speed();
data.maxWind = topWind;
data.rainRate = rainFallRate();
data.rain = rainInches;
data.battery = readBattery();
data.CKSum = CheckSum();
} // END readSensors() ******************************
float rainFallRate() {
if (tipCount > 0) {
float tipDuration = rainMillis - rainLastMillis;
if (millis() > rainMillis + 600000) {// no new tips in last 10 minutes
tipDuration = millis() - rainLastMillis;
}
if (tipDuration > 120 * 60000) { // tip takes over 2 hour2, effective rate 0 for our purposes
return (0);
}
else {
float tipSeconds = tipDuration / 1000.00;
float tipsPerHour = 3600.00 / tipSeconds;
return ( rainPerTip * tipsPerHour);
}
}
else {
return (0);
}
}
float baroReading() {
if (baroSensorPresent == true) {
float pascals = baro.getPressure();
// Our weather page presents pressure in Inches (Hg)
// Use http://www.onlineconversion.com/pressure.htm for other units
// Serial.print(pascals/3377); Serial.println(" Inches (Hg)");
return ((pascals / 3377.00) + 0.87); // 0.87 is barometric adjustment
}
else {
return (99.99); // bogus reading if sensor not detected.
}
}// End baroReading() ----------------------------------------------
/*
float rainfallRate() {
float tipDuration = rainMillis - rainLastMillis;
float tipSeconds = tipDuration / 1000;
float tipsPerHour = 3600 / tipSeconds;
float myRainRate = rainPerTip * tipsPerHour;
return (myRainRate);
}// END of RainfallRate() ************************
*/
int Wind_Speed() {
float retValF = 717.00 * windAdj / (revTime);
if (millis() >= windLastMillis + 10000) { // No wind pulses in 10 seconds, wind is effectively 0
retValF = 0.00;
}
curWindMPH = retValF;
if (data.windPacketPeak < retValF){
data.windPacketPeak = retValF;
}
if (topWind<data.wind){
topWind = data.wind;
}
return retValF;
}// END of Wind_Speed() ---------------------------------------------
float baroTempReading() {
float tempF = baro.getTemperature() * 1.8 + 32;
//Serial.println(tempF);
//Serial.println(tempF);
return (tempF);
// Serial.print(tempC); Serial.println("*C");
}// End baroTempReading() -----------------------------------------
void showHelp() {
Serial.println(F("- Valid Serial Commands -"));
Serial.println(F("W ## - Sets Max Wind to ##"));
Serial.println(F("I ## - Interval in seconds - Sets Packet Interval"));
Serial.println(F("R ## - Sets Rainfall"));
Serial.println(F("+ Wind Adj up 1"));
Serial.println(F("- Wind Adj down 1"));
Serial.println(F("XW - Max Wind Reset"));
Serial.println(F("XR - Rainfall Reset"));
Serial.println(F("XX - ALL Reset"));
Serial.println(F("S - Toggle Serial Dump Mode"));
Serial.println(F("\n** ? - Shows This Help Screen **"));
}// END showHelp()------------------------------------------------
void checkSerial(void) {
if (Serial.available() > 0) { // Check for and Begin Processing of incoming Serial commands
String myCmd = Serial.readString();
char myChr = myCmd.charAt(0);
if ((myChr == 'W') || (myChr == 'w')) { // "w" should be followed by a number to set max wind to
myCmd.replace("w", "");
myCmd.replace("W", "");
myCmd.replace(",", "");
myCmd.replace(" ", "");
float newWind = myCmd.toFloat();
data.maxWind = newWind;
topWind = newWind;
}
if ((myChr == 'I') || (myChr == 'i')) { // "I" should be followed by a number to set data transmission interval
myCmd.replace("i", "");
myCmd.replace("I", "");
myCmd.replace(",", "");
myCmd.replace(" ", "");
float newInterval = myCmd.toFloat();
xmitRate = newInterval * 1000;// entry will be in seconds, converting to millis()
}
if ((myChr == 'R') || (myChr == 'r')) { // "R" should be followed by a number to set rainfall
myCmd.replace("r", "");
myCmd.replace("R", "");
myCmd.replace(",", "");
myCmd.replace(" ", "");
float rcmd = myCmd.toFloat();
if (rcmd > 0) { // If number is entered, set rainfall to that, else add 1 tip
rain_Set(rcmd);
}
else {
rain_Count();
}
}
if ((myChr == 'S') || (myChr == 's')) { // toggles Serial Dump mode
serialDump = !serialDump;
}
if (myChr == '+') {
windAdj ++;
Serial.print(F("Wind Adj = "));
Serial.println(windAdj);
}
if (myChr == '-') {
windAdj --;
Serial.print(F("Wind Adj = "));
Serial.println(windAdj);
}
if (myChr == '!') { // Toggle Test Mode
testMode = !testMode;
serialDump = testMode;
Serial.print(F("Toggling Test Mode - Mode="));
Serial.println(testMode);
}
if (myChr == '?') {
showHelp();
}
}
}// END checkSerial() *****************************************
void enterSleep(void)
{
set_sleep_mode(SLEEP_MODE_PWR_SAVE);
wdt_reset();
sleep_enable();
/* Disable all of the unused peripherals. This will reduce power
consumption further and, more importantly, some of these
peripherals may generate interrupts that will wake our Arduino from
sleep!
*/
power_adc_disable();
power_spi_disable();
power_timer0_disable();
power_timer2_disable();
power_twi_disable();
beenAsleep = true;
/* Now enter sleep mode. */
sleep_mode();
/* The program will continue from here after the timer timeout*/
sleep_disable(); /* First thing to do is disable sleep. */
power_all_enable();
//wokeUpMillis = millis()-10;
/* Re-enable the peripherals. */
}
void loop()
{
static int oldTipCount;//Use to check if a Rain "Tip" has generated an Interrupt response
static long nextSendMillis;
static byte nextSendSecond;
static byte nextSendMinute;
static long packetCount;
static float maxWindThisPkt;
long initTestEnd = long(60000 * testMinutes);
static bool doneFirstLoop;
// Serial.println("SofarSOgood");
//initTestEnd = 0;// Testing line-to be removfed
stampTime();// called here to check time for reset at midnight. stampTime updates values in ckHour, ckMinute, ckSecond
if (!doneFirstLoop) { // first iteration. Set up a few things for future iterations
if (xmitRate >= 60000) {
nextSendMinute = ckMinute + (xmitRate / 60000);
nextSendSecond = 00;//ckSecond;
}
else if (xmitRate < 60000) {
nextSendMinute = ckMinute;
nextSendSecond = ckSecond + xmitRate / 1000;
}
doneFirstLoop = true;
}
if ((millis() >= initTestEnd) && (millis() <= initTestEnd + 60000)) {
testMode = false;
}
if ((needsReset == false) && (ckHour == 23) && (ckMinute == 59)) {
needsReset = true;
//rain_Reset();
}
if ((needsReset == true) && (ckMinute < 5)) {
all_Reset();// Resets Rain, wind Counter, stores todays values as yesterday at midnight, ready for a brand new day!
needsReset = false;
// *********************************************************************************
}
checkSerial();
//serialReport;
if (xmitRate < 5000) { // Don't allow less than 5 second interval.
xmitRate = 5000;
}
rainInches = tipCount * rainPerTip;
readSensors();
if (maxWindThisPkt <= data.wind) {
maxWindThisPkt = data.wind;
if ((data.maxWind < maxWindThisPkt) && (maxWindThisPkt < 120)) { // if maxWindThisPkt >120, either data is corrupted, *OR*
data.maxWind = maxWindThisPkt;// Weather Station is being destroyed by tornado
topWind = data.maxWind;
}
}
/*
Serial.println("---");
Serial.println(ckMinute);
Serial.println(nextSendMinute);
Serial.println(ckSecond);
Serial.println(nextSendSecond);
Serial.println("---");
*/
if (((ckMinute >= nextSendMinute) && (ckSecond >= nextSendSecond)) || (testMode == true)) { // Time to send data packet.
if ((ckMinute == 59) && (nextSendMinute == 0)) { //Problem condition-need to skip
}
else {
data.windPacketPeak = maxWindThisPkt;
maxWindThisPkt = 0;
packetCount ++;
nextSendMillis = millis() + xmitRate;
stampTime();
data.packetID = packetCount;
readSensors();
for (int cnt = 0; cnt < 2 ; cnt++) { // Send each packet 3 times, to hopefully eliminate/minimize lost packets.
sendData();
//d+elay(500);// NO longer needed, delays built into SendData for turning radio on.
}
if (serialDump == true) {
serialReport();
}
if (xmitRate >= 60000) {
nextSendMinute = (xmitRate / 60000) + ckMinute;
//nextSendSecond = ckSecond;
if (nextSendMinute > 59) {
nextSendMinute -= 60;
}
}
else if (xmitRate < 60000) {
//nextSendMinute = ckMinute;
nextSendSecond = ckSecond + xmitRate / 1000;
if (nextSendSecond > 59) {
nextSendSecond -= 60;
}
}
}
}//*******
if ((!testMode) && (millis() > nextSleepMillis)) {
Serial.println("Sleeping");
delay(300);
beenAsleep = true;
enterSleep();
beenAsleep = false;
Serial.println("Back to Life");
}
//*/
//
}/// END OF LOOP!!!!
Comments