Geoff,
No Problem - here is the code. Please note that I am not a software engineer, although I have a professional background in hard- and software engineering. I tried to keep everything as simple as possible rather than creating a masterpiece of software design. A major part of the code is just for debug output.
Everything you might do with the code is at your own risk!
MCRider
// #define DebugOut
/*
Flap overspeed detector for MCR01
The circuit:
* Arduino Pro Mini 328
* Status LED attached from pin 13 to ground
* Serial out from Dynon SkyView connected to pin RXI, 9600 baud
* Sparkfun MAX3232 Breakout to convert serial signal level
* Relay output pin 11, relay normally closed, interrupts flap down control signal
Functions:
* Reads airspeed from EFIS data stream (ADAHRS data)
* If airspeed is valid and above limit relay is activated
* LED: no valid airspeed data -> LED off, valid airspeed data -> LED on, airspeed to high -> LED flashes
Changes:
V1 (2013-12-17): Inital version
Test data lines:
!1121144703-014+00003310811+01736+003-03+1013-033+110831245+01650023176C
!1121144703-014+00003310711+01736+003-03+1013-033+110831245+01650023176B
!1121144703-014+0000331XXXX+01736+003-03+1013-033+110831245+016500231702
*/
// Constants
const int cPinLedOnBoard = 13; // on-board status LED pin
const int cPinRelayOut = 11; // Relay output pin
const long cBaudrate = 9600;
const int cMaxAirspeed = 760; // Max airspeed to move flaps down in knots * 10
const int cRecBufferLen = 0x100; // Length of receive buffer
const int cMaxBytesAtOnce = 16; // No. of bytes read from EFIS in one Loop()-cycle
const char cReqLineType = '1'; // Line type of SkyView data
const char cReqDataVersion = '1'; // Version of SkyView data
const int cPosAirspeedData = 23; // Position of airspeed data in line
const int cLenAirspeedData = 4; // Length of airspeed data
const int cIntervalLEDFlash = 200; // Length of cycle (msec) for flashing LED
const int cDelayRelay = 500; // Minimum delay (msec) before relay state changes
const int cTimeoutAirspeed = 5000; // Time (msec) without valid data from SkyView before last airspeed gets invalid
// Global variable
unsigned long TimestampAirspeed; // System time of last successfully received airspeed data
int Airspeed = 0; // Current airspeed
bool ADAHRSDataValid = false; // Valid data from ADAHRS available
int ReadCount; // Counter for reading data
bool Reading = false; // Currently reading data?
char RecBuffer [cRecBufferLen+1]; // Receive buffer
bool LEDFlashOn = false; // LED currently on? (for flashing LED)
unsigned long TimestampLEDFlash; // System time of last LED status change (for flashing LED)
bool RelayOn = false; // Relay currently active?
unsigned long TimestampRelay; // System time of last relay status change
void setup()
{
pinMode (cPinLedOnBoard, OUTPUT);
digitalWrite (cPinLedOnBoard, LOW);
pinMode (cPinRelayOut, OUTPUT);
digitalWrite (cPinRelayOut, LOW);
Serial.begin (cBaudrate);
TimestampLEDFlash = millis ();
TimestampRelay = TimestampLEDFlash;
#ifdef DebugOut
Serial.write ("Setup\n");
#endif
}
long int TimeDiffMillis (unsigned long Then, unsigned long Now)
{
if (Now >= Then)
return Now - Then;
else
{
Then = 0xFFFFFFFF - Then;
return Now + Then + 1;
}
}
void UpdateLED ()
{
if (!ADAHRSDataValid)
digitalWrite (cPinLedOnBoard, LOW);
else
{
if (Airspeed <= cMaxAirspeed)
digitalWrite (cPinLedOnBoard, HIGH);
else
{
if (TimeDiffMillis (TimestampLEDFlash, millis ()) > cIntervalLEDFlash)
{
TimestampLEDFlash = millis ();
LEDFlashOn = !LEDFlashOn;
if (LEDFlashOn)
digitalWrite (cPinLedOnBoard, HIGH);
else
digitalWrite (cPinLedOnBoard, LOW);
}
}
}
}
void ReadEFIS ()
{
int i = 0;
char c;
while (Serial.available () && (i++ < cMaxBytesAtOnce))
{
c = Serial.read ();
if (c == '!') // Start of data detected
{
Reading = true;
ReadCount = 0;
#ifdef DebugOut
Serial.write ("! found\n");
#endif
}
if (!Reading || (ReadCount+1 >= cRecBufferLen)) // Not reading or buffer overflow?
return;
if (c == 0x0D)
{
Reading = false;
#ifdef DebugOut
Serial.write ("End of line\n");
#endif
char TmpStr [cLenAirspeedData+1];
byte Sum, CheckSum;
char *Ptr;
// End of data detected
if (ReadCount < 5)
break; // Too short
RecBuffer [ReadCount++] = 0x00;
#ifdef DebugOut
Serial.write ("Line: ");
Serial.write (RecBuffer);
Serial.write ("\n");
Serial.write ("Data: ");
#endif
Sum = 0;
for (int j=0; j < ReadCount-3; j++)
Sum += RecBuffer [j];
#ifdef DebugOut
Serial.write ("Chksum calc: ");
itoa (Sum, TmpStr, 16);
Serial.write (TmpStr);
Serial.write ("\n");
#endif
strncpy (TmpStr, &RecBuffer [ReadCount-3], 2);
TmpStr [2] = 0x00;
CheckSum = strtol (TmpStr, &Ptr, 16);
#ifdef DebugOut
Serial.write ("Chksum from data: ");
itoa (CheckSum, TmpStr, 16);
Serial.write (TmpStr);
Serial.write ("\n");
#endif
if (Sum != CheckSum)
break; // Checksum not correct
if ((RecBuffer [1] != cReqLineType) || ((RecBuffer [2] != cReqDataVersion)) || (ReadCount < cPosAirspeedData + cLenAirspeedData))
break; // Wrong line type, wrong data version or data too short
ADAHRSDataValid = true;
strncpy (TmpStr, &RecBuffer [cPosAirspeedData], cLenAirspeedData);
TmpStr [cLenAirspeedData] = 0x0;
#ifdef DebugOut
Serial.write ("Airspeed: ");
Serial.write (TmpStr);
Serial.write ("\n");
#endif
Airspeed = strtol (TmpStr, &Ptr, 10);
#ifdef DebugOut
itoa (Airspeed, TmpStr, 10);
Serial.write ("Airspeed read: ");
Serial.write (TmpStr);
Serial.write ("\n");
#endif
TimestampAirspeed = millis ();
}
else
RecBuffer [ReadCount++] = c;
}
}
void SetRelay ()
{
if (TimeDiffMillis (TimestampRelay, millis ()) > cDelayRelay)
{
bool NewRelayOn = ADAHRSDataValid && (Airspeed > cMaxAirspeed);
if (NewRelayOn != RelayOn)
{
RelayOn = NewRelayOn;
if (RelayOn)
digitalWrite (cPinRelayOut, HIGH);
else
digitalWrite (cPinRelayOut, LOW);
TimestampRelay = millis ();
#ifdef DebugOut
if (RelayOn)
Serial.write ("Relay on\n");
else
Serial.write ("Relay off\n");
#endif
}
}
}
void loop()
{
ReadEFIS ();
if (ADAHRSDataValid && (TimeDiffMillis (TimestampAirspeed, millis ()) > cTimeoutAirspeed)) // last valid airspeed too old?
{
#ifdef DebugOut
Serial.write ("Timeout\n");
#endif
ADAHRSDataValid = false;
}
SetRelay ();
UpdateLED ();
}