学校向けスマート体温監視システム: 正確、リアルタイム、安全
部品と材料
| 部品 | 数量 |
|---|---|
| Pycom SiPy(Sigfox対応マイコン) | 1 |
| Raspberry Pi 3 Model B | 1 |
| Arduino Nano R3 | 1 |
| ロジックレベル変換モジュール(双方向) | 1 |
| NFC RFID-RC522モジュール | 1 |
| Sharp GP2Y0A41SK0F 距離センサー(4-30cm) | 1 |
| MLX90614-DCI 赤外線温度センサー(I2C) | 1 |
| DFRobot I2C 16x2 LCDディスプレイ | 1 |
| LED(汎用) | 1 |
| 抵抗 330Ω | 2 |
| ブザー | 1 |
使用ソフトウェア・サービス
- Arduino IDE
- Visual Studio Code + Pymakr拡張(SiPy開発用)
- ThingSpeak API(データ可視化・アラート)
- IFTTT(メール通知自動化)
- PuTTY / IDLE(Raspberry Pi操作用)
課題と解決策
課題
アジア地域では暑い気候とSARS経験から、学校で定期的な体温測定が義務付けられています。しかし、生徒が各自で体温計を持参する運用では、紛失・故障・忘れで測定できないケースが多く、衛生面から共有も不可。教職員が40名分のデータを手入力する負担も大きいです。
解決策: 自動化された3層アーキテクチャ


テーマ: Social Impact(SqwidNet)、SDGs「すべての人に健康と福祉を」
動作仕組み
1. Raspberry Pi + Arduino Nano(エッジ層)
- Raspberry Pi: Pythonプログラム + MySQLデータベース。起動時にDB/テーブル自動作成。
- Arduino Nano: RFIDカード読み取り → ユーザー認証 → 距離センサーで測定姿勢確認 → 赤外線温度計で測定 → LCD表示 → Raspberry Piへシリアル送信
- ハンドシェイク同期: 双方が「READY」/「RECEIVED」シグナルを交換してからデータ送受信。タイミングズレを防止。

2. Sigfox (SiPy)(通信層)
- Arduinoからシリアルで受信した「ユーザーID, 温度整数部, 温度小数部」をバイト列としてSigfoxバックエンドへ送信
- 低消費電力・広域通信で校内どこでもデータ送信可能
- UbidotsとThingSpeakの両方に対応できるようバイト形式で送信

3. クラウド(ThingSpeak + IFTTT)
- プライベートチャネル: 生データ(整数部・小数部分離)を受信
- MATLAB Analysis: データ挿入時にトリガー発火(React)、整数部+小数部を結合して実温度に変換、パブリックチャネルへ書き込み
- パブリックチャネル: 見やすい温度データ+ユーザーID、自動更新テーブルで管理者が監視
- IFTTT Webhook: MATLAB解析で発熱(≥38°C)検知時にメール通知


詳細セットアップガイド
1. Arduino Nano 配線


| モジュール | 接続ピン |
|---|---|
| RFID-RC522 | SDA:D10, SCK:D13, MOSI:D11, MISO:D12, RST:D9, 3.3V, GND |
| I2C LCD | SDA:A4, SCL:A5, 5V, GND |
| 距離センサー | VCC:5V, GND, OUT:A0 |
| ブザー | GND, OUT:D6 |
| 温度センサー | SDA:A4, SCL:A5, 5V, GND |
| LED | カソード:GND, アノード:D7 |
2. Raspberry Pi セットアップ
- Raspbian OSをbalenaEtcherでSDカードに書き込み
- HDMI接続で初期設定、
raspi-configでSSH/VNC有効化 - 固定IP設定(任意):
/etc/dhcpcd.confにinterface eth0 static ip_address=192.168.0.11/24追加
3. MySQLデータベース構築
sudo apt update && sudo apt upgrade
sudo apt install apache2
sudo apt install php php-mbstring
sudo apt install mariadb-server php-mysql
# MySQL rootユーザー作成(重要: 手順厳守)
sudo mysql --user=root
DROP USER 'root'@'localhost';
CREATE USER 'root'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;
exit;
# 動作確認
mysql --user=root --password=yourpassword
# phpMyAdminインストール
sudo apt install phpmyadmin
sudo phpenmod mysqli
sudo /etc/init.d/apache2 restart
以降 https:// でDB管理可能。
4. SiPy Expansion Board 2.0 セットアップ
- SiPyモジュールをリセットボタンがUSBコネクタ側になるよう装着
- ファームウェアアップデート(Pycom公式ツール使用、RCZ地域選択に注意)
- Visual Studio Code + Pymakr拡張で開発環境構築
- ファイル名は
main.pyでアップロード - 無限ループ書き込み時はFileZillaでFTP接続し
main.py削除で復旧
5. Sigfoxバックエンド登録
from network import Sigfox
import binascii
sigfox = Sigfox(mode=Sigfox.SIGFOX, rcz=Sigfox.RCZ4) // アジアはRCZ4
print(binascii.hexlify(sigfox.id())) // Device ID
print(binascii.hexlify(sigfox.pac())) // PAC番号
取得したID/PACでSigfoxバックエンドにデバイス登録。コールバックでThingSpeak連携設定。
6. クラウド側設定(ThingSpeak + IFTTT)
- ThingSpeakで2チャネル作成: プライベート(生データ受信)、パブリック(整形後データ表示)
- MATLAB Analysisでデータ結合・パブリックチャネル書き込み・発熱判定
- Reactで「データ挿入時」にMATLAB Analysisをトリガー
- IFTTTでWebhooksトリガー(イベント名:
fever)→ メール送信アクション作成 - MATLAB内で
webwriteでIFTTT URL叩いてメール発火
ソースコード
[C] Arduino Nano
/*** 90% Aden自作 (https://www.astero.me) / 10% オープンソースライブラリ ***/
#include <SPI.h> #include <MFRC522.h> #include <LiquidCrystal_I2C.h> #include <Wire.h> #include <SoftwareSerial.h> #include <Adafruit_MLX90614.h>
#define RST_PIN 9 #define SS_PIN 10
#define BUZZER_PIN 7 #define LED_PIN 6 #define DIST_SENSE_PIN A0
MFRC522 mfrc522(SS_PIN, RST_PIN);
LiquidCrystal_I2C lcd(0x27,16,2);
Adafruit_MLX90614 mlx = Adafruit_MLX90614();
SoftwareSerial unoSerial(2, 3); // SiPy通信用
boolean tapped = true; boolean confirmation = false;
String GetUID() { /* UIDを16進文字列に変換 */ }
void setup() {
Serial.begin(9600);
while(!confirmation) { Serial.println("ARDUINO"); confirmation = Serial.readString() == "RAPI"; delay(1000); }
SPI.begin(); mfrc522.PCD_Init(); mlx.begin(); unoSerial.begin(9600);
pinMode(BUZZER_PIN, OUTPUT); pinMode(LED_PIN, OUTPUT);
lcd.init(); lcd.backlight();
}
void loop() {
boolean readySignal = Serial.readString() == "READY";
if(readySignal) { tapped = true; Serial.println("RECEIVED"); }
if(tapped) {
digitalWrite(BUZZER_PIN, 0); digitalWrite(LED_PIN, 0); lcd.clear();
lcd.setCursor(0,0); lcd.print(" Please tap"); lcd.setCursor(0,1); lcd.print(" your card.");
delay(500); tapped = false;
} else {
if (!mfrc522.PICC_IsNewCardPresent()) return;
if (!mfrc522.PICC_ReadCardSerial()) return;
String card_UID = GetUID(); card_UID.toUpperCase();
Serial.print(card_UID); Serial.print("\n");
String rawData = Serial.readString();
int commaPos = rawData.indexOf(',');
String userInitial = rawData.substring(0, commaPos);
int userID = rawData.substring(commaPos + 1).toInt();
lcd.clear();
if(userID != 0 && userInitial != "0") {
lcd.setCursor(0,0); lcd.print("Welcome, "); lcd.setCursor(0,1); lcd.print(userInitial);
delay(2000);
int dist = analogRead(DIST_SENSE_PIN);
byte temp_H = 0, temp_L = 0; float meas_temp = 0.0;
while((dist < 170) || (dist > 200)) { // 測定距離待ち
dist = analogRead(DIST_SENSE_PIN);
lcd.setCursor(0,0); lcd.print("Please take"); lcd.setCursor(0,1); lcd.print("your temperature.");
}
digitalWrite(BUZZER_PIN, 1); digitalWrite(LED_PIN, 1); lcd.clear();
meas_temp = (float)mlx.readObjectTempC();
temp_H = floor(meas_temp); temp_L = (meas_temp - temp_H) * 100;
String sMeasTemp = String(userID) + "," + String(temp_H) + "," + String(temp_L);
Serial.println("TEMP"); Serial.println(meas_temp);
if(meas_temp >= 28 && meas_temp <= 42) {
unoSerial.print(sMeasTemp); // SiPyへ送信
lcd.setCursor(0,0); lcd.print("Temperature: "); lcd.setCursor(0,1); lcd.print(" " + String(meas_temp));
}
} else {
lcd.setCursor(0,0); lcd.print("Error!"); lcd.setCursor(0,1); lcd.print("Invalid User.");
}
// Raspberry Piからの温度妥当性判定結果を受信
rawData = Serial.readString(); commaPos = rawData.indexOf(',');
userInitial = rawData.substring(0, commaPos);
if(userInitial == "0") { lcd.clear(); lcd.setCursor(0,0); lcd.print("Error!"); lcd.setCursor(0,1); lcd.print("Invalid Temperature."); }
Serial.println("STANDBY"); delay(1000);
mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1();
}
}
[Python] Raspberry Pi
''' Coded by Aden for Hackster.io Sigfox Competition. https://www.astero.me '''
import mysql.connector, serial, os, time
host="localhost"; user="root"; password="password"; databaseName="smartTemp"
conn = mysql.connector.connect(host=host, user=user, passwd=password)
comPort = 'ttyUSB0'; baudRate = 9600
ser = serial.Serial('/dev/' + comPort, baudRate, timeout=0)
# COMポート自動検出・確認
for x in range(2):
time.sleep(2); ser.write("RAPI")
if ser.readline().strip() == "ARDUINO": break
comPort = 'ttyUSB1'
ser = serial.Serial('/dev/' + comPort, baudRate) # timeoutなしで再オープン
countFile = open("count.txt", "a++")
cursor = conn.cursor(buffered=True)
cursor.execute("USE `" + databaseName + "`")
# DB/テーブル自動作成
cursor.execute("CREATE DATABASE IF NOT EXISTS " + databaseName)
cursor.execute("CREATE TABLE IF NOT EXISTS `userInfo` (id int, cardID VARCHAR(9), userID VARCHAR(4), userInitial VARCHAR(8))")
cursor.execute("CREATE TABLE IF NOT EXISTS `tempData` (id int, userID VARCHAR(4), dateMeas date, timeMeas time, tempMeasure decimal(5,2))")
count = 1 if os.stat("count.txt").st_size == 0 else int(open("count.txt").read())
while True:
cursor = conn.cursor(buffered=True)
cursor.execute("USE `" + databaseName + "`")
cardID = str(ser.readline().strip())
cursor.execute("SELECT * FROM userInfo WHERE cardID = '" + cardID + "'")
items = cursor.fetchone()
if items:
userID = str(items[2]); userDetails = str(items[3] + ", " + items[2])
currentDate = time.strftime("%Y-%m-%d", time.gmtime())
currentTime = time.strftime("%H:%M:%S", time.gmtime())
ser.write(userDetails.encode())
countFile.close(); countFile = open("count.txt", 'w')
tempSignal = False
while not tempSignal:
tempSignal = ser.readline().strip() == "TEMP"
tempData = float(ser.readline().strip())
if 28 <= tempData <= 42:
cursor.execute("INSERT INTO `tempData` VALUES (" + str(count) + ",'" + userID + "','" + currentDate + "','" + currentTime + "','" + str(tempData) + "')")
conn.commit(); count += 1; countFile.write(str(count))
else:
ser.write(b"0, 0") # 無効温度通知
else:
ser.write(b", 0") # 無効ユーザー通知
# ハンドシェイクで次のサイクル待ち
readySignal = ser.readline().strip() == "STANDBY"
while readySignal:
ser.write(b"READY")
if ser.readline().strip() == "RECEIVED": readySignal = False
cursor.close()
[MicroPython] SiPy
''' 100% coded by Reginald '''
from machine import UART
from network import Sigfox
import binascii, socket, time, pycom, sys
pycom.heartbeat(False)
sigfox = Sigfox(mode=Sigfox.SIGFOX, rcz=Sigfox.RCZ4) # アジア
uart1 = UART(1, baudrate=9600)
uart1.init(9600, bits=8, parity=None, stop=1, pins=('P3','P4'), timeout_chars=20)
while True:
try:
recv = uart1.readline()
if recv:
print(binascii.hexlify(sigfox.id()))
print(binascii.hexlify(sigfox.pac()))
sk1 = socket.socket(socket.AF_SIGFOX, socket.SOCK_RAW)
sk1.setblocking(False)
sk1.setsockopt(socket.SOL_SIGFOX, socket.SO_RX, False)
dataList = recv.decode("utf-8")
split_val = dataList.split(",")
userID = int(split_val[0])
temp_H = int(split_val[1])
temp_L = int(split_val[2])
bData = [userID, temp_H, temp_L]
sk1.send(bytes(bData))
sk1.close()
time.sleep(5)
except KeyboardInterrupt:
sys.exit()
[ThingSpeak] MATLABスクリプト
%% Made by Aden for Hackster.io Sigfox Competition. https://www.astero.me
readChannelID = 870479;
webhookTrigger = 'https://maker.ifttt.com/trigger/student_fever/with/key/...';
readAPIKey = 'QGHINBPNJQKULBH2';
writeChannelID = 870482;
writeAPIKey = 'R6NJIM8NT5A42R9N';
wholeNumber = thingSpeakRead(readChannelID, 'ReadKey', readAPIKey, 'Fields', 1);
decimalNumber = thingSpeakRead(readChannelID, 'ReadKey', readAPIKey, 'Fields', 2)/100;
userID = thingSpeakRead(readChannelID, 'ReadKey', readAPIKey, 'Fields', 3);
analyzedData = wholeNumber + decimalNumber;
thingSpeakWrite(writeChannelID, 'Fields', [1,2], 'Values', {analyzedData, userID}, 'WriteKey', writeAPIKey);
if analyzedData >= 38
webwrite(webhookTrigger, 'value1', analyzedData, 'value2', userID);
end
回路図・接続図







貢献者
- Aden: Raspberry Pi + Arduino + モジュール + ThingSpeak (MATLAB/設定)
- Reginald: Sigfox + ThingSpeakデータ収集 + 回路図
- Bo Sheng: IFTTT設定
製造プロセス
- Raspberry PiとDS18B20によるプロフェッショナルな温度監視
- Raspberry Piでサーバークローゼットの温度を正確に監視
- DIY自家醸造温度制御:Arduinoで監視・調節
- K30 CO2センサで室内空気質をリアルタイム監視する
- スマートブラインド:Arduinoとソーラー電源で実現する自動光制御
- 非接触温度スクリーニングゲート:Arduino MKR1000で迅速・高精度な検温
- Arduino UNO + WiFiシールドで温度をWebリアルタイム表示 — シリアル出力だけでグラフ化
- Arduino用8ビットI/Oポートライブラリ:デジタル制御を拡張
- スマート手話グローブ:ジェスチャーをテキスト/音声に変換
- ヘルスバンド:高齢者を見守る信頼のスマートウェアラブルアシスタント
- スマート農業:未来の農業のためのIoT対応包括的監視