智能喂食器远程控制系统:硬件设计与软件实现

摘要:本文深入讲解智能宠物喂食器的硬件设计、固件开发、云端控制、定时计划、食物识别等完整技术方案。


一、产品需求分析

1.1 核心功能

功能 需求描述 技术实现
定时投喂 支持10组定时计划 RTC + Cron表达式
远程控制 APP一键投喂 MQTT指令
精准称重 ±1g精度 HX711 + 称重传感器
食量统计 日/周/月统计 时序数据库
缺粮提醒 低于阈值告警 红外/超声波检测
语音呼唤 主人录音播放 I2S + 功放
食物识别 干粮/湿粮识别 摄像头 + AI
防卡粮 卡粮检测与处理 电流检测 + 反转

二、硬件设计

2.1 系统框图

复制代码
┌─────────────────────────────────────────────────┐
│                ESP32-S3 主控                     │
│  ┌─────────────────────────────────────────┐    │
│  │ WiFi + BLE + 外设接口                    │    │
│  └─────────────────────────────────────────┘    │
│       │           │           │                  │
│  ┌────┴────┐ ┌────┴────┐ ┌────┴────┐           │
│  │ 电机驱动│ │ 称重模块│ │ 语音模块│           │
│  │ TB6612  │ │ HX711   │ │ MAX98357│           │
│  └────┬────┘ └────┬────┘ └────┬────┘           │
│       │           │           │                  │
│  ┌────┴────┐ ┌────┴────┐ ┌────┴────┐           │
│  │ 直流电机│ │ 称重传感│ │ 喇叭    │           │
│  │ +螺旋杆 │ │ 器      │ │ +麦克风 │           │
│  └─────────┘ └─────────┘ └─────────┘           │
│                                                  │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐           │
│  │ 红外检测│ │ 温湿度  │ │ 摄像头  │           │
│  │ 缺粮    │ │ 传感器  │ │ OV2640  │           │
│  └─────────┘ └─────────┘ └─────────┘           │
└─────────────────────────────────────────────────┘

2.2 电机控制电路

c 复制代码
// TB6612电机驱动
#define MOTOR_IN1   GPIO_NUM_1
#define MOTOR_IN2   GPIO_NUM_2
#define MOTOR_PWM   GPIO_NUM_3

void motor_init(void) {
    gpio_config_t io_conf = {
        .pin_bit_mask = (1ULL << MOTOR_IN1) | (1ULL << MOTOR_IN2),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE
    };
    gpio_config(&io_conf);
    
    // PWM配置
    ledc_timer_config_t timer_conf = {
        .speed_mode = LEDC_LOW_SPEED_MODE,
        .timer_num = LEDC_TIMER_0,
        .duty_resolution = LEDC_TIMER_10_BIT,
        .freq_hz = 1000,
        .clk_cfg = LEDC_AUTO_CLK
    };
    ledc_timer_config(&timer_conf);
    
    ledc_channel_config_t channel_conf = {
        .speed_mode = LEDC_LOW_SPEED_MODE,
        .channel = LEDC_CHANNEL_0,
        .timer_sel = LEDC_TIMER_0,
        .gpio_num = MOTOR_PWM,
        .duty = 0,
        .hpoint = 0
    };
    ledc_channel_config(&channel_conf);
}

void motor_set_speed(int speed) {
    // speed: -100 ~ 100
    if (speed > 0) {
        gpio_set_level(MOTOR_IN1, 1);
        gpio_set_level(MOTOR_IN2, 0);
    } else if (speed < 0) {
        gpio_set_level(MOTOR_IN1, 0);
        gpio_set_level(MOTOR_IN2, 1);
        speed = -speed;
    } else {
        gpio_set_level(MOTOR_IN1, 0);
        gpio_set_level(MOTOR_IN2, 0);
    }
    
    uint32_t duty = speed * 1023 / 100;
    ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty);
    ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
}

2.3 称重传感器

c 复制代码
// HX711称重模块
#define HX711_SCK   GPIO_NUM_4
#define HX711_DOUT  GPIO_NUM_5

typedef struct {
    float scale_factor;     // 比例系数
    long offset;            // 零点偏移
    float weight;           // 当前重量(g)
    float last_weight;      // 上次重量
} hx711_t;

void hx711_init(hx711_t *hx) {
    gpio_set_direction(HX711_SCK, GPIO_MODE_OUTPUT);
    gpio_set_direction(HX711_DOUT, GPIO_MODE_INPUT);
    hx->scale_factor = 1.0;
    hx->offset = 0;
}

long hx711_read_raw(void) {
    // 等待数据就绪
    while (gpio_get_level(HX711_DOUT) == 1) {
        vTaskDelay(1);
    }
    
    long data = 0;
    for (int i = 0; i < 24; i++) {
        gpio_set_level(HX711_SCK, 1);
        ets_delay_us(1);
        data = (data << 1) | gpio_get_level(HX711_DOUT);
        gpio_set_level(HX711_SCK, 0);
        ets_delay_us(1);
    }
    
    // 第25个脉冲设置增益
    gpio_set_level(HX711_SCK, 1);
    ets_delay_us(1);
    gpio_set_level(HX711_SCK, 0);
    
    // 转换为有符号数
    if (data & 0x800000) {
        data |= 0xFF000000;
    }
    
    return data;
}

float hx711_get_weight(hx711_t *hx, int times) {
    long sum = 0;
    for (int i = 0; i < times; i++) {
        sum += hx711_read_raw();
    }
    long avg = sum / times;
    
    hx->weight = (avg - hx->offset) / hx->scale_factor;
    return hx->weight;
}

void hx711_calibrate(hx711_t *hx, float known_weight) {
    // 1. 空载时记录零点
    printf("请移除所有物品...\n");
    vTaskDelay(3000 / portTICK_PERIOD_MS);
    hx->offset = hx711_read_raw();
    printf("零点偏移: %ld\n", hx->offset);
    
    // 2. 放置已知重量物品
    printf("请放置 %.1fg 物品...\n", known_weight);
    vTaskDelay(3000 / portTICK_PERIOD_MS);
    long reading = hx711_read_raw();
    
    // 3. 计算比例系数
    hx->scale_factor = (reading - hx->offset) / known_weight;
    printf("比例系数: %.2f\n", hx->scale_factor);
}

三、定时投喂系统

3.1 定时任务管理

c 复制代码
typedef struct {
    uint8_t hour;
    uint8_t minute;
    uint8_t weekdays;      // 位掩码:bit0=周日, bit1=周一...
    float amount_g;         // 投喂量(克)
    bool enabled;
    char voice_msg[64];     // 语音消息
} feeding_schedule_t;

typedef struct {
    feeding_schedule_t schedules[MAX_SCHEDULES];
    int count;
    time_t last_feed_time;
    float daily_total;
} feeding_manager_t;

// 检查是否需要投喂
bool check_schedule(feeding_manager_t *mgr) {
    time_t now = time(NULL);
    struct tm *t = localtime(&now);
    
    for (int i = 0; i < mgr->count; i++) {
        feeding_schedule_t *s = &mgr->schedules[i];
        if (!s->enabled) continue;
        
        // 检查时间
        if (t->tm_hour != s->hour || t->tm_min != s->minute) continue;
        
        // 检查星期
        uint8_t weekday_bit = 1 << t->tm_wday;
        if (!(s->weekdays & weekday_bit)) continue;
        
        // 检查是否已投喂(防止重复)
        if (now - mgr->last_feed_time < 60) continue;
        
        // 执行投喂
        execute_feeding(s->amount_g, s->voice_msg);
        mgr->last_feed_time = now;
        mgr->daily_total += s->amount_g;
        
        return true;
    }
    return false;
}

// Cron表达式解析(高级版本)
typedef struct {
    uint8_t minute[60];     // 0-59
    uint8_t hour[24];       // 0-23
    uint8_t day_of_month[32]; // 1-31
    uint8_t month[12];      // 0-11
    uint8_t day_of_week[7]; // 0-6 (0=周日)
} cron_expr_t;

bool cron_match(cron_expr_t *cron, struct tm *t) {
    return cron->minute[t->tm_min] &&
           cron->hour[t->tm_hour] &&
           cron->day_of_month[t->tm_mday] &&
           cron->month[t->tm_mon] &&
           cron->day_of_week[t->tm_wday];
}

3.2 精准投喂控制

c 复制代码
typedef struct {
    float target_amount;    // 目标投喂量
    float current_amount;   // 当前已投喂
    float start_weight;     // 开始时食盆重量
    bool in_progress;       // 投喂进行中
    uint32_t start_time;    // 开始时间
    uint32_t timeout_ms;    // 超时时间
} feeding_process_t;

void execute_feeding(float amount_g, const char *voice_msg) {
    feeding_process_t process = {
        .target_amount = amount_g,
        .current_amount = 0,
        .start_weight = hx711_get_weight(&hx, 10),
        .in_progress = true,
        .start_time = get_timestamp(),
        .timeout_ms = 30000  // 30秒超时
    };
    
    // 播放语音
    if (voice_msg[0] != '\0') {
        play_voice(voice_msg);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    
    // 启动电机
    motor_set_speed(80);
    
    // 监控投喂量
    while (process.in_progress) {
        float current_weight = hx711_get_weight(&hx, 5);
        process.current_amount = current_weight - process.start_weight;
        
        // 达到目标量
        if (process.current_amount >= process.target_amount) {
            motor_set_speed(0);
            process.in_progress = false;
            break;
        }
        
        // 超时检测
        if (get_timestamp() - process.start_time > process.timeout_ms) {
            motor_set_speed(0);
            report_error(ERROR_FEEDING_TIMEOUT);
            process.in_progress = false;
            break;
        }
        
        // 卡粮检测
        if (detect_jam()) {
            motor_set_speed(-30);  // 反转
            vTaskDelay(500 / portTICK_PERIOD_MS);
            motor_set_speed(80);   // 重新正转
        }
        
        vTaskDelay(100 / portTICK_PERIOD_MS);
    }
    
    // 记录投喂日志
    log_feeding(process.current_amount, process.start_time);
}

四、食物识别

4.1 摄像头方案

c 复制代码
// OV2640摄像头初始化
camera_config_t camera_config = {
    .pin_pwdn = CAM_PIN_PWDN,
    .pin_reset = CAM_PIN_RESET,
    .pin_xclk = CAM_PIN_XCLK,
    .pin_sccb_sda = CAM_PIN_SIOD,
    .pin_sccb_scl = CAM_PIN_SIOC,
    .pin_d7 = CAM_PIN_D7,
    .pin_d6 = CAM_PIN_D6,
    .pin_d5 = CAM_PIN_D5,
    .pin_d4 = CAM_PIN_D4,
    .pin_d3 = CAM_PIN_D3,
    .pin_d2 = CAM_PIN_D2,
    .pin_d1 = CAM_PIN_D1,
    .pin_d0 = CAM_PIN_D0,
    .pin_vsync = CAM_PIN_VSYNC,
    .pin_href = CAM_PIN_HREF,
    .pin_pclk = CAM_PIN_PCLK,
    .xclk_freq_hz = 20000000,
    .pixel_format = PIXFORMAT_JPEG,
    .frame_size = FRAMESIZE_QVGA,
    .jpeg_quality = 12,
    .fb_count = 2,
    .grab_mode = CAMERA_GRAB_LATEST
};

esp_err_t camera_init() {
    return esp_camera_init(&camera_config);
}

4.2 食物类型识别

python 复制代码
# 食物识别模型
class FoodTypeClassifier:
    def __init__(self, model_path):
        self.model = tflite.Interpreter(model_path)
        self.model.allocate_tensors()
        
        self.labels = ['dry_food', 'wet_food', 'treats', 'empty', 'unknown']
    
    def classify(self, image):
        # 预处理
        input_details = self.model.get_input_details()
        height, width = input_details[0]['shape'][1:3]
        
        resized = cv2.resize(image, (width, height))
        normalized = resized / 255.0
        input_data = np.expand_dims(normalized, axis=0).astype(np.float32)
        
        # 推理
        self.model.set_tensor(input_details[0]['index'], input_data)
        self.model.invoke()
        
        output_details = self.model.get_output_details()
        output = self.model.get_tensor(output_details[0]['index'])
        
        # 解析结果
        class_idx = np.argmax(output[0])
      
相关推荐
TMT星球39 分钟前
虎鲸文娱春苗编剧计划全面升级,已孵化20个剧本进入前期开发
大数据·人工智能
奔跑中的小象7 小时前
统信UOS + 天数AI卡部署SGLang服务手册
人工智能·uos·sglang·天数智芯
DevSecOps选型指南7 小时前
中国版Mythos,为何是悬镜安全灵脉CodeAI?
人工智能·安全
Shockang7 小时前
LangGraph 状态机实战
人工智能
刹那芳华19928 小时前
循环神经网络的从零开始实现(RNN)
人工智能·rnn·深度学习
阿童木写作8 小时前
跨境图片翻译工具多合一,批量图片视频字幕翻译加智能抠图
人工智能·python·音视频·语音识别
科技之门8 小时前
AI3D从建模到贴图、绑骨和动画的完整流程怎么做?V2Fun完整工作流指南
人工智能·3d·贴图
宇宙第一小趴菜8 小时前
二、机器学习的应用领域和发展史
人工智能·机器学习
前端开发江鸟8 小时前
我写过 MCP Server,却一直以为 MCP 只有 Tool
人工智能
天国梦8 小时前
自习室智能化升级避坑指南:天学网AI智习室方案实测与选型建议
大数据·人工智能