51、C++项目实战:智能家居控制系统

智能家居,说白了就是让家里的设备学会「听话」。灯、空调、窗帘、门锁……这些设备如果能统一管理,生活能省不少事。我几年前接过一个项目,客户想把老宅子改造成智能屋,结果发现设备来自五六个品牌,协议五花八门。嗯,那时候我就意识到——一个灵活的设备抽象层,比什么都重要

今天这个实战项目,我们就来搭建一个轻量级的智能家居控制系统。它不依赖任何第三方库,纯 C++ 实现,核心思路是:设备抽象 + 命令模式 + 简单调度

系统整体架构

先看看我们要做什么。整个系统分三层:

  • 设备层:每个设备都是一个独立对象,比如灯、空调、传感器。
  • 控制层:接收用户指令,转换成设备能理解的命令。
  • 调度层:支持定时任务、场景联动(比如「离家模式」一键关所有)。

下面这张图是我画的结构图,你看一眼就明白了:

智能家居控制系统架构 用户 / App / 语音 控制层(命令解析 + 命令分发) Command Pattern 设备层(抽象基类 + 具体设备) 空调 窗帘 传感器 调度层:定时任务 / 场景联动(离家模式、睡眠模式)

设备抽象基类

所有设备都继承自同一个基类。我习惯把公共接口定义得尽量简洁:

class Device {
public:
    virtual ~Device() = default;
    virtual void turnOn() = 0;
    virtual void turnOff() = 0;
    virtual std::string getStatus() const = 0;
    virtual std::string getDeviceName() const = 0;
};

你想想看,如果每个设备都自己定义一套接口,控制层就得写一堆 if-else。有了基类,控制层只需要操作 Device* 指针,多态就搞定了。

小技巧:我建议把 getStatus() 返回 JSON 格式的字符串,这样将来对接 Web 或 App 时,解析起来非常方便。

具体设备实现

拿「智能灯」举个例子。它除了开关,还支持亮度调节和色温控制:

class Light : public Device {
public:
    Light(const std::string& name) : name_(name), on_(false), brightness_(100), colorTemp_(4000) {}
    
    void turnOn() override {
        on_ = true;
        std::cout << name_ << " 已打开\n";
    }
    
    void turnOff() override {
        on_ = false;
        std::cout << name_ << " 已关闭\n";
    }
    
    void setBrightness(int level) {
        if (level < 0 || level > 100) {
            std::cerr << "亮度范围 0-100\n";
            return;
        }
        brightness_ = level;
        std::cout << name_ << " 亮度设为 " << level << "%\n";
    }
    
    void setColorTemp(int kelvin) {
        colorTemp_ = kelvin;
        std::cout << name_ << " 色温设为 " << kelvin << "K\n";
    }
    
    std::string getStatus() const override {
        return "{\"name\":\"" + name_ + "\",\"on\":" + (on_ ? "true" : "false") +
               ",\"brightness\":" + std::to_string(brightness_) +
               ",\"colorTemp\":" + std::to_string(colorTemp_) + "}";
    }
    
    std::string getDeviceName() const override { return name_; }
    
private:
    std::string name_;
    bool on_;
    int brightness_;
    int colorTemp_;
};
注意:我曾经在项目中遇到过亮度值没做范围校验,结果用户传了个 200,灯直接闪了一下然后自动保护断电。所以,所有设备参数一定要做边界检查,别偷懒。

命令模式:让控制更灵活

控制层如果直接调用设备方法,代码会耦合得很紧。我选择用命令模式——每个操作都是一个命令对象:

class Command {
public:
    virtual ~Command() = default;
    virtual void execute() = 0;
    virtual void undo() = 0;
};

class LightOnCommand : public Command {
public:
    explicit LightOnCommand(Light* light) : light_(light) {}
    void execute() override { light_->turnOn(); }
    void undo() override { light_->turnOff(); }
private:
    Light* light_;
};

这样做的好处是什么?你可以把命令存到队列里,支持撤销、重做,甚至批量执行。我记得有个客户要求「一键离家模式」,其实就是把十几个命令打包成一个宏命令:

class MacroCommand : public Command {
public:
    void addCommand(std::unique_ptr<Command> cmd) {
        commands_.push_back(std::move(cmd));
    }
    void execute() override {
        for (auto& cmd : commands_) cmd->execute();
    }
    void undo() override {
        for (auto it = commands_.rbegin(); it != commands_.rend(); ++it)
            (*it)->undo();
    }
private:
    std::vector<std::unique_ptr<Command>> commands_;
};

调度器:定时任务与场景

调度器是整个系统的「大脑」。它维护一个任务列表,每个任务包含触发条件和要执行的命令:

struct ScheduledTask {
    std::chrono::system_clock::time_point triggerTime;
    std::unique_ptr<Command> command;
    bool repeatDaily;  // 是否每天重复
};

class Scheduler {
public:
    void addTask(ScheduledTask task) {
        tasks_.push_back(std::move(task));
    }
    
    void runOnce() {
        auto now = std::chrono::system_clock::now();
        for (auto& task : tasks_) {
            if (task.triggerTime <= now) {
                task.command->execute();
                if (task.repeatDaily) {
                    task.triggerTime += std::chrono::hours(24);
                }
            }
        }
    }
    
private:
    std::vector<ScheduledTask> tasks_;
};
核心思路:调度器不关心命令具体做什么,它只负责「到点了就执行」。这种解耦方式让系统很容易扩展——想加个新场景?写个新命令塞进去就行。

场景联动:让设备协同工作

场景是智能家居的灵魂。比如「睡眠模式」:关灯、关窗帘、空调调到睡眠温度。用我们的系统实现起来很简单:

auto sleepMode = std::make_unique<MacroCommand>();
sleepMode->addCommand(std::make_unique<LightOffCommand>(bedroomLight));
sleepMode->addCommand(std::make_unique<CurtainCloseCommand>(bedroomCurtain));
sleepMode->addCommand(std::make_unique<ACSetTempCommand>(bedroomAC, 26));

// 注册到调度器,每晚22:00执行
ScheduledTask task;
task.triggerTime = getTimeToday(22, 0);
task.command = std::move(sleepMode);
task.repeatDaily = true;
scheduler.addTask(std::move(task));

你可能会问:如果某个设备执行失败了怎么办?嗯,这里要注意——宏命令默认是「尽力而为」的。我建议在实际项目中加入回滚机制,或者至少记录日志。我曾经因为窗帘卡住没关上,结果空调一整晚都在拼命制冷,电费哗哗的。

完整示例:主循环

最后,我们把这些拼起来,跑一个简单的主循环:

int main() {
    Light livingRoomLight("客厅灯");
    Light bedroomLight("卧室灯");
    Curtain livingRoomCurtain("客厅窗帘");
    
    Scheduler scheduler;
    
    // 注册场景
    auto leaveHome = std::make_unique<MacroCommand>();
    leaveHome->addCommand(std::make_unique<LightOffCommand>(&livingRoomLight));
    leaveHome->addCommand(std::make_unique<LightOffCommand>(&bedroomLight));
    leaveHome->addCommand(std::make_unique<CurtainCloseCommand>(&livingRoomCurtain));
    
    // 模拟用户触发
    std::cout << "=== 离家模式 ===\n";
    leaveHome->execute();
    
    // 模拟定时任务
    auto nightLight = std::make_unique<LightOnCommand>(&bedroomLight);
    ScheduledTask task;
    task.triggerTime = std::chrono::system_clock::now() + std::chrono::seconds(2);
    task.command = std::move(nightLight);
    task.repeatDaily = false;
    scheduler.addTask(std::move(task));
    
    std::this_thread::sleep_for(std::chrono::seconds(3));
    scheduler.runOnce();
    
    return 0;
}

输出效果:

=== 离家模式 ===
客厅灯 已关闭
卧室灯 已关闭
客厅窗帘 已关闭
(2秒后)
卧室灯 已打开

总结一下

这个项目虽然小,但五脏俱全。你掌握了:

  • 设备抽象:用基类统一接口,方便扩展新设备
  • 命令模式:把操作封装成对象,支持撤销和宏命令
  • 调度器:定时任务 + 场景联动,让系统自动化

说实话,智能家居的难点从来不是单个设备控制,而是如何优雅地管理一堆设备之间的协作。这个架构你吃透了,以后做物联网后台、机器人控制,思路都是相通的。

代码量不大,我建议你亲手敲一遍。遇到问题?很正常。调试的时候多打点日志,把每个命令的执行结果打印出来,问题很快就定位了。


公众号:蓝海资料掘金营,微信 deep3321