Skip to content

Instantly share code, notes, and snippets.

@Scott216
Created January 21, 2015 12:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Scott216/bc0a77b91b5a8ddc6f61 to your computer and use it in GitHub Desktop.
Save Scott216/bc0a77b91b5a8ddc6f61 to your computer and use it in GitHub Desktop.
Using Moteino to listen for Davis ISS packets
// #define USE_SD_CARD // comment this out if not using SD card
#include <SPI.h> // DavisRFM69.h needs this http://arduino.cc/en/Reference/SPI
#include <DavisRFM69.h> // http://github.com/dekay/DavisRFM69
#include <SD.h> // Micro SD Card. http://arduino.cc/en/Reference/SD
// Reduce number of bogus compiler warnings, see http://bit.ly/1pU6XMe
#undef PROGMEM
#define PROGMEM __attribute__(( section(".progmem.data") ))
const byte SS_PIN_MICROSD = 13;
#ifdef __AVR_ATmega1284P__
#define MOTEINO_LED 15 // Moteino MEGAs have LEDs on D15
const byte SS_PIN_RADIO = 4;
#else
#define MOTEINO_LED 9 // Moteinos have LEDs on D9
const byte SS_PIN_RADIO = 10;
#endif
#define ISS_UV_INDEX 0x4
#define ISS_RAIN_SECONDS 0x5
#define ISS_SOLAR_RAD 0x6
#define ISS_OUTSIDE_TEMP 0x8
#define ISS_WIND_GUST 0x9
#define ISS_HUMIDITY 0xA
#define ISS_RAIN 0xE
DavisRFM69 radio;
const byte TRANSMITTER_STATION_ID = 3; // ISS station ID to be monitored. Default station ID is normally 1
// Weather data
byte g_rainCounter = 0; // rain data sent from outside weather station. 1 = 0.01". Just counts up to 127 then rolls over to zero
byte g_windgustmph = 0; // Wind in MPH
uint16_t g_dayRain = 0; // Accumulated rain for the day in 1/100 inch
float g_rainRate = 0; // Rain rate in inches per hour
int16_t g_outsideTemperature = 0; // Outside temperature in tenths of degrees
byte g_outsideHumidity = 0; // Outside relative humidity in %.
byte g_windSpeed = 0; // Wind speed in miles per hour
float g_windDirection_Now = 0; // Instantanious wind direction, from 1 to 360 degrees (0 = no wind data)
uint16_t g_windDirection_Avg = 0; // Average wind direction, from 1 to 360 degrees (0 = no wind data)
//=============================================================================
// Setup radio
//=============================================================================
void setup()
{
Serial.begin(9600);
pinMode(MOTEINO_LED, OUTPUT);
digitalWrite(MOTEINO_LED, LOW);
#ifdef USE_SD_CARD
pinMode(SS_PIN_MICROSD, OUTPUT);
digitalWrite(SS_PIN_MICROSD, HIGH);
// Initialize SD Card
if ( SD.begin(SS_PIN_MICROSD) )
{ Serial.println(F("\ncard initialized")); }
else
{ Serial.println(F("\nCard failed, or not present")); }
#endif
// Setup Moteino radio
radio.initialize();
radio.setChannel(0); // Frequency - Channel is *not* set in the initialization. Need to do it now
delay(10000);
printRadioInfo();
printFreeRam();
Serial.println(F("MoteinoMega test for Davis ISS"));
} // end setup()
//=============================================================================
// Main loop()
//=============================================================================
void loop()
{
bool haveFreshWeatherData = getWirelessData();
#ifdef USE_SD_CARD
if ( haveFreshWeatherData )
{ logDataToSdCard( g_windDirection_Now ); }
#endif
// Heartbeat LED
static uint32_t heartBeatLedTimer = millis();
if ( (long)(millis() - heartBeatLedTimer) > 200 )
{
digitalWrite(MOTEINO_LED, !digitalRead(MOTEINO_LED));
heartBeatLedTimer = millis();
}
} // end loop()
//=============================================================================
// Read wireless data coming from Davis ISS weather station
//=============================================================================
bool getWirelessData()
{
static uint32_t lastRxTime = 0; // timestamp of last received data. Doesn't have to be good data
static byte hopCount = 0;
bool gotGoodData = false;
// Process ISS packet
if ( radio.receiveDone() )
{
packetStats.packetsReceived++;
// check CRC
unsigned int crc = radio.crc16_ccitt(radio.DATA, 6);
if ((crc == (word(radio.DATA[6], radio.DATA[7]))) && (crc != 0))
{
processPacket();
packetStats.receivedStreak++;
hopCount = 1;
gotGoodData = true;
}
else
{
packetStats.crcErrors++;
packetStats.receivedStreak = 0;
Serial.print("CRC errors ");
Serial.println(packetStats.crcErrors);
}
// Whether CRC is right or not, we count that as reception and hop
lastRxTime = millis();
radio.hop();
} // end if(radio.receiveDone())
// If a packet was not received at the expected time, hop the radio anyway
// in an attempt to keep up. Give up after 25 failed attempts. Keep track
// of packet stats as we go. I consider a consecutive string of missed
// packets to be a single resync. Thx to Kobuki for this algorithm.
const uint16_t PACKET_INTERVAL = 2555;
if ( (hopCount > 0) && ((millis() - lastRxTime) > (hopCount * PACKET_INTERVAL + 200)) )
{
packetStats.packetsMissed++;
if (hopCount == 1)
{ packetStats.numResyncs++; }
if (++hopCount > 25)
{ hopCount = 0; }
radio.hop();
}
return gotGoodData;
} // end getWirelessData()
//=========================================================================================================
// Log data to SD Card
//=========================================================================================================
bool logDataToSdCard(float testData)
{
#ifdef USE_SD_CARD
// Build log file name: YYYYMMDD.log
char logfile[13];
sprintf(logfile, "%02d%02d%02d.log", 2015, 1, 11);
// Check to see if the file exists:
if (!SD.exists(logfile))
{
// log file doesn't exist, create it
File newFile = SD.open(logfile, FILE_WRITE);
// Create header row
newFile.print(F("Timestamp\tRx delta sec\tWind Dir")); // time stamp in minutes since reboot, Rx delta sec is seconds between date received
newFile.println();
newFile.close();
}
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
File dataFile = SD.open(logfile, FILE_WRITE);
// if the file is available, write to it:
static uint32_t lastRxTime = millis();
if (dataFile)
{
dataFile.print(millis() / 60000L ); // minutes since startup
dataFile.print(F("\t"));
dataFile.print((millis() - lastRxTime)/1000L); // seconds from last time data was receied
dataFile.print(F("\t"));
dataFile.print(testData);
dataFile.println();
dataFile.close();
lastRxTime = millis(); // timestamp for when data was received
return true;
}
// if the file isn't open, pop up an error:
else
{
Serial.print(F("error opening "));
Serial.println(logfile);
return false;
}
#endif
} // logDataToSdCard()
//=============================================================================
// Read the data from the ISS
//=============================================================================
void processPacket()
{
// Flags are set true as each variable comes in for the first time
static bool gotTempData = false;
static bool gotHumidityData = false;
static bool gotRainData = false;
uint16_t rainSeconds = 0; // seconds between rain bucket tips
byte byte4MSN = 0; // Holds MSB of byte 4 - used for seconds between bucket tips
// station ID - the low order three bits are the station ID. Station ID 1 is 0 in this data
byte stationId = (radio.DATA[0] & 0x07) + 1;
if ( stationId != TRANSMITTER_STATION_ID )
{ return; } // exit function if this isn't the station ID program is monitoring
// Every packet has wind speed, wind direction and battery status in it
g_windSpeed = radio.DATA[1];
// There is a dead zone on the wind vane. No values are reported between 8
// and 352 degrees inclusive. These values correspond to received byte
// values of 1 and 255 respectively
// See http://www.wxforum.net/index.php?topic=21967.50
// float windDir = 9 + radio.DATA[2] * 342.0f / 255.0f; - formula has dead zone from 352 to 10 degrees
if ( radio.DATA[2] == 0 )
{ g_windDirection_Now = 0; }
else
{ g_windDirection_Now = ((float)radio.DATA[2] * 1.40625) + 0.3; } // This formula doesn't have dead zone, see: http://bit.ly/1uxc9sf
// 0 = battery ok, 1 = battery low. Not used by anything in program
byte transmitterBatteryStatus = (radio.DATA[0] & 0x8) >> 3;
// Look at MSB in firt byte to get data type
switch (radio.DATA[0] >> 4)
{
case ISS_OUTSIDE_TEMP:
g_outsideTemperature = (int16_t)(word(radio.DATA[3], radio.DATA[4])) >> 4;
gotTempData = true; // one-time flag when data first arrives. Used to track when all the data has arrived and can be sent to PWS
break;
case ISS_HUMIDITY:
// Round humidity to nearest integer
g_outsideHumidity = (byte) ( (float)( word((radio.DATA[4] >> 4), radio.DATA[3]) ) / 10.0 + 0.5 );
gotHumidityData = true; // one-time flag when data first arrives
break;
case ISS_WIND_GUST:
g_windgustmph = radio.DATA[3];
break;
case ISS_RAIN:
g_rainCounter = radio.DATA[3];
gotRainData = true; // one-time flag when data first arrives
break;
case ISS_RAIN_SECONDS: // Seconds between bucket tips, used to calculate rain rate. See: http://www.wxforum.net/index.php?topic=10739.msg190549#msg190549
byte4MSN = radio.DATA[4] >> 4;
if ( byte4MSN < 4 )
{ rainSeconds = (radio.DATA[3] >> 4) + radio.DATA[4] - 1; }
else
{ rainSeconds = radio.DATA[3] + (byte4MSN - 4) * 256; }
break;
default:
break;
}
printData(rainSeconds); // Print data, useful for debuggging
} // end processPacket()
//=============================================================================
// Prints radio channel and RSSI
//=============================================================================
void printRadioInfo()
{
Serial.print(F("ISS ID "));
Serial.print((radio.DATA[0] & 0x07) + 1);
Serial.print(F("\tch: "));
Serial.print(radio.CHANNEL);
Serial.print(F("\tRSSI: "));
Serial.println(radio.RSSI);
} // end printRadioInfo()
//=============================================================================
// print ISS data packet in Hex
//=============================================================================
void printPacket()
{
for (byte i = 0; i < DAVIS_PACKET_LEN; i++)
{
if( radio.DATA[i] < 16 )
{ Serial.print(F("0"));} // leading zero
Serial.print(radio.DATA[i], HEX);
Serial.print(F(" "));
}
} // end printPacket()
//=============================================================================
// Prints KSS data - used for debugging
//=============================================================================
void printData(uint16_t rainSeconds)
{
static uint32_t timeElapsed = millis(); // time elapsed since last tiem printData() was called. Pretty much the same as time between data packets received
static byte headerCounter = 0;
// Header
if (headerCounter == 0)
{ Serial.println("RxTime\tR-Cnt\tdaily\tr_secs\tr-rate\tmillis_sec\tspeed\tgusts\tAvgDir\tNowDir\ttemp\thumid\t\tpacket"); }
if( headerCounter++ > 20)
{ headerCounter = 0; }
Serial.print(millis() - timeElapsed);
Serial.print("\t");
Serial.print(g_rainCounter);
Serial.print("\t");
Serial.print(g_dayRain);
Serial.print("\t");
Serial.print(rainSeconds);
Serial.print("\t");
Serial.print(g_rainRate);
Serial.print("\t");
Serial.print(millis()/1000);
Serial.print("\t");
Serial.print(g_windSpeed);
Serial.print("\t");
Serial.print(g_windgustmph);
Serial.print("\t");
Serial.print(g_windDirection_Now);
Serial.print("\t");
Serial.print(g_windDirection_Avg);
Serial.print("\t");
Serial.print(g_outsideTemperature);
Serial.print("\t");
Serial.print(g_outsideHumidity);
Serial.print("\t");
printPacket();
Serial.println();
timeElapsed = millis(); // save new timestamp
} // end printData()
//=============================================================================
// Prints free RAM
//=============================================================================
void printFreeRam()
{
extern int __heap_start, *__brkval;
int v;
Serial.print(F("Free mem: "));
Serial.println((int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval));
} // end printFreeRam()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment