モータードライバーが動かないので切り分けメモ

ラズパイからモータードライバー経由でモーター2つを動かそうとしているけど期待どおりに動かない。ハードの切り分けなど殆どしたことがないので切り分け作業用のメモを残しておく。基本時系列に沿って書いていく。

切り分け開始前

GPIO22とGPIO13とGPIO17とGPIO27でセットでVREFからの電圧で動かす予定。組み合わせは以下の認識。

GPIO221100
GPIO130110
動作動く(順方向)動かない(ロック)動く(逆方向)動かない(空転)
GPIO22と13(17と22のセットも同じ動作)

配線をいじったりしたが部品によって動作が違いそう。整理しながらやらないとハマりそうなので腰を据えて調べていく。

切り分け開始

モーター動作用の電源は電池ボックスから5.5Vほどで取れている。モーターに電源を直結しても想定どおりの回転方向で動作。

各GPIO毎に電圧をかけて個別にLEDを光らせたところPINからの電圧は正常どおり出ている模様。

gpio -g write n 1
gpio -g write n 0

モータードライバーは秋月で売っていたBD6211Fを手作業で半田付けしたもの。半田付けの失敗などの理由でモータードライバーが正常に動作していない気がする。2つのモータードライバーで動作が違いそうなのでマジックで色を塗って見分けられるようにする。以降ドライバー白/黒。確認した結果は以下(GPIO17,27の組合せでも同じ動作だったが表は省略)

GPIO221100
GPIO130110
想定動く(順方向)動かない(ロック)動く(逆方向)動かない(空転)
結果×
ドライバー白
GPIO221100
GPIO130110
想定動く(順方向)動かない(ロック)動く(逆方向)動かない(空転)
結果×(かろうじて動いている)×
ドライバー黒

結果は

  • 白/黒ともに動かないが、それぞれ別の動きをしている
  • GPIO22,13ともに1の時にロックは動作している
  • ドライバー白の順方向が動作しないとはいえかろうじて動いている

どこかがショートしていて動作に必要な量がモーターまでうまく流れていない気がする

通電状況を調べてみる

白から

GPIO221100
GPIO130110
想定動く(順方向)動かない(ロック)動く(逆方向)動かない(空転)
結果×
V少し(0.1vぐらい)と0を数秒で行き来040
ma少し(0.1vぐらい)と0を数秒で行き来0250以上0
ドライバー白

黒も

GPIO221100
GPIO130110
想定動く(順方向)動かない(ロック)動く(逆方向)動かない(空転)
結果×(かろうじて動いている)×
V少し(0.5vぐらい)と0を1秒で00.05v程度安定0
ma100mA程度と0を1秒で00.1ma程度で安定0
ドライバー黒

これから

Raspberry PIのGPIO初期値

いま作っている殺菌ロボットがラズパイの電源を入れるとモーターが動き出していた。

使っているB+だとBCMで17&27,22&6にモータードライバー2つのFIN/RINをそれぞれつなげていたのだけど、GPIO6だけ初期値が1(IN)。

pi@raspberrypi:~$ gpio readall
 +-----+-----+---------+------+---+---Pi B+--+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |
 |   2 |   8 |   SDA.1 |   IN | 1 |  3 || 4  |   |      | 5v      |     |     |
 |   3 |   9 |   SCL.1 |   IN | 1 |  5 || 6  |   |      | 0v      |     |     |
 |   4 |   7 | GPIO. 7 |   IN | 1 |  7 || 8  | 1 | ALT0 | TxD     | 15  | 14  |
 |     |     |      0v |      |   |  9 || 10 | 1 | ALT0 | RxD     | 16  | 15  |
 |  17 |   0 | GPIO. 0 |   IN | 0 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  |
 |  27 |   2 | GPIO. 2 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     |
 |  22 |   3 | GPIO. 3 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  |
 |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  |
 |  10 |  12 |    MOSI |   IN | 0 | 19 || 20 |   |      | 0v      |     |     |
 |   9 |  13 |    MISO |   IN | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  |
 |  11 |  14 |    SCLK |   IN | 0 | 23 || 24 | 1 | IN   | CE0     | 10  | 8   |
 |     |     |      0v |      |   | 25 || 26 | 1 | IN   | CE1     | 11  | 7   |
 |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   |
 |   5 |  21 | GPIO.21 |   IN | 1 | 29 || 30 |   |      | 0v      |     |     |
 |   6 |  22 | GPIO.22 |   IN | 1 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  |
 |  13 |  23 | GPIO.23 |   IN | 0 | 33 || 34 |   |      | 0v      |     |     |
 |  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  |
 |  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  |
 |     |     |      0v |      |   | 39 || 40 | 0 | IN   | GPIO.29 | 29  | 21  |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+---Pi B+--+---+------+---------+-----+-----+

XORが1の時に動作をするのでGPIO22&GPIO6が1で動作していた。GPIOの初期値は(起動時にソフト的に書き換えられるけど)ハード的に決まっているもよう。トラブルで電源入れたら動き出すと気持ち悪いので初期値0のGPIOにつなげることにする。

古いRaspberry PIではVS Code remoteは使えない

“Unsupported architecture: armv6l”といわれる。使おうとした端末はRaspberry PI1のB+

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
[11:47:52.753] > ready: b428c5836ec1
[11:47:52.769] > Linux 4.19.66+ #1253 Thu Aug 15 11:37:30 BST 2019
[11:47:52.770] Platform: linux
[11:47:52.890] > b428c5836ec1: running
[11:47:52.950] > Unsupported architecture: armv6l
[11:47:52.952] > b428c5836ec1##27##
[11:47:52.952] Received install output: b428c5836ec1##27##
[11:47:52.953] Unsupported architecture
[11:47:52.953] Terminating local server
[11:47:52.956] Resolver error: The remote host's architecture is not supported
[11:47:52.963] ------

要望はあるけど使えないらしい。zeroでも使えないのね。

コロナ対策の換気メモ

厚生労働省の換気に関する資料をみると一人辺り30立米/hが取り敢えずの推奨値。なお、ゼロリスクということではなく一般事業者がとれる現実的な水準としての値。

同時に10人入れるとして300立米/hの換気。遮光をしたエリアで紫外線使えば余裕をもって殺菌できる量。その3倍ぐらいになったら水銀灯タイプの電球だとオゾンがですぎる気がする。手持ちの電球だと600立米/hぐらいが換気がわりの殺菌ができる現実ライン?LEDタイプだとオゾン発生する波長しぼれている模様なのでオゾンを気にしないで出力あげられる?

なお、一時的な来客増ならば自然/機械換気を積みませる。なのでエアコンの效きが悪くなることを受け入れられる範囲で同時入店人数を増やせる。うちは夏の昼はエアコンいっぱいいっぱいなので+50%も厳しいけどそれ以外の時間は+100%ぐらいは余力あり。

LEDは単品だと電球より高いけれどもソケットが不要になったりするので値段的にはトントンぐらいになると思う。部品が減るので組み立てはむしろ楽?最近聞く人体に影響のない波長のは売っているのを見たことはないけど250nmぐらいのはAliとかで売っている。今のところ供給も安定してそう。

店の消毒をオゾンで行う検討メモ(クライミングジム)

目的

表面は紫外線で殺菌できるけど(ガバとか)裏面は殺菌できない。裏面もある程度手軽に殺菌したい。

要件

  • 毎日1回裏面もウィルス1/10ぐらいまで殺菌
  • 色々腐食されそうだけどほとんどのものはDIYで修復できるのでもろもろの寿命が短くなるのはこのさい無視してOK
  • 殺菌中は原則として店内に立ち入らないけど緊急で入れる程度の濃度で押さえておく

要件を満たす条件

  • 1日1回やればよいので2330-0930の10時間が稼働時間(換気込み)
  • 緊急時の立ち入りを考え濃度は0.3ppm程度まで(参照
  • CT60(ppm*min=60)で1/10から1/100程度らしい(参照)のでCT60を目指す(表現あっている?)

計算

換気等を考え殺菌可能時間は9時間程度。オゾン発生させて濃度が安定するまで1時間程度らしいので8時間=720分。目標値の60にするには60/720で0.083ppm程度。

こちらに目的の濃度にするために必要なオゾン発生量の式が乗っているがそれに基づいて計算する。うちの店の体積が約900m3(幅7.5,奥行22,高さ5.5)なので900÷0.083÷2.14で160mg/h~320mg/h程度。薄まらない環境(発生量*0.5が安定する平均濃度)で計算しても濃度は0.17ppm程度で緊急時の立ち入りもできる。8wのオゾンランプ(参考:極光電気)1本で16mg/hなので20本あれば足りる。

AdoptOpenJDKをyumインストール(CentOS7)

CentOSにAdoptOpenJDKのでメモ

環境

  • CentOS Linux release 7.6.1810 (Core)
  • AdoptOpenJDK (build 11.0.3+7)

yumレポジトリ追加

基本公式の通り。(RPM installation on Centos, RHEL, or Fedoraあたり)

cat < /etc/yum.repos.d/adoptopenjdk.repo
[AdoptOpenJDK]
name=AdoptOpenJDK
baseurl=http://adoptopenjdk.jfrog.io/adoptopenjdk/rpm/centos/7/$(uname -m)
enabled=1
gpgcheck=1
gpgkey=https://adoptopenjdk.jfrog.io/adoptopenjdk/api/gpg/key/public
EOF

https://adoptopenjdk.net/installation.html?variant=openjdk11&jvmVariant=hotspot#x64_linux-jdk

確認及びインストール

選択肢確認

yum list adoptopenjdk*


読み込んだプラグイン:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
base: ftp.iij.ad.jp
epel: ftp.jaist.ac.jp
extras: ftp.iij.ad.jp
nux-dextop: mirror.li.nux.ro
updates: mirror.horizon.vn
インストール済みパッケージ
adoptopenjdk-11-hotspot.x86_64 11.0.3+7-1 @AdoptOpenJDK
利用可能なパッケージ
adoptopenjdk-11-hotspot.x86_64 11.0.4+11-1 AdoptOpenJDK
adoptopenjdk-11-hotspot-jre.x86_64 11.0.4+11-1 AdoptOpenJDK
adoptopenjdk-11-openj9.x86_64 11.0.4+11-1 AdoptOpenJDK
adoptopenjdk-11-openj9-jre.x86_64 11.0.4+11-1 AdoptOpenJDK
adoptopenjdk-12-hotspot.x86_64 12.0.2+10-1 AdoptOpenJDK
adoptopenjdk-12-hotspot-jre.x86_64 12.0.2+10-1 AdoptOpenJDK
adoptopenjdk-12-openj9.x86_64 12.0.2+10-1 AdoptOpenJDK
adoptopenjdk-12-openj9-jre.x86_64 12.0.2+10-1 AdoptOpenJDK
adoptopenjdk-8-hotspot.x86_64 8u222_b10-1 AdoptOpenJDK
adoptopenjdk-8-hotspot-jre.x86_64 8u222_b10-1 AdoptOpenJDK
adoptopenjdk-8-openj9.x86_64 8u222_b10.openj9_0.15.1-1 AdoptOpenJDK
adoptopenjdk-8-openj9-jre.x86_64 8u222_b10.openj9_0.15.1-1 AdoptOpenJDK

インストール

好きな奴

sudo yum install adoptopenjdk-11-hotspot.x86_64

設定

sudo update-alternatives –config java

(2020/9/30追記)

最近だと多分こっち

sudo alternatives –config java

4 プログラムがあり ‘java’ を提供します。
選択 コマンド
1 java-11-openjdk.x86_64 (/usr/lib/jvm/java-11-openjdk-11.0.3.7-0.el7_6.x86_64/bin/java)
2 java-1.7.0-openjdk.x86_64 (/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.221-2.6.18.0.el7_6.x86_64/jre/bin/java)
3 java-1.8.0-openjdk.x86_64 (/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.212.b04-0.el7_6.x86_64/jre/bin/java)
4 /usr/lib/jvm/adoptopenjdk-11-hotspot/bin/java
Enter を押して現在の選択 [+] を保持するか、選択番号を入力します:

古いAndroidとラズパイで多機能モニターを作る

目的

以前作った店の呼び出しブザーは押されたらline&メール通知を飛ばすようになっている。ただし、押した側からみるとなんの反応も無いため動作しているか不安。なので、googleクラウドで合成させた音声を古いAndroidで流すようにした。(+画面でメッセージ表示)

なお、この記事を書いている段階ではエミュレータで動いたのみ。実機は古すぎて現行のAndroidStudioで素直にビルドできていない。ビルドする方法を調べてから再挑戦。

環境

呼出側

開発環境

  • OS Centos7
  • python 3.7.0
  • the Cloud Client Libraries for the Cloud Text-to-Speech API のアルファ版(ドキュメントが古いだけかも)

想定環境

  • raspberry pi typeB
  • python 3.7.0
  • the Cloud Client Libraries for the Cloud Text-to-Speech API のアルファ版(ドキュメントが古いだけかも)

触った限り、本番環境用のraspberry pi typeBで動作はするもののCloud Text-to-Speech APIの呼出が重い。10文字程度で3秒ほど。gRPCの接続/暗号化処理周りでcpuがいっぱいいっぱいになっている模様で端末能力の問題と思う。

呼び出される側(android)

開発環境

  • android studio 3.3.1
  • SHL22
  • Androidバージョン 4.2.2
  • compileSdkVersion/targetSdkVersion 28
  • minSdkVersion 16 (開発端末がこの時代の)
  • com.nanohttpd:nanohttpd-webserver:2.1.1

想定環境

  • IS11PT
  • Androidバージョン2.3.4

端末が古すぎるので開発環境を簡単に整えられなかったので、まずは新しめ(といっても8年ぐらい前)の端末で作成。

呼出側機能

概要

googleのText-to-Speech API を呼び出して、requestでAndroid内に起動させているnanohttpsにpostをするのみ。Text-to-Speech APIはほぼドキュメントどおり。requestのファイルのところはパラメータにMIMEタイプまでしていしてやらないとAndroid側で使っっているnanohttpdでファイルをよみ込めなかったので注意。

ソース

#!/usr/bin/env python3
# coding: utf-8
import requests

from google.cloud import texttospeech

def make_voice(current_time):
    _make_voice(current_time,"少々お待ちください")

def _make_voice(current_time,msg):
    file_name = 'output.mp3'
    client = texttospeech.TextToSpeechClient()
    synthesis_input = texttospeech.types.SynthesisInput(text=msg)

    voice = texttospeech.types.VoiceSelectionParams(
        language_code='ja-JP',
        ssml_gender=texttospeech.enums.SsmlVoiceGender.NEUTRAL)
    audio_config = texttospeech.types.AudioConfig(
        audio_encoding=texttospeech.enums.AudioEncoding.MP3)

    response = client.synthesize_speech(synthesis_input, voice, audio_config)

    with open('output.mp3', 'wb') as out:
        out.write(response.audio_content)

    url = 'http://127.0.0.1:8081/msg/'
    files = {'file': (file_name,open(file_name, 'rb'),'audio/mpeg')}
    payload = {"message" :  msg}
    r = requests.post(url ,files=files ,params=payload)
    print(r)
if __name__ == '__main__':
    make_voice( '2019/02/12 固定' )

呼び出される側

概要

内部で立ち上げているnanohttpdで情報(リクエスト)を受け取って、音声再生し画面にメッセージを出しておく。ちなみに、古いバージョンで動かそうとしてたので文法が古いものから新しいものまで混在しているので適宜読み替えてください。

package jp.co.epea.monitor;

import android.media.MediaPlayer;
import android.os.Handler;
import android.widget.TextView;


import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import fi.iki.elonen.NanoHTTPD;

import static fi.iki.elonen.NanoHTTPD.Response.Status.NOT_FOUND;
import static fi.iki.elonen.NanoHTTPD.Response.Status.OK;

public class MoniterServer extends NanoHTTPD {
    private Handler handler;
    private TextView textView;

    public MoniterServer(Handler handler, TextView textView) {
        super(8080);
        this.handler = handler;
        this.textView = textView;

    }

    @Override
    public Response serve(IHTTPSession session) {
        Method method = session.getMethod();
        String uri = session.getUri();

        System.out.println(method + " '" + uri + "' ");
        if (uri.equals("/msg/")) {
            final String msg = session.getParms().get("message");


            Map<String, String> files = new HashMap<>();
            try {
                session.parseBody(files);
            } catch (IOException ioe) {
                return new NanoHTTPD.Response(NOT_FOUND, MIME_HTML, "none");
            } catch (ResponseException re) {
                return new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
            }

            MediaPlayer player = new MediaPlayer();
            try {
                player.setDataSource(files.get("file"));
                player.prepare();
                player.start();
            } catch (IOException e) {
                e.printStackTrace();
            }

            handler.post(new Runnable() {
                @Override
                public void run() {
                    textView.setText(msg);
                }
            });
            return new NanoHTTPD.Response(OK, MIME_HTML, "ok");
        } else if (uri.equals("/clear/")) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    textView.setText("");
                }
            });
            return new NanoHTTPD.Response(OK, MIME_HTML, "ok");
        }
        return new NanoHTTPD.Response(NOT_FOUND, MIME_HTML, "none");
    }
}

いつもどおり関係ないけど

そういえば嫁のサイトをazureにも置きました

GitlabPagesでアカウント名にドットが入っているとうまく動かない

事象

アカウント名がfoo.varとドットが入っているとGitlabPagesは https://foo.var.gitlab.io/val  みたいなURLになる。

しかし、GitlabPagesからもらえる証明書は*.gitlab.ioのワイルドカード証明書なため、foo.varと二段階になっているサブドメインではエラーとなる。

対処

URLにこだわりはないのでドットの入っていないグループを作成しそちらにレポジトリ移管した。https://epea.gitlab.io/sanko5/ (epeaが新しくつくったグループ)

自力でLetsEncript動かしてもよさそうなきもするけどそれはそれで面倒そうなので試していない。

ElementUIのForm-Item Attributesのerrorがうまく動作しない

やりたいこと

現在、ElementUIとNuxt.jsでサーバサイドで発生したエラー(一意制約違反)のメッセージを
Form-Item Attributesのerrorに設定することで表示させているけど、2度目の操作で祭表示されない。

暫定対処

ソースをみるとerrorをwatchしているけど、その中でエラーメッセージのレンダリングの判定を操作している。多分watchの反映がされる前に、レンダリングが走っている模様。

取り敢えず、待ちの処理を入れて対策。きちんと同期をさせていないがそもそもこんな状況になるということは何か、入り口が間違っている気がする。terateilに初めて質問してみたけどまだ回答はない。

ソース

抜粋。(全部はterateilに張っておいた)

<template>
  <section class="container">
    <el-form
      ref="userForm"
      :model="userForm"
      :rules="rules"
      @submit.native.prevent>
      <el-form-item
        ref="nickname"
        :error="errorMessage"
        label="ニックネームを登録してください。"
        prop="nickname">
        <el-input
          v-model="userForm.nickname"
          type="text"
          autocomplete="off"/>
      </el-form-item>
      <el-button
        type="primary"
        @click="registNickName">登録</el-button>
    </el-form>

  </section>
</template>

<script>

export default {
  data: function() {
    return {
      userForm: {
        nickname: ''
      },
      errorMessage: '',
      rules: {
        nickname: [
          {
            required: true,
            whitespace: true,
            message: 'ニックネームは必ず入力してください。'
          }
        ]
      }
    }
  },
  methods: {
    registNickName: async function() {
        // 一意制約(ニックネーム重複)

        /*
          XXX 2度目の時にthis.errorを一度クリアしないと発火しない。
          ただし、少しまってやらないといけない。
          データの変更が非同期に走っているから整合性がとれなくなっている?
         */
        this.errorMessage = ''
        await new Promise(function(resolve, reject) {
          window.setTimeout(resolve, 10)
        })
        const duplicateMessage =
          this.userForm.nickname +
          'は既に使用されています。別のニックネームにしてください。'
        this.errorMessage = duplicateMessage

        return

    }
  }
}
</script>

いつもどおり関係ないけど

掲載してもらったBOLLOGのリンクがgoogleに認識されない。大手だけど更新がしばらく止まってるとクローラー見に行かないのかな??