Closed ilium007 closed 2 years ago
Note, this is my debug output above relating to my own CANbus message protocol. The bus fails when bus_state goes over state 0x03:
CAN.ERROR_PASSIVE – the controller is on and in the Error Passive state (at least one of TEC or REC is 128 or greater);
I think this has something to do with reading the fifo buffer using event() class (set, wait and clear). I have changed it to use threadsafeflag for reading and for now the issues have stopped. In my testing I have pushbuttons and rotary encoders that send messages on the CANbus and a seperate STM32F405 that is receiving them.
I stripped out all of there asyncio pushbutton code and just running two rotary encoders, it all seems to be working fine. This is just prototyping code, I am really just learning about uasyncio...
Sender:
from machine import Pin
import uasyncio as asyncio
from primitives.encoder import Encoder
from pyb import CAN
can1 = CAN(1, CAN.NORMAL, extframe = True, auto_restart=True, baudrate=500000)
nid = 0x75 #DEC 117 node id
# encoder pins
pin_c2 = Pin(Pin.cpu.C2, Pin.IN)
pin_c3 = Pin(Pin.cpu.C3, Pin.IN)
pin_b10 = Pin(Pin.cpu.B10, Pin.IN)
pin_b11 = Pin(Pin.cpu.B11, Pin.IN)
def enc_send_msg(pos, id):
abs_pos = abs(pos)
input_id = id
byte0 = abs_pos & 0xff
count_upper = (abs_pos >> 8) & 0x03
byte1 = (input_id << 2) | count_upper
# set sign bit
if pos >= 0:
byte2 = 0x00
else:
byte2 = 0x01
send_msg(can1, nid, 0x0103, [byte2, byte1, byte0])
def send_msg(bus, nid, cid, msg):
fpf = 1 #first packet flag
lpf = 1 #last packet flag
nid_lower = nid & 0x7 # split node id
nid_upper = nid >> 3
#4 bytes in a 29-bit msg_id; lower 2 bytes are channel_id
byte2 = (nid_lower << 5) | (lpf << 4)
byte3 = (fpf << 4) | nid_upper
msg_id = (byte3 << 24) | (byte2 << 16) | cid
buf = bytearray(msg)
bus.send(buf, msg_id)
#print("{0:08b} {1:08b} {2:08b} {3:08b} | ".format(
# (msg_id >> 24), ((msg_id >> 16) & 0xff), ((msg_id >> 8) & 0xff), msg_id & 0xff), end='')
#for x in buf[:-1]:
# print("{0:08b} ".format(x) , end='')
#print("{0:08b} ".format(buf[-1]))
print('msg_id: 0x{:X}\tnid: 0x{:X}\tcid: 0x{:X}\tbus_state: 0x{:X}\tdata: 0x{:X}'.format(
msg_id, nid, cid, can1.state(), int.from_bytes(buf, 'big')))
enc0 = Encoder(pin_c2, pin_c3, div=4, callback=lambda p,d: enc_send_msg(p, 0x00))
enc1 = Encoder(pin_b10, pin_b11, div=4, callback=lambda p,d: enc_send_msg(p, 0x01))
async def main():
while True:
await asyncio.sleep_ms(0)
try:
asyncio.run(main())
except (KeyboardInterrupt, Exception) as e:
print('Exception {} {}\n'.format(type(e).__name__, e))
finally:
ret = asyncio.new_event_loop() # Clear retained uasyncio state
Receiver:
import uasyncio as asyncio
from pyb import Pin, CAN
can1 = CAN(1, CAN.NORMAL, extframe = True, auto_restart=True, baudrate=500000)
# define filters later, for now let everything through
can1.setfilter(0,CAN.MASK16,0,(0,0,0,0))
buf = bytearray(8)
# tuple containing id, rtr flag, filter match index, data
lst = [0, 0, 0, memoryview(buf)]
tsf = asyncio.ThreadSafeFlag()
def cb(bus, reason):
if reason == 0:
#print('pending')
bus.recv(0, lst)
#event.set()
tsf.set()
if reason == 1:
print('full')
if reason == 2:
print('overflow')
can1.rxcallback(0, cb)
def process_msg(lst):
#print("{0:08b} {1:08b} {2:08b} {3:08b} | ".format(
# (lst[0] >> 24), ((lst[0] >> 16) & 0xff), ((lst[0] >> 8) & 0xff), lst[0] & 0xff), end='')
#for x in lst[3][:-1]:
# print("{0:08b} ".format(x) , end='')
#print("{0:08b} ".format(lst[3][-1]))
msg_id = lst[0]
cid = msg_id & 0xffff
nid_lower = (msg_id >> 21) & 0x07
nid_upper = (msg_id >> 24) & 0x0f
nid = (nid_upper << 3) | nid_lower
if cid == 0x0101: #push button
state_id = lst[3][1] & 0x1f #mask bottom 5 bits from byte1
if state_id == 0x03:
state_name = 'push'
elif state_id == 0x04:
state_name = 'release'
elif state_id == 0x05:
state_name = 'hold'
input_id_lower = lst[3][1] >> 5
input_id_upper = (lst[3][0] & 0x07)
input_id = input_id_upper | input_id_lower
print('input: {}\tmsg_id: 0x{:X}\tnid: 0x{:X}\tcid: 0x{:X}\tbus_state: 0x{:X}\tinput_id: 0x{:X}\tstate_id: 0x{:X}\tstate_name: {}'.format(
'button', msg_id, nid, cid, can1.state(), input_id, state_id, state_name))
elif cid == 0x0102: #toggle switch
state_id = lst[3][0] & 0x03 #mask bottom 2 bits
input_id = lst[3][0] >> 2
if state_id == 0x01:
state_name = 'on'
elif state_id == 0x02:
state_name = 'off'
print('input: {}\tmsg_id: 0x{:X}\tnid: 0x{:X}\tcid: 0x{:X}\tbus_state: 0x{:X}\tinput_id: 0x{:X}\tstate_id: 0x{:X}\tstate_name: {}'.format(
'switch', msg_id, nid, cid, can1.state(), input_id, state_id, state_name))
elif cid == 0x0103: #rotary encoder
input_id = lst[3][1] >> 0x02
sign = lst[3][0] & 0x01
count_msb = lst[3][1] & 0x03
count = (count_msb << 8) | lst[3][2]
print('input: {}\tmsg_id: 0x{:X}\tnid: 0x{:X}\tcid: 0x{:X}\tbus_state: 0x{:X}\tinput_id: 0x{:X}\tsign: 0x{:X}\tcount: 0x{:X}'.format(
'encoder', msg_id, nid, cid, can1.state(), input_id, sign, count))
elif cid == 0x0104: #hall effect sensor
input_id_lower = lst[3][1] >> 5
input_id_upper = (lst[3][0] & 0x07)
input_id = input_id_upper | input_id_lower
state_id = lst[3][1] & 0x1f #mask bottom 5 bits
if state_id == 0x03:
state_name = 'sense'
elif state_id == 0x04:
state_name = 'wait'
elif state_id == 0x05:
state_name = 'hold'
print('input: {}\tmsg_id: 0x{:X}\tnid: 0x{:X}\tcid: 0x{:X}\tbus_state: 0x{:X}\tinput_id: 0x{:X}\tstate_id: 0x{:X}\tstate_name: {}'.format(
'hall', msg_id, nid, cid, can1.state(), input_id, state_id, state_name))
else:
print('undefined')
async def main():
while True:
await tsf.wait()
process_msg(lst)
await asyncio.sleep_ms(0)
try:
asyncio.run(main())
except (KeyboardInterrupt, Exception) as e:
print('Exception {} {}\n'.format(type(e).__name__, e))
finally:
ret = asyncio.new_event_loop() # Clear retained uasyncio state
Problem was in my receiving code. Jimmo helped me out on the Micropython Discord channel.
import uasyncio as asyncio
from pyb import Pin, CAN
screen_debug = False
tsf = asyncio.ThreadSafeFlag()
can1 = CAN(1, CAN.NORMAL, extframe = True, auto_restart=True, baudrate=500000)
# fix filters
can1.setfilter(0,CAN.MASK16,0,(0,0,0,0))
# set can1 callback
can1.rxcallback(0, cb)
def cb(bus, reason):
if reason == 0:
#print('pending')
tsf.set()
if reason == 1:
print('full')
# raise exception and halt
if reason == 2:
print('overflow')
# raise exception and halt
def process_msg():
buf = can1.recv(0)
if screen_debug:
print('CAN info: {}'.format(can1.info()))
print("{0:08b} {1:08b} {2:08b} {3:08b} | ".format(
(buf[0] >> 24), ((buf[0] >> 16) & 0xff), ((buf[0] >> 8) & 0xff), buf[0] & 0xff), end='')
for x in buf[3][:-1]:
print("{0:08b} ".format(x) , end='')
print("{0:08b} ".format(buf[3][-1]))
msg_id = buf[0]
cid = msg_id & 0xffff
nid_lower = (msg_id >> 21) & 0x07
nid_upper = (msg_id >> 24) & 0x0f
nid = (nid_upper << 3) | nid_lower
if cid == 0x0101: #push button
state_id = buf[3][1] & 0x1f #mask bottom 5 bits from byte1
if state_id == 0x03:
state_name = 'push'
elif state_id == 0x04:
state_name = 'release'
elif state_id == 0x05:
state_name = 'hold'
input_id_lower = buf[3][1] >> 5
input_id_upper = (buf[3][0] & 0x07)
input_id = input_id_upper | input_id_lower
print('input: {}\tmsg_id: 0x{:X}\tnid: 0x{:X}\tcid: 0x{:X}\tbus_state: 0x{:X}\tinput_id: 0x{:X}\tstate_id: 0x{:X}\tstate_name: {}'.format(
'button', msg_id, nid, cid, can1.state(), input_id, state_id, state_name))
elif cid == 0x0102: #toggle switch
state_id = buf[3][0] & 0x03 #mask bottom 2 bits
input_id = buf[3][0] >> 2
if state_id == 0x01:
state_name = 'on'
elif state_id == 0x02:
state_name = 'off'
print('input: {}\tmsg_id: 0x{:X}\tnid: 0x{:X}\tcid: 0x{:X}\tbus_state: 0x{:X}\tinput_id: 0x{:X}\tstate_id: 0x{:X}\tstate_name: {}'.format(
'switch', msg_id, nid, cid, can1.state(), input_id, state_id, state_name))
elif cid == 0x0103: #rotary encoder
input_id = buf[3][1] >> 0x02
sign = buf[3][0] & 0x01
count_msb = buf[3][1] & 0x03
count = (count_msb << 8) | buf[3][2]
print('input: {}\tmsg_id: 0x{:X}\tnid: 0x{:X}\tcid: 0x{:X}\tbus_state: 0x{:X}\tinput_id: 0x{:X}\tsign: 0x{:X}\tcount: {}'.format(
'encoder', msg_id, nid, cid, can1.state(), input_id, sign, count))
elif cid == 0x0104: #hall effect sensor
input_id_lower = buf[3][1] >> 5
input_id_upper = (buf[3][0] & 0x07)
input_id = input_id_upper | input_id_lower
state_id = buf[3][1] & 0x1f #mask bottom 5 bits
if state_id == 0x03:
state_name = 'sense'
elif state_id == 0x04:
state_name = 'wait'
elif state_id == 0x05:
state_name = 'hold'
print('input: {}\tmsg_id: 0x{:X}\tnid: 0x{:X}\tcid: 0x{:X}\tbus_state: 0x{:X}\tinput_id: 0x{:X}\tstate_id: 0x{:X}\tstate_name: {}'.format(
'hall', msg_id, nid, cid, can1.state(), input_id, state_id, state_name))
else:
print('undefined')
async def main():
while True:
await tsf.wait()
process_msg()
await asyncio.sleep_ms(0)
try:
asyncio.run(main())
except (KeyboardInterrupt, Exception) as e:
print('Exception {} {}\n'.format(type(e).__name__, e))
finally:
ret = asyncio.new_event_loop() # Clear retained uasyncio state
I have no idea where to start looking for the issue but when I use this encoder.py and then send a CANbus message via the callback I end up with consistent CANbus errors and bus failure.
When I use the miketeachman rotary.py (https://github.com/miketeachman/micropython-rotary) with similar callback CANbus message sending there are never any bus state errors.