「#ラズパイ」タグアーカイブ

【ラズパイ】0~9の数字を表示する

7セグLEDの桁を4桁に変更しました。

それに伴い、回路も修正。

さて、実際にプログラミを作成していきます。

まずは初期化。

def initialize():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(17, GPIO.OUT)
    GPIO.setup(27, GPIO.OUT)
    GPIO.setup(22, GPIO.OUT)
    GPIO.setup(10, GPIO.OUT)
    GPIO.setup(9, GPIO.OUT)
    GPIO.setup(11, GPIO.OUT)
    GPIO.setup(5, GPIO.OUT)
    GPIO.setup(14, GPIO.OUT)
    GPIO.setup(15, GPIO.OUT)
    GPIO.setup(18, GPIO.OUT)
    GPIO.setup(23, GPIO.OUT)
    GPIO.setup(24, GPIO.OUT)


def clear():
    GPIO.output(17, GPIO.LOW)
    GPIO.output(27, GPIO.LOW)
    GPIO.output(22, GPIO.LOW)
    GPIO.output(10, GPIO.LOW)
    GPIO.output(9, GPIO.LOW)
    GPIO.output(11, GPIO.LOW)
    GPIO.output(5, GPIO.LOW)
    GPIO.output(14, GPIO.LOW)
    GPIO.output(15, GPIO.LOW)
    GPIO.output(18, GPIO.LOW)
    GPIO.output(23, GPIO.LOW)
    GPIO.output(24, GPIO.LOW)

LEDを点灯させるのですが、GPIOの番号では分かりづらいので、LEDの番号に置き換え。

def ledA():
    GPIO.output(17, GPIO.HIGH)

def ledB():
    GPIO.output(27, GPIO.HIGH)

def ledC():
    GPIO.output(22, GPIO.HIGH)

def ledD():
    GPIO.output(10, GPIO.HIGH)

def ledE():
    GPIO.output(9, GPIO.HIGH)

def ledF():
    GPIO.output(11, GPIO.HIGH)

def ledG():
    GPIO.output(5, GPIO.HIGH)

これを使って、数字0~9を点灯する処理を作っていきます。


def output0():
    clear()
    ledA()
    ledB()
    ledC()
    ledD()
    ledE()
    ledF()

def output1():
    clear()
    ledB()
    ledC()

def output2():
    clear()
    ledA()
    ledB()
    ledG()
    ledE()
    ledD()

def output3():
    clear()
    ledA()
    ledB()
    ledG()
    ledC()
    ledD()

def output4():
    clear()
    ledF()
    ledB()
    ledG()
    ledC()

def output5():
    clear()
    ledA()
    ledF()
    ledG()
    ledC()
    ledD()

def output6():
    clear()
    ledA()
    ledF()
    ledG()
    ledC()
    ledE()
    ledD()

def output7():
    clear()
    ledA()
    ledB()
    ledC()

def output8():
    clear()
    ledA()
    ledB()
    ledC()
    ledD()
    ledE()
    ledF()
    ledG()

def output9():
    clear()
    ledA()
    ledB()
    ledC()
    ledD()
    ledF()
    ledG()

【ラズパイ】7セグLEDを試す。

久しぶりのラズパイ。

以前は、温湿度計の測定結果をLCDに表示されていたのですが、

配線が壊れまして、

直巣のめんどくさい、となって、そのまま放置していました。

しかし、7セグLEDを使用したらどうだろう?と思いまして、試してみることにしました。

これを実際に回路に組み込んでみた。

思った以上に配線汚い。

7セグLEDの回路図はこんなかんじ。

下の7端子はラズパイのGPIOに接続。

上の3端子は、トランジスタのコレクタに接続します。

トランジスタのベースには、GPIO14,15,18を接続します。

エミッタはGNDに接続します。

これは、フラッシュ型になっていまして、3桁を異なる値にするためには、出力を点滅するように操作することによって、表示を実現する仕組み。

トランジスタはその出力をコントロールするためのスイッチです。

この回路でLED点灯を確認したので、実際の処理を作っていきます。

【ALEXAスキル開発】どうも、今のままではAlexa連携はできないっぽい。

あれからAlexaからWebAPIを実行する方法を探ってみたのですが、

結論から言うと、今のままでは無理っぽいです。

理由は、WebAPIを実行するためには、インターネットからHTTPSでアクセスできることが条件と言うことです。

https://developer.amazon.com/ja-JP/docs/alexa/custom-skills/host-a-custom-skill-as-a-web-service.html

今の環境下では、テレビの赤外線を使用しているラズパイはローカルネットワーク環境下でのみ実行可能で、インターネットからアクセスしようにも、プロバイダー(ビッグローブ)側で自宅IPへの直アクセスもブロックされているっぽいのです。

外からアクセスする手段が無い以上、どうすることもできません。

Arduinoだとこんな感じで使用できるみたいです。

https://dev.classmethod.jp/articles/smart-home-skill/

なので、Alexaスキル開発は一旦中断。

ただ、テレビの赤外線通信はこのままではもったいないので、別の活用法を考えてみたいと思います。

リモコンが無くても、PCやスマホからコントロールできるようにするとか。

ちょっとそっちの方で進めてみましょうか。

他にやることないし。

【ラズパイ】WebAPIで赤外線信号を送信する

結局は、ほとんどの信号をmode2コマンドでトレースして、赤外線信号データを登録しました。

https://github.com/takishita2nd/tv_ir/blob/master/tv.conf

全てきちんと動作することを確認しています。

これをWebAPIで実行できるように、簡易httpサーバをpythonで作成します。

すでにやってきたことなので、サクッと作成。

import json
import time
import threading
import subprocess

from http.server import BaseHTTPRequestHandler
from http.server import HTTPServer
from http import HTTPStatus
from typing import TypeVar
from urllib.parse import urlparse

PORT = 8000

def __main__():
    thread = threading.Thread(target=httpServe)
    thread.start()
    
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        return

def httpServe():
    handler = StubHttpRequestHandler
    httpd = HTTPServer(('',PORT),handler)
    httpd.serve_forever()

class StubHttpRequestHandler(BaseHTTPRequestHandler):
    server_version = "HTTP Stub/0.1"
    thread = None
    aviFilename = ""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def do_POST(self):
        content_len = int(self.headers.get('content-length'))
        requestBody = json.loads(self.rfile.read(content_len).decode('utf-8'))
        command = requestBody['contents']['command']
        args = ['irsend', 'SEND_ONCE', 'tv', command]
        res = ""
        try:
            res = subprocess.run(args, stdout=subprocess.PIPE)
        except:
            ""

        response = { 'status' : 200 }
        self.send_response(200)
        self.send_header('Content-type', 'application/json')
        self.end_headers()
        responseBody = json.dumps(response)

        self.wfile.write(responseBody.encode('utf-8'))

__main__()

これに対して、PostmanというWebAPIを送信するツールを使って、リクエストを送ります。

これで動きました。

【ラズパイ】赤外線信号の学習

えっと、

配線を間違えたせいで、赤外線受信モジュールが壊れました。

まぁ、もう一個あったので、そちらに交換しましたが。

そして、もう一つ、

前回のやり方だと、TVが反応してくれるコマンドもあれば、反応してくれないコマンドもありました。

むしろ、反応してくれないコマンドの方が多い。

なので、やり方を変えます。

こちらの記事を参考にしました。

https://qiita.com/amaebi-tabetai/items/211b9c6f751c96ff1fd7

irrecordというコマンドを使用して、赤外線のデータを何回か入力させて、正しい信号に直してくれることができるみたいです。

irrecord --disable-namespace -f -d /dev/lirc1 --driver default ~/lircd.conf

受信機をlirc1にしているので、こちらを使用します。

pi@raspberrypi:~ $ irrecord --disable-namespace -f -d /dev/lirc1 --driver default ~/lircd.
conf
Using raw access on device /dev/lirc1

irrecord -  application for recording IR-codes for usage with lirc
Copyright (C) 1998,1999 Christoph Bartelmus(lirc@bartelmus.de)

This program will record the signals from your remote control
and create a config file for lircd.

A proper config file for lircd is maybe the most vital part of this
package, so you should invest some time to create a working config
file. Although I put a good deal of effort in this program it is often
not possible to automatically recognize all features of a remote
control. Often short-comings of the receiver hardware make it nearly
impossible. If you have problems to create a config file READ THE
DOCUMENTATION at https://sf.net/p/lirc-remotes/wiki

If there already is a remote control of the same brand available at
http://sf.net/p/lirc-remotes you might want to try using such a
remote as a template. The config files already contains all
parameters of the protocol used by remotes of a certain brand and
knowing these parameters makes the job of this program much
easier. There are also template files for the most common protocols
available. Templates can be downloaded using irdb-get(1). You use a
template file by providing the path of the file as a command line
parameter.

Please take the time to finish the file as described in
https://sourceforge.net/p/lirc-remotes/wiki/Checklist/ an send it
to  <lirc@bartelmus.de> so it can be made available to others.

Press RETURN to continue.

Checking for ambient light  creating too much disturbances.
Please don't press any buttons, just wait a few seconds...

No significant noise (received 0 bytes)

Enter name of remote (only ascii, no spaces) :
Enter name of remote (only ascii, no spaces) :tv
Using tv.lircd.conf as output filename

Now start pressing buttons on your remote control.

It is very important that you press many different buttons randomly
and hold them down for approximately one second. Each button should
generate at least one dot but never more than ten dots of output.
Don't stop pressing buttons until two lines of dots (2x80) have
been generated.

Press RETURN now to start recording.
................................................................................

Please enter the name for the next button (press <ENTER> to finish recording)
channel1

Now hold down button "channel1".
Timeout (10 seconds), try again (29 retries left).

Now hold down button "channel1".

Please enter the name for the next button (press <ENTER> to finish recording)


Successfully written config file tv.lircd.conf

Press RETURN now to start recording.のところで、学習するボタンをひたすら押し続ける。

それが終われば、コマンド名に名前をつけて終了。

作成されたconfファイルには、こんな感じになっていました。

      begin raw_codes

          name channel1
             9082    4465     624     508     619     514
              621     514     622     515     621     515
              596     541     621    1645     596     541
              640    1636     588    1671     621    1645
              595    1671     596    1683     585    1671
              594     543     564    1703     566    1703
              593     548     560     571     565     573
              563     571     564     572     564     572
              564     572     563     572     563    1708
              567    1696     564    1702     564    1708
              560    1702     576    1692     565    1700
              565

      end raw_codes

これを、前回と同じように/etc/lirc/lircd.conf.d/tv.confに記入して、lircdを再起動。

これで、コマンドが動くことを確認しました。

こっちのやり方の方が確実かもしれない。

【ラズパイ】リモコンの赤外線通信を再現してみる。

まずは、前回のプログラムを元に、以下のようなプログラムを作成しました。

import RPi.GPIO as GPIO
import time

def __main__():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(21,GPIO.IN)
    try:
        while True:
            out = GPIO.input(21)
            if out == 0:
                print(1)
            else:
                print(0)
            time.sleep(0.0001)
    except KeyboardInterrupt:
        GPIO.cleanup()

__main__()

0.1ミリ秒周期で赤外線通信の受信信号を読み取って、0/1で出力するプログラムを作成してみました。

これを使用して、テレビのボリュームUP/DOWNの信号を読み取って、

こんなプログラムを作成して、赤外線LEDを点灯させるプログラムを作成。

import RPi.GPIO as GPIO
import time

pattern = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,
           0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,
           0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,
           0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,0,1,1,1,0,
           0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
           1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1]


def __main__():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(20,GPIO.OUT)
    try:
        for out in pattern:
            GPIO.output(20, GPIO.HIGH)
            time.sleep(0.0001)
        GPIO.output(20, GPIO.LOW)
    except KeyboardInterrupt:
        GPIO.cleanup()
    GPIO.cleanup()

__main__()

import RPi.GPIO as GPIO
import time

pattern = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
           0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,
           0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,
           0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,
           0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,
           1,1,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,1,1,1]


def __main__():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(20,GPIO.OUT)
    try:
        for out in pattern:
            GPIO.output(20, GPIO.HIGH)
            time.sleep(0.0001)
        GPIO.output(20, GPIO.LOW)
    except KeyboardInterrupt:
        GPIO.cleanup()
    GPIO.cleanup()

__main__()

これで、リモコンの信号を再現できるはず!

そう思っていましたが、

テレビは何も反応しませんでした。

やっぱり考えは甘かったか。

そこで、ネットの情報を探してみると、

こんなサイトを見つけました。

https://deviceplus.jp/hobby/entry_y18/

リモコンの信号を読み取って、再現するライブラリがラズパイにあるらしい。

しかも、このサイトの中では、音声でテレビをコントロールしようともしている。

ならばこっちは、最終的にAlexaでコントロールできるようにしてしまおうか。

できるよね?

知らんけど。

いろいろ調べてみる。

赤外線LED、レシーバを購入しました。

右が赤外線LED、左側が赤外線受信機です。

梅沢無線で買ってきました。

この二つがあれば、赤外線通信ができるはずです。

はい、今度は赤外線をやってみたいと思います。

目指すところは、テレビのリモコンの赤外線を受信機でトレースし、その信号を再現してテレビを操作しちゃおうと考えています。

で、まずは赤外線LED(信号送信側)を試してみようと思うのですが、

例によって、「抵抗は必要なのか?」という疑問が出てきます。

はい、過去に発光ダイオードを抵抗なしで電圧かけたことによって、発光ダイオードを破壊したことがあります。

初心者あるあるですよね??

秋月電子のオンラインショップのページにデータシートなどが掲載されているので、これを確認すると、

https://akizukidenshi.com/catalog/g/gI-03261/

やはり抵抗が必要なようです。

では、何Ωの抵抗が必要なのか。

計算式は、

(V - VF) ÷ IF = Ω

データシートからこの公式に当てはめると、

(5 - 1,6) ÷ 0.1 = 34Ω

手持ちにある20Ωの抵抗を1~2個使えば大体近い値になりそうです。

で、回路を組んでみた。

電圧をかけてみる。

抵抗2個の場合。

抵抗1個の場合。

肉眼では分かりませんが、スマホのカメラを通してみてみると、赤外線LEDの先っちょが赤く光っているのが分かると思います。

写真では1個と2個で違いが分かりませんが、実際にスマホのカメラのプレビュー画面を見てみると、1個の方が光が強いように見えます。

今回は入力を5V一定にしましたが、これをプログラマブルなパルス信号にすると、リモコンの赤外線通信を再現できると思います。

【ラズパイ】【カメラ】クライアントを閉じたら動画撮影を終了する

前回のままだと、クライアント側(ブラウザ)を撮影中に閉じてしまうと、動画撮影を終了する人がいなくなってしまいます。

これを防ぐためには、クライアントが生存していることを常に確認する処理が必要になります。

まぁ、今回はプレビュー画面で常にデータのやりとりを行っているので、これを利用しましょう。

    def do_GET(self):
        parsed = urlparse(self.path)
        if parsed.path == '/Streaming':
            global lasttime
            lasttime = time.time()

            enc = sys.getfilesystemencoding()

プレビュー画をリクエストがあったら、その時間を記憶しておきます。

def videoCapture():
    global capture
    global out

    while capture:
        nowtime = time.time()
        if nowtime - lasttime > 10:
            capture = False
            out.release()
            out = None
            break
        _, img = cap.read()
        out.write(img)

ビデオキャプチャーの周期処理の中で、現在時刻と、プレビュー画要求時の時刻を比較します。

周期処理の時刻がキャプチャー時の時刻より10秒経過していたら撮影を終了します。

ブラウザを撮影途中で閉じた場合、プレビュー画要求時の時刻が更新されなくなりますので、こうすることで、ブラウザを閉じてから10秒後に撮影は終了します。

さて、カメラでやりたいことが終わってしまった・・・

次何しようかな。

【ラズパイ】【カメラ】WEBから動画を撮影する。

今回はWebのプレビュー画面から動画の撮影を行いたいと思います。

赤丸のボタンを設置し、押すと録画開始、もう一度押すと録画停止という感じです。

まずはサーバ側。

すでに動画を撮影する方法は知っているので、これを使用します。

POSTリクエストを受け付ける処理を書いていきます。

    def do_POST(self):
        global thread
        global aviFilename
        global capture
        global out

        content_len = int(self.headers.get('content-length'))
        requestBody = json.loads(self.rfile.read(content_len).decode('utf-8'))

        if requestBody['contents']['command'] == 1:
            _, img = cap.read()
            dt_now = datetime.datetime.now()
            filename = dt_now.strftime('%Y%m%d_%H%M%S')+".jpg"
            cv2.imwrite(filename, img)

            response = {
                'status' : 200,
                'path': "http://pi4.local:8000/" + filename
            }
        if requestBody['contents']['command'] == 2:
            dt_now = datetime.datetime.now()
            aviFilename = dt_now.strftime('%Y%m%d_%H%M%S')+".avi"
            out = cv2.VideoWriter(aviFilename, fourcc, 20.0, (640,480))
            
            capture = True
            thread = threading.Thread(target=videoCapture)
            thread.start()

            response = {
                'status' : 200,
            }
        if requestBody['contents']['command'] == 3:
            capture = False

            thread.join()

            out.release()
            out = None

            response = {
                'status' : 200,
                'path': "http://pi4.local:8000/" + aviFilename
            }
        else:
            response = {
                'status' : 200
            }

        self.send_response(200)
        self.send_header('Content-type', 'application/json')
        self.end_headers()
        responseBody = json.dumps(response)

        self.wfile.write(responseBody.encode('utf-8'))

録画開始(command=2)を受信した場合、VideoWriterを作成し、動画を保存するスレッドを起動します。

録画停止(command=3)を受信した場合は、このスレッドを停止するように動作します。

実際のスレッド処理はこんな感じです。

def videoCapture():
    while capture:
        _, img = cap.read()
        out.write(img)

captureフラグがTrueのときは延々とカメラの映像を動画ファイルに保存します。

録画停止時にcaptureフラグをFalseに変更し、このスレッドが終了するのをjoinで待ってから、作成された動画ファイルのパスを返信します。

動画のダウンロードですが、たぶん、content-typeを用意しないと行けないので、Dictionaryに追加しています。

CONTENT_TYPE = {'.html': 'text/html; charset=utf-8', '.txt': 'text/plain; charset=utf-8', '.js': 'text/javascript', '.json': 'application/json',
        '.jpeg': 'image/jpeg', '.jpg': 'image/jpeg', '.png': 'image/png', '.gif': 'image/gif',
        '.css':'text/css', '.avi': 'video/x-msvideo'}

さて、このままだと録画中にブラウザを閉じてしまって、録画を止める手段が無くなってしまいます。

これの対処を次回やります。

(そろそろカメラネタも無くなってきた。)

【ラズパイ】【カメラ】Webからカメラの画像を保存する。

前回はカメラのプレビュー画面をWebに表示させましたが、

今回はWebからシャッターボタンを設置して、カメラの画像を保存させます。

すでにPOSTリクエストを処理する方法も知っていますし、カメラの画像を保存する方法も知っているので、これらを組み合わせればできるはずです。

まずはサーバ(ラズパイ)の処理。


    def do_POST(self):
        content_len = int(self.headers.get('content-length'))
        requestBody = json.loads(self.rfile.read(content_len).decode('utf-8'))

        if requestBody['contents']['command'] == 1:
            _, img = cap.read()
            dt_now = datetime.datetime.now()
            filename = dt_now.strftime('%Y%m%d_%H%M%S')+".jpg"
            cv2.imwrite(filename, img)

            response = {
                'status' : 200,
                'path': "http://pi4.local:8000/" + filename
            }
        else:
            response = {
                'status' : 200
            }

        self.send_response(200)
        self.send_header('Content-type', 'application/json')
        self.end_headers()
        responseBody = json.dumps(response)

        self.wfile.write(responseBody.encode('utf-8'))

POST処理を追加しました。

カメラの画像をファイルに保存し、そのファイルパスをレスポンスで返すようなイメージです。

次はWeb側。

<!DOCTYPE html>
<html>
<head>
  <title>My first Vue app</title>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script src="vue.min.js"></script>
  <script src="jquery-3.5.1.slim.min.js"></script>
</head>
<body>
  <div id="app">
      <image id="camera" src="" /><br />
      <button @click="onShutter">Shutter</button><br />
      <a id="picture" href="" target="_blank">{{ path }}</a>
  </div>

  <script>
    var app = new Vue({
      el: '#app',
      data: {
        timer: null,
        param: {},
        contents: {
          command: 1,
        },
        path: "",
      },
      created: function() {
        self = this;
        this.timer = setInterval(function() {self.onLoad()}, 50)
      },
      methods: {
        onLoad: function() {
            axios.get('http://pi4.local:8000/Streaming').then(function(response){
                $("#camera").attr('src', response.data.image);
            }).catch(function(error){
            });
        },
        onShutter: function() {
          self = this;
          this.param.contents = this.contents;
          axios.post('http://pi4.local:8000/', this.param).then(function(response){
                $("#picture").attr('href', response.data.path);
                self.path = response.data.path;
            }).catch(function(error){
            });
        }
      }
    })
  </script>
</body>
</html>

ボタンとリンクを追加しました。

ボタンをクリックすると、onShutter処理が実行され、ラズパイ側にPOSTリクエストを送信します。

そのレスポンスから画像のファイルパスを取得し、リンクに反映させます。

同じ仕組みで動画の撮影もできそう。

次回やります。