ソース公開するArduinoプログラムに秘密情報を埋め込む

aoirint/RoomSystemSensorESP32: ESP32とFirebaseを使った部屋センシング・オンライン化 クライアントの開発中に Arduinoプログラム(.ino)にWiFiパスワード・APIキーなどの秘密情報を埋め込む必要が出てきた。

ここでは、Arduino IDEの主要機能を持つCLIソフトウェアarduino-cliを使う。

arduino/arduino-cli: Arduino command line interface

arduino-cliの使い方については、別記事参照。

秘密情報の埋め込み

秘密情報の埋め込みには、以下のようなシェルスクリプトcompile.shを作成するのが楽でよい。 DEFINES=の部分の-Dから=までの文字列が定数名、=の右辺が定数値として 定義された状態でソースコードがコンパイルされる。 ここでは同ディレクトリの.envファイルを読み込んで使用する。 .envファイルのフォーマットはよくあるものと同じで、 改行で区切られ、#から始まる行を無視するKEY=VALUE形式のテキストファイル。

ボードへの書き込みには以下のupload.shのようなスクリプトを使うとよい。

screenコマンドをラップするスクリプトserialmon.shもおいておく。

compile.sh

#!/bin/bash

set -eu

if [ -f .env ]; then
    echo "Found .env file."
    export $(cat .env | sed 's/#.*//g' | xargs)
fi


# ESP32-DevKitC
FQBN="esp32:esp32:esp32"

# Arduino UNO
# FQBN="arduino:avr:uno"


DEFINES="-DSECRET_WIFI_SSID=$WIFI_SSID"
DEFINES="${DEFINES} -DSECRET_WIFI_PW=$WIFI_PW"
DEFINES="${DEFINES} -DSECRET_FIREBASE_HOST=$FIREBASE_HOST"
DEFINES="${DEFINES} -DSECRET_FIREBASE_AUTH=$FIREBASE_AUTH"


SKETCH="$(basename $PWD).ino"

arduino-cli compile \
    -b "$FQBN" \
    --build-properties \
        "build.defines=${DEFINES}" \
    "$SKETCH" "$@"

プログラム側では以下のようにする。#xはコメントではないので注意(文字列リテラルとして展開するマクロ)。

#define _Q(x) #x
#define Q(x) _Q(x)

#ifdef SECRET_WIFI_SSID
  #define WIFI_SSID Q(SECRET_WIFI_SSID)
#else
  #define WIFI_SSID "WIFI-SSID"
#endif

#ifdef SECRET_WIFI_PW
  #define WIFI_PW Q(SECRET_WIFI_PW)
#else
  #define WIFI_PW "WIFI-PASSWORD"
#endif

void initWiFi() {
  WiFi.begin(WIFI_SSID, WIFI_PW);
  Serial.print("WiFi ");
  Serial.print(WIFI_SSID);
  Serial.println(" connecting");

  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }

  Serial.println();
  Serial.print("WiFi connected: ");
  Serial.println(WiFi.localIP());
}

upload.sh

#!/bin/bash

set -eu

SERIAL_PORT="$1"


# ESP32-DevKitC
FQBN="esp32:esp32:esp32"

# Arduino UNO
# FQBN="arduino:avr:uno"


SKETCH="$(basename $PWD).ino"
ARGS="${@:2}"

arduino-cli upload \
    -b "$FQBN" \
    -p "$SERIAL_PORT" \
    "$ARGS"
./upload.sh /dev/ttyACM0

serialmon.sh

#!/bin/bash

set -eu

SERIAL_PORT="$1"
BAUDRATE=115200

screen "$SERIAL_PORT" "$BAUDRATE"
./serialmon.sh /dev/ttyACM0

閉じるにはCtrl+A kを押した後にyを押してEnter

update.sh

まとめて実行する

#!/bin/bash

set -eu

SERIAL_PORT="$1"

./compile.sh && ./upload.sh "$SERIAL_PORT" && ./serialmon.sh "$SERIAL_PORT"
./update.sh

compile.shの中身

同ディレクトリ中にある.envファイルを読み込み、 C言語の定数として取り込まれるbuild.definesに手動で変数を列挙している (nginx Dockerのtemplate置換のように定義済みの環境変数を自動で入れ込む改良もしたい)。

またDEFINESの中身をエスケープしたい(変数内に-Dを許容したい)が、 まだやり方がわかっていない。

関連