Armadillo-640で、下記LoRaデバイスを動かしてみました。
LoRaモジュール評価ボード:E220-900T22S(JP)-EV1
(以下では、「LoRaデバイス」と略称します)
LoRa用アンテナ(TX915-JKS-20)
以下の手順で動かすにあたり、
上記製品サイトの下記資料(データシート等)
を参考にさせていただきました。
ここでは、
送信側:LoRaデバイス+Armadillo-640(送信用プログラム実行)
受信側:LoRaデバイス+Armadillo-640(受信用プログラム実行)
を用意して、送信側から受信側へLoRa通信した手順を説明しています。
Armadillo-640は、二種類のOS(ABOS(※)/Debian)に対応しています。
※)ABOS:Armadillo Base OS
ここでは、ABOS版での標準的な開発手順に沿って説明します。
Debian版は、
Armadillo-640(OS:Debian):LoRaデバイスを動かしてみたを参照ください。
参考)ABOS版で、簡易的にコマンドラインだけで確認したい場合の手順は、
Armadillo-640(OS:ABOS):LoRaデバイスを動かしてみた(簡易確認版)
を参照ください。
手順1. Armadillo-640の準備
LoRaデバイスとの接続には、UART×1(TXD/RXD)、GPIO×3(M0/M1/AUX)、電源5V(VCC)、GNDが必要です。
Armadillo-640の拡張I/Fの下記の端子を使います。
ここでは、ABOSを使用するため、
Armadillo-640:インストール方法(手順まとめ版)を参考に
Armadillo Base OS対応 Armadillo-640 インストールディスクイメージでABOSをインストールしておきます。
実のところ、上記の方法で標準のインストールディスクイメージでインストールすると、
上記の端子設定が有効になっています。
他にカーネル、端子設定をする場合は、カーネルをビルドし直す必要がありますが、
ここでは、このまま変更せずに手順2に進みます。
手順2. Armadillo-640とLoRaデバイスの接続
Armadillo-640の拡張I/FとLoRaデバイスの各端子を下図のように接続します。
上記端子とアンテナを接続した状態の写真です。
(双方のピンヘッダをヘッダ用接続ケーブルで接続しました)
ここでは、送信/受信用に上図の状態のArmadillo-640+LoRaデバイスを2個用意しました。
手順3. プログラムの作成(はじめに)
ここでの確認では、簡易的なGPIO制御とシリアル通信のプログラムで動かしてみます。
以下のプログラムでは、エラー処理、フェイルセーフは考慮していません。
また、上図でAUXは接続していますが、以下のプログラムでは使用していません。
補足)
LoRaデバイスの製品サイトには、サンプルプログラムも用意されています。
手順4. プログラム作成の準備
ここでは、ABOSDE(備考を参照)のサンプルプロジェクトを使って、プログラムを作成します。
備考)ABOS対応製品のアプリケーション開発にあたっては、VSCode上の開発環境として
ABOSDE(※)が用意されています。
※)ABOSDE:Armadillo Base OS Development Environmetの略称
ABOSDEには、C言語/Python/Shell/GUI等、プログラミング言語や機能に応じたサンプルプロジェクトがあります。
これらのサンプルプロジェクトをベースに、ユーザー独自のアプリケーション開発が始められます。
Armadillo Base OS:開発手順・Tips集の
【必須】①initial_setup
【任意】②ネットワーク設定
【任意】③拡張インターフェース設定
まで進めておきます。
以降、同ブログの
【必須】④アプリケーション作成
Armadillo Base OS:アプリケーションの作り方
の手順をベースに説明します。
手順5. サンプルプロジェクトを使ってLoRa通信アプリケーションを作成
ABOSDEには複数のサンプルプロジェクトが用意されていますが、
ここでは「Shell New Project」を対象とします。
5-1. 「Shell New Project」で新規プロジェクト(example1)を作成
Armadillo Base OS:アプリケーションの作り方
1.プロジェクト作成(※)
※)この図中の「OPEN NEW PROJECT」の箇所では「A600」を選択します。
2.SSH接続設定を行う(任意)
注)「任意」となっていますが、後述のLoRa通信確認をSSHで接続して実施するため、
この手順は実施ください。
以上の手順に沿って、「Shell New Project」でのプロジェクト作成を進めます。
5-2. サンプルプログラムが動くコンテナを生成するための設定
プロジェクト(下記、赤枠アイコンで展開した赤線のファイル)を下記の内容で書き換えます。
Dockerfile
このファイルの全体を下記の内容に書き換え、Debian:busterコンテナを作成するようにします。
補足)Debianのバージョンは、bullseye、bookwormの方が新しいですが、後述のプログラムは動作しませんでした。
pythonのバージョン依存のようなので、バージョンに合わせて改変すれば動くと思われますが未確認です。
ARG ARCH
FROM docker.io/${ARCH}/debian:buster
LABEL version="2.0.0"
COPY resources/etc/apt /etc/apt/
ARG PACKAGES
RUN apt-get update && apt-get upgrade -y \
&& apt-get install -y ${PACKAGES} \
&& apt-get clean
ARG PRODUCT
COPY resources [r]esources_${PRODUCT} /
RUN useradd -m -u 1000 atmark
RUN apt install -y python3
RUN apt install -y python3-pip
RUN apt install -y python3-libgpiod
RUN python3 -m pip install pyserial
RUN python3 -m pip install timeout-decorator
app.conf
このファイル全体をLoRaデバイスとのインタフェース(※)へのアクセス許可を含むコンフィギュレーションに書き換えます。
※)手順1の図より、UART:ttymxc4、GPIO:gpiochip3
補足)動作確認用にLEDを使うことを想定して、/sysへのアクセス許可も含めています。
set_image localhost/{{PROJECT}}:latest
add_volumes /var/app/rollback/volumes/{{PROJECT}}:/vol_app
add_volumes /var/app/volumes/{{PROJECT}}:/vol_data
# Allow LED
add_volumes /sys
# Allow UART and GPIO for LoRa device
add_devices /dev/ttymxc4
add_devices /dev/gpiochip3
# Allow input to containers and see output from containers
add_args -it
set_command bash /vol_app/src/main.sh
app/src
この箇所には、下記のファイルを追加します。
set_gpio.py:LoRaデバイスのM0/M1/AUX用のGPIOを設定
get_conf_all.py:LoRaデバイスのコンフィグの読み込み
set_conf_all.py:LoRaデバイスのコンフィグの設定
send_sample.py:送信
recv_sample_v2.py:受信
「app/src」を右クリックして、「New File」で以下に説明する内容で各ファイルを作成します。
以下のプログラム作成にあたっては、
LoRaデバイスの製品サイトの
「製品データシート Firmware Ver.1.0/1.2 rRev2.1.2」を参考にしてます。
備考)上記Dockerfileの箇所での説明の通り、下記プログラムは、Debian:buster(python3.7)で動作確認したものです。
Debian:bullseye(python3.9)では動作しませんでした。
以下内容のプログラムを作成すると、下記赤枠の「arc/src」内のようなファイルが生成されることになります。
set_gpio.py
import gpiod
# Set Armadillo-640 GPIO
# AUX: gpiochip3 7(CON9_26) for INPUT
# M0: gpiochip3 8(CON9_27) for OUTPUT
# M1: gpiochip3 9(CON9_28) for OUTPUT
chip = gpiod.Chip('gpiochip3', gpiod.Chip.OPEN_BY_NAME)
e220_aux = chip.get_line(7)
e220_m0 = chip.get_line(8)
e220_m1 = chip.get_line(9)
e220_aux.request(consumer='foo', type=gpiod.LINE_REQ_DIR_IN)
e220_m0.request(consumer='foo', type=gpiod.LINE_REQ_DIR_OUT)
e220_m1.request(consumer='foo', type=gpiod.LINE_REQ_DIR_OUT)
# For setting E220 Mode
# (M0, M1) = (0, 0): Normal Communication
# (M0, M1) = (1, 1): Config
def set_e220_mode(m0_val, m1_val):
e220_m0.set_value(m0_val)
e220_m1.set_value(m1_val)
# Set E220 Config Mode:(M0, M1) = (1, 1)
set_e220_mode(1, 1)
説明)LoRaデバイスのM0/M1/AUX用のGPIOを設定します。
プログラム内の「gpiochip3」「7/8/9」は、LoRaデバイスと接続している
Armadillo-640の「gpiochip3 7/8/9」に相当します。
本プログラム内の「set_e220_mode」は、以下のプログラムでもimportして使用します。
get_conf_all.py
import serial
import time
import timeout_decorator
# For using set_e220_mode in set_gpio.py
import set_gpio
# Set E220 Config Mode:(M0, M1) = (1, 1)
set_gpio.set_e220_mode(1, 1)
# Set serial port
ser = serial.Serial('/dev/ttymxc4', 9600)
# Read command E220 register(00h-08h: 9Byte): begin
read_cmd_conf0_8 = bytearray([0xc1,0x00,0x09])
def send_cmd(cmd):
ser.write(cmd)
@timeout_decorator.timeout(1)
def recv_res():
# get response: [command] [starting address] [length]
res = ser.read(3)
print('Response command:', res.hex())
# get register data by [length]
num = int(res.hex()[5:])
res = ser.read(num)
print('Register value[00h-08h]:', res.hex())
ser.reset_input_buffer()
ser.reset_output_buffer()
send_cmd(read_cmd_conf0_8)
time.sleep(1)
recv_res()
# Read command E220 register(00h-08h: 9Byte): end
説明)LoRaデバイスをコンフィグモード:(M0, M1) = (1, 1)にします。
コンフィグ(レジスタ:00h-08h)を読み込みます。
以降のプログラム中の「ttymxc4」は、LoRaデバイスと接続している
Armadillo-640のUART5(ttymxc4)を指しています。
UARTのボーレート(9600)は、LoRaデバイスのデフォルトに合わせています。
set_conf_all.py
import sys
import serial
import time
import timeout_decorator
# For using set_e220_mode in set_gpio.py
import set_gpio
# Set E220 Config Mode:(M0, M1) = (1, 1)
set_gpio.set_e220_mode(1, 1)
# Set serial port
ser = serial.Serial('/dev/ttymxc4', 9600)
# Write command E220 register(00h-07h: 8Byte)
write_cmd_conf0_7 = bytearray([0xc0,0x00,0x08])
conf0_7 = bytearray([0x00,0x00,0x70,0x21,0x00,0xc0,0x00,0x00])
# Overwrite my address and channel from args
addr = int(sys.argv[1]).to_bytes(2, 'big')
ch = int(sys.argv[2]).to_bytes(1, 'big')
conf0_7[0] = addr[0]
conf0_7[1] = addr[1]
conf0_7[4] = ch[0]
# Write command E220 register(00h-07h: 8Byte): begin
def send_cmd(cmd):
ser.write(cmd)
@timeout_decorator.timeout(1)
def recv_res():
# get response: [command] [starting address] [length]
res = ser.read(3)
print('Response command:', res.hex())
# get register data by [length]
num = int(res.hex()[5:])
res = ser.read(num)
print('Register value[00h-07h]:', res.hex())
ser.reset_input_buffer()
ser.reset_output_buffer()
send_cmd(write_cmd_conf0_7 + conf0_7)
time.sleep(1)
recv_res()
# Write command E220 register(00h-07h: 8Byte): end
説明)LoRaデバイスをコンフィグモード:(M0, M1) = (1, 1)にします。
コンフィグ(レジスタ:00h-07h)を設定します。
conf0_7 = bytearray([0x00,0x00,0x70,0x21,0x00,0xc0,0x00,0x00])の箇所
00h=0x00:自身のアドレス上位バイト(0x00)
01h=0x00:自身のアドレス下位バイト(0x00)
02h=0x70:UARTのボーレート(9600bps)、Air Data Rate(1758bps)
03h=0x21:ペイロード長(200Byte)、RSSI取得(有効)、送信出力(13dBm)
04h=0x00:自身のチャネル(0x00)
05h=0xc0:RSSIバイト(有効)、送信方法(通常送信)、定電圧動作(無効)、WORサイクル(500ms)
06h=0x00:キー上位バイト(0x00)
07h=0x00:キー下位バイト(0x00)
上記設定のうち、自身のアドレスとチャネルのレジスタ(00h, 01h, 04h)は、
コマンド実行時の引数で変更できるようにしています。
これ以外のコンフィグを変更する場合は、予めconf0_7を直接改変してください。
send_sample.py
import serial
import sys
# For using set_e220_mode in set_gpio.py
import set_gpio
# Set E220 Normal Communication Mode:(M0, M1) = (0, 0)
set_gpio.set_e220_mode(0, 0)
# Set serial port
ser = serial.Serial('/dev/ttymxc4', 9600)
# Set send data: begin
addr = int(sys.argv[1]).to_bytes(2, 'big')
ch = int(sys.argv[2]).to_bytes(1, 'big')
length = len(sys.argv[3]).to_bytes(1, 'big')
data = sys.argv[3].encode()
padding = '**********'.encode()
send_data = addr + ch + length + data + padding
# Set send data: end
# Send data
ser.reset_output_buffer()
ser.write(send_data)
print(send_data.hex())
説明)LoRaデバイスを通常送受信モード:(M0, M1) = (0, 0)にします。
コマンド実行時に、宛先のアドレス、チャネル、データを引数に記述します。
送信データの先頭に、データ長を自動で挿入しています。
補足)ここでの確認では、10Byte以上のデータでないと、うまくデータが送信?、受信?
できなかったため、
padding(「*」を10個)を入れています。(原因不明。未調査)
recv_sample.py
import serial
import time
# For using set_e220_mode in set_gpio.py
import set_gpio
# Set E220 Normal Communication Mode:(M0, M1) = (0, 0)
set_gpio.set_e220_mode(0, 0)
# Set serial port
ser = serial.Serial('/dev/ttymxc4', 9600)
ser.reset_input_buffer()
# Recieve data
while(1):
length = int.from_bytes(ser.read(1), 'big')
print(length)
if length < 100:
data = ser.read(length)
print(data)
time.sleep(0.1)
rest = ser.read(ser.in_waiting)
print(rest)
if len(rest) == 11:
print(rest[10] - 256, '[dBm]')
ser.reset_input_buffer()
time.sleep(0.1)
説明)自身のアドレスとチャネルで受信したデータをコンソールに表示します。
上記send_sample.pyの引数に与えたデータと、padding以降を分けて表示します
補足)上記set_conf_all.pyでは、RSSIバイトの表示を有効化しているので、
paddingに続く末尾のByteからRSSIを算出して表示しています。
main.sh
上記のapp.confの最終行の記述(set_command)により、main.shは自動起動するようになっています。
ここでは、送信側/受信側が下記のような動作するmain.shをそれぞれ作成します。
(送信側/受信側のmain.sh全体を下記のように書き換えます。)
main.sh(送信側)
#!/bin/sh
#### Set path ####
APP_PATH="/vol_app/src"
LED_PATH="/sys/class/leds/red"
sleep 1
#### Set LoRa configuration ####
echo "######## Set configuration ########"
python3 /vol_app/src/set_gpio.py
python3 /vol_app/src/set_conf_all.py 2 5
echo "######## Start sending data ########"
#### Loop by 5 seconds ####
while :
do
echo 1 > $LED_PATH/brightness
#### Send data via LoRa ####
MESSAGE=`date "+%T"`
python3 $APP_PATH/send_sample.py 1 5 $MESSAGE
#### Record Log file ####
echo $MESSAGE >> /vol_data/log.txt
LOG_SIZE=`wc -c /vol_data/log.txt | awk '{print $1}'`
if [ ${LOG_SIZE} -gt 1000 ]; then
mv /vol_data/log.txt /vol_data/log_backup.txt
fi
sleep 1
echo 0 > $LED_PATH/brightness
sleep 4
done
説明)
#### Set path ####
APP_PATH:プログラムが配置されている/vol_app/srcへのパス
LED_PATH:赤LEDを制御するためのパス
#### Set LoRa configuration ####
この(送信側)の「LoRaデバイスアドレス:2、チャンネル:5」に設定。
#### Loop by 5 seconds ####
以下を5秒毎に繰り返す
赤LEDが1秒点灯
#### Send data via LoRa ####
dateコマンドの実行結果(時刻)を受信側「LoRaデバイスアドレス:1、チャンネル:5」に送信
#### Record Log file ####
送信データ(ここでは、dateコマンドの実行結果)を/vol_data/log.txtに保存
log.txtのサイズが1kBを越えると、log_backup.txtに移動(簡易的なログローテーション)
main.sh(受信側)
#!/bin/sh
#### Set path ####
APP_PATH="/vol_app/src"
LED_PATH="/sys/class/leds/red"
sleep 1
#### Set LoRa configuration ####
echo "######## Set configuration ########"
python3 /vol_app/src/set_gpio.py
python3 /vol_app/src/set_conf_all.py 1 5
echo "######## Start receiving data ########"
#### Receive data via LoRa ####
python3 -u /vol_app/src/recv_sample.py | while read line; do
echo 1 > $LED_PATH/brightness
echo "$line"
#### Record Log file ####
echo "$line" >> /vol_data/log.txt
LOG_SIZE=`wc -c /vol_data/log.txt | awk '{print $1}'`
if [ ${LOG_SIZE} -gt 1000 ]; then
mv /vol_data/log.txt /vol_data/log_backup.txt
fi
echo 0 > $LED_PATH/brightness
done
説明)
#### Set path ####
APP_PATH:プログラムが配置されている/vol_app/srcへのパス
LED_PATH:赤LEDを制御するためのパス(ここでは使用しない)
#### Set LoRa configuration ####
この(受信側)の「LoRaデバイスアドレス:1、チャンネル:5」に設定。
#### Receive data via LoRa ####
受信側「LoRaデバイスアドレス:1、チャンネル:5」宛のデータをコンテナ内の標準出力に出力
pythonの「-u」オプションで、受信データの標準出力をwhileにパイプ
受信データを標準出力
受信データを/vol_data/log.txtに保存
log.txtのサイズが1kBを越えると、log_backup.txtに移動(簡易的なログローテーション)
上記の受信処理中、赤LEDが1秒点灯
手順6. コンテナのビルドとArmadilloへの書き込み
Armadillo Base OS:アプリケーションの作り方の
4.コンテナのビルド
「Generate development swu」を実行します。
参考)環境にも依存しますが、python等のインストールに時間がかかり、30分程度要します。
5.アップデートでSWUファイル書き込み
を実施します。(ここで作成したコンテナのサイズが大きいため、10分程度かかります。)
上記により書き込みが終了すると、自動的に再起動して、main.shが実行されます。
手順7. LoRa通信の確認(コンテナ内の標準出力をVSCodeで確認)
手順6によりLoRa通信のアプリケーションは自動起動します。
(送信/受信のタイミングは、赤LEDの点灯で確認できます。)
ここでは、コンテナ内の標準出力に出ているログを確認します。
Armadillo Base OS:アプリケーションの作り方「6.デバッグ実行(任意)」の方法で動作確認してみます。
6.デバッグ実行(任意)
に記載の通り、
1.App stop on Armadillo でコンテナを停止
2.App run on Armadillo でコンテナを起動
App run on Armadillo を実行するとVScodeのコンソールにログが表示されるようになります。
送信側のログ
######## Set configuration ########
コンフィギュレーションでLoRaデバイスに書き込んだ値(16進数→10進数に表記)
######## Start sending data ########
送信の際にLoRaデバイスに書き込んだデータ(16進数→10進数に表記)
補足)送信データは、dateコマンドの実行結果ですが、このログではASCIIコードに変換されて表示されています。
受信側のログ
######## Set configuration ########
コンフィギュレーションでLoRaデバイスに書き込んだ値(16進数→10進数に表記)
######## Start receiving data ########
受信ごとに下記のように表示されます。
1行目:データ長
2行目:データ
3行目:paddingとRSSI値(LoRaデバイスが出力する生データ(16進数))
4行目:RSSI値(dBmに算出したもの)
手順8. LoRa通信の確認(コンテナ外のログファイルで確認)
手順5のmain.shでは、コンテナ内の標準出力と同じログを下記のファイルに出力しています。
/vol_data/log.txt
/vol_data/log_backup.txt
なお、app.confの設定(add_volumes)により、/vol_dataはコンテナの外のディレクトリ
/var/app/volumes/[コンテナ名]
が割り当てられています。
ここでのコンテナ(example1)の/vol_dataのファイルは、コンテナの外からは、
/var/app/volumes/example1/log.txt
/var/app/volumes/example1/log_backup.txt
で見ることができます。
送信側のArmadilloにログインしてlog.txtの確認した例
armadillo:~# tail /var/app/volumes/example1/log.txt
01:55:01
01:55:07
01:55:12
01:55:17
01:55:23
01:55:28
01:55:34
01:55:39
01:55:44
01:55:50
受信側のArmadilloにログインしてlog.txtの確認した例
armadillo:~# tail /var/app/volumes/example1/log.txt
b'**********xec'
-20 [dBm]
8
b'01:55:44'
b'**********xef'
-17 [dBm]
8
b'01:55:50'
b'**********xef'
-17 [dBm]