Notes About Working with Various Arduino & Netduino Microcontroller Boards

Friday, June 5, 2015

C Program for the Arduino Galileo to Read RFID Cards with an Innovations ID20-LA & Sparkfun RFID USB Reader Board

The Sparkfun RFID USB reader board provides an easy way to hook up an Innovations RFID reader chip.  While its main feature is the USB connection, the RFID USB reader board also provides a break out of the serial and power connections.  In this example, I present a C program for the Arduino Galileo that runs at the Linux command line (not an Arduino sketch) that configures the Galileo's serial pins, creates a serial connection, and reads the ID numbers of a 125kHz RFID card brought near the reader.  To reduce complexity, this code simply prints the card's number to the terminal.

Once again, I found information on Sergey Kiselev's blog - this time "Configuring the Serial Port in Linux" - very helpful.  Working with the RFID reader is by itself fairly simple, but configuring the GPIO pins for the serial connection and setting up the serial connection is a bit more complicated. The code for configuring and opening the serial connection is derived (and updated) from the Linux Documentation Project's program examples.

The program below runs, reads cards, and prints card IDs until the user presses control-c to quit.  The signal() handler restores the serial port's original settings and unexports the GPIO pins from sysfs before the program ends.

Connections


Reader Board   Arduino Galileo
VCC            3.3V
TX             RX (Digital 0)
TXR            TX (Digital 1)
GND            GND

Code 


Here is the C code. Again, this is designed to be compiled and run at the Galileo's Linux command prompt and is not an Arduino sketch.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

/* Galileo's digital pins 0 & 1 connect to /dev/ttyS0 */
#define SERIAL_DEV "/dev/ttyS0"

/* baudrasettings are defined in <asm/termbits.h>, which is
   included by <termios.h>. Change as needed, keep B 
*/
#define BAUDRATE B9600

/* POSIX compliant source */
#define _POSIX_SOURCE 1

// Function declarations
void configPins();
int openSerial(const char *);
void handleSIGINT(int);

/* Declare variable for original serial port settings here
   so it can be accessed from handleSIGINT function 
*/
struct termios savedTio;

/* Make descripter for serial connection global so
   handleSIGINT() can access it to restore original serial
   settings when program ends. 
*/ 
int serialHandle;

// Set loop that reads serial port to run indefinitely
int run = 1; 

int main() {
   int fd, c, res;
   char buf[255];

   printf("\nScan a tag to see its ID number. Press ctrl-c to quit.\n\n");

   // Program runs until user presses ctrl-c
   signal(SIGINT, handleSIGINT);
   
   configPins();
   serialHandle = openSerial(SERIAL_DEV);

   /* Loop continuously to handle serial input */
   while (run) {     
      /*  read blocks program execution until a line terminating character is
          input, even if more than 255 chars are input. If the number
          of characters read is smaller than the number of chars available,
          subsequent reads will return the remaining chars. res will be set
          to the actual number of characters actually read. 
      */
      res = read(serialHandle, buf, 255);
      /* set end of string, so we can printf */
      buf[res] = 0;
      printf("%s", buf, res);
   }
}

/* Function to configure the Galileo's serial pins (gpio40, gpio41). gpio4 
   is multiplexer that controls how serial pins behave.
*/
void configPins() {
   char *gpio[] = {"4", "40", "41"};
   char path[30];

   // Export ports for sysfs access
   int fd = open("/sys/class/gpio/export", O_WRONLY);
   int c;

   for(c = 0; c < 3; c++) {
      write(fd, gpio[c], strlen(gpio[c]));
   }
   close(fd);

   // Set direction
   for(c = 0; c < 3; c++) {
      sprintf(path, "/sys/class/gpio/gpio%s/direction", gpio[c]);
      fd = open(path, O_WRONLY);
      write(fd, "out", 3);
      close(fd);
   }

   // Set drive to strong for gpio40 & gpio41
   for(c = 1; c < 3; c++) {
      sprintf(path, "/sys/class/gpio/gpio%s/drive", gpio[c]);
      fd = open(path, O_WRONLY);
      write(fd, "strong", 6);
      close(fd);
   }

   // Turn on level shifter controlled by gpio4. Needed for serial.
   fd = open("/sys/class/gpio/gpio4/value", O_WRONLY);
   write(fd, "1", 1);
   close(fd);

   // Set mux for serial by setting gpio40 & gpio41 to 0
   for(c = 1; c < 3; c++) {
      sprintf(path, "/sys/class/gpio/gpio%s/value", gpio[c]);
      fd = open(path, O_WRONLY);
      write(fd, "0", 1);
      close(fd);
   }
}

// Configure & open serial port. Run configPins() first.
int openSerial(const char *serial) {
   struct termios tio;
   /* Open device for reading and writing and not as controlling tty
      because we don't want to get killed if linenoise sends CTRL-C. */
   int fd = open(serial, O_RDWR | O_NOCTTY );
   if (fd < 0) { 
      printf("\nError opening %s.\n", serial); 
      exit(1); 
   }
   /* save current serial port settings to global var so they can be 
      restored when the program ends (see handleSIGINT). . 
   */
   tcgetattr(fd, &savedTio);
   /* clear struct for new port settings */
   memset(&tio, 0, sizeof(tio));

   /* CRTSCTS : output hardware flow control
      CS8     : 8n1 (8bit,no parity,1 stopbit)
      CLOCAL  : local connection, no modem contol
      CREAD   : enable receiving characters 
   */
   tio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

   /* IGNPAR  : ignore bytes with parity errors
      otherwise make device raw (no other input processing) 
   */
   tio.c_iflag = IGNPAR;

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

   /* ICANON  : enable canonical input disable all echo functionality, and 
      don't send signals to calling program 
   */
   tio.c_lflag = ICANON;
   /* now clean serial line & activate settings for the port */
   tcflush(fd, TCIFLUSH);
   tcsetattr(fd,TCSANOW, &tio);
   return fd;
}

// Function to clean up & end program when user presses ctrl-C
void handleSIGINT(int signum) {
   char *gpio[] = {"4", "40", "41"};
   char path[30];
   tcsetattr(serialHandle, TCSANOW, &savedTio);
   printf("\nProgram ending...\n");
   // Export ports for sysfs access
   int fd = open("/sys/class/gpio/unexport", O_WRONLY);
   int c;
   for(c = 0; c < 3; c++) {
      write(fd, gpio[c], strlen(gpio[c]));
   }
   close(fd);
   exit(0);
}



1 comment:

  1. Thanks for sharing the info, keep up the good work going.... I really enjoyed exploring your site. good resource... RFID tags

    ReplyDelete