学习记录:ESP32控制舵机 FREERTOS BLE

控制舵机

PWM信号

PWM信号是一种周期性变化的方波信号,它有两个关键参数:

  1. 周期(Period):一个完整的PWM信号的时间长度,通常用秒(s)或毫秒(ms)表示。
  2. 占空比(Duty Cycle):高电平(ON状态)持续时间与周期的比值,通常以百分比表示。占空比决定了信号的平均功率。

脉宽

脉宽(Pulse Width)是指PWM信号中高电平的持续时间。它与占空比密切相关,脉宽越长,占空比越大;脉宽越短,占空比越小。

PWM信号的工作原理

PWM信号通过快速切换开关,在一个固定周期内改变脉冲的宽度,以达到控制功率输出的目的。比如在电机控制中,通过调整PWM信号的占空比,可以控制电机的转速。占空比越高,电机转速越快;占空比越低,电机转速越慢。

例子说明

假设我们有一个周期为10毫秒的PWM信号:

  1. 占空比为50%

    • 脉宽:5毫秒
    • 效果:在10毫秒的周期中,5毫秒为高电平,5毫秒为低电平。
  2. 占空比为25%

    • 脉宽:2.5毫秒
    • 效果:在10毫秒的周期中,2.5毫秒为高电平,7.5毫秒为低电平。
  3. 占空比为75%

    • 脉宽:7.5毫秒
    • 效果:在10毫秒的周期中,7.5毫秒为高电平,2.5毫秒为低电平。

代码# include <ESP32Servo.h>
Servo servo1;  // 定义servo对象
Servo servo2;  // 定义servo对象
int minUs=500;  //0度时的脉宽,单位us
int maxUs=2500; //180度时的脉宽,单位us
int servo1Pin=15;
int servo2Pin=16;
int pos=-1;
bool up=true;void setup() {ESP32PWM::allocateTimer(1);//使用指定的硬件定时器servo1.setPeriodHertz(50);//指定PWM的频率servo2.setPeriodHertz(50);//指定PWM的频率servo1.attach(servo1Pin,minUs,maxUs);servo2.attach(servo2Pin,minUs,maxUs);// servo1.write(pos);//转到指定的角度(0~180)// servo1.detach();//不需要的时候将引脚和ledc分离
}void loop() {if(pos == 181)up=false;else if(pos==-1)up=true;if(up)pos++;elsepos--;servo1.write(pos);servo2.write(180-pos);delay(15);
}
1

FREERTOS


void task1(void *pt){pinMode(47,OUTPUT);while(1){digitalWrite(47,!digitalRead(47));// delay(1000);vTaskDelay(1000);}
}void task2(void *pt){pinMode(42,OUTPUT);while(1){digitalWrite(42,!digitalRead(42));vTaskDelay(2000);}
}
void setup() {xTaskCreate(task1,"Blink,47,19",1024,NULL,1,NULL);xTaskCreate(task2,"Blink,42,39",1024,NULL,1,NULL);
}void loop() {}

  1. 任务堆栈大小的单位

在 FreeRTOS 中,任务堆栈大小的单位通常是字(word),而不是字节(byte)。在大多数微控制器平台上,一个字是 4 字节(32 位)。因此,当你指定堆栈大小时,实际上是指定了多少个 4 字节的块。

示例:

如果你指定 configMINIMAL_STACK_SIZE 为 128,则实际上分配的堆栈空间是 128 * 4 = 512 字节。

2. 优先级的作用

任务优先级决定了任务的执行顺序和调度策略。在 FreeRTOS 中,数值越高的优先级表示任务的重要性越高。任务调度器会优先执行优先级高的任务。当多个任务具有相同优先级时,它们将以时间片轮转的方式执行。

示例:

  • 任务 A 优先级为 2

  • 任务 B 优先级为 1

在这种情况下,任务 A 会优先于任务 B 执行。如果任务 A 阻塞或进入等待状态,任务 B 才会执行。

3.任务句柄的使用

任务句柄(Task Handle)用于在运行时引用特定任务。你可以使用任务句柄来挂起、恢复或删除任务。

l示例:

c
复制代码
#include <FreeRTOS.h>#include <task.h>#include <stdio.h>// 任务函数声明void vTaskFunction(void *pvParameters);// 全局变量,用于存储任务句柄
TaskHandle_t taskHandle = NULL;int main(void)
{xTaskCreate(vTaskFunction,       // 任务函数"Task 1",            // 任务名称configMINIMAL_STACK_SIZE, // 任务堆栈大小NULL,                // 任务参数tskIDLE_PRIORITY + 1, // 任务优先级&taskHandle          // 任务句柄);
// 启动调度器vTaskStartScheduler();
// 正常情况下,不会运行到这里for(;;);
}void vTaskFunction(void *pvParameters)
{for(;;){printf("Task is running\n");vTaskDelay(1000 / portTICK_PERIOD_MS);
// 示例:挂起任务vTaskSuspend(taskHandle);
// 示例:恢复任务vTaskResume(taskHandle);}
}

蓝牙BLE

  1. 服务器(Server)

服务器是 BLE 外设设备的一部分,负责管理多个服务并处理与中央设备的连接和通信。服务器本身不直接包含数据,而是通过服务和特性来组织和传输数据。

  • 角色:在 BLE 通信中,服务器(通常是传感器设备)提供数据和功能,而中央设备(通常是手机或电脑)请求和使用这些数据和功能。

  1. 服务(Service)

服务是服务器中用于组织和描述特定功能的一组相关特性。每个服务都有一个唯一的标识符(UUID),用来标识和区分不同的服务。

  • 角色:服务是功能的逻辑集合。例如,一个心率监测设备可能有一个“心率服务”,包含心率测量、心率控制点等特性。

  1. 特性(Characteristic)

特性是服务中的基本数据单元,包含实际的数据和相关属性。每个特性也有一个唯一的标识符(UUID),用于标识特性。特性可以包含一个值和零个或多个描述符。

  • 角色:特性是传输数据的具体单元。例如,“心率服务”中的“心率测量”特性可能包含当前的心率值。

它们之间的关系

  • 服务器包含多个服务

  • 每个服务包含多个特性

  • 每个特性包含一个值和零个或多个描述符。

图示关系
scss
复制代码
服务器 (Server)├── 服务1 (Service 1, UUID: 0000180f-0000-1000-8000-00805f9b34fb)│     ├── 特性1 (Characteristic 1, UUID: 00002a19-0000-1000-8000-00805f9b34fb)│     │     └── 描述符1 (Descriptor 1)│     └── 特性2 (Characteristic 2, UUID: 00002a1a-0000-1000-8000-00805f9b34fb)└── 服务2 (Service 2, UUID: 0000180a-0000-1000-8000-00805f9b34fb)├── 特性1 (Characteristic 1, UUID: 00002a29-0000-1000-8000-00805f9b34fb)└── 特性2 (Characteristic 2, UUID: 00002a2a-0000-1000-8000-00805f9b34fb)

示例代码中的对应关系

  1. 服务器:在代码中,通过 BLEDevice::createServer() 创建。

  2. 服务:在代码中,通过 pServer->createService(SERVICE_UUID) 创建。

  3. 特性:在代码中,通过 pService->createCharacteristic(CHARACTERISTIC_UUID, ...) 创建。

问题 1:pServer->setCallbacks(new MyServerCallbacks()); 这一行代码的作用是什么?

解答:

这行代码的作用是为 BLE 服务器设置回调函数。具体来说,它将一个自定义的回调类 MyServerCallbacks 注册到 BLE 服务器 pServer 上。当设备连接或断开连接时,BLE 库会自动调用 MyServerCallbacks 类中的 onConnectonDisconnect 方法,以便我们在这些事件发生时执行特定的操作。

问题 2:在什么情况下会调用服务器回调类?又是谁调用?

解答:

服务器回调类中的方法会在以下情况下被调用:

  • 设备连接:当一个中央设备(如手机)连接到 ESP32 BLE 服务器时,BLE 库会调用 onConnect 方法。

  • 设备断开连接:当中央设备断开连接时,BLE 库会调用 onDisconnect 方法。

这些方法是由 BLE 库自动调用的。当 BLE 库检测到连接或断开连接事件时,会触发相应的回调函数。

问题 3:pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE); 这段代码的作用是什么?| 符号是按位或吗?

解答:

这段代码的作用是创建一个新的 BLE 特性,并将其添加到服务中。具体步骤如下:

  • pService->createCharacteristic(...):这是一个方法调用,用于在服务中创建一个新的 BLE 特性。

  • CHARACTERISTIC_UUID:这是特性的唯一标识符,用于区分不同的特性。

  • BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE:这是特性的属性,指定该特性支持读取和写入操作。

| 是按位或(bitwise OR)运算符,用于将多个标志(flags)组合在一起。在这个上下文中,它用于同时设置特性的读取和写入属性。

问题 4:BLECharacteristic 类是从哪里来的?

解答:

BLECharacteristic 类定义在我们引入的头文件中,如 BLEDevice.hBLEServer.h。这些头文件包含了所有与 BLE 相关的类和函数。在代码的顶部,我们通过以下方式包含了这些头文件:

完整示例代码
 

#include <BLEDevice.h>  // 包含 BLE 设备库
#include <BLEServer.h>  // 包含 BLE 服务器库
#include <BLEUtils.h>   // 包含 BLE 工具库
#include <BLE2902.h>    // 包含 BLE 描述符库BLEServer *pServer = NULL; // 声明服务器指针
BLECharacteristic *pCharacteristic = NULL; // 声明 BLE 特性指针
bool deviceConnected = false; // 设备是否连接#define SERVICE_UUID "0000180f-0000-1000-8000-00805f9b34fb"  // 服务的 UUID
#define CHARACTERISTIC_UUID "00002a19-0000-1000-8000-00805f9b34fb"  // 特性的 UUID// 回调类,处理连接和断开事件
class MyServerCallbacks: public BLEServerCallbacks {void onConnect(BLEServer *pServer) {deviceConnected = true; // 设置设备连接标志Serial.println("客户端连接"); // 打印连接消息}void onDisconnect(BLEServer *pServer) {deviceConnected = false; // 重置设备连接标志Serial.println("客户端断开"); // 打印断开连接消息}
};// 回调类,处理特性写操作
class MyCharacteristicCallbacks: public BLECharacteristicCallbacks {void onWrite(BLECharacteristic *pCharacteristic) {string value = pCharacteristic->getValue();Serial.print("Received Value: ");for (int i = 0; i < value.length(); i++) {Serial.print(value[i]);}Serial.println();}
};void setup() {Serial.begin(115200); // 初始化串口通信Serial.println("Starting BLE work!"); // 打印初始化消息BLEDevice::init("ESP32_BLE"); // 初始化 BLE 设备,并命名// 创建服务器pServer = BLEDevice::createServer();// 设置服务器回调pServer->setCallbacks(new MyServerCallbacks());// 创建服务BLEService *pService = pServer->createService(SERVICE_UUID);// 创建特性,支持读、写和通知属性pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,BLECharacteristic::PROPERTY_READ |BLECharacteristic::PROPERTY_WRITE |BLECharacteristic::PROPERTY_NOTIFY);// 添加描述符,使能通知pCharacteristic->addDescriptor(new BLE2902());// 设置特性回调pCharacteristic->setCallbacks(new MyCharacteristicCallbacks());// 启动服务pService->start();// 开始广播pServer->getAdvertising()->start();Serial.println("Waiting for a client connection to notify...");
}void loop() {if (deviceConnected) {// 更新特性值pCharacteristic->setValue("Hello World");// 通知客户端pCharacteristic->notify();// 通知间隔delay(1000);}
}

蓝牙控制舵机

#include <ESP32Servo.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>Servo servo1;  // 定义 servo 对象
Servo servo2;  // 定义 servo 对象
int minUs = 500;  // 0度时的脉宽,单位us
int maxUs = 2500; // 180度时的脉宽,单位us
int servo1Pin = 37;
int servo2Pin = 36;
int pos = -1;
bool up = true;BLEServer *pServer = NULL; // 声明服务器指针
BLECharacteristic *pCharacteristic = NULL; // 声明 BLE 特性指针
bool deviceConnected = false; // 设备是否连接#define SERVICE_UUID "0000180f-0000-1000-8000-00805f9b34fb"  // 服务的 UUID
#define CHARACTERISTIC_UUID "00002a19-0000-1000-8000-00805f9b34fb"  // 特性的 UUIDvoid servo(unsigned char num) {while (num > 0) {for (int i = 0; i <= 360; i++) {if (pos == 181)up = false;else if (pos == -1)up = true;if (up)pos++;elsepos--;servo1.write(pos);servo2.write(180 - pos);delay(3);}num--;}Serial.println("完成一次摆动");
}// 回调类,处理连接和断开事件
class MyServerCallbacks: public BLEServerCallbacks {void onConnect(BLEServer *pServer) {deviceConnected = true; // 设置设备连接标志Serial.println("客户端连接"); // 打印连接消息}void onDisconnect(BLEServer *pServer) {deviceConnected = false; // 重置设备连接标志Serial.println("客户端断开"); // 打印断开连接消息}
};class MyCharacteristicCallbacks: public BLECharacteristicCallbacks {void onWrite(BLECharacteristic *pCharacteristic) {String value = pCharacteristic->getValue();Serial.print("Received Value: ");Serial.println(value);  // 打印接收到的值if (value == "1") {servo(2);}}
};void setup() {Serial.begin(115200);ESP32PWM::allocateTimer(1); // 使用指定的硬件定时器servo1.setPeriodHertz(50); // 指定 PWM 的频率servo2.setPeriodHertz(50); // 指定 PWM 的频率servo1.attach(servo1Pin, minUs, maxUs);servo2.attach(servo2Pin, minUs, maxUs);// Serial.begin(115200); // 已在 setup 中初始化串口,可注释掉Serial.println("Starting BLE work!"); // 打印初始化消息BLEDevice::init("ESP32_BLE"); // 初始化 BLE 设备,并命名// 创建服务器pServer = BLEDevice::createServer();// 设置服务器回调pServer->setCallbacks(new MyServerCallbacks());// 创建服务BLEService *pService = pServer->createService(SERVICE_UUID);// 创建特性,支持读、写和通知属性pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,BLECharacteristic::PROPERTY_READ |BLECharacteristic::PROPERTY_WRITE |BLECharacteristic::PROPERTY_NOTIFY);// 添加描述符,使能通知pCharacteristic->addDescriptor(new BLE2902());// 设置特性回调pCharacteristic->setCallbacks(new MyCharacteristicCallbacks());// 启动服务pService->start();// 开始广播pServer->getAdvertising()->start();Serial.println("Waiting for a client connection to notify...");
}void loop() {// 你的主循环代码
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端开发:HTML与CSS

文章目录 前言1.1、CS架构和BS架构1.2、网页构成 HTML1.web开发1.1、最简单的web应用程序1.2、HTTP协议1.2.1 、简介1.2.2、 http协议特性1.3.3、http请求协议与响应协议 2.HTML概述3.HTML标准结构4.标签的语法5.基本标签6.超链接标签6.1、超链接基本使用6.2、锚点 7.img标签8.…

算法:BFS解决 FloodFill 算法

目录 FloodFill 算法 题目一&#xff1a;图像渲染 题目二&#xff1a;岛屿数量 题目三&#xff1a;岛屿的最大面积 题目四&#xff1a;被围绕的区域 FloodFill 算法 在递归搜索回溯中已经说到过 FloodFill 算法了&#xff0c;但是那里是用 dfs 解决的&#xff0c;这里会使…

【Web开发手礼】探索Web开发的魅力(十一)-Vue(1)配置环境、创建导航栏、各页面整体框架

主要讲解了vue的下载、配置环境、项目创建、导航栏、页面整体框架&#xff01;&#xff01;&#xff01; 文章目录 前言 配置环境 终端 安装Nodejs 安装vue/cli 启动vue自带的图形化项目管理界面 基本概念 script部分 template部分 style部分 第三方组件 创建导航栏 总结 前言 …

数据结构——单链表OJ题(上)

目录 一、移除链表元素 1.思路 2.注意 3.解题 二、反转链表 思路1&#xff1a;三指针翻转法 &#xff08;1&#xff09;注意 &#xff08;2&#xff09;解题 思路2&#xff1a;头插法 &#xff08;1&#xff09;注意 &#xff08;2&#xff09;解题 三、链表的中间结…

目标检测算法:深入探索与前沿展望

大家好&#xff0c;我是一名测试开发工程师&#xff0c;已经开源一套【自动化测试框架】和【测试管理平台】&#xff0c;欢迎大家联系我&#xff0c;一起【分享测试知识&#xff0c;交流测试技术】 在人工智能的浩瀚星空中&#xff0c;目标检测算法无疑是一颗璀璨的明星&#x…

uniapp的h5,读取本地txt带标签的文件

效果图 使用的回显的标签是u-parse&#xff0c;下面的网址讲了这个标签的相关 https://www.cnblogs.com/huihuihero/p/12978903.html 导入此插件 https://ext.dcloud.net.cn/plugin?id364 使用 uni.request({// 本地文件url: "/static/互联网医院医师端用户协议.txt…

开始尝试从0写一个项目--前端(三)

器材管理板块 添加器材管理导航 src\views\home\Home.vue src\router\index.js src\views\equipment\Equipment.vue <template><div>hello!</div></template> 测试 搜索导航分页查询 src\views\equipment\Equipment.vue <template><div&…

51.TFT_LCD液晶屏驱动设计与验证(4)

&#xff08;1&#xff09;顶层文件&#xff1a; module tft_colorbar(input clk ,input reset_n ,output hsync ,output vsync ,output [23:0] rgb_tft ,output tft_bl ,output …

LeetCode算法——滑动窗口矩阵篇

1、长度最小的子数组 题目描述&#xff1a; 解法&#xff1a; 设一个 for 循环来改变指向窗口末尾的指针&#xff0c;再不断抛弃当前窗口内的首元素 最终确定满足条件的最小长度 class Solution { public:int minSubArrayLen(int target, vector<int>& nums) {int …

Python 教程(五):理解条件语句和循环结构

目录 专栏列表前言条件语句if 语句elif 语句else 语句示例 循环结构for 循环while 循环break 和 continue实例演示 循环控制语句range 函数enumerate 函数 模式匹配总结 在前四篇教程中&#xff0c;我们学习了 Python 的基本语法和数据结构。本篇教程&#xff0c;我们将深入探讨…

git sendemail使用

教程参考&#xff1a; git-send-email - 以电子邮件形式发送补丁集 1、安装git-email 2、配置 SMTP 服务器 git config --global sendemail.smtpserver smtp.163.com git config --global sendemail.smtpserverport 465 git config --global sendemail.smtpuser xxxxxx163.c…

【故障排查】Docker启动Nacos报错:No DataSource set 问题解决

Nacos报错内容 Nacos Server did not start because dumpservice bean construction failure : No DataSource set原因分析 Nacos 配置的是单机模式&#xff0c;使用mysql 进行存储配置文件&#xff0c;Nacos的启动脚本已经配置了MySQL的连接方式&#xff0c;根据错误提示&a…

大话成像公众号文章阅读学习(二)--- 下一代 AI-ISP会更好

系列文章目录 大话成像公众号文章阅读学习&#xff08;一&#xff09;---- 索尼Alpha 9 III 大话成像公众号文章阅读学习&#xff08;二&#xff09;— 下一代 AI-ISP会更好 文章目录 系列文章目录前言一、AI-ISP1.1 定义与工作原理1.2 应用场景 二、展望总结 前言 这篇是 下…

AWS-Lambda的使用

介绍 Lambda 是一种无服务器(Serverless), 而且设计成事件驱动的计算服务器. 简单来说, 你可以将你的 code 上传, 当有事件产生(例如cronjob , 或者S3有新的文件被上传上來) , 你的code 就会在瞬间(零点几秒以內)被叫起來执行. 由于你不用管 Server如何维护, 或者自动扩展之类…

【Android】安卓四大组件之广播知识总结

文章目录 动态注册使用BroadcastReceiver监听Intent广播注册Broadcast Receiver 静态注册自定义广播标准广播发送广播定义广播接收器注册广播接收器 有序广播修改发送方法定义第二个广播接收器注册广播接收器广播截断 使用本地广播实践-强制下线使用ActivityCollector管理所有活…

微信答题小程序产品研发-UI界面设计

高保真原型虽然已经很接近产品形态了&#xff0c;但毕竟还不能够直接交付给开发。这时就需要UI设计师依据之前的原型设计&#xff0c;进一步细化和实现界面的视觉元素&#xff0c;包括整体视觉风格、颜色、字体、图标、按钮以及交互细节优化等。 UI设计不仅关系到用户的直观感…

Scrapy 爬取旅游景点相关数据(四)

本节内容主要为&#xff1a; &#xff08;1&#xff09;创建数据库 &#xff08;2&#xff09;创建数据库表 &#xff08;3&#xff09;爬取数据进MYSQL库 1 新建数据库 使用MYSQL数据库存储数据&#xff0c;创建一个新的数据库 create database scrapy_demo;2 新建数据表 CR…

tensorflow2(快速入门)

版本问题 导包 import tensorflow as tf 加载数据 加载并准备 MNIST 数据集。将样本数据从整数转换为浮点数&#xff1a; mnist tf.keras.datasets.mnist (x_train, y_train), (x_test, y_test) mnist.load_data() x_train, x_test x_train / 255.0, x_test / 255.0 搭…

Redis:AOF持久化

1. 简介 以日志的形式来记录每个写操作&#xff0c;将redis执行的每个写操作记录下来&#xff08;读操作不记录&#xff09;&#xff0c;只需追加文件但不可以改写文件&#xff0c;redis启动之初会重新构建数据&#xff0c;即redis重启后会将日志中的所有写指令重新执行一遍以达…

WordPress主题追格企业官网主题免费开源版V1.1.6

追格企业官网主题免费开源版由追格开发的一款开源wordpress主题&#xff0c;专为企业建站和追格企业官网小程序&#xff08;开源版&#xff09;PC配套而设计&#xff0c;功能集新闻动态、留言反馈、产品与服务、公司简介、联系我们等模块。