Hobby Science&Experiment

愛と工作の日々

趣味でやっている工作や勉強したことのメモ書きです。

Switchbot温度計とラズパイで快適IoTライフ

Switchbot温度計をフル活用するためラズパイで測定値を取得することを試みます。意外と情報が少なく苦戦したため記録しておきます。

Switchbot温湿度計

ボタンプッシュロボットでおなじみのWonderlabs IncによるIoT温湿度計です。電源も電池駆動なので使い勝手が良く、スマートハウス利用には無限の可能性を感じます。

同社が発売中のHubを利用すればAlexaやその他の家電と連携させることも可能です。私はHubが必要と知らずに1年前に買ってショックを受けました…。単体でもBluetoothスマホアプリと連携可能ですが、アプリの出来があんまり良くないです。
私はケチな上ラズパイを中心にスマートホームを組みたいと考えているため、Hubを使わない方法を模索します。

うまくいかなかったコード

検索すると一番にヒットするこちらの記事の方法は、私の環境では上手く行きませんでした・・・。少しコードをいじったりしてみたのですが、小手先の修正では動かず・・・。原因が分かったら報告します。
qiita.com

実行結果

$ sudo python3  switchbot_meter.py
^CTraceback (most recent call last):
  File "switchbot_meter.py", line 48, in <module>
    scanner.scan( 0 )
  File "/usr/local/lib/python3.5/dist-packages/bluepy/btle.py", line 853, in scan
    self.process(timeout)
  File "/usr/local/lib/python3.5/dist-packages/bluepy/btle.py", line 821, in process
    resp = self._waitResp(['scan', 'stat'], remain)
  File "/usr/local/lib/python3.5/dist-packages/bluepy/btle.py", line 347, in _waitResp
    rv = self._helper.stdout.readline()
KeyboardInterrupt

実行すると固まってしまいますので、キーボードインタラプトしてます。スキャンのタイムアウトが無効になっているため、いつまでもスキャンを続けてるみたいです。デバイスはラズパイからは見えているのに、なんでスキャンが終わらないんだろう。
結局ギブアップして、別の方法を探しました。

とりあえず動いたコード

諦め掛けていたのですが、スイッチボット開発元のWonderLabsが公開しているpython-hostというライブラリの中にそれっぽいのを見つけました。
GitHub - OpenWonderLabs/python-host: The python code running on Raspberry Pi or other Linux based boards to control SwitchBot.
f:id:tara-chang:20200615215728p:plain
インストール方法はリンク先のgithubに詳しく書いてあります。このライブラリは、ボタンタイプのスイッチボットでも使えるやつなので、私はすでにインストールしてました。
インストールが済んだら早速ディレクトリに移動し、 switchbot_meter.pyを実行してみます。MACアドレスを入れる所がないのですが、とりあえず実行。

~$ cd python-host
~/python-host $ sudo python switchbot_meter.py

結果

Usage: "sudo python switchbot.py [mac_addr  cmd]" or "sudo python switchbot.py"
Start scanning...
22 16b Service Data 000d5410d6089a40 16
scan timeout
(0, [u'XX:XX:XX:XX:XX:XX', "Humiture:26.8'C 64%"])
Input the device number to control:

おお、温度と湿度が取得できました。私のスイッチボットの表示温度、MACアドレスの値が表示されてます!ちなみにpython3のコード(switchbot_meter_py3.py)を実行する際には、pip3でpexpectのインストールが必要でした(No module named 'pexpect’ というエラーが出る)。
「Input the device number to control:」の表示でデバイスナンバーの入力を求められましたので、0を入れると、処理が先に進みました。

Input the device number to control:0
Preparing to connect.
Connection successful.
Traceback (most recent call last):
  File "switchbot_meter.py", line 232, in <module>
    main()
  File "switchbot_meter.py", line 222, in main
    trigger_device(bluetooth_adr)
  File "switchbot_meter.py", line 167, in trigger_device
    tempFra = int(data[3], 16) / 10.0
IndexError: string index out of range

コネクション成功、その後よくわからないエラーで止まってしまいました。デバイスナンバーに関してはおそらく複数台のスイッチボットを一括で管理することを想定しているのではないでしょうか。今回は1台ですので、0以上の数字はレンジアウトとなります。
とりあえず温度と湿度をラズパイで取得する、という目的はこれで達せられそうです。返り値がシンプルになるようにコードを書き換えます。

とりあえず動いたコードを修正

やっつけではありますがシンプルに温度と湿度とアドレスだけ返してくれるように修正しました。ライブラリが揃っていればpython2でも3でも動くと思います。

#!/usr/bin/env python3
import pexpect
import sys
from bluepy.btle import Scanner, DefaultDelegate
import binascii

class ScanDelegate(DefaultDelegate):
    def __init__(self):
        DefaultDelegate.__init__(self)

class DevScanner(DefaultDelegate):
    def __init__( self ):
        DefaultDelegate.__init__(self)
        #print("Scanner inited")

    def dongle_start(self):
        self.con = pexpect.spawn('hciconfig hci0 up')
        time.sleep(1)

    def dongle_restart(self):
        print("restart bluetooth dongle")
        self.con = pexpect.spawn('hciconfig hci0 down')
        time.sleep(3)
        self.con = pexpect.spawn('hciconfig hci0 up')
        time.sleep(3)

    def scan_loop(self):
        # service_uuid = '1bc5d5a50200b89fe6114d22000da2cb'
        service_uuid = 'cba20d00-224d-11e6-9fb8-0002a5d5c51b'
        menufacturer_id = '5900f46d2c8a5f31'
        dev_list =[]
        bot_list =[]
        enc_list =[]
        link_list =[]
        meter_list=[]
        self.con = pexpect.spawn('hciconfig')
        pnum = self.con.expect(["hci0",pexpect.EOF,pexpect.TIMEOUT])
        if pnum==0:
            self.con = pexpect.spawn('hcitool lescan')
            #self.con.expect('LE Scan ...', timeout=5)
            scanner = Scanner().withDelegate(DevScanner())
            devices = scanner.scan(10.0)
            print("Start scanning...")
        else:
            raise Error("no bluetooth error")

        for dev in devices:
            mac = 0
            for (adtype, desc, value) in dev.getScanData():
                # print(adtype,desc,value)
                if desc == '16b Service Data':
                    model = binascii.a2b_hex(value[4:6])
                    mode  = binascii.a2b_hex(value[6:8])
                    if len(value) == 16:
                        # print(adtype,desc,value,len(value))
                        # celsius
                        tempFra = int(value[11:12].encode('utf-8'), 16) / 10.0
                        tempInt = int(value[12:14].encode('utf-8'), 16)
                        if tempInt < 128:
                            tempInt *= -1
                            tempFra *= -1
                        else:
                            tempInt -= 128
                        meterTemp = tempInt + tempFra
                        meterHumi = int(value[14:16].encode('utf-8'), 16) % 128
                        # print("meter:", meterTemp, meterHumi)
                    else:
                        meterTemp = 0
                        meterHumi = 0
                elif desc == 'Local name':
                    if value == "WoHand":
                        mac   = dev.addr
                        model = 'H'
                        mode  = 0
                    elif value == "WoMeter":
                        mac   = dev.addr
                        model = 'T'
                        mode = 0
                elif desc == 'Complete 128b Services' and value == service_uuid :
                    mac = dev.addr

            if mac != 0 :
                #print(binascii.b2a_hex(model),binascii.b2a_hex(mode))
                dev_list.append([mac, model.decode('utf-8'), mode, meterTemp, meterHumi])

        # print(dev_list)
        for (mac, dev_type,mode,meterTemp, meterHumi) in dev_list:
            if dev_type == 'L':
                link_list.append(mac)
            if dev_type == 'H'  or ord(dev_type) == ord('L') + 128:
                #print(int(binascii.b2a_hex(mode),16))
                if int(binascii.b2a_hex(mode),16) > 127 :
                    bot_list.append([mac,"Turn On"])
                    bot_list.append([mac,"Turn Off"])
                    bot_list.append([mac,"Up"])
                    bot_list.append([mac,"Down"])
                else :
                    bot_list.append([mac,"Press"])
            elif dev_type == 'T':
                meter_list.append([mac, meterTemp, meterHumi])
#                meter_list = {"a":"1", "b":meterHumi}
            if ord(dev_type) == ord('L') + 128:
                enc_list.append([mac,"Press"])
        # print(bot_list)
        #print("Scan timeout.")
        return bot_list + meter_list
        pass

    def register_cb( self, fn ):
        self.cb=fn
        return

    def close(self):
        #self.con.sendcontrol('c')
        self.con.close(force=True)

def main():
    #Check bluetooth dongle
    #print('Usage: "sudo python switchbot.py [mac_addr  cmd]" or "sudo python switchbot.py"')
    connect = pexpect.spawn('hciconfig')
    pnum = connect.expect(["hci0",pexpect.EOF,pexpect.TIMEOUT])
    if pnum!=0:
        print('No bluetooth hardware, exit now')
        sys.exit()
    connect = pexpect.spawn('hciconfig hci0 up')

    if len(sys.argv) == 3 or len(sys.argv) == 4:
        dev = sys.argv[1]
        act = sys.argv[2] if len(sys.argv) < 4  else  ('Turn ' + sys.argv[3] )
        #trigger_device([dev,act])

    elif len(sys.argv) == 1:
        #Start scanning...
        scan = DevScanner()
        dev_list = scan.scan_loop()
        print(dev_list)
        dev = sys.argv[1] if len(sys.argv) > 1 else None
        dev_number = None

        if not dev_list:
            print("No SwitchBot nearby, exit")
            sys.exit()

    else :
        print('wrong cmd.')
        print('Usage: "sudo python switchbot_meter_py3.py [mac_addr  cmd]" or "sudo python switchbot_meter_py3.py"')

    #connect = pexpect.spawn('hciconfig')
    return(dev_list)
    #sys.exit()
if __name__ == "__main__":
    main()

実行結果

~/python-host $ sudo python3 SBHumiture.py
Start scanning...
[['XX:XX:XX:XX:XX:XX', 27.0, 65]]

色々余分なコードも残ってそうですが、とりあえず所望の値が帰ってきました。スキャンに失敗した際は"No SwitchBot nearby, exit"と表示されます。
毎回スキャンするのではなくアドレス指定でコマンドした方がシンプルだし省エネのような気がしますが、そこまでは出来ませんでした・・・。もし出来たら載せます!

ESP32-WROOM-32DでDS18B20のアドレスを取得する

DS18B20はi-wire通信でデータを取得できる温度センサーです。防水プローブを入手したので、早速ESP32ボードで遊んでみたいと思います。アドレスの取得は必須ではありませんが、後々センサーの並列動作などに必要になるのでやっておきましょう!
こちらの方法を参考にします。Arduinoでのチュートリアルになってますが、ESP32ボードでも流用できました。
henrysbench.capnfatz.com

センサー入手

AmazonのVKLSVANなる出品者から399円で購入しました。レビューがないのが気になりますし、単価がもう少し安いのもありましたが、とりあえず単品で安いのが欲しかったのでこちらで購入。

防水性はなかなかしっかりしてそうです。Amazonではホワイト(DATA / MCUと書いてますが、実際は黄色のようです。
f:id:tara-chang:20200606000917p:plain

配線

以下の表のように配線しました。データ線は33pinに繋いでます(26pinでは何故かだめでした)。DATAケーブルとVCCケーブルの間に4.7kΩの抵抗を挿入しました。

DS18B20側 ESP32側
GND
GND
DATA(黄色ケーブル))
33番ピン(IO21)
VCC(赤ケーブル)
5Vピン

※pin nameとNo.の関係はこちらに詳しいです。

写真も載せておきます。
f:id:tara-chang:20200606001828p:plain

コード

pin番号部分(3行目)を33に変更。後は完全に参考リンクのまんまで動きました。

#include <OneWire.h>

OneWire  ds(33);  // This is where DQ of your DS18B20 will connect.

void setup(void) {
  Serial.begin(9600);
  getDeviceAddress();
}

void getDeviceAddress(void) {
  byte i;
  byte addr[8];
  
  Serial.println("Getting the address...\n\r");
  /* initiate a search for the OneWire object we created and read its value into
  addr array we declared above*/
//http://henrysbench.capnfatz.com/henrys-bench/arduino-temperature-measurements/ds18b20-arduino-user-manual-introduction-and-contents/ds18b20-user-manual-part-2-getting-the-device-address/  
  while(ds.search(addr)) {
    Serial.print("The address is:\t");
    //read each byte in the address array
    for( i = 0; i < 8; i++) {
      Serial.print("0x");
      if (addr[i] < 16) {
        Serial.print('0');
      }
      // print each byte in the address array in hex format
      Serial.print(addr[i], HEX);
      if (i < 7) {
        Serial.print(", ");
      }
    }
    // a check to make sure that what we read is correct.
    if ( OneWire::crc8( addr, 7) != addr[7]) {
        Serial.print("CRC is not valid!\n");
        return;
    }
  }
  ds.reset_search();
  return;
}

void loop(void) {
  // do nothing
}

結果

アドレスが表示されました!この固体に関しては0x28, 0x7E, 0x98, 0x79, 0xA2, 0x01, 0x03, 0xB8がアドレスとなります。
f:id:tara-chang:20200605234706p:plain

ESP-WROOM-32D開発ボードでSwitchbotを動作させる

ESP-WROOM-32D開発ボードとSwitchbotを動かしてみたいと思います。
前回のコードからM5StickC関係の部分を削除するだけで動作しました。
並列処理はまだ使い方が分からないので、スライドスイッチを利用してPIN 25がONであればスイッチボットが10秒に一回動作するように設定します。

配線

ESP-WROOM-32D開発ボードの25番ピンとGNDを接続すると、PIN25=ONと判断されスイッチボットが動作するようにします。なのでこの間にスライドスイッチを差し込んでおきます。
スライドスイッチがONである間は、10秒に一回スイッチボットへ動作指令が飛ぶようなプログラムになってます。

コード

static String MAC_SWITCHBOT = "xx:xx:xx:xx:xx:xx";のxxの部分にスイッチボットのMACアドレスを入力してください。

//#include <M5StickC.h>
#include <driver/i2s.h>
#include "BLEDevice.h"

//追加
#define BTN_A_PIN 34
#define BTN_B_PIN 18
#define LED_PIN   10

// このLEDは、GPIO10の電位を下げることで発光するタイプ
#define LED_ON  LOW
#define LED_OFF HIGH

// INPUT_PULLUPが有効かは不明だが、有効という前提で定義
#define BTN_ON  LOW
#define BTN_OFF HIGH

uint8_t prev_btn_a = BTN_OFF;
uint8_t btn_a      = BTN_OFF;

// このLEDは、GPIO10の電位を下げることで発光するタイプ
#define LED_ON  LOW
#define LED_OFF HIGH

// INPUT_PULLUPが有効かは不明だが、有効という前提で定義
#define BTN_ON  LOW
#define BTN_OFF HIGH

// 手元のSwitchBotのMACアドレス
static String MAC_SWITCHBOT = "C3:8D:26:47:8A:55";

// SwitchBotのBLE情報
static BLEUUID SERV_SWITCHBOT("cba20d00-224d-11e6-9fb8-0002a5d5c51b");
static BLEUUID CHAR_SWITCHBOT("cba20002-224d-11e6-9fb8-0002a5d5c51b");
// SwitchBot へ送信するコマンド
// アームを倒し、引く動作の場合 {0x57, 0x01, 0x00}
// 以下2つはスマートフォンアプリでモードを変える必要あり
// アームを倒す動作の場合 {0x57, 0x01, 0x01}
// アームを引く動作の場合 {0x57, 0x01, 0x02}
static uint8_t cmdPress[3] = {0x57, 0x01, 0x01};

#define PIN_CLK  0
#define PIN_DATA 34
#define READ_LEN (2 * 1024)
#define SAMPLING_FREQUENCY 44100

uint8_t BUFFER[READ_LEN] = {0};
uint16_t *adcBuffer = NULL;
//const uint16_t FFTsamples = 256;  // サンプル数は2のべき乗//消去
//double vReal[FFTsamples];  // vReal[]にサンプリングしたデーターを入れる//消去
//double vImag[FFTsamples];//消去
//arduinoFFT FFT = arduinoFFT(vReal, vImag, FFTsamples, SAMPLING_FREQUENCY);  // FFTオブジェクトを作る//消去

bool doScan = true;
BLEScan* pBLEScan;
static BLEAddress *pGattServerAddress;
static BLEAdvertisedDevice* myDevice;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEClient*  pClient = NULL;

unsigned int sampling_period_us;
float dmax = 10000.0;

bool doDetection = false;
bool sendFlg = false;
bool cas = false;


//int printCount = 0;

void log(String s) {
  Serial.println(s);
}


// BLEへの接続 コールバック
class MyClientCallback : public BLEClientCallbacks {
    void onConnect(BLEClient* pclient) {
      Serial.println("onConnect");
      //    log("cc conn");
    }
    void onDisconnect(BLEClient* pclient) {
      Serial.println("onDisconnect");
      //    log("dis");
      //    doScan = true;
      //    doDetection = false;
      if (cas) {
        esp_restart();
      }
    }
};

// アドバタイズ検出時のコールバック
class advdCallback: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
      if (advertisedDevice.haveServiceUUID()) {
        String addr = advertisedDevice.getAddress().toString().c_str();
        Serial.printf("It have service addr: %s \n", advertisedDevice.getAddress().toString().c_str());
        if (addr.equalsIgnoreCase(MAC_SWITCHBOT)) {
          // SwitchBot を発見
          //        if (advertisedDevice.getServiceUUID().equals(SERV_SWITCHBOT)) {
          log("found");

          advertisedDevice.getScan()->stop();
          pGattServerAddress = new BLEAddress(advertisedDevice.getAddress());
          myDevice = new BLEAdvertisedDevice(advertisedDevice);

          doScan = false;
          doDetection = true;
//          m5LED(500, 2);
        }
      }
    }
};


void setup() {
  Serial.begin(115200);
  pinMode(BTN_A_PIN, INPUT_PULLUP);//追加

  log("Start");

  // BLE 初期化
  BLEDevice::init("");
  // デバイスからのアドバタイズをスキャン
  pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new advdCallback());
  pBLEScan->setActiveScan(true);
  pBLEScan->setInterval(100);
  pBLEScan->setWindow(99);  // less or equal setInterval value
}

void loop() {
  btn_a = digitalRead(BTN_A_PIN);//追加
  if((btn_a == BTN_ON)){//追加   

    if (doScan) {
      log("Scan");
      pBLEScan->start(5, false);
    }

    if(doScan == false){
      digitalWrite(LED_PIN, LED_ON);
     //digitalWrite(LED_PIN, LED_OFF);
      
      if (connectAndSendCommand(*pGattServerAddress)) {
        log("Done!");
        sendFlg = false;
        doScan = false;
        delay(10000);
      } else {
        log("Failed.");
        doScan = true;
        delay(3000);
      }
    }
  }
  vTaskDelay(500 / portTICK_RATE_MS);
}

// SwitchBot の GATT サーバへ接続 ~ Press コマンド送信
static bool connectAndSendCommand(BLEAddress pAddress) {
  cas = true;
  try {
    pClient = BLEDevice::createClient();

    pClient->setClientCallbacks(new MyClientCallback());

    log("Connecting");
    while (!pClient->connect(myDevice)) {
      log("reconnect");
      delay(1000);
    }

    // 対象サービスを得る
    BLERemoteService* pRemoteService = pClient->getService(SERV_SWITCHBOT);
    if (pRemoteService == nullptr) {
      log("e:service not found");
      return false;
    }

    // 対象キャラクタリスティックを得る
    pRemoteCharacteristic = pRemoteService->getCharacteristic(CHAR_SWITCHBOT);
    if (pRemoteCharacteristic == nullptr) {
      log("e:characteristic not found");
      return false;
    }

    // キャラクタリスティックに Press コマンドを書き込む
    pRemoteCharacteristic->writeValue(cmdPress, sizeof(cmdPress), false);
    //    pRemoteCharacteristic->writeValue("test", true);
    log("Send");

    cas = false;

    delay(3000);
    pClient->disconnect();
    pClient = NULL;
    delay(1000);
  }
  catch (...) {
    log("error");
    if (pClient) {
      pClient->disconnect();
      pClient = NULL;
    }
    return false;
  }

  return true;
}

動作

M5StickCのボタンでSwitchbotを操作する

M5StickCのLチカに昨日成功ばかりの新参者ですが、本来の目的であるSwitchbotの操作に取り掛かって行きます。
M5StickC(ESP32チップ)でSwitchbotを操作しようと検索し、以下の2件を発見できました。
qiita.com
dsas.blog.klab.org
一件目はなんとか動作しました。しかし不安定というか、動作したりしなかったり、数十秒止まったりしてしまうようでした。
二件目は使い方が間違っているのかまったく動作しませんでした。シリアルモニタを見ると、文字化けしたテキストが延々吐き出されていました…。
コードを見比べるとキモ(スイッチボット操作)の部分は似通っており、二件目のコードをベースに一件目は作成されたようした。
接続に関する改良が図られているようなので、その点が効いていたのかもしれません。
f:id:tara-chang:20200531183926p:plain

しかし音をトリガーにするのではなく純粋にスイッチボットをON/OFFするコードがまず必要でしたので、動作した一件目をベースに改造することにしました。
私はC言語の経験と知識がほとんどなかったのでだいぶハードルが高かったのですが、
前述一件目のコードと以下のコードをかなり雑に合体させてとりあえず所望の動作を得ることが出来たので、記録しておきます。
M5StickCであそぶ 〜ボタンとLEDを使う〜 | MUDAなことをしよう。

コード①

M5ボタンを押すと(つまりPIN 37がON)スイッチボットが1回動作するようになってます。
static String MAC_SWITCHBOT = "xx:xx:xx:xx:xx:xx";のxxの部分にMACアドレスを入力してください。

#include <M5StickC.h>
#include <driver/i2s.h>
#include "BLEDevice.h"

//追加
#define BTN_A_PIN 37
#define BTN_B_PIN 39
#define LED_PIN   10

// このLEDは、GPIO10の電位を下げることで発光するタイプ
#define LED_ON  LOW
#define LED_OFF HIGH

// INPUT_PULLUPが有効かは不明だが、有効という前提で定義
#define BTN_ON  LOW
#define BTN_OFF HIGH

uint8_t prev_btn_a = BTN_OFF;
uint8_t btn_a      = BTN_OFF;
uint8_t prev_btn_b = BTN_OFF;
uint8_t btn_b      = BTN_OFF;

// このLEDは、GPIO10の電位を下げることで発光するタイプ
#define LED_ON  LOW
#define LED_OFF HIGH

// INPUT_PULLUPが有効かは不明だが、有効という前提で定義
#define BTN_ON  LOW
#define BTN_OFF HIGH

// 手元のSwitchBotのMACアドレス
static String MAC_SWITCHBOT = "xx:xx:xx:xx:xx:xx";

// SwitchBotのBLE情報
static BLEUUID SERV_SWITCHBOT("cba20d00-224d-11e6-9fb8-0002a5d5c51b");
static BLEUUID CHAR_SWITCHBOT("cba20002-224d-11e6-9fb8-0002a5d5c51b");
// SwitchBot へ送信するコマンド
// アームを倒し、引く動作の場合 {0x57, 0x01, 0x00}
// 以下2つはスマートフォンアプリでモードを変える必要あり
// アームを倒す動作の場合 {0x57, 0x01, 0x01}
// アームを引く動作の場合 {0x57, 0x01, 0x02}
static uint8_t cmdPress[3] = {0x57, 0x01, 0x01};

#define PIN_CLK  0
#define PIN_DATA 34
#define READ_LEN (2 * 1024)
#define SAMPLING_FREQUENCY 44100

uint8_t BUFFER[READ_LEN] = {0};
uint16_t *adcBuffer = NULL;
//const uint16_t FFTsamples = 256;  // サンプル数は2のべき乗//消去
//double vReal[FFTsamples];  // vReal[]にサンプリングしたデーターを入れる//消去
//double vImag[FFTsamples];//消去
//arduinoFFT FFT = arduinoFFT(vReal, vImag, FFTsamples, SAMPLING_FREQUENCY);  // FFTオブジェクトを作る//消去

bool doScan = true;
BLEScan* pBLEScan;
static BLEAddress *pGattServerAddress;
static BLEAdvertisedDevice* myDevice;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEClient*  pClient = NULL;

unsigned int sampling_period_us;
float dmax = 10000.0;

bool doDetection = false;
bool sendFlg = false;
bool cas = false;


//int printCount = 0;

void log(String s) {
  Serial.println(s);
  M5.Lcd.println(s);
}


// BLEへの接続 コールバック
class MyClientCallback : public BLEClientCallbacks {
    void onConnect(BLEClient* pclient) {
      Serial.println("onConnect");
      //    log("cc conn");
    }
    void onDisconnect(BLEClient* pclient) {
      Serial.println("onDisconnect");
      //    log("dis");
      //    doScan = true;
      //    doDetection = false;
      if (cas) {
        esp_restart();
      }
    }
};

// アドバタイズ検出時のコールバック
class advdCallback: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
      if (advertisedDevice.haveServiceUUID()) {
        String addr = advertisedDevice.getAddress().toString().c_str();
        Serial.printf("It have service addr: %s \n", advertisedDevice.getAddress().toString().c_str());
        if (addr.equalsIgnoreCase(MAC_SWITCHBOT)) {
          // SwitchBot を発見
          //        if (advertisedDevice.getServiceUUID().equals(SERV_SWITCHBOT)) {
          log("found");

          advertisedDevice.getScan()->stop();
          pGattServerAddress = new BLEAddress(advertisedDevice.getAddress());
          myDevice = new BLEAdvertisedDevice(advertisedDevice);

          doScan = false;
          doDetection = true;
//          m5LED(500, 2);
        }
      }
    }
};




void setup() {
  Serial.begin(115200);
  M5.begin();
//  i2sInit();

  pinMode(BTN_A_PIN, INPUT_PULLUP);//追加
  pinMode(BTN_B_PIN, INPUT_PULLUP);//追加
  pinMode(LED_PIN,   OUTPUT);//追加
  digitalWrite(LED_PIN, LED_OFF);//追加 

  //  pinMode(M5_LED, OUTPUT);
  M5.Axp.ScreenBreath(8);    // 画面の輝度を少し下げる
  M5.Lcd.setTextSize(2);    // 画面内文字のサイズを大きく

  log("Start");

  //xTaskCreate(mic_fft_task, "mic_fft_task", 2048, NULL, 1, NULL);//消去

  // BLE 初期化
  BLEDevice::init("");
  // デバイスからのアドバタイズをスキャン
  pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new advdCallback());
  pBLEScan->setActiveScan(true);
  pBLEScan->setInterval(100);
  pBLEScan->setWindow(99);  // less or equal setInterval value
}

void loop() {
 
  btn_a = digitalRead(BTN_A_PIN);//追加

  if (doScan) {
    log("Scan");
    pBLEScan->start(5, false);
  }

//  if (sendFlg == true && doScan == false) {
  if(prev_btn_a == BTN_OFF && btn_a == BTN_ON && doScan == false){//追加

    // log reset
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(0, 0);
    digitalWrite(LED_PIN, LED_ON);
    delay(500);
    digitalWrite(LED_PIN, LED_OFF);

    //log("Detect Sound");
    if (connectAndSendCommand(*pGattServerAddress)) {
      log("Done!");
      sendFlg = false;
      doScan = false;
    } else {
      log("Failed.");
      doScan = true;
      delay(3000);
    }
  }

  vTaskDelay(500 / portTICK_RATE_MS);
}

// SwitchBot の GATT サーバへ接続 ~ Press コマンド送信
static bool connectAndSendCommand(BLEAddress pAddress) {
  cas = true;
  try {
    pClient = BLEDevice::createClient();

    pClient->setClientCallbacks(new MyClientCallback());

    log("Connecting");
    while (!pClient->connect(myDevice)) {
      log("reconnect");
      delay(1000);
    }

    // 対象サービスを得る
    BLERemoteService* pRemoteService = pClient->getService(SERV_SWITCHBOT);
    if (pRemoteService == nullptr) {
      log("e:service not found");
      return false;
    }

    // 対象キャラクタリスティックを得る
    pRemoteCharacteristic = pRemoteService->getCharacteristic(CHAR_SWITCHBOT);
    if (pRemoteCharacteristic == nullptr) {
      log("e:characteristic not found");
      return false;
    }

    // キャラクタリスティックに Press コマンドを書き込む
    pRemoteCharacteristic->writeValue(cmdPress, sizeof(cmdPress), false);
    //    pRemoteCharacteristic->writeValue("test", true);
    log("Send");

    cas = false;

    delay(3000);
    pClient->disconnect();
    pClient = NULL;
    delay(1000);
  }
  catch (...) {
    log("error");
    if (pClient) {
      pClient->disconnect();
      pClient = NULL;
    }
    return false;
  }

  return true;
}

動作

気持ち長押し目で押してやってください。ディレイに入っている間はボタンを押しても受け付けられないためです。
対策としては並列処理にしたり、タイミングを逃さないトグルスイッチを使用することでしょうか。

コード②

M5ボタンを一度押すと、10秒に一回スイッチボットを動作させます。
ループを解除するには側面ボタンを一回押します(PINを39をON)。

#include <M5StickC.h>
#include <driver/i2s.h>
#include "BLEDevice.h"

//追加
#define BTN_A_PIN 37
#define BTN_B_PIN 39
#define LED_PIN   10

// このLEDは、GPIO10の電位を下げることで発光するタイプ
#define LED_ON  LOW
#define LED_OFF HIGH

// INPUT_PULLUPが有効かは不明だが、有効という前提で定義
#define BTN_ON  LOW
#define BTN_OFF HIGH

uint8_t prev_btn_a = BTN_ON;
uint8_t btn_a      = BTN_ON;
uint8_t prev_btn_b = BTN_OFF;
uint8_t btn_b      = BTN_OFF;

// このLEDは、GPIO10の電位を下げることで発光するタイプ
#define LED_ON  LOW
#define LED_OFF HIGH

// INPUT_PULLUPが有効かは不明だが、有効という前提で定義
#define BTN_ON  LOW
#define BTN_OFF HIGH

// 手元のSwitchBotのMACアドレス
static String MAC_SWITCHBOT = "xx:xx:xx:xx:xx:xx";

// SwitchBotのBLE情報
static BLEUUID SERV_SWITCHBOT("cba20d00-224d-11e6-9fb8-0002a5d5c51b");
static BLEUUID CHAR_SWITCHBOT("cba20002-224d-11e6-9fb8-0002a5d5c51b");
// SwitchBot へ送信するコマンド
// アームを倒し、引く動作の場合 {0x57, 0x01, 0x00}
// 以下2つはスマートフォンアプリでモードを変える必要あり
// アームを倒す動作の場合 {0x57, 0x01, 0x01}
// アームを引く動作の場合 {0x57, 0x01, 0x02}
static uint8_t cmdPress[3] = {0x57, 0x01, 0x01};

#define PIN_CLK  0
#define PIN_DATA 34
#define READ_LEN (2 * 1024)
#define SAMPLING_FREQUENCY 44100

uint8_t BUFFER[READ_LEN] = {0};
uint16_t *adcBuffer = NULL;
//const uint16_t FFTsamples = 256;  // サンプル数は2のべき乗//消去
//double vReal[FFTsamples];  // vReal[]にサンプリングしたデーターを入れる//消去
//double vImag[FFTsamples];//消去
//arduinoFFT FFT = arduinoFFT(vReal, vImag, FFTsamples, SAMPLING_FREQUENCY);  // FFTオブジェクトを作る//消去

bool doScan = true;
BLEScan* pBLEScan;
static BLEAddress *pGattServerAddress;
static BLEAdvertisedDevice* myDevice;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEClient*  pClient = NULL;

unsigned int sampling_period_us;
float dmax = 10000.0;

bool doDetection = false;
bool sendFlg = false;
bool cas = false;


//int printCount = 0;

void log(String s) {
  Serial.println(s);
  M5.Lcd.println(s);
}


// BLEへの接続 コールバック
class MyClientCallback : public BLEClientCallbacks {
    void onConnect(BLEClient* pclient) {
      Serial.println("onConnect");
      //    log("cc conn");
    }
    void onDisconnect(BLEClient* pclient) {
      Serial.println("onDisconnect");
      //    log("dis");
      //    doScan = true;
      //    doDetection = false;
      if (cas) {
        esp_restart();
      }
    }
};

// アドバタイズ検出時のコールバック
class advdCallback: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
      if (advertisedDevice.haveServiceUUID()) {
        String addr = advertisedDevice.getAddress().toString().c_str();
        Serial.printf("It have service addr: %s \n", advertisedDevice.getAddress().toString().c_str());
        if (addr.equalsIgnoreCase(MAC_SWITCHBOT)) {
          // SwitchBot を発見
          //        if (advertisedDevice.getServiceUUID().equals(SERV_SWITCHBOT)) {
          log("found");

          advertisedDevice.getScan()->stop();
          pGattServerAddress = new BLEAddress(advertisedDevice.getAddress());
          myDevice = new BLEAdvertisedDevice(advertisedDevice);

          doScan = false;
          doDetection = true;
//          m5LED(500, 2);
        }
      }
    }
};




void setup() {
  Serial.begin(115200);
  M5.begin();
//  i2sInit();

  pinMode(BTN_A_PIN, INPUT_PULLUP);//追加
  pinMode(BTN_B_PIN, INPUT_PULLUP);//追加
  pinMode(LED_PIN,   OUTPUT);//追加
  digitalWrite(LED_PIN, LED_OFF);//追加 

  //  pinMode(M5_LED, OUTPUT);
  M5.Axp.ScreenBreath(8);    // 画面の輝度を少し下げる
  M5.Lcd.setTextSize(2);    // 画面内文字のサイズを大きく

  log("Start");

  //xTaskCreate(mic_fft_task, "mic_fft_task", 2048, NULL, 1, NULL);//消去

  // BLE 初期化
  BLEDevice::init("");
  // デバイスからのアドバタイズをスキャン
  pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new advdCallback());
  pBLEScan->setActiveScan(true);
  pBLEScan->setInterval(100);
  pBLEScan->setWindow(99);  // less or equal setInterval value
}

void loop() {
 
  btn_a = digitalRead(BTN_A_PIN);//追加
  btn_b = digitalRead(BTN_B_PIN);
  
  if (doScan) {
    log("Scan");
    pBLEScan->start(5, false);
  }

//  if (sendFlg == true && doScan == false) {
  if((prev_btn_a == BTN_ON || btn_a == BTN_ON) && doScan == false){//追加   

    // log reset
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(0, 0);

    digitalWrite(LED_PIN, LED_ON);
   //digitalWrite(LED_PIN, LED_OFF);
    prev_btn_a = BTN_ON;
    prev_btn_b = BTN_OFF;

    log("Detect Sound");
    if (connectAndSendCommand(*pGattServerAddress)) {
      log("Done!");
      sendFlg = false;
      doScan = false;
      delay(10000);
    } else {
      log("Failed.");
      doScan = true;
      delay(3000);
    }
    if(prev_btn_b == BTN_ON || btn_b == BTN_ON){
    // ボタンBが押されたとき。今回は2回点滅
      digitalWrite(LED_PIN, LED_ON);
      delay(100);
      digitalWrite(LED_PIN, LED_OFF);
      delay(100);
      digitalWrite(LED_PIN, LED_ON);
      delay(100);
      digitalWrite(LED_PIN, LED_OFF);
      prev_btn_a = BTN_OFF;
      prev_btn_b = BTN_ON;
  }
  }

  vTaskDelay(500 / portTICK_RATE_MS);
}

// SwitchBot の GATT サーバへ接続 ~ Press コマンド送信
static bool connectAndSendCommand(BLEAddress pAddress) {
  cas = true;
  try {
    pClient = BLEDevice::createClient();

    pClient->setClientCallbacks(new MyClientCallback());

    log("Connecting");
    while (!pClient->connect(myDevice)) {
      log("reconnect");
      delay(1000);
    }

    // 対象サービスを得る
    BLERemoteService* pRemoteService = pClient->getService(SERV_SWITCHBOT);
    if (pRemoteService == nullptr) {
      log("e:service not found");
      return false;
    }

    // 対象キャラクタリスティックを得る
    pRemoteCharacteristic = pRemoteService->getCharacteristic(CHAR_SWITCHBOT);
    if (pRemoteCharacteristic == nullptr) {
      log("e:characteristic not found");
      return false;
    }

    // キャラクタリスティックに Press コマンドを書き込む
    pRemoteCharacteristic->writeValue(cmdPress, sizeof(cmdPress), false);
    //    pRemoteCharacteristic->writeValue("test", true);
    log("Send");

    cas = false;

    delay(3000);
    pClient->disconnect();
    pClient = NULL;
    delay(1000);
  }
  catch (...) {
    log("error");
    if (pClient) {
      pClient->disconnect();
      pClient = NULL;
    }
    return false;
  }

  return true;
}

不用な部分も残ってるかもしれませんが、これでお目当ての動作が得られました。
スイッチボットのトリガーを変更したい場合は、以下の部分をお好みに変化させてください。

 if((prev_btn_a == BTN_ON || btn_a == BTN_ON) && doScan == false)

BlynkでRaspberry Pi3+のCO2センサー測定値を取得

Blynkは各種IoTデバイスに対してスマホ等外部のデバイスから相互に通信を行えるシステムのことです。今回はBlynkを利用して、Raspberry Pi3+にアクセスしCO2センサー測定値を取得します。Lチカでも大変お世話になったこちらの記事を参考にし手順を進めました。
blog.livedoor.jp

難易度:☆★★★★
新規性:☆★★★★
有用性:☆☆☆★★

準備

Blynkスマホアプリ側の準備

Blynkアプリをスマホにインストールし、新規プロジェクトを作成しトークンを入手しておきます。
BlynkのRaspberry Piへのインストールに関してはこちらの記事にまとめました。

配線

ラズパイとCO2センサー(MH-Z19B)のRXとTXをクロスにして接続します。詳細はこちらの記事にまとめました。

CO2センサー側 ラズパイ側
GND
GND
VCC
5VO
RX
TXD0(14pin)
TX
RXD0(15pin)

※pinはGPIO番号です。

main.cppの書換え

main.cppはBlynkライブラリに含まれるソースファイルです。vimでmain.cppの中身を書き換えます。cppとはC++固有の拡張子らしいです。

pi@raspberrypi:~ $ cd blynk
pi@raspberrypi:~/blynk $ cd blynk-library/linux
pi@raspberrypi:~/blynk/blynk-library/linux $ sudo vi main.cpp

この中で次の行を探し出し、

BLYNK_WRITE(V1)
{
  printf("Got a value: %s\n", param[0].asStr());
}

次のコードに置き換えます。参考記事のまんまで使用させて頂いてます。

void blynk_read_exec(int pin) {
  char command[256] = "";
  char buff[256] = "";
  FILE *fp;
  sprintf(command, "/home/pi/blynk/BLYNK_READ_V%d.sh", pin);
  if((fp=popen(command,"r")) != NULL) {
    if(fgets(buff, 255, fp) != NULL) strtok(buff, "\n\0");
  }
  pclose(fp);
  Blynk.virtualWrite(pin, buff);
  BLYNK_LOG("Command: %s -> %s", command, buff);
}

void blynk_write_exec(int pin, const BlynkParam& param) {
  char command[256] = "";
  char buff[256] = "";
  sprintf(command, "/home/pi/blynk/BLYNK_WRITE_V%d.sh", pin);
  for (int i=0; i<3; i++) {
    if(! param[i].isValid()) break;
    sprintf(buff, " %d", param[i].asInt());
    strcat(command, buff);
  }
  BLYNK_LOG("Command: %s", command);
  system(command);
}

BLYNK_READ(V0) { blynk_read_exec(V0); }
BLYNK_READ(V1) { blynk_read_exec(V1); }
BLYNK_READ(V2) { blynk_read_exec(V2); }
BLYNK_READ(V3) { blynk_read_exec(V3); }
BLYNK_READ(V4) { blynk_read_exec(V4); }
BLYNK_READ(V5) { blynk_read_exec(V5); }
BLYNK_READ(V6) { blynk_read_exec(V6); }
BLYNK_READ(V7) { blynk_read_exec(V7); }
BLYNK_READ(V8) { blynk_read_exec(V8); }
BLYNK_READ(V9) { blynk_read_exec(V9); }
BLYNK_WRITE(V10) { blynk_write_exec(V10,param); }
BLYNK_WRITE(V11) { blynk_write_exec(V11,param); }
BLYNK_WRITE(V12) { blynk_write_exec(V12,param); }
BLYNK_WRITE(V13) { blynk_write_exec(V13,param); }
BLYNK_WRITE(V14) { blynk_write_exec(V14,param); }
BLYNK_WRITE(V15) { blynk_write_exec(V15,param); }
BLYNK_WRITE(V16) { blynk_write_exec(V16,param); }
BLYNK_WRITE(V17) { blynk_write_exec(V17,param); }
BLYNK_WRITE(V18) { blynk_write_exec(V18,param); }
BLYNK_WRITE(V19) { blynk_write_exec(V19,param); }

vimへコピペすると先頭行が消えたりしたのですが、テキストエディタ等に一度張り付けてダミーの行を加えてやりました。

この書き換え操作によりBlynkでバーチャルピンのX番をONにした際に、"/home/pi/blynk/BLYNK_WRITE_VX.sh"シェルスクリプトファイルが実行されることになります。今後新しいバーチャルピンに機能をアサインする際、main.cppを弄る必要がなくなりました。凄い!
もう一つ私にとって大いに助かる点は、間接的にpythonファイルを扱えるようになったということですね。

動作確認

今までの操作に問題がなかったかを確認してみます。参考記事に載っているCPU温度取得を試してみます。
blynkディレクトリにBLYNK_READ_V0.shを作成します。

pi@raspberrypi:~ $ cd blynk
pi@raspberrypi:~/blynk $ sudo vi BLYNK_READ_V0.sh

次のように記入し保存します。

#!/bin/sh
data=`cat /sys/class/thermal/thermal_zone0/temp | awk '{printf($1/1000)}'`
printf "%3.1f" $data

作成したシェルスクリプトファイルに権限を与えます。Linuxでのshファイル実行にはこの操作が必要なようです(参考)。

pi@raspberrypi:~/blynk $ chmod 700 BLYNK_READ_V0.sh

BLYNK_READ_V0.shを作成しましたので、アプリ側でV0ピンをONにすればこのファイルが実行されるはずです。
LXTerminalでBlynkを起動します。

$ cd blynk
$ cd blynk-library/linux
$ sudo ./blynk --token=<トークン>

スマホのBlynkアプリでプロジェクトを開き、Widget BoxからValue Displayを選択します。
Value Displayのセッティング画面でINPUTをVirtualピンの0番、すなわちV0を選択し、READING RATEを適当な値に設定します。
プロットする場合はSuperChartを選択し、+Add Datastream→INPUTをV0に設定します。
▷ボタンを押せばBlynkサーバーとの通信が始まります。成功すればCPUの温度がスマホで確認できるはずです。

CO2センサーの値を取得する

以前の記事pythonスクリプトでCO2センサーの値を取得したので、こちらを流用したいと思います。
co2.pyという名前で、任意の場所に次の内容のファイルを保存します。

import mh_z19
co2 = mh_z19.read_all()["co2"]
print(co2)

次にBlynkディレクトリに以下の内容のファイルを保存します。

python3 /home/pi/co2.py

ここではファイル名をBLYNK_READ_V1.shとしました。パス部分はco2.pyの保存場所に合わせて適宜変更してください。
最後に作成したシェルスクリプトに権限を付与します。

pi@raspberrypi:~/blynk $ chmod 700 BLYNK_READ_V1.sh

これで準備は完了です。BlynkアプリでV1ピンをONにすればCO2濃度が取得できます。
f:id:tara-chang:20200509214328p:plain

BlynkでスマホからRaspberry Pi3+でLチカする

Blynkは各種IoTデバイスに対してスマホ等外部のデバイスから相互に通信を行えるシステムのことです。一つの用途としては、外部ネットからラズパイへのアクセスが考えられます。今回はBlynkからのラズパイLチカを目標に、Blynkの環境構築を試みました。
f:id:tara-chang:20200506100425p:plain
なお今回はこちらの記事の内容をトレースすることで成功しました。Blynk公式のチュートリアルも試したのですが失敗しました➡失敗編

まず下準備

Blynkスマホアプリ側の準備

新しいプロジェクトを作成し、トークンを生成しておきます。これは直感操作で殆ど出来るかと思いますので詳細は割愛します。

配線

Raspberry PiのGPIO18ピンとGNDにLEDを接続しました。GPIO18をBlynkアプリでONできれば目標は達成となります。

Blynkのインストール

まずblynkディレクトリを作成し、そこにblynkのライブラリをgit cloneします。

pi@raspberrypi:~ $ cd blynk
pi@raspberrypi:~/blynk $ git clone https://github.com/blynkkk/blynk-library.git
Cloning into 'blynk-library'...
remote: Enumerating objects: 11456, done.
remote: Total 11456 (delta 0), reused 0 (delta 0), pack-reused 11456
Receiving objects: 100% (11456/11456), 9.20 MiB | 1.80 MiB/s, done.
Resolving deltas: 100% (7204/7204), done.

先ほど生成されたblynk-library/linuxディレクトリに移動し、コンパイル(多分)を行います。

pi@raspberrypi:~/blynk $ cd blynk-library/linux
pi@raspberrypi:~/blynk/blynk-library/linux $ make clean all target=raspberry

rm main.o ../src/utility/BlynkDebug.o ../src/utility/BlynkHandlers.o ../src/utility/BlynkTimer.o blynk
rm: 'main.o' を削除できません: そのようなファイルやディレクトリはありません
rm: '../src/utility/BlynkDebug.o' を削除できません: そのようなファイルやディレクトリはありません
rm: '../src/utility/BlynkHandlers.o' を削除できません: そのようなファイルやディ レクトリはありません
rm: '../src/utility/BlynkTimer.o' を削除できません: そのようなファイルやディレクトリはありません
rm: 'blynk' を削除できません: そのようなファイルやディレクトリはありません
Makefile:64: ターゲット 'clean' のレシピで失敗しました
make: [clean] エラー 1 (無視されました)
g++ -I ../src/ -I ./ -DLINUX -c -O3 -w -DRASPBERRY main.cpp -o main.o
g++ -I ../src/ -I ./ -DLINUX -c -O3 -w -DRASPBERRY ../src/utility/BlynkDebug.cpp -o ../src/utility/BlynkDebug.o
g++ -I ../src/ -I ./ -DLINUX -c -O3 -w -DRASPBERRY ../src/utility/BlynkHandlers.cpp -o ../src/utility/BlynkHandlers.o
g++ -I ../src/ -I ./ -DLINUX -c -O3 -w -DRASPBERRY ../src/utility/BlynkTimer.cpp -o ../src/utility/BlynkTimer.o
g++ main.o ../src/utility/BlynkDebug.o ../src/utility/BlynkHandlers.o ../src/utility/BlynkTimer.o -lrt -lpthread -s -lwiringPi -o blynk

読み方が良く分かりませんが、main.cppがコンパイルされたんだと思います

Blynk立ち上げとLチカ

次のコマンドでBlynkサーバー立ち上げます。

pi@raspberrypi:~/blynk/blynk-library/linux $ sudo ./blynk --token=<トークン>
[0]
    ___  __          __
   / _ )/ /_ _____  / /__
  / _  / / // / _ \/  '_/
 /____/_/\_, /_//_/_/\_\
        /___/ v0.6.1 on Linux

[1] Connecting to blynk-cloud.com:80
[468] Ready (ping: 172ms).

無事Blynkが立ち上がりました。
またスマホアプリからのLチカにも応答しました!

f:id:tara-chang:20200507124502p:plain
Blynkアプリ画面。sliderでLED光度も調節可能
f:id:tara-chang:20200507124615p:plain

【失敗編】BlynkでRaspberry Pi3+とスマホを接続するための環境を構築する

Blynkは各種IoTデバイスに対してスマホ等外部のデバイスから相互に通信を行えるシステムのことです。一つの用途としては、外部ネットからラズパイへのアクセスが考えられます。今回はBlynkからのラズパイLチカを目標に、Blynkの環境構築を試みました。
f:id:tara-chang:20200506100425p:plain
……が、かなり遠回りしたのでその失敗談を記録として残しておきます。エラー文も全て載せてあるので、同様のエラーに遭遇した方の参考になれば幸いです。十分知識あるの方からすると頭痛のするような内容だと思います。
※この記事は失敗編です。成功した方法は別の記事にまとめてあります。

試したことと結果

・Blynk公式の動画チュートリアルに従って環境構築するも、npmのインストールに躓く。
・npmをアップデートした。
・Blynkのインストールとサーバー立ち上げ、スマホアプリとのコネクションに成功。しかしLチカには失敗。
・結局Blynk公式のチュートリアルとは別な方法でBlynkをインストールし直した結果、Lチカに成功。

まず下準備

Blynkスマホアプリ側の準備

新しいプロジェクトを作成し、トークンを生成しておきます。これは直感操作で殆ど出来るかと思いますので詳細は割愛します。

配線

Raspberry PiのGPIO18ピンとGNDにLEDを接続しました。GPIO18をBlynkアプリでONできれば目標は達成となります。

公式動画チュートリアルに従って環境構築を試みる

動画はこちら。このチュートリアルを見る限りではとても簡単そうです。
youtu.be
動画の元となるコートと詳細な説明はこちらに載っています。公開はちょうど三年前です。
まずはNode.js.をインストールする前に、過去のバージョンを削除するとのことです。

pi@raspberrypi:~ $ sudo apt-get purge node nodejs node.js -y
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
注意、regex 'node.js' のために 'node-json-localizer' を選択します
注意、regex 'node.js' のために 'node-json-stable-stringify' を選択します
注意、regex 'node.js' のために 'node-jsv' を選択します
注意、regex 'node.js' のために 'node-jsonstream' を選択します
注意、regex 'node.js' のために 'node-jsdom' を選択します
注意、regex 'node.js' のために 'node-jsesc' を選択します
注意、regex 'node.js' のために 'node-json-stringify-safe' を選択します
注意、regex 'node.js' のために 'node-json5' を選択します
注意、regex 'node.js' のために 'node-jscoverage' を選択します
注意、regex 'node.js' のために 'node-js-yaml' を選択します
注意、regex 'node.js' のために 'node-jssip' を選択します
注意、regex 'node.js' のために 'node-jsonminify' を選択します
注意、regex 'node.js' のために 'node-jsonselect' を選択します
注意、regex 'node.js' のために 'node-jsonfile' を選択します
注意、regex 'node.js' のために 'node-jsonparse' を選択します
注意、regex 'node.js' のために 'node-jsonify' を選択します
注意、regex 'node.js' のために 'node-js-tokens' を選択します
注意、regex 'node.js' のために 'node-json-parse-helpfulerror' を選択します
注意、regex 'node.js' のために 'node-jsconfig' を選択します
パッケージ 'node-jscoverage' はインストールされていないため削除もされません
パッケージ 'node' はインストールされていないため削除もされません
パッケージ 'node-js-tokens' はインストールされていないため削除もされません
パッケージ 'node-js-yaml' はインストールされていないため削除もされません
パッケージ 'node-jsconfig' はインストールされていないため削除もされません
パッケージ 'node-jsdom' はインストールされていないため削除もされません
パッケージ 'node-jsesc' はインストールされていないため削除もされません
パッケージ 'node-json-localizer' はインストールされていないため削除もされません
パッケージ 'node-json-parse-helpfulerror' はインストールされていないため削除もされません
パッケージ 'node-json-stable-stringify' はインストールされていないため削除もされません
パッケージ 'node-json-stringify-safe' はインストールされていないため削除もされません
パッケージ 'node-json5' はインストールされていないため削除もされません
パッケージ 'node-jsonfile' はインストールされていないため削除もされません
パッケージ 'node-jsonify' はインストールされていないため削除もされません
パッケージ 'node-jsonminify' はインストールされていないため削除もされません
パッケージ 'node-jsonparse' はインストールされていないため削除もされません
パッケージ 'node-jsonselect' はインストールされていないため削除もされません
パッケージ 'node-jsonstream' はインストールされていないため削除もされません
パッケージ 'node-jssip' はインストールされていないため削除もされません
パッケージ 'node-jsv' はインストールされていないため削除もされません
以下のパッケージは「削除」されます:
  nodejs*
アップグレード: 0 個、新規インストール: 0 個、削除: 1 個、保留: 0 個。
この操作後に 83.7 MB のディスク容量が解放されます。
(データベースを読み込んでいます ... 現在 126503 個のファイルとディレクトリがインストールされています。)
nodejs (12.16.2-1nodesource1) を削除しています ...
dpkg: 警告: nodejs の削除中、ディレクトリ '/usr/lib/node_modules/npm/docs' が空でないため削除できませんでした
dpkg: 警告: nodejs の削除中、ディレクトリ '/usr/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/generator' が空でないため削除できませんでした
dpkg: 警告: nodejs の削除中、ディレクトリ '/usr/lib/node_modules/npm/lib' が空でないため削除できませんでした
man-db (2.7.6.1-2) のトリガを処理しています ...

インストールされていた古いバージョンが削除されたようです。しかし何やら最後のほうに不穏な警告が出てしまいました。同様の警告はチュートリアルにも出現していますが、問題はないんでしょうか。npmというnodeモジュールがあり、それが削除できなかったということでしょうか。しかし空でないから削除できないというのも私には良く分かりません。現段階では特に成す術がないので先に進みます…。
次のコマンドはautoremoveです。これにより先ほどのpurgeで削除し切れなかった依存パッケージを削除できるそうです(参考)。

pi@raspberrypi:~ $ sudo apt-get autoremove
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
アップグレード: 0 個、新規インストール: 0 個、削除: 0 個、保留: 0 個。

特に削除はありませんでした。
次のコマンドからチュートリアルで言う「Automatic Node.js installation」のセクションに入ります。これはNode.jsのインストールの前準備みたいなものでしょうか。

pi@raspberrypi:~ $ curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -

================================================================================
================================================================================

                              DEPRECATION WARNING                            

  Node.js 6.x LTS Boron is no longer actively supported!

  You will not receive security or critical stability updates for this version.

  You should migrate to a supported version of Node.js as soon as possible.
  Use the installation script that corresponds to the version of Node.js you
  wish to install. e.g.

   * https://deb.nodesource.com/setup_10.x — Node.js 10 LTS "Dubnium" (recommended)
   * https://deb.nodesource.com/setup_12.x — Node.js 12 LTS "Erbium"

  Please see https://github.com/nodejs/Release for details about which
  version may be appropriate for you.

  The NodeSource Node.js distributions repository contains
  information both about supported versions of Node.js and supported Linux
  distributions. To learn more about usage, see the repository:
    https://github.com/nodesource/distributions

================================================================================
================================================================================

Continuing in 20 seconds ...


## Installing the NodeSource Node.js 6.x LTS Boron repo...


## Populating apt-get cache...

+ apt-get update
取得:1 http://archive.raspberrypi.org/debian stretch InRelease [25.4 kB]
取得:2 http://raspbian.raspberrypi.org/raspbian stretch InRelease [15.0 kB]
取得:3 https://deb.nodesource.com/node_12.x stretch InRelease [4,585 B]
取得:4 http://archive.raspberrypi.org/debian stretch/main armhf Packages [220 kB]
取得:5 https://deb.nodesource.com/node_12.x stretch/main armhf Packages [775 B]
取得:6 http://raspbian.raspberrypi.org/raspbian stretch/main armhf Packages [11.7 MB]
11.9 MB を 17秒 で取得しました (667 kB/s)                                      
パッケージリストを読み込んでいます... 完了

## Confirming "stretch" is supported...

+ curl -sLf -o /dev/null 'https://deb.nodesource.com/node_6.x/dists/stretch/Release'

## Adding the NodeSource signing key to your keyring...

+ curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -
OK

## Creating apt sources list file for the NodeSource Node.js 6.x LTS Boron repo...

+ echo 'deb https://deb.nodesource.com/node_6.x stretch main' > /etc/apt/sources.list.d/nodesource.list
+ echo 'deb-src https://deb.nodesource.com/node_6.x stretch main' >> /etc/apt/sources.list.d/nodesource.list

## Running `apt-get update` for you...

+ apt-get update
ヒット:1 http://raspbian.raspberrypi.org/raspbian stretch InRelease
ヒット:2 http://archive.raspberrypi.org/debian stretch InRelease
取得:3 https://deb.nodesource.com/node_6.x stretch InRelease [4,608 B]
取得:4 https://deb.nodesource.com/node_6.x stretch/main armhf Packages [1,008 B]
5,616 B を 2秒 で取得しました (2,419 B/s)
パッケージリストを読み込んでいます... 完了

## Run `sudo apt-get install -y nodejs` to install Node.js 6.x LTS Boron and npm
## You may also need development tools to build native addons:
     sudo apt-get install gcc g++ make
## To install the Yarn package manager, run:
     curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
     echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
     sudo apt-get update && sudo apt-get install yarn

Node.js 6.x LTS Boronはサポートされないという注意文が出た後に、NodeSource Node.js 6.x LTS Boron repoなるファイルをインストールし始めました。大丈夫でしょうか(^^;)。
次のコマンドはapt-getのupgradeとupgradeです。

pi@raspberrypi:~ $ sudo apt-get update && sudo apt-get upgrade
ヒット:1 http://raspbian.raspberrypi.org/raspbian stretch InRelease
ヒット:2 https://deb.nodesource.com/node_6.x stretch InRelease
ヒット:3 http://archive.raspberrypi.org/debian stretch InRelease
パッケージリストを読み込んでいます... 完了
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
アップグレードパッケージを検出しています... 完了
以下のパッケージはアップグレードされます:
  libldap-2.4-2 libldap-common libtiff5
アップグレード: 3 個、新規インストール: 0 個、削除: 0 個、保留: 0 個。
501 kB のアーカイブを取得する必要があります。
この操作後に追加で 3,072 B のディスク容量が消費されます。
続行しますか? [Y/n] y
取得:1 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf libldap-common all 2.4.44+dfsg-5+deb9u4 [85.7 kB]
取得:2 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf libldap-2.4-2 armhf 2.4.44+dfsg-5+deb9u4 [196 kB]
取得:3 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf libtiff5 armhf 4.0.8-2+deb9u5 [220 kB]
501 kB を 2秒 で取得しました (187 kB/s)
changelog を読んでいます... 完了
(データベースを読み込んでいます ... 現在 121698 個のファイルとディレクトリがインストールされています。)
.../libldap-common_2.4.44+dfsg-5+deb9u4_all.deb を展開する準備をしています ...
libldap-common (2.4.44+dfsg-5+deb9u4) で (2.4.44+dfsg-5+deb9u1 に) 上書き展開しています ...
.../libldap-2.4-2_2.4.44+dfsg-5+deb9u4_armhf.deb を展開する準備をしています ...
libldap-2.4-2:armhf (2.4.44+dfsg-5+deb9u4) で (2.4.44+dfsg-5+deb9u1 に) 上書き展開しています ...
.../libtiff5_4.0.8-2+deb9u5_armhf.deb を展開する準備をしています ...
libtiff5:armhf (4.0.8-2+deb9u5) で (4.0.8-2+deb9u4 に) 上書き展開しています ...
libldap-common (2.4.44+dfsg-5+deb9u4) を設定しています ...
libtiff5:armhf (4.0.8-2+deb9u5) を設定しています ...
libc-bin (2.24-11+deb9u4) のトリガを処理しています ...
libldap-2.4-2:armhf (2.4.44+dfsg-5+deb9u4) を設定しています ...
man-db (2.7.6.1-2) のトリガを処理しています ...
libc-bin (2.24-11+deb9u4) のトリガを処理しています ...

次のコマンドはnodejsのインストールですね。2個前でインストールしたソースファイル(NodeSource Node.js 6.x LTS Boron repo)を元にインストールされるのではないでしょうか。

pi@raspberrypi:~ $ sudo apt-get install build-essential nodejs -y
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
build-essential はすでに最新バージョン (12.3) です。
以下の追加パッケージがインストールされます:
  libc-ares2 libhttp-parser2.8 libuv1 nodejs-doc
以下のパッケージが新たにインストールされます:
  libc-ares2 libhttp-parser2.8 libuv1 nodejs nodejs-doc
アップグレード: 0 個、新規インストール: 5 個、削除: 0 個、保留: 0 個。
5,087 kB のアーカイブを取得する必要があります。
この操作後に追加で 22.2 MB のディスク容量が消費されます。
取得:1 http://archive.raspberrypi.org/debian stretch/main armhf libc-ares2 armhf 1.14.0-1~bpo9+1 [80.7 kB]
取得:2 http://archive.raspberrypi.org/debian stretch/main armhf libhttp-parser2.8 armhf 2.8.1-1~bpo9+1 [19.3 kB]
取得:3 http://archive.raspberrypi.org/debian stretch/main armhf libuv1 armhf 1.18.0-3~bpo9+1 [86.3 kB]
取得:4 http://archive.raspberrypi.org/debian stretch/main armhf nodejs armhf 8.11.1~dfsg-2~bpo9+1 [4,126 kB]
取得:5 http://archive.raspberrypi.org/debian stretch/main armhf nodejs-doc all 8.11.1~dfsg-2~bpo9+1 [775 kB]
5,087 kB を 8秒 で取得しました (568 kB/s)                                      
以前に未選択のパッケージ libc-ares2:armhf を選択しています。
(データベースを読み込んでいます ... 現在 121698 個のファイルとディレクトリがインストールされています。)
.../libc-ares2_1.14.0-1~bpo9+1_armhf.deb を展開する準備をしています ...
libc-ares2:armhf (1.14.0-1~bpo9+1) を展開しています...
以前に未選択のパッケージ libhttp-parser2.8:armhf を選択しています。
.../libhttp-parser2.8_2.8.1-1~bpo9+1_armhf.deb を展開する準備をしています ...
libhttp-parser2.8:armhf (2.8.1-1~bpo9+1) を展開しています...
以前に未選択のパッケージ libuv1:armhf を選択しています。
.../libuv1_1.18.0-3~bpo9+1_armhf.deb を展開する準備をしています ...
libuv1:armhf (1.18.0-3~bpo9+1) を展開しています...
以前に未選択のパッケージ nodejs を選択しています。
.../nodejs_8.11.1~dfsg-2~bpo9+1_armhf.deb を展開する準備をしています ...
nodejs (8.11.1~dfsg-2~bpo9+1) を展開しています...
以前に未選択のパッケージ nodejs-doc を選択しています。
.../nodejs-doc_8.11.1~dfsg-2~bpo9+1_all.deb を展開する準備をしています ...
nodejs-doc (8.11.1~dfsg-2~bpo9+1) を展開しています...
nodejs-doc (8.11.1~dfsg-2~bpo9+1) を設定しています ...
libuv1:armhf (1.18.0-3~bpo9+1) を設定しています ...
libc-bin (2.24-11+deb9u4) のトリガを処理しています ...
man-db (2.7.6.1-2) のトリガを処理しています ...
libc-ares2:armhf (1.14.0-1~bpo9+1) を設定しています ...
libhttp-parser2.8:armhf (2.8.1-1~bpo9+1) を設定しています ...
nodejs (8.11.1~dfsg-2~bpo9+1) を設定しています ...
update-alternatives: /usr/bin/js (js) を提供するために自動モードで /usr/bin/nodejs を使います
libc-bin (2.24-11+deb9u4) のトリガを処理しています ...

特にエラーは出ていないようです。インストールがうまく行ったかどうかを確認します。

pi@raspberrypi:~ $ npm -v
-bash: npm: コマンドが見つかりません

pi@raspberrypi:~ $ node -v
v8.11.1

npmが入ってなさそうです(^^;)チュートリアルではnodeはv6.10.12、npmはv3.10.10が入っています。次のコマンドはBlynkのインストールですが、npm不在の状態では失敗するのは明らかでしょう。
そもそもnpmとは何なのか良く分かってませんでしたが、こちらの記事によると開発に必要なパッケージ(ライブラリとか、プラグインとか色々)を管理するためのツールです。全ての人が、全く同じ開発環境を再現するためにとても重要な役割を果たしています。とのことです。この記事の内容を全部理解できるくらいの知識が本当は必要だと思います。

pi@raspberrypi:~ $ sudo npm install blynk-library -g
sudo: npm: コマンドが見つかりません

npmの再インストールが必要と思われます。チュートリアル動画のコメント欄を覗いた所、やはり同様の問題が発生した人がいるようでした。
f:id:tara-chang:20200505170214p:plain
素直にapt-get installでnpmをインストールすればよいようです。

pi@raspberrypi:~ $ sudo apt-get install npm
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
以下の追加パッケージがインストールされます:
  gyp libjs-inherits libjs-node-uuid libssl-dev libssl-doc libuv1-dev
  node-abbrev node-ansi node-ansi-color-table node-archy node-async
  node-balanced-match node-block-stream node-brace-expansion
  node-builtin-modules node-combined-stream node-concat-map node-cookie-jar
  node-delayed-stream node-forever-agent node-form-data node-fs.realpath
  node-fstream node-fstream-ignore node-github-url-from-git node-glob
  node-graceful-fs node-gyp node-hosted-git-info node-inflight node-inherits
  node-ini node-is-builtin-module node-isexe node-json-stringify-safe
  node-lockfile node-lru-cache node-mime node-minimatch node-mkdirp
  node-mute-stream node-node-uuid node-nopt node-normalize-package-data
  node-npmlog node-once node-osenv node-path-is-absolute node-pseudomap
  node-qs node-read node-read-package-json node-request node-retry node-rimraf
  node-semver node-sha node-slide node-spdx-correct node-spdx-expression-parse
  node-spdx-license-ids node-tar node-tunnel-agent node-underscore
  node-validate-npm-package-license node-which node-wrappy node-yallist
  nodejs-dev
提案パッケージ:
  node-hawk node-aws-sign node-oauth-sign node-http-signature debhelper
以下のパッケージが新たにインストールされます:
  gyp libjs-inherits libjs-node-uuid libssl-dev libssl-doc libuv1-dev
  node-abbrev node-ansi node-ansi-color-table node-archy node-async
  node-balanced-match node-block-stream node-brace-expansion
  node-builtin-modules node-combined-stream node-concat-map node-cookie-jar
  node-delayed-stream node-forever-agent node-form-data node-fs.realpath
  node-fstream node-fstream-ignore node-github-url-from-git node-glob
  node-graceful-fs node-gyp node-hosted-git-info node-inflight node-inherits
  node-ini node-is-builtin-module node-isexe node-json-stringify-safe
  node-lockfile node-lru-cache node-mime node-minimatch node-mkdirp
  node-mute-stream node-node-uuid node-nopt node-normalize-package-data
  node-npmlog node-once node-osenv node-path-is-absolute node-pseudomap
  node-qs node-read node-read-package-json node-request node-retry node-rimraf
  node-semver node-sha node-slide node-spdx-correct node-spdx-expression-parse
  node-spdx-license-ids node-tar node-tunnel-agent node-underscore
  node-validate-npm-package-license node-which node-wrappy node-yallist
  nodejs-dev npm
アップグレード: 0 個、新規インストール: 70 個、削除: 0 個、保留: 0 個。
4,438 kB のアーカイブを取得する必要があります。
この操作後に追加で 17.3 MB のディスク容量が消費されます。
続行しますか? [Y/n] y
取得:1 http://archive.raspberrypi.org/debian stretch/main armhf libuv1-dev armhf 1.18.0-3~bpo9+1 [102 kB]
取得:2 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf gyp all 0.1+20150913git1f374df9-1 [266 kB]
取得:3 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf libjs-node-uuid all 1.4.0-1 [12.6 kB]
取得:4 http://archive.raspberrypi.org/debian stretch/main armhf nodejs-dev armhf 8.11.1~dfsg-2~bpo9+1 [353 kB]
取得:5 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf libssl-dev armhf 1.1.0l-1~deb9u1 [1,387 kB]
取得:6 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf libssl-doc all 1.1.0l-1~deb9u1 [1,480 kB]
取得:7 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-async all 0.8.0-1 [24.6 kB]
取得:8 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-builtin-modules all 1.1.1-1 [3,298 B]
取得:9 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-fs.realpath all 1.0.0-1 [5,522 B]
取得:10 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-hosted-git-info all 2.1.5-1 [5,696 B]
取得:11 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-wrappy all 1.0.2-1 [3,114 B]
取得:12 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-once all 1.4.0-2 [3,356 B]
取得:13 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-inflight all 1.0.6-1 [3,334 B]
取得:14 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-is-builtin-module all 1.0.0-1 [2,860 B]
取得:15 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-isexe all 1.1.2-1 [3,948 B]
取得:16 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-node-uuid all 1.4.0-1 [9,194 B]
取得:17 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-path-is-absolute all 1.0.0-1 [3,266 B]
取得:18 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-pseudomap all 1.0.2-1 [3,504 B]
取得:19 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-spdx-license-ids all 1.2.2-1 [4,754 B]
取得:20 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-spdx-correct all 1.0.2-1 [3,678 B]
取得:21 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-spdx-expression-parse all 1.0.4-1 [12.1 kB]
取得:22 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-underscore all 1.8.3~dfsg-1 [37.6 kB]
取得:23 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-validate-npm-package-license all 3.0.1-1 [3,442 B]
取得:24 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-yallist all 2.0.0-1 [5,362 B]
取得:25 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf libjs-inherits all 2.0.3-1 [2,762 B]
取得:26 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf node-abbrev all 1.0.9-1 [3,668 B]
取得:27 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-ansi all 0.3.0-2 [8,608 B]
取得:28 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-ansi-color-table all 1.0.0-1 [4,408 B]
取得:29 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-archy all 1.0.0-1 [4,052 B]
取得:30 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-balanced-match all 0.4.2-1 [3,982 B]
取得:31 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-inherits all 2.0.3-1 [3,744 B]
取得:32 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-block-stream all 0.0.9-1 [4,488 B]
取得:33 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-concat-map all 0.0.1-1 [3,462 B]
取得:34 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-brace-expansion all 1.1.6-1+deb9u1 [5,788 B]
取得:35 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-delayed-stream all 0.0.5-1 [4,482 B]
取得:36 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-combined-stream all 0.0.5-1 [4,918 B]
取得:37 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-cookie-jar all 0.3.1-1 [3,570 B]
取得:38 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-forever-agent all 0.5.1-1 [3,066 B]
取得:39 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-mime all 1.3.4-1 [11.8 kB]
取得:40 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-form-data all 0.1.0-1 [6,084 B]
取得:41 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-minimatch all 3.0.3-1 [12.8 kB]
取得:42 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-glob all 7.1.1-1 [18.4 kB]
取得:43 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf node-rimraf all 2.5.4-2 [7,482 B]
取得:44 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-mkdirp all 0.5.0-1 [4,634 B]
取得:45 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-graceful-fs all 4.1.11-1 [10.8 kB]
取得:46 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-fstream all 1.0.10-1+deb9u1 [18.2 kB]
取得:47 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf node-fstream-ignore all 0.0.6-2 [5,536 B]
取得:48 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-github-url-from-git all 1.4.0-1 [3,730 B]
取得:49 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-nopt all 3.0.6-3 [9,538 B]
取得:50 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-npmlog all 0.0.4-1 [5,516 B]
取得:51 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-osenv all 0.1.0-1 [3,734 B]
取得:52 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-tunnel-agent all 0.3.1-1 [3,840 B]
取得:53 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf node-json-stringify-safe all 5.0.0-1 [3,356 B]
取得:54 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf node-qs all 2.2.4-1 [8,474 B]
取得:55 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-request all 2.26.1-1 [15.7 kB]
取得:56 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf node-semver all 5.3.0-1 [21.9 kB]
取得:57 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-tar all 2.2.1-1 [17.6 kB]
取得:58 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-which all 1.2.11-1 [4,946 B]
取得:59 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-gyp all 3.4.0-1 [28.9 kB]
取得:60 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-ini all 1.1.0-1 [4,720 B]
取得:61 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-lockfile all 0.4.1-1 [5,150 B]
取得:62 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-lru-cache all 4.0.2-1 [7,412 B]
取得:63 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-mute-stream all 0.0.7-1 [4,344 B]
取得:64 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-normalize-package-data all 2.3.5-2 [10.5 kB]
取得:65 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-read all 1.0.7-1 [4,540 B]
取得:66 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-read-package-json all 1.2.4-1 [7,740 B]
取得:67 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-retry all 0.6.0-1 [6,124 B]
取得:68 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-sha all 1.2.3-1 [4,064 B]
取得:69 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf node-slide all 1.1.4-1 [6,078 B]
取得:70 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf npm all 1.4.21+ds-2 [354 kB]
4,438 kB を 48秒 で取得しました (91.3 kB/s)                                    
パッケージからテンプレートを展開しています: 100%
以前に未選択のパッケージ gyp を選択しています。
(データベースを読み込んでいます ... 現在 121846 個のファイルとディレクトリがインストールされています。)
.../00-gyp_0.1+20150913git1f374df9-1_all.deb を展開する準備をしています ...
gyp (0.1+20150913git1f374df9-1) を展開しています...
以前に未選択のパッケージ libjs-node-uuid を選択しています。
.../01-libjs-node-uuid_1.4.0-1_all.deb を展開する準備をしています ...
libjs-node-uuid (1.4.0-1) を展開しています...
以前に未選択のパッケージ libssl-dev:armhf を選択しています。
.../02-libssl-dev_1.1.0l-1~deb9u1_armhf.deb を展開する準備をしています ...
libssl-dev:armhf (1.1.0l-1~deb9u1) を展開しています...
以前に未選択のパッケージ libssl-doc を選択しています。
.../03-libssl-doc_1.1.0l-1~deb9u1_all.deb を展開する準備をしています ...
libssl-doc (1.1.0l-1~deb9u1) を展開しています...
以前に未選択のパッケージ libuv1-dev:armhf を選択しています。
.../04-libuv1-dev_1.18.0-3~bpo9+1_armhf.deb を展開する準備をしています ...
libuv1-dev:armhf (1.18.0-3~bpo9+1) を展開しています...
以前に未選択のパッケージ node-async を選択しています。
.../05-node-async_0.8.0-1_all.deb を展開する準備をしています ...
node-async (0.8.0-1) を展開しています...
以前に未選択のパッケージ node-builtin-modules を選択しています。
.../06-node-builtin-modules_1.1.1-1_all.deb を展開する準備をしています ...
node-builtin-modules (1.1.1-1) を展開しています...
以前に未選択のパッケージ node-fs.realpath を選択しています。
.../07-node-fs.realpath_1.0.0-1_all.deb を展開する準備をしています ...
node-fs.realpath (1.0.0-1) を展開しています...
以前に未選択のパッケージ node-hosted-git-info を選択しています。
.../08-node-hosted-git-info_2.1.5-1_all.deb を展開する準備をしています ...
node-hosted-git-info (2.1.5-1) を展開しています...
以前に未選択のパッケージ node-wrappy を選択しています。
.../09-node-wrappy_1.0.2-1_all.deb を展開する準備をしています ...
node-wrappy (1.0.2-1) を展開しています...
以前に未選択のパッケージ node-once を選択しています。
.../10-node-once_1.4.0-2_all.deb を展開する準備をしています ...
node-once (1.4.0-2) を展開しています...
以前に未選択のパッケージ node-inflight を選択しています。
.../11-node-inflight_1.0.6-1_all.deb を展開する準備をしています ...
node-inflight (1.0.6-1) を展開しています...
以前に未選択のパッケージ node-is-builtin-module を選択しています。
.../12-node-is-builtin-module_1.0.0-1_all.deb を展開する準備をしています ...
node-is-builtin-module (1.0.0-1) を展開しています...
以前に未選択のパッケージ node-isexe を選択しています。
.../13-node-isexe_1.1.2-1_all.deb を展開する準備をしています ...
node-isexe (1.1.2-1) を展開しています...
以前に未選択のパッケージ node-node-uuid を選択しています。
.../14-node-node-uuid_1.4.0-1_all.deb を展開する準備をしています ...
node-node-uuid (1.4.0-1) を展開しています...
以前に未選択のパッケージ node-path-is-absolute を選択しています。
.../15-node-path-is-absolute_1.0.0-1_all.deb を展開する準備をしています ...
node-path-is-absolute (1.0.0-1) を展開しています...
以前に未選択のパッケージ node-pseudomap を選択しています。
.../16-node-pseudomap_1.0.2-1_all.deb を展開する準備をしています ...
node-pseudomap (1.0.2-1) を展開しています...
以前に未選択のパッケージ node-spdx-license-ids を選択しています。
.../17-node-spdx-license-ids_1.2.2-1_all.deb を展開する準備をしています ...
node-spdx-license-ids (1.2.2-1) を展開しています...
以前に未選択のパッケージ node-spdx-correct を選択しています。
.../18-node-spdx-correct_1.0.2-1_all.deb を展開する準備をしています ...
node-spdx-correct (1.0.2-1) を展開しています...
以前に未選択のパッケージ node-spdx-expression-parse を選択しています。
.../19-node-spdx-expression-parse_1.0.4-1_all.deb を展開する準備をしています ...
node-spdx-expression-parse (1.0.4-1) を展開しています...
以前に未選択のパッケージ node-underscore を選択しています。
.../20-node-underscore_1.8.3~dfsg-1_all.deb を展開する準備をしています ...
node-underscore (1.8.3~dfsg-1) を展開しています...
以前に未選択のパッケージ node-validate-npm-package-license を選択しています。
.../21-node-validate-npm-package-license_3.0.1-1_all.deb を展開する準備をしています ...
node-validate-npm-package-license (3.0.1-1) を展開しています...
以前に未選択のパッケージ node-yallist を選択しています。
.../22-node-yallist_2.0.0-1_all.deb を展開する準備をしています ...
node-yallist (2.0.0-1) を展開しています...
以前に未選択のパッケージ nodejs-dev を選択しています。
.../23-nodejs-dev_8.11.1~dfsg-2~bpo9+1_armhf.deb を展開する準備をしています ...
nodejs-dev (8.11.1~dfsg-2~bpo9+1) を展開しています...
以前に未選択のパッケージ libjs-inherits を選択しています。
.../24-libjs-inherits_2.0.3-1_all.deb を展開する準備をしています ...
libjs-inherits (2.0.3-1) を展開しています...
以前に未選択のパッケージ node-abbrev を選択しています。
.../25-node-abbrev_1.0.9-1_all.deb を展開する準備をしています ...
node-abbrev (1.0.9-1) を展開しています...
以前に未選択のパッケージ node-ansi を選択しています。
.../26-node-ansi_0.3.0-2_all.deb を展開する準備をしています ...
node-ansi (0.3.0-2) を展開しています...
以前に未選択のパッケージ node-ansi-color-table を選択しています。
.../27-node-ansi-color-table_1.0.0-1_all.deb を展開する準備をしています ...
node-ansi-color-table (1.0.0-1) を展開しています...
以前に未選択のパッケージ node-archy を選択しています。
.../28-node-archy_1.0.0-1_all.deb を展開する準備をしています ...
node-archy (1.0.0-1) を展開しています...
以前に未選択のパッケージ node-balanced-match を選択しています。
.../29-node-balanced-match_0.4.2-1_all.deb を展開する準備をしています ...
node-balanced-match (0.4.2-1) を展開しています...
以前に未選択のパッケージ node-inherits を選択しています。
.../30-node-inherits_2.0.3-1_all.deb を展開する準備をしています ...
node-inherits (2.0.3-1) を展開しています...
以前に未選択のパッケージ node-block-stream を選択しています。
.../31-node-block-stream_0.0.9-1_all.deb を展開する準備をしています ...
node-block-stream (0.0.9-1) を展開しています...
以前に未選択のパッケージ node-concat-map を選択しています。
.../32-node-concat-map_0.0.1-1_all.deb を展開する準備をしています ...
node-concat-map (0.0.1-1) を展開しています...
以前に未選択のパッケージ node-brace-expansion を選択しています。
.../33-node-brace-expansion_1.1.6-1+deb9u1_all.deb を展開する準備をしています ...
node-brace-expansion (1.1.6-1+deb9u1) を展開しています...
以前に未選択のパッケージ node-delayed-stream を選択しています。
.../34-node-delayed-stream_0.0.5-1_all.deb を展開する準備をしています ...
node-delayed-stream (0.0.5-1) を展開しています...
以前に未選択のパッケージ node-combined-stream を選択しています。
.../35-node-combined-stream_0.0.5-1_all.deb を展開する準備をしています ...
node-combined-stream (0.0.5-1) を展開しています...
以前に未選択のパッケージ node-cookie-jar を選択しています。
.../36-node-cookie-jar_0.3.1-1_all.deb を展開する準備をしています ...
node-cookie-jar (0.3.1-1) を展開しています...
以前に未選択のパッケージ node-forever-agent を選択しています。
.../37-node-forever-agent_0.5.1-1_all.deb を展開する準備をしています ...
node-forever-agent (0.5.1-1) を展開しています...
以前に未選択のパッケージ node-mime を選択しています。
.../38-node-mime_1.3.4-1_all.deb を展開する準備をしています ...
node-mime (1.3.4-1) を展開しています...
以前に未選択のパッケージ node-form-data を選択しています。
.../39-node-form-data_0.1.0-1_all.deb を展開する準備をしています ...
node-form-data (0.1.0-1) を展開しています...
以前に未選択のパッケージ node-minimatch を選択しています。
.../40-node-minimatch_3.0.3-1_all.deb を展開する準備をしています ...
node-minimatch (3.0.3-1) を展開しています...
以前に未選択のパッケージ node-glob を選択しています。
.../41-node-glob_7.1.1-1_all.deb を展開する準備をしています ...
node-glob (7.1.1-1) を展開しています...
以前に未選択のパッケージ node-rimraf を選択しています。
.../42-node-rimraf_2.5.4-2_all.deb を展開する準備をしています ...
node-rimraf (2.5.4-2) を展開しています...
以前に未選択のパッケージ node-mkdirp を選択しています。
.../43-node-mkdirp_0.5.0-1_all.deb を展開する準備をしています ...
node-mkdirp (0.5.0-1) を展開しています...
以前に未選択のパッケージ node-graceful-fs を選択しています。
.../44-node-graceful-fs_4.1.11-1_all.deb を展開する準備をしています ...
node-graceful-fs (4.1.11-1) を展開しています...
以前に未選択のパッケージ node-fstream を選択しています。
.../45-node-fstream_1.0.10-1+deb9u1_all.deb を展開する準備をしています ...
node-fstream (1.0.10-1+deb9u1) を展開しています...
以前に未選択のパッケージ node-fstream-ignore を選択しています。
.../46-node-fstream-ignore_0.0.6-2_all.deb を展開する準備をしています ...
node-fstream-ignore (0.0.6-2) を展開しています...
以前に未選択のパッケージ node-github-url-from-git を選択しています。
.../47-node-github-url-from-git_1.4.0-1_all.deb を展開する準備をしています ...
node-github-url-from-git (1.4.0-1) を展開しています...
以前に未選択のパッケージ node-nopt を選択しています。
.../48-node-nopt_3.0.6-3_all.deb を展開する準備をしています ...
node-nopt (3.0.6-3) を展開しています...
以前に未選択のパッケージ node-npmlog を選択しています。
.../49-node-npmlog_0.0.4-1_all.deb を展開する準備をしています ...
node-npmlog (0.0.4-1) を展開しています...
以前に未選択のパッケージ node-osenv を選択しています。
.../50-node-osenv_0.1.0-1_all.deb を展開する準備をしています ...
node-osenv (0.1.0-1) を展開しています...
以前に未選択のパッケージ node-tunnel-agent を選択しています。
.../51-node-tunnel-agent_0.3.1-1_all.deb を展開する準備をしています ...
node-tunnel-agent (0.3.1-1) を展開しています...
以前に未選択のパッケージ node-json-stringify-safe を選択しています。
.../52-node-json-stringify-safe_5.0.0-1_all.deb を展開する準備をしています ...
node-json-stringify-safe (5.0.0-1) を展開しています...
以前に未選択のパッケージ node-qs を選択しています。
.../53-node-qs_2.2.4-1_all.deb を展開する準備をしています ...
node-qs (2.2.4-1) を展開しています...
以前に未選択のパッケージ node-request を選択しています。
.../54-node-request_2.26.1-1_all.deb を展開する準備をしています ...
node-request (2.26.1-1) を展開しています...
以前に未選択のパッケージ node-semver を選択しています。
.../55-node-semver_5.3.0-1_all.deb を展開する準備をしています ...
node-semver (5.3.0-1) を展開しています...
以前に未選択のパッケージ node-tar を選択しています。
.../56-node-tar_2.2.1-1_all.deb を展開する準備をしています ...
node-tar (2.2.1-1) を展開しています...
以前に未選択のパッケージ node-which を選択しています。
.../57-node-which_1.2.11-1_all.deb を展開する準備をしています ...
node-which (1.2.11-1) を展開しています...
以前に未選択のパッケージ node-gyp を選択しています。
.../58-node-gyp_3.4.0-1_all.deb を展開する準備をしています ...
node-gyp (3.4.0-1) を展開しています...
以前に未選択のパッケージ node-ini を選択しています。
.../59-node-ini_1.1.0-1_all.deb を展開する準備をしています ...
node-ini (1.1.0-1) を展開しています...
以前に未選択のパッケージ node-lockfile を選択しています。
.../60-node-lockfile_0.4.1-1_all.deb を展開する準備をしています ...
node-lockfile (0.4.1-1) を展開しています...
以前に未選択のパッケージ node-lru-cache を選択しています。
.../61-node-lru-cache_4.0.2-1_all.deb を展開する準備をしています ...
node-lru-cache (4.0.2-1) を展開しています...
以前に未選択のパッケージ node-mute-stream を選択しています。
.../62-node-mute-stream_0.0.7-1_all.deb を展開する準備をしています ...
node-mute-stream (0.0.7-1) を展開しています...
以前に未選択のパッケージ node-normalize-package-data を選択しています。
.../63-node-normalize-package-data_2.3.5-2_all.deb を展開する準備をしています ...
node-normalize-package-data (2.3.5-2) を展開しています...
以前に未選択のパッケージ node-read を選択しています。
.../64-node-read_1.0.7-1_all.deb を展開する準備をしています ...
node-read (1.0.7-1) を展開しています...
以前に未選択のパッケージ node-read-package-json を選択しています。
.../65-node-read-package-json_1.2.4-1_all.deb を展開する準備をしています ...
node-read-package-json (1.2.4-1) を展開しています...
以前に未選択のパッケージ node-retry を選択しています。
.../66-node-retry_0.6.0-1_all.deb を展開する準備をしています ...
node-retry (0.6.0-1) を展開しています...
以前に未選択のパッケージ node-sha を選択しています。
.../67-node-sha_1.2.3-1_all.deb を展開する準備をしています ...
node-sha (1.2.3-1) を展開しています...
以前に未選択のパッケージ node-slide を選択しています。
.../68-node-slide_1.1.4-1_all.deb を展開する準備をしています ...
node-slide (1.1.4-1) を展開しています...
以前に未選択のパッケージ npm を選択しています。
.../69-npm_1.4.21+ds-2_all.deb を展開する準備をしています ...
npm (1.4.21+ds-2) を展開しています...
node-lockfile (0.4.1-1) を設定しています ...
node-spdx-expression-parse (1.0.4-1) を設定しています ...
node-qs (2.2.4-1) を設定しています ...
node-osenv (0.1.0-1) を設定しています ...
node-ansi (0.3.0-2) を設定しています ...
libjs-node-uuid (1.4.0-1) を設定しています ...
node-hosted-git-info (2.1.5-1) を設定しています ...
node-delayed-stream (0.0.5-1) を設定しています ...
libjs-inherits (2.0.3-1) を設定しています ...
node-tunnel-agent (0.3.1-1) を設定しています ...
node-balanced-match (0.4.2-1) を設定しています ...
node-node-uuid (1.4.0-1) を設定しています ...
node-yallist (2.0.0-1) を設定しています ...
node-slide (1.1.4-1) を設定しています ...
libssl-dev:armhf (1.1.0l-1~deb9u1) を設定しています ...
node-github-url-from-git (1.4.0-1) を設定しています ...
node-pseudomap (1.0.2-1) を設定しています ...
node-spdx-license-ids (1.2.2-1) を設定しています ...
node-combined-stream (0.0.5-1) を設定しています ...
node-wrappy (1.0.2-1) を設定しています ...
node-mime (1.3.4-1) を設定しています ...
node-abbrev (1.0.9-1) を設定しています ...
node-semver (5.3.0-1) を設定しています ...
node-retry (0.6.0-1) を設定しています ...
node-forever-agent (0.5.1-1) を設定しています ...
node-underscore (1.8.3~dfsg-1) を設定しています ...
gyp (0.1+20150913git1f374df9-1) を設定しています ...
node-json-stringify-safe (5.0.0-1) を設定しています ...
node-inherits (2.0.3-1) を設定しています ...
node-graceful-fs (4.1.11-1) を設定しています ...
node-archy (1.0.0-1) を設定しています ...
node-path-is-absolute (1.0.0-1) を設定しています ...
node-builtin-modules (1.1.1-1) を設定しています ...
man-db (2.7.6.1-2) のトリガを処理しています ...
node-isexe (1.1.2-1) を設定しています ...
libssl-doc (1.1.0l-1~deb9u1) を設定しています ...
node-spdx-correct (1.0.2-1) を設定しています ...
node-async (0.8.0-1) を設定しています ...
node-cookie-jar (0.3.1-1) を設定しています ...
node-mute-stream (0.0.7-1) を設定しています ...
node-form-data (0.1.0-1) を設定しています ...
node-request (2.26.1-1) を設定しています ...
node-concat-map (0.0.1-1) を設定しています ...
node-ini (1.1.0-1) を設定しています ...
node-mkdirp (0.5.0-1) を設定しています ...
node-once (1.4.0-2) を設定しています ...
node-sha (1.2.3-1) を設定しています ...
node-fs.realpath (1.0.0-1) を設定しています ...
libuv1-dev:armhf (1.18.0-3~bpo9+1) を設定しています ...
node-brace-expansion (1.1.6-1+deb9u1) を設定しています ...
node-ansi-color-table (1.0.0-1) を設定しています ...
node-npmlog (0.0.4-1) を設定しています ...
node-is-builtin-module (1.0.0-1) を設定しています ...
node-nopt (3.0.6-3) を設定しています ...
node-which (1.2.11-1) を設定しています ...
node-lru-cache (4.0.2-1) を設定しています ...
node-block-stream (0.0.9-1) を設定しています ...
node-validate-npm-package-license (3.0.1-1) を設定しています ...
node-inflight (1.0.6-1) を設定しています ...
node-read (1.0.7-1) を設定しています ...
node-minimatch (3.0.3-1) を設定しています ...
nodejs-dev (8.11.1~dfsg-2~bpo9+1) を設定しています ...
node-normalize-package-data (2.3.5-2) を設定しています ...
node-glob (7.1.1-1) を設定しています ...
node-rimraf (2.5.4-2) を設定しています ...
node-read-package-json (1.2.4-1) を設定しています ...
node-fstream (1.0.10-1+deb9u1) を設定しています ...
node-fstream-ignore (0.0.6-2) を設定しています ...
node-tar (2.2.1-1) を設定しています ...
node-gyp (3.4.0-1) を設定しています ...
npm (1.4.21+ds-2) を設定しています ...====

エラー出ず終わりました。バージョンチェックの結果は

pi@raspberrypi:~ $ npm -v
1.4.21
(node:3126) [DEP0022] DeprecationWarning: os.tmpDir() is deprecated. Use os.tmpdir() instead.

んん?npmは入ったようですがWarningが出てしまいました。一応Blynkを入れてみます。

pi@raspberrypi:~ $ sudo npm install blynk-library -g
(node:2907) [DEP0022] DeprecationWarning: os.tmpDir() is deprecated. Use os.tmpdir() instead.
/usr/local/bin/blynk-client -> /usr/local/lib/node_modules/blynk-library/bin/blynk-client.js
/usr/local/bin/blynk-ctrl -> /usr/local/lib/node_modules/blynk-library/bin/blynk-ctrl.js
blynk-library@0.5.4 /usr/local/lib/node_modules/blynk-library

やっぱり同様のWarningが出ていますね…。ググったところnpmのバージョンが古すぎるのが問題のようです。

npmの再インストール

とりあえず一つ目の処方として次のコードでnpmのインストールを試みます。
Node.js - npmのアップデートができない|teratail

pi@raspberrypi:~ $ npm i -g npm
(node:4035) [DEP0022] DeprecationWarning: os.tmpDir() is deprecated. Use os.tmpdir() instead.
npm ERR! tar.unpack untar error /home/pi/.npm/npm/6.14.4/package.tgz
npm ERR! Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/npm'
npm ERR!  { Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/npm'
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'mkdir',
npm ERR!   path: '/usr/local/lib/node_modules/npm',
npm ERR!   fstream_type: 'Directory',
npm ERR!   fstream_path: '/usr/local/lib/node_modules/npm',
npm ERR!   fstream_class: 'DirWriter',
npm ERR!   fstream_stack: 
npm ERR!    [ '/usr/lib/nodejs/fstream/lib/dir-writer.js:35:25',
npm ERR!      '/usr/lib/nodejs/mkdirp/index.js:46:53',
npm ERR!      'FSReqWrap.oncomplete (fs.js:152:21)' ] }
npm ERR! 
npm ERR! Please try running this command again as root/Administrator.

npm ERR! System Linux 4.19.66-v7+
npm ERR! command "/usr/bin/node" "/usr/bin/npm" "i" "-g" "npm"
npm ERR! cwd /home/pi
npm ERR! node -v v8.11.1
npm ERR! npm -v 1.4.21
npm ERR! path /usr/local/lib/node_modules/npm
npm ERR! syscall mkdir
npm ERR! fstream_path /usr/local/lib/node_modules/npm
npm ERR! fstream_type Directory
npm ERR! fstream_class DirWriter
npm ERR! code EACCES
npm ERR! errno -13
npm ERR! stack Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/npm'
npm ERR! fstream_stack /usr/lib/nodejs/fstream/lib/dir-writer.js:35:25
npm ERR! fstream_stack /usr/lib/nodejs/mkdirp/index.js:46:53
npm ERR! fstream_stack FSReqWrap.oncomplete (fs.js:152:21)
npm ERR! 
npm ERR! Additional logging details can be found in:
npm ERR!     /home/pi/npm-debug.log
npm ERR! not ok code 0

こちらの方法ではダメだったようです。別の方法を探します。
Raspberry PiにNode.jsとnpmの最新版をインストールする - Qiita
同様のエラーと対策について詳しく報告されています。これは行けそうです。一つずつ実行していきます。

pi@raspberrypi:~ $ sudo apt-get update
ヒット:1 https://deb.nodesource.com/node_6.x stretch InRelease
ヒット:2 http://archive.raspberrypi.org/debian stretch InRelease
ヒット:3 http://raspbian.raspberrypi.org/raspbian stretch InRelease            
パッケージリストを読み込んでいます... 完了
pi@raspberrypi:~ $ sudo apt-get install -y nodejs npm
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
npm はすでに最新バージョン (1.4.21+ds-2) です。
nodejs はすでに最新バージョン (8.11.1~dfsg-2~bpo9+1) です。
アップグレード: 0 個、新規インストール: 0 個、削除: 0 個、保留: 0 個。
pi@raspberrypi:~ $ sudo npm cache clean
(node:4714) [DEP0022] DeprecationWarning: os.tmpDir() is deprecated. Use os.tmpdir() instead.
pi@raspberrypi:~ $ sudo npm install npm n -g
(node:4753) [DEP0022] DeprecationWarning: os.tmpDir() is deprecated. Use os.tmpdir() instead.
/usr/local/bin/n -> /usr/local/lib/node_modules/n/bin/n
npm WARN package.json path-is-inside@1.0.2 No README data
npm WARN package.json sorted-object@2.0.1 No README data
/usr/local/bin/npx -> /usr/local/lib/node_modules/npm/bin/npx-cli.js
/usr/local/bin/npm -> /usr/local/lib/node_modules/npm/bin/npm-cli.js
npm WARN package.json config-chain@1.1.12 No license field.
npm WARN package.json cyclist@0.2.2 No license field.
npm WARN package.json json-schema@0.2.3 No license field.
npm WARN package.json punycode@1.4.1 punycode is also the name of a node core module.
npm WARN package.json qrcode-terminal@0.12.0 No license field.
npm WARN package.json string_decoder@1.3.0 string_decoder is also the name of a node core module.
n@6.5.1 /usr/local/lib/node_modules/n

npm@6.14.4 /usr/local/lib/node_modules/npm
pi@raspberrypi:~ $ sudo n stable

  installing : node-v12.16.3
       mkdir : /usr/local/n/versions/node/12.16.3
       fetch : https://nodejs.org/dist/v12.16.3/node-v12.16.3-linux-armv7l.tar.xz
   installed : v12.16.3 (with npm 6.14.4)

Note: the node command changed location and the old location may be remembered in your current shell.
         old : /usr/bin/node
         new : /usr/local/bin/node
To reset the command location hash either start a new shell, or execute PATH="$PATH"

途中またWarningが出ていますね‥。とりあえずバージョンチェックします。

pi@raspberrypi:~ $ node -v
v8.11.1
pi@raspberrypi:~ $ npm -v
1.4.21
(node:4917) [DEP0022] DeprecationWarning: os.tmpDir() is deprecated. Use os.tmpdir() instead.

まだインストールが反映されていないようなので、ターミナルを再起動したのちもう一度チェックします。

pi@raspberrypi:~ $ exit
ログアウト

pi@raspberrypi:~ $ npm -v
6.14.4

pi@raspberrypi:~ $ node -v
v12.16.3

npm6.14.をインスタトール出来たっぽいです。
Blynkをインストールします。

pi@raspberrypi:~ $ sudo npm install blynk-library -g
/usr/local/bin/blynk-client -> /usr/local/lib/node_modules/blynk-library/bin/blynk-client.js
/usr/local/bin/blynk-ctrl -> /usr/local/lib/node_modules/blynk-library/bin/blynk-ctrl.js
+ blynk-library@0.5.4
updated 1 package in 2.108s

pi@raspberrypi:~ $ sudo npm install onoff -g
+ onoff@6.0.0
updated 1 package and moved 3 packages in 3.073s

エラーなくインストールできました!!
Blynkサーバーを立ち上げます。

pi@raspberrypi:~ $ export PATH=$PATH:/opt/nodejs/bin/
pi@raspberrypi:~ $ unset NODE_PATH
pi@raspberrypi:~ $ blynk-client <トークン>

    ___  __          __
   / _ )/ /_ _____  / /__
  / _  / / // / _ \/  '_/
 /____/_/\_, /_//_/_/\_\
        /___/

  Give Blynk a Github star! => https://github.com/vshymanskyy/blynk-library-js

Connecting to: blynk-cloud.com 443
SSL authorization...
Connected
Authorized
Blynk ready.

無事立ち上がりました!!スマホ側でもonlineと表示されており、接続がうまく行っているようです。
次はアプリのBlynkからGPIB18に信号を送ります。これでLEDが光るはずです。

No direct pin operations available.
Maybe you need to install mraa or onoff modules?

No direct pin operations available.
Maybe you need to install mraa or onoff modules?

アプリでスイッチを押すと、ターミナルにこのようなエラーが表示されるだけでLEDは光りませんでした。
コネクション自体は上手く行ってるのであと一歩というような感じはします。エラー文でググると同様の報告はありましたが、遂に解決には至りませんでした…。

別のアプローチ

もはや何が問題だったのか分かりませんが、別な方法でBlynkをインストールするとあっさりとLチカが成功しました。
こちらの記事にまとめています。

学んだこと

・公式のチュートリアル通りに進めたとしても成功するとは限らない。
・根本的な部分を理解していないと苦労する。別の方法でうまく行ったとしても自分の知識レベルの問題は解決していない。