--- tags: Python系統設計 --- # Python,Django,DHT22,Raspberry Pi 4,树莓派搭派DHT22架设温湿度侦测,Django架站接收资料,Python编程,与AP设定,HTML5前端图形显示分享 ## AP设定: PS:示范AP型号RT-AC58U 1.你需要有个固定IP。 2.ASUS AP 提供固定域名功能: ![image](https://hackmd.io/_uploads/BJw0S-ZTR.png) 3.外部网路(WAN)-虚拟伺服器IP PORT 转发设定: ![image](https://hackmd.io/_uploads/SJwtLZWTA.png) ## Raspberry Pi 4 DHT22 安装与设定 ![image](https://hackmd.io/_uploads/B11lTMbpR.png) 1.更新系统 ``` sudo apt update sudo apt full-upgrade ``` 2.安装 Python 3(如果未安装) ``` sudo apt install python3 ``` 3.安装 pip(Python 包管理工具) ``` sudo apt install python3-pip ``` 4.安装常用的 Python 库 ``` pip3 install numpy pip3 install pandas pip3 install matplotlib ``` 5-1.安装 Adafruit_DHT 库 Adafruit 提供了一个用于读取 DHT22 传感器的库。你可以通过以下命令安装该库: ``` sudo pip3 install Adafruit_DHT ``` 5-2.(我使用的是)安装 Adafruit Blinka Adafruit Blinka 是让 CircuitPython 库能够在 Raspberry Pi 上运行的库。首先,你需要安装这个库: ``` sudo pip3 install --upgrade adafruit-blinka ``` 5-2-1.启用 I2C 和 SPI(如果需要) 有些项目可能需要启用 I2C 或 SPI,可以通过 Raspberry Pi 配置工具来启用: ``` sudo raspi-config ``` 选择以下选项: Interface Options 启用 I2C 和 SPI(如果项目使用这些接口) 5-2-2.安装 Adafruit CircuitPython DHT 库 接下来,安装处理 DHT22 传感器的库 adafruit_dht: ``` sudo pip3 install adafruit-circuitpython-dht ``` 5-2-3.安装 libgpiod adafruit_dht 库依赖 libgpiod 来控制 GPIO,因此你还需要安装 libgpiod: ``` sudo apt install libgpiod2 ``` 5-2-4.Python代码 1.在根目录建立一个Python资料夹 2.将左右Python指令都放入此资料夹 3.DHT22所运行的Python程式码: ``` import board import adafruit_dht import requests import time from datetime import datetime # 设置 DHT22 传感器 dht_device = adafruit_dht.DHT22(board.D4) # GPIO 4 引脚 # 获取温度和湿度数据的函数 def get_sensor_data(): try: # 尝试读取温度和湿度数据 temperature_c = dht_device.temperature humidity = dht_device.humidity if temperature_c is None or humidity is None: raise ValueError("Temperature or humidity data is None") print(f"Temp: {temperature_c:.1f} C Humidity: {humidity:.1f}%") return temperature_c, humidity except Exception as e: # 如果读取数据失败,打印错误并返回 None print(f"Failed to retrieve data from sensor: {e}") return None, None # 设置服务器 URL(确保这是你的 Django 服务器的 URL) server_url = 'http://genesisofdestiny.asuscomm.com/upload/' while True: # 获取温度和湿度数据 temperature, humidity = get_sensor_data() # 如果数据有效,发送到服务器 if temperature is not None and humidity is not None: timestamp = datetime.now().strftime("%Y%m%d%H%M%S") payload = { 'timestamp': timestamp, 'temperature': temperature, 'humidity': humidity } # 发送数据到服务器 try: response = requests.post(server_url, json=payload) #print(response.text) # 打印服务器响应 except requests.RequestException as e: print(f"Request failed: {e}") else: print("Skipping data transmission due to invalid sensor data.") # 每秒获取一次数据 time.sleep(1) ``` 4.存成DHT22.py檔 ## Raspberry Pi 4 与 DHT22 硬体连接 ![image](https://hackmd.io/_uploads/BydG2WZ6R.png) ![图片_20240913100605](https://hackmd.io/_uploads/SySJyXbpC.jpg) 1.将DHT22 VCC脚连接至Raspberry Pi 4的3.3V GPIO 接脚 2.将DHT22 GND脚连接至Raspberry Pi 4 的GND GPIO 接脚 3.将DHT22 out脚连接至Raspberry Pi 4的GPIO 4 接脚 ## Python Django安装、设定、程式码 1.[安装Python](https://www.python.org/downloads/) 2-1.安装 virtualenv(可选但推荐) ``` pip install virtualenv ``` 2-2.创建和激活虚拟环境 在C:\中 ``` CD C:\ ``` 在你希望存放 Django 项目的目录下,创建一个新的虚拟环境: ``` virtualenv Django ``` Windows: 激活虚拟环境: ``` CD C:\Django Scripts\activate ``` 2-3.安装 Django 和其他依赖 在激活的虚拟环境中,安装 Django 和 Django REST framework: ``` pip install django djangorestframework ``` 2-4.创建 Django 项目 使用 Django 的 django-admin 工具创建一个新的项目: ``` django-admin startproject DHT22 ``` 这将创建一个名为 DHT22 的目录,其中包含 Django 项目的基本结构。 2-5.启动开发服务器 进入项目目录: ``` cd DHT22 ``` 2-6.创建 Django 应用 在 Django 项目中,你可以创建多个应用(app)来实现不同的功能。创建一个新应用: ``` python manage.py startapp myapp ``` 这将创建一个名为 myapp 的目录,其中包含了应用的基本结构。 2-7.配置 Django 项目 在项目的 Django\DHT22\DHT22\settings.py 文件中,你可以进行各种配置,如数据库设置、安装的应用、静态文件配置等。你还需要在 INSTALLED_APPS 列表中添加你的应用: ``` INSTALLED_APPS = [ ... 'myapp', ... ] ``` 2-8.在手动建立资料夹data,static,templates ![image](https://hackmd.io/_uploads/SJ_QZG-pC.png) 1.在C:\Django\DHT22\中手动建立三个资料夹:data,static,templates 2-9.测试启动DHT22伺服器 1.C:\Django\DHT22\DHT22\settings.py ``` ALLOWED_HOSTS = ['*'] ``` 2. ``` CD C:\Django Scripts\activate CD DHT22 python manage.py runserver 0.0.0.0:80 ``` 3.开启浏览器测试是否能连上网页 ``` 127.0.0.1 ``` 2-10.Python安装所需库 1. ``` CD C:\Django Scripts\activate CD DHT22 pip install matplotlib # 用来绘图 pip install pandas # 用来处理数据 ``` 2.URL 设置 编辑 C:\Django\DHT22\DHT22\urls.py,配置主页和次页的 URL: ``` """ URL configuration for DHT22 project. The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/5.1/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin # from django.urls import path # urlpatterns = [ # path('admin/', admin.site.urls), # ] from django.urls import path from myapp import views urlpatterns = [ path('', views.home, name='home'), path('upload/', views.upload_data, name='upload_data'), ] ``` 2-11.接下来在 C:\Django\DHT22\myapp/views.py 中编写两个视图函数,一个用于显示主页的曲线图,另一个用于接收并保存数据。 ``` from django.shortcuts import render # Create your views here. import os import pandas as pd import matplotlib.pyplot as plt # from django.shortcuts import render from datetime import datetime def home(request): # 获取今天的日期字符串 today_str = datetime.now().strftime("%Y%m%d") filename = f"DHT22_{today_str}.txt" filepath = os.path.join("data", filename) if not os.path.exists(filepath): return render(request, 'home.html', {'error': 'No data available'}) # 读取数据 data = pd.read_csv(filepath, sep='\t', header=None, names=['Time', 'Temperature', 'Humidity']) # 将数据传递给模板 return render(request, 'home.html', {'data': data.to_dict(orient='list')}) ################################################################# from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt import json # from datetime import datetime # import os @csrf_exempt def upload_data(request): if request.method == 'POST': try: data = json.loads(request.body) # 解析 JSON 数据 timestamp = data.get('timestamp') temperature = data.get('temperature') humidity = data.get('humidity') if not all([timestamp, temperature, humidity]): return JsonResponse({"error": "Missing data"}, status=400) today_str = datetime.now().strftime("%Y%m%d") filename = f"DHT22_{today_str}.txt" filepath = os.path.join("data", filename) if not os.path.exists('data'): os.makedirs('data') with open(filepath, 'a') as f: f.write(f"{timestamp}\t{temperature}\t{humidity}\n") return JsonResponse({"message": "Data saved successfully"}) except Exception as e: return JsonResponse({"error": f"Error processing request: {e}"}, status=500) else: return JsonResponse({"error": "Invalid request method"}, status=405) ``` 2-12.创建模板 1.创建一个home.html在下面路径: C:\Django\DHT22\templates\home.html ``` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Home</title> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> </head> <body> <h1>Data Charts</h1> {% if error %} <p>{{ error }}</p> {% else %} <canvas id="temperatureChart"></canvas> <canvas id="humidityChart"></canvas> <script> const temperatureCtx = document.getElementById('temperatureChart').getContext('2d'); const humidityCtx = document.getElementById('humidityChart').getContext('2d'); const data = {{ data|safe }}; new Chart(temperatureCtx, { type: 'line', data: { labels: data['Time'], datasets: [{ label: 'Temperature', data: data['Temperature'], borderColor: 'rgba(255, 99, 132, 0.2)', borderWidth: 1 }] } }); new Chart(humidityCtx, { type: 'line', data: { labels: data['Time'], datasets: [{ label: 'Humidity', data: data['Humidity'], borderColor: 'rgba(54, 162, 235, 0.2)', borderWidth: 1 }] } }); </script> {% endif %} </body> </html> ``` 2-13.静态文件设置 确保 Django 能够找到生成的图表图片。添加如下配置到 settings.py 中: C:\Django\DHT22\DHT22\settings.py ``` STATIC_URL = '/static/' STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] ``` 2-14.告诉 Django 模板的路径 在 settings.py 中,你需要告诉 Django 在哪里寻找模板文件。编辑 settings.py 并修改 TEMPLATES 设置: C:\Django\DHT22\DHT22\settings.py ``` TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], # 告诉 Django 去哪里查找模板 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] ``` ## 相关Python与HTML程式码 1.Raspberry Pi 4 DTH22.py Python\DTH22.py ``` import board import adafruit_dht import requests import time from datetime import datetime # 设置 DHT22 传感器 dht_device = adafruit_dht.DHT22(board.D4) # GPIO 4 引脚 # 获取温度和湿度数据的函数 def get_sensor_data(): try: # 尝试读取温度和湿度数据 temperature_c = dht_device.temperature humidity = dht_device.humidity if temperature_c is None or humidity is None: raise ValueError("Temperature or humidity data is None") print(f"Temp: {temperature_c:.1f} C Humidity: {humidity:.1f}%") return temperature_c, humidity except Exception as e: # 如果读取数据失败,打印错误并返回 None print(f"Failed to retrieve data from sensor: {e}") return None, None # 设置服务器 URL(确保这是你的 Django 服务器的 URL) server_url = 'http://genesisofdestiny.asuscomm.com/upload/' while True: # 获取温度和湿度数据 temperature, humidity = get_sensor_data() # 如果数据有效,发送到服务器 if temperature is not None and humidity is not None: timestamp = datetime.now().strftime("%Y%m%d%H%M%S") payload = { 'timestamp': timestamp, 'temperature': temperature, 'humidity': humidity } # 发送数据到服务器 try: response = requests.post(server_url, json=payload) print(response.text) # 打印服务器响应 except requests.RequestException as e: print(f"Request failed: {e}") else: print("Skipping data transmission due to invalid sensor data.") # 每秒获取一次数据 time.sleep(1) ``` 2-1.Django settings.py C:\Django\DHT22\DHT22\settings.py ``` """ Django settings for DHT22 project. Generated by 'django-admin startproject' using Django 5.1.1. For more information on this file, see https://docs.djangoproject.com/en/5.1/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/5.1/ref/settings/ """ from pathlib import Path import os # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent # BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'django-insecure-2tz%upjb8kc81i)=9ix!ce&i$l1%b609ba5w6*-339xve#z&v5' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = ['*'] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'myapp', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'DHT22.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'DHT22.wsgi.application' # Database # https://docs.djangoproject.com/en/5.1/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 'NAME': BASE_DIR / 'db.sqlite3', } } # Password validation # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/5.1/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.1/howto/static-files/ STATIC_URL = 'static/' STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] # Default primary key field type # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' ``` 2-2.Django urls.py C:\Django\DHT22\DHT22\urls.py ``` """ URL configuration for DHT22 project. The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/5.1/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin # from django.urls import path # urlpatterns = [ # path('admin/', admin.site.urls), # ] from django.urls import path from myapp import views urlpatterns = [ path('', views.home, name='home'), path('upload/', views.upload_data, name='upload_data'), ] ``` 2-3.Django views.py C:\Django\DHT22\myapp\views.py ``` from django.shortcuts import render # Create your views here. import os import pandas as pd import matplotlib.pyplot as plt # from django.shortcuts import render from datetime import datetime def home(request): # 获取今天的日期字符串 today_str = datetime.now().strftime("%Y%m%d") filename = f"DHT22_{today_str}.txt" filepath = os.path.join("data", filename) if not os.path.exists(filepath): return render(request, 'home.html', {'error': 'No data available'}) # 读取数据 data = pd.read_csv(filepath, sep='\t', header=None, names=['Time', 'Temperature', 'Humidity']) # 将数据传递给模板 return render(request, 'home.html', {'data': data.to_dict(orient='list')}) ################################################################# from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt import json # from datetime import datetime # import os @csrf_exempt def upload_data(request): if request.method == 'POST': try: data = json.loads(request.body) # 解析 JSON 数据 timestamp = data.get('timestamp') temperature = data.get('temperature') humidity = data.get('humidity') if not all([timestamp, temperature, humidity]): return JsonResponse({"error": "Missing data"}, status=400) today_str = datetime.now().strftime("%Y%m%d") filename = f"DHT22_{today_str}.txt" filepath = os.path.join("data", filename) if not os.path.exists('data'): os.makedirs('data') with open(filepath, 'a') as f: f.write(f"{timestamp}\t{temperature}\t{humidity}\n") return JsonResponse({"message": "Data saved successfully"}) except Exception as e: return JsonResponse({"error": f"Error processing request: {e}"}, status=500) else: return JsonResponse({"error": "Invalid request method"}, status=405) ``` 3.Django home.html C:\Django\DHT22\templates\home.html ``` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Home</title> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> </head> <body> <h1>Data Charts</h1> {% if error %} <p>{{ error }}</p> {% else %} <canvas id="temperatureChart"></canvas> <canvas id="humidityChart"></canvas> <script> const temperatureCtx = document.getElementById('temperatureChart').getContext('2d'); const humidityCtx = document.getElementById('humidityChart').getContext('2d'); const data = {{ data|safe }}; new Chart(temperatureCtx, { type: 'line', data: { labels: data['Time'], datasets: [{ label: 'Temperature', data: data['Temperature'], borderColor: 'rgba(255, 99, 132, 0.2)', borderWidth: 1 }] } }); new Chart(humidityCtx, { type: 'line', data: { labels: data['Time'], datasets: [{ label: 'Humidity', data: data['Humidity'], borderColor: 'rgba(54, 162, 235, 0.2)', borderWidth: 1 }] } }); </script> {% endif %} </body> </html> ```