ASCII Serial Com
Serial communication library between computers, microcontrollers, FPGAs, etc. Uses only ASCII. Not the most efficient protocol, but meant to be easy to read
Loading...
Searching...
No Matches
ascii_serial_com.c
Go to the documentation of this file.
1#include "ascii_serial_com.h"
2
3#define crc_bbb 0
4#define crc_bbf 1
5#define crc_tbl4bit 2
6#define crc_crcmod 3
7#define crc_option crc_tbl4bit
8
9#if crc_option == crc_bbb
10#include "crc_16_dnp_bbb.h"
11#define crc_init crc_16_dnp_bbb_init
12#define crc_update crc_16_dnp_bbb_update
13#define crc_finalize crc_16_dnp_bbb_finalize
14#elif crc_option == crc_bbf
15#include "crc_16_dnp_bbf.h"
16#define crc_init crc_16_dnp_bbf_init
17#define crc_update crc_16_dnp_bbf_update
18#define crc_finalize crc_16_dnp_bbf_finalize
19#elif crc_option == crc_tbl4bit
20#include "crc_16_dnp_tbl4bit.h"
21#define crc_init crc_16_dnp_tbl4bit_init
22#define crc_update crc_16_dnp_tbl4bit_update
23#define crc_finalize crc_16_dnp_tbl4bit_finalize
24#elif crc_option == crc_crcmod
25#include "crc_16_dnp_crcmod.h"
26#else
27#error "crc_option not supported!"
28#endif
29
30/** \file */
31
32#include <inttypes.h>
33//#include <stdio.h>
34
35#define error_message_max_copy_data_len 9
36#define max_remove_unfinished_frames_tries 10
37
39
40 circular_buffer_init_uint8(&(asc->in_buf), MAXMESSAGELEN, asc->raw_buffer);
41 circular_buffer_init_uint8(&(asc->out_buf), MAXMESSAGELEN,
42 asc->raw_buffer + MAXMESSAGELEN);
43
47 asc->ignoreCRCMismatch = false;
48}
49
51 ascii_serial_com *asc, char ascVersion, char appVersion, char command,
52 const char *data, size_t dataLen) {
53 // fprintf(stderr,"ascii_serial_com_put_message_in_output_buffer called with
54 // ascVer: %c appVer: %c command: %c datalen: %zu\n",ascVersion, appVersion,
55 // command, dataLen); fprintf(stderr,"in_buf: %p\n",asc->in_buf.buffer);
56 // fprintf(stderr,"out_buf: %p\n",asc->out_buf.buffer);
57
58 if (dataLen > MAXDATALEN) {
59 Throw(ASC_ERROR_DATA_TOO_LONG);
60 }
65 for (size_t i = 0; i < dataLen; i++) {
67 }
69 char checksum[NCHARCHECKSUM];
70 ascii_serial_com_compute_checksum(asc, checksum, true);
71 for (size_t i = 0; i < NCHARCHECKSUM; i++) {
72 circular_buffer_push_back_uint8(&asc->out_buf, checksum[i]);
73 }
75}
76
78 char *ascVersion,
79 char *appVersion,
80 char *command, char *data,
81 size_t *dataLen) {
82 char computeChecksum[NCHARCHECKSUM];
83 size_t buf_size = circular_buffer_get_size_uint8(&asc->in_buf);
84 if (buf_size == 0) {
85 // fprintf(stderr, "Error Buffer size == 0\n");
86 *command = '\0';
87 *dataLen = 0;
88 return;
89 }
91 buf_size = circular_buffer_get_size_uint8(&asc->in_buf); // could have changed
92 size_t iEnd = circular_buffer_find_first_uint8(&asc->in_buf, '\n');
93 if (iEnd >= buf_size) {
94 // fprintf(stderr, "Error: start and/or end of frame not found\n");
95 // Don't throw away frame, the rest of the frame may just nead another read
96 // to get
97 *command = '\0';
98 *dataLen = 0;
99 return;
100 }
101 size_t iPeriod = circular_buffer_find_first_uint8(&asc->in_buf, '.');
102 if (iPeriod > iEnd) {
103 for (size_t i = 0; i <= iEnd; i++) {
104 circular_buffer_pop_front_uint8(&asc->in_buf); // pop off bad frame
105 }
106 Throw(ASC_ERROR_INVALID_FRAME_PERIOD);
107 }
108 if (!asc->ignoreCRCMismatch) {
109 ascii_serial_com_compute_checksum(asc, computeChecksum, false);
110 }
111 // fprintf(stderr,"Received message: \n");
112 // circular_buffer_print_uint8(&asc->in_buf,stderr);
113 circular_buffer_pop_front_uint8(&asc->in_buf); // pop off starting '>'
114 *ascVersion = circular_buffer_pop_front_uint8(&asc->in_buf);
115 *appVersion = circular_buffer_pop_front_uint8(&asc->in_buf);
116 *command = circular_buffer_pop_front_uint8(&asc->in_buf);
117 *dataLen = 0;
118 for (size_t iData = 0; iData < iPeriod - 4; iData++) {
119 char dataByte = circular_buffer_pop_front_uint8(&asc->in_buf);
120 if (dataByte == '.') {
121 break;
122 }
123 data[iData] = dataByte;
124 *dataLen += 1;
125 }
126 circular_buffer_pop_front_uint8(&asc->in_buf); // pop off '.'
127 if (circular_buffer_get_size_uint8(&asc->in_buf) < NCHARCHECKSUM + 1 ||
128 circular_buffer_get_element_uint8(&asc->in_buf, NCHARCHECKSUM) != '\n') {
129 Throw(ASC_ERROR_INVALID_FRAME);
130 // fprintf(stderr, "Error: checksum incorrect length\n");
131 //*command = '\0';
132 //*dataLen = 0;
133 // return;
134 }
135 char receiveChecksum[NCHARCHECKSUM];
136 for (size_t iChk = 0; iChk < NCHARCHECKSUM; iChk++) {
137 receiveChecksum[iChk] = circular_buffer_pop_front_uint8(&asc->in_buf);
138 }
139 if (!asc->ignoreCRCMismatch) {
140 for (size_t iChk = 0; iChk < NCHARCHECKSUM; iChk++) {
141 if (receiveChecksum[iChk] != computeChecksum[iChk]) {
142 // checksum mismatch!
143 // fprintf(stderr, "Error: checksum mismatch, computed: ");
144 // for (size_t i = 0; i < NCHARCHECKSUM; i++) {
145 // fprintf(stderr, "%c", computeChecksum[i]);
146 // }
147 // fprintf(stderr, ", received: ");
148 // for (size_t i = 0; i < NCHARCHECKSUM; i++) {
149 // fprintf(stderr, "%c", receiveChecksum[i]);
150 // }
151 // fprintf(stderr, "\n");
152 circular_buffer_pop_front_uint8(&asc->in_buf); // pop off remaining '\n'
153 *command = '\0';
154 *dataLen = 0;
155 return;
156 }
157 }
158 }
159 circular_buffer_pop_front_uint8(&asc->in_buf); // pop off trailing '\n'
160 return; // success!
161}
162
167
172
174 bool outputBuffer) {
175 circular_buffer_uint8 *circ_buf;
176 if (outputBuffer) {
177 circ_buf = &asc->out_buf;
178 } else {
179 circ_buf = &asc->in_buf;
180 }
181 const size_t size = circular_buffer_get_size_uint8(circ_buf);
182 size_t iStart = circular_buffer_find_last_uint8(circ_buf, '>');
183 size_t iStop = circular_buffer_find_last_uint8(circ_buf, '.');
184 if (!outputBuffer) { // want the first one on input
185 iStart = circular_buffer_find_first_uint8(circ_buf, '>');
186 iStop = circular_buffer_find_first_uint8(circ_buf, '.');
187 }
188 if (iStart >= size) {
189 Throw(ASC_ERROR_INVALID_FRAME);
190 }
191 if (iStop >= size) {
192 Throw(ASC_ERROR_INVALID_FRAME_PERIOD);
193 }
194 if (iStop >= MAXMESSAGELEN - NCHARCHECKSUM - 1) {
195 Throw(ASC_ERROR_CHECKSUM_PROBLEM);
196 }
197 if (iStop <= iStart || iStop - iStart < 4) {
198 Throw(ASC_ERROR_INVALID_FRAME_PERIOD);
199 }
200 uint8_t *blocks[2];
201 size_t blocks_sizes[2];
202 size_t nBlocks = circular_buffer_get_blocks_uint8(
203 circ_buf, iStart, iStop - iStart + 1, blocks, blocks_sizes);
204#if crc_option == crc_crcmod
205 uint16_t crc = 0xFFFF;
206 for (size_t iBlock = 0; iBlock < nBlocks; iBlock++) {
207 crc = computeCRC_16_DNP(blocks[iBlock], blocks_sizes[iBlock], crc);
208 }
209#else
210 uint16_t crc = crc_init();
211 for (size_t iBlock = 0; iBlock < nBlocks; iBlock++) {
212 crc = crc_update(crc, blocks[iBlock], blocks_sizes[iBlock]);
213 }
214 crc = crc_finalize(crc);
215#endif
216 convert_uint16_to_hex(crc, checksumOut, true);
217}
218
220 char ascVersion,
221 char appVersion,
222 const char *data,
223 size_t dataLen) {
224
225 if (dataLen > MAXSPAYLOADEN) {
226 Throw(ASC_ERROR_DATA_TOO_LONG);
227 }
228 char outData[MAXDATALEN];
231 outData[2] = ',';
232 for (size_t i = 0; i < dataLen && i < (MAXDATALEN - 3); i++) {
233 outData[i + 3] = data[i];
234 }
235 ascii_serial_com_put_message_in_output_buffer(asc, ascVersion, appVersion,
236 's', outData, dataLen + 3);
237}
238
240 char ascVersion,
241 char appVersion, char command,
242 char *data, size_t dataLen,
243 enum asc_exception errorCode) {
244 char outData[error_message_max_copy_data_len + 5];
245 convert_uint8_to_hex((uint8_t)errorCode, outData, true);
246 outData[2] = ',';
247 outData[3] = command;
248 outData[4] = ',';
249 size_t outDataLen = 5;
250 for (size_t i = 0; i < dataLen && i < error_message_max_copy_data_len; i++) {
251 outData[i + 5] = data[i];
252 outDataLen++;
253 }
254 ascii_serial_com_put_message_in_output_buffer(asc, ascVersion, appVersion,
255 'e', outData, outDataLen);
256}
257
258void ascii_serial_com_set_ignore_CRC_mismatch(ascii_serial_com *asc) {
259 asc->ignoreCRCMismatch = true;
260}
261
262void ascii_serial_com_unset_ignore_CRC_mismatch(ascii_serial_com *asc) {
263 asc->ignoreCRCMismatch = false;
264}
265
266/////////////////////////////////////////////////////
267
268void convert_uint8_to_hex(uint8_t num, char *outstr, bool caps) {
269 for (uint8_t i = 0; i < 2; i++) {
270 uint8_t nibble = (num >> (4 * i)) & 0xF;
271 if (nibble < 10) {
272 outstr[1 - i] = 0x30 + nibble;
273 } else if (caps) {
274 outstr[1 - i] = 0x41 + nibble - 10;
275 } else {
276 outstr[1 - i] = 0x61 + nibble - 10;
277 }
278 }
279}
280
281void convert_uint16_to_hex(uint16_t num, char *outstr, bool caps) {
282 for (uint8_t i = 0; i < 2; i++) {
283 convert_uint8_to_hex((num >> (8 * i)) & 0xFF, outstr + (1 - i) * 2, caps);
284 }
285}
286
287void convert_uint32_to_hex(uint32_t num, char *outstr, bool caps) {
288 for (uint8_t i = 0; i < 4; i++) {
289 convert_uint8_to_hex((num >> (8 * i)) & 0xFF, outstr + (3 - i) * 2, caps);
290 }
291}
292
293uint8_t convert_hex_to_uint8(const char *instr) {
294 uint8_t result = 0;
295 for (uint8_t i = 0; i < 2; i++) {
296 char thischar = instr[1 - i];
297 if (thischar >= 0x30 && thischar <= 0x39) {
298 result |= (thischar - 0x30) << (i * 4);
299 } else if (thischar >= 0x41 && thischar <= 0x46) {
300 result |= (thischar - 0x41 + 10) << (i * 4);
301 } else if (thischar >= 0x61 && thischar <= 0x66) {
302 result |= (thischar - 0x61 + 10) << (i * 4);
303 } else {
304 // fprintf(stderr,"Problem char: '%c' =
305 // %"PRIX8"\n",thischar,(uint8_t) thischar); fflush(stderr);
306 Throw(ASC_ERROR_NOT_HEX_CHAR);
307 }
308 }
309 return result;
310}
311
312uint16_t convert_hex_to_uint16(const char *instr) {
313 uint16_t result = 0;
314 for (uint8_t i = 0; i < 2; i++) {
315 result |= (uint16_t)convert_hex_to_uint8(instr + (1 - i) * 2) << (8 * i);
316 }
317 return result;
318}
319
320uint32_t convert_hex_to_uint32(const char *instr) {
321 uint32_t result = 0;
322 for (uint8_t i = 0; i < 4; i++) {
323 result |= (uint32_t)convert_hex_to_uint8(instr + (3 - i) * 2) << (8 * i);
324 }
325 return result;
326}
asc_exception
Exception type to be used with CException.
void convert_uint8_to_hex(uint8_t num, char *outstr, bool caps)
convert uint8 to hex string
circular_buffer_uint8 * ascii_serial_com_get_output_buffer(ascii_serial_com *asc)
ASCII Serial Com get output buffer.
void convert_uint16_to_hex(uint16_t num, char *outstr, bool caps)
convert uint16 to hex string
void ascii_serial_com_put_s_message_in_output_buffer(ascii_serial_com *asc, char ascVersion, char appVersion, const char *data, size_t dataLen)
ASCII Serial Com Pack and put 's' message in output buffer.
void ascii_serial_com_init(ascii_serial_com *asc)
ASCII Serial Com Interface init method.
uint16_t convert_hex_to_uint16(const char *instr)
convert hex string to uint16
void ascii_serial_com_put_message_in_output_buffer(ascii_serial_com *asc, char ascVersion, char appVersion, char command, const char *data, size_t dataLen)
ASCII Serial Com Pack and put message in output buffer.
void ascii_serial_com_get_message_from_input_buffer(ascii_serial_com *asc, char *ascVersion, char *appVersion, char *command, char *data, size_t *dataLen)
ASCII Serial Com pop message from input buffer and unpack.
uint8_t convert_hex_to_uint8(const char *instr)
convert hex string to uint8
uint32_t convert_hex_to_uint32(const char *instr)
convert hex string to uint32
void ascii_serial_com_put_error_in_output_buffer(ascii_serial_com *asc, char ascVersion, char appVersion, char command, char *data, size_t dataLen, enum asc_exception errorCode)
ASCII Serial Com put error message in out buffer.
void ascii_serial_com_compute_checksum(ascii_serial_com *asc, char *checksumOut, bool outputBuffer)
ASCII Serial Com compute checksum of message.
circular_buffer_uint8 * ascii_serial_com_get_input_buffer(ascii_serial_com *asc)
ASCII Serial Com get input buffer.
void convert_uint32_to_hex(uint32_t num, char *outstr, bool caps)
convert uint32 to hex string
size_t circular_buffer_get_size_uint8(const circular_buffer_uint8 *circ_buf)
circular buffer get number of elements
void circular_buffer_init_uint8(circular_buffer_uint8 *circ_buf, const size_t capacity, uint8_t *buffer)
circular buffer init method
size_t circular_buffer_remove_front_unfinished_frames_uint8(circular_buffer_uint8 *circ_buf, const char startChar, const char endChar)
circular buffer remove unfinished frames from front of buffer
size_t circular_buffer_get_blocks_uint8(circular_buffer_uint8 *circ_buf, size_t iStart, size_t nElem, uint8_t **blocks, size_t *blocks_sizes)
circular buffer access raw blocks of data for a given range of elements
uint8_t circular_buffer_get_element_uint8(const circular_buffer_uint8 *circ_buf, const size_t iElement)
circular buffer get element
size_t circular_buffer_find_first_uint8(const circular_buffer_uint8 *circ_buf, const uint8_t value)
circular buffer find index of first occurance of value
uint8_t circular_buffer_pop_front_uint8(circular_buffer_uint8 *circ_buf)
circular buffer pop front
size_t circular_buffer_find_last_uint8(const circular_buffer_uint8 *circ_buf, const uint8_t value)
circular buffer find index of last occurance of value
void circular_buffer_push_back_uint8(circular_buffer_uint8 *circ_buf, const uint8_t element)
circular buffer push back
ASCII Serial Com Interface State struct.
circular_buffer_uint8 in_buf
uint8_t raw_buffer[2 *MAXMESSAGELEN]
circular_buffer_uint8 out_buf
circular buffer struct