From 5234197c2dd38e017efd3cb76b5e16638ed1c4fe Mon Sep 17 00:00:00 2001 From: Nishimura Date: Tue, 2 Sep 2025 16:15:47 +0900 Subject: [PATCH 1/2] first commit --- 2025_09_02.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 2025_09_02.sh diff --git a/2025_09_02.sh b/2025_09_02.sh new file mode 100644 index 0000000..3b2d9c0 --- /dev/null +++ b/2025_09_02.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +echo"_START_" +BASE_DIR=`pwd` +echo"現在のディレクトリは${BASE_DIR}です" +ls -al +echo "${BASE_DIR}内のファイルリストを表示" + +exit + + + From 8040ffd48ca18d65c59ed67a8a4bf77cd5e765d8 Mon Sep 17 00:00:00 2001 From: Nishimura Date: Fri, 5 Sep 2025 16:17:38 +0900 Subject: [PATCH 2/2] new --- src/sample/2025/.gitkeep | 0 src/sample/2025/ne241100 | 1 + src/sample/data/.gitkeep | 0 src/sample/data/cpu_thermal.dat | 12 ++ src/sample/getAccelerometerData.py | 19 +++ src/sample/get_cpu_temp.sh | 8 ++ src/sample/load_mat_file.py | 31 +++++ src/sample/shellscript_sample_01.sh | 10 ++ src/sample/shellscript_sample_02.sh | 23 ++++ src/sample/update_channel.py | 78 +++++++++++ src/sample/web_version/collect.py | 96 +++++++++++++ src/sample/web_version/focus.service | 12 ++ src/sample/web_version/index.html | 184 +++++++++++++++++++++++++ src/sample/web_version/js/detect_os.js | 42 ++++++ src/sample/web_version/main.py | 70 ++++++++++ src/sample/web_version/save.csv | 25 ++++ 16 files changed, 611 insertions(+) create mode 100644 src/sample/2025/.gitkeep create mode 160000 src/sample/2025/ne241100 create mode 100644 src/sample/data/.gitkeep create mode 100644 src/sample/data/cpu_thermal.dat create mode 100644 src/sample/getAccelerometerData.py create mode 100755 src/sample/get_cpu_temp.sh create mode 100644 src/sample/load_mat_file.py create mode 100644 src/sample/shellscript_sample_01.sh create mode 100644 src/sample/shellscript_sample_02.sh create mode 100644 src/sample/update_channel.py create mode 100644 src/sample/web_version/collect.py create mode 100644 src/sample/web_version/focus.service create mode 100644 src/sample/web_version/index.html create mode 100644 src/sample/web_version/js/detect_os.js create mode 100644 src/sample/web_version/main.py create mode 100644 src/sample/web_version/save.csv diff --git a/src/sample/2025/.gitkeep b/src/sample/2025/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/sample/2025/ne241100 b/src/sample/2025/ne241100 new file mode 160000 index 0000000..c777f88 --- /dev/null +++ b/src/sample/2025/ne241100 @@ -0,0 +1 @@ +Subproject commit c777f889cd0c9e5b43c9dcf691317ffc5abc96d6 diff --git a/src/sample/data/.gitkeep b/src/sample/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/sample/data/cpu_thermal.dat b/src/sample/data/cpu_thermal.dat new file mode 100644 index 0000000..36e841f --- /dev/null +++ b/src/sample/data/cpu_thermal.dat @@ -0,0 +1,12 @@ +2021-09-09T11:36:09 JST CPU Thermal level: 84 +2021-09-09T11:36:25 JST CPU Thermal level: 83 +2022-09-09T16:13:07 JST CPU Power: 76 mW +2022-09-09T16:13:23 JST CPU Power: 410 mW +2022-09-09T16:13:39 JST CPU Power: 392 mW +2022-09-09T16:13:55 JST CPU Power: 54 mW +2022-09-09T16:14:11 JST CPU Power: 121 mW +2022-09-09T16:14:27 JST CPU Power: 220 mW +2022-09-09T16:14:44 JST CPU Power: 124 mW +2022-09-09T16:15:00 JST CPU Power: 145 mW +2022-09-09T16:15:16 JST CPU Power: 61 mW +2022-09-09T16:15:32 JST CPU Power: 161 mW diff --git a/src/sample/getAccelerometerData.py b/src/sample/getAccelerometerData.py new file mode 100644 index 0000000..218a1d2 --- /dev/null +++ b/src/sample/getAccelerometerData.py @@ -0,0 +1,19 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import sys + +base_dir = '/home/pi' +prj_dir = base_dir + '/csn_raspi' +data_dir = prj_dir + '/earthquakes' +base_filename = 'csn.log' + +def getCurrentData(): + with open(data_dir + '/' + base_filename, mode='r') as f: + reader = f.readlines() + last_index = len(reader) - 1 + return reader[last_index] + +if __name__ == "__main__": + _data = getCurrentData() + print(_data) diff --git a/src/sample/get_cpu_temp.sh b/src/sample/get_cpu_temp.sh new file mode 100755 index 0000000..e944603 --- /dev/null +++ b/src/sample/get_cpu_temp.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +while true +do +# /usr/bin/powermetrics -b 1 -n 1 -s tasks --show-process-energy --show-process-io | sed -l "s/^/$(date +%Y-%m-%dT%H:%M:%S" "%Z) /" >> ./data/process_energy_io.dat + /usr/bin/powermetrics -b 1 -n 1 -s smc | grep "CPU Thermal level" | sed -l "s/^/$(date +%Y-%m-%dT%H:%M:%S" "%Z) /" >> ./data/cpu_thermal.dat + sleep 10 +done diff --git a/src/sample/load_mat_file.py b/src/sample/load_mat_file.py new file mode 100644 index 0000000..5347f48 --- /dev/null +++ b/src/sample/load_mat_file.py @@ -0,0 +1,31 @@ +import scipy.io +#mat = scipy.io.loadmat('/Users/hiroki_u/MATLAB-Drive/MobileSensorData/sensorlog_20210905_221919.mat') +#mat = scipy.io.loadmat('/Users/hiroki_u/MATLAB-Drive/MobileSensorData/sensorlog_20210905_233837.mat') +mat = scipy.io.loadmat('/Users/hiroki_u/MATLAB-Drive/test.mat') + +print(mat) +print(sorted(mat.keys())) +print(mat['__header__']) +print(mat['__version__']) +print(mat['__globals__']) +#print(mat['None']) +#print(mat['None'][0][3]) +#print(mat['__function_workspace__'][0].shape[0]) +#print(mat['__function_workspace__'][0]) + +#teststruct = mat['__function_workspace__'] +#print(teststruct.dtype) +#print(teststruct.size) +#print(teststruct.shape) + +#for _data in mat['__function_workspace__']: +# for _d in _data: +# print(_d) + +print(mat['matVar1']) + +for _data in mat['matVar1']: + print("x : " + str(_data[0]), end = '\t') + print("y : " + str(_data[1]), end = '\t') + print("z : " + str(_data[2])) + diff --git a/src/sample/shellscript_sample_01.sh b/src/sample/shellscript_sample_01.sh new file mode 100644 index 0000000..206ab56 --- /dev/null +++ b/src/sample/shellscript_sample_01.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +echo ”___START___” +BASE_DIR=`pwd` +echo “現在のディレクトリは${BASE_DIR}です” +ls –al +echo “${BASE_DIR}内のファイルリストを表示” + +exit + diff --git a/src/sample/shellscript_sample_02.sh b/src/sample/shellscript_sample_02.sh new file mode 100644 index 0000000..ee85719 --- /dev/null +++ b/src/sample/shellscript_sample_02.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +echo ”___START___” +BASE_DIR=`pwd` +echo “現在のディレクトリは${BASE_DIR}です” +ls -al +echo “${BASE_DIR}内のファイルリストを表示しました。” +echo "" +echo “${BASE_DIR}内のファイルリストをサイズの降順に並べ替えます。” +ls -alh | sort -k 5 -h -r + + +echo “${BASE_DIR}以下のファイル名をすべて取得します。” +FILELIST=`find ${BASE_DIR} -name '*.*'` +for _FILES in ${FILELIST[@]}; do + echo ${_FILES} + FILENAME=${_FILES##*/} + echo "${FILENAME}と同じ名前のをファイル探します。" + find ${HOME}/Documents -name '${FILENAME' +done + +exit + diff --git a/src/sample/update_channel.py b/src/sample/update_channel.py new file mode 100644 index 0000000..2b0cf32 --- /dev/null +++ b/src/sample/update_channel.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import sys +import requests + +api_key = '' +channle_id = '' +data_file = "./data/cpu_thermal.dat" + +_ts_base_url = "https://api.thingspeak.com" +ts_update_url = _ts_base_url + "/update" +# GET https://api.thingspeak.com/update?api_key=MSUJ80Z21B6XIS7G&field1=0 + +# HTTPでのデータ登録のための設定 +headers = {'X-THINGSPEAKAPIKEY': api_key} + + +#------ +# powermetricsで取得したCPU die tempartureの値を取得して、配列を返す +# 引数:データが入ったファイルのパス +# return : cpu_temp リスト(配列) +# 2021-09-07T23:59:36 JST CPU die temperature: 69.77 C +#------ +def getCpuTempFromFile(filename): + + _cpu_temps = [] + + # ファイルの存在を確認 + is_file = os.path.exists(filename) + if not is_file: + print("正しいファイル名を指定してください。") + sys.exit(1) + + # ファイルを開いてデータを取得 + with open(filename) as f: + _lines = f.readlines() + for _line in _lines: # 配列を1行ずつ取り出す + _data = _line.split() # ファイルの1行を空白で区切って配列にする + print(_data) # 1行の中身を確認 + # ここで、1行のデータのどの部分をどう使うか考えて処理する + if _data[5] == 'Normal' : + _data[5] = 100 + _cpu_temps.append(_data[5]) # 区切った5つ目を結果用の配列に追記する + + return _cpu_temps + +#------ +# 指定したデータをThingSpeakに登録 +# 引数:req_url, headers, post_data +#------ +def post2ThingSpeak(req_url, headers, post_data): + while True: + response = requests.post(req_url, headers=headers, data=post_data) + if response.text != '0': + break + time.sleep(1) + + +# メイン処理 +cpu_temps = [] + +print(data_file + " のデータをThingSpeakに登録します。") +# CPU温度の情報をファイルから取得 +cpu_temps = getCpuTempFromFile(data_file) + +print("CPU温度のデータが " + str(len(cpu_temps)) + " 件あります。") +# データの中身をすべて表示 +print(cpu_temps) + +# 最新のデータ(一番最後)をThingSpeakに登録 +# 登録するデータを設定 +post_data = {'field1': cpu_temps[-1]} +post2ThingSpeak(ts_update_url, headers, post_data) + +print("CPU温度:" + str(cpu_temps[-1]) + " を登録しました。") +sys.exit(0) diff --git a/src/sample/web_version/collect.py b/src/sample/web_version/collect.py new file mode 100644 index 0000000..f8223f9 --- /dev/null +++ b/src/sample/web_version/collect.py @@ -0,0 +1,96 @@ +import time +import board +import adafruit_mpu6050 +import csv +import os +from datetime import datetime +import firebase_admin +from firebase_admin import credentials, firestore + +# --- 設定 --- +# Firebaseの秘密鍵ファイル +CRED_PATH = 'your-firebase-credentials.json' +# Firebaseのコレクション名 +COLLECTION_NAME = 'angle_data' +# ローカル保存するCSVファイル名 +CSV_FILE = 'angle_log.csv' +# データ取得間隔(秒) +INTERVAL = 5 + +# --- Firebaseの初期化 --- +try: + cred = credentials.Certificate(CRED_PATH) + firebase_admin.initialize_app(cred) + db = firestore.client() + print(" Firebaseの初期化に成功しました。") +except Exception as e: + print(f" Firebaseの初期化に失敗: {e}") + exit() + +# --- センサーの初期化 --- +try: + i2c = board.I2C() # uses board.SCL and board.SDA + mpu = adafruit_mpu6050.MPU6050(i2c) + print(" MPU6050センサーの初期化に成功しました。") +except Exception as e: + print(f" センサーの初期化に失敗: {e}") + # ダミーモードで動作させるためのフラグ + is_dummy_mode = True + print(" センサーが見つからないため、ダミーデータで動作します。") +else: + is_dummy_mode = False + +# --- CSVファイルのヘッダー書き込み --- +if not os.path.exists(CSV_FILE): + with open(CSV_FILE, 'w', newline='') as f: + writer = csv.writer(f) + writer.writerow(['timestamp', 'x_angle', 'y_angle']) + +# --- メインループ --- +def main(): + print(" データ収集を開始します...") + while True: + try: + # タイムスタンプ取得 + now = datetime.now() + timestamp_str = now.strftime('%Y-%m-%d %H:%M:%S') + + # 2. センサーから角度データを取得 + if is_dummy_mode: + import random + # 集中力が途切れる可能性のある値をランダムに生成 + x = random.uniform(-50, -10) + y = random.uniform(-70, -30) + else: + # 実際のセンサーから値を取得 (MPU6050ライブラリは加速度を返すので、 + # 実際にはカルマンフィルタ等で角度に変換する処理が必要。ここでは簡略化) + accel = mpu.acceleration + # ここでは加速度のX, Yを角度に見立てて代入します + x = accel[0] + y = accel[1] + + x_angle = round(x, 2) + y_angle = round(y, 2) + + print(f"[{timestamp_str}] X: {x_angle}, Y: {y_angle}") + + # 3. Raspberry Pi上にローカル保存 (CSV) + with open(CSV_FILE, 'a', newline='') as f: + writer = csv.writer(f) + writer.writerow([timestamp_str, x_angle, y_angle]) + + # 3. クラウドに保存 (Firestore) + data = { + 'x_angle': x_angle, + 'y_angle': y_angle, + 'timestamp': firestore.SERVER_TIMESTAMP # クラウド側の正確な時刻を記録 + } + db.collection(COLLECTION_NAME).add(data) + + except Exception as e: + print(f"エラーが発生しました: {e}") + + time.sleep(INTERVAL) + +if __name__ == '__main__': + main() diff --git a/src/sample/web_version/focus.service b/src/sample/web_version/focus.service new file mode 100644 index 0000000..bf43360 --- /dev/null +++ b/src/sample/web_version/focus.service @@ -0,0 +1,12 @@ +[Unit] +Description=Focus Monitor Service +After=network.target + +[Service] +ExecStart=/usr/bin/python3 /home/pi/focus/main.py +Restart=always +User=pi + +[Install] +WantedBy=multi-user.target + diff --git a/src/sample/web_version/index.html b/src/sample/web_version/index.html new file mode 100644 index 0000000..8fd6950 --- /dev/null +++ b/src/sample/web_version/index.html @@ -0,0 +1,184 @@ + + + + + + + ITスキル実習サンプル + + + + + + + +

スマートフォンのセンサーデータを取得

+

各種センサーの値を表示

+ +

センサー使用の許可

+

+ iOSの場合、ユーザの許可が必要なため、下記ボタンをタップ +

+ +

データの登録

+

+ ボタンをタップしたときの加速度データをThingSpeakに登録 +

+ + + diff --git a/src/sample/web_version/js/detect_os.js b/src/sample/web_version/js/detect_os.js new file mode 100644 index 0000000..01dcb4a --- /dev/null +++ b/src/sample/web_version/js/detect_os.js @@ -0,0 +1,42 @@ +function detectOSSimply() { + let ret; + if ( + navigator.userAgent.indexOf("iPhone") > 0 || + navigator.userAgent.indexOf("iPad") > 0 || + navigator.userAgent.indexOf("iPod") > 0 + ) { + // iPad OS13のsafariはデフォルト「Macintosh」なので別途要対応 + ret = "iphone"; + } else if (navigator.userAgent.indexOf("Android") > 0) { + ret = "android"; + } else { + ret = "pc"; + } + + return ret; +} + +// iPhone + Safariの場合はDeviceMotion APIの使用許可をユーザに求める +function permitDeviceMotionForSafari() { + DeviceMotionEvent.requestPermission() + .then(response => { + if (response === "granted") { + window.addEventListener( + "devicemotion", + motionHandler + ); + } + }).catch(console.error); +} +// iPhone + Safariの場合はDeviceOrientation APIの使用許可をユーザに求める +function permitDeviceOrientationForSafari() { + DeviceOrientationEvent.requestPermission() + .then(response => { + if (response === "granted") { + window.addEventListener( + "deviceorientation", + orientationHandler + ); + } + }).catch(console.error); +} \ No newline at end of file diff --git a/src/sample/web_version/main.py b/src/sample/web_version/main.py new file mode 100644 index 0000000..9a71811 --- /dev/null +++ b/src/sample/web_version/main.py @@ -0,0 +1,70 @@ +import functions_framework +from google.cloud import firestore +import requests + +# --- 設定 --- +LINE_NOTIFY_TOKEN = '自分のLINEのトークンを入れる' +LINE_NOTIFY_API_URL = 'https://notify-api.line.me/api/notify' +COLLECTION_NAME = 'angle_data' + +# --- 判定 --- +X_MIN, X_MAX = -40, -20 +Y_MIN, Y_MAX = -60, -40 +CONSECUTIVE_COUNT_THRESHOLD = 10 # 連続とみなす回数 + +db = firestore.Client() + +def send_line_notification(message): + """LINE Notifyへ通知を送信する関数""" + try: + headers = {'Authorization': f'Bearer {LINE_NOTIFY_TOKEN}'} + data = {'message': message} + response = requests.post(LINE_NOTIFY_API_URL, headers=headers, data=data) + return response.status_code == 200 + except Exception as e: + print(f"LINE通知エラー: {e}") + return False + +def is_concentration_lapsed(data): + """集中力が途切れているかを判定する関数""" + x = data.get('x_angle', 0) + y = data.get('y_angle', 0) + return Y_MIN <= y <= Y_MAX and X_MIN <= x <= X_MAX + +@functions_framework.cloud_event +def concentration_check(cloud_event): + """Firestoreへの書き込みをトリガーに実行されるメイン関数""" + + # 5. 判定 + docs = db.collection(COLLECTION_NAME).order_by( + 'timestamp', direction=firestore.Query.DESCENDING + ).limit(CONSECUTIVE_COUNT_THRESHOLD).stream() + + recent_data = [doc.to_dict() for doc in docs] + + if len(recent_data) < CONSECUTIVE_COUNT_THRESHOLD: + print(f"データが{CONSECUTIVE_COUNT_THRESHOLD}件未満のため、連続判定をスキップします。") + return + + consecutive_lapses = 0 + for data in recent_data: + if is_concentration_lapsed(data): + consecutive_lapses += 1 + else: + break + + # 6. 通知 + if consecutive_lapses == CONSECUTIVE_COUNT_THRESHOLD: + message = "【警告】集中力が途切れている可能性が非常に高いです。休憩を取りましょう。" + print(f"通知実行: {message}") + send_line_notification(message) + + elif is_concentration_lapsed(recent_data[0]) and not is_concentration_lapsed(recent_data[1]): + message = "【注意】集中力が途切れている可能性があります。" + print(f"通知実行: {message}") + send_line_notification(message) + + else: + print("通知条件を満たしませんでした。") + + return 'OK' diff --git a/src/sample/web_version/save.csv b/src/sample/web_version/save.csv new file mode 100644 index 0000000..0024fe7 --- /dev/null +++ b/src/sample/web_version/save.csv @@ -0,0 +1,25 @@ +import time +import random + +# 保存先ファイル(SDカード上の任意のパス) +filename = "/home/pi/data_log.csv" + +def get_sensor_data(): + # センサーからデータを取得する処理をここに書く + # 今回はダミーとしてランダム値を返す + return random.uniform(20.0, 30.0) + +def main(): + with open(filename, "a") as f: # 追記モードで開く + while True: + data = get_sensor_data() + timestamp = time.strftime("%Y-%m-%d %H:%M:%S") + line = f"{timestamp},{data}\n" + f.write(line) + f.flush() # バッファを即時書き込み(電源断対策) + print("Saved:", line.strip()) + time.sleep(1) # 1秒ごとに取得 + +if __name__ == "__main__": + main() +