3
3
記事投稿キャンペーン 「2024年!初アウトプットをしよう」

PythonでAndroidスマホのシリアル通信をする。

Last updated at Posted at 2024-01-06

はじめに

WindosやLinuxのPCからPythonでシリアル通信をする場合pyserialを使えば可能。しかしAndroidスマホでは使えなかった。本記事では、Androidスマホでシリアル通信するライブラリusbserial4aについてまとめる。

目次

usbserial4a

Androidのスマホでシリアル通信するためのライブラリ。pip install usbserial4apip install usb4aでライブラリ取得。kivy上で動作するライブラリなので、Kivyとセットで使う。Kivyについての記事は  PythonのGUIライブラリKivyを使う を参照。
usbserial4aはAndroid用なのでPCで動作できない。スマホにPydroid3をインストールするか、Buildozerでアプリ化して動作させる必要がある点注意。

戻る

ポート番号のリストとポートのオープン

スマホにつながっているUSBシリアルデバイスの確認をする場合は下記のようにget_usb_device_list()でUSBデバイスリストを取得し、getDeviceName()で、ポートの番号を取得する。

port_list.py
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、パリティーなしでポートオープンする場合の例。

port_open.py
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()する必要がある。受信はデータを受信するまで留まるので、リアルタイムに送受信したい場合は、並列処理できるよう別スレッドで実装する必要あり。

tx_rx.py
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回目はうまく通信できないときがある。その場合は、もう一度起動しなおすと通信できるようになる。

image.png

戻る

作ったコード

👇のmain.pyをPydroid3で起動させればアプリを確認できる。
また、buildozerでアプリ化するためのbuildozer.specも併せて記載する。buildozer.specは、初期値から変更した行だけ抜き出した。

main.py/buildozer.specファイルはこちら。
main.py
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()
buildozer.spec
[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)

アプリの作り方は 下記記事を参照。

戻る

3
3
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
3