フィラメント吸湿対策

3Dプリンタのフィラメントは吸湿しやすく、特に夏場の高湿時には過酷です。吸湿すると造形品質劣化するのと、ノズルも痛むし、硬化して折れてしまって送りもうまくいかなくなります。

一度吸湿したフィラメントを乾燥できるというものがあったので購入してみました。

『3Dフィラメントストレージ用のSUNLUドライバックス』という製品です。

https://www.amazon.co.jp/gp/product/B08C9RF7R7/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1

 

熱中症指数把握のためのM5Stackセンサーが温湿度測定にピッタリなのでモニターしてみました。

湿度は今少し下げたほうがいいだろうから、中に吸湿剤も入れたほうが良さそうです。効果が明確にわかるのは夏場になるだろうから、それまでに環境を整備していきます。

 

admin

Mifare cardでの参加者表示アプリ作成

これを使って、USBシリアル経由でProcessingでカード情報を受け取り、UIDと参加者の対応表からカードをタッチした参加者は出席とSQLiteのテーブルに書き込み、デスクトップ画面に表示をするようなアプリです。

M5Stickc plusとProcessingのコードは以下のリンクから、

https://github.com/chateight/rfid

注意点はserialEvent()は割り込み処理なので、この中で共通リソースを変更すると、他で使用中のコードからは意図しない(不規則な)変更に見えるので、この中では共通リソースの書き込み処理を行ってはいけない。具体的にはdbTableUpdate()の呼び出しをserialEvent()中で行っていたら、draw()からの呼び出し中にString[] membersの配列を破壊してパニックが発生しました。dbTableUpdate()の実行中に配列を書き換えなければ問題ないのでそれで対応。

10人分の登録をしたらこんな画面になります。

 

admin

M5Stickc plus

https://isehara-3lv.sakura.ne.jp/blog/2023/02/17/mifareカードリーダー/

M5StackでRFカードリーダーのアプリ作ろうと思っていましたが、カードリーダー専用ならばM5Stackである必要性はないので、より小型のM5Stickc plusを使ってみました。

サイズの差、

動かしてみたところ、

M5StackとM5Stickc plusにはいくつか差分がありますが、以下はM5Stickc plus用のコードです。

主な違いを列挙すると、

    1. ライブラリは別です、PlatformIO使うときにM5Stack用のソースから書き換えにはplatforio.iniの書き換えが必要です。
    2. 画面はローテーションしないと幅が狭すぎて使いづらいので横方向で使います。
    3. 内蔵の赤LEDをカード読み取り信号として使っています。
    4. 内蔵のブザーも同じ目的で使っています。

5. 電源オンオフは、M5」と書かれたボタンを正面に見て左側にあるボタンを6秒長押しすると電源が切れます。電源ONは同じボタンを押します

    6. リセットは対応するボタンがないので、ネット情報を参考にBボタンに割り当てています。
    7. 本体の取り付けは2mmネジです(現物合わせ)、長すぎると本体を破壊しそうですが、係りはおよそ3mmぐらいです。ネジ穴センター間の距離は実測で15.8mmでした。
    1. -> P.S. たまたま目にした図面によると、ネジ穴センター間の距離は16mmのようです。
/*
to change for the M5stickc-plus 2023/2/23
*******************************************************************************
* Copyright (c) 2022 by M5Stack
*                  Equipped with M5Core sample source code
*                          配套  M5Core 示例源代码
* Visit for more information: https://docs.m5stack.com/en/core/gray
* 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/gray
*
* Describe: RFID.
* Date: 2021/8/19
*******************************************************************************
  Please connect to Port A(22、21),Use the RFID Unit to read the Fudan card ID
and display the ID on the screen. 请连接端口A(22、21),使用RFID Unit
读取ID卡并在屏幕上显示。
*/

#include <m5stickcplus.h>

#include "MFRC522_I2C.h"

MFRC522 mfrc522(0x28);  // Create MFRC522 instance.  创建MFRC522实例

void setup() {
    M5.begin();             // Init M5Stack.  初始化M5Stack
    //M5.Power.begin();       // Init power  初始化电源模块
    M5.Lcd.setRotation(3);
    M5.lcd.setTextSize(2);  // Set the text size to 2.  设置文字大小为2
    M5.Lcd.println("MFRC522 RFC reader");
    Wire.begin();  // Wire init, adding the I2C bus.  Wire初始化, 加入i2c总线
    Serial.begin(115200);

    mfrc522.PCD_Init();  // Init MFRC522.  初始化 MFRC522
    M5.Lcd.println("Please put the card\n\nUID:");
    // LED port as output & set to "OFF"
    pinMode(10, OUTPUT);
    digitalWrite(10, HIGH);
}

void loop() {
    // reset by B button push
    if(M5.BtnB.wasPressed()){
        esp_restart();
    }
    M5.update();

    M5.Lcd.setCursor(40, 47);
    if (!mfrc522.PICC_IsNewCardPresent() ||
        !mfrc522.PICC_ReadCardSerial()) {  //如果没有读取到新的卡片
        delay(200);
        return;
    }
    M5.Lcd.fillRect(42, 47, 240, 20, BLACK);
    for (byte i = 0; i < mfrc522.uid.size;
         i++) {  // Output the stored UID data.  将存储的UID数据输出
        M5.Lcd.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
        M5.Lcd.print(mfrc522.uid.uidByte[i], HEX);
        Serial.print(mfrc522.uid.uidByte[i], HEX);
        // beep & LED on for 50ms    
        M5.Beep.tone(880);
        digitalWrite(10, LOW);
        delay(50);
        M5.Beep.mute();
        digitalWrite(10, HIGH);
    }
    M5.Lcd.println("");
    Serial.println("");
}

M5Stackファミリーも後から出てくるハードの方が進化しています。

 

admin

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