液晶モニタ故障?

突然、液晶モニタ(デュアルモニタで使っている内の1枚)が、XGA以上の表示ができなくなってしまいました。

調べていくと、Windows10では表示できるのだけど、LinuxではNG。dmesgで見ると、起動中に EDID invalid というメッセージが出ていました。

一時は買い替えも覚悟しましたが、結局カーネルの起動オプションに

drm_kms_helper.edid_firmware=VGA-1:edid/1920x1080.bin

を書き足して凌ぎました。具体的には、

/etc/default/grub の「GRUB_CMDLINE_LINUX_DEFAULT」の起動時オプションに「drm_kms_helper.edid_firmware=VGA-1:edid/1920×1080.bin」を追加して、

$ sudo grub-mkconfig -o /boot/grub/grub.cfg

として、再起動。こういうときは、Arch Linuxのドキュメントが完成度が高くて参考になります。

ESP8266からMQTT送受信してみる

AdafruitのMQTTライブラリを使ってローカルに立てたブローカー(MQTTサーバー)と通信してみます。

ライブラリのインストール

ArduinoIDEで「ツール→ライブラリを管理」でライブラリマネージャーから「Adafruit MQTT Library」をインストールします。

サンプルプログラムの呼び出し

「スケッチの例→Adafruit MQTT Library→mqtt_esp8266」でサンプルプログラムを呼び出します。

/***************************************************
  Adafruit MQTT Library ESP8266 Example

  Must use ESP8266 Arduino from:
    https://github.com/esp8266/Arduino

  Works great with Adafruit's Huzzah ESP board & Feather
  ----> https://www.adafruit.com/product/2471
  ----> https://www.adafruit.com/products/2821

  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Tony DiCola for Adafruit Industries.
  MIT license, all text above must be included in any redistribution
 ****************************************************/
#include <ESP8266WiFi.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

/************************* WiFi Access Point *********************************/

#define WLAN_SSID       "SSID"
#define WLAN_PASS       "PASSWORD"

/************************* Adafruit.io Setup *********************************/

#define AIO_SERVER      "ブローカーのIPアドレス"
#define AIO_SERVERPORT  1883                   // use 8883 for SSL
#define AIO_USERNAME    "username"  // 適当に変更(あまり関係ないっぽい)
#define AIO_KEY         "password"  // 適当に変更(あまり関係ないっぽい)

/************ Global State (you don't need to change this!) ******************/

// Create an ESP8266 WiFiClient class to connect to the MQTT server.
WiFiClient client;
// or... use WiFiFlientSecure for SSL
//WiFiClientSecure client;

// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);

/****************************** Feeds ***************************************/

// Setup a feed called 'photocell' for publishing.
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, "mqtttest");
//Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/photocell");
// AIO_USERNAME "/feeds/photocell" の部分をトピックに書き換えた 

// Setup a feed called 'onoff' for subscribing to changes.
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, "mqtttest");
//Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");
// AIO_USERNAME "/feeds/onoff" の部分をトピックに書き換えた 
// 以降は変更していない

/*************************** Sketch Code ************************************/

// Bug workaround for Arduino 1.6.6, it seems to need a function declaration
// for some reason (only affects ESP8266, likely an arduino-builder bug).
void MQTT_connect();

void setup() {
  Serial.begin(115200);
  delay(10);

  Serial.println(F("Adafruit MQTT demo"));

  // Connect to WiFi access point.
  Serial.println(); Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WLAN_SSID);

  WiFi.begin(WLAN_SSID, WLAN_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.println("WiFi connected");
  Serial.println("IP address: "); Serial.println(WiFi.localIP());

  // Setup MQTT subscription for onoff feed.
  mqtt.subscribe(&onoffbutton);
}

uint32_t x=0;

void loop() {
  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).  See the MQTT_connect
  // function definition further below.
  MQTT_connect();

  // this is our 'wait for incoming subscription packets' busy subloop
  // try to spend your time here

  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(5000))) {
    if (subscription == &onoffbutton) {
      Serial.print(F("Got: "));
      Serial.println((char *)onoffbutton.lastread);
    }
  }

  // Now we can publish stuff!
  Serial.print(F("\nSending photocell val "));
  Serial.print(x);
  Serial.print("...");
  if (! photocell.publish(x++)) {
    Serial.println(F("Failed"));
  } else {
    Serial.println(F("OK!"));
  }

  // ping the server to keep the mqtt connection alive
  // NOT required if you are publishing once every KEEPALIVE seconds
  /*
  if(! mqtt.ping()) {
    mqtt.disconnect();
  }
  */
}

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }

  Serial.print("Connecting to MQTT... ");

  uint8_t retries = 3;
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
       Serial.println(mqtt.connectErrorString(ret));
       Serial.println("Retrying MQTT connection in 5 seconds...");
       mqtt.disconnect();
       delay(5000);  // wait 5 seconds
       retries--;
       if (retries == 0) {
         // basically die and wait for WDT to reset me
         while (1);
       }
  }
  Serial.println("MQTT Connected!");
}

これでESP8266に書き込んで実行すると、リスナー側で

Client mosqsub|3941-armbian received PUBLISH (d0, q0, r0, m0, 'mqtttest', ... (1 bytes))
0
Client mosqsub|3941-armbian received PUBLISH (d0, q0, r0, m0, 'mqtttest', ... (1 bytes))
1
Client mosqsub|3941-armbian received PUBLISH (d0, q0, r0, m0, 'mqtttest', ... (1 bytes))
2
Client mosqsub|3941-armbian received PUBLISH (d0, q0, r0, m0, 'mqtttest', ... (1 bytes))

という感じで送信されているのが確認できました。

Arduino IDEのシリアルモニタ側でも

Connecting to xxxxxxxx
...
WiFi connected
IP address: 
xxx.xxx.xxx.xxx
Connecting to MQTT... MQTT Connected!

Sending photocell val 0...OK!
Got: 0

Sending photocell val 1...OK!
Got: 1

Sending photocell val 2...OK!
Got: 2

という感じでPublish/Subscribeの確認ができました。

 

ArmbianにMQTT環境をインストール

syslogサーバになっているArmbianにMQTT環境をインストールします。作業内容はこちらの記事を参考にしました。(というか、そのまま)

注)ローカルネットワーク内でのテストのため、ユーザー認証なしの状態で実施しています。

サーバー (Broker) のインストール

$ sudo apt install mosquitto

サーバー(Broker)の起動

$ sudo systemctl start mosquitto
$ sudo systemctl start mosquitto
$ sudo systemctl status mosquitto
● mosquitto.service - LSB: mosquitto MQTT v3.1 message broker
   Loaded: loaded (/etc/init.d/mosquitto; generated)
   Active: active (running) since Mon 2019-05-13 01:24:52 JST; 40s ago
     Docs: man:systemd-sysv-generator(8)
    Tasks: 1 (limit: 413)
   CGroup: /system.slice/mosquitto.service
           └─3781 /usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf

 5月 13 01:24:51 armbian systemd[1]: Starting LSB: mosquitto MQTT v3.1 message broker...
 5月 13 01:24:51 armbian mosquitto[3775]:  * Starting network daemon: mosquitto
 5月 13 01:24:52 armbian mosquitto[3775]:    ...done.
 5月 13 01:24:52 armbian systemd[1]: Started LSB: mosquitto MQTT v3.1 message broker.

クライアントのインストール

$ sudo apt install mosquitto-clients

リスナー(Subscribe)を動かす

リスナーの起動はサーバ(Broker)の起動後に行う。下記はその後のPublishをした際の記録を含む。トピックは mqtttest としてみた。

$ mosquitto_sub -d -t mqtttest
Client mosqsub|3941-armbian sending CONNECT
Client mosqsub|3941-armbian received CONNACK
Client mosqsub|3941-armbian sending SUBSCRIBE (Mid: 1, Topic: mqtttest, QoS: 0)
Client mosqsub|3941-armbian received SUBACK
Subscribed (mid: 1): 0
Client mosqsub|3941-armbian received PUBLISH (d0, q0, r0, m0, 'mqtttest', ... (18 bytes))
Hello MQTT world !
Client mosqsub|3941-armbian sending PINGREQ
Client mosqsub|3941-armbian received PINGRESP
Client mosqsub|3941-armbian sending PINGREQ
Client mosqsub|3941-armbian received PINGRESP

今回はローカルのサーバ(Broker)に対して接続(subscribe)しているが、リモートのサーバ(Broker)に対して接続(subscribe)する場合は「-h server名」とする。

別のPCでもクライアントのみ起動してサーバー(Broker)を指定してリスナーを起動すると、Publishした瞬間に同時に両方のリスナーで表示される。

Publishを動かす

$ mosquitto_pub -d -t mqtttest -m "Hello MQTT world !"
Client mosqpub|3942-armbian sending CONNECT
Client mosqpub|3942-armbian received CONNACK
Client mosqpub|3942-armbian sending PUBLISH (d0, q0, r0, m1, 'mqtttest', ... (18 bytes))
Client mosqpub|3942-armbian sending DISCONNECT
$

今回はローカルのサーバ(Broker)に対して接続(Publish)しているが、リモートのサーバ(Broker)に対して接続(Publish)する場合は「-h server名」とする。

サーバー(Broker)を自動起動するように設定

$ sudo systemctl enable mosquitto
mosquitto.service is not a native service, redirecting to systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable mosquitto
$

ESP8266/ESP32でsyslog出力

ESP32でセンサ情報を収集できるようになったのですが、ESP32は電源を落とすとデータが消えてしまいます。そこでログをsyslogで出力してみます。

ぐぐると、GitHubにちょうど Arduino用の Syslog ライブラリがありましたので、これを試してみます。

GitHubからZIPでダウンロードしてきて、ダウンロードしたファイル(Syslog-master.zip)を展開、展開したディレクトリの名前を Syslog-master から Syslog に変更して、 ~/Arduino/librariesの下に移動します。

その後、Arduino IDEを起動すると、「ファイル→スケッチ例」の中に「Syslog」が出てきますので、この中の AdvancedLoggingESP8266 を開いて、冒頭の部分を修正します。

// WIFI credentials
#define WIFI_SSID "**************" ← WiFiのSSIDをセット
#define WIFI_PASS "**************" ← WiFiのパスワードをセット

// Syslog server connection info
#define SYSLOG_SERVER "syslog-server"  ← syslogサーバのIPアドレスを記載
#define SYSLOG_PORT 514

// This device info
#define DEVICE_HOSTNAME "my-device"   ← ESP8266のデバイス名をセット
#define APP_NAME "my-app"           ← 適当に設定
#define ANOTHER_APP_NAME "my-another-app" ← 適当に設定

修正して実行すると、syslogサーバ側に以下のようなログが記録されました。

May 13 00:23:29 esp8266 my-app Begin loop
May 13 00:23:29 esp8266 my-app This is error message no. 2
May 13 00:23:29 esp8266 my-app This is info message no. 2
May 13 00:23:29 esp8266 my-app This is daemon info message no. 2
May 13 00:23:29 esp8266 my-app This is ftp info message no. 2
May 13 00:23:29 esp8266 my-another-app This is error message no. 2 from my-another-app
May 13 00:23:29 esp8266 my-app This is logged.
May 13 00:23:29 esp8266 my-app This is logged.
May 13 00:23:39 esp8266 my-app Begin loop
May 13 00:23:39 esp8266 my-app This is error message no. 3
May 13 00:23:39 esp8266 my-app This is info message no. 3
May 13 00:23:39 esp8266 my-app This is daemon info message no. 3
May 13 00:23:39 esp8266 my-app This is ftp info message no. 3
May 13 00:23:39 esp8266 my-another-app This is error message no. 3 from my-another-app
May 13 00:23:39 esp8266 my-app This is logged.
May 13 00:23:39 esp8266 my-app This is logged.

ドップラーセンサ用のケースを設計

以前作ったESP32のテスト用の基板以前買った秋月のドップラーセンサを適当に組みあわせてFusion360でケースを設計、出力してみました。

組み合わせた基板はこんな感じでいきあたりばったりです。

でも、基板に合わせてこんな感じのケースを設計しました。

こんな感じで基板を載せます。

蓋をしてネジ止めするとそれっぽくなるのが3Dプリンタのいいところです(笑)。

ネジ穴はM3x6のネジのネジ部を実測して、2.8mmの穴を開けて、入れやすいようにC1で面取りしただけで、いい感じにネジ止めできるようになります。何度も開け閉めするものでなければインサートナットを挿入しなくてもいいので楽ちんです。

ESP32でマイクロ波近接センサをロギング

ESP32はしばらく放置している間にすごく使いやすくなっていたので、調子に乗って、以前買ったESP32 DevKitCOLEDモジュールをつけて、ドップラーセンサーモジュールで壁越しに動体検知するのにチャレンジしてみました。

開発はArduino環境で行いました。無線LANの初期化やNTPでの時刻同期、OLEDの表示に関する部分、Webサーバーに関する部分はT-Cameraのソースをベースに不要な部分を削除して用意しました。

今回使用したセンサはUARTで通信します。ESP32にはUARTが3つあり、1つはPC(Arduino IDE)との通信用となっていますので、残り2つが利用可能です。今回はUART1の送信を16ピン、受信を17ピンに割り当てて使用しました。

#define UART1_TX 16
#define UART1_RX 17

Serial1.begin(9600, SERIAL_8O1, UART1_RX, UART1_TX

で初期化しました。

受け取ったセンサ情報は接近時、離別時、停止時の3つのパターンがありますが、それぞれ毎分0秒を境界とした1分毎、毎時0分を境界とした1時間毎に発生数を計数して Chart.js を用いてグラフ化してみました。

 

TTGO T-Cameraの画像をngrok経由で取得

NAT配下のWebサーバ等をインターネットからアクセスできるようにトンネリングをするngrokというサービスがあります。

これを使って、3Dプリンタの監視画像をインターネットから取得できないか試してみました。

テスト環境は以下のとおりです。

   |
 WiFi Router
   | 192.168.10.1
   |
   +---- TTGO T-Camera
   |       192.168.10.2
   |          80/TCP Web, 81/TCP Streaming
   |
   +---- Raspberry Pi(Raspbian Lite)
           192.168.10.3

まず、ngrok公式サイトからサインアップをします。サインアップすると、「Setup & Installation」という画面に移行しますので、これに沿って操作していきます。

まず、ngrokのダウンロードですが、Linux(ARM)のURLをコピーして、wgetでzipファイルを取得します。(下記はURLを書き換えてありますので、必ずURLをコピーしてください)

~$ wget https://bin.equinox.io/c/xxxxxxxxxx/ngrok-stable-linux-arm.zip

ダウンロードしたら、②、③に沿って展開とトークンの取得を行います。トークンの取得は③のところのコマンドラインをコピー&ペーストして実行するだけです。

~ $ unzip ngrok-stable-linux-arm.zip 
Archive: ngrok-stable-linux-arm.zip
inflating: ngrok 
~ $ ./ngrok authtoken xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Authtoken saved to configuration file: /home/pi/.ngrok2/ngrok.yml

トークンを取得したら、 ~/.ngrok2/ngrok.yml にある設定ファイルを作成します。

authtoken: xxxxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxx
tunnels:
  ttgo:
    addr: 192.168.10.2:80
    proto: http
    auth: "user:password"
    bind_tls: true

1行目の内容はトークン取得で作成されている内容です。2行目以降が追加した内容になります。

tunnels:
  ttgo:        <-------- トンネルにつけた名前です
    addr: 192.168.10.2:80    <---- アクセスされた際の転送先です
    proto: http              <---- httpプロトコルを指定します
    auth: "user:password"    <---- アクセスの際の認証を追加します
    bind_tls: true           <---- TLSで暗号化を強制します

作成したら、

~ $ ./ngrok start --all

で起動します。(終了すると tunnel も消滅するので screen と併用するとよいかと思います)

ngrok by @inconshreveable                                           (Ctrl+C to quit)
                                                                                    
Session Status                online                                                
Account                       XXXXXXXXXXXXXXXXX (Plan: Free)                       
Version                       2.3.27                                                
Region                        United States (us)                                    
Web Interface                 http://127.0.0.1:4040                                 
Forwarding                    https://b3d16323.ngrok.io -> http://192.168.10.2:80   
                                                                                    
Connections                   ttl     opn     rt1     rt5     p50     p90           
                              0       0       0.00    0.00    0.00    0.00

という感じで状態が表示されます。この状態でインターネット側から https://b3d16323.ngrok.io にアクセスするとBASIC認証が求められ、設定したユーザー名とパスワードを入力すると、TTGO T-Cameraへアクセスすることができました。アクセスすると上記の状態の下にHTTPリクエストの状態がリストで表示されます。なお、b3d16323の部分は起動する度にランダムに変わります。有料プランにするとこの部分を固定にすることができるようです。

実際にGet Stillをクリックすると、

という感じで実際に画像を取得できました。なお、ストリーミングについては異なるポート(81/TCP)でのアクセスが必要なのですが、ngrokのコンフィグファイルでは複数のポートの設定も記述できるものの、ポートごとに異なるURLが割り付けられます。このため、うまくいきませんでした。有料プランにするとURLを指定できるので、うまく行く方法があるのかもしれません。

PCのファンを修理

ネットアクセス用のAthlon5350マシンがあるのですが、しばらく前にケースFANがビビリ音で煩くなってFANを交換しました。もともとついていたFANは回転数センサなしの電源だけが接続されるタイプだったのですが、使っているASUS製のマザー側にFANの回転数制御機能があるようだったので、できるだけ高速のFANを買ってマザーボード側で速度を落として使うことにしました。

・・・・が、交換後、実はPWM制御しかできないということが判明して、爆音PCになってしまいました。速度制御できないとうるさくてたまりませんが、冬場だったこともありFANのケーブルを抜いて使っていました。しかし、気温が上がってきたせいか、最近CPUファンの音が煩くなってきたので、なんとかせにゃならん、ということで、もともとついていたFANにグリースを差して戻してみました。

もともとついていたFANは以下のようなものです。

シールを剥がすと、軸受が見えます。

ここに、グリースを詰めました。爪楊枝の先にグリースを載せて、穴の中に詰めていきました。

たくさん詰めたら、シールを蓋して、ケースにつけると、以前のようなビビリ音はしなくなりました。

今回使ったグリースは、近所のホームセンターにあった下記のグラファイトグリースです。適切なものなのかわかりませんが、見たところ金属同士の軸受かなーと思ったので、これにしました。まあ、FANがだめになったら、またFANを交換すればいいかな、ということで。
蛇腹タイプで40gも入っていますが、はっきり言って一生分くらいありそうです(笑)。

 

リチウムイオン電池制御IC IP5306について

TTGO T-Cameraに搭載されているリチウムイオン電池制御ICのIP5306について調べてみました。

データシートはこちらの通販サイト?で見ることができます(ダウンロードできなくなった場合のために、コピーを置いておきます)。簡体字中国語で書かれていますが、整然と書かれているのでこの手のチップの英語のデータシートが読める人なら大体わかると思います。細部はディスプレイに拡大表示させて、スマートフォンのGoogle翻訳でカメラから翻訳させれば、かなり精度よく翻訳してくれます。

このデータシートを見ると不思議なのはPDFのタイトルでは「1.2A充電、1A放電」と書かれているのですが、中身では「2.1A充電、2.4A放電」と書かれています。

まあ、細かいことはさておき、大雑把に捉えると、モバイルバッテリー用に特化したICです。面白いところを見てみます。

たとえば、LED表示ですが、端子3つでLED1本〜4本までの仕様に対応できるようになっています。1kΩの抵抗でプルアップすることにより、LEDが何個の仕様で動作するかを読み取っているんじゃないかと思います。

また、リファレンスデザインの項目を見てみると、付加価値として操作用のスイッチと照明用のLEDもつけられるようになっています。

この操作用のスイッチは50ms以上2秒以下の押下によって残量表示LEDと出力がONになり、2秒以上の押下で同じ端子に接続された照明用のLEDが点灯、1秒以内に2回連続して短く押すと出力や残量表示LED、照明用LEDがOFFになるというものです。これらの機能が不要の場合には5ピンは未接続でよいようです。
こんな感じで、とにかく2〜5ピンには機能が盛り沢山に詰め込まれています。単に出力端子として使うだけではなく、時分割で入力端子としても使用することでピン数削減を図り、とことんコストダウンを狙っています。かつての日本の半導体では(あるいは欧米の半導体でも)ここまで徹底的にピン数削減を図ったものはみたことがありません。

中国のローカルベンダーの製品にはこういう面白い半導体もあったりするのは興味深いところですし、彼らの並々ならぬパワーを感じるところです。また、数年前は中国語のデータシートがあっても雰囲気しかわからなかったのですが、データシートの内容自体も洗練されたものが増えていることや、Google翻訳のカメラ翻訳を使うことで中身がかなり読めるようになりました。

また面白いものがあったら取り上げてみたいと思います。

TTGO T-Cameraを3Dプリンタ監視用に

TTGO T-Cameraを3Dプリンタの監視用に使ってみました。

まず支柱を設計して、3Dプリンタで出力しました。

Ender-3の左手前に共締めして取り付けるとこんな感じです。

3D Benchyを出力してみました。

PCからブラウザ上でストリーム表示させるとこんな感じです。

VGAサイズくらいであればスムーズに表示されます。スマホからでも表示可能。

暗いのでノイジーですが、UXGAでのキャプチャもこの通り。魚眼レンズなので近くても全景が入ります。画像は歪んでしまいますが、ちょっと状況を確認するには十分な出来になりました。