今天在抖音冲浪,突然刷到了某佬用鸡胸肉当键盘。

OK,这东西熟悉呀,好酷。

我在家疯狂寻找。

原理阐述

由于人体在接触ESP32的IO时会导致电容值发生变化,ESP32总共有十个可以直接读取电容的IO,我们可以利用电容量变化来判断我们是否按下。

在Arduino IDE中利用以下函数读取触摸传感器



for(;;)
  {
    Serial.print("A:");
    Serial.println(touchRead(4));
    delay(10);
  }

用上述代码,我们进行测试。


可以发现,在空置的时候,触摸传感器的值在80作用,当我们触摸IO(我拉了根杜邦线)的时候,电容的值会降到10+。变化非常明显。

知道这个原理,我们加点判断逻辑,即可实现触摸的检测。

接下来写一个上位机程序


      using System.Management;
      using System.Runtime.InteropServices;
      
       [DllImport("user32.dll", SetLastError = true)]
       static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);

       struct INPUT
       {
           public int type;
           public InputUnion u;
       }

       [StructLayout(LayoutKind.Explicit)]
       struct InputUnion
       {
           [FieldOffset(0)]
           public MOUSEINPUT mi;
           [FieldOffset(0)]
           public KEYBDINPUT ki;
           [FieldOffset(0)]
           public HARDWAREINPUT hi;
       }

       struct MOUSEINPUT
       {
           public int dx;
           public int dy;
           public uint mouseData;
           public uint dwFlags;
           public uint time;
           public IntPtr dwExtraInfo;
       }

       struct KEYBDINPUT
       {
           public ushort wVk;
           public ushort wScan;
           public uint dwFlags;
           public uint time;
           public IntPtr dwExtraInfo;
       }

       struct HARDWAREINPUT
       {
           public uint uMsg;
           public ushort wParamL;
           public ushort wParamH;
       }

       const int INPUT_KEYBOARD = 1;
       const uint KEYEVENTF_KEYUP = 0x0002;

       public static void SendKey(ushort key, bool keyUp)
       {
           INPUT[] inputs = new INPUT[]
           {
           new INPUT
           {
               type = INPUT_KEYBOARD,
               u = new InputUnion
               {
                   ki = new KEYBDINPUT
                   {
                       wVk = key,
                       wScan = 0,
                       dwFlags = keyUp ? KEYEVENTF_KEYUP : 0,
                       time = 0,
                       dwExtraInfo = IntPtr.Zero
                   }
               }
           }
           };

           SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT)));
       }
       char boolc;
       private void SendKeysToApplication(string input)
       {
           foreach (char c in input)
           {
               if(c!='T')
               {
                   SendKey(c, true);
                   boolc = c;
               }
               else
               {
                   SendKey(boolc, false);
               }
           }
       }

我们调用系统API将我们接收到的文本转化为键盘硬件输出(有些游戏会把模拟键盘视作外挂(英雄联盟我试了不行))并且在串口接收的部分调用这个函数。

   System.Management 用于管理和操作Windows管理信息。

   System.Runtime.InteropServices 用于在托管代码和非托管代码之间进行工作,特别是用于调用Windows API。


[DllImport("user32.dll", SetLastError = true)]
static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);


    这是 SendInput 函数的声明,它来自user32.dll(一个Windows系统库),用于插入键盘或鼠标输入事件到Windows的消息队列。

   SetLastError = true 表示如果函数调用失败,可以使用 Marshal.GetLastWin32Error() 来获取错误代码。

    结构体定义

    代码中定义了几个结构体,它们对应于 SendInput 函数所需的参数。


struct INPUT
{
    public int type;
    public InputUnion u;
}


type 表示输入类型,例如键盘输入(INPUT_KEYBOARD)。

u 是一个联合体,可以表示鼠标、键盘或硬件输入。


[StructLayout(LayoutKind.Explicit)]
struct InputUnion
{
    [FieldOffset(0)]
    public MOUSEINPUT mi;
    [FieldOffset(0)]
    public KEYBDINPUT ki;
    [FieldOffset(0)]
    public HARDWAREINPUT hi;
}



    一个具有显式布局的结构体,其成员共享相同的内存位置(所有成员的 FieldOffset 都是0)。这意味着 mi、ki 和 hi 实际上是互相重叠的,这个结构体在任何时候只能表示其中一种输入。MOUSEINPUT, KEYBDINPUT, HARDWAREINPUT 结构体这些结构体包含了模拟鼠标、键盘和硬件输入所需的具体信息。

  • 最后SendKey中,第一个参数是字符,第二个参数是模拟按下还是释放


嘉立创PCB

还没有评论,抢个沙发!