Open5
AIスタックチャンをPlatformIOで動かすまで
まとめ
- 不具合が発生したらissueとpull & requestを見る
- さらっと見るのではなくしっかり見る
- 有名なライブラリなど使っているんだったら特に。更新から時間経ってるのに自分が一番先に不具合踏む、なんてよっぽどないから
ことの経緯
実家の母にプレゼントするためにAIスタックチャンを触り始めた
全くの初心者で何をさわれば良いかもよくわからなかったので、M5coreS3で次のGitHubリポジトリから使うことにした
過程
- Forkしてghq get
- PlatformIOのVScode拡張をインストール
- CLIの方が良いかとも思ったが、拡張機能でも十分そうだったのでこれを採用
- C++だったが読めたので開発続行
- PlatformIOで書き込んだらスタックチャンの顔が出てきて楽勝ムードだったが、ここからハマってしまう
wifi繋がらない問題
- スマホのテザリング(実家にwifiないので・・・)で進めていたがうまく繋がらない
- 結論、実家の電波が弱い。足立慶友整形外科でテザリングしている時は特に問題がなかった
- だが、これに結構時間がかかってしまった
- 結局、繋がるときは時間かかるけど繋がるからスルーすることにした
会話できない問題
- CoreS3の上のほうを触ると
- 初めてのC++だし、そもそもこのAIスタックチャンは何ができるのかよくわからない(readmeにはスマホアプリで話せるとか書いてあるから、M5のスピーカーで会話できるのかがわからなかった)。
- 仕方ないのでGPTと一緒にコードを読み解いていくと、以下のフローが実装されていることがわかった
- マイクで音声を取得
- whisperもしくはGCPのspeech to textに投げる
- 返ってきたテキストをOpenAI APIに投げる
- voicevoxAPIで音声データにする
- スピーカーで発話
- whisperAPIのところでコケているっぽかったのでJSで検証を始める(これマジで失敗。すごい時間かかった。curlでよかった。とはいえfetchAPIに関する知見が溜まったので結果オーライ)
- OPENAI_API_KEYとwhisperAPI自体には問題がないことが判明
- GPTにエラーを聞くとSSL認証エラーと言われる
[ 28279][E][ssl_client.cpp:37] _handle_error(): [start_ssl_client():273]: (-9984) X509 - Certificate verification failed, e.g. CRL, CA or signature check failed
[ 28282][E][WiFiClientSecure.cpp:144] connect(): start_ssl_client: -9984
こんな感じのエラーで色々知ってる範囲で試してみたがうまくいかない(ESP32からAPIを叩くことの知見がなかったので)
- OpenAIのAPIの証明書っぽい気がしてきたので、Google speech to text APIを使うように切り替える
- Google Cloudはめんどくさいイメージしかないが、APIキー発行済みで、APIを有効化するだけで問題なかったのですぐに検証できた
- 音声認識でテキストが返ってくるところまで通る
- その後のGPTにchatを投げるところでコケる
- やっぱり証明書っぽい
- この前にも独り言モードでvoicevoxAPIが成功しているので、どうやらESP32でAPIを叩くときはこのルート証明書というものが必要になるっぽいことを理解し始めた
解決
openssl s_client -showcerts -connect api.openai.com:443
ターミナルで実行して次の結果が得られる
Connecting to 104.18.6.192
CONNECTED(00000006)
depth=2 C=US, O=Google Trust Services LLC, CN=GTS Root R4
verify return:1
depth=1 C=US, O=Google Trust Services, CN=WE1
verify return:1
depth=0 CN=api.openai.com
verify return:1
---
Certificate chain
0 s:CN=api.openai.com
i:C=US, O=Google Trust Services, CN=WE1
a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA256
v:NotBefore: Jul 10 21:02:14 2024 GMT; NotAfter: Oct 8 21:02:13 2024 GMT
-----BEGIN CERTIFICATE-----
MIIDnTCCA0OgAwIBAgIRAM3tBUOTPNfdE9pyQ6U4BeIwCgYIKoZIzj0EAwIwOzEL
MAkGA1UEBhMCVVMxHjAcBgNVBAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczEMMAoG
A1UEAxMDV0UxMB4XDTI0MDcxMDIxMDIxNFoXDTI0MTAwODIxMDIxM1owGTEXMBUG
A1UEAxMOYXBpLm9wZW5haS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQZ
eQm9dolQtuBJR0fUtzlW8+Qybs0/Npfuh/awaQXMoL/x3d7hm4ASbmLgVeOfKQv1
wQkqx3Q2WAIglVe/wVxAo4ICSDCCAkQwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQM
MAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFI2Yb3xvA6Lik1UI
QjKWvPvfr5sfMB8GA1UdIwQYMBaAFJB3kjVnxP+ozKnme9mAeXvMk/k4MF4GCCsG
AQUFBwEBBFIwUDAnBggrBgEFBQcwAYYbaHR0cDovL28ucGtpLmdvb2cvcy93ZTEv
emUwMCUGCCsGAQUFBzAChhlodHRwOi8vaS5wa2kuZ29vZy93ZTEuY3J0MBkGA1Ud
EQQSMBCCDmFwaS5vcGVuYWkuY29tMBMGA1UdIAQMMAowCAYGZ4EMAQIBMDYGA1Ud
HwQvMC0wK6ApoCeGJWh0dHA6Ly9jLnBraS5nb29nL3dlMS9qa0lqRGtEcUZCYy5j
cmwwggEFBgorBgEEAdZ5AgQCBIH2BIHzAPEAdwBIsONr2qZHNA/lagL6nTDrHFIB
y1bdLIHZu7+rOdiEcwAAAZCeqyd/AAAEAwBIMEYCIQCb0OAL4AYf4CfJa+Hd+FSE
ZezCeQrgjxkCaQfUeOTwbwIhAJaJ8PAAklw2DsRPud5H6XCcqpm51ZJG/aUsNRpm
gNvEAHYA7s3QZNXbGs7FXLedtM0TojKHRny87N7DUUhZRnEftZsAAAGQnqsnYQAA
BAMARzBFAiEA021vIz7X2ddT4+ueoXRWPw37QNJ7r+Py1J339xLeVtUCIAgI1dNW
6ziZGGh1I1HpFpTGEz86iu1Pp8Sq1aRIXBamMAoGCCqGSM49BAMCA0gAMEUCIQCt
znrSkFU/0vkW0ZqPf48HYCEttRnxphZjteM/xqv9hQIgDGjQCQFDTqnKznLVbGnv
QNzu4rO+/P+l4lFAL7ZHWZM=
-----END CERTIFICATE-----
1 s:C=US, O=Google Trust Services, CN=WE1
i:C=US, O=Google Trust Services LLC, CN=GTS Root R4
a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA384
v:NotBefore: Dec 13 09:00:00 2023 GMT; NotAfter: Feb 20 14:00:00 2029 GMT
-----BEGIN CERTIFICATE-----
MIICnzCCAiWgAwIBAgIQf/MZd5csIkp2FV0TttaF4zAKBggqhkjOPQQDAzBHMQsw
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMjMxMjEzMDkwMDAwWhcNMjkwMjIwMTQw
MDAwWjA7MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVR29vZ2xlIFRydXN0IFNlcnZp
Y2VzMQwwCgYDVQQDEwNXRTEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARvzTr+
Z1dHTCEDhUDCR127WEcPQMFcF4XGGTfn1XzthkubgdnXGhOlCgP4mMTG6J7/EFmP
LCaY9eYmJbsPAvpWo4H+MIH7MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggr
BgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU
kHeSNWfE/6jMqeZ72YB5e8yT+TgwHwYDVR0jBBgwFoAUgEzW63T/STaj1dj8tT7F
avCUHYwwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzAChhhodHRwOi8vaS5wa2ku
Z29vZy9yNC5jcnQwKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL2MucGtpLmdvb2cv
ci9yNC5jcmwwEwYDVR0gBAwwCjAIBgZngQwBAgEwCgYIKoZIzj0EAwMDaAAwZQIx
AOcCq1HW90OVznX+0RGU1cxAQXomvtgM8zItPZCuFQ8jSBJSjz5keROv9aYsAm5V
sQIwJonMaAFi54mrfhfoFNZEfuNMSQ6/bIBiNLiyoX46FohQvKeIoJ99cx7sUkFN
7uJW
-----END CERTIFICATE-----
2 s:C=US, O=Google Trust Services LLC, CN=GTS Root R4
i:C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
a:PKEY: id-ecPublicKey, 384 (bit); sigalg: RSA-SHA256
v:NotBefore: Nov 15 03:43:21 2023 GMT; NotAfter: Jan 28 00:00:42 2028 GMT
-----BEGIN CERTIFICATE-----
MIIDejCCAmKgAwIBAgIQf+UwvzMTQ77dghYQST2KGzANBgkqhkiG9w0BAQsFADBX
MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEQMA4GA1UE
CxMHUm9vdCBDQTEbMBkGA1UEAxMSR2xvYmFsU2lnbiBSb290IENBMB4XDTIzMTEx
NTAzNDMyMVoXDTI4MDEyODAwMDA0MlowRzELMAkGA1UEBhMCVVMxIjAgBgNVBAoT
GUdvb2dsZSBUcnVzdCBTZXJ2aWNlcyBMTEMxFDASBgNVBAMTC0dUUyBSb290IFI0
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE83Rzp2iLYK5DuDXFgTB7S0md+8Fhzube
Rr1r1WEYNa5A3XP3iZEwWus87oV8okB2O6nGuEfYKueSkWpz6bFyOZ8pn6KY019e
WIZlD6GEZQbR3IvJx3PIjGov5cSr0R2Ko4H/MIH8MA4GA1UdDwEB/wQEAwIBhjAd
BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAd
BgNVHQ4EFgQUgEzW63T/STaj1dj8tT7FavCUHYwwHwYDVR0jBBgwFoAUYHtmGkUN
l8qJUC99BM00qP/8/UswNgYIKwYBBQUHAQEEKjAoMCYGCCsGAQUFBzAChhpodHRw
Oi8vaS5wa2kuZ29vZy9nc3IxLmNydDAtBgNVHR8EJjAkMCKgIKAehhxodHRwOi8v
Yy5wa2kuZ29vZy9yL2dzcjEuY3JsMBMGA1UdIAQMMAowCAYGZ4EMAQIBMA0GCSqG
SIb3DQEBCwUAA4IBAQAYQrsPBtYDh5bjP2OBDwmkoWhIDDkic574y04tfzHpn+cJ
odI2D4SseesQ6bDrarZ7C30ddLibZatoKiws3UL9xnELz4ct92vID24FfVbiI1hY
+SW6FoVHkNeWIP0GCbaM4C6uVdF5dTUsMVs/ZbzNnIdCp5Gxmx5ejvEau8otR/Cs
kGN+hr/W5GvT1tMBjgWKZ1i4//emhA1JG1BbPzoLJQvyEotc03lXjTaCzv8mEbep
8RqZ7a2CPsgRbuvTPBwcOMBBmuFeU88+FSBX6+7iP0il8b4Z0QFqIwwMHfs/L6K1
vepuoxtGzi4CZ68zJpiq1UvSqTbFJjtbD4seiMHl
-----END CERTIFICATE-----
---
Server certificate
subject=CN=api.openai.com
issuer=C=US, O=Google Trust Services, CN=WE1
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 2818 bytes and written 402 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 256 bit
This TLS version forbids renegotiation.
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
全部コピペしてGPTに作らせた
// To get the certificate for your region run:
// openssl s_client -showcerts -connect api.openai.com:443
// Copy the certificate (all lines between and including ---BEGIN CERTIFICATE---
// and --END CERTIFICATE--) to root.cert and put here on the root_cert variable.
// certificate for https://api.openai.com
// GTS Root R4, valid until Sat Feb 20 2029, size: 1901 bytes
const char* root_ca_openai = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIDejCCAmKgAwIBAgIQf+UwvzMTQ77dghYQST2KGzANBgkqhkiG9w0BAQsFADBX\n" \
"MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEQMA4GA1UE\n" \
"CxMHUm9vdCBDQTEbMBkGA1UEAxMSR2xvYmFsU2lnbiBSb290IENBMB4XDTIzMTEx\n" \
"NTAzNDMyMVoXDTI4MDEyODAwMDA0MlowRzELMAkGA1UEBhMCVVMxIjAgBgNVBAoT\n" \
"GUdvb2dsZSBUcnVzdCBTZXJ2aWNlcyBMTEMxFDASBgNVBAMTC0dUUyBSb290IFI0\n" \
"MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE83Rzp2iLYK5DuDXFgTB7S0md+8Fhzube\n" \
"Rr1r1WEYNa5A3XP3iZEwWus87oV8okB2O6nGuEfYKueSkWpz6bFyOZ8pn6KY019e\n" \
"WIZlD6GEZQbR3IvJx3PIjGov5cSr0R2Ko4H/MIH8MA4GA1UdDwEB/wQEAwIBhjAd\n" \
"BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAd\n" \
"BgNVHQ4EFgQUgEzW63T/STaj1dj8tT7FavCUHYwwHwYDVR0jBBgwFoAUYHtmGkUN\n" \
"l8qJUC99BM00qP/8/UswNgYIKwYBBQUHAQEEKjAoMCYGCCsGAQUFBzAChhpodHRw\n" \
"Oi8vaS5wa2kuZ29vZy9nc3IxLmNydDAtBgNVHR8EJjAkMCKgIKAehhxodHRwOi8v\n" \
"Yy5wa2kuZ29vZy9yL2dzcjEuY3JsMBMGA1UdIAQMMAowCAYGZ4EMAQIBMA0GCSqG\n" \
"SIb3DQEBCwUAA4IBAQAYQrsPBtYDh5bjP2OBDwmkoWhIDDkic574y04tfzHpn+cJ\n" \
"odI2D4SseesQ6bDrarZ7C30ddLibZatoKiws3UL9xnELz4ct92vID24FfVbiI1hY\n" \
"+SW6FoVHkNeWIP0GCbaM4C6uVdF5dTUsMVs/ZbzNnIdCp5Gxmx5ejvEau8otR/Cs\n" \
"kGN+hr/W5GvT1tMBjgWKZ1i4//emhA1JG1BbPzoLJQvyEotc03lXjTaCzv8mEbep\n" \
"8RqZ7a2CPsgRbuvTPBwcOMBBmuFeU88+FSBX6+7iP0il8b4Z0QFqIwwMHfs/L6K1\n" \
"vepuoxtGzi4CZ68zJpiq1UvSqTbFJjtbD4seiMHl\n" \
"-----END CERTIFICATE-----\n";
これをrootCACertificate.h
として更新したら動いた
プルリクしようと思ったら既にしてあった
反省
- 早期にvoicevoxAPIの挙動を確認して、APIへのPOST自体は成功していることを確認したことはよかった
- その後、面倒がらずにGoogle speech to text APIを試していれば、より早期に証明書エラーが根本原因ということに気づくことができた
- PlatformIO, C++, ESP32でAPIを叩くことの勉強になったので結果オーライ