Changeset - 3243bfc71b35
[Not reviewed]
default
0 9 0
ethanzonca@CL-ENS241-08.cedarville.edu - 12 years ago 2013-03-22 15:56:41
ethanzonca@CL-ENS241-08.cedarville.edu
Fixed warnings on master, implemented watchdog ISR to flash LEDs and save program timer to EEPROM. Program timer is restored upon reboot after the watchdog triggers, the error LED is activated, and an error message is logged to the SD card.
9 files changed with 75 insertions and 7 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>
 
 
// --------------------------------------------------------------------------
 
// Hardware settings
 
// --------------------------------------------------------------------------
 
 
#define F_CPU 11059200
 
#define BOARDTEMP_ADDR 0x90
 
 
 
// --------------------------------------------------------------------------
 
// Module config (master.c)
 
// --------------------------------------------------------------------------
 
 
// Debug Output
 
//#define DEBUG_OUTPUT
 
 
// Blackout (turn off all but power LEDs)
 
#define BLACKOUT_ENABLE
 
//#define BLACKOUT_ENABLE
 
#define BLACKOUT_TIMEOUT 300000 // Blackout after 5 minutes (hopefully after fix)
 
 
// Board heater setpoint
 
#define HEATER_THRESHOLD 80
 
 
// Touchdown buzzer
 
#define BUZZER_RATE 3000
 
#define BUZZER_DURATION 3000
 
#define BUZZER_FAILSAFE_DURATION 600000
 
#define BUZZER_TRIGGER_MINDURATION 1
 
#define BUZZER_TRIGGER_MAXALTITUDE 1
 
 
// --------------------------------------------------------------------------
 
// Error Codes config (logger.c)
 
// --------------------------------------------------------------------------
 
 
// 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
 
#define ERROR_WATCHDOG 9
 
// !!! Please specify/update detailed messages for these error codes in logger.c
 
 
// --------------------------------------------------------------------------
 
// Slave Sensors config (slavesensors.c)
 
// --------------------------------------------------------------------------
 
 
// Slave data structure size
 
#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"
 
 
// Rate to request data from slaves. Must be greater than AT delay * number slaves
 
#define DATAREQUEST_RATE 3000
 
 
// Timeouts
 
#define TIMEOUT_SLAVEREQUEST 500
 
#define TIMEOUT_NETWORKSCAN 7000
 
#define TIMEOUT_EXITAT 2000
 
#define TIMEOUT_XBEERESPONSE 2000
 
 
// Slave data request max retries (for retry upon corrupt data reception)
 
#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 
 
 
 
// --------------------------------------------------------------------------
 
// GPS config (xxx.c)
 
// --------------------------------------------------------------------------
 
 
// NMEA circular buffer size. Must be large enough to hold all received sentences
 
#define NMEABUFFER_SIZE 150
 
 

	
 
// --------------------------------------------------------------------------
 
// USART config (serial.c)
 
// --------------------------------------------------------------------------
 
 
// Baud rates for XBEE and GPS serial ports
 
#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)
 
// --------------------------------------------------------------------------
 
 
// Log number EEPROM address (this number is incremented on boot, used for filenames)
 
#define LOGGER_ID_EEPROM_ADDR 0x10
 
 
// Watchdog save/restore of program timer
 
#define WATCHDOG_PROGTIMER_EEPROM_ADDR 0x20
 
 
// 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/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 8
 
#define MAX_ERRNO 9
 

	
 
// 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 err_9[] PROGMEM = "watchdog";
 

	
 
const char *const errorMessageLookup[] PROGMEM =
 
{
 
	err_0,
 
	err_1,
 
	err_2,
 
	err_3,
 
	err_4,
 
	err_5,
 
	err_6,
 
	err_7,
 
	err_8,
 
	err_9
 
};
 
 
 
 
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;
 
	}
 
		
 
	// 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((uint8_t*)LOGGER_ID_EEPROM_ADDR) + 1;
 
	eeprom_update_byte((uint8_t*)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);
 
	}	
 
}
 
 
 
char aprsInfoText[64];
 
bool aprsInfoTextAvailable = false;
 
char* logger_getAprsInfoText() 
 
{
 
	return aprsInfoText;	
 
}
 
 
bool logger_aprsInfoTextAvailable() 
 
{
 
	return aprsInfoTextAvailable;
 
}
 
 
void logger_aprsInfoTextConsumed() 
 
{
 
	aprsInfoTextAvailable = false;
 
}
 
 
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);
 
 
	snprintf(aprsInfoText, 64, "e[%u,%s,%s]", errNo, labelBuffer, infoText);
 
 
	aprsInfoTextAvailable = true;
 
	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);
 
	snprintf(aprsInfoText, 64, "i[%s]", infoText);
 
	aprsInfoTextAvailable = true;
 
	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/looptime.c
Show inline comments
 
/*
 
 * Master Firmware: Program Timer
 
 *
 
 * Wireless Observational Modular Aerial Network
 
 * 
 
 * Ethan Zonca
 
 * Matthew Kanning
 
 * Kyle Ripperger
 
 * Matthew Kroening
 
 *
 
 */
 
 
#include "../config.h"
 
#include "looptime.h"
 
#include <avr/io.h>
 
#include <avr/interrupt.h>
 
#include <util/delay.h>
 
 
volatile uint32_t millis = 0; // Milliseconds since initialization
 

	
 
void time_setup() 
 
{
 
	// Generic microcontroller config options
 
	OCR0A = 173; // Approx 172.7969 ticks per ms with 64 prescaler
 
	
 
	TCCR0A |= (1 << WGM01) | (1 << WGM00) | // Count until OCR0A, then overflow (wgm02 in the next line specifies this as well)
 
			  (1<< COM0B1); // Non-inverting PWM on OC0B
 
	TCCR0B |= (1 << CS01) | (1 << CS00) | (1 << WGM02); // clock div by 64
 
	TIFR0 |= ( 1 << TOV0 ); // clear pending interrupts
 
	TIMSK0 |= (1 << TOIE0); // enable overflow interrupt
 
}
 

	
 
ISR(TIMER0_OVF_vect) 
 
{
 
	millis = millis + 1;
 
}
 

	
 
uint32_t time_millis() 
 
{
 
	return millis; // meh accuracy, but that's OK
 
}
 

	
 
void time_watchdogrestore(uint32_t saved_millis)
 
{
 
	millis = saved_millis;
 
}	
master/master/lib/looptime.h
Show inline comments
 
/*
 
 * Master Firmware: Program Timer
 
 *
 
 * Wireless Observational Modular Aerial Network
 
 * 
 
 * Ethan Zonca
 
 * Matthew Kanning
 
 * Kyle Ripperger
 
 * Matthew Kroening
 
 *
 
 */
 
 
#ifndef LOOPTIME_H_
 
#define LOOPTIME_H_
 
 
#include <inttypes.h>
 

	
 
void time_setup();
 
uint32_t time_millis();
 
void time_watchdogrestore(uint32_t saved_millis);
 
 
#endif /* LOOPTIME_H_ */
 
\ No newline at end of file
master/master/lib/sdcard/sd_raw.c
Show inline comments
 

	
 
/*
 
 * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
 
 *
 
 * This file is free software; you can redistribute it and/or modify
 
 * it under the terms of either the GNU General Public License version 2
 
 * or the GNU Lesser General Public License version 2.1, both as
 
 * published by the Free Software Foundation.
 
 */
 

	
 
#include <string.h>
 
#include <avr/io.h>
 
#include "sd_raw.h"
 
#include "sd_raw_config.h"
 
#include "../led.h"
 
#include <avr/delay.h>
 

	
 
/**
 
 * \addtogroup sd_raw MMC/SD/SDHC card raw access
 
 *
 
 * This module implements read and write access to MMC, SD
 
 * and SDHC cards. It serves as a low-level driver for the
 
 * higher level modules such as partition and file system
 
 * access.
 
 *
 
 * @{
 
 */
 
/**
 
 * \file
 
 * MMC/SD/SDHC raw access implementation (license: GPLv2 or LGPLv2.1)
 
 *
 
 * \author Roland Riegel
 
 */
 

	
 
/**
 
 * \addtogroup sd_raw_config MMC/SD configuration
 
 * Preprocessor defines to configure the MMC/SD support.
 
 */
 

	
 
/**
 
 * @}
 
 */
 

	
 
/* commands available in SPI mode */
 

	
 
/* CMD0: response R1 */
 
#define CMD_GO_IDLE_STATE 0x00
 
/* CMD1: response R1 */
 
#define CMD_SEND_OP_COND 0x01
 
/* CMD8: response R7 */
 
#define CMD_SEND_IF_COND 0x08
 
/* CMD9: response R1 */
 
#define CMD_SEND_CSD 0x09
 
/* CMD10: response R1 */
 
#define CMD_SEND_CID 0x0a
 
/* CMD12: response R1 */
 
#define CMD_STOP_TRANSMISSION 0x0c
 
/* CMD13: response R2 */
 
#define CMD_SEND_STATUS 0x0d
 
/* CMD16: arg0[31:0]: block length, response R1 */
 
#define CMD_SET_BLOCKLEN 0x10
 
/* CMD17: arg0[31:0]: data address, response R1 */
 
#define CMD_READ_SINGLE_BLOCK 0x11
 
/* CMD18: arg0[31:0]: data address, response R1 */
 
#define CMD_READ_MULTIPLE_BLOCK 0x12
 
/* CMD24: arg0[31:0]: data address, response R1 */
 
#define CMD_WRITE_SINGLE_BLOCK 0x18
 
/* CMD25: arg0[31:0]: data address, response R1 */
 
#define CMD_WRITE_MULTIPLE_BLOCK 0x19
 
/* CMD27: response R1 */
 
#define CMD_PROGRAM_CSD 0x1b
 
/* CMD28: arg0[31:0]: data address, response R1b */
 
#define CMD_SET_WRITE_PROT 0x1c
 
/* CMD29: arg0[31:0]: data address, response R1b */
 
#define CMD_CLR_WRITE_PROT 0x1d
 
/* CMD30: arg0[31:0]: write protect data address, response R1 */
 
#define CMD_SEND_WRITE_PROT 0x1e
 
/* CMD32: arg0[31:0]: data address, response R1 */
 
#define CMD_TAG_SECTOR_START 0x20
 
/* CMD33: arg0[31:0]: data address, response R1 */
 
#define CMD_TAG_SECTOR_END 0x21
 
/* CMD34: arg0[31:0]: data address, response R1 */
 
#define CMD_UNTAG_SECTOR 0x22
 
/* CMD35: arg0[31:0]: data address, response R1 */
 
#define CMD_TAG_ERASE_GROUP_START 0x23
 
/* CMD36: arg0[31:0]: data address, response R1 */
 
#define CMD_TAG_ERASE_GROUP_END 0x24
 
/* CMD37: arg0[31:0]: data address, response R1 */
 
#define CMD_UNTAG_ERASE_GROUP 0x25
 
/* CMD38: arg0[31:0]: stuff bits, response R1b */
 
#define CMD_ERASE 0x26
 
/* ACMD41: arg0[31:0]: OCR contents, response R1 */
 
#define CMD_SD_SEND_OP_COND 0x29
 
/* CMD42: arg0[31:0]: stuff bits, response R1b */
 
#define CMD_LOCK_UNLOCK 0x2a
 
/* CMD55: arg0[31:0]: stuff bits, response R1 */
 
#define CMD_APP 0x37
 
/* CMD58: arg0[31:0]: stuff bits, response R3 */
 
#define CMD_READ_OCR 0x3a
 
/* CMD59: arg0[31:1]: stuff bits, arg0[0:0]: crc option, response R1 */
 
#define CMD_CRC_ON_OFF 0x3b
 

	
 
/* command responses */
 
/* R1: size 1 byte */
 
#define R1_IDLE_STATE 0
 
#define R1_ERASE_RESET 1
 
#define R1_ILL_COMMAND 2
 
#define R1_COM_CRC_ERR 3
 
#define R1_ERASE_SEQ_ERR 4
 
#define R1_ADDR_ERR 5
 
#define R1_PARAM_ERR 6
 
/* R1b: equals R1, additional busy bytes */
 
/* R2: size 2 bytes */
 
#define R2_CARD_LOCKED 0
 
#define R2_WP_ERASE_SKIP 1
 
#define R2_ERR 2
 
#define R2_CARD_ERR 3
 
#define R2_CARD_ECC_FAIL 4
 
#define R2_WP_VIOLATION 5
 
#define R2_INVAL_ERASE 6
 
#define R2_OUT_OF_RANGE 7
 
#define R2_CSD_OVERWRITE 7
 
#define R2_IDLE_STATE (R1_IDLE_STATE + 8)
 
#define R2_ERASE_RESET (R1_ERASE_RESET + 8)
 
#define R2_ILL_COMMAND (R1_ILL_COMMAND + 8)
 
#define R2_COM_CRC_ERR (R1_COM_CRC_ERR + 8)
 
#define R2_ERASE_SEQ_ERR (R1_ERASE_SEQ_ERR + 8)
 
#define R2_ADDR_ERR (R1_ADDR_ERR + 8)
 
#define R2_PARAM_ERR (R1_PARAM_ERR + 8)
 
/* R3: size 5 bytes */
 
#define R3_OCR_MASK (0xffffffffUL)
 
#define R3_IDLE_STATE (R1_IDLE_STATE + 32)
 
#define R3_ERASE_RESET (R1_ERASE_RESET + 32)
 
#define R3_ILL_COMMAND (R1_ILL_COMMAND + 32)
 
#define R3_COM_CRC_ERR (R1_COM_CRC_ERR + 32)
 
#define R3_ERASE_SEQ_ERR (R1_ERASE_SEQ_ERR + 32)
 
#define R3_ADDR_ERR (R1_ADDR_ERR + 32)
 
#define R3_PARAM_ERR (R1_PARAM_ERR + 32)
 
/* Data Response: size 1 byte */
 
#define DR_STATUS_MASK 0x0e
 
#define DR_STATUS_ACCEPTED 0x05
 
#define DR_STATUS_CRC_ERR 0x0a
 
#define DR_STATUS_WRITE_ERR 0x0c
 

	
 
/* status bits for card types */
 
#define SD_RAW_SPEC_1 0
 
#define SD_RAW_SPEC_2 1
 
#define SD_RAW_SPEC_SDHC 2
 

	
 
#if !SD_RAW_SAVE_RAM
 
/* static data buffer for acceleration */
 
static uint8_t raw_block[512];
 
/* offset where the data within raw_block lies on the card */
 
static offset_t raw_block_address;
 
#if SD_RAW_WRITE_BUFFERING
 
/* flag to remember if raw_block was written to the card */
 
static uint8_t raw_block_written;
 
#endif
 
#endif
 

	
 
/* card type state */
 
static uint8_t sd_raw_card_type;
 

	
 
/* private helper functions */
 
static void sd_raw_send_byte(uint8_t b);
 
static uint8_t sd_raw_rec_byte();
 
static uint8_t sd_raw_send_command(uint8_t command, uint32_t arg);
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Initializes memory card communication.
 
 *
 
 * \returns 0 on failure, 1 on success.
 
 */
 
uint8_t sd_raw_init()
 
{
 
    /* enable inputs for reading card status */
 
    configure_pin_available();
 
    configure_pin_locked();
 

	
 
    /* enable outputs for MOSI, SCK, SS, input for MISO */
 
    configure_pin_mosi();
 
    configure_pin_sck();
 
    configure_pin_ss();
 
    configure_pin_miso();
 

	
 
    unselect_card();
 
	
 
    // initialize SPI with lowest frequency; max. 400kHz during identification mode of card
 
    SPCR = (0 << SPIE) | // SPI Interrupt Enable 
 
            (1 << SPE)  | // SPI Enable 
 
            (0 << DORD) | // Data Order: MSB first 
 
            (1 << MSTR) | // Master mode 
 
            (0 << CPOL) | // Clock Polarity: SCK low when idle 
 
            (0 << CPHA) | // Clock Phase: sample on rising SCK edge 
 
            (1 << SPR1);   // Clock Frequency: f_OSC / 64 which gives over 100khz required minimum clock
 
    //SPSR &= ~(1 << SPI2X); // No doubled clock frequency 
 
	SPCR |= (1<< SPI2X); // Double clock frequ3ency so just less than 400khz
 

	
 
    /* initialization procedure */
 
    sd_raw_card_type = 0;
 
    
 
    if(!sd_raw_available()) {
 
        return 0;
 
	}		
 

	
 
    /* card needs 74 cycles minimum to start up */
 
    for(uint8_t i = 0; i < 10; ++i)
 
    {
 
        /* wait 8 clock cycles */
 
        sd_raw_rec_byte();
 
    }
 

	
 
    /* address card */
 
    select_card();
 

	
 
    /* reset card */
 
    uint8_t response;
 
    for(uint16_t i = 0; ; ++i)
 
    {
 
        response = sd_raw_send_command(CMD_GO_IDLE_STATE, 0);
 
        if(response == (1 << R1_IDLE_STATE))
 
            break;
 

	
 
        if(i == 0x1ff)
 
        {
 
            unselect_card();
 
            return 0;
 
        }
 
    }
 

	
 
#if SD_RAW_SDHC
 
    /* check for version of SD card specification */
 
    response = sd_raw_send_command(CMD_SEND_IF_COND, 0x100 /* 2.7V - 3.6V */ | 0xaa /* test pattern */);
 
    if((response & (1 << R1_ILL_COMMAND)) == 0)
 
    {
 
        sd_raw_rec_byte();
 
        sd_raw_rec_byte();
 
        if((sd_raw_rec_byte() & 0x01) == 0)
 
            return 0; /* card operation voltage range doesn't match */
 
        if(sd_raw_rec_byte() != 0xaa)
 
            return 0; /* wrong test pattern */
 

	
 
        /* card conforms to SD 2 card specification */
 
        sd_raw_card_type |= (1 << SD_RAW_SPEC_2);
 
    }
 
    else
 
#endif
 
    {
 
        /* determine SD/MMC card type */
 
        sd_raw_send_command(CMD_APP, 0);
 
        response = sd_raw_send_command(CMD_SD_SEND_OP_COND, 0);
 
        if((response & (1 << R1_ILL_COMMAND)) == 0)
 
        {
 
            /* card conforms to SD 1 card specification */
 
            sd_raw_card_type |= (1 << SD_RAW_SPEC_1);
 
        }
 
        else
 
        {
 
            /* MMC card */
 
        }
 
    }
 

	
 
    /* wait for card to get ready */
 
    for(uint16_t i = 0; ; ++i)
 
    {
 
        if(sd_raw_card_type & ((1 << SD_RAW_SPEC_1) | (1 << SD_RAW_SPEC_2)))
 
        {
 
            uint32_t arg = 0;
 
#if SD_RAW_SDHC
 
            if(sd_raw_card_type & (1 << SD_RAW_SPEC_2))
 
                arg = 0x40000000;
 
#endif
 
            sd_raw_send_command(CMD_APP, 0);
 
            response = sd_raw_send_command(CMD_SD_SEND_OP_COND, arg);
 
        }
 
        else
 
        {
 
            response = sd_raw_send_command(CMD_SEND_OP_COND, 0);
 
        }
 

	
 
        if((response & (1 << R1_IDLE_STATE)) == 0)
 
            break;
 

	
 
        if(i == 0x7fff)
 
        {
 
            unselect_card();
 
            return 0;
 
        }
 
    }
 

	
 
#if SD_RAW_SDHC
 
    if(sd_raw_card_type & (1 << SD_RAW_SPEC_2))
 
    {
 
        if(sd_raw_send_command(CMD_READ_OCR, 0))
 
        {
 
            unselect_card();
 
            return 0;
 
        }
 

	
 
        if(sd_raw_rec_byte() & 0x40)
 
            sd_raw_card_type |= (1 << SD_RAW_SPEC_SDHC);
 

	
 
        sd_raw_rec_byte();
 
        sd_raw_rec_byte();
 
        sd_raw_rec_byte();
 
    }
 
#endif
 

	
 
    /* set block size to 512 bytes */
 
    if(sd_raw_send_command(CMD_SET_BLOCKLEN, 512))
 
    {
 
        unselect_card();
 
        return 0;
 
    }
 

	
 
    /* deaddress card */
 
    unselect_card();
 

	
 
    /* switch to highest SPI frequency possible */
 
    SPCR &= ~((1 << SPR1) | (1 << SPR0)); /* Clock Frequency: f_OSC / 4 */
 
    SPSR |= (1 << SPI2X); /* Doubled Clock Frequency: f_OSC / 2 */
 

	
 

	
 

	
 
#if !SD_RAW_SAVE_RAM
 
    /* the first block is likely to be accessed first, so precache it here */
 
    raw_block_address = (offset_t) -1;
 
#if SD_RAW_WRITE_BUFFERING
 
    raw_block_written = 1;
 
#endif
 
    if(!sd_raw_read(0, raw_block, sizeof(raw_block))) {
 
		return 0;
 
	}
 
	
 
#endif
 
			
 
    return 1;
 
}
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Checks wether a memory card is located in the slot.
 
 *
 
 * \returns 1 if the card is available, 0 if it is not.
 
 */
 
uint8_t sd_raw_available()
 
{
 
	return 1; // EMZ: Assume always available
 
    return get_pin_available() == 0x00;
 
}
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Checks whether the memory card is locked for write access.
 
 *
 
 * \returns 1 if the card is locked, 0 if it is not.
 
 */
 
uint8_t sd_raw_locked()
 
{
 
	return 0; // EMZ: Assume always unlocked
 
    return get_pin_locked() == 0x00;
 
}
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Sends a raw byte to the memory card.
 
 *
 
 * \param[in] b The byte to sent.
 
 * \see sd_raw_rec_byte
 
 */
 
void sd_raw_send_byte(uint8_t b)
 
{
 
    SPDR = b;
 
    /* wait for byte to be shifted out */
 
    while(!(SPSR & (1 << SPIF)));
 
    SPSR &= ~(1 << SPIF);
 
}
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Receives a raw byte from the memory card.
 
 *
 
 * \returns The byte which should be read.
 
 * \see sd_raw_send_byte
 
 */
 
uint8_t sd_raw_rec_byte()
 
{
 
    /* send dummy data for receiving some */
 

	
 
    SPDR = 0xff;
 
    while(!(SPSR & (1 << SPIF)));
 
    SPSR &= ~(1 << SPIF);
 

	
 
    return SPDR;
 
}
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Send a command to the memory card which responses with a R1 response (and possibly others).
 
 *
 
 * \param[in] command The command to send.
 
 * \param[in] arg The argument for command.
 
 * \returns The command answer.
 
 */
 
uint8_t sd_raw_send_command(uint8_t command, uint32_t arg)
 
{
 
    uint8_t response;
 

	
 
    /* wait some clock cycles */
 
    sd_raw_rec_byte();
 

	
 
    /* send command via SPI */
 
    sd_raw_send_byte(0x40 | command);
 
    sd_raw_send_byte((arg >> 24) & 0xff);
 
    sd_raw_send_byte((arg >> 16) & 0xff);
 
    sd_raw_send_byte((arg >> 8) & 0xff);
 
    sd_raw_send_byte((arg >> 0) & 0xff);
 
    switch(command)
 
    {
 
        case CMD_GO_IDLE_STATE:
 
           sd_raw_send_byte(0x95);
 
           break;
 
        case CMD_SEND_IF_COND:
 
           sd_raw_send_byte(0x87);
 
           break;
 
        default:
 
           sd_raw_send_byte(0xff);
 
           break;
 
    }
 
    
 
    /* receive response */
 
    for(uint8_t i = 0; i < 10; ++i)
 
    {
 
        response = sd_raw_rec_byte();
 
        if(response != 0xff)
 
            break;
 
    }
 

	
 
    return response;
 
}
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Reads raw data from the card.
 
 *
 
 * \param[in] offset The offset from which to read.
 
 * \param[out] buffer The buffer into which to write the data.
 
 * \param[in] length The number of bytes to read.
 
 * \returns 0 on failure, 1 on success.
 
 * \see sd_raw_read_interval, sd_raw_write, sd_raw_write_interval
 
 */
 
uint8_t sd_raw_read(offset_t offset, uint8_t* buffer, uintptr_t length)
 
{
 
    offset_t block_address;
 
    uint16_t block_offset;
 
    uint16_t read_length;
 
    while(length > 0)
 
    {
 
        /* determine byte count to read at once */
 
        block_offset = offset & 0x01ff;
 
        block_address = offset - block_offset;
 
        read_length = 512 - block_offset; /* read up to block border */
 
        if(read_length > length)
 
            read_length = length;
 
        
 
#if !SD_RAW_SAVE_RAM
 
        /* check if the requested data is cached */
 
        if(block_address != raw_block_address)
 
#endif
 
        {
 
#if SD_RAW_WRITE_BUFFERING
 
            if(!sd_raw_sync())
 
                return 0;
 
#endif
 

	
 
            /* address card */
 
            select_card();
 

	
 
            /* send single block request */
 
#if SD_RAW_SDHC
 
            if(sd_raw_send_command(CMD_READ_SINGLE_BLOCK, (sd_raw_card_type & (1 << SD_RAW_SPEC_SDHC) ? block_address / 512 : block_address)))
 
#else
 
            if(sd_raw_send_command(CMD_READ_SINGLE_BLOCK, block_address))
 
#endif
 
            {
 
                unselect_card();
 
                return 0;
 
            }
 

	
 
            /* wait for data block (start byte 0xfe) */
 
            while(sd_raw_rec_byte() != 0xfe);
 

	
 
#if SD_RAW_SAVE_RAM
 
            /* read byte block */
 
            uint16_t read_to = block_offset + read_length;
 
            for(uint16_t i = 0; i < 512; ++i)
 
            {
 
                uint8_t b = sd_raw_rec_byte();
 
                if(i >= block_offset && i < read_to)
 
                    *buffer++ = b;
 
            }
 
#else
 
            /* read byte block */
 
            uint8_t* cache = raw_block;
 
            for(uint16_t i = 0; i < 512; ++i)
 
                *cache++ = sd_raw_rec_byte();
 
            raw_block_address = block_address;
 

	
 
            memcpy(buffer, raw_block + block_offset, read_length);
 
            buffer += read_length;
 
#endif
 
            
 
            /* read crc16 */
 
            sd_raw_rec_byte();
 
            sd_raw_rec_byte();
 
            
 
            /* deaddress card */
 
            unselect_card();
 

	
 
            /* let card some time to finish */
 
            sd_raw_rec_byte();
 
        }
 
#if !SD_RAW_SAVE_RAM
 
        else
 
        {
 
            /* use cached data */
 
            memcpy(buffer, raw_block + block_offset, read_length);
 
            buffer += read_length;
 
        }
 
#endif
 

	
 
        length -= read_length;
 
        offset += read_length;
 
    }
 

	
 
    return 1;
 
}
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Continuously reads units of \c interval bytes and calls a callback function.
 
 *
 
 * This function starts reading at the specified offset. Every \c interval bytes,
 
 * it calls the callback function with the associated data buffer.
 
 *
 
 * By returning zero, the callback may stop reading.
 
 *
 
 * \note Within the callback function, you can not start another read or
 
 *       write operation.
 
 * \note This function only works if the following conditions are met:
 
 *       - (offset - (offset % 512)) % interval == 0
 
 *       - length % interval == 0
 
 *
 
 * \param[in] offset Offset from which to start reading.
 
 * \param[in] buffer Pointer to a buffer which is at least interval bytes in size.
 
 * \param[in] interval Number of bytes to read before calling the callback function.
 
 * \param[in] length Number of bytes to read altogether.
 
 * \param[in] callback The function to call every interval bytes.
 
 * \param[in] p An opaque pointer directly passed to the callback function.
 
 * \returns 0 on failure, 1 on success
 
 * \see sd_raw_write_interval, sd_raw_read, sd_raw_write
 
 */
 
uint8_t sd_raw_read_interval(offset_t offset, uint8_t* buffer, uintptr_t interval, uintptr_t length, sd_raw_read_interval_handler_t callback, void* p)
 
{
 
    if(!buffer || interval == 0 || length < interval || !callback)
 
        return 0;
 

	
 
#if !SD_RAW_SAVE_RAM
 
    while(length >= interval)
 
    {
 
        /* as reading is now buffered, we directly
 
         * hand over the request to sd_raw_read()
 
         */
 
        if(!sd_raw_read(offset, buffer, interval))
 
            return 0;
 
        if(!callback(buffer, offset, p))
 
            break;
 
        offset += interval;
 
        length -= interval;
 
    }
 

	
 
    return 1;
 
#else
 
    /* address card */
 
    select_card();
 

	
 
    uint16_t block_offset;
 
    uint16_t read_length;
 
    uint8_t* buffer_cur;
 
    uint8_t finished = 0;
 
    do
 
    {
 
        /* determine byte count to read at once */
 
        block_offset = offset & 0x01ff;
 
        read_length = 512 - block_offset;
 
        
 
        /* send single block request */
 
#if SD_RAW_SDHC
 
        if(sd_raw_send_command(CMD_READ_SINGLE_BLOCK, (sd_raw_card_type & (1 << SD_RAW_SPEC_SDHC) ? offset / 512 : offset - block_offset)))
 
#else
 
        if(sd_raw_send_command(CMD_READ_SINGLE_BLOCK, offset - block_offset))
 
#endif
 
        {
 
            unselect_card();
 
            return 0;
 
        }
 

	
 
        /* wait for data block (start byte 0xfe) */
 
        while(sd_raw_rec_byte() != 0xfe);
 

	
 
        /* read up to the data of interest */
 
        for(uint16_t i = 0; i < block_offset; ++i)
 
            sd_raw_rec_byte();
 

	
 
        /* read interval bytes of data and execute the callback */
 
        do
 
        {
 
            if(read_length < interval || length < interval)
 
                break;
 

	
 
            buffer_cur = buffer;
 
            for(uint16_t i = 0; i < interval; ++i)
 
                *buffer_cur++ = sd_raw_rec_byte();
 

	
 
            if(!callback(buffer, offset + (512 - read_length), p))
 
            {
 
                finished = 1;
 
                break;
 
            }
 

	
 
            read_length -= interval;
 
            length -= interval;
 

	
 
        } while(read_length > 0 && length > 0);
 
        
 
        /* read rest of data block */
 
        while(read_length-- > 0)
 
            sd_raw_rec_byte();
 
        
 
        /* read crc16 */
 
        sd_raw_rec_byte();
 
        sd_raw_rec_byte();
 

	
 
        if(length < interval)
 
            break;
 

	
 
        offset = offset - block_offset + 512;
 

	
 
    } while(!finished);
 
    
 
    /* deaddress card */
 
    unselect_card();
 

	
 
    /* let card some time to finish */
 
    sd_raw_rec_byte();
 

	
 
    return 1;
 
#endif
 
}
 

	
 
#if DOXYGEN || SD_RAW_WRITE_SUPPORT
 
/**
 
 * \ingroup sd_raw
 
 * Writes raw data to the card.
 
 *
 
 * \note If write buffering is enabled, you might have to
 
 *       call sd_raw_sync() before disconnecting the card
 
 *       to ensure all remaining data has been written.
 
 *
 
 * \param[in] offset The offset where to start writing.
 
 * \param[in] buffer The buffer containing the data to be written.
 
 * \param[in] length The number of bytes to write.
 
 * \returns 0 on failure, 1 on success.
 
 * \see sd_raw_write_interval, sd_raw_read, sd_raw_read_interval
 
 */
 
uint8_t sd_raw_write(offset_t offset, const uint8_t* buffer, uintptr_t length)
 
{
 
    if(sd_raw_locked())
 
        return 0;
 

	
 
    offset_t block_address;
 
    uint16_t block_offset;
 
    uint16_t write_length;
 
    while(length > 0)
 
    {
 
        /* determine byte count to write at once */
 
        block_offset = offset & 0x01ff;
 
        block_address = offset - block_offset;
 
        write_length = 512 - block_offset; /* write up to block border */
 
        if(write_length > length)
 
            write_length = length;
 
        
 
        /* Merge the data to write with the content of the block.
 
         * Use the cached block if available.
 
         */
 
        if(block_address != raw_block_address)
 
        {
 
#if SD_RAW_WRITE_BUFFERING
 
            if(!sd_raw_sync())
 
                return 0;
 
#endif
 

	
 
            if(block_offset || write_length < 512)
 
            {
 
                if(!sd_raw_read(block_address, raw_block, sizeof(raw_block)))
 
                    return 0;
 
            }
 
            raw_block_address = block_address;
 
        }
 

	
 
        if(buffer != raw_block)
 
        {
 
            memcpy(raw_block + block_offset, buffer, write_length);
 

	
 
#if SD_RAW_WRITE_BUFFERING
 
            raw_block_written = 0;
 

	
 
            if(length == write_length)
 
                return 1;
 
#endif
 
        }
 

	
 
        /* address card */
 
        select_card();
 

	
 
        /* send single block request */
 
#if SD_RAW_SDHC
 
        if(sd_raw_send_command(CMD_WRITE_SINGLE_BLOCK, (sd_raw_card_type & (1 << SD_RAW_SPEC_SDHC) ? block_address / 512 : block_address)))
 
#else
 
        if(sd_raw_send_command(CMD_WRITE_SINGLE_BLOCK, block_address))
 
#endif
 
        {
 
            unselect_card();
 
            return 0;
 
        }
 

	
 
        /* send start byte */
 
        sd_raw_send_byte(0xfe);
 

	
 
        /* write byte block */
 
        uint8_t* cache = raw_block;
 
        for(uint16_t i = 0; i < 512; ++i)
 
            sd_raw_send_byte(*cache++);
 

	
 
        /* write dummy crc16 */
 
        sd_raw_send_byte(0xff);
 
        sd_raw_send_byte(0xff);
 

	
 
        /* wait while card is busy */
 
        while(sd_raw_rec_byte() != 0xff);
 
        sd_raw_rec_byte();
 

	
 
        /* deaddress card */
 
        unselect_card();
 

	
 
        buffer += write_length;
 
        offset += write_length;
 
        length -= write_length;
 

	
 
#if SD_RAW_WRITE_BUFFERING
 
        raw_block_written = 1;
 
#endif
 
    }
 

	
 
    return 1;
 
}
 
#endif
 

	
 
#if DOXYGEN || SD_RAW_WRITE_SUPPORT
 
/**
 
 * \ingroup sd_raw
 
 * Writes a continuous data stream obtained from a callback function.
 
 *
 
 * This function starts writing at the specified offset. To obtain the
 
 * next bytes to write, it calls the callback function. The callback fills the
 
 * provided data buffer and returns the number of bytes it has put into the buffer.
 
 *
 
 * By returning zero, the callback may stop writing.
 
 *
 
 * \param[in] offset Offset where to start writing.
 
 * \param[in] buffer Pointer to a buffer which is used for the callback function.
 
 * \param[in] length Number of bytes to write in total. May be zero for endless writes.
 
 * \param[in] callback The function used to obtain the bytes to write.
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"
 

	
 

	
 
// !!! Remember to update the ENUM in slavesensors.h when changing things here
 

	
 
// 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(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 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();
 
	
 
	// If we don't have slaves to worry about, we're good to go
 
	if(nodeCount == 0)
 
	{
 
		dataReady = true;
 
	}		
 
	
 
	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(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();
 
			led_power_toggle();
 
			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(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() 
 
{
 
	if(nodeCount == 0)
 
	{
 
		return;
 
	}		
 
	requesting = true;
 
	slavesensors_request();		
 
}
 
 
 
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;
 
		
 
		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;
 
	}
 
}
 
 
// Request data from slave modules
 
void slavesensors_process(uint8_t parseResult) 
 
{
 
	if(!requesting) 
 
	{
 
		// 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) > 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
 

	
 
			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
 
			
 
			gotoNextSlaveOrSensor(true);
 
		}
 
	}
 
	
 
	// 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 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 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) 
 
		{
 
			if(numRetries < MAX_SLAVEREQUEST_RETRIES) 
 
			{
 
				slavesensors_request();	// re-request
 
			}
 
			else {
 
				numRetries = 0;
 
				
 
				char msg[128];
 
				snprintf(msg, 128, "Slave %u (%s) failed max retries",currentSlave,slaveNames[currentSlave]);
 
				error_log_msg(ERROR_SLAVETIMEOUT, false, msg); // log error, don't blink LED
 
				
 
				gotoNextSlaveOrSensor(true);
 
			}
 
		}			
 
	}
 
	
 
	
 
	else if(parseResult == PARSERESULT_STILLPARSING)
 
	{
 
		return; // do nothing
 
	}
 
	else 
 
	{
 
		error_log_msg(ERROR_FATAL, true, "parseResult invalid!");
 
		return;
 
	}
 
}		
 
\ No newline at end of file
master/master/lib/watchdog.c
Show inline comments
 
/*
 
 * Master Firmware: Watchdog Timer
 
 *
 
 * Wireless Observational Modular Aerial Network
 
 * 
 
 * Ethan Zonca
 
 * Matthew Kanning
 
 * Kyle Ripperger
 
 * Matthew Kroening
 
 *
 
 */
 
 
 
#include "../config.h"
 
#include <avr/io.h>
 
#include <avr/interrupt.h>
 
#include <avr/wdt.h>
 
#include <util/delay.h>
 
#include <avr/eeprom.h>
 
 
#include "looptime.h"
 
#include "led.h"
 
#include "logger.h"
 
 
//initialize watchdog
 
void watchdog_setup(void)
 
{
 
	cli();
 
	wdt_reset();
 
	// Set change enable bit, enable the WDT
 
	WDTCSR = (1<<WDCE)|(1<<WDE);
 
	WDTCSR = (1<<WDCE)|(1<<WDE)| (1<<WDIE);
 
	// Start watchdog, 4 second timeout
 
	WDTCSR = (1<<WDE)|(1<<WDP3)|(1<<WDP0);
 
	WDTCSR = (1<<WDE)|(1<<WDP3)|(1<<WDP0)|(1<<WDIE);
 
	sei();
 
}
 
 
void watchdog_checkreset(void)
 
{
 
	// Check if WDT reset occurred
 
	if( (MCUSR & (1<<WDRF)) > 0)
 
	{
 
		MCUSR &= ~(1<<WDRF);
 
		
 
		// Restore timer from EEPROM
 
		uint32_t oldtimer = 0;
 
		oldtimer = eeprom_read_dword((uint32_t*)WATCHDOG_PROGTIMER_EEPROM_ADDR);
 
		time_watchdogrestore(oldtimer);
 
		
 
		error_log_msg(ERROR_WATCHDOG,false,"Booting up after watchdog reset");
 
	}
 
}
 
 
ISR(WDT_vect) 
 
{
 
	// Store timer in EEPROM
 
	eeprom_write_dword((uint32_t*)WATCHDOG_PROGTIMER_EEPROM_ADDR, time_millis());
 
	
 
	// Trippy error LEDs while waiting 4 seconds for system reset
 
	while(1)
 
	{
 
		led_on(LED_ACT0);
 
		led_on(LED_ACT1);
 
		led_on(LED_ACT2);
 
		led_on(LED_ACT3);
 
		led_on(LED_ERROR);
 
		led_on(LED_STATUS);
 
		led_on(LED_ACTIVITY);
 
		led_on(LED_SIDEBOARD);
 
		_delay_ms(50);
 
		led_off(LED_ACT0);
 
		led_off(LED_ACT1);
 
		led_off(LED_ACT2);
 
		led_off(LED_ACT3);
 
		led_off(LED_ERROR);
 
		led_off(LED_STATUS);
 
		led_off(LED_ACTIVITY);
 
		led_off(LED_SIDEBOARD);
 
		_delay_ms(50);
 
	}	
 
}
 
\ No newline at end of file
master/master/lib/watchdog.h
Show inline comments
 
/*
 
 * Master Firmware: Watchdog Timer
 
 *
 
 * Wireless Observational Modular Aerial Network
 
 * 
 
 * Ethan Zonca
 
 * Matthew Kanning
 
 * Kyle Ripperger
 
 * Matthew Kroening
 
 *
 
 */
 
 
 
#ifndef WATCHDOG_H
 
#define WATCHDOG_H
 
 
void watchdog_setup(void);
 
void watchdog_checkreset(void);
 
 
#endif
 
\ No newline at end of file
master/master/master.c
Show inline comments
 
/*
 
 * Master Firmware
 
 *
 
 * Wireless Observational Modular Aerial Network
 
 * 
 
 * Ethan Zonca
 
 * Matthew Kanning
 
 * Kyle Ripperger
 
 * Matthew Kroening
 
 *
 
 */
 

	
 

	
 
#include "config.h"
 

	
 
#include <avr/io.h>
 
#include <util/delay.h>
 
#include <avr/wdt.h>
 
#include <avr/interrupt.h>
 
#include <stdio.h>
 
#include <stdbool.h>
 
#include <string.h>
 

	
 
#include "lib/serial.h"
 
#include "lib/aprs.h"
 
#include "lib/afsk.h"
 
#include "lib/led.h"
 
#include "lib/logger.h"
 
#include "lib/watchdog.h"
 
#include "lib/gps.h"
 
#include "lib/i2c.h"
 
#include "lib/sensors.h"
 
#include "lib/heater.h"
 
#include "lib/looptime.h"
 
#include "lib/slavesensors.h"
 
#include "lib/serparser.h"
 
#include "lib/sensordata.h"
 

	
 
int main(void)
 
{
 
	// Initialize libraries
 
	time_setup();
 
	watchdog_setup(); // enables interrupts
 
	
 
	// Power debounce
 
	_delay_ms(1000);
 
	
 
	led_setup();
 
	gps_setup();
 
	serial0_setup();
 
	serial1_setup();
 
	i2c_init();
 
	sensordata_setup(); // must happen before slavesensors/logger/AFSK
 
	slavesensors_setup();
 
	sensors_setup();
 
	logger_setup();
 
	watchdog_checkreset();
 
	afsk_setup();
 
	
 
	serial0_sendString("\r\nHello.\r\n\r\n");
 
	
 
	// Blocking ZigBee node discovery
 
	slavesensors_network_scan();
 
	
 
	// Software timers	
 
	uint32_t lastAprsBroadcast = 0;
 
	uint32_t lastLog = 0;
 
	uint32_t lastLedCycle = 0;
 
	uint32_t lastDataReq = 0;
 
	uint32_t lastBuzz = 0;
 
	
 
	// Result of last parser run
 
	int parseResult = PARSERESULT_NODATA;
 
	
 
	while(1)
 
    {
 
		// Periodic: LED execution indicator
 
		if(time_millis() - lastLedCycle > LEDCYCLE_RATE) 
 
		{
 
			led_power_toggle();
 
			led_spin();
 
			
 
			// Enable GPS serial interrupts if we aren't doing AFSK
 
			if(!afsk_busy())
 
				serial1_ion();
 
				
 
			lastLedCycle = time_millis();	
 
		}
 
		
 
		// Periodic: Logging
 
		if(time_millis() - lastLog > LOGGER_RATE) 
 
		{
 
			led_on(LED_CYCLE);
 
			
 
			heater_regulateTemp();
 
			
 
			// Turn on sideboard LED if we have a fix
 
			if(gps_hasfix()) 
 
			{
 
				led_on(LED_SIDEBOARD);
 
			}
 
			else 
 
			{
 
				led_off(LED_SIDEBOARD);
 
			}
 
			
 
			sensors_readBoardTemp();
 
			sensors_readBatt();
 
			
 
			//char buftmp[15];
 
			//snprintf(buftmp, 15, "vbatt: %u\r\n", sensors_getBatt());
 
			//serial0_sendString(buftmp);
 
		
 
			// Write CSV header and log data values
 
			sensordata_logvalues();			
 
			
 
			led_off(LED_CYCLE);
 
			lastLog = time_millis();
 
		}		
 
		
 
		// Periodic: Buzzer
 
		if(time_millis() - lastBuzz > BUZZER_RATE) {
 
			if(sensordata_isTouchdown())
 
			{
 
				led_on(LED_BUZZ);
 
				_delay_ms(BUZZER_DURATION);
 
				led_off(LED_BUZZ);
 
			}			
 
			lastBuzz = time_millis();
 
		}
 
		
 
		// Periodic: Data Request
 
		if(time_millis() - lastDataReq > DATAREQUEST_RATE)  
 
		{
 
			// Start getting values for next transmission
 
			if(slavesensors_isrequesting())
 
			{
 
				// This probably is a non-issue
 
				//error_log_msg(ERROR_FATAL, false, "Still requesting on following loop");
 
			}
 
			else
 
			{
 
				slavesensors_startprocess();
 
			}
 
			
 
			lastDataReq = time_millis();
 
		}
 
		
 
		
 
		// Periodic: APRS transmission
 
		if(time_millis() - lastAprsBroadcast > APRS_TRANSMIT_PERIOD) 
 
		{
 
			// Check for touchdown
 
			sensordata_checkTouchdown();
 
			
 
			// Ensure we aren't already transmitting
 
			while(afsk_busy());
 
			
 
			// Turn off interrupts and transmit APRS sentence
 
			serial1_ioff();
 
			aprs_send(); // non-blocking
 
			
 
			lastAprsBroadcast = time_millis();
 
		}			
 
		
 
		#ifdef BLACKOUT_ENABLE
 
		bool black = false;
 
		if(!black && time_millis() > BLACKOUT_TIMEOUT) 
 
		{
 
			// LED blackout
 
			led_blackout();
 
			black = true;
 
		}
 
		#endif
 
		
 
		// Parse any serial data in the XBee software buffer
 
		parseResult = serparser_parse();
 
		slavesensors_process(parseResult);
 
		
 
		
 
		// Parse any NMEA messages in the GPS software buffer
 
		parse_gps_transmission();
 
		
 
		
 
		wdt_reset();
 
    }
 
}
 
\ No newline at end of file
0 comments (0 inline, 0 general)