ラズベリーパイ内でアプリケーションを動かしてみる

スポンサーリンク
tech系(Linux)
スポンサーリンク

(※2023年10月13日にもともと3つあった記事を1つにまとめました。)

こんちわー。

今回はラズベリーパイでWeb serverを構築するチャレンジにあたり、記録を残していこうと思います。

今回ラズベリーパイを買ったのは勉強のためという事もあるのですが、一番の理由は自分が持っている米国株の監視サービスをラズベリーパイをWeb serverとして構築しようという事を目論んでいます。

今回はラズベリーパイ、略してラズパイの構築の様子を記載していきます。ぶっちゃけ、説明書がついていますのでビビらなくてもできますし、僕の記事を読むより説明書の方が分かりやすいと思います(笑)なのでぜひ買ってトライしてみてみてください。こういうのは実際に手を動かさないと学べないものです。

構築

今回購入したのが以下のものです。ラズベリーパイ4です。僕は今回ラズベリーパイを始めて触ります。

まずは以下のものを使います。真ん中の黒い箱がケースになります。左の緑の基盤がラズベリーパイ本体になります。そして右のビニールに入っているものがヒートシンクになります。右端の見切れている緑の冊子が説明書になります。英語と日本語で書かれています。

まず以下のヒートシンクを基盤の所定の位置にくっつけます。ヒートシンクには両面テープがついているので特に接着剤などは必要ありません。以下2つの画像がヒートシンクをくっつける前とくっつけた後です。わざわざ二つの画像用意する必要がなかったですかね(笑)

次にラズベリーパイの基盤(本体)がむき出しにならないように基盤を入れるケースにファンをつけていきます。

ファンは銀のねじで止めます。ケースのふたの方に着けることになります。以下がケースを開いてファンを所定位置に設置した様子です。(今回購入したラズベリーパイのスタートアップキットには銀のねじ、黒いねじ、プラスドライバーがついてきますので、別途購入する必要はありません。)

上記の画像のファンを銀のねじで固定したら以下のような感じです。

基盤をケースの箱の方に着けて黒いねじで固定します。これが意外と入れるのがムズイです。。。イライラして力を入れて壊さないように注意してください(笑)。基盤をはめたらファンのコードを基盤の指定の位置に差し込みます。こちらの差し込む場所は決まっていて場所は説明書に書いてあります。

これでケースの内側は完成です。最後にOSが入っているSDカードがあるのでこれも所定の場所に差し込みます。こちらも結構むずいです。基盤がずれていると差し込めない可能性もあるので注意です。

以下が差し込んだ結果の様子です。向きを間違えたりして、入らないと焦って力を強くして壊してしまう事のないように注意です。これもなかなか入らなくて苦戦しました。。。(-_-;)

画面に接続します。ラズパイにはUSB端子が2つついているのでそこにマウスとキーボードを接続、コンセントで電源をさして、コンセントについているスイッチをONにすると。。。

画面がついた!

ここから国の設定であったり言語であったりを設定し、OSをインストールしていきます。画面の手順通りに進めていけば問題ないはずです。

ちなみにOSのインストール1時間ぐらいかかるので時間には余裕をもっておくといいでしょう。

設定

設定を行っていきます。

以下のサイトを参考にしています。以下のサイトがとても分かりやすく、僕もこのサイトの通りにしただけなので以下のサイトを見てみれば大丈夫です(笑)

raspberry piでお手軽自家サーバー webサーバー編 - Qiita
ラズベリーパイでサーバー運用はすでに多くの方がやられていて今更感がありますが、現行の環境で今までラズパイやサーバーを触ってこなかった人が、勉強を兼ねてお手軽にラズパイサーバーを立ち上げるにはどうする…

一応、上記のサイトの補足情報を載せていく感じにしようと思いますが上記サイトの方が分かりやすいです。(-_-;)

上記サイトに書いてありますがコマンドを順に行っていきます。

最新パッケージのインストール

$ sudo apt-get update
$ sudo apt-get upgrade

OSのアップデート

$ sudo apt-get dist-upgrade

ラズベリーパイのファームウェアのアップデート

$ sudo rpi-update

ここで再起動します。

上記のリンク①のページではセキュリティ対策としてrootのパスワードの設定とデフォルトユーザpiの削除を行っています。こちらはサーバとしてラズベリーパイを公開する前に行う事らしく、今回は公開することはないのですが、「重要度大」と書かれていたのでやってしまいました。( ´∀` )

rootのパスワードの設定

$ sudo passwd root

このコマンドを打ったらパスワードの入力するところになるのでパスワードを入力して設定します

新規ユーザを作成

以下の[ユーザー名]のところは自分で任意に決めたユーザ名を入力してください。

補足としてはユーザを確認するときは「sudo vi /etc/passwd」で確認できます。

ユーザを加えた時も「sudo vi /etc/passwd」を追加されたのを確認できます。

$ sudo adduser [ユーザー名]

新規ユーザにルート権限を与える

$ sudo gpasswd -a [ユーザー名] sudo

新しいユーザでログインしてpiを削除する

piはデフォルトのユーザです。乗っ取り対策のためにpiを削除します。

「メニュー」→「設定」→「raspberry piの設定」→「現在のユーザーとしてログインする」のチェックを外してから再起動。

新しいユーザにログインした後以下のコマンドを実行しpiを削除します

$ sudo userdel -r pi

ファイヤーウォール(ufw)のインストール

$ sudo apt-get install ufw

ポートの解放(22と80)

今回はSSHぐらいしか接続はしないから22しか開けなくていいかもしれないけど、一応リンク①のサイトに乗っ取って80も開けときます。

$ sudo ufw allow 22
$ sudo ufw allow 80
$ sudo ufw reload

ファイアーウォールの有効化

$ sudo ufw enable

有効化されているかの確認

$ sudo ufw status

SSHの設定 + 接続確認

rootでのssh接続ログインを禁止する

設定ファイルであるsshd_configでrootへのログイン禁止を設定します。

まず以下のコマンドでsshd_configを開き、

$ sudo mousepad /etc/ssh/sshd_config

「#PermitRootLogin prohibit-password」と書かれた行があるので、先頭の#を外して「PermitRootLogin prohibit-password」とします。

SSHを許可する

「メニュー」→「設定」→「Raspberry Piの設定」→「インターフェース」→「SSH」を有効にして再起動します。

SSHの接続確認

SSH接続の確認はwindowsで行いました。なので、PCを別途用意します。普段使っているPCで大丈夫です。確認自体はコマンドプロンプトでできます。

ラズパイの方で「ip a」とコマンドを打ってましょう

$ ip a

そしたら以下のような結果が出るかと思います。

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN group default qlen 1000
    link/ether dc:a6:32:a2:d9:96 brd ff:ff:ff:ff:ff:ff
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether dc:a6:32:a2:d9:97 brd ff:ff:ff:ff:ff:ff
    inet XXX.XXX.XX.X/24 brd XXX.XXX.XX.255 scope global dynamic noprefixroute wlan0
       valid_lft 78172sec preferred_lft 67372sec
    inet6 2400:2412:243:df00:938:3afd:486e:5b10/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 86386sec preferred_lft 14386sec
    inet6 fe80::44e6:cb59:ac2f:409c/64 scope link
       valid_lft forever preferred_lft forever

3の項目のwlan0、その中にinetがあると思いますがここのip(上記だとXXX.XXX.XX.Xのところ。実際にはXに数字の値が入っています。)を使って確認します。windowsPCのコマンドプロンプトを起動してラズベリーパイにssh接続してみましょう。

コマンドプロンプトで以下のようにコマンドを打ちます。<USERNAME>のところは今回新しく設定したユーザ名を入れてください。XXX.XXX.XX.Xのところはip -aで調べて出た値、さっきの値を入れてください。

ssh <USERNAME>@XXX.XXX.XX.X -o "ServerAliveInterval 15"

これで接続できるはず。

ここまで出来たら、windows PCからssh接続できるのでラズパイはディスプレイから外していいです。もちろん、起動したままにしてくださいね。

teratermをwindows PCでインストールして使うのがおすすめです。これに関しては以下のサイトが参考になります。

WindowsからRaspberry Pi 3 Model B+にリモート接続する - Qiita
Windows(やMac等)からRaspberry Piにリモート接続する場合、SSH接続とVNC接続の2つの接続方法があります。SSH接続は主にCLI環境、VNC接続は主にGUI環境(リモートデス…

このサイトではリモートデスクトップの方法も書いてあるのですが、僕はこの方法を試してもうまくいきませんでした。。。

一応以下のサイトでは画面が表示されない場合の対処法が書かれていましたが、そうではなくそもそもipアドレスを入れてリモートデスクトップ接続をしようとしてもtimeoutになってしまいます。

Raspberry Pi 4にSSHとVNCで接続してみた | DevelopersIO
先日、Raspberry Pi 4を購入しましたので、さっそく開発環境を整えているところです。Rasberry Pi(ラズベリーパイ)の公式からリリースされたRaspberry Pi ImagerでmicroSDカードに …

ちょっとここは調査中です。。。

プログラム作成 + 稼働

プログラムできる環境を整える

今回使用しているラズベリーパイにはpython3がインストールされていますが、pip3コマンドがインストールされていませんのでpip3コマンドをインストールします。

$ sudo apt install python3-pip

今回作成するプログラムにはpandasを使うのですが、まだ入ってませんのでこのままだとpandasを入れていないのでpythonを実行したらエラーが発生してしまいます。

pandasを入れます。

$ pip3 install pandas

これでpythonで書いたコードをを以下のコマンドで実行します。(この時はテスト用のコードを作ってます。そのコード内でpandasをimportで読み込んでいるという想定です。)

$ python3 scripts/test.py

そこでなぜかよくわからないエラーが発生します。以下のようなやつです。

Traceback (most recent call last):
  File "scripts/test.py", line 2, in <module>
    import pandas as pd
  File "/usr/local/lib/python3.7/dist-packages/pandas/__init__.py", line 17, in <module>
    "Unable to import required dependencies:\n" + "\n".join(missing_dependencies)
ImportError: Unable to import required dependencies:
numpy:

IMPORTANT: PLEASE READ THIS FOR ADVICE ON HOW TO SOLVE THIS ISSUE!

Importing the numpy C-extensions failed. This error can happen for
many reasons, often due to issues with your setup or how NumPy was
installed.

We have compiled some common reasons and troubleshooting tips at:

    https://numpy.org/devdocs/user/troubleshooting-importerror.html

Please note and check the following:

  * The Python version is: Python3.7 from "/usr/bin/python3"
  * The NumPy version is: "1.19.4"S

and make sure that they are the versions you expect.
Please carefully study the documentation linked above for further help.

Original error was: libf77blas.so.3: cannot open shared object file: No such file or directory

これの解決は以下の記事に書いてます。

https://apao-m-appare99999.com/?p=340

上記の記事にも書いてありますが、以下のように「libatlas-base-dev」をインストールすればOK

$ sudo apt-get install libatlas-base-dev

これで準備完了です。

LINE Notifyに登録

次にLINE Notifyに登録します。

が、こちらはネットに落ちている画像付きで解説しているサイトを参考にしていただければ、そちらの方が分かりやすいかと思います。

僕は以下のサイトを参考にしました。

PythonでLINE Notifyへ通知を送る - Qiita
ちょっとしたツールやbotを作ったとき、LINEに通知したいなーということがあります。その度にググったり過去のソースを見たりしてるのでここにやり方を記しておきます。LINE Notifyのトーク…

これでトークンが発行されているのでこのトークンを使っていくことになります。

ラズベリーパイ内で動かすプログラムを作成する

プログラムをラズベリーパイにsshで入ってラズベリーパイ内で書いてもいいのですが、windowsにjupyter notebookが入っているので、まずはjupyter notebookでコードを書いてテストをしてうまく動いたらコピペをしてラズベリーパイ内にコードを残すという方法が割かしお勧めです。

プログラムは以下のようになりました。細かい部分に関する解説は長くなってしまうので省きます。注意点だけ以下のプログラムコードの下に書いてあります。またファイルパスの名前などは少し変えています。参考にする際はファイル構成なども注意して各々の構成を元に書いてみてください。

import requests
import pandas as pd
from bs4 import BeautifulSoup
import re
import numpy as np
import urllib.request, urllib.error
import time

################################################################

df_monitering = pd.read_table("/home/USERNAME/scripts/monitering_data.txt", sep=" ")
ticker_list = []
ticker_list = df_monitering['Ticker'].tolist()

################################################################

## 銘柄毎に見ていく
index_count = 0

df_dict = df_monitering.to_dict(orient='duct')

flag_buy_message = 0
flag_sell_message = 0

for ticker_item in ticker_list:
    time.sleep(2)
    
    #######   各変数を取ってくる   ######
    base_url = "https://finviz.com/quote.ashx?t="
    url = base_url + ticker_item
    user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36'
    headers = {'User-Agent': user_agent}

    request = urllib.request.Request(
        url=url,
        headers=headers
    )

    soup = BeautifulSoup(requests.get(url, headers = headers).content, 'html.parser')
    
    ret_table = soup.find('table', {'class':'snapshot-table2'})
    ret_td = ret_table.select('td')
    
    if df_dict["BuyorSell"][index_count] == "buy":
        rsi_flag = 0
        sam50_flag = 0
        sam200_flag = 0
        opermargin_flag = 0

        for td_elem in ret_td:    
            try:
                if rsi_flag == 1:
                    rsi = float(td_elem.get_text())
                    rsi_flag = 0
            except:
                rsi = 999

            if td_elem.get_text() == "RSI (14)":
                rsi_flag = 1

    if df_dict["BuyorSell"][index_count] == "sell":
        rsi_flag = 0
        
        for td_elem in ret_td:
            try:
                if rsi_flag == 1:
                    rsi = float(td_elem.get_text())
                    rsi_flag = 0
            except:
                rsi = -1

            if td_elem.get_text() == "RSI (14)":
                rsi_flag = 1
                
    ###### 値が売りもしくは買いのタイミングの値を満たしていれば発砲する ######
    if df_dict["BuyorSell"][index_count] == "buy":
        print("test")
        if rsi <= df_dict["RSI"][index_count]:
            
            file = {"imageFile":open('/home/USERNAME/scripts/image_data/eva_monica_rain.png','rb')}
            message_buy1 = ("この銘柄が買い時ですわ : " + ticker_item + \
                      "\n監視パラメータは以下ですわ\nRSI : " + str(df_dict["RSI"][index_count]) + \
                       "\n現在の実データは以下ですわ\nRSI : " + str(rsi)\
                      )
            url = "https://notify-api.line.me/api/notify"
            token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
            headers = {'Authorization': f'Bearer {token}'}
            
            param_buy1 = {"message": message_buy1}
            if flag_buy_message == 0:
                message_buy0 = "買い時銘柄がありますわ"
                param_buy0 = {"message": message_buy0}
                requests.post(url ,headers = headers, files=file)
                requests.post(url ,headers = headers, params=param_buy0)
                flag_buy_message = 1
                
            requests.post(url ,headers = headers, params=param_buy1)
            
    if df_dict["BuyorSell"][index_count] == "sell":
        print(df_dict["RSI"][index_count])
        if rsi >= df_dict["RSI"][index_count]:
            file = {"imageFile":open('/home/USERNAME/scripts/image_data/eva_monica_rain.png','rb')}
            message_sell1 = ("この銘柄が売り時ですわ : " + ticker_item + \
                      "\n監視パラメータは以下ですわ\nRSI : " + str(df_dict["RSI"][index_count]) + \
                       "\n現在の実データは以下ですわ\nRSI : " + str(rsi)\
                      )
            url = "https://notify-api.line.me/api/notify"
            token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
            headers = {'Authorization': f'Bearer {token}'}
            
            param_sell1 = {"message": message_sell1}
            if flag_sell_message == 0:
                message_sell0 = "売り時銘柄がありますわ"
                param_sell0 = {"message": message_sell0}
                requests.post(url ,headers = headers, files=file)
                requests.post(url ,headers = headers, params=param_sell0)
                flag_sell_message = 1
            
            requests.post(url ,headers = headers, params=param_sell1)

    index_count = index_count + 1

monitering_data.txtについて

11行目にmonitering_data.txtというtextファイルを置いてありますが、これは以下のように書いてあります。カラムはTickerとRSIとBuyorSellでDataframeができるようになっており、BuyorSellの値がsellになっていたらRSIの指定している値より大きくなってたら売り時にしてあり、逆に、BuyorSellの値がbuyになっていたらRSIの指定している値より小さくなってたら売り時のようにしてあります。

Ticker RSI BuyorSell
AAPL 65.0 sell
TSLA 60.0 sell

今回はテストという事で、アップル(AAPL)とテスラ(TSLA)の値で検証していきます。

画像の配置

80行目と102行目のところ、image_dataというフォルダ内のeva_monica_rain.pngというファイルを読み込んでいるのですがこれは以下のような画像です。名前は「エヴァ・モニカ・レイン」でお嬢様キャラです。これでLINE Notfyの使用により親しみが込められると考えました。

LINE Notifyはアイコンの変更ができないようです。当初アイコンを変更してお知らせキャラとして親しみやすいようにしたかったのですが、それができません。

そこで、一旦の対応策として画像で話しているような感じにできたらいいなと思いました。

補足

この画像をwindows PCからteratermでssh接続しているラズベリーパイに移そうと思ったのですが、当初、scpでうまくいくのかなと思ったのですが、イマイチうまくいかなかったです。

そこで以下のqiitaのサイトを見つけました。ドラック&ドロップでできるんですね。結構目からうろこでした。

TeraTermで効率良くファイル転送 - Qiita
Windowsを使用している開発者の方々は、LinuxサーバにSSH接続する場合にTeraTermをよく使われるかと思います。そのTeraTermで、効率的にファイル転送(Windowsクライアン…

LINE Notifyのトークンについて

86行目、109行目のところに”XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX”と記載してありますが、こちらはLINE Notifyで発行したトークンになります。発行したトークンの値をXの部分に入れてください。

cronを設定し、プログラムを実行するタイマーを設定する

さて、最後の仕上げです。発報のタイマーをセットします。以下のコマンドでcronの設定を開きます。

$ crontab -e

そして一番下に以下の一文を追記します。

00 21 * * * python3 /home/USERNAME/scripts/test.py

これは21時00分になったらtest.pyを実行するという意味です。これでもう発報のタイマーの設定は完了です。

一旦、完成

以上で一旦、作りたかったものは完成しました。もし、textファイルに書いてあった条件を満たしている銘柄があるなら、21時になるとLINE NotifyでLINEにメッセージを送ってくれます。以下のような感じ。テストでは21時を過ぎてしまっていたため、21時25分にして行いました。

あれ?と思うかもしれません。そうです、「エヴァ・モニカ・レイン」の画像が表示されていないんです。windows PCのjupyter notebookでテストしたときはちゃんと送れていたんです。。。

理想(windowsのjupyter notebookでデモをした際には成功)

【余談】残った課題

一旦、完成といたしましたが、以下の課題が残っています。

  • ラズベリーパイからLINE Notifyに画像が送れない
  • VNC Viewerが使えない

これら2つの課題が解決した暁には本当の完成としたいところです。(「VNC Viewerが使えない」の方は前回の記事で触れた部分になります。)

時間を見つけて対応して、できたらブログに載せようと思います。


タイトルとURLをコピーしました