Detecting pulses of certain lengths using interrupts

I’m working on a project where I use the arduino to decode signals being transmitted wirelessly and received by a 433MHz receiver. The data I’m trying to receive contains a start signal followed by 3 data bytes (at least that’s what I suspect from looking at the signal on an oscilloscope). The start signal is high for 8ms followed by a low for 1ms. After that there’s either a 800us high + 1400us low, or 800us high + 2800us low, constituting a “1” or “0”. As a start I’ve tried to make a code that recognized this start signal by using interrupts to get the time whenever the signal goes high and comparing the period against the start signal (8ms + 1ms = 9ms).

When it detects the start signal I want it to write the following 24 bits into an array or unsigned int for storage.

The problem I have is that I can’t get this to work and I’m not even sure if I’m on the right track. When I run the code it receives something and prints random numbers, both positive and negative numbers, even though there are no pulses that matches the “if-criteria”.

const byte rxPin = 2; // 433MHz receiver
const byte ledPin = 13;

volatile unsigned long firstRise, lastRise, duration = 0;

void setup() {
  Serial.begin(115200);
  pinMode(rxPin, INPUT);
  pinMode(ledPin, OUTPUT);
  attachInterrupt(0, pulse, RISING); // trigger interrupt when pulse rises
}

void loop() {
  // put your main code here, to run repeatedly:

}

void pulse() {

  lastRise = micros(); // time of last rising edge detected
  duration = lastRise - firstRise; // period between last rising edge and first rising edge
  firstRise = lastRise; // new period
  if (8500 < duration < 9500)
  {
    printSomething(duration);
  }
}

void printSomething(int data) {
  Serial.println(data);
  delay(100);
}
if (8500 < duration < 9500)

Works on paper, but not for code.

For code you need two separate comparisons.

if (8500 < duration && duration < 9500)

Also calling printSomething from inside the ISR is a bad idea. Serial shouldn't be used inside an ISR as it has a tendency to lock things up. Instead, set a flag to let loop know that something needs to be printed and call the printing routine from there.

And certainly don't do a delay in an ISR.

What's the name of this modulation? I have a feeling I've seen code for this before.

What you need is a buffer that is filled by the isr, and emptied in the loop.

The isr needs a 'triggered' state that gets set by detecting the preamble.

When triggered becomes true, remember the buffer position.

Collect edges until done (storing the durations in the buffer).

If sequence is valid signal loop, if invalid back up to remembered position.

To have more than one sequence in the buffer, you need a first(or last) of sequence bit. For the buffer unsigned ints work in my implementation. The durations can be shifted down, to make room or make them fit.

OOK or ASK? I don’t really know.

Delta_G:
Also calling printSomething from inside the ISR is a bad idea. Serial shouldn’t be used inside an ISR as it has a tendency to lock things up. Instead, set a flag to let loop know that something needs to be printed and call the printing routine from there.

Thanks. I think I read somewhere that Serial also uses interrupts.

I did some changes to the code and it seems to detect the startpulse and print some numbers that are consistent with the received signal: 111111111100000001000000 : 16760896.
After a while (quite a few prints) it starts printing random numbers again:
111111111100000001000000 : 16760896
11000000000000000000000 : 6291456
11101111111110000000000 : 7863296
11101111111111110000000 : 7864192
11101111111111111111111 : 7864319
11101111111111111111111 : 7864319
11101111111111111111111 : 7864319
11101111111111111111111 : 7864319


const byte rxPin = 2;
const byte ledPin = 13; // blink onboard led when start pulse is detected

boolean startReceive = 0; // == "1" if start pulse is detected
boolean endReceive = 0; // == "1" when 24 bits are received
unsigned long inData = 0; // variable to hold received data

// Pulse definitions
const unsigned int periodMargin = 200; // [us]
const unsigned int periodStart = 9000; // [us], width of start period (800us + 1000us)
const unsigned int periodOne = 2200; // [us] width of period for bit "1", 800 high + 1400 low
const unsigned int periodZero = 3600; // [us] width of period for bit "0", 800 high + 2800 low

volatile byte bitsLeft = 24; // number of bits received


volatile unsigned long firstRise, lastRise, duration = 0; // variables for pulse widths
unsigned long previousMillis, currentMillis = 0; // for keeping track of how long the onboard led has been on.


void setup() {
  Serial.begin(115200);
  pinMode(rxPin, INPUT);
  pinMode(ledPin, OUTPUT);
  attachInterrupt(0, pulse, RISING); // trigger when pulse rises
}


void loop()
{
  // Turn off onboard led
  if (digitalRead(ledPin) ==  HIGH && ((millis() - previousMillis) > 2000)) {
    previousMillis = currentMillis;
    digitalWrite(ledPin, LOW);
  } // end turn off onboard led after approx 2 seconds (doesn't seem to work well because

  // Print incoming data
  if (endReceive) {
    Serial.print(inData, BIN); Serial.print(" : ");
    Serial.println(inData);

    delay(5000);
  }
}

void pulse() {
  lastRise = micros(); // time of last rising edge detected
  duration = lastRise - firstRise; // period between last rising edge and first rising edge
  firstRise = lastRise; // new period
  if ((periodStart - periodMargin) < duration && duration < (periodStart + periodMargin)) {
    bitsLeft = 24;
    inData = 0;
    startReceive = 1; // flag that start pulse is detected and next pulses are data
    previousMillis = millis();
    digitalWrite(ledPin, HIGH);
  }

  // Receive "1"
  if (duration > (periodOne - periodMargin) && duration < (periodOne + periodMargin) && startReceive) {
    bitSet(inData, --bitsLeft);
  }
  // Receive "0"
  if (duration > (periodZero - periodMargin) && duration < (periodZero + periodMargin) && startReceive) {
    bitClear(inData, --bitsLeft);
  }

  // Received all bits
  if (bitsLeft == 0) {
    bitsLeft = 24;
    endReceive = 1;
    startReceive = 0;
  }

}

I suspect all the noise when it’s not receiving signal from the transmitter is causing the interrupt to fire constantly, almost causing the loop() to hang. That could perhaps be a problem.

Inside the ISR, you are not waiting for the previous data to be printed before overwriting it with the new data bits, so the results may look random.

Try using two or more different dataIn longs, and switching between them.

arduinodlb: Inside the ISR, you are not waiting for the previous data to be printed before overwriting it with the new data bits, so the results may look random.

Try using two or more different dataIn longs, and switching between them.

Perhaps it's easier to detach the interrupt before println(inData) and attach it again afterwards? I don't think this is time critical anyway.

Lars81: Perhaps it's easier to detach the interrupt before println(inData) and attach it again afterwards? I don't think this is time critical anyway.

Detaching interrupts is slow. If you are OK with missing some pulses, then just don't collect more bits in the interrupt until after endReceive == 0. And set endReceive to 0 in the loop after you have finished printing and are ready to go again.

But, the better way to do it is to use additional dataIn storage so you don't miss anything.

Incidentally, I just noticed you never set endReceive = 0. That could also be causing you a problem at the moment. Set it to 0 immediately after your prints so you don't print values while they are still being collected.

And take out the delay(5000);

Decide fast whether the interrupt is noise or part of a valid message.

Do not signal every edge, signal full messages.

I described above in detail how I made it work.

If it is any help, I dug up my code for a PPM decoder. This (I think) is the sort of thing radio-controlled cars use, it detects a pulse width (ie. how fast you want the car to go). It isn’t exactly 0s and 1s, but shows how the interrupt code can be quite short.

const byte SIGNAL_PIN = 2;
const int SIGNAL_COUNT = 6;

volatile unsigned long widths [SIGNAL_COUNT + 1];
volatile byte count;

unsigned long lastPulse;

// ISR
void gotPulse ()
  {
  unsigned long now = micros ();
  
  // a long gap means we start again
  if ((now - lastPulse) >= 25000)
    count = 0;
    
  lastPulse = now;
  if (count >= (SIGNAL_COUNT + 1))
    return;
    
  widths [count++] = now;
  }
  
void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  attachInterrupt (0, gotPulse, RISING);
  EIFR = bit (INTF0);  // clear flag for interrupt 0
  }  // end of setup

void loop ()
  {
  if (count >= SIGNAL_COUNT)
    {
    Serial.println (F("Got sequence."));  
    for (int i = 1; i < SIGNAL_COUNT + 1; i++)
      {
      Serial.print ("Channel ");
      Serial.print (i);
      Serial.print (" = ");
      Serial.println (widths [i] - widths [i - 1]);
      }
    count = 0;      
    }
  
  }  // end of loop

Maybe you can play with this:

#include <EdgeBuffer.h>

//      irq, elms, msgMinlen, leadBurst, minPulse
EdgeBuffer ed(2, 150, 15, 8000, 100);

void setup() {
  Serial.begin(115200);
  ed.begin();
}

void loop() {
  if (ed.msg()) {
    while (ed.available()) {
      feedEdges();
    }
  }
}

void feedEdges() {
unsigned int val;
unsigned int TMV;
int zeros = 0;
byte reason = 0;

    do {
      val = ed.read();
      TMV = val & EB_TMask;
    } while ((TMV!=EB_First) && ed.available());
    Serial.print(F("S_"));
    Serial.print(micros());
    for (;;) {
      Serial.write('_');
      Serial.print((val&EB_VMask)<<1);
      if (!(val & EB_High)) {
        zeros++;
      }
      if (TMV==EB_Last||TMV==EB_inval) {
        if (TMV == EB_inval) {
          reason = 'S'; 
        } else {
          reason = 'B';
        }
        if (!ed.available()) {
          ed.msg();  
        }
        break;
      }
      if(!ed.available()) {
        break;
      }
      val = ed.read();
      TMV = val & EB_TMask;
    }
    Serial.print(F("_#"));
    Serial.print(zeros);
    Serial.write('_');
    Serial.write(reason);
    Serial.println();
}

EdgeBuffer.h + EdgeBuffer.cpp attached.

EdgeBuffer.cpp (8.34 KB)

EdgeBuffer.h (2.85 KB)