Skip to main content
Topic: Problem talking to BusPirate in C on Linux (Read 4781 times) previous topic - next topic

Problem talking to BusPirate in C on Linux

I've got a few programmes written in Java to talk to devices like the SHT21 temperature/humidity sensor. All were rather easy to develop with the Java RXTX library.  I am hoping to convert them to C is it's more universally available than Java. However I just can't seem to get the most simple thing working properly with the Linux terminal IO API. I've reduced my problem down to a simple test program to write a CR (return key) and look for the "HiZ>" prompt in response. I can get this to work on rare occasions (about 1 time in 5) if I close the serial port and immediately reopen after writing the CR character. However if I use tcdrain() which is supposed to ensure that the write buffer is written to the wire, it does not work.

To compile using tcdrain():
gcc -DUSE_TCDRAIN -o test_bp test_bp.c

To compile using close/reopen instead of tcdrain():
gcc -DCLOSE_AND_REOPEN -o test_bp test_bp.c

To run:
./test_bp /dev/ttyUSBx
(substitute x with whatever the BusPirate device is).

I wonder could any expert in the Linux teminal IO API cast their eye over this code and tell me what I'm doing wrong.

Thanks very much,

Joe.

Code: [Select]
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <termios.h>
#include <sys/fcntl.h>

/**
 * This is a unix/Linux serial port test to write
 * a CR (return key) to a BusPirate in normal
 * command mode and wait for the "HiZ>" prompt
 * in reponse to the CR.
 * It does not work. It seems the CR is only
 * written when the port is closed. tcdrain()
 * does not appear to be working. I can make
 * it work by closeing the port after writing
 * the CR and quickly reopening it. In most cases
 * I loose the first few characters of "HiZ>"
 * but about 1 time in 5 I can catch them all.
 * Using tcdrain() never works.
 */

void usage (char *exeName) {
fprintf (stderr,"%s tty-devicen",exeName);
}

int main (int argc, char **argv) {

int fd,i,c,v,n;
uint8_t buf[4];

if (argc < 2) {
usage(argv[0]);
exit(-1);
}

char *deviceName = argv[1];

fprintf (stderr,"Opening device %sn", argv[1]);


/* open device file */
fd = open(deviceName,O_RDWR);
if (fd==0) {
fprintf (stderr,"error opening device %sn",deviceName);
return EXIT_FAILURE;
}


struct termios tios;
int status=tcgetattr(fd,&tios);
if (status < 0) {
    fprintf (stderr,"error calling tcgetattrn");
return -1;
}

// Set tx/rx speed at 115200bps, and set raw mode
  cfsetispeed(&tios,B115200);
  cfsetospeed(&tios,B115200);
cfmakeraw(&tios);

fprintf (stderr,"sending CRn");
buf[0] = 0x0D;
write(fd,buf,1);

#ifdef USE_TCDRAIN
// tcdrain() is supposed to ensure that anything written has been
// sent on the wire.
tcdrain(fd);
#endif

#ifdef CLOSE_AND_REOPEN
// By closing and immediately reopening the port I can make
// this work about 1 time out of 5.
close(fd);
fd = open(deviceName,O_RDWR);
if (fd==0) {
fprintf (stderr,"error opening device %sn",deviceName);
return EXIT_FAILURE;
}
#endif


// Wait for prompt HiZ>
// 'H' = 0x48, 'i' = 0x69, 'Z' = 0x5A, '>' = 0x3E
v=0; i=8;
while ( (v != 0x48695A3E) && (--i != 0) ) {

n = read(fd,buf,1);
// timeout
if (n==0) {
fprintf(stderr,"T ");
continue;
}
// error
if (n<0) {
fprintf(stderr,"E ");
continue;
}
c = buf[0];
fprintf (stderr,"i=%d fd=%d n=%d c=%x v=%xn",i,fd,n,c,v); fflush(stderr);
v <<= 8;
v |= c;
}

if (i == 0) {
fprintf (stderr,"Fail!n");
return EXIT_FAILURE;
}

fprintf (stderr,"Success!n");
return EXIT_SUCCESS;
}

Re: Problem talking to BusPirate in C on Linux

Reply #1
cfsetspeed and cfmakeraw only modify the termios structure.  If you want these changes to take effect, you have to send that structure to the serial driver by calling tcsetattr.  Officially, you should call tcsetattr after each modification so that if it fail, you know which modification was rejected but for standard speeds and settings it's overkill.  Simply add:
Code: [Select]
tcsetattr(fd, TCSANOW, &tios);
after your call to cfmakeraw and it should work.

Re: Problem talking to BusPirate in C on Linux

Reply #2
Vincent: yes, well spotted. Thanks.  But unfortunately it doesn't fix the problem. Still getting partial success with the close/open port after writing CR character and no success with tcdrain(). My hunch is this is handshaking related. And I'm sure it's going to be a simple and stupid omission. But I just can't see it yet.

Re: Problem talking to BusPirate in C on Linux

Reply #3
For the Bus Pirate to work, your serial port must be configured in 115200bauds, 8bits data, 1 stop bit, no parity and no flow control.  You also need to make sure the receiver is enabled and disable all modem stuff.  cfsetspeed take care of the baud rate, cfmakeraw take care of 8bits data and no parity and some of the modem settings.  To do the rest, you need to add these lines:
Code: [Select]
tios.c_cflag &= ~CSTOPB; // Set 1 stop bit
tios.c_cflag |= (CREAD | CLOCAL); // Enable receiver and disable hardware flow control
tios.c_oflag = 0; // Disable some modem settings
  You should also read the documentation and set c_cc[VMIN] and c_cc[VTIME] however you want read() to behave.

Re: Problem talking to BusPirate in C on Linux

Reply #4
You can always look in the DP svn, and "steal" code from there :-)

http://code.google.com/p/dangerous-prot ... e/serial.c

These routines are used in many places (PiratePICProg, OpenOCD, ols-fwloader, etc...) to talk to BP and I have never had trouble with them. The source is written with WIN32 in mind, so you can compile your program with mingw under windows, and it will work reliably.

EDIT:

https://github.com/robots/ols-fwloader/ ... c/serial.c
This one is probably better, as it supports MAC, and has the "debug" stuff removed.

Re: Problem talking to BusPirate in C on Linux

Reply #5
D'oh. Vincent: I thought I had tried CLOCAL in an earlier test before whittling it down to the test case above, but looking back on the code I did not. Yes... that combo seems to be working perfectly. Thanks very much for your help.  robots: thanks for the tip: having something that will work with WIN32 also is a good idea.  BTW: has anyone written an comprehensive C library for the buspirate yet? (covering all the bitbang modes etc)

Re: Problem talking to BusPirate in C on Linux

Reply #6
There is no comprehensive C library yet. I think the major one is in Python.
Got a question? Please ask in the forum for the fastest answers.