5

I was unable to find this question anywhere but I want to use my Arduino Uno to capture the output of a Code 39 signal coming out of a separate device. Currently, my setup is similar to this:

enter image description here

I have a biometric hand reader that outputs a Code 39 signal along D1 while D0 stays at a constant 0 which can be seen here:

enter image description here

Ultimately, I want to use the Arduino Uno to take the input from the data line and convert the Code 39 signal to ASCII. The process should functions like this:

  1. User enters their code into the hand reader and hand reader measures their hand
  2. Reader outputs Code 39 signal to Arduino and software on Arduino converts barcode format to ASCII.
    • Note: I believe the Code 39 signal will represent something similar to this *12345*. The user ID code will always be five digits. * represent the start and stop characters for Code 39 barcode format
  3. Arduino sends ASCII code to computer which checks remote database to ensure user is valid

I have my correct pins set as follows:

#include <SoftwareSerial.h>
#define D1 3
#define D0 2

void setup() {
    pinMode(D1, INPUT);
    pinMode(D0, INPUT);
}

Is it possible to either print out the binary representation of the signal via the Serial console or write to a file that I can view later on? My idea is to use the loop function to listen for the signals via digitalRead()

void loop() {
    digitalRead(D1);
    digitalRead(D0);
}

The last part is the thing I'm having an issue with since I can't seem to get the information printed to the serial console or written to a file. I tried this but it didn't work:

void loop() {
    foo();
}

void foo() {
    digitalRead(D1) == HIGH ? Serial.print("0") : Serial.print("1");
    Serial.println("");
    digitalRead(D0) == HIGH ? Serial.print("0") : Serial.print("1");
}

Any ideas on how to make this happen or is it impossible? I want to see the output of device so I can verify it's outputting what I think it is.

Lastly, I am using Visual Studio with the Visual Micro plugin so I can attempt to debug my code. I also have a OS X machine that has the Arduino IDE installed on it if I need a Mac for anything.

Addendum: I don't have ready access to an oscilloscope. However, I have tested this device on an oscilloscope and I did have output that seemed all wrong. I want to see what the arduino says it is to verify things before I continue with this project.

5
  • This is going to be extraordinarily difficult unless you have some more information about the other device. Dec 23, 2014 at 2:59
  • Added updates! I explain how everything should work and included the type of devices I am using. Please let me know if anything needs clarification!
    – djthoms
    Dec 23, 2014 at 5:32
  • 1
    See if you can find existing code for interfacing a mag stripe reader - though your problem is simpler as the simulated swipes probably have a consistent speed. You could also look at the receive half of the software serial implementation for ideas, though the signaling you are trying to receive is different. Dec 23, 2014 at 15:05
  • @ChrisStratton I agree. I tried looking for conversions for Code 39 specifically but I found classes that included many other barcode formats. The conversion from Code 39 to ASCII is pretty straight forward though -- 12 bits can represent A-Z0-9%*\-+. Thanks for pointing me in the right direction though!
    – djthoms
    Dec 26, 2014 at 18:02
  • I filled in some explanation to your problems at the beginning of my answer. If this doesn't work for you, we can work out better solutions, e.g. using a buffer.
    – Ariser
    Dec 28, 2014 at 12:38

1 Answer 1

1

First, let's find, what's wrong with your method. You try to poll the signal pins continously and write them out to serial. Default serial rate is 9600. Which means, that you can transmit approx. 900 characters per second. Looking at your oscillogram we can tell, that the signal you want to analyse has a base clock of roughly 2.5 kHz. Which means you definitely won't be able to sample every change of level with your method.

You can in fact use the loop() function to listen for changes, i.e. poll the signal, when you increase your serial baud rate. But I suggest using interrupts.

My suggestion is a three step process.

  1. get the signal thoroughly recorded with all relevant information about the signal. This includes transmission to your PC via serial interface.
  2. Analyse the signal offline. i.e. after having it received on your PC
  3. Adapt your arduino code to decode it further before transmitting it to your PC

let's get to part 1): read Arduino documentation on interrupts the interrupt allows you to prevent interference of your two tasks (reading the inputs and writing to serial). What is the interesting information? I think it's the waveform (i.e. the signal in best resolution you can deliver). You can sample your waveform at a high rate, but the resulting data volume will overwhelm your serial output. Fortunately your signal has some obvious characteristics. Which are:

  • obeys some timing (i.e. a clock)
  • has a limited transmission length (protocol frame)
  • has a limited transmission rate (as can be seen from oscillograph)

The best way to keep as much of the signal information by reducing the amount of serial transmission is to record the time of every level change.

Updates: buffer used, buffered output after receiving timeout

#define RECVTIMEOUT 2000 // we think, more than 2000 usec is end of transmission
#define MAXEVENTS 100    

uint8_t inpin=2;
volatile unsigned long timestart=0;
uint8_t transmitLevel=LOW;

volatile uint8_t state=0; // little state machine: 0=nothing happened yet, 1=receiving data, 2=receiving timeout
volatile uint16_t tstamps[MAXEVENTS]; // we store timestamps of received symbols here
volatile uint16_t levels[MAXEVENTS]; // we store levels of received symbols here
volatile uint8_t index=0; // we save index of data here.
uint8_t count=0; // counter for later use.

volatile bool conflict=false; // will be set, if transmission conflicts with receive function.

void pinchange() // the ISR (interrupt service routine)
{
    switch(state)
    {
    case 0:       
       timestart=micros();
       tstamps[index]=0;
       levels[index]=digitalRead(inpin);
       state=1;
       ++index;
    break;
    case 1:
       tstamps[index]=micros()-timestart;
       levels[index]=digitalRead(inpin);
       ++index;
    break;
    case 2:
       conflict=true;
    }
}

void setup()
{
    Serial.begin(115200); // high serial rate. don't forget to set the same in your IDE
    attachInterrupt(0, &pinchange, CHANGE); // the controller needs to know, where your ISR is.
}

void loop()
{
    switch(state)
    {
    case 1:
       if (index > 20)
       {
          if (micros()-timestart > RECVTIMEOUT)
          {
             state=2;
          }
       }
       if (index >= MAXEVENTS)
       {
          state=2;
       }

    break;
    case 2:   
       for (count=0; count<index; ++count)
       {
         Serial.print(tstamps[count]);
         Serial.print(";");
         Serial.println(levels[count]);
       }
       if (conflict)
       {
         Serial.println("too less time to send data");
       }
       index=0;
       conflict=0;
       state=0;
    }
 }

This should output a fine csv with all your signal edges in microseconds, which you can paste directly to your favourite spreadsheet application. This is only for one signal. For analysing both, you have to enhance it accordingly.

Update 1

part 2)

from the gained data of our previous code version (snippet for brevity)

time signal timedifference
2614592 0   468
2615056 1   464
2615524 0   468
2616496 0   972
2616960 1   464
2617956 0   996
2618960 1   1004
2620424 1   1464
2621352 1   928
2622324 1   972
2623320 0   996

we can see two things.

  1. The code in my first version of the answer didn't work properly. Every 1 should be followed by a 0 in column 2 and vice versa. This is apparently not true, which leads us to the conlusion, that we missed some events.
  2. time differences seem to be multiples of roughly 470 μs. We can consider this as the length of a symbol.

So what's wrong with the code? I used noInterrupts() to stop Interrupts for copying the data. I hoped, there won't be level changes in this short period of time, but apparently this was not true. How to fix it? We better find another way to register the events and transfer them. I recommend sticking to the interrupt technique, but changing the way we store the events and reading it out. Let's use a buffer big enough for all events of a transmission. After a transmission we can send all data without hassle via serial to the PC.

What can we expect? 0s and 1s should be transmitted alternating, with reasonable timings. If we can prove this, we can change the main loop again for decoding code 39.

part 3)

  • write code which stores a word of code 39 in a buffer
  • another code which decodes that word into a byte when the buffer is full
  • transmit that byte via serial

(many improvements possible).

8
  • Hi Ariser, a few things that I'm seeing: 1. I set the transfer rate and adjusted it in the serial monitor but I'm only seeing about 57 changes when I should see, at minimum, 60 changes. Any idea why that might be the case? Again, the format is Code 3 of 9. The output should be a user's five digit user code with start and stop characters. Like this: *12345* P.s. sorry it took me forever to reply! I had holiday off and now I'm finally back at work
    – djthoms
    Dec 29, 2014 at 17:16
  • Humm, I feared that. I thought, the occasion of receiving a pin change while blocking interrupts was very rare, but I'm proved wrong. Were you able to extract the clock from the received data? I'll update my answer, but it takes some time to brew something usable.
    – Ariser
    Dec 29, 2014 at 19:31
  • I don't know what you mean by "extract the clock from the received data"
    – djthoms
    Dec 29, 2014 at 19:32
  • If you get the timing information via serial monitor, you can calculate the clock frequency, because the time between two adjacent level changes must be either one or two clock cycles long. Once you have the length of a clock cycle, it is easier to build some code to decode the data on the arduino instead of transmitting every bit to the PC.
    – Ariser
    Dec 29, 2014 at 20:03
  • Well, I copied the code you had and used that. I got an output but I can't really make sense of it. I know the device outputting the signal is transferred at 9600 baud. Here is that data: Trial 1 and Trial 2. I appreciate all your help!
    – djthoms
    Dec 29, 2014 at 20:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.