2026年8月3日 星期一

[水井村USR] EP05|ESP32 × Django REST API:將感測資料上傳雲端

智慧生活科技專業社群|IoT 入門系列|適合大一新生
前四篇已完成 I²C、四種水質感測器、ESP32 讀值,以及 Wi-Fi AP 與 NVS 設定。EP05 將把這些成果送上雲端:ESP32 透過 Wi-Fi 將水溫、pH、溶氧與鹽度組成 JSON,再以 HTTP POST 傳送到 Django REST API,最後顯示於智慧養殖 Dashboard。

請先將 EP05 主圖上傳 Blogger,再把 EP05_MAIN_IMAGE_URL 換成圖片網址。


圖:從 Atlas 水質感測器、ESP32、Django REST API 到 Dashboard 的完整資料流程。

一、這一篇要完成什麼?

任務 1|建立 JSON把感測值轉成 API 可接受的資料格式。
任務 2|HTTP POSTESP32 將 JSON 傳送到 Django。
任務 3|REST APIDjango 驗證並儲存資料。
任務 4|Dashboard顯示各養殖戶最新水質狀況。

二、完整系統架構

Atlas 感測器 ESP32 Wi-Fi HTTP POST Django REST API 資料庫 Dashboard

ESP32 不需要知道資料庫帳號密碼,也不需要直接操作資料表,只要把資料送到 API 即可。

核心觀念:ESP32 是資料提供者;Django REST API 是資料入口;資料庫負責保存;Dashboard 負責呈現。

三、實際使用的 API 與 Dashboard

本專案 API 端點:

Django REST API
https://shuijingusr.pythonanywhere.com/api/pond/sensor-reading/

智慧養殖 Dashboard:

開啟智慧養殖 Dashboard

四、什麼是 REST API?

REST API 是讓不同裝置或程式透過 HTTP 交換資料的方式。ESP32、手機 App、網站與 Python 程式,都可以使用相同 API。

HTTP 方法用途本篇是否使用
GET讀取資料Dashboard 查詢資料時使用
POST新增資料ESP32 上傳感測資料
PUT/PATCH修改資料本篇未使用
DELETE刪除資料本篇未使用

五、JSON 是什麼?

JSON 是 IoT 最常見的資料交換格式。每筆資料由 Key 與 Value 組成:

本專案 JSON 格式
{
  "token": "abc123",
  "farm_name": "湖虎戰隊",
  "pond_code": "1",
  "water_temperature": 30.01,
  "salinity": 0.44,
  "ph": 6.80,
  "dissolved_oxygen": 31.92,
  "water_source": "直流變頻水車運轉中",
  "recorded_at": "2026-08-03T21:30:00+08:00"
}
數值欄位應使用數字,不要加上引號。例如 "ph": 6.80,不要寫成 "ph": "6.80"

六、ESP32 建立 JSON

程式碼 1:建立 Payload
String buildPayload() {
  String payload;

  payload += "{";
  payload += "\"token\":\"abc123\",";
  payload += "\"farm_name\":\"湖虎戰隊\",";
  payload += "\"pond_code\":\"1\",";
  payload += "\"water_temperature\":30.01,";
  payload += "\"salinity\":0.44,";
  payload += "\"ph\":6.80,";
  payload += "\"dissolved_oxygen\":31.92,";
  payload += "\"water_source\":\"直流變頻水車運轉中\"";
  payload += "}";

  return payload;
}

正式程式中,不應把感測值寫死,而是帶入 RTD、pH、DO 與 EC 讀值。

七、確認 Wi-Fi 已連線

程式碼 2:連線檢查
if (WiFi.status() != WL_CONNECTED) {
  Serial.println("Wi-Fi 尚未連線,本次不上傳");
  return;
}

若 Wi-Fi 尚未連線,程式應停止本次 POST,避免重複產生連線錯誤。

八、ESP32 使用 HTTPS POST

程式碼 3:HTTP POST 核心流程
#include <WiFi.h>
#include <HTTPClient.h>
#include <NetworkClientSecure.h>

const char* API_URL =
  "https://shuijingusr.pythonanywhere.com/"
  "api/pond/sensor-reading/";

void uploadData(String payload) {
  if (WiFi.status() != WL_CONNECTED) {
    return;
  }

  NetworkClientSecure client;

  // 測試階段使用
  client.setInsecure();

  HTTPClient http;

  http.begin(client, API_URL);

  http.addHeader(
    "Content-Type",
    "application/json"
  );

  http.addHeader(
    "Accept",
    "application/json"
  );

  int httpCode =
    http.POST(payload);

  String response =
    http.getString();

  Serial.print("HTTP Code:");
  Serial.println(httpCode);

  Serial.print("Response:");
  Serial.println(response);

  http.end();
}
測試時可使用 setInsecure(),正式部署時建議改用伺服器根憑證驗證 HTTPS。

九、Content-Type 為什麼重要?

Django 需要知道 Request Body 是 JSON,因此必須設定:

HTTP Header
Content-Type: application/json

若沒有這個 Header,後端可能無法正確解析資料。

十、Django REST API 的資料流程

Request JSON View Serializer Model Database
Model定義資料庫欄位。
Serializer驗證 JSON 與轉換資料。
View處理 POST Request。
URL設定 API 路徑。

十一、Django Model 概念

models.py 範例
from django.db import models

class SensorReading(models.Model):
    token = models.CharField(max_length=100)
    farm_name = models.CharField(max_length=100)
    pond_code = models.CharField(max_length=30)

    water_temperature = models.FloatField(
        null=True,
        blank=True
    )

    salinity = models.FloatField(
        null=True,
        blank=True
    )

    ph = models.FloatField(
        null=True,
        blank=True
    )

    dissolved_oxygen = models.FloatField(
        null=True,
        blank=True
    )

    water_source = models.CharField(
        max_length=200,
        blank=True
    )

    recorded_at = models.DateTimeField()

十二、Serializer 的工作

serializers.py 範例
from rest_framework import serializers
from .models import SensorReading

class SensorReadingSerializer(
    serializers.ModelSerializer
):
    class Meta:
        model = SensorReading
        fields = "__all__"

Serializer 會檢查:

  • 欄位是否存在。
  • 數值格式是否正確。
  • 時間格式是否可解析。
  • 必填資料是否缺少。

十三、API View 概念

views.py 範例
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status

@api_view(["POST"])
def sensor_reading(request):
    serializer = SensorReadingSerializer(
        data=request.data
    )

    if serializer.is_valid():
        serializer.save()

        return Response(
            serializer.data,
            status=status.HTTP_201_CREATED
        )

    return Response(
        serializer.errors,
        status=status.HTTP_400_BAD_REQUEST
    )

十四、URL 設定

urls.py 範例
from django.urls import path
from . import views

urlpatterns = [
    path(
        "api/pond/sensor-reading/",
        views.sensor_reading,
        name="sensor-reading"
    ),
]

十五、HTTP 狀態碼如何判斷?

狀態碼意義處理方式
200請求成功後端已接受或回傳資料
201新增成功感測資料已寫入資料庫
400資料格式錯誤檢查 JSON、欄位與資料型態
401/403驗證失敗檢查 token 或權限
500後端錯誤檢查 Django Log、Model、Serializer
負值ESP32 連線錯誤檢查 Wi-Fi、DNS、HTTPS 與網址

十六、使用 Postman 先測試 API

在 ESP32 上傳前,可先用 Postman 測試:

  1. Method 選擇 POST。
  2. 輸入 API 網址。
  3. Body 選擇 raw。
  4. 格式選擇 JSON。
  5. 貼入 Payload。
  6. 按 Send。
若 Postman 可成功,但 ESP32 不行,問題通常在 Wi-Fi、HTTPS、Header 或 ESP32 JSON 字串。

十七、實際上傳結果

序列監控範例
========== Django API 上傳 ==========
[API] URL:
https://shuijingusr.pythonanywhere.com/
api/pond/sensor-reading/

[API] HTTP 狀態碼:201
[API] 上傳成功
====================================

十八、Dashboard 即時顯示

資料寫入資料庫後,Dashboard 可以顯示:

  • 養殖戶與魚塭。
  • 水溫。
  • 鹽度。
  • pH。
  • 溶氧。
  • 最新資料時間。
水井村 USR 智慧養殖 Dashboard
請將 Dashboard 截圖上傳 Blogger,再替換 EP05_DASHBOARD_IMAGE_URL
圖:ESP32 上傳後,Dashboard 顯示多家養殖戶的最新水質狀態。

十九、常見問題與排除

問題可能原因建議
POST 回傳 -1Wi-Fi、DNS、HTTPS 失敗先確認 ESP32 能否取得 IP
400 Bad RequestJSON 或欄位格式不符查看 Django 回應內容
403 ForbiddenToken 或權限錯誤檢查 token
500 Internal Server ErrorDjango 程式錯誤查看 PythonAnywhere Error Log
Dashboard 沒更新查詢不是最新資料檢查排序與最新一筆查詢
時間不正確NTP 未同步或時區錯誤使用 ISO 8601+08:00

二十、本篇重點

  1. ESP32 使用 JSON 封裝感測資料。
  2. 透過 HTTP POST 傳送到 Django REST API。
  3. Content-Type 必須設定為 application/json。
  4. Serializer 負責驗證資料。
  5. 201 表示資料新增成功。
  6. Dashboard 從資料庫取得最新監測值。

結語

完成 EP05 後,智慧養殖系統已經建立從感測器到雲端的完整資料鏈。ESP32 透過 I²C 取得水質資料,使用 Wi-Fi 與 HTTPS 將 JSON 傳送到 Django REST API,再由資料庫與 Dashboard 完成保存與呈現。這也是 IoT 系統中最重要的核心能力:把真實世界的感測資料,轉化為可查詢、可分析、可視化的數位資訊。

AI 協作聲明:本文由作者主導系統設計、ESP32 程式開發、Django REST API 建置、資料格式規劃、API 測試與 Dashboard 整合,並使用生成式 AI 協助文字整理、程式碼說明、版面設計與教學資訊圖生成;所有 API 欄位、測試流程與系統架構均依本專案實際開發成果整理,並由作者審核修訂。

沒有留言:

張貼留言