# 嵌入式系統
# 設定
要記得調upload speed



# 使用


pin2是板子自己本身的led
# 範例
## 溫濕度與rfid與光敏
光敏電阻要接pin腳26
```cpp
#include "DHT.h"
#define DHTPIN 27
#define DHTTYPE DHT11
#include <SPI.h>
#include <MFRC522.h>
// **這一行是關鍵,宣告 dht1 變數**
DHT dht1(DHTPIN, DHTTYPE);
String read_id;
MFRC522 rfid(/*SS_PIN*/ 5, /*RST_PIN*/ 22);
void setup()
{
Serial.begin(115200);
dht1.begin();
SPI.begin();
rfid.PCD_Init();
pinMode(26, INPUT);//光敏
}
String mfrc522_readID()
{
String ret;
if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial())
//讀取並確認不是重複卡片
{
MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);
for (byte i = 0; i < rfid.uid.size; i++) {
ret += (rfid.uid.uidByte[i] < 0x10 ? "0" : "");
ret += String(rfid.uid.uidByte[i], HEX);
}//轉成16進制儲存
}
rfid.PICC_HaltA();
// Stop encryption on PCD
rfid.PCD_StopCrypto1();
return ret;//回傳已完成轉存之id
}
void loop()
{
read_id = mfrc522_readID(); //呼叫函式取得16進制id
if (read_id != "") {
Serial.print("相對溼度,");
Serial.print(dht1.readHumidity());
Serial.print(",攝氏溫度,");
Serial.print(dht1.readTemperature());
Serial.print(",華氏溫度,");
Serial.print(dht1.readTemperature(true));
Serial.print(",RFID,");
Serial.print(read_id);
Serial.print(",光敏電阻,");
Serial.println(analogRead(26));//光敏
}
delay(1000);
}
```
## led矩陣

如果你只有 1 個 LED 矩陣,可以改成:
```cpp=
#define NBR_MTX 1
```
```cpp=
#include <LedControlMS.h> // 引入 LedControlMS 函式庫,用於控制 MAX7219 LED 矩陣
#define NBR_MTX 2 // 定義使用的 LED 矩陣數量(這裡是 2 個)
// 初始化 LedControl 物件
// 參數 (DIN, CLK, CS, NBR_MTX)
// DIN -> GPIO27(數據輸入)
// CLK -> GPIO26(時鐘信號)
// CS -> GPIO25(片選信號)
// NBR_MTX -> 2,表示有 2 個 LED 矩陣模組
LedControl lc = LedControl(27, 26, 25, NBR_MTX);
int digitCounter = 0; // 計數變數(目前沒用到)
unsigned long delaytime = 300; // 延遲時間(目前沒用到)
void setup() {
Serial.begin(115200); // 設定序列埠通訊速率為 115200,方便 debug
Serial.println("Setup"); // 印出 "Setup",表示程式開始運行
digitCounter = 0; // 設定計數變數為 0
// 迴圈設定每個 LED 矩陣
for (int i = 0; i < NBR_MTX; i++) {
lc.shutdown(i, false); // 取消 MAX7219 省電模式,讓 LED 矩陣開啟
lc.setIntensity(i, 5); // 設定 LED 亮度(範圍 0~15,這裡設定為 5)
lc.clearDisplay(i); // 清除 LED 矩陣的畫面
}
}
void loop() {
// LED 矩陣 (0) 的四個角落 LED 依序開啟
Serial.println("LED0: 0 0");
lc.setLed(0, 0, 0, true); // 開啟 第 1 個 LED 矩陣 的 (0,0)
delay(1000);
Serial.println("LED0: 0 7");
lc.setLed(0, 0, 7, true); // 開啟 第 1 個 LED 矩陣 的 (0,7)
delay(1000);
Serial.println("LED0: 7 0");
lc.setLed(0, 7, 0, true); // 開啟 第 1 個 LED 矩陣 的 (7,0)
delay(1000);
Serial.println("LED0: 7 7");
lc.setLed(0, 7, 7, true); // 開啟 第 1 個 LED 矩陣 的 (7,7)
delay(1000);
// LED 矩陣 (0) 的四個角落 LED 依序關閉
Serial.println("LED0: 0 0 off");
lc.setLed(0, 0, 0, false); // 關閉 第 1 個 LED 矩陣 的 (0,0)
delay(1000);
Serial.println("LED0: 0 7 off");
lc.setLed(0, 0, 7, false); // 關閉 第 1 個 LED 矩陣 的 (0,7)
delay(1000);
Serial.println("LED0: 7 0 off");
lc.setLed(0, 7, 0, false); // 關閉 第 1 個 LED 矩陣 的 (7,0)
delay(1000);
Serial.println("LED0: 7 7 off");
lc.setLed(0, 7, 7, false); // 關閉 第 1 個 LED 矩陣 的 (7,7)
delay(1000);
}
```
# 資料庫
下載xampp管理資料庫

**windows**




**mac**
接著開啟他的程式

開啟MySQL跟Apache

到網頁輸入localhost

看到這個代表成功

輸入這個進入圖形管理頁面
```
http://localhost/phpmyadmin/
```

新增使用者


主機名稱記得要改 並且要勾權限那些東西

然後到python
```python=
pip install pymysql
```
**測試是否連上**
```python=
import pymysql
# 資料庫連接的參數設定
db = pymysql.connect(host="localhost", user="testt",
password="test", db="testt")
# 連接上資料庫後,設定游標
cursor = db.cursor()
# 輸入 SQL 語法指令
cursor.execute("SELECT VERSION()")
# 如該指令有回傳值能用此接收回傳
data = cursor.fetchone()
# 顯示回傳的資訊
print("Database version : %s " % data)
# 關閉連接
db.close()
```
**建立新的表格**
```python=
import pymysql
# 資料庫連接的參數設定
db = pymysql.connect(host="localhost", user="testt",
password="test", db="testt")
# 連接上資料庫後,設定游標
cursor = db.cursor()
# 輸入 SQL 語法指令:如果有名叫 ID 的資料表就刪除它
cursor.execute("DROP TABLE IF EXISTS ID")
# 用變數的方式儲存指令,此為建立一個表格,有兩個欄位
sql = """CREATE TABLE ID(
NAME CHAR(20) NOT NULL,
ID CHAR(50))"""
# 將變數儲存的指令送出
cursor.execute(sql)
# 用變數的方式儲存指令,建立第二個表格
sql2 = """CREATE TABLE TEST(
fname CHAR(50) NOT NULL,
lname CHAR(50),
age INT(50),
sex CHAR(50),
income INT(50))"""
cursor.execute(sql2)
print("創建完成")
# 關閉連接
db.close()
```
預覽結果

**資料寫入**
```python=
import pymysql
# 資料庫連接的參數設定
db = pymysql.connect(host="localhost", user="testt",
password="test", db="testt")
# 連接上資料庫後,設定游標
cursor = db.cursor()
# 用變數的方式儲存指令,對 TEST 的特定欄位輸入資料
sql01 = """INSERT INTO TEST (fname, lname, age, sex, income)
VALUES('Ming', 'Fong', 29, 'man', 999999)"""
try:
cursor.execute(sql01)
db.commit()
print("DONE")
except:
db.rollback()
print("ERROR")
# 關閉連接
db.close()
```
## arduino作業
arduino作業
```cpp
#include "DHT.h"
#define DHTPIN 27
#define DHTTYPE DHT11
#include <SPI.h>
#include <MFRC522.h>
// **這一行是關鍵,宣告 dht1 變數**
DHT dht1(DHTPIN, DHTTYPE);
String read_id;
MFRC522 rfid(/*SS_PIN*/ 5, /*RST_PIN*/ 22);
void setup()
{
Serial.begin(115200);
dht1.begin();
SPI.begin();
rfid.PCD_Init();
}
String mfrc522_readID()
{
String ret;
if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial())
//讀取並確認不是重複卡片
{
MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);
for (byte i = 0; i < rfid.uid.size; i++) {
ret += (rfid.uid.uidByte[i] < 0x10 ? "0" : "");
ret += String(rfid.uid.uidByte[i], HEX);
}//轉成16進制儲存
}
rfid.PICC_HaltA();
// Stop encryption on PCD
rfid.PCD_StopCrypto1();
return ret;//回傳已完成轉存之id
}
void loop()
{
read_id = mfrc522_readID(); //呼叫函式取得16進制id
if (read_id != "") {
Serial.print("相對溼度,");
Serial.print(dht1.readHumidity());
Serial.print(",攝氏溫度,");
Serial.print(dht1.readTemperature());
Serial.print(",華氏溫度,");
Serial.print(dht1.readTemperature(true));
Serial.print(",RFID,");
Serial.println(read_id);
}
delay(1000);
}
```
建立
```python
import pymysql
# 資料庫連接的參數設定
db = pymysql.connect(host="localhost", user="testt", password="test", database="testt")
cursor1 = db.cursor()
# 建立 dht 資料表(如果不存在)
create_table_query = """
CREATE TABLE IF NOT EXISTS dht (
id INT AUTO_INCREMENT PRIMARY KEY,
Celsius VARCHAR(10),
Fahrenheit VARCHAR(10),
Humidity VARCHAR(10),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
"""
cursor1.execute(create_table_query)
print("資料表 dht 檢查完成,若不存在則已建立。")
# 插入資料的範例
Celsius_data = "29.00"
Fahrenheit_data = "84.20"
Humidity_data = "60.50"
# 使用參數化 SQL 插入資料
insert_query = "INSERT INTO dht (Celsius, Fahrenheit, Humidity) VALUES (%s, %s, %s)"
cursor1.execute(insert_query, (Celsius_data, Fahrenheit_data, Humidity_data))
db.commit()
insert_query = "ALTER TABLE dht MODIFY COLUMN Celsius VARCHAR(20);"
cursor1.execute(insert_query)
print("資料已成功插入!")
cursor1.execute("ALTER TABLE dht ADD COLUMN Rfid VARCHAR(20);")
db.commit()
# 關閉資料庫連接
cursor1.close()
db.close()
```
讀取板子資料並存到資料庫
```python=
import sys
import pymysql
import serial
import time
import re
COM_PORT = '/dev/cu.usbserial-140'
BAUD_RATES = 115200
ser = serial.Serial(COM_PORT, BAUD_RATES)
Celsius_data = ''
Fahrenheit_data = ''
Humidity_data = ''
# host='127.0.0.1'
# host = '192.168.43.246'
try:
db = pymysql.connect(host="localhost", user="testt", password="test", database="testt")
cursor1 = db.cursor()
while True:
while ser.in_waiting:
try:
msg = ser.readline().decode()
if (msg==""):
continue
else:
print("msg:{}".format(msg))
msg_data=msg.split(',')
# if '相對溼度' in str(msg_data[2]) and '華氏溫度' in str(msg_data[4]):
if ("攝氏溫度" in str(msg_data[2])) and ("華氏溫度" in str(msg_data[4])) \
and ("相對溼度" in str(msg_data[0]) and ("RFID" in str(msg_data[6]))):
Celsius_data = msg_data[1]
Fahrenheit_data = msg_data[3]
Humidity_data = msg_data[5]
Rfid = msg_data[7].replace("\r\n", "")
cursor1.execute("INSERT INTO dht (Celsius, Fahrenheit, Humidity, Rfid) \
VALUES ('%s', '%s', '%s', '%s')" % (Celsius_data, Fahrenheit_data, Humidity_data, Rfid))
db.commit()
else:
continue
print("完成上傳")
time.sleep(1)
except OSError as er:
# 如果發生錯誤,回報錯誤訊息
db.rollback()
db.close()
print(er)
except KeyboardInterrupt:
ser.close()
db.close()
print('closed!')
```
# php
到這個路徑
```
/Applications/XAMPP/htdocs/
```

可以添加php檔案或直接編輯index.php
upload.php
```php
<?php
$host = "localhost";
$db = "nodeRedTest";
$user = "testt"; // 根據你的設定
$pass = "test"; // 密碼
// 連接資料庫
$conn = new mysqli($host, $user, $pass, $db);
if ($conn->connect_error) {
die("資料庫連線失敗: " . $conn->connect_error);
}
// 查詢正確欄位名稱
$sql = "SELECT id, Celsius, Humidity, created_at FROM dht ORDER BY created_at ASC";
$result = $conn->query($sql);
// 判斷是否為 AJAX/程式存取(回傳 JSON),或一般網頁存取(顯示表格)
$is_json = isset($_GET['json']);
if ($is_json) {
$data = [];
while ($row = $result->fetch_assoc()) {
$data[] = $row;
}
header('Content-Type: application/json');
echo json_encode($data);
} else {
// ✅ 加上 HTML 標頭與自動刷新
echo "<!DOCTYPE html>";
echo "<html><head><meta charset='UTF-8'>";
echo "<meta http-equiv='refresh' content='5'>"; // ← 每 5 秒刷新
echo "<title>溫溼度資料</title></head><body>";
echo "<h2>溫溼度資料列表(每 5 秒自動更新)</h2>";
echo "<table border='1' cellpadding='5'><tr><th>ID</th><th>溫度 (°C)</th><th>濕度 (%)</th><th>時間</th></tr>";
while ($row = $result->fetch_assoc()) {
echo "<tr>
<td>{$row['id']}</td>
<td>{$row['Celsius']}</td>
<td>{$row['Humidity']}</td>
<td>{$row['created_at']}</td>
</tr>";
}
echo "</table>";
echo "</body></html>"; // ✅ 補上 HTML 結尾
}
$conn->close();
?>
```
到網頁開啟127.0.0.1/upload.php

# python gui
## 範例
老師的範例程式
按三個扭之後 可以回傳指定的資料
**解釋:**
**SerialWrite(b'a') 之中 `b'a'` 是什麼?**
是 Python 的 **bytes 物件**,代表「字元 `a` 的 ASCII 位元組」:
python
```python
import serial
import tkinter
from time import sleep
# 設定串口 要記得看接口有沒有一樣
ser = serial.Serial('/dev/cu.usbserial-1120', 115200, timeout=2)
condition = False
temparature = 0
# 串口發送指令
def SerialWrite(command):
ser.write(command) # 發送指令到 Arduino
rv = ser.readline() # 讀取 Arduino 回應
data = rv.decode('utf-8').strip() # 轉換為字串並去除換行符號
print(data) # 在終端機輸出資料
sleep(1) # 暫停 1 秒
ser.flushInput() # 清除輸入緩衝區
return data # 返回資料
# 發送 'a' 指令(開啟 LED)
def SendCmdA():
global condition
condition = False # 停止持續讀取
SerialWrite(b'a') # 發送 'a' 指令 b'a' 是什麼?這是 Python 的 bytes 物件,代表「字元 a 的 ASCII 位元組」:
LabelA.config(text='已發送指令 "a" 至 Arduino') # 更新介面顯示文字
LabelA.update_idletasks() # 立即更新 UI
# 發送 'b' 指令(關閉 LED)
def SendCmdB():
global condition
condition = False # 停止持續讀取
SerialWrite(b'b') # 發送 'b' 指令
LabelA.config(text='已發送指令 "b" 至 Arduino') # 更新介面顯示文字
LabelA.update_idletasks() # 立即更新 UI
# 發送 'c' 指令(讀取 DHT11 溫濕度感測器數據)
def SendCmdC():
global condition
condition = True # 開啟持續讀取模式
SerialWrite(b'c') # 發送 'c' 指令
while condition:
rv1 = ser.readline() # 讀取 Arduino 回應
data = rv1.decode('utf-8', errors='ignore').strip() # 轉換為字串並忽略錯誤
print(data) # 在終端機輸出資料
# 先用空白分開濕度與溫度
humidity_str, temperature_str = data.split()
# 去掉單位並轉換為 float
tmp_humidity = float(humidity_str.strip('%'))
tmp_temperature = float(temperature_str.strip('C'))
print(temparature)
if tmp_temperature > temparature :
LabelA.config(text="超過溫度") # 更新介面顯示文字
LabelA.update_idletasks() # 立即更新
else:
LabelA.config(text=data) # 更新介面顯示文字
LabelA.update_idletasks() # 立即更新 UI
if rv1 != "" :
break
# 連接 Arduino
def Serial_Connect():
print('正在連接 Arduino..........')
LabelA.config(text='正在連接 Arduino..........') # 更新介面顯示文字
LabelA.update_idletasks()
sleep(1) # 暫停 1 秒
for i in range(10): # 嘗試 10 次連線
rv1 = ser.readline() # 讀取 Arduino 回應
data = rv1.decode('utf-8').strip() # 轉換為字串
print('載入中...')
if data.startswith("Ready"): # 如果 Arduino 回應 "Ready"
print('Arduino 已準備就緒!')
LabelA.config(text='Arduino 已準備就緒!')
buttonStart.config(state='disable') # 停用連接按鈕!防止重複連接
break
sleep(1) # 暫停 1 秒後繼續嘗試
def insertEntry():
global temparature
var=entry.get()
temparature=float(var)
LabelA.config(text=var) # 更新介面顯示文字
LabelA.update_idletasks() # 立即更新 UI
# 退出程式
def Exit():
print('退出程式...')
LabelA.config(text='退出程式...') # 更新介面顯示文字
LabelA.update_idletasks()
sleep(1) # 暫停 1 秒
SerialWrite(b'\x1b') # 發送 ESC 碼(0x1B)來通知 Arduino 停止
ser.close() # 關閉串口連接
Tkwindow.destroy() # 關閉 Tkinter 視窗
# 創建 Tkinter 視窗
Tkwindow = tkinter.Tk()
Tkwindow.title('Python 與 Arduino 通訊') # 設定視窗標題
Tkwindow.minsize(600, 400) # 設定視窗最小大小
# 顯示 Arduino 狀態的標籤
LabelA = tkinter.Label(Tkwindow, bg='white', fg='black', text='請按「連接」開始', width=30, height=10)
LabelA.pack(side=tkinter.TOP)
#輸入框
entrytext=tkinter.StringVar()#打包他的字
entrytext.set('insert tempature')
entry=tkinter.Entry(master = Tkwindow)
entry.pack()
buttoEntry=tkinter.Button(master=Tkwindow,textvariable=entrytext,width=15,height=2,command=insertEntry)
buttoEntry.pack()
# LED 開啟按鈕
buttonA = tkinter.Button(Tkwindow, text='LED ON', width=10, command=SendCmdA)
buttonA.pack(side=tkinter.LEFT)
# LED 關閉按鈕
buttonB = tkinter.Button(Tkwindow, text='LED OFF', width=10, command=SendCmdB)
buttonB.pack(side=tkinter.LEFT)
# 讀取 DHT11 溫濕度數據按鈕
buttonC = tkinter.Button(Tkwindow, text='顯示溫濕度', width=10, command=SendCmdC)
buttonC.pack(side=tkinter.LEFT)
# 連接 Arduino 按鈕
buttonStart = tkinter.Button(Tkwindow, text='連接', width=10, command=Serial_Connect)
buttonStart.pack(side=tkinter.RIGHT)
# 退出按鈕
buttonEnd = tkinter.Button(Tkwindow, text='退出', width=10, command=Exit)
buttonEnd.pack(side=tkinter.RIGHT)
# 開始 Tkinter 事件迴圈
Tkwindow.mainloop()
```
arduino
```cpp
#include "DHT.h"
#define DHTPIN 27
#define DHTTYPE DHT11
#define Led 2
String Str01 = "";
DHT dht1(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(115200); // 初始化 Serial 串口通訊
dht1.begin(); // 初始化 DHT 感測器
Serial.println("Ready"); // 回傳訊號給 Python 程式知道「準備完成」
pinMode(Led, OUTPUT); // 設定 LED 腳位為輸出
digitalWrite(Led, LOW); // 預設 LED 關閉
}
void loop() {
if (Serial.available()) {
Str01 = "";
delay(10);
while (Serial.available()) {
char c = Serial.read();
if (c == '\n') break; // 遇到換行符號就停止
Str01 += c;
}
Serial.println(Str01); // 印出收到的指令
}
// a 指令 → 開 LED 0.5 秒、關 0.5 秒
if (Str01 == "a") {
digitalWrite(Led, HIGH);
delay(500);
digitalWrite(Led, LOW);
delay(500);
}
// b 指令 → 關 LED
if (Str01 == "b") {
digitalWrite(Led, LOW);
}
// c 指令 → 讀取 DHT11 濕度與溫度,並透過 Serial 傳出
if (Str01 == "c") {
float humidity = dht1.readHumidity();
float temperature = dht1.readTemperature();
if (isnan(humidity) || isnan(temperature)) {
Serial.println("Failed to read from DHT sensor!");
} else {
Serial.print(humidity);
Serial.print("% ");
Serial.print(temperature);
Serial.println("C");
}
}
}
```
## 作業:要逼卡才可以控制燈
應該不是因為多線程才對 而是把無限迴圈的bug修復才對
arduino
```cpp
#include "DHT.h"
#include <SPI.h>
#include <MFRC522.h>
#define DHTPIN 27
#define DHTTYPE DHT11
#define Led 2
// RFID 模組接腳定義
#define SS_PIN 5
#define RST_PIN 22
String Str01 = "";
DHT dht1(DHTPIN, DHTTYPE);
// RFID 初始化
MFRC522 rfid(SS_PIN, RST_PIN);
bool rfidEnabled = false;
// 讀取 RFID 卡片 UID
String mfrc522_readID()
{
String ret = "";
// 檢查是否有新卡片
if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial())
{
Serial.println("檢測到卡片!");
// 獲取卡片類型
MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);
// 讀取 UID
for (byte i = 0; i < rfid.uid.size; i++) {
ret += (rfid.uid.uidByte[i] < 0x10 ? "0" : "");
ret += String(rfid.uid.uidByte[i], HEX);
}
// 停止讀卡
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
Serial.print("CARD:");
Serial.println(ret);
}
return ret;
}
void setup(){
// 初始化串口
Serial.begin(115200);
delay(1000); // 等待串口穩定
// 初始化 DHT11
dht1.begin();
// 初始化 LED
pinMode(Led, OUTPUT);
digitalWrite(Led, LOW);
// 初始化 SPI 和 RFID
SPI.begin();
rfid.PCD_Init();
Serial.println("STATUS:Ready");
Serial.println("初始化完成,等待命令...");
}
void loop(){
// 讀取串口命令
if (Serial.available()){
Str01 = "";
delay(10);
while (Serial.available()){
char c = Serial.read();
if(c == '\n' || c == '\r') break;
Str01 += c;
}
// 打印收到的命令
Serial.print("收到命令: ");
Serial.println(Str01);
// 處理命令
processCommand(Str01);
}
// 如果 RFID 讀取已啟用
if(rfidEnabled) {
String id = mfrc522_readID(); // 讀取 RFID 卡片
if (id != "") {
Serial.print("卡片 ID: ");
Serial.println(id);
// RFID 讀取後自動關閉
rfidEnabled = false;
}
delay(100); // 短暫延遲,避免過度頻繁讀取
}
}
// 處理串口命令
void processCommand(String command) {
if(command == "a"){
digitalWrite(Led, HIGH);
Serial.println("LED:ON");
}
else if(command == "b"){
digitalWrite(Led, LOW);
Serial.println("LED:OFF");
}
else if(command == "c"){
readDHT();
}
else if(command == "d"){
Serial.println("啟動 RFID 讀取...");
rfidEnabled = true; // 啟用 RFID 讀取
}
else {
Serial.println("未知命令");
}
}
// 讀取 DHT11 溫濕度
void readDHT() {
float humidity = dht1.readHumidity();
float temperature = dht1.readTemperature();
if(isnan(humidity) || isnan(temperature)){
Serial.println("DHT:Error");
}
else{
Serial.print("DHT:");
Serial.print(humidity);
Serial.print(",");
Serial.println(temperature);
}
}
```
python
```python
import serial
import tkinter
from time import sleep
import threading
import queue
# 創建數據隊列,用於線程間通信
data_queue = queue.Queue()
# 設定串口
SERIAL_PORT = '/dev/cu.usbserial-140' # 修正串口名稱
ser = None
connect_status = False
condition = False
authorized = False # 新增 RFID 授權狀態追踪
# 串口讀取線程
def serial_reader():
global ser, connect_status
while connect_status:
try:
if ser and ser.is_open and ser.in_waiting:
data = ser.readline().decode('utf-8', errors='ignore').strip()
if data:
print(f"收到數據: {data}")
data_queue.put(data)
sleep(0.1) # 短暫休眠,避免CPU使用率過高
except Exception as e:
print(f"讀取錯誤: {e}")
sleep(1)
# 處理數據隊列
def process_queue():
try:
while not data_queue.empty():
data = data_queue.get_nowait()
process_response(data)
except Exception as e:
print(f"處理數據錯誤: {e}")
finally:
# 每100毫秒檢查一次隊列
Tkwindow.after(100, process_queue)
# 串口發送指令
def SerialWrite(command):
global ser
if not ser or not ser.is_open:
print("串口未連接")
return None
try:
ser.write(command) # 發送指令到 Arduino
print(f"發送命令: {command}")
return True
except serial.SerialException as e:
print(f"串口通信錯誤: {e}")
return None
# 處理 Arduino 回應
def process_response(data):
global authorized
if data.startswith("STATUS:"):
status = data.split(":")[1]
print(f"收到狀態: {status}")
if status == "Authorized":
authorized = True
buttonA.config(state='normal')
buttonB.config(state='normal')
LabelA.config(text="卡片驗證成功!可以控制 LED")
elif status == "Unauthorized":
authorized = False
buttonA.config(state='disabled')
buttonB.config(state='disabled')
LabelA.config(text="卡片未授權!")
elif status == "Need_Auth":
LabelA.config(text="請先刷授權卡片!")
elif status == "Ready":
LabelA.config(text="Arduino 已就緒!")
buttonC.config(state='normal') # 啟用溫濕度按鈕
buttonD.config(state='normal') # 啟用 RFID 按鈕
elif data.startswith("LED:"):
status = data.split(":")[1]
LabelA.config(text=f"LED 狀態:{status}")
elif data.startswith("DHT:"):
print("收到溫濕度數據")
if data == "DHT:Error":
print("溫濕度讀取錯誤")
LabelA.config(text="溫濕度感測器讀取錯誤!")
else:
try:
values = data.split(":")[1].split(",")
humidity = float(values[0])
temperature = float(values[1])
print(f"溫度: {temperature}°C, 濕度: {humidity}%")
LabelA.config(text=f"濕度: {humidity}%\n溫度: {temperature}°C")
except Exception as e:
print(f"解析溫濕度數據錯誤: {e}")
print(f"原始數據: {data}")
LabelA.config(text="數據格式錯誤!")
elif data.startswith("CARD:"):
card_uid = data.split(":")[1]
print(f"讀取到卡片 UID: {card_uid}")
# 檢查卡片 UID,如果是 228a11d2,則啟用 LED 控制按鈕
if card_uid.lower() == "228a11d2":
print("授權卡片!啟用 LED 控制")
authorized = True
buttonA.config(state='normal')
buttonB.config(state='normal')
LabelA.config(text=f"授權卡片 {card_uid}!\n可以控制 LED")
else:
print("非授權卡片")
authorized = False
buttonA.config(state='disabled')
buttonB.config(state='disabled')
LabelA.config(text=f"非授權卡片: {card_uid}")
# 處理 Arduino 發送的普通消息
elif "卡片 ID:" in data and "228a11d2" in data.lower():
print("接收到授權卡片信息")
authorized = True
buttonA.config(state='normal')
buttonB.config(state='normal')
LabelA.config(text="授權卡片!可以控制 LED")
# 更新界面
LabelA.update()
# 發送 'a' 指令(開啟 LED)
def SendCmdA():
global condition
condition = False # 停止持續讀取
SerialWrite(b'a\n') # 發送 'a' 指令
# 發送 'b' 指令(關閉 LED)
def SendCmdB():
global condition
condition = False # 停止持續讀取
SerialWrite(b'b\n') # 發送 'b' 指令
# 發送 'c' 指令(讀取 DHT11 溫濕度感測器數據)
def SendCmdC():
global condition, ser
if not ser or not ser.is_open:
LabelA.config(text="串口未連接!")
return
SerialWrite(b'c\n') # 發送 'c' 指令,添加換行符
LabelA.config(text="正在讀取溫濕度...")
# 發送 'd' 指令(啟用 RFID 檢測)
def SendCmdD():
global condition, ser
if not ser or not ser.is_open:
LabelA.config(text="串口未連接!")
return
SerialWrite(b'd\n') # 發送 'd' 指令,添加換行符
LabelA.config(text="正在檢測 RFID...")
# 連接 Arduino
def Serial_Connect():
global ser, connect_status
print('正在連接 Arduino..........')
LabelA.config(text='正在連接 Arduino..........')
LabelA.update_idletasks()
# 如果串口已經打開,先關閉
if ser and ser.is_open:
connect_status = False
sleep(1) # 給讀取線程時間退出
ser.close()
sleep(1) # 等待串口完全關閉
try:
ser = serial.Serial(SERIAL_PORT, 115200, timeout=0.5)
print("串口打開成功")
# 啟動讀取線程
connect_status = True
threading.Thread(target=serial_reader, daemon=True).start()
# 初始化按鈕狀態
buttonA.config(state='disabled')
buttonB.config(state='disabled')
buttonC.config(state='normal')
buttonD.config(state='normal')
buttonStart.config(state='disabled')
# 發送初始命令
SerialWrite(b'\n')
LabelA.config(text='Arduino 已連接!等待狀態...')
return True
except serial.SerialException as e:
print(f"串口連接錯誤: {e}")
LabelA.config(text='串口連接失敗!請檢查設備連接')
return False
# 退出程式
def Exit():
global ser, connect_status
print('退出程式....')
LabelA.config(text='退出程式...')
LabelA.update_idletasks()
# 停止讀取線程
connect_status = False
sleep(1)
try:
if ser and ser.is_open:
SerialWrite(b'\x1b\n') # 發送 ESC 碼 (0x1B) 來通知 Arduino 停止
ser.close()
except serial.SerialException as e:
print(f"關閉串口時發生錯誤: {e}")
finally:
Tkwindow.destroy()
# 創建 Tkinter 視窗
Tkwindow = tkinter.Tk()
Tkwindow.title('Python 與 Arduino 通訊')
Tkwindow.minsize(600, 400)
# 顯示 Arduino 狀態的標籤
LabelA = tkinter.Label(Tkwindow, bg='white', fg='black', text='請按 "連接" 開始', width=30, height=10)
LabelA.pack(side=tkinter.TOP)
# 建立框架排列按鈕
button_frame = tkinter.Frame(Tkwindow)
button_frame.pack(side=tkinter.BOTTOM, fill=tkinter.X, padx=10, pady=10)
left_frame = tkinter.Frame(button_frame)
left_frame.pack(side=tkinter.LEFT)
right_frame = tkinter.Frame(button_frame)
right_frame.pack(side=tkinter.RIGHT)
# LED 開啟按鈕(初始為禁用狀態)
buttonA = tkinter.Button(left_frame, text='LED ON', width=10, command=SendCmdA, state='disabled')
buttonA.pack(side=tkinter.LEFT, padx=5)
# LED 關閉按鈕(初始為禁用狀態)
buttonB = tkinter.Button(left_frame, text='LED OFF', width=10, command=SendCmdB, state='disabled')
buttonB.pack(side=tkinter.LEFT, padx=5)
# 讀取 DHT11 溫濕度按鈕
buttonC = tkinter.Button(left_frame, text='顯示溫濕度', width=12, command=SendCmdC, state='disabled')
buttonC.pack(side=tkinter.LEFT, padx=5)
# RFID 檢測按鈕
buttonD = tkinter.Button(left_frame, text='RFID', width=10, command=SendCmdD)
buttonD.pack(side=tkinter.LEFT, padx=5)
# 連接 Arduino 按鈕
buttonStart = tkinter.Button(right_frame, text='連接', width=10, command=Serial_Connect)
buttonStart.pack(side=tkinter.RIGHT, padx=5)
# 退出按鈕
buttonEnd = tkinter.Button(right_frame, text='退出', width=10, command=Exit)
buttonEnd.pack(side=tkinter.RIGHT, padx=5)
# 啟動數據處理
Tkwindow.after(100, process_queue)
# 開始 Tkinter 事件迴圈
Tkwindow.mainloop()
```
## 作業:設定溫度之後 超過溫度要上傳資料庫
python
```python
import serial
import tkinter
import pymysql
from time import sleep
# 設定串口 要記得看接口有沒有一樣
ser = serial.Serial('/dev/cu.usbserial-1140', 115200, timeout=2)
condition = False
temparature = 0
try:
db = pymysql.connect(host="localhost", user="testt", password="test", database="testt")
cursor1 = db.cursor()
except Exception as e:
print("資料庫連線失敗!", e)
exit(1) # 如果資料庫連線失敗就直接中止程式
# 串口發送指令
def SerialWrite(command):
ser.write(command) # 發送指令到 Arduino
rv = ser.readline() # 讀取 Arduino 回應
data = rv.decode('utf-8').strip() # 轉換為字串並去除換行符號
print(data) # 在終端機輸出資料
sleep(1) # 暫停 1 秒
ser.flushInput() # 清除輸入緩衝區
return data # 返回資料
# 發送 'a' 指令(開啟 LED)
def SendCmdA():
global condition
condition = False # 停止持續讀取
SerialWrite(b'a') # 發送 'a' 指令 b'a' 是什麼?這是 Python 的 bytes 物件,代表「字元 a 的 ASCII 位元組」:
LabelA.config(text='已發送指令 "a" 至 Arduino') # 更新介面顯示文字
LabelA.update_idletasks() # 立即更新 UI
# 發送 'b' 指令(關閉 LED)
def SendCmdB():
global condition
condition = False # 停止持續讀取
SerialWrite(b'b') # 發送 'b' 指令
LabelA.config(text='已發送指令 "b" 至 Arduino') # 更新介面顯示文字
LabelA.update_idletasks() # 立即更新 UI
# 發送 'c' 指令(讀取 DHT11 溫濕度感測器數據)
def SendCmdC():
global condition
condition = True # 開啟持續讀取模式
SerialWrite(b'c') # 發送 'c' 指令
while condition:
rv1 = ser.readline() # 讀取 Arduino 回應
data = rv1.decode('utf-8', errors='ignore').strip() # 轉換為字串並忽略錯誤
print(data) # 在終端機輸出資料
# 先用空白分開濕度與溫度
humidity_str, temperature_str = data.split()
# 去掉單位並轉換為 float
tmp_humidity = float(humidity_str.strip('%'))
tmp_temperature = float(temperature_str.strip('C'))
print(temparature)
if tmp_temperature > temparature :
LabelA.config(text="超過溫度") # 更新介面顯示文字
LabelA.update_idletasks() # 立即更新
cursor1.execute("INSERT INTO temparature (Temparature) \
VALUES ('%d')" % (tmp_temperature))
db.commit()
else:
LabelA.config(text=data) # 更新介面顯示文字
LabelA.update_idletasks() # 立即更新 UI
if rv1 != "" :
break
# 連接 Arduino
def Serial_Connect():
print('正在連接 Arduino..........')
LabelA.config(text='正在連接 Arduino..........') # 更新介面顯示文字
LabelA.update_idletasks()
sleep(1) # 暫停 1 秒
for i in range(10): # 嘗試 10 次連線
rv1 = ser.readline() # 讀取 Arduino 回應
data = rv1.decode('utf-8').strip() # 轉換為字串
print('載入中...')
if data.startswith("Ready"): # 如果 Arduino 回應 "Ready"
print('Arduino 已準備就緒!')
LabelA.config(text='Arduino 已準備就緒!')
buttonStart.config(state='disable') # 停用連接按鈕!防止重複連接
break
sleep(1) # 暫停 1 秒後繼續嘗試
def insertEntry():
global temparature
var=entry.get()
temparature=float(var)
LabelA.config(text=var) # 更新介面顯示文字
LabelA.update_idletasks() # 立即更新 UI
# 退出程式
def Exit():
print('退出程式...')
LabelA.config(text='退出程式...') # 更新介面顯示文字
LabelA.update_idletasks()
sleep(1) # 暫停 1 秒
SerialWrite(b'\x1b') # 發送 ESC 碼(0x1B)來通知 Arduino 停止
ser.close() # 關閉串口連接
Tkwindow.destroy() # 關閉 Tkinter 視窗
# 創建 Tkinter 視窗
Tkwindow = tkinter.Tk()
Tkwindow.title('Python 與 Arduino 通訊') # 設定視窗標題
Tkwindow.minsize(600, 400) # 設定視窗最小大小
# 顯示 Arduino 狀態的標籤
LabelA = tkinter.Label(Tkwindow, bg='white', fg='black', text='請按「連接」開始', width=30, height=10)
LabelA.pack(side=tkinter.TOP)
#輸入框
entrytext=tkinter.StringVar()#打包他的字
entrytext.set('insert tempature')
entry=tkinter.Entry(master = Tkwindow)
entry.pack()
buttoEntry=tkinter.Button(master=Tkwindow,textvariable=entrytext,width=15,height=2,command=insertEntry)
buttoEntry.pack()
# LED 開啟按鈕
buttonA = tkinter.Button(Tkwindow, text='LED ON', width=10, command=SendCmdA)
buttonA.pack(side=tkinter.LEFT)
# LED 關閉按鈕
buttonB = tkinter.Button(Tkwindow, text='LED OFF', width=10, command=SendCmdB)
buttonB.pack(side=tkinter.LEFT)
# 讀取 DHT11 溫濕度數據按鈕
buttonC = tkinter.Button(Tkwindow, text='顯示溫濕度', width=10, command=SendCmdC)
buttonC.pack(side=tkinter.LEFT)
# 連接 Arduino 按鈕
buttonStart = tkinter.Button(Tkwindow, text='連接', width=10, command=Serial_Connect)
buttonStart.pack(side=tkinter.RIGHT)
# 退出按鈕
buttonEnd = tkinter.Button(Tkwindow, text='退出', width=10, command=Exit)
buttonEnd.pack(side=tkinter.RIGHT)
# 開始 Tkinter 事件迴圈
Tkwindow.mainloop()
```
arduino
```cpp
#include "DHT.h"
#define DHTPIN 27
#define DHTTYPE DHT11
#define Led 2
String Str01 = "";
DHT dht1(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(115200); // 初始化 Serial 串口通訊
dht1.begin(); // 初始化 DHT 感測器
Serial.println("Ready"); // 回傳訊號給 Python 程式知道「準備完成」
pinMode(Led, OUTPUT); // 設定 LED 腳位為輸出
digitalWrite(Led, LOW); // 預設 LED 關閉
}
void loop() {
if (Serial.available()) {
Str01 = "";
delay(10);
while (Serial.available()) {
char c = Serial.read();
if (c == '\n') break; // 遇到換行符號就停止
Str01 += c;
}
Serial.println(Str01); // 印出收到的指令
}
// a 指令 → 開 LED 0.5 秒、關 0.5 秒
if (Str01 == "a") {
digitalWrite(Led, HIGH);
delay(500);
digitalWrite(Led, LOW);
delay(500);
}
// b 指令 → 關 LED
if (Str01 == "b") {
digitalWrite(Led, LOW);
}
// c 指令 → 讀取 DHT11 濕度與溫度,並透過 Serial 傳出
if (Str01 == "c") {
float humidity = dht1.readHumidity();
float temperature = dht1.readTemperature();
if (isnan(humidity) || isnan(temperature)) {
Serial.println("Failed to read from DHT sensor!");
} else {
Serial.print(humidity);
Serial.print("% ");
Serial.print(temperature);
Serial.println("C");
}
}
}
```
創建資料庫
```python
import pymysql
# 資料庫連接的參數設定
db = pymysql.connect(host="localhost", user="testt", password="test", database="testt")
cursor1 = db.cursor()
# 建立 資料表(如果不存在)
create_table_query = """
CREATE TABLE IF NOT EXISTS temparature (
id INT AUTO_INCREMENT PRIMARY KEY,
Temparature VARCHAR(10),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
"""
cursor1.execute(create_table_query)
print("資料表檢查完成,若不存在則已建立。")
# 關閉資料庫連接
cursor1.close()
db.close()
```
# django api


## api請求
在python3.10虛擬環境之下
```bash
pip install django==3.2 #一定要3.2版
pip install PyMySQL
```
```bash
django-admin startproject chumpower_api
cd chumpower_api
python manage.py startapp gps
brew install mysql
```
到`chumpower_api/chumpower_api`
打開`settings.py`貼上gps

執行變更
```
python manage.py makemigrations #確認
python manage.py migrate #執行更動
```
到`chumpower_api/urls.py`新增
```python
from django.contrib import admin
from django.urls import path
from gps import views
urlpatterns = [
path('admin/', admin.site.urls),
#(新增)
path('G000/', views.G000, name='G000'),
path('G001/', views.G001, name='G001'),
]
```
到`chumpower_api/gps/views.py`新增
```python
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
import pymysql, json, cv2
import numpy as np
# 圖片轉檔用
def convertToBinaryData(filename):
pass # TODO: 這裡的內容未補上
# 開始新增需要的功能:建立資料表
@csrf_exempt
def G000(request):
# 判斷是否使用 GET 方法才處理
if request.method == "GET":
# 資料庫建立語法
sql = (
"CREATE TABLE GPS (ID Int AUTO_INCREMENT,"
"Longitude FLOAT,"
"Latitude FLOAT,"
"Map BLOB,"
"PRIMARY KEY (ID))"
)
try:
# 資料庫連線
db = pymysql.connect(
host="127.0.0.1",
user="testt",
password="test",
database="chumpower_gps"
)
# 使用 cursor() 方法建立一個游標對象 cursor
cursor = db.cursor()
# 使用 execute() 方法執行 SQL,如果資料表存在則刪除
cursor.execute("DROP TABLE IF EXISTS GPS")
# 執行建立資料表語法
cursor.execute(sql)
# 提交變更
db.commit()
print("創建完成")
status = "OK"
except:
# 資料庫回傳錯誤訊息
db.rollback()
print("創建失敗")
status = "ERROR"
# 關閉資料庫連線
db.close()
# 回傳狀態
return HttpResponse(status)
```
開啟apache

在chumpower_api
```python
python manage.py runserver
```
另外在自己資料夾開一個python檔去測試有沒有成功抓到網頁的東西並創建資料庫
```python
import requests
import json
# -------------------- API_G000_GET_TEST --------------------
# 輸入要 GET 的 API 路徑
get_rdata = requests.get('http://127.0.0.1:8000/G000/')
# 將 Response 的內容以文字形式輸出
print(get_rdata.text)
```
成功

創建資料庫資料

到`chumpower_api/gps/views.py`新增
```python
@csrf_exempt
def G001(request):
if request.method == "POST":
try:
data = json.loads(request.body)
print(data['Longitude'], data['Latitude'])
# 使用參數化避免 SQL injection
sql = "INSERT INTO GPS (Longitude, Latitude) VALUES (%s, %s)"
values = (data['Longitude'], data['Latitude'])
# 資料庫連線
db = pymysql.connect(
host="127.0.0.1",
user="testt",
password="test",
database="chumpower_GPS"
)
cursor = db.cursor()
cursor.execute(sql, values)
db.commit()
print("上傳完成")
status = "OK"
except Exception as e:
db.rollback()
print("上傳失敗", str(e))
status = "ERROR"
finally:
db.close()
return HttpResponse(status)
else:
return HttpResponse("Only POST allowed", status=405)
```
在chumpower_api
```python
python manage.py runserver
```
另外在自己資料夾開一個python檔去測試有沒有成功上傳資料
```python
import requests
# -------------------- API_G001_POST_TEST --------------------
# 傳送的參數
payload = {
"Longitude": 125.5,
"Latitude": 37.5
}
# POST 到 G001 API
r = requests.post("http://127.0.0.1:8000/G001/", json=payload)
# 顯示結果
print("上傳完成,伺服器回應:", r.text)
```















## 網頁
### 功能1
```bash
pip install django==3.2
django-admin --version #檢查
pip install PyMySQL
#千萬不要用老師說的sqlclient
```
開啟資料庫




```bash
mkdir apipython
cd apipython
Django-admin.py startproject apitest
cd apitest
python manage.py startapp app
# app是應用名稱
```




建立資料庫

apitest/apitest/settings.py加上應用名稱

apitest/apitest/settings.py再加上這行
```python
# DATABASES = { 改掉的地方
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': BASE_DIR / 'db.sqlite3',
# }
# }
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'embedding', # 資料庫名稱
'USER': 'testt', # 使用者
'PASSWORD': 'test', # 密碼
'HOST': '127.0.0.1', # 使用 127.0.0.1 而不是 localhost
'PORT': '3306', # mysql 預設port
'OPTIONS': {
'charset': 'utf8mb4',
'use_unicode': True,
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
}
}
}
```
在apitest/app/models貼上
```python
from django.db import models
class dht(models.Model):
temperature = models.FloatField() # 溫度 (°C)
humidity = models.FloatField() # 濕度 (%)
recorded_at = models.DateTimeField(auto_now_add=True) # 自動紀錄時間
def __str__(self):
return f"{self.recorded_at}|溫度: {self.temperature}°C|濕度: {self.humidity}%"
```
```
python manage.py makemigrations #確認
python manage.py migrate #執行更動
```
成功

到apitest/app/views貼上
```python
from django.shortcuts import render, redirect
from .models import dht # 從models.py載入需要的資料表
# 上傳資料
def sensor_upload(request):
if request.method == 'POST':
temp = request.POST.get('temperature')
hum = request.POST.get('humidity')
dht.objects.create(temperature=temp, humidity=hum)
return redirect('sensor_list')
return render(request, 'sensor_upload.html')
# 查詢資料
def sensor_data(request):
data = dht.objects.all().order_by('-recorded_at')
return render(request, 'sensor_data.html', {'data': data})
```
到apitest/apitest/urls.py貼上
```python
from django.contrib import admin
from django.urls import path
from app import views #從view載入所有api功能
urlpatterns = [
path('admin/', admin.site.urls),
path('sensor/upload/', views.sensor_upload, name='sensor_upload'), #剛剛view中的
path('sensor/list/', views.sensor_data, name='sensor_list'),
path('sensor/data/', views.sensor_data, name='sensor_data'),
#都是網頁的網址 例如把sensor_data導到http://127.0.0.1:8000/sensor/data/
]
```
到apitest/apitest/template之下新增sensor_data.html與sensor_upload.html
**sensor_data.html**
```html
<!DOCTYPE html>
<html>
<head>
<title>感測資料紀錄</title>
</head>
<body>
<h2>感測資料列表</h2>
<table border="1">
<tr>
<th>時間</th>
<th>溫度 (℃)</th>
<th>濕度 (%)</th>
</tr>
{% for item in data %}
<tr>
<td>{{ item.recorded_at }}</td>
<td>{{ item.temperature }}</td>
<td>{{ item.humidity }}</td>
</tr>
{% endfor %}
</table>
<a href="/sensor/upload/">返回上傳表單</a>
</body>
</html>
```
**sensor_upload.html**
```html
<!DOCTYPE html>
<html>
<head>
<title>上傳感測資料</title>
</head>
<body>
<h2>輸入感測資料</h2>
<form method="POST">
{% csrf_token %}
溫度 (℃):
<input type="number" name="temperature" step="0.1" /><br />
濕度 (%):
<input type="number" name="humidity" step="0.1" /><br />
<button type="submit">上傳</button>
</form>
<a href="/sensor/list/">查看所有資料</a>
</body>
</html>
```
到apitest/之下執行
```bash
python manage.py runserver 127.0.0.1:8000
```
成功

在網頁打`http://127.0.0.1:8000`會得到

輸入`http://127.0.0.1:8000/sensor/upload/`進入資料上傳頁面

輸入`http://127.0.0.1:8000/sensor/data/`進入資料查詢頁面

`http://localhost/phpmyadmin/`中可以看到新增的資料

### 功能2
apitest/apitest/settings.py再加上這些
```python
import os
#要在BASE_DIR = ... 下面
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
```
放的位置長這樣

apitest/app/views再加上一些東西
```python
from django.shortcuts import render, redirect
from .models import dht # 從models.py載入需要的資料表
#(新增)
from django.conf import settings
from django.http import FileResponse, Http404
import os
# 上傳資料
def sensor_upload(request):
if request.method == 'POST':
temp = request.POST.get('temperature')
hum = request.POST.get('humidity')
dht.objects.create(temperature=temp, humidity=hum)
return redirect('sensor_list')
return render(request, 'sensor_upload.html')
# 查詢資料
def sensor_data(request):
data = dht.objects.all().order_by('-recorded_at')
return render(request, 'sensor_data.html', {'data': data})
# 圖片下載(新增)
def image_download(request, filename):
file_path = os.path.join(settings.MEDIA_ROOT, filename)
if os.path.exists(file_path):
return FileResponse(open(file_path, 'rb'), as_attachment=True)
raise Http404("圖片不存在")
# 上傳圖片(新增)
def image_upload(request):
# 若找不到 media 資料夾,自動建立
if not os.path.exists(settings.MEDIA_ROOT):
os.makedirs(settings.MEDIA_ROOT)
message = ''
if request.method == 'POST' and request.FILES.get('image'):
image = request.FILES['image']
file_path = os.path.join(settings.MEDIA_ROOT, image.name)
with open(file_path, 'wb+') as destination:
for chunk in image.chunks():
destination.write(chunk)
message = '圖片已成功上傳!'
# 列出所有檔案名稱
files = os.listdir(settings.MEDIA_ROOT)
return render(request, 'image_upload.html', {
'files': files,
'message': message
})
```
apitest/apitest/urls.py再加上一些東西
```python
from django.contrib import admin
from django.urls import path
from app import views #從view載入所有api功能
#(新增)
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('sensor/upload/', views.sensor_upload, name='sensor_upload'), #剛剛view中的,
path('sensor/list/', views.sensor_data, name='sensor_list'),
path('sensor/data/', views.sensor_data, name='sensor_data'),
#(新增)
path('image/upload/', views.image_upload, name='image_upload'),
path('image/download/<str:filename>/', views.image_download, name='image_download'),
]
#這行要放在所有程式的底下
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
```
到apitest/之下執行
```bash
python manage.py runserver 127.0.0.1:8000
```
成功

網頁中輸入`http://127.0.0.1:8000`

網頁中輸入`http://127.0.0.1:8000/upload`
可以上傳、下載、查看圖片

# node red 雲端平台
下載完node之後下載node-red
```bash
sudo npm install -g --unsafe-perm node-red
```
執行node-red 然後到`http://127.0.0.1:1880/`(他給的網址)
```
node-red
```


dashboard會出錯所以要用terminal裝
```
sudo npm cache clean --force
node-red admin install node-red-dashboard
```


Time
```javascript
msg.topic ="SELECT recorded_at FROM app_dht"
return msg;
```
getvalueT
```javascript
var test = msg.payload;
test.forEach(function(e,i){
msg.payload=e['recorded_at'];});
return msg;
```





```javascript
msg.topic ="SELECT id FROM app_dht"
return msg;
```
```javascript
msg.topic ="SELECT temperature FROM app_dht"
return msg;
```
```javascript
msg.topic ="SELECT humidity FROM app_dht"
return msg;
```
原本的複製過去

多三個function

functionC
```javascript
var test = msg.payload;
test.forEach(function (e, i) {
msg.payload = e['temperature'];
});
```
functionF
```javascript
var test = msg.payload;
test.forEach(function (e, i) {
msg.payload = e['Fahrenheit'];
});
```
functionH
```javascript
var test = msg.payload;
test.forEach(function (e, i) {
msg.payload = e['humidity'];
});
```



# 期末專題報告
### 學術文章 IOT
### 反應力測試:
要先逼rfid還會開始
開始後 led會倒數 321 倒數完之後led會亮 會計時當物體小於一定距離才會 傳資料上去資料庫 並且拍一張使用者的大頭照 然後用node red去呈現排名
- [x] 兩種以上感測元件:rfid、led、距離感測
- [x] 通訊網路:wifi
- [x] Mysql
- [x] api:django
- [x] node-red:呈現資料庫
- [x] cam:讓電腦可以看到畫面

- [x] **step1:**
esp32:
led會倒數 321 倒數完之後會計時當物體小於一定距離才會 傳資料上去資料庫
- [x] **step2:**
倒數完傳資料要用wifi
- [x] **step3:**
用django接收並且傳到mysql
並且在拍一張照片
- [x] **step4:**
用node red去呈現排名與照片
#### 使用方法
先到xampp開啟mysql跟apache
cd到django資料夾api_server 開啟虛擬環境
再run django的server
```bash
source .venv/bin/activate
cd ./game/gameApi
python manage.py runserver 172.20.10.2:8000
```
手機開啟網路讓電腦與esp32連上之後
到終端機開啟node-red
```
node-red
```
然後開啟第一個
* http://127.0.0.1:1880/ranking ➡︎ 整頁 5 秒刷新(高級 UI)
* http://127.0.0.1:1880/ui ➡︎ Dashboard 版
#### 成果報告
:::spoiler
# 反應時間測試遊戲系統
### 姓名:王冠傑 學號:D1149768
## 1. 動機與目的
### 動機
觀察到在現代社會中,反應速度對於許多場景都至關重要,從運動競技到日常駕駛。本專題的宗旨為:
- 建立一個可量化的反應時間測試系統
- 提供即時的視覺回饋和數據記錄
- 透過遊戲化方式提高使用者參與度
### 目的與辦法
由使用者用磁扣逼rfid作為遊戲的開始,開始後led燈會先提示使用者開始倒數,並且選擇2~4秒的隨機倒數時間。倒數完之後led會亮,表示開始測反應時間。當物體接近距離感測小於一定距離才會增測到,並且把傳資料上去資料庫,同時間用手機拍一張使用者的大頭照作為參賽者的紀錄,除此之外這樣也比較好辨認數據是誰的。最後用node-red去以排行榜去呈現排名以及使用者的大頭照。
## 2. 現有方法與缺點
### 傳統測試方法:
- 簡單的按鈕測試
- 基礎電腦程式
### 現有方法的缺點:
- 缺乏實時數據收集
- 無法提供視覺化回饋
- 數據分析能力有限
- 不有趣使用者參與度低
- 測試場景單一
## 3. 系統架構與情境圖
### 系統架構:
```mermaid
graph TD
A[硬體感測器] -->|距離數據| B[ESP32 控制器]
B -->|HTTP POST| C[Django API 服務器]
C -->|存儲反應數據| D[資料庫]
C -->|拍攝| E[攝像頭]
E -->|照片| D
F[使用者] -->|互動| A
D -->|數據分析| G[網頁成果展示]
```
### 主要元件:
1. **硬體層**
- ESP32 微控制器
- 手機攝像頭系統
- 超聲波距離感測器
- led燈
- rfid偵測器
2. **後端服務**
- Django REST API
- 資料庫系統 mysql
- 影像處理模組 opencv
- node-red 排行榜呈現
## 4. 成果展示
### 硬體實現
### 數據分析

### 系統特點:
1. **即時反應測試**
- 精確的距離感測
- 毫秒級響應時間記錄
- 自動影像捕捉
2. **數據收集與分析**
- 自動化數據存儲
- 多維度數據分析
- 視覺化結果呈現
3. **系統優勢**
- 高精度測量
- 自動化數據收集
- 完整的數據記錄
- 視覺化成果展示
- 擴展性強
### 實際應用場景:
- 運動員反應能力訓練
- 駕駛員反應測試
- 遊戲玩家反應力評估
- 醫療康復訓練
- 教育研究使用
:::