Reading pulse width data

I'm just starting on a project that will need to read a series of 5v pulses. I've got a sign that lights up the numbers for a BINGO game. From looking at the data that is sent to the sign (called a Flashboard) on the oscilloscope, the sign receives the data as a stream of 5v pulses, with a pulse for "B", 15 pulses for 1-15, then a pulse for "I", then another 15 pulses for 16-30, and so on. If the number or letter is to be lit, the pulse is about 40 microseconds, and if unlit, about 10 microseconds, and these all have 10 microseconds between them. The data stream is 128 pulses each time, which includes some data I don't need at the end and can ignore.

What I need to do is start reading at the beginning of the stream, then store all the pulse widths, or at least 1 or 0, to an array. I know I have to look for the ~90 millisecond interval between pulse streams to determine where to start reading. I was a programmer decades ago, but suffered some brain damage since then and am trying to re-learn a lot, I think. My approach so far is something like this:

Watch for the (relatively) long LOW state to start waiting for pulses (to make sure I'm not starting in the middle of a pulse stream

Use pulseIn to get the high time of each pulse, storing that to an array

Manipulate the data from the array, then wait for another long LOW to prepare to read again

Does this sound like a practical approach? Can pulseIn handle 10 usec pulses? I'll be happy starting out if I can just recognize the start of each cycle, then building from there. Any hints?

10us is quite short for a 16MHz Arduino. Its only 160 clock cycles, 160 machine instructions at most. It might work, but will require some very careful C coding, or perhaps even some assembler coding.

If you look at the pulsein Arduino function, it says that pulses down to 10us can be measured. Its the 10us gaps between pulses I would be more concerned about. Your code will need to be efficient to store the previous pulse and get ready for the next pulse in such a short time. But it's worth a try.

You can also specify a timeout with the pulsein function. You could use this to detect the gaps between pulse streams. Set the timeout to, say, 100us and the long gap will cause the function to time out and return zero. If your code keeps a count of the pulses read, then after 128 pulses, or a zero from the function, you know the pulse stream has ended. If you did not count 128 pulses, then ignore the data.

If using pulsein does not work, another approach would be to use an interrupt pin, set to trigger on only rising edges. The interrupt function would use the micros() function to time the interval since the previous rising edge. Under 25us would indicate a zero was received, over 25us indicates a one was received, over 100us indicates the start of a new pulse stream.

Good point on the speed. I've got a few 8266's around here...I suppose I could go with that. Does that get me kicked out of the forum?

No, of course not, as long as you are using Arduino IDE and Arduino language / libraries etc, then you are using it as an Arduino. If you were proposing to use MicroPython or LUA languages, then you would not find much help here. But then I don't think you could do this in those languages anyway. Even with 5~10 times more processing speed, interpreted languages like those can't come close to the speed of compiled C.

If you do go with esp8266, don't forget their pins are not 5V tolerant. You will need to use a voltage divider to get the signal down to 3.3V.

But I suspect a 16MHz Arduino will be fast enough, it would be interesting to find out.

Have a careful look at the DHT22 library. That sensor is using a 26-28µs pulse for a 0 and 70 µs pulse for a 1. Yours are of course even shorter, but for the rest it’s very similar with longer/shorter pulses for 0s and 1s.

I will, thanks!

Youngpatriot:
Use pulseIn to get the high time of each pulse, storing that to an array
Manipulate the data from the array

Seems like a nefarious project.

There's something I don't understand about the timing.

look for the ~90 millisecond interval between pulse streams to determine where to start reading

The 128 pulses (with most 10 us on 10 us off) take a few milliseconds. With the interval, it still looks like bingo numbers are coming at 10/second.

Who is playing this game? :confused:

Nothing nefarious. Just making a homebrew flashboard for existing equipment.

In case my description of the signal isn't clear (likely, sorry), here's a crude illustration:

datastream.png

In this case, it is sending a signal that illuminates the letter "B" and the number 2.

datastream.png

No, I think your original description was clear, the above picture is what I imagined.

So if the code were to measure the intervals between rising edges:

90000 --> start of new frame
50 --> "B" lit
20 --> "1" not lit
50 --> "2" lit
20 --> "3' not lit
....

Okay since it’s not nefarious, I am going to refer to one complete cycle as a “data”

I would make a 5 x 3 array of integers to store few sets of data for processing.

I would choose integers because each subset of the data has 16 values to track so I would use bitwrite to record each subset into one variable.

I think I would actually be more inclined to set ISR to trigger on “Change” as we are looking for a long low, short highs and long highs.

The controller for the board may update frequently but the data itself should change slowly. It would be reasonable to “only” capture 1 set of data and then turn off the ISR during the next set so you can “manipulate” the array. Then turn the ISR back on. Pretty sure most people would not notice if the “new ball” took 100ms longer to show up on your project.

To the more knowledgeable: Any advantage to using timer1's input capture register for this?

I doubt it will make any difference. This is high speed, but not high precision timing.

The accuracy of measuring with the input capture pin is a bit higher, but the 1-2 clock pulses you're off with a normal interrupt shouldn't make a difference in decoding this kind of signal. I'd program it to have anything less than 15µs high "short" and anything 30-50µs high "long". You anyway have to allow for timing inaccuracies on both the sender and receiver side.

I appreciate all the guidance, folks...thank you. The only thing I'm having trouble with is having my measurement start on the first pulse after the long (around 90 millisecond) LOW. I know it should be just a line or two, but I haven't been able to successfully make that happen (although reading the HIGH pulses works). Could someone point me to an example of something even close?

Your own code should be an example of something close... why don't you start by posting that, so we can see what you're doing, and maybe find out where it's going wrong.

So the only place that the signal should be low for more than ~40 usec is between full sets of data pulses. The readFullStream() function (I can attach that if needed) seems to work fine if I just run that alone...I omitted that, but can include it if necessary. I just can't seem to start that function in the correct place. Further, there may be a way better way to approach this in the first place. Like I said - drain bamage :confused:

int pin = 7;
unsigned long duration;

void setup() {
  Serial.begin(115200);
  pinMode(pin, INPUT);
}

void loop() {
  duration = pulseIn(pin, LOW, 200);              // set the timeout to a nice long interval
  if (duration = 0){                                       // zero means it timed out
    readFullStream();                                    // now go read a full set of 128 HIGH pulses
  }
}

Youngpatriot:

  if (duration = 0){

This won't work well.

cattledog:
There's something I don't understand about the timing.

The 128 pulses (with most 10 us on 10 us off) take a few milliseconds. With the interval, it still looks like bingo numbers are coming at 10/second.

Who is playing this game? :confused:

It just keeps sending the current board status that often. I'd guess that it is so that it updates quickly when a number is called. It also flashes the last number/letter on and off with this data stream, so there's that.