Processingで使えるシリアルポートをスキャンする

M5StackからPCへの接続方法にはいろいろありますが、無線でなくても良い、つまりUSBで電源供給しているならばUSB経由のシリアルで接続するのが妥当そうです。

このときにシリアルポートを指定しないといけないですが、ポートをスキャンするサンプルがあったので使ってみました。

参考は、

https://garchiving.com/processing-auto-serial-port/

になります。

PC側はProcessingを使っていますが、12行目から21行目までがポート検出の該当スクリプトです。ポート指定方法としてnew Serialの第二引数に”/dev/tty.usbserial-54260147311″のように直接指定する方法もありますが、環境が変われば改めて設定しなければならないでしょうから、自動検出にはメリットがあります。

import processing.serial.*;

Serial myPort;

int x;
String[] comPort = myPort.list();

void setup(){
  // 画面サイズ
  size(256, 256);

  int num = comPort.length;
  for (int i=0; i<num; i++) {
    try {
      myPort = new Serial(this, comPort[i], 115200);
      println(comPort[i]);
    }
    catch(Exception e) {
      continue;
    }
  }
}

void draw(){
  // 背景色を白に設定
  background(255);

  // XY座標を(x,100)に設定し、
  // 幅50、高さ50の円を描画
  ellipse(x,100,50,50);
}

void serialEvent(Serial p){
  //変数xにシリアル通信で読み込んだ値を代入
  x = p.read();
  println(x);
}

M5Stack側は極めてシンプルなスクリプトで1秒ごとに異なる値を送るだけ。Processing側ではcircleの座標として扱っています。

#include

void setup(){
  Serial.begin(115200);
}

void loop(){
  Serial.write(100);
  delay(1000);
  Serial.write(200);
  delay(1000);
}

 

admin

関数に可変長引数をスライスで渡すとき(Golang)

Goで可変引数を渡すときにの記述方法についてです。

可変長引数は関数で内部的にはスライスに変換されるのでスライスで渡すこともできますが、関数側は”v1(names ...string)“のように受け取りますが、呼び出し側は”v1(names...)“のように…が関数とは逆の位置に来ますよということです。

/*
Variadic functions in Golang
*/ package main import ( "fmt" ) func v1(names ...string) { // pack operator used before type in argument fmt.Println(names) } func main() { names := []string{"Albert", "Issac"} v1("John", "Jane", "Dexter", "Bruce") // [John Jane Dexter Bruce] // Here is unpack operator in action v1(names...) // [Albert Issac] name1 := []string{"John", "F.", "Kennedy"} v1(name1...) }

決まり事ではありますが、論理的に考えれば受け取り側は型を指定していて、呼び出し側は変数名(スライス名)を指定しているから合理的とは言えます。

 

admin

Goのフルスライス

あまりWebの記事中にはフルスライスという表現は出てこないようですが、スライスのスライス作成時は値参照ではなくてメモリは共有されるので、作成したスライスの振る舞いが意図したようにはなりません。

フルスライスとはスライスからスライス作成時にキャパシティと長さ(length)を同じにすることでappend()したときに必ず別領域に新たなスライス用のメモリを確保することで参照ポインタを分離して元のスライスとの干渉を避ける方法です。

y := x[:2:5]の5がキャパシタ指定です。

package main

import "fmt"

func main() {
	x := make([]int, 0, 5)
	x = append(x, 1, 2, 3, 4)
	fmt.Println("x:", x)
	y := x[:2:5]
	fmt.Println("y:", y)
	fmt.Println(len(x), len(y))
	fmt.Println(cap(x), cap(y))
	y = append(y, 30, 40, 50)
	x = append(x, 60)
	fmt.Println("x:", x)
	fmt.Println("y:", y)
	fmt.Println(len(x), len(y))
	fmt.Println(cap(x), cap(y))
}

実行結果は、メモリを共有しているのでスライス名は別でも相互干渉しています。

x: [1 2 3 4]
y: [1 2]
4 2
5 5
x: [1 2 30 40 60]
y: [1 2 30 40 60]
5 5
5 5

フルスライスにすると、

y := x[:2:2]

メモリが別領域に確保されるので干渉は発生しません。領域は1024バイト以下なら領域不足の都度二倍されるので本来は3バイトですが、6バイト確保されています。1024以上なら25%増加になるようですが、これは保証値ではなくて今後のバージョンアップで変更されるかもしれません。いずれにしてもスライスのサイズが大きくなるとメモリ領域確保とデータ転送が発生するので、サイズが大きければ性能に影響してきます。

x: [1 2 3 4]
y: [1 2]
4 2
5 2
x: [1 2 3 4 60]
y: [1 2 30 40 50]
5 5
5 6

スライスのキャパシティとサイズの遷移を図示すると以下の手書きのようになります。つまり

 

admin

Mifareカードリーダー

非接触カードで出席管理とか入室管理をするための素材です。日本はFericaですが世界的にはMifareが主流だし、Fericaカードより安いから用途によるけれどもセキュア性が重要でなければMifareで良いんじゃないかと思う。

<hardware>

いつものスイッチサイエンスで購入、送料安いしすぐ届くから便利だと思う。

https://www.switch-science.com/products/8301

<sample code>

https://github.com/m5stack/M5Stack/tree/master/examples/Unit/RFID_RC522

のコードをそのままM5Stackで動かしてみる。環境はVScode + PlatformIOです。

<ソースコードのコメントから>

The card reader and the tags communicate using a 13.56MHz electromagnetic field. The protocol is defined in ISO/IEC 14443-3 Identification cards. eg Mifare or NTAG203(古い規格)

まあMifareだけでFericaは読めないから、どのカードが何の規格採用というのは理解出来てないけれどもセキュアなカードはFerica使っているらしいから、以下の試験結果も納得できます。

キャッシュカード、運転免許証、マイナカード、ETCカード、PASMO、クレカ複数でやってみてUIDが認識されたのはクレカが2枚だけ。

唯一認識されたクレカの読み取り情報見ると7-BYTE UIDのようです。認識された2枚のUIDは当然別物になってます。従って一番単純にはUIDだけで識別ができます、それほどセキュアではないけれども。

 

受信部にはコイルが入っていて、電磁的な結合でエネルギー供給して読み取りや書き込みができます。Mifareカード買ってアプリ作ってみよう。

 

admin

 

 

recursive call

どの言語にも存在すると思いますがrecursive call(再帰呼び出し)、よく例として挙げられるのがnの階乗です。

recur()が再起呼び出しを使う方法でfact()は通常の繰り返し処理です。これだけ見たらなぜ再起処理必要なのというところでしょうが、例えばbinary tree(二分木)ではrecursive callが必須でこれ以外の方法ではうまく記述できないはずです。

package main

import (
	"fmt"
)

func main(){
	fmt.Println(recur(10))
	fmt.Println(fact(10))

}

// recursive call
func recur(n int) int{
	if n == 2{
		return 2
	}
	return n*recur(n -1)
}

// conventional method
func fact(n int) int{
	m := 1
	nf := n
	for i := 1; i <= n; i++{		
		m *= nf
		nf--
	}
	return m
}

以下はweb上に挙げられてるソースにノードの遷移がわかるようにPrint文を埋め込んだものです。よく見るとinsert()もprintNode()も再帰処理を使って同じような構造になっているのが理解できると思います。さらにdelete()を追加してもそうなります。

/*
https://selfnote.work/20210930/programming/golang-binary-tree-2/
*/

package main

import (
	"fmt"
)

type Tree struct {
	node *Node
}

type Node struct {
	value int
	left  *Node
	right *Node
}

func (t *Tree) insert(value int) *Tree {
	if t.node == nil {
		t.node = &Node{value: value}			// used only at the first node 
	} else {
		t.node.insert(value)
	}
	return t
}

func (n *Node) insert(value int) {
	if n.value >= value {
		if n.left == nil {
			n.left = &Node{value: value}
			fmt.Println("value_add_l", value)
		} else {
			fmt.Println("skip_l", value, n.left.value)
			n.left.insert(value)
		}
	} else {
		if n.right == nil {
			n.right = &Node{value: value}
			fmt.Println("value_add_r", value)
		} else {
			fmt.Println("skip_r", value, n.right.value)
			n.right.insert(value)
		}
	}
}

var i = 0

func printNode(n *Node) {
	if n == nil {
		i += 1
		fmt.Print("nil;")
		return
	}

	fmt.Println("value", n.value)
	printNode(n.left)
	fmt.Println("l",i, "node", n.value)
	printNode(n.right)
	fmt.Println("r",i, "node", n.value)
}

func main() {
	t := &Tree{}
	t.insert(2).
		insert(7).
		insert(5).
		insert(6).
		insert(1).
		insert(11).
		insert(9).
		insert(4).
		insert(20).
		insert(10)

	fmt.Println("-----------------------")
	printNode(t.node)
}

以下に実行結果から手書きでprintNode()の遷移を書いてみましたが、このように動作してます。

 

で、binary treeってどこで使われるの?ですがエンドのアプリで記述するよりもDBの内部処理などでは当たり前に使われているようです。insert(), delete(), search()ってDBでは普通に使われて、なおかつそれらを配列やスライスで表現させるとデータ追加の都度に再度作り直しが発生しますが、binary treeならば追加処理も簡単、さらにはアクセスも平均でn*log(n)で済むだろうから効率的であるとも言えます。

 

admin

Go言語(O’REILLYの初めてのGo言語)

以下の記事の書籍ですが、

https://isehara-3lv.sakura.ne.jp/blog/2023/01/07/go-lang/

とりあえず通読、理解の浅いところも無論ありますが。

O’Reilly本はハズレが少ないけど、この本も当たりでしょう。Goはかなりバージョンアップが頻繁ですが、1.18で追加されたgenericsもきちんとカバーさせてるし。「初めての」意味はもちろんコード書くのが初めてではなくて他の言語の経験が前提です。

副題にイディオマティックというタイトルがつけられていますが、これはGoFでは無いけれどもGoのミニデザインパターン的なことを言っているんだと思います。

結局Go言語の特徴(らしさ)は、並行処理はともかくも型定義、構造体とインターフェースに集約できるような気がします。

 

admin

 

Golang型指定でのチルダの意味

型指定時にチルダを使って~intとか~stringとか出てきますが、どういう意味かというと基底型を意味するのだと。

例えば以下のコードは、

https://github.com/mushahiroyuki/lgo/blob/main/example/ch15/ex1506b.go

を多少修正したものですが、独自の型をstringの拡張として定義するとstringに~なしではコンパイルエラーになりますが~を付けることでstringの派生型も含めることが出来るわけです。

package main

import (
	"fmt"
)

type BuiltInOrdered interface {
	~string | int | int8 | int16 | int32 | int64 | float32 | 
		float64 | uint | uint8 | uint16 | uint32 | uint64 | uintptr

} 

func Min[T BuiltInOrdered](v1, v2 T) T { 
	if v1 < v2 {
		return v1
	}
	return v2
}

type Mystring string

func main() {
	var Astring Mystring = "a"
	var Bstring Mystring = "b"
	fmt.Println(Min(Astring, Bstring)) 
} 

 

admin

 

 

brewとGolang版数

Golangは2023/2に1.20になっていますが、brewで配布されるのは1.19.5が最新版になっています。

brewの場合には多少の遅延があるということだろうと思いますが、とりあえず実害はありませんが。

 

admin

 

M5Stackでバッテリ容量が読み取れない

パルスオキシセンサーでバッテリー容量の表示をしようと思ってコードを追加しましたが、センサーの初期化処理が干渉しているようで値(初期化処理後は-1が帰る)が読み取れません。

void setup()
{
  M5.begin();
  M5.Power.begin();
  Wire.begin();         // Wire init, adding the I2C bus.
  Serial.begin(115200); // to PC via USB
  M5.Lcd.clear(BLACK);
  M5.Lcd.setTextSize(4);
  // Initialize sensor
  if (!pox.begin()) {
    M5.Lcd.println("FAILED");
    for(;;);
   } else {
    M5.Lcd.println("SUCCESS");
  }
  int bat_level = M5.Power.getBatteryLevel();
  Serial.println(bat_level);

loop()処理で読み取れないのでsetup()処理中に読み出しを埋め込むと// Initialize sensorの直後の(

pox.begin()

)から後ろでは-1が帰るようです。

なんだろう?

 

admin

 

 

心拍センサーでパルスオキシメーターを作る

心拍センサーというのが市販されていて、それを使うとパルスオキシメーターが作れるので使ってみました。

https://qiita.com/ghibi/items/aa9ae016dbbe9138a505

にやりたいイメージ通りの例があるので、ほぼそのままを使っています。ただしVScodeで使おうとすると関数を先に定義しないと呼び出せないので、入れ替えはしてあります。関数宣言でもいいですが、これはArduino IDEとVScodeの差分です。

また、デフォルトの設定ではうまく測定値が返ってこないので、

  pox.setIRLedCurrent(MAX30100_LED_CURR_4_4MA);
  //Register a callback for the beat detection
  pox.setOnBeatDetectedCallback(onBeatDetected);
pox.setIRLedCurrent(MAX30100_LED_CURR_4_4MA); 行を追加して、IR ledの電流値を調整しています。画面左上のHeart Pulseマークを見ながら指を当てて測定するのですが、測定値の安定には数秒かかります。
P.S. (2023/2/9)
ライブラリのURL追記
admin