用ESP32-S2实现一个USB键盘鼠标设备
本项目使用Arduino框架进行开发,基于USBHID扩展库,将ESP32-S2实现为一个USB键盘&鼠标复合设备,通过扩展板上的摇杆设备,来控制鼠标的移动,然后使用一个按键作为鼠标左键,另外一个按键则自动输入一串字符"eetree.cn"
标签
Arduino
ESP32-S2
鼠标
2023寒假在家练
USBHID
键盘
HonestQiao
更新2023-03-27
4738

项目介绍:

本项目使用Arduino框架进行开发,基于USBHID扩展库,将ESP32-S2实现为一个USB键盘&鼠标复合设备,通过扩展板上的摇杆设备,来控制鼠标的移动,然后使用一个按键作为鼠标左键,另外一个按键则自动输入一串字符"eetree.cn"

 

设计思路:

硬禾提供的ESP32核心板是ESP32-S2-MINI-1,集成了USB TYPE -C接口,当使用该USB接口连接到电脑设备后,可以通过USBHID将自身模拟被电脑识别为键盘和鼠标,从而通过扩展板上的输入设备,来模拟键盘和鼠标的控制。

而在Arduino 的ESP32实例中,就有KeyboardAndMouseControol的例子。基于该实例,结合本次项目的要求,进行了具体的功能实现:

  1. 将自身模拟为USBHID设备,实际需要为键盘和鼠标复合设备
  2. 通过读取扩展板上的摇杆输入设备的信息,转换为上下左右四个方向的动作,从而控制鼠标的运动
  3. 通过读取扩展板上的ADC按键的信息,分别来控制左键的点击,以及模拟键盘输入字符串。

 

硬件介绍:

本次使用的硬件由硬禾提供,分别为:

ESP32-S2核心板:

ESP32-S2扩展板:

在扩展板上,提供了PWM输入的摇杆设备,以及ADC输入的按键设备。

通过扩展板原理图,可以得到具体的GPIO信息:

FiVlw6B4Sk8hC2YjdVFApzCNOFx0

Fj0nq1U81r0-kSK-Z4KrHCuV9MUJ

从上可以得知:

  1. 按键连接到了A_OUT,对应GPIO1
  2. 摇杆连接到了PWM_OUT,对应GPIO2

因为扩展板提供了上述外设完整的连接和控制线路,所以无需其他辅助,仅使用ESP32-S2核心板+扩展板就可以完成全部工作。

 

实现功能:

ESP32-S2开发方式非常多,可以使用ESP-IDF开发,也可以使用Arduino开发,还可以使用micropython和circuitpython进行开发。本项目使用Arduino框架进行开发。

通过Arduino,最终实现的功能如下:

  1. 将设备连接到电脑后,在电脑上会识别为HID设备,包括了鼠标和键盘设备
  2. 当拨动扩展板上的摇杆设备时,电脑上的鼠标指针将会对应的进行运动
  3. 但按下旋转编码器的按键时,将会出发鼠标左键的操作
  4. 当按下K2按键时,将会出发键盘操作,自动输出一串字符"eetree.cn"

 

代码说明:

/*
    ESP32-S2 键盘鼠标模拟
*/

#include "USB.h"
#include "USBHIDMouse.h"
#include "USBHIDKeyboard.h"

const byte adcPin = 1;  // ADC引脚
const byte pwmPin = 2;  // PWM引脚

// 定时器和中断设置
hw_timer_t * timer1 = NULL;
volatile SemaphoreHandle_t timerSemaphore1;
portMUX_TYPE timerMux1 = portMUX_INITIALIZER_UNLOCKED;

hw_timer_t * timer2 = NULL;
volatile SemaphoreHandle_t timerSemaphore2;
portMUX_TYPE timerMux2 = portMUX_INITIALIZER_UNLOCKED;

// 定时器内变量
volatile uint32_t isrAnalogValue = 0;
volatile uint32_t isrAnalogVolts = 0;
volatile uint32_t isrPwmValue = 0;

// 普通变量
uint32_t analogValue = 0;
uint32_t analogVolts = 0;
uint32_t pwmValue = 0;

// 键盘定义
const uint32_t keyNum = 5;
const uint32_t keyMap[10] = {1, 0, 2, 1110, 3, 3050, 4, 3370, 5, 3530};
const uint32_t keyPrecision = 100;

// 方向定义
const uint32_t directionNum = 4;
const uint32_t directionMap[8] = {1, 2472, 2, 1170, 3, 1289, 4, 2640};
const uint32_t directioPrecision = 50;

// 键鼠定义
USBHIDMouse Mouse;
USBHIDKeyboard Keyboard;

void ARDUINO_ISR_ATTR onTimer1(){
  portENTER_CRITICAL_ISR(&timerMux1);
  // ADC读取
  isrAnalogValue = analogRead(adcPin);
  isrAnalogVolts = analogReadMilliVolts(adcPin);
  portEXIT_CRITICAL_ISR(&timerMux1);

  // 释放信号量
  xSemaphoreGiveFromISR(timerSemaphore1, NULL);
}

void ARDUINO_ISR_ATTR onTimer2(){
  portENTER_CRITICAL_ISR(&timerMux2);
  // PWM读取
  isrPwmValue = pulseIn(pwmPin, HIGH);
  portEXIT_CRITICAL_ISR(&timerMux2);

  // 释放信号量
  xSemaphoreGiveFromISR(timerSemaphore2, NULL);
}

uint32_t getKey(uint32_t val) {
  if (val >= 3700) {
    //
  } else {
    for (int i = 0; i < keyNum; i++) {
      if (keyMap[i*2+1]>0 && val > keyMap[i*2+1]-keyPrecision && val < keyMap[i*2+1] + keyPrecision) return keyMap[i*2];
    }
  }
  return 0;
}

uint32_t getDirection(uint32_t val) {
  if (val >= 3700) {
    //
  } else {
    for (int i = 0; i < directionNum; i++) {
      if (directionMap[i*2+1]>0 && val > directionMap[i*2+1]-directioPrecision && val < directionMap[i*2+1] + directioPrecision) return directionMap[i*2];
    }
  }
  return 0;
}

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();

  Mouse.begin();
  Keyboard.begin();
  USB.begin();

  //  创建信号量
  timerSemaphore1 = xSemaphoreCreateBinary();
  timerSemaphore2 = xSemaphoreCreateBinary();

  // 定时器1设置
  timer1 = timerBegin(0, 80, true);  // 启动定时器
  timerAttachInterrupt(timer1, &onTimer1, true);  // 设置中断回调
  timerAlarmWrite(timer1, 100000, true);  // 中短周期设置为1000000-1s

  // 定时器2设置
  timer2 = timerBegin(1, 80, true);  // 启动定时器
  timerAttachInterrupt(timer2, &onTimer2, true);  // 设置中断回调
  timerAlarmWrite(timer2, 100000, true);  // 中短周期设置为1000000-1s

  // 定时器启用
  timerAlarmEnable(timer1);  // 使能定时器
  delay(50);
  timerAlarmEnable(timer2);  // 使能定时器

  // 设置ADC分辨率,2^12,4096
  analogReadResolution(12);

  // 设置PWM输入
  pinMode(pwmPin, INPUT);
}

void loop () {
  // 检查ADC信号量
  if (xSemaphoreTake(timerSemaphore1, 0) == pdTRUE){
    // 获取中断内的数据
    portENTER_CRITICAL(&timerMux1);
    analogValue = isrAnalogValue;
    analogVolts = isrAnalogVolts;
    portEXIT_CRITICAL(&timerMux1);

    // 输出
    // Serial.printf("analogValue=%d analogVolts=%d\n", analogValue, analogVolts);
    uint32_t key = getKey(analogValue);
    if(key) {
      Serial.print(analogValue);
      Serial.print(" ");
      Serial.println(key);

      switch(key) {
        case 2:
          // K2按键
          //Keyboard.write('2');
          Keyboard.print("eetree.cn");
          delay(300);
          break;
        case 3:
          // 鼠标左键
          Mouse.click(MOUSE_LEFT);
          delay(100);
          break;                             
      }      
    }
  }

// 检查PWM信号量
  if (xSemaphoreTake(timerSemaphore2, 0) == pdTRUE){
    // 获取中断内的数据
    portENTER_CRITICAL(&timerMux2);
    pwmValue = isrPwmValue;
    portEXIT_CRITICAL(&timerMux2);

    // 输出
    //Serial.printf("pwmVal=%d\n", pwmValue);
    uint32_t direction = getDirection(pwmValue);
    if(direction) {
      Serial.print(pwmValue);
      Serial.print(" ");
      Serial.println(direction);
      switch(direction) {
        case 1:
          // 鼠标左移
          Mouse.move(-40, 0);
          break;
        case 2:
          // 鼠标上移
          Mouse.move(0, -40);
          break;
        case 3:
          // 鼠标右移
          Mouse.move(40, 0);
          break;
        case 4:
          // 鼠标下移
          Mouse.move(0, 40);
          break;                              
      }
    }
  }  
}

 

在上述代码中,调用了三个支持库:

  1. USB.h:USB支持
  2. USBHIDMouse.h:USB鼠标支持
  3. USBHIDKeyboard:USB键盘支持

因为摇杆为PWM输入,故代码中定义了摇杆各个方向对应的PWM输入信息:

// 方向定义
const uint32_t directionNum = 4;
const uint32_t directionMap[8] = {1, 2472, 2, 1170, 3, 1289, 4, 2640};
const uint32_t directioPrecision = 50;

其中设置了4个方向,容错范围为上下50

 

而am建使用ADC输入,故代码中定义了各按键对应的ADC输入信息:

// 键盘定义
const uint32_t keyNum = 5;
const uint32_t keyMap[10] = {1, 0, 2, 1110, 3, 3050, 4, 3370, 5, 3530};
const uint32_t keyPrecision = 100;

为了兼容ESP32-S2和MSP430的扩展板,这里定义了5个按键。

实际使用时,K2对应2,旋转编码器按键为3。

同时,对读取到的模拟值设置了上下100的容错范围。

 

为了更有效的获取摇杆和按键的数据,代码中使用了定时器来进行读取,使得其信息的获取,不受主流程代码的影响,有效提升了获取输入信息的稳定。

在获取到按键信息和摇杆信息后,再根据实际获取的情况,控制键盘和鼠标的动作。

 

 

操作步骤:

  1. 使用Arduino IDE编写代码
    1. Fmim5pfNbiFvdlaxld5daEsmHnf4
  2. 设置连接参数
    1. FmLQSq5L054BJPQGrNPqRSzvZkcx
  3. 编译并下载代码
    1. FgRMhm9TQI0Xoje7xHVBBBlRlClW
  4. 实际控制测试:
    1. 可拨动摇杆方向,观察鼠标运动
    2. 按一下K2,观察键盘输出
    3. 按一下旋转编码器按键,观察鼠标点击情况 
    4. FnJQyiQqF0bT6YbKQTa6QdYe9__w

 

附件下载
代码提交.zip
ESP32-S2项目代码
团队介绍
一个狂热的开源爱好者和传播者,同时也是一名极客爱好者,长期关注嵌入式发展和少儿创客教育,既擅长互联网系统架构设计与研发,又拥有丰富的嵌入式研发经验。为人精力充沛,古道热肠,圈内人称乔大妈、乔帮主。
团队成员
HonestQiao
狂热的开源爱好者和传播者
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号