1089 lines
32 KiB
C++
Executable File
1089 lines
32 KiB
C++
Executable File
/****************************
|
|
Scooterputer
|
|
Logic / Sensor Module - Arduino
|
|
|
|
Created April 23, 2010
|
|
Rev 1.0
|
|
Kurt Schulz
|
|
|
|
This software is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
Update History:
|
|
Rev 1.1 07/11/2010
|
|
: Fixed heading value scaling 0 - 36000 (not 360000)
|
|
: Accumulate trip meter & ODO values every 1/25th of mile
|
|
|
|
*****************************/
|
|
#include <TimedAction.h>
|
|
#include <NewSoftSerial.h>
|
|
#include <TinyGPS++.h>
|
|
#include <DallasTemperature.h>
|
|
#include <OneWire.h>
|
|
#include <Wire.h>
|
|
#include <string.h>
|
|
|
|
// Uncomment the defs to send values to Serial for debug
|
|
// GPS is using the serial port and will be disabled while debugging
|
|
//#define DEBUG_ACCEL
|
|
//#define DEBUG_TEMP
|
|
//#define DEBUG_BATTMON
|
|
//#define DEBUG_RTC
|
|
//#define DEBUG_CELL
|
|
//#define DEBUG_BUZZER
|
|
//#define DEBUG_RESETS
|
|
|
|
// Digital pin assignments
|
|
#define GPS_RX_PIN 0 // GPS serial receive data
|
|
#define GPS_TX_PIN 1 // GPS serial transmit data
|
|
#define CELL_RX_PIN 2 // CELL serial receive data
|
|
#define CELL_TX_PIN 3 // CELL serial transmit data
|
|
#define TS_TX_PIN 4 // TouchShield serial transmit data
|
|
#define TS_RX_PIN 5 // TouchShield serial receive data
|
|
#define TEMP_PIN 6 // Temperature sensor DS18B20
|
|
#define BUZZ_PIN 7 // Buzzer 2.048Khz
|
|
|
|
// Analog pin assignments
|
|
#define ACCEL_X_PIN 0 // Accelerometer ADXL-320 X axis
|
|
#define ACCEL_Y_PIN 1 // Accelerometer ADXL-320 Y axis
|
|
#define BATT_PIN 2 // Battery Monitor level
|
|
#define RTC_SDA_PIN 4 // Real time clock DS1307 data
|
|
#define RTC_SCL_PIN 5 // Real time clock DS1307 clock
|
|
|
|
// RTC non-volatile storage
|
|
#define RTC_I2C_ADDR 0x68 // Real time clock address on the I2C bus
|
|
#define RAM_ADDR 8 // RTC RAM registers start at address 8
|
|
#define TRIPA_ADDR RAM_ADDR // Trips, ODO, lat and long are all floats, 4 bytes ea.
|
|
#define TRIPB_ADDR TRIPA_ADDR + 4
|
|
#define ODO_ADDR TRIPB_ADDR + 4
|
|
#define LAT_ADDR ODO_ADDR + 4
|
|
#define LON_ADDR LAT_ADDR + 4
|
|
|
|
#define MILES_PER_METER 0.00062137f
|
|
#define EARTH_RADIUS_METERS 6372795.0f
|
|
|
|
// Simple serial protocol
|
|
#define STX 0x02 // Start of text
|
|
#define ETX 0x03 // End of text
|
|
#define CR 0x0D // Carriage return
|
|
#define CTRL_Z 0x1A // Control-z
|
|
|
|
// Tasks - TimedAction( msec, funcName )
|
|
TimedAction RTC_UpdateTask = TimedAction( 1000, RTC_Update );
|
|
TimedAction TEMP_UpdateTask = TimedAction( 10000, TEMP_Update );
|
|
TimedAction TS_SendDataPacketTask = TimedAction( 250, TS_SendDataPacket );
|
|
TimedAction TS_ReceiveDataPacketTask = TimedAction( 1000, TS_ReceiveDataPacket );
|
|
TimedAction CELL_NetConnectTimeoutTask = TimedAction( 20000, CELL_NetConnectTimeout );
|
|
TimedAction CELL_ReplyTimeoutTask = TimedAction( 5000, CELL_ReplyTimeout );
|
|
|
|
// Interfaces
|
|
NewSoftSerial TS_Serial = NewSoftSerial( TS_RX_PIN, TS_TX_PIN );
|
|
NewSoftSerial CELL_Serial = NewSoftSerial( CELL_RX_PIN, CELL_TX_PIN );
|
|
OneWire oneWire( TEMP_PIN );
|
|
DallasTemperature TEMP_Sensor( &oneWire );
|
|
TinyGPS gps;
|
|
|
|
// Cell protocol Command - Reply
|
|
enum CellCmnd
|
|
{
|
|
Cmnd_None,
|
|
Cmnd_Connect,
|
|
Cmnd_SMS_Type,
|
|
Cmnd_SMS_Indication,
|
|
Cmnd_CallForwarding,
|
|
Cmnd_Location,
|
|
Cmnd_SendGPSMessage,
|
|
Cmnd_DeleteMsgs,
|
|
Cmnd_OK
|
|
} nextCellCmnd = Cmnd_Connect;
|
|
|
|
enum CellReply
|
|
{
|
|
Reply_None,
|
|
Reply_Connect,
|
|
Reply_SMS_Type,
|
|
Reply_SMS_Indication,
|
|
Reply_CallForwarding,
|
|
Reply_Location,
|
|
Reply_SendGPSMessage,
|
|
Reply_MessageText,
|
|
Reply_DeleteMsgs,
|
|
Reply_OK
|
|
} nextCellReply = Reply_Connect;
|
|
|
|
// Cell Module
|
|
#define BUFFSIZE 100
|
|
|
|
// Private cell phone number for fowarding calls and sending location info at startup
|
|
// Put your cell phone number here...
|
|
char *privCellPhone = "+15555551212";
|
|
|
|
char cellMsgBuff[ BUFFSIZE ] = "";
|
|
char cellCmndBuff[ BUFFSIZE ] = "";
|
|
char cellPhoneNumber[ 20 ] = "";
|
|
int cellMsgBuffIndex = 0;
|
|
char incomingChar = 0;
|
|
int connectRespCnt = 0;
|
|
bool cellConnected = false;
|
|
bool cellNetConnectTimeout = false;
|
|
bool cellReplyTimeout = false;
|
|
|
|
// GPS
|
|
float latitude = 0.0;
|
|
float longitude = 0.0;
|
|
float f_TripA = 0.0;
|
|
float f_TripB = 0.0;
|
|
float f_ODO = 0.0;
|
|
|
|
// Sensor values
|
|
int valMPH = 0; // Miles per hour, GPS: 0 to 100
|
|
long valODO = 0L; // Odometer in miles, internal: 0 to 999,999
|
|
int valTripA = 0; // Trip A miles, internal: 0 to 9,999
|
|
int valTripB = 0; // Trip B miles, internal: 0 to 9,999
|
|
int valGPSPulse = 0; // Indicator of GPS acquisition, 0 or 1
|
|
int valSpareInt = 0; // Spare integer value
|
|
int valTimeHH = 0; // Current time hours, RTC: 1 to 12
|
|
int valTimeMM = 0; // Current time minutes, RTC: 0 to 59
|
|
int valDateMonth = 0; // Current date month, RTC: 1 to 12
|
|
int valDateDay = 0; // Current date day, RTC: 1 to 31
|
|
int valDateYear = 0; // Current date year, RTC: 0 to 99
|
|
int valTemp = 0; // Temperature, TEMP: -255 to 255 (degrees F)
|
|
int valBatt = 0; // Battery level, BATT: 0 to 100 (percent)
|
|
int valLean = 0; // Lean amount, ACCEL: -90 to 90 (angle)
|
|
long valHeading = 0L; // Heading, GPS: 0 to 36000 (degrees)
|
|
int valLatDegrs = 0; // Latitude degrees, GPS:
|
|
long valLatFrac = 0L; // Latitude fraction, GPS:
|
|
int valLatNS = 0; // Latitude NS, GPS: N = 0, S = 1
|
|
int valLonDegrs = 0; // Longitude degrees, GPS:
|
|
long valLonFrac = 0L; // Longitude fraction, GPS:
|
|
int valLonEW = 2; // Longitude EW, GPS: E = 2, W = 3
|
|
|
|
void setup( )
|
|
{
|
|
pinMode( BUZZ_PIN, OUTPUT );
|
|
|
|
Serial.begin( 9600 ); // Start GPS interface
|
|
CELL_Serial.begin( 4800 ); // Start CELL Module interface
|
|
TS_Serial.begin( 19200 ); // Start TouchShield interface
|
|
TEMP_Sensor.begin( ); // Start Temperature interface
|
|
Wire.begin(); // Start RTC interface
|
|
|
|
TEMP_Update( ); // Get initial temperature reading
|
|
// (modified DallasTemperature.cpp - changed all delay(n) calls to delay(0)
|
|
|
|
RTC_UpdateTask.enable( ); // Enable task for reading the time and date from the RTC
|
|
TEMP_UpdateTask.enable( ); // Enable task for reading the temperature
|
|
TS_SendDataPacketTask.enable( ); // Enable task for sending packets to the TouchShield
|
|
TS_ReceiveDataPacketTask.enable( ); // Enable task for receiving packets from the TouchShield
|
|
CELL_NetConnectTimeoutTask.enable( ); // Enable task for Cell Network Connect timeout
|
|
CELL_NetConnectTimeoutTask.reset( ); // Wait a full time period
|
|
CELL_ReplyTimeoutTask.disable( ); // Disable the Cell reply timeout timer
|
|
|
|
// Read trip, ODO, and location data from RTC storage to init vals
|
|
f_TripA = RTC_ReadFloat( TRIPA_ADDR );
|
|
f_TripB = RTC_ReadFloat( TRIPB_ADDR );
|
|
f_ODO = RTC_ReadFloat( ODO_ADDR );
|
|
|
|
latitude = RTC_ReadFloat( LAT_ADDR );
|
|
longitude = RTC_ReadFloat( LON_ADDR );
|
|
|
|
valTripA = f_TripA;
|
|
valTripB = f_TripB;
|
|
valODO = f_ODO;
|
|
|
|
// KEEP THIS - Needed to set the odometer
|
|
// f_ODO = 5577.0f;
|
|
// valODO = f_ODO;
|
|
// RTC_WriteFloat( ODO_ADDR, f_ODO );
|
|
|
|
// KEEP THIS - Needed to set the time and date in the DS1307 RTC
|
|
// second, minute, hour, dayOfWeek, dayOfMonth, month, year
|
|
// dayOfWeek: 1 thru 7, Sunday thru Saturday
|
|
// RTC_SetTimeDate( 0, 25, 10, 1, 11, 7, 10 );
|
|
|
|
// KEEP THIS - Needed to re-init LS23060 GPS if reset back to default (57600 baud)
|
|
// Serial.println( "$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" ); // RMC Recommended Minimum Specific @ once per second
|
|
// Serial.println( "$PMTK251,19200*22" ); // Data rate is 19200 baud
|
|
// Serial.println( "$PMTK251,9600*17" ); // Data rate is 9600 baud
|
|
// Serial.println( "$PMTK251,4800*14" ); // Data rate is 4800 baud
|
|
// Serial.println( "$PMTK220,500*2B" ); // Send sentences every 500msec $PMTK220,500*2B
|
|
|
|
// digitalWrite( GPS_TX_PIN, HIGH ); // Keep the TX pin HIGH when not in use
|
|
|
|
BUZZ_Play( ); // Signal setup complete
|
|
|
|
// Serial comm to PC if any debug defs exist
|
|
// Be sure to disable all serial comm when uploading to the TouchShield
|
|
#if defined DEBUG_ACCEL || defined DEBUG_TEMP || defined DEBUG_BATTMON || defined DEBUG_RTC || defined DEBUG_GPS || defined DEBUG_CELL || defined DEBUG_RESETS
|
|
Serial.println( "Debug enabled..." );
|
|
#define DEBUG
|
|
#endif
|
|
}
|
|
|
|
//************************
|
|
// Ready to rock...
|
|
//
|
|
void loop( )
|
|
{
|
|
// Get updates from the sensors
|
|
#ifndef DEBUG // Can't use GPS if serial port is being used for debugging
|
|
GPS_Update( );
|
|
#endif
|
|
|
|
ACCEL_Update( );
|
|
BATTMON_Update( );
|
|
RTC_UpdateTask.check( );
|
|
TEMP_UpdateTask.check( );
|
|
|
|
CELL_Update( );
|
|
CELL_Actions( );
|
|
CELL_NetConnectTimeoutTask.check( );
|
|
CELL_ReplyTimeoutTask.check( );
|
|
|
|
if( cellNetConnectTimeout || ( nextCellCmnd == Cmnd_None ) && ( nextCellReply == Reply_None ) )
|
|
{
|
|
// Send updates to the display and check for button presses
|
|
TS_SendDataPacketTask.check( );
|
|
TS_ReceiveDataPacketTask.check( );
|
|
}
|
|
}
|
|
|
|
//************************
|
|
// Accelerometer AXDL-320
|
|
//
|
|
void ACCEL_Update( )
|
|
{
|
|
int accelX = analogRead( ACCEL_X_PIN );
|
|
int accelY = analogRead( ACCEL_Y_PIN );
|
|
|
|
// Full range is 453 to 580 (127)
|
|
valLean = map( accelY, 473, 560, 90, -90 );
|
|
|
|
#ifdef DEBUG_ACCEL
|
|
Serial.print( "ACCEL X, Y = ");
|
|
Serial.print( accelX, DEC );
|
|
Serial.print( ", " );
|
|
Serial.print( accelY, DEC );
|
|
Serial.print( " " );
|
|
Serial.print( "valLean = " );
|
|
Serial.println( valLean, DEC );
|
|
#endif
|
|
}
|
|
|
|
//************************
|
|
// Temperature DS18B20
|
|
// Timed action every 32 secs
|
|
//
|
|
void TEMP_Update( )
|
|
{
|
|
TEMP_Sensor.requestTemperatures( );
|
|
valTemp = ( int )TEMP_Sensor.getTempFByIndex( 0 );
|
|
|
|
#ifdef DEBUG_TEMP
|
|
Serial.print( "valTemp = " );
|
|
Serial.println( valTemp, DEC );
|
|
#endif
|
|
}
|
|
|
|
//************************
|
|
// Battery Monitor
|
|
//
|
|
void BATTMON_Update( )
|
|
{
|
|
int battLevel = analogRead( BATT_PIN );
|
|
|
|
valBatt = map( battLevel, 604, 870, 0, 100 );
|
|
|
|
#ifdef DEBUG_BATTMON
|
|
Serial.print( "BATT = ");
|
|
Serial.print( battLevel, DEC );
|
|
Serial.print( " " );
|
|
Serial.print( "valBatt = " );
|
|
Serial.println( valBatt, DEC );
|
|
#endif
|
|
}
|
|
|
|
//************************
|
|
// Realtime Clock DS1307
|
|
// Timed action every second
|
|
//
|
|
void RTC_Update( )
|
|
{
|
|
// Reset the register pointer
|
|
Wire.beginTransmission( RTC_I2C_ADDR );
|
|
Wire.send( 0 );
|
|
Wire.endTransmission( );
|
|
|
|
Wire.requestFrom( RTC_I2C_ADDR, 7 );
|
|
|
|
// Need to mask off the control bits on a couple of these
|
|
byte second = bcdToDec( Wire.receive( ) & 0x7f );
|
|
byte minute = bcdToDec( Wire.receive( ) );
|
|
byte hour = bcdToDec( Wire.receive( ) & 0x1f ); // 0x3f for 24 hr, 0x1f for 12 hr, am/pm = bit 5 (high = PM)
|
|
byte dayOfWeek = bcdToDec( Wire.receive( ) );
|
|
byte dayOfMonth = bcdToDec( Wire.receive( ) );
|
|
byte month = bcdToDec( Wire.receive( ) );
|
|
byte year = bcdToDec( Wire.receive( ) );
|
|
|
|
valTimeHH = ( int )hour;
|
|
valTimeMM = ( int )minute;
|
|
valDateDay = ( int )dayOfMonth;
|
|
valDateMonth = ( int )month;
|
|
valDateYear = ( int )year;
|
|
|
|
#ifdef DEBUG_RTC
|
|
Serial.print( hour, DEC );
|
|
Serial.print( ":" );
|
|
Serial.print( minute, DEC );
|
|
Serial.print( ":" );
|
|
Serial.print( second, DEC );
|
|
Serial.print( " " );
|
|
Serial.print( month, DEC );
|
|
Serial.print( "/" );
|
|
Serial.print( dayOfMonth, DEC );
|
|
Serial.print( "/" );
|
|
Serial.print( year, DEC );
|
|
Serial.print( " Day_of_week:" );
|
|
Serial.println( dayOfWeek, DEC );
|
|
|
|
Serial.print( valTimeHH, DEC );
|
|
Serial.print( ":" );
|
|
Serial.print( valTimeMM, DEC );
|
|
Serial.print( " " );
|
|
Serial.print( valDateMonth, DEC );
|
|
Serial.print( "/" );
|
|
Serial.print( valDateDay, DEC );
|
|
Serial.print( "/" );
|
|
Serial.println( valDateYear, DEC );
|
|
#endif
|
|
}
|
|
|
|
//************************
|
|
// GPS LS23060
|
|
//
|
|
|
|
long lat, lon;
|
|
float f_lat, f_lon;
|
|
unsigned long age = 0;
|
|
unsigned long heading = 0;
|
|
float mph = 0.0;
|
|
float distance = 0.0;
|
|
int incomingByte;
|
|
|
|
/*
|
|
unsigned long date, time, chars;
|
|
int year;
|
|
byte month, day, hour, minute, second, hundredths;
|
|
unsigned short sentences, failed;
|
|
|
|
float fHeading = gps.f_course( );
|
|
alt = gps.altitude( );
|
|
gps.get_datetime( &date, &time, &age );
|
|
gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age);
|
|
gps.stats(&chars, &sentences, &failed);
|
|
*/
|
|
|
|
void GPS_Update( )
|
|
{
|
|
while( Serial.available( ) )
|
|
{
|
|
incomingByte = Serial.read( );
|
|
|
|
if( gps.encode( incomingByte ) ) // Returns true if valid NMEA sentence received
|
|
{
|
|
gps.get_position( &lat, &lon, &age );
|
|
gps.f_get_position( &f_lat, &f_lon, &age );
|
|
heading = gps.course( );
|
|
mph = gps.f_speed_mph( );
|
|
|
|
// If we're moving, calculate and save distance traveled
|
|
if( mph > 2.0f )
|
|
{
|
|
if( latitude != 0.0f )
|
|
{
|
|
float distance = DistanceBetween2Points( latitude, longitude, f_lat, f_lon, MILES_PER_METER );
|
|
|
|
if( distance >= 0.04f ) // Accumulate every 25th of a mile
|
|
{
|
|
f_TripA += distance;
|
|
f_TripB += distance;
|
|
f_ODO += distance;
|
|
|
|
if( f_TripA > 9999.9f ) // Reset trip meters after 9999 miles
|
|
f_TripA = 0.0f;
|
|
|
|
if( f_TripB > 9999.9f )
|
|
f_TripB = 0.0f;
|
|
|
|
valTripA = f_TripA; // Update display values
|
|
valTripB = f_TripB;
|
|
valODO = f_ODO;
|
|
|
|
latitude = f_lat;
|
|
longitude = f_lon;
|
|
|
|
// Save trips and ODO floats to RTC storage
|
|
RTC_WriteFloat( TRIPA_ADDR, f_TripA );
|
|
RTC_WriteFloat( TRIPB_ADDR, f_TripB );
|
|
RTC_WriteFloat( ODO_ADDR, f_ODO );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
latitude = f_lat;
|
|
longitude = f_lon;
|
|
}
|
|
// Save latitude and longitude to RTC storage
|
|
RTC_WriteFloat( LAT_ADDR, latitude );
|
|
RTC_WriteFloat( LON_ADDR, longitude );
|
|
}
|
|
|
|
// N=0 S=1 E=2 W=3
|
|
valLatNS = ( lat < 0 ) ? 1 : 0; // +Lat is North, -Lat is South
|
|
lat = ( lat < 0 ) ? lat * -1 : lat; // Now make latitude absolute
|
|
|
|
valLonEW = ( lon < 0 ) ? 3 : 2; // +Lon is East, - Lon is West
|
|
lon = ( lon < 0 ) ? lon * -1 : lon; // Now make longitude absolute
|
|
|
|
valLatDegrs = lat / 100000L;
|
|
valLatFrac = lat % 100000L;
|
|
valLonDegrs = lon / 100000L;
|
|
valLonFrac = lon % 100000L;
|
|
|
|
valMPH = mph; // Update MPH display
|
|
|
|
// We won't need every value change in heading so check range for +/- 32 degr change
|
|
if( ( heading > valHeading + 1125L ) || ( heading < valHeading - 1125L ) )
|
|
valHeading = heading;
|
|
|
|
// Toggle the GPS pulse to indicate ongoing acquisition
|
|
valGPSPulse = ( valGPSPulse == 0 ) ? 1 : 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//************************
|
|
// Great Circle distance calculation
|
|
// Returns the distance between two lat/lon points on this great Earth
|
|
// (Note: Assumes sea level, does not take into account altitude. Close enough.)
|
|
//
|
|
float DistanceBetween2Points( float Lat1, float Lon1, float Lat2, float Lon2, float unit_conversion )
|
|
{
|
|
float dLat = radians( Lat2 - Lat1 );
|
|
float dLon = radians( Lon2 - Lon1 );
|
|
|
|
float a = sin( dLat / 2.0f ) * sin( dLat / 2.0f ) +
|
|
cos( radians( Lat1 ) ) * cos( radians( Lat2 ) ) *
|
|
sin( dLon / 2.0f ) * sin( dLon / 2.0f );
|
|
|
|
float d = 2.0f * atan2( sqrt( a ), sqrt( 1.0f - a ) );
|
|
|
|
return d * EARTH_RADIUS_METERS * unit_conversion;
|
|
}
|
|
|
|
|
|
//************************
|
|
// Cell Module GSM/GPRS
|
|
// CELL_Update( )
|
|
// Assemble messages from the Cell Module
|
|
//
|
|
void CELL_Update( )
|
|
{
|
|
while( CELL_Serial.available( ) )
|
|
{
|
|
incomingChar = CELL_Serial.read( ); // Get the character from the cell
|
|
|
|
#ifdef DEBUG_CELL
|
|
Serial.print( incomingChar );
|
|
#endif
|
|
|
|
// First check to see if we're waiting to be prompted by the cell module
|
|
// (greater than sign) to enter a text message to be sent, or have timed out waiting...
|
|
if( nextCellCmnd == Cmnd_SendGPSMessage )
|
|
{
|
|
if( incomingChar == '>' || cellReplyTimeout )
|
|
{
|
|
SendGPSMessage( ); // Send text message with our location
|
|
cellMsgBuffIndex = 0;
|
|
cellMsgBuff[cellMsgBuffIndex] = '\0';
|
|
|
|
CELL_ResetReplyTimeout( );
|
|
nextCellCmnd = Cmnd_None;
|
|
nextCellReply = Reply_SendGPSMessage;
|
|
}
|
|
}
|
|
// Look for a CR which terminates incoming messages and replies from the cell module...
|
|
else if( incomingChar == '\r' )
|
|
{
|
|
if( cellMsgBuffIndex > 0 ) // Just ignore completely if < 1
|
|
{
|
|
// We have a message...
|
|
ProcessCellReply( );
|
|
}
|
|
|
|
// Reset index to beginning for next message
|
|
cellMsgBuffIndex = 0;
|
|
cellMsgBuff[cellMsgBuffIndex] = '\0';
|
|
}
|
|
// Store incoming characters until a CR is received, which indicates
|
|
// a message is ready to be processed
|
|
else if( incomingChar > 0x1F && incomingChar < 0x7F )
|
|
{
|
|
cellMsgBuff[cellMsgBuffIndex] = incomingChar;
|
|
if( ++cellMsgBuffIndex >= BUFFSIZE )
|
|
cellMsgBuffIndex = 0;
|
|
cellMsgBuff[cellMsgBuffIndex] = '\0';
|
|
}
|
|
}
|
|
|
|
// Reset message state if timed out waiting for a reply
|
|
// Send AT command in attempt to resync
|
|
if( cellReplyTimeout )
|
|
{
|
|
cellMsgBuffIndex = 0;
|
|
cellMsgBuff[cellMsgBuffIndex] = '\0';
|
|
CELL_ResetReplyTimeout( );
|
|
nextCellCmnd = Cmnd_OK;
|
|
nextCellReply = Reply_None;
|
|
}
|
|
}
|
|
|
|
//************************
|
|
// Cell Module GSM/GPRS
|
|
// ProcessCellReply( )
|
|
// Process replies from the Cell Module and determine the next Cmnd
|
|
// Refer to SM5100B AT Command Set guide
|
|
//
|
|
void ProcessCellReply( )
|
|
{
|
|
// Reset message state if cell decides to start over...
|
|
if( strncmp( cellMsgBuff, "+SIND: 1", 10 ) == 0)
|
|
{
|
|
nextCellReply = Reply_Connect;
|
|
nextCellCmnd = Cmnd_Connect;
|
|
connectRespCnt = 0;
|
|
cellConnected = false;
|
|
CELL_NetConnectTimeoutTask.enable( );
|
|
CELL_NetConnectTimeoutTask.reset( );
|
|
return;
|
|
}
|
|
// Check to see if we've received an unsolicited message...
|
|
else if( cellConnected && ( strncmp( cellMsgBuff, "+CMT:", 5 ) == 0 ) )
|
|
{
|
|
// We're receiving a message...
|
|
// Get source phone number, then set up to get the text on the next CR...
|
|
strncpy( cellPhoneNumber, &cellMsgBuff[7], 12 );
|
|
cellPhoneNumber[13] = '\0';
|
|
nextCellReply = Reply_MessageText;
|
|
nextCellCmnd = Cmnd_None;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
The following automates connecting, configuring call forwarding, and setting SMS type and return mode, deleting messages
|
|
Stream from cell at startup should look something like the following:
|
|
+SIND: 1
|
|
+SIND: 10,"SM",1,"FD",1,"LD",1,"MC",1,"RC",1,"ME",1
|
|
+SIND: 3
|
|
+SIND: 11
|
|
+SIND: 4
|
|
*/
|
|
|
|
switch( nextCellReply )
|
|
{
|
|
case Reply_Connect:
|
|
if( strncmp( cellMsgBuff, "+SIND: 4", 10 ) == 0 )
|
|
connectRespCnt += 1;
|
|
else if( strncmp( cellMsgBuff, "+SIND: 11", 10 ) == 0 )
|
|
connectRespCnt += 2;
|
|
|
|
// If we've gotten a 4 and an 11, assume we're good to go
|
|
if( connectRespCnt > 2 && !cellConnected )
|
|
{
|
|
cellConnected = true;
|
|
CELL_NetConnectTimeoutTask.disable( );
|
|
nextCellCmnd = Cmnd_CallForwarding;
|
|
nextCellReply = Reply_None;
|
|
}
|
|
// Reset the timeout interval with each connect response while waiting to connect
|
|
CELL_NetConnectTimeoutTask.reset( );
|
|
break;
|
|
|
|
case Reply_CallForwarding:
|
|
if( ( strncmp( cellMsgBuff, "OK", 2 ) == 0 ) || ( strncmp( cellMsgBuff, "+CME ERROR", 10 ) == 0 ) )
|
|
{
|
|
CELL_ResetReplyTimeout( );
|
|
nextCellCmnd = Cmnd_SMS_Type;
|
|
nextCellReply = Reply_None;
|
|
}
|
|
break;
|
|
|
|
case Reply_SMS_Type:
|
|
if( ( strncmp( cellMsgBuff, "OK", 2 ) == 0 ) || ( strncmp( cellMsgBuff, "+CME ERROR", 10 ) == 0 ) )
|
|
{
|
|
CELL_ResetReplyTimeout( );
|
|
nextCellCmnd = Cmnd_SMS_Indication;
|
|
nextCellReply = Reply_None;
|
|
}
|
|
break;
|
|
|
|
case Reply_SMS_Indication:
|
|
if( ( strncmp( cellMsgBuff, "OK", 2 ) == 0 ) || ( strncmp( cellMsgBuff, "+CME ERROR", 10 ) == 0 ) )
|
|
{
|
|
CELL_ResetReplyTimeout( );
|
|
nextCellCmnd = Cmnd_DeleteMsgs;
|
|
nextCellReply = Reply_None;
|
|
}
|
|
break;
|
|
|
|
case Reply_Location:
|
|
if( ( strncmp( cellMsgBuff, "OK", 2 ) == 0 ) || ( strncmp( cellMsgBuff, "+CME ERROR", 10 ) == 0 ) )
|
|
{
|
|
CELL_ResetReplyTimeout( );
|
|
nextCellCmnd = Cmnd_None;
|
|
nextCellReply = Reply_None;
|
|
}
|
|
break;
|
|
|
|
case Reply_SendGPSMessage:
|
|
if( ( strncmp( cellMsgBuff, "OK", 2 ) == 0 ) || ( strncmp( cellMsgBuff, "+CME ERROR", 10 ) == 0 ) )
|
|
{
|
|
CELL_ResetReplyTimeout( );
|
|
nextCellCmnd = Cmnd_None;
|
|
nextCellReply = Reply_None;
|
|
}
|
|
break;
|
|
|
|
case Reply_MessageText:
|
|
// We received a text message and captured the sending phone number in cellPhoneNumber.
|
|
// Parse message and reply with GPS location message if requested. We'll be prompted by the
|
|
// Cell Module for when to send the message data with a '>'.
|
|
nextCellCmnd = ( strncmp( cellMsgBuff, "Loc?", 4 ) == 0 ) ? Cmnd_Location : Cmnd_OK;
|
|
nextCellReply = Reply_None;
|
|
break;
|
|
|
|
case Reply_DeleteMsgs:
|
|
if( ( strncmp( cellMsgBuff, "OK", 2 ) == 0 ) || ( strncmp( cellMsgBuff, "+CME ERROR", 10 ) == 0 ) )
|
|
{
|
|
CELL_ResetReplyTimeout( );
|
|
strcpy( cellPhoneNumber, privCellPhone );
|
|
nextCellCmnd = Cmnd_Location; // Cmnd_Location if send location on startup, else Cmnd_None
|
|
nextCellReply = Reply_None;
|
|
}
|
|
break;
|
|
|
|
case Reply_OK:
|
|
CELL_ResetReplyTimeout( );
|
|
nextCellCmnd = Cmnd_None;
|
|
nextCellReply = Reply_None;
|
|
break;
|
|
|
|
default:
|
|
nextCellCmnd = Cmnd_None;
|
|
nextCellReply = Reply_None;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//************************
|
|
// Cell Module GSM/GPRS
|
|
// CELL_Actions( )
|
|
// Execute Cell Module Commands
|
|
// Refer to SM5100B AT Command Set guide
|
|
//
|
|
void CELL_Actions( )
|
|
{
|
|
switch( nextCellCmnd )
|
|
{
|
|
case Cmnd_Connect:
|
|
break;
|
|
|
|
case Cmnd_CallForwarding:
|
|
strcpy( cellCmndBuff, "AT+CCFC=0,3,\"" );
|
|
strcat( cellCmndBuff, privCellPhone );
|
|
strcat( cellCmndBuff, "\",129" );
|
|
SendATCommand( cellCmndBuff );
|
|
nextCellReply = Reply_CallForwarding;
|
|
nextCellCmnd = Cmnd_None;
|
|
break;
|
|
|
|
case Cmnd_SMS_Type:
|
|
SendATCommand( "AT+CMGF=1" );
|
|
nextCellReply = Reply_SMS_Type;
|
|
nextCellCmnd = Cmnd_None;
|
|
break;
|
|
|
|
case Cmnd_SMS_Indication:
|
|
SendATCommand( "AT+CNMI=3,3" );
|
|
nextCellReply = Reply_SMS_Indication;
|
|
nextCellCmnd = Cmnd_None;
|
|
break;
|
|
|
|
case Cmnd_Location:
|
|
strcpy( cellCmndBuff, "AT+CMGS=\"" );
|
|
strcat( cellCmndBuff, cellPhoneNumber );
|
|
strcat( cellCmndBuff, "\"" );
|
|
SendATCommand( cellCmndBuff );
|
|
nextCellReply = Reply_None;
|
|
nextCellCmnd = Cmnd_SendGPSMessage;
|
|
break;
|
|
|
|
case Cmnd_DeleteMsgs:
|
|
SendATCommand( "AT+CMGD=1,4" );
|
|
nextCellReply = Reply_DeleteMsgs;
|
|
nextCellCmnd = Cmnd_None;
|
|
break;
|
|
|
|
case Cmnd_OK:
|
|
SendATCommand( "AT" );
|
|
nextCellReply = Reply_OK;
|
|
nextCellCmnd = Cmnd_None;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUG_CELL
|
|
while( Serial.available( ) )
|
|
{
|
|
char incoming_char = Serial.read( ); // Get the character from the Serial port.
|
|
|
|
if( incoming_char == '~' ) // If it's a tilde...
|
|
incoming_char = CR; // ...convert to a carriage return
|
|
else if( incoming_char == '^' ) // If it's an up caret...
|
|
incoming_char = CTRL_Z; // ...convert to ctrl-Z
|
|
|
|
CELL_Serial.print( incoming_char ); // Send the character to the cellular module.
|
|
Serial.print( incoming_char ); // Echo it back to the terminal
|
|
|
|
if( incoming_char == CR || incoming_char == CTRL_Z )
|
|
{
|
|
Serial.println( );
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void SendATCommand( char *cmnd )
|
|
{
|
|
delay( 150 );
|
|
CELL_Serial.println( cmnd );
|
|
|
|
CELL_StartReplyTimeout( );
|
|
|
|
#ifdef DEBUG_CELL
|
|
Serial.print( "\n>> " );
|
|
Serial.println( cmnd );
|
|
#endif
|
|
}
|
|
|
|
void SendGPSMessage( )
|
|
{
|
|
// Send text message with GPS location
|
|
delay( 150 );
|
|
CELL_Serial.print( "Loc: " );
|
|
CELL_Serial.print( valLatDegrs, DEC );
|
|
CELL_Serial.print( "." );
|
|
CELL_Serial.print( valLatFrac, DEC );
|
|
CELL_Serial.print( ", " );
|
|
CELL_Serial.print( valLonDegrs, DEC );
|
|
CELL_Serial.print( "." );
|
|
CELL_Serial.print( valLonFrac, DEC );
|
|
CELL_Serial.print( " Heading: " );
|
|
CELL_Serial.print( heading / 1000, DEC );
|
|
CELL_Serial.print( " MPH: " );
|
|
CELL_Serial.print( valMPH, DEC );
|
|
CELL_Serial.print( " Time: " );
|
|
CELL_Serial.print( valTimeHH, DEC );
|
|
CELL_Serial.print( ":" );
|
|
CELL_Serial.print( valTimeMM, DEC );
|
|
CELL_Serial.print( CTRL_Z, BYTE );
|
|
|
|
CELL_StartReplyTimeout( );
|
|
|
|
#ifdef DEBUG_CELL
|
|
Serial.println( "Sent location..." );
|
|
#endif
|
|
}
|
|
|
|
|
|
//***************************************************
|
|
// Cell Module Message Repy Timeout
|
|
// Timed Action
|
|
// Manages the timer for cell message replies
|
|
//
|
|
void CELL_ReplyTimeout( )
|
|
{
|
|
cellReplyTimeout = true;
|
|
CELL_ReplyTimeoutTask.disable( );
|
|
|
|
#ifdef DEBUG_CELL
|
|
Serial.println( "ReplyTimeout" );
|
|
#endif
|
|
}
|
|
|
|
void CELL_StartReplyTimeout( )
|
|
{
|
|
// Start reply timeout task...
|
|
cellReplyTimeout = false;
|
|
CELL_ReplyTimeoutTask.enable( );
|
|
CELL_ReplyTimeoutTask.reset( );
|
|
}
|
|
|
|
void CELL_ResetReplyTimeout( )
|
|
{
|
|
cellReplyTimeout = false;
|
|
CELL_ReplyTimeoutTask.disable( );
|
|
}
|
|
|
|
//***************************************************
|
|
// Timeout for Cell Module Network Connection
|
|
// Timed Action
|
|
// Re-enables serial comm with TouchShield after timeout
|
|
//
|
|
void CELL_NetConnectTimeout( )
|
|
{
|
|
cellNetConnectTimeout = true;
|
|
CELL_NetConnectTimeoutTask.disable( );
|
|
|
|
#ifdef DEBUG_CELL
|
|
Serial.println( "NetConnectTimeout" );
|
|
#endif
|
|
}
|
|
|
|
//************************
|
|
// BUZZER 2.048Khz
|
|
//
|
|
void BUZZ_Play( )
|
|
{
|
|
for ( int i = 0; i < 1048; i++ )
|
|
{
|
|
// 1 / 2048Hz = 488uS, or 244uS high and 244uS low to create 50% duty cycle
|
|
digitalWrite( BUZZ_PIN, HIGH );
|
|
delayMicroseconds( 244 );
|
|
digitalWrite( BUZZ_PIN, LOW );
|
|
delayMicroseconds( 244 );
|
|
}
|
|
}
|
|
|
|
|
|
//***************************************************
|
|
// Serial data transfer to the TouchShield display
|
|
// Timed Action
|
|
//
|
|
void TS_SendDataPacket( )
|
|
{
|
|
TS_Serial.print( STX, BYTE );
|
|
|
|
TS_WriteInt( valMPH );
|
|
|
|
TS_WriteLong( valODO );
|
|
|
|
TS_WriteInt( valTripA );
|
|
TS_WriteInt( valTripB );
|
|
|
|
TS_WriteInt( valGPSPulse );
|
|
TS_WriteInt( valSpareInt );
|
|
|
|
TS_WriteInt( valTimeHH );
|
|
TS_WriteInt( valTimeMM );
|
|
|
|
TS_WriteInt( valDateMonth );
|
|
TS_WriteInt( valDateDay );
|
|
TS_WriteInt( valDateYear );
|
|
|
|
TS_WriteInt( valTemp );
|
|
TS_WriteInt( valBatt );
|
|
TS_WriteInt( valLean );
|
|
|
|
TS_WriteLong( valHeading );
|
|
TS_WriteInt( valLatDegrs );
|
|
TS_WriteLong( valLatFrac );
|
|
TS_WriteInt( valLatNS );
|
|
|
|
TS_WriteInt( valLonDegrs );
|
|
TS_WriteLong( valLonFrac );
|
|
TS_WriteInt( valLonEW );
|
|
|
|
TS_Serial.print( ETX, BYTE );
|
|
}
|
|
|
|
void TS_WriteInt( int value )
|
|
{
|
|
// Write high byte, low byte
|
|
TS_Serial.print( ( unsigned char )( value >> 8 ) );
|
|
TS_Serial.print( ( unsigned char )value );
|
|
}
|
|
|
|
void TS_WriteLong( long value )
|
|
{
|
|
// Write high word, low word
|
|
TS_WriteInt( ( unsigned int )( value >> 16 ) );
|
|
TS_WriteInt( ( unsigned int )value );
|
|
}
|
|
|
|
// Handy for debugging communications with TouchShield
|
|
int whirlyGig = 0;
|
|
void TS_WriteWhirlyGig( )
|
|
{
|
|
char* whirlyGigs[] = { "|", "/", "-", "\\", "|", "/", "-", "\\" };
|
|
|
|
TS_Serial.print( whirlyGigs[ whirlyGig ] );
|
|
whirlyGig = ( ++whirlyGig > 7 ) ? 0 : whirlyGig;
|
|
}
|
|
|
|
|
|
//****************************************************
|
|
// Data (button presses) coming from the TouchShield
|
|
//
|
|
void TS_ReceiveDataPacket( )
|
|
{
|
|
while( TS_Serial.available( ) > 0 )
|
|
{
|
|
switch( TS_Serial.read( ) )
|
|
{
|
|
case 'A': // Reset TripA meter
|
|
f_TripA = 0.0f;
|
|
valTripA = f_TripA;
|
|
RTC_WriteFloat( TRIPA_ADDR, f_TripA );
|
|
|
|
#ifdef DEBUG_RESETS
|
|
Serial.println( "Trip A pressed" );
|
|
#endif
|
|
break;
|
|
|
|
case 'B': // Reset TripB meter
|
|
f_TripB = 0.0f;
|
|
valTripB = f_TripB;
|
|
RTC_WriteFloat( TRIPB_ADDR, f_TripB );
|
|
|
|
#ifdef DEBUG_RESETS
|
|
Serial.println( "Trip B pressed" );
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
// TS_Serial.flush( );
|
|
}
|
|
}
|
|
|
|
//**************************
|
|
// RTC DS1307
|
|
// Utility functions
|
|
|
|
// Convert decimal to BCD
|
|
byte decToBcd( byte val )
|
|
{
|
|
return ( ( val / 10 * 16) + ( val % 10 ) );
|
|
}
|
|
|
|
// Convert BCD to decimal
|
|
byte bcdToDec( byte val )
|
|
{
|
|
return ( ( val / 16 * 10) + ( val % 16 ) );
|
|
}
|
|
|
|
// Set the time and date
|
|
void RTC_SetTimeDate( byte second, // 0-59
|
|
byte minute, // 0-59
|
|
byte hour, // 1-23
|
|
byte dayOfWeek, // 1-7
|
|
byte dayOfMonth, // 1-28/29/30/31
|
|
byte month, // 1-12
|
|
byte year ) // 0-99
|
|
{
|
|
Wire.beginTransmission( RTC_I2C_ADDR );
|
|
Wire.send( 0 );
|
|
Wire.send( decToBcd( second ) ); // 0 to bit 7 starts the clock
|
|
Wire.send( decToBcd( minute ) );
|
|
Wire.send( decToBcd( hour ) | 0x40 ); // If you want 12 hour am/pm you need to set
|
|
// bit 6 (also need to change readDateDs1307)
|
|
Wire.send( decToBcd( dayOfWeek ) );
|
|
Wire.send( decToBcd( dayOfMonth ) );
|
|
Wire.send( decToBcd( month ) );
|
|
Wire.send( decToBcd( year ) );
|
|
Wire.endTransmission( );
|
|
}
|
|
|
|
//**************************
|
|
// RTC DS1307
|
|
// RAM register access functions
|
|
//
|
|
|
|
// Reads a 2-byte integer value from the RTC RAM registers
|
|
int RTC_ReadInteger( int valAddr )
|
|
{
|
|
// Set the register pointer
|
|
Wire.beginTransmission( RTC_I2C_ADDR );
|
|
Wire.send( valAddr );
|
|
Wire.endTransmission( );
|
|
|
|
// Read 2 bytes into int value
|
|
Wire.requestFrom( RTC_I2C_ADDR, 2 );
|
|
int value = Wire.receive( );
|
|
value = ( value << 8 ) + Wire.receive( );
|
|
|
|
return value;
|
|
}
|
|
|
|
// Reads a 4-byte float value from the RTC RAM registers
|
|
float RTC_ReadFloat( int valAddr )
|
|
{
|
|
float value;
|
|
byte *byteArray = (byte *) &value;
|
|
|
|
// Set the register pointer
|
|
Wire.beginTransmission( RTC_I2C_ADDR );
|
|
Wire.send( valAddr );
|
|
Wire.endTransmission( );
|
|
|
|
// Read 4 bytes can convert to float value
|
|
Wire.requestFrom( RTC_I2C_ADDR, 4 );
|
|
byteArray[3] = Wire.receive( );
|
|
byteArray[2] = Wire.receive( );
|
|
byteArray[1] = Wire.receive( );
|
|
byteArray[0] = Wire.receive( );
|
|
|
|
return value;
|
|
}
|
|
|
|
// Writes a 2-byte integer value to the RTC RAM registers
|
|
void RTC_WriteInteger( int valAddr, int value )
|
|
{
|
|
if( valAddr > 7 && valAddr < 63 ) // Don't let writes go to the RTC registers 0 - 7
|
|
{
|
|
Wire.beginTransmission( RTC_I2C_ADDR );
|
|
Wire.send( valAddr );
|
|
|
|
// Write high byte, low byte
|
|
Wire.send( ( unsigned char )( value >> 8 ) );
|
|
Wire.send( ( unsigned char )value );
|
|
|
|
Wire.endTransmission( );
|
|
}
|
|
}
|
|
|
|
// Writes a 4-byte float value to the RTC RAM registers
|
|
void RTC_WriteFloat( int valAddr, float value )
|
|
{
|
|
if( valAddr > 7 && valAddr < 61 ) // Don't let writes go to the RTC registers 0 - 7
|
|
{
|
|
Wire.beginTransmission( RTC_I2C_ADDR );
|
|
Wire.send( valAddr );
|
|
|
|
// Write high word (high byte/low byte), low word (high byte/low byte)
|
|
byte *byteArray;
|
|
byteArray = (byte *) &value;
|
|
Wire.send( byteArray[3] );
|
|
Wire.send( byteArray[2] );
|
|
Wire.send( byteArray[1] );
|
|
Wire.send( byteArray[0] );
|
|
|
|
Wire.endTransmission( );
|
|
}
|
|
}
|
|
|