基于STM32的智能外卖柜设计(仿真+实物)

        基于STM32的外卖柜,使用红外模块检测外卖状态、舵机模式外卖门,使用GSM模块进行通信,利用矩阵按键进行用户交互。将外卖放入外卖柜,输入手机号码,系统将自动发送数据并携带验证码给我们输入的手机号,拿外卖的时候,我们需要输入验证码才可以打开外卖柜并取走外卖,实现整个智能存取过程。

一、仿真工程搭建

1、电路设计

        本次设计为基于STM32的智能外卖柜,由于Proteus并没有GSM模块,所以本次设计利用串口进行设计,实际上STM32实现GSM功能也是通过UART进行与GSM模块的通信。由于Proteus矩阵按键的仿真效果并不好,这边为了系统可用,使用独立按键完成功能。并使用单刀双开关设计红外模块,实际上的红外传感器也是数字传感器,检测到物体输出0,否则为1。为了可视化,本次设计采用LCD1602进行显示,虽然正式硬件设计上使用的是0.96寸OLED,但是可以实现类似功能的仿真。如下图:

2、功能演示

        仿真开始,LCD1602显示 Phone 的英文字体,此时我们可以输入号码,并且将红外打到低电平,此时外卖已经放入柜子中,串口会发送 111111111111 Your takeaway has arrived ,同时设置了超时提醒,当外卖放置时间过长,会发出提示:111111111111 You didn't take the takeaway

        当我们将红外打到高电平,此时显示屏显示,has been taken,串口调试助手会显示 The takeaway has been picked up。

        按下清零,被清空,此时可以进行下一次放置。

3、仿真总结

        本次仿真设计借助了Proteus仿真,由于Proteus的限制,功能和实物呈现的效果有细微差异,但是大致仿真出了该系统功能。

二、硬件设计

        综合上述仿真设计的经验,我们接下来需要设计硬件,由于本次设计硬件上并不是难点,一般只需要一下接口然后放置外部器件就可以实现,绘制PCB也仅仅是为了美观整合这些模块:如下图:

1、原理图

2、PCB图

3、最终效果图

        本次设计在外观上呈现效果并不好,但是功能大致已经实现

三、软件设计

        1、Unicode

        本次设计的GSM模块利用串口进行通信,但是其编码一般要求是unicode格式,虽然也可以进行更改,将Unicode编码改为UTF-8,因为本次设计并不难,所以编码并不进行修改。号码是数字,量并不算大,所以在使用API函数进行编码转化并不是难事,如下:

/**********************************************************************
描述: ASCII 转 unicode      比如 '1'  转成 "0031"
***********************************************************************/
void ASCII_TO_Unicode(const char *ASCII,char *Unicode)
{
    int length;
    int i = 0;
    int j = 0;
	memset(Unicode,'\0',sizeof(Unicode));
    length = strlen(ASCII);

    for(i=0;i<length;i++)
    {
        Unicode[j++] = '0';
        Unicode[j++] = '0';

        Unicode[j++] = (ASCII[i] / 16) + 0x30;
        Unicode[j++] = (ASCII[i] % 16) + 0x30;
    }
}

        但是一些中文用API进行编码会比较繁琐,本次设计中的中文只有固定两句,所以并没有必要进行灵活中文设计,我们可以通过一些网站进行中文转Unicode。借助如下工具便可完成 :在线 Unicode 编码转换 | 菜鸟工具 (jyshare.com)

2、系统功能设计

        本次系统功能不同于仿真,本次系统使用STM32作为主控,矩阵按键作为用户交互器件,输入外卖收货人的手机号,输入完毕会给收货人发送信息并携带验证码,同时舵机驱动门关闭,红外检测外卖在柜子内状态。如果长时间未取,系统发送长时间未取的消息给用户手机。取外卖还需要核对系统随机给用户发送的验证码,如果验证码不正确,外卖将无法取出,验证码正确,舵机驱动门打开,用户取走外卖,系统恢复到初始状态。核心代码如下:

#include "stm32f10x.h"
#include "bsp_adc.h"
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"	
#include "Led.h"
#include "MatrixKey.h"
#include "sys.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h> 
#include <wchar.h>
#include <locale.h>
#include <math.h>
#include "timer.h"
#include "Servo.h"

//配置传感器的GPIO
#define Sensor_ClockConfig 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE)
#define Sensor_GPIOx		GPIOB
#define Sensor_GPIOPin0		GPIO_Pin_11	

#define Senor PBin(11)

//红外传感器初始化
void Sensor_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;	
	Sensor_ClockConfig;
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = Sensor_GPIOPin0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(Sensor_GPIOx, &GPIO_InitStructure);
}

int dis=0;
uint8_t comfire=0;
uint32_t cnt=0;
uint8_t phoneFlag=0;
uint8_t layFlag=0;
char phone[12];
uint8_t index_p=0;

/**********************************************************************
描述: ASCII 转 unicode      比如 '1'  转成 "0031"
***********************************************************************/
void ASCII_TO_Unicode(const char *ASCII,char *Unicode)
{
    int length;
    int i = 0;
    int j = 0;
	memset(Unicode,'\0',sizeof(Unicode));
    length = strlen(ASCII);

    for(i=0;i<length;i++)
    {
        Unicode[j++] = '0';
        Unicode[j++] = '0';

        Unicode[j++] = (ASCII[i] / 16) + 0x30;
        Unicode[j++] = (ASCII[i] % 16) + 0x30;
    }
}

//GMS初始化
void GMS_Init()
{
	printf("AT+CSQ\r\n");
	Delay_ms(1000);//延时1秒
	printf("AT+CMGF=1\r\n");//文本模式
	Delay_ms(1000);//延时1秒
	printf("AT+CSMP=17,167,2,25\r\n");//设置在工作模式
	Delay_ms(1000);//延时1秒
	
	printf("AT+CSCS=\"UCS2\"\r\n");//UCS2编码字集
	Delay_ms(1000);//延时1秒	
}

//发送数据
void Send_Chinsese(char *phone, char *mess)
{
	char Unicode[50];
	
	//将号码进行编码
	ASCII_TO_Unicode(phone, Unicode);
	printf("AT+CMGS=\"%s\"\r\n", Unicode);//unicode电话号码
	Delay_ms(3000);//延时1秒
	
	//发送数据
	printf("%s\r\n", mess);//内容unicode码
	
	Delay_ms(3000);//延时1秒
	//结束位
	Serial_SendByte(0x1A);
}


uint8_t IN_flag=0;
uint8_t runmode=0;

/****
矩阵:
	横: PA0 - PA3
	竖: PA4 - PA7
OLED显示屏:
	SCL: PB10
	SDA: PB11
***/

char tem[5];
uint16_t s_cnt=0;
uint8_t timeFlag=0;

uint8_t min_set=1;

int main(void)
{	
	int i=0;
	int res=0;
	int pos=0;
	char code[5];
	char codeIn[5];
	char code_index=0;
	char sendData[50];
	char unicode[50];
	int8_t keyNum=-1;
	//led初始化
	Led_Init();
	//关闭led
	Led=1;
	//伺服电机初始化
	Servo_Init();
	//伺服电机设置角度
	Servo_SetAngle(90);
	//红外初始化
	Sensor_Init();
	//串口1初始化
	Serial_Init();
	//矩阵按键初始化
	MatrixKey_Init();
	//oled初始化
	OLED_Init();
	//定时器3初始化
	TIM3_Int_Init(72, 100);
	//显示屏显示提示
	OLED_ShowString(1, 1, "GSM Init...");
	//GSM初始化
	GMS_Init();
	//关闭计时
	Clockflag=0;
	while (1)
	{
		//进行计数,作为随机数的种子
		s_cnt++;
		s_cnt%=1000;
		
		//获取按键值
		keyNum = MatrixKey_Scan();
		
		//判断是否在柜子里,并显示
		if(Senor==0){
			Led=0;
			OLED_ShowNum(4, 15, 1, 1);
		}
		else{
			Led=1;
			OLED_ShowNum(4, 15, 0, 1);
		}
		
		//模式1,输入手机号,放入外卖模式
		if(runmode == 0){
			Servo_SetAngle(90);
			timeFlag=0;
			OLED_ShowString(1, 1, "Phone:     ");
			
			//当值大于0时,表示有按键按下
			if(keyNum>=0){
				//填充电话数组
				if(keyNum >=0 && keyNum <= 9){
					phone[index_p] = 0x30 + keyNum;	
					if(index_p<11){
						index_p++;
					}				
				}			
			}
			
			//显示设定提醒时间
			OLED_ShowNum(1, 13, min_set, 2);
			if(keyNum == 10){
				min_set--;
			}
			else if(keyNum == 11){
				min_set++;
			}
			
			//确定按键
			if(keyNum==15){
				comfire=1;
				phoneFlag=1;
			}
			//清零
			if(keyNum==14){
				for(i=0; i<11; i++){
					phone[i]='0';
				}
				index_p=0;
				comfire=0;
				phoneFlag=0;
				OLED_ShowString(2, 1, "                        ");
			}
			
			
			//号码显示 
			pos=1;
			for(i=0; i<index_p; i++){
				OLED_ShowChar(2, pos, phone[i]);
				//固定数加间隔,用于区分
				if(i==2)pos+=2;
				//固定数加间隔,用于区分
				else if(i==6)pos+=2;
				//正常段
				else pos++;	
			}
			
				
			//确定按下,发送第一条短信
			if(comfire==1 && Senor==0){
				//随机数种子
				srand(s_cnt);
				// 生成随机数字字符
				for(i = 0; i < 4; i++) {
					code[i] = '0' + rand() % 10; 
				}
				code[4]='\0';
				//备份验证码
				for(i=0; i<5; i++){
					tem[i] = code[i];
				}
				
				//显示发送
				OLED_ShowString(4, 1, "Sending...");
				//进行编码
				ASCII_TO_Unicode(code, unicode);
				//发送信息,不能直接发送中文,需要进行unicode编码
				sprintf(sendData, "60a87684591653565df25230ff0c9a8c8bc17801662f%s", unicode);
				phone[11]='\0';
				//测试信号
				printf("AT+CSQ\r\n");
				Delay_ms(1000);//延时1秒	
				//发送中文短信
				Send_Chinsese(phone, sendData);
				comfire=0;
				runmode=1;
				//OLED清屏
				OLED_Clear();
			}
			OLED_ShowString(4, 1, "          ");			
		}
		else if(runmode == 1){
			//伺服电机转到180,关闭外卖柜
			Servo_SetAngle(180);
			//开始计时
			Clockflag=1;
			//显示提示
			OLED_ShowString(1, 1, "Captcha:");
			//显示时间
			OLED_ShowNum(3, 1, Min, 2);
			OLED_ShowString(3, 3, ":");
			OLED_ShowNum(3, 4, Sec, 2);
			
			//超时提醒
			if(Min >= min_set && timeFlag==0){
				timeFlag=1;
				
			}
			//超时提醒
			if(timeFlag == 1){
				Clockflag=0;
				timeFlag=2;
				OLED_ShowString(4, 1, "Sending...");
				phone[11]='\0';
				printf("AT+CSQ\r\n");
				Delay_ms(1000);//延时1秒	
				Send_Chinsese(phone, "68c06d4b523060a8768459165356957f65f695f4672a53d6ff0c8bf753ca65f653d68d70ff01");				
			}
			OLED_ShowString(4, 1, "          ");
			
			//当值大于0时,表示有按键按下
			if(keyNum>=0){
				//填充电话数组
				if(keyNum >=0 && keyNum <= 9){
					codeIn[code_index] = 0x30 + keyNum;	
					if(code_index<4){
						code_index++;
					}
				}			
			}
			//加上结束符
			codeIn[4] = '\0';
			
			
			//验证码显示 
			pos=1;
			for(i=0; i<code_index; i++){
				OLED_ShowChar(2, pos, codeIn[i]);
				pos++;
			}
			
			if(keyNum==14){
				//清空验证码
				for(i=0; i<4; i++){
					code[i]='0';
				}
				code_index=0;	
				OLED_ShowString(2, 1, "                        ");
			}
			
			//校验验证码
			if(keyNum==15){
				res = strcmp(codeIn, tem);
				//检验成功,切换到模式2
				if(res == 0){
					runmode=2;
					OLED_Clear();
					
					//清空电话号码
					for(i=0; i<11; i++){
						phone[i]='0';
					}
					
					//清空验证码
					for(i=0; i<4; i++){
						code[i]='0';
					}
					code_index=0;
					
					index_p=0;
					comfire=0;
					phoneFlag=0;
					
					//计时变量清零
					Hour=0;
					Min=0;
					Sec=0;
					
				}
				else{
					//清空验证码
					for(i=0; i<4; i++){
						code[i]='0';
					}
					code_index=0;	
					OLED_ShowString(2, 1, "                        ");
				}			
			}
		}	
		//验证码正确
		else if(runmode == 2){
			//打开外卖柜门
			Servo_SetAngle(90);
			//关闭计时
			Clockflag=0;
			//oled显示,“验证正确,请取走”
			OLED_ShowString(1, 1, "The verification");
			OLED_ShowString(2, 1, "code is correct");
			OLED_ShowString(3, 1, "please take it!");
			//判断外卖是否被拿走,拿走切换回模式0,并清屏
			if(Senor == 1){
				runmode=0;
				OLED_Clear();
			}
		}
	}
}

四、实物演示

1、首先输入号码放入外卖按下确认消息发送到了目标手机

2、目标手机查看验证码,同时系统开始计时

3、此时输入验证码,系统响应,外卖柜打开,外卖取走,系统回归原始状态。

4、长时间未取,系统发出信息,提醒取走,同时时间可以自己调节,这里为了演示,设计提醒时间是一分钟。

五、项目总结

        本次设计使用STM32作为主控芯片,完成了一个外卖柜的设计,实现了短信提示、模拟门、用户自动输入号码,红外传感器检测外卖状态,验证码开门,完成了STM32智能外卖柜设计。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值