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

前期通过查找资料,发现合宙ESP32-C3自带蓝牙不是经典蓝牙,无法使用BluetoothSerial.h进行编程,只能通过低功耗BLE使用蓝牙。
下面为需要用到的库,PIO环境自带,无需下载。
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
具体原理,没有理解明白,不在赘述。
蓝牙调试器有3种模式,分别为对话模式、专业调试、按钮控制
需要特别注意的是其通信设置,也就是通信协议。


数据包结构为
| 包头 | 数据 | 校验和 | 包尾 |
|---|---|---|---|
| 1字节 | bool为1字节 | 1字节 | 1字节 |
| 0xA5 | 0x5A |
/*********************头文件*********************/
#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();
}
}
GivenIamadumbprogrammerandIamusingrspecandIamusingsporkandIwanttodebug...mmm...let'ssaaay,aspecforPhone.那么,我应该把“require'ruby-debug'”行放在哪里,以便在phone_spec.rb的特定点停止处理?(我所要求的只是一个大而粗的箭头,即使是一个有挑战性的程序员也能看到:-3)我已经尝试了很多位置,除非我没有正确测试它们,否则会发生一些奇怪的事情:在spec_helper.rb中的以下位置:require'rubygems'require'spork'
rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送
使用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
我的工作要求我为某些测试自动生成电子邮件。我一直在四处寻找,但未能找到可以快速实现的合理解决方案。它需要在outlook而不是其他邮件服务器中,因为我们有一些奇怪的身份验证规则,我们需要保存草稿而不是仅仅发送邮件的选项。显然win32ole可以做到这一点,但我找不到任何相当简单的例子。 最佳答案 假设存储了Outlook凭据并且您设置为自动登录到Outlook,WIN32OLE可以很好地完成此操作:require'win32ole'outlook=WIN32OLE.new('Outlook.Application')message=
文章目录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.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
Ruby是否有逐步调试器,类似于Perl的“perl-d”? 最佳答案 ruby-debug(对于ruby1.8),debugger(对于ruby1.9),byebug(对于ruby2.0)以及trepanning系列都有一个-x或--trace选项。在调试器内部,命令setlinetrace将打开或关闭线路跟踪。这是themanualforruby-debug原来的答案已经修改,因为数据噪声文章的链接,唉,不再有效了。还添加了ruby-debug的后继者 关于ruby-Ruby
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()还是其他方法完成
我很难理解Ruby中sender和receiver的实际含义。它们一般是什么意思?到目前为止,我只是将它们理解为方法调用和获取其返回值的调用。但是,我知道我的理解还远远不够。谁能给我一个Ruby中发送者和接收者的具体解释? 最佳答案 面向对象中的一个核心概念是消息传递和早期概念化,这在很大程度上借鉴了计算的Actor模型。艾伦·凯(AlanKay)创造了面向对象一词并发明了最早的OO语言之一SmallTalk,他拥有voicedregretatusingatermwhichputthefocusonobjectsinsteadofo
假设我们有A、B、C类。Adefself.inherited(sub)#metaprogramminggoeshere#takeclassthathasjustinheritedclassA#andforfooclassesinjectprepare_foo()as#firstlineofmethodthenrunrestofthecodeenddefprepare_foo#=>prepare_foo()neededhere#somecodeendendBprepare_foo()neededhere#somecodeendend如您所见,我正在尝试将foo_prepare()调用注入
如果我必须在一个HTTP请求中发送一堆post参数,所有这些参数都具有相同的名称,我该如何构建要发布的data对象?想象一个带有一些复选框的表单,它们都具有相同的name属性但具有不同的值(如果它们被选中):我想用ruby构建它(但它需要根据在表单上选择的内容动态创建):data={"color"=>"red","color"=>"green","color"=>"blue"}然后将数据发送到某个URL:Net::HTTP.post_form(url,data)我无法控制接收端,所以我必须发送它期望接收的参数。怎么办? 最佳答案