プロセス間通信

概要

 PythonSignal Handlerを調べているとInter thread communicationという単語が気になった。
 Inter thread communicationはスレッド間通信であり、WikiPediaを見る限りプロセス間通信 - Wikipediaと同じだ。  WikiPediaのIPC(Inter Process Communication)の一覧を示す。
 シグナルもプロセス間通信に該当する。使う場合は「非同期シグナル安全」な関数を利用する、それ以外では排他処理する必要がある。

UiFLOWのmicroWebSrvでWebAPI作成

概要

 UiFLOWのmicroPythonにてM5StickC上にWebAPIを実装し、遠隔からサーボモータをコントールする。

動作の様子

コード

from m5stack import *
from m5ui import *
from uiflow import *
from MicroWebSrv.microWebSrv import MicroWebSrv
import wifiCfg
import ubinascii
wifiCfg.autoConnect(lcdShow = False)
ip = wifiCfg.wlan_sta.ifconfig()  
label0 = M5TextBox(10, 25, ip[0], lcd.FONT_Default, 0xFFFFFF, rotate=0)

import hat
hat_servo_0 = hat.get(hat.SERVO)

@MicroWebSrv.route('/set0deg')
def handlerFuncGet(httpClient, httpResponse) :
  print("In GET-TEST HTTP set0deg")
  hat_servo_0.write_angle(0)

@MicroWebSrv.route('/set90deg')
def handlerFuncGet(httpClient, httpResponse) :
  print("In GET-TEST HTTP set90deg")
  hat_servo_0.write_angle(90)

'''
@MicroWebSrv.route('/post-test', 'POST')
def handlerFuncPost(httpClient, httpResponse) :
  print("In POST-TEST HTTP")
  hat_servo_0.write_angle(90)
'''

mws = MicroWebSrv()      # TCP port 80 and files in /flash/www
mws.Start(threaded=True) # Starts server in a new thread

def buttonA_wasPressed():
  # global params
  hat_servo_0.write_angle(0)
  pass
btnA.wasPressed(buttonA_wasPressed)

def buttonB_wasPressed():
  # global params
  hat_servo_0.write_angle(90)
  pass
btnB.wasPressed(buttonB_wasPressed)

参考

GitHub - jczic/MicroWebSrv: A micro HTTP Web server that supports WebSockets, html/python language templating and routing handlers, for MicroPython (used on Pycom modules & ESP32)

詰まったところ

  • USB接続でM5C単体の5Vピンからサーボモータの電源を取ると、電流不足のためかリセットを繰り返してしまった。
  • M5Stack社製の白い18650Cのリチウムバッテリーから電源を取ると動作したため、一先ずこれにした。
  • 同じ理由のためかM5 Atomでも動作しなかった。今度tailバッテリーを付けてみて試したい。

所感

 遠隔電源ON/OFFできない機器はこれを使うのが良い・・・?

Thread処理と非同期処理をやってみた(Python)

並行処理としてマルチプロセス処理、並列処理としてThread処理、非同期処理がある。 Python multiprocessing vs threading vs asyncio - JX通信社エンジニアブログ

今回はThread処理、非同期処理について実行した備忘録となる。

方法

Threadクラスを継承する

Python3でマルチスレッド処理を試してみる - Corgi Lab. ~備忘録のための技術ブログ~

Threadクラスのインスタンスをつくる

Python3でマルチスレッド処理を試してみる - Corgi Lab. ~備忘録のための技術ブログ~

import time
import threading

def proc():
    for i in range(0,5):
        time.sleep(1)
        print("count", i)

if __name__ == '__main__':
    th1 = threading.Thread(target=proc)
    th2 = threading.Thread(target=proc)
    th1.start()
    th2.start()

クラスの中で更にスレッド処理させようとも試みたがうまく行かなかった。

非同期I/Oを利用する

非同期I/Oでやってみたコードが下記。 iPerf Client / Serverを実行し、結果をリアルタイムに取得する。

# 5.2. プロセス生成: SSHClientConnection.create_process()
# https://blog.hoxo-m.com/entry/asyncssh

import asyncio, asyncssh, sys, getpass

async def async_ssh(cmd):
    pw = "password"
    async with asyncssh.connect("localhost", username='user',password=pw) as conn:
        if "-c" in cmd: await asyncio.sleep(1)
        async with conn.create_process(cmd, stdout=sys.stdout) as process:
            await process.wait()

try:
    iperf_srv = "iperf -s -u -i1 -t5"
    iperf_cli = "iperf -u -c localhost -i1 -t5"
    gather = asyncio.gather(
        async_ssh(iperf_srv),
        async_ssh(iperf_cli)
    )
    asyncio.get_event_loop().run_until_complete(gather)   
    #loop = asyncio.get_event_loop()
    
except (OSError, asyncssh.Error) as exc:
    sys.exit('SSH connection failed: ' + str(exc))

孫クラスからThreadクラスのinit()をコール

ベースクラスとしてThreadクラスを持たせ、その孫クラスにてスレッドを実行する場合を書く。

Threadクラスを継承するaクラス、aクラスを継承するbクラス、bクラスを継承するcクラスといった構成にて、 それぞれIinit()コンストラクタが設定されている場合、各々init()をオーバーライトしているため、 最下のcクラスからThreadクラスのinit()をコールする事ができない。

この場合、Threadクラスのinit()をコールするためには、各クラスで上位クラスのコンストラクタをコールすればよい。

#a.py
import threading

class a(threading.Thread):
    def __init__(self):
        print('a')
        super().__init__()
#b.py
from a import a

class b(a):
    def __init__(self):
        print('b')
        super().__init__()
#c.py
from b import b
import threading
import time

class c(b):
    def __init__(self,name):
        print('c',name)
        self.__name = name
        super().__init__()

    def run(self):
        while 1:
            print('c-loop',self.__name)
            time.sleep(1)

if __name__ == '__main__' :
    c1=c('c1')
    c1.start()
    c2=c('c2')
    c2.start()

余談

余談だが、クラス図の作成はpylintを用いて作成した。

pyreverse -o png . -A -f ALL

オプションを設定しないと全クラスやプライベート変数を表示できないため、-Aで全クラス、-fでプライベート変数を表示する。 参考:Python でパッケージやクラス間の依存関係を可視化してみた.[Pyreverse] - 人生ゲーム - 限界理系大学院生の日々の呟き

参考

ESP32 無線関係資料

ESP32のWIFI_MODE_APSTA(Client AP共用モード)を調べていると、1228pageに渡る"Kolban's book on ESP32"と称される秘伝の書に辿り着いた。仙人の巻物感ある Leanpub: Publish Early, Publish Often

AP STA BT 共用方法

WiFI AP起動後にSTAを立ち上げる。

#include "WiFi.h"
...
     WiFi.mode(WIFI_AP);
     WiFi.softAP(AP_SSID);
     WiFi.begin(STA_SSID, STA_PASS);

Bluetooth + STAも可能。

#include "WiFi.h"
...
      WiFi.begin(STA_SSID, STA_PASS);
      btStart();

詳細は、Arduino IDE > Examples > WiFi > WiFiBlueToothSwitch を参照。

Web server

同期式

ESP32 Web Server - Arduino IDE | Random Nerd Tutorials

非同期

VS codeショートカット

VS Code の便利なショートカットキー - Qiita

C++ headerとcppファイルによるコンパイル

headerとcppファイルを分割してコンパイルする方法がわからなかったので備忘録として記録する。

C++にてheaderとcppファイルによるコンパイルを行うには、分割コンパイルとリンクを行う必要がある。 student.hとstudent.cppによりクラスを作成し、mainからstudentインスタンスを呼び出す事を考える。

student.h

using namespace std;

class student{
        int ID;
        //string name;

public:
        student(int id=100);

        int getID();
};

student.cpp

#include "student.h"
#include <iostream>

student::student(int id){
        ID = id;
}

int student::getID(){
        return ID;
}
#include "student.h"
#include <iostream>

using namespace std;

main.cpp

int main(){
        //student s;
        //cout << s.getID() << endl;

        student s(5);
        cout << s.getID() << endl;
        return 0;
}

二つのcppファイルをそれぞれ分割コンパイルし、オブジェクトファイルを生成し、最後にオブジェクトファイルをリンクする。

g++ -c student.cpp -o student.o
g++ -c main.cpp -o main.o 
g++ -o main main.o student.o main.o
 ```

./main を実行すると正常動作しているのがわかる。

M5StickC Magic Wandからの学び

M5StickCのジェスチャでLEDを変色させるMagic Wandのコードで学びがあったため記載しておく。

プロジェクトは下記から確認できる。 それにしても認識は早い。これは機械学習を使っていないためという事もある。

https://m5stack.hackster.io/shasha-liu/magic-wand-752f52 GitHub - m5stack/MagicWand: IMU sensor apply to Motion capture

実際ジェスチャという点ではMotion Controller | ProtoPedia と似ており、 Magic WandはRoll/Pitch/Yawを相補フィルターによって求めている。

相補フィルターは、加速度センサ、ジャイロセンサから取得した値に係数を乗じて角度を算出する方法だ。

新入社員が制作実習でドローンの自作に挑戦した話。(3) | 組込み技術ラボ

尚、Motion ControllerはIMU FilterにMahony Filterを利用している。

さて気になる所だが、コードでTaskSchedulerライブラリ#include <TaskScheduler.h> が呼ばれていたため、動作を調べてみた。 https://github.com/arkhipenko/TaskScheduler

利点としては、センサやLED等をTask割当してスケジューリング実行したい場合にTask割当をシンプルに記載できるところか。 定常的にTaskを実行する処理は、Taskにコールバック関数を登録してloop関数中のrunner.execute()から常にTaskを呼び出す流れでかける。

下記はコード一例だ。

#include <TaskScheduler.h>

void led_blink();

Scheduler runner;

Task M5LED(5000,TASK_FOREVER,&led_blink);
bool statusLED = false;

void setup(){
  Serial.begin(9600);
  runner.addTask(M5LED);
  M5LED.enable(); 
  pinMode(10,OUTPUT);
}

void loop(){
  runner.execute();
}

void led_blink(){
  statusLED = !statusLED;
  digitalWrite(10,statusLED);
  Serial.println(statusLED);
}

vTask...はCPU割当が可能だがTask Schedulerはできそうにない。

GNURadioで生成した.pyファイルを自動起動する