Files
Arduino/TFT_SCOOTER/TFT_SCOOTER.ino
Jérôme Delacotte 7b30d6e298 first commit
2025-03-06 11:15:32 +01:00

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( );
}
}