Last active
July 4, 2022 11:37
-
-
Save speters/d9d0e5e79d077fe970d836abd24000c7 to your computer and use it in GitHub Desktop.
Decode the NASA Clipper Data into a NMEA compatible serial string; https://wiki.openseamap.org/wiki/De:NASA_Clipper_Range
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
NASAClipper_I2C_to_NMEA v0.5.2 | |
Decode the NASA Clipper Data into a NMEA compatible serial string | |
written by Peter Holtermann | |
with additions from Victor Klein | |
This software is distributed under the GPL v3.0 License | |
At the back of your NASA Clipper should be a round connector | |
with 5 pins, looking roughly like this ASCII art: | |
Pin Location Pin Number | |
| 2 | |
\ / 4 5 | |
- - 1 3 | |
O 6 | |
Pin 1: SCL | |
Pin 2: GND | |
Pin 3: SDA | |
Pin 4: 12V | |
Pin 5: GND | |
For Detail refer also to: | |
http://wiki.openseamap.org/wiki/De:NASA_Clipper_Range | |
If you connect SCL, SDA and GND with your arduino | |
and upload this sketch you should get the depth | |
information as serial data. | |
*/ | |
#include <Arduino.h> | |
#include <Wire.h> | |
const int ledPin = 13; // the number of the LED pin | |
static bool FLAG_LED_ON = LOW; | |
static byte I2C_nrec; | |
unsigned char data_handler[11]; | |
unsigned char data[11]; | |
static byte FLAG_NEW_DATA = 0; | |
static bool FLAG_VALID_DATA = LOW; | |
static bool FLAG_DEPTH = LOW; | |
void myHandler(int numBytes) | |
{ | |
byte i = 0; | |
for (i = 0; i < numBytes; i++) | |
{ | |
data_handler[i] = Wire.read(); | |
} | |
I2C_nrec = numBytes; | |
FLAG_NEW_DATA = 1; | |
} | |
void setup() | |
{ | |
pinMode(ledPin, OUTPUT); | |
Wire.begin(0x3e); | |
Serial.begin(4800); | |
Wire.onReceive(&myHandler); | |
Serial.println("$HELLO SKIPPER! THIS IS NASACLIPPER I2C to NMEA v0.5!\n"); | |
} | |
// The NASA Clipper sends a 12 byte I2C data packet, this data is directly send to a pcf8566p | |
// LCD Driver. The first byte is the address and the write direction, the next eleven bytes is data. | |
// The first 5 bytes is a command, the proceeding 6 bytes are data | |
// positions and contain the single LCD elements. | |
// Example data {0x7c,0xce,0x80,0xe0,0xf8,0x70,0x00,0x00,0x00,0x00,0x00,0x00}; | |
// addr 0 1 2 3 4 5 6 7 8 9 10 | |
// com com com com com dta dta dta dta dta dta | |
// Example depth : 23.3 | |
// Digit number : 12.3 | |
unsigned char I2C_predata[5] = {0xce, 0x80, 0xe0, 0xf8, 0x70}; | |
unsigned char depth_mask[6] = {0x01, 0, 0, 0, 0, 0}; | |
unsigned char decpoint_mask[6] = {0, 0, 0, 0x80, 0x0, 0x0}; | |
unsigned char metres_mask[6] = {0, 0, 0, 0x40, 0x0, 0x0}; | |
unsigned char digit3_mask[6] = {0, 0xbf, 0, 0, 0, 0}; | |
unsigned char digit3[10][6] = { | |
// from https://en.wikipedia.org/wiki/Seven-segment_display | |
{0, 0xbb, 0, 0, 0, 0}, // zero, a,b,c,d,e,f,/g | |
{0, 0x11, 0, 0, 0, 0}, // one /a,b,c,/d,/e,/f,/g | |
{0, 0x9e, 0, 0, 0, 0}, // two a,b,/c,d,e,/f,g | |
{0, 0x97, 0, 0, 0, 0}, // three a,b,c,d,/e,/f,g | |
{0, 0x35, 0, 0, 0, 0}, // four /a,b,c,/d,/e,f,g | |
{0, 0xa7, 0, 0, 0, 0}, // five a,/b,c,d,/e,f,g | |
{0, 0xaf, 0, 0, 0, 0}, // six a,/b,c,d,e,f,g | |
{0, 0x91, 0, 0, 0, 0}, // seven a,b,c,/d,/e,/f,/g | |
{0, 0xbf, 0, 0, 0, 0}, // eight a,b,c,d,e,f,g | |
{0, 0xb7, 0, 0, 0, 0}, // nine a,b,c,d,/e,f,g | |
}; | |
unsigned char digit2_mask[6] = {0xfe, 0, 0, 0, 0, 0}; | |
unsigned char digit2[10][6] = { | |
// from https://en.wikipedia.org/wiki/Seven-segment_display | |
{0xee, 0, 0, 0, 0, 0}, // zero, a,b,c,d,e,f,/g | |
{0x44, 0, 0, 0, 0, 0}, // one /a,b,c,/d,/e,/f,/g | |
{0xb6, 0, 0, 0, 0, 0}, // two a,b,/c,d,e,/f,g | |
{0xd6, 0, 0, 0, 0, 0}, // three a,b,c,d,/e,/f,g | |
{0x5c, 0, 0, 0, 0, 0}, // four /a,b,c,/d,/e,f,g | |
{0xda, 0, 0, 0, 0, 0}, // five a,/b,c,d,/e,f,g | |
{0xfa, 0, 0, 0, 0, 0}, // six a,/b,c,d,e,f,g | |
{0x46, 0, 0, 0, 0, 0}, // seven a,b,c,/d,/e,/f,/g | |
{0xfe, 0, 0, 0, 0, 0}, // eight a,b,c,d,e,f,g | |
{0xde, 0, 0, 0, 0, 0}, // nine a,b,c,d,/e,f,g | |
}; | |
unsigned char digit1_mask[6] = {0, 0, 0, 0, 0x2f, 0xc0}; | |
unsigned char digit1[10][6] = { | |
// from https://en.wikipedia.org/wiki/Seven-segment_display | |
{0, 0, 0, 0, 0x2e, 0xc0}, // zero, a,b,c,d,e,f,/g | |
{0, 0, 0, 0, 0x04, 0x40}, // one /a,b,c,/d,/e,/f,/g | |
{0, 0, 0, 0, 0x27, 0x80}, // two a,b,/c,d,e,/f,g | |
{0, 0, 0, 0, 0x25, 0xC0}, // three a,b,c,d,/e,/f,g | |
{0, 0, 0, 0, 0x0d, 0x40}, // four /a,b,c,/d,/e,f,g | |
{0, 0, 0, 0, 0x29, 0xC0}, // five a,/b,c,d,/e,f,g | |
{0, 0, 0, 0, 0x2b, 0xC0}, // six a,/b,c,d,e,f,g | |
{0, 0, 0, 0, 0x24, 0x40}, // seven a,b,c,/d,/e,/f,/g | |
{0, 0, 0, 0, 0x2f, 0xc0}, // eight a,b,c,d,e,f,g | |
{0, 0, 0, 0, 0x2d, 0xc0}, // nine a,b,c,d,/e,f,g | |
}; | |
void loop() | |
{ | |
byte i, j; | |
byte digit_tmp0, digit_tmp1, dig1, dig2, dig3, dec_point, checksum; | |
char dptstr[5], NMEADPTstr[20], cksstr[3]; | |
unsigned char ind_dptstr; | |
//static char incomingByte, command[60], DEBUG_MODE=0; | |
if (FLAG_NEW_DATA) | |
{ | |
// DEBUG, stuff: | |
//Serial.print("Got "); | |
//Serial.print(I2C_nrec,DEC); | |
//Serial.print(" bytes.\n"); | |
/* | |
for(i=0;i<I2C_nrec;i++) | |
{ | |
Serial.print(data_handler[i] & 0xFF, HEX); | |
Serial.write('_'); | |
} | |
Serial.print("\n"); | |
*/ | |
// RAW I2C Data as a NMEA string. | |
Serial.print("$IIDTA"); | |
for (i = 0; i < I2C_nrec; i++) | |
{ | |
Serial.print(","); | |
Serial.print(data_handler[i] & 0xFF, DEC); | |
} | |
Serial.print("*\n"); | |
ind_dptstr = 0; | |
// Copy the rawdata | |
for (j = 0; j < 11; j++) | |
{ | |
data[j] = data_handler[j]; | |
data_handler[j] = 0; | |
} | |
// Check if the first 5 byte (the command) are correct | |
// They seem to stay always the same | |
for (i = 0; i < 5; i++) | |
{ | |
dptstr[i] = 0; | |
if ((data[i] & 0xFF) == (I2C_predata[i] & 0xFF)) | |
{ | |
FLAG_VALID_DATA = HIGH; | |
} | |
else | |
{ | |
FLAG_VALID_DATA = LOW; | |
break; | |
} | |
} | |
if ((FLAG_VALID_DATA) & (I2C_nrec == 11)) | |
{ | |
// Decode the digits | |
dig1 = 'N'; | |
dig2 = 'N'; | |
dig3 = 'N'; | |
dec_point = 'N'; | |
// DIGIT 3 | |
digit_tmp0 = data[6] & digit3_mask[1]; | |
for (i = 0; i < 10; i++) | |
{ | |
if ((digit3[i][1] & 0xFF) == (digit_tmp0 & 0xFF)) | |
{ | |
dig3 = '0' + i; | |
break; | |
} | |
} | |
// decimal point | |
if ((data[8] & decpoint_mask[3] & 0xFF) == (0x80)) | |
{ | |
dec_point = '.'; | |
} | |
// We only consider data good, when the "DEPTH" symbol appears on the LCD | |
if ((data[5] & depth_mask[0] & 0xFF) == (0x01)) | |
{ | |
FLAG_DEPTH = HIGH; | |
} | |
else | |
{ | |
FLAG_DEPTH = LOW; | |
} | |
// DIGIT 2 | |
digit_tmp0 = data[5] & digit2_mask[0]; | |
for (i = 0; i < 10; i++) | |
{ | |
if ((digit2[i][0] & 0xFF) == (digit_tmp0 & 0xFF)) | |
{ | |
dig2 = '0' + i; | |
break; | |
} | |
} | |
// DIGIT 1 | |
digit_tmp0 = data[9] & digit1_mask[4]; | |
digit_tmp1 = data[10] & digit1_mask[5]; | |
for (i = 0; i < 10; i++) | |
{ | |
if (((digit1[i][4] & 0xFF) == (digit_tmp0 & 0xFF)) & | |
((digit1[i][5] & 0xFF) == (digit_tmp1 & 0xFF))) | |
{ | |
dig1 = '0' + i; | |
break; | |
} | |
} | |
i = 0; | |
// Do we have good data? (FLAG_DEPTH and at least one digit | |
if (((dig1 != 'N') | (dig2 != 'N') | (dig3 != 'N')) & (FLAG_DEPTH == HIGH)) | |
{ | |
ind_dptstr = 0; | |
if (dig1 != 'N') | |
{ | |
dptstr[ind_dptstr] = dig1; | |
ind_dptstr++; | |
} | |
if (dig2 != 'N') | |
{ | |
dptstr[ind_dptstr] = dig2; | |
ind_dptstr++; | |
} | |
if (dec_point != 'N') | |
{ | |
dptstr[ind_dptstr] = dec_point; | |
ind_dptstr++; | |
} | |
if (dig3 != 'N') | |
{ | |
dptstr[ind_dptstr] = dig3; | |
ind_dptstr++; | |
} | |
dptstr[ind_dptstr] = '\0'; | |
strcpy(NMEADPTstr, "$IIDPT,"); | |
strcat(NMEADPTstr, dptstr); | |
strcat(NMEADPTstr, ",0.0*"); | |
// Calculate Checksum | |
checksum = 0; | |
i = 0; | |
while (1) | |
{ | |
i++; | |
if (NMEADPTstr[i] == '*') | |
{ | |
break; | |
} | |
checksum = checksum ^ NMEADPTstr[i]; | |
} | |
sprintf(cksstr, "%X", checksum & 0xFF); | |
strcat(NMEADPTstr, cksstr); | |
strcat(NMEADPTstr, "\r\n"); | |
if (FLAG_DEPTH == HIGH) | |
{ | |
Serial.print(NMEADPTstr); | |
} | |
// Toggle the LED | |
if (FLAG_LED_ON == HIGH) | |
{ | |
FLAG_LED_ON = LOW; | |
digitalWrite(ledPin, LOW); | |
} | |
else | |
{ | |
FLAG_LED_ON = HIGH; | |
digitalWrite(ledPin, HIGH); | |
} | |
} | |
else | |
{ | |
Serial.println("$bad data"); | |
} | |
} | |
// Get rid of old data | |
for (i = 0; i < 11; i++) | |
{ | |
data[i] = 0; | |
} | |
FLAG_NEW_DATA = 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment