NMEA2000 Library  0.1
Library to handle NMEA 2000 Communication written in C++
Seasmart.cpp
Go to the documentation of this file.
1/*
2 The MIT License
3
4 Copyright (c) 2017-2024 Thomas Sarlandie thomas@sarlandie.net
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 THE SOFTWARE.
23*/
24
25#include <string.h>
26#include <ctype.h>
27#include <stdlib.h>
28#include "Seasmart.h"
29
30/* Some private helper functions to generate hex-serialized NMEA messages */
31static const char *hex = "0123456789ABCDEF";
32
33static int appendByte(char *s, uint8_t byte) {
34 s[0] = hex[byte >> 4];
35 s[1] = hex[byte & 0xf];
36 return 2;
37}
38
39static int append2Bytes(char *s, uint16_t i) {
40 appendByte(s, i >> 8);
41 appendByte(s + 2, i & 0xff);
42 return 4;
43}
44
45static int appendWord(char *s, uint32_t i) {
46 append2Bytes(s, i >> 16);
47 append2Bytes(s + 4, i & 0xffff);
48 return 8;
49}
50
51static uint8_t nmea_compute_checksum(const char *sentence) {
52 if (sentence == 0) {
53 return 0;
54 }
55
56 // Skip the $ at the beginning
57 int i = 1;
58
59 int checksum = 0;
60 while (sentence[i] != '*') {
61 checksum ^= sentence[i];
62 i++;
63 }
64 return checksum;
65}
66
67size_t N2kToSeasmart(const tN2kMsg &msg, uint32_t timestamp, char *buffer, size_t size) {
68 size_t pcdin_sentence_length = 6+1+6+1+8+1+2+1+msg.DataLen*2+1+2 + 1;
69
70 if (size < pcdin_sentence_length) {
71 return 0;
72 }
73
74 char *s = buffer;
75
76 strcpy(s, "$PCDIN,");
77 s += 7;
78 s += appendByte(s, msg.PGN >> 16);
79 s += append2Bytes(s, msg.PGN & 0xffff);
80 *s++ = ',';
81 s += appendWord(s, timestamp);
82 *s++ = ',';
83 s += appendByte(s, msg.Source);
84 *s++ = ',';
85
86 for (int i = 0; i < msg.DataLen; i++) {
87 s += appendByte(s, msg.Data[i]);
88 }
89
90 *s++ = '*';
91 s += appendByte(s, nmea_compute_checksum(buffer));
92 *s = 0;
93
94 return (size_t)(s - buffer);
95}
96
97/*
98 * Attempts to read n bytes in hexadecimal from input string to value.
99 *
100 * Returns true if successful, false otherwise.
101 */
102static bool readNHexByte(const char *s, unsigned int n, uint32_t &value) {
103 value=(uint32_t)(-1); // required to avoid warning about uninitialized variable.
104 if (strlen(s) < 2*n) {
105 return false;
106 }
107 for (unsigned int i = 0; i < 2*n; i++) {
108 if (!isxdigit(s[i])) {
109 return false;
110 }
111 }
112
113 char sNumber[2*n + 1];
114 strncpy(sNumber, s, sizeof(sNumber));
115 sNumber[sizeof(sNumber) - 1] = 0;
116
117 value = strtol(sNumber, 0, 16);
118 return true;
119}
120
121bool SeasmartToN2k(const char *buffer, uint32_t &timestamp, tN2kMsg &msg) {
122 msg.Clear();
123
124 const char *s = buffer;
125 if (strncmp("$PCDIN,", s, 6) != 0) {
126 return false;
127 }
128 s += 7;
129
130 uint32_t pgnHigh;
131 uint32_t pgnLow;
132 if (!readNHexByte(s, 1, pgnHigh)) {
133 return false;
134 }
135 s += 2;
136 if (!readNHexByte(s, 2, pgnLow)) {
137 return false;
138 }
139 s += 5;
140 msg.PGN = (pgnHigh << 16) + pgnLow;
141
142 if (!readNHexByte(s, 4, timestamp)) {
143 return false;
144 }
145 s += 9;
146
147 uint32_t source;
148 if (!readNHexByte(s, 1, source)) {
149 return false;
150 }
151 msg.Source = source;
152 s += 3;
153
154 int dataLen = 0;
155 while (s[dataLen] != 0 && s[dataLen] != '*') {
156 dataLen++;
157 }
158 if (dataLen % 2 != 0) {
159 return false;
160 }
161 dataLen /= 2;
162
163 msg.DataLen = dataLen;
164 if (msg.DataLen > msg.MaxDataLen) {
165 return false;
166 }
167 for (int i = 0; i < dataLen; i++) {
168 uint32_t byte;
169 if (!readNHexByte(s, 1, byte)) {
170 return false;
171 }
172 msg.Data[i] = byte;
173 s += 2;
174 }
175
176 // Skip the terminating '*' which marks beginning of checksum
177 s += 1;
178 uint32_t checksum;
179 if (!readNHexByte(s, 1, checksum)) {
180 return false;
181 }
182
183 if (checksum != nmea_compute_checksum(buffer)) {
184 return false;
185 }
186
187 return true;
188}
bool SeasmartToN2k(const char *buffer, uint32_t &timestamp, tN2kMsg &msg)
Converts a null terminated $PCDIN NMEA sentence into a tN2kMsg.
Definition: Seasmart.cpp:121
size_t N2kToSeasmart(const tN2kMsg &msg, uint32_t timestamp, char *buffer, size_t size)
Converts a tN2kMsg into a $PCDIN NMEA sentence.
Definition: Seasmart.cpp:67
static int append2Bytes(char *s, uint16_t i)
Definition: Seasmart.cpp:39
static bool readNHexByte(const char *s, unsigned int n, uint32_t &value)
Definition: Seasmart.cpp:102
static int appendWord(char *s, uint32_t i)
Definition: Seasmart.cpp:45
static uint8_t nmea_compute_checksum(const char *sentence)
Definition: Seasmart.cpp:51
static int appendByte(char *s, uint8_t byte)
Definition: Seasmart.cpp:33
static const char * hex
Definition: Seasmart.cpp:31
Conversion functions between N2k and Seasmart.
This class contains all the data of an NMEA2000 message.
Definition: N2kMsg.h:656
unsigned char Data[MaxDataLen]
Byte array which carries all the data of the NMEA2000 message.
Definition: N2kMsg.h:675
unsigned char Source
Source of the NMEA2000 message.
Definition: N2kMsg.h:669
static const int MaxDataLen
Maximum number of bytes that can be stored in the data buffer With fast packet the first frame can ha...
Definition: N2kMsg.h:663
virtual void Clear()
Clears the content of the N2kMsg object The method sets the PGN, DataLen and MsgTime to zero.
Definition: N2kMsg.cpp:94
int DataLen
Number of bytes already stored in tN2kMsg::Data of this message.
Definition: N2kMsg.h:673
unsigned long PGN
Parameter Group Number (PGN) of the NMEA2000 message.
Definition: N2kMsg.h:667