はじめに
WindosやLinuxのPCからPythonでシリアル通信をする場合pyserial
を使えば可能。しかしAndroidスマホでは使えなかった。本記事では、Androidスマホでシリアル通信するライブラリusbserial4a
についてまとめる。
目次
usbserial4a
Androidのスマホでシリアル通信するためのライブラリ。pip install usbserial4a
とpip install usb4a
でライブラリ取得。kivy上で動作するライブラリなので、Kivyとセットで使う。Kivyについての記事は PythonのGUIライブラリKivyを使う を参照。
usbserial4a
はAndroid用なのでPCで動作できない。スマホにPydroid3をインストールするか、Buildozerでアプリ化して動作させる必要がある点注意。
ポート番号のリストとポートのオープン
スマホにつながっているUSBシリアルデバイスの確認をする場合は下記のようにget_usb_device_list()
でUSBデバイスリストを取得し、getDeviceName()
で、ポートの番号を取得する。
from usb4a import usb
from usbserial4a import serial4a
# 合わせてkivyライブラリもインポート必要
#port取得
serial_ports=[]
for port in usb.get_usb_device_list():
serial_list.append(port.getDeviceName())
ポートのオープンは下記の通りにポート番号、ボーレート、パリティーの設定をする。下記例は、リストの先頭のポートを、115200bps、パリティーなしでポートオープンする場合の例。
from usb4a import usb
from usbserial4a import serial4a
# 合わせてkivyライブラリもインポート必要
serial_ports=[]
for port in usb.get_usb_device_list():
serial_ports.append(port.getDeviceName())
port_val = serial_ports[0] # この例ではポートリストの最初のものを設定
boud_val = 115200 # 数値で設定
prty_val = 'N' # 'N:None','O:Odd','E:Even'のN/O/Eを抜き出す。
Serial_Port=serial4a.get_serial_port(device_name=port_val, baudrate=boud_val, parity= prty_val)
シリアル通信の送受信
シリアル通信の送信受信はwrite():送信、read():受信でできる。データの送受信はバイナリデータで行うため、文字列を送受信する場合はencode()/decode()する必要がある。受信はデータを受信するまで留まるので、リアルタイムに送受信したい場合は、並列処理できるよう別スレッドで実装する必要あり。
from usb4a import usb
from usbserial4a import serial4a
# 合わせてkivyライブラリもインポート必要
# ポートオープン:'先頭のポート' 115200bps ParityなしのOpen
serial_ports=[]
for port in usb.get_usb_device_list():
serial_ports.append(port.getDeviceName())
port_val = serial_ports[0] # この例ではポートリストの最初のものを設定
boud_val = 115200 # 数値で設定
prty_val = 'N' # 'N:None','O:Odd','E:Even'のN/O/Eを抜き出す。
Serial_Port=serial4a.get_serial_port(device_name=port_val, baudrate=boud_val, parity= prty_val)
#送信
data='test'
data=data.encode('utf-8')
Serial_Port.write(data)
#受信
data=Serial_Port.read() # dataに受信データが格納される。
data=data.decode('utf-8')
Kivyでシリアル通信アプリを作る
Kivyで下記のようなアプリを作る。今回kivyで使ったウィジェットはButton、Spinner、TextInput。Kivyについての記事は PythonのGUIライブラリKivyを使う を参照。
Spinnerでポート番号(PortNo)、ボーレート(Speed(bps))、パリティー(Parity)を選択できるようにして、Openボタンでシリアル通信を開始。ポート番号は、PortNoをクリックしたときにリアルタイムにポート番号をスキャンする形にした。
ポートオープンと、USB_Serialのアクセス許可画面の兼ね合いで、1回目はうまく通信できないときがある。その場合は、もう一度起動しなおすと通信できるようになる。
作ったコード
👇のmain.py
をPydroid3で起動させればアプリを確認できる。
また、buildozerでアプリ化するためのbuildozer.specも併せて記載する。buildozer.specは、初期値から変更した行だけ抜き出した。
main.py/buildozer.specファイルはこちら。
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.uix.boxlayout import BoxLayout #boxのレイアウト
import time
from usb4a import usb
from usbserial4a import serial4a
import threading
#kivy GUI
Layout = Builder.load_string('''
#第一階層
BoxLayout:
orientation:'vertical'
#Serial設定===============================
BoxLayout:
orientation:'horizontal'
size_hint_y:0.05 #y軸のサイズ
# Port
Spinner:
id :spinner_port
size_hint_x :0.25 #x軸のサイズ
text :'PortNo'
values :''
on_press :app.port_list() # Spinnerを押されたとき
# Speed
Spinner:
id :spinner_speed
size_hint_x :0.25 #x軸のサイズ
text :'Speed(bps)'
values :'9600','38400','115200'
# Parity
Spinner:
id :spinner_parity
size_hint_x :0.25 #x軸のサイズ
text :'Parity'
values :'N:None','O:Odd','E:Even'
#Open
Button:
id:button_open
size_hint_x :0.25 #x軸のサイズ
background_color:(0,1,0,1)
text:'Open'
on_press:app.serial_open()
#送信データ設定==========================
BoxLayout:
orientation:'horizontal'
size_hint_y:0.05 #y軸のサイズ
# Text
TextInput:
id:textinput_sendout
text: ''
font_size:50
size_hint_x :0.80 #x軸のサイズ
multiline:False # 1行
on_text_validate:app.enter_out() #Enterキーでイベント発生
# SendOut
Button:
id:button_sendout
size_hint_x :0.20 #x軸のサイズ
background_color:(0,1,0,1)
text:'SendOut'
on_press:app.send_out()
#送受信データ出力========================
TextInput:
id:textinput_serial
text: ''
font_size:50
size_hint_y:0.95 #y軸のサイズ
''')
#--------------------
# Serial通信
#--------------------
class Serial_app(App):
#Global
Serial_Port=''
OutPut=''
Thread_rx=''
#----------------------
# Kivy初期化
#----------------------
def build(self):
self.title = 'Serial_app'
#データ受信関数を設定
self.Thread_rx = threading.Thread(target=self.serial_read)
self.Thread_rx.start()
#Kivyの周期関数を設定
Clock.schedule_interval(self.update_gui, 1/30)
return Layout
#----------------------
# Kivy終了
#----------------------
def on_stop(self):
self.Serial_Port.close()
#----------------------
# 1/30 sec 毎に画面更新
#----------------------
def update_gui(self, dt):
self.root.ids.textinput_serial.text = self.OutPut
#50000文字以上になると古い文字から削除
if len(self.OutPut)>50000:
self.OutPut = self.OutPut[50000:]
#----------------------
# thread_rx(データを受信)
#----------------------
def serial_read(self):
while(1):
if self.Serial_Port !='':
data=self.Serial_Port.read() #受信
data=data.decode('utf-8')
self.OutPut+=data
else:
time.sleep(0.1)
#----------------------
# [SendOut]ボタンでデータ送信
#----------------------
def send_out(self):
self.Serial_Port.write((self.root.ids.textinput_sendout.text+'\r\n').encode('utf-8'))
#----------------------
# データ入力+[Enter]でデータ送信
#----------------------
def enter_out(self):
self.Serial_Port.write((self.root.ids.textinput_sendout.text+'\r\n').encode('utf-8'))
self.root.ids.textinput_sendout.text=''
#----------------------
# シリアルポートのlist
#----------------------
def port_list(self):
self.root.ids.spinner_port.values.clear()
for port in usb.get_usb_device_list():
self.root.ids.spinner_port.values.append(f'{port.getDeviceName()}')
#----------------------
# シリアルポートをOpen
#----------------------
def serial_open(self):
port_val = self.root.ids.spinner_port.text
boud_val = self.root.ids.spinner_speed.text
prty_val = self.root.ids.spinner_parity.text[:1] # 'N:None','O:Odd','E:Even'のN/O/Eを抜き出す。
#OpenPort
if self.Serial_Port =='':
try:
self.Serial_Port=serial4a.get_serial_port(device_name=port_val, baudrate=int(boud_val), parity= prty_val)
self.OutPut = f'OpenPort({port_val},{boud_val},{prty_val})\n'
except:
self.OutPut = 'The parameter is not available.'
if __name__ == '__main__':
Serial_app().run()
[app]
# (str) Title of your application
title = USB_Serial
# (str) Package name
package.name = usb_serial
# (list) Application requirements
requirements = python3,kivy,usbserial4a,usb4a
# (list) Permissions
android.permissions = android.permission.INTERNET, (name=android.permission.WRITE_EXTERNAL_STORAGE;maxSdkVersion=18)
アプリの作り方は 下記記事を参照。