Shop OBEX P1 Docs P2 Docs Learn Events
BS2 not listening to a linux program — Parallax Forums

BS2 not listening to a linux program

jb9jb9 Posts: 7
edited 2012-12-12 06:23 in BASIC Stamp
I'm trying to interface a BS2 to a computer and use a C program to talk to the BS2, but when I send data from a C program over serial, the BS2 doesn't see/process it.

I've tried every combination of communication I can think of...
1) C program to/from another PC over serial
2) minicom on the PC to/from the BS2
3) C program FROM the BS2

All of those work. The only combination that doesn't work is data from a C program on the PC to the BS2.

When I say "PC" here, I've tried all of these combinations with a raspberry pi as well as linux running in VirtualBox on a win7 PC, with the same results on both (minicom works, C program doesn't).

I've stripped down the code to the basics and I'll post it below. Any thoughts on something I could be overlooking?

thanks,
JB

BS2 program:
sData0   VAR     Byte

START:
DO
  SERIN 16, 16468, [sData0]
  ' Do something interesting to indicate that I saw data
LOOP

C Program (adapted from 'Serial Programming HOWTO' example):
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BAUDRATE B9600
#define MODEMDEVICE "/dev/ttyS0"
#define _POSIX_SOURCE 1 /* POSIX compliant source */

main(int argc, char **argv)
{
  int fd,c, res;
  struct termios oldtio,newtio;
  char buf[255];
  char readbuf[255];
/*
  Open modem device for reading and writing and not as controlling tty
  because we don't want to get killed if linenoise sends CTRL-C.
*/
 fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY);
 if (fd <0) {perror(MODEMDEVICE); exit(-1); }

 tcgetattr(fd,&oldtio); /* save current serial port settings */
 bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */

/*
  BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
  CRTSCTS : output hardware flow control (only used if the cable has
            all necessary lines. See sect. 7 of Serial-HOWTO)
  CS8     : 8n1 (8bit,no parity,1 stopbit)
  CLOCAL  : local connection, no modem contol
  CREAD   : enable receiving characters
*/
// newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
 newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
/*
  IGNPAR  : ignore bytes with parity errors
  ICRNL   : map CR to NL (otherwise a CR input on the other computer
            will not terminate input)
  otherwise make device raw (no other input processing)
*/
 newtio.c_iflag = IGNPAR | ICRNL;

/*
 Raw output.
*/
 newtio.c_oflag = 0;

/*
  ICANON  : enable canonical input
  disable all echo functionality, and don't send signals to calling program
*/
 newtio.c_lflag = ICANON;

/*
  initialize all control characters
  default values can be found in /usr/include/termios.h, and are given
  in the comments, but we don't need them here
*/
 newtio.c_cc[VINTR]    = 0;     /* Ctrl-c */
 newtio.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
 newtio.c_cc[VERASE]   = 0;     /* del */
 newtio.c_cc[VKILL]    = 0;     /* @ */
 newtio.c_cc[VEOF]     = 4;     /* Ctrl-d */
 newtio.c_cc[VTIME]    = 0;     /* inter-character timer unused */
 newtio.c_cc[VMIN]     = 1;     /* blocking read until 1 character arrives */
 newtio.c_cc[VSWTC]    = 0;     /* '\0' */
 newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */
 newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
 newtio.c_cc[VSUSP]    = 0;     /* Ctrl-z */
 newtio.c_cc[VEOL]     = 0;     /* '\0' */
 newtio.c_cc[VREPRINT] = 0;     /* Ctrl-r */
 newtio.c_cc[VDISCARD] = 0;     /* Ctrl-u */
 newtio.c_cc[VWERASE]  = 0;     /* Ctrl-w */
 newtio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
 newtio.c_cc[VEOL2]    = 0;     /* '\0' */

/*
  now clean the modem line and activate the settings for the port
*/
 tcflush(fd, TCIFLUSH);
 tcsetattr(fd,TCSANOW,&newtio);

  sprintf(buf," CMD0 %c%c ",0x0d,0x0A);
  res = write(fd,buf,strlen(buf));
  printf("wrote:%s:%d:%d:\n", buf, strlen(buf),res);

 /* restore the old port settings */
 tcsetattr(fd,TCSANOW,&oldtio);
}


Comments

  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2012-12-10 13:43
    I am guessing that isn't all of the code for the BASIC Stamp side of things? What are you doing on receipt of the character? Assuming you are using 9600, 8, N, 1 on the PC side you should be able to get the characters on the BASIC Stamp side, however one thing that will differ from your other connection exampls is flow control. When you're connected to another PC or similar device serially the two exchange handshaking signals to communicate whereas the BASIC Stamp programming port doesn't even use these signals. I didn't look in detail at your C code, but if you're just writing a byte to the serial port you should be able to receive the byte on the BS2. Do you get anything? Are you sending a stream of bytes or a single byte? I have sent data from a VB.NET application to the BASIC Stamp via the programming port before without issues.
  • jb9jb9 Posts: 7
    edited 2012-12-11 09:43
    Yeah, I cut out where the BS2 actually does something and just left the comment. In the "real" program, I've had it echo something, send an X10 command, etc.

    Later on, I got it working, sort of. I set aside the C program and wrote a minicom script to do the same thing (open the serial port and send some characters), thinking that minicom definitely has all the right serial port settings. That didn't work initially, but eventually I inserted a 100ms sleep between each character sent. This caused it to work perfectly. I tried a similar inter-character delay in the C program, but so far that doesn't work. I can make this minicom hack work, but I was hoping to better understand why it's happening because this sounds more like a problem with serial port settings.

    thanks,
    JB
  • stlericstleric Posts: 13
    edited 2012-12-11 10:55
    Sorry if my comments aren't helpful but I just started futzing with that serial how-to also (not Stamp related).

    That how-to, if it's the same one I looked at, is like ten years old. Could it be outdated? As in some "under the hood" changes were made in linux which breaks the sample code? I had a similar experience where the sample code didn't work but I only made a half-baked attempt at it before getting sidetracked to something else.

    Secondly, does it have to be a C program? Perl and Python both have serial comm modules and I know for a fact the Python one works (I'm 95% sure the Perl one does too).

    eric
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2012-12-11 11:02
    Well, having something in there to provide an indication that a character was received is a start, but just printing the character to the screen may not be a viable solution since the character may be a linefeed or carriage return or something non-printable. I would recommend printing the decimal value of the incoming character so you can compare values sent with those received. Also, since you're only receiving one byte at a time when the BASIC Stamp module stops to send out an X-10 command or do something else it's not ready to receive the next character which is why you're missing characters. The BASIC Stamp Modules do not have a UART or FIFO Buffer, so they can only accep what they're ready to receive while waiting in the SERIN command. Once you execute the next command any incoming data is lost.

    Also, I don't know if you knew this or not, but any data you send to the BASIC Stamp Module via the programming port is echoed back to the sender.
  • SRLMSRLM Posts: 5,045
    edited 2012-12-11 11:06
    The problem that you're seeing is the receive speed that the BS2 can operate. Generally, PC programs will send the characters down the serial line without any delay between them, which overwhelms the receiver and causes characters to be lost.

    With the Propeller, I used something like this:
    repeat
        serial.tx(serial.rx)
    

    That just echos the characters as fast as possible back to the PC. With this, I found that the Propeller required, at most, a 1ms delay between each character. I couldn't get my terminal program (picocom with "ascii-xfr -s -c 0") to send the characters with a non-integer millisecond delay, so I couldn't try .5ms or so.

    Anyway, I'd setup something similar with the BS2 and see what the minimum delay is between characters.
  • SRLMSRLM Posts: 5,045
    edited 2012-12-11 11:09
    Also, I don't know if you knew this or not, but any data you send to the BASIC Stamp Module via the programming port is echoed back to the sender.

    How is that done? I can see two options:
    1. Done is hardware (result: no matter what the BS2 is doing, it will always respond with the character).
    2. Done in the interpreter (result: only when it's SERIN'ing will it echo the character).

    Anyway, I didn't know that. Still, my previous method will still work: you just look for doubles of each character.
  • jb9jb9 Posts: 7
    edited 2012-12-11 12:38
    The current serin command in my program looks like this:
    SERIN 16, 16468, [WAIT("CMD"), HEX1 sys_command, HEX1 x10_housecode, HEX1 x10_unitcode, HEX1 x10_command]
    
    This is to read a command of the form 'CMD0001'. I'm pretty new to the language, so I thought of reading in the whole string into one variable and parsing it after the fact, but this was easier for a first pass. I suspected that there might be overhead in reading into several variables, causing it to miss characters, but when I constructed the simplistic example from earlier, it didn't seem to catch any characters at all. I had it set up to turn on/off an X10 module whenever it saw any character and that never happened.

    To answer the question above, yes, I really do want this to work in C. I've been programming in C for almost 15 years and have only limited experience with those other languages. I tried this in perl and it isn't working.
    #!/usr/bin/perl
    
    use Device::SerialPort;
    
    my $port = new Device::SerialPort("/dev/ttyUSB0");
    $port->user_msg(ON);
    $port->baudrate(9600);
    $port->parity("none");
    $port->databits(8);
    $port->stopbits(1);
    $port->handshake("none");
    $port->write_settings;
    
    $port->lookclear;
    $port->write(" CMD0001 ");
    

    In the code I pasted originally, I used pin 16, but I've tried pin 16 for in/out as well as other pins (8/9, maybe) with the same result. I'm back to using pin 16 now, though because I ended up with 3 serial cables coming out of my BoE and I only have 2 proper connectors. The 3rd was breadboard jumper wires going into a DB9, so a little flaky, to say the least. :-)

    By the way, one other test I did was to echo output to the device, e.g. 'echo CMD0001 > /dev/ttyUSB0', but no luck there, either.
  • jb9jb9 Posts: 7
    edited 2012-12-11 13:00
    For the sake of thoroughness, I cooked up a simple python script that didn't work either.
    #!/usr/bin/python
    
    import serial
    ser = serial.Serial()
    ser.port = "/dev/ttyUSB0"
    ser.baudrate = 9600
    ser.open()
    if ser.isOpen():
        ser.write(" CMD0001 ")
    


    Editing to add one other note:
    I reduced the inter-character delay in my minicom script to 1 ms and it still worked. When I eliminated the delay, it stopped working again.
  • SRLMSRLM Posts: 5,045
    edited 2012-12-11 13:05
    I think you've already made it past this point, but just in case: on my Ubuntu system, I don't have permission to access the serial port (/dev/tty*). To solve that, I have to enter "sudo chmod 666 /dev/ttyUSB0" (or whatever the particular port is).

    Also, you can try your serial code by looping back in hardware your serial port connection. You should be able to remove your BS2, and put a wire between the RX pin and the TX pin (double check the schematics, first!). Then, you can take the BS2 variable out of the equation.
  • jb9jb9 Posts: 7
    edited 2012-12-11 15:40
    Yeah, I definitely had permissions problems. The way I fixed it was to add all necessary users to the 'dialout' group, as my /dev/ttyUSB0 is owned by root but is group writable (the group being 'dialout'). Originally, I chmodded it like you suggested, but found that it reverted upon reboot. This was a good, permanent fix.

    I've definitely narrowed it down to a BS2 issue. The same methods successfully send bytes to my laptop (via its own USB/serial adapter) as well as a desktop PC with a built-in serial port. The BS2 is the only one which won't receive them.
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2012-12-12 00:04
    SRLM wrote: »
    How is that done? I can see two options:
    1. Done is hardware (result: no matter what the BS2 is doing, it will always respond with the character).
    2. Done in the interpreter (result: only when it's SERIN'ing will it echo the character).

    Anyway, I didn't know that. Still, my previous method will still work: you just look for doubles of each character.

    There is a 4.7K resistor between the SIN/SOUT pins causing the echo. It is done in hardware and is used by the IDE (required) whereas loopback is not required.
  • UnsoundcodeUnsoundcode Posts: 1,532
    edited 2012-12-12 06:23
    A couple of things I might suggest.

    First off the "WAIT" instruction is flakey at 9600 baud, try your initial tests at 4800.

    See if the SERIN instruction will recieve the "CMD" string without the other data to begin with, also when sending data from your C program always try and append a carriage return to the data. (decimal 13)

    When transmitting to the BS2 clear the serial port buffer before sending, this will clear any characters that may have been echoed back and prevent them from mixing in with the new data.

    One last thing I would use the SERIN with a timeout to prevent it sitting there and hanging the program because it has not received a complete packet.

    Jeff T.
Sign In or Register to comment.