Bar Code Reader

 

History

This is a low cost bar code reader made from a product which has a very long history. You can see it at this link.  This bar code reader can be purchased for around 6$ at http://mavin.com/computermice.html.

The bar code reader named CueCat is built to be connected in a PS2 keyboard port (standard PC/AT Keyboard). The problem is the information encrypted.  I decided to build a small interface which fits in the CueCat to convert, decode the information and send it via RS232. Each time you scan a product, the decoded code is sent serially and is stored in an EEPROM which could store up to 128 bar codes.  The idea is to read many bar codes away from the PC.  This information could be retrieved later when the bar code reader will be connected to your PC. Later, you could send some commands to communicate with the CueCat. For example, "G" will get all the codes currently stored in the EEPROM; "C" will clear the entire memory.

 

 

Features

puce Very low cost
puce 128 bar codes memory
puce RS232 interface

Pictures

Click to enlarge

CueCat Look

Open with my modification

My small PCB

 

Sources codes & Schematics

-Schematic in PDF format


//**************************************************************************
// CueCat RS232
// Version 1.0 Jan 2003
// Sylvain Bissonnette
//**************************************************************************
//
//                R E T U R N   S T A C K   1 6
//
//**************************************************************************

//**************************************
//            I N C L U D E
//**************************************
#include <io2313v.h>
#include <macros.h>

//**************************************
//            D E F I N E
//**************************************
#define TRUE      0x01
#define FALSE     0x00

#define SI        0x04
#define SO        0x01
#define CLK       0x08
#define CS        0x02

#define READ      0x03
#define WRITE     0x02
#define WRDI      0x04
#define WREN      0x06
#define RDSR      0x05
#define WRSR      0x01

#define SOUND     0x20
//**************************************
//  I N T E R R U P T   H A N D L E R
//**************************************
#pragma interrupt_handler RxChar_interrupt:8
#pragma interrupt_handler INT0_interrupt:2
#pragma interrupt_handler Timer0_OVF:7
#pragma interrupt_handler Timer1_OVF:6

//**************************************
//          P R O T O T Y P E
//**************************************
void RxChar(void);
void GetData(void);
void SendData(char *);
void TxChar(unsigned char);
void Convert(unsigned char ScanCode);
void Decode(void);
void Add(unsigned char ch);
void INT0_interrupt(void);
void InitLib(void);
void SaveData(unsigned char *ptr);
void UnlockEEprom(void);
void ClearEEprom(void);
void WriteEEprom(unsigned char *ptr,unsigned char page);
void ReadEEprom(unsigned char *ptr,unsigned char page);
unsigned char SPI(unsigned char OutByte);
void SPIDelay(void);

//**************************************
//   G L O B A L   V A R I A B L E
//**************************************
unsigned char Pulse;
unsigned char NumByte;
unsigned char Byte;
unsigned char Tick;
unsigned char Next;
unsigned char Cat[25];
unsigned char Real[16];
unsigned char CatPtr;
unsigned char RealPtr;
unsigned char Dot;
unsigned char NewScan;

//**************************************
//            M A I N
//**************************************
void main()
{
int i;

InitLib();

while(1)
   {
   _StackCheck();
   asm("WDR");
   if (NewScan == TRUE)
      {
      TIMSK |= 0x02;   // Sound ON
      Decode();
      SendData(&Real[0]);
      SendData("\n\r\0");
      for (i=-32000;i<32000;i++);
      TIMSK &= ~0x02;     // Sound OFF
      SaveData(&Real[0]);
      NewScan = FALSE;
      }
   }
}

/**********************************************************

Name:       void _StackOverflowed(char c)

Description:   This function is automaticaly called if
            the stack trash, PB0 will be set.
            

Input:         none

Output:        PB7

Misc:       
**********************************************************/
void _StackOverflowed(char c)
   {
   PORTB |= 0x80;
   }

/**********************************************************

Name:       void RxChar_interrupt(void)

Description:   This function is automaticaly called each
            time a char is received on the RS232.
            

Input:         Serial port Data
            
Output:        None

Misc:       
**********************************************************/
void RxChar_interrupt(void)
{
unsigned char InChar;
InChar = UDR;

if (InChar == 'C') ClearEEprom();
if (InChar == 'G') GetData();
if (InChar == 'V') SendData("CueCat V1.0\n\r\0");

}

/**********************************************************

Name:       void GetData(void)

Description:   Send data from External EEprom to Serial port

Input:         None

Output:        None

Misc:       

**********************************************************/
void GetData(void)
{
unsigned char i;

for (i=0;i<128;i++)
   {
   asm("WDR");
   ReadEEprom(&Cat[0],i);
   SendData(&Cat[0]);
   SendData("\n\r\0");
   }
}

/**********************************************************

Name:       void SendData(char *)

Description:   Send String of data,on the serial port

Input:         * char

Output:        none

Misc:

**********************************************************/
void SendData(char *buffer)
{
while(*buffer != 0x00) TxChar(*buffer++);
}

/**********************************************************

Name:       void TxChar(unsigned char)

Description:   Send a char on RS485 bus,

Input:         char

Output:        none

Misc:

**********************************************************/
void TxChar(unsigned char ch)
{
while ((USR & 0x20) != 0x20); // Wait for empty transmit buffer
UDR = ch;                    // Write char
while ((USR & 0x40) != 0x40); // Wait for the char to be cue off
USR |= 0x40;                 // Clear Tx finish flag
}

/**********************************************************

Name:       void INT0_interrupt(void)

Description:   Called each time a falling eadge from
            the keyboard clock arrived. Read the Data
            keyboard pin to form the scan code

Input:         none

Output:        none

Misc:

**********************************************************/
void INT0_interrupt(void)
{
unsigned char i;

Tick = 0;
if (Pulse == 0)
   {
   Byte = 0x00;
   Pulse++;
   return;
   }
if ((Pulse > 0) && (Pulse < 9))
   {
   Byte = Byte >> 1;
   if ((PIND & 0x08) == 0x08) Byte = Byte | 0x80;
   Pulse++;
   }
if (Pulse == 9)
   {
   Pulse++;
   return;
   }
if (Pulse == 10)
   {
   Convert(Byte);
   NumByte++;
   Pulse = 0;
   }
}

/**********************************************************

Name:       char Convert(unsigned char ScanCode)

Description:   Convert the ScanCode to the real key stroke

Input:         unsigned char -> ScanCode

Output:        in the Cat[..] array

Misc:

**********************************************************/
void Convert(unsigned char ScanCode)
{
unsigned char Val;

Val = 0;

if (Next == TRUE)
   {
   Next = FALSE;
   if (ScanCode == 0x45) Val = '0';
   if (ScanCode == 0x16) Val = '1';
   if (ScanCode == 0x1e) Val = '2';
   if (ScanCode == 0x26) Val = '3';
   if (ScanCode == 0x36) Val = '6';
   if (ScanCode == 0x3d) Val = '7';

   if (ScanCode == 0x32) Val = 'b';
   if (ScanCode == 0x2b) Val = 'f';
   if (ScanCode == 0x33) Val = 'h';
   if (ScanCode == 0x3b) Val = 'j';
   if (ScanCode == 0x3a) Val = 'm';
   if (ScanCode == 0x31) Val = 'n';
   if (ScanCode == 0x2d) Val = 'r';
   if (ScanCode == 0x2a) Val = 'v';
   if (ScanCode == 0x22) Val = 'x';
   if (ScanCode == 0x1a) Val = 'z';

   if (ScanCode == 0x21) Val = 'C';
   if (ScanCode == 0x23) Val = 'D';
   if (ScanCode == 0x24) Val = 'E';
// if (ScanCode == 0x31) Val = 'N'; // HMM 
   if (ScanCode == 0x4d) Val = 'P';
   if (ScanCode == 0x2c) Val = 'T';
   if (ScanCode == 0x1d) Val = 'W';
// if (ScanCode == 0x22) Val = 'X'; // HMM 
   if (ScanCode == 0x35) Val = 'Y';
// if (ScanCode == 0x1a) Val = 'Z'; // HMM 

   if (ScanCode == 0x49) Dot++;

   if ((Dot == 3) && (Val != 0)) Cat[CatPtr++] = Val;

   if (ScanCode == 0x5a)
        {
     NewScan = TRUE;
     }
   }
if (ScanCode == 0xf0) Next = TRUE;
}

/**********************************************************

Name:       void Decode(void)

Description:   Decode the encoded 64 of the cue cat 

Input:         Array Cat[..]

Output:        Array Real[..]

Misc:

**********************************************************/
void Decode(void)
{
unsigned char i;
unsigned char a,b,c,d;

RealPtr = 0;
for (i=0;i<CatPtr;i=i+4)
   {
   a = Cat[i];
   b = Cat[i+1];
   c = Cat[i+2];
   d = Cat[i+3];
   if ((a == 'C') && (b == '3')) Add('0');
   if ((a == 'C') && (b == 'n')) Add('1');
   if ((a == 'C') && (b == 'x')) Add('2');
   if ((a == 'C') && (b == 'h')) Add('3');
   if ((a == 'D') && (b == '3')) Add('4');
   if ((a == 'D') && (b == 'n')) Add('5');
   if ((a == 'D') && (b == 'x')) Add('6');
   if ((a == 'D') && (b == 'h')) Add('7');
   if ((a == 'E') && (b == '3')) Add('8');
   if ((a == 'E') && (b == 'n')) Add('9');

   if (c == 'n') Add('0');
   if (c == 'j') Add('1');
   if (c == 'f') Add('2');
   if (c == 'b') Add('3');
   if (c == 'D') Add('4');
   if (c == 'z') Add('5');
   if (c == 'v') Add('6');
   if (c == 'r') Add('7');
   if (c == 'T') Add('8');
   if (c == 'P') Add('9');

   if (d == 'z') Add('0');
   if (d == 'Y') Add('1');
   if (d == 'x') Add('2');
   if (d == 'W') Add('3');
   if (d == '3') Add('4');
   if (d == '2') Add('5');
   if (d == '1') Add('6');
   if (d == '0') Add('7');
   if (d == '7') Add('8');
   if (d == '6') Add('9');
   }
CatPtr = 0;;
Dot = 0;
Add(0x00);
}

/**********************************************************

Name:       void Add(unsigned char ch)

Description:   Add a char to the Array Real[..]
               
Input:         Char

Output:        Real[..]

Misc:

**********************************************************/
void Add(unsigned char ch)
{
Real[RealPtr++] = ch;
}

/**********************************************************

Name:       void SaveData(unsigned char *ptr)

Description:   Save a array of 16 byte in eeprom

Input:         Char pointer

Output:        none

Misc:

**********************************************************/
void SaveData(unsigned char *ptr)
{
unsigned char i;
int j;

for (i=0;i<128;i++)
   {
   ReadEEprom(&Cat[0],i);
   if (Cat[0] == 0x00)
      {
      WriteEEprom(ptr,i);
      break;
      }
   if (i > 126)
      {
      for (i=0;i<10;i++)
            {
            TIMSK |= 0x02;   // Sound ON
            for (j=0;j<3000;j++)
               {
            SPIDelay();
            asm("WDR");
            }
            TIMSK &= ~0x02;     // Sound OFF
            for (j=0;j<3000;j++)
                  {
            SPIDelay();
            asm("WDR");
            }
         }
      }
   }
}

/**********************************************************

Name:       void Timer1_OVF(void

Description:   100us Tick interrupt

Input:         none

Output:        none

Misc:       Clear flags

**********************************************************/
void Timer1_OVF(void)
{
TCNT1H = 0xFF; //reload counter high value
TCNT1L = 0x9C; //reload counter low value
Tick++;
if (Tick > 5)
   {
   Tick = 0;
   Pulse = 0;
   NumByte = 0;
   }
}

/**********************************************************

Name:       void UnlockEEprom()

Description:   Unlock the external EEprom for write enable

Input:         none

Output:        none

Misc:       

**********************************************************/
void UnlockEEprom(void)
{
PORTB &= ~CS;        // CS low
SPI(WREN);                 // Send WREN Command
PORTB |= CS;         // CS High
}

/**********************************************************

Name:       void ClearEEprom()

Description:   Clear the external EEprom

Input:         none

Output:        none

Misc:       

**********************************************************/
void ClearEEprom()
{
unsigned char i;
unsigned char buff[16];

for (i=0;i<16;i++) buff[i] = 0x00;

for (i=0;i<128;i++)
   {
   asm("WDR");
   WriteEEprom(&buff[0],i);
   }
}

/**********************************************************

Name:       void WriteEEprom(unsigned char *ptr,unsigned char page)

Description:   Write a array of 16 byte in page x of the
            external EEprom

Input:         char *Array[16]
            char page to write (0..127)

Output:        none

Misc:       

**********************************************************/
void WriteEEprom(unsigned char *ptr,unsigned char page)
{
int i;

UnlockEEprom();
SPIDelay();
PORTB &= ~CS;        // CS low
SPIDelay();
SPI(WRITE);             // Send Write Command
SPI(page>>4);           // Hi 16 bit address
SPI(page<<4);           // low 16 bit address
for (i=0;i<16;i++) SPI(*ptr++);
SPIDelay();
PORTB |= CS;         // CS High

for (i=0;i<1000;i++) SPIDelay(); // 5ms
}

/**********************************************************

Name:       void ReadEEprom(unsigned char *ptr,unsigned char page)

Description:   Read a array of 16 byte in page x of the
            external EEprom

Input:         none

Output:        none

Misc:       

**********************************************************/
void ReadEEprom(unsigned char *ptr,unsigned char page)
{
unsigned char i;

PORTB &= ~CS;        // CS low
SPIDelay();
SPI(READ);                 // Send Read Command
SPI(page>>4);           // Hi 16 bit address
SPI(page<<4);           // low 16 bit address
for (i=0;i<16;i++) *ptr++ = SPI(0x00);
SPIDelay();
PORTB |= CS;         // CS High
}

/**********************************************************

Name:       unsigned char SPI(unsigned char)

Description:   Read and write byte on the SPI buss

Input:         none

Output:        unsigned char -> byte read

Misc:       

**********************************************************/
unsigned char SPI(unsigned char OutByte)
{
unsigned char i;
unsigned char InByte;

InByte = 0x00;
for (i=0;i<8;i++)
   {
   if ((OutByte & 0x80) == 0x80) PORTB |= SI;
   else PORTB &= ~SI;
   OutByte = OutByte << 1;

   SPIDelay();
   PORTB |= CLK;
   SPIDelay();

   if ((PINB & SO) == SO) InByte |= 0x01;
   if (i < 7) InByte = InByte << 1;

   PORTB &= ~CLK;
   SPIDelay();
   }
return InByte;
}

/**********************************************************

Name:       void SPIDelay(void)

Description:   Delay of 5us

Input:         none

Output:        none

Misc:       

**********************************************************/
void SPIDelay(void)
{
unsigned char i;

for (i=0;i<10;i++);
}

/**********************************************************

Name:       void Timer0_OVF(void)

Description:   Overflow 3.6khz for the piezo

Input:         none

Output:        none

Misc:       

**********************************************************/
void Timer0_OVF(void)
{
TCNT0 = 0xf1;
PORTD ^= SOUND;
}

/**********************************************************

Name:       void InitLib(void

Description:   Initialize code

Input:         none

Output:        none

Misc:       

**********************************************************/
void InitLib(void)
{
WDTCR = 0x0e;        // Enable WatchDog at 0.97 sec
_CLI();

//PORTB
DDRB = 0x8e;         // PB0-I PB1-O PB2-O PB3-O PB4-I PB5-I PB6-I PB7-O

//PORTD           
PORTD = 0xfd;        // Pull up on PD1-3-4-5-6
DDRD = 0x22;         // PD0-I PD1-O PD2-I PD3-I PD4-I PD5-O PD6-I PD7-I

//UART
UBRR = 12;           // 38400bps at 8Mhz  
UCR = 0x98;          // Rx & Tx enable, Enable RX interrupt

//INT0
MCUCR = 0x02;        // Int0 generate int on falling eadge
GIMSK = 0x40;        // Int0 enable

//Timer1
TCCR1B = 0x02;       // Timer1 / 8 

//Timer0 
TCCR0 = 0x03;        // Timer0 / 64

// Key Scan
TIMSK = 0x80;        // int enable on Timer1 Overflow
NewScan = FALSE;

_SEI();
}