jjzjj

006.合宙ESP32-C3+蓝牙调试器通过BLE发送接收数据教程

魏纳海森堡的门徒 2023-12-14 原文

合宙ESP32-C3+蓝牙调试器通过BLE发送接收数据教程

一、问题提出

在平衡小车制作过程中,需要对KP/KD/KSP/KSI等PID系数进行调试,而平衡小车无法通过USB等进行有线调试,而ESP32-C3自带蓝牙+WIFI,使用WIFI比较吃算力,故选择通过蓝牙进行调参,同时能够将Angle/Encoder/PWM等数据回传至手机端进行查看。

二、合宙ESP32-C3自带蓝牙分析

前期通过查找资料,发现合宙ESP32-C3自带蓝牙不是经典蓝牙,无法使用BluetoothSerial.h进行编程,只能通过低功耗BLE使用蓝牙。
下面为需要用到的库,PIO环境自带,无需下载。

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

具体原理,没有理解明白,不在赘述。

三、蓝牙调试器分析

蓝牙调试器有3种模式,分别为对话模式、专业调试、按钮控制
需要特别注意的是其通信设置,也就是通信协议。

数据包结构为

包头数据校验和包尾
1字节bool为1字节1字节1字节
0xA50x5A

四、代码框架

/*********************头文件*********************/
#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
/************************************************/

/********************基本设置********************/
/**/    hw_timer_t *timer=NULL;
/**/    void BClock_Init(int duty_ms);
/**/    static void IRAM_ATTR Timer0_CallBack(void);  //以上为定时器
/**/    void Short2Byte(short i,uint8_t *byte);
/**/    void Int2Byte(int i,uint8_t *byte);
/**/    void Float2Byte(float f,uint8_t *byte);       //以上为数据类型转BYTE
/************************************************/

#define Step1_BlueTooth 1

/*********************蓝牙BLE********************/    
/**/#if Step1_BlueTooth
/**/    BLEServer *pServer = NULL;
/**/    BLECharacteristic * pTxCharacteristic;
/**/    bool deviceConnected = false;
/**/    bool oldDeviceConnected = false;
/**/    uint8_t txValue = 0;
/**/    #define SERVICE_UUID           "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
/**/    #define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
/**/    #define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
/**/    class MyServerCallbacks: public BLEServerCallbacks 
/**/    {
/**/        void onConnect(BLEServer* pServer) 
/**/        {
/**/            deviceConnected = true;
/**/        };
/**/    
/**/        void onDisconnect(BLEServer* pServer) 
/**/        {
/**/            deviceConnected = false;
/**/        }
/**/    };
/**/    void getBlueData(uint8_t *Value);
/**/    uint8_t modes[8];
/**/    class MyCallbacks: public BLECharacteristicCallbacks 
/**/    {
/**/        void onWrite(BLECharacteristic *pCharacteristic) 
/**/        {
/**/            std::string rxValue = pCharacteristic->getValue();
/**/            if (rxValue.length() > 0) 
/**/            {
/**/                for (int i = 0; i < rxValue.length(); i++)
/**/                {
/**/                    modes[i]=rxValue[i];
/**/                }
/**/                getBlueData(modes);
/**/            }
/**/        }
/**/    };
/**/    void BLEinit()
/**/    {
/**/        BLEDevice::init("BalanceCar");
/**/        pServer = BLEDevice::createServer();
/**/        pServer->setCallbacks(new MyServerCallbacks());
/**/        BLEService *pService = pServer->createService(SERVICE_UUID);
/**/        pTxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX,BLECharacteristic::PROPERTY_NOTIFY);                      
/**/        pTxCharacteristic->addDescriptor(new BLE2902());
/**/        BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX,BLECharacteristic::PROPERTY_WRITE);
/**/        pRxCharacteristic->setCallbacks(new MyCallbacks());
/**/        pService->start();
/**/        pServer->getAdvertising()->start();
/**/        Serial.println("BLE is OK.");
/**/    }
/**/#endif 
/************************************************/

void setup() 
{
    Serial.begin(115200);
    #if Step1_BlueTooth
        BLEinit();
    #endif
    BClock_Init(1000);  //定时器单位为ms,发送数据至手机的频率
}

//
以上不需要做任何修改
//
//+++++_____+++++_____+++++_____+++++_____+++++_____以上不需要做任何修改_____+++++_____+++++_____+++++_____+++++_____+++++//
//
以上不需要做任何修改
//





//
以下不需要做任何修改
//
//+++++_____+++++_____+++++_____+++++_____+++++_____以下不需要做任何修改_____+++++_____+++++_____+++++_____+++++_____+++++//
//
以下不需要做任何修改
//


static void IRAM_ATTR Timer0_CallBack(void)
{
    BLEsendflag=true;
}

void BClock_Init(int duty_ms)
{
    timer=timerBegin(0,80,true);
    timerAttachInterrupt(timer,Timer0_CallBack,true);
    timerAlarmWrite(timer,1000*duty_ms,true); // 单位us,定时模式,10ms
    timerAlarmEnable(timer); // 启动定时器
}

void Short2Byte(short i,uint8_t *byte)
{
    unsigned long longdata=0;
    longdata=*(unsigned long *)&i;
    byte[1]=(longdata&0xFF00)>>8;
    byte[0]=(longdata&0x00FF);
}

void Int2Byte(int i,uint8_t *byte)
{
    unsigned long longdata=0;
    longdata=*(unsigned long *)&i;
    byte[3]=(longdata&0xFF000000)>>24;
    byte[2]=(longdata&0x00FF0000)>>16;
    byte[1]=(longdata&0x0000FF00)>>8;
    byte[0]=(longdata&0x000000FF);
}

void Float2Byte(float f,uint8_t *byte)
{
    unsigned long longdata=0;
    longdata=*(unsigned long *)&f;
    byte[3]=(longdata&0xFF000000)>>24;
    byte[2]=(longdata&0x00FF0000)>>16;
    byte[1]=(longdata&0x0000FF00)>>8;
    byte[0]=(longdata&0x000000FF);
}

自己需要做的只是在中间加上 void loop()

五、手机——>单片机解码

手机发送5个数据 bool(MotorStatus)、int16_t(kp)、int16_t(kd)、int16_t(ksp)、int16_t(ksi)

/*****************蓝牙发送接收数据*****************/
/**/#if Step1_BlueTooth
/**/    bool MotorStatus;//控制电机开关
/**/    int16_t kp,kd,ksp,ksi;
/**/    //以上为手机发送来的数据
/**/    bool BLEsendflag=false;//定时发送标志
/**/#endif  
/************************************************/

void loop() 
{

}

#if Step1_BlueTooth
/**/    void getBlueData(uint8_t *Value)
/**/    {
/**/        MotorStatus=Value[1];
/**/        kp=(Value[3]<<8)+Value[2];  
/**/        kp=(Value[3]<<8)+Value[2];
/**/        kd=(Value[5]<<8)+Value[4];
/**/        ksp=(Value[7]<<8)+Value[6];
/**/        ksi=(Value[9]<<8)+Value[8];
/**/        Serial.printf("MotorStatus=%d,kp=%d,kd=%d,ksp=%d,ksi=%d\n",MotorStatus,kp,kd,ksp,ksi);
/**/    }
#endif 

六、单片机——>手机编码

单片机发送3个数据 short(PwmOut)、int(Encode_L)、float(Angle)

/*****************蓝牙发送接收数据*****************/
/**/#if Step1_BlueTooth
/**/    bool BLEsendflag=false;//定时发送标志/**/    
/**/    uint8_t BLEBUF[13];// 数据包的顺序为BOOL(1)/BYTE(1)/SHORT(2)/INT(4)/FLOAT4)
/**/    short PwmOut=-12;
/**/    int Encode_L=-34;
/**/    float Angle=5.6;
/**/#endif  
/************************************************/



void loop() 
{
    if(deviceConnected&BLEsendflag)
    {
        BLEsendflag=false;
        BLEBUF[0]=0xA5;//包头
        Short2Byte(PwmOut,&BLEBUF[1]);
        Int2Byte(PwmOut,&BLEBUF[3]);
        Float2Byte(Angle,&BLEBUF[7]);
        BLEBUF[11]=(uint8_t)((BLEBUF[1]+BLEBUF[2]+BLEBUF[3]+BLEBUF[4]+BLEBUF[5]+BLEBUF[6]+BLEBUF[7]+BLEBUF[8]+BLEBUF[9]+BLEBUF[10])&0xFF);
        BLEBUF[12]=0x5A;//包尾
        pTxCharacteristic->setValue(BLEBUF,13);
        pTxCharacteristic->notify();
    }
}

有关006.合宙ESP32-C3+蓝牙调试器通过BLE发送接收数据教程的更多相关文章

  1. ruby-on-rails - 无法让 rspec、spork 和调试器正常运行 - 2

    GivenIamadumbprogrammerandIamusingrspecandIamusingsporkandIwanttodebug...mmm...let'ssaaay,aspecforPhone.那么,我应该把“require'ruby-debug'”行放在哪里,以便在phone_spec.rb的特定点停止处理?(我所要求的只是一个大而粗的箭头,即使是一个有挑战性的程序员也能看到:-3)我已经尝试了很多位置,除非我没有正确测试它们,否则会发生一些奇怪的事情:在spec_helper.rb中的以下位置:require'rubygems'require'spork'

  2. jquery - 我的 jquery AJAX POST 请求无需发送 Authenticity Token (Rails) - 2

    rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送

  3. ruby - JetBrains RubyMine 3.2.4 调试器不工作 - 2

    使用Ruby1.9.2运行IDE提示说需要gemruby​​-debug-base19x并提供安装它。但是,在尝试安装它时会显示消息Failedtoinstallgems.Followinggemswerenotinstalled:C:/ProgramFiles(x86)/JetBrains/RubyMine3.2.4/rb/gems/ruby-debug-base19x-0.11.30.pre2.gem:Errorinstallingruby-debug-base19x-0.11.30.pre2.gem:The'linecache19'nativegemrequiresinstall

  4. ruby - 使用 Ruby 通过 Outlook 发送消息的最简单方法是什么? - 2

    我的工作要求我为某些测试自动生成电子邮件。我一直在四处寻找,但未能找到可以快速实现的合理解决方案。它需要在outlook而不是其他邮件服务器中,因为我们有一些奇怪的身份验证规则,我们需要保存草稿而不是仅仅发送邮件的选项。显然win32ole可以做到这一点,但我找不到任何相当简单的例子。 最佳答案 假设存储了Outlook凭据并且您设置为自动登录到Outlook,WIN32OLE可以很好地完成此操作:require'win32ole'outlook=WIN32OLE.new('Outlook.Application')message=

  5. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  6. ruby - Ruby 是否有类似于 Perl 的 "perl -d"的逐步调试器? - 2

    Ruby是否有逐步调试器,类似于Perl的“perl-d”? 最佳答案 ruby-debug(对于ruby1.8),debugger(对于ruby1.9),byebug(对于ruby​​2.0)以及trepanning系列都有一个-x或--trace选项。在调试器内部,命令setlinetrace将打开或关闭线路跟踪。这是themanualforruby-debug原来的答案已经修改,因为数据噪声文章的链接,唉,不再有效了。还添加了ruby​​-debug的后继者 关于ruby-Ruby

  7. ruby - 是否可以在不实际发送或读取数据的情况下查明 ruby​​ 套接字是否处于 ESTABLISHED 或 CLOSE_WAIT 状态? - 2

    s=Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0)s.connect(Socket.pack_sockaddr_in('port','hostname'))ssl=OpenSSL::SSL::SSLSocket.new(s,sslcert)ssl.connect从这里开始,如果ssl连接和底层套接字仍然是ESTABLISHED,或者它是否在默认值7200之后进入CLOSE_WAIT,我想检查一个线程几秒钟甚至更糟的是在实际上不需要.write()或.read()的情况下关闭。是用select()、IO.select()还是其他方法完成

  8. ruby - 如何理解 Ruby 中的发送者和接收者? - 2

    我很难理解Ruby中sender和receiver的实际含义。它们一般是什么意思?到目前为止,我只是将它们理解为方法调用和获取其返回值的调用。但是,我知道我的理解还远远不够。谁能给我一个Ruby中发送者和接收者的具体解释? 最佳答案 面向对象中的一个核心概念是消息传递和早期概念化,这在很大程度上借鉴了计算的Actor模型。艾伦·凯(AlanKay)创造了面向对象一词并发明了最早的OO语言之一SmallTalk,他拥有voicedregretatusingatermwhichputthefocusonobjectsinsteadofo

  9. ruby - 动态扩展现有方法或覆盖 ruby​​ 中的发送方法 - 2

    假设我们有A、B、C类。Adefself.inherited(sub)#metaprogramminggoeshere#takeclassthathasjustinheritedclassA#andforfooclassesinjectprepare_foo()as#firstlineofmethodthenrunrestofthecodeenddefprepare_foo#=>prepare_foo()neededhere#somecodeendendBprepare_foo()neededhere#somecodeendend如您所见,我正在尝试将foo_prepare()调用注入

  10. ruby-on-rails - 如何通过 POST 发送多个相同的键/参数? - 2

    如果我必须在一个HTTP请求中发送一堆post参数,所有这些参数都具有相同的名称,我该如何构建要发布的data对象?想象一个带有一些复选框的表单,它们都具有相同的name属性但具有不同的值(如果它们被选中):我想用ruby​​构建它(但它需要根据在表单上选择的内容动态创建):data={"color"=>"red","color"=>"green","color"=>"blue"}然后将数据发送到某个URL:Net::HTTP.post_form(url,data)我无法控制接收端,所以我必须发送它期望接收的参数。怎么办? 最佳答案

随机推荐