電子工作」カテゴリーアーカイブ

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

現象

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不要らしい。

ATTinyのArduinoIDE用パッケージダウンロード先の暫定対処

いつの日からかArduinoIDEで追加ボードマネージャーURLに入れていたAttiny用(プロジェクトの正式名称ATTinyCore?megatinycore?)のjsonがダウンロード先の証明書が切れてつながらなくなっていた。

Some indexes could not be updated. Get "https://drazzy.com/package_drazzy.com_index.json": tls: failed to verify certificate: x509: certificate has expired or is not yet valid: : http://drazzy.com/package_drazzy.com_index.json

adafruitのgithubに以下の暫定対処先のURLダウンロード先がのっていたのでそちらに修正

https://web.archive.org/web/20230504110614/http://drazzy.com/package_drazzy.com_index.json

ArduinoIDEのprefarences->settingタブ->additional board manager urls

とりあえず暫定対処っぽいこと書いてあるけど、数か月前にfixされてそのままみたいなのでしばらくそのままかも

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

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

環境

  • 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を複数ストアして使えるやつっぽい。容量気にしない状況ならこいつが使いやすそう。

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がどこから拾ってきたかよくわからない。

picoprobeの環境構築で手間取ったのでメモ

Interface7月号のラズパイPicoでゼロから作るOSを試そうとしたら環境構築で手間取ったのでメモ。

先に結論

Interface記載の各ダウンロード先最新のもので環境構築をするとpicoprobeのデバックが動かない。対処としてxpmで入れたopenocdを使用すると動いた。

インストール方法

node/npm/xpmが入っている環境で

cd my-project
xpm init # Only at first use.

xpm install @xpack-dev-tools/openocd@latest --verbose

事象

手順通りだとエラー。エラーメッセージを取り損ねているけどlocalhost:3333がtimeoutとかの類だったはず。

cmsis-dap.cfgを修正しadapter speed 5000を追記するという手順をしても以下エラー。

Error: Sequence 4 not supported. 
Info : DAP init failed


Error: Sequence 3 not supported.
Error: Sequence 4 not supported.

原因

Interface記載のopenocdだと今のpicoprobeの処理に対応していない。こちらのリンク先のパッチが未適用。(公式だと取り込まれていた。)Interface記載の段階では動いたようなのでおそらくpicoprobeのバージョンアップで動作が変わったと思う。(が、picoprobe側の変更履歴は確認していない。)

各バージョン

多分直接関係するのはopenocdとpicoprobeだけだと思う

C:\Users\kitam>node -v
v18.17.1

C:\Users\kitam>npm -v
9.6.7

C:\Users\kitam>xpm --version
0.16.3
picoprobe-cmsis-v1.0.3
  "xpack": {
    "minimumXpmRequired": "0.16.3",
    "dependencies": {},
    "devDependencies": {
      "@xpack-dev-tools/arm-none-eabi-gcc": {
        "specifier": "12.3.1-1.1.1",
        "local": "link",
        "platforms": "all"
      },
      "@xpack-dev-tools/windows-build-tools": {
        "specifier": "4.4.0-1.1",
        "local": "link",
        "platforms": "all"
      },
      "@xpack-dev-tools/openocd": {
        "specifier": "0.12.0-2.1",
        "local": "link",
        "platforms": "all"
      }
    },
Eclipse IDE for Embedded C/C++ Developers (includes Incubating components)

Version: 2023-06 (4.28.0)
Build id: 20230608-1333

CH32V003とWCH-LinkEでLチカ

秋月で販売開始になったCH32V003J4M6(マイコン本体)とWCH-LinkEエミュレーター(書き込み/デバック装置)で書き込みができたのでそこまでのメモ。

基本的にはこちらの記事をトレースしてわからなかったことを追記した感じ

必要だったのもの

ハード

  • CH32V003J4M6 1個
  • WCH-LinkEエミュレーター 1個
  • ピッチ変換基盤 1個(これの同等品)
  • LED 1個(3mm使った)
  • 抵抗 1個(1 KΩ 大体で)
  • ブレッドボード 1個
  • コンデンサ 0.1uF 1個
  • ジャンパーケーブル 5本ぐらい
  • 書き込み用PC Windows11(1台)

ソフト

元記事と違った箇所、または追加したとこ

「いつも通り Workspace の場所を決めて Launch をクリックします。」は出なかった。後で選べる。

File⇒Open Procets from File System をクリックします。」は「Open Procets from File System」のとこが微妙に名前変わっていた

「コンパイル」のトンカチアイコンは見当たらず、ビルドアイコンがツールバーの左寄りにあった。ダウンロードアイコンもツールバーの左寄り。

サンプルソースはGPIO_Pin_0でLチカしているけどSOP8のだとPD0は出ていないのでGPIO_Pin_6(PD6の1番ピン)に変更

書き込みのたびに、MounRiver Studioにて以下操作

  1. メニューバーからFlash選択
  2. configration選択
  3. target modeにあるqueryボタンクリック
  4. プルダウンに出てくるWCH-LinkRV選択
  5. 少し下のErase Code FlashでBy power off を選択してApplyクリック

接続

PCのUSBにPCのUSBにWCH-LinkEエミュレーター接続

WCH-LinkEエミュレーターCH32V003
GND2番ピン(VSS)
3V34番ピン(VDD)
SWDIZO8番ピン()

CH32V003の1番ピン – Lチカ用のLED – 1KΩ抵抗 – CH32V003の2番ピン(VSS)

CH32V003の2番ピン(VSS) – 0.1uFコンデンサ – CH32V003の4番ピン(VDD)

ソース

ほぼそのままだけど下の

/********************************** (C) COPYRIGHT *******************************
 * File Name          : main.c
 * Author             : WCH
 * Version            : V1.0.0
 * Date               : 2022/08/08
 * Description        : Main program body.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for 
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/

/*
 *@Note
 GPIO routine:
 PD0 push-pull output.

*/

#include "debug.h"

/* Global define */

/* Global Variable */

/*********************************************************************
 * @fn      GPIO_Toggle_INIT
 *
 * @brief   Initializes GPIOA.0
 *
 * @return  none
 */
void GPIO_Toggle_INIT(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOD, &GPIO_InitStructure);
}

/*********************************************************************
 * @fn      main
 *
 * @brief   Main program.
 *
 * @return  none
 */
int main(void)
{
    u8 i = 0;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    Delay_Init();
    USART_Printf_Init(115200);
    printf("SystemClk:%d\r\n", SystemCoreClock);

    printf("GPIO Toggle TEST\r\n");
    GPIO_Toggle_INIT();

    while(1)
    {
        Delay_Ms(250);
        GPIO_WriteBit(GPIOD, GPIO_Pin_6, (i == 0) ? (i = Bit_SET) : (i = Bit_RESET));
    }
}

Aliで買ったLEDシリコンチューブ届いた

ここのやつ。

5mのオレンジで12Vで約1.2A。

明るさとしては室内の照明だと十分目立つけど昼の屋外だとほぼわからないぐらい。屋外で使うならやや日が傾きだしたぐらいからかな。晴れた6月の京都で18時ぐらいで見えるぐらい。

なお開封直後はゴム臭がけっこうした。

チューブに黒い点がついていてそこから切り離せるはずだけど試していない。

看板に使用したいので曲げてみた。折り目はつかないのでターンのきついところでは接着剤等で角度保持してやらなくてはならない。

ちなみに看板は最初トリマーで掘り込もうと思ったけど丁寧にやるのは時間がかかるのであきらめた。大きいCNCがあったら楽そうだけどCNC事態を持っていないのでレーザーカッターでアクリルを切り抜いたやつで作る予定。

サーボモーターSG90が半分の角度しか回らないので調査

環境

  • VSCode
  • PlatformIO
  • ESP-WROOM-02(自作基盤)
  • SG90(のAliexpressで買った互換品?)
  • Windows11

事象

SG90をPlatformIOで入れたライブラリで0,90,180度で動かそうとしたところそれぞれ0,45,90度ぐらいしか動かなかった

ソースは以下

#include <Arduino.h>
#include <Servo.h>


#define SERVO_PIN   12


Servo servoMotor; 

void setup() {
  Serial.begin(115200);
  delay(100);
  digitalWrite(LED_PIN, HIGH);
  servoMotor.attach(SERVO_PIN);
}

int count = 0;
int angle[] = {0,90,180};
void loop() {
  
  servoMotor.write(angle[count%3]);

  Serial.printf("%s - run %d %d \n",__func__,count,servoMotor.readMicroseconds());
  count++;
  delay(1000);
}
[env:esp_wroom_02]
platform = espressif8266
board = esp_wroom_02
framework = arduino
monitor_speed = 115200
upload_port = COM9

servoMotor.readMicroseconds()でみてみると本来500,1450,2400(ms)ぐらいであってほしい数値が1000,1500,2000ぐらいになっていた。

対応

サーボモーターの初期化をするときに以下のように明示的に0度と180度(データシート的には-90度と90度)の値を指定してやったら動いた。

servoMotor.attach(SERVO_PIN,500,2400);

原因

PlatformIOだとplatform を切り替えるとそれぞれのマイコンに応じたライブラリが読み込まれるみたい。そしてそこ(Servo.h)にあるデフォルト値が欲しい値じゃなかった

#define DEFAULT_MIN_PULSE_WIDTH      1000 // uncalibrated default, the shortest duty cycle sent to a servo
#define DEFAULT_MAX_PULSE_WIDTH      2000 // uncalibrated default, the longest duty cycle sent to a servo 
#define DEFAULT_NEUTRAL_PULSE_WIDTH  1500 // default duty cycle when servo is attached

読み込まれているライブラリはPIOホーム -> Libraries -> Built-inから(必要なら検索で絞って)Servoを選んでRevealを押すと確認可能。

ATTiny85でI2CしたデータをUARTシリアル通信

概要

ATTiny85にてスレーブからI2Cで取得(readのみwriteしてない)したデータをPCにUARTで連携してみたメモ。

流れとしては「Masterとして使うATTinyの準備/設定」「Slaveとして使うArduinoNanoの設定」「動作確認」。

全体ソース

環境

開発環境

Windows11(64bit)

ArduinoIDE 1.8.19 (多分Microsoft Storeからインストール)

Arduino UNO ( 後で出てくるArduino as ISPとして使う。使えればほかでもOK)

試験用ハード

PC

開発環境と同じWindows

ATTiny85-20PU

秋月で買った純正品 これ

USBシリアル変換モジュール

CH340が搭載されているやつを使用。(リンクこれから準備)

最近のWindowsだったら元からドライバーは入っているはず。

Arduino Nano 互換機

I2Cのスレーブとして。手持ちのI2Cなセンサーとかでもよいけど動かない時の切り分けが大変。

その他

  • ジャンパー必要数
  • コンデンサー 計100uFほど(書き込み時のリセット操作用なのでなければジャンパー手操作でも)
  • LEDと1Kほどの抵抗(動作しているかチェック用。なければないで)

Masterとして使うATTinyの準備/設定

ATTinyのブートローダー書き込み及びプログラム書き込み方法

このままこちら参照

書き込むプログラム

UART周り

ATTiny85にはハードウェアシリアルがないのでソフトウェアシリアルを使う必要がある。

pinout(参照)のPBnnで使うピンを指定するけれど(PB)0,(PB)2はI2Cで使うので利用できるのは残り。とりあえずRXを(PB)3,TXを(PB)4に指定する。

なお、(PB)5はプログラム書き込みの時に使うので他用途で使うと試験中は面倒。

ざっくり雰囲気は以下。

#include <SoftwareSerial.h>


#define RX_PIN 3 // PB3はUSBシリアル変換モジュールのTXにつなぐ
#define TX_PIN 4 // PB4はUSBシリアル変換モジュールのRXにつなぐ

SoftwareSerial softSerial =  SoftwareSerial(RX_PIN, TX_PIN); 

void setup() {
  softSerial.begin(9600);
  softSerial.println("Master start");

}

注意事項

  • TX,RXを入れ子でつなぐの間違えがち。
  • softSerialなので通信速度はあんまり頑張れない(まだまだいけるだろうけど必要なければ無理はしない)
  • 間違えてSerial.print~としてもエラーは出ない。(softSerial.println)

I2C周り

よく使うWireは使えないらしい。マイコンごとに実装分かれていて実装されていないとかどっかで見た。(ソース1分斜め読みで見つからなかったけどまぁ信じる)

かわりにArduinoIDEのライブラリ管理から入れられるTinyWireMが使える。

先ほど見たpinoutにあるようにPB0がSDA,PB2がSCL。

ざっくりプログラムは以下

#include <TinyWireM.h>

#define SLAVE_ADDR   0x08 // Slaveのアドレス。

void setup() {
  TinyWireM.begin(); // 開始
}

void loop() {
  int code = TinyWireM.requestFrom(SLAVE_ADDR, 1); // スレーブのアドレスから1バイト(第二引数)読み込み。
   if ( code == 0){
    softSerial.println(TinyWireM.read()); // 読み込んだものを一括取得(そして書き出し)

   } else {
// エラーコードはここにしかなさそうhttps://github.com/adafruit/TinyWireM/blob/master/USI_TWI_Master.h
    softSerial.printf("error with code[%d]\n",code);
   }
  delay(500);
}

注意事項としてTinyWireM.requestFromの戻り値は要確認。エラーがあった場合チェックしないでreadを呼ぶと初期値の0がそのまま取得される。(ライブラリのExampleはチェックしていないけど大事)

Slaveとして使うArduinoNanoの設定

呼ばれたら返すだけ。アドレスは0x08に設定している。(予約アドレス以外任意可のはず)

#include <Wire.h>

byte b=0;

void setup() {
    Serial.begin(9600);
  Serial.println("slave START");
  Wire.begin(0x08);// Slave ID #8
  Wire.onRequest(requestEvent);
}

void loop() {
}

void requestEvent() {
  Serial.print("event call current data[");
   Serial.print(b);
   Serial.println("]");
  Wire.write(b++);
}

動作確認

結線

ATTinyの番号はピン番号(PBnのnじゃない)

役割USBシリアル変換ATTinyNano
5v5Vのピン85v
GNDGNDのピン4GND
(Attinyからみて)TXRXのピン3
(Attinyからみて)RXTXのピン2
SCL7A5
SDA5A4

(全体ソースでは動作確認用のLEDがありATTinyの6番から1K抵抗経由でグランドにつながっている。)

動作ログ

同時につなげるとNanoの準備ができるまで見つからない旨のエラーが返るけど準備できたら取得できるようになる。

Loop [1]
by available
error with code[3]
Loop [2]
by available
error with code[3]
Loop [3]
by available
0
Loop [4]
by available
1
Loop [5]
by available
2
Loop [6]
by available
3

おばけくるましーんのメモ

寝ない幼児を脅すために、リモコン操作で扉とかをガタガタさせたい。

通信はそこそこ距離が届きそうなESPNow使う。(ESPNow調べた時の記事)

揺らす道具は130モーターに何かつけて自作した振動モーター+ESP32-DevKit。DevKit高いけどとりあえず。

リモコンはM5StickC。こいつも高いけどどんな機能必要になるかわからないので手軽にいじれるやつで。

電池と手持ちの部品で動かすには突入電流が大きい(参考)のでコンデンサが足りなかった。コンデンサ100uFぐらいだとパチモンのDCDCでは2.7vぐらいまで、パチモン及び正規品レギュレーターでも2.8vぐらいまでおちる。レギュレーターだと半々の確率でESP起動まではいけることが多いけどモーターを回すとまあだめ。とりあえずコンデンサ大き目ぽちって待機。

今のプログラムはこれ

ハードは

電源の3.3VからESPの3.3vピン、電源グランドからGND

GPIO2-抵抗 – 動作確認用のLED -GND

GPIO15-抵抗 – 2SK4017のBase -GND

電源ライン – モーター – 2SK4017

あとはコンデンサやら帰還ダイオードやらざっくり

USBコンセント電源から5vでとったら動作するので追加コンデンサでも厳しかったら揺れるとこ(モーター)だけ扉につけるように半分離するとよいかも。(電池動作の動作確認機を兼ねているので極力電池で頑張る)