智能家居项目
本文将通过一个完整的智能家居项目,演示如何从零搭建物联网系统,涵盖硬件选型、软件开发、云端部署等完整流程。
项目概述
项目目标
搭建一个完整的智能家居系统,实现以下功能:
- 温湿度监测
- 智能照明控制
- 门窗状态检测
- 远程控制与自动化
- 数据可视化
系统架构
┌─────────────────────────────────────────────────────────┐
│ 用户层 │
│ 手机 App │ Web 控制台 │ 语音助手 │
├─────────────────────────────────────────────────────────┤
│ 云平台 │
│ 设备管理 │ 数据存储 │ 规则引擎 │ 场景联动 │
├─────────────────────────────────────────────────────────┤
│ 网关层 │
│ 智能网关(ESP32/树莓派) │
├─────────────────────────────────────────────────────────┤
│ 设备层 │
│ 温湿度传感器 │ 智能灯 │ 门窗传感器 │ 智能插座 │
└─────────────────────────────────────────────────────────┘
硬件准备
设备清单
| 设备 | 型号 | 数量 | 功能 |
|---|---|---|---|
| 主控板 | ESP32 DevKit | 1 | 智能网关 |
| 温湿度传感器 | DHT22 | 2 | 环境监测 |
| 继电器模块 | 5V 继电器 | 2 | 灯光控制 |
| 门窗传感器 | 干簧管 | 2 | 门窗检测 |
| LED 灯带 | WS2812B | 1 | 智能照明 |
| 电源 | 5V 2A | 1 | 供电 |
接线图
ESP32 引脚分配:
┌─────────────────────────────────────┐
│ GPIO 4 ─── DHT22 数据线 │
│ GPIO 5 ─── 继电器 1 │
│ GPIO 18 ─── 继电器 2 │
│ GPIO 19 ─── 干簧管 1 │
│ GPIO 21 ─── 干簧管 2 │
│ GPIO 23 ─── WS2812B 数据线 │
│ 3.3V ─── 传感器 VCC │
│ GND ─── 公共地 │
└─────────────────────────────────────┘
设备端开发
项目结构
smart-home-device/
├── src/
│ ├── main.c
│ ├── sensors.c
│ ├── actuators.c
│ ├── mqtt_client.c
│ └── config.h
├── include/
│ ├── sensors.h
│ ├── actuators.h
│ └── mqtt_client.h
├── CMakeLists.txt
└── sdkconfig
主程序
// main.c
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "sensors.h"
#include "actuators.h"
#include "mqtt_client.h"
static const char *TAG = "smart-home";
void app_main(void)
{
ESP_LOGI(TAG, "Smart Home Device Starting...");
// 初始化 WiFi
wifi_init();
// 初始化传感器
sensors_init();
// 初始化执行器
actuators_init();
// 初始化 MQTT
mqtt_app_start();
// 创建任务
xTaskCreate(sensor_task, "sensor_task", 4096, NULL, 5, NULL);
xTaskCreate(actuator_task, "actuator_task", 4096, NULL, 5, NULL);
}
传感器模块
// sensors.c
#include "sensors.h"
#include "driver/gpio.h"
#include "dht.h"
#define DHT_GPIO 4
#define DOOR_GPIO_1 19
#define DOOR_GPIO_2 21
static float temperature = 0;
static float humidity = 0;
static bool door_state_1 = false;
static bool door_state_2 = false;
void sensors_init(void)
{
// 配置干簧管输入
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << DOOR_GPIO_1) | (1ULL << DOOR_GPIO_2),
.mode = GPIO_MODE_INPUT,
.pull_up_en = 1,
};
gpio_config(&io_conf);
}
void sensor_task(void *pvParameters)
{
while (1) {
// 读取温湿度
if (dht_read_float_data(DHT_TYPE_AM2301, DHT_GPIO, &humidity, &temperature) == ESP_OK) {
ESP_LOGI("SENSOR", "Temp: %.1f°C, Humidity: %.1f%%", temperature, humidity);
}
// 读取门窗状态
door_state_1 = gpio_get_level(DOOR_GPIO_1);
door_state_2 = gpio_get_level(DOOR_GPIO_2);
// 发布数据
publish_sensor_data(temperature, humidity, door_state_1, door_state_2);
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
}
执行器模块
// actuators.c
#include "actuators.h"
#include "driver/gpio.h"
#include "led_strip.h"
#define RELAY_GPIO_1 5
#define RELAY_GPIO_2 18
#define LED_GPIO 23
static led_strip_handle_t led_strip;
void actuators_init(void)
{
// 配置继电器
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << RELAY_GPIO_1) | (1ULL << RELAY_GPIO_2),
.mode = GPIO_MODE_OUTPUT,
};
gpio_config(&io_conf);
// 配置 LED 灯带
led_strip_config_t strip_config = {
.strip_gpio_num = LED_GPIO,
.max_leds = 30,
};
led_strip_new_rmt_device(&strip_config, &led_strip);
}
void set_light(int light_id, bool state)
{
int gpio = (light_id == 1) ? RELAY_GPIO_1 : RELAY_GPIO_2;
gpio_set_level(gpio, state ? 1 : 0);
ESP_LOGI("ACTUATOR", "Light %d: %s", light_id, state ? "ON" : "OFF");
}
void set_led_color(uint8_t r, uint8_t g, uint8_t b)
{
for (int i = 0; i < 30; i++) {
led_strip_set_pixel(led_strip, i, r, g, b);
}
led_strip_refresh(led_strip);
}
MQTT 客户端
// mqtt_client.c
#include "mqtt_client.h"
#include "esp_mqtt.h"
#include "cJSON.h"
#define MQTT_BROKER "broker.example.com"
#define MQTT_PORT 1883
#define MQTT_TOPIC_SUB "home/+/command"
#define MQTT_TOPIC_PUB "home/sensors"
static esp_mqtt_client_handle_t mqtt_client;
void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
esp_mqtt_event_handle_t event = event_data;
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
esp_mqtt_client_subscribe(mqtt_client, MQTT_TOPIC_SUB, 0);
break;
case MQTT_EVENT_DATA:
handle_command(event->topic, event->data, event->data_len);
break;
default:
break;
}
}
void mqtt_app_start(void)
{
esp_mqtt_client_config_t mqtt_cfg = {
.broker.address.uri = "mqtt://" MQTT_BROKER ":" STRINGIFY(MQTT_PORT),
.credentials.client_id = "smart-home-device",
};
mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(mqtt_client);
}
void publish_sensor_data(float temp, float humi, bool door1, bool door2)
{
cJSON *root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "temperature", temp);
cJSON_AddNumberToObject(root, "humidity", humi);
cJSON_AddBoolToObject(root, "door1", door1);
cJSON_AddBoolToObject(root, "door2", door2);
char *json = cJSON_Print(root);
esp_mqtt_client_publish(mqtt_client, MQTT_TOPIC_PUB, json, 0, 1, 0);
cJSON_Delete(root);
free(json);
}
void handle_command(const char *topic, const char *data, int len)
{
cJSON *root = cJSON_ParseWithLength(data, len);
if (strstr(topic, "light")) {
int light_id = cJSON_GetObjectItem(root, "id")->valueint;
bool state = cJSON_GetObjectItem(root, "state")->valueint;
set_light(light_id, state);
}
cJSON_Delete(root);
}
云端开发
后端服务
# main.py
from fastapi import FastAPI, WebSocket
from fastapi.middleware.cors import CORSMiddleware
import aiomqtt
import asyncio
import json
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# 存储最新数据
latest_data = {}
async def mqtt_listener():
async with aiomqtt.Client("broker.example.com") as client:
await client.subscribe("home/#")
async for message in client.messages:
topic = message.topic.value
payload = json.loads(message.payload)
if "sensors" in topic:
latest_data.update(payload)
await broadcast_to_websockets(payload)
# WebSocket 连接管理
websocket_connections = []
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
websocket_connections.append(websocket)
try:
while True:
data = await websocket.receive_text()
# 处理客户端命令
command = json.loads(data)
await send_to_device(command)
except:
websocket_connections.remove(websocket)
async def broadcast_to_websockets(data):
for ws in websocket_connections:
await ws.send_json(data)
# REST API
@app.get("/api/sensors")
async def get_sensors():
return latest_data
@app.post("/api/lights/{light_id}")
async def control_light(light_id: int, state: bool):
command = {"id": light_id, "state": state}
await send_to_device(command)
return {"status": "ok"}
前端界面
<!DOCTYPE html>
<html>
<head>
<title>智能家居控制台</title>
<script src="https://cdn.jsdelivr.net/npm/vue@3"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5"></script>
</head>
<body>
<div id="app">
<div class="dashboard">
<div class="sensor-card">
<h3>温度</h3>
<div class="value">{{ sensors.temperature }}°C</div>
</div>
<div class="sensor-card">
<h3>湿度</h3>
<div class="value">{{ sensors.humidity }}%</div>
</div>
<div class="sensor-card">
<h3>门状态</h3>
<div class="value">{{ sensors.door1 ? '开启' : '关闭' }}</div>
</div>
</div>
<div class="control-panel">
<div class="light-control" v-for="i in 2" :key="i">
<h3>灯光 {{ i }}</h3>
<button @click="toggleLight(i)">
{{ lights[i] ? '关闭' : '开启' }}
</button>
</div>
</div>
<div id="chart" style="width: 100%; height: 400px;"></div>
</div>
<script>
const app = Vue.createApp({
data() {
return {
sensors: {},
lights: {},
ws: null,
chart: null,
chartData: []
}
},
mounted() {
this.connectWebSocket();
this.initChart();
},
methods: {
connectWebSocket() {
this.ws = new WebSocket('ws://localhost:8000/ws');
this.ws.onmessage = (event) => {
this.sensors = JSON.parse(event.data);
this.updateChart();
};
},
async toggleLight(id) {
const state = !this.lights[id];
await fetch(`/api/lights/${id}`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({state})
});
this.lights[id] = state;
},
initChart() {
this.chart = echarts.init(document.getElementById('chart'));
this.chart.setOption({
title: {text: '温湿度趋势'},
xAxis: {type: 'category'},
yAxis: {type: 'value'},
series: [
{name: '温度', type: 'line', data: []},
{name: '湿度', type: 'line', data: []}
]
});
},
updateChart() {
this.chartData.push({
time: new Date().toLocaleTimeString(),
temp: this.sensors.temperature,
humi: this.sensors.humidity
});
// 保留最近 50 条数据
if (this.chartData.length > 50) {
this.chartData.shift();
}
this.chart.setOption({
xAxis: {data: this.chartData.map(d => d.time)},
series: [
{data: this.chartData.map(d => d.temp)},
{data: this.chartData.map(d => d.humi)}
]
});
}
}
});
app.mount('#app');
</script>
</body>
</html>
场景联动
自动化规则
# rules.py
class AutomationEngine:
def __init__(self):
self.rules = []
def add_rule(self, name, trigger, actions):
self.rules.append({
'name': name,
'trigger': trigger,
'actions': actions
})
def evaluate(self, data):
for rule in self.rules:
if rule['trigger'](data):
for action in rule['actions']:
action(data)
engine = AutomationEngine()
# 规则1:温度超过28度开灯
engine.add_rule(
name='high_temp_light',
trigger=lambda d: d.get('temperature', 0) > 28,
actions=[lambda d: control_light(1, True)]
)
# 规则2:门打开时开灯
engine.add_rule(
name='door_open_light',
trigger=lambda d: d.get('door1') == True,
actions=[lambda d: control_light(1, True)]
)
部署上线
Docker Compose
version: '3'
services:
mqtt-broker:
image: eclipse-mosquitto:2
ports:
- "1883:1883"
api-server:
build: ./backend
ports:
- "8000:8000"
depends_on:
- mqtt-broker
web:
build: ./frontend
ports:
- "80:80"
depends_on:
- api-server
小结
本项目演示了完整的智能家居系统开发流程,包括硬件选型、设备端开发、云端开发和前端界面。通过这个项目,你可以掌握物联网系统的完整开发流程,并将所学知识应用到实际项目中。
下一步,我们将学习工业物联网项目,了解工业场景的特殊需求和解决方案。