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

news2025/2/13 10:03:24

一、项目介绍

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

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

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

image-20230530231109062

二、设计思路

【1】功能细节总结

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

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

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

【2】硬件选型

  1. 主控芯片:STM32F103RCT6是一款主流的32位ARM Cortex-M系列微控制器,具有高性能、低功耗和易于开发等特点,因此被选择作为该系统的主控芯片。
  2. 重力传感器:HX711重力传感器模块采用24位高精度芯片,能够精确测量重量,适用于该系统中货柜内商品的重量监测。
  3. SG90舵机:该系统需要控制柜门的打开和关闭,因此使用舵机来实现柜门控制。
  4. 矩阵键盘:用户需要输入账号密码进行登录,因此使用矩阵键盘作为输入设备。
  5. 显示屏:OLED显示屏具有低功耗、高对比度、快速响应等特点,适用于该系统中的桌面显示界面。
  6. WiFi模块:ESP8266-WIFI模块是一款成本低、体积小、性能稳定的WiFi通信模块,适合在该系统中与手机APP进行无线通信。

【2】程序设计思路

  1. 初始化系统,包括各个外设的初始化,如WiFi模块、重力传感器HX711模块、矩阵键盘等;
  2. 用户输入账号密码,判断是否为有效用户;
  3. 根据重力传感器读取货柜内商品重量,判断用户拿取的商品并从账户自动扣费;
  4. 控制柜门打开和关闭,同时显示屏上显示相关提示信息;
  5. 同步数据到手机APP。

【3】设备操作流程

  1. 用户输入账号密码,系统进行验证,判断是否为有效用户;
  2. 如果验证通过,屏幕上显示“登录成功”,并显示货柜内商品列表和对应价格;
  3. 用户选择需要购买的商品,系统根据重力传感器读取货柜内商品重量,并判断用户拿取的商品并从账户自动扣费;
  4. 系统控制电磁锁或舵机将柜门打开,用户自取商品后关闭柜门;
  5. 重力传感器监测到货柜内重量变化,系统自动判断用户拿取的商品种类和数量,并在显示屏上显示相关提示信息,如显示扣费金额;
  6. 控制柜门锁定,确保商品安全,同时在显示屏上显示“门已锁定”等相关提示信息;
  7. 同步扣费记录和商品库存信息到手机APP,以便用户查看消费流水和进行补货等操作。
  8. 如需要充值,用户可以在手机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] != '\0'){
        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\r\n", 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; //计数器最大值
    TIM_TimeBaseStructure.TIM_Prescaler = (72 * 2) - 1; //时钟分频,72是系统时钟频率,2是倍频
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 0;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM2, &TIM_OCInitStructure);

    TIM_Cmd(TIM2, ENABLE);
}

void servoSetAngle(uint8_t angle)
{
    uint16_t pwmVal = (uint16_t)(500 + angle * 10.0 / 9.0);

    TIM_SetCompare1(TIM2, pwmVal);
    delay_ms(100);
}

int main(void)
{
    SystemInit();
    delay_init();

    servoInit();

    while(1)
    {
        servoSetAngle(0);
        delay_ms(1000);

        servoSetAngle(90);
        delay_ms(1000);

        servoSetAngle(180);
         delay_ms(1000);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/639582.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

哪吒汽车,莫做“普信男”

作者 | 魏启扬 来源 | 洞见新研社 今年初&#xff0c;哪吒汽车创始人方运舟和张勇联合发表新年致辞&#xff0c;文末总结说 “2023-2025年&#xff0c;必将是一场艰难的挑战&#xff0c;也是哪吒汽车的生死存亡之战。” 哪吒汽车或许过于敏感了&#xff0c;就今年以来的市场表…

Tensorflow两步安装(超简单)

一、查看python版本&#xff0c;下载对应tensorflow文件 1.Anaconda已安装&#xff0c;找到Anaconda3文件夹&#xff0c;双击打开anaconda prompt&#xff0c;输入python&#xff0c;查看python版本 可以看到我的版本是3.9的 2.进入下面的网站&#xff0c;选择你需要的cpu或g…

【appium】appium自动化入门之API(下)——两万字API长文,建议收藏

目录 Appium API 前言 1.contexts &#xff08;返回当前会话中的上下文&#xff0c;使用后可以识别 H5 页面的控件&#xff09; 2.current_context &#xff08;返回当前会话的当前上下文 &#xff09; 3. context &#xff08;返回当前会话的当前上下文&#xff09; 4.find_e…

Django-搭建sysinfo获取系统信息

文章目录 前言一、项目搭建二、主机信息监控三、Celery定时任务和异步任务 前言 本篇基于&#xff1a;https://github.com/hypersport/sysinfo#readme 使用Django&#xff0c;搭建sysinfo&#xff0c;Linux中,sysinfo是用来获取系统相关信息的结构体 一、项目搭建 &#xff0…

CV方向如何找到适合自己的研究创新点?

做CV的论文创新的一些思路与方向。分别是无事生非&#xff0c;后浪推前浪&#xff0c;推陈出新&#xff0c;出奇制胜。 无事生非 在原始的数据集上加一些噪声&#xff0c;例如随机遮挡&#xff0c;或者调整饱和度亮度什么的&#xff0c;主要是根据具体的任务来增加噪声或扰动&a…

大模型LLM-微调经验分享总结

模型越大对显卡的要求越高&#xff0c;目前主流对大模型进行微调方法有三种&#xff1a;Freeze方法、P-Tuning方法和Lora方法。笔者也通过这三种方法&#xff0c;在信息抽取任务上&#xff0c;对ChatGLM-6B大模型进行模型微调。liucongg/ChatGLM-Finetuning: 基于ChatGLM-6B模型…

I/O设备详解

目录 一. 什么是IO设备 二. IO设备分类 2.1按照使用特性分类 2.2按照传输速率分配 2.3按照信息交换的单位分类 三. IO设备的构成 3.1 IO的机械部件 3.2 IO的电子部件 3.2.1设备控制器&#xff08;IO控制器功能简介&#xff09; 3.2.2设备控制器&#xff08;IO控制器&…

【C++】红黑树的模拟实现

文章目录 一、红黑树的概念二、红黑树的性质三、红黑树节点的定义四、红黑树结构五、红黑树的插入操作六、红黑树的调整1.叔叔存在且为红2.叔叔不存在或者存在且为黑3.插入完整代码4.总结 七、红黑树的验证八、红黑树的删除九、红黑树与AVL树的比较十、红黑树的应用十一、红黑树…

d2l_第四章学习_Softmax Regression

x.1 Classification 分类问题 x.1.1 Classification和Regression的区别 注意&#xff0c;广义上来讲&#xff0c;Classification/Softmax Regression 和 Linear Regression 都属于线性模型。但人们口语上更习惯用Classification表示Softmax Regression&#xff0c;而用Regres…

C++特殊类的设计与类型转换

特殊类的设计与类型转换 特殊类的设计请设计一个类&#xff0c;只能在堆上创建对象请设计一个类&#xff0c;只能在栈上创建对象请设计一个类&#xff0c;只能创建一个对象(单例模式) C的类型转换 特殊类的设计 请设计一个类&#xff0c;只能在堆上创建对象 通过new创建的类就…

Baumer工业相机堡盟工业相机如何使用BGAPISDK对两个万兆网相机进行硬件触发同步(C++)

Baumer工业相机堡盟工业相机如何使用BGAPISDK对两个万兆网相机进行硬件触发同步&#xff08;C&#xff09; Baumer工业相机Baumer工业相机BGAPISDK和触发同步的技术背景Baumer工业相机使用BGAPISDK进行双相机主从相机触发1.引用合适的类文件2.使用BGAPISDK设置主相机硬件触发从…

C++中内存泄漏,内存溢出区别

C/C中内存泄露和内存溢出的区别 注&#xff1a;泄露为没有释放内存&#xff0c;溢出为分配空间不够&#xff0c;数据溢出了 内存溢出&#xff08;out of memory&#xff09;是指程序在申请内存时&#xff0c;没有足够的内存空间供其使用。 内存泄漏&#xff08;memory leak&…

【ROS_Driver驱动真实UR机械臂】

【ROS_Driver驱动真实UR机械臂】 1. 前言2. 安装fmauch_universal_robot和驱动3. 仿真3.1 启动gazebo3.2 启动move it规划3.3 启动rviz 4. 运行机械臂4.1 启动rviz4.2 启动示教器程序4.3 启动moveit4.4 启动rviz 5. 一些说明补充5.1 ur_calibration 提取标定信息5.2 自带程序5.…

从原理到实践:使用Mediacodec编码H265并实现解码H265码流

H265 H265&#xff0c;也称为HEVC&#xff08;High Efficiency Video Coding&#xff09;&#xff0c;是一种高效视频编码格式。它是H264&#xff08;AVC&#xff09;的后继者&#xff0c;也是ITU-T和ISO/IEC联合开发的标准。相比H264&#xff0c;H265可以在同样的视频质量下&…

【数据库原理与应用 - 第三章】数据库设计

数据库设计的步骤 需求分析阶段概念模型设计阶段 —— E-R图逻辑模型设计阶段 —— 关系模型物理结构设计阶段 数据库实施阶段数据库运行和维护阶段 目录 数据库设计的步骤 一、需求分析 1、主要任务 2、对象模型 二、数据库概念结构设计 1、概念数据模型 E-R图 1、概念…

Mybatis《学习笔记(22版尚硅谷)》

Mybatis简介 MyBatis历史 MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下&#xff0c;iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到GithubiBatis一词来源于“intern…

C语言中的for循环语句

表达式1&#xff1a;设置初始条件&#xff0c;只执行一次&#xff0c;可以为多个变量设置初始值 表达式2&#xff1a;循环条件表达式&#xff0c;判断是否集训循环 表达式3&#xff1a;执行循环体后再执行 例如&#xff0c;使用for循环打印1-10的数字首先定义整形变量a0&…

【V4L2】 v4l2框架分析之v4l2_device

&#x1f440;&#x1f449;本系列文章基于linux内核版本4.1.15分析media子系统下的V4L2。先分析组成V4L2的核心数据结构以及各组成元素的含义和作用。相关文章&#xff1a; ❤&#xff08;1&#xff09;《【V4L2】v4l2框架分析之video_device》 ❤&#xff08;2&#xff09;《…

30分钟了解并学会git的使用(绝对干货)

概述&#xff1a; 在programmer行业有一句流传许久的话&#xff1a;不会用git的程序员&#xff0c;不是真的程序员&#xff01;&#xff01;&#xff01; 足以看出来git在业界的认可度有多高&#xff0c;所以我们混这行的都应该学会这个由Linux之父林纳斯开发 的第二大发明&a…

【QT/OpenCV】Qt创建并发布OpenCV dll项目

文章目录 01、开发版本02、QT创建dll项目03、dll项目配置说明04、dll动态库测试 01、开发版本 以下所有源码与软件都是基于下面的版本。 1、QT&#xff1a; Based on Qt 5.12.2 (MSVC 2017, 32 bit) 2、OpenCV&#xff1a; OpenCV – 4.5.1 tips&#xff1a; OpenCV的版本不一…