打印
[应用相关]

基于STM32F103+涂鸦三明治的宠物自动喂食器

[复制链接]
手机看帖
扫描二维码
随时随地手机跟帖
41
// 串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
  uint8_t ucTemp;
        if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
        {               
                ucTemp = USART_ReceiveData(DEBUG_USARTx);
                uart_receive_input(ucTemp);   
        }        
        USART_ClearFlag(USART1,USART_FLAG_RXNE);        
}

使用特权

评论回复
42
高级安全大使|  楼主 | 2022-2-28 23:10 | 只看该作者
5.在主函数的while(1) 循环后调用 mcu_api.c 文件内的 wifi_uart_service() 函数。该函数用于wifi串口数据处理服务,同时维持心跳。此函数无需任何判断条件,直接调用。在使用该函数时最好不要关闭总中断或串口中断,防止数据丢失,如必要,尽可能短的时间关闭。

使用特权

评论回复
43
高级安全大使|  楼主 | 2022-2-28 23:11 | 只看该作者
至此,SDK移植便已完成,此时可将单片机的串口接到电脑进行调试。此时调试助手选择模组模拟。

为了便于观察,我们在protocol.c文件中将all_data_update() 函数中的所有DP点上传函数打开,默认为0。

使用特权

评论回复
44
高级安全大使|  楼主 | 2022-2-28 23:11 | 只看该作者
此时MCU接到调试助手,打开串口,添加DP点文件,启动调试,便会看到所有DP点的上报。

使用特权

评论回复
45
高级安全大使|  楼主 | 2022-2-28 23:13 | 只看该作者

使用特权

评论回复
46
高级安全大使|  楼主 | 2022-2-28 23:13 | 只看该作者
进入DP CMD添加一个开启小夜灯的指令并下发,便可看到模组成功接收,说明MCU的SDK移植成功。

使用特权

评论回复
47
高级安全大使|  楼主 | 2022-2-28 23:15 | 只看该作者
到此为止,一个完整的自动喂食器的MCU工程便已搭建完成,后续我们只需要解析wifi模组下发的消息,并进行相应的外设控制便可。当然,每次MCU完成动作后也要上传数据给模组。完成服务器和手机APP端的数据刷新。


使用特权

评论回复
48
高级安全大使|  楼主 | 2022-2-28 23:15 | 只看该作者
完善功能
添加配网功能及指示灯函数
前面的准备工作完成后我们需要添加功能,从最重要的配网开始吧,总不能每次使用都插上电脑用助手配网呀,那多不方便,一键上云才是王道。其实语音也可以,嘻嘻嘻,而且音色也不错,是个萌妹子的声音。跑题了,语音功能容后考虑,因为我觉得加个语音功能我家的主子会炸毛,可能会有吃饭恐惧症。
配网有两种模式AP配网和smart配网,这里仅介绍smart类型。
配网指令有两个函数可以实现:mcu_reset_wifi() 和 mcu_set_wifi_mode()。通常在按键触发配网后,在按键处理函数中调用。
mcu_reset_wifi()调用后复位 Wi-Fi 模组,复位后之前的配网信息全部清除。mcu_reset_wifi() 每调用一次,Wi-Fi 模块即在 AP 和 Smart 之间切换一次配网模式。

mcu_set_wifi_mode()参数为SMART_CONFIG和AP_CONFIG。调用后清除配网信息,进入 Smart 模式或者 AP 模式。

通常在 while(1) 调用 mcu_get_wifi_work_state() 函数获取 Wi-Fi 状态。根据 Wi-Fi 状态,写入相应闪灯的模式。

通过switch()判断进入何种状态,状态可选参数如下:

所以,我们先开始写按键的驱动以及外部中断:
//按键初始化函数
void KEY_Init(void) //IO初始化
{
         GPIO_InitTypeDef GPIO_InitStructure;

         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能PORTA,PORTE时钟

        //初始化 WK_UP-->GPIOA.0          下拉输入
        GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //PA0设置成输入,默认下拉         
        GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0

}
//外部中断0服务程序
void EXTIX_Init(void)
{

           EXTI_InitTypeDef EXTI_InitStructure;
          NVIC_InitTypeDef NVIC_InitStructure;
  KEY_Init();         //        按键端口初始化
   

          RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);        //使能复用功能时钟


   //GPIOA.0          中断线以及中断初始化配置 上升沿触发 PA0  WK_UP
           GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);

          EXTI_InitStructure.EXTI_Line=EXTI_Line0;
        /* EXTI 为中断模式 */
        EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
          EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
        EXTI_InitStructure.EXTI_LineCmd = ENABLE;
          EXTI_Init(&EXTI_InitStructure);                //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器

        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
          NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;                        //使能按键WK_UP所在的外部中断通道
          NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;        //抢占优先级2,
          NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;                                        //子优先级3
          NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                                                //使能外部中断通道
          NVIC_Init(&NVIC_InitStructure);
}

//外部中断0服务程序
void EXTI0_IRQHandler(void)
{
        mcu_reset_wifi();
        mcu_set_wifi_mode(SMART_CONFIG);
        EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位  
}

使用特权

评论回复
49
高级安全大使|  楼主 | 2022-2-28 23:16 | 只看该作者
在外部中断函数0里面添加了mcu_reset_wifi(),复位模组,清除全部配网信息,然后调用mcu_set_wifi_mode()函数,参数添加SMART_CONFIG进入smart模式。然后写两个闪灯函数,用于指示配网状态,Led_Blink_Quick()快闪,用于指示进入smart配网模式,Led_Blink_Slow()慢闪,用于指示进入AP配网模式。

void Led_Blink_Quick(void)
{
        LED2_ON;
        Delay(500000);
        LED2_OFF;
        Delay(500000);

}

void Led_Blink_Slow(void)
{
        LED2_ON;
        Delay(5000000);
        LED2_OFF;
        Delay(5000000);

}

使用特权

评论回复
50
高级安全大使|  楼主 | 2022-2-28 23:17 | 只看该作者
最后在主函数的while(1)中添加配网状态判断函数:
get_wifi_status(mcu_get_wifi_work_state());
void get_wifi_status(unsigned char result)
{
//  #error "请自行完成获取 WIFI 状态结果代码,并删除该行"

    switch(result) {
        case 0:
            //处于 Smart 配置状态,即 LED 快闪
                        Led_Blink_Quick();
        break;
   
        case 1:
                        //处于 AP 配置状态,即 LED 慢闪
                        Led_Blink_Slow();
        break;
        
        case 2:
                        //Wi-Fi 配置完成,正在连接路由器,即 LED 常暗
                        LED2_OFF;
        break;
        
        case 3:
            //WIFI 已配置且连上路由器
                        LED1_ON;
        break;
        
        case 4:
                        LED2_ON;
                        LCD_SetFont(&Font8x16);       
                        LCD_SetColors(RED,BLACK);
                        ILI9341_DisplayStringEx(2*48,0*48,16,16,(uint8_t *)"Wifi connected",0);
        break;
        
        case 5:
            //wifi工作状态6
        break;
      
        case 6:
            //wifi工作状态7
        break;
        
        default:break;
    }
}

使用特权

评论回复
51
高级安全大使|  楼主 | 2022-2-28 23:18 | 只看该作者
现在将MCU与模组通过串口1连接,注意TX与RX反接并共地。按下按键,LED快闪,打开手机APP进行配网,当wifi配置完成灯会熄灭,连接上路由器之后灯会重新点亮,并保持常亮。此过程本人已测试无问题,但是过程比较长不适合贴图,会在视频中展示。

使用特权

评论回复
52
高级安全大使|  楼主 | 2022-2-28 23:18 | 只看该作者
添加小夜灯执行功能
配网完成了,那么怎么执行功能呢?在protocol.c中dp_download_handle()函数可以处理下发的数据。在此函数中会对下发的指令进行归类,我们找到小夜灯的处理

static unsigned char dp_download_light_handle(const unsigned char value[], unsigned short length)
{
    //示例:当前DP类型为BOOL
    unsigned char ret;
    //0:关/1:开
    unsigned char light;
   
    light = mcu_get_dp_download_bool(value,length);
    if(light == 0) {
        //开关关
                LED1_OFF;
                mcu_dp_bool_update(DPID_LIGHT,0);
    }else {
        //开关开
                LED1_ON;
                mcu_dp_bool_update(DPID_LIGHT,1);
    }
  
    //处理完DP数据后应有反馈
    ret = mcu_dp_bool_update(DPID_LIGHT,light);
    if(ret == SUCCESS)
        return SUCCESS;
    else
        return ERROR;
}

使用特权

评论回复
评论
高级安全大使 2022-2-28 23:20 回复TA
这里可以进行小夜灯指令的处理,处理完成之后会上报数据,用于更新APP数据。我们跳转进dp_download_light_handle()函数,此函数中有具体的处理,针对不同的开或关会进入不同的if函数。我们在对LED的端口初始化后便可以将开关灯填入。达到不同下发指令实现开关灯。开关功能类似,不赘述 
53
高级安全大使|  楼主 | 2022-2-28 23:21 | 只看该作者
添加手动喂食执行功能
此次涂鸦提供了H桥驱动板,那么我们直接上一个12V的减速电机,每分钟12转,驱动力大,速度慢,易于控制。将电机接到驱动板的U和V接线柱上,控制口PWM1和PWM2接到单片机PA2和PA3。给PA2和PA3不同的高低电平就可以实现正反转,因为电机本身速度比较慢,就不用软件进行控制速度了。而且因为场景的关系,不用控制正反两个反向,只控制正转和停止。
编写控制函数,参数为投喂量,通过转动时间来控制投喂量,延时时间需要与宠物的饭盒出粮口搭配,不同出粮口修改延时基数。
void motor_concrol_right(uint8_t tim)//电机正转延时后停止
{
        GPIO_SetBits(PWM_PORT,PWM2_GPIO);
        GPIO_ResetBits(PWM_PORT,PWM1_GPIO);
        Delay(10000000*tim);
        GPIO_ResetBits(PWM_PORT,PWM1_GPIO);
        GPIO_ResetBits(PWM_PORT,PWM2_GPIO);   
}


随后在dp_download_handle()函数中的case DPID_MANUAL_FEED:条件下执行该函数,参数由mcu_get_dp_download_value()函数提供,该函数会提取手机下发的出粮量。

使用特权

评论回复
54
高级安全大使|  楼主 | 2022-2-28 23:22 | 只看该作者
触摸屏手动喂食
我在无线的基础上添加屏幕,还是触摸屏,毕竟我们在家的时候还用手机投喂,这不是舍近求远吗?直接手动不是很喵吗?

屏幕太贵?增加成本?占用资源?增加功耗?体积太大?墨水屏就行?

这是我们工程师该考虑的问题吗?帅就完了,成本是资本家考虑的事,反正我又不量产,自己家主子用的,就是要贵,买不起我还做不起吗。

那么开始吧,添加屏幕驱动,这里我直接用的野火的电阻触摸屏例程,额,因为屏幕和开发板都是他们家的,开发板太大了,以后考虑做个板子,现在就将就用了,毕竟我还不一定会做成实物,毕竟我外壳还没有。。。。。

那些LCD和触摸板界面初始化就不贴了,主要说我改动的地方。

首先放一张丑图,没有时间美化,仅实现功能。



左边的“加”“减”用于设定手动喂食量,设定好单次喂食量按喂食就会控制电机进行出粮,开灯和关灯就是打开和关闭小夜灯,主界面会实时显示服务器在线状态和单次喂食量,总喂食量以及小夜灯的状态。

使用特权

评论回复
55
高级安全大使|  楼主 | 2022-2-28 23:23 | 只看该作者
代码首先要修改按钮的初始化
/**
* [url=home.php?mod=space&uid=247401]@brief[/url]  Touch_Button_Init 初始化按钮参数
* @param  无
* @retval 无
*/
void Touch_Button_Init(void)
{
  /*第一列,主要为颜色按钮*/
  button[0].start_x = BUTTON_START_X;
  button[0].start_y = 0;
  button[0].end_x = BUTTON_START_X+COLOR_BLOCK_WIDTH ;
  button[0].end_y = COLOR_BLOCK_HEIGHT;
  button[0].para = 1;
  button[0].touch_flag = 0;  
  button[0].draw_btn = Draw_Num_Button ;
  button[0].btn_command = Command_Select_Meannum ;
  
  button[1].start_x = BUTTON_START_X;
  button[1].start_y = COLOR_BLOCK_HEIGHT;
  button[1].end_x = BUTTON_START_X+COLOR_BLOCK_WIDTH ;
  button[1].end_y = COLOR_BLOCK_HEIGHT*2;
  button[1].para = 2;
  button[1].touch_flag = 0;  
  button[1].draw_btn = Draw_Num_Button ;
  button[1].btn_command = Command_Select_Light ;
  
  button[2].start_x = BUTTON_START_X;
  button[2].start_y = COLOR_BLOCK_HEIGHT*2;
  button[2].end_x = BUTTON_START_X+COLOR_BLOCK_WIDTH ;
  button[2].end_y = COLOR_BLOCK_HEIGHT*3;
  button[2].para = 3;
  button[2].touch_flag = 0;  
  button[2].draw_btn = Draw_Num_Button ;
  button[2].btn_command = Command_Select_Color ;
  
  button[3].start_x = BUTTON_START_X;
  button[3].start_y = COLOR_BLOCK_HEIGHT*3;
  button[3].end_x = BUTTON_START_X+COLOR_BLOCK_WIDTH ;
  button[3].end_y = COLOR_BLOCK_HEIGHT*4;
  button[3].para = 4;
  button[3].touch_flag = 0;  
  button[3].draw_btn = Draw_Num_Button ;
  button[3].btn_command = Command_Select_Color ;
  
  button[4].start_x = BUTTON_START_X;
  button[4].start_y = COLOR_BLOCK_HEIGHT*4;
  button[4].end_x = BUTTON_START_X+COLOR_BLOCK_WIDTH ;
  button[4].end_y = COLOR_BLOCK_HEIGHT*5;
  button[4].para = 5;
  button[4].touch_flag = 0;  
  button[4].draw_btn = Draw_Num_Button ;
  button[4].btn_command = Command_Select_Color ;
  
  button[5].start_x = BUTTON_START_X;
  button[5].start_y = COLOR_BLOCK_HEIGHT*5;
  button[5].end_x = BUTTON_START_X+COLOR_BLOCK_WIDTH ;
  button[5].end_y = COLOR_BLOCK_HEIGHT*6;
  button[5].para = 6;
  button[5].touch_flag = 0;  
  button[5].draw_btn = Draw_Num_Button ;
  button[5].btn_command = Command_Select_Color ;
  
  button[6].start_x = BUTTON_START_X;
  button[6].start_y = COLOR_BLOCK_HEIGHT*6;
  button[6].end_x = BUTTON_START_X+COLOR_BLOCK_WIDTH ;
  button[6].end_y = COLOR_BLOCK_HEIGHT*7;
  button[6].para = 7;
  button[6].touch_flag = 0;  
  button[6].draw_btn = Draw_Num_Button ;
  button[6].btn_command = Command_Select_Color ;  
  
  button[7].start_x = BUTTON_START_X;
  button[7].start_y = COLOR_BLOCK_HEIGHT*7;
  button[7].end_x = BUTTON_START_X+COLOR_BLOCK_WIDTH ;
  button[7].end_y = LCD_Y_LENGTH;
  button[7].para = CL_BUTTON_GREY;
  button[7].touch_flag = 0;  
  button[7].draw_btn = Draw_Clear_Button ;
  button[7].btn_command = Command_Clear_Palette ;
  
  
  /*第二列,主要为画刷按钮*/
  button[8].start_x = BUTTON_START_X + COLOR_BLOCK_WIDTH;
  button[8].start_y = 0;
  button[8].end_x = BUTTON_START_X + COLOR_BLOCK_WIDTH*2 ;
  button[8].end_y = COLOR_BLOCK_HEIGHT;
  button[8].para = 9;
  button[8].touch_flag = 0;  
  button[8].draw_btn = Draw_Num_Button ;
  button[8].btn_command = Command_Select_Meannum ;
  
  button[9].start_x = BUTTON_START_X + COLOR_BLOCK_WIDTH;
  button[9].start_y = COLOR_BLOCK_HEIGHT;
  button[9].end_x = BUTTON_START_X + COLOR_BLOCK_WIDTH*2 ;
  button[9].end_y = COLOR_BLOCK_HEIGHT*2;
  button[9].para = 10;
  button[9].touch_flag = 0;  
  button[9].draw_btn = Draw_Num_Button ;
  button[9].btn_command = Command_Select_Light ;
  
  button[10].start_x =BUTTON_START_X +  COLOR_BLOCK_WIDTH;
  button[10].start_y = COLOR_BLOCK_HEIGHT*2;
  button[10].end_x = BUTTON_START_X + COLOR_BLOCK_WIDTH*2 ;
  button[10].end_y = COLOR_BLOCK_HEIGHT*3;
  button[10].para = 11;
  button[10].touch_flag = 0;  
  button[10].draw_btn = Draw_Shape_Button ;
  button[10].btn_command = Command_Select_Brush ;
  
  button[11].start_x = BUTTON_START_X + COLOR_BLOCK_WIDTH;
  button[11].start_y = COLOR_BLOCK_HEIGHT*3;
  button[11].end_x = BUTTON_START_X + COLOR_BLOCK_WIDTH*2 ;
  button[11].end_y = COLOR_BLOCK_HEIGHT*4;
  button[11].para = 12;
  button[11].touch_flag = 0;  
  button[11].draw_btn = Draw_Shape_Button ;
  button[11].btn_command = Command_Select_Brush ;
  
  button[12].start_x = BUTTON_START_X + COLOR_BLOCK_WIDTH;
  button[12].start_y = COLOR_BLOCK_HEIGHT*4;
  button[12].end_x = BUTTON_START_X + COLOR_BLOCK_WIDTH*2 ;
  button[12].end_y = COLOR_BLOCK_HEIGHT*5;
  button[12].para = 13;
  button[12].touch_flag = 0;  
  button[12].draw_btn = Draw_Shape_Button ;
  button[12].btn_command = Command_Select_Brush ;
  
  button[13].start_x = BUTTON_START_X + COLOR_BLOCK_WIDTH;
  button[13].start_y = COLOR_BLOCK_HEIGHT*5;
  button[13].end_x = BUTTON_START_X + COLOR_BLOCK_WIDTH*2 ;
  button[13].end_y = COLOR_BLOCK_HEIGHT*6;
  button[13].para = 14;
  button[13].touch_flag = 0;  
  button[13].draw_btn = Draw_Shape_Button ;
  button[13].btn_command = Command_Select_Brush ;
  
  button[14].start_x = BUTTON_START_X + COLOR_BLOCK_WIDTH;
  button[14].start_y = COLOR_BLOCK_HEIGHT*6;
  button[14].end_x = BUTTON_START_X + COLOR_BLOCK_WIDTH*2 ;
  button[14].end_y = COLOR_BLOCK_HEIGHT*7;
  button[14].para = 15;
  button[14].touch_flag = 0;  
  button[14].draw_btn = Draw_Shape_Button ;
  button[14].btn_command = Command_Select_Brush ;   
  
  button[15].start_x = BUTTON_START_X + COLOR_BLOCK_WIDTH;
  button[15].start_y = COLOR_BLOCK_HEIGHT*7;
  button[15].end_x = BUTTON_START_X + COLOR_BLOCK_WIDTH*2 ;
  button[15].end_y = LCD_Y_LENGTH;
  button[15].para = 16;
  button[15].touch_flag = 0;  
  button[15].draw_btn = Draw_Shape_Button ;
  button[15].btn_command = Command_Select_Brush ;
}

使用特权

评论回复
56
高级安全大使|  楼主 | 2022-2-28 23:24 | 只看该作者
button[0].para 参数用于定位按键,主函数做如下改变,初始化相关外设。
#include "stm32f10x.h"
#include "./usart/bsp_usart.h"        
#include "./lcd/bsp_ili9341_lcd.h"
#include "./lcd/bsp_xpt2046_lcd.h"
#include "./flash/bsp_spi_flash.h"
#include "./led/bsp_led.h"
#include "palette.h"
#include <string.h>
#include "mcu_api.h"
#include "protocol.h"
#include "system.h"
#include "wifi.h"
#include "concrol.h"


extern int MANUAL_FEED_NUM;
char dispBuff[100];
extern int MANUAL_FEED_SUM;
int main(void)
{               
        //LCD 初始化
        ILI9341_Init();  
        
        //触摸屏初始化
        XPT2046_Init();
        //从FLASH里获取校正参数,若FLASH无参数,则使用模式3进行校正
        Calibrate_or_Get_TouchParaWithFlash(3,0);

        /* USART config */
        USART_Config();  
        LED_GPIO_Config();
        EXTIX_Init();

        //其中0、3、5、6 模式适合从左至右显示文字,
        //不推荐使用其它模式显示文字        其它模式显示文字会有镜像效果                        
        //其中 6 模式为大部分液晶例程的默认显示方向  
  ILI9341_GramScan ( 3 );        
        
        //绘制触摸画板界面
        Palette_Init(LCD_SCAN_MODE);
        wifi_protocol_init();
        GPIO_CONFIG();               
        LCD_SetFont(&Font8x16);        
        LCD_SetColors(RED,BLACK);
        sprintf(dispBuff,"手动投喂量: %d ",MANUAL_FEED_NUM);
        ILI9341_DispString_EN_CH(2*48,2*48,dispBuff);
        ILI9341_DisplayStringEx(2*48,0*48,16,16,(uint8_t *)"服务器在线!!!",0);        
        while ( 1 )
        {               
                        wifi_uart_service();//心跳检测
                        //触摸检测函数,本函数至少10ms调用一次
                        XPT2046_TouchEvenHandler();
                        LCD_SetFont(&Font8x16);        
                        LCD_SetColors(RED,BLACK);
                        sprintf(dispBuff,"总喂食量: %d ",MANUAL_FEED_SUM);
                        ILI9341_DispString_EN_CH(2*48,1*48,dispBuff);      
                                                get_wifi_status(mcu_get_wifi_work_state());                       
        

                        
        }
               
}

使用特权

评论回复
57
高级安全大使|  楼主 | 2022-2-28 23:25 | 只看该作者
温湿度采集显示
因为H桥驱动板烧了,在自己的垃圾箱里面翻找替代品的时候发现了以前做毕设遗留下来的DHT11温湿度模块和WS2812灯环,物联网设计嘛,玩啥不是玩呢,都安排上。

使用特权

评论回复
58
高级安全大使|  楼主 | 2022-2-28 23:26 | 只看该作者
页面也更新了:

使用特权

评论回复
59
jcky001| | 2022-3-1 14:35 | 只看该作者
有具体的应用案例吗?这个方案挺棒啊

使用特权

评论回复
60
AloneKaven| | 2022-10-7 20:07 | 只看该作者
有案例吗?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则