閑話休題1.いろいろラズパイの設定をいじる

前置き

別にサボってた訳ではないです。ちょっとグラブルが忙しくて…(言い訳)
一応カメラを接続していろいろいじり中です。近いうちにカメラいじりの中間報告しようかと。
なお来週から古戦場…

やること1 ユーザー権限周りの設定変更

ずっとpiユーザーで作業していましたけど、実は色々不便なのと、あとpiユーザーのままだとセキュリティ的に不安…らしい? 半スタンドアロンな端末になるのであまり気にしなくていいとは思いますけど。
なのでとりあえずpiユーザーを削除して新たに管理者権限を持つユーザーを作成します。以下のサイトさんを参考に。
初心者向!Raspberry Pi 最低限のセキュリティ設定【所要時間 30分】 - Qiita

やること2 WinSCPをroot権限で扱う

次いで、WinSCPは初期設定のままだとroot権限のファイル/ディレクトリを操作できなくて不便なので、以下サイトさんを参考にWinSCPからroot権限での操作ができるように環境設定を行います。
WinSCPをroot権限で使用する - Qiita

やること3 i起動時にスクリプトを自動実行

最後に、前回作成したシャットダウンスイッチと冷却ファン制御をdaemon化します。
これで次からラズパイ起動時に両制御が自動実行されるようになるのでCPU温度を気にしなくていいですし落とすのもラクになりますね。
以下サイトさんを参考に。
pythonスクリプトをdaemonにする[systemd編] - Qiita
ファン制御プログラムはそのまま、シャットダウンスイッチプログラムは一部整理した以下のソースコードを用います。

そしてサービスを作成登録して起動を確認。
f:id:nekhbet:20200211233116p:plain:w300
無事に動きました。 今日はここまで。
次こそはカメラ関係の記事を…あげたいですねぇ… 一応目標として27日までにカメラをある程度モノにできるようにするつもりです。

その6.フルカラーLEDを制御する

前置き

前回はファン制御ができましたので、今度はLEDを制御していきます。巷ではLチカとか言うそうです。ハイカラですね。 

配線とソースコード準備

以下のサイトさんを参考に配線とフルカラーLED制御を行います。
pigpioでNeopixelを使う | BotaLab
今回SPIを使う必要がある為、利用するピンは全く同じです。  f:id:nekhbet:20200206013032j:plain
上記サイトさんで紹介されているのはWS2812Bというマイコンが入ったタイプですが、今回私が用意したものはWS2811が入ったタイプですので、そのままでは正常に動作しません。なので内容を以下のように調整します。

  • SPI周波数 1wordの転送時間が倍の2500nsですので、半分の3200000に設定
  • ビット判別 仕様を元に1=0xF8(11111000)、0=0x80(10000000)とします
  • RGB→BRG変換処理 WS2811ではRGBの順序で受け取るので不要
  • その他 stop関数をデストラクタに変更。今回操作するLEDは一個のみなので処理を最適化・簡素化

これらの変更を行ったソースが以下になります。

動作確認

ちゃんとうっすら青く光りました。
f:id:nekhbet:20200206013101j:plain

これで無事に動作…と言いたいですが、実は問題が少し発生しています。というのも、稀にLEDが想定しない色を出力するのです。原因は不明…
WS281x系は時間による判別に依存する部分が大きいらしく、おそらく周波数値の調整やマイコンの状態を監視する何かが必要なのかもしれませんね…
この問題は他の事を進めつつ調べて、後日調整を施してみようと思います。

さて、明日はいよいよカメラ制御に取り掛かっていきます。多分一番の山場ですね。どうなるやら…

その5-2.冷却ファンを接続(以下略)に再チャレンジ

前置き

前回はPWMでファン制御を行おうとして見事にずっこけました。
その原因究明を試みて数日経過しましたが、結論としては未だ原因は判っていません。
現象としてはpigpiod IF実行時にPI_NOT_PERMITTEDが返ってきます。権限周りの問題が発生してるという事なのでしょうけど、果たしてpigpiodからGPIO制御に失敗しての事なのか、作成したC++実行ファイルからのアクセスに失敗しての事なのか…
Pythonに置き換えての実行は正常に実行されてますので、原因が判明するまではとりあえずPythonで進めていきます。

というわけでPython

前回のソースコードPython化、あと若干のバグがあったので修正しています。
やはりデバッグもしていないソースを上げてはいけませんね…

余談ですがGitHubにアップしたソースを貼り付ける素敵な方法がありました。これでソース公開は大分ラクになりましたね。
ファンもちゃんとくるくる回ってくれてます。

ひとまずファン制御はここまで

可能ならここでファン制御をsystemdに登録して常時稼働としたいですが、ひとまず今回はここまで。
明日はマイコンの醍醐味、LED制御に挑戦してみます。

その5-1.冷却ファンを接続してPWMでCPU温度に応じた回転数制御を行う(そして失敗した話)

Raspberry Pi 4は熱い!

界隈が熱い(らしい?)のは昔からの話。そうじゃないんです。Raspberry Pi、物理的に熱いのです。一応CPU冷却用に小型ファンを購入しましたが、常時フル稼働では騒音(実はそんなにしませんけど)が気になりますし、常時稼働を前提としたドラレコと考えた場合、ある程度の出力制御を行って電力消費を少しでも抑えたい所。
なので今回はPWMを使ってファンの制御を行う事にします。

まずは配線

以下のサイトさんを参考に配線します。

Raspberry Pi でPWM信号を使って5Vファンを制御する - Qiita

ただ今回はpigpioを使ったソフトウェア制御を行います。なのでピンはどこを利用しても良いので、今回は空く予定のGPIO4へ接続。

f:id:nekhbet:20200130234706j:plain

制御プログラムのコーディング

早速コーディングを開始。今回はpigpioのIFではなく、pigpiodのIFを使ってコーディングします。
CPUの温度取得方法は2通りありますが、今回は文字列操作の必要のない方法を使います。(やってもいいしポインタ使って脳汁出るソース書いたりして楽しかったですけど、処理の軽さも重視していくので大人しくわかりやすい方法で)
コマンド実行結果の標準出力を用いるので、C++のpopen関数を利用します。

#include <stdio.h>
#include <cstring>
#include <stdlib.h>
#include <unistd.h>
#include <pigpiod_if2.h>

#define FAN_GPIO			4	// FANコントロールGPIO番号
#define FAN_PWM_DUTY_MIN	20	// PWM Duty最小値
#define FAN_PWM_DUTY_MAX	100	// PWM Duty最大値
#define FAN_PWM_FREQUENCY	10	// PWM周波数値
#define CPU_THERMAL_MIN		40	// ファン稼働CPU最低温度
#define CPU_THERMAL_MAX		80	// ファン稼働CPU最高温度

int main() {
	// pigpioへ接続
	int pi = pigpio_start(NULL, NULL);
	if (pi < 1) {
		printf("%d\n", pi);
		return 1;
	}

	// GPIO設定
	set_mode(pi, FAN_GPIO, PI_OUTPUT);
	set_PWM_range(pi, FAN_GPIO, FAN_PWM_DUTY_MAX);
	set_PWM_frequency(pi, FAN_GPIO, FAN_PWM_FREQUENCY);

	// CPU温度取得用変数
	const char* cmd = "cat /sys/class/thermal/thermal_zone0/temp";
	FILE* fp;
	char buf[1024];
	memset(buf, 0, sizeof(buf));

	// duty算出用設定値
	int pwm_duty_range = FAN_PWM_DUTY_MAX - FAN_PWM_DUTY_MIN;
	int cpu_thermal_range = CPU_THERMAL_MAX - CPU_THERMAL_MIN;

	while(1) {
		// CPU温度取得
		int thermal = 0;
		fp = popen(cmd,"r");
		if (fp != NULL) {
			fgets(buf, sizeof(buf), fp);
			thermal = atoi(buf) / 1000;
			pclose(fp);

			// Duty値算出
			int duty = 0;
			if (thermal > CPU_THERMAL_MAX) {
				// MAX以上なら100固定
				duty = FAN_PWM_DUTY_MAX;
			} else if (thermal > CPU_THERMAL_MIN) {
				// MINより上なら温度とduty値を比例させる
				duty = (CPU_THERMAL_MAX - thermal) / cpu_thermal_range * pwm_duty_range + FAN_PWM_DUTY_MIN;
			}
	
			// Duty値に変更があれば変更
			if (get_PWM_dutycycle(pi, FAN_GPIO) != duty) {
				set_PWM_dutycycle(pi, FAN_GPIO, duty);
			}
		}

		// 次のループ開始まで1秒待機
		time_sleep(1);
	}

	// pigpioへの接続終了
	pigpio_stop(pi);
	return 0;
}

これで動くはず…がなぜかpigpio_startで0が返ってきました。なぜ…
明日は不具合の原因究明と、バックグラウンド実行の設定を…行えたらいいですね。ちょっとこれは問題が面倒な気配…

その4-3.シャットダウンボタン制御プログラムをC++に置き換える

前置き

ごめんなさい、題名の通りですがLuaJITは諦めました。
いえ、別にLuaでpigpio操作する方法がない訳ではないのですが、結局Cでラッパーを作ってLuaで呼び出す、というわりと面倒な事をしなければならないという結論に達しまして、なら最初から全部C++で書けばいいじゃないって次第です。
というわけで、先日のPythonソースをC++へと置き換えます。

とりあえずソースをどん!

 C++ソース化しました。一応学生時代に慣れ親しんだC++ですが、組み込みで扱うのは初だった為少し手間取りました。今回いろんなサイトさんから少しずつ参考にさせて頂いて、正直挙げきれないので紹介は割愛させていただきます。

#include <stdio.h>
#include <unistd.h>
#include <iostream>
#include <pigpio.h>

int main(int argc, char *argv[]) {
	if (gpioInitialise() < 0) return 1;

	gpioSetMode(14, PI_INPUT);
	gpioSetPullUpDown(14, PI_PUD_DOWN);

	int sdcnt = 0;
	while(1) {
		if (gpioRead(14) == 1) {
			sdcnt++;
			std::cout << sdcnt << std::endl;
			if (sdcnt == 50) {
				system("shutdown -h now");
			}
		} else {
			sdcnt = 0;
		}
		usleep(100000);
	}

	gpioTerminate();
	return 0;
}

やってる事は前回のPythonソースと変わらないので説明は省きます。
なお組み込み初心者としてハマった事。標準出力はprintf関数だと処理完了後に一気に出る仕様らしく、iostreamライブラリのstd::coutを利用しています。

そしてコンパイル

PythonLuaスクリプト言語なので実行された段階でコンパイルされますが、c++コンパイルして実行ファイルを作成しなければなりません。というわけで以下を入力してコンパイル
この時、-lpigpioでコンパイル時の参照にpigpioライブラリを追加する事を忘れずに。

g++ -o switchtest switchtest.cpp -lpigpio

これで実行ファイルswitchtestが作成されるので、早速実行します。

./switchtest

これで正常に動く…はずが、pigpio実行権限がうんたらかんたら怒られました。どうやらpigpioのC用ライブラリは独自にデーモンの実行権限を持つらしく、既にpigpiodが起動しているとダメらしく…
なので起動済みのpigpioデーモンを終了してから実行します。実行には管理者権限が必要になるのでsudoをつけるのを忘れずに。

sudo killall pigpiod
sudo ./switchtest

f:id:nekhbet:20200130004421p:plain

ようやく動きました。
とりあえずボタン関連の動作はこの辺りで一度ストップして、明日からは空連ファンのPWM制御を行っていきます。

その4-2.シャットダウンボタンをpigpioで制御する

前置き

古戦場お疲れさまでした。

f:id:nekhbet:20200127230051p:plain

というわけで、Raspberry Piいじりを再開していこうと思います。

pigpioを使ってみる

今回は今後のgpio制御のベースとなるpigpioライブラリを使った形へソースを書き換えていきます。
調べた限り、Raspberry PiのGPIO制御を行う上で最も優秀なライブラリがpigpioらしく、またC言語向けのIFも準備されているので、LuaJITのffiを用いればC言語経由での制御もできそうな様子なので、今後はこのpigpioライブラリを用いて開発を行っていきます。

pigpioをインストールする

まずはpigpioのインストールから。
apt update を実行した後に apt install pigpio を実行します。管理者権限がないと失敗する場合があるので sudo もつけておきましょう。

f:id:nekhbet:20200128001856p:plain

なお標準でもpigpioライブラリは入ってるとか? その辺りちゃんと確認せずインストールしてしまいましたが、気にせず進めます。

pigpioを使ったソースへ改修する

インストールが完了したので、早速先日のソースコードをpigpioを使ったソースへ改修します。 

#!/usr/bin/env python

import pigpio
import time
import os

pi = pigpio.pi()
pi.set_mode(14, pigpio.INPUT)
pi.set_pull_up_down(14, pigpio.PUD_DOWN)

try:
    pushtime = 0
    while True:
        if pi.read(14) == 1:
            pushtime += 1
            print(pushtime)

            if pushtime >= 50:
                os.system("sudo shutdown -h now")
        else:
            pushtime = 0

        time.sleep(0.1)
except KeyboardInterrupt:
    pass

pi.stop()

こんなカンジ。

動かしてみる

早速pigpio版ソースを動かしてみますが、その前に sudo pigpiod を実行しておきます。pigpioはGPIOの状態を監視するデーモンを常時実行させてうんたらかんたら詳しい事はよくわかりませんが、デーモン実行のコマンドはRaspberry Pi起動毎に実行しておく必要があるとのこと。
早速動作確認…してみましたがあれ? pigpioライブラリなんてそんなのないって怒られます。ナゼ…
OSはLiteを入れたせいか、何か他にも入れなければライブラリがあるようでした。以下を参考に追加インストール。

はじめてのラズパイ - Qiita

$sudo apt-get install pigpio python-pigpio python3-pigpio
$sudo pigpiod

そして再び実行。

f:id:nekhbet:20200128012008p:plain

無事に動きました。

さて、少しトラブルがありましたが今回はpigpioを使った操作まで完了しましたので、次回はこれをLuaJITソースへと変更していきます。

その4-1.シャットダウンボタンを作る

着手する前に…

ここからプログラムを組んでいく事になるので、WinSCPをインストールしておきます。
なくてもvimで書けばいいのですが、私vim苦手なので…あとやっぱりTeraPad使えるのはすごく便利です。サクラエディタ? 秀丸? そんなものは知りません。
それにWin10側とRaspberrtPi側を分けておけば作業工程がそのままバックアップ作業になりますからね。

とりあえずボタンをつけてみる

開発環境の構築が完了したので、早速いろいろ作ってみようと思います。
まずはボタン制御から。以下サイトを参考に組んでいきます。

【Raspberry Pi 2 Model B】初めての電子工作 タクトスイッチに挑戦 - Qiita

今回は抵抗を用意してないので(迂闊、でも計画通り)ラズパイ内のプルダウン抵抗に任せることにします。

f:id:nekhbet:20200119155918p:plain

ソースコードをコピペして動作確認。

f:id:nekhbet:20200119211736p:plain

無事に動きました。

5秒間押し続けたらシャットダウンするようにする

 先のソースを少し変更して、5秒間押し続けたらシャットダウンする内容へ変更します。

#!/usr/bin/env python

import RPi.GPIO as GPIO
import time
import os

GPIO.setmode(GPIO.BCM)
GPIO.setup(14, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

try:
    pushtime = 0
    while True:
        if GPIO.input(14) == GPIO.HIGH:
            pushtime += 1
            print(pushtime)
            if pushtime >= 50:
                os.system("sudo shutdown -h now")
        else:
            pushtime = 0

        time.sleep(0.1)
except KeyboardInterrupt:
    pass

GPIO.cleanup()
?
変更内容は至極簡単。pushtime変数で押下している間カウンティングして50になる(5秒経過)したらシャットダウンコマンドを走らせるだけです。非押下時を想定してピン入力がhighではない時はカウントリセットするのを忘れず。

まだ課題はたくさん…

とりあえずシャットダウンボタンはできましたけど、まだ課題は山積しています。

  1. ソースコードLua
  2. ボタン制御を常時動作させる

次からは上記課題の解消を目指すつもりですが、もしかしたら少しタイミングを前後して冷却ファン制御に着手するかもしれません。Raspberry Pi 4結構熱くなってるので…