Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Framing error #7

Closed
waleckaw opened this issue Jun 4, 2020 · 8 comments
Closed

Framing error #7

waleckaw opened this issue Jun 4, 2020 · 8 comments
Labels

Comments

@waleckaw
Copy link

waleckaw commented Jun 4, 2020

Apologies, this is not an issue with your library

I am writing an application the STM32 B-L475E-IOT01A discovery board that uses the SPBTLE-RF BlueNRG chip as well.

I have been dealing with the following issue for a few days now. If you ever encountered a similar issue, I am wondering what you did to fix it.

The Issue:

I am having trouble interfacing with the B-L475E-IOT01A's on-board SPBTLE-RF bluetooth chip.

I am able to send header packets inquiring about the the bluetooth chip's write and read buffers. I have confirmed that I am doing this correctly, because I get responses in the correct format: 0x02 0x?? 0x00 0x?? 0x00. The ?? bytes always give me the correct number of available writable bytes and bytes to be read, respectively.

I am also able to read messages from the bluetooth chip, as I always receive the expected packet when I perform a chip reset: 0x04 0xff 0x03 0x01 0x00 0x01, and after sending an HCI command packet I am able to poll for and read the responses described below.

The issue that I am having is that, whenever I check that the write buffer is available and attempt to send a full write message such as ...

header = 0x0A 0x00 0x00 0x00 0x00 (check write buffer... only sends rest of message if write buffer > 0)
HCI command packet = 0x1003 (example: Read_Local_Supported_Features)
param total length = 0x00 (i have also tried not including this byte at all and raising the chip select one byte sooner instead)
params: none

... I always receive the same HCI event packet in response, no matter the contents of my HCI command packet's body:

response: 0x04 0x10 0x01 0x00

This implies that I am having a hardware framing error with my SPI, and I have no idea what may be causing that. As I just described, the rest of the processes work fine, so the SPI bus seems to be functional in most cases.

Note: My send an receive methods are almost identical in structure to yours

If you can offer me any advice, I would appreciate it immensely. Otherwise, thank you for the awesome library!

@dmazzella
Copy link
Owner

Hi, can you give me an example of code that I can verify?

P.S.
I'm sorry if I answer with such delays, but I keep this library in my spare time and lately it's been less and less.

@dmazzella
Copy link
Owner

Close for no, reopen it if you need help.

@waleckaw
Copy link
Author

Apologies, I am not sure how I missed your response!

dmazella_imitation_BLESPItest.txt

In the file I attached (which I ran from the REPL), I attempted to copy your SPI code almost line-for-line, and was met with the same issue I described above. As you can see, I did have to port my code to fit the microcontroller I was using, the STM32 B-L475E-IOT01A, which contains an SPBTLE-RF on board.

@dmazzella
Copy link
Owner

no problem,
I just tried your code and the problem is in not sending the reset command.
As you can see without the reset, the result of the script is identical to yours:

➜  micropython (master) ✗ python3 -B tools/pyboard.py --device /dev/tty.usbmodem3167398C35352 /Users/damianomazzella/Downloads/ww.py
bytearray(b'\x04\x10\x01\x00')
➜  micropython (master) ✗ 

Adding the reset, the behavior is correct:

➜  micropython (master) ✗ python3 -B tools/pyboard.py --device /dev/tty.usbmodem3167398C35352 /Users/damianomazzella/Downloads/ww.py
bytearray(b'\x04\xff\x03\x01\x00\x01')
➜  micropython (master) ✗ 

This is the code I tried:

# hail mary BLE SPI test

import machine
import pyb
import utime
import micropython
from micropython import const

from machine import Pin

# spi = machine.SPI(3, baudrate=8000000, polarity=0, phase=0,
#                   bits=8, firstbit=machine.SPI.MSB)
spi = machine.SPI(2, baudrate=8000000, polarity=0)

#CS = Pin('D13', Pin.OUT_PP, pull=Pin.PULL_NONE)
CS = machine.Pin('B12', machine.Pin.OUT_PP)
CS.on()

#RS = Pin('A8', Pin.OUT_PP, pull=Pin.PULL_NONE)
RS = machine.Pin('B9', machine.Pin.OUT_PP)
RS.on()

_READ_HEADER_MASTER = b'\x0B\x00\x00\x00\x00'
_WRITE_HEADER_MASTER = b'\x0A\x00\x00\x00\x00'
HCI_READ_PACKET_SIZE = const(128)

# IR = Pin('E6', Pin.IN, pull=Pin.PULL_DOWN)
IR = machine.Pin('B8', machine.Pin.IN, machine.Pin.PULL_DOWN)

class CSContext(object):

    def __init__(self, pin):
        self._pin = pin

    def __enter__(self):
        # Assert CS line
        self._pin.off()

    def __exit__(self, exc_type, exc_value, traceback):
        # Release CS line
        self._pin.on()
        # return all(map(lambda x: x is None, [exc_type, exc_value, traceback]))


def write(retry=5, header=b'\x10\x03', param=0):
    """
    Write packet to BlueNRG-MS module
    """
    _rw_header_slave = bytearray(len(_WRITE_HEADER_MASTER))
    result = None
    while retry:
        with CSContext(CS):
            # Exchange header
            spi.write_readinto(
                _WRITE_HEADER_MASTER,
                _rw_header_slave
            )
            rx_write_bytes = _rw_header_slave[1]
            rx_read_bytes = (
                _rw_header_slave[4] << 8
            ) | _rw_header_slave[3]
            if _rw_header_slave[0] == 0x02 and (
                    rx_write_bytes > 0 or rx_read_bytes > 0):
                # SPI is ready
                if header:
                    # avoid to write more data that size of the buffer
                    if rx_write_bytes >= len(header):
                        result = bytearray(len(header))
                        spi.write_readinto(header, result)
                        if param:
                            rx_write_bytes -= len(header)
                            # avoid to read more data that size of the
                            # buffer
                            if len(param) > rx_write_bytes:
                                tx_bytes = rx_write_bytes
                            else:
                                tx_bytes = len(param)
                            result = bytearray(tx_bytes)
                            spi.write_readinto(param, result)
                            break
                        else:
                            break
                    else:
                        break
                else:
                    break
            else:
                utime.sleep_us(50)
        retry -= 1

    return result


def read(size=HCI_READ_PACKET_SIZE, retry=5):
    """
    Read packet from BlueNRG-MS module
    """
    result = None
    _rw_header_slave = bytearray(len(_WRITE_HEADER_MASTER))
    while retry:
        with CSContext(CS):
            # Exchange header
            spi.write_readinto(
                _READ_HEADER_MASTER,
                _rw_header_slave
            )
            rx_read_bytes = (
                _rw_header_slave[4] << 8
            ) | _rw_header_slave[3]
            if _rw_header_slave[0] == 0x02 and rx_read_bytes > 0:
                # SPI is ready
                # avoid to read more data that size of the buffer
                if rx_read_bytes > size:
                    rx_read_bytes = size
                data = b'\xFF' * rx_read_bytes
                result = bytearray(rx_read_bytes)
                spi.write_readinto(data, result)
                break
            else:
                utime.sleep_us(50)
        retry -= 1

    # Add a small delay to give time to the BlueNRG to set the IRQ pin low
    # to avoid a useless SPI read at the end of the transaction
    utime.sleep_us(150)
    return result


def run():

    # reset
    RS.off()
    utime.sleep_us(5)
    RS.on()
    utime.sleep_us(5)

    write()  # retry=5, header=b'\x10\x03', param=0)
    while True:
        utime.sleep_us(12)
        if (IR.value() == 1):
            hi = read(size=HCI_READ_PACKET_SIZE, retry=5)
            print(hi)
            break
        utime.sleep_us(25)

run()

Let me know how it goes.
best regards,
D.

@dmazzella dmazzella reopened this Jun 24, 2020
@waleckaw
Copy link
Author

Ok, I probably should have mentioned in my previous post that I always reset the BlueNRG-MS module before running the code. My mistake.

When I run the code you posted, I get the correct initialization response like you did, but all of my following commands are met with the 0x04 0x10 0x01 0x00 error code. Below is an example with a few further modifications I added to continuously send commands to the chip. As you can see, I am trying to send the HCI_Read_Local_Supported_Features command. I get the same response if I send HCI_Read_Local_Supported_Commands, HCI_Read_Remote_Version_Information, or HCI_Read_Local_Version_Information as defined on page 7 in the blueNRG-MS document found here:
[https://www.st.com/content/ccc/resource/technical/document/user_manual/6d/a1/5b/6c/dc/ab/48/76/DM00162667.pdf/files/DM00162667.pdf/jcr:content/translations/en.DM00162667.pdf]. I have tried many other commands, but those are 4 that I believe should work without any initialization other than a reset.

code:

def run():
    RS.off()
    utime.sleep_us(5)
    RS.on()
    utime.sleep_us(5)

    write() # HCI_Read_Local_Supported_Features
    while True: 
        utime.sleep_us(12)
        if (IR.value() == 1):
            hi = read(size=HCI_READ_PACKET_SIZE, retry=5)
            print(hi)
            utime.sleep(2)
            write() # HCI_Read_Local_Supported_Features

run()

and here is the output from the REPL:

from BLEProj.test import dmzBLESPI
bytearray(b'\x04\xff\x03\x01\x00\x01')
bytearray(b'\x04\x10\x01\x00')
bytearray(b'\x04\x10\x01\x00')
bytearray(b'\x04\x10\x01\x00')
bytearray(b'\x04\x10\x01\x00')
bytearray(b'\x04\x10\x01\x00')
bytearray(b'\x04\x10\x01\x00')
bytearray(b'\x04\x10\x01\x00')
bytearray(b'\x04\x10\x01\x00')

@dmazzella
Copy link
Owner

dmazzella commented Jun 28, 2020

with your example:

# hail mary BLE SPI test

import machine
import pyb
import ustruct
import utime
import micropython
from micropython import const

from machine import Pin

# spi = machine.SPI(3, baudrate=8000000, polarity=0, phase=0,
#                   bits=8, firstbit=machine.SPI.MSB)
spi = machine.SPI(2, baudrate=8000000, polarity=0)

#CS = Pin('D13', Pin.OUT_PP, pull=Pin.PULL_NONE)
CS = machine.Pin('B12', machine.Pin.OUT_PP)
CS.on()

#RS = Pin('A8', Pin.OUT_PP, pull=Pin.PULL_NONE)
RS = machine.Pin('B9', machine.Pin.OUT_PP)
RS.on()

_READ_HEADER_MASTER = b'\x0B\x00\x00\x00\x00'
_WRITE_HEADER_MASTER = b'\x0A\x00\x00\x00\x00'
HCI_READ_PACKET_SIZE = const(128)

# IR = Pin('E6', Pin.IN, pull=Pin.PULL_DOWN)
IR = machine.Pin('B8', machine.Pin.IN, machine.Pin.PULL_DOWN)

class CSContext(object):

    def __init__(self, pin):
        self._pin = pin

    def __enter__(self):
        # Assert CS line
        self._pin.off()

    def __exit__(self, exc_type, exc_value, traceback):
        # Release CS line
        self._pin.on()
        # return all(map(lambda x: x is None, [exc_type, exc_value, traceback]))


def write(retry=5, header=b'', param=0):
    """
    Write packet to BlueNRG-MS module
    """
    _rw_header_slave = bytearray(len(_WRITE_HEADER_MASTER))
    result = None
    while retry:
        with CSContext(CS):
            # Exchange header
            spi.write_readinto(
                _WRITE_HEADER_MASTER,
                _rw_header_slave
            )
            rx_write_bytes = _rw_header_slave[1]
            rx_read_bytes = (
                _rw_header_slave[4] << 8
            ) | _rw_header_slave[3]
            if _rw_header_slave[0] == 0x02 and (
                    rx_write_bytes > 0 or rx_read_bytes > 0):
                # SPI is ready
                if header:
                    # avoid to write more data that size of the buffer
                    if rx_write_bytes >= len(header):
                        result = bytearray(len(header))
                        spi.write_readinto(header, result)
                        if param:
                            rx_write_bytes -= len(header)
                            # avoid to read more data that size of the
                            # buffer
                            if len(param) > rx_write_bytes:
                                tx_bytes = rx_write_bytes
                            else:
                                tx_bytes = len(param)
                            result = bytearray(tx_bytes)
                            spi.write_readinto(param, result)
                            break
                        else:
                            break
                    else:
                        break
                else:
                    break
            else:
                utime.sleep_us(50)
        retry -= 1

    return result


def read(size=HCI_READ_PACKET_SIZE, retry=5):
    """
    Read packet from BlueNRG-MS module
    """
    result = None
    _rw_header_slave = bytearray(len(_WRITE_HEADER_MASTER))
    while retry:
        with CSContext(CS):
            # Exchange header
            spi.write_readinto(
                _READ_HEADER_MASTER,
                _rw_header_slave
            )
            rx_read_bytes = (
                _rw_header_slave[4] << 8
            ) | _rw_header_slave[3]
            if _rw_header_slave[0] == 0x02 and rx_read_bytes > 0:
                # SPI is ready
                # avoid to read more data that size of the buffer
                if rx_read_bytes > size:
                    rx_read_bytes = size
                data = b'\xFF' * rx_read_bytes
                result = bytearray(rx_read_bytes)
                spi.write_readinto(data, result)
                break
            else:
                utime.sleep_us(50)
        retry -= 1

    # Add a small delay to give time to the BlueNRG to set the IRQ pin low
    # to avoid a useless SPI read at the end of the transaction
    utime.sleep_us(150)
    return result

def run():

    # reset
    RS.off()
    utime.sleep_us(5)
    RS.on()
    utime.sleep_us(5)

    # wait event "device ready"
    while True:
        if IR.value() and read() == b'\x04\xff\x03\x01\x00\x01':
            break

    # HCI_Read_Local_Version_Information: OGF: 0x04 OCF: 0x01 OPCODE: 0x1001
    param = b''
    ogf = 0x04
    ocf = 0x01
    opcode = ustruct.pack("<H", (ocf & 0x03ff) | (ogf << 10)) # OPCODE
    header = ustruct.pack("<B3s", 0x01, opcode)
    print(header, param)
    write(header=header, param=param)
    while True:
        if IR.value():
            hi = read()
            print("-------", hi)
            break

    # HCI_Read_Local_Supported_Features: OGF: 0x04 OCF: 0x03 OPCODE: 0x1003
    param = b''
    ogf = 0x04
    ocf = 0x03
    opcode = ustruct.pack("<H", (ocf & 0x03ff) | (ogf << 10)) # OPCODE
    header = ustruct.pack("<B3s", 0x01, opcode)
    print(header, param)
    write(header=header, param=param)
    while True:
        if IR.value():
            hi = read()
            print("-------", hi)
            break

run()

expected output:

➜  micropython (master) ✗ python3 -B tools/pyboard.py --device /dev/tty.usbmodem3167398C35352 /Users/damianomazzella/Downloads/test_uble.py
b'\x01\x01\x10\x00' b''
------- bytearray(b'\x04\x0e\x0c\x01\x01\x10\x00\x07\x071\x070\x00#\x00')
b'\x01\x03\x10\x00' b''
------- bytearray(b'\x04\x0e\x0c\x01\x03\x10\x00\x00\x00\x00\x00`\x00\x00\x00')
➜  micropython (master) ✗ 

Please try it and give me feedback.

@waleckaw
Copy link
Author

waleckaw commented Jul 1, 2020

It worked! I can't believe it. The issue was definitely the header format. For example, when I told you I sent the HCI_Read_Local_Supported_Features command, I was sending the header in a simple bytearray as it was given in the spec: OpCode = 0x1003.
I can see now that, when the header gets packed into a ustruct, its formatting changes and it becomes 0x0103. I am not sure where or if that ever gets clarified in the BlueNRG-MS spec I linked above.

I can't thank you enough, especially because you mentioned this is an old project. Without your help I doubt I ever would have ever solved this problem. I am a relative newcomer when it comes to embedded python, and never would have thought to use a struct. Many thanks!

@dmazzella
Copy link
Owner

I'm glad that everything works.

Best regards,
D.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants