基于STM32的重力感应售货机系统设计

发布时间:2023-09-22  

一、项目介绍

随着智能物联网技术的不断发展,人们的生活方式和消费习惯也正在发生改变。如今越来越多的人习惯于在线购物、自助购物等新型消费模式,因此智能零售自助柜应运而生。


本项目设计开发一款基于STM32主控芯片的智能零售自助柜,通过重力传感器监测货柜内商品重量变化,并通过WiFi通信模块与手机端实现交互。用户可以通过输入账号密码,柜门自动打开,用户自取商品后关闭柜门,柜门锁定,系统根据重量变化判断用户拿取的商品并从账户自动扣费。同时,用户也可以通过手机端查看消费流水、商品库存,并进行补货和充值等操作。


智能零售自助柜的应用场景非常广泛,可以应用于商场、超市、酒店、机场、车站等各类场景。通过自助购物,可以提高消费者的消费体验和购物效率,同时也降低了商家的人力成本和物流成本。

image-20230530231109062

二、设计思路

【1】功能细节总结

(1)ESP8266配置成AP+TCP服务器模式与手机APP连接。


(2)手机APP可以完成用户的注册,充值功能,然后通过连接货柜将数据同步到货柜的存储芯片上(W25Q64-FLASH保存数据)。


(3)手机APP连接货柜之后,可以拉取数据显示,了解货柜现在的物品哪些已经售卖出去,哪些还没有售卖。,每个物品是放在一个货柜格子里,透明玻璃可以查看到物品。


【2】硬件选型

主控芯片:STM32F103RCT6是一款主流的32位ARM Cortex-M系列微控制器,具有高性能、低功耗和易于开发等特点,因此被选择作为该系统的主控芯片。

重力传感器:HX711重力传感器模块采用24位高精度芯片,能够精确测量重量,适用于该系统中货柜内商品的重量监测。

SG90舵机:该系统需要控制柜门的打开和关闭,因此使用舵机来实现柜门控制。

矩阵键盘:用户需要输入账号密码进行登录,因此使用矩阵键盘作为输入设备。

显示屏:OLED显示屏具有低功耗、高对比度、快速响应等特点,适用于该系统中的桌面显示界面。

WiFi模块:ESP8266-WIFI模块是一款成本低、体积小、性能稳定的WiFi通信模块,适合在该系统中与手机APP进行无线通信。

【2】程序设计思路

初始化系统,包括各个外设的初始化,如WiFi模块、重力传感器HX711模块、矩阵键盘等;

用户输入账号密码,判断是否为有效用户;

根据重力传感器读取货柜内商品重量,判断用户拿取的商品并从账户自动扣费;

控制柜门打开和关闭,同时显示屏上显示相关提示信息;

同步数据到手机APP。

【3】设备操作流程

用户输入账号密码,系统进行验证,判断是否为有效用户;

如果验证通过,屏幕上显示“登录成功”,并显示货柜内商品列表和对应价格;

用户选择需要购买的商品,系统根据重力传感器读取货柜内商品重量,并判断用户拿取的商品并从账户自动扣费;

系统控制电磁锁或舵机将柜门打开,用户自取商品后关闭柜门;

重力传感器监测到货柜内重量变化,系统自动判断用户拿取的商品种类和数量,并在显示屏上显示相关提示信息,如显示扣费金额;

控制柜门锁定,确保商品安全,同时在显示屏上显示“门已锁定”等相关提示信息;

同步扣费记录和商品库存信息到手机APP,以便用户查看消费流水和进行补货等操作。

如需要充值,用户可以在手机APP上进行余额充值操作。

三、代码实现

【1】OLED显示屏驱动代码

下面是OLED显示屏的测试代码。使用的SPI接口的OLED显示屏。


#include "stm32f10x.h"

 #include "OLED.h"   // OLED驱动库头文件

 

 void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *str)

 {

     uint8_t i = 0;

     while(str[i] != ''){

         if(x > OLED_WIDTH - 8){  // 满行自动换行

             x = 0;

             y++;

         }

         OLED_ShowChar(x, y, str[i]);  // 显示单个字符

         x += 8;  // 水平方向上的下一个字符

         i++;

     }

 }

 

 void OLED_SPI_SendByte(uint8_t data)

 {

     while(SPI_I2S_GetFlagStatus(OLED_SPI_PORT, SPI_I2S_FLAG_TXE) == RESET);  // 等待发送缓冲区空

     SPI_I2S_SendData(OLED_SPI_PORT, data);  // 通过SPI发送数据

 }

 

 void OLED_WriteCmd(uint8_t cmd)

 {

     OLED_DC_Clr();  // 将DC置为0,表示发送命令

     OLED_CS_Clr();  // 将CS置为0,选中OLED芯片

     OLED_SPI_SendByte(cmd);  // 发送命令

     OLED_CS_Set();  // 将CS置为1,取消OLED芯片选中

 }

 

 void OLED_WriteData(uint8_t data)

 {

     OLED_DC_Set();  // 将DC置为1,表示发送数据

     OLED_CS_Clr();  // 将CS置为0,选中OLED芯片

     OLED_SPI_SendByte(data);  // 发送数据

     OLED_CS_Set();  // 将CS置为1,取消OLED芯片选中

 }

 

 

 int main(void)

 {

     uint32_t i;

 

     // 初始化SPI接口

     SPI_InitTypeDef SPI_InitStructure;

     RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);  // 打开SPI1时钟

     SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  // 设置SPI工作模式

     SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

     SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;  // 数据位宽8bit

     SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;  // 时钟极性为低电平

     SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;  // 时钟第一个边沿采样

     SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;  // 软件控制CS信号

     SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;  // 预分频系数为256

     SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;  // MSB先行

     SPI_InitStructure.SPI_CRCPolynomial = 7;  // CRC校验值

     SPI_Init(SPI1, &SPI_InitStructure);

     SPI_Cmd(SPI1, ENABLE);  // 使能SPI1

 

     // 初始化OLED显示屏

     OLED_Init();  // OLED初始化

 

     // 显示数字

     char str[] = "1234567890";

     OLED_ShowString(0, 0, (uint8_t *)str);  // 在(0,0)坐标处显示字符串

 

     while(1){

         for(i = 0; i < 10000000; i++);  // 延时等待

     }

 }

 

OLED_WriteCmd 函数用于向 OLED 显示屏发送命令,而 OLED_WriteData 函数用于向 OLED 显示屏发送数据。OLED_SPI_SendByte 函数是底层SPI数据传输的关键代码部分。


【2】HX711称重传感器代码

#include "stm32f10x.h"

 #include < stdio.h >

 #include "usart.h"

 

 #define HX711_SCK_GPIO_RCC  RCC_APB2Periph_GPIOB

 #define HX711_SCK_GPIO_PORT GPIOB

 #define HX711_SCK_GPIO_PIN  GPIO_Pin_13

 

 #define HX711_DOUT_GPIO_RCC  RCC_APB2Periph_GPIOB

 #define HX711_DOUT_GPIO_PORT GPIOB

 #define HX711_DOUT_GPIO_PIN  GPIO_Pin_15

 

 uint32_t read_HX711_data(void);

 void init_GPIO(void);

 void init_USART1(void);

 void USART1_SendChar(char ch);

 

 int main(void)

 {

     uint32_t hx711_value;

 

     init_GPIO();

     init_USART1();

 

     while(1){

         hx711_value = read_HX711_data();  // 读取 HX711 传感器数据

         printf("The weight is: %d g ", hx711_value);  // 通过串口打印 HX711 传感器读取的数据

     }

 }

 

 // 从 HX711 传感器读取数据

 uint32_t read_HX711_data(void)

 {

     uint32_t weight = 0;

     uint8_t i;

 

     GPIO_SetBits(HX711_SCK_GPIO_PORT, HX711_SCK_GPIO_PIN);  // 拉高 SCK 管脚

     GPIO_ResetBits(HX711_DOUT_GPIO_PORT, HX711_DOUT_GPIO_PIN);  // 拉低 DOUT 管脚

     for(i = 0; i < 24; i++){

         GPIO_ResetBits(HX711_SCK_GPIO_PORT, HX711_SCK_GPIO_PIN);  // 拉低 SCK 管脚,使得 HX711 将数据推入 DOUT 管脚

         weight < <= 1;  // 左移一位,为下一次读取做准备

         if(GPIO_ReadInputDataBit(HX711_DOUT_GPIO_PORT, HX711_DOUT_GPIO_PIN)) weight++;  // 如果 DOUT 管脚为高电平,那么就在 weight 中保存 "1"

         GPIO_SetBits(HX711_SCK_GPIO_PORT, HX711_SCK_GPIO_PIN);  // 拉高 SCK 管脚,为下一次读取做准备

     }

     GPIO_ResetBits(HX711_SCK_GPIO_PORT, HX711_SCK_GPIO_PIN);  // 最后时刻需要拉低 SCK 管脚一次

 

     weight = (weight ^ 0x800000) - 0x800000;  // 将读出的24位二进制重量值转化为带符号数,这里我们只考虑单通道读取的情况(如有多个物理传感器需进行一定的计算处理)

 

     return weight;

 }

 

 // 初始化 GPIO 管脚

 void init_GPIO(void)

 {

     GPIO_InitTypeDef GPIO_InitStructure;

 

     RCC_APB2PeriphClockCmd(HX711_SCK_GPIO_RCC | HX711_DOUT_GPIO_RCC, ENABLE);  // 打开 SCK 和 DOUT 管脚时钟

 

     GPIO_InitStructure.GPIO_Pin = HX711_SCK_GPIO_PIN;

     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

     GPIO_Init(HX711_SCK_GPIO_PORT, &GPIO_InitStructure);  // 初始化 SCK 管脚

 

     GPIO_InitStructure.GPIO_Pin = HX711_DOUT_GPIO_PIN;

     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

     GPIO_Init(HX711_DOUT_GPIO_PORT, &GPIO_InitStructure);  // 初始化 DOUT 管脚

 }

 

 // 初始化 USART1

 void init_USART1(void)

 {

     USART_InitTypeDef USART_InitStructure;

 

     RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);  // 打开 USART1 时钟

 

     USART_InitStructure.USART_BaudRate = 115200;  // 波特率 115200

     USART_InitStructure.USART_WordLength = USART_WordLength_8b;  // 数据位 8 位

     USART_InitStructure.USART_StopBits = USART_StopBits_1;  // 停止位 1 位

     USART_InitStructure.USART_Parity = USART_Parity_No;  // 无奇偶校验

     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  // 无硬件流控制

     USART_InitStructure.USART_Mode = USART_Mode_Tx;  // 只启用串口发送

 

     USART_Init(USART1, &USART_InitStructure);  // 初始化 USART1

     USART_Cmd(USART1, ENABLE);  // 使能 USART1

 }

 

 // 通过 USART1 发送字符

 void USART1_SendChar(char ch)

 {

     while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);  // 等待发送缓冲区为空

     USART_SendData(USART1, (uint8_t)ch);  // 发送数据

 }

 

代码执行流程说明:


(1)通过 init_GPIO() 函数初始化 SCK 和 DOUT 两个 GPIO 管脚,并通过 init_USART1() 函数初始化 USART1 串口。其中,初始化 SCK 管脚为输出模式,DOUT 管脚为输入模式,USART1 算是串口助手,用于将数据打印输出。


(2)read_HX711_data() 函数用于向 HX711 传感器发出读取数据的指令,并将返回的数据进行处理(将24位二进制重量值转化为带符号数)后返回。


(3)在主函数的 while 循环中,不断调用 read_HX711_data() 函数读取 HX711 传感器的数据,并通过串口打印出来。


【3】SG90舵机控制代码

下面是SG90舵机的控制代码,可以按照指定的角度旋转。


#include "stm32f10x.h"

 #include "delay.h"

 

 #define GPIO_PORT           GPIOA

 #define GPIO_PIN            GPIO_Pin_1

 #define RCC_APB2Periph_GPIO RCC_APB2Periph_GPIOA

 #define PWM_FREQ            50

 

 void servoInit(void)

 {

     GPIO_InitTypeDef GPIO_InitStructure;

     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

     TIM_OCInitTypeDef TIM_OCInitStructure;

 

     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO, ENABLE);

 

     GPIO_InitStructure.GPIO_Pin = GPIO_PIN;

     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

     GPIO_Init(GPIO_PORT, &GPIO_InitStructure);

 

     TIM_TimeBaseStructure.TIM_Period = 9999; //计数器最大值

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    亲身摸过才感受那种非凡的体验品质。 小编一直在使用PaperWhite,不得不说捧在手中长时间阅读还是挺累的,轻柔如烟的Oasis无疑改变了阅读者的使用感受,极具人性化的重力感应也使得用户能够将Oasis在左......
    集成量子传感器和压力感应器,新工具可精确检测超导体特性;据发表于最新一期《自然》杂志的论文,美国哈佛大学开发了一种精准测量超导体的基础工具。他们创造性地将量子传感器集成到标准的压力感应设备中,从而......
    种强大功能的支撑点。 传感器分布图 语音加速感应器 语音加速感应器是一个语音活动检测器,专门检测由用户所产生的振动。不论是用户声带产生的“既浊音”,还是不使用声带产生的“清音”,感应器......
    电流传感器 和 磁通门电流传感器 有什么区别呢? 一、原理1 磁通门电流传感器原理磁通门电流传感器是利用被测磁场中高导磁率磁芯在交变磁场的饱和激励下,其磁感应强度与磁场强度的非线性关系来测量弱磁场,这种......
    基于STM32的重力感应无线智能遥控小车设计;引言 随着社会进步,电子消费产品的需求越来越高,遥控智能车辆以及在智能化车辆基础上开发出来的产品已广泛应用到自动化物流运输等领域。目前,遥控......
    时,测量A、B之间的直流电压为0V,则判断接近开关也正常。 但是有时候满足了上面两点,X0信号仍然不能接通。比如感应器线缆从电柜到感应器的途中腐蚀导致线路有接触电阻,或者感应器......
    控制围栏的开启和闭合、监测红外感应器的状态、控制信号灯的变化以及控制蜂鸣器的报警声音。 【2】步进电机是用来控制铁路围栏的开启和闭合的设备,其动力来源为驱动芯片ULN2003。 【3】红外感应器是......
    用于下雨天等复杂环境等优点。无实体按键、一体化的高度集成结构设计,打造更具科技感、极简化、同时更人性化的人机交互体验。 纽迪瑞作为压力感应领域领导者,基于NDT微距压力传感器,推出......
    ? 首先,咱们聊聊人体传感器是什么? 人体传感器的基本功能是感应人体的移动,一定要注意,它感应的不是人体,而是人体的移动,这是由其原理决定的。是智能家居关键传感器之一。严谨来说应该叫“物体......
    研究整间出售的可能性。该公司的感应器产品主要供应苹果iPhone 与三星智能手机。 InvenSense 上周五股价急升最多13% ,收市时升61 美仙至7.48 美元。以收市价计算,其市......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>