Reading a custom serial protocol transmitted on one wire

Hi, everyone

I'm new to these forums, so I apologize if this has been addressed in another topic. I have a device that transmits serial data on a single wire bus. I want to read the data bits via my Arduino UNO and print to the serial screen in the IDE on my PC. There is a twist that I will describe:

On the physical wire, a single bit period is 100us. These physical bits from the wire then are translated to the 'data' bits.

The start bit for the beginning of the packet is, in terms of the physical bits, LOW/LOW/LOW/HIGH, or 0001, (the start bit has a total length of 400us, or 4 bits on the physical bus).

Furthermore, a 1 in terms of a 'data' bit would be LOW/LOW/HIGH, or 001, on the physical bus (a 1 in terms of data is 300us long, or 3 bits on the physical bus)

A 0 in terms of the 'data' bit would be LOW/HIGH, or 01, on the physical bus (a 0 in terms of data is 200us long, or 2 bits on the physical bus).

An example, let's say the 'data'byte is: [start bit] + 11010011. On the physical bus you would read the following bits: 0001 (start bit) 001 001 01 001 01 01 001 001.

The bus is active low.

I'm wondering if anyone has a good starting point in terms of code that I could use to read the physical bits from the bus and then translate them into the 'data' bits/bytes.

Thank you,

Look for "bit banged uart serial" source code for AVR or ATmega328. Other alternatives include software to decode remote RF weather sensors, which use a similar encoding scheme.

Most people would use a hardware timer for the bit timing, and so the code is processor-specific.

a simple algorithm could be

  1. the start bit could be detected using change of level interrupt
  2. a 50uSec timer is started
  3. on odd timer interrupts the center level of each bit is sampled

is your data 11010011 along the lines of (assuming active LOW)
image

Observation. All three "types" of bit end with a single-period high. The line is left high between transmissions. Conversely, all three types of bit start with a high-low transition. So it should be a "simple" matter of tracking the time between high-low, and low-high, transitions. You might even capture "micros()" at each transition, buffer the transition times in a ring buffer, and analyze outside the interrupt routine.
YMMV.

Hello, Horace

Yes, the physical bus is along the lines of your image. Do you know where there may be a template of code to help me get started with what you described in post#4 - I'm not familiar with all the function libraries for the arduino that I may use for this.

Thank you

This is an interesting way to approach this - I"m not sure of all the arduino functions I could use to accomplish this. Could you share the main ones I would use?

Thank you

trying

  1. the start bit detected using change of level interrupt
  2. a 50uSec timer is started
  3. on odd timer interrupts the center level of each bit is sampled

for post 5 data 11010011 gives

1 1 1 0 1 1 0 1 1 0 1 0 1 1 0 1 0 1 0 1 1 0 1 1 0 0 0 0 0

one could look for 000001110 start bits then start decoding

what is the STOP bit(s) just return to idle?

Hello, Horace

Yes

have a look at the following which works on an ESP32

// ESP32 read serial bit patterm every 50uSec

#include "ESP32TimerInterrupt.h"

// Init ESP32 timer 0
ESP32Timer ITimer0(0);
#define RXPIN 16
#define MAX_LENGTH 30

volatile long printer = millis();
volatile int start = 0;
volatile bool startError = false, dataError = false;

// Timer0 interrupt handler - output data pattern
volatile int counter = 0;
volatile byte dataByte = 0, errorByte = 0, inputByte, input[MAX_LENGTH] = { 0 };  // stoes bits read
volatile int inputIndex = 0, oddCount = 0, bitCount = 0;
void IRAM_ATTR handleInterrupt2();

bool IRAM_ATTR TimerHandler0(void *timerNo) {
  byte bitValue = digitalRead(RXPIN);           // read digital value
  if ((++oddCount % 2) && (inputIndex < 50)) {  // if odd and winthin array count
    inputByte = (inputByte << 1) | bitValue;    // add current bit to input byte
    if ((inputIndex >= MAX_LENGTH) || (bitCount == 0 && bitValue == 1 and inputIndex > 2)){                                      // ERROR! exceeds maximum data length
      if (inputIndex >= MAX_LENGTH) dataError = true;       // error!
      else startError=true;
      errorByte = inputByte;  // note errors                        //   bitCount = 1;
      ITimer0.stopTimer();    // stop timer ready for next start bit level change
      attachInterrupt(digitalPinToInterrupt(RXPIN), handleInterrupt2, CHANGE);
      return true;
    }
    input[inputIndex++] = bitValue;           // add  bit to data array
    if (bitValue == 0) {           // if bit is 0 check if start bit or data bit
      if (bitCount == 0) {         // checking start bit
        if (inputByte != B1110) {  // check start bits 1110
          startError = true;       // error!
          errorByte = inputByte;   // note errors                       
          ITimer0.stopTimer();     // stop timer ready for next start bit level change
          attachInterrupt(digitalPinToInterrupt(RXPIN), handleInterrupt2, CHANGE);
        }
        inputByte = dataByte = 0;   // start bits OK 
        bitCount = 1;               // check next for data bits
      } else {                    
        if (bitCount++ <= 8) {      // check data bits else finished
          if (inputByte == B110)                // is a 1
            dataByte = (dataByte << 1) | 1;     // add 1 to data byte
          else if (inputByte == B10)            // is a 0
            dataByte = dataByte << 1;
          else {                                // is an error!
            dataError = true;       // error!
            errorByte = inputByte;  // note errors                       
            ITimer0.stopTimer();    // stop timer ready for next start bit level change
            attachInterrupt(digitalPinToInterrupt(RXPIN), handleInterrupt2, CHANGE);
            return true;
          }
          inputByte = 0;
        } else {                    // data complete 
          bitCount = 0;
          ITimer0.stopTimer();  // stop timer ready for next start bit level change
          attachInterrupt(digitalPinToInterrupt(RXPIN), handleInterrupt2, CHANGE);
        }
      }
    }
  }
  counter++;
  return true;
}

// interrupt handler - change in level
void IRAM_ATTR handleInterrupt2() {
  if (!digitalRead(RXPIN)) return;                // look for rising edge
  detachInterrupt(digitalPinToInterrupt(RXPIN));  // detach the interrupt
  counter = inputIndex = oddCount = 0;            // reset array index etc to start
  start = 1;
  bitCount = inputByte = dataByte = 0;  // clear data bits
  startError = dataError = false;
  printer = millis();      // restart the printer timer
  ITimer0.restartTimer();  // restart the bit timer
}

void setup() {
  Serial.begin(115200);
  pinMode(RXPIN, INPUT_PULLDOWN);
  attachInterrupt(digitalPinToInterrupt(RXPIN), handleInterrupt2, CHANGE);
  delay(500);
  // setup timer 0 interrupt ever 500uSec
  if (ITimer0.attachInterruptInterval(50, TimerHandler0)) {
    ITimer0.stopTimer();  // stop timer ready for start bit level change
    Serial.print(F("Starting  ITimer0 OK "));
  } else
    Serial.println(F("Can't set ITimer0. Select another freq. or timer"));
}

// calculate period width in microseconds
void loop() {
  // if had inperrupt on pin - start bit?
  if (start) {
    Serial.print("\nstart ");
    start = 0;
    // printer = millis();                             // restart the printer timer
  }
  // after 10 seconds print average pulse width time
  /* if (millis() - printer > 1000) {
    printer += 1000;
   // Serial.println(counter);  // print average pulse width
    counter = 0;              // reset initialse values
                              // if (restart++ == 10) attachInterrupt(digitalPinToInterrupt(RXPIN), handleInterrupt2, CHANGE);
  }*/
  if (startError) {
    startError = 0;
    Serial.print("start bit ERROR! ");
    Serial.print(errorByte, BIN);
  }
  if (dataError) {
    dataError = 0;
    Serial.print(" data ERROR! ");
    Serial.print(errorByte, BIN);
  }

  if (bitCount == 9) {
    //Serial.printf("bitCount %d dataByte ", bitCount);
    Serial.print(dataByte, BIN);
    Serial.print(" ");
    Serial.println((char)dataByte);
    for (int i = 0; i < inputIndex; i++) Serial.print(input[i]);
    Serial.println();
    bitCount = dataByte = 0;
  }
}

transmitting sample data to program serial monitor displays

start 11010011 �
11101101101011010101101100

start 1100001 a
111010110110101010101100

start 1100001 a
111010110110101010101100

start 1100010 b
111010110110101010110100

start 1100010 b
111010110110101010110100

start 1100011 c
1110101101101010101101100

start 1100100 d
111010110110101011010100