RESTでMySQLを操作する

MicroPythonでMySQLを操作する


こちらにesp8266とesp32の MicroPythonが公開されています。
MycroPythonは元々こちらの PyBoard用に開発された開発環境です。
urequestsライブラリを使えばHTTPのGetだけでなく、Post、Put、Deleteが動きます。
そこで、ESP8266にMicroPythonをインストールし、MicroPythonでMySQLを操作してみました。

MicroPythonのインストール手順はこ ちらに日本語のドキュメントが公開されています。
最初にesptool.pyをインストールする必要が有りますが、esp-idfとESP8266_RTOS_SDKにはesptool.pyが 含まれています。
$ $HOME/esp-idf/components/esptool_py/esptool/esptool.py -h
usage: esptool [-h] [--chip {auto,esp8266,esp32}] [--port PORT] [--baud BAUD]
               [--before {default_reset,no_reset,no_reset_no_sync}]
               [--after {hard_reset,soft_reset,no_reset}] [--no-stub]
               [--trace] [--override-vddsdio [{1.8V,1.9V,OFF}]]
               {load_ram,dump_mem,read_mem,write_mem,write_flash,run,image_info,make_image,elf2image,read_mac,chip_id,flash_id,read_flash_status,write_flash_status,read_flash,verify_flash,erase_flash,erase_region,version}
               ...

esptool.py v2.7-dev - ESP8266 ROM Bootloader Utility


$ $HOME/ESP8266_RTOS_SDK/components/esptool_py/esptool/esptool.py -h
usage: esptool [-h] [--chip {auto,esp8266,esp32}] [--port PORT] [--baud BAUD]
               [--before {default_reset,no_reset,no_reset_no_sync}]
               [--after {hard_reset,soft_reset,no_reset}] [--no-stub]
               [--trace] [--override-vddsdio [{1.8V,1.9V,OFF}]]
               {load_ram,dump_mem,read_mem,write_mem,write_flash,run,image_info,make_image,elf2image,read_mac,chip_id,flash_id,read_flash_status,write_flash_status,read_flash,verify_flash,erase_flash,erase_region,version}
               ...

esptool.py v2.4.0 - ESP8266 ROM Bootloader Utility

今回はesp-idfに含まれているesptool.pyを使って、esp8266-20191220-v1.12.binを書き込みました。
$ wget http://micropython.org/resources/firmware/esp8266-20191220-v1.12.bin

$ ls /dev/ttyUSB*
/dev/ttyUSB0

$ $HOME/esp-idf/components/esptool_py/esptool/esptool.py --port /dev/ttyUSB0 erase_flash

$ $HOME/esp-idf/components/esptool_py/esptool/esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=detect 0 esp8266-20191220-v1.12.bin

$ picocom --baud 115200 /dev/ttyUSB0



MicroPythonを使うにはターミナルソフトが必要になります。
WindowsではTeraTermが有名ですが、Linuxではpicocomやscreenなどが有ります。
今回はpicocomを使いました。


picocomの操作は少し変わっていて、Ctrl+aとそれに続くコントールコードで色々な操作を行うことができます。
Ctrl+a Ctrl+hでHelpを表示します。


picocomの終了はCtrl+a Ctrl+q、またはCtrl+a Ctrl+xです。



MicroPythonはデフォルトでREPL(対話型実行環境)で動きますが、Ctrl+eでpaste modeに入ります。
paste modeに入ると、プロンプトが>>>から===に変わります。


この状態でクリップボードからソースをPasteすることができます。


paste modeの終了はCtrl+dです。
paste modeを終了するとプロンプトが>>>に戻ります。


MicroPythonは一度読み込んだソースをメモリ上に覚えています。
そこで、Wifiに接続する以下の関数をPasteします。
引数のデフォルト値は環境に合わせて変更してください。
def do_connect(ssid='ssid_of_wifi', password='password_of_wifi'):
    import network
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        print('connecting to network...')
        wlan.connect(ssid, password)
        while not wlan.isconnected():
            pass
    print('network config:', wlan.ifconfig())

この関数を呼び出すコードを入力します。


do_connect関数を呼び出すだけでWifiに接続します。
esp8266への電源が供給されている間は読み込んだコードは有効ですが、
電源供給をやめると読み込んだコードは無効になります。




## List ##
テーブル内の全件を表示します。
WEBサーバーのIPアドレス、ポート番号は環境に合わせて変更してください。
import urequests
do_connect()
url = "http://192.168.10.43:8080/api.php/records/posts/"
r = urequests.get(url)
#print(r)
#print("content={}".format(r.content))
print("text={}".format(r.text))
print("json={}".format(r.json()))

# It's mandatory to close response objects as soon as you finished
# working with them. On MicroPython platforms without full-fledged
# OS, not doing so may lead to resource leaks and malfunction.
r.close()

結果は以下の様になります。
network config: ('192.168.10.165', '255.255.255.0', '192.168.10.1', '192.168.10.1')
text={"records":[{"id":1,"user_id":1,"category_id":1,"content":"blog started"},{"id":2,"user_id":1,"category_id":2,"content":"It works!"}]}
json={'records': [{'id': 1, 'content': 'blog started', 'user_id': 1, 'category_id': 1}, {'id': 2, 'content': 'It works!', 'user_id': 1, 'category_id': 2}]}



## Read ##
テーブル内の特定の1件を表示します。
import urequests
do_connect()
url = "http://192.168.10.43:8080/api.php/records/posts/2"
r = urequests.get(url)
#print(r)
#print("content={}".format(r.content))
print("text={}".format(r.text))
print("json={}".format(r.json()))

# It's mandatory to close response objects as soon as you finished
# working with them. On MicroPython platforms without full-fledged
# OS, not doing so may lead to resource leaks and malfunction.
r.close()

結果は以下の様になります。
network config: ('192.168.10.165', '255.255.255.0', '192.168.10.1', '192.168.10.1')
text={"id":2,"user_id":1,"category_id":2,"content":"It works!"}
json={'id': 2, 'content': 'It works!', 'user_id': 1, 'category_id': 2}




## Create ##
データを追加します。
import urequests
do_connect()
url = "http://192.168.10.43:8080/api.php/records/posts/"
r = urequests.post(url, json=dict(user_id='1',category_id="3",content="Hello World"))
#print(r)
#print("content={}".format(r.content))
print("text={}".format(r.text))
print("json={}".format(r.json()))

# It's mandatory to close response objects as soon as you finished
# working with them. On MicroPython platforms without full-fledged
# OS, not doing so may lead to resource leaks and malfunction.
r.close()

新しいレコードが追加されます。
$ curl http://192.168.10.43:8080/api.php/records/posts/3 | python -mjson.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    60  100    60    0     0   8571      0 --:--:-- --:--:-- --:--:--  8571
{
    "category_id": 3,
    "content": "Hello World",
    "id": 3,
    "user_id": 1
}



## Update ##
データを更新します。
import urequests
do_connect()
url = "http://192.168.10.43:8080/api.php/records/posts/3"
r = urequests.put(url, json=dict(content="Hello Japan"))
#print(r)
#print("content={}".format(r.content))
print("text={}".format(r.text))
print("json={}".format(r.json()))

# It's mandatory to close response objects as soon as you finished
# working with them. On MicroPython platforms without full-fledged
# OS, not doing so may lead to resource leaks and malfunction.
r.close()

$ curl http://192.168.10.43:8080/api.php/records/posts/3 | python -mjson.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    60  100    60    0     0  15000      0 --:--:-- --:--:-- --:--:-- 15000
{
    "category_id": 3,
    "content": "Hello Japan",
    "id": 3,
    "user_id": 1
}



## Delete ##
データを削除します。
import urequests
do_connect()
r = urequests.delete("http://192.168.10.43:8080/api.php/records/posts/3")
#print(r)
#print("content={}".format(r.content))
print("text={}".format(r.text))
print("json={}".format(r.json()))

# It's mandatory to close response objects as soon as you finished
# working with them. On MicroPython platforms without full-fledged
# OS, not doing so may lead to resource leaks and malfunction.
r.close()

$ curl http://192.168.10.43:8080/api.php/records/posts/3 | python -mjson.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    46  100    46    0     0  11500      0 --:--:-- --:--:-- --:--:-- 11500
{
    "code": 1003,
    "message": "Record '3' not found"
}



MicroPythonはesp32でも動きます。
ハードウェアの違いはMicroPythonが吸収してくれるので、esp8266と同じコードがesp32でも動きます。
esp-idfに含まれているesptool.pyを使って、esp32-idf3-20191220-v1.12.binを書き込みました。
$ wget http://micropython.org/resources/firmware/esp32-idf3-20191220-v1.12.bin

$ $HOME/esp-idf/components/esptool_py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 erase_flash

$ $HOME/esp-idf/components/esptool_py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x1000 esp32-idf3-20191220-v1.12.bin

$ picocom --baud 115200 /dev/ttyUSB0

do_connect()をpasteし、続いてListのコードをPasteします。
Ctrl+dでpaste modeを終了すると、ずらずらとbootメッセージが表示され、Wifiに接続した後、MySQLのデータを Listします。
I (218850) modsocket: Initializing
I (218870) wifi: wifi driver task: 3ffe2f50, prio:23, stack:3584, core=0
I (223209) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (223219) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (223279) wifi: wifi firmware version: aeed694
I (223279) wifi: config NVS flash: enabled
I (223279) wifi: config nano formating: disabled
I (223279) wifi: Init dynamic tx buffer num: 32
I (223279) wifi: Init data frame dynamic rx buffer num: 32
I (223289) wifi: Init management frame dynamic rx buffer num: 32
I (223299) wifi: Init management short buffer num: 32
I (223299) wifi: Init static rx buffer size: 1600
I (223309) wifi: Init static rx buffer num: 10
I (223309) wifi: Init dynamic rx buffer num: 32
W (223309) phy_init: failed to load RF calibration data (0x1102), falling back to full calibration
I (223459) phy: phy_version: 4102, 2fa7a43, Jul 15 2019, 13:06:06, 0, 2
I (223479) wifi: mode : sta (24:0a:c4:c5:46:fc)
I (223479) wifi: STA_START
connecting to network...
I (223599) wifi: new:<1,0>, old:<1,0>, ap:<255,255>, sta:<1,0>, prof:1
I (224449) wifi: state: init -> auth (b0)
I (224449) wifi: state: auth -> assoc (0)
I (224459) wifi: state: assoc -> run (10)
I (224479) wifi: connected with aterm-22fa1c-g, channel 1, HT20, bssid = 10:66:82:28:a1:cf
I (224479) wifi: pm start, type: 1

I (224479) network: CONNECTED
I (226689) event: sta ip: 192.168.10.198, mask: 255.255.255.0, gw: 192.168.10.1
I (226689) network: GOT_IP
network config: ('192.168.10.198', '255.255.255.0', '192.168.10.1', '192.168.10.1')
text={"records":[{"id":1,"user_id":1,"category_id":1,"content":"blog started"},{"id":2,"user_id":1,"category_id":2,"content":"It works!"}]}
json={'records': [{'id': 1, 'content': 'blog started', 'user_id': 1, 'category_id': 1}, {'id': 2, 'content': 'It works!', 'user_id': 1, 'category_id': 2}]}

続く...