/* Teensy 3.2 weather station */
String VERSION_NUMBER = "0.0910";
#include <Wire.h>
#include <TimeLib.h> // For RTC
#include <Snooze.h>
#include <FreqMeasure.h>
// CREATE TIMERS
elapsedMillis master;
elapsedMillis windSpeedDebounce; // ---- To do
// CREATE OBJECTS
SnoozeDigital digital;
SnoozeAlarm alarm;
SnoozeBlock config_teensy32(digital, alarm);
// PIN DEFINES
#define XBEE_SLEEP_CONTROL 4
#define COLLECTING_DATA_INDICATOR 5
#define LED_PIN 13
#define RAIN_INPUT 16
#define WIND_SPEED_INPUT 22
// MISC DEFINES
#define ARRAY_SIZE 200
// REPORTING LEVEL DEFINES (These tell the RPi what color to use for console messages )
#define OH_NO "0"
#define REQUEST_DATA "2"
#define SENSOR_DATA "3"
#define RAIN_DATA "3"
#define WIND_DATA "3"
#define GOOD_TO_KNOW "5"
#define NO_CHANGE "7"
#define MISC_DATA "8"
#define XBEE_CONNECT "8"
#define LOW_PRIORITY "9"
// Variables
String Node = "Backyard";
String datastring;
String messageType, reportingLevel, sensorName, sensorModel, sensorInfo, data01, data02, data03, future1, future2, comment;
boolean newData = false;
boolean recvInProgress = false;
boolean restarted = true;
unsigned int masterTime = 3000; // 3 second timer
unsigned int pingCount = 0;
unsigned long externalTime;
int who;
float seaLevelPressure;
// RTC VARIABLES
int RTCTime[6]; // ------ To do
// SERIAL2 VARIABLES
double freqsum = 0; // frequency counter summing
int freqcount = 0;
int incomingByte = 0; // incoming serial data
String serial2InData;
// WIND SPEED VARIABLES
int windSpeedRotations = 0;
int windSpeedSampleCount = 0;
float windSpeedArray[ARRAY_SIZE];
// RAINFALL VARIABLES
int rainfallTipCount = 0;
//timer counters
unsigned int Counter30sec = 10, Counter5min = 100, Counter20min = 400, Counter60min = 1200;
unsigned int uptimeSeconds = 0, uptimeMinutes, uptimeDays, uptimeHours;
void setup(){
//Serial.begin(19200);
Serial2.begin(9600, SERIAL_8N1);
FreqMeasure.begin();
delay(1000); // Give Serial2 a moment to collect itself.
pinMode(LED_PIN, OUTPUT);
pinMode(XBEE_SLEEP_CONTROL, INPUT); // This gets set to OUTPUT when XBee needs to be turned on. Otherwise it floats.
pinMode(COLLECTING_DATA_INDICATOR, OUTPUT); // Visual indicator that sensors are being read.
// RTC initialize
setSyncProvider(getTeensy3Time); // Use RTC
externalTime = 278631501; // Fake epoch time for testing
Teensy3Clock.set(externalTime);
setTime(externalTime); // Set the RTC
// RAINFALL INITIALIZE
digital.pinMode(16, INPUT_PULLDOWN, RISING); // Rainfall
// attachInterrupt(digitalPinToInterrupt(16), rainfallGetSample, RISING);
// WIND SPEED INITIALIZE
digital.pinMode(22, INPUT_PULLDOWN, RISING); // Wind speed
// attachInterrupt(digitalPinToInterrupt(22), windSpeedGetSample, RISING);
// RESTART MESSAGE
xbeeMeshNetworkSync();
messageType = "Diagnostics";
reportingLevel = OH_NO;
comment = "Weather station " + VERSION_NUMBER + " has restarted.";
sendMessageToPi();
restarted = false;
alarm.setRtcTimer(0, 0, 3); // Set RTC alarm for 3 seconds.
xbeeSleep();
}
void loop() {
who = Snooze.deepSleep( config_teensy32 ); // return module that woke processor
if (who == 16) {
rainfallGetSample();
}
if (who == 22) {
windSpeedGetSample();
}
if (who == 35) { // process counters
// 3 second events
alarm.setRtcTimer(0, 0, 3); // Set RTC alarm for 3 seconds.
Counter30sec += 1; Counter5min += 1; Counter20min += 1; Counter60min += 1; uptimeSeconds += 3;
uptimeUpdate();
if (Counter30sec >= 10){
windSpeedSave();
Counter30sec = 0;
}
if ((Counter5min >= 100) || (Counter20min >= 400) || (Counter60min >= 1200)){
externalTime = now();
xbeeMeshNetworkSync();
if (Counter5min >= 100){
sendPing();
pingCount += 1;
xminuteUpdate(5);
uptimeSendToPi();
Counter5min = 0;
}
if (Counter20min >= 400){
xminuteUpdate(20);
requestDataFromPi("SeaLevelPressure");
windSpeedProcessData();
rainfallProcessData();
Counter20min = 0;
}
if (Counter60min >= 1200){
xminuteUpdate(60);
requestDataFromPi("CurrentTime");
Counter60min = 0;
}
}
xbeeSleep();
}
}
// FUNCTIONS
void xminuteUpdate(int minutes){
messageType = "Diagnostics";
reportingLevel = LOW_PRIORITY;
comment = String(minutes) + " minute update";
sendMessageToPi();
}
void showCounters(){
messageType = "Diagnostics";
reportingLevel = GOOD_TO_KNOW;
comment = "Counters 30sec: " + String(Counter30sec) + " 5min: " + String(Counter5min) + " 20min: " + String(Counter20min) + " 60min: " + String(Counter60min);
sendMessageToPi();
}
void xbeeSleep(){
pinMode(XBEE_SLEEP_CONTROL, INPUT); // This is set to output when the xbee is awake, but all other time it's an input so it can float.
}
void xbeeMeshNetworkSync(){ // Ensures that xbee is connected to its mesh network.
pinMode(XBEE_SLEEP_CONTROL, OUTPUT); // This is set to output when the xbee is awake, but all other time it's an input so it can float.
digitalWrite(XBEE_SLEEP_CONTROL, LOW); // turn on the xbee
float frequency = 0;
while ( frequency <= 1.99 || frequency >= 2.10) {
if (FreqMeasure.available()) {
freqsum = freqsum + FreqMeasure.read();
freqcount = freqcount + 1;
if (freqcount > 10) { // Used to be "4" but that caused the bug where it didn't connect once per hour at the :05 minute mark (?!)
frequency = FreqMeasure.countToFrequency(freqsum / freqcount);
freqsum = 0;
freqcount = 0;
}
}
}
messageType = "Diagnostics";
reportingLevel = MISC_DATA;
comment = "Connected to XBee network. Associate pin frequency: " + String(frequency) + "Hz";
sendMessageToPi();
}
float rounder(float roundme){ // Round float to the nearest .5
roundme = roundme * 2;
roundme = round(roundme);
roundme = roundme / 2;
return roundme;
}
void uptimeUpdate(){
if (uptimeSeconds >= 60) { uptimeSeconds = 0; uptimeMinutes += 1; }
if (uptimeMinutes >= 60) { uptimeMinutes = 0; uptimeHours += 1; }
if (uptimeHours >= 24) { uptimeHours = 0; uptimeDays += 1; }
}
void uptimeSendToPi(){
// SensorType = "Uptime";
String uptimeDays_string = "";
String uptimeHours_string = "";
String uptimeMinutes_string = "";
if (uptimeMinutes < 10) { uptimeMinutes_string = "0" + String(uptimeMinutes); } else { uptimeMinutes_string = String(uptimeMinutes); }
if (uptimeHours < 10) { uptimeHours_string = "0" + String(uptimeHours); } else { uptimeHours_string = String(uptimeHours); }
if (uptimeDays >= 10 && uptimeDays < 100 ){ uptimeDays_string = "0" + String(uptimeDays); }
if (uptimeDays < 10) { uptimeDays_string = "00" + String(uptimeDays); }
messageType = "Diagnostics";
reportingLevel = GOOD_TO_KNOW;
comment = "Uptime: " + String(uptimeDays_string) + ":" + String(uptimeHours_string) + ":" + String(uptimeMinutes_string);
sendMessageToPi();
}
void sendMessageToPi(){
datastring = "<" + messageType + "|" + reportingLevel + "|" + Node + "|" + sensorName + "|" + sensorModel + "|" + sensorInfo + "|" + data01 + "|" + data02 + "|" + data03 + "|" + future1 + "|" + future2 + "|" + comment + ">";
Serial2.println(datastring);
Serial2.flush();
// clears out variables. Don't comment out (again)
messageType = ""; reportingLevel = ""; sensorName = ""; sensorModel = ""; sensorInfo = ""; data01 = ""; data02 = ""; data03 = ""; future1 = ""; future2 = ""; comment = "";
}
void sendPing(){
messageType = "Diagnostics";
sensorName = "ping";
data01 = String(pingCount);
reportingLevel = MISC_DATA;
comment = Node + " " + sensorName + " " + data01;
sendMessageToPi();
}
void requestDataFromPi(String RequestData){
messageType = "RequestData";
reportingLevel = REQUEST_DATA;
comment = RequestData;
sendMessageToPi();
readFromSerial();
}
void readFromSerial() {
delay(500);
char startMarker = '<';
char endMarker = '>';
while (Serial2.available() > 0) {
incomingByte = Serial2.read();
if (recvInProgress == true) {
if (incomingByte != endMarker) {
serial2InData = (serial2InData + (char)incomingByte);
}
else {
serial2InData = (serial2InData + "\n"); // terminate the string
recvInProgress = false;
newData = true;
}
}
else if (incomingByte == startMarker) {
recvInProgress = true;
}
}
if (newData == true){
String sa[7]; // Create temporary array
int r=0, t=0;
String superdata;
for (unsigned int i=0; i < serial2InData.length(); i++) {
if(serial2InData.charAt(i) == ',') {
sa[t] = serial2InData.substring(r, i);
r=(i+1);
t++;
}
}
if (sa[0] == "SeaLevelPressure"){
messageType = "DataConfirmed";
reportingLevel = REQUEST_DATA;
data01 = sa[1];
comment = sa[0];
sendMessageToPi();
seaLevelPressure = sa[1].toInt();
}
if (sa[0] == "CurrentTime") {
messageType = "DataConfirmed";
reportingLevel = REQUEST_DATA;
for (int i=0; i<t; i++) {
superdata = superdata + sa[i] + ",";
}
comment = superdata;
sendMessageToPi();
}
newData = false;
serial2InData = "";
superdata = "";
}
}
// WINDSPEED
void windSpeedGetSample() { // USES INTERUPT: This function is triggered by an interupt on the windspeed input pin
windSpeedRotations += 1;
}
void windSpeedSave(){
windSpeedArray[windSpeedSampleCount] = windSpeedRotations;
windSpeedSampleCount += 1;
windSpeedRotations = 0;
if (windSpeedSampleCount >= ARRAY_SIZE){
windSpeedSampleCount = 0;
}
}
void windSpeedProcessData() {
float windSpeedMax = 0, windSpeedAvg = 0, windSpeedMin = 100, windspeedMPH;
if (windSpeedSampleCount > 0) {
for (int j = 1; j < windSpeedSampleCount; j++) {
windspeedMPH = (windSpeedArray[j] * (2.25/30)); // Convert pulses to MPH
if (windspeedMPH >= windSpeedMax){ windSpeedMax = windspeedMPH; }
if (windspeedMPH <= windSpeedMin){ windSpeedMin = windspeedMPH; }
windSpeedAvg = windSpeedAvg + windspeedMPH;
}
windSpeedAvg = windSpeedAvg / (windSpeedSampleCount-1);
messageType = "SensorData";
reportingLevel = SENSOR_DATA;
sensorName = "WindSpeed";
sensorModel = "Davis 6410";
data01 = String(windSpeedMax);
data02 = String(windSpeedAvg);
data03 = String(windSpeedMin);
comment = "Max, Avg, Min";
sendMessageToPi();
}
/*
else {
messageType = "Diagnostics";
reportingLevel = NO_CHANGE;
comment = "No wind activity since last transmission.";
sendMessageToPi();
}
*/
windSpeedSampleCount = 0;
}
// RAINFALL
void rainfallGetSample() { // USES INTERUPT: This function is triggered by an interupt on the 'rain' input pin
rainfallTipCount += 1;
// if (rainSensorDebounce >= 500) { rain_tip_count += 1; rainSensorDebounce = 0; } // ------ To do.
}
void rainfallProcessData() {
float rainfallAmount = rainfallTipCount * .01;
messageType = "SensorData";
sensorName = "Rainfall";
sensorModel = "Davis 6466";
reportingLevel = RAIN_DATA;
data01 = String(rainfallAmount);
data02 = String(rainfallTipCount);
comment = "Amount, RainfallTipCount";
sendMessageToPi();
rainfallTipCount = 0;
}
time_t getTeensy3Time()
{
return Teensy3Clock.get();
}