2022年2月12日土曜日

ESP32でFTPサーバを立ててみた。

 ESP32-CAMを主にストリーミング用途として使っていたんだけども、microSDカードスロットを使ってみることに。

ESP32-CAMのmicroSDスロットはHS2に接続されているっぽいのでSDMMCライブラリを使用して取り扱うことができるっぽい。HS2に接続しているってことは比較的高速でアクセスできる?

SDカードに写真を保存する方法は色々紹介されているけど、せっかくWiFiにつながるモジュールなのでWiFiからSDカードにアクセスしたいということで色々調べてみた。

一番簡単そうなのはHTTPで一覧を作成してダウンロードする方法。アップロードも一応できるっぽいし。ただこれだと一個ずつダウンロードするのが面倒そう…

次にWebDAVもESP32用のライブラリが出ていた。Windows上でネットワークの場所の追加をしないといけないのが面倒だけど、一応エクスプローラから使用できるのでファイル操作が楽そう。

あとはFTPサーバのライブラリを作ってる人もいた。これならエクスプローラからそのまま行けそうなので便利かも。ということで試してみることに。

今回使用させてもらったのはESPFtpServerライブラリ。
どうやらESP32上で動かせるFTPサーバは色んな方々が開発を進めていてくれてるようで、それをまとめてくれたもののよう。こちらであればSD_MMCライブラリでも内蔵フラッシュメモリ上のSPIFFSでもFTPサーバでファイル転送ができるらしい。

早速サンプルを使用してみることに。

ライブラリのインストールは手動にしか対応していなのでESPFtpServerからCodeのDownload ZIPでダウンロードしてくる。解凍したら中にはいっているフォルダをArduinoのlibrariesにぶち込む。
このライブラリのサンプルスケッチのフォルダ階層がおかしくてちゃんと認識されないので、"ESPFtpServer-master\examples"の中に"ESPFtpServer"っていうフォルダを作って"ESPFtpServer.ino"を作成したフォルダにぶち込む。

これでサンプルスケッチが認識されるようになった。(Arduinoはinoファイルが同じファイル名のフォルダの中に入っていないとダメな仕様)

スケッチ例→ESPFtpServer→ESPFtpServerでサンプルスケッチを開く。
上の方でファイルシステムを設定するのだけども、今回はESP32-CAM上のmicroSDスロットを使用して試してみたので、define FS_LITTLEFSをコメントアウトして#define FS_SD_MMCのコメントアウトを外して有効にした。

ファイルの更新日時用に用意されてるNTPの設定を日本に合わせて変更する。

const char* ntpServer = "ntp.nict.jp";
const long gmtOffset_sec = 3600 * 9;
const int daylightOffset_sec = 0;
struct tm timeinfo;

45~48行目あたりにある設定をこんな感じに変更した。

あとはssidとpasswordを設定してとりあえず書き込み。

‎TGTF016GWA

microSDカードはTeamの16GBを使用した。SDメモリカードフォーマッターでフォーマット済み。SD_MMCライブラリは今のところexFAT非対応なので64GB以上のSDXCには対応していない。でも試しに64GBのmicroSDをFAT32でフォーマットしてみたら使えるようになった。そこまで容量いらないと思うけど…

あとはエクスプローラから"ftp://ESP32のIP"にアクセスするとFTPフォルダエラーが出た。とりあえずOKを押して

エクスプローラの白いところを右クリックしてログイン方法を押してユーザー名とパスワードにesp32と入れてログオンするとフォルダが表示される。(ftp://esp32:esp32@192.168.11.86/のようにユーザー名とパスワードをアドレスに含めてしまってても行ける。)ESP32-CAMの場合フラッシュのLEDにSDMMCで使われてるピンが接続されているため、アクセスするたびにフラッシュが点滅する。

ファイル転送スピードは書き込みは500kb/sぐらいで読み込みが1MB/sぐらい出ていた。文字コードの問題があるみたいで、ファイル名に結構制約がある。試しにFileZillaで文字コードをUTF-8固定にすると日本語のファイル名とかまで行けたのでWindows標準のエクスプローラだとエンコードがうまく設定できてないのかな?

  //
  //  OPTS
  //
  else if (!strcmp (command, "OPTS")) {
    for (uint8_t i=0; i <= strlen (parameters); i++){
      if(parameters[i] > 0x40){
	parameters[i] = parameters[i] & ~(0x20);
      }
    }
    if (!strcmp (parameters, "UTF8 ON")) {
      client.println ("200 Always in UTF8 mode.");
      //client.println ("451 Unable to accept OPTS UTF8");
    }
    else{
      client.println ("500 Unknown command " + String (parameters));
    }
  }

ということでESPFtpServer.cppを少し修正して"OPTS UTF8 ON"に反応するように変更してみた。807行目あたりにこんな感じでOPTSコマンドへの応答を返すように変更して、Windows 10のエクスプローラからアクセスしてみた。
しかしやっぱりWindowsのエクスプローラ内蔵のFTPクライアントが悪いのか、日本語ファイル名が化ける…他のツールならUTF-8モードにすれば何でも行けるんだけどなぁ…

Windows標準のFTPクライアントの仕様を見ているとどうやらLISTコマンドがMS-DOSスタイルにしか対応していないようで、それですべてのファイルがショートカットみたいに見えるようになることがあるらしい。

        while (file) {
          String fname = file.name();
          time_t ftime = file.getLastWrite();
          struct tm* ptm = localtime(&ftime);

          int pos = fname.lastIndexOf("/");
          if (pos >= 0) fname.remove(0, pos + 1);

          char dateStr[9];
          char timeStr[8];
          int hour = ptm->tm_hour;
          const char* ampm = "AM";
          if (hour >= 12) {
            ampm = "PM";
            if (hour > 12) hour -= 12;
          }
          if (hour == 0) hour = 12;
          sprintf(dateStr, "%02d-%02d-%02d", ptm->tm_mon + 1, ptm->tm_mday, (ptm->tm_year + 1900) % 100);
          sprintf(timeStr, "%02d:%02d%s", hour, ptm->tm_min, ampm);
          if (file.isDirectory()) {
            sprintf(buffer, "%s  %s    <DIR>          %s", dateStr, timeStr, fname.c_str());
          } else {
            sprintf(buffer, "%s  %s %12lu %s", dateStr, timeStr, (unsigned long)file.size(), fname.c_str());
          }
          data.println (buffer);
          nm ++;
          file = dir.openNextFile ();
        }

ESPFtpServer.cppのLISTコマンドのESP32用の部分をごっそりUNIXスタイルからMS-DOSスタイルに書き換えてみた。するとエクスプローラーでもちゃんとファイルが見える!ちゃんとフォルダ内のファイルも確認できた。文字コードの問題じゃなかったのか…

色々確認しているときにftpコマンドで接続してみたんだけど、ftpコマンドでopenまではいいんだけど、ユーザー名を入れた瞬間に切断される…
おそらくこのせいでエクスプローラから一発目アクセスしたときにFTPフォルダエラーが出たのかな?ソースを確認してみるとuserIdentity()の戻り値を待っているところでユーザー名が入っていなかったりするとcmdStatus=0で切断されるっぽい。

    if (cmdStatus == 3) {            // Ftp server waiting for user identity
      if (userIdentity ()) {
        cmdStatus = 4;
      }
      else {
        cmdStatus = 3;
      }
    }

とりあえずこんな感じでESPFtpServer.cppの100行目付近をいじってcmdStatus=3に変更してみた。これでftpコマンドでもうまく接続できるようになった。これでも何回かミスってるとタイムアウトのほうが効いてくるので問題ないと思う。

ついでにanonymousログインも追加してみた。

  if (_FTP_USER == "anonymous"){
    client.println ("230 Anonymous login successful");
    return true;
  }

これをESPFtpServer.cppの中のboolean FtpServer::userPassword ()のすぐ下に差し込むと、スケッチでuser名をanonymousにしておけばパスワードは何でも通ってしまうというアレ。

最初にSoftAPモードでテストしていたときにデータ転送できなかった謎も解けた。これはPASVモードのときにサーバのIPアドレスが0.0.0.0になってしまうためだった。300行目ぐらいにある、

  	dataIp = WiFi.localIP ();

と、なっている箇所を

        if (WiFi.getMode() == WIFI_MODE_AP) {
	  dataIp = WiFi.softAPIP();
	}else{
  	  dataIp = WiFi.localIP();
  	}	

のように修正した。SoftAPのときはlocalIP()じゃなくてsotAPIPじゃないとサーバ側のIPアドレスが0.0.0.0で報告してしまうためうまく動かなかった。これでSoftAPでもSTAモードでも動作するはず。SoftAPのときはNTPからRTCを更新できないので、Webサーバも一緒に立てて先日の技が使えるかも。

ちなみにサンプルのままでも便利だけど、IPがわかりにくいのでmDNSを追加しておくと便利かも。

他のESP32用のFTPサーバはスケッチ本体だったりしていたのでライブラリになっていることによりほかのスケッチと統合が楽になって扱いやすいかも。これでESP32-CAMでタイムラプスを撮ってそれをFTPでダウンロードができそう。

2022年2月6日日曜日

ESP32のRTCをオフラインでブラウザから設定する。

 ESP32をSoftAPとかでオフラインで使用する場合に内蔵のRTCの時間を簡単に設定できないか試してみた。IoT系マイコンなのでネットに接続されていてNTPから時刻を取得するのは簡単なんだけど、オフラインの場合は外付けRTCを接続して電池で保持するのが定番なのかな。

その昔WebページにJavascriptでリアルタイム時刻を表示していた時代も有ったなーというところからの発想を得て、パソコンやスマホからESP32にブラウザを使ってアクセスしてJavascriptから時刻をESP32に送ってやれないかなと。

ということで早速作ってみた。ページを開くたびに問答無用で時間セットするサンプル。

#include <WiFi.h>
#include <WebServer.h>
#include "time.h"
#include "esp_mac.h"  //MACアドレス取得用

WebServer server(80);

const char INDEX_HTML[] =
  "<!DOCTYPE HTML>"
  "<html>"
  "<head>"
  "<link rel=\"icon\" href=\"data:,\">"
  "<title>Timeset</title>"
  "</head>"
  "<script type=\"text/javascript\">\n"
  "var xhr = new XMLHttpRequest();\n"
  "xhr.open('POST', './settime', true);\n"
  "xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');\n"
  "const dtime = new Date().getTime() / 1000.0;\n"
  "xhr.send(\"time=\" + dtime);\n"
  "window.onload = function(){\n"
  "document.getElementById(\"textbox\").value = dtime;\n"
  "}\n"
  "</script>\n"
  "<body>"
  "<p>Unix Time</p><br>"
  "<input type=\"text\" id=\"textbox\">"
  "</body>"
  "</html>";

void setup() {
  uint8_t macaddr[6];
  esp_read_mac(macaddr, ESP_MAC_WIFI_SOFTAP);  //WiFi Macアドレス読み込み
  char SSID[14];
  sprintf(SSID, "ESP32-%02X%02X", macaddr[4], macaddr[5]);
  WiFi.softAP(SSID);  //SoftAPを起動
  delay(100);

  server.enableCORS();  //CORS対策 (JavaScriptデバッグ用)
  server.on("/", []() {
    server.send(200, "text/html", INDEX_HTML);
  });
  server.on("/settime", []() {
    if (server.args() > 0) {
      timeval epoch = { server.arg(0).toInt(), 0 };//構造体に格納
      settimeofday(&epoch, NULL);//RTCに時間をセット
    }
    server.send(204);
  });
  server.begin();
  setenv("TZ", "JST-9", 1);  //日本標準時に設定

  xTaskCreatePinnedToCore(task, "task", 4 * 1024, NULL, 5, NULL, 0);  //別タスク起動
}

void loop() {
  server.handleClient();
}

void task(void *pvParameters) {
  Serial.begin(115200);
  while (1) {
    static unsigned long lastUpdate = 0;
    if (lastUpdate + 1000 <= millis()) {
      lastUpdate = millis();  //次の実行のために時間更新//ms周期
      struct tm timeinfo;
      if (!getLocalTime(&timeinfo)) {
        Serial.println("Failed to obtain time");
      } else {
        Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
      }
    }
    delay(1);  //WDT対策
  }
  vTaskDelete(NULL);
}

ESP32でSoftAPでアクセスポイントを作ったらWebサーバーを立てて、それにスマホやPCのブラウザからアクセスするとシステム時刻をJavaScriptで取得してESP32にPOSTで投げるという荒業。もちろん2038年問題対策はされていない。

getLocalTimeがfalseの場合、処理に時間がかかるのでWebサーバーをブロッキングしないように無駄にマルチタスクでシリアルモニタを実装。

ページを読み込んだ瞬間にJavaScriptから時間を取得してPOSTで投げるのでロードにかかった分とかは時間がずれる。でもシリアルコンソールの時間表示と比べてみる限り1秒もずれていなかった。PC版ChromeとiOSでは動作確認できた。

外付けRTCで電池の心配しなくても良いので(ESP32の内蔵RTCがどのぐらいでずれていくのかわからないけど)簡易的な用途だったらこれで十分かも。ちなみに内蔵RTCはそこそこ制度が悪いので日をまたいだりするとかなりずれる。そんなときは外付けクリスタルにも対応しているらしいので精度が必要であれば32kHzのクリスタルを外付けすれば良さそう。

2022年1月25日火曜日

2D LiDAR LDS-006を買ってみた。

 Aliexpressを見ていたらロボット掃除機のLiDARが10ドルで売ってたのでつい買ってしまった。送料いれて15ドルだった。

シールからはEcovacs RoboticsのLDS-006っていうセンサーだということがわかる。
レーザーToFセンサーによる回転式のLiDAR。仕様は不明…
一応台座ごとプチプチに包まれて届いたので届いた。台座が付いてるのでそのまま平面部に置ける。
センサー部を回すのはベルト駆動のDCモーター。ベルトなのでまあまあ静か。ちなみにセンサー部めっちゃ回してるけど、センサー部の電力はコイルで非接触で給電してる模様。
通信は回転中心に赤外線LEDとそれの受光部があってセンサーから台座には一方通行でシリアル通信してるみたい。
接続はPHコネクタっぽい。
分解画像とピンアサインはLDS-006 Lidarを参照した。
赤が5V、緑がRX、青がTX、黒がGND。baudrateは115200
とりあえずPHコネクタがピッタリ入った。ピッチが2.0mmなのでピンヘッダとかそのままさせないのよねぇ。UARTのレベルは3.3VらしいのでCP2102のUSBのドングルを使用した。5V電源もそちらからとった。

少し調べてみると中国語の情報だけどこのLDS-006を解析している人がいたので参考にさせてもらった。
Laser-Radar-LDS-006-Drive-Test
色んなサイトを見ているとモーターのコントロールを別にやらないとダメそうなんだけど試しにstartlds$コマンドを送ってみたら回ってデータも取れてそう。回転方向は反時計回りだった。
回り始めると22バイトずつのデータがめっちゃ流れてくる。おそらく4°ずつのデータかな。
ただ、距離は4個分入っているので分解能としては1°って言うことでいいのかなぁ?
とりあえず反応したコマンドは
startlds$
getserial$
holdlds$
ぐらいかな?holdlds$をすると止まる。ちなみに改行コードとかが入っていると次のコマンドを受け付けなくなるのでholdlds$したあとにstartlds$ができない場合は余計な文字が送られている可能性。
getserial$したら"L01500003131808091484"って帰ってきた。
CamsenseX1用に表示プログラムを作った人が公開してくださっていたのでこれをLDS-006のプロトコルに対応するようにテキトーに改造して試してみた。
とりあえず取れてそう。センサー本体を回すと平面部の角度が変化する。
距離はどのぐらい飛ぶんだろうなぁ
これを作ってから気がついたんだけど、通信プロトコルがNeato XV11 LIDARのfirmware v2.4とv2.6のプロトコルにそっくりである。もしかしてまんまそのままだったりして?
LDS-006の解析結果との違いは距離の2bytesのうちの上位2bit分がエラーflagとして使われているぐらいなもんだし。
掃除機の部品で角度の分解能がいまいちとはいえ、LiDARも安く手に入るようになったなぁ
カスタムファーム開発してる人もいるっぽいんだけど分解能とか改善できるのかね。

追記:LDS-006のプロトコルが実はXV11と一緒なんじゃないかというところが気になって後日距離データの上位2bit分を解析してみた。やっぱりXV11と同様、距離データは14bitでHigh byteの上位2bit分が0以外になるとLow byteがエラーコード?になるようで16bit目がHighになると距離のLow byteは0x88とか0x99固定になってた。
XV11用に開発されたツールとかも使えたりするんじゃないかな。

2022年1月22日土曜日

ESP32でバーチャルスティックを使ってみた。

 スマホゲームとかで画面上に指を置くとアナログのジョイスティックみたいに使えるバーチャルスティックをESP32で使ってみた。ESP32でWebサーバーを立てて、ブラウザ上のバーチャルジョイスティックからラジコンを操作してみた。


ブラウザ上でJavascriptによってバーチャルスティックを実現できるライブラリは古くから存在しているようで、今回はvirtualjoystick.jsを使わせてもらった。

このライブラリでは画面をタッチするとバーチャルスティックが出て座標が取得できる。このライブラリからESP32本体にデータを送るためにはWebSocketを使ってみた。

virtualjoystick.jsから出てきた座標データをESP32のWebSocketサーバーにJavascriptで投げる感じ。

<html>
	<head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		
		<style>
		body {
			overflow	: hidden;
			padding		: 0;
			margin		: 0;
			background-color: #BBB;
			overscroll-behavior-y: none;
		}
		#info {
			position	: absolute;
			top		: 0px;
			width		: 100%;
			padding		: 5px;
			text-align	: center;
		}
		#info a {
			color		: #66F;
			text-decoration	: none;
		}
		#info a:hover {
			text-decoration	: underline;
		}
		#container {
			width		: 100%;
			height		: 100%;
			overflow	: hidden;
			padding		: 0;
			margin		: 0;
			-webkit-user-select	: none;
			-moz-user-select	: none;
		}
		</style>

	</head>
	<body>
		<div id="container"></div>
		<div id="info">
			画面をタッチしてスライド
			<br/>
			<span id="result"></span>
			<br/>
			<span id="consoleArea"></span>
		</div> 
		<div id="consoleArea">sa</div>
<script src="./virtualjoystick.js"></script>
<script>
  var gateway = `ws://${window.location.hostname}/ws`;
  var websocket;
  window.addEventListener('load', onLoad);
  function initWebSocket() {
    console.log('Trying to open a WebSocket connection...');
    websocket = new WebSocket(gateway);
    websocket.onopen    = onOpen;
    websocket.onclose   = onClose;
    websocket.onmessage = onMessage; // <-- add this line
  }
  function onOpen(event) {
    console.log('Connection opened');
  }
  function onClose(event) {
    console.log('Connection closed');
    setTimeout(initWebSocket, 2000);
  }
  function onMessage(event) {
    var consoleArea = document.getElementById("consoleArea");
    consoleArea.innerHTML = event.data;
  }
  function onLoad(event) {
    initWebSocket(); 
  }
  function toggle(){
    websocket.send('toggle');
  }
			console.log("touchscreen is", VirtualJoystick.touchScreenAvailable() ? "available" : "not available");
	
			var joystick	= new VirtualJoystick({
				container	: document.getElementById('container'),
				mouseSupport	: true,
				limitStickTravel	: true,
			});
			joystick.addEventListener('touchStart', function(){
				console.log('down')
			})
			joystick.addEventListener('touchEnd', function(){
				console.log('up')
			})
			var prevX = 0;
			var prevY = 0;
			var newX = 0;
			var newY = 0;
			setInterval(function(){
				var outputEl	= document.getElementById('result');
				newX = Math.round(joystick.deltaX());
				newY = Math.round(joystick.deltaY()) * -1;
				outputEl.innerHTML	= '<b>Position:</b> '
					+ ' X:'+newX
					+ ' Y:'+newY;
				if ( newX != prevX || newY != prevY ){
				    websocket.send("x:"+newX+",y:"+newY);
				}
				prevX = newX;
				prevY = newY;
			}, 1/30 * 1000);
</script>
</body>
</html>

ESP32のWebサーバーで表示させるHTMLファイルはこんな感じ。エラー処理適当だけどとりあえずESP32のWebSocketサーバーにバーチャルスティックの座標データを送りつける感じ。

void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
  AwsFrameInfo *info = (AwsFrameInfo*)arg;
  if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
    data[len] = 0;
    /*if (strcmp((char*)data, "toggle") == 0) {
      ledState = !ledState;
      notifyClients();
    }
    */
    //Serial.println((char*)data);
    uint8_t ypos = 0;
    char strx[4];
    char stry[4];
    if(data[0] == 'x'){
      for (int i=2; i <= len; i++){
        if(data[i] == 'y'){
          ypos = i + 2;
          break;
        }else{
          strx[i-2] = data[i];
        }
      }
      for (int i=ypos; i <= len; i++){
        stry[i-ypos] = data[i];
      }
      //Serial.println(ypos);
      x = atoi(strx);
      y = atoi(stry);
      #ifdef debug
      Serial.print("x:");
      Serial.print(x);
      Serial.print(",y:");
      Serial.println(y);
      #endif
    }
  }
}

そんでもってESP32側のWebSocketサーバーでバーチャルジョイスティックのデータを受けたらグローバル変数に突っ込んでloopの中でモーターにデータを送る形にしてみた。

HTMLファイルとvirtualjoystick.jsは結構大きくなってしまったのでOnline converter: File to (cpp) gzip byte arrayを使用してgz圧縮してスケッチ内に組み込んでしまった。

とりあえず前に作った車体に乗っけてみて実験してみた。(写真撮りながら難しすぎ

対向二輪タイプの車体なのでバーチャルジョイスティックの値をそれぞれの車輪にどう振り分けるかの計算をもう少し調整しないといけないかも。レスポンス的にも十分問題なさそうなのでもう少し改良を進めようかな。

blynkとかでも同じようなことができるけどESP32だけでスマホ側にアプリのインストール要らないのは便利かもしれない。


2022年1月4日火曜日

Mirakurunにリバースプロキシで外部からアクセスする

 Mirakurunを外部からアクセスするのにApache2のリバースプロキシを使用してみた。これならDigest認証をApache側でやったり、バーチャルホストでサブドメインで管理できたりして便利そうだし。でもMirakurunの作者もリバースプロキシの下に置かないでくださいって言ってるので本当はやってはいけません…ちょっと設定するときだけ使いたかったので…

まずはMirakurun側の設定。Hostnameをちゃんと設定しないとリバースプロキシからアクセスするとJavaScript系が全部403で弾かれてこまった…

sudo EDITOR=nano mirakurun config server

で設定ファイルを開いて、hostnameがもうあるならそちらを編集して、ない場合は追加する。

# logLevel: <number>
logLevel: 2

# path: <string>
path: /var/run/mirakurun.sock

# port: <number>
# You can change this if port conflicted.
# Don't expose this port on the internet, not even with NAPT.
# Use this in LAN or VPN.
# `~` to disable TCP port listening.
port: 40772
hostname: mk.hoge.f5.si

こんな感じでhostnameに使用するDDNSのアドレスを入れて保存する。

sudo mirakurun restart

で再起動する。

次にApache2の設定。

sudo nano /etc/apache2/sites-available/000-default.conf

設定するコンフィグファイルを開いて、バーチャルホストを追加。

<VirtualHost *:80>
ServerName mk.hoge.f5.si

        <IfModule mod_proxy.c>
        ProxyRequests   Off
        ProxyPreserveHost       Off
        ProxyErrorOverride Off
        ProxyPass       /rpc       ws://127.0.0.1:40772/rpc
        ProxyPassReverse        /rpc       ws://127.0.0.1:40772/rpc
        ProxyPass       /       http://127.0.0.1:40772/
        ProxyPassReverse        /       http://127.0.0.1:40772/
        </IfModule>

</VirtualHost>

ここでWebSocketの部分も追加しておかないとメインページしか表示されなくて焦った。認証とかは適宜追加しておく。

sudo a2enmod proxy proxy_wstunnel
sudo service apache2 restart

これで設定が有効になってるはず。

今回ハマったのはMirakurun側のHostnameがちゃんと設定されてないとうまくアクセスできない(mDNSとかでアクセスする際もちゃんと設定しておかないとIPからじゃないと行けないかも)のと、Apache側でWebSocketもリバースプロキシ設定してやらないと行けないところ。/rpcのところは/rpc/だと行けなかったり色々ハマってしまったのでまた設定するとき用のメモでした。

Mirakurunは外部ネットワークからアクセスする想定ではないので本来はVPNとかを使ったほうが良いかも。とりあえず設定が終わったらアクセスできないように設定しておいたほうがよいですな。

2021年12月13日月曜日

RP-PB186を買ってみた。

 最近ぼちぼち電車で出張に行く機会があるのだけれども、iPhone 12 miniだと結構使いまくると帰りのバッテリ残量が怪しい。電子マネーも切符もスマホだからバッテリなくなったらやばいよなぁ。ということでモバイルバッテリを買ってみた。

超久しぶりにモバイルバッテリを購入したわけだけど、とりあえず最近流行りのPD対応でお安くて安心できそうなやつということで、ちょうどRavPowerのRP-186のリニューアル版が安かったので買ってみた。10000mAhで1699円。
Amazonに締め出し食らったようでRavPowerとは言っていない状態だったけど…

パッケージはやっぱりRavPower。品番もPB186っていう表記だったけどね…
ちゃんと両端Type Cのケーブルも付属していた。Amazonの製品画像ではRavPowerロゴ消されていたけど実物にはちゃんと入ってる。
Type Cのモバイルバッテリって充電も放電も同じ端子で行けるのがべんりねぇ。一応Type Aもついてる。Type Aからも9V2A出せるって何の規格なんだろ…
PDは12Vまで出せるとか。
18Wだと思って購入したんだけど、実はiPhoneの20Wに対応してそうな感じ。
USB Type C→Lightningケーブルも買っておけばよかったなぁ。

LEDランプはめっちゃ暗い。だがそれがいい。寝てる間に充電しても眩しくない仕様かな。
充電中はバッテリー残量が点滅する仕様。0から25%だったら一個目のLEDだけ点滅するし、75から99%だったら4個目のLEDだけ点滅する。わかりやすくていい感じ。
充電していないときはボタンを押すとバッテリ残量が確認できる仕様。
4つ目のランプが点滅し始めてしばらくするといきなりコイルが泣き始める…
そしていつの間にか治ってる。まぁほぼ気にならないからいいか。
充電が完了すると全部点灯したままになるっぽい。

まぁセルもLG製とかちゃんと書いてあるし、怪しいメーカーのやつよりはいいかなぁ
やすいし。
12Vまで出力できるからUSB PDデコイとか使って遊べるかなぁ。

2021年12月4日土曜日

Edifier TWS330 NBを買ってみた。

 Aliexpressのセールで安くなったりクーポン配ってたりしていたのでEdifier TWS330 NBを買ってみた。レビューも良さげだった。換算レートが微妙なので3,466円だったけど。
Edifierって昔ドスパラでスピーカーが売っていて結構良さそうだったので聞き覚えがあるメーカーということでこいつにしてみた。一応38dBのアクティブノイキャンがついているのにこの価格は安いし。

今回は箱潰れがひどい。まぁこの箱薄いし…
ちゃんと裏面には日本語も書いてある。まぁAmazonでも売ってるから大丈夫かなということで。
まぁここはあえて白を選択してみたけど、BeatsとAirPods Proの中間ぐらいの丸み。結構丸い。
イヤーピースがたくさんついてる。本体にも装着されているので5セット分。あとはTypeCケーブルが付いてくる。
フィルム剥がしたら静電気でめっちゃゴミが付いてしまったけど、表面はテカテカしてるし質感も良い。
技適マークもちゃんとついてる。蓋の裏側に色々プリントしてあるのはAirPods Proみたいな感じ。
イヤホン本体は結構大きく感じる。
一応アプリからノイキャンと外部取り込みモードが切り替えられる。アプリ自体はBLEで接続してるっぽい。なるほどなぁ

PCとつなぐとアプリが使えないので左のイヤホンをダブルタップでノイキャンと外部取り込みモードとノーマルモードを切り替えられる。
対応コーデックはAACとSBCのみなのでiPhoneやMacなどのApple製品系ではAACで使用できるけどWindows 10だとSBCになってしまう。Windows 11ではAAC対応したみたいなんだけどなぁ。
試しにMacBook ProにつないでみたらちゃんとAACで接続されていた。

ちなみにイヤホン本体は1時間の充電で4時間(ノイキャンオフで5時間)持つらしい。ケースは1.5時間の充電で3回イヤホンを充電できるらしい。
意外と充電早いなぁ。

この価格でANCついてて、ちゃんとしたメーカー製で技適も付いてるし、TWS入門機としてはなかなかいいかもしれない。