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.
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.
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.
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.
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?
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