Serial Wombat
a general-purpose digital interface device for hobbyists, engineers and students

 


Home
Overview
Protocol
Documentation
Getting Started
Connectivity
Pin Mode SDK Beta
Sample Projects
WombatPanel
iTunes Control
Serial To TCP/IP
QBasic
'C'
Solar Water Display
Downloads
Contact Us
Purchase
Forum

Did you know...

 

Interfacing the Serial Wombat from C on Posix compatible systems

Serial communication on different platforms from C is hard. That's because there is no standardized way to access hardware, such as a serial port. POSIX is a standard which is used by many Unix-like systems to provide uniform access to hardware. This example will demonstrate a piece of C code running on two platforms: the new Asus eee PC, which runs a derivative of the Xandros Linux distribution, and Cygwin under Windows XP.

For this example, we're going to plug the Serial Wombat chip into a Development Board created by Olimex, and available for puchase from Sparkfun. This development board provides an onboard FTDI chip for serial to USB conversion, a couple of buttons, a bi-color LED, and a two-line HD44780 compatible LCD. I love this little guy, because it's powered off of the USB port. I frequently carry this guy with me when I travel, so that I have an easy access to actual Serial Wombat hardware for testing. See the Schematic here...

The code below is relatively simple. It:

  • initializes the Wombat with a resync message,
  • sets the backlight to 50% (via PWM),
  • Reads the public data from the Red LED pin
  • If the Red LED pin is on, it turns it off and turns on the Green LED. If not, it turns off the Green LED and turns on the Red LED. Therefore the LED will change colors each time the program is run.
  • It writes to the LCD using the generic HD44780 Serial Wombat pin mode
  • It monitors the Public data from one of the pins attached to a button. The program waits in a loop until a button press is detected.
  • It writes "Goodbye' to the LCD and exits.
The code below works good on my eee PC when I plug in an Edgeport Serial to USB adapter, which shows up under /dev/ttyUSB0 . Under cygwin, the port is whatever your COM port number is, minus one. Be sure to use the /dev/tty## interface, and not the /dev/COM## interface, as only the tty interface provides all of the POSIX functionality.

The only problem I ran into was that the FIONREAD constant wasn't defined on my eee Linux distribution. Ideally, I'd like to check the number of received bytes then read, rather than looking for an arbitrary number and timing out if we don't get them.

Special thanks to Michael Sweet for creating the Serial Programming Guide for POSIX Operating Systems. It's fantastic!

Anyways, without further delay, here's the code:

 

#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

typedef unsigned char  uint8;
typedef unsigned short uint16;

typedef enum {
   LOW = 0,
   HIGH = 1,
   INPUT = 2,
   UNCHANGED = 3
   } state_t;

// The pin defines below are specific to the Olemex development board.
#define REDLEDPIN 34
#define GREENLEDPIN 35
#define LCDRWPIN 20
#define LCDEPIN 21
#define LCDBACKLIGHTPIN 22
#define B2PIN 37

// device id for the serial port
int serial_port;

// Since only one transaction can happen at a time, so we can
// use one set of buffers.  We only use the first 8 bytes.  
// The 9th byte holds a null string termintor to simplify
// string calls which act on the buffers
uint8 rxbuffer[9];
uint8 txbuffer[9];

void resync();
void print_transaction();
void set_pin(uint8 pin, state_t state);
uint16 get_public_data(uint8 pin);
void set_pwm(uint8 pin, uint16 PWM);
void put_text_lcd(char* string, uint8 position);

int main(int argc, char *argv[])
{
   struct termios options;
   int i;

   rxbuffer[8] = '\0';
   printf("Opening port...\n");
   serial_port = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY );
//   serial_port = open("/dev/ttyS13",O_RDWR | O_NOCTTY ); // for cygwin.  use COM Port # - 1.
   sleep (1);
   if (serial_port == -1)
   {
       perror("Couldn't open port");
   }
   else
   {
       fcntl(serial_port,F_SETFL,0);
   }
   tcgetattr(serial_port,&options);

   cfsetispeed(&options,B9600);  // Baud rate 9600
   cfsetospeed(&options,B9600);

   options.c_cflag &= ~CSIZE;   // 8 data bits
   options.c_cflag &= ~PARENB;  // no parity
   options.c_cflag &= ~CSTOPB;  // No hw flow control
   options.c_cflag &= ~CRTSCTS;
   options.c_cflag |= CS8;
   options.c_lflag &= ~(ICANON|ECHO|ECHOE|ISIG);  // No terminal processing
   options.c_oflag &= ~OPOST;

   options.c_cc[VMIN] = 0;   // Timeout before packet (unused)
   options.c_cc[VTIME] = 10;  // Timeout between characters
   options.c_iflag &= ~(IXON|IXOFF|IXANY); // Disable sw flow control

   options.c_cflag |= (CLOCAL|CREAD);

   tcsetattr(serial_port,TCSANOW, &options);
   fcntl(serial_port,F_SETFL,0);

   printf("Resyncing...\n");

   resync(); // Send U to sync and set baud

   set_pwm(LCDBACKLIGHTPIN,32768);  //50% .  No reason, except to show
                                    // we can do it

   set_pin(LCDRWPIN,LOW); //HD44780 pin mode assumes LCD RW line
                          //Is always low

   if (get_public_data(REDLEDPIN)) // Get the public data for RED 
                                   // LED pin.  If it's 1, set it
                                   // to zero, and vice-versa.
                                   // This will toggle the red/green
                                   // LED each time the program is run.
   {
           set_pin(REDLEDPIN,LOW);
           set_pin(GREENLEDPIN,HIGH);
   }
   else
   {
           set_pin(REDLEDPIN,HIGH);
           set_pin(GREENLEDPIN,LOW);
   }


   txbuffer[0] = 200;   // Configure pin command 1
   txbuffer[1] = LCDEPIN;    // Pin 21 is the 'E' pin for the lcd
                        // (see documentation for HD44780 generic
                        // driver on serialwombat.com
   txbuffer[2] = 27;    // HD44780 generic driver mode
   txbuffer[3] = 19;    // RS PIN
   txbuffer[4] = 27;    // D4 pin
   txbuffer[5] = 28;    // D5 pin
   txbuffer[6] = 29;    // D6 pin
   txbuffer[7] = 30;    // D7 pin
   transfer(txbuffer,rxbuffer);
   print_transaction();

   txbuffer[0] = 201;   // Second command.  Puts text on LCD
   txbuffer[1] = LCDEPIN;
   txbuffer[2] = 27;
   txbuffer[3] = 0;    // Start at address 0
   txbuffer[4] = 'E';
   txbuffer[5] = 'e';
   txbuffer[6] = 'e';
   txbuffer[7] = ' ';
   transfer(txbuffer,rxbuffer);
   print_transaction();

   txbuffer[3] += 4;  // Address 4.  txbuffer[0-2] unchanged.
   txbuffer[4] = 'P';
   txbuffer[5] = 'C';
   txbuffer[6] = ' ';
   txbuffer[7] = 'A';
   transfer(txbuffer,rxbuffer);
   print_transaction();

   txbuffer[3] += 4;  // Address 8, and so on...
   txbuffer[4] = 'n';
   txbuffer[5] = 'd';
   txbuffer[6] = ' ';
   txbuffer[7] = ' ';
   transfer(txbuffer,rxbuffer);
   print_transaction();

   txbuffer[3] += 4;
   txbuffer[4] = ' ';
   txbuffer[5] = ' ';
   txbuffer[6] = ' ';
   txbuffer[7] = ' ';
   transfer(txbuffer,rxbuffer);
   print_transaction();

   txbuffer[3] = 0x40;  // Second line starts at 0x40
   txbuffer[4] = 'S';
   txbuffer[5] = 'e';
   txbuffer[6] = 'r';
   txbuffer[7] = 'i';
   transfer(txbuffer,rxbuffer);
   print_transaction();

   txbuffer[3] += 4;
   txbuffer[4] = 'a';
   txbuffer[5] = 'l';
   txbuffer[6] = ' ';
   txbuffer[7] = 'W';
   transfer(txbuffer,rxbuffer);
   print_transaction();

   txbuffer[3] += 4;
   txbuffer[4] = 'o';
   txbuffer[5] = 'm';
   txbuffer[6] = 'b';
   txbuffer[7] = 'a';
   transfer(txbuffer,rxbuffer);
   print_transaction();

   txbuffer[3] += 4;
   txbuffer[4] = 't';
   txbuffer[5] = ' ';
   txbuffer[6] = ' ';
   txbuffer[7] = ' ';
   transfer(txbuffer,rxbuffer);
   print_transaction();
   while (get_public_data(B2PIN) != 0)
   {
     sleep(1);
   }
   put_text_lcd("Goodbye!        ",0);
   put_text_lcd("                ",0x40);
   close(serial_port);
   return(serial_port);

}


// Dump all of the data out of the receive buffer
// Ideally, we'd use FIONREAD to see if any are available, and
// read them.  But FIONREAD doesn't appear to be implemented
// in the distribution I'm using, so I'll try a read and let it
// timeout if there's no data.  This is inefficient, but I can't
// think of any better way without FIONREAD

void flush(void)
{
   uint8 dummy[1002];
   int i,j ;
   printf("Flushing...\n");
   do
   {
           i = read(serial_port,dummy,1000);
           if (i > 0)
           {
              printf ("Flushed bytes: ");
              for (j = 0; j < i ; ++j)
              {
                 printf ("%2X ", dummy[j]);
              }
              printf("\n");
           }
   } while (i > 0);
}


// This function sends 8 bytes from the array tx, and waits for an 8
// byte return.  If 8 bytes are not received without a timeout then
// a series of ReSync characters is sent, and the buffer is flushed.
// A second transmission is then attempted.
int transfer(uint8* tx, uint8* rx)
{
   int i;
   int rxbytes = 0; // Number of bytes received
   uint8 rxtemp[9];
   if (rx == NULL)
   {
       rx = rxtemp; // If no rxarray was specified, put the result
                    // into a temp variable
   }
   rx[0] = 'E'; // Assume a bad result

   write(serial_port,tx,8);  //Send the transmit

   // Now, read bytes until we get 8 bytes, or a timeout with
   // no bytes read.
   do {
           i = read(serial_port,&rx[rxbytes],8);
           rxbytes += i;
   } while (rxbytes < 8 && i > 0);

   if (rxbytes < 8  || rx[0] == 'E')
   {
      // Try again if error, or less than 8 bytes received

      // Print what we did get for debugging purposes. 
      printf("Incomplete response received. ");
      for (i =0; i < rxbytes; ++i)
      {
          printf("%d = %2X ",i,rx[i]);
      }
      printf("\n");

      // Send resync characters
      write(serial_port,"UUUUUUUUUU",10);

      //flush the buffer...
      flush();

      //Try send again
      write(serial_port,tx,8);
      rxbytes = read(serial_port,rx,8);
   }
   return(rxbytes);
}

void resync ()
{
   int i;
   uint8 tempbuffer[20];
   for (i = 0; i < 20; ++i)
   {
           tempbuffer[i] = 'U';
   }

   write(serial_port,tempbuffer,20);
   flush();
}


// A utility function to print out txbuffer & rxbuffer in hex
void print_transaction()
{
   int i;
   for (i = 0; i < 8; ++i)
   {
      printf ("%2X ",txbuffer[i]);
   }

   printf("       ");
   for (i = 0; i < 8; ++i)
   {
      printf ("%2X ",rxbuffer[i]);
   }
   printf("\n");

}

void set_pin(uint8 pin, state_t state)
{
        txbuffer[0] = 'P';
        txbuffer[1] = pin / 10 + '0';
        txbuffer[2] = pin % 10 + '0';

        switch (state)
        {
           case LOW:
                   txbuffer[3] = '0';
           break;

           case HIGH:
                   txbuffer[3] = '1';
           break;

           case INPUT:
                   txbuffer[3] = 'I';
           break;

           default:
                   txbuffer[3] = 'U';
           break;
        }
        txbuffer[4] = 'U';
        txbuffer[5] = 'U';
        txbuffer[6] = 'U';
        txbuffer[7] = 'U';
        transfer(txbuffer,rxbuffer);
        print_transaction();
}

uint16 get_public_data(uint8 pin)
{
        uint16 result = 0;
        txbuffer[0] = 'A';
        txbuffer[1] = pin / 10 + '0';
        txbuffer[2] = pin % 10 + '0';
        txbuffer[3] = 'D';

        transfer(txbuffer,rxbuffer);
        result = rxbuffer[3] - '0';
        result *= 10;
        result += rxbuffer[4] - '0';
        result *= 10;
        result += rxbuffer[5] - '0';
        result *= 10;
        result += rxbuffer[6] - '0';
        result *= 10;
        result += rxbuffer[7] - '0';
        printf ("Pin %d public data %d\n",pin,result);
        print_transaction();
        return(result);

}

void set_pwm(uint8 pin, uint16 PWM)
{
   txbuffer[0] = 200;
   txbuffer[1] = pin;
   txbuffer[2] = 18;
   txbuffer[3] = PWM/256;
   txbuffer[4] = PWM%256;
   transfer(txbuffer,rxbuffer);
}

void put_text_lcd(char* string, uint8 position)
{
        int i = 0;
        int counter = 0;
        printf ("%c %X \n",string[i],string[i]);
        while (string[i] != '\0')
        {
           txbuffer[0] = 201;   // Second command.  Puts text on LCD
           txbuffer[1] = LCDEPIN;
           txbuffer[2] = 27;
           txbuffer[3] = position;
           txbuffer[4 + counter] = string[i];
           ++counter;
           ++i;
           if (counter == 4)
           {
              transfer(txbuffer,rxbuffer);
              print_transaction();
              counter = 0;
              position += 4;
           }
        }
        if (counter != 0)
        {
           txbuffer[0] = 201;   // Second command.  Puts text on LCD
           txbuffer[1] = LCDEPIN;
           txbuffer[2] = 27;
           txbuffer[3] = position;
            while (counter < 4)
            {
                    txbuffer[4 + counter] = ' ';
                    ++counter;
            }
              transfer(txbuffer,rxbuffer);
              print_transaction();
        }

}





 

 

Copyright Wombat Interface Products, 2005-2008. All Rights Reserved.