基于ESP32的智能运动装置

1. 项目的背景

1.1 项目的意义

  随着科技的进度,人们对于健康的要求越来越高,对于体育运动的训练也越来越重视。对于运动装备的智能化已经成为大家的考虑范围。因此基于ESP32的智能运动装置便成为一个引人注目的课题。
  •提升运动体验:智能运动装置结合了传感器技术、数据分析和语音播报等先进技术,可以根据具体的运动去做相应的调整,实现运动动作正确性检测、播报、运动数据上云等等。
  •促进科技与体育的结合:智能运动装置推动科技与体育的融合。通过先进的传感器技术和数据处理以及云技术,不仅可以提升棒球运动的水平和质量,还有助于推动体育科技的发展和创新。这对于激发年轻人对科技和体育的兴趣,促进体育产业的发展具有重要意义。

1.2 需求分析

1.2.1硬件需求

1. MPU6050模块
在这里插入图片描述

    MPU6050模块可以获得加速度值、饶X、Y、Z三个轴的角速度值,分别为ACC_X、ACC_Y、ACC_ZGYR_X、GYR_Y、GYR_Z,还可以通过四元数的格式给出。其中MPU6050通过IIC与ESP32通信。
Tips :四元数主要应用于机器人的姿态解算。
2. ESP32主控板
在这里插入图片描述

    ESP32具有很强的功能,主控芯片具体的配置如下:

  • 处理器:Tensilica LX6 双核处理器(一核处理高速连接;一核独立应用开发)
  • 主频:32 位双核处理器,CPU 正常工作速度为 80 MHz,最高可达 240 MHz
  • SRAM:520KB,最大支持 8 MB 片外 SPI SRAM
  • Flash:最大支持 16 MB 片外 SPI Flash
  • WiFi 协议:支持 802.11 b/g/n/d/e/i/k/r 等协议,速度高达150 Mbps
  • 蓝牙协议:支持蓝牙 v4.2 完整标准,包含传统蓝牙 (BR/EDR) 和低功耗蓝牙 (BLE)
  • 同时他还具备丰富的外设接口:比如 GPIO、ADC、DAC、SPI、I²C、I²S、UART 等常用接口。
    其中,本发明的ESP32用于读取MPU6050的IIC数据和压力传感器ADC数值,经过数据处理之后,可以给ASRPRO语音模块发送串口信号,实现语音播报;同时也向blinker云服务器发送数据,实现数据上云。

3. 薄膜压力传感器
在这里插入图片描述

    本项目使用的是薄膜压力应变片,需要配合相应的电路使用,这里接了33k上拉电阻,通过ESP32的ADC通道读取读取传感器的分压,将压力的变化转换成电信号的变化传递给ESP32主控板。

4. 具有蓝牙和wifi的手机
在这里插入图片描述

    手机的功能主要是下载blinker客户端,用于实现ESP32的wifi和蓝牙接入,从而实现数据在手机端端实时查看。

2. 技术路线

   电子部分有ESP32主控板、ASR PRO语音模块、降压模块、薄膜压力传感器和传感器电路、MPU6050等模块。通过ESP32主控板,可以完成击球旋转的检测、击球位置的检测、发送相关的语音控制信号给ASR PRO语音模块。通过MPU6050可以根据加速度和角度信息完成击球旋转的检测。通过薄膜压力传感器和相关的电阻上拉电路完成ADC模拟量的读取,以实现击球位置的检测,并将击球位置的信号发给语音模块ASR PRO。

3. 软件和硬件设计

3.1 薄膜压力传感器ADC取样电路

在这里插入图片描述

   本文采用薄膜压力传感器获取压力,而压力应变片本身是一个可变的电阻,加之本发明对于压力只需要知道变化情况即可,为了方便起见,采用ESP32等ADC通道读取压力传感器的分压,从而知道压力的大小变化情况。
为此,本文设计了采样电路,主要是将薄膜压力应变片接上拉电阻。

3.2 代码讲解

   这里是ESP32的代码,下面会对代码进行详细讲解。

#if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_RUNNING_CORE 1
#endif

#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif

#include <MPU6050_tockn.h>
#include <Button.h>
#define BLINKER_BLE
#include <Blinker.h>
#include <Wire.h>

#define SDA 25
#define SCL 26


Button button1(27);
MPU6050 mpu6050(Wire);

// char auth[] = "a93660bb7639";
// char ssid[] = "maegae";
// char pswd[] = "awhkd8nfje2b7";

BlinkerNumber Number1("Total");
BlinkerNumber Number2("Success");
BlinkerNumber Number3("ContactPoint");

//int value[]={};
int flag1=1;
int flag2=1;
int done=0;
// unsigned long time1=0;
// unsigned long time2=0;
float total=0;
float fail=0;
int contactpoint=0; //6.15

int i=0;

void dataRead(const String & data)
{
    BLINKER_LOG("Blinker readString: ", data);
    
}

// define two tasks for Blink & AnalogRead
void TaskBlink( void *pvParameters );
void TaskAnalogReadA3( void *pvParameters );

// the setup function runs once when you press reset or power the board
void setup() {
  
  // initialize serial communication at 115200 bits per second:
  BLINKER_DEBUG.stream(Serial);
  Blinker.begin();
  button1.begin();
  Serial.begin(115200);
  Serial2.begin(9600);
  Wire.begin(SDA,SCL);
  mpu6050.begin();
  mpu6050.calcGyroOffsets(true);
  
  // Now set up two tasks to run independently.
  xTaskCreatePinnedToCore(
    TaskBlink
    ,  "TaskBlink"   // A name just for humans
    ,  2048  // This stack size can be checked & adjusted by reading the Stack Highwater
    ,  NULL
    ,  2  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,  NULL 
    ,  ARDUINO_RUNNING_CORE);

  xTaskCreatePinnedToCore(
    TaskAnalogReadA3
    ,  "AnalogReadA3"
    ,  1024  // Stack size
    ,  NULL
    ,  1  // Priority
    ,  NULL 
    ,  ARDUINO_RUNNING_CORE);
}

void loop()
{
  Blinker.run();
}


void TaskBlink(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  for (;;) // A Task shall never return or exit.
  {
  
  int adc1=analogRead(32);
  int adc2=analogRead(35);
  int adc3=analogRead(34);
  Serial.println(adc1);
  Serial.println(adc2);
  Serial.println(adc3);
  if(adc1!=4095||adc2!=4095||adc3!=4095)
  {
  if(flag2==0&&adc1<=adc2&&adc2<=adc3)
  {
    Serial.print("a");
    Serial2.print("a");
    flag2=1;
    done=1;
    contactpoint=1; 
    Number3.print(contactpoint);
    delay(5);
  }
  else if(flag2==0&&adc1<=adc3&&adc3<=adc2)
  {
    Serial.print("a");
    Serial2.print("a");
    flag2=1;
    done=1;
    contactpoint=1; 
    Number3.print(contactpoint);
    delay(5);
  }
  else if(flag2==0&&adc2<=adc1&&adc1<=adc3)
  {
    Serial.print("b");
    Serial2.print("b");
    flag2=1;
    done=1;
    contactpoint=2; 
    Number3.print(contactpoint);
    delay(5);
  }
  else if(flag2==0&&adc2<=adc3&&adc3<=adc1)
  {
    Serial.print("b");
    Serial2.print("b");
    flag2=1;
    done=1;
    contactpoint=2; 
    Number3.print(contactpoint);
    delay(5);
  }
  else if(flag2==0&&adc3<=adc1&&adc1<=adc2)
  {
    Serial.print("c");
    Serial2.print("c");
    flag2=1;
    done=1;
    contactpoint=3; 
    Number3.print(contactpoint);
    delay(5);
  }
  else if(flag2==0&&adc3<=adc2&&adc2<=adc1)
  {
    Serial.print("c");
    Serial2.print("c");
    flag2=1;
    done=1;
    contactpoint=3; 
    Number3.print(contactpoint);
    delay(5);
  }
  }

  if (button1.released())
  {
    if(total<5)
    {
      total+=1;
      flag1=0;
      flag2=0;
      done=0;
      Serial2.print("begin");
      Number1.print(total);
      delay(5);
    }
  }

  if (flag1==0 && mpu6050.getAngleY()<=-65 && done==0)
    {
      Serial2.print("false");
      Serial.println("false");
      flag1=1;
      fail+=1;
    }
    if(total>=5 && done==1)
  {
    Serial.println(total);
    Serial.println(fail);
    float suc=(total-fail)/total;
    Serial.println(int(suc*100));
    Serial2.print("w");
    Serial2.print(int(((total-fail)/(total))*100));
    Number2.print(int(((total-fail)/(total))*100));
    delay(10);
    total=0;
    fail=0;
    Number1.print(total);
    delay(5);
  }
  delay(100);
  }
}

void TaskAnalogReadA3(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  for (;;)
  {
    mpu6050.update();
    vTaskDelay(10);  // one tick delay (15ms) in between reads for stability
  }
}

   由于本项目需要完成串口和IIC通信,还需要完成数据处理的功能以及数据上云的功能,涉及的任务量比较多,因此需要考虑ESP32的多核任务调度的问题,以实现运算资源的合理分配。为此,本项目采用FreeRTOS实时操作系统去分配任务。定义了两个TASK,一个是完成MPU6050模块的更新;另一个TASK是完成ADC采样以及分析旋转和击打位置,并完成Blinker上云和语音模块的播报。
Tips: ESP32的FreeRTOS可以进行任务的分配,可以定义堆栈的大小以及任务的优先级。!!! 注意:堆栈大小不能随意调整,以防出现堆栈溢出的情况。每个任务都要配有延时,以确保任务调度的正常进行。
   这里是ASR PRO语音模块的代码。

#include "asr.h"
extern "C"{ void * __dso_handle = 0 ;}
#include "setup.h"
#include "HardwareSerial.h"
#include "myLib/asr_event.h"

uint32_t snid;
String Rec;
void hardware_init();
void UART_RX();
void UART1_RX();
void ASR_CODE();

//{ID:250,keyword:"命令词",ASR:"最大音量",ASRTO:"音量调整到最大"}
//{ID:251,keyword:"命令词",ASR:"中等音量",ASRTO:"音量调整到中等"}
//{ID:252,keyword:"命令词",ASR:"最小音量",ASRTO:"音量调整到最小"}
void hardware_init(){
  Rec = "";
  xTaskCreate(UART_RX,"UART_RX",128,NULL,4,NULL);
  xTaskCreate(UART1_RX,"UART1_RX",256,NULL,4,NULL);
  vTaskDelete(NULL);
}

void UART_RX(){
  while (1) {
    if(Serial.available() > 0){
      Rec = Serial.readString();
      if(Rec == "hello"){
        Serial.print(Rec);
        delay(200);
        enter_wakeup(5000);
        delay(200);
        //{ID:500,keyword:"命令词",ASR:"耍接官",ASRTO:"收到ESP32信息"}
        play_audio(500);
      }
    }
    delay(20);
  }
  vTaskDelete(NULL);
}

void UART1_RX(){
  while (1) {
    if(Serial1.available() > 0){
      Rec = Serial1.readString();
      Serial.println(Rec);
      if(Rec == "false"){
        Serial.print(Rec);
        delay(200);
        enter_wakeup(100000);
        delay(200);
        //{ID:501,keyword:"命令词",ASR:"接耍官",ASRTO:"击球发生旋转,请改正姿势"}
        play_audio(501);
      }
      else if(Rec == "begin"){
        delay(200);
        enter_wakeup(100000);
        delay(200);
        //{ID:502,keyword:"命令词",ASR:"官耍接",ASRTO:"开始击球"}
        play_audio(502);
      }
      else if(Rec == "a"){
        Serial.print(Rec);
        delay(200);
        enter_wakeup(100000);
        delay(200);
        //{ID:503,keyword:"命令词",ASR:"丝耍接",ASRTO:"击球位置偏高"}
        play_audio(503);
        //play_num((int64_t)((uint8_t)Rec[2] * 100), 1);
      }
      else if(Rec == "b"){
        Serial.print(Rec);
        delay(200);
        enter_wakeup(100000);
        delay(200);
        //{ID:504,keyword:"命令词",ASR:"粮耍接",ASRTO:"好球"}
        play_audio(504);
      }
      else if(Rec == "c"){
        Serial.print(Rec);
        delay(200);
        enter_wakeup(100000);
        delay(200);
        //{ID:505,keyword:"命令词",ASR:"菌耍接",ASRTO:"击球位置偏低"}
        play_audio(505);
      }
      
      if(isdigit(Rec[2]))
      {
        if(Rec[0]=="a")
        {
          Serial.print(Rec);
          delay(200);
          enter_wakeup(100000);
          delay(200);
          
          play_audio(503);
          //play_num((int64_t)((uint8_t)Rec[2] * 100), 1);
        }
        else if(Rec[0]=="b")
        {
          Serial.print(Rec);
          delay(200);
          enter_wakeup(100000);
          delay(200);
     
          play_audio(504);
        }
        else if(Rec[0]=="c")
        {
          Serial.print(Rec);
          delay(200);
          enter_wakeup(100000);
          delay(200);
          
          play_audio(505);
        }
        delay(10);
        int len = sizeof(Rec);
        int n = len-2;
        String broadcast=Rec.substring(2,n);
        char cbroadcast[10];
        strcpy(cbroadcast,broadcast.c_str());
        Serial.println(atoi(cbroadcast));
        //{ID:506,keyword:"命令词",ASR:"耍接官",ASRTO:"击球成功率"}
        play_audio(506);
        delay(20);
        play_num((int64_t)((uint8_t)(atoi(cbroadcast)) * 100), 1);
        // char broadcast[n+1];
        // strncpy(broadcast, Rec+2, n);
        // //memcpy(broadcast,Rec+3,n);
        // broadcast[n] = '\0';
        //Serial.print(atoi(broadcast));
        //delay(200);
      }
    }
    delay(20);
  }
  vTaskDelete(NULL);
}

/*描述该功能...
*/
void ASR_CODE(){
  switch (snid) {
   case 1:
    digitalWrite(4,0);
    break;
   case 2:
    digitalWrite(4,1);
    break;
  }

}

void setup()
{
  //{speak:小蝶-清新女声,vol:10,speed:10,platform:haohaodada}
  //{playid:10001,voice:欢迎使用智能棒球棒}
  //{playid:10002,voice:我退下了,请用小棒唤醒我}
  //{ID:0,keyword:"唤醒词",ASR:"天问五幺",ASRTO:"我在"}
  //{ID:1,keyword:"命令词",ASR:"打开灯光",ASRTO:"好的,马上打开灯光"}
  setPinFun(13,SECOND_FUNCTION);
  setPinFun(14,SECOND_FUNCTION);
  Serial.begin(9600);
  setPinFun(2,FORTH_FUNCTION);
  setPinFun(3,FORTH_FUNCTION);
  Serial1.begin(9600);
  xTaskCreate(hardware_init,"hardware_init",256,NULL,100,NULL);

  //{ID:84,keyword:"命令词",ASR:"条耍改",ASRTO:"零"}
  //{ID:85,keyword:"命令词",ASR:"官接思",ASRTO:"一"}
  //{ID:86,keyword:"命令词",ASR:"痛官松",ASRTO:"二"}
  //{ID:87,keyword:"命令词",ASR:"削丝误",ASRTO:"三"}
  //{ID:88,keyword:"命令词",ASR:"景粮载",ASRTO:"四"}
  //{ID:89,keyword:"命令词",ASR:"博菌避",ASRTO:"五"}
  //{ID:90,keyword:"命令词",ASR:"裁纯碉",ASRTO:"六"}
  //{ID:91,keyword:"命令词",ASR:"插趣悟",ASRTO:"七"}
  //{ID:92,keyword:"命令词",ASR:"辞暖慌",ASRTO:"八"}
  //{ID:93,keyword:"命令词",ASR:"纵猛淡",ASRTO:"九"}
  //{ID:94,keyword:"命令词",ASR:"锦耗暂",ASRTO:"十"}
  //{ID:95,keyword:"命令词",ASR:"燃智截",ASRTO:"百"}
  //{ID:96,keyword:"命令词",ASR:"佛驻延",ASRTO:"千"}
  //{ID:97,keyword:"命令词",ASR:"隔枪绍",ASRTO:"万"}
  //{ID:98,keyword:"命令词",ASR:"惨愤昂",ASRTO:"亿"}
  //{ID:99,keyword:"命令词",ASR:"丙迈扯",ASRTO:"负"}
  //{ID:100,keyword:"命令词",ASR:"铸猜隆",ASRTO:"点"}
}

4. 排坑笔记

4.1 ADC通道与Wi-Fi的冲突问题:


!!!请注意,ADC2通道在ESP32开启WIFI后,功能会受到限制,据说是WIFI需要ADC2通道的IO口去驱动,建议使用ADC1通道!上图展示了ADC1和ADC2通道。

4.2字符串的处理

   ESP32串口给ASRPRO发送串口数据时,ASRPRO是一个字节读取的,并且存储的类型是String,为了对字符串进行便捷的处理操作,需要借助C语言库函数,但是 C语言库函数只支持char类型的处理,因此需要考虑String和char类型的转换。
具体的实现流程如下:

int len = sizeof(Rec);
int n = len-2;
String broadcast=Rec.substring(2,n);
char cbroadcast[10];
strcpy(cbroadcast,broadcast.c_str());
Serial.println(atoi(cbroadcast));

TIPS: ASR PRO语音模块接到的是Rec(是一个String型),需要用到substring()去取某些位,然后使用strcpy()函数做字符串的copy,其中要使用到c_str()String型转char类型

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于ESP32智能门锁是一种融合物联网技术的智能门锁。该系统通过ESP32模块将门锁与互联网连接起来,实现远程控制和智能化的功能。 首先,ESP32模块作为核心部件,具备稳定的Wi-Fi或蓝牙连接能力,能够与智能手机、平板电脑等设备进行通信。用户可以通过手机APP或Web界面与门锁进行互动。 其次,智能门锁配备多种解锁方式,如密码、指纹识别、IC卡、远程开锁等。用户可以根据自己的需求和习惯来选择合适的解锁方式,提高门锁的安全性和便利性。 此外,智能门锁还支持远程控制功能。用户可以通过手机APP远程开锁,无需亲自到现场,便捷实用。而且可以对授权的用户进行管理,确保安全性。 同时,智能门锁还具备智能化的功能。它可以记录每一次的开锁记录,用户可以随时查看开锁日志,轻松掌握门锁的使用情况。一旦发生异常操作,还可以自动报警或发送通知,确保房屋的安全。 基于ESP32智能门锁的设计还可以与其他智能家居设备进行联动,实现更智能化的场景应用。例如,门锁与智能灯光、智能家电等设备联动,当打开门锁时自动开启灯光或调节室内温度等。 总之,基于ESP32智能门锁的设计将传统的门锁升级为具备远程控制、智能化和联动功能的智能门锁,提升安全性和便利性,让用户体验更加智能化和舒适化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值