2019/03/30

sdcard.pyで正しく読めないSDカードを調査

ESP32でSDカードを読ませたいのだが、一部のSDカードを除き、普通にWindowsでフォーマットしたSDは正しく読めない。sdcard.pyではブロック単位でリードが可能なので(RAWデバイスとしてブロック単位で直書きができる)、SDの先頭ブロックを読んでみた。以下は正しく読めるSDの先頭ブロック(512バイト分)(マウントできるSDカード、micropython のファイルシステムで、FATファイルシステムとして認識できるSD)



fa b8 00 10 8e d0 bc 00 b0 b8 00 00 8e d8 8e c0 . . . . . . . . . . . . . . . .
fb be 00 7c bf 00 06 b9 00 02 f3 a4 ea 21 06 00 . . . | . . . . . . . . . ! . .
00 be be 07 38 04 75 0b 83 c6 10 81 fe fe 07 75 . . . . 8 . u . . . . . . . . u
f3 eb 16 b4 02 b0 01 bb 00 7c b2 80 8a 74 01 8b . . . . . . . . . | . . . t . .
4c 02 cd 13 ea 00 7c 00 00 eb fe 00 00 00 00 00 L . . . . . | . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 48 f7 0b 00 00 00 80 01 . . . . . . . . H . . . . . . .
01 00 0e f7 3e 09 3e 00 00 00 62 58 02 00 00 00 . . . . > . > . . . b X . . . .
01 0a 83 a6 cc fc a0 58 02 00 60 1f ed 00 00 00 . . . . . . . X . . ` . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa . . . . . . . . . . . . . . U .
以下は正しく読めない場合の先頭ブロック(マウント時にエラーになるSDカード、micropython のファイルシステムで、FATファイルシステムとして認識できないSD))

fa 33 c0 8e d0 bc 00 7c 8b f4 50 07 50 1f fb fc . 3 . . . . . | . . P . P . . .
bf 00 06 b9 00 01 f2 a5 ea 1d 06 00 00 be be 07 . . . . . . . . . . . . . . . .
b3 04 80 3c 80 74 0e 80 3c 00 75 1c 83 c6 10 fe . . . < . t . . < . u . . . . .
cb 75 ef cd 18 8b 14 8b 4c 02 8b ee 83 c6 10 fe . u . . . . . . L . . . . . . .
cb 74 1a 80 3c 00 74 f4 be 8b 06 ac 3c 00 74 0b . t . . < . t . . . . . < . t .
56 bb 07 00 b4 0e cd 10 5e eb f0 eb fe bf 05 00 V . . . . . . . ^ . . . . . . .
bb 00 7c b8 01 02 57 cd 13 5f 73 0c 33 c0 cd 13 . . | . . . W . . _ s . 3 . . .
4f 75 ed be a3 06 eb d3 be c2 06 bf fe 7d 81 3d O u . . . . . . . . . . . } . =
55 aa 75 c7 8b f5 ea 00 7c 00 00 49 6e 76 61 6c U . u . . . . . | . . I n v a l
69 64 20 70 61 72 74 69 74 69 6f 6e 20 74 61 62 i d   p a r t i t i o n   t a b
6c 65 00 45 72 72 6f 72 20 6c 6f 61 64 69 6e 67 l e . E r r o r   l o a d i n g
20 6f 70 65 72 61 74 69 6e 67 20 73 79 73 74 65   o p e r a t i n g   s y s t e
6d 00 4d 69 73 73 69 6e 67 20 6f 70 65 72 61 74 m . M i s s i n g   o p e r a t
69 6e 67 20 73 79 73 74 65 6d 00 00 00 00 00 00 i n g   s y s t e m . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 . . . . . . . . . . . . . . . .
04 00 06 05 e5 cb 81 00 00 00 7f df 1d 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa . . . . . . . . . . . . . . U .

sdcard.pyで、sd.readblocks(sector,buffer)の関数は正常に動作するので、SDのメディア自体は正しく認識されている。FATファイルシステムが解釈できない問題。どう違うのか、FATの仕様書と読み比べると原因が分かりそうだけど、、そこまで時間が取れない。 SD カードフォーマッターという、SDコンソーシアム?の公式フォーマットツールを使ってフォーマットしたところ、MBR領域は綺麗になったが、やはりマウント時にエラーになった。なぜだろうか。。

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 df dd 21 e5 00 00 00 02 . . . . . . . . . . ! . . . . .
04 00 06 1f ff ca 81 00 00 00 1f de 1d 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa . . . . . . . . . . . . . . U .

調べ切れていないが、これはフォーマットの違いではなく、SDカードの仕様に対応しきれていないのが原因ではないかと考えている。SDカードに対してReadBlocks/WriteBlocks要求を出す時、読み書きする対象の領域を指定するのに2つの方式があり、バイトアドレス指定か、ブロックアドレス指定か?に分かれている。
本来は、OCR中のCCS[30]を見てどっちかを決めないといけないそうなのだが(ElmChan氏解説記事より)、どうもこの部分が正しく実装されていないのでは?と思える(今はまだ推測)。sdcard.pyではブロックアドレス方式のSDカードしか扱えないのではないか。。 
以下は仮説含みでの自分なりの解釈

  • mountでエラーになってアクセスできないSDは1G程度の比較的容量の少ないやつ
  • エラー発生は、SDカードに対するwriteblocksコマンド実行時で、エラー内容はアドレス指定に対するアライメントエラー。1ブロックが512Bなので、512の倍数でアドレスを指定しないといけないのだが指定時はそうなっていない(これは確認済み)
  • エラーを起こしているSDはバイトアドレス指定方式のタイプと思われる
  •  sdcard.pyではブロックアドレス指定のみで実装されており、バイトアドレス指定方式の場合、上記の アライメントエラーが発生する。 
  • 一方、エラーが発生しないSDは4Gタイプでこれらはブロックアドレス方式のタイプと思われる(OCRの値を確認できていないので推測、だけど指定アドレスが小さい値なのでブロックアドレスを指定していると思われる)。だから、正常に読める。
  最初はSDカードのフォーマットの違い(MBRの配置あたり)かと思っていたけど、多分SD容量に起因する、アドレス指定方式の違いによってマウントできるSD,できないSDに分かれたと推測。 
参考にしたElmChan氏のSDカード解説
 http://elm-chan.org/docs/mmc/mmc.html
KingstonのSDアクセス手引き
https://docsplayer.net/41452623-Kingston-technology.html

2019/03/29

ESP32(トラ技スペシャル IoT Express)でSDを動かす

自分が使っているESPボードは、トラ技スペシャルの付録の、IoT Expressボードで、SDカードが付いているので、これを動かしてみる。sdカード用ドライバが必要なようで、これをuPyCraftを使ってESPのフラッシュに送り込む。

https://github.com/micropython/micropython/blob/master/drivers/sdcard/sdcard.py

https://github.com/pycom/pycom-micropython-sigfox/blob/master/drivers/sdcard/sdcard.py

トラ技スペシャルより、SPIは1系統で、CSはGPIO5なので以下を指定
import machine, sdcard, os
sd = sdcard.SDCard(machine.SPI(1), machine.Pin(5))
しっかり怒られた。
>>> sd = sdcard.SDCard(machine.SPI(1), machine.Pin(5))
Traceback (most recent call last):
  File "", line 1, in 
  File "sdcard.py", line 54, in __init__
  File "sdcard.py", line 82, in init_card
OSError: no SD card
本ではもう少しオプションが付いていて、その通りに打ち込んだ。
spi = machine.SPI(1,baudrate=32000000,polarity=1,phase=0,sck=machine.Pin(18),mosi=machine.Pin(23),miso=machine.Pin(19))
すると、下記のように、動いているように思える。
>>> sdcard.SDCard(spi,machine.Pin(5))
 [0;32mI (1471856) gpio: GPIO[23]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0  [0m
 [0;32mI (1471856) gpio: GPIO[19]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0  [0m
 [0;32mI (1471866) gpio: GPIO[18]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0  [0m
 [0;32mI (1472166) gpio: GPIO[23]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0  [0m
 [0;32mI (1472166) gpio: GPIO[19]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0  [0m
 [0;32mI (1472176) gpio: GPIO[18]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0  [0m

>>> 

osライブラリのコマンドを使ってマウントが必要 https://dfram.hatenablog.jp/entry/2017/11/27/215940
>>> os.listdir()
['boot.py', 'blink.py', 'sdcard.py', 'sd']
>>> os.listdir('/sd')
[]
>>> 

>>> os.mount(sd,'/sd')
Traceback (most recent call last):
  File "", line 1, in 
OSError: [Errno 19] ENODEV


フォーラム等を見てみる。。
https://forum.pycom.io/category/21/micropython
https://forum.micropython.org/


https://github.com/micropython/micropython/issues/3816#issuecomment-392512522


上記コメントによると、0セクタを読んだ時にバッファの最後は0x55aa でないといけないとある。
ためしてみると。。以下の通り、SDのセクタ読み込みのレベルでは正しく動作しているようである。
buffer=bytearray(512)
sd.readblocks(0,buffer)
>>> "{:x}".format(buffer[510])
'55'
>>> "{:x}".format(buffer[511])
'aa'

試しに、BeagleBoneの起動SDならマウントできた(一体どのようなフォーマットだったのか)。よって、読めるかどうかは、マウントさせようとしているSDのファイルフォーマットによると思われる。

ラズパイ用の起動SDだとエラーになった。
>>> uos.mount(sd,'/sd')
Traceback (most recent call last):
  File "", line 1, in 
OSError: [Errno 1] EPERM
memo

https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo

読める、読めないの違いは、SDカードのMBR(Master Boot Record)の扱いの差と思っているのですが、
そもそもたまたま読めるBeagleBone用のSDがどういう素性なのかわかっておらず。

https://jp.easeus.com/partition-manager/how-to-format-sd-card.html
https://qiita.com/mt08/items/c684d72345b591f4a27f

2019/03/24

SwitchScienceの BME280を扱う


ESP32で温湿度を計測したいので、BME280を接続して計測する

https://github.com/BoschSensortec/BME280_driver


MicroPython用にBME280用ドライバが公開されているのでそれを使わせてもらう。

https://github.com/catdog2/mpy_bme280_esp8266

以下のコードで動くようである(上記GitHUBの説明より引用)。すばらしい。
import machine
import bme280

i2c = machine.I2C(scl=machine.Pin(5), sda=machine.Pin(4))
bme = bme280.BME280(i2c=i2c)

print(bme.values)

手元のESP32はCQのトランジスタ技術スペシャルの付録基板で、空いてる適当なGPIOとしては、14,15があるのでこれをI2Cに使うことにする。よって、以下のピン指定で動くはず。

import machine
import bme280

i2c = machine.I2C(scl=machine.Pin(14), sda=machine.Pin(15))
bme = bme280.BME280(i2c=i2c)

print(bme.values)

参考:
NodeMCU(Lua)によるBME280のライブラリ例
https://nodemcu.readthedocs.io/en/dev/modules/bme280/#example

http://trac.switch-science.com/wiki/BME280
http://pages.switch-science.com/letsiot/airquality/

2019/03/23

ESP32でGrove Serial Camera(OV528)を使う


ESP32でGrove Serial Cameraを使う



Grove - Serial Camera Kit

http://wiki.seeedstudio.com/Grove-Serial_Camera_Kit/

使っているコントローラ:OV528の仕様書

GroveSerialCameraとはUARTで接続する。ただし、ESP32にシルク印刷されているRxD,TxDは、開発用に使っているので、その他のピンで使えるUARTを探す

MicroPython リファレンスより

Available Pins are from the following ranges (inclusive): 0-19, 21-23, 25-27, 32-39. 

  • Pins 1 and 3 are REPL UART TX and RX respectively
  • Pins 6, 7, 8, 11, 16, and 17 are used for connecting the embedded flash, and are not recommended for other uses
  • Pins 34-39 are input only, and also do not have internal pull-up resistors
  • The pull value of some pins can be set to Pin.PULL_HOLD to reduce power consumption during deepsleep.

Pin1,Pin3とは、GPIO1,GPIO3の事の様である(下記のRxD,TxDと一致)
UARTは3系統あるはずで、開発用のUART0以外がどうなっているのかを確認。
結果、U2TxD:GPIO17,U2RxD:GPIO16を使うことにする。

esp32-wroom-32_datasheet_en.pdf  より


No.NameTypeFunction
1GNDPGround
23V3PPower supply
3ENIModule-enable signal. Active high.
4SENSOR_VPIGPIO36, ADC1_CH0, RTC_GPIO0
5SENSOR_VNIGPIO39, ADC1_CH3, RTC_GPIO3
6IO34IGPIO34, ADC1_CH6, RTC_GPIO4
7IO35IGPIO35, ADC1_CH7, RTC_GPIO5
8IO32I/OGPIO32, XTAL_32K_P (32.768 kHz crystal oscillator input),ADC1_CH4,TOUCH9, RTC_GPIO9
9IO33I/OGPIO33, XTAL_32K_N (32.768 kHz crystal oscillator output),ADC1_CH5,TOUCH8,RTC_GPIO8 Espressif Systems 3 ESP32-WROOM-32 Datasheet V2.8
10IO25I/OGPIO25, DAC_1, ADC2_CH8, RTC_GPIO6, EMAC_RXD0
11IO26I/OGPIO26, DAC_2, ADC2_CH9, RTC_GPIO7, EMAC_RXD1
12IO27I/OGPIO27, ADC2_CH7, TOUCH7, RTC_GPIO17, EMAC_RX_DV
13IO14I/OGPIO14, ADC2_CH6, TOUCH6, RTC_GPIO16, MTMS, HSPICLK, HS2_CLK,SD_CLK, EMAC_TXD2
14IO12I/OGPIO12, ADC2_CH5, TOUCH5, RTC_GPIO15, MTDI, HSPIQ, HS2_DATA2,SD_DATA2, EMAC_TXD3
15GNDPGround
16IO13I/OGPIO13, ADC2_CH4, TOUCH4, RTC_GPIO14, MTCK, HSPID, HS2_DATA3,SD_DATA3, EMAC_RX_ER
17SHD/SD2*I/OGPIO9, SD_DATA2, SPIHD, HS1_DATA2, U1RXD
18SWP/SD3*I/OGPIO10, SD_DATA3, SPIWP, HS1_DATA3, U1TXD
19SCS/CMD*I/OGPIO11, SD_CMD, SPICS0, HS1_CMD, U1RTS
20SCK/CLK*I/OGPIO6, SD_CLK, SPICLK, HS1_CLK, U1CTS
21SDO/SD0*I/OGPIO7, SD_DATA0, SPIQ, HS1_DATA0, U2RTS
22SDI/SD1*I/OGPIO8, SD_DATA1, SPID, HS1_DATA1, U2CTS
23IO15I/OGPIO15, ADC2_CH3, TOUCH3, MTDO, HSPICS0, RTC_GPIO13, HS2_CMD,SD_CMD, EMAC_RXD3
24IO2I/OGPIO2, ADC2_CH2, TOUCH2, RTC_GPIO12, HSPIWP, HS2_DATA0,SD_DATA0
25IO0I/OGPIO0, ADC2_CH1, TOUCH1, RTC_GPIO11, CLK_OUT1, EMAC_TX_CLK
26IO4I/OGPIO4, ADC2_CH0, TOUCH0, RTC_GPIO10, HSPIHD, HS2_DATA1,SD_DATA1, EMAC_TX_ER
27IO16I/OGPIO16, HS1_DATA4, U2RXD, EMAC_CLK_OUT
28IO17I/OGPIO17, HS1_DATA5, U2TXD, EMAC_CLK_OUT_180
29IO5I/OGPIO5, VSPICS0, HS1_DATA6, EMAC_RX_CLK
30IO18I/OGPIO18, VSPICLK, HS1_DATA7
31IO19I/OGPIO19, VSPIQ, U0CTS, EMAC_TXD0
32NC--
33IO21I/OGPIO21, VSPIHD, EMAC_TX_EN
34RXD0I/OGPIO3, U0RXD, CLK_OUT2
35TXD0I/OGPIO1, U0TXD, CLK_OUT3, EMAC_RXD2
36IO22I/OGPIO22, VSPIWP, U0RTS, EMAC_TXD1
37IO23I/OGPIO23, VSPID, HS1_STROBE
38GNDPGround



memo heap in MicroPython
https://docs.micropython.org/en/latest/reference/constrained.html#the-heap


OV528の仕様書より、立ち上げ時はまずコネクション確立を行うようにと書かれていて、速度は9600bpsであった。それに従い、まずは、SYNCコマンドを送る関数を作る。

OV528(Seeed Studio Grove Serial Cameraのコントローラ)に対してシリアルで接続してコネクションを確立するまでの手順 (言語はMycroPython)
--------------------------------------------------
#

from machine import UART
from time import sleep

uart1 = UART(1, 9600)
uart1.init(9600, bits=8, parity=None, stop=1,tx=17,rx=16) 

INIT = bytes([0xAA,0x0D,0x00,0x00,0x00,0x00])

def make_connection():
   for x in range(60):
      uart1.write(INIT)
      sleep(1)
      if(uart1.any() > 0):
          packet=uart1.read(uart1.any())
          show_packet(packet)
          return True
      #print(uart1.any())
      #print(uart1.read(1))
   return False


def show_packet(packet):
  for i in range(6):
     print("[{:02x}]".format(packet[i]),end="")
  print("")
--------------------------------------------------
シリアルコンソールでの表示
>>> make_connection()
[aa][0e][0d][00][00][00]
True
多少てこずったのでオシロで確認
同期が確立した時の波形
上がカメラへの送信、下がカメラからの返却
速度は9600bps
安定動作のためには、SYNCパケット送信の際、一発目を送ってから、少し時間を空けて2発目を送ると、かなり確実にACKが返る。上記実装例では1秒のSLEEPを入れている。連続してバリバリに送ると、いつまでたってもACKが返らない。


追記
Grove Serial Cameraの製品説明ページで確認すると、電源5Vで、信号は232Cと書かれている。5Vのシリアルではなく、本気の232Cなのだろう。。多分。今回は、カメラの電源は5Vを供給しているが、カメラのシリアルはESP32のバス直結なので、3.3Vで繋いでいる。現状動いているがこれは規格外と思われる。Grove Serial Cameraの基板側の回路図がないので、基板内のレベル変換がどうなっているのか分からず。

2019/03/16

MicroPythonで使えるモジュール一式

MicroPythonで使えるモジュール一式

ざっと見てみると、、http,urllib等は無いけど、websocket,mqttなんてのがある。。

>>> help('modules')
__main__          framebuf          socket            upip
_boot             gc                ssl               upip_utarfile
_onewire          hashlib           struct            upysh
_thread           heapq             sys               urandom
_webrepl          inisetup          time              ure
apa106            io                ubinascii         urequests
array             json              ucollections      uselect
binascii          machine           ucryptolib        usocket
btree             math              uctypes           ussl
builtins          micropython       uerrno            ustruct
cmath             neopixel          uhashlib          utime
collections       network           uhashlib          utimeq
dht               ntptime           uheapq            uwebsocket
ds18x20           onewire           uio               uzlib
errno             os                ujson             webrepl
esp               random            umqtt/robust      webrepl_setup
esp32             re                umqtt/simple      websocket_helper
flashbdev         select            uos               zlib
Plus any modules on the filesystem
>>>

ESP-32でWebREPLを使う


MicroPythonを使ってESPのプログラミングを行う場合、PCとESPをシリアルで接続するのが大半と思いますが、WebREPLを走らせることでPC上のブラウザからWeb経由でESPに接続してプログラミングすることも可能です。

ESP8266のMycroPythonの記述では、(0)のwebrepl_setupを行うだけでWebREPLが使えるらしいのですが、ESP32の場合、素でブートさせるとWiFiのAccessPointモードはDisableなので接続できないようです(調べきっていないので推測)。

手元のESP32はWiFiはStationモードで動作させているので、下記手順でWebREPLの動作を確認しました。
(0)最初に使うときは、improt webrepl_setup  を実行してEnableにする
(1)WiFiをStationモード(STA_IF)で立ち上げ、WiFiルータと接続してIPを取得
(2)WebREPLを起動して ESP-32側でWebREPL待ち受けに設定
--------------------------------------
# wifi でネットワーク接続
import network
AP_NAME = "AP名を設定"
AP_PASS = "接続パスワードを設定"
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
sta_if.scan()                             # Scan for available access points
sta_if.connect(AP_NAME, AP_PASS)
sta_if.isconnected()                      # Check for successful connection

# WebREPLを起動

import webrepl
webrepl.start()
--------------------------------------

ESPが待ち受けになっている状態で、PC側(ブラウザが動いているPC)で、webrepl.htmlをブラウザに読み込ませると、JS経由でWebSocketが動いてESPと接続されます。


ESP8266用のMicroPythonの解説では起動時、WiFiはAPモードで立ち上がり、WebREPL Enableなら何も設定せずともWebREPLとしてデバッグできるようですが、手元のESP32の場合、起動時にAPモードDisableになっている。だから、、もし起動時にWebREPLの待ち受けをさせたい場合は、ESP32の起動時にWebREPLが走るように仕組んで置く必要あり。そうすればシリアルなしでもWebブラウザからプログラミングができるはず。ただ、、WebREPLの動作はシリアル接続の場合と比べてかなり遅いです。ですので、試してみたけど、WebREPLは使わない予定。

PC側のブラウザ内で動作させるスクリプト(WebREPL Client)は以下に接続することでロードされる。
http://micropython.org/webrepl/

PCのローカルにファイル一式をダウンロードして、ブラウザで起動してWebREPL Clientを動かすのも可能
https://github.com/micropython/webrepl

2019/03/09

MicroPythonでサーボ制御をやってみる

MicroPythonの仕様をざっと読んでみたので、以前からやってみたかったサーボ制御をやってみる。入力は1Kのボリュームで、それをAD変換して、得られたレベルに従って、PWM変換してデューティを変えてサーボの角度を調整する。

まずは、ボリューム値を定期的に取ってくるサンプル。
アッテネーションを11DBに設定して3.3Vでも扱えるようにする。また、取得値の幅は計算がしやすいように0-1023と設定。以下のサンプルではIO32に0V-3.3Vまでの変動する電圧がかかるのを想定している。

from machine import ADC
import time

adc = ADC(Pin(32))          # create ADC object on ADC pin
adc.atten(ADC.ATTN_11DB)    # set 11dB input attentuation (voltage range roughly 0.0v - 3.6v)
adc.width(ADC.WIDTH_10BIT)   # set 9 bit return values (returned range 0-1023)

while True:
  print(adc.read())   
  time.sleep_ms(500)

オンラインマニュアルよりコードを引用して改変
http://docs.micropython.org/en/latest/esp32/quickref.html#adc-analog-to-digital-conversion


 上記ADCのサンプルとPWMによるサーボ制御を連結させて作ったのが以下のソース
PWMの設定として、50Hz周期で、パルス幅が0.5 - 2.4の間を変動させることでサーボのアームの角度が変わる。ボリュームのレベルの比率で、0.5msec-2.4msecの間を遷移させればボリュームの回した状態に応じてアームが動くと。。
  デバッグ用にprintさせている、また、貧乏省のため、loop内では100msecWAIT(sleep)を入れている。100msecのWAITを入れるとやっぱり応答が遅くなり、微妙な制御の目的には使えない。一方、WAITを取ってしまうと、AD変換でサンプリングの誤差が出て、サーボがプルプル微振動する。だから、、人のフィードバックには忠実に、しかも、サンプリングした値をサーボに与える時点で±1~2の変動は丸めるといった変換が必要と思う。簡単なフィルタ関数かと。。


#-------------------------------------------------------------
# servo_ctrl.py

from machine import Pin, PWM
from machine import ADC
import time

LOOP_WAIT = 100             # wait 100msec

SERVO_MIN = 27
SERVO_MAX = 126
SERVO_CENTER = int((SERVO_MAX - SERVO_MIN)/2) + SERVO_MIN

servo = PWM(Pin(12), freq=50, duty=SERVO_CENTER)  # setup servo  freq=50Hz  set Center=77

vol = ADC(Pin(32))          # create ADC object on ADC pin
vol.atten(ADC.ATTN_11DB)    # set 11dB input attentuation (voltage range roughly 0.0v - 3.6v)
vol.width(ADC.WIDTH_10BIT)   # set 9 bit return values (returned range 0-1023)

while True:
  level = vol.read()
  servo_setting = int((SERVO_MAX - SERVO_MIN) * level / 1023) + SERVO_MIN
  servo.duty(servo_setting)
  print("VR:{:d} Servo:{:d}".format(level,servo_setting))
  time.sleep_ms(LOOP_WAIT)

ESP-WROOM-03にサーボとボリュームを付けて動かしているところ。デバッグにはオシロも欠かせない。













勝手にリンク。。
http://nomolk.hatenablog.com/entry/2017/04/09/222415
滅茶苦茶サーボに詳しい記事 nomolkのブログ 様

ESP32上でMicroPythonを使う

 組込み用にMicroPythonを使うので、ESP起動時に自分の書いたpythonスクリプトが走ってほしい。ファイルシステムがどうなっているのか??
os モジュールが提供されているようで、osモジュール内にlistdir関数があるのでそれを実行してみる。。すると、boot.pyなるファイルが存在する。これが多分起動時に呼ばれると思われる。

>>> import os
>>> dir(os)['__class__', '__name__', 'remove', 'VfsFat', 'chdir', 'dupterm', 'dupterm_notify', 'getcwd', 'ilistdir', 'listdir', 'mkdir', 'mount', 'rename', 'rmdir', 'stat', 'statvfs', 'umount', 'uname', 'urandom']
>>> os.listdir()
['boot.py']

boot.pyを読んでみる。catコマンドとかあるのかもしれないが、、調べるのがまどろっこしいので、、ファイルオープンしてreadしてみる。

>>> with open('boot.py') as b:
...     print(b.read())
...
...
...
# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
#import webrepl
#webrepl.start()

webreplが走っている。これはREPLがwebサーバとして提供されているということと思えるが。。シリアルコンソール以外に、web版のREPLも提供しているということか? と思ったら、、落ち着いてよく考えると、#はpythonのコメントなので、boot.pyは実質何もしていないということか。

microbitの場合、microfsというツールが提供されており、microfsツールを使うことでMicroPythonが管理するファイルシステムにプログラム等を転送できるようである。

https://microbit-micropython.readthedocs.io/ja/latest/tutorials/storage.html

で、、main.pyなるファイルを置いておけば、MicroPython起動時にmain.pyが読み込まれて実行されるらしい。micropython  main.py で逆引きしたら、、以下のようにESP8266でもmain.pyを内蔵ファイルシステムに置いておけば走りますということであった。

https://docs.micropython.org/en/latest/esp8266/tutorial/filesystem.html#start-up-scripts

だから、、開発スタイルとしては、、REPLでチビチビテストしながら部品を作って、部品は内蔵ファイルシステムにmoduleとして配置する。自作モジュールが揃ってそれらを組み立ててテストしてOKなら、最後の起動関数をmain.pyに書き込めば、電源ONで希望する動きをするはず。

まずはお決まりのLチカをやってみる。上記スクリプトでIO12のポートがH->Lと変化する。LEDを接続すれば光る。

from machine import Pin

p12 = Pin(12, Pin.OUT)

p12.on()

p12.off()

以下のスクリプトはwhileループでLHLH...を繰り返す。これをオシロで測ればループの速度も分かる。(on/offさせるのがIOレジスタを直接操作しておらずメソッドなのでオーバーヘッドは大きいが)
while True:
    p12.on()
    p12.off()
オシロで計測すると、1サイクル76usecで13.6KHz。マイコンコアの速度が240MHzだったのでスクリプトでの実行という点でオーバーヘッドが大きい。。

直接IOのレジスタを叩く手法

Accessing hardware directly

http://docs.micropython.org/en/latest/reference/speed_python.html#accessing-hardware-directly

ESP WROOM-32 ROMを焼く (MicroPython)

ESP WROOM-32の内蔵ROMに書き込むには GOIO0をGNDに落としてResetをかける。ResetはENをH->L->Hにすることでリセットがかかる。この時のメッセージは以下

rst:0x1 (POWERON_RESET),boot:0x3 (DOWNLOAD_BOOT(UART0/UART1/SDIO_REI_REO_V2))
waiting for download

以前ESP8266を触っていた時はpythonの書き込みツールを使っていた。が、、VMWare内のUbuntuで開発環境を構築していてかなり忘れたので、、ここはおとなしく、Espressif社が提供してくれている書き込みツールでMicroPythonを焼いてみる。

ESP32用のMicroPythonは以下から入手可能

 MicroPython



http://micropython.org/download#esp32

書き込み上のポイントとして、(1)FLASHを消去する(2)開始番地は0x1000

MicroPythonのサイトでもesptool.pyによる書き込みが案内されているが、、Ubuntuから書き込むのは面倒なので、、Espressifの書き込みツールで書いてみることに。以下がDownload Toolの設定。焼きたいファイルを指定して、開始番地を指定(0x1000)。開始番地はデフォルトでも設定されるかもしれません。そこまで調査しておらず。[START]で書き込む前に、上記助言に従い、[ERASE]を実行しました。



ファーム書き込みのハマリ点としては、、[ERASE]した後、すぐに[START]で書き込みはできず、ESP WROOM 32を再度ダウンロードモードに遷移させてから[START]で書き込む必要あり。これをやらなかったので、[ERASE]->[START]と続けて操作したら通信タイムアウトになった。 

書き込みが終了後、ResetするとMicroPythonが起動される。起動メッセージは以下

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
ets Jun  8 2016 00:22:57

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:5060
load:0x40078000,len:8788
ho 0 tail 12 room 4
load:0x40080400,len:6772
entry 0x40081610
I (251) cpu_start: Pro cpu up.
I (251) cpu_start: Application information:
I (251) cpu_start: Compile time:     00:48:25
I (253) cpu_start: Compile date:     Mar  9 2019
I (259) cpu_start: ESP-IDF:          v3.3-beta1-268-g5c88c5996
I (265) cpu_start: Single core mode
I (270) heap_init: Initializing. RAM available for dynamic allocation:
I (277) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (283) heap_init: At 3FFB92B0 len 00026D50 (155 KiB): DRAM
I (289) heap_init: At 3FFE0440 len 0001FBC0 (126 KiB): D/IRAM
I (295) heap_init: At 40078000 len 00008000 (32 KiB): IRAM
I (302) heap_init: At 40092834 len 0000D7CC (53 KiB): IRAM
I (308) cpu_start: Pro cpu start user code
I (27) cpu_start: Starting scheduler on PRO CPU.
OSError: [Errno 2] ENOENT
MicroPython v1.10-194-g41e7ad647 on 2019-03-09; ESP32 module with ESP32
Type "help()" for more information.
>>>
特に苦労なくmicroPythonが入り、リセットすることでmicroPythonが起動された。
Single core mode となっている。。 ESP32は以下のようにDual Coreだったはず
  Architecture: Xtensa Dual-Core 32-bit LX6
また、OSError [Error 2] ENOENT  のメッセージが気になるが。。

マニュアル類:

Quick reference for the ESP32

micropythonのGithub

ESP WROOM-32 を使ってみる

 諸般の事情により、IoT技術を自習することになった。一番使いやすいデバイス、ESP WROOM-32を使ってみることに。デバイスを理解するためになるべく周辺回路が付いていない素の状態から立ち上げたいので、SwitchScience社の「ESP-WROOM-32ピッチ変換済みモジュール」を買って、モジュールに電源とか最低限のPIN PullUp/Downを設定して走らせてみた。 WROOM-32 起動時のブートメッセージは以下

**************************************
*       hello espressif ESP32!       *
*        2nd boot is running!        *
*            version (V0.1)          *
**************************************
compile time 18:16:58

  SPI Speed      : 40MHz
  SPI Mode       : DIO
  SPI Flash Size : 4MB
Partition Table:
## Label            Usage          Type ST Offset   Length
 0 factory          factory app      00 00 00010000 00100000
 1 rfdata           RF data          01 01 00110000 00040000
 2 wifidata         WiFi data        01 02 00150000 00040000
End of partition table
Loading app partition at offset 00010000
section 0: paddr=0x00000020 vaddr=0x00000000 size=0x0ffe8 ( 65512)
section 1: paddr=0x00010010 vaddr=0x3f400010 size=0x05b64 ( 23396) map
section 2: paddr=0x00015b7c vaddr=0x3ffba720 size=0x01378 (  4984) load
section 3: paddr=0x00016efc vaddr=0x40080000 size=0x00400 (  1024) load
section 4: paddr=0x00017304 vaddr=0x40080400 size=0x126ac ( 75436) load
section 5: paddr=0x000299b8 vaddr=0x00000000 size=0x06658 ( 26200)
section 6: paddr=0x00030018 vaddr=0x400d0018 size=0x325b4 (206260) map
start: 0x400807ac

Initializing heap allocator:
Region 19: 3FFBBA98 len 00024568 tag 0
Region 25: 3FFE8000 len 00018000 tag 1
Pro cpu up.
Pro cpu start user code
nvs_flash_init
frc2_timer_task_hdl:3ffbc564, prio:22, stack:2048
tcpip_task_hdlxxx : 3ffbeca8, prio:20,stack:2048
phy_version: 80, Aug 26 2016, 13:04:06, 0
pp_task_hdl : 3ffc34f0, prio:23, stack:8192

:>enter uart init
uart init wait fifo succeed
exit uart init

IDF version : master(db93bceb)
WIFI LIB version : master(934d079b)
ssc version : master(r283 4d376412)

!!!ready!!!
mode : softAP(26:0a:c4:07:55:e4)
dhcp server start:(ip: 192.168.4.1, mask: 255.255.255.0, gw: 192.168.4.1)
+WIFI:AP_START


 PIN設定上の注意点:3番ピンのEN(Enable)について、、最初チップ内部でプルアップされていると思って、プルアップにせず放置していたらまともに起動しなかった。ENは10K等の抵抗を介して3.3Vに引き上げるべし。。当たり前か。。

内蔵ROMの工場出荷状態では、softAP(AccessPoint)として動作するようで、ATコマンド等で制御可能(のはず)。 RxD,TxDでシリアル接続していて、115200baudで通信中

適当にATコマンドを叩いても:>what?と聞き返されコマンドは受け付けられず。。? を打ち込むと以下のメッセージが。

Please refer to document ssc_commands.xlsx for detail

他人の記事を見て、、例えば以下の様にコマンドを打ち込んで使うらしい。

:>ap -S -s esp3    .....  自分の入力
+SAP:OK                 .....   ESPからの応答(上記入力には正しく応答)

microPythonで制御したいので、初期ファームの確認はこれぐらいで終わりにする。