LoginSignup
3
2

More than 1 year has passed since last update.

Raspberry PiでCAN通信

Posted at

はじめに

Raspberry Piに慣れるために、以前、NUCLEO L476RGとCANシールドで作成したボードとRaspberry PiでCAN通信を行うことにしました。

使用機器

メーカ 型格
Raspberry Pi Raspberry Pi 4 Model B
inno-maker USB2CAN

作りたいもの

  • Raspberry Pi上で動くGUIアプリケーション
  • CAN通信ができる
    これを実現するためにPythonを使うことを使うことにしました。GUIを実現する方法はいくつかあるようでしたがGoogle Trendsで上位であったTkinterを使うことにしました。

Pythonの準備

Raspberry Piには標準でPython3がインストールされているのでアップデートするだけ使用できました。
エラーが発生したのでpip(Pythonのパッケージを管理するツール)のアップデートを行いました。

sudo pip3 install --upgrade pip

python-canの準備

PythonでCAN通信を行うためにpython-canをインストールしました。

pip install python-can

inno-markerのユーザーズマニュアルにツールをインストールすることとあったので下記をインストールしました。

sudo apt-get install can-utils

python-canをインストールすればこちらは不要なのかもしれません。

Tkinterの準備

Tkinterのインストールは必要ありませんでした。

GUIアプリケーション

NUCLEOから送られてくるCANデータを1000ミリ秒ごとにポーリングしています。
通信速度は250kbpsとなります。

tk.py
import tkinter as tk
import tkinter.filedialog
from tkinter.scrolledtext import ScrolledText
from UsbCan import UsbCan

def canopen():
    openbtn['state'] = 'disable'
    closebtn['state'] = 'active'
    fopenbtn['state'] = 'active'
    textbox.delete(0, 'end')
    can.open()

    global id
    id = root.after(1000, polling)
    
    return

def canclose():
    root.after_cancel(id)

    openbtn['state'] = 'active'
    closebtn['state'] = 'disable'
    fopenbtn['state'] = 'disable'
    dwlbtn['state'] = 'disable'
    textbox.delete(0, 'end')
    can.close()

    return

def fileOpen():
    fname = tk.filedialog.askopenfilename(filetypes=[('binary', '*.bin')], initialdir='./')

    if fname != None:
        textbox.insert('end', fname)
        dwlbtn['state'] = 'active'

    return

def polling():
    msg = can.receive()

    if msg != None:
        text.insert('end', msg)
        text.insert('end', '\n')

    global id
    id = root.after(100, polling)

    return

can = UsbCan()

root = tk.Tk()
root.title('USB 2 CAN')

# set window size
root.geometry('680x480')

# frame
frame = tk.Frame(root, relief='flat')
frame.pack(fill='x', side='top')

# create open button
openbtn = tk.Button(frame, text='open', state='active', command=canopen)
openbtn.pack(fill='x', side='left')

# create close button
closebtn = tk.Button(frame, text='close', state='disable', command=canclose)
closebtn.pack(fill='x', side='left')

# frame
fileFrame = tk.Frame(root, relief='flat')
fileFrame.pack(fill='x', side='top')

# create file open button
fopenbtn = tk.Button(fileFrame, text='file', state='disable', command=fileOpen)
fopenbtn.pack(fill='x', side='left')

# create text
textbox = tk.Entry(fileFrame, width=80)
textbox.pack(fill='both', side='left')

# create download button
dwlbtn = tk.Button(root, text='Download', state='disable')
dwlbtn.pack(side='top')

# create scrolled text
text = ScrolledText(root)
text.pack(fill='both', side='top')

# loop
root.mainloop()
UsbCan.py
import subprocess
import os
import can

class UsbCan:

    """constructor"""
    def __init__(self):
        self.__state = False
        return

    """destructor"""
    def __del__(self):
        if self.__state == True:
            os.system('sudo ifconfig can0 down')

        return

    """open can port"""
    def open(self):
        self.__state = True
        os.system('sudo ip link set can0 type can bitrate 250000')
        os.system('sudo ifconfig can0 up')

        self.__can0 = can.interface.Bus(channel='can0', bustype='socketcan')

        return

    """close can port"""
    def close(self):
        self.__state = False
        os.system('sudo ifconfig can0 down')
        return

    """send data"""
    def send(self, id, data, ext=False):
        if self.__state == True:
            msg = can.Message(id, data, ext)
            self.__can0.send(msg)

        return

    """receive data"""
    def receive(self):
        if self.__state == True:
            msg = self.__can0.recv(1.0)
        else:
            msg = None

        return msg

編集後記

  • os.system と subprocess.call について
    Pythonで他のプログラム(コマンド)を実行するための標準ライブラリであるos.systemですが、これからはsubprocess.callに置き換わるということで使ってみましたが動作しませんでした。
  • socketについて
    inno-markerのサンプルでは
sample.py
# Bind the socket to 'can0'.
can0 = can.interface.Bus(cahnnel='can0', bustype='socketcan_ctypes')

とありましたが、socketcan_ctypesがないとエラーになりました。
socketcanにすることで動作しました。
socketcan_nativeを使用すればいいとの情報もありどのように決めるのか分かりませんでした。

python-canの下記のドキュメントからsocketcanにするのだろうと考えています。

python-can versions before 2.2 had two different implementations named socketcan_ctypes and socketcan_native. These were removed in version 4.0.0 after a deprecation period.

DeepLの翻訳
2.2 より前のバージョンの python-can には、socketcan_ctypes と socketcan_native という 2 種類の実装がありました。これらは非推奨期間を経て、バージョン 4.0.0 で削除されました。

参考サイト

Python3
Python-CAN
Tkinter
SocketCAN
USB2CAN WiKi
subprocess

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2