一、项目概述
随着智能家居技术的快速发展,越来越多的用户希望通过智能设备提升家居生活的便利性和智能化水平。本项目旨在搭建一个基于树莓派的AI接入问心一言智能家居中控系统。该系统能够根据AI获取的实时数据(如天气、温度、湿度等)自动控制家居设备,并支持用户通过可视化界面进行手动控制。
通过接入问心一言的AI接口,我们可以获取天气预报、空气质量等信息,进而智能化地控制家居设备。例如,当天气预报显示即将下雨时,系统可以自动关闭窗帘,或者在高温天气时启动空调。
二、系统架构
1. 硬件架构
- 主控单元:树莓派 4B
- 传感器:DHT11温湿度传感器、BMP180气压传感器
- 执行器:继电器模块(控制灯光、风扇等设备)
- 网络模块:Wi-Fi模块(树莓派自带)
2. 软件架构
- 操作系统:Raspberry Pi OS
- 编程语言:Python
- 后端框架:Flask
- 数据库:SQLite(用于存储用户设置和设备状态)
- 消息协议:MQTT(用于设备间通信)
- 前端框架:HTML/CSS/JavaScript(用于可视化界面)
- AI接口:问心一言API(获取天气等信息)
3. 系统流程
- 数据采集:传感器实时采集环境数据并上传至后端。
- 数据处理:后端根据采集的数据和问心一言AI接口获取的天气信息进行处理。
- 设备控制:根据处理结果通过MQTT控制继电器模块,进而控制家居设备。
- 用户交互:用户通过可视化界面查看状态和手动控制设备。
三、环境搭建
1. 硬件连接
将传感器和继电器模块连接至树莓派的GPIO接口。以DHT11为例,接线如下:
- DHT11 VCC接树莓派5V
- DHT11 GND接树莓派GND
- DHT11 DATA接树莓派GPIO 17
2. 软件环境
1. 系统更新
sudo apt update
sudo apt upgrade
2. 安装Python及相关库
sudo apt install python3 python3-pip
3. 安装Flask和MQTT库
pip3 install Flask paho-mqtt requests
4. 安装传感器库
pip3 install Adafruit-DHT
5. 创建SQLite数据库
使用SQLite创建数据库和表:
python
import sqlite3
conn = sqlite3.connect('smart_home.db')
c = conn.cursor()
# 创建设备状态表
c.execute('''CREATE TABLE IF NOT EXISTS device_status
(id INTEGER PRIMARY KEY, name TEXT, state INTEGER)''')
conn.commit()
conn.close()
四、代码实现
1. 硬件端代码
1.1 读取传感器数据
以下是用于读取DHT11温湿度传感器数据并通过MQTT发布的代码:
python
import Adafruit_DHT
import paho.mqtt.client as mqtt
import requests
import time
import sqlite3
# 传感器设置
DHT_SENSOR = Adafruit_DHT.DHT11
DHT_PIN = 17
# MQTT设置
MQTT_BROKER = "localhost"
MQTT_TOPIC = "home/sensors"
# 问心一言API设置
WENXIN_API_URL = "https://api.wenxinyiyan.com/weather" # 替换为实际的问心一言API URL
WENXIN_API_KEY = "your_api_key" # 替换为你的问心一言API密钥
def get_weather_data():
"""获取实时天气数据"""
try:
response = requests.get(WENXIN_API_URL, params={"apikey": WENXIN_API_KEY})
if response.status_code == 200:
return response.json() # 返回JSON数据
else:
print("获取天气数据失败:", response.status_code)
return None
except Exception as e:
print("请求问心一言API时出错:", e)
return None
def read_sensor_and_control():
"""读取传感器数据并根据天气数据控制设备"""
humidity, temperature = Adafruit_DHT.read_retry(DHT_SENSOR, DHT_PIN)
if humidity is not None and temperature is not None:
mqtt_publish(temperature, humidity)
# 获取天气数据
weather_data = get_weather_data()
if weather_data:
# 根据天气数据做出智能决策
if weather_data['weather'] == "Rain":
control_device("窗帘", 0) # 关闭窗帘
elif weather_data['temperature'] > 30:
control_device("空调", 1) # 打开空调
else:
print("无法从湿度传感器获取数据")
def mqtt_publish(temperature, humidity):
"""将温湿度数据发布到MQTT主题"""
client = mqtt.Client() # 创建MQTT客户端
client.connect(MQTT_BROKER) # 连接到MQTT代理
client.publish(MQTT_TOPIC, f"Temperature: {temperature}°C, Humidity: {humidity}%") # 发布数据
client.disconnect() # 断开连接
def control_device(device_name, state):
"""根据设备名称和状态来控制设备"""
conn = sqlite3.connect('smart_home.db')
c = conn.cursor()
c.execute("UPDATE device_status SET state=? WHERE name=?", (state, device_name)) # 更新设备状态
conn.commit()
conn.close()
if __name__ == "__main__":
while True:
read_sensor_and_control() # 持续读取传感器数据并控制设备
time.sleep(10) # 每10秒读取一次
2. 软件层代码
2.1 Flask后端代码
接下来,我们需要实现Flask后端,以便用户可以通过HTTP请求与系统交互。
python
from flask import Flask, jsonify, request
import sqlite3
import requests
app = Flask(__name__)
def init_db():
"""初始化数据库,创建设备状态表"""
conn = sqlite3.connect('smart_home.db')
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS device_status
(id INTEGER PRIMARY KEY, name TEXT, state INTEGER)''')
conn.commit()
conn.close()
@app.route('/devices', methods=['GET'])
def get_devices():
"""获取所有设备的状态"""
conn = sqlite3.connect('smart_home.db')
c = conn.cursor()
c.execute("SELECT * FROM device_status")
devices = c.fetchall() # 获取所有设备状态
conn.close()
return jsonify(devices) # 以JSON格式返回设备状态
@app.route('/devices/<int:device_id>', methods=['POST'])
def control_device(device_id):
"""控制设备的开关状态"""
data = request.json
state = data.get('state')
conn = sqlite3.connect('smart_home.db')
c = conn.cursor()
c.execute("UPDATE device_status SET state=? WHERE id=?", (state, device_id)) # 更新设备状态
conn.commit()
conn.close()
return jsonify({"message": "设备状态已更新"}), 200 # 返回成功消息
@app.route('/weather', methods=['GET'])
def get_weather():
"""获取天气信息"""
weather_data = get_weather_data() # 调用获取天气数据的函数
if weather_data:
return jsonify(weather_data) # 返回天气数据
else:
return jsonify({"error": "无法获取天气数据"}), 500
if __name__ == "__main__":
init_db() # 初始化数据库
app.run(host='0.0.0.0', port=5000) # 启动Flask应用
3. 前端代码
3.1 HTML文件(index.html)
首先,创建一个HTML文件来构建用户界面:
html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>智能家居控制中心</title>
<link rel="stylesheet" href="style.css"> <!-- 引入CSS样式 -->
<script src="script.js" defer></script> <!-- 引入JavaScript文件 -->
</head>
<body>
<h1>智能家居控制中心</h1>
<h2>设备状态</h2>
<div id="device-list"></div> <!-- 显示设备状态的区域 -->
<button id="refresh-button">刷新设备状态</button> <!-- 刷新按钮 -->
<h2>当前天气</h2>
<div id="weather-info"></div> <!-- 显示天气信息的区域 -->
<script>
// 页面加载时自动获取设备状态和天气信息
window.onload = function() {
fetchDevices();
fetchWeather();
};
</script>
</body>
</html>
代码说明:
- 创建基本的HTML结构,包含标题和显示设备状态的区域。
- 添加一个按钮用于手动刷新设备状态和天气信息。
- 在页面加载时,自动调用
fetchDevices()
和fetchWeather()
函数以获取设备状态和天气信息。
3.2 CSS样式(style.css)
接下来,创建一个CSS文件来美化页面。
css
body {
font-family: Arial, sans-serif; /* 设置字体 */
margin: 0;
padding: 20px;
background-color: #f4f4f4; /* 背景颜色 */
}
h1 {
color: #333; /* 标题颜色 */
}
h2 {
color: #555; /* 副标题颜色 */
}
#device-list {
margin-top: 20px; /* 设备列表的间距 */
}
.device {
background-color: #fff; /* 每个设备的背景颜色 */
border: 1px solid #ccc; /* 边框样式 */
border-radius: 5px;
padding: 10px;
margin-bottom: 10px; /* 设备之间的间距 */
}
button {
padding: 10px 15px; /* 按钮内边距 */
background-color: #007bff; /* 按钮背景颜色 */
border: none;
color: white; /* 按钮文本颜色 */
border-radius: 5px; /* 按钮圆角 */
cursor: pointer; /* 鼠标悬停时显示为手型 */
}
button:hover {
background-color: #0056b3; /* 鼠标悬停时的背景颜色 */
}
代码说明:
- 使用简单的CSS样式来美化页面,包括背景颜色、字体、边框和按钮样式。
.device
类用于设置设备状态显示的样式。
3.3 JavaScript代码(script.js)
javascript
const apiUrl = "http://<树莓派IP>:5000/devices"; // 替换为你的树莓派IP地址
const weatherApiUrl = "http://<树莓派IP>:5000/weather"; // 替换为你的树莓派IP地址
// 页面加载时自动获取设备状态和天气信息
window.onload = function() {
fetchDevices();
fetchWeather();
};
// 获取设备状态的函数
async function fetchDevices() {
try {
const response = await fetch(apiUrl); // 发起GET请求获取设备状态
const devices = await response.json(); // 解析JSON响应
displayDevices(devices); // 显示设备状态
} catch (error) {
console.error("获取设备状态失败:", error); // 错误处理
}
}
// 显示设备状态的函数
function displayDevices(devices) {
const deviceList = document.getElementById("device-list");
deviceList.innerHTML = ""; // 清空之前的内容
devices.forEach(device => {
const deviceDiv = document.createElement("div");
deviceDiv.className = "device"; // 设置设备样式
deviceDiv.innerHTML = `
<h2>${device[1]}</h2> <!-- 设备名称 -->
<p>状态: ${device[2] === 1 ? "开" : "关"}</p> <!-- 设备状态 -->
<button onclick="toggleDevice(${device[0]}, ${device[2]})">
${device[2] === 1 ? "关闭" : "打开"}
</button>
`; // 创建设备状态和控制按钮
deviceList.appendChild(deviceDiv); // 添加到设备列表中
});
}
// 切换设备状态的函数
async function toggleDevice(deviceId, currentState) {
const newState = currentState === 1 ? 0 : 1; // 切换状态
try {
const response = await fetch(`http://<树莓派IP>:5000/devices/${deviceId}`, { // 替换为你的树莓派IP
method: 'POST', // 指定请求方法为POST
headers: {
'Content-Type': 'application/json', // 设置请求头为JSON格式
},
body: JSON.stringify({ state: newState }) // 将新状态转为JSON格式
});
if (response.ok) {
fetchDevices(); // 更新设备状态
alert("设备状态已更新!"); // 提示用户设备状态已更新
} else {
alert("设备状态更新失败!"); // 提示用户更新失败
}
} catch (error) {
console.error("更新设备状态时出错:", error); // 错误处理
}
}
// 获取天气信息的函数
async function fetchWeather() {
try {
const response = await fetch(weatherApiUrl); // 发起GET请求获取天气信息
const weatherData = await response.json(); // 解析JSON响应
displayWeather(weatherData); // 显示天气信息
} catch (error) {
console.error("获取天气信息失败:", error); // 错误处理
}
}
// 显示天气信息的函数
function displayWeather(weatherData) {
const weatherInfo = document.getElementById("weather-info");
weatherInfo.innerHTML = ""; // 清空之前的内容
// 显示天气信息
if (weatherData) {
weatherInfo.innerHTML = `
<p>温度: ${weatherData.temperature} °C</p>
<p>天气: ${weatherData.weather}</p>
<p>湿度: ${weatherData.humidity} %</p>
`;
} else {
weatherInfo.innerHTML = "<p>无法获取天气信息</p>"; // 显示错误信息
}
}
// 刷新设备状态的按钮功能
document.getElementById("refresh-button").addEventListener("click", function() {
fetchDevices(); // 刷新设备状态
fetchWeather(); // 刷新天气信息
});
代码说明:
-
fetchDevices()
:- 使用
fetch
API发起GET请求,获取当前设备的状态数据。 - 如果请求成功,解析JSON格式的响应并调用
displayDevices(devices)
函数将设备信息显示在页面上。 - 如果请求失败,捕获并打印错误信息到控制台。
- 使用
-
displayDevices(devices)
:- 接收设备列表作为参数,清空之前显示的内容。
- 遍历每个设备,创建一个新的
div
元素来展示设备的名称、状态(开/关)和对应的控制按钮。 - 按钮的
onclick
事件绑定到toggleDevice(deviceId, currentState)
函数,以便用户可以通过点击按钮来切换设备的状态。 - 将每个设备的
div
元素添加到设备列表中。
-
toggleDevice(deviceId, currentState)
:- 接收设备ID和当前状态作为参数,计算出新的状态(如果当前是开,则切换为关,反之亦然)。
- 使用
fetch
API发起POST请求,向后端发送更新设备状态的请求。 - 请求的URL为
http://<树莓派IP>:5000/devices/${deviceId}
,其中deviceId
是要控制的设备的ID。 - 请求体使用JSON格式,包含新的状态。
- 如果请求成功,调用
fetchDevices()
刷新设备状态并显示更新后的信息,同时弹出提示框通知用户设备状态已更新。 - 如果请求失败,则弹出提示框通知用户更新失败,并在控制台打印错误信息。
-
fetchWeather()
:- 使用
fetch
API发起GET请求,获取天气信息。 - 如果请求成功,解析JSON格式的响应并调用
displayWeather(weatherData)
函数将天气信息显示在页面上。 - 如果请求失败,捕获并打印错误信息到控制台。
- 使用
-
displayWeather(weatherData)
:- 接收天气数据作为参数,清空之前显示的内容。
- 如果天气数据存在,则更新天气信息的显示,包括温度、天气状况和湿度。
- 如果没有获取到天气数据,则显示相应的错误信息。
-
刷新按钮功能:
- 为"刷新设备状态"按钮添加事件监听器,当用户点击按钮时,会调用
fetchDevices()
和fetchWeather()
函数,以获取最新的设备状态和天气信息,确保用户界面始终显示最新的数据。
- 为"刷新设备状态"按钮添加事件监听器,当用户点击按钮时,会调用
五、项目总结
通过本项目,我们成功搭建了一个基于树莓派的AI接入问心一言智能家居中控系统。该系统能够实时获取环境数据,并根据获取的天气信息自动控制家居设备。同时,用户可以通过可视化界面方便地查看设备状态和手动控制设备开关。
技术栈总结
- 硬件:树莓派、DHT11传感器、继电器模块
- 后端:Flask(Python)、SQLite(数据库)
- 前端:HTML、CSS、JavaScript
- 通信协议:MQTT(用于传感器和设备的通信)
- AI接口:问心一言API(获取天气和空气质量信息)