Changeset - 6ab6b1fe2314
[Not reviewed]
default
0 6 0
ethanzonca@CL-ENS241-08.cedarville.edu - 12 years ago 2013-02-21 16:37:15
ethanzonca@CL-ENS241-08.cedarville.edu
Added InfoText logger for generic informational messages, GPS fix light now reports 3d location fixes, log is made when fix is acquired/lost. Added maximum retransmission limit.
6 files changed with 60 insertions and 33 deletions:
0 comments (0 inline, 0 general)
master/master/config.h
Show inline comments
 
/*
 
 * Master Firmware: Configuration
 
 *
 
 * Wireless Observational Modular Aerial Network
 
 * 
 
 * Ethan Zonca
 
 * Matthew Kanning
 
 * Kyle Ripperger
 
 * Matthew Kroening
 
 *
 
 */
 
 
#ifndef CONFIG_H_
 
#define CONFIG_H_
 
 
#include <avr/pgmspace.h>
 
 
// --------------------------------------------------------------------------
 
// Module config (master.c)
 
// --------------------------------------------------------------------------
 
 
//#define DEBUG_OUTPUT
 
 
#define F_CPU 11059200
 
#define MODULE_ID '1'
 
#define BOARDTEMP_ADDR 0x90
 
 
#define HEATER_THRESHOLD 60
 
 
 
// --------------------------------------------------------------------------
 
// Error Codes config (led.c, used throughout code)
 
// --------------------------------------------------------------------------
 
 
// SD Card
 
#define ERROR_SLAVETIMEOUT 0
 
#define ERROR_SD_INIT 1
 
#define ERROR_SD_PARTITION 2
 
#define ERROR_SD_FILE 3
 
 
#define ERROR_XBEETIMEOUT 4
 
 
 
#define ERROR_FATAL 5
 
 
#define ERROR_ATFAIL 6
 
#define ERROR_EXITAT 7
 
 
#define ERROR_INFOTEXT 8
 
 
// !!! Please specify detailed messages for these error codes in logger.c
 
 
// --------------------------------------------------------------------------
 
// Slave Sensors config (slavesensors.c)
 
// --------------------------------------------------------------------------
 
 
#define MAX_NUM_SLAVES 5  // Maximum number of nodes in the system
 
#define MAX_NUM_SENSORS 10 // Maximum number of unique types of sensors in the system
 
 
// Node identifier of log destination xbee
 
#define XBEE_LOGDEST_NAME "HAB-LOGGER"
 
 
#define DATAREQUEST_RATE 3000
 
 
// Timeouts
 
#define TIMEOUT_SLAVEREQUEST 1000
 
#define TIMEOUT_NETWORKSCAN 7000
 
#define TIMEOUT_EXITAT 2000
 
#define TIMEOUT_XBEERESPONSE 2000
 
 
// Retries
 
#define MAX_SLAVEREQUEST_RETRIES 2
 
 
// --------------------------------------------------------------------------
 
// Command Parser config (serparser.c)
 
// --------------------------------------------------------------------------
 
 
// Maximum payload size of command
 
#define MAX_PAYLOAD_LEN 16
 
 
// Circular serial buffer size. Must be at least MAX_CMD_LEN + 5
 
#define BUFFER_SIZE 128 
 
 
// Public broadcast address
 
#define BROADCAST_ADDR 0 
 
 
 
// --------------------------------------------------------------------------
 
// GPS config (xxx.c)
 
// --------------------------------------------------------------------------
 
#define NMEABUFFER_SIZE 150
 
 
// --------------------------------------------------------------------------
 
// USART config (serial.c)
 
// --------------------------------------------------------------------------
 
 
#define USART0_BAUDRATE 115200
 
#define USART1_BAUDRATE 115200
 
 
 
// --------------------------------------------------------------------------
 
// AX.25 config (ax25.c)
 
// --------------------------------------------------------------------------
 

	
 
// TX delay in milliseconds
 
#define TX_DELAY      500
 

	
 
// Maximum packet delay
 
#define MAX_PACKET_LEN 512  // bytes
 
 

	
 
// --------------------------------------------------------------------------
 
// APRS config (aprs.c)
 
// --------------------------------------------------------------------------
 

	
 
// Set your callsign and SSID here. Common values for the SSID are
 
// (from http://zlhams.wikidot.com/aprs-ssidguide):
 
//
 
// - Balloons:  11
 
// - Cars:       9
 
// - Home:       0
 
// - IGate:      5
 
#define S_CALLSIGN      "KD8TDF"
 
#define S_CALLSIGN_ID   9 // 11
 

	
 
// Destination callsign: APRS (with SSID=0) is usually okay.
 
#define D_CALLSIGN      "APRS"
 
#define D_CALLSIGN_ID   0
 

	
 
// Digipeating paths:
 
// (read more about digipeating paths here: http://wa8lmf.net/DigiPaths/ )
 
// The recommended digi path for a balloon is WIDE2-1 or pathless. The default
 
// is pathless. Uncomment the following two lines for WIDE2-1 path:
 
#define DIGI_PATH1      "WIDE2"
 
#define DIGI_PATH1_TTL  1
 
 
// Transmit the APRS sentence every X milliseconds
 
#define APRS_TRANSMIT_PERIOD 20000
 

	
 

	
 
// --------------------------------------------------------------------------
 
// Logger config (logger.c)
 
// --------------------------------------------------------------------------
 
 
#define LOGGER_ID_EEPROM_ADDR 0x10
 
 
// Written to the beginning of every log file
 
#define LOGGER_HEADERTEXT "HAB Control Master - 1.0\n"
 
 
// Log to SD card every X milliseconds
 
#define LOGGER_RATE 1000 
 
 
// LED cycle indicator speed
 
#define LEDCYCLE_RATE 100 
 
 
#endif /* CONFIG_H_ */
 
\ No newline at end of file
master/master/lib/gps.c
Show inline comments
 
/*
 
 * Master Firmware: NMEA Parser
 
 *
 
 * Wireless Observational Modular Aerial Network
 
 * 
 
 * Ethan Zonca
 
 * Matthew Kanning
 
 * Kyle Ripperger
 
 * Matthew Kroening
 
 *
 
 */
 
 
#include <stdbool.h>
 
#include <string.h>
 
#include <stdio.h>
 
#include <avr/io.h>
 
#include <avr/interrupt.h>
 
#include "gps.h"
 
#include "serial.h"
 
#include "../config.h"
 
#include "led.h"
 
 
// Circular buffer for incoming data
 
uint8_t nmeaBuffer[NMEABUFFER_SIZE];
 
 
// Location of parser in the buffer
 
uint8_t nmeaBufferParsePosition = 0;
 
 
// Location of receive byte interrupt in the buffer
 
volatile uint16_t nmeaBufferDataPosition = 0;
 

	
 
// holds the byte ALREADY PARSED. includes starting character
 
int bytesReceived = 0;
 

	
 
//data (and checksum) of most recent transmission
 
char data[16];
 

	
 
//used to skip over bytes during parse
 
int skipBytes = 0;
 

	
 
//used to index data arrays during data collection
 
int numBytes = 0;
 

	
 
//variables to store data from transmission
 
//least significant digit is stored at location 0 of arrays
 
char tramsmissionType[7];
 

	
 
char timestamp[12];	//hhmmss.ss
 
char* get_timestamp() 
 
{
 
	return timestamp;
 
}
 
	
 
char latitude[14];	//lllll.lla
 
char latitudeTmp[8];
 
char* get_latitudeTrimmed() 
 
{
 
	strncpy(latitudeTmp, &latitude[0], 7);
 
	latitudeTmp[7] = 0x00;
 
	return latitudeTmp;
 
}
 
char* get_latitudeLSBs()
 
{
 
	strncpy(latitudeTmp, &latitude[7], 3);
 
	latitudeTmp[3] = 0x00;
 
	return latitudeTmp;
 
}
 

	
 
char longitude[14];	//yyyyy.yyb
 
char longitudeTmp[9];
 

	
 
char* get_longitudeTrimmed() 
 
{
 
	strncpy(longitudeTmp, &longitude[0], 8);
 
	longitudeTmp[8] = 0x00;
 
	return longitudeTmp;
 
}
 
char* get_longitudeLSBs()
 
{
 
	strncpy(longitudeTmp, &longitude[8], 3);
 
	longitudeTmp[3] = 0x00;
 
	return longitudeTmp;
 
}
 

	
 
char quality;		//quality for GGA and validity for RMC
 
char numSatellites[4];
 
char* get_sv() 
 
{
 
	return numSatellites;
 
}
 

	
 
char hdop[6];		//xx.x
 
char* get_hdop() 
 
{
 
	return hdop;
 
}
 

	
 
char altitude[10];	//xxxxxx.x
 
char wgs84Height[8];	//sxxx.x
 
char lastUpdated[8];	//blank - included for testing
 
char stationID[8];	//blank - included for testing
 
char checksum[3];	//xx
 

	
 
char knots[8];		//xxx.xx
 
char* get_speedKnots() 
 
{
 
	return knots;
 
}
 

	
 
char course[8];		//xxx.x
 
char* get_course() 
 
{
 
	return course;
 
}
 
	
 
char dayofmonth[9];	//ddmmyy
 
char* get_dayofmonth() 
 
{
 
	return dayofmonth;
 
}
 

	
 

	
 
bool gps_hadfix = false;
 

	
 
bool gps_hasfix() 
 
{
 
	return strcmp("99.99", get_hdop());
 
	bool hasFix = get_latitudeTrimmed()[0] != 0x00;
 
	
 
	if(hasFix && !gps_hadfix) {
 
		info_log_msg("Lost GPS fix");
 
	}
 
	else if(!hasFix && gps_hadfix) {
 
		info_log_msg("Acquired GPS fix");
 
	}
 
	
 
	gps_hadfix = hasFix;
 
	return hasFix;
 
}
 

	
 
char variation[9];	//xxx.xb
 
int calculatedChecksum;
 
int receivedChecksum;
 

	
 
// transmission state machine
 
enum decodeState {
 
	//shared fields
 
	INITIALIZE=0,
 
	GET_TYPE,
 
	GPS_CHECKSUM,	//XOR of all the bytes between the $ and the * (not including the delimiters themselves), written in hexadecimal
 
	//GGA data fields
 
	GGA_TIME,
 
	GGA_LATITUDE,
 
	GGA_LONGITUDE,
 
	GGA_QUALITY,
 
	GGA_SATELLITES,
 
	GGA_HDOP,
 
	GGA_ALTITUDE,
 
	GGA_WGS84,
 
	GGA_LAST_UPDATE,
 
	GGA_STATION_ID,
 
	//RMC data fields
 
	RMC_TIME,
 
	RMC_VALIDITY,
 
	RMC_LATITUDE,
 
	RMC_LONGITUDE,
 
	RMC_KNOTS,
 
	RMC_COURSE,
 
	RMC_DATE,
 
	RMC_MAG_VARIATION,
 
	
 
}decodeState;
 

	
 

	
 
ISR(USART1_RX_vect)
 
{
 
	nmeaBuffer[nmeaBufferDataPosition % NMEABUFFER_SIZE] = UDR1;
 
	nmeaBufferDataPosition = (nmeaBufferDataPosition + 1) % NMEABUFFER_SIZE;
 
}
 

	
 
void gps_setup() 
 
{
 
	snprintf(timestamp,2, "0");
 
	snprintf(latitude,2, "0");
 
	snprintf(longitude,2, "0");
 
	snprintf(numSatellites,2, "0");
 
	snprintf(hdop,2, "0");
 
	snprintf(knots,2, "0");
 
	snprintf(course,2, "0");
 
	snprintf(dayofmonth,2, "0");
 
}
 

	
 

	
 
// Could inline if program space available
 
static void setParserState(uint8_t state)
 
{
 
	decodeState = state;
 
 
	
 
	// If resetting, clear vars
 
	if(state == INITIALIZE)
 
	{
 
		calculatedChecksum = 0;
 
	}
 
	
 
	// Every time we change state, we have parsed a byte
 
	nmeaBufferParsePosition = (nmeaBufferParsePosition + 1) % NMEABUFFER_SIZE;
 
}
 

	
 

	
 

	
 
//// MKa GPS transmission parser START
 
void parse_gps_transmission(void){
 
	
 
	// Pull byte off of the buffer
 
	char byte;
 
	
 
	
 
	
 
	while(nmeaBufferDataPosition != nmeaBufferParsePosition) 
 
	{
 
		led_on(LED_ACTIVITY);
 
		
 
		byte = nmeaBuffer[nmeaBufferParsePosition];
 
		
 
		if(decodeState == INITIALIZE) //start of transmission sentence
 
		{
 
			if(byte == '$') 
 
			{
 
				#ifdef DEBUG_NMEA
 
				serial0_sendString("found $\r\n");
 
				#endif
 
				
 
				setParserState(GET_TYPE);
 
				numBytes = 0; //prep for next phases
 
				skipBytes = 0;
 
				calculatedChecksum = 0;
 
			}		
 
			
 
			else 
 
			{
 
				setParserState(INITIALIZE);
 
			}
 
		}
 
	
 
		//parse transmission type
 
		else if (decodeState == GET_TYPE)
 
		{
 
			tramsmissionType[numBytes] = byte;
 
			numBytes++;
 
			
 
			#ifdef DEBUG_NMEA
 
			serial0_sendString("stored a type byte\r\n");
 
			#endif
 
			
 
			if(byte == ',') //end of this data type
 
			{
 
				tramsmissionType[5] = 0x00;
 
				
 
				if (tramsmissionType[2] == 'G' &&
 
				tramsmissionType[3] == 'G' &&
 
				tramsmissionType[4] == 'A')
 
				{
 
					setParserState(GGA_TIME);
 
					numBytes = 0;
 
				}
 
				else if (tramsmissionType[2] == 'R' &&
 
				tramsmissionType[3] == 'M' &&
 
				tramsmissionType[4] == 'C')
 
				{
 
					setParserState(RMC_TIME);
 
					numBytes = 0;
 
				}
 
				else //this is an invalid transmission type
 
				{
 
					setParserState(INITIALIZE);
 
				}
 
			}
 
			else {
 
				// continue
 
				setParserState(GET_TYPE);
 
			}
 
			
 
		}
 
	
 
		///parses GGA transmissions START
 
		/// $--GGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*xx
 
		//timestamp
 
		else if (decodeState == GGA_TIME)
 
		{
 
			if (byte == ',') //end of this data type
 
			{
 
				timestamp[4] = 0x00; // Cut off at 4 (no seconds) for APRS
 
				setParserState(GGA_LATITUDE);
 
				skipBytes = 0; //prep for next phase of parse
 
				numBytes = 0;
 
			}
 
			else //store data
 
			{
 
				#ifdef DEBUG_NMEA
 
				serial0_sendString("found GGA time byte\r\n");
 
				#endif
 
				
 
				setParserState(GGA_TIME);
 
				timestamp[numBytes] = byte; //byte; //adjust number of bytes to fit array
 
				numBytes++;
 
			}
 
		}
 
	
 
		//latitude
 
		else if (decodeState == GGA_LATITUDE)
 
		{
 
			if (byte == ',' && skipBytes == 0) //discard this byte
 
			{
 
				#ifdef DEBUG_NMEA
 
				serial0_sendString("found lat skip byte\r\n");
 
				#endif
 
				
 
				skipBytes = 1;
 
				setParserState(GGA_LATITUDE);
 
			}
 
			else if (byte == ',') //end of this data type
 
			{
 
				
 
				latitude[numBytes] = 0x00; // null terminate
 
				
 
				setParserState(GGA_LONGITUDE);
 
				skipBytes = 0; //prep for next phase of parse
 
				numBytes = 0;
 
			}
 
			else //store data
 
			{
 
				#ifdef DEBUG_NMEA
 
				serial0_sendString("found lat byte\r\n");
 
				#endif
 
				
 
				latitude[numBytes] = byte; //adjust number of bytes to fit array
 
				numBytes++;
 
				setParserState(GGA_LATITUDE);
 
			}
 
		}
 
	
 
		//longitude
 
		else if (decodeState == GGA_LONGITUDE)
 
		{
 
			if (byte == ',' && skipBytes == 0) //discard this byte
 
			{
 
				#ifdef DEBUG_NMEA
 
				serial0_sendString("found long skip byte\r\n");
 
				#endif
 
				
 
				skipBytes = 1;
 
				setParserState(GGA_LONGITUDE);
 
			}
 
			else if (byte == ',') //end of this data type
 
			{
 
				longitude[numBytes] = 0x00;
 
				setParserState(GGA_QUALITY);
 
				numBytes = 0; //prep for next phase of parse
 
				skipBytes = 0;
 
			}
 
			else //store data
 
			{
 
				#ifdef DEBUG_NMEA
 
				serial0_sendString("found long byte\r\n");
 
				#endif
 
				
 
				longitude[numBytes] = byte; //adjust number of bytes to fit array
 
				numBytes++;
 
				setParserState(GGA_LONGITUDE);
 
			}
 
		}
 
	
 
		//GGA quality
 
		else if (decodeState == GGA_QUALITY)
 
		{
 
			if (byte == ',') //end of this data type
 
			{
 
				setParserState(GGA_SATELLITES);
 
				numBytes = 0; //prep for next phase of parse
 
			}
 
			else //store data
 
			{
 
				#ifdef DEBUG_NMEA
 
				serial0_sendString("found quality byte\r\n");
 
				#endif
 
				
 
				quality = byte; //maybe reset if invalid data ??
 
				setParserState(GGA_QUALITY);
 
			}
 
		}
 
	
 
		//number of satellites
 
		else if (decodeState == GGA_SATELLITES)
 
		{
 
			if (byte == ',') //end of this data type
 
			{
 
				numSatellites[numBytes] = 0x00;
 
				setParserState(GGA_HDOP);
 
				numBytes = 0; //prep for next phase of parse
 
			}
 
			else //store data
 
			{
 
				numSatellites[numBytes] = byte; //adjust number of bytes to fit array
 
				numBytes++;
 
				setParserState(GGA_SATELLITES);
 
			}
 
		}
 
	
 
		//HDOP
 
		else if (decodeState == GGA_HDOP)
 
		{
 
			if (byte == ',' ) //end of this data type
 
			{
 
				hdop[numBytes] = 0x00;
 
				setParserState(GGA_ALTITUDE);
 
				numBytes = 0; //prep for next phase of parse
 
				skipBytes = 0;
 
			}
 
			else //store data
 
			{
 
				hdop[numBytes] = byte; //adjust number of bytes to fit array
 
				numBytes++;
 
				setParserState(GGA_HDOP);
 
			}
 
		}
 
	
 
		//altitude
 
		else if (decodeState == GGA_ALTITUDE)
 
		{
 
			if (byte == ',' && skipBytes == 0) //discard this byte
 
			{
 
				skipBytes = 1;
 
				setParserState(GGA_ALTITUDE);
 
			}
 
			else if(byte == ',') //end of this data type
 
			{
 
				altitude[numBytes] = 0x00;
 
				setParserState(GGA_WGS84);
 
				numBytes = 0; //prep for next phase of parse
 
			}
 
			else //store data
 
			{
 
				altitude[numBytes] = byte; //adjust number of bytes to fit array
 
				numBytes++;
 
				setParserState(GGA_ALTITUDE);
 
			}
 
		}
 
	
 
		//WGS84 Height
 
		else if (decodeState == GGA_WGS84)
 
		{
 
			if (byte == ',' && skipBytes == 0) //discard this byte
 
			{
 
				skipBytes = 1;
 
				setParserState(GGA_WGS84);
 
			}
 
			else if(byte == ',') //end of this data type
 
			{
 
				wgs84Height[numBytes] = 0x00;
 
				setParserState(GGA_LAST_UPDATE);
 
				skipBytes = 0; //prep for next phase of parse
 
				numBytes = 0;
 
			}
 
			else //store data
 
			{
 
				wgs84Height[numBytes] = byte; //adjust number of bytes to fit array
 
				numBytes++;
 
				setParserState(GGA_WGS84);
 
			}
 
		}
 
	
 
		//last GGA DGPS update
 
		else if (decodeState == GGA_LAST_UPDATE)
 
		{
 
			if (byte == ',') //end of this data type
 
			{
 
				lastUpdated[numBytes] = 0x00;
 
				setParserState(GGA_STATION_ID);
 
				numBytes = 0; //prep for next phase of parse
 
			}
 
			else //store data - this should be blank
 
			{
 
				lastUpdated[numBytes] = byte; //adjust number of bytes to fit array
 
				numBytes++;
 
				setParserState(GGA_LAST_UPDATE);
 
			}
 
		}
 
	
 
		//GGA DGPS station ID
 
		else if (decodeState == GGA_STATION_ID)
 
		{
 
			if (byte == ',' || byte == '*') //end of this data type
 
			{
 
				stationID[numBytes] = 0x00;
 
				setParserState(GPS_CHECKSUM);
 
				numBytes = 0; //prep for next phase of parse
 
			}
 
			else //store data - this should be blank
 
			{
 
				stationID[numBytes] = byte; //adjust number of bytes to fit array
 
				numBytes++;
 
				setParserState(GGA_STATION_ID);
 
			}
 
		}
 
		///parses GGA transmissions END
 
	
 
		/// $GPRMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,ddmmyy,x.x,a*hh
 
		///parses RMC transmissions
 
		//time
 
		// emz: commented setter, GMC time better?
 
		else if(decodeState == RMC_TIME)
 
		{
 
			if (byte == ',') //end of this data type
 
			{
 
				//timestamp[numBytes] = 0x00;
 
				setParserState(RMC_VALIDITY);
 
				numBytes = 0; //prep for next phase of parse
 
			}
 
			else //store data
 
			{
 
				#ifdef DEBUG_NMEA
master/master/lib/logger.c
Show inline comments
 
/*
 
 * Master Firmware: SD Card Data Logger
 
 *
 
 * Wireless Observational Modular Aerial Network
 
 * 
 
 * Ethan Zonca
 
 * Matthew Kanning
 
 * Kyle Ripperger
 
 * Matthew Kroening
 
 *
 
 */
 
 
#include "../config.h"
 
#include <util/delay.h>
 
#include <string.h>
 
#include <stdio.h>
 
#include <avr/pgmspace.h>
 
#include <avr/sleep.h>
 
#include <avr/eeprom.h>
 
#include <string.h>
 
#include "sdcard/fat.h"
 
#include "sdcard/fat_config.h"
 
#include "sdcard/partition.h"
 
#include "sdcard/sd_raw.h"
 
#include "sdcard/sd_raw_config.h"
 
#include "serial.h"
 
#include "logger.h"
 
#include "led.h"
 
#include "looptime.h"
 

	
 
#define MAX_ERRNO 7
 
#define MAX_ERRNO 8
 

	
 
// Label lookup table
 
// Make sure there are never more labels than there are MAX_NUM_SENSORS!
 
const char err_0[] PROGMEM = "slave timeout";
 
const char err_1[] PROGMEM = "initializing SD card failed";
 
const char err_2[] PROGMEM = "opening SD partition failed";
 
const char err_3[] PROGMEM = "opening SD file failed";
 
const char err_4[] PROGMEM = "XBee timeout";
 
const char err_5[] PROGMEM = "FATAL UNHANDLED ERROR";
 
const char err_6[] PROGMEM = "enter AT mode failed";
 
const char err_7[] PROGMEM = "exit AT mode failed";
 
const char err_8[] PROGMEM = "infotext";
 

	
 
const char *const errorMessageLookup[] PROGMEM =
 
{
 
	err_0,
 
	err_1,
 
	err_2,
 
	err_3,
 
	err_4,
 
	err_5,
 
	err_6,
 
	err_7,
 
	err_8,
 
};
 
 
 
 
struct partition_struct* partition;
 
struct fat_fs_struct* fs;
 
struct fat_dir_struct* dd;
 
struct fat_file_struct* fd_datalog;
 
struct fat_file_struct* fd_errorlog;
 
 
void logger_setup()
 
{
 
 
	if(!sd_raw_init())
 
	{
 
		error_log(ERROR_SD_INIT, true);
 
		return;
 
	}
 
 
	// TODO: Check SD card switch to see if inserted.
 
	// this was included in the library, but is commented out right now
 
	
 
	// Open first partition
 
	partition = partition_open(sd_raw_read, sd_raw_read_interval, sd_raw_write, sd_raw_write_interval, 0);
 
	
 
	// Check that partition was created correctly
 
	if(!partition)
 
	{
 
		// Error opening partition. MBR might be screwed up.
 
		error_log(ERROR_SD_PARTITION, true);
 
		return;
 
	}
 
	
 
	
 
	// Open FAT filesystem
 
	fs = fat_open(partition);
 
	if(!fs)
 
	{
 
		// opening filesystem failed
 
		error_log(ERROR_SD_PARTITION, true);
 
		return;
 
	}
 
	
 
	// Open root directory
 
	struct fat_dir_entry_struct rootDirEntry;
 
	fat_get_dir_entry_of_path(fs, "/", &rootDirEntry);
 

	
 
	dd = fat_open_dir(fs, &rootDirEntry);
 
	if(!dd)
 
	{
 
		// opening root directory failed
 
		_delay_ms(10);
 
		error_log(ERROR_SD_FILE, true);
 
		return;
 
	}
 
	
 
		
 
	// we pre-increment logid here because it starts at 255, then wraps to 0
 
	uint8_t logid = eeprom_read_byte(LOGGER_ID_EEPROM_ADDR) + 1;
 
	eeprom_update_byte(LOGGER_ID_EEPROM_ADDR, logid);
 
	
 
	int32_t errorOffset = 0;
 
	char errorFilename[17];
 
	
 
	
 
	// Form filename
 
	snprintf(errorFilename, 17, "run%derror.csv",logid);
 
	struct fat_dir_entry_struct errorDirEntry;
 
	if(fat_create_file(dd, errorFilename, &errorDirEntry) == 0)
 
	{
 
		serial0_sendString("Error create errorlog\r\n");
 
		error_log(ERROR_SD_FILE, true);
 
	}
 
	// Search for file in current directory and open it
 
	fd_errorlog = open_file_in_dir(fs, dd, errorFilename);
 
	if(!fd_errorlog)
 
	{
 
		serial0_sendString("Error open errorlog!\r\n");
 
		error_log(ERROR_SD_FILE, true);
 
		return;
 
	}
 
	errorOffset=0;
 
	if(!fat_seek_file(fd_errorlog, &errorOffset, FAT_SEEK_SET))
 
	{
 
		// Error seeking to file
 
		serial0_sendString("Error seek errorlog!\r\n");
 
		error_log(ERROR_SD_FILE, true);
 
		fat_close_file(fd_errorlog);
 
		return;
 
	}
 
	
 
		
 
	int32_t dataOffset = 0;
 
	char dataFilename[17];
 
	
 
	// Form filename
 
	snprintf(dataFilename, 17, "run%ddata.csv",logid);
 
	struct fat_dir_entry_struct dataDirEntry;
 
	// Create new data log file
 
	if(fat_create_file(dd, dataFilename, &dataDirEntry) == 0) 
 
	{
 
		serial0_sendString("Error create datalog\r\n");
 
		error_log(ERROR_SD_FILE, true);
 
	}
 
	// Search for file in current directory and open it
 
	fd_datalog = open_file_in_dir(fs, dd, dataFilename);
 
	if(!fd_datalog)
 
	{
 
		serial0_sendString("Error open datalog!\r\n");
 
		error_log(ERROR_SD_FILE, true);
 
		return;
 
	}
 
	dataOffset=0;
 
	if(!fat_seek_file(fd_datalog, &dataOffset, FAT_SEEK_SET))
 
	{
 
		// Error seeking to file
 
		serial0_sendString("Error seek datalog!\r\n");
 
		error_log(ERROR_SD_FILE, true);
 
		fat_close_file(fd_datalog);
 
		return;
 
	}
 
	
 
	
 
	// Write header information
 
	logger_log(LOGGER_HEADERTEXT);
 
	logger_log("\n-- BEGIN DATA --\n");
 
	
 
	error_log_rawwrite(LOGGER_HEADERTEXT);
 
	error_log_rawwrite("\n-- BEGIN ERROR --\n");
 
	error_log_rawwrite("\nErrorNo,ErrorMsg,ErrorInfo,\r\n");
 
}	
 
 
void logger_log(char *buffer) 
 
{
 
	uint8_t len = strlen(buffer);
 
	if(fat_write_file(fd_datalog, (uint8_t*) buffer, len) != len)
 
	{
 
		// Error writing to file
 
		return;
 
	}
 
}
 
 
void error_log(uint8_t errNo, bool flashLED)
 
{
 
	char labelBuffer[32];
 
	labelBuffer[0] = 0x00;
 
	
 
	if(errNo <= MAX_ERRNO) 
 
	{
 
		strncpy_P(labelBuffer,(char*)pgm_read_word(&(errorMessageLookup[errNo])),32);
 
	}
 
	char errorLine[128];
 
	snprintf(errorLine, 128, "%lu, %u, %s,,\r\n", time_millis(), errNo, labelBuffer);
 
	error_log_rawwrite(errorLine);
 
	
 
	led_on(LED_ERROR);
 
	if(flashLED) 
 
	{
 
		led_errorcode(errNo);
 
	}	
 
}
 
 
void error_log_msg(uint8_t errNo, bool flashLED, char* infoText)
 
{
 
	char labelBuffer[32];
 
	labelBuffer[0] = 0x00;
 
	
 
	if(errNo <= MAX_ERRNO)
 
	{
 
		strncpy_P(labelBuffer,(char*)pgm_read_word(&(errorMessageLookup[errNo])),32);
 
	}
 
	char errorLine[256];
 
	snprintf(errorLine, 256, "%lu,%u,%s,%s,\r\n", time_millis(), errNo, labelBuffer, infoText);
 
	error_log_rawwrite(errorLine);
 
	
 
	led_on(LED_ERROR);
 
	if(flashLED)
 
	{
 
		led_errorcode(errNo);
 
	}
 
}
 
 
void info_log_msg(char* infoText)
 
{
 
	char errorLine[256];
 
	snprintf(errorLine, 256, "%lu,8,infotext,%s,\r\n", time_millis(), infoText);
 
	error_log_rawwrite(errorLine);
 
}
 
 
 
void error_log_rawwrite(char *buffer) 
 
{
 
	uint8_t len = strlen(buffer);
 
	if(fat_write_file(fd_errorlog, (uint8_t*) buffer, len) != len)
 
	{
 
		// Error writing to file
 
		return;
 
	}
 
}
 
 
 
void logger_closeLog() 
 
{
 
	fat_close_file(fd_datalog);
 
	fat_close_dir(dd);
 
	fat_close(fs);
 
	partition_close(partition);
 
}
 
 
 
// INTERNAL FUNCTIONS
 
 
// Opens a file so it can be read/written
 
struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name)
 
{
 
	struct fat_dir_entry_struct file_entry;
 
	if(!find_file_in_dir(fs, dd, name, &file_entry))
 
	return 0;
 

	
 
	return fat_open_file(fs, &file_entry);
 
}
 

	
 
// Searches for file in directory listing
 
uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry)
 
{
 
	while(fat_read_dir(dd, dir_entry))
 
	{
 
		if(strcmp(dir_entry->long_name, name) == 0)
 
		{
 
			fat_reset_dir(dd);
 
			return 1;
 
		}
 
	}
 
	return 0;
 
}
master/master/lib/logger.h
Show inline comments
 
/*
 
 * Master Firmware: Status and Error LED Handler
 
 *
 
 * Wireless Observational Modular Aerial Network
 
 * 
 
 * Ethan Zonca
 
 * Matthew Kanning
 
 * Kyle Ripperger
 
 * Matthew Kroening
 
 *
 
 */
 
 
 
#ifndef LOGGER_H_
 
#define LOGGER_H_
 
 
#include <stdbool.h>
 
 
void logger_setup();
 
uint8_t logger_writeLine(char* dateLine, uint8_t length);
 
struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name);
 
uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry);
 
void error_log(uint8_t errNo, bool flashLED);
 
void error_log_msg(uint8_t errNo, bool flashLED, char* infoText);
 
void info_log_msg(char* infoText);
 
void error_log_rawwrite(char *buffer);
 
void logger_log(char *buffer);
 
void logger_closeLog();
 
 
#endif /* LOGGER_H_ */
 
\ No newline at end of file
master/master/lib/sensordata.c
Show inline comments
 
/*
 
 * Master Firmware: Sensor Data
 
 *
 
 * Wireless Observational Modular Aerial Network
 
 * 
 
 * Ethan Zonca
 
 * Matthew Kanning
 
 * Kyle Ripperger
 
 * Matthew Kroening
 
 *
 
 */
 

	
 
#include "../config.h"
 
#include <stdio.h>
 
#include <stdbool.h>
 
#include <string.h>
 
#include "sensordata.h"
 
#include "slavesensors.h"
 
#include "boardtemp.h"
 
#include "looptime.h"
 
#include "gps.h"
 
#include "logger.h"
 

	
 
// Slave sensor reading storage
 
int32_t slaves[MAX_NUM_SLAVES][MAX_NUM_SENSORS];
 

	
 
void sensordata_setup() 
 
{
 
	for(int i=0; i<MAX_NUM_SLAVES; i++) 
 
	{
 
		for(int j=0; j<MAX_NUM_SENSORS; j++) 
 
		{
 
			slaves[i][j] = -2111111111; // minimum value of 16 bit integer
 
		}
 
	}
 
}
 
 
// Store a sensor value in memory
 
void sensordata_set(uint8_t nodeID, uint8_t type, int32_t value)
 
{
 
	if(nodeID < MAX_NUM_SLAVES) 
 
	{
 
		slaves[nodeID][type] = value;
 
	}	
 
}
 
 
// Retrieve a sensor value from memory
 
int32_t sensordata_get(uint8_t nodeID, uint8_t type) 
 
{
 
	// Avoid reading out of bad places!
 
	if(nodeID < MAX_NUM_SLAVES) 
 
	{
 
		return slaves[nodeID][type];
 
	}
 
	else 
 
	{
 
		return 0;
 
	}
 
}
 
 
 
// Generate APRS comment
 
// TODO: Can we move this buffer to a local scope of this function?
 
char commentBuffer[128];
 
char* slavesensors_getAPRScomment() 
 
{
 
	snprintf(commentBuffer,128, "T%d S%s V%s H%s _%s |%s", sensors_getBoardTemp(), get_sv(), get_speedKnots(), get_hdop(), get_latitudeLSBs(), get_longitudeLSBs());
 
	snprintf(commentBuffer,128, "t9%d s%s v%s h%s _%s |%s", sensors_getBoardTemp(), get_sv(), get_speedKnots(), get_hdop(), get_latitudeLSBs(), get_longitudeLSBs());
 
	return commentBuffer;
 
}
 
 
 
// Generates CSV headers on first run and logs values to the SD card (if data available)
 
bool dataWasReady = false;
 
void sensordata_logvalues() 
 
{
 
	// Generate CSV header after we have queried all slaves once
 
	if(slavesensors_dataReady()) 
 
	{
 
	
 
		// Only generate/write header the first time data is ready
 
		if(!dataWasReady) 
 
		{
 
			#define CSV_BUFFER_SIZE 64
 
			char csvHeader[CSV_BUFFER_SIZE];
 
			csvHeader[0] = 0x00;
 
			
 
			// Add master data headers
 
			logger_log("Time,BoardTemp,GPSTime,GPSLat,GPSLon,GPSSpeed,GPSHDOP,GPSCourse,GPSSV,");
 
			
 
			// Add slave data headers
 
			for(uint8_t i=0; i<MAX_NUM_SLAVES; i++) 
 
			{
 
				for(uint8_t j=0; j<MAX_NUM_SENSORS; j++) 
 
				{
 
					int32_t tmp = sensordata_get(i, j);
 
					
 
					// If a sensor value exists, write a header for it
 
					if(tmp != -2111111111) 
 
					{
 
						snprintf(csvHeader, CSV_BUFFER_SIZE,"%s-%s,", slavesensors_slavename(i), slavesensors_getLabel(j));
 
						logger_log(csvHeader);
 
					}
 
				}
 
			}
 
		
 
			// End line and write to SD card
 
			snprintf(csvHeader, CSV_BUFFER_SIZE,"\r\n");
 
			logger_log(csvHeader);
 
			
 
			dataWasReady = true;
 
		}
 
	
 
		// Write CSV sensor values to SD card
 
		#define CSV_LOGLINE_SIZE 512
 
		char logbuf[CSV_LOGLINE_SIZE];
 
		logbuf[0] = 0x00;
 
		
 
		// Write master sensor values
 
		snprintf(logbuf, CSV_LOGLINE_SIZE, "%lu,%d,%s,%s,%s,%s,%s,%s,%s,", time_millis(), sensors_getBoardTemp(),get_timestamp(),get_latitudeTrimmed(),get_longitudeTrimmed(),get_speedKnots(),get_hdop(), get_course(), get_sv());
 
		
 
		// Write slave sensor values
 
		for(int i=0; i<MAX_NUM_SLAVES; i++) 
 
		{
 
			for(int j=0; j<MAX_NUM_SENSORS; j++) 
 
			{
 
				int32_t tmp = sensordata_get(i, j);
 
				
 
				// If a sensor value exists, log the data
 
				if(tmp != -2111111111) 
 
				{
 
					snprintf(logbuf + strlen(logbuf),CSV_LOGLINE_SIZE-strlen(logbuf)," %ld,", tmp);
 
				}
 
			
 
			}
 
		}
 
		
 
		// End line and write to log
 
		snprintf(logbuf + strlen(logbuf),CSV_LOGLINE_SIZE-strlen(logbuf),"\r\n");
 
		logger_log(logbuf);
 
	}
 
}
 
\ No newline at end of file
master/master/lib/slavesensors.c
Show inline comments
 
/*
 
 * Master Firmware: Slave Sensor Data Acquisition
 
 *
 
 * Wireless Observational Modular Aerial Network
 
 * 
 
 * Ethan Zonca
 
 * Matthew Kanning
 
 * Kyle Ripperger
 
 * Matthew Kroening
 
 *
 
 */
 
 
#include "../config.h"
 
#include <avr/io.h>
 
#include <stdbool.h>
 
#include <stdlib.h>
 
#include <stdio.h>
 
#include <string.h>
 
#include <util/delay.h>
 
#include <avr/wdt.h>
 
#include <avr/pgmspace.h>
 
#include "serial.h"
 
#include "serparser.h"
 
#include "slavesensors.h"
 
#include "sensordata.h"
 
#include "led.h"
 
#include "looptime.h"
 
#include "logger.h"
 

	
 
// Label lookup table
 
// Make sure there are never more labels than there are MAX_NUM_SENSORS! 
 
const char label_0[] PROGMEM = "BoardTemp";
 
const char label_1[] PROGMEM = "HeaterStatus";
 
const char label_2[] PROGMEM = "BatteryLevel";
 
const char label_3[] PROGMEM = "AirTemp";
 
const char label_4[] PROGMEM = "AmbientLight";
 
const char label_5[] PROGMEM = "Humidity";
 
const char label_6[] PROGMEM = "Pressure";
 
const char label_7[] PROGMEM = "Altitude";
 
const char label_8[] PROGMEM = "CPM-Radiation";
 

	
 
const char *const labelLookup[] PROGMEM =
 
{
 
	label_0,
 
	label_1,
 
	label_2,
 
	label_3,
 
	label_4,
 
	label_5,
 
	label_6,
 
	label_7,
 
	label_8,
 
};
 

	
 
char labelBuffer[32]; // Size to length of label
 
char* slavesensors_getLabel(uint8_t sensorID) 
 
{
 
	if(sensorID < 9)
 
	{
 
		strncpy_P(labelBuffer,(char*)pgm_read_word(&(labelLookup[sensorID])),15);
 
		
 
		return labelBuffer;
 
	}
 
	else 
 
	{
 
		return NULL;
 
	}
 
}
 

	
 
uint8_t currentSlave = 0;
 
uint8_t currentSlaveSensor = 0;
 
 
bool requesting = false;
 

	
 
//#define DEBUG_NETWORKSCAN
 
//#define DEBUG_GETSLAVEDATA
 
 
char* bufPtr = 0x00;
 

	
 
static char slaveAddressLow[MAX_NUM_SLAVES][9];
 
static char slaveAddressHigh[MAX_NUM_SLAVES][9];
 
static char slaveNames[MAX_NUM_SLAVES][15];
 

	
 
static char loggerAddressLow[9];
 
static char loggerAddressHigh[9];
 

	
 
uint8_t nodeCount = 0;
 
bool dataReady = false;
 

	
 
void slavesensors_setup()
 
{
 
	loggerAddressLow[0] = 0x00;
 
	loggerAddressHigh[0] = 0x00;
 
}
 

	
 
char* slavesensors_slavename(uint8_t id) 
 
{
 
	return slaveNames[id];
 
}
 

	
 
void slavesensors_network_scan() 
 
{
 
	serial0_ioff();
 
	
 
	int atOK;
 
	
 
	#ifdef DEBUG_OUTPUT
 
	serial0_sendString("Beginning network scan...\r\n\r\n");
 
	#endif
 
	
 
	_delay_ms(500); // xbee warmup
 
	_delay_ms(200); // xbee warmup
 
	wdt_reset();
 
	
 
	led_on(LED_ACTIVITY);
 
	atOK = slavesensors_enterAT();
 
	
 
	// wait for OK
 
	if(atOK == 0)
 
	{
 
		led_on(LED_CYCLE);
 
		serial0_sendString("ATND");
 
		serial0_sendChar(0x0D);
 
				
 
		// Scan data end when newline by itself ("")	
 
		int lineCount = 0;	
 
	
 
		while(1) 
 
		{
 
			// Wait for scan to complete. If we timeout, return.
 
			if(waitTimeout(7000)) 
 
			if(waitTimeout(TIMEOUT_NETWORKSCAN)) 
 
			{
 
				return;
 
			}
 
			
 
			bufPtr = serial0_readLine();
 

	
 
			// If we're starting a new block but got a newline instead, we're done!
 
			if(lineCount == 0 && strcmp(bufPtr, "") == 0) 
 
			{
 
				break;			
 
			}
 
			
 
			if(lineCount == 1) 
 
			{
 
				strncpy(slaveAddressHigh[nodeCount],bufPtr, 9);
 
			}
 
			else if(lineCount == 2) 
 
			{
 
				strncpy(slaveAddressLow[nodeCount],bufPtr, 9);
 
			}
 
			else if(lineCount == 3) 
 
			{
 
				strncpy(slaveNames[nodeCount], bufPtr, 15);
 
			}
 
			
 
			// If we've finished one chunk (including the newline after it). Can't be else if because it controls increment.
 
			// If we finished one chunk (including the newline after it). Can't be else if because it controls increment.
 
			if(lineCount == 9) 
 
			{
 
				if(strcmp(slaveNames[nodeCount], XBEE_LOGDEST_NAME) == 0)
 
				{
 
					// Save logger address in the loggerAddressXXXX variables
 
					strncpy(loggerAddressHigh, slaveAddressHigh[nodeCount], 9);
 
					strncpy(loggerAddressLow, slaveAddressLow[nodeCount], 9);
 
					lineCount = 0;
 
					// don't increment, just overwrite this next time
 
				}
 
				else {
 
					// bufPtr should be null at this point, because we read in a newline after one chunk
 
					nodeCount++;
 
					lineCount = 0;
 
				}				
 
			}
 
			else 
 
			{
 
				lineCount++;
 
			}
 

	
 
		}		
 

	
 
		slavesensors_exitAT();
 

	
 
	}
 
	
 

	
 
	// Display number of found nodes on spinning indicator
 
	led_off(LED_ACT0);
 
	led_off(LED_ACT1);
 
	led_off(LED_ACT2);
 
	led_off(LED_ACT3);
 
	
 
	switch(nodeCount) 
 
	{
 
		case 0:
 
			break;
 
		case 3:
 
			led_on(LED_ACT2);
 
			_delay_ms(100);
 
		case 2:
 
			led_on(LED_ACT1);
 
			_delay_ms(100);	
 
		case 1:
 
			led_on(LED_ACT0);
 
			_delay_ms(100);
 
	}
 
	_delay_ms(500);
 
	led_on(LED_SIDEBOARD);
 
	_delay_ms(500);
 
	led_off(LED_SIDEBOARD);
 

	
 
	#ifdef DEBUG_NETWORKSCAN
 
	
 
	char debugBuf[64];
 
	serial0_sendString("Discovered: \r\n");
 
	for(int i=0; i<nodeCount; i++) 
 
	{
 
		snprintf(debugBuf, 64, "  %s - %s%s (%u)\r\n", slaveNames[i],slaveAddressHigh,slaveAddressLow[i], i);
 
		serial0_sendString(debugBuf);
 
	}
 
	serial0_sendString("\r\n");
 
	if(atOK != 0) 
 
	{
 
		serial0_sendString("AT mode failed \r\n");
 
	}
 
	
 
	#endif
 
	
 
	char infobuf[25];
 
	snprintf(infobuf, 25, "discovered %u nodes", nodeCount);
 
	info_log_msg(infobuf);
 

	
 
	_delay_ms(100);
 
	
 
	slavesensors_selectlogger();
 
	
 
	serial0_ion();
 
}
 
 
//#define DEBUG_CONTEXTSWITCH
 
//#define DEBUG_SELECTNODE
 
 
uint8_t selectedNode = 255;
 
uint8_t slavesensors_getselectednode() 
 
{
 
	return selectedNode;
 
}
 
 
void slavesensors_selectnode(uint8_t nodeIndex)
 
{
 
	if(selectedNode == nodeIndex)
 
	{
 
		return;
 
	}
 
	
 
	if(slavesensors_selectaddress(slaveAddressHigh[nodeIndex],slaveAddressLow[nodeIndex]) == true) 
 
	{
 
		selectedNode = nodeIndex;
 
	}	
 
}
 
	
 
bool slavesensors_selectaddress(char* addrHigh, char* addrLow) 
 
{
 
	serial0_ioff();
 
	
 
	#ifdef DEBUG_CONTEXTSWITCH
 
	uint32_t startTime = time_millis();
 
	#endif
 
	
 
	#ifdef DEBUG_SELECTNODE
 
	serial0_sendString("Switch to node ");
 
	serial0_sendChar(nodeIndex + 0x30);
 
	serial0_sendString("\r\n");
 
	#endif
 
	
 
	_delay_ms(20);
 
	char tmpBuf[23];
 
	
 
	// If we can get into AT mode
 
	if(slavesensors_enterAT() == 0) 
 
	{
 
		
 
		snprintf(tmpBuf, 23, "ATDH %s%c",addrHigh, 0x0D);
 
		serial0_sendString(tmpBuf);
 
		
 
		if(xbeeIsOk() != 0) 
 
		{
 
			error_log(ERROR_XBEETIMEOUT, true);
 
			return false;
 
		}
 
		
 
		snprintf(tmpBuf, 23, "ATDL %s%c",addrLow, 0x0D);
 
		serial0_sendString(tmpBuf);
 
		
 
		if(xbeeIsOk() != 0) 
 
		{
 
			error_log(ERROR_XBEETIMEOUT, true);
 
			return false;
 
		}
 
		
 
		slavesensors_exitAT();
 
	}
 
	_delay_ms(2);
 
	
 
	#ifdef DEBUG_SELECTNODE
 
	serial0_sendString("Selected ");
 
	serial0_sendChar(nodeIndex + 0x30);
 
	serial0_sendString("\r\n");
 
	#endif
 
	
 
	#ifdef DEBUG_CONTEXTSWITCH
 
	uint32_t switchTime = time_millis() - startTime;
 
	char tmpB[32];
 
	snprintf(tmpB, 32, "CTXSW: %lu ms\r\n", switchTime);
 
	serial0_sendString(tmpB);
 
	#endif
 
	
 
	serial0_ion();
 
	return true;
 
}
 
 
void slavesensors_selectlogger() 
 
{
 
	if(loggerAddressLow[0] != 0x00) 
 
	{
 
		slavesensors_selectaddress(loggerAddressHigh,loggerAddressLow);
 
	}	
 
}
 
 
void slavesensors_exitAT() 
 
{
 
	// Exit AT
 
	serial0_sendString("ATCN");
 
	serial0_sendChar(0x0D);
 
 
	if(waitTimeout(2000)) 
 
	if(waitTimeout(TIMEOUT_EXITAT)) 
 
	{
 
		return;
 
	}
 
	
 
	xbeeIsOk();
 
}
 
 
bool waitTimeout(uint32_t timeout) {
 
	uint32_t scanStart = time_millis();
 
	uint32_t lastBlink = 0;
 
	while(!serial0_hasChar())
 
	{
 
		if(time_millis() - scanStart > timeout)
 
		{
 
			error_log(ERROR_XBEETIMEOUT, true);
 
			return true;
 
		}
 
		if(time_millis() - lastBlink > 50)
 
		{
 
			led_spin();
 
			
 
			lastBlink = time_millis();
 
		}
 
		wdt_reset();
 
	}
 
	return false;
 
}
 
 
// Enter AT mode. Leaves "OK" on the buffer.
 
int slavesensors_enterAT() 
 
{
 
	// Delay guard time
 
	_delay_ms(2);
 
 
	serial0_ioff(); // interrupts MUST be off
 
	
 
	// Enter AT mode
 
	serial0_sendChar('+'); // Enter AT mode
 
	serial0_sendChar('+');
 
	serial0_sendChar('+');
 
 
	return xbeeIsOk();
 
}
 
 
int xbeeIsOk() 
 
{
 
	if(waitTimeout(2000)) {
 
	if(waitTimeout(TIMEOUT_XBEERESPONSE)) {
 
		error_log(ERROR_XBEETIMEOUT, true);
 
		return 1;
 
	}
 
	char* tmppntr = serial0_readLine();
 
	if(strcmp(tmppntr, "OK") == 0)
 
	{
 
		return 0;
 
	}
 
	else
 
	{
 
		error_log(ERROR_SLAVETIMEOUT, true);
 
		return 1;
 
	}
 
}
 
 
bool slavesensors_dataReady() 
 
{
 
	return dataReady;
 
}
 
 
bool slavesensors_isrequesting() 
 
{
 
	return requesting;	
 
}
 
 
void slavesensors_startprocess() 
 
{
 
	requesting = true;
 
	slavesensors_request();		
 
}
 
 
// TODO: inline. static.
 
uint32_t beginRequest = 0;
 
void slavesensors_request() 
 
{
 
	slavesensors_selectnode(currentSlave);
 
	beginRequest = time_millis();
 
	serial_sendCommand("@"); // Request data!
 
}
 
 
 
uint8_t numReadingsToExpect = 0; // number of values that the slave is about to send
 
uint8_t numRetries = 0;
 
 
void gotoNextSlaveOrSensor(bool fail) {
 
	// If we finished all sensors for all slaves
 
	if(currentSlave >= (nodeCount-1) && currentSlaveSensor >= (numReadingsToExpect-1))
 
	{
 
		#ifdef DEBUG_GETSLAVEDATA
 
		serial0_sendString("We got all data for all slaves!\r\n");
 
		#endif
 
		
 
		dataReady = true;
 
		currentSlave = 0;
 
		currentSlaveSensor = 0;
 
		requesting = false;
 
		
 
		if(!fail) 
 
		{
 
			led_alert();	
 
		}
 
		
 
	}
 
	// If we finished up one slave, go to the next
 
	else if(currentSlaveSensor >= (numReadingsToExpect-1))
 
	{
 
		#ifdef DEBUG_GETSLAVEDATA
 
		serial0_sendString("Finished up one slave, go to another.\r\n");
 
		#endif
 
		
 
		currentSlave++;
 
		currentSlaveSensor = 0;
 
		requesting = true;
 
		
 
		// EXPECT PROBLEMS HERE FROM NOT INCREMENTING
 
		//if(currentSlave == loggerIndex)
 
		//{
 
			//if(currentSlave >= (nodeCount-1))
 
			//{
 
				//// We hit the last one, we're done.
 
				//dataReady = true;
 
				//currentSlave = 0;
 
				//currentSlaveSensor = 0;
 
				//requesting = false;
 
				//led_alert();
 
				//return;
 
			//}
 
			//else
 
			//{
 
				//currentSlave++; // Skip the logger: increment to the next slave after the logger
 
			//}
 
		//}
 
		
 
		slavesensors_request();
 
	}
 
	// If we haven't finished a slave (or all of them), just get the next sensor of the current slave
 
	else
 
	{
 
		#ifdef DEBUG_GETSLAVEDATA
 
		serial0_sendString("Give me another sensor value...");
 
		#endif
 
		
 
		// request data for the current sensor of the current slave
 
		currentSlaveSensor++;
 
		requesting = true;
 
		//slavesensors_request();	 slaves now send all values at once, we don't need to keep requesting
 
	}
 
}
 
 
 
// TODO: needs to skip logger!
 
// Request data from slave modules
 
void slavesensors_process(uint8_t parseResult) 
 
{
 
	if(!requesting) 
 
	{
 
		// we got a command when we didn't request anything. probably skip it.
 
		// we got a command when we didn't request anything. skip it.
 
		return;
 
	}
 
	
 
	// TODO: If we time out, WE NEED TO RESET THE PARSER. It could be in a bad state.
 
	else if(parseResult == PARSERESULT_NODATA) 
 
	{
 
		// Wait for data
 
		if(requesting && time_millis() - beginRequest > 1000) {
 
		if(requesting && time_millis() - beginRequest > TIMEOUT_SLAVEREQUEST) {
 
			// if we're requesting, we have no data, and we're over the timeout, this is bad!
 
			// setParserState(STATE_RESET); - meh, can't do this because it freaking increments the cirbufptr
 
			gotoNextSlaveOrSensor(true);
 
			char* msg[128];
 
			snprintf(msg, 128, "Slave %u (%s) timeout",currentSlave,slaveNames[currentSlave]);
 
			error_log_msg(ERROR_SLAVETIMEOUT, false, msg); // log error, don't blink LED
 
		}
 
	}
 
	
 
	// Finished reception of a message (one sensor data value). If not finished, send out command to get the next one
 
	else if(parseResult == PARSERESULT_PARSEOK)
 
	{
 
		
 
		#ifdef DEBUG_GETSLAVEDATA
 
		char debug[50];
 
		snprintf(debug, 50, "Slave %u sensor %u of total nodes %u\r\n", currentSlave, currentSlaveSensor,nodeCount);
 
		serial0_sendString(debug);
 
		#endif
 
		
 
		// We got data, reset retries
 
		numRetries = 0;
 
		
 
		// We got some data, let's handle it
 
		// ASCII payload
 
		uint8_t len = getPayloadLength();
 
		char* load = getPayload();
 
		uint8_t type = getPayloadType();
 
		int32_t parsedVal = strtol(load, NULL, 10);//atoi(load);
 

	
 
		// Special case for slave telling us how many things we're about to get		
 
		if(type + 0x30 == '@')
 
		{
 
			
 
			#ifdef DEBUG_GETSLAVEDATA
 
			serial0_sendString("Got an awesome count!\r\n");
 
			serial0_sendChar(parsedVal + 0x30);
 
			serial0_sendString("\r\n");
 
			#endif
 
			
 
			numReadingsToExpect = parsedVal;
 
			currentSlaveSensor = 0;
 
			requesting = true;
 
		}
 
		else 
 
		{
 
		
 
			// Store data in structure
 
			sensordata_set(currentSlave,type,parsedVal);
 
			
 
			#ifdef DEBUG_GETSLAVEDATA
 
			serial0_sendString("Stored some sexy data!\r\n");
 
			#endif 
 
			
 
			gotoNextSlaveOrSensor(false);
 
		}
 
	}
 
	
 
	// If fail, try retransmit. Or we could skip and hit it next time.
 
	// TODO: Maximum number of retransmissions
 
	else if(parseResult == PARSERESULT_FAIL) 
 
	{
 
		if(requesting) 
 
		{
 
			slavesensors_request();	// re-request
 
			if(numRetries < MAX_SLAVEREQUEST_RETRIES) 
 
			{
 
				slavesensors_request();	// re-request
 
			}
 
			else {
 
				numRetries = 0;
 
				gotoNextSlaveOrSensor(true);
 
			}
 
		}			
 
	}
 
	
 
	
 
	else if(parseResult == PARSERESULT_STILLPARSING)
 
	{
 
		return; // do nothing
 
	}
 
	else 
 
	{
 
		// something is terribly wrong!
 
		error_log_msg(ERROR_FATAL, true, "parseResult is invalid!");
 
		return;
 
	}
 
}		
 
\ No newline at end of file
0 comments (0 inline, 0 general)