HELP! New to fine tune this code

Hi,

I am receiving the following signals on Pin 2 on an Arduino Uno,

In the picture attached b0-b8 in data signal2 can be either 1's or zeros. all bits in data signal1 are zeros.

I have written the following code to capture and output to serial monitor:

#define MAX_BUFF 32
#define SS_SPEED 9600 //speed in Hz

uint8_t buffer_head=0, buffer_tail=0, brk=0;
uint16_t Buffer[MAX_BUFF];
unsigned long us_delay;

void rcev() {
  uint8_t d = 0;

    // Wait approximately 1/2 of a bit width to "center" the sample
    delayMicroseconds(us_delay/2);

    // Read each of the 8 bits
    for (uint8_t i=8; i > 0; --i)
    {
      delayMicroseconds(us_delay);
      d >>= 1;
      
      if (digitalRead(2)==HIGH) d |= 0x80;
    }

    delayMicroseconds(us_delay);

    if (digitalRead(2)==HIGH){ //confirm bit nineth is high
      uint8_t next = (buffer_tail + 1) % MAX_BUFF;
      
      if (next != buffer_head)
      {
        // save new data in buffer: tail points to where byte goes
        Buffer[buffer_tail] = d; // save new byte

        if(brk==1){
          Buffer[buffer_tail] |=0x0100;
          brk=0;
        }
        
        buffer_tail = next; 
      }
    else{
      brk=1; 
    }
  }
}
void setup() {
  Serial.begin(9600);

  us_delay = 1000000/SS_SPEED;

  pinMode(2, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(2), rcev, FALLING);

}

void loop() {
    if (buffer_head != buffer_tail){

      if(Buffer[buffer_head]&0x0100==0x0100) Serial.println("brk: ")
      else Serial.print(" ")
  
      Serial.print(String(Buffer[buffer_head],HEX));
      buffer_head = (buffer_head + 1) % MAX_BUFF;
    }
}

the output seem to be ok for the first few data picked up but after that it got completely random which I suspect is because the timings to read the data is drifting.

As the bit reads are time critical, I would be grateful if anyone could help out with fine tuning the timings.

I have already set the complier to optimise code for speed (O3)

the code should be able to work for a frequecy range of 9-21khz

Logic failure! I see you are expecting 9 bits, the first being a start bit. Then 8 data bits. All normal. Then you expect an interrupt on the next bit going low to be another start bit. But I don't see where you include a "stop" bit, always high, so you have 10 bits per data character.

So, are you getting out of time after the first 9 bits?

Paul

      Serial.print(String(Buffer[buffer_head],HEX));Why the conversion to String ?

Image from Original Post so we don't have to download it. See this Image Guide

...R

If you are trying to write your own version of 'software serial' my Yet Another Software Serial may be of interest.

...R

you are right Robin,
I am trying to write a modified version of SoftwareSerial but am struggling with getting a working code that would reliably detect data signal1 (which by the way can be more than 14 bits long) hence my posting here for help on improving my code.

And thank you for pointing me on how to inline images - I was wondering how to do that! :slight_smile:

sherzaad:
you are right Robin,
I am trying to write a modified version of SoftwareSerial but am struggling with getting a working code that would reliably detect data signal1

I reckon you could extend my code to deal with any number of bits.

However, when you mention non-standard data lengths it makes me wonder what you are doing and whether there could be a much simpler way.

Where is the data coming from?
Why are there non-standard lengths?
Can you re-write the software that is sending the data? if not, post a link to the datasheet for the device that is sending it.

...R

... or NeoSWSerial.

If you connect the RX pin to a different digital input pin, you can receive characters on the RX pin with AltSoftSerial or NeoSWSerial, and detect the BREAK condition with the other digital input pin.

Here's a sketch that shows how to test it:

static const int RXpin    = 2;
static const int TXpin    = 3;
static const int breakPin = 4;  // break detector, normally connected to RXpin

  // For testing, connect the breakPin to the TXpin so we can simulate "sending"
  //   a BREAK to ourself.

#include <NeoSWSerial.h>
NeoSWSerial testPort( RXpin, TXpin );

static const unsigned long BAUD_RATE = 9600;
static const uint32_t MIN_BREAK_TIME = (13 * 1000000) / BAUD_RATE + 1; // microseconds

//  Variables for detecting the BREAK signal on the breakPin
uint32_t lastLow;
bool     lastState     = false;
bool     breakReceived = false;

//  Variables for simulating the BREAK signal on the TXpin
uint32_t lastTest;
uint8_t  testing = 1;

void setup()
{
  Serial.begin( 9600 );
  Serial.println( F("Break test") );
  Serial.flush();

  lastTest = micros();
  testPort.begin( BAUD_RATE );
}

void loop()
{
  checkForBreak();

  simulateBreak();

} // loop


void checkForBreak()
{
  bool rxState = digitalRead( breakPin );

  if (lastState != rxState) { // breakPin changed?
    lastState = rxState;

    if (!rxState) {
      lastLow = micros();     // Remember when it went LOW
    } else {
      breakReceived = false;  // It went HIGH, clear the break flag
    }

  } else { // same state

    if (!rxState) {
      uint32_t lowTime = micros() - lastLow;

      if (!breakReceived && (lowTime >= MIN_BREAK_TIME)) {
        breakReceived = true; // stays true until line goes high again

        Serial.println( F("BREAK") );// do something?
      }
    }
  }
} // checkForBreak


void simulateBreak()
{
  uint32_t dt = (micros() - lastTest);

  //  Once per second, start a BREAK signal
  if (dt > 1000000UL) {
    startBreak();

  } else if (!testing) {

    // do nothing from dt = 0..MIN_BREAK_TIME
    if (dt > MIN_BREAK_TIME+8) { // plus a little longer
      stopBreak();
    }

  } else { // testing -- send characters

    //  At 9600 each char sent takes 1.0417ms.  Don't send them any faster than that.

    static const uint32_t CHAR_INTERVAL = 1043; // us
                 uint16_t charToSend    = dt/CHAR_INTERVAL;

    //  Send chars until...
    if ((dt <= (1000000UL - 3*CHAR_INTERVAL))
        //  ...it's almost time to start the BREAK, *AND*
                     &&
        (testing != (charToSend & 0xFF))) {
        //  ...it's time to send another character

      testing = charToSend;      // `testing` is the current character interval and...
      testPort.write( testing ); // ... the byte to send

      // Also display the "printable" character we are sending
      if ((' ' <= testing) && (testing <= '~')) {
        Serial.write( testing );
      }
    }
  }
} // simulateBreak


void startBreak()
{
  testPort.end();             // stop sending characters...
  pinMode( TXpin, OUTPUT );
  digitalWrite( TXpin, LOW ); // ...so we can start the BREAK signal

  Serial.println();
  lastTest = micros();
  testing  = 0;

} // startBreak


void stopBreak()
{
  digitalWrite( TXpin, HIGH ); // stop the BREAK signal
  pinMode( TXpin, INPUT );
  testPort.begin( BAUD_RATE );
  testing = 2; // wait 2 whole character times before sending test chars

} // stopBreak

When you run it, it should display the following:

Break test
 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
BREAK
 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
BREAK

You should see "BREAK" appear once per second. The other characters are just to test that it doesn't detect a legal character as a BREAK condition.

For your sketch, you'll use the same break detection code, but don't use the simulation code. Connect the breakPin to the RXpin

Using a separate pin to detect the break condition has been discussed several times recently. We keep coming back to using a separate pin so you don't have to modify an existing library (or write your own).

Hello Dev,

Thank you for your suggestion.

I did do something similar to detect the "break" but used SoftwareSerial library instead. It was having the same problem I mentioned in my initial post when I actually applied it to the real signal. maybe the libraries you suggested may work better...

I will give it a go.

Robin2,

Robin2:
if not, post a link to the datasheet for the device that is sending it.

Check out LIN Communication

sherzaad:
Check out LIN Communication

I helped with something similar about 3 years ago in this Thread

I have forgotten the details now but I think the key was to detect the inter-frame space by measuring the time between bits.

...R

Robin2:
I helped with something similar about 3 years ago in this Thread

Its unfortunately not the same protocol Robin2.

As mentioned the main problem is detecting the the "break" event reliably all other received data is you normal UART.

Dev' solution unfortunately did not work for me either :frowning:

sherzaad:
Its unfortunately not the same protocol Robin2.

As mentioned the main problem is detecting the the "break" event reliably all other received data is you normal UART.

But maybe there are similarities. A huge amount of programming is using bits of one idea to solve a different problem.

What is a "break event"? It sounds like a longer period of time with no change in the state of an input? So recording the time (micros) whenever there is a change of state should provide the data for detecting a long interval. Something like this pseudo code

if (change-of-state) {
   lastStateChange = micros();
}
if (micros() - lastStateChange >= breakIntervalMicros) {
   // a break has been detected
}

...R

Dev' solution unfortunately did not work for me either :frowning:

Inconceivable. And vague.

How did you connect everything?

What code?

How do you know the BREAK duration? You should use an oscilloscope or logic analyzer to verify the timing. You could also write a sketch to display the min and max LOW times that are greater than 10 bit times.

The sketch I provided should absolutely work if it is connected correctly and the MIN_BREAK_TIME is set correctly.

Hi,

After may trial and error I have been able to partly solve the problem I was having with the timing; it's still not quite reading the data correctly but it seems that the "serial.print" was affecting the read timing as increasing serial baud rate to 250k improved the data being read.

As I said it's still quite not right but at least its not complete garbage that I'm reading on the serial moritor. Its consistent and no longer completely random.

still need to fine tune the timings somehow....

Good to hear you are making progress.

If you would be kind enough to post your code it would help others.

...R