RaspberryPi
IoT
AirPlay
NanoPi

NanoPi Neo で、作業場音楽環境をいろいろやらかしてみた

秋月で、なんとなく買ってしまった、nanopi。
あまらしとくのも難なので、作業場の音楽環境を、いろいろやらかすネタとして。
使ってみることに、しましたとさ。
DSC_0385.jpg

導入前の環境

作業場には、パソコンとモニタ、デジタル入力のアンプと、あとAirmac Expressがあります。
Airmac Express経由で、サーバに入れてあるiTunesを流しています。
モニタはTV的なのを流用しているため、自動電源OFFが無く、パソコンとの電源連動がアレ。
アンプは中華製のやつで、入力切り替えがめんどくさく。
また、隣のまったり系のお部屋まで、デジタル分岐した光回線が伸びてます。
Airplay使わない時にAirmac Expressの電源落としたりしてるのですが、まあめんどくさく!。
Airplayのキックも、スマホのリモコンツール経由だったりしてね。
このへん、どうにかしていきたいな、と。

ざっくり設計

適当に秋月を眺め、以下の方針でいくことに決定。

オーディオ切り替え

・SPDIFベース、nanopiはSPDIF出力ありそうだしね
・同軸はめんどくさげなので、光入出力
・スイッチは125あたりで
・隣の部屋送りのため、2系統独立出力、切り替えで
・予備ふくめて、光2系統つけとくべ

電源回り

・商用電源のスイッチは、SSRで
 ・通常のリレー、入れるのに電流使うからね
・同じ筐体に一次側入れるのやなので、分離構造で
・途中配線はUSBだのEtherだのは避け、RJ11(モジュラー)で

UI

 ・一応ほら、音モノだし、LED+スイッチで
 ・ノイズ的なアレ?

I/F

 ・どうみてもIO足りないので、まあI2Cで増やしますかね
 ・リモコン使えるようにしたいかな
 ・配線はパッチケーブルベースでいいべ、めんどいし

おまけ要素

 ・どうせだしPC用のUSB DACも入れちゃうべ

詳細設計、電気編

・LEDは3mmをいくつか
・LEDの電流制限抵抗は、510Ω(緑黄赤)、270Ω(白青)
・パスコンは10uF
・SPDIF入力側の電源用Lは、47uH
・基板は秋月C基板で

・・・で、秋月発注、ざっくりしすぎてて、スイッチを買い忘れる失態ぶりでした。

naniPiにAirplay喋らせる、基本動作編

まあ、ざっくりと。
DSC_0384.jpg

OS

・ArmbianのUbuntuベース、5.35を使用
・最近のだと、SPDIF回りが、いろいろめんどいみたいです
・というか挫折しました

SPDIF回り

・SPDIF認識のさせざまは、以下URLでなんとかなりました
 https://forum.armbian.com/topic/1891-spdif-output-on-nanopi-m1/
・SPDIFが正常に認識されると、以下な感じになりますです

root@nanopineo:~# cat /proc/asound/cards
 0 [audiocodec     ]: audiocodec - audiocodec
                      audiocodec
 1 [sndhdmi        ]: sndhdmi - sndhdmi
                      sndhdmi
 2 [sndspdif       ]: sndspdif - sndspdif
                      sndspdif

shairport

・perlが絡んでるので、改造とからくそーだし、あえての無印を使いました
・systemdに以下登録して、動かしてます

shairport.service
[Unit]
Description = shairport

[Service]
ExecStart=/data/shairport/shairport.pl -a AirSelect --ao_devicename "default:CARD=sndspdif" --play_prog="/bin/touch /ramdisk/airplay_enable" --stop_prog="/bin/rm /ramdisk/airplay_enable"
Restart=always
Type=simple

[Install]
WantedBy=multi-user.target

 ・ポイントは以下な感じ
  ・SPDIF出力は、「--ao_devicename "default:CARD=sndspdif"」でいける
  ・自動制御用に、airplay動いてるかをハンドリングしたかったんだけど
   ファイルの有無で判定することに
   キックは、オプションでいける親切設計でした
   「--play_prog="/bin/touch /ramdisk/airplay_enable"」
   「--stop_prog="/bin/rm /ramdisk/airplay_enable"」
   まあ、そゆことです

 ・これでサービスを有効化しておくと、同じネットワーク上のiTunesに、以下のステキ表示が!
Image2.png
・あ、出ない時は、iTunesのホームシェアリング回りをぐぐってみて下さいな、確かアカウント登録とかいるはず
・shairportはまあ、Sync機能が無いので、それぞれの出力音が混ざる環境だと、エコー的になりますが、離れていれば大丈夫、ぶっちゃけKodiあたりと一緒です動作は

SPDIF出力回り(電気要素)

・nanoPiは、1列ピンヘッダ側、I2Sの隣が、SPDIF出力です
・3.3VTTL出力っぽい、そのまま光なTOSLINK出力に突っ込めば、光デジタルとして使えますー

SPDIF切り替え回り

切り替えデバイス

・切り替えは、74ロジックの3ステートバッファで、まあ「SN74HC125N」を使う感じ
 ・選定ミスった!、126のほうがいいかもね
 ・理由は、125はOEが反転動作なので、電源投入時には出力がぶつかっちゃう
・A入力にSPDIF入力を繋ぎ、OEをGPIOで制御、Y出力を4つぶつけて、「4入力1出力」にしました
・それを2回路で、4入力2出力ね
・パスコンは10uFを入れときましたとさ

入出力

・SPDIF TTL入力2系統
・SPDIF 光入力2系統
・SPDIF 光出力2系統
 な、感じで

基板

・さくっと、作りました、右側ね
DSC_0387.jpg
・TOSLINKは東芝のが終売状態
・秋月の「ちっこいほう」のは、固定用の足が2.54mmピッチなので、かなり取り回しがラクですよん

I2cエキスパンダー回り

デバイス

・秋月に在庫がある、MicrochipのMCP23017を、2コ使用
・アドレスが3bitで選べるので、適当に設定
・データシート確認せずに基板を造るという、恐ろしい行為を行った為、無駄にプルアップ入れてしまった、、、あっはっは
・基板はこんなかんじー
DSC_0386.jpg

製作留意点(電気要素)

・パスコンは10uF
・nanoPiは、I2cプルアップが入ってないので、この基板で(1k)
・ロジック入力側は、1kのバッファ抵抗を
・ロジック出力側は、1kのプルアップ/ダウンを
・LED駆動は、電流制限として、270Ωと510Ω入れてます
・このIC、中身はマイコンらしく、起動に多少時間がかかります、数秒
・リセットピンは1kプルアップしてあげないと、動作不安定です

nanoPi側からの認識

・ま、さくっと

root@nanopineo:~# i2cdetect -y 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- 22 23 -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
root@nanopineo:~#

・どうして「22」と「23」なのか、よーわかってない適当さw
・まあ、てきとーに、アドレスピンをいじってます

リモコン回り

リモコンの選定

・まあ、Apple Remoteでいいかと
・中古を大人買いしたあと、自宅からも発掘されるというダメっぷりですが

lirc設定

・まず、WiringNPをセットアップ
command
git clone https://github.com/friendlyarm/WiringNP
cd WiringNP/
chmod 755 build
./build

・このままだと動かないので、WiringNPの以下を変更

$HOME/WiringNP/wiringPi/boardtype_friendlyelec.c
//allwinner h3
// kernel 3.x
の、該当部分以外をコメントアウト、該当部分を「-1(-1)」にする
参考:「/sys/class/sunxi_info/sys_info」の 「sunxi_board_id」

 このへんは、以下URLを参考にしました。
 http://nopnop2002.webcrow.jp/NanoPi-M1/NanoPi-M1-2.html

 これでリブートすると、sunxiのcirが生きてくるので、lircが使えるようになります。
 OrangePi用ですが、以下URLで!
 https://forum.armbian.com/topic/1953-configuring-orange-pi-pc-to-receive-irinfrared/

Apple Remote回り

・Apple Remote、デバイスIDとかも送ってるみたいで、なかなかうまく学習してくれません
・以下URLから、設定貰ってきました
 http://lirc.sourceforge.net/remotes/apple/A1156

リモコンによる、コマンドキック

・irexecを使います
・/etc/lirc/lircrc というファイルに、定義を書けばOKみたい

begin
        prog = irexec
        button = KEY_PLAY
        repeat = 2
        config = /data/airplay_control/control.pl playpause
        flags = quit
end
begin
        prog = irexec
        button = KEY_KPPLUS
        repeat = 2
        config = /data/airplay_control/control.pl volup
        flags = quit
end
begin
        prog = irexec
        button = KEY_FASTFORWARD
        repeat = 2
        config = /data/airplay_control/control.pl next
        flags = quit
end
begin
        prog = irexec
        button = KEY_REWIND
        repeat = 2
        config = /data/airplay_control/control.pl prev
        flags = quit
end
begin
        prog = irexec
        button = KEY_KPMINUS
        repeat = 2
        config = /data/airplay_control/control.pl voldown
        flags = quit
end
begin
        prog = irexec
        button = KEY_MENU
        repeat = 2
        config = /data/airplay_control/control.pl shuffle
        flags = quit
end

・で、systemdに登録、キック、テスト動作は単純に「irexec」を実行でOK

irexec.service
[Unit]
Description = irexec

[Service]
ExecStart=/usr/bin/irexec
Restart=always
Type=simple

[Install]
WantedBy=multi-user.target

aiplayによるiTunesのコントロール

・iTunesは、「DACP」という手順で、コントロールできます
 https://ja.wikipedia.org/wiki/Digital_Audio_Control_Protocol
・まあざっくり、httpで特定のポートにアクセス、リクエスト投げるだけ
・が、認証キーを一緒に送らないと、だめみたいです
・認証キーは、shairportに送られてるので、それ取り出します
・shairport.pl内の、「conn_handle_request」の中のやりとりで、サーバ応答があるので
・そこにトラップを仕掛けます、こんなかんじ

shairport.pl
sub conn_handle_request {
    my ($fh, $conn) = @_;

    my $req = $conn->{req};;
    my $clen = $req->header('content-length') // 0;
    if ($clen > 0 && !length($req->content)) {
        $conn->{req_need} = $clen;
        return; # need more!
    }

    my $resp = HTTP::Response->new(200);
    $resp->request($req);
    $resp->protocol($req->protocol);

#get Active-remote
    my $remoteid = $req->header('Active-Remote');
    if ($remoteid ne ""){
        open (OUTPUTREMOTE,">/ramdisk/airplay_activeremote");
        print OUTPUTREMOTE $remoteid;
        close (OUTPUTREMOTE);
    }
    $resp->header('CSeq', $req->header('CSeq'));
    $resp->header('Audio-Jack-Status', 'connected; type=analog');

・取り出したキーを使って、コントローラを適当に書いてみます

test.pl
#!/usr/bin/perl

use Socket;

 $activeremotefile = "/ramdisk/airplay_activeremote";
 open (FILE,$activeremotefile);
 ($activeremotekey) = (<FILE>);
 close (FILE);

 $command = $ARGV[0];
 $socket_port = 3689;
 $hostname = "192.168.0.221";

 $url = "/ctrl-int/1/" . $command;
 $addr = inet_aton("$hostname");
 socket(S, PF_INET, SOCK_STREAM, 0);
 my $socket_name = pack_sockaddr_in($socket_port, $addr);
 connect(S, $socket_name);
 binmode(S);
 select(S);
 $|=1;
 select(stdout);
 print S "GET ".$url." HTTP/1.0\r\nHost: $hostname\r\n";
 print S "Active-Remote: $activeremotekey\r\n\r\n";
 while (<S>) {
 print $_;
 }
 close (S);

・IPはiTunes動いてるPCのに書き換えてね、と
・コマンドは、以下資料が参考になるかと
 https://nto.github.io/AirPlay.html
・但し!、この資料古いらしく、使えないコマンドとか、ちらほらあります
・そこでiTunesを適当にハック、いまんとここんな感じみたい(iTunes12系列)

コマンド 内容
beginff begin fast forward
beginrew begin rewind
mutetoggle toggle mute status
nextitem play next item in playlist
previtem play previous item in playlist
pause pause playback
playpause toggle between play and pause
play start playback
stop stop playback
playresume play after fast forward or rewind
shuffle_songs shuffle playlist
volumedown turn audio volume down
volumeup turn audio volume up
prevgroup 前のグループへ
nextgroup 次のグループへ
restartitem 再生中のアイテムを、頭から
increment
playspec
cue 謎だけどまあ、cue的な要素?
repeatadv リピート設定のトグル
beginrew restartitemと同一動作?
nextcontainer 次コンテナ
prevchapter 前チャプタ
shuffletoggle シャッフルのトグル
prevcontainer 前コンテナ
discrete-pause
nextchapter 次チャプタ
controlpromptentry

・ちなみに、使えるコマンドは206が、無いコマンドは400が、dacp以外でパラメータとか渡さないとダメ系コマンドは501が帰ってくるみたいです
・先のリモコンと合わせて、無事リモコン操作ができるよーになりました、とさ

箱の作成

・ま、アクリルで適当に
・一家に一台、レーザー加工機、べんりよー、30分作業だし!
Image5.png
Image6.png

自動制御

・ま、やるきなく、スクリプトからコマンド呼び出しで、ごまかしてます
・自動切り替えとかべんりべんり

airselect.pl
#!/usr/bin/perl

use Time::HiRes;
$tempdir = "/ramdisk";

&i2cinit;

while(){
 &polling;
}
exit;

sub polling {

#detect
 my $statusrawdata = &i2cget("0x22","0x12");
 $statusrawdata = substr($statusrawdata,2,2);
 $statusrawdata = unpack("B8",  pack("H2", $statusrawdata ));

 my $detectbutton = &buttondetect($statusrawdata);
 my $detectpower = &powerdetect($statusrawdata);
 my $detectairplay = &loadstatus("airplay_enable");

#load autostatus
 my $autostatustime = &loadstatus("select_autostatustime","time");

#load status
 my $statuspower1 = &loadstatus("power1_status","data");
 my $statuspower2 = &loadstatus("power2_status","data");
 my $statusaudio1 = &loadstatus("audio1_status","data");
 my $statusaudio2 = &loadstatus("audio2_status","data");

#set default
 if ($statuspower1 eq "0"){$statuspower1 = "off";}
 if ($statuspower2 eq "0"){$statuspower2 = "off";}
 if ($statusaudio1 eq "0"){$statusaudio1 = "disable";}
 if ($statusaudio2 eq "0"){$statusaudio2 = "disable";}

#heartbeat
 my $statusheartbeat = &loadstatus("airswitch_heartbeat","data");
 my $statusheartbeattime = &loadstatus("airswitch_heartbeat","time");
 if ($statusheartbeat eq "0"){
   $statusheartbeattime = 100;
 }
 if ($statusheartbeattime > 10){
  if ($statusheartbeat eq "on"){
   $statusheartbeat = "off";
  }
  else {
   $statusheartbeat = "on";
  }
  &updatestatusdata("airswitch_heartbeat",$statusheartbeat);
 }

#autostatusupdate detect
 my $autostatusupdateflag = 0;
 if (($autostatustime > 7200)||($autostatustime == 0)){
  if ($detectbutton eq "off"){
   $autostatusupdateflag = 1;
  }
 }
#autostatusupdate exec
 if ($autostatusupdateflag == 1){
  &updatestatus("select_autostatustime","0");
  $statusaudio1 = "disable";
  $statusaudio2 = "disable";
  $statuspower1 = "off";
  $statuspower2 = "off";
  if ($detectpower eq "on"){
   $statusaudio1 = "spdif2";
   $statusaudio2 = "spdif2";
   $statuspower1 = "on";
   $statuspower2 = "on";
  }
  if ($detectairplay eq "1"){
   $statusaudio1 = "airplay";
   $statusaudio2 = "airplay";
   $statuspower2 = "on";
  }
 }

#manualstatusupdate exec
 else {
  if ($detectbutton eq "power1"){
   if    ($statuspower1 eq "on")      { $statuspower1 = "off";     }
   elsif ($statuspower1 eq "off")     { $statuspower1 = "on";      }
  }
  if ($detectbutton eq "power2"){
   if    ($statuspower2 eq "on")      { $statuspower2 = "off";     }
   elsif ($statuspower2 eq "off")     { $statuspower2 = "on";      }
  }
  if ($detectbutton eq "audio1"){
   if    ($statusaudio1 eq "disable") { $statusaudio1 = "airplay"; }
   elsif ($statusaudio1 eq "airplay") { $statusaudio1 = "pc";      }
   elsif ($statusaudio1 eq "pc")      { $statusaudio1 = "spdif1";  }
   elsif ($statusaudio1 eq "spdif1")  { $statusaudio1 = "spdif2";  }
   elsif ($statusaudio1 eq "spdif2")  { $statusaudio1 = "disable"; }
  }
  if ($detectbutton eq "audio2"){
   if    ($statusaudio2 eq "disable") { $statusaudio2 = "airplay"; }
   elsif ($statusaudio2 eq "airplay") { $statusaudio2 = "pc";      }
   elsif ($statusaudio2 eq "pc")      { $statusaudio2 = "spdif1";  }
   elsif ($statusaudio2 eq "spdif1")  { $statusaudio2 = "spdif2";  }
   elsif ($statusaudio2 eq "spdif2")  { $statusaudio2 = "disable"; }
  }
  if ($detectbutton ne "off"){
   &updatestatus("select_autostatustime","2");
  }
 }

#update status
 &updatestatusdata("power1_status",$statuspower1);
 &updatestatusdata("power2_status",$statuspower2);
 &updatestatusdata("audio1_status",$statusaudio1);
 &updatestatusdata("audio2_status",$statusaudio2);

#update led status

#  bit0 : O : POWER AUTO 1 (use power1 on/off)
#  bit1 : O : POWER ON 1 (use detect airplay)
#  bit2 : O : POWER AUTO 2 (use poser2 on/off)
#  bit3 : O : POWER ON 2 (use detect pc)
#  bit4 : O : POWER DETECT AIRPLAY (use automode)
#  bit5 : O : POWER DETECT PC (heartbeat)
#  bit6 : O : UNUSED
#  bit7 : O : UNUSED
#  bit8 : O : AUDIO TOSLINK1 1
#  bit9 : O : AUDIO AIRPLAY 1
#  bitA : O : AUDIO PCUSB 1
#  bitB : O : AUDIO TOSLINK2 1
#  bitC : O : AUDIO TOSLINK1 2
#  bitD : O : AUDIO TOSLINK2 2
#  bitE : O : AUDIO AIRPLAY 2
#  bitF : O : AUDIO PCUSB 2

 my (@ledarray);
 for (0..15){
  $ledarray[$_] = "0";
 }

 if ($statuspower1 eq "on")       {$ledarray[0]  = "1";}
 if ($detectairplay eq "1")       {$ledarray[1]  = "1";}
 if ($statuspower2 eq "on")       {$ledarray[2]  = "1";}
 if ($detectpower eq "on")         {$ledarray[3]  = "1";}
 if ($autostatusupdateflag eq "1"){$ledarray[4]  = "1";}
 if ($statusheartbeat eq "on")    {$ledarray[5]  = "1";}

 if ($statusaudio1 eq "spdif1")   {$ledarray[8]  = "1";}
 if ($statusaudio1 eq "airplay")  {$ledarray[9]  = "1";}
 if ($statusaudio1 eq "pc")       {$ledarray[10] = "1";}
 if ($statusaudio1 eq "spdif2")   {$ledarray[11] = "1";}
 if ($statusaudio2 eq "spdif1")   {$ledarray[12] = "1";}
 if ($statusaudio2 eq "spdif2")   {$ledarray[13] = "1";}
 if ($statusaudio2 eq "airplay")  {$ledarray[14] = "1";}
 if ($statusaudio2 eq "pc")       {$ledarray[15] = "1";}

 my $setstatus = unpack("H2", pack("B8","$ledarray[0]$ledarray[1]$ledarray[2]$ledarray[3]$ledarray[4]$ledarray[5]$ledarray[6]$ledarray[7]"));
 $setstatus = uc($setstatus);
 &i2cset("0x23","0x14","0x".$setstatus);
 $setstatus = unpack("H2", pack("B8","$ledarray[8]$ledarray[9]$ledarray[10]$ledarray[11]$ledarray[12]$ledarray[13]$ledarray[14]$ledarray[15]"));
 $setstatus = uc($setstatus);
 &i2cset("0x23","0x15","0x".$setstatus);

#update audioselector status
 my $setstatusaudio1 = "1111";
 my $setstatusaudio2 = "1111";

 if ($statusaudio1 eq "airplay")  {$setstatusaudio1 = "1101";}
 if ($statusaudio1 eq "pc")       {$setstatusaudio1 = "1110";}
 if ($statusaudio1 eq "spdif1")   {$setstatusaudio1 = "0111";}
 if ($statusaudio1 eq "spdif2")   {$setstatusaudio1 = "1011";}
 if ($statusaudio2 eq "airplay")  {$setstatusaudio2 = "1101";}
 if ($statusaudio2 eq "pc")       {$setstatusaudio2 = "1110";}
 if ($statusaudio2 eq "spdif1")   {$setstatusaudio2 = "0111";}
 if ($statusaudio2 eq "spdif2")   {$setstatusaudio2 = "1011";}
 if ($statusaudio1 eq "disable") {
  if ($detectairplay eq "0") {
   $setstatusaudio1 = "1101";
  }
 }
 if ($statusaudio2 eq "disable") {
  if ($detectairplay eq "0") {
   $setstatusaudio2 = "1101";
  }
 }
 $setstatus = unpack("H2", pack("B8","$setstatusaudio1$setstatusaudio2"));
 $setstatus = uc($setstatus);
 &i2cset("0x22","0x15","0x".$setstatus);

#update power status
 $setstatus = "0x00";
 if (($statuspower1 eq "on")&&($statuspower2 eq "on")){
  $setstatus = "0xC0";
 }
 elsif ($statuspower1 eq "on"){
  $setstatus = "0x80";
 }
 elsif ($statuspower2 eq "on"){
  $setstatus = "0x40";
 }
 &i2cset("0x22","0x14",$setstatus);
}

sub buttondetect {
 my $rawdata = $_[0];
 $rawdata = substr($rawdata,4,4);

 my $filename = $tempdir . "/button_used";
 if (-e $filename){
  if ($rawdata eq "0000"){
   unlink ($filename);
  }
  return ("off");
 }
 if ($rawdata ne "0000"){
  open (STATUSFILE,">$filename");
  print STATUSFILE ".";
  close (FILE);
 }

 if ($rawdata eq "0001"){ return ("power2");}
 if ($rawdata eq "0010"){ return ("power1");}
 if ($rawdata eq "0100"){ return ("audio2");}
 if ($rawdata eq "1000"){ return ("audio1");}
 return ("off");
}

sub powerdetect {
 my $rawdata = $_[0];
 $rawdata = substr($rawdata,2,1);

 if ($rawdata eq "1"){
  &updatestatus("power_detect",1);
  return ("on");
 }
 return ("off");
}

sub loadstatus {
 my $filename  = $tempdir . "/" . $_[0];
 if (-e $filename){
  if ($_[1] eq "data"){
   open (STATUSFILE,$filename);
   my ($filedata) = (<STATUSFILE>);
   close (FILE);
   return ($filedata);
  }
  if ($_[1] eq "time") {
   my $modtime = (stat($filename))[9];
   $modtime = abs ($modtime - time);
   return ($modtime);
  }
  return (1);
 }
 return (0);
}

sub updatestatus {
 my $filename  = $tempdir . "/" . $_[0];
 if ($_[1] eq "1"){
  if (!-e $filename){
   open (STATUSFILE,">$filename");
   print STATUSFILE ".";
   close (STATUSFILE);
  }
  return;
 }
 if ($_[1] eq "2"){
  open (STATUSFILE,">$filename");
  print STATUSFILE ".";
  close (STATUSFILE);
  return;
 }
 if ($_[1] eq "0"){
  if (-e $filename){
   unlink ($filename);
  }
  return;
 }
}

sub updatestatusdata {
 my $filename  = $tempdir . "/" . $_[0];
 my $prevstatus = &loadstatus($_[0],"data");
 if ($_[1] ne $prevstatus){
  open (STATUSFILE,">$filename");
  print STATUSFILE $_[1];
  close (STATUSFILE);
 }
}

sub i2cinit {
 &i2cset("0x22","0x00","0x2F");
 &i2cset("0x22","0x0c","0x00");

 &i2cset("0x22","0x01","0x00");
 &i2cset("0x22","0x0d","0x00");

 &i2cset("0x23","0x00","0x00");
 &i2cset("0x23","0x01","0x00");

 &i2cset("0x23","0x0c","0x00");
 &i2cset("0x23","0x0d","0x00");

 &i2cset("0x23","0x14","0xFF");
 &i2cset("0x23","0x15","0xFF");

 &i2cset("0x22","0x14","0x00");
 &i2cset("0x22","0x15","0xFF");

# sleep (3);

 &i2cset("0x23","0x14","0x00");
 &i2cset("0x23","0x15","0x00");
}

sub i2cset {
 my $i2caddress = $_[0];
 my $memaddress = $_[1];
 my $memsetdata = $_[2];

 my $systemcommand = "/usr/sbin/i2cset -y 0 " . $i2caddress . " " . $memaddress . " " .$memsetdata;
 system ($systemcommand);
 Time::HiRes::sleep(0.2);
}

sub i2cget {
 my $i2caddress = $_[0];
 my $memaddress = $_[1];
 my $systemcommand = "/usr/sbin/i2cget -y 0 " . $i2caddress . " " . $memaddress;
 my $result = `$systemcommand`;
 Time::HiRes::sleep(0.2);
 return ($result);
}

・で、systemdに登録して、完了

配線とか

・検討中
DSC_0389.jpg

・ごちゃってます
DSC_0395.jpg

・LEDは、いいかんじ
DSC_0394.jpg

ま、完成

・べんりべんり
DSC_0425.jpg