0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何利用STM32实现酒精测试仪的设计

科技观察员 来源:hackster.io 作者:hackster.io 2022-05-13 16:40 次阅读

背景

该项目的诞生是因为观察到在大多数汽车共享服务中,人们即使喝醉了也可以开车,因为没有检查他们的状况。事实上,要驾驶汽车,您只需使用移动应用程序打开它并拿到里面的钥匙。为了解决这个问题,我创建了一个基于云的物联网呼气测醉器,连接到一个包含汽车钥匙的盒子;如果测试返回负值,则该框将打开,否则将保持关闭状态。以下是更详细的分析:IoT 设备架构、云层和 IoT 设备的 RIOT-OS 代码。

物联网设备

pYYBAGJ-GN-AM8qIAAKemmR3oIw033.png

上图显示了传感器和执行器如何连接到 SMT NUCLEO-f401re 板。

使用的传感器是超声波传感器和MQ-3酒精传感器;使用的执行器是伺服电机、三个 LED(迷你交通灯)、一个按钮和一个蜂鸣器。

超声波传感器(HC SR04):

它用于允许酒精传感器计算正确的测量值。事实上,它位于 MQ 3 传感器附近,只有当传感器与人的距离小于 5 厘米时,MQ 3 模块才会在人呼气时开始测量人的血液酒精水平。通过发送触发信号和接收回波信号来估计距离;计算出的时间(以我们为单位)除以 58 是超声波传感器前方物体的距离(以厘米为单位)。可测量2-400厘米范围内的距离,测距精度可达3毫米。一旦通过移动应用程序打开汽车(通过为系统供电),超声波传感器就会进行定期感应(每 5 秒执行一次新的测量)。当装有钥匙的盒子被打开时,传感器停止采取措施。

MQ 3 传感器:

它测量空气中酒精的浓度。其检测范围从 0.04 到 4 mg/l 酒精。它是一种金属氧化物半导体,通过改变电阻来检测周围是否存在酒精蒸汽。事实上,当酒精浓度变高时,传感器的电导率也会上升。电导率的这种变化被转换为指示酒精含量的输出值。特别是,当返回的值减去 100 大于 450 时,酒精含量被认为太高,并且框键将保持关闭状态。该传感器具有模拟输出和数字输出,但对于本项目,使用的是模拟输出。MQ 3 传感器仅在超声波传感器计算的距离小于 5 cm 时进行测量,因此可以计算出正确的测量值。

伺服电机:

伺服电机用于打开或关闭装有汽车钥匙的盒子。如果酒精传感器返回的值小于或等于 450,则该框将打开,以便取走钥匙。如果测量值大于 450,框键将保持关闭状态。

迷你红绿灯:

它具有三个 LED:红色、黄色和绿色。它们用于为超声波传感器测量的距离提供反馈。距离大于 15 厘米时红色 LED 亮;距离在5厘米至15厘米之间时黄色灯亮;当距离小于 5 厘米时,绿色会亮起。当绿色 LED 亮起时,表示该人距离传感器足够近,可以进行酒精测试,因此 MQ 3 传感器被激活并可以测量酒精水平。

按钮:

它用于关闭框键。按下时,伺服电机被激活,框键将关闭。为了将按钮连接到电路板,它使用了一个 10K 欧姆的电阻器

蜂鸣器:

它用于在呼气测醉器返回的值超出限制时提供反馈。当 MQ 3 传感器测量的值大于 450 时,蜂鸣器开启 1 秒。为了将蜂鸣器连接到电路板上,它使用了一个 1 欧姆的电阻器。

云级别

云级别完全使用 AWS 生态系统开发。在下图中,有一个架构说明了所使用的 AWS 服务如何在整个系统中连接。

pYYBAGJ-GOaAGT3fAAE4bs7n7XE438.png

物联网设备层和云端通过基于发布/订阅机制的通信协议交换消息。董事会使用 MQTT-SN 协议将酒精传感器采取的措施发送到 Mosquitto 代理。这些消息在“alcool_level”主题下发布。此外,该板订阅了主题“topic_in”以接收从外部发送的消息,这些消息用于关闭或打开包含密钥的框。Mosquitto 使用 MQTT 通过透明桥与 AWS 生态系统交换消息,这是一个 Python 脚本,用作 Mosquitto 和 AWS IoT Core 之间的桥梁。实际上,它将“alcool_level”的消息从板发布到 IoT Core,并将 IoT Core 在主题“topic_in”下发布的输入消息作为输入消息,这些消息被定向到板。然后,通过设置适当的规则,从板传到 IoT Core 的消息直接存储到 DynamoDB。然后通过调用 REST API 将它们显示在 Web 仪表板上,这会触发从数据库中获取数据的 lambda 函数(“get_data_from_db.py”)。从 Web 仪表板,可以通过在主题“topic_in”下发布消息“关闭”或消息“打开”来关闭或打开框键。消息通过调用使用另一个 lambda 函数(“publish_to_iotcore.py”)执行此操作的 REST API 发布到 IoT Core。

AWS Amplify 用于托管 Web 仪表板的所有静态 Web 内容。这会触发从数据库中获取数据的 lambda 函数(“get_data_from_db.py”)。从 Web 仪表板,可以通过在主题“topic_in”下发布消息“关闭”或消息“打开”来关闭或打开框键。消息通过调用使用另一个 lambda 函数(“publish_to_iotcore.py”)执行此操作的 REST API 发布到 IoT Core。AWS Amplify 用于托管 Web 仪表板的所有静态 Web 内容。这会触发从数据库中获取数据的 lambda 函数(“get_data_from_db.py”)。从 Web 仪表板,可以通过在主题“topic_in”下发布消息“关闭”或消息“打开”来关闭或打开框键。消息通过调用使用另一个 lambda 函数(“publish_to_iotcore.py”)执行此操作的 REST API 发布到 IoT Core。AWS Amplify 用于托管 Web 仪表板的所有静态 Web 内容。消息通过调用使用另一个 lambda 函数(“publish_to_iotcore.py”)执行此操作的 REST API 发布到 IoT Core。AWS Amplify 用于托管 Web 仪表板的所有静态 Web 内容。消息通过调用使用另一个 lambda 函数(“publish_to_iotcore.py”)执行此操作的 REST API 发布到 IoT Core。AWS Amplify 用于托管 Web 仪表板的所有静态 Web 内容。

网络仪表板上有:

两个图表用于显示:过去 7 天内一天内打开盒子钥匙的次数(MQ-3 传感器测量的值小于或等于 450)和酒精测试返回阳性的次数过去 7 天内一天的价值;

显示 MQ-3 传感器在当天采取的所有措施的表格;

用于打开或关闭框键的两个按钮;

关于过去 7 天计算的测试的一些统计数据:测试结果为阳性的最大时间段(8-12、12-17、17-20、20-24 和 00-8 之间的值);装有钥匙的盒子被打开的次数;呼气测醉器检测到超过限值的次数;阳性测试占总测试的百分比。

RIOT代码的逻辑
主要功能如下:

int main(void){
int result;

sensor_init();
mqtts_init();

while(true){
if(box_keys==0){
dist=distance_ultrasonic();

if(dist<5){
set_led("verde");
check_alcool();
}
else if(dist>=5 && dist<15){
set_led("giallo");
}
else{
set_led("rosso");
}
}
else{
while(box_keys==1){
result = gpio_read(box_pin);
if(result>0){
box_keys=0;
/*close box keys*/
servo_set(&servo, SERVO_MAX);
}
xtimer_sleep(0.5);
}
}
xtimer_sleep(5);
}
return 0;
}

如果全局变量box_keys等于 0,则意味着包含键的框已关闭,因此我们可以继续进行测量。函数distance_ultrasonic返回从超声波传感器计算的距离(以厘米为单位)。

如果距离小于 5 厘米:通过set_led("verde")函数打开迷你交通灯的绿色 LED ,用户可以继续进行酒精测试。函数check_alcool管理与测试相关的所有部分(更多细节在下面解释)。

如果距离在 5 厘米到 15 厘米之间,黄色 LED 灯亮,表示计算测试的距离差不多,但用户必须更靠近

如果距离大于 15 厘米,红色 LED 会亮起,表示距离太远,用户必须更靠近传感器才能进行酒精测试。

如果全局变量box_keys不等于 0,则表示包含键的框已打开,因此我们进入“else”块。在其值等于 1 之前,每 0.5 秒读取一次连接到按钮的引脚。如果它返回一个大于零的值(当它被按下时它返回值 256),通过用伺服电机锁定它来关闭盒子,并且变量box_keys设置为 0 以允许进入前面的“if”块下一轮 while 循环。

如果box_keys等于 0,则超声波传感器将每 5 秒感应一次,这是由于在 main while 中的“if-else”块之外设置的计时器。

下面将对 main 函数中前面提到的所有函数进行更详细的解释。

sensor_init函数:在 main 函数开始时使用,用于初始化传感器和执行器的所有 GPIO 引脚,以及伺服电机。

void sensor_init(void){
/*ultrasonic*/
gpio_init(trigger_pin, GPIO_OUT);
gpio_init_int(echo_pin, GPIO_IN, GPIO_BOTH, &call_back, NULL);
distance_ultrasonic(); /*first read returns always 0*/

/*mq3*/
adc_init(ADC_LINE(0));

/*traffic light*/
gpio_init(red_pin, GPIO_OUT);
gpio_init(yellow_pin, GPIO_OUT);
gpio_init(green_pin, GPIO_OUT);

/*button box keys*/
gpio_init(box_pin,GPIO_IN);

/*buzzer*/
gpio_init(buzzer_pin,GPIO_OUT);

/*servo init*/
servo_init(&servo, DEV, CHANNEL, SERVO_MIN, SERVO_MAX);
servo_set(&servo, SERVO_MAX);
}

用于引脚和伺服变量的所有变量都是全局的,因此它们是在函数之外定义的(您可以在项目的 GitHub 存储库中的代码中找到有关它们的更多信息)。对于 MQ 3 传感器,它被初始化为板接收值的模拟线路。用于初始化伺服电机的常量DEV、CHANNEL、SERVO_MIN、SERVO_MAX在函数外部定义。

check_alcool功能:它检查用户呼吸中的酒精含量并据此采取行动。

void check_alcool(void){
int sample = 0;
char msg[4];
sample=read_mq3();
sprintf(msg, "%d", sample);

if (sample > 450) {
gpio_set(buzzer_pin);
xtimer_sleep(1);
gpio_clear(buzzer_pin);
} else {
/*open box keys*/
servo_set(&servo, SERVO_MIN);
box_keys=1;
}
pub(TOPIC_OUT1,msg);
}

函数read_mq3返回 MQ 3 传感器计算的值,如果大于 450 表示超过法定限制,因此无法驾驶汽车。包含按键的盒子将保持关闭状态,并激活蜂鸣器 1 秒钟(蜂鸣器用于向用户提供酒精测试阳性结果的反馈)。如果传感器返回的值小于或等于 450,则打开盒子(通过伺服电机解锁盒子)并将全局变量box_keys设置为 1。在这两种情况下,由Breathalyzer 与主题“alcool_level”下的函数pub一起发布(这是常量TOPIC_OUT1的值)。

read_mq3函数:返回 MQ 3 传感器测量的值。

int read_mq3(void){
int sample = 0;
int min = 100;
sample = adc_sample(ADC_LINE(0), RES);
sample = (sample > min) ? sample - min : 0;
return sample;
}

如果传感器测量的值大于 100,则返回减去 100 的值,否则返回 0。

distance_ultrasonic函数:返回超声波传感器测量的值。

int distance_ultrasonic(void){
uint32_t dist;
dist=0;
echo_time = 0;
gpio_clear(trigger_pin);
xtimer_usleep(20);
gpio_set(trigger_pin);
xtimer_msleep(100);
if(echo_time > 0){
dist = echo_time/58;
}
return dist;
}

它向传感器发送一个脉冲并等待 100 毫秒以读取全局变量echo_time 的值。如果该值大于 0,则将其除以 58 以计算传感器前方物体的距离(以厘米为单位)。

call_back函数:它与distance_ultrasonic函数一起用于计算超声波传感器测量的值。

void call_back(void* arg){
int val = gpio_read(echo_pin);
uint32_t echo_time_stop;
(void) arg;
if(val){
echo_time_start = xtimer_now_usec();
}
else{
echo_time_stop = xtimer_now_usec();
echo_time = echo_time_stop - echo_time_start;
}
}

当检测到回显引脚上的变化时,该功能被激活。它测量从发送超声波脉冲到接收回超声波脉冲的时间差。它将值存储在全局变量echo_time中, distance_ultrasonic函数使用该变量来计算传感器前方物体的距离(以厘米为单位)。echo_time_stop也是一个全局变量。

set_led函数:用于根据传递给函数的参数设置迷你红绿灯的正确 LED。

void set_led(char *str){
if(strcmp(str,"verde")==0){
gpio_clear(red_pin);
gpio_clear(yellow_pin);
gpio_set(green_pin);
}
else if(strcmp(str,"rosso")==0){
gpio_clear(yellow_pin);
gpio_clear(green_pin);
gpio_set(red_pin);
}
else if(strcmp(str,"giallo")==0){
gpio_clear(red_pin);
gpio_clear(green_pin);
gpio_set(yellow_pin);
}
}

如果str为“verde”,则绿色 LED 亮起,其他 LED 熄灭。如果str为“giallo”,则黄色的打开,其他的关闭。如果str是“rosso”,则红色的打开,其他的关闭。

mqtts_init函数:它初始化与 MQTT-SN 代理的连接,并使用函数sub订阅主题“topic_in”(常量TOPIC_IN的值) 。

static char stack[THREAD_STACKSIZE_DEFAULT];
static msg_t queue[8];
static emcute_sub_t subscriptions[NUMOFSUBS];
static char topics[NUMOFSUBS][TOPIC_MAXLEN];

void mqtts_init(void){
/* the main thread needs a msg queue to be able to run `ping`*/
msg_init_queue(queue, ARRAY_SIZE(queue));

/* initialize our subscription buffers */
memset(subscriptions, 0, (NUMOFSUBS * sizeof(emcute_sub_t)));

/* start the emcute thread */
thread_create(stack, sizeof(stack), EMCUTE_PRIO, 0, emcute_thread, NULL, "emcute");

char * addr1 = "fec0:affe::99";
add_address(addr1);
con();
sub(TOPIC_IN);
}

以下函数用于初始化部分:

static void *emcute_thread(void *arg){
(void)arg;
emcute_run(BROKER_PORT, "board");
return NULL;
}

static int add_address(char* addr){
char * arg[] = {"ifconfig", "4", "add", addr};
return _gnrc_netif_config(4, arg);
}

static int con(void){
sock_udp_ep_t gw = { .family = AF_INET6, .port = BROKER_PORT };
char *topic = NULL;
char *message = NULL;
size_t len = 0;

ipv6_addr_from_str((ipv6_addr_t *)&gw.addr.ipv6, BROKER_ADDRESS);

if (emcute_con(&gw, true, topic, message, len, 0) != EMCUTE_OK) {
printf("error: unable to connect to [%s]:%i\n", BROKER_ADDRESS, (int)g w.port);
return 1;
}

printf("Successfully connected to gateway at [%s]:%i\n", BROKER_ADDRESS, (int)gw.port);
return 0;
}

函数sub用于订阅作为参数传递的主题。

static int sub(char* topic){
unsigned flags = EMCUTE_QOS_0;

if (strlen(topic) > TOPIC_MAXLEN) {
puts("error: topic name exceeds maximum possible size");
return 1;
}

/* find empty subscription slot */
unsigned i = 0;
for (; (i < NUMOFSUBS) && (subscriptions[i].topic.id != 0); i++) {}
if (i == NUMOFSUBS) {
puts("error: no memory to store new subscriptions");
return 1;
}

subscriptions[i].cb = on_pub;
strcpy(topics[i], topic);
subscriptions[i].topic.name = topics[i];
if (emcute_sub(&subscriptions[i], flags) != EMCUTE_OK) {
printf("error: unable to subscribe to %s\n", topic);
return 1;
}

printf("Now subscribed to %s\n", topic);
return 0;
}

当在订阅的主题(在本例中为主题“topic_in”)下接收到消息时,函数on_pub对其进行管理:

static void on_pub(const emcute_topic_t *topic, void *data, size_t len){
(void)topic;
char *in = (char *)data;
printf("### got publication for topic '%s' [%i] ###\n", topic->name, (int)topic->id);

for (size_t i = 0; i < len; i++) {
printf("%c", in[i]);
}
puts("");

char msg[len+1];
strncpy(msg, in, len);
msg[len] = '\0';
if (strcmp(msg, "open") == 0){
if(box_keys==0){
/*open box keys*/
servo_set(&servo, SERVO_MIN);
box_keys=1;
}
}
else if (strcmp(msg, "close") == 0){
if(box_keys==1){
/*close box keys*/
servo_set(&servo, SERVO_MAX);
box_keys=0;
}
}
}

如果收到的消息是“打开”,则通过伺服电机解锁包含钥匙的盒子,并将全局变量box_keys设置为 1。如果消息是“关闭”,则使用伺服电机锁定盒子,并且全局变量box_keys设置为 0。函数的第一部分用于通过在终端上打印收到的消息及其相关主题来获取反馈。

函数pub用于发布消息。

static int pub(char* topic,char* msg){
emcute_topic_t t;
unsigned flags = EMCUTE_QOS_0;

printf("pub with topic: %s and name %s and flags 0x%02x\n", topic, msg, (int)flags);

/* step 1: get topic id */
t.name = topic;
if (emcute_reg(&t) != EMCUTE_OK) {
puts("error: unable to obtain topic ID");
return 1;
}

/* step 2: publish data */
if (emcute_pub(&t, msg, strlen(msg), flags) != EMCUTE_OK) {
printf("error: unable to publish data to topic '%s [%i]'\n",t.name, (int)t.id);
return 1;
}

printf("Published %i bytes to topic '%s [%i]'\n", (int)strlen(msg), t.name, t.id);
return 0;
}

特别是,该函数的第二个参数是您要发布的消息,第一个参数是相关主题的名称。

pYYBAGJ-GNmAU6rpAAYD0481yqY874.png

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • STM32
    +关注

    关注

    2266

    文章

    10876

    浏览量

    354921
  • 酒精测试仪
    +关注

    关注

    0

    文章

    12

    浏览量

    6328
收藏 人收藏

    评论

    相关推荐

    便携式酒精测试仪电路设计

    测试仪只要被试者问由气敏元件组成的传感探头吹一口气,便可显示出被试者醉酒的深度,决定出被试者是否还适宜驾驶车辆。实用酒精测试仪的电路如图,它是实用酒精
    发表于 11-05 16:13 8833次阅读
    便携式<b class='flag-5'>酒精</b><b class='flag-5'>测试仪</b>电路设计

    【CANNON申请】蓝牙酒精测试仪

    申请理由:市场现有的各种蓝牙模块二次开发性不高,不开放,需要另加MCU才能实现功能,希望有一颗蓝牙芯片就能搞定一切(春节回家所以收货地址是老家)项目描述:我的酒精测试仪,适用于各种测试
    发表于 01-26 13:52

    简易手持式酒精测试仪电路设计与单面板pcb绘制

    的功能、特点和基本工作原理;以及完成酒精测试仪制作后电路各部分的调试方法。最终实现酒精测试仪酒精
    发表于 06-30 19:53

    如何采用89C52和MQ-3酒精浓度传感器设计酒精浓度测试仪

    基于单片机的酒精浓度测试仪的设计本文设计了一款便携式的酒精浓度测试仪,该设计方案采用的是89C52单片机和MQ-3酒精浓度传感器。待检测到气
    发表于 11-10 06:44

    酒精测试仪电路

    酒精测试仪电路 如图所示为实用酒精测试仪的电路,该测试仪只要被试者向传感器吹一口气,便可显示出醉酒的程序,确定被试者是
    发表于 11-06 12:37 4645次阅读
    <b class='flag-5'>酒精</b><b class='flag-5'>测试仪</b>电路

    智能酒精测试仪中信息处理模块的设计与测试_李耀辉

    智能酒精测试仪中信息处理模块的设计与测试,基于51。
    发表于 03-11 16:55 2次下载

    智能酒精测试仪设计研究

    ,对酒精测试仪进行了完善,能够更加贴合人手握的舒适感受;在满足有效监测饮酒驾驶的同时,充分考虑了酒精测试仪的交互方式,对手柄、连接结构及各个部件进行了材质选择、色彩搭配等具体设计工作。
    发表于 03-09 10:42 3次下载

    机械酒精耐磨测试仪的用途是什么,它的特点有哪些

    多功能酒精铅笔橡皮耐磨测试仪简介标示耐水酒精耐磨测试仪适用于按键表面、塑料外壳、涂料之涂层耐溶解性试验,常用于电子、机械、家具、印刷等行业,试验产品表面之印刷字体、标示耐水及腐蚀液体溶
    发表于 05-18 11:23 793次阅读

    基于单片机的酒精浓度测试仪的设计

    基于单片机的酒精浓度测试仪的设计本文设计了一款便携式的酒精浓度测试仪,该设计方案采用的是89C52单片机和MQ-3酒精浓度传感器。待检测到气
    发表于 11-04 21:06 40次下载
    基于单片机的<b class='flag-5'>酒精</b>浓度<b class='flag-5'>测试仪</b>的设计

    酒精测试仪原理和酒精的代谢过程

    现实生活中,我们的驾驶人经常会遇到交警对我们进行酒精测试,就是俗称的查酒驾。这一期我们就来讨论一下,酒精测试仪的原理是什么?我们喝酒后,酒精
    发表于 12-01 10:24 4090次阅读

    语音芯片在酒精测试仪的应用

    酒精气体吹气检测,可以短时间内判断是否有酒后驾车的嫌疑,主要应用于交通运输业,地下开采等,大家最常看到的就是交通警察手中拿的酒精测试仪,使用酒精
    发表于 06-23 10:12 607次阅读
    语音芯片在<b class='flag-5'>酒精</b><b class='flag-5'>测试仪</b>的应用

    技术干货 | 基于晶华微SD8114便携式酒精测试仪方案

    概述 便携式酒精含量测试仪是专用于测试人体呼出气体中酒精含量的仪器,可进行自身快速排查,帮助预判是否处于醉酒状态,给以酒驾或是醉驾警示。 便携式酒精
    发表于 10-13 17:01 817次阅读
    技术干货 | 基于晶华微SD8114便携式<b class='flag-5'>酒精</b><b class='flag-5'>测试仪</b>方案

    基于SD8114芯片的便携式酒精测试仪应用方案

    便携式酒精含量测试仪是专用于测试人体呼出气体中酒精含量的仪器,可进行自身快速排查,帮助预判是否处于醉酒状态,给以酒驾或是醉驾警示。
    发表于 10-14 11:23 1129次阅读

    技术干货 | 基于晶华微SD8114便携式酒精测试仪方案

      01·概述 便携式酒精含量测试仪是专用于测试人体呼出气体中酒精含量的仪器,可进行自身快速排查,帮助预判是否处于醉酒状态,给以酒驾或是醉驾警示。 便携式
    发表于 12-01 11:59 802次阅读
    技术干货 | 基于晶华微SD8114便携式<b class='flag-5'>酒精</b><b class='flag-5'>测试仪</b>方案

    酒精测试仪的分类和功能原理介绍

    具体的酒精测试仪设备可以按测试方式分为以下五类: 1,燃料电池型(电化学) 2、半导体型、 3,红外线型、 4,气体色谱分析型、 5,比色型。
    的头像 发表于 07-13 17:50 2465次阅读
    <b class='flag-5'>酒精</b><b class='flag-5'>测试仪</b>的分类和功能原理介绍