Arduino 蓝牙键盘 Immolator 发送意外的 ASCII 字符

问题描述 投票:0回答:1

我有两个按钮连接到加速度计和 Adafruit Bluefruit nRF52 Feather 板上。我在 Arduino IDE 中编写了一个程序,主要用于读取按下按钮时的加速度,将加速度映射到 0-4 的范围,然后将相应的字符(通过蓝牙)发送到我的笔记本电脑。按钮一仅发送 0、1、2、3 或 4;按钮二应该发送 q、w、e、r 或 t,但问题就在这里。使用以下代码:

/*********************************************************************
Target = Adafruit NRF52 Feather
Button wired to A4/Pin28
LED wired to A2/Pin4
6050 IMU sensor wired to I2C 
*********************************************************************/

/*
 *  map 0-30.0 to 0-4 
 *  
 *  Device 1 gets 0,1,2,3,4
 *  Device 2 gets q,w,e,r,t
 */
 
#include <bluefruit.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

#define ACCEL_BUFFER_SIZE     50  // was 100 but really the interesting stuff happens in the last 20 or so samples
#define SENSOR_UPDATE_DELAY   1   // ms = 1000Sps
#define KEYPRESS_DELAY        4   // how long between keypress down and back up.  was 5

#define BUTTON_PIN            28// A4
#define LED_PIN               4 // A2

#define BOX_NUMBER_SWITCH     16
#define REPEAT_BUTTON_DELAY   500 // numebr fo milliseconds delay min between registered presses.

#define MIN_ACCEL 0.0
#define MAX_ACCEL 30.0

#define DEBUG

// Define bit mapping for key descriptors.
//  Kana    Compose   Scroll   Caps   Num
#define KEYMASK_KANA      0x16  
#define KEYMASK_COMPOSE   0x08  
#define KEYMASK_SCROLLOCK 0x04  
#define KEYMASK_CAPSLOCK  0x02  
#define KEYMASK_NUMLOCK   0x01  

#define OFFSET            10.7  // Offset in read value to remove to zero things out.
#define POST_SAMPLES      3     // number of samples to collect after button press.  this adds Xms delay to button press timing.

Adafruit_MPU6050  mpu;                      // IMU / Accelerometer
BLEDis            bledis;                   // Bluetooth descriptor characteristic
BLEHidAdafruit    blehid;                   // Bluetooth HID characteristic.

double prevAccelVal = 0;                    // Store our previously read value to allow for a little smoothing
double accelBuffer[ACCEL_BUFFER_SIZE];      // Circular buffer for accelerometer data.
unsigned int accelBufferUpto = 0;           // Pointer to current buffer position.
unsigned long lastSensorReadingTime = 0;    // Last time we read accelerometer data.
bool hasKeyPressed = false;                 //
bool currLedState = false;                  // Current state of LED
//char txChar = '>';                          // Character to send to signify button press. not used.
unsigned char boxNumber = 0;                // Our own box number,  will be set to 1 or 2
char numbers[] = {'0','1','2','3','4','q','w','e','r','t'};       // Little cheat to help us get ascii characters from integers...
unsigned long lastButtonPress = 0;                                // Record time of last button press.
unsigned char ledCmdMask[] = {KEYMASK_CAPSLOCK,KEYMASK_NUMLOCK};  // Set the key descriptors to look out for per box number

/*
 * Read state of slide switch to decide which box number we are.
 */
unsigned char readBoxNumber()
{
  unsigned char ret = 0;
  pinMode(BOX_NUMBER_SWITCH,INPUT);         // Make sure GPIO is set as an input
  if(digitalRead(BOX_NUMBER_SWITCH) == LOW) // If its LOW....
  {
    ret = 1;                                  // we are box #1
  }
  else                                      // Else...
  {
    ret = 2;                                  // we are box #2
  }
  return ret;
}

/* 
 * Initialise the IMU 
 */
void initImu()
{
  // Try to initialize!
  if (!mpu.begin()) 
  {
    while (1)   // death loop on failure.
    {
      Serial.println("Failed to find MPU6050 chip");
      delay(10000);
    }
  }
  mpu.setAccelerometerRange(MPU6050_RANGE_16_G);  // Make sure high bandwidth
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);        // we are not using gyro anyway...
  mpu.setFilterBandwidth(MPU6050_BAND_260_HZ);    // 260HZ should be ok i think?!
}

/* 
 *  Initialise Led
 */
void initLed()
{
  pinMode(LED_PIN,OUTPUT);
}

/* 
 *  Set value of Led
 */
void setLed(bool ledState)
{
  if(ledState == true)
  {
    digitalWrite(LED_PIN,HIGH);   // Set LED on.
  }
  else
  {
    digitalWrite(LED_PIN,LOW);    // Set LED off.
  }
}

/* 
 *  Take a readings from accelerometer,  calc absolute value and add to array.
 */
void updateReading()
{
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  /*
   * We want absolute magnitude of acceleration such that force works in any orientation.
   * We should do:
   * AccelMagnitude = SQRT (AccellX^2 + AccellY^2 + AccellZ^2)
   */
  double accelMagnitudeS = 0.0;                 
  accelMagnitudeS = (a.acceleration.x * a.acceleration.x);
  accelMagnitudeS += (a.acceleration.y * a.acceleration.y);
  accelMagnitudeS += (a.acceleration.z * a.acceleration.z);

  double accelMagnitude = sqrt(accelMagnitudeS);      // Take the square root to get total magnitude.
  accelMagnitude = abs(accelMagnitude-OFFSET);        // Remove offset and take absolute value - we done care about acceleration direction.
  double accelMagnitudeLpf = ((accelMagnitude *0.75) + (prevAccelVal * 0.25));  // low pass value by using 3/4 of current value added to 1/4 previous
  accelBuffer[accelBufferUpto] = accelMagnitudeLpf;   // Store our new value 
  prevAccelVal = accelMagnitudeLpf;                   // Save previous value as our new value for next time.
  accelBufferUpto++;                                  // increment and wrap our circular buffer pointer.
  if(accelBufferUpto >= ACCEL_BUFFER_SIZE)
    accelBufferUpto = 0;
}

/* 
 *  initialise the button.
 */
void initButton()
{
  pinMode(BUTTON_PIN,INPUT);
}

/* 
 *  check if button is pressed.  return true if yes,  false if not.
 */
bool isButtonPressed()
{
  if(digitalRead(BUTTON_PIN) == LOW)  // button has been pressed.
  {
    return true;          
  }
  return false;
}

/* 
 *  Find the maximum value in our acceleration data buffer.
 */
double findMaxValue()
{
  double maxVal = 0.0;
  for(int i=0;i<ACCEL_BUFFER_SIZE;i++)    // Loop through our buffer
  {
    if(accelBuffer[i] > maxVal)           // if current value is bigger than our stored max...
    { 
      maxVal = accelBuffer[i];                // update our maximum value.
    }
  }
  return maxVal;
}

// print out the array for test purposes
// start at accelBufferUpto+1 and loop back circular buffer.
void printAccelArray()
{
  unsigned int arrayPointer = accelBufferUpto;  // We want to start printing from the upto value not start of array
                                                // so that the printed values are aligned newest value last.
  for(int i=0;i<ACCEL_BUFFER_SIZE;i++)          // Loop through array
  {
    Serial.println(accelBuffer[arrayPointer]);    // Print current value.
    arrayPointer = arrayPointer + 1;              // increment and wrap array  upto pointer.
    if(arrayPointer >= ACCEL_BUFFER_SIZE)
      arrayPointer = 0;
  }
}

/* 
 * Setup 
 */
void setup() 
{
  Serial.begin(115200);
  //while ( !Serial ) delay(10);   // for nrf52840 with native usb

  Serial.println("Button smashhhhh...");
  Serial.println("--------------------------------\n");

  initButton();         // Initialise Button gpio
  initImu();            // Initialise IMU
  initLed();            // Initialise LED GPIO
  boxNumber = readBoxNumber();  //Work out which boix number we are.

  Serial.print("Box number: ");
  Serial.println(boxNumber);

  Bluefruit.begin();
  Bluefruit.setTxPower(4);    // Check bluefruit.h for supported values

  // Configure and Start Device Information Service
  bledis.setManufacturer("UOSA");
  bledis.setModel("SmashyButton");
  bledis.begin();

  /* Start BLE HID
   * Note: Apple requires BLE device must have min connection interval >= 20m ( The smaller the connection interval the faster we could send data).
   * However for HID and MIDI device, Apple could accept min connection interval  up to 11.25 ms. Therefore BLEHidAdafruit::begin() will try to set the min and max
   * connection interval to 11.25  ms and 15 ms respectively for best performance.
   */
  blehid.begin();

  blehid.setKeyboardLedCallback(set_keyboard_led);  // Set callback for set LED from central

  /* Set connection interval (min, max) to your perferred value.
   * Note: It is already set by BLEHidAdafruit::begin() to 11.25ms - 15ms
   * min = 9*1.25=11.25 ms, max = 12*1.25= 15 ms 
   */
  /* Bluefruit.Periph.setConnInterval(9, 12); */

  // Set up and start advertising
  startAdv();
}

/* 
 * Start bluetooth advertising 
 */
void startAdv(void)
{  
  // Advertising packet
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();
  Bluefruit.Advertising.addAppearance(BLE_APPEARANCE_HID_KEYBOARD);
  
  // Include BLE HID service
  Bluefruit.Advertising.addService(blehid);

  // There is enough room for the dev name in the advertising packet
  Bluefruit.Advertising.addName();
  
  /* Start Advertising
   * - Enable auto advertising if disconnected
   * - Interval:  fast mode = 20 ms, slow mode = 152.5 ms
   * - Timeout for fast mode is 30 seconds
   * - Start(timeout) with timeout = 0 will advertise forever (until connected)
   * 
   * For recommended advertising interval
   * https://developer.apple.com/library/content/qa/qa1931/_index.html   
   */
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setInterval(32, 244);    // in unit of 0.625 ms
  Bluefruit.Advertising.setFastTimeout(30);      // number of seconds in fast mode
  Bluefruit.Advertising.start(0);                // 0 = Don't stop advertising after n seconds
}

/* 
 *  send a keypress over bluetooth HID
 */
void sendKeypress(char ch)
{
    blehid.keyPress(ch);
    delay(KEYPRESS_DELAY);    // Delay a bit after a report
    blehid.keyRelease();
}

/*  
 *  loop 
 */
void loop() 
{
  unsigned long currTime = millis();                          // Check millis time.
  if(currTime >= (lastSensorReadingTime+SENSOR_UPDATE_DELAY)) // if its time to udpate accelerometer readings...
  {
    updateReading();                                          // update....
    lastSensorReadingTime = millis();
  }

  unsigned long timeSinceLastButtonPress = millis() - lastButtonPress;  // Calculate numebr of ms since last button press.
  if (isButtonPressed() == true && (timeSinceLastButtonPress >= REPEAT_BUTTON_DELAY)) // check if button has been pressed and enough time has elapsed
  {

    for(int i=0;i<POST_SAMPLES;i++)   // collect 5 more samples...
    {
      delay(1);         // this is crude!
      updateReading();   
    }
    
    double maxVal = findMaxValue();                                   // Get max acceleration value
    int intMaxVal = (int)min(maxVal,MAX_ACCEL);                       // cap it to defined max to prevent map from doing something stupid
    unsigned int newMaxVal = map(intMaxVal,MIN_ACCEL,MAX_ACCEL,0,4);  // Map our range onto 0-4.
    newMaxVal = min(newMaxVal,4);                                     // make sure we are max 4!
    if(boxNumber == 2)                                                // add offset for box number 2.
    {
      if(newMaxVal == 0)
      {
        newMaxVal = 'q';
      }
      else if(newMaxVal == 1)
      {
        newMaxVal = 'w';
      }
      else if(newMaxVal == 2)
      {
        newMaxVal = 'e';
      }
      else if(newMaxVal == 3)
      {
        newMaxVal = 'r';
      }
      else if(newMaxVal == 4)
      {
        newMaxVal = 't';
      }
    }
    sendKeypress(numbers[newMaxVal]);                                 // Send out keypress
    lastButtonPress = millis();                                       // Update our recorded last keypress time

    #ifdef DEBUG                                                      // Print stuff for debug...  comment out DEBUG define to remove.
    printAccelArray();
    Serial.print("Max val:");     Serial.println(maxVal);
    Serial.print("New max val:"); Serial.println(newMaxVal);
    #endif
  }
}

/**
 * Callback invoked when received Set LED from central.
 * Must be set previously with setKeyboardLedCallback()
 *
 * The LED bit map is as follows: (also defined by KEYBOARD_LED_* )
 *    Kana (4) | Compose (3) | ScrollLock (2) | CapsLock (1) | Numlock (0)
 */
void set_keyboard_led(uint16_t conn_handle, uint8_t led_bitmap)
{
  (void) conn_handle;

  if ( led_bitmap & ledCmdMask[(boxNumber-1)] ) // dont forget to remove box number offset (array start at zero - we use box 1 or 2)
  {
    setLed(true);   // Set led on                             
  }
  else
  {
    setLed(false);  // Set led off
  }
}

我从按钮一中得到了预期的行为,但我只收到了“|” (长线)按钮二中的字符。我以前从未使用过 C++ 或 Arduino。最初我在编写代码时得到了帮助(只使用了数字 0-9),但现在我只能靠自己了……我认为代码中的关键点是

char numbers[] = {'0','1','2','3','4','q','w','e','r','t'};

    if(boxNumber == 2)                                                // add offset for box number 2.
    {
      if(newMaxVal == 0)
      {
        newMaxVal = 'q';
      }
      else if(newMaxVal == 1)
      {
        newMaxVal = 'w';
      }
      else if(newMaxVal == 2)
      {
        newMaxVal = 'e';
      }
      else if(newMaxVal == 3)
      {
        newMaxVal = 'r';
      }
      else if(newMaxVal == 4)
      {
        newMaxVal = 't';
      }
    }

如果我将这些关键点中的字母替换为数字 5-9,程序将按预期运行。出于不想让您厌烦的原因,我需要一个按钮输入数字,另一个按钮输入字母。有谁知道如何使这段代码正常工作?

arduino ascii arduino-c++
1个回答
0
投票

问题就在这里

sendKeypress(numbers[newMaxVal]);

您正在使用

newMaxVal
访问numbersArray,在按钮二的情况下,它不是整数值而是字符。

你说换了就可以了

  if(newMaxVal == 0)
  {
    newMaxVal = 'q';
  }

  if(newMaxVal == 0)
  {
    newMaxVal = 5;
  }

等等。那你为什么不这样做呢?这似乎是正确的解决方案。为什么说它按预期工作,但由于某种原因这是不可接受的?

© www.soinside.com 2019 - 2024. All rights reserved.