Attaching newer sensor output protocols like the Single Edge Nibble Transmission (SENT) to the Raspberry Pi Zero can be problematic. I’ve been working on connecting a LX3302A inductive position sensor to the Raspberry Pi using the SENT and PWM output protocols of the IC. My first attempt to use the RPI.GPIO library and a simple polling algorithm failed dramatically. With a lot of filtering, I eventually was able to accurately read a 250Hz PWM signal, but the SENT protocol was just too fast for this method even when using a slower 24 microsecond tick-time. This blog describes the Python script running on a Raspberry Pi Zero that overcame the drawbacks to accurately read a sensor that outputs the SENT protocol.
The solution to the implementation problem was to use a callback function with the fast pigpio library. By supplying a callback function and using the callback() routine in the library, the solution was solved. The library samples the GPIOs of the Raspberry Pi at a default sample rate of five microseconds but can be decreased for even greater resolution. On the pigpio site, is a nice example of how to implement a PWM monitor that quickly solved my PWM sensor output reading problems. I used this PWM example and concept to build the library for SENT and is available for download and view on Github. The code can be run with either python 2 or python 3.
Single Edge Nibble Transmission
The SENT protocol gets its definition from the automobile industry and is used for sensors that are distributed throughout a car that need accurate and robust error checking. Because of this, you will see this protocol’s use on sensors such as power steering sensors, pedal sensors, throttle position valves sensors, and chassis height sensors among others. It is more robust than PWM when your sensor is several feet away from the host computer. Not only is the protocol easy to read with a general-purpose input/output (GPIO) pin of a minicomputer, the receiver can synchronize its timing each packet, the channel can add data redundancy and there is a built-in cyclic redundancy check (CRC). The communication starts with a sync pulse that has a 56 tick time pulse period comprised of 5 ticks low and 51 ticks high. On a microcontroller, this sync pulse can allow precise data timing of subsequent data. On the Raspberry Pi, we just looked for edge triggers and the timing between them. After the sync pulse, are 8 pulse periods that represent 8 nibbles of 4-bit data. Like the sync pulse, these periods are low for 5 ticks, then high from 12 to 27 ticks and this varies as a function of the data value. For a nibble value of zero, the high tick time is 12 ticks. When a nibble value is 15, then that period will be high for 27 ticks. The tick time is fixed with a valid range of 3 to 90 microseconds. While you will need the standard to get the full implementation details, a brief summary is shown below and additional details can be found from the LX3302A inductive position sensor datasheet and other ICs with this output protocol.
SENT packet frame summary
- Sync Pulse: 56 ticks
- 4 bit Status and Message Pulse: 17-32 ticks
- 4 bit (9:12) Data1 Field: 17-32 ticks
- 4 bit (5:8) Data1 Field: 17-32 ticks
- 4 bit (1:4) Data1 Field: 17-32 ticks
- 4 bit (9-12) Data2 Field: 17-32 ticks
- 4 bit (5-8) Data2 Field: 17-32 ticks
- 4 bit (1-4) Data2 Field: 17-32 ticks
- 4 bit CRC: 17-32 ticks
To run this in your scripts, make sure you have the pigpiod daemon running. It needs to be run as a root but can be started when the Linux system starts up.
sudo pigpiod -s 1
The sample rate was set to one microsecond so that it can read three microseconds SENT tick times. If the sensor you are using transmits at slower speeds (i.e. a 24 microsecond tick time), then the default slower sample rate of five microseconds will also work and reduce the workload on the system. With the daemon running, add these modules into your file
import pigpio # http://abyz.co.uk/rpi/pigpio/python.html
Then create pigpio and SENTReader objects
pi = pigpio.pi()
p = read_SENT.SENTReader(pi, SENT_GPIO)
The next step is to read the SENT data where you need it in your code. The SENTReader object will be collecting data in the background and will have the latest ready when you need to get it. The example below shows how both data fields are being read, along with status nibble, crc nibble, tick time, sync pulse size and if any error occurred:
status, data1, data2, ticktime, crc, errors, syncPulse = p.SENTData() print("Sent Status= %s - 12-bit DATA 1= %4.0f - DATA 2= %4.0f - tickTime(uS)= %4.2f - CRC= %s - Errors= %s - PERIOD = %s" % (status,data1,data2,ticktime,crc,errors,syncPulse))
To test out a system, the script can be run directly by directly hooking up a SENT signal to GPIO BCD output 18 (pin 12 on the connector).
Under the Hood of the Script
The Python script uses a threaded 200ms sampling algorithm to allow reading the three microseconds SENT tick times with a one-microsecond GPIO sample rate on the Raspberry Pi. Without this sampling algorithm, the callback functions from the pigpio daemon cannot be serviced fast enough and an eventual error occurs. The error first manifests itself as latency in reading the sensor output by as much as 10-20 seconds. The pigpio is reading each GPIO edge and sending callback commands to the self._cbf() routine, but this routine is not fast enough to keep up. As a result, the calls get queued up.
The sampling is handled by the SampleCallBack() routine.
# this will run in a loop and sample the SENT path # this sampling is required when 1us sample rate for SENT 3us tick time
self.SampleStopped = False
self._cb = self.pi.callback(self.gpio, pigpio.EITHER_EDGE, self._cbf)
# wait until sample stopped
while self.SampleStopped == False:
# gives the callback time to cancel so we can start again.
To solve the speed issue, the self._cbf() routine will collect two complete SENT frames and then cancel the pigpio self.pi.callback() function with a self.cancel() command and set self.SampleStopped = True. The routine above will look for this event, then wait 200 milliseconds to allow the code to clear, before calling the callback() routing again with self.pi.callback(). This code worked for a one microsecond sampling of a three microsecond SENT tick time signal. Slower sampling rates and longer tick times will allow this sleep time to be reduced.
When the sent message and slow channel data that is transmitted over many frames are implemented, the sleep will need to be increased to allow more SENT frames to be captured. On the other hand, faster Raspberry Pi variants will allow this time to be decreased.
Summary and Future Improvements
I hope this provides a good start on implementing SENT on the Raspberry Pi, expanding the pool of sensor types that can be used directly without any intermediate microcontroller. The advantage of the SENT protocol is its built-in robust error checking. This is an advantage when reading this with the Linux based non-real-time operating system. While the pigpio library works really well, any errors that could occur during the routines polling can be double-checked with the redundancy built into the CRC code. This SENT library is just the start of the protocol. In the status nibble is the ability to transmit additional messages and data. Stay tuned as these are added in the future.
Also, I would love to hear any improvement you would like added to the code. Is there a better way to implement it? Are there any features that you need in your project? Just let me know in the comments below.
- SENT SAE J2716 Protocol
- LX3302A Inductive Position Sensor with SENT
- pigpio library : The C based routine that samples the GPIOs at 5us or less