ESP-WROOM-02」タグアーカイブ

自宅用通知マシン作成(5)

前回の続き

スタックチャンがしゃべれるようになったので、次は外部からしゃべる指令を出す端末のバージョンアップ。呼び出しは予約確認のための自宅用通知マシンと兼用。

エッジ検出処理&疑似的な並列処理はこちらに大体まとめた感じ。

メインのループ(Arduinoのloop関数)は並行処理するループ(yoyakuCheckLoop,yobidashiCheckLoop)をそれぞれ呼び出しyobidashiCheckLoopの中でボタンが押されている判定をして押されていたらネットワーク越しにスタックチャンがしゃべるURLをたたいてしゃべらせる。

最初はPOSTで叩こうとしたけどPOSTの引数違うぞみたいなエラーメッセージが出たのでとりいそぎGETメソッド(5秒も調べていない)

メインのループの中でyobidashiCheckLoopを呼び出し。下に張った中のyobidashiCheckLoopでやっていることはこちらに大体まとめた感じ。で、execYobidashi(開発環境ならexecDevYobidashi)で、httpsのリクエストを投げているだけ。セキュリティ的なものはつけていないのでそのまま叩ける。

一応全体

続けてネットワーク回り

ESP8266でエッジ検出処理

やりたいこと

ボタンを押したときにそれを検出して処理をしたい。ループの中とかで検出処理をごにょごにょやるのはつらいのでボタン検知の割り込み処理を試す。LOWからHIGHになったとき(立ち上がりエッジ:RISING)や逆にHIGHからLOW(立ち下がりエッジ:FALLING)になった時を検出するやつ。

概要

void ICACHE_RAM_ATTR shopYobidashiCallback() {
  Serial.println("working");
}
void setup() {
  pinMode(INPUT_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(INPUT_PIN), shopYobidashiCallback, FALLING);
}

割り込みが効くGPIOでattachInterruptを呼び出し。GPIOがどれかはちゃんと確認していない。(データシート参照)とりあえず、12は効いて1-15が効くと思う。

呼び出し元

attachInterrupt(割り込み番号,コールバック関数名,検出エッジ)の引数。

割り込み番号はdigitalPinToInterrupt(ピン番号)で取得できる。

コールバック関数名はそのまま関数名

検出エッジはLOW/CHANGING/RISING/FALLINGが選べる模様。LOWはLOWの時常に。実際に試したのはFALLINGのみ。(参照 ESPでなくArduinoのドキュメントなので少し違うかもしれない)

コールバック

ドキュメントはこちら

コールバックには関数をRAM上に配置する指定のIRAM_ATTRまたはICACHE_RAM_ATTR(多分こっちはDupricated)がいる。

つけ忘れると下みたいなメッセージが出て再起動するはず。

ISR not in IRAM!

User exception (panic/abort/assert)
--------------- CUT HERE FOR EXCEPTION DECODER ---------------

Abort called

>>>stack>>>

ctx: cont

RAMにない=Flashにあるとそっちの書き込みとかぶってだめよという事らしい。

その他制限として

  • delay() or yield()を読んだり内部で使うものを使用できない
  • 1ms以上かかる処理をすると不安定orクラッシュ引き起こす。よって、時間かかる処理は他でして極力そのためのフラグ操作など推奨。フラグはvolatileつける(参照
  • ヒープ操作は不具合起こしえる。malloc最小限にしてreallocとfreeは使うなと。stringやvectorも要注意。詳細はドキュメント。
  • C++のnewやdeleteも

あとは、ドキュメントに書いてないけどコールバックの宣言は使う前に。

チャタリング対策

立ち上がり、立ち上がりを検出したいが変化した時に押しているのか押していないのか中間の状態を経由するのでその際に何度もコールバック関数が呼ばれてしまう。(参照

今ブレッドボードでやっているけどイメージは下。

0件だよ
0
working
working
working
working
working
working
working
working

呼ばれたときの処理で出力される”working”が一回であってほしいが何度も出力される。

参照先に色々な対策があるが、ソフトウェアで対策できれば楽。今回の使用用途としては押されたときにあるURLをたたければよい。また、即時に何度も呼ばれなくてもよい。なので、エッジ検出後一定期間その状態が続いていれば処理を実行するとすればよい。

  1. ボタン押す
  2. 最初の立ち上がり/立下りを検出後最初数100msはチャタリングしているかもしれないので読み飛ばす
  3. その後、100ms間隔で数回状態が継続していたら押されていると判断する

みたいな感じで今回は大丈夫。

ソフトウェア的にやらないなら積分回路組んだり、ラッチ回路組んだり、いいスイッチ使ったりとあるが要件厳しくなければソフトで切り抜けた方が大体良い気がする。(というかそういう要件に収めたい)

で、ループからボタン検知と状況確認の並行処理しようとしたらESP8266はスレッドなかった。並行処理するためにFreeRTOSいれる。FreeRTOSを使おうと思ったら「ESP8266などいまさら開発する暇ないんじゃ!」とのお言葉。ESP32-C3なら使えるらしい。

ループ切り替えながらやる。2つの処理があるけど、時間はざっくりでよい。

下のイメージになった。

コールバック(shopYobidashiCallback)はフラグの設定のみ

メインのループ(Arduinoのloop関数)は並行処理するループ(yoyakuCheckLoop,yobidashiCheckLoop)をそれぞれ呼び出しのみ。

yobidashiCheckInterval 経過していなかったらスキップする処理を入れつつチェックをいれる。yobidashiCheckLoopが走っている間はもう一個の処理(yoyakuCheckLoop)は順番回ってこない。

ESP8266には基本INPUT_PULLDOWNなかった。

ESP8266(ESP-WROOM-02)で内部プルダウン使おうと思ったら怒られた。

C:\Users\kitam\git\yobidashi\client\home_yobidashi\home_yobidashi.ino:109:22: error: 'INPUT_PULLDOWN' was not declared in this scope; did you mean 'INPUT_PULLDOWN_16'?
109 | pinMode(INPUT_PIN, INPUT_PULLDOWN);
| ^~~~~~~~~~~~~~
| INPUT_PULLDOWN_16

ESP8266のGPIO1から15にはPULL_DOWN抵抗はない。INPUT/OUTPUT/INPUT_PULLUPのいずれか指定。

GPIO16だけプルダウン抵抗がある。GPIO16はディープスリープ用の特別なやつ。指定する場合はINPUT_PULLDOWN_16。

https://links2004.github.io/Arduino/dc/d6f/md_esp8266_doc_reference.html

ブレッドボードの配線的にPULL_DOWNの方が指しやすかっただけなのでおとなしくPULL_UP使う。

ESP Touchがつながらない時のチェックポイントメモ

なかなかつながらなくて調べたのでメモ。未検証も含む

接続先Wifiが分離モードになっていないか

分離モード(呼び名は色々ある)になっているとつながらなそう

WPAの世代が古くないか

古い親機でWPAが昔のタイプだとセキュリティが弱いのでそのままだとつながらない

してやるかそれでもダメな場合はESP8266のライブラリをver.2.0.0まで戻す。

esp-touchでブロードキャスト、マルチキャストを変更してみる

スマホアプリ側でブロードキャスト、マルチキャストを変更してみる。理由はわからないがこれで動いた。

自宅用通知マシン作成(1)

店の予約が入ったときすぐ気づきたいとか、店から自宅に呼び出しかけたりとか細々としたとこ改善したいので自宅用通知マシンを作成開始する。

第一段階としては、店に予約が入ったときにすぐ気づけるように予約が入ったら通知としてLEDを光らせる機能をつくる。

予約が入ったらじゃらんとかアソビューから通知メールが飛んでくる。数分以内に気づけたら十分なので、自宅側の通知マシンから定期的に通知メールがあるか確認する方式にする。

ちなみにメールサーバは自前(VPS)でpostfix&dovecotなのでそっちで一部処理しておく。自宅側の端末はESP8266(ESP-WROOM-02)で作成する。秋月で現在420円するので今ならESP-WROOM-32とほとんど値段変わらないのでそっち使ってもよい。(200円台の時に買ったのが数10個余っているので消費もかねて)

全体構成

  1. VPS上メールサーバのdovecotに予約メールが入ったときに、表題をみてメールを指定フォルダに振り分け。使用ツールはseive
  2. メールサーバ上にpythonで立てた未読予約メールカウント返却APIを準備。(最前面はnginx)
  3. espからAPIを定期的に呼び出し、未読の予約メールがあったらLEDを光らせて通知する。(既読にするのはPC上のメーラーで)

メールサーバサイド

seiveの設定

seiveで予約メールが届いた際に指定フォルダ(今回はINBOX.ジム.予約)に振り分け。(seiveのインストールは手順まとめていない。)

予約メールはとりあえず件名で判断。設定ファイルは以下。件名がXXだったらYYフォルダに振り分ける。

APIの準備

以前書いていた。こちら

APIのサービス化

/etc/systemd/system/mailapi.service作成

※ ~/apiをベースパスにしていたけどgit cloneの時のパスがずれたので後ほど~/yobidashi/server/apiに変更

一応メールアカウントとパスワードは外部から読み出しに(/home/yoshitake/api/mailapi.conf)

サービスに登録

ESP8266用(BearSSL_CertStore.ino)のSSL接続用ファイル更新

ESP8266(ESP-WROOM-02)の内容。ESP-WROOM-32ではないので注意。

コマンドプロンプトにて

足りないメッセージが出た。

Traceback (most recent call last):
  File "C:\Users\kitam\Documents\ArduinoData\packages\esp8266\hardware\esp8266\3.1.2\libraries\ESP8266WiFi\examples\BearSSL_CertStore\certs-from-mozilla.py", line 28, in <module>
    raise Exception("You need the program 'ar' from xtensa-lx106-elf found here: (esp8266-arduino-core)/hardware/esp8266com/esp8266/tools/xtensa-lx106-elf/xtensa-lx106-elf/bin/ar")
Exception: You need the program 'ar' from xtensa-lx106-elf found here: (esp8266-arduino-core)/hardware/esp8266com/esp8266/tools/xtensa-lx106-elf/xtensa-lx106-elf/bin/ar

ar.exeのコピー

メッセージとパスがだいぶ違うけど下にいた。microsoftストアから入れると触れないところにあるかも。

C:\Users\kitam\AppData\Local\Arduino15\packages\esp8266\tools\xtensa-lx106-elf-gcc\3.1.0-gcc10.3-e5f9fec\xtensa-lx106-elf\bin

公式のexampleフォルダの中で作業したくないので、exampleのBearSSL_CertStore.inoをとりあえずデスクトップに保存。 デスクトップに作ったフォルダにar.exeを保存

opensslのインストール

実行したら、opensslもいると

メッセージにあるURLにwindowsのバイナリなかったのでこちらから落とす。とりあえず最新の3.2.1のexe。

ダブルクリックしてインストール。path通すと他のツール類と重なりそうなのでとりあえずパスの設定は一時的。

pythonの動作文字コード一時変更

一見動いてそうに見えたけどエラー出ている

C:\Users\kitam\Desktop\BearSSL_CertStore>python certs-from-mozilla.py > out.txt
Warning: Reading certificate from stdin since no -in or -new option is given
Warning: Reading certificate from stdin since no -in or -new option is given
Warning: Reading certificate from stdin since no -in or -new option is given
Warning: Reading certificate from stdin since no -in or -new option is given
Warning: Reading certificate from stdin since no -in or -new option is given
Warning: Reading certificate from stdin since no -in or -new option is given
Warning: Reading certificate from stdin since no -in or -new option is given
Warning: Reading certificate from stdin since no -in or -new option is given
Warning: Reading certificate from stdin since no -in or -new option is given
Warning: Reading certificate from stdin since no -in or -new option is given
Warning: Reading certificate from stdin since no -in or -new option is given
Warning: Reading certificate from stdin since no -in or -new option is given
Warning: Reading certificate from stdin since no -in or -new option is given
Traceback (most recent call last):
  File "C:\Users\kitam\Desktop\BearSSL_CertStore\certs-from-mozilla.py", line 64, in <module>
    print(names[i] + " -> " + certName)
UnicodeEncodeError: 'cp932' codec can't encode character '\xf3' in position 24: illegal multibyte sequence
 C:\Users\kitam\Desktop\BearSSL_CertStore\data のディレクトリ

2024/02/29  13:54    <DIR>          .
2024/02/29  13:54    <DIR>          ..
2024/02/29  13:54             1,463 ca_000.der
2024/02/29  13:54               837 ca_001.der
2024/02/29  13:54             1,349 ca_002.der
2024/02/29  13:54               442 ca_003.der
2024/02/29  13:54               502 ca_004.der
2024/02/29  13:54             1,011 ca_005.der
2024/02/29  13:54               784 ca_006.der
2024/02/29  13:54               617 ca_007.der
2024/02/29  13:54               959 ca_008.der
2024/02/29  13:54             1,494 ca_009.der
2024/02/29  13:54             1,476 ca_010.der
2024/02/29  13:54             1,560 ca_011.der
2024/02/29  13:54             1,523 ca_012.der
              13 個のファイル              14,017 バイト
               2 個のディレクトリ  21,649,969,152 バイトの空き領域

data/certs.arというファイルが一つできるはずだが中間ファイルの生成途中で終わっている。

pythoの文字コードを一時UTF8に。

改めて実行。

できた模様。

アップロードの手順はこちら

BrokenPipeError: [Errno 32] Broken pipe調査

ESP8266(ESP-WROOM-02)の内容。ESP-WROOM-32ではないので注意。

事象

esp8266 -> nginx(proxy) -> python(HTTPServer)という構成でサーバへespからサーバーへリクエストを投げたところ、かなりの確率で以下のログがpythonで吐かれていた。

Exception occurred during processing of request from ('127.0.0.1', 48024)
Traceback (most recent call last):
  File "/usr/lib/python3.10/socketserver.py", line 316, in _handle_request_noblock
    self.process_request(request, client_address)
  File "/usr/lib/python3.10/socketserver.py", line 347, in process_request
    self.finish_request(request, client_address)
  File "/usr/lib/python3.10/socketserver.py", line 360, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/usr/lib/python3.10/socketserver.py", line 747, in __init__
    self.handle()
  File "/usr/lib/python3.10/http/server.py", line 433, in handle
    self.handle_one_request()
  File "/usr/lib/python3.10/http/server.py", line 421, in handle_one_request
    method()
  File "/home/yoshitake/api/mail_api.py", line 26, in do_GET
    self.wfile.write(response_content)
  File "/usr/lib/python3.10/socketserver.py", line 826, in write
    self._sock.sendall(b)
BrokenPipeError: [Errno 32] Broken pipe

直接のメッセージは、pythonがリクエスト元に書きこもうとしたら相手がすでに受信待ち状態を終了しているような場合に起きるやつ。esp側では接続したもののヘッダーも取得せずにすぐに終了している。

ちなみにnginxのaccess.logには以下のようにステータス499と(pythonのHTTPServer他で使われている非標準の)クライアントが先に閉じたよというのが残っている。

150.9.94.220 - - [26/Feb/2024:11:31:56 +0900] "GET /api/reserve_count HTTP/1.0" 499 0 "-" "ESP8266"

直接の原因箇所

リクエストを送るesp側でデータの受信待ちをしなくてすぐに閉じているのが直接の原因

delay(1000);とかを入れたらとりあえず動く。

とはいえ、リクエストを送ったのちにデータ受信後の終了信号的なものをみたらまたはタイムアウトでクローズの方がよさそう。

また、同じ接続方式でself-signed.badssl.comにリクエストを送るとレスポンスがきちんと取得できているのでサーバサイドにも何か改善点がありそう。

根本解決

クライアントサイド(ESP8266)

取り急ぎavailable()では接続できたかしか確認していない。 WiFiServerSecureBearSSL.cpp(サーバの方)を間違えてみていた。

改めてWiFiClientSecureBearSSL.cpp確認。

字面で見る限りなんか入ってたら0以外が入って、なにも入っていないとサーバ側が準備中でも一番下に入って0がくるっぽい。だいぶ中の方に書いてあるので内部データを見ると色々多変そうなので最初のデータ来るかタイムアウトまでの待ち処理を入れるのがよさそう。断続的にデータがくるならちゃんと終端処理を見た方がよいかも。

本体から、書き出しメソッド呼び出し。

サーバサイド(nginx+pythonのHTTPServer)

こちらは単純にレスポンスが早く返りだすかどうかで違いが出ていたっぽい。レスポンスが早いという理由だった場合はクライアント側の処理で解決すべき話なので深く追わない。

ESP8266用(BearSSL_Validation.ino)のSSL接続用ファイル更新

ESP8266(ESP-WROOM-02)の内容。ESP-WROOM-32ではないので注意。

こちらのBearSSL_Validation.ino事前準備の手順。試している環境はWindows11。

前提

BearSSL_Validation.inoと同じディレクトリにあるcerts.hだが、サーバ側の証明書が更新されているとつながらなかったりする。(レポジトリ側の更新タイミング次第ではつながったりつながらなかったりだと思う)

certs.hを見ると下のように書いてあり

// this file is autogenerated - any modification will be overwritten
// unused symbols will not be linked in the final binary
// generated on 2023-03-20 23:02:42
// by ['../../../../tools/cert.py', '-s', 'www.example.com', '-n', 'SSL']

して更新するらしい。他のドメイン用に作る場合もおそらくcert.pyをたたけばよいと思う。

なお、cert.pyのファイル先頭コメント

#!/usr/bin/env python3

# Script to download/update certificates and public keys
# and generate compilable source files for c++/Arduino.
# released to public domain

Windowsのはまりポイント

WindowsでMS Storeからインストールするとツールは見れない場所にありそう。(MS Storeから入れたソフト類のディレクトリは見れない模様)インストーラー直ダウンロードしたIDE場合多分下みたいな配置。

C:\Users\hoge\Documents\ArduinoData\packages\esp8266\hardware\esp8266\3.1.2\libraries\ESP8266WiFi\examples\BearSSL_Validation

だと下にある

C:\Users\hoge\Documents\ArduinoData\packages\esp8266\hardware\esp8266\3.1.2\tools

MS Storeからインストールして見れない人は、レポジトリのソース一式ダウンロードしたら多分中に入っているcert.pyを使える。

環境構築

cert.pyはpython3なので必要ならインストール。

ライブラリインストール

ないと下みたいなメッセージ

python  '../../../../tools/cert.py' '-s' 'www.example.com' '-n' 'SSL'                            
Traceback (most recent call last):
  File "C:\Users\kitam\Documents\ArduinoData\packages\esp8266\hardware\esp8266\3.1.2\tools\cert.py", line 15, in <module>
    from cryptography import x509
ModuleNotFoundError: No module named 'cryptography'

もしかしたら

もいるかも。

import re
import ssl
import sys
import socket
import argparse
import datetime

あたりは元々使えると思うけど必要ならそいつらも

証明書生成実行

パスにユーザ名入っているので適宜書き換え。

出力ファイル名(test.h)も適宜書き換え

結果

PS C:\Users\kitam\Documents\ArduinoData\packages\esp8266\hardware\esp8266\3.1.2\libraries\ESP8266WiFi\examples\BearSSL_Validation> python  '../../../../tools/cert.py' '-s' 'www.example.com' '-n' 'SSL' > test.h
C:\Users\kitam\Documents\ArduinoData\packages\esp8266\hardware\esp8266\3.1.2\tools\cert.py:44: CryptographyDeprecationWarning: Properties that return a naïve datetime object have been deprecated. Please switch to not_valid_before_utc.
  print('// not valid before:', xcert.not_valid_before)
C:\Users\kitam\Documents\ArduinoData\packages\esp8266\hardware\esp8266\3.1.2\tools\cert.py:45: CryptographyDeprecationWarning: Properties that return a naïve datetime object have been deprecated. Please switch to not_valid_after_utc.
  print('// not valid after: ', xcert.not_valid_after)

なんか余計な警告出ているけどchatgpt先生によると以下らしいのでとりあえず無視

この警告は、Cryptography ライブラリが提供する一部の機能において、適切なタイムゾーン情報が欠如している(naïveな)datetimeオブジェクトを返すプロパティが非推奨であることを示しています。代わりに、not_valid_before_utcというプロパティを使用することが推奨されています。

ino修正

修正目的

ExampleのBearSSL_Validation.inoを任意フォルダ(今回はデスクトップ)に保存。

その後、cert.hの中身を先ほど出力したtest.hに変更

そのまま実行すると

C:\Users\kitam\Desktop\BearSSL_Validation\BearSSL_Validation.ino: In function 'void fetchCertAuthority()':
C:\Users\kitam\Desktop\BearSSL_Validation\BearSSL_Validation.ino:15:14: error: 'cert_DigiCert_TLS_RSA_SHA256_2020_CA1' was not declared in this scope; did you mean 'cert_DigiCert_Global_G2_TLS_RSA_SHA256_2020_CA1'?
   15 | #define CERT cert_DigiCert_TLS_RSA_SHA256_2020_CA1
      |              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Users\kitam\Desktop\BearSSL_Validation\BearSSL_Validation.ino:15:14: note: in definition of macro 'CERT'
   15 | #define CERT cert_DigiCert_TLS_RSA_SHA256_2020_CA1
      |              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

exit status 1

Compilation error: 'cert_DigiCert_TLS_RSA_SHA256_2020_CA1' was not declared in this scope; did you mean 'cert_DigiCert_Global_G2_TLS_RSA_SHA256_2020_CA1'?

出力されたtest.hにあるcert_DigiCert_Global_G2_TLS_RSA_SHA256_2020_CA1が元ヘッダーファイルにあったcert_DigiCert_TLS_RSA_SHA256_2020_CA1から変わっている。ちなみに、cert_DigiCert_Global_Root_G2とcert_DigiCert_Global_Root_CAも変わっている。

証明書のCNからcert.pyで自動命名しているのでしょうがない。

長期運用するならcert.pyをいじるか出てきたヘッダーの命名を書き換えて固定にした方が良いかも。深く考えないで取り急ぎinoの方を修正する。(15行目)

動作確認は元のページ

ESPのWiFiClientSecureでサーバから情報をもらう時サーバでContentsLength指定していないと面倒

ESP8266(ESP-WROOM-02)の内容。ESP-WROOM-32ではないのでがおおよそ同じと思う。

現象

ESP8266のWiFiClientSecureでサーバからデータを取得しようとした。本来レスポンスの本体(ペイロード本文)は”[0]“を期待していたが以下の下3行のようにレスポンスが期待していたものと違う形になっていた。

Connecting to https://hoge.co.jp/api/fuga
Connected to server
Server: nginx
Date: Wed, 21 Feb 2024 05:21:47 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: close

3
[0]
0

理由

これはTransfer-Encoding: chunkedになっていてペイロード本文

データ1のバイト数\r\nデータ1\r\n0\r\n

というフォーマットになっているため。長いもの送る場合は

データ1のバイト数\r\nデータ1\r\nデータ2のバイト数\r\nデータ2\r\n…データxのバイト数\r\nデータx\r\n…0\r\n

となる。なおデータのバイト数は16 進数表記。

対処

その1

対処としてそういうものとして読み込んでもよいけど、マルチバイトの場合データ1,2の間でぶった切られるのでつなげるのとか面倒。

その2

可能ならサーバ側で送る際にTransfer-Encoding: chunkedじゃなくしたい。

今回はサーバ側はpythonのBaseHTTPRequestHandlerを使っているがレスポンスにContent-Lengthをつけてやれば自動でTransfer-Encoding: chunkedじゃなくなった。「とりあえず、Content-Lengthついてないからchunkedでおくとっくわ」という仕様だったっぽい。(明示的にヘッダー付けた場合に消えるかは未確認)

http1.1の仕様的にはTransfer-Encoding: chunkedの時にはContent-Length不要らしい。

ESP8266のWiFiClientSecure系でcertを扱う時のメモ

今、調べながら試しているけどchatgptに存在しないメソッド言われたりと騙されるっぽいのでメモを残す。終了

ESP8266(ESP-WROOM-02)の内容。ESP-WROOM-32ではないので注意。

環境

  • Windows11
  • Arfuino IDE 2.3.1
  • esp8266 by ESP8266 Community 3.1.2(レポジトリ)

動きそうなやつ(これから確認、動いたら記事修正する)

WiFiClientSecureBearSSL#setInsecure

基本こちらのまんまで動いた。(リンク先はESP8266でなくESP32なので他を参考にするときはクラス仕様違うので注意)

最初は戻り値にバイト数とか入ってたけどサーバ側でcontents-length指定していないのが原因だった。

BearSSL_Validation.ino

Example->Esp8266Wifiの中にいるサンプル。

単独のCAをツールでヘッダーファイルに変えて読み込むやつだと思う。

includeで気になった奴

#include <StackThunk.h>というのがある。(実装のcpp

StackThunk.c - Allow use second stack for BearSSL calls


  BearSSL uses a significant amount of stack space, much larger than
  the default Arduino core stack. These routines handle swapping
  between a secondary, user-allocated stack on the heap and the real
  stack.

BearSSLはスタックすごい使うからうんぬんと。通常のスタックだと足りないから、ヒープを使ってごにょごにょしてくれるらしい。

サンプルソースだと割り当て関連の機能は使っておらず使用量確認のみに見えるけど中で読んでいるかも。一見、足りてそうに見えるときも内部でこいつが使い切ってるとかはあるかもしれないので心に留めておく。

事前準備

cert.pyを使用して証明書のデータを更新。(長くなったのでこちら

フィンガープリントだけで接続

フィンガープリント設定すればとりあえずつながる。

何もないよりましだといっている

The SHA-1 fingerprint of an X.509 certificate can be used to validate it
instead of the while certificate.  This is not nearly as secure as real
X.509 validation, but is better than nothing.  

自己署名証明書で接続

allowSelfSignedCertすればOKとのこと。

こいつは自己署名は誰でも作れるから注意を(略

It is also possible to accept *any* self-signed certificate.  This is
absolutely insecure as anyone can make a self-signed certificate.

ハードコード公開鍵で接続

公開鍵をハードコードしておいてそいつを使って(通常のSSLは認証プロセスを飛ばして)通信できる。一般公開しないサービスならこれでもよさそうな気がする。

The server certificate can be completely ignored and its public key
hardcoded in your application. This should be secure as the public key
needs to be paired with the private key of the site, which is obviously
private and not shared.  A MITM without the private key would not be
able to establish communications.

certをハードコード方式(多分一番固い)

certをハードコードして検証する方式。

有効期限チェックはするけれどもCRLはサポートしていないらしい。発行された証明書が有効期限内に無効化されていてもチェックされない。自己署名証明書をルートにしたチェーンも作れるらしい。

A specific certification authority can be passed in and used to validate
a chain of certificates from a given server.  These will be validated
using BearSSL's rules, which do NOT include certificate revocation lists.
A specific server's certificate, or your own self-signed root certificate
can also be used.  ESP8266 time needs to be valid for checks to pass as
BearSSL does verify the notValidBefore/After fields.

上のようにcertをルート証明書にしても動くはずだがレスポンスとりそびれるときがあるのに気づいた。ルートじゃなくてサーバのcertを使っているときも取りそびれ発生しているかもしれない。原因調査未。とり急ぎ調査はおいておいて他の方式の確認進める。と、思ったら方式別でも発生している。=> 解決方法

暗号化方式の選択

早さ優先したいときとか

client.setInsecure();

ノーチェック

client.setCiphersLessSecure();

以下のやつら指定(と思う)

// For apps which want to use less secure but faster ciphers, only
  static const uint16_t faster_suites_P[] PROGMEM = {
    BR_TLS_RSA_WITH_AES_256_CBC_SHA256,
    BR_TLS_RSA_WITH_AES_128_CBC_SHA256,
    BR_TLS_RSA_WITH_AES_256_CBC_SHA,
    BR_TLS_RSA_WITH_AES_128_CBC_SHA };
  • BR_TLS_RSA_WITH_AES_256_CBC_SHA256: RSA鍵交換、AES-256-CBC暗号、SHA-256ハッシュ
  • BR_TLS_RSA_WITH_AES_128_CBC_SHA256: RSA鍵交換、AES-128-CBC暗号、SHA-256ハッシュ
  • BR_TLS_RSA_WITH_AES_256_CBC_SHA: RSA鍵交換、AES-256-CBC暗号、SHA-1ハッシュ
  • BR_TLS_RSA_WITH_AES_128_CBC_SHA: RSA鍵交換、AES-128-CBC暗号、SHA-1ハッシュ
自分で指定

使えるリストはおそらくこちら。(さらに制限かかっているかもしれないが細かくは追っていない)

ちなみにサンプルソースのwww.example.comはsha1のBR_TLS_RSA_WITH_AES_256_CBC_SHAとBR_TLS_RSA_WITH_AES_128_CBC_SHAは通じたがsha256では通じなかったので上の指定だと接続エラーになった。

つなげるサーバの証明書を取得

こちらを参照

変数名サンプルソースと違うので適宜修正

BearSSL_CertStore.ino

モジラから読み込んだルートcaを複数ストアして使えるやつっぽい。容量気にしない状況ならこいつが使いやすそう。

証明書の更新

長くなったのでこちら

証明書のアップロード

初めにアップロード用ツールのインストール。(手順はこちら

IDEから[Ctrl] + [Shift] + [P]から ‘Upload Little FS to Pico/ESP8266‘ を選択し、(リセットボタン押したりするプログラム書き込みモードで)アップロード実行。

手順の方にも書いたけどシリアルモニター開いていると競合して書きこめないので注意

動作確認

サイトのドメイン入れて実行。大体の場合、htmlソースの表示がレスポンス待ち処理が足りなくてされない。 解決方法

課題

Upload LittleFSだとファイルシステムがすべて上書きされるらしい。一部差し換え的なことがやりたい。ファイルシステムルート直下のcerts.idxとcerts.arを差し替えたらよさそうだがcerts.idxがどこでできているのかまだ理解していない。CertStore::initCertStoreの中でcerts.idx作成しているのでcerts.arだけさしかえればよさそう。失敗したら接続できなくなりそうだけど、ネット越しにファイル放り込んで再起動とかもできそうかな。

ESP8266HTTPClientへの連携

以下の感じでcertを設定済みのWiFiClientSecureをHTTPClient のbeginメソッドに渡してやると動いた

以下蛇足

Chatgptに言われたけどなさそうな奴

無いっぽい奴1

とchatgptにいわれた。が、

C:\Users\kitam\Desktop\https\https.ino: In function 'void setup()':
https:32:10: error: 'class BearSSL::WiFiClientSecure' has no member named 'setCACertBundle'
   32 |   client.setCACertBundle(x509_crt_bundle);
      |          ^~~~~~~~~~~~~~~
exit status 1
'class BearSSL::WiFiClientSecure' has no member named 'setCACertBundle'

ないよと。ソース見ると確かにいない。なお、ESP32ならこれで動く模様。(参考

無いっぽい奴2

といわれたが、同じように

C:\Users\kitam\Desktop\https\https.ino: In function 'void setup()':
C:\Users\kitam\Desktop\https\https.ino:32:9: error: 'class BearSSL::WiFiClientSecure' has no member named 'addServerKey'
   32 |  client.addServerKey(x509_crt_bundle);
      |         ^~~~~~~~~~~~

exit status 1

Compilation error: 'class BearSSL::WiFiClientSecure' has no member named 'addServerKey'

addServerKeyはぱっとみESP32のライブラリにもなさそうなのでchatgptがどこから拾ってきたかよくわからない。