/*
 * David Muller; Germán Alfaro
 * davehmuller@gmail.com; alfaro.germanevera@gmail.com
 * Thom Maughan, tm@mbari.org
 *
 * Optode driver for MFET and MpHOX
 */




#include "system.h"
#include "uartstdio.h"
#include "user_io.h"
#include "uart3.h"
#include "optode.h"

extern unsigned long Timer_10ms(bool reset);
// open / power on
//    time = 230 msec
//%
//!do sample
//    time = 1460 msec
//4831    856     232.810 93.450  26.906  26.489  28.780  37.056  8.276   548.4   628.1   -34.0
//#




extern unsigned char uart3_prn_buf[];

struct optodeDriver optode;   // struct defd in optode.h


 //MEASUREMENT     4831    856     O2Concentration[uM]     273.654 AirSaturation[%]        91.832  Temperature[Deg.C]      17.614  CalPhase[Deg]   28.336  TCPhase[Deg]    30.627  C1RPh[Deg]      38.676  C2RPh[Deg]      8.049   C1Amp[mV]        613.5   C2Amp[mV]       713.9   RawTemp[mV]     270.7

//
// %
// !do sample
// 4831    856     280.829 94.059  17.521  28.077  30.368  38.407  8.039   605.6   711.9   273.8
// #
// do sample
// 4831    856     280.826 94.062  17.523  28.076  30.367  38.403  8.037   605.6   711.5   273.7
// #
// do sample
// 4831    856     280.786 94.055  17.526  28.076  30.367  38.403  8.036   605.5   711.5   273.6
// #
//
// optode serial analyzer, bytes and times
// open
// RX: ŕ [0xe0]  time = 10 msec
// RX: % [0x25]  time = 220 msec
// TX: crlf
// RX: ! [0x21]  time = 10 msec
// TX: crlf do sample crlf
// RX: 4 [0x34]  time = 1820 msec
// RX: 8 [0x38]  time = 0 msec
// RX: 3 [0x33]  time = 0 msec
// RX: 1 [0x31]  time = 0 msec
// RX:      [0x09]  time = 0 msec
// RX: 8 [0x38]  time = 0 msec
// RX: 5 [0x35]  time = 10 msec
// RX: 6 [0x36]  time = 0 msec
// RX:      [0x09]  time = 0 msec


/*
 * ASCII characters.
 */
#define SPACE 0x20
#define OPENBRACE 0x7b
#define TAB 0x09

//char uart3_buff[OPTODE_BUFFER_SIZE];
//static unsigned char optode_buff[OPTODE_BUFFER_SIZE];   // optode driver output buf, size defd in optode.h

//SEE OPTODE MANUAL PAGES 17-19 TO LEARN MORE ABOUT RESPONSE TIMES
//AND ABOUT XON XOFF HANDSHAKES   XON XOFF needs to be disabled



// optode_init configures onchip uart3 peripheral, isr, and buffers.   Does not turn on 5v output
 void optode_init(void)
 {
     uart3_init();

     uart3_setup(OPTODE_BAUD, 16000000);

     uart3_echo_set(0);
 }




/*
 * Apply power to the Optode and prepare uart3 and driver state vars.
 * Assumes optode_init() is called.
 */
void optode_open(void)
{

    // on MPHOX, triple rs232 level translator is enabled for Optode J7, Console J6, and CTD J8 by console_enable/shutdown
    // on MFET, dual rs232 level translator is enabled for Optode J7 and Console J6 by console_enable/shutdown

#if BOARD_MFET >= 2  || BOARD_MPHOX >= 1  || BOARD_MSC == 2
	// turn on 5v supply on J7 MFET rev C, MPHOX rev A
    ROM_GPIOPinWrite(GPIO_PORTL_BASE, GPIO_PIN_2, 0xff);    // PL2, pin 106 is 5vON
#endif

    optode.state = 0;
    optode.bufIndx = 0;

}

void optode_close(void)
{

#if BOARD_MFET >= 2  || BOARD_MPHOX >= 1  || BOARD_MSC == 2
    // turn on 5v supply on J7 MFET rev C, MPHOX rev A
    ROM_GPIOPinWrite(GPIO_PORTL_BASE, GPIO_PIN_2, 0x00);    // PL2, pin 106 is 5vON
#endif

}

unsigned char optode_getc(void)
{
    unsigned char c;

    c = uart3_rx_byte();
    return c;
}

void optode_putc(unsigned char c)
{
    uart3_tx_byte(c);
}

int optode_rxBytesAvail(void)
{
    return(uart3_rx_bytes_avail());
}

void optode_flushRx(void)
{
    uart3_rx_bufr_flush();
}

void optode_flushTx(bool bDiscard)
{
    uart3_tx_bufr_flush(bDiscard);
}

int optode_write(const char *pcBuf, uint32_t ui32Len)
{
    return(uart3_write(pcBuf, ui32Len));
}

void optode_tx_str(void)        // was optode_buf_print
{
    uprintf("%s", uart3_prn_buf);
}

int optode_rx_buf_size(void)
{
    return(UART3_RX_BUFFER_SIZE);
}

int optode_tx_buf_size(void)
{
    return(UART3_TX_BUFFER_SIZE);
}

int optode_buf_write(unsigned char inChar)
{
    uart3_prn_buf[optode.bufIndx] = inChar;
    optode.bufIndx++;
    if(optode.bufIndx >= optode_tx_buf_size())
    {
        uprintf("\r\nDEBUG: error in optode driver bufIndx exceeds buf size, optode.c\r\n");

        optode.bufIndx = optode_tx_buf_size() - 1;
        if(sys_data.diag > 0) uprintf("\r\nDEBUG: error in optode driver bufIndx exceeds buf size, optode.c\r\n");
        wait_consoleTx();
        return(0);      // return of zero is buffer overflow error
    }
    return(1);
}



/*
 * optode_stateMachine
 *
 * pass in state=1 to start it, check for finish when returned state value is 0
 *
 */
int optode_stateMachine(int optodeState)
{
    int nextState = 0;
    unsigned int indx;
    unsigned int tick1, tick2;
    unsigned int wait_ms;
    unsigned char inChar;


    // example usage:
    //optodeState = 1;
    //optodeState = optode_statemachine(optodeState);     //pass in 1 to start the state machine, hereafter pass retVal
    //call statemachine repeatedly until optodeState == 0

    // optode_open
    // look for % within 600msec, then send crlf
    // look for !, then send do_sample and crlf
    // look for output terminated by crlf and # within 2000msec
    // trim crlf
    // closeOptde
    // look at retVal, if retVal=-1, that's a timeout, then terminate statemachine by sending it a 0 as arg

    // optode open (turn on power, wait 1msec, flush rx and tx bufs


    optode.state = optodeState;    // for debugging, optode.state not used at this time.

    switch(optodeState)
    {
        case 0:
            // do nothing, caller needs to pass in state=1 to start things
            return(0);

        case 1:
			optode_init();      // moved from init()
            optode_open();   // power on optode 5v supply, 10msec with flush

            ROM_SysCtlDelay(MILLISECOND*1);  // power stable for 1mec
            optode_flushRx();
            optode_flushTx(true);   // true says discard

            optode.old_tick10ms = Timer_10ms(false);
            //uprintf("\r\nold tick10ms = %u \r\n", optode.old_tick10ms);
            optode.state = 2;
            return(2);


        case 2:
            //look for % char response within 600msec, then send crlf
            // if longer than 600msec, then next state = 0;
            if( optode_rxBytesAvail() )
            {
                inChar = optode_getc();
                //uprintf( "[%02x]", inChar );   // DEBUG
                if(inChar == '%')       // 0x25
                {
                    optode.old_tick10ms = Timer_10ms(false);
                    optode_putc(0x0d);  // CR=13
                    optode_putc(0x0a);  // LF=10
                    //uprintf("[GO3]");

                    // TODO - add a time out here
                    inChar = optode_getc();
                    //uprintf( "(%02x)", inChar );
                    if(inChar == '!')
                    {
                        optode_write("do sample\r\n",12 );
                        optode.old_tick10ms = Timer_10ms(false);
                        optode.state = 4;
                        return(4);
                    }

                    //return(3);
                }
            }

            // statemachine resilience - call as slow as once per 1.5sec in fast_sample
            optode.new_tick10ms = Timer_10ms(false);
            //if((optode.new_tick10ms - optode.old_tick10ms) >= 60)  // 600msec = 60*10msec
            if((optode.new_tick10ms - optode.old_tick10ms) >= 160)  // 1600msec = 160*10msec
            {
                if(sys_data.diag > 0) uprintf("\r\nOptode power on did not receive %% char delimiter in 600msec - check optode connection or setup\r\n");
                sprintf((char *)&uart3_prn_buf[0], "Optode not responding with %% char after power up");
                wait_consoleTx();
                optode_close();
                optode.state = -1;
                return(0);
            }
            optode.state = 2;
            return(2);

            // case 3 is gone
        case 3:
            // look for !, then send do_sample and crlf
            if( optode_rxBytesAvail() )
            {
                inChar = optode_getc();
                //uprintf( "(%02x)", inChar );
                if(inChar == '!')
                {
                    optode_write("do sample\r\n",12 );
                    optode.old_tick10ms = Timer_10ms(false);
                    optode.state = 4;
                    return(4);
                }
            }

            optode.new_tick10ms = Timer_10ms(false);
            if((optode.new_tick10ms - optode.old_tick10ms) >= 200)  //200 is 2sec, was 500msec = 50*10msec
            {
                if(sys_data.diag > 0) uprintf("\r\nOptode power on did not receive ! char delimiter in 2000msec - check optode connection or setup\r\n");
                sprintf((char *)&uart3_prn_buf[0], "Optode not responding with ! char after crlf");
                wait_consoleTx();
                optode_close();  // Close Optode, power off
                optode.state = -1;
                return(0);
            }
            optode.state = 3;
            return(3);

        case 4:
            // look for output terminated by crlf and # within 2000msec
            // trim crlf
            // optode_close
            // nextState = 0

            if( optode_rxBytesAvail() )      // if 1st char is available, get the entire do sample response string upto 1200msec
            {
                optode.old_tick10ms = Timer_10ms(false);  // reset timer
                while(1)       // expect no more than 100 chars
                {
#define WAIT10MSTICKVAL  5
                    wait_ms = optode_waitRx(WAIT10MSTICKVAL);   // 50msec ought to be enough
                    if(wait_ms == WAIT10MSTICKVAL)
                    {
                        if(sys_data.diag > 0) uprintf("\r\nDEBUG: inter-character delay exceeded 50msec receiving do_sample string\r\n");
                        sprintf((char *)&uart3_prn_buf[0], "Optode Err ST4: inter-character delay exceeded 50msec receiving do_sample string");
                        wait_consoleTx();
                        optode_close();  // Close Optode, power off
                        return(0);
                    }
                    // check timeout (all chars should be received within 120msec (10msec per char * 91 chars + crlf + #) + pad)
                    inChar = optode_getc();

                    // 4831    856     280.786 94.055  17.526  28.076  30.367  38.403  8.036   605.5   711.5   273.6
                    // #
                    if(inChar == 0x0d)
                    {
                        inChar = 0;     //zero terminate optode.buf
                    }
                    if(inChar == '#')  // end of data
                    {
                        optode_close();  // Close Optode, power off
                        optode.state = 0;       // success
                        return(0);
                        //break;  // from while(1), do sample is done
                    }

                    // put the data into optode_buff[indx]
                    //optode.new_tick10ms = Timer_10ms(false);  // DEBUG
                    //tick2 = optode.new_tick10ms - optode.old_tick10ms;  // DEBUG
                    //uprintf(" %c", inChar );  // DEBUG
                    //uprintf("[%02x](%ums) ", inChar, tick2); //DEBUG

                    if(!optode_buf_write(inChar))
                    {
                        optode_close();  // Close NanoFET, power off
                        optode.state = -1;
                        return(0);
                    }
#ifdef OLDCODE
                    uart3_prn_buf[optode.bufIndx] = inChar;
                    optode.bufIndx++;
                    if(optode.bufIndx >= optode_buf_size())      //OPTODE_BUFFER_SIZE)
                    {
                        optode.bufIndx = optode_buf_size() - 1;
                        if(sys_data.diag > 0) uprintf("\r\nDEBUG: error in optode driver bufIndx exceeds buf size, optode.c\r\n");
                        wait_consoleTx();
                        optode_close();  // Close Optode, power off
                        optode.state = -1;
                        return(0);
                    }
#endif

                }

            } // if rxByte

            optode.new_tick10ms = Timer_10ms(false);
            if((optode.new_tick10ms - optode.old_tick10ms) >= 500)  // 5000msec = 500*10msec
            {
                if(sys_data.diag > 0) uprintf("\r\nOptode power on did not receive sample string after do sample - check optode connection or setup\r\n");
                sprintf((char *)&uart3_prn_buf[0], "Optode not responding with sample string after do sample");
                optode_close();  // Close Optode, power off
                wait_consoleTx();
                optode.state = -1;
                return(0);
            }
            return(4);


        default:
            optode_close();
            return(0);
            //break;


    }


}



uint32_t optode_qa_test(void)
{
    //uint32_t timeout;
    //uint32_t ticks, tickDelta;

    int optodeState;


    //start the optode driver by passing 1, when complete, the statemachine returns 0
    optodeState = 1;
    optodeState = optode_stateMachine(optodeState);     //pass in 1 to start the state machine, hereafter pass retVal

    //tickDelta = Timer_1ms(false) - tickDelta;
    //ticks = (Timer_1ms(false) - tickStart);
    //uprintf("\r\nOptode St8Ma = %5u,   %5u", tickDelta, ticks);
    //tickDelta = ticks;


    while(1)
    {
        if(optodeState == 0)            // makes sure optode statemachine is finished
        {
            if(optode.state == -1)
                return(0);              // fail
            else
                return(1);              // pass
            break;
        }
        optodeState = optode_stateMachine(optodeState);

    }
    //tickDelta = Timer_1ms(false) - tickDelta;
    //ticks = (Timer_1ms(false) - tickStart);
    //if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nOptode       = %5u,   %5u", tickDelta, ticks);
    //tickDelta = ticks;

    return(0);   // fail, never gets here
}



void optode_serial_analyzer(void)
{
    int nextState = 0;
    unsigned int indx;
    unsigned int tick1, tick2;
    unsigned char inChar;

    uprintf("optode serial analyzer, bytes and times\r\n");
#ifdef NOCODE
    set passkey(1)
    set mode(smart sensor terminal)
    set enable polled mode(yes)
    save

    To set the output mode:
    do sample
    set passkey(1)
    set enable text(no)
    save
    do sample

    // ctrl-q and ctrl-s will be sent from optode and conductivity, this can be removed by changing flow control to none
    set passkey(1000)
    #
    set Flow Control(none)
    #
    save

    get flow control
    Flow Control    4831    856     None
#endif
	optode_init();      // moved from init()
    while(1)
    {
        uprintf("open\r\n");
        optode_open();   // power on optode 5v supply
        optode.old_tick10ms = Timer_10ms(false);
        while(1)
        {
        // look for !, then send do_sample and crlf
            if( optode_rxBytesAvail() )
            {
               // [0x11]  time = 220 msec
               //% [0x25]  time = 0 msec
               //! [0x21]  time = 10 msec
               // [0x11]  time = 940 msec


#ifdef NOCODE
                optode serial analyzer, bytes and times
                open
                RX:  [0x11]  time = 230 msec
                RX: % [0x25]  time = 0 msec
                TX: crlf
                RX: ! [0x21]  time = 10 msec
                TX: crlf do sample crlf
                RX:  [0x11]  time = 940 msec
                RX:  [0x13]  time = 0 msec
                RX:  [0x11]  time = 940 msec
                RX: 4 [0x34]  time = 0 msec
                RX: 8 [0x38]  time = 0 msec
                RX: 3 [0x33]  time = 0 msec
                RX: 1 [0x31]  time = 10 msec

#endif

                optode.new_tick10ms = Timer_10ms(false);
                inChar = optode_getc();
                uprintf("RX: %c [0x%02x] ", inChar, inChar );
                tick1 = (optode.new_tick10ms - optode.old_tick10ms)*10;
                uprintf(" time = %u msec\r\n", tick1);
                optode.old_tick10ms = Timer_10ms(false);

                if(inChar == '%')
                {
                    uprintf("TX: crlf \r\n");
                    optode_putc(0x0d);  // CR=13
                    optode_putc(0x0a);  // LF=10
                }
                if(inChar == '!')
                {
                    uprintf("TX: crlf do sample crlf \r\n");
                    optode_putc(0x0d);  // CR=13
                    optode_putc(0x0a);  // LF=10
                    optode_write("do sample\r\n",12 );
                }
            }
            if(UARTRxBytesAvail())
            {
                inChar = UARTgetc();


                if(inChar == 24) // ctrl-x to exit loop
                {
                    optode_close();
                    ms_delay(500);
                    break;
                }


            }

        } // while(1)

        if(UARTRxBytesAvail())
        {
            inChar = UARTgetc();

            if(inChar == 24) // ctrl-x to exit loop
                break;

        }

    }


}



/*
 * optode_poll() tells the Optode to "do_sample."  Data is received
 * via the UART3 interrupt handler.
 */
void optode_poll(void)
{
	//send "do_sample\r\n" to Optode

    //optode_write(" ",1); //added space before do sample for 4835 optodes so only have to do 1 "do sample" 5/5/17 - TW
    //ROM_SysCtlDelay(100*MILLISECOND);
    optode_write("do sample\r\n",12 );
    ROM_SysCtlDelay(1000*MILLISECOND); //add 800 to try to limit "SYNTAX ERROR"
    //optode_flushRx();
    //ROM_SysCtlDelay(100*MILLISECOND);
 	//optode_write("do_sample\r\n", 12);
 	//SEE OPTODE MANUAL PAGES 17-19 TO LEARN MORE ABOUT RESPONSE TIMES
	//AND ABOUT XON XOFF HANDSHAKES
}






void optode_comm_test(void)
{
    unsigned char inChar;


    // Tested with Aanderaa Oxygen 5730 https://www.aanderaa.com/media/pdfs/d419_aanderaa_oxygen_sensor_5730.pdf
    uprintf("\r\n Baud = %u\r\n\r\n", OPTODE_BAUD);
    uprintf("\r\nNote: smart sensor mode, disable data descriptor text, set flow control to none \r\n\r\n");
    uprintf("\r\n\r\nCommand procedure to setup Optode for numeric output:\r\n\r\n");
    uprintf("set passkey(1)\r\n");
    uprintf("set mode(smart sensor terminal)\r\n");
    uprintf("set enable polled mode(yes)\r\n");
    uprintf("save\r\n");
    uprintf("\r\n");
    uprintf("set passkey(1)\r\n");
    uprintf("set enable text(no)\r\n");
    uprintf("save\r\n");
    uprintf("\r\n");
    uprintf("set passkey(1000)\r\n");       // response: #
    uprintf("set flow control(none)\r\n");  // response: #
    uprintf("save\r\n");
    uprintf("\r\n");

    uprintf("do sample\r\n");
    uprintf("\r\nThis should return a sample: \t4831\t856\t286.196\t94.284\t16.728\t28.200\t30.491\t38.573\t8.082\t621.1\t720.0\t300.0\t\r\n\r\n");
    //uprintf("                               \t5860\t29\t-0.003\t16.892\t0.009\t998.802\t1472.435\t\r\n");
    uprintf("\r\n\r\n*Press CTRL-X to stop communicating with the Optode.*\r\n\r\n");

    //set up the optode
	optode_init();      // moved from init()
    optode_open();               // power on optode
    ROM_SysCtlDelay(500);       //500 is a guess
    //Optode_UARTwrite("do sample\r\n",12 );
    //ROM_SysCtlDelay(MILLISECOND*2000);

    while(1)
    {
        //if Optode sent anything, print it out on UART1 (the desktop)
        if( optode_rxBytesAvail() )      // Optode
        {
            uprintf( "%c", optode_getc() );
        }

        //if user types anything, send it to the Optode--exit on CTRL-X
        if(UARTRxBytesAvail())
        {
            inChar = UARTgetc();

            if(inChar == 13)     // If user hit carriage return, add a line feed
            {
                optode_putc(13);  // CR
                optode_putc(10);     // LF
            }
            else if(inChar == 24)
            {
                break;
            }
            else
            {
                optode_putc(inChar);
            }
        }
    }

    optode_close();  // Close up the Optode
}


/*
 * Parse the data returned by the Optode and put it in the output form we'd like (tab delimited etc.).
 * While parsing, move the data out of poll_Buffers.optodeBuffer and into the instrument buffer which
 * will have all of our measurements.
 */
// optode data looks like:
//StartupInfo     5730    1565    Mode    AADI Smart Sensor Terminal Protocol FW2 RS232 Protocol Version  3       Config Version  12
//MEASUREMENT     5730    1565    O2Concentration(uM)     215.385 AirSaturation(%)        99.342  Temperature(Deg.C)      24.506  CalPhase(Deg)   29.867  TCPhase(Deg)    29.867  C1RPh(Deg)      37.565  C2RPh(Deg)      7.698   C1Amp(mV)       753.6   C2Amp(mV)       737.3   RawTemp(mV)     96.2

//void parseOptodeData(void)
int optode_parseData(void)
{

    // REWRITE OR GET RID OF THIS
#ifdef NOCODE
	//bytesRead holds the # of bytes received from Optode
	int bytesRead;
	int i;
	int bytes2delete = 0;

	bytesRead = uart3_rxBytesAvail();

	// DEBUG code
	//uprintf("optode bytesRead = %d\r\n", bytesRead);
	//uprintf("%s", uart3_buff);

	if(bytesRead)
	{
		uart3_gets(optode_buff, bytesRead);

		// look for first "integer" in Optode string. Will delete until then for junk characters. //YT 3/23/2015
		for(i = bytesRead; i >= 0; --i)
		{
			//uprintf("%c",optode_buff[i]);
			if(optode_buff[i] >47 & optode_buff[i] < 58)
			{
				bytes2delete = i;
			}
		}
		// End YT

		for(i = 0; i < bytesRead-bytes2delete; ++i)
		{
			optode_buff[i] = optode_buff[i+bytes2delete];	// Get rid of first few characters
		}
	}
	else
	{
	    strcpy(optode_buff, "NAN\tNAN\tNAN\tNAN\tNAN\tNAN\tNAN\tNAN\tNAN\tNAN\tNAN\tNAN\t");
	}
#endif

	return(1);
}


uint32_t optode_waitRx(uint32_t maxWait_10msTick)
{
    uint32_t start_10msTick;
    //uint32_t end_10msTick;
    uint32_t now_10msTick;

    // maxWaitMsec = 0, wait indefinitely
    // return wait in msec
    start_10msTick = Timer_10ms(false);
    while(1)
    {
        if( optode_rxBytesAvail() )
        {
            now_10msTick = Timer_10ms(false);
            return(now_10msTick-start_10msTick);
        }
        ROM_SysCtlDelay(MICROSECOND*500);       // throttle the loop
        now_10msTick = Timer_10ms(false);
        if(maxWait_10msTick > 0)
        {
            if((now_10msTick - start_10msTick) >= maxWait_10msTick)
            {
                return(maxWait_10msTick);
            }
        }
        if(start_10msTick > now_10msTick)
        {
            uprintf("DEBUG: optode_waitRx 10msTick timer rolled over\r\n");
            return(0);
        }
    }

}









#ifdef TIMINGFORSIMULATOR
optode serial analyzer, bytes and times
open
RX: % [0x25]  time = 230 msec
TX: crlf
RX: ! [0x21]  time = 10 msec
TX: crlf do sample crlf
RX: 5 [0x35]  time = 1460 msec
RX: 8 [0x38]  time = 0 msec
RX: 6 [0x36]  time = 0 msec
RX: 0 [0x30]  time = 0 msec
RX:      [0x09]  time = 0 msec
RX: 2 [0x32]  time = 0 msec
RX: 9 [0x39]  time = 10 msec
RX:      [0x09]  time = 0 msec
RX: - [0x2d]  time = 0 msec
RX: 0 [0x30]  time = 0 msec
RX: . [0x2e]  time = 0 msec
RX: 0 [0x30]  time = 0 msec
RX: 0 [0x30]  time = 0 msec
RX: 5 [0x35]  time = 0 msec
RX:      [0x09]  time = 0 msec
RX: 1 [0x31]  time = 0 msec
RX: 6 [0x36]  time = 10 msec
RX: . [0x2e]  time = 0 msec
RX: 3 [0x33]  time = 0 msec
RX: 7 [0x37]  time = 0 msec
RX: 3 [0x33]  time = 0 msec
RX:      [0x09]  time = 0 msec
RX: 0 [0x30]  time = 0 msec
RX: . [0x2e]  time = 0 msec
RX: 0 [0x30]  time = 0 msec
RX: 1 [0x31]  time = 0 msec
RX: 0 [0x30]  time = 10 msec
RX:      [0x09]  time = 0 msec
RX: 9 [0x39]  time = 0 msec
RX: 9 [0x39]  time = 0 msec
RX: 8 [0x38]  time = 0 msec
RX: . [0x2e]  time = 0 msec
RX: 8 [0x38]  time = 0 msec
RX: 9 [0x39]  time = 0 msec
RX: 0 [0x30]  time = 0 msec
RX:      [0x09]  time = 0 msec
RX: 1 [0x31]  time = 0 msec
RX: 4 [0x34]  time = 0 msec
RX: 7 [0x37]  time = 0 msec
RX: 0 [0x30]  time = 0 msec
RX: . [0x2e]  time = 0 msec
RX: 6 [0x36]  time = 0 msec
RX: 9 [0x39]  time = 0 msec
RX: 2 [0x32]  time = 0 msec
 [0x0d]  time = 10 msec
RX:
 [0x0a]  time = 0 msec
RX: # [0x23]  time = 0 msec
 [0x0d]  time = 0 msec
RX:
 [0x0a]  time = 0 msec
#endif
