SPI bus not behaving as expected on the timing

  • Yes we are using spidev directly, this is how we use the Infrared. Might as well share the whole file:
    The run method in a different file calls get_ir_signal() in a loop and returns something if the code matches.
        self._spi = SpiDev(1, 0)
from spidev import SpiDev
from configparser import ConfigParser
import time

SEND_IR = 1
READ_IR = 2
CONF_IR_TIMER = 10


class Infrared:
    """
    Class for sending receiving infrared signals
    """

    def __init__(self):
        """
        Imports IR codes from config file and initiates SPI bus for IR interface
        """
        self.parser = ConfigParser()
        self.parser.read("config.ini")
        self._spi = SpiDev(1, 0)
        self._spi.mode = 0b01
        self._read_toggle = "0"
        self._write_toggle = "0"
        self._last_code = ""

    def _read_write(self, code=None):
        """
        Read write function. SPI interface reads everytime it writes. If code is none means we want to read.
        For reading a zero message is generated and sent just to read from the bus.
        For sending signal a message is extracted from hex code provided and sent over SPI bus. System will read
        incoming signals even when sending.
        :param code: IR code as hex string
        :return: data read from SPI bus
        """
        speed = 40000
        if code is None:
            message = self._get_zero_message()
        else:
            message = self._hex_to_spi_output(code)

        data = self._spi.xfer2(message, speed)
        return data

        # TODO: lägg in något stopp för längd på meddelande

    def _get_raw_ir_data(self):
        """
        Reads and decodes raw IR data from SPI bus. A buffer from SPI bus is read and recoded. If no data is reveived
        all data in buffer will be 255 and ignored. If there are zeros in buffer it will be decoded.
        Values < 125 = 0, values >= 125 = 1.
        The infrared signal will look like 0000111100001111 with 4 ones/zeros representing low/high. This is the way
        infrared protocols work. One bit in the RC5 protocol is represented by two bits, a one is 01 and a zero is 10
        For more info read up on Infrared RTC5 protocol.
        The filtering will read 3 ro more ones/zeros in a row as one bit. 00001111000111 = 0101
        Six or more ones/zeros in a row will be read as two bits. 00011100000001111 = 01001
        Data will be read until length of message is greater than 28 bits.
        If there are more than 9 ones in a row the message is incomplete and None is returned.
        :return: list of 28 integers representing the RC5 14 bit signal. None if incomplete.
        """
        start_bit_read = False
        zeros_added = ones_added = 0
        zeros = ones = 0
        bits = "1"
        data = self._read_write()
        read = data.count(0) > 0
        while read:
            for d in data:
                if d < 125:
                    start_bit_read = True
                    zeros += 1
                    if zeros_added == 0 or (zeros > 5 and zeros_added < 2):
                        bits += '0'
                        zeros_added += 1
                    ones = 0
                    ones_added = 0
                else:
                    if start_bit_read:
                        ones += 1
                        if ones_added == 0 or (ones > 5 and ones_added < 2):
                            bits += '1'
                            ones_added += 1
                        zeros = 0
                        zeros_added = 0
                        if ones > 9:
                            if len(bits) > 28:
                                bits = bits[0:28]
                                print(bits)
                                return bits
                            else:
                                return None
            data = self._read_write()
        return None

    def _filter_raw_ir_signal(self, raw):
        """
        Filers 28 integer list to RC5 14 integers signal.
        Converts 01 -> 1 and 10 -> 0.
        :param raw: unfiltered data
        :return: RC5 code, toggle status of message
        """
        toggle_bit = ""
        rc5_code = None
        if raw is not None and len(raw) >= 28:
            signal = ""
            for i in range(0, 28, 2):
                signal += raw[i + 1]
            if len(signal) != 14:
                print("wrong signal", signal, len(signal))
                print("raw", raw)
                return None, ""
            data = signal[len(signal) - 11:len(signal)]
            # data = signal[3:14]
            toggle_bit = signal[2]
            rc5_code = hex(int(data, 2))
        return rc5_code, toggle_bit

    def _hex_to_spi_output(self, hex_string):
        """
        Converts RC5 hex code to SPI data to be sent
        0 -> [255, 255, 255, 255, 0, 0, 0, 0]
        1 -> [0, 0, 0, 0, 255, 255, 255, 255]
        :param hex_string: RC5 code
        :return: Buffer for SPI bus
        """
        scale = 16
        num_of_bits = 11
        self._write_toggle = str(int(not int(self._write_toggle)))
        bit_string = "00" + self._write_toggle
        bit_string += bin(int(hex_string, scale))[2:].zfill(num_of_bits)
        spi_output = []
        for bit in bit_string:
            if bit == "0":
                spi_output += [255, 255, 255, 255, 0, 0, 0, 0]
            else:
                spi_output += [0, 0, 0, 0, 255, 255, 255, 255]
        return spi_output

    def _get_zero_message(self):
        """
        Return zero buffer to be sent on SPI bus
        :return: SPI buffer full of zeros
        """
        message = []
        for i in range(32):
            message += [0, 0, 0, 0]
        return message

    def get_ir_signal(self):
        """
        Decode RC5 code and find which key the code represents
        :return: key representation, RC5 code
        """
        key_pressed = None
        raw = self._get_raw_ir_data()
        rc5_code, toggle_bit = self._filter_raw_ir_signal(raw)

        if rc5_code is not None and (rc5_code != self._last_code or self._read_toggle != toggle_bit):
            for key, value in self.parser.items("rc5_read"):
                if value == rc5_code:
                    key_pressed = key
            self._read_toggle = toggle_bit
            self._last_code = rc5_code
            self._read_write()
            print("key_pressed ", key_pressed, "rc_5 ", rc5_code)
        return key_pressed, rc5_code

    def send_ir_signal(self, key, group):
        """
        Send IR signal for key pressed
        :param key: Key pressed
        """
        parser_key = f"rc5_send_{group}"
        value = self.parser[parser_key][str(key)]
        print("send key group", key, group, value)
        self._read_write(value)
        time.sleep(0.5)

Here is the community thread we found it in:

But yeah I guess you are right since the workaround didn’t really help. We have also tried filling the SPI buffer with a lot of bytes to reduce the amount of time delays but that did not work either, also it comes with another set of problems.