• Strange behavior when communicating with Arduino using Tcl/Tk

    From Info Libera@21:1/5 to All on Wed Aug 25 13:07:59 2021
    Hi All,
    My Tcl/Tk code for serial communication with Arduino is working but with a strange behavior.
    I send a message through the serial port that calls a function in arduino and returns a message. The problem is that, at the beginning, I need to send the command 2 or 3 times before arduino start to respond. After the second or third attempt, the
    arduino starts to respond normally.

    The Tcl/Tk code is:

    set serial_device [exec ls {*}[glob /dev/ttyACM*]]
    set baud 9600
    set parity n
    set data_bit 8
    set stop_bit 1
    set id_serial_port [open $serial_device r+]
    chan configure $id_serial_port -mode "$baud,$parity,$data_bit,$stop_bit"
    chan configure $id_serial_port -buffering line
    chan configure $id_serial_port -blocking 0
    chan configure $id_serial_port -encoding ascii

    #To set the procedure (receiveReadout) to be called when serial port is readable
    chan event $id_serial_port readable [list [self] receiveReadout $id_serial_port]

    And the procedure receiveReadout
    proc receiveReadout { id_serial_port } {

    if { [eof $id_serial_port ] } {
    puts "Closing $id_serial_port"
    catch { close $id_serial_port }
    return
    }

    #To read the response from Arduino
    set serial_return [chan gets $id_serial_port]
    }

    And send the command to check the connection:

    chan puts $id_serial_port checkConnection

    And the Arduino code is: (adapted from http://www.gammon.com.au/forum/?id=11425)
    void setup() {
    Serial.begin(9600);
    Serial.flush();
    }

    void processIncomingByte (const byte inByte) {
    static char input_line[MAX_INPUT];
    static unsigned int input_pos = 0;

    switch (inByte)
    {
    case '\n': // end of text
    input_line[input_pos] = 0; // terminating null byte
    // terminator reached! process input_line here ...
    process_data (input_line);

    // reset buffer for next time
    input_pos = 0;
    break;

    case '\r': // discard carriage return
    break;

    default:
    // keep adding if not full ... allow for terminating null byte
    if (input_pos < (MAX_INPUT - 1))
    input_line[input_pos++] = inByte;
    break;
    } // end of switch
    }// end of processIncomingByte

    //Process_data to process incoming serial data after a terminator received
    void process_data (char *data) {
    cmd = strtok(data, ";");
    if ( (strcmp(cmd, "checkConnection") == 0) || (strcmp(cmd, "checkconnection") == 0) ) {
    transaction_id = strtok(NULL, ";");
    checkConnection( transaction_id );
    } else {
    Serial.print("#unknown command: ");
    Serial.println(cmd);
    } // end of if
    }

    //Send message "ACK" to to confirm the connection with the Arduino
    void checkConnection(char *transaction_id ) {
    Serial.print("ACK;");
    Serial.println(transaction_id);
    }

    void loop() {
    // if serial data available, process it
    while (Serial.available () > 0)
    processIncomingByte (Serial.read());
    }


    But as I said, to start receiving Arduino messages I need to send this command 2 or 3 times.

    Please, any tips on how to resolve this?
    Thank you,
    Info Libera

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Christian Werner@21:1/5 to All on Wed Aug 25 16:10:42 2021
    Most likely you're seeing the delayed startup of the Arduino. When you perform the open of the serial device per Tcl the Arduino is first reset, then waits about 2 seconds in its boot loader, then starts up and executes your program from its flash memory.
    On the Tcl side use a "after 2000" right after the open, then proceed to normal communication. This might fix your issue.

    HTH,
    Christian

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Heller@21:1/5 to Christian Werner on Wed Aug 25 18:37:44 2021
    Alternitively, use a "handshake protocol": my Bike Computer program sends a startup message:

    Serial.println("BikeComputer 0.0");

    in setup(), and then
    does:
    Serial.print(">>");
    Serial.flush();

    And after processing a command, it does:
    Serial.print(">>");
    Serial.flush();

    The Tcl code waits for the prompt before sending a command.

    That is, the Tcl code waits for the Arduino to get itself up to speed before asking it to do things.



    At Wed, 25 Aug 2021 16:10:42 -0700 (PDT) Christian Werner <undroidwish@gmail.com> wrote:


    Most likely you're seeing the delayed startup of the Arduino. When you perform the open of the serial device per Tcl the Arduino is first reset, then waits about 2 seconds in its boot loader, then starts up and executes your program from its flash
    memory. On the Tcl side use a "after 2000" right after the open, then proceed to normal communication. This might fix your issue.

    HTH,
    Christian



    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services
    heller@deepsoft.com -- Webhosting Services

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Heller@21:1/5 to Info Libera on Wed Aug 25 18:26:51 2021
    At Wed, 25 Aug 2021 13:07:59 -0700 (PDT) Info Libera <liberinforma@gmail.com> wrote:


    Hi All,
    My Tcl/Tk code for serial communication with Arduino is working but with a strange behavior.
    I send a message through the serial port that calls a function in arduino and returns a message. The problem is that, at the beginning, I need to send the command 2 or 3 times before arduino start to respond. After the second or third attempt, the
    arduino starts to respond normally.

    The Tcl/Tk code is:

    set serial_device [exec ls {*}[glob /dev/ttyACM*]]
    set baud 9600
    set parity n
    set data_bit 8
    set stop_bit 1
    set id_serial_port [open $serial_device r+]
    chan configure $id_serial_port -mode "$baud,$parity,$data_bit,$stop_bit" chan configure $id_serial_port -buffering line
    chan configure $id_serial_port -blocking 0
    chan configure $id_serial_port -encoding ascii

    I have an Arduino-based 'Bike Computer' (speedometer, with a RTC with a battery-backed NVRam) and my program for uploading the NVRam to my computer is much the same, but I have this:

    chan configure $id_serial_port -translation {crlf cr}


    #To set the procedure (receiveReadout) to be called when serial port is readable
    chan event $id_serial_port readable [list [self] receiveReadout $id_serial_port]

    And the procedure receiveReadout
    proc receiveReadout { id_serial_port } {

    if { [eof $id_serial_port ] } {
    puts "Closing $id_serial_port"
    catch { close $id_serial_port }
    return
    }

    #To read the response from Arduino
    set serial_return [chan gets $id_serial_port]
    }

    And send the command to check the connection:

    chan puts $id_serial_port checkConnection

    And the Arduino code is: (adapted from http://www.gammon.com.au/forum/?id=11425)
    void setup() {
    Serial.begin(9600);
    Serial.flush();
    }

    void processIncomingByte (const byte inByte) {
    static char input_line[MAX_INPUT];
    static unsigned int input_pos = 0;

    switch (inByte)
    {
    case '\n': // end of text
    input_line[input_pos] = 0; // terminating null byte
    // terminator reached! process input_line here ...
    process_data (input_line);

    // reset buffer for next time
    input_pos = 0;
    break;

    case '\r': // discard carriage return
    break;

    default:
    // keep adding if not full ... allow for terminating null byte
    if (input_pos < (MAX_INPUT - 1))
    input_line[input_pos++] = inByte;
    break;
    } // end of switch
    }// end of processIncomingByte

    //Process_data to process incoming serial data after a terminator received void process_data (char *data) {
    cmd = strtok(data, ";");
    if ( (strcmp(cmd, "checkConnection") == 0) || (strcmp(cmd, "checkconnection") == 0) ) {
    transaction_id = strtok(NULL, ";");
    checkConnection( transaction_id );
    } else {
    Serial.print("#unknown command: ");
    Serial.println(cmd);
    } // end of if
    }

    //Send message "ACK" to to confirm the connection with the Arduino
    void checkConnection(char *transaction_id ) {
    Serial.print("ACK;");
    Serial.println(transaction_id);
    }

    void loop() {
    // if serial data available, process it
    while (Serial.available () > 0)
    processIncomingByte (Serial.read());
    }


    But as I said, to start receiving Arduino messages I need to send this command 2 or 3 times.

    Please, any tips on how to resolve this?
    Thank you,
    Info Libera



    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services
    heller@deepsoft.com -- Webhosting Services

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ralf Fassel@21:1/5 to All on Thu Aug 26 11:27:25 2021
    * Info Libera <liberinforma@gmail.com>
    | The Tcl/Tk code is:

    | set serial_device [exec ls {*}[glob /dev/ttyACM*]]

    What is this 'exec ls' supposed to achieve?

    # use first serial device available or let glob err out if there are none
    set serial_device [lindex [glob /dev/ttyACM*] 0]

    | chan puts $id_serial_port checkConnection

    Note that you don't send any ";" here...

    | //Process_data to process incoming serial data after a terminator received
    | void process_data (char *data) {
    | cmd = strtok(data, ";");

    ...yet you look for one here. If there is no ";" in the line received, strtok() will return NULL, and hence the strcmp() in the next line has undefined behaviour (crash, reboot arduino, send the ACK, whatever).

    | if ( (strcmp(cmd, "checkConnection") == 0) || (strcmp(cmd, "checkconnection") == 0) ) {
    | transaction_id = strtok(NULL, ";");
    | checkConnection( transaction_id );

    HTH
    R'

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Info Libera@21:1/5 to All on Fri Aug 27 09:44:58 2021
    Em quarta-feira, 25 de agosto de 2021 às 20:10:44 UTC-3, Christian Werner escreveu:
    Most likely you're seeing the delayed startup of the Arduino. When you perform the open of the serial device per Tcl the Arduino is first reset, then waits about 2 seconds in its boot loader, then starts up and executes your program from its flash
    memory. On the Tcl side use a "after 2000" right after the open, then proceed to normal communication. This might fix your issue.

    HTH,
    Christian

    Dear Christian,

    I found that this strategy works. :-)
    This was the first program I open and then immediately send a command.
    That's why I never observed this behavior.

    Thanks for your attention,
    Info Libera

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Info Libera@21:1/5 to All on Fri Aug 27 09:53:44 2021
    Em quarta-feira, 25 de agosto de 2021 às 20:37:52 UTC-3, Robert Heller escreveu:
    Alternitively, use a "handshake protocol": my Bike Computer program sends a startup message:

    Serial.println("BikeComputer 0.0");

    in setup(), and then
    does:
    Serial.print(">>");
    Serial.flush();

    And after processing a command, it does:
    Serial.print(">>");
    Serial.flush();

    The Tcl code waits for the prompt before sending a command.

    That is, the Tcl code waits for the Arduino to get itself up to speed before asking it to do things.
    At Wed, 25 Aug 2021 16:10:42 -0700 (PDT) Christian Werner <undro...@gmail.com> wrote:




    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services hel...@deepsoft.com -- Webhosting Services

    Dear Robert,

    I tested the tip to change the Serial.println to

    Serial.print
    Serial.flush

    And varying the encoding:
    lf lf
    crlf lf
    lf crlf
    crlf crlf

    But I realized the need to use Serial.println for Tcl to be able to read the response with "chan gets $id_serial_port".

    Thanks for your comments. They helped me learn a little more about Arduino. :-) Info Libera

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Info Libera@21:1/5 to All on Fri Aug 27 10:08:38 2021
    Em quinta-feira, 26 de agosto de 2021 às 06:27:28 UTC-3, Ralf Fassel escreveu:
    * Info Libera <liberi...@gmail.com>
    | The Tcl/Tk code is:

    | set serial_device [exec ls {*}[glob /dev/ttyACM*]]
    What is this 'exec ls' supposed to achieve?

    Dear Ralf,

    I used exec ls to list the device file of type tty/ACM corresponding to the Arduino board.

    It was a way to automate the device location. But it works if only the board Arduino creates an ACM serial device.


    # use first serial device available or let glob err out if there are none set serial_device [lindex [glob /dev/ttyACM*] 0]

    | chan puts $id_serial_port checkConnection

    Note that you don't send any ";" here...
    | //Process_data to process incoming serial data after a terminator received | void process_data (char *data) {
    | cmd = strtok(data, ";");
    ...yet you look for one here. If there is no ";" in the line received, strtok() will return NULL, and hence the strcmp() in the next line has undefined behaviour (crash, reboot arduino, send the ACK, whatever).
    | if ( (strcmp(cmd, "checkConnection") == 0) || (strcmp(cmd, "checkconnection") == 0) ) {
    | transaction_id = strtok(NULL, ";");
    | checkConnection( transaction_id );
    HTH

    R'
    Sorry for having summarized the text of the message too much so the text doesn't get too long.
    I actually attach a transaction identifier obtained from the "clock milliseconds" command to identify who requested the command.
    Because several types of commands are sent and each one has an ID.

    Some command examples:
    SET;D2;0;6 - set the digital pin “D2” to “0” (LOW) for the request with ID=6
    SET;D3;255;10 - set the digital pin “D3” to the value “255” in PWM mode; for requester with ID=10
    GET;A0;8 - returns the reading of the analog pin "A0" to the requester with ID=8

    So I add an ID to the command checkConnection;12345...

    The most complete Arduino code, used in another project, is available here: http://www.c2o.pro.br/hackaguas/ar01s25.html#idm9504

    Thanks for your attention,
    Info Libera

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Info Libera on Fri Aug 27 18:10:14 2021
    Info Libera <liberinforma@gmail.com> wrote:
    Em quinta-feira, 26 de agosto de 2021 s 06:27:28 UTC-3, Ralf Fassel escreveu:
    * Info Libera <liberi...@gmail.com>
    | The Tcl/Tk code is:

    | set serial_device [exec ls {*}[glob /dev/ttyACM*]]
    What is this 'exec ls' supposed to achieve?

    Dear Ralf,

    I used exec ls to list the device file of type tty/ACM corresponding
    to the Arduino board.

    It was a way to automate the device location. But it works if only
    the board Arduino creates an ACM serial device.

    Yes, and you used Tcl's built in glob to first find the device files,
    passing the resulting filenames as parameters to ls, only to get them
    back from ls as ls's output.

    [glob /dev/ttyACM*] would have done the exact same as your circuitus
    exec ls call, without needing to fork an ls process to list filenames
    that ls was given by Tcl's built in glob.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Info Libera@21:1/5 to All on Tue Aug 31 06:15:26 2021
    Em sexta-feira, 27 de agosto de 2021 às 15:10:18 UTC-3, Rich escreveu:
    Info Libera <liberi...@gmail.com> wrote:
    Em quinta-feira, 26 de agosto de 2021 às 06:27:28 UTC-3, Ralf Fassel escreveu:
    * Info Libera <liberi...@gmail.com>
    | The Tcl/Tk code is:

    | set serial_device [exec ls {*}[glob /dev/ttyACM*]]
    What is this 'exec ls' supposed to achieve?

    Dear Ralf,

    I used exec ls to list the device file of type tty/ACM corresponding
    to the Arduino board.

    It was a way to automate the device location. But it works if only
    the board Arduino creates an ACM serial device.
    Yes, and you used Tcl's built in glob to first find the device files, passing the resulting filenames as parameters to ls, only to get them
    back from ls as ls's output.

    [glob /dev/ttyACM*] would have done the exact same as your circuitus
    exec ls call, without needing to fork an ls process to list filenames
    that ls was given by Tcl's built in glob.

    Dear Ralf,

    Really I don't need to use "ls".

    Thanks for the tip.

    Combining a number of improvements like this can make code more efficient. :-)

    Best Regards,
    Markos

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)