Initial commit for stepper controller
[radiotelescope.git] / stepper_controller / arduino / stepper / serial_command.c
1 //
2 // Product: Serial command/response HSM (Samek, Orthoganol Component pattern)
3 // Version: 0.1
4 // Date:    30 November 2010
5 // Author:  Harry McNally
6 //
7 //                                          +-----------------------+
8 //                      |   d e c i s i o n s   |
9 //                                          +-----------------------+
10 //                                          | a n d   d e s i g n s |
11 //                      +-----------------------+
12 //
13 // Copyright (C) 2010 Decisions and Designs Pty Ltd. All rights reserved.
14 //
15 // This software may be distributed and modified under the terms of the GNU
16 // General Public License version 2 (GPL) as published by the Free Software
17 // Foundation and appearing in the file GPL2.TXT included in the packaging of
18 // this file. Please note that GPL Section 2[b] requires that all works based
19 // on this software must also be made publicly available under the terms of
20 // the GPL ("Copyleft").
21 //
22 // Contact information:
23 // Decisions and Designs Web site: http://www.decisions-and-designs.com.au
24 // e-mail:                         [email protected]
25 //
26
27 #include "serial_command.h" /* SerialCommand class for Container VArg access */ 
28 #include "serial_io.h"
29 #include "signals.h"
30 #include "qpn_port.h"
31
32 #define COMMAND_VARGS           4
33 #define EOC                     0x80
34
35 /* SerialCommand states -----------------------------------------------------*/
36
37 static QState SerialCommand_initial(SerialCommand *me);
38 static QState SerialCommand_operational(SerialCommand *me);
39 static QState SerialCommand_parsing(SerialCommand *me);
40 static QState SerialCommand_startArgument(SerialCommand *me);
41 static QState SerialCommand_firstArgumentDigit(SerialCommand *me);
42 static QState SerialCommand_processArgumentDigits(SerialCommand *me);
43 static QState SerialCommand_awaitSeparator(SerialCommand *me);
44 static QState SerialCommand_awaitTermination(SerialCommand *me);
45 static QState SerialCommand_failTermination(SerialCommand *me);
46
47 /* local services */
48 void serialClearVargs(SerialCommand *me);
49 void serialInitialiseVarg(SerialCommand *me, uint8_t type);
50 uint8_t serialProcessDigit(SerialCommand *me, uint8_t ch);
51 void serialFinishVarg(SerialCommand *me);
52
53 /* HSM constructor .........................................................*/
54 void SerialCommand_ctor(SerialCommand *me, QActive *container, Menu *menu,
55                         MenuSignal *menuSignals, char *txBuffer, uint8_t txSize,\r
56                                                 uint8_t port, uint32_t baud) {
57     me->container = container;
58     me->menu = menu;
59     me->menuSignals = menuSignals;\r
60         me->txBuffer = txBuffer;\r
61         me->txSize = txSize;
62     QActive_ctor((QActive *)me, (QStateHandler)&SerialCommand_initial);
63     switch (port) {
64         case USART : {
65             me->serialTransmit = serialTransmit;
66             me->serialTransmitComplete = serialTransmitComplete;
67             initSerial(baud, (QActive *)container, (QActive *)container);
68             break;
69         }
70         #if defined (__AVR_ATmega1280__)
71         case USART1 : {
72             me->serialTransmit = serial1Transmit;
73             me->serialTransmitComplete = serial1TransmitComplete;
74             initSerial1(baud, (QActive *)container, (QActive *)container);
75             break;
76         }
77         case USART2 : {
78             me->serialTransmit = serial2Transmit;
79             me->serialTransmitComplete = serial2TransmitComplete;
80             initSerial2(baud, (QActive *)container, (QActive *)container);
81             break;
82         }
83         case USART3 : {
84             me->serialTransmit = serial3Transmit;
85             me->serialTransmitComplete = serial3TransmitComplete;
86             initSerial3(baud, (QActive *)container, (QActive *)container);
87             break;
88         }
89         #endif
90     }    
91 }
92 /* HSM definition ----------------------------------------------------------*/
93 QState SerialCommand_initial(SerialCommand *me) {
94     return Q_TRAN(&SerialCommand_operational);
95 }
96 /*..........................................................................*/
97 QState SerialCommand_operational(SerialCommand *me) {
98         switch (Q_SIG(me)) {
99         case Q_INIT_SIG : {
100             return Q_TRAN(&SerialCommand_parsing);
101         }
102                 case Q_ENTRY_SIG : {
103             /* initialise the tx buffer and varg counter */
104             me->txHead = me->txTail = 0;
105             me->vidx = 0;
106                         return Q_HANDLED();
107         }
108         /* transmitter */
109                 case SERIAL_TX_EMPTY_SIG : {
110             /* no characters left in buffer to transmit */
111             if (me->txHead == me->txTail) {
112                 return Q_HANDLED();
113             }
114             /* if non-zero (busy) returned, signal was stale, wait for next */ 
115             if ((*me->serialTransmit)(*(me->txBuffer + me->txTail))) {
116                 return Q_HANDLED();
117             }
118             /* else character is transmitting, update tail index */
119             me->txTail++;
120             if (me->txTail >= me->txSize)
121                 me->txTail = 0;
122             return Q_HANDLED();           
123         }
124         /* command rx errors wait for next CR/LF and NACK */
125         case SERIAL_RX_ERROR_SIG : {
126             return Q_TRAN(&SerialCommand_failTermination);
127         }
128     }    
129         return Q_SUPER(&QHsm_top);
130 }
131 /*..........................................................................*/
132 QState SerialCommand_parsing(SerialCommand *me) {
133         switch (Q_SIG(me)) {
134         case Q_ENTRY_SIG : {
135             me->parser = me->menu;
136             return Q_HANDLED();
137         }
138         /* command parser */
139         case SERIAL_RX_DATA_SIG : {
140             char ch = ((SerialParam *)(&Q_PAR(me)))->msg.ch;       
141             /* command terminating CR or LF */
142             if ((ch == '\r') || (ch == '\n')) {
143                 /* end of command not possible at menu root so drop ch */
144                 if (me->parser == me->menu) {
145                     return Q_HANDLED();
146                 }
147                 /* unexpected early termination, send 0th menuSignal (fail) */
148                 QActive_post(me->container, me->menuSignals->sig, 0);
149                 return Q_TRAN(&SerialCommand_operational);
150             }
151             /* scan the menu tree. transition to await CR/LF if no match */
152             while (me->parser->idx) {
153                 /* search for matching char in menu */
154                 if (ch == me->parser->ch) {
155                     /* end of command */
156                     if (me->parser->idx & EOC) {
157                         /* no vargs to fetch */\r
158                                                 MenuSignal *msig = me->menuSignals;
159                         msig += me->parser->idx & ~EOC;                          
160                         if (!(msig->varg & (VARG_TYPE << 0))) {\r
161                                                         return Q_TRAN(&SerialCommand_awaitTermination);\r
162                                                 }
163                         /* fail if container has not released vargs in time */
164                         if (me->vidx) {
165                             return Q_TRAN(&SerialCommand_failTermination);
166                         }
167                         /* prepare and go fetch any vargs */
168                         serialClearVargs(me);
169                         return Q_TRAN(&SerialCommand_startArgument);
170                     }
171                     else {
172                         /* index to the next menu sub-list */
173                         me->parser = me->menu + me->parser->idx;
174                         return Q_HANDLED();
175                     }
176                 }
177                 /* next entry in menu list */
178                 me->parser++;
179             }
180             /* no match for ch so await CR/LF and then signal failure */
181             return Q_TRAN(&SerialCommand_failTermination);
182         }
183     }    
184         return Q_SUPER(&SerialCommand_operational);
185 }
186 /*..........................................................................*/
187 QState SerialCommand_startArgument(SerialCommand *me) {
188         switch (Q_SIG(me)) {
189         case Q_ENTRY_SIG : {
190             uint8_t type;
191             /* unpack the two bit VARG type; one of four in menuSignal byte */
192             type = (me->menuSignals + (me->parser->idx & ~EOC))->varg;
193             type >>= me->vidx << 1;
194             type &= VARG_TYPE;
195             serialInitialiseVarg(me, type);
196             return Q_HANDLED();
197         }
198         case SERIAL_RX_DATA_SIG : {
199             char ch = ((SerialParam *)(&Q_PAR(me)))->msg.ch;       
200             if ((ch == '\r') || (ch == '\n')) {
201                 /* unexpected early termination, send 0th menuSignal (fail) */
202                 QActive_post(me->container, me->menuSignals->sig, 0);
203                 return Q_TRAN(&SerialCommand_operational);
204             }
205             #ifndef PACKED_VARGS 
206             if ((ch == ' ') || (ch == '\t')) {
207                 return Q_HANDLED();
208             }
209             #endif
210             if (ch == '+') {
211                 /* allow + prefix */
212                 return Q_TRAN(&SerialCommand_firstArgumentDigit);
213             }
214             if (ch == '-') {
215                 /* negate on - prefix */
216                 me->vargs[me->vidx].type |= VARG_NEGATIVE;
217                 return Q_TRAN(&SerialCommand_firstArgumentDigit);
218             }
219             if ((ch == 'x') || (ch == 'X')) {
220                 /* x or X prefix a hex number */
221                 me->vargs[me->vidx].type |= VARG_HEX;
222                 return Q_TRAN(&SerialCommand_firstArgumentDigit);
223             }
224             /* failure to consume char as digit returns non-zero */
225             if (serialProcessDigit(me, ch)) {\r
226                                 serialClearVargs(me);
227                 return Q_TRAN(&SerialCommand_failTermination);
228             }
229             /* successfully accumulated first number */
230             return Q_TRAN(&SerialCommand_processArgumentDigits);
231         }
232     }
233     return Q_SUPER(&SerialCommand_operational);
234 }
235 /*..........................................................................*/
236 QState SerialCommand_firstArgumentDigit(SerialCommand *me) {
237     switch (Q_SIG(me)) {
238         case SERIAL_RX_DATA_SIG : {
239             char ch = ((SerialParam *)(&Q_PAR(me)))->msg.ch;       
240             if ((ch == '\r') || (ch == '\n')) {
241                 /* unexpected early termination, send 0th menuSignal (fail) */
242                 QActive_post(me->container, me->menuSignals->sig, 0);\r
243                                 serialClearVargs(me);
244                 return Q_TRAN(&SerialCommand_operational);
245             }
246             /* failure to consume char as digit returns non-zero */
247             if (serialProcessDigit(me, ch)) {\r
248                                 serialClearVargs(me);
249                 return Q_TRAN(&SerialCommand_failTermination);
250             }
251             /* Successfully accumulated first number */
252             return Q_TRAN(&SerialCommand_processArgumentDigits);
253         }
254     }
255     return Q_SUPER(&SerialCommand_operational);
256 }
257 /*..........................................................................*/
258 QState SerialCommand_processArgumentDigits(SerialCommand *me) {
259     switch (Q_SIG(me)) {
260         case SERIAL_RX_DATA_SIG : {
261             char ch = ((SerialParam *)(&Q_PAR(me)))->msg.ch;
262             MenuSignal *msig = me->menuSignals;
263             msig += me->parser->idx & ~EOC;                          
264             if ((ch=='\r')||(ch=='\n')||(ch==' ')||(ch==',')||(ch=='\t')) {
265                 serialFinishVarg(me);
266                 me->vidx++; /* final vargs count */
267                 /* still more arguments to fetch */
268                 if ((me->vidx < VARG_COUNT) &&
269                     (msig->varg & (VARG_TYPE << (me->vidx << 1)))) {
270                     if ((ch == ' ') || (ch == '\t')) {
271                         #ifdef PACKED_VARGS
272                         /* space is the separator */
273                         return Q_TRAN(&SerialCommand_startArgument);
274                         #else 
275                         /* a separator may follow */
276                         return Q_TRAN(&SerialCommand_awaitSeparator);
277                         #endif
278                     }
279                     if (ch == ',') {
280                         return Q_TRAN(&SerialCommand_startArgument);
281                     }
282                     /* CR/LF, early termination, send 0th menuSignal (fail) */
283                     QActive_post(me->container, me->menuSignals->sig, 0);\r
284                                         serialClearVargs(me);
285                     return Q_TRAN(&SerialCommand_operational);
286                 }
287                 /* all arguments accumulated */
288                 else {
289                     if ((ch == ' ') || (ch == '\t')) {
290                         #ifdef PACKED_VARGS
291                         /* expected terminator so fail */
292                         QActive_post(me->container, me->menuSignals->sig, 0);
293                         return Q_TRAN(&SerialCommand_operational);
294                         #else
295                         /* trailing space is valid */
296                         return Q_TRAN(&SerialCommand_awaitTermination);
297                         #endif
298                     }
299                     if (ch == ',') {
300                         /* separator is invalid so fail */
301                         QActive_post(me->container, me->menuSignals->sig, 0);
302                         return Q_TRAN(&SerialCommand_operational);
303                     }                        
304                     /* CR/LF so signal the command to the container class */
305                     QActive_post(me->container, msig->sig, me->vargs[0].v.uint32);
306                     /* single argument passed in signal, flag vargs available */
307                     if (me->vidx == 1)
308                         me->vidx = 0;
309                     return Q_TRAN(&SerialCommand_operational);
310                 }
311             }
312             /* any char not accepted as a digit returns non-zero */
313             if (serialProcessDigit(me, ch)) {\r
314                                 serialClearVargs(me);
315                 return Q_TRAN(&SerialCommand_failTermination);
316             }
317             /* Successfully accumulated digit */
318             return Q_HANDLED();
319         }
320     }
321     return Q_SUPER(&SerialCommand_operational);
322 }
323 /*..........................................................................*/
324 static QState SerialCommand_awaitSeparator(SerialCommand *me) {
325     switch (Q_SIG(me)) {
326         case Q_ENTRY_SIG : {
327             uint8_t type;
328             /* unpack the two bit VARG type; one of four in menuSignal byte */
329             type = (me->menuSignals + (me->parser->idx & ~EOC))->varg;
330             type >>= me->vidx << 1;
331             type &= VARG_TYPE;
332             serialInitialiseVarg(me, type);
333             return Q_HANDLED();
334         }
335         case SERIAL_RX_DATA_SIG : {
336             char ch = ((SerialParam *)(&Q_PAR(me)))->msg.ch;
337             if (ch == ',') {
338                 /* this will re-initialise the varg on entry without issue */ 
339                 return Q_TRAN(&SerialCommand_startArgument);
340             }
341             /* remaining handling is the same as SerialCommand_startArgument */ 
342             if ((ch == '\r') || (ch == '\n')) {
343                 /* unexpected early termination, send 0th menuSignal (fail) */
344                 QActive_post(me->container, me->menuSignals->sig, 0);
345                 return Q_TRAN(&SerialCommand_operational);
346             }
347             #ifndef PACKED_VARGS 
348             if ((ch == ' ') || (ch == '\t')) {
349                 return Q_HANDLED();
350             }
351             #endif
352             if (ch == '+') {
353                 /* allow + prefix */
354                 return Q_TRAN(&SerialCommand_firstArgumentDigit);
355             }
356             if (ch == '-') {
357                 /* negate on - prefix */
358                 me->vargs[me->vidx].type |= VARG_NEGATIVE;
359                 return Q_TRAN(&SerialCommand_firstArgumentDigit);
360             }
361             if ((ch == 'x') || (ch == 'X')) {
362                 /* x or X prefix a hex number */
363                 me->vargs[me->vidx].type |= VARG_HEX;
364                 return Q_TRAN(&SerialCommand_firstArgumentDigit);
365             }
366             /* failure to consume char as digit returns non-zero */
367             if (serialProcessDigit(me, ch)) {\r
368                                 serialClearVargs(me);
369                 return Q_TRAN(&SerialCommand_failTermination);
370             }
371             /* successfully accumulated first number */
372             return Q_TRAN(&SerialCommand_processArgumentDigits);
373         }
374     }
375     return Q_SUPER(&SerialCommand_operational);
376 }
377 /*..........................................................................*/
378 static QState SerialCommand_awaitTermination(SerialCommand *me) {
379     switch (Q_SIG(me)) {
380         case SERIAL_RX_DATA_SIG : {
381             char ch = ((SerialParam *)(&Q_PAR(me)))->msg.ch;
382             if ((ch == '\r') || (ch == '\n')) {\r
383                                 MenuSignal *msig = me->menuSignals;
384                 msig += me->parser->idx & ~EOC;\r
385                 QActive_post(me->container, msig->sig, me->vargs[0].v.uint32);
386                 return Q_TRAN(&SerialCommand_operational);
387             }
388             #ifndef PACKED_VARGS 
389             if ((ch == ' ') || (ch == '\t')) {
390                 return Q_HANDLED();
391             }
392             #endif
393             /* any other chars are invalid */\r
394                         serialClearVargs(me);
395             return Q_TRAN(SerialCommand_failTermination);
396         }
397     }
398     return Q_SUPER(&SerialCommand_operational);
399 }
400 /*..........................................................................*/
401 static QState SerialCommand_failTermination(SerialCommand *me) {
402     switch (Q_SIG(me)) {
403         case SERIAL_RX_DATA_SIG : {
404             char ch = ((SerialParam *)(&Q_PAR(me)))->msg.ch;
405             if ((ch == '\r') || (ch == '\n')) {
406                 /* termination, send 0th menuSignal (fail) */
407                 QActive_post(me->container, me->menuSignals->sig, 0);
408                 return Q_TRAN(&SerialCommand_operational);
409             }
410         }
411     }
412     return Q_SUPER(&SerialCommand_operational);
413 }
414 /* Local services ..........................................................*/
415 void serialClearVargs(SerialCommand *me) {
416     VArg *varg = me->vargs;
417     varg->type = 0; varg++;
418     varg->type = 0; varg++;
419     varg->type = 0; varg++;
420     varg->type = 0;\r
421         me->vidx   = 0;
422 }    
423 /*..........................................................................*/
424 void serialInitialiseVarg(SerialCommand *me, uint8_t type) {
425     VArg *varg = &me->vargs[me->vidx];
426     varg->type = type;
427     switch (type) {
428         case VARG_UINT : {
429             varg->v.uint32 = 0;
430             break;
431         }
432         case VARG_INT : {
433             varg->v.int32 = 0;
434             break;
435         }
436         case VARG_DBL : {
437             varg->v.dbl = 0.0;
438             me->vdiv = 0.0;
439             break;
440         }
441     }
442 }
443 /*..........................................................................*/
444 uint8_t serialProcessDigit(SerialCommand *me, uint8_t ch) {
445     VArg *varg = &me->vargs[me->vidx];
446     if ((ch >= '0') && (ch <= '9')) {
447         ch -= '0';
448     }\r
449         else if (    (ch == '.') 
450               && ((varg->type & VARG_TYPE) == VARG_DBL)
451               && (me->vdiv == 0.0)) {
452         /* start of fractional part */
453         me->vdiv = 10.0;\r
454                 return 0; /* V_OK */
455     }
456     /* if varg is not a hex then any other char is not a number */
457     else if (!(varg->type & VARG_HEX)) {
458         return V_INVALID;
459     }
460     else if ((ch >= 'a') && (ch <= 'f')) {
461         ch -= 'a' - 10;
462     }
463     else if ((ch >= 'A') && (ch <= 'F')) {
464         ch -= 'A' - 10;
465     }
466     else {
467         /* didn't find a hex number either */
468         return V_INVALID;
469     }
470     /* handle number based on varg type */
471     switch (varg->type & VARG_TYPE) {
472         case VARG_UINT : {
473             if (varg->type & VARG_HEX) {
474                 /* range test and return fail on overflow */
475                 if (varg->v.uint32 & 0xf0000000) {
476                     return V_OVERFLOW;
477                 }
478                 /* scale accumulator for hex */
479                 varg->v.uint32 <<= 4;
480             } else {
481                 /* range test and return on overflow */
482                 if (varg->v.uint32 > 429496729) {
483                     return V_OVERFLOW;
484                 }
485                 /* range test and return on overflow */
486                 if ((varg->v.uint32 == 429496729) && (ch > 5)) {
487                     return V_OVERFLOW;
488                 }
489                 /* scale accumulator */     
490                 varg->v.uint32 *= 10;
491             }
492             /* accumulate the character now converted to number */
493             varg->v.uint32 += ch;
494             break;
495         }
496         /* int32 is accumulated negatively to achieve -2147483648 */
497         case VARG_INT : {
498             /* range test and return on overflow */
499             if (varg->v.int32 < -214748364) {
500                 return V_OVERFLOW;
501             }
502             if (varg->v.int32 == -214748364) {
503                 if (varg->type & VARG_NEGATIVE) {
504                     /* underflow of -2147483648 */
505                     if (ch > 8) {
506                         return V_OVERFLOW;
507                     }
508                 /* overflow of 2147483647 */ 
509                 } else if (ch > 7) {
510                     return V_OVERFLOW;
511                 }
512             
513             }
514             /* scale accumulator */
515             varg->v.int32 *= 10;
516             /* accumulate the character now converted to number */
517             varg->v.int32 -= ch;
518             break;
519         }    
520         case VARG_DBL : {
521             if (me->vdiv == 0.0) {
522                 /* accumulate matissa */
523                 varg->v.dbl *= 10.0;
524                 varg->v.dbl += (double)ch;
525                 break;
526             }
527             /* accumulate fraction */
528             varg->v.dbl += (double)(ch) / me->vdiv;
529             me->vdiv *= 10.0;
530             break;
531         }
532     }
533     return 0; /* V_OK */            
534 }
535 /*..........................................................................*/
536 void serialFinishVarg(SerialCommand *me) {
537     VArg *varg = &me->vargs[me->vidx]; 
538     switch (varg->type & VARG_TYPE) {
539         /* int32 was accumulated negatively to allow for -2147483648 */
540         case VARG_INT : {
541             if (!(varg->type & VARG_NEGATIVE)) {
542                 /* negate the accumulator to get all positive int32 values */
543                 varg->v.int32 = -varg->v.int32;
544             }
545             break;
546         }
547         case VARG_DBL : {
548             if (varg->type & VARG_NEGATIVE) {
549                 varg->v.dbl *= -1.0;
550             }
551             break;
552         }
553         default : {
554             break;
555         }
556     }
557     /* clean away the hex and negative type flags */
558     varg->type &= VARG_TYPE;
559 }
560 /* Container Services ......................................................*/
561 void serialDispatch(SerialCommand *me, QSignal sig, QParam par) {
562     Q_SIG(me) = sig;    
563     Q_PAR(me) = par;
564     QHsm_dispatch((QHsm *)me);
565 }
566 /*..........................................................................*/
567 void serialReleaseVargs(SerialCommand *me) {
568     me->vidx = 0;
569 }
570 /*..........................................................................*/
571 void serialDumpArgs(SerialCommand *me) {
572     uint8_t idx;
573     for (idx = 0; idx < VARG_COUNT; idx++) {
574         switch (me->vargs[idx].type) {
575             case VARG_NONE : {
576                 serialStr(me, "None:  ");
577                 break;
578             }
579             case VARG_UINT : {
580                 serialStr(me, "UInt: ");
581                 serialUnum(me, me->vargs[idx].v.uint32);
582                 serialStr(me, "  ");
583                 break;
584             }
585             case VARG_INT : {
586                 serialStr(me, "Int: ");
587                 serialNum(me, me->vargs[idx].v.int32);
588                 serialStr(me, "  ");
589                 break;
590             }
591             case VARG_DBL : {
592                 serialStr(me, "Dbl: ");
593                 serialDbl(me, me->vargs[idx].v.dbl, 6);
594                 serialStr(me, "  ");
595                 break;
596             }
597         }
598     }
599     serialStr(me, "\r\n");
600 }
601 /*..........................................................................*/
602 void serialChar(SerialCommand *me, char c) {   
603     int16_t length;
604
605     /* tx buffer is empty, try to transmit */
606     if (me->txTail == me->txHead) {
607         /* if SERIAL_BUSY returned, queue the character in the buffer */ 
608         if ((*me->serialTransmit)(c)) {
609             *(me->txBuffer + me->txHead) = c;
610             me->txHead++;
611             if (me->txHead >= me->txSize) {
612                 me->txHead = 0;
613             }
614         }
615         return;
616     }
617     /* queue the character if possible */
618     length = me->txTail - me->txHead;
619     if (length < 0) {
620         length += me->txSize;
621     }
622     switch (length) {
623         case 1 : {
624             return; /* buffer is full, an overrun ~ char was added previously */
625         }
626         case 2 : {
627             if (*(me->txBuffer + me->txHead) == '~') {
628                 return; /* don't keep stashing more ~ overruns */
629             }
630             c = '~'; /* overrun so advise in last character of buffer */
631             break;
632         }
633     }
634     /* queue either the character or an overrun ~ character */
635     *(me->txBuffer + me->txHead) = c;
636     me->txHead++;
637     if (me->txHead >= me->txSize) {
638         me->txHead = 0;
639     }
640 }
641 /*..........................................................................*/
642 void serialStr(SerialCommand *me, char *s) {
643     while (*s) {
644         serialChar(me, *s++);
645     }            
646 }
647 /*..........................................................................*/
648 void serialUnum(SerialCommand *me, uint32_t v) {
649     char b[10];
650     uint8_t i;
651     
652     if (v == 0) {
653         serialChar(me, '0');
654         return;
655     }
656     i = 0;
657     while (v > 0) {
658         b[i] = '0' + (v % 10);
659         v /= 10;
660         i++;
661     }
662     while (i > 0) {
663         i--;
664         serialChar(me, b[i]);
665     }
666 }
667 /*..........................................................................*/
668 void serialNum(SerialCommand *me, int32_t v) {
669     if (v < 0) {
670         serialChar(me, '-');
671         serialUnum(me, -v);
672         return;
673     }
674     serialUnum(me, v);    
675 }
676 /*..........................................................................*/
677 void serialDbl(SerialCommand *me, double number, uint8_t digits) {
678   /* Adapted from Arduino Print.cpp, Print::printFloat */
679
680   // Handle negative numbers
681   if (number < 0.0)
682   {
683      serialChar(me, '-');
684      number = -number;
685   }
686
687   // Round correctly so that print(1.999, 2) prints as "2.00"
688   double rounding = 0.5;
689   for (uint8_t i=0; i<digits; ++i)
690     rounding /= 10.0;
691
692   number += rounding;
693
694   // Extract the integer part of the number and print it
695   unsigned long int_part = (unsigned long)number;
696   double remainder = number - (double)int_part;
697   serialUnum(me, int_part);
698
699   // Print the decimal point, but only if there are digits beyond
700   if (digits > 0)
701     serialChar(me, '.');
702
703   // Extract digits from the remainder one at a time
704   while (digits-- > 0)
705   {
706     int toPrint;
707     remainder *= 10.0;
708     toPrint = (int)(remainder);
709     serialChar(me, toPrint + '0');
710     remainder -= toPrint;
711   }
712 }
713 /*..........................................................................*/
714 void serialNACK(SerialCommand *me, uint8_t unused) {\r
715     serialStr(me, "N\r\n");
716 }
717 /*..........................................................................*/
718 void serialACK(SerialCommand *me, uint8_t only) {
719     if (only) {
720         serialStr(me, "A\r\n");
721     } else {    
722         serialStr(me, "A"); /* and finish response string in ui function */
723     }
724 }
725 /*..........................................................................*/

UCC git Repository :: git.ucc.asn.au