Androidのメモとか

ポキオの日記です。今日も遅延してない。

ポキオ的Raspberry Piセットアップ

メモメモ。

ポキオ Raspberry Pi 初期設定

OSをSDカードに焼く

今回はRaspberry Pi Imagerで焼きました。

relativelayout.hatenablog.com

Wi-Fi接続情報作成・SSH有効化

Wi-Fiの接続情報(wpa_supplicant.conf)とSSHを有効化するためのssh.txtという空ファイルをSDカードに予め入れておくと、起動時に自動でWi-Fi接続・SSH有効になって非常に便利。

mascii.github.io

wpa_supplicant.conf作成には@masciiさんのWebアプリをいつも使わせていただいておりますー。ありがとうございます。

SSHでアクセス

同じネットワークから、おもむろにSSH接続します。初期パスワードはraspberry

$ ssh pi@raspberrypi.local

お守り代わりにパッケージ更新しておきます。

$ sudo apt update
$ sudo apt upgrade

その後、Raspberry Piの名前とかパスワードを変更。

$ sudo raspi-config 

設定後に再起動。

Node-REDの設定

何故かRaspberry Pi ImagerでOSを焼くと、Node-REDが入っていないっぽく、イチから入れていきます。

nodered.jp

とりあえずインストール。

$ bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)

あとはRaspberry Pi起動時に自動で立ち上がるように設定しておわり。

$ sudo systemctl enable nodered.service

これでNode-REDまわりの設定はいい感じ。

ポキオ Raspberry Pi 初期設定

とりあえず、ここまでできればOKですね。

いつの間にかRaspberry Pi Imagerができてて爆速OS焼きができた

秒で焼いていく。

ポキオ Raspberry Pi Imager

Raspberry Pi Imagerって?

Raspberry Piを使う上で面倒なことの一つにOSをSDカードに焼くという作業がありました。いつもmacOS標準のターミナルからコマンドを打ったりしてたんですが・・・。

www.raspberrypi.org

ポキオ Raspberry Pi Imager

いつの間にか各OS向けにRaspberry Pi Imagerなるかんたんアプリが登場していましたー。

早速インストール

macOS向けのアプリはdmgファイルになっているので、とりあえずDL。開くとドロップアンドドロップでインストールできるタイプのアプリになっています。

ポキオ Raspberry Pi Imager

さっそくアプリを起動すると、こんな画面。OSと出力先のSDカードを選択できるボタン、それと実行ボタンが用意されているだけのシンプルなアプリになっています。

ポキオ Raspberry Pi Imager

すごいのは、いわゆるメジャーなRaspberry Pi OSだけでなくUbuntuなど他のOSも選べるようになっています。すごい便利ですねー。

ポキオ Raspberry Pi Imager

とりあえずRaspberry Pi OSを選択して書き込み開始。Verifyまで自動でやってくれます。

ポキオ Raspberry Pi Imager

焼くのにかかる時間はあまり変わらないものの、焼く作業が格段にかんたんになりました。そんなに頻繁にOSを書くことはありませんが、あると便利なアプリですねー。

livedoor天気のAPIが終了してしまった話

突然の死。

ポキオ livedoor 天気 API Node-RED

サービス・エンドは突然に

2年ほど前に作った天気予報ガジェット、テレワーク中も自宅で頑張って稼働していました。

relativelayout.hatenablog.com

しかし、今月頭に何故か動作していないことに気づきました。

そうなんですよ、情報元であるlivedoor天気がサービス終了してまして・・・。

help.livedoor.com

目の前が真っ暗になりました。

というのも

Node-REDユーザーグループの一員として執筆させていただいた本でバリバリlivedoor天気のAPIについて触れてたんですよね…。

読者の皆さん、本当にごめんなさい…。とりあえず、そのあたりの経緯を先日の出版記念イベントで話させていただきました。

ポキオ livedoor 天気 API Node-RED

node-red.connpass.com

speakerdeck.com

私以外にもサービス終了の話をされてる方がいたので強い心で生きていこうと思いました(笑)

さて、どうするか

livedoor天気のAPIを失った今、とりあえずバックアップで作っていた気象庁データの活用でなんとか凌ぐことに。

ポキオ livedoor 天気 API Node-RED

一応、下記ブランチにて公開中です。

github.com

また、livedoor天気予報APIをつかったNode-RED向けノードですが、公開を停止させていただきました。

ポキオ livedoor 天気 API Node-RED

ソースコードGithubで引き続き公開しています。

github.com

さてさて

皆々様にはご迷惑をおかけしておりますが、今後も便利なノードを作っていきたい所存です。何卒よろしくお願いいたしますますます。

この投稿をInstagramで見る

気象庁のデータで復活。

pokio(@pokiiiiio)がシェアした投稿 -

人生初のHHKB購入

ハッピー?

ポキオ HHKB Lite2

出来心で購入

マツコの知らない世界でキーボード特集をやっていた影響もあって、どうせテレワークだしキーボードでも買ったろうっていう機運が高まってきてたわけなんですが。

ポキオ HHKB Lite2

AmazonのセールでLite2が3800円だったので、つい購入してしましました。macOS用だしUS配列だし、試しに使ってみようかなと思って、ね。

とりあえず使ってみる

予定より早く届いてしまったので、早速開梱。

ポキオ HHKB Lite2

USB接続タイプで、ケーブルは本体から直接生えてるタイプ。キーボード側にUSBポートが2つ付いてるので、秒でLogicoolのUnifying差し込み余裕でした。いやー、パット見普通じゃん?って思いながら、実際に仕事で使ってみました。

ポキオ HHKB Lite2

あえてネガティブな点から言えば、コイツら。バックスラッシュとバッククオートの場所が変。特にバッククオート。おまえ、左側の隅が定位置じゃなかったのかな?って小一時間問い詰めたくなりますが、これはHHKBなんだなと思い体をこれから慣らしていこうと思います。

あとは、打鍵音。控えめに言ってもちょっとにぎやかですね。どうせテレワークで、一人でタイピングするので、まぁうるさくてもいいです。HHKBの独特なキー配列に早くなれて、カタカタカタタッターンってやってみたいものです。

ポキオ HHKB Lite2

とはいえ、3800円でHHKBの雰囲気が味わえればなによりです。キーの押し具合も嫌いじゃないです。ControlもAの左で調子いいですし。矢印キーもついてて素人にはぴったりです。まだまだ使い始めたばかりですが、このキーボードになれてきたら、高級HHKBにも手を出してみようかなぁ。

PFU HHKB Professional BT 英語配列/墨 PD-KB600B

PFU HHKB Professional BT 英語配列/墨 PD-KB600B

  • 発売日: 2016/04/12
  • メディア: Personal Computers

Node-REDとESP32で神奈川県内のCovid-19の新規陽性者数を表示してみる

オープンデータ様様。

ポキオ Covid-19 Node-RED ESP32

tl;dr

  • Githubや神奈川県HPでCovid-19関係のデータが公開されている
  • Node-REDでそれらをパースする仕組みを作りWebAPI化
  • それをESP32から叩いてOLEDに表示
  • 陽性者数だけ見てていいのかは分からない

コロナ禍

テレビを付けると、嫌でもコロナの話題が耳に入ってくるんですが、たいてい東京の新規陽性者数の話題ばっかりでちょっとうんざり。自分が住んでる都道府県はどうなのか気になるのですが、どうしてもピーキーなデータが出てる部分ばかり報道されるんですよねぇ。

というわけで今回は、私が住んでいる神奈川県の新規陽性者数のデイリーデータを表示できるガジェットを作ってみました。

データはどこから?

ざっくりググると、以下の2ヶ所から取れそう。

Github

kaz-ogiwara氏がまとめている下記のリポジトリで、国内の詳細なデータが公開されている。

github.com

特に、都道府県別のデータはこちらのCSVファイルで取得可能。検査数と陽性者数を得ることができます。

covid19/prefectures.csv at master · kaz-ogiwara/covid19 · GitHub

神奈川県HP

こちらは神奈川県が独自に公開しているデータ。

www.pref.kanagawa.jp

こちらもCSVデータで取得可能だが、陽性者のひとりひとりの属性(性別や年代)も取得可能。今回は使ってませんが、これはこれで可視化できると楽しそう。

CSVをNode-REDで取得してみる

さて、先程のCSVをNode-REDで取得してみます。

ポキオ Covid-19 Node-RED ESP32

普通にHTTP-GETで文字列を取得して、CSVノードでJavaScriptのObjectデータに変換すれば便利に使えそうです。ただし、神奈川県のデータは日本語が含まれていてShift-JISでエンコードされているため、HTTP-GETでバイナリバッファとして取得し、その後node-red-contrib-iconvで変換してからCSVノードに渡しています。あとはよしなーにパースするだけ。

ポキオ Covid-19 Node-RED ESP32

とりあえず、神奈川県のデータを使って、WebAPI化してみました。

これをESP32から叩いてみる

このWebAPIを前回触ったOLED付きのESP32から叩いてみます。

relativelayout.hatenablog.com

表示内容はNode-RED側ですべて準備してしまうので、ESP32側はHTTP-GETしてそれをそのまま表示するだけです。

#include <SSD1306.h>
#include <WiFi.h>
#include <HTTPClient.h>

#define SSID "xxxxx"
#define PASSWORD "xxxxx"
#define URL "xxxxx"
#define DELAY 5 * 60 * 1000

SSD1306 display(0x3c, 5, 4);

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println();

  connectWiFi();

  display.init();
  display.setFont(ArialMT_Plain_16);
}

void loop() {
  show(get());
  delay(DELAY);
}

void connectWiFi() {
  WiFi.begin(SSID, PASSWORD);
  Serial.print("connecting...");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }
  Serial.print("connected : ");
  Serial.println(WiFi.localIP());
}

String get() {
  HTTPClient http;
  http.begin(URL);
  int httpCode = http.GET();

  if (httpCode != HTTP_CODE_OK) {
    Serial.println("http-get failed.");
    return "http-get failed.";
  }

  String body = http.getString();
  Serial.println(body);
  return body;
}

void show(String message) {
  display.clear();
  display.setTextAlignment(TEXT_ALIGN_CENTER);
  display.drawString(64, 4, message);
  display.display();
}

これを実行すると・・・

ポキオ Covid-19 Node-RED ESP32

おー。表示できました。今日は33人のようですねぇ。

これで陽性者をウォッチできますね!

まだまだ神奈川県の陽性者数は少ないようですが、気を抜けませんねぇ。

あとは、陽性者数だけ見てても良いわけはなくて、陽性率だったり重症患者数、医療体制の逼迫具合なども勘案して総合的に判断していきたいものです。それらもよしなにNode-REDのダッシュボードなどで可視化できるといいですねぇ。

Node-REDのフローはこちら

[
    {
        "id": "e16d9838.5519a",
        "type": "tab",
        "label": "Covid-19",
        "disabled": false,
        "info": ""
    },
    {
        "id": "6092239b.0dbd9c",
        "type": "inject",
        "z": "e16d9838.5519a",
        "name": "Githubから取得",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 180,
        "y": 80,
        "wires": [
            [
                "48ec884a.e861f"
            ]
        ]
    },
    {
        "id": "48ec884a.e861f",
        "type": "http request",
        "z": "e16d9838.5519a",
        "name": "",
        "method": "GET",
        "ret": "txt",
        "paytoqs": false,
        "url": "https://raw.githubusercontent.com/kaz-ogiwara/covid19/master/data/prefectures.csv",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 170,
        "y": 120,
        "wires": [
            [
                "82278aeb.234d4"
            ]
        ]
    },
    {
        "id": "82278aeb.234d4",
        "type": "csv",
        "z": "e16d9838.5519a",
        "name": "",
        "sep": ",",
        "hdrin": true,
        "hdrout": "",
        "multi": "mult",
        "ret": "\\n",
        "temp": "",
        "skip": "0",
        "strings": true,
        "x": 150,
        "y": 160,
        "wires": [
            [
                "3b7e8bd6.48c4a4"
            ]
        ]
    },
    {
        "id": "bfd441d3.dbdd18",
        "type": "debug",
        "z": "e16d9838.5519a",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "x": 170,
        "y": 280,
        "wires": []
    },
    {
        "id": "3b7e8bd6.48c4a4",
        "type": "function",
        "z": "e16d9838.5519a",
        "name": "神奈川だけフィルタ",
        "func": "var kanagawaList = [];\n\nmsg.payload.forEach(element => {\n   if(element.prefectureNameE === \"Kanagawa\"){\n       kanagawaList.push(element);\n   } \n});\n\nmsg.payload = kanagawaList;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 200,
        "y": 200,
        "wires": [
            [
                "cbcbd716.db236"
            ]
        ]
    },
    {
        "id": "cbcbd716.db236",
        "type": "function",
        "z": "e16d9838.5519a",
        "name": "最新のデータだけ抽出",
        "func": "var data = msg.payload.slice();\nvar todayData = data[data.length - 1];\nvar yesterdayData = data[data.length - 2];\n\nvar testedPositive = todayData.testedPositive - yesterdayData.testedPositive;\n\nmsg.payload = {};\nmsg.payload.date = todayData.year + \"/\" + todayData.month + \"/\" + todayData.date;\nmsg.payload.testedPositive = testedPositive;\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 200,
        "y": 240,
        "wires": [
            [
                "bfd441d3.dbdd18"
            ]
        ]
    },
    {
        "id": "e6bec6cf.0856a",
        "type": "http request",
        "z": "e16d9838.5519a",
        "name": "",
        "method": "GET",
        "ret": "bin",
        "paytoqs": false,
        "url": "https://www.pref.kanagawa.jp/osirase/1369/data/csv/patient.csv",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 490,
        "y": 120,
        "wires": [
            [
                "be7264f5.2fbbc"
            ]
        ]
    },
    {
        "id": "1c08950f.ec2513",
        "type": "csv",
        "z": "e16d9838.5519a",
        "name": "",
        "sep": ",",
        "hdrin": true,
        "hdrout": "",
        "multi": "mult",
        "ret": "\\n",
        "temp": "",
        "skip": "0",
        "strings": true,
        "x": 470,
        "y": 200,
        "wires": [
            [
                "c3f97218.b9c768"
            ]
        ]
    },
    {
        "id": "a11490b1.e1df38",
        "type": "inject",
        "z": "e16d9838.5519a",
        "name": "神奈川県HPから取得",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 520,
        "y": 80,
        "wires": [
            [
                "e6bec6cf.0856a"
            ]
        ]
    },
    {
        "id": "2cdc0310.c76f24",
        "type": "debug",
        "z": "e16d9838.5519a",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "x": 490,
        "y": 280,
        "wires": []
    },
    {
        "id": "be7264f5.2fbbc",
        "type": "converter",
        "z": "e16d9838.5519a",
        "name": "Shift_JIS変換",
        "from": "Shift_JIS",
        "x": 500,
        "y": 160,
        "wires": [
            [
                "1c08950f.ec2513"
            ]
        ]
    },
    {
        "id": "c3f97218.b9c768",
        "type": "function",
        "z": "e16d9838.5519a",
        "name": "最新のデータだけ抽出",
        "func": "var data = msg.payload.slice();\nvar lastData = data[data.length - 1];\nvar lastDate = lastData[\"発表日\"];\n\nvar testedPositive = 0;\ndata.forEach(element => {\n    if(element[\"発表日\"] === lastDate){\n        testedPositive++;\n    }\n})\n\nmsg.payload = {};\nmsg.payload.date = lastDate.split('-').join('/').split('/0').join('/');\nmsg.payload.testedPositive = testedPositive;\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 520,
        "y": 240,
        "wires": [
            [
                "2cdc0310.c76f24",
                "f5d3f833.c57c78"
            ]
        ]
    },
    {
        "id": "6ee2fb46.fcd2ec",
        "type": "http in",
        "z": "e16d9838.5519a",
        "name": "",
        "url": "/covid",
        "method": "get",
        "upload": false,
        "swaggerDoc": "",
        "x": 160,
        "y": 440,
        "wires": [
            [
                "e6bec6cf.0856a"
            ]
        ]
    },
    {
        "id": "eccb91a6.147fb8",
        "type": "http response",
        "z": "e16d9838.5519a",
        "name": "",
        "statusCode": "",
        "headers": {},
        "x": 590,
        "y": 440,
        "wires": []
    },
    {
        "id": "f5d3f833.c57c78",
        "type": "function",
        "z": "e16d9838.5519a",
        "name": "レスポンス作成",
        "func": "var message = '';\n\nmessage += 'Positive Tests\\nReported : ';\nmessage += msg.payload.testedPositive + '\\n';\nmessage += '( ' + msg.payload.date + ' )'\n\n\nmsg.payload = message;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 380,
        "y": 440,
        "wires": [
            [
                "eccb91a6.147fb8"
            ]
        ]
    }
]

リングフィットアドベンチャーをついに全面クリア?(ネタバレあり)

ガッキーになれるかな。

ポキオ リングフィットアドベンチャー

ようやく

去年末に買ったリングフィットアドベンチャーですが、ようやく全面クリアができたっぽいです。

relativelayout.hatenablog.com

リングフィット アドベンチャー -Switch

リングフィット アドベンチャー -Switch

  • 発売日: 2019/10/18
  • メディア: Video Game

途中、サボった期間もありましたが、おおよそ90日くらいでクリアできました。

ポキオ リングフィットアドベンチャー

84kmも(しか?)走ってました。

生活のリズムとリングフィットアドベンチャー

一時期サボりがちになってしまったわけですが、幸か不幸かテレワークのおかげで「業務開始前に20分程度体を動かす」という日課として定着できたので、なんとかここまでやってこれました。

毎朝の日課として定着できたので、

  • コンスタントに続けられる
  • 雨でも運動できる
  • 汗をかくので必ず着替えざるを得ない(パジャマで仕事することがない)
  • 「さぁ、仕事するぞー」みたいな感じで気持ちの切り替えができる

こんな感じで良さみを感じています。また、コンスタントに続けてこれたこともあり、

  • 胸筋・腹筋がついてきた気がする(個人の感想です)
  • 腹筋系の技が楽にできるようになった
  • 自転車に乗ってるときの安定感が増した(ダンシングが楽になった)

みたいな感じで、着実に結果を体感できています。ただし、シックスパックはまだまだこれからのようで、お腹の厚い脂肪はまだクリアできてません・・・(笑)

あれ、全クリじゃない?

という感じで、リングフィットアドベンチャーは非常に良い日課として定着してるんですが・・・。

ラスボスであるドラゴを倒したので、これで全クリかと思ってたんですが。

エクストラフィットネスと言う名の裏面がUnlockされただけで、まだまだ筋トレ生活は続きそうです・・・(笑)

ポキオ リングフィットアドベンチャー

いやー、でも逆に言えば、長い期間に渡って楽しめるという意味では、よくできたゲームだなぁとつくづくと。本当の全クリを目指してがんばります。

Wemos Lolin ESP32 OLEDのケースを作ってみた

まずは形から。

ポキオ Wemos Lolin ESP32 OLED

今回はESP32系ボード

Wemos Lolin ESP32 OLEDって、文字通りESP32が乗ったボードなんですが、OLEDディスプレイもついているので、色々と面白そうなやつなんです。

ポキオ Wemos Lolin ESP32 OLED

今回は、こいつのケースを3Dプリンターで作ってみようと思います。

Fusion360でモデル作成

ノギスでボードのサイズを測りながら、Fusion360でモデルを作っていきます。

ポキオ Wemos Lolin ESP32 OLED

排熱を考えて、少し隙間を多めにとっています。また、サポート材が要らないようなシンプルな形で作っていきます。

ポキオ Wemos Lolin ESP32 OLED

モデルをCuraに渡して、3Dプリンタ用のファイルを作成。ここまではいい感じ。

印刷するよー

3Dプリンターは、この前買ったAnycubic Mega-S。

ポキオ Wemos Lolin ESP32 OLED

だんだん慣れてきました。フィラメントも防湿庫で管理しているせいか、特に劣化してる感じもなく調子いい感じです。

ポキオ Wemos Lolin ESP32 OLED

とりあえず下側作成完了。いい感じですね。続いて、蓋も作成。

ポキオ Wemos Lolin ESP32 OLED

ESP32部分は排熱を考慮して、あみあみにしてみました。

この投稿をInstagramで見る

ケースできたお。 #lolin32 #esp32 #anycubic #anycubicmegas

pokio(@pokiiiiio)がシェアした投稿 -

いざ実装

ケースができたので、早速動かしてみます。

ポキオ Wemos Lolin ESP32 OLED

こんな感じでコーディングします。

#include "SSD1306.h"
SSD1306  display(0x3c, 5, 4);

void setup() {
  display.init();
  display.setFont(ArialMT_Plain_24);
  display.setTextAlignment(TEXT_ALIGN_CENTER);
  display.drawString(64, 0, "Hello!");
  display.drawString(64, 20 - 4, "My name is");
  display.drawString(64, 40 - 4, "@pokiiio");
  display.display();
}

void loop() {
}

フォントも英数だけですが準備されているのでかんたんに文字表示可能です。

今回はWi-Fi/BLEを使っていない実装ですが、表示内容を動的に変えられると楽しそうですよねー。

「Androidのメモとか」は、Amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、Amazonアソシエイト・プログラムの参加者です。

このブログは個人的なメモ書きであったり、考えを書く場所であります。執筆者の所属する団体や企業のコメントや意向とは無関係であります。また、このブログは必ずしも正しいことが書かれているとは限らず、誤字脱字や意図せず誤った情報を載せる場合がありえます。それが原因で読者が不利益を被ったとしても、執筆者はいかなる責任も負いません。ありがとうございます。