Decoding SENT protocol using Arduino Uno

Hello,

I am trying to decode the SENT signal from my high pressure sensor using an arduino. I am trying to calculate the pulse duration of falling edges using input capture registers feature of timer1. But I cannot understand is the output from the capture register is clocks or the pulse duration between the falling edges or what does it capture. Please explain me understand this. I will provide my code below.

const int capturePin = 8; // The pin where the input signal is connected

volatile unsigned long pulseDuration = 0;
volatile unsigned long clocks = 0;


void setup() {
  pinMode(capturePin, INPUT);
  Serial.begin(9600);

  noInterrupts();
  // Configure Timer1 for Input Capture mode with falling edge trigger
  TCCR1A = 0; // Clear Timer1 control registers
  TCCR1B = 0;

  // Set the prescaler 
  TCCR1B |= 0b10000010;

  // Enable Input Capture Interrupt
  TIMSK1 |= 0b00100000;

  // Set the initial value for the Input Capture Register
  ICR1 = 0;

  // Enable global interrupts
  interrupts();
}

// Interrupt service routine for Input Capture
ISR(TIMER1_CAPT_vect) {
clocks = ICR1; 
pulseDuration = clocks / 16;
}

void loop() {
  // This is where you can use the pulse duration
  // For example, you can print it to the Serial monitor

  Serial.print("clocks: ");
  Serial.println(clocks);
  Serial.print("pulseDuration: ");
  Serial.print(pulseDuration);
  Serial.println(" microseconds");
  
  // Add a delay or perform other tasks as needed
  //delay(1000); // Delay for 1 second (adjust as needed)
}



maybe try the code in the link offered here:

or this one (in same thread! :wink: )

hope that helps....

1 Like

But this code is only giving me the minimum values all the time. When I connected some pressure sources and tried to read the pressure from the sensor and it still shows the same minimum value?
What can be the issue ?

Please post your current code, so we may offer comments that are appropriate.

maybe you need to 'request' to send you the updated value....

would you need to check that the datasheet for your sensor says about how it sends out its readings...

I am using this code now and it works fine for now. But I have a question regarding this is whether this will continuously keep calculating the pressure or it stops after some values ? Because I once saw that it calculates a certain number of values and then it stops after a while.

#include <Wire.h> // Include Wire library for I2C
#include <LiquidCrystal_I2C.h> // Include LiquidCrystal library for I2C

LiquidCrystal_I2C lcdPressure(0x27, 16, 2);


// Connect data pin of sensor to D8 of the Arduino Uno/Nano/Mini

// Time for one tick (in µs)
constexpr uint8_t tick_time = 3;

constexpr auto serial_baud = 115200;

/************************************************************/

constexpr uint8_t tick_syn = 56;
constexpr uint8_t tick_offest = 12;

constexpr uint16_t cycl_tick = F_CPU / 1000000 * tick_time; // CPU cycles per tick

// Allow +/- 20% difference
constexpr uint16_t cycl_syn_min = cycl_tick * tick_syn * 4 / 5;
constexpr uint16_t cycl_syn_max = cycl_tick * tick_syn * 6 / 5;

constexpr auto LOOKUP_SIZE = 256;
constexpr auto LOOKUP_DIV = 4;

uint16_t cycl_syn;
uint16_t cycl_offset;
int8_t cycl_lookup[LOOKUP_SIZE];

constexpr uint8_t BUFFER_SIZE = 32;

volatile uint16_t buffer[BUFFER_SIZE];
volatile uint8_t buffer_write_pointer;
volatile uint8_t buffer_read_pointer;

uint16_t last_icr = 0;
volatile bool error = false;
ISR(TIMER1_CAPT_vect) {
  uint16_t value = ICR1;
  buffer[buffer_write_pointer] = value - last_icr;
  last_icr = value;

  buffer_write_pointer++;
  if (buffer_write_pointer >= BUFFER_SIZE) {
    buffer_write_pointer = 0;
  }
  if (buffer_write_pointer == buffer_read_pointer) {
    error = true;
  }
}

bool buffer_available() {
  return buffer_write_pointer != buffer_read_pointer;
}

uint16_t buffer_read() {
  while (!buffer_available()) {
    // wait for data
  }
  uint16_t ret = buffer[buffer_read_pointer];
  auto new_p = buffer_read_pointer + 1;
  if (new_p >= BUFFER_SIZE) {
    new_p = 0;
  }

  uint8_t oldSREG = SREG;
  cli();
  buffer_read_pointer = new_p;
  SREG = oldSREG;

  return ret;
}

uint16_t get_syn_cycl() {
  while (true) {
    uint16_t dx = buffer_read();
    if (dx >= cycl_syn_min && dx <= cycl_syn_max) {
      return dx;
    }
  }
}

void wait_for_syn() {
  while (true) {
    uint16_t dx = buffer_read();
    auto t = dx > cycl_syn ? dx - cycl_syn : cycl_syn - dx;
    if ( t < 10) {
      return;
    }
  }
}

void setup() {
  Serial.begin(serial_baud);
  lcdPressure.init();       // Initialize the temperature LCD
  lcdPressure.backlight();    // Turn on the backlight for temperature LCD
  lcdPressure.setCursor(0, 0);
  lcdPressure.print("Press:       bar");
 


  Serial.print("cycl_tick = ");
  Serial.println(cycl_tick);

  TCCR1A = 0;
  TCCR1B = 1;
  TCCR1C = 0;

  TIMSK1 = (1 << ICIE1);

  cycl_syn = get_syn_cycl();
  cycl_offset = cycl_syn * (tick_offest * 2 - 1) / tick_syn / 2; // cycl_tick * 11.5

  for (uint16_t c = 0; c < LOOKUP_SIZE; c++) {
    int8_t value = c * tick_syn * LOOKUP_DIV / cycl_syn;
    if ( value >= 16) {
      value = -1;
    }
    cycl_lookup[c] = value;
  }

  auto cycl_syn1 = get_syn_cycl();
  auto dx = cycl_syn > cycl_syn1 ? cycl_syn - cycl_syn1 : cycl_syn1 - cycl_syn;

  Serial.print("cycl_syn = ");
  Serial.println(cycl_syn);

  Serial.print("cycl_syn ok? ");
  Serial.println(dx < 10 ? "yes" : "no");

  Serial.print("cycl_offset = ");
  Serial.println(cycl_offset);

  if (LOOKUP_SIZE < (cycl_syn * 17 / tick_syn / LOOKUP_DIV)) {
    Serial.println("Lookup table would exceed expected size!");
    while (true) {
      ;
    }
  }

  Serial.println("Lookup table:");
  for (int x = 0; x < 16; x++) {
    Serial.print(x * 16, HEX);
    Serial.print(": ");
    for (int y = 0; y < 16; y++) {
      Serial.print(cycl_lookup[x * 16 + y], HEX);
      Serial.print(" ");
    }
    Serial.println("");
  }
  Serial.println("************************");
  Serial.flush();
  error = false;
}

constexpr char toHex[] = "0123456789ABCDEF";
void loop() {
  constexpr auto FRAME_SIZE = 8;
  char frame[FRAME_SIZE + 1];

  wait_for_syn();
  
  for (int c = 0; c < FRAME_SIZE; c++) {
    auto dx = buffer_read();
    dx = (dx - cycl_offset) / LOOKUP_DIV;
    if (dx > LOOKUP_SIZE || cycl_lookup[dx] < 0) {
      frame[c] = '!';
    } else {
      frame[c] = toHex[cycl_lookup[dx]];
    }
  }

  // Extract the 2nd, 3rd, and 4th hex digits and convert to a numerical value
  char hexDigits[] = {frame[1], frame[2], frame[3], '\0'};
  int digitalValue = strtol(hexDigits, NULL, 16);

  // Calculate the pressure using the provided formula
  float pressure = 0.25 * digitalValue - 50;

  Serial.print("Digital Value: ");
  Serial.println(digitalValue);
  Serial.print("Pressure: ");
  Serial.println(pressure,2);

  lcdPressure.setCursor(6, 0);
  lcdPressure.print(pressure);
  
  Serial.write(frame, FRAME_SIZE);
  Serial.println("");


  delay(1000);

}

In the original code the ISR checks for a ringbuffer overflow and sets error to true. loop() checked it and stopped.

You probably want to add overflow checking again to detect corrupt frames:

BTW.: The code has problems with integer overflows when tick_time > 3 or the CPU is clocked faster than 16Mhz. Also when the sensor is sending a padding, it can be detected as sync and ruin the calculation of the lookup table in setup().

How can i discard the frame which has this error true. The problem is only when the frame has this ! expression included. How can I solve this issue?

I added a few lines (See // <-----------)

  error = false;  // <-----------
  wait_for_syn();
  
  for (int c = 0; c < FRAME_SIZE; c++) {
    auto dx = buffer_read();
    dx = (dx - cycl_offset) / LOOKUP_DIV;
    if (dx > LOOKUP_SIZE || cycl_lookup[dx] < 0) {
      frame[c] = '!';
      error = true;  // <-----------
    } else {
      frame[c] = toHex[cycl_lookup[dx]];
    }
  }

if(error){  // <-----------
  return;   // <-----------
}           // <-----------
1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.