我有两个按钮连接到加速度计和 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,程序将按预期运行。出于不想让您厌烦的原因,我需要一个按钮输入数字,另一个按钮输入字母。有谁知道如何使这段代码正常工作?
问题就在这里
sendKeypress(numbers[newMaxVal]);
您正在使用
newMaxVal
访问numbersArray,在按钮二的情况下,它不是整数值而是字符。
你说换了就可以了
if(newMaxVal == 0)
{
newMaxVal = 'q';
}
与
if(newMaxVal == 0)
{
newMaxVal = 5;
}
等等。那你为什么不这样做呢?这似乎是正确的解决方案。为什么说它按预期工作,但由于某种原因这是不可接受的?