HSP3からM5GOのLチカ

# 概要

HSP3からの指示でM5GOをLチカしてみました。

事前に Wi-Fiルータに HSP3 が動作する Windows PC と M5GO がぶらさがっている環境ができていることを前提としています。
M5GOの環境準備に関しては、バージョンが違いますが、とっかかりとしては以下のページが参考になりました。ありがとうございます。
[M5stack(M5GO)とM5UI.Flowであそんでみた](https://qiita.com/mashed-p/items/1ba7a3dc54e3b766c2a8)

公式ページはこっち。
[M5stack UIFlow 公式ページ](https://m5stack.github.io/UIFlow_doc/ja/)

2019/9/21 時点で
 **HSP ver.3.6β1**
 **M5GO ファームウェアバージョン V1.3.2**
を使用しました。

注: ユーザー認証などしていませんのでセキュリティなどはご自身で留意してください。
またHSP3側のMQTT実装もLチカに必要な機能のみの実装となっています。

## HSP3側

下記コードを実行します。

```hs
#include "hspsock.as"

#enum T_CONNECT = 1
#enum T_CONNACK
#enum T_PUBLISH
#enum T_PUBACK
#enum T_PUBREC
#enum T_PUBREL
#enum T_PUBCOMP
#enum T_SUBSCRIBE
#enum T_SUBACK
#enum T_UNSUBSCRIBE
#enum T_UNSUBACK
#enum T_PINGREQ
#enum T_PINGRESP
#enum T_DISCONNECT

	gosub *makebut

	sdim head,64
	sdim wbuf,256
	pid = 1
	pingnum = 0
	
*main
	port = 19499
	ipget
	mes "このPCのアドレスとポート " + refstr + ":" + port
	sockmake 0, port
	if stat {
		dialog "Socket error"
		goto *errbye
	}
	a = 0
	repeat
		sockwait 0
		title "TCP接続待ち " + a
		if stat > 1 {
			dialog "Socket error"
			goto *errbye
		}
		if stat == 0 {
			break
		}
		a ++
		wait 10
	loop

	mes "クライアントが接続しました"

*main01
// ヘッダの受信
	offset = 0
	reqb = 2

*main03
	sockcheck
	result = stat
	if result >= 2 {
		mes "error, " + result
		goto *bye
	}

	c ++
	title "head sockgetb calling - " + c
	wait 1

	if result == 1 {
		goto *main03
	}

	sockgetb head,offset,reqb
	result = stat

	offset += result
	reqb -= result

	if offset < 2 {
		goto *main03
	}

*main05
	title "sockgetb done - " + result
	wait 1

	h0 = peek(head, 0)
	h1 = peek(head, 1)
	retain = h0 & 1
	qos = (h0 >> 1) & 3
	dupf = h0 & 0x08
	mt = h0 >> 4

	len = h1
	if len > 127 {
		mes "可変長には非対応"
		wait 1
		repeat
			reqb = 1
			sockgetb head,offset,reqb

			result = stat
			cur = peek(head, offset)

			offset += result
			reqb -= result

			if cur <= 127 {
				break
			}
			if offset >= 5 {
				break
			}
		loop
		len = 0
		repeat offset - 1
			len = (len << 7) | (peek(head, offset - 1 - cnt) & 0x7f)
		loop
		mes "len, " + len
	}

	if mt != T_PINGREQ {
		mes "" + retain + ", q:" + qos + ", " + dupf + ", " + mt + ", " + len
	}

	if len == 0 { // 0バイトならskip
		goto *main11
	}

	sdim buf,len
	offset = 0
	reqb = len

*main09
	sockgetb buf, offset, reqb
	rb = stat
	offset += rb
	mes "read, " + rb + " / " + reqb + " / " + len
	wait 1

	if offset < len {
		reqb = len - offset
		goto *main09
	}

	s = ""
	repeat len
		s += strf(",%02x", peek(buf, cnt))
	loop
	mes s

*main11

	if mt == T_CONNECT {
		mes " C->S 接続"

		sdim sbuf,64
		poke sbuf,0, (T_CONNACK << 4)
		poke sbuf,1,2
		
		poke sbuf,2,0x00
		poke sbuf,3,0x00 // 接続許可 0x00

		sockputb sbuf, 0, 4

		mes " S->C 接続ACK, " + stat

		goto *main01
	}
	if mt == T_SUBSCRIBE {
		mes " C->S 購読"

		mid = (peek(buf, 0) << 8) | peek(buf, 1)

		sdim sbuf,64
		poke sbuf, 0, (T_SUBACK << 4)
		poke sbuf, 1, 3
		poke sbuf, 2, (mid >> 8) & 0xff
		poke sbuf, 3, (mid & 0xff)

		repeat 1
			poke sbuf, 4 + cnt, 0x00 // Suc QoS 0
		loop

		sockputb sbuf, 0, 5
		mes " S->C 購読ACK, " + stat

		goto *main01
	}
	if mt == T_PINGREQ {
		pingnum ++
		x = ginfo(22)
		y = ginfo(23)
		color 255,255,255
		boxf 320,240, 640,480
		color 0,0,0
		pos 320,240
		mes " C->S PING 要求, " + pingnum + "回目"

		sdim sbuf,64
		poke sbuf, 0, (T_PINGRESP << 4)
		poke sbuf, 1, 0
		sockputb sbuf, 0, 2
		mes " S->C PING 応答, " + stat

		pos x,y
		
		goto *main01
	}
	goto *main01

*bye
	sockclose
	goto *main

*errbye
	sockclose
	mes "終了"
	stop

*makebut
	pos ginfo(26) - 64, 0
	button gosub "open", *pubopen
	button gosub "close", *pubclose
	pos 0,0
	return

*pubopen
	s = "open"
	goto *pub01

*pubclose
	s = "close"
	goto *pub01

*pub01
	t = "RGB"
	tlen = strlen(t)
	slen = strlen(s)

	poke wbuf, 0, (T_PUBLISH << 4) | (0x01 << 1)
//	poke wbuf, 1, 6

	woffset = 2

	poke wbuf, woffset, 0
	poke wbuf, woffset + 1, tlen
	woffset += 2

	memcpy wbuf, t, tlen, woffset, 0
	woffset += tlen

	poke wbuf, woffset, (pid >> 8) & 0xff
	poke wbuf, woffset+1, pid & 0xff

	woffset += 2

	pid ++
	if pid > 0xffff {
		pid = 1
	}

// アプリ固有
	memcpy wbuf,s, slen, woffset,0
	woffset += slen

	poke wbuf, 1, woffset - 2

	sockputb wbuf, 0, woffset
	mes "S->C 配信, " + stat
	return
```

|HSP3 実行結果1|
|:-:|
|![sub3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213145/afd65f0d-04a8-22e0-51e6-4fb382fa8330.png)|
|接続待ちを開始|

自分のIPアドレスとポート番号はM5GOで使用します。

## M5GO側

M5GO側は M5Flow で以下のようなブロックを組みます。
MQTT 関連は高度なブロックの一番下にあります。

|M5Flow uiFLOW ブロック構成|
|:-:|
|![sub22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213145/b6693505-e0f3-d5f7-2d27-b7de1ebcd7bb.png)|
|server,portにはHSP3側のIPアドレスを指定する|

M5GO で mqtt を使用するには RUN ではなく Download ダウンロード で転送する必要があります。
再ダウンロードする場合には再起動して SETUP から "Link WiFi: 使用するWiFi" を select して WiFi に接続して flow.m5stack.com の右の丸が緑色になった状態から ダウンロード しました。

M5GO に Download ダウンロード を指定すると
再起動がかかります。
私の環境ではだいたい1回めの再起動直後の Connect WiFi: に失敗するのでM5GO本体の reconnect で再接続します。

M5GO がうまく起動するとHSP3ソフト側に接続します。

|HSP3 実行結果2|
|:-:|
|![sub4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213145/165fe3e2-7f8e-d233-802c-0b4570442d99.png)|
|接続が維持されているとPING要求を受信する回数が増えていきます|

open をクリックするとM5GOのLEDが赤く光ります。
close をクリックするとM5GOのLEDが消えます。

|open|close|
|:-:|:-:|
|![sub4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213145/79378757-4f87-faa6-c10d-168b619653aa.jpeg)|![sub5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213145/17483dd3-9509-2994-32c9-a00800ddee97.jpeg)|
|実際はもっと赤いです|消灯時|

ハンダづけ無しでも Lチカ できる M5GO 面白いですね。

## 参考

ファームバージョン V1.3.2 の MQTT は version 3.1.1 のようです。

* [MQTT Version 3.1.1 Plus Errata 01](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html)