实在不想那种中规中矩的写东西了,写的自己都看不下去了,想到啥就写啥吧!
聊两句
最近没事就看了一CAN总线相关的东西,平时工作上接触的不多,大部分时间都是在移动端的自动化上,服务端的自动化上面,针对这块了解的非常非常的少。不看就会焦虑。
因为平时工作用的不多,也就是看一下混个眼熟吧,记录下来一些自己觉得重要的信息,以便后面使用。
安装
一行命令吧
pip install python-can
没有CAN硬件咋办
我也是看到知乎上的一个帖子才知道Kvaser这个软件。链接地址
上个示例代码吧,看看是什么样子
- 代码①
# coding:utf-8
"""
This example shows how sending a single message works
"""
import can
def send_one():
""" Sends a single message."""
# Using specific buses works similar:
# bus = can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000)
# bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=250000)
# bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000)
bus = can.interface.Bus(bustype='kvaser', channel=0, bitrate=250000)
msg = can.Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
try:
bus.send(msg)
print("Message sent on {}".format(bus.channel_info))
except can.CanError:
print("Message NOT sent")
if __name__ == '__main__':
send_one()
- 代码②
# coding:utf-8
"""
通过代码设置实现
"""
import can
can.rc['interface'] = 'kvaser'
can.rc['channel'] = '0'
can.rc['bitrate'] = 250000
from can.interface import Bus
bus = Bus()
# 或者可以通过以下方式
# bus = Bus(bustype='kvaser', channel=0, bitrate=250000)
msg = can.Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
try:
bus.send(msg)
print("Message sent on {}".format(bus.channel_info))
except can.CanError:
print("Message NOT sent")
上面两种示例代码主要是体现了两种bus的创建方式,针对CAN的配置还有更多的方式,比如配置文件,环境变量,还有刚才的代码中配置。下面看看有哪些配置方式。
配置方式
配置文件
在linux系统中,配置文件主要在以下路径中搜索:
- ~/can.conf
- /etc/can.conf
- $HOME/.can
- $HOME/.canrc
在Windows系统中的配置文件路径:
- %USERPROFILE%/can.conf
- can.ini (当前工作目录)
- %APPDATA%/can.ini
can.ini文件长啥样呢?
[default]
interface = kvaser
channel = 0
bitrate = 250000
除了设置default项,还可以设置别的项,比如可以配置更多的CAN硬件信息,配置文件样式如下:(没有的配置项可以共享default的配置)
[default]
interface = <the name of the interface to use>
channel = <the channel to use by default>
bitrate = <the bitrate in bits/s to use by default>
[HS]
*# All the values from the 'default' section are inherited*
channel = <the channel to use>
bitrate = <the bitrate in bits/s to use. i.e. 500000>
[MS]
*# All the values from the 'default' section are inherited*
channel = <the channel to use>
bitrate = <the bitrate in bits/s to use. i.e. 125000>
那怎么使用呢,在创建BUS对象的时候传入context参数就可以了
from can.interface import Bus
hs_bus = Bus(context='HS')
ms_bus = Bus(context='MS')
有了配置文件,就在创建BUS对象的时候不需要传递参数了
# coding: utf-8
"""
通过with语句执行can
"""
from can import CanError, Message
from can.interface import Bus
with Bus() as bus:
msg = Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
try:
bus.send(msg)
except CanError as e:
print(e)
环境变量配置
也可以通过配置环境变量来配置can信息,主要有以下配置项:
- CAN_INTERFACE
- CAN_CHANNEL
- CAN_BITRATE
- CAN_CONFIG
CAN_CONFIG 环境变量可以通过json格式来设置任意的bug配置信息.
CAN_CONFIG = {"interface":"kvaser","channel":0,"bitrate":250000}
支持的CAN名称
名称 | 描述信息 |
---|---|
“socketcan” | SocketCAN |
“kvaser” | Kvaser’s CANLIB |
“serial” | CAN over Serial |
“slcan” | CAN over Serial / SLCAN |
“ixxat” | IXXAT Virtual CAN Interface |
“pcan” | PCAN Basic API |
“usb2can” | USB2CAN Interface |
“nican” | NI-CAN |
“iscan” | isCAN |
“neovi” | neoVI |
“vector” | Vector |
“virtual” | Virtual |
“canalystii” | CANalyst-II |
“systec” | SYSTEC interface |
基本上python-can的基本信息也就这么多,写法也比较简单,重要的就是怎么发消息,收消息之类的,下面就看一下常见的API信息。
常用API
直接上pycharm的代码吧,就当是笔记了,注释部分也是重点哦
BUS对象创建
有两种创建方式
- 可以通过with语句
此处展示不传递参数,通过读取配置文件或者环境变量
with Bus() as bus: # 具体参数可以通过环境变量或者配置文件进行配置
pass
配置文件如下:
[default]
interface = kvaser
channel = 0
bitrate = 250000
- 通过普通方式创建
顺便展示一下通过传递参数方式
bus = Bus(bustype='kvaser', channel=0, bitrate=250000)
设置过滤器
- set_filters(filters=None)
设置过滤器可以接收指定的消息,如果参数是空或者长度为0 会接收所有消息
参数格式: [{“can_id”:0x11, “can_mask”:0x21, “extended”: False}]
可以在创建BUS对象的时候设置,也可以通过BUS对象调用此方法进行设置
filters = [{"can_id":0x123, "can_mask":0x21, "extended": False}]
bus.set_filters(filters)
# 或者
bus = Bus(can_filters=filters)
CAN报文封装
通过调用Message类实现CAN报文的创建
- Message(timestamp=0.0, arbitration_id=0, is_extended_id=True, is_remote_frame=False,is_error_frame=False, channel=None, dlc=None, data=None, is_fd=False, is_rx=True,bitrate_switch=False, error_state_indicator=False, check=False)
参数: 基本上数据帧结构中的字段都包含在内,具体可以根据实际情况设置
描述:
- Message对象可用于CAN信号的发送,接收,和不同格式之间的转换
- 同时也支持copy()和deepcopy()方法
消息的发送
两种方法send() 和send_periodic()
- send(msg, timeout=None)
with Bus() as bus:
msg = Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
bus.send(msg)
- send_periodic(msgs, period, duration=None, store_taks=True)
在特定的时间内每个特定时间发送消息,返回CyclicSendTaskABC类型的任务
参数解释
- msgs: CAN消息,数组的话 要保证id都是一样
- period: 每条消息间隔时间(秒)
- duration:消息发送的总时长,如果不设置,会一直发送
- store_task: 默认设置为true,会附加到bus实例中管理,如果设置false,需要手动管理控制
- 何时设置store_task为False呢?
- 如果一个can任务是一个临时性的任务,发送后就结束了,就最好设置为False, 因为如果默认设置的话,这个任务就会存在bus实例中,还会占用一定的内存空间
什么场景下发送会停止呢?
在以下几种情况下,此任务会停止执行
- duration字段设置的时间过期
- Bus实例离开了作用域,也就是无效
- Bus实例停止
- BusABC.stop_all_periodic_tasks()被调用 bus对象方法
- CyclicTask.stop()方法被调用,任务对象方法
基于上面提到的几种方法,顺便也说一下吧
- shutdown()
- 停止bus 在with语句中不需要使用此方法
- stop_alll_perriodic_tasks(remove_tasks=True)
- 停止发送任何消息,和send_periodic方法对应,如果在停止的时候任务出现异常,返回结果为None
- 参数: remove_tasks: 停止跟进的任务
使用任务管理方法
with Bus() as bus: # ①
msg1 = Message(arbitration_id=0xc0ffaa, data=[3, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
msg2 = Message(arbitration_id=0xc0ffaa, data=[4, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
msg3 = Message(arbitration_id=0xc0ffaa, data=[5, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
print("msgs是个消息数组")
t1 = bus.send_periodic(msgs=[msg1, msg2, msg3], period=2, duration=30)
print("msgs是单个消息")
t2 = bus.send_periodic(msgs=msg, period=2, duration=30)
print("在第十秒的时候停止")
for i in range(10):
print("第{}秒.............".format(i+1))
time.sleep(1)
# t1.stop() # 通过任务来管理
# t2.stop() # 通过任务来管理
bus.stop_all_periodic_tasks() # 通过调用函数任务停止
# bus.shutdown() # 通过bus来管理 如果使用with语句初始化bus 此方法会报错
使用bus.shutdown方法
bus = Bus()
# 以下 ④
msg = Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
msg1 = Message(arbitration_id=0xc0ffaa, data=[3, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
msg2 = Message(arbitration_id=0xc0ffaa, data=[4, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
msg3 = Message(arbitration_id=0xc0ffaa, data=[5, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
print("msgs是个消息数组")
t1 = bus.send_periodic(msgs=[msg1, msg2, msg3], period=2, duration=30)
print("msgs是单个消息")
t2 = bus.send_periodic(msgs=msg, period=2, duration=30)
print("在第十秒的时候停止")
for i in range(10):
print("第{}秒.............".format(i + 1))
time.sleep(1)
bus.shutdown() # 通过bus来管理 停止bus ⑥ 在with语句中不需要使用shutdown函数
消息接收
消息接收有两种方法
- 调用recv()
- 直接在bus对象中迭代获取
这个没啥好说的,咱写作能力也就这水平(太差劲),上代码吧
with Bus() as bus: # ①
# recv(timeout=None) 在给定timeout时间内接受消息,超时未接到消息 返回None
msg = bus.recv()
# 或者如下方法
for msg in bus: # ⑩ 此类方法会一直循环下去,只要有消息就会执行打印
print("发送的消息看一看吧:{}".format(msg))
监听 Listener
用于注册接收can消息的通知,可以通过两种方式使用
- 默认方式,新的can消息作为参数调用listener
- 通过调用on_message_received()
监听类必须继承Listener类并重写on_message_received方法
停止监听,调用stop()方法
class MyListener(Listener):
def on_message_received(self, msg: Message) -> None:
print("监听器收到消息咯----{}".format(msg))
if __name__ == '__main__':
listener = MyListener() # ①② 定义监听器 必须要继承抽象类
with Bus() as bus: # ①
msg = bus.recv()
# listener(msg) # ①② 结合MyListener一起来看 第一种方法
listener.on_message_received(msg) # ①② 结合MyListener一起来看 第二种方法
listener.stop() # 停止监听
以下选择性无视了
以上的代码都在一个文件里调试了,全都放上来吧(选择性无视)
# coding:utf-8
"""
针对python-can的api的学习示例
"""
import time
import can
from can import CanError, Message, Listener
from can.interface import Bus
class MyListener(Listener):
def on_message_received(self, msg: Message) -> None:
print("监听器收到消息咯----{}".format(msg))
# ①. bus对象创建
# 可以通过with语句
# with Bus() as bus: # 具体参数可以通过环境变量或者配置文件进行配置
# pass
# 配置文件如下:
"""
[default]
interface = kvaser
channel = 0
bitrate = 250000
"""
# 可以通过传递参数的方式
# bus = Bus(bustype='kvaser', channel=0, bitrate=250000)
# ②. recv(timeout=None)
# 在给定timeout时间内接受消息,超时未接到消息 返回None
# ③ send(msg, timeout=None)
# 发送CAN消息
# ④ send_periodic(msgs, period, duration=None, store_taks=True)
# 在特定的时间内每个特定时间发送消息,返回CyclicSendTaskABC类型的任务
# 参数解释
# msgs: CAN消息,数组的话 要保证id都是一样
# period: 每条消息间隔时间(秒)
# duration:消息发送的总时长,如果不设置,会一直发送
# store_task: 默认设置为true,会附加到bus实例中管理,如果设置false,需要手动管理控制
# - 何时设置store_task为False呢,如果一个can任务是一个临时性的任务,发送后就结束了,就最好设置为False,
# 因为如果默认设置的话,这个任务就会存在bus实例中,还会占用一定的内存空间
# 在以下几种情况下,此任务会停止执行
# - duration字段设置的时间过期
# - Bus实例离开了作用域,也就是无效
# - Bus实例停止
# - BusABC.stop_all_periodic_tasks()被调用 bus对象方法
# - CyclicTask.stop()方法被调用,任务对象方法
# ⑤ set_filters(filters=None)
# 设置过滤器可以接收指定的消息,如果参数是空或者长度为0 会接收所有消息
# 参数:
# filters:[{"can_id":0x11, "can_mask":0x21, "extended": False}]
# ⑥ shutdown()
# 停止bus 在with语句中不需要使用此方法
# ⑦ state
# 属性字段 获取当前硬件状态
# ⑧ stop_alll_perriodic_tasks(remove_tasks=True)
# 停止发送任何消息,和send_periodic方法对应,如果在停止的时候任务出现异常,返回结果为None
# 参数:
# remove_tasks: 停止跟进的任务
# ⑨ 发送消息
# 可以通过send()方法传递一个Message对象,发送一条独立的can信号
# ⑩ 接收消息
# 两种方法用来接收消息,一种是调用recv()方法,第二种是直接在bus对象中迭代获取
# 还可以通过Listener来实现
# ①① CAN报文
# 对象结构
# Message(timestamp=0.0, arbitration_id=0, is_extended_id=True, is_remote_frame=False,is_error_frame=False,
# channel=None, dlc=None, data=None, is_fd=False, is_rx=True,bitrate_switch=False,
# error_state_indicator=False, check=False)
# 参数:基本上数据帧结构中的字段都包含在内,具体可以根据实际情况设置
# 描述:
# Message对象可用于CAN信号的发送,接收,和不同格式之间的转换
# 同时也支持copy()和deepcopy()方法
# ①② 监听 Listener
# 用于注册接收can消息的通知,可以通过两种方式使用
# 1. 默认方式,新的can消息作为参数调用listener
# 2. 通过调用on_message_received()
# 监听类必须继承Listener类并重写on_message_received方法
# 停止监听,调用stop()方法
if __name__ == '__main__':
filters = [{"can_id":0x123, "can_mask":0x21, "extended": False}]
listener = MyListener() # ①② 定义监听器 必须要继承抽象类
with Bus() as bus: # ①
msg = Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
print("消息体的数据内容:{}".format(msg.data)) # ①①
print("消息体的数据长度:{}".format(msg.dlc)) # ①①
print("发送的消息体为:{}".format(msg))
bus.send(msg) # ③
print(bus.state) # ⑦ BusState.ACTIVE
msg = bus.recv()
# listener(msg) # ①② 结合MyListener一起来看 第一种方法
listener.on_message_received(msg) # ①② 结合MyListener一起来看 第二种方法
listener.stop() # 停止监听
"""
for msg in bus: # ⑩ 此类方法会一直循环下去,只要有消息就会执行打印
print("发送的消息看一看吧:{}".format(msg))
can.CSVWriter("zz.txt", True).on_message_received(msg) # 将收到的can消息写入文件
# 以下 ④
msg1 = Message(arbitration_id=0xc0ffaa, data=[3, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
msg2 = Message(arbitration_id=0xc0ffaa, data=[4, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
msg3 = Message(arbitration_id=0xc0ffaa, data=[5, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
print("msgs是个消息数组")
t1 = bus.send_periodic(msgs=[msg1, msg2, msg3], period=2, duration=30)
print("msgs是单个消息")
t2 = bus.send_periodic(msgs=msg, period=2, duration=30)
print("在第十秒的时候停止")
for i in range(10):
print("第{}秒.............".format(i+1))
time.sleep(1)
# t1.stop() # 通过任务来管理
# t2.stop() # 通过任务来管理
bus.stop_all_periodic_tasks() # 通过调用函数任务停止
# bus.shutdown() # 通过bus来管理 如果使用with语句初始化bus 此方法会报错
"""
# bus.set_filters(filters) # ⑤ 设置过滤器 也可以在初始化bus时设置 如 Bus(can_filters=filters)
# res = bus.recv(30) # 30s的超时时间 ②
# print("接收到的消息为:{res}".format(**locals()))
"""
# 针对不使用with语句初始化bus的示例 ④
bus = Bus()
# 以下 ④
msg = Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
msg1 = Message(arbitration_id=0xc0ffaa, data=[3, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
msg2 = Message(arbitration_id=0xc0ffaa, data=[4, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
msg3 = Message(arbitration_id=0xc0ffaa, data=[5, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
print("msgs是个消息数组")
t1 = bus.send_periodic(msgs=[msg1, msg2, msg3], period=2, duration=30)
print("msgs是单个消息")
t2 = bus.send_periodic(msgs=msg, period=2, duration=30)
print("在第十秒的时候停止")
for i in range(10):
print("第{}秒.............".format(i + 1))
time.sleep(1)
bus.shutdown() # 通过bus来管理 停止bus ⑥ 在with语句中不需要使用shutdown函数
"""
先写到这吧!最近太忙了!