c++ - 在 Arduino 中用 NULL 替换数组中的旧客户端
问题描述
我试图为 Node MCU esp-12e 制作一个 arduino 程序,我想用当前连接的客户端列表更新 esp,所以我创建了一个函数来替换断开连接的客户端并将新客户端添加到WiFiClients
调用列表clients
中我已设置为空。我使用的代码可以在这里看到:
/* ============================================
I2Cdev device library code is placed under the MIT license
Copyright (c) 2012 Jeff Rowberg
==============================
GY-521 NodeMCU
MPU6050 devkit 1.0
board Lolin Description
======= ========== ====================================================
VCC VU (5V USB) Not available on all boards so use 3.3V if needed.
GND G Ground
SCL D1 (GPIO05) I2C clock
SDA D2 (GPIO04) I2C data
XDA not connected
XCL not connected
AD0 not connected
INT D8 (GPIO15) Interrupt pin
*/
// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files
// for both classes must be in the include path of your project
#include "I2Cdev.h"
#include <ESP8266WiFi.h>
#include "MPU6050_6Axis_MotionApps20.h"
//#include "MPU6050.h" // not necessary if using MotionApps include file
// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
// is used in I2Cdev.h
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
// class default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board)
// AD0 high = 0x69
MPU6050 mpu;
//MPU6050 mpu(0x69); // <-- use for AD0 high
// MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
// uncomment "OUTPUT_READABLE_QUATERNION" if you want to see the actual
// quaternion components in a [w, x, y, z] format (not best for parsing
// on a remote host such as Processing or something though)
#define OUTPUT_READABLE_QUATERNION
// uncomment "OUTPUT_READABLE_EULER" if you want to see Euler angles
// (in degrees) calculated from the quaternions coming from the FIFO.
// Note that Euler angles suffer from gimbal lock (for more info, see
// http://en.wikipedia.org/wiki/Gimbal_lock)
//#define OUTPUT_READABLE_EULER
// uncomment "OUTPUT_READABLE_YAWPITCHROLL" if you want to see the yaw/
// pitch/roll angles (in degrees) calculated from the quaternions coming
// from the FIFO. Note this also requires gravity vector calculations.
// Also note that yaw/pitch/roll angles suffer from gimbal lock (for
// more info, see: http://en.wikipedia.org/wiki/Gimbal_lock)
//#define OUTPUT_READABLE_YAWPITCHROLL
// uncomment "OUTPUT_READABLE_REALACCEL" if you want to see acceleration
// components with gravity removed. This acceleration reference frame is
// not compensated for orientation, so +X is always +X according to the
// sensor, just without the effects of gravity. If you want acceleration
// compensated for orientation, us OUTPUT_READABLE_WORLDACCEL instead.
//#define OUTPUT_READABLE_REALACCEL
// uncomment "OUTPUT_READABLE_WORLDACCEL" if you want to see acceleration
// components with gravity removed and adjusted for the world frame of
// reference (yaw is relative to initial orientation, since no magnetometer
// is present in this case). Could be quite handy in some cases.
#define OUTPUT_READABLE_WORLDACCEL
// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorInt16 aa; // [x, y, z] accel sensor measurements
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
#ifdef OUTPUT_READABLE_EULER
float euler[3]; // [psi, theta, phi] Euler angle container
#endif
#ifdef OUTPUT_READABLE_YAWPITCHROLL
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
#endif
#define INTERRUPT_PIN 15 // use pin 15 on ESP8266
#define CLIENT_COUNT 5 // amount of clients allowed on server
const char DEVICE_NAME[] = "mpu6050";
// ================================================================
// === INTERRUPT DETECTION ROUTINE ===
// ================================================================
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void ICACHE_RAM_ATTR dmpDataReady() {
mpuInterrupt = true;
}
void mpu_setup()
{
// join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
// initialize device
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();
pinMode(INTERRUPT_PIN, INPUT);
// verify connection
Serial.println(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
// load and configure the DMP
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788); // 1688 factory default for my test chip
// make sure it worked (returns 0 if so)
if (devStatus == 0) {
// turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// enable Arduino interrupt detection
Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
// get expected DMP packet size for later comparison
packetSize = mpu.dmpGetFIFOPacketSize();
} else {
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
}
//push buttons
int aButtonPin = 12;//6
int bButtonPin = 13;//7
int aVal = 0;
int bVal = 0;
bool aDown = false;
bool bDown = false;
String message;
//wifi config
WiFiServer server(5001);
IPAddress IP(192,168,4,15);
IPAddress mask = (255, 255, 255, 255);
WiFiClient clients[CLIENT_COUNT] = {NULL};
const char* ssid = "VRControllers";
const char* password = "VRControllers";
void setup()
{
Serial.begin(9600);
delay(10);
//setup the access point
WiFi.mode(WIFI_AP);
WiFi.softAP(ssid, password);
bool result = WiFi.softAPConfig(IP, IP, mask);
server.begin();
Serial.println(result ? "Server started." : "Server failed to start");
//set up buttons
pinMode(aButtonPin, INPUT);
pinMode(bButtonPin, INPUT);
//set up accelerometer
mpu_setup();
}
void mpu_loop()
{
// if programming failed, don't try to do anything
if (!dmpReady) return;
// wait for MPU interrupt or extra packet(s) available
if (!mpuInterrupt && fifoCount < packetSize) return;
// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
// reset so we can continue cleanly
mpu.resetFIFO();
Serial.println(F("FIFO overflow!"));
// otherwise, check for DMP data ready interrupt (this should happen frequently)
} else if (mpuIntStatus & 0x02) {
// wait for correct available data length, should be a VERY short wait
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
// track FIFO count here in case there is > 1 packet available
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;
#ifdef OUTPUT_READABLE_YAWPITCHROLL
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
String x = String(ypr[0] * 180/M_PI);
String y = String(ypr[1] * 180/M_PI);
String z = String(ypr[2] * 180/M_PI);
delay(100);
mpu.resetFIFO();
message = x + "," + y + "," + z;
Serial.println(message);
msgAllClients(message);
#endif
#ifdef OUTPUT_READABLE_QUATERNION
// display quaternion values in easy matrix form: w x y z
mpu.dmpGetQuaternion(&q, fifoBuffer);
delay(100);
mpu.resetFIFO();
message = String(q.w) + "," + String(q.x) + "," + String(q.y) + "," + String(q.z);
Serial.println(message);
msgAllClients(message);
#endif
#ifdef OUTPUT_READABLE_EULER
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetEuler(euler, &q);
String x = String(euler[0] * 180/M_PI);
String y = String(euler[1] * 180/M_PI);
String z = String(euler[2] * 180/M_PI);
delay(100);
mpu.resetFIFO();
message = x + "," + y + "," + z;
Serial.println(message);
msgAllClients(message);
#endif
#ifdef OUTPUT_READABLE_REALACCEL
// display real acceleration, adjusted to remove gravity
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetAccel(&aa, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
delay(100);
mpu.resetFIFO();
message = String(aaReal.x) + "," + String(aaReal.y) + "," + String(aaReal.z);
Serial.println(message);
msgAllClients(message);
#endif
#ifdef OUTPUT_READABLE_WORLDACCEL
// display initial world-frame acceleration, adjusted to remove gravity
// and rotated based on known orientation from quaternion
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetAccel(&aa, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
delay(100);
mpu.resetFIFO();
message = String(aaWorld.x) + "," + String(aaWorld.y) + "," + String(aaWorld.z);
Serial.println(message);
msgAllClients(message);
#endif
}
}
void updateClients(){
WiFiClient newClient = server.available();
bool present = false;
int openIndex = CLIENT_COUNT + 1;
for(int i = 0; i < CLIENT_COUNT; i++){
if(clients[i] != NULL){
if(!clients[i].connected()){
clients[i].stop();
clients[i] = NULL;
openIndex = i < openIndex ? i : openIndex;
continue;
}
present = newClient.remoteIP() == clients[i].remoteIP() ? true : present;
}
else{
openIndex = i < openIndex ? i : openIndex;
}
}
if(!present && openIndex < CLIENT_COUNT){
clients[openIndex] = newClient;
}
}
void msgAllClients(String message){
for(int j = 0; j < CLIENT_COUNT; j++){
if(clients[j] != NULL){
clients[j].println(message);
}
}
}
void buttonLoop(){
aVal = digitalRead(aButtonPin);
if (aVal == HIGH && aDown) {
aDown = false;
Serial.println("A_DOWN");
msgAllClients("A_DOWN");;
} else if (aVal == LOW && !aDown) {
aDown = true;
Serial.println("A_UP");
msgAllClients("A_UP");
}
bVal = digitalRead(bButtonPin);
if (bVal == HIGH && bDown) {
bDown = false;
Serial.println("B_DOWN");
msgAllClients("B_DOWN");
} else if (bVal == LOW && !bDown) {
bDown = true;
Serial.println("B_UP");
msgAllClients("B_UP");
}
}
void loop()
{
updateClients();
mpu_loop();
buttonLoop();
}
但是当我运行它时,我收到错误:
Arduino: 1.8.12 (Windows 10), Board: "NodeMCU 1.0 (ESP-12E Module), 80 MHz, Flash, Legacy (new can return nullptr), All SSL ciphers (most compatible), 4MB (FS:2MB OTA:~1019KB), 2, v2 Lower Memory, Disabled, None, Only Sketch, 115200"
In file included from C:\Users\mlfre\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.6.3\libraries\ESP8266WiFi\src/ESP8266WiFi.h:39:0,
from C:\Users\mlfre\OneDrive\Desktop\packetHandler\packetHandler.ino:23:
C:\Users\mlfre\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.6.3\libraries\ESP8266WiFi\src/WiFiClient.h:47:3: error: 'WiFiClient::WiFiClient(ClientContext*)' is protected
WiFiClient(ClientContext* client);
^
packetHandler:181:41: error: within this context
WiFiClient clients[CLIENT_COUNT] = {NULL};
^
In file included from C:\Users\mlfre\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.6.3\libraries\ESP8266WiFi\src/ESP8266WiFi.h:39:0,
from C:\Users\mlfre\OneDrive\Desktop\packetHandler\packetHandler.ino:23:
C:\Users\mlfre\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.6.3\libraries\ESP8266WiFi\src/WiFiClient.h: In function 'void updateClients()':
C:\Users\mlfre\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.6.3\libraries\ESP8266WiFi\src/WiFiClient.h:47:3: error: 'WiFiClient::WiFiClient(ClientContext*)' is protected
WiFiClient(ClientContext* client);
^
packetHandler:322:20: error: within this context
clients[i] = NULL;
^
exit status 1
within this context
而且由于这个问题似乎是特定于上下文的,谷歌在解决我的问题方面已经无效,这就是为什么我求助于堆栈溢出的良好社区。我开始使用 python 并逐渐转向 c++,你们中的一些人可能能够通过我的编码方式看出这一点,所以请放轻松,哈哈。如果需要,我可以提供更多信息。先感谢您!=)
解决方案
(我不知道这个问题的背景,但会尝试猜测问题是什么以及解决方案可能是什么。)
首先注意,这WiFiClient::WiFiClient(ClientContext*)
是一个受保护的构造函数。此构造函数由编译器选择以转换NULL
为WiFiClient
(在进行此选择后执行访问检查)。
当你写
WiFiClient clients[CLIENT_COUNT] = {NULL};
您创建一个数组clients
,使其第一个(不是全部!)元素通过受保护的WiFiClient(ClientContext*)
构造函数构造,其余元素通过公共WiFiClient()
构造函数构造。此行失败,因为受保护的构造函数在此处无法访问。
你想要的可能就是这样:
WiFiClient clients[CLIENT_COUNT];
现在所有元素都是通过WiFiClient()
. 您可以添加= {}
,但在这里它不会改变任何东西。
同样的事情发生
clients[i] = NULL;
编译器尝试转换NULL
为WiFiClient
使用受保护的构造函数。要使用公共WiFiClient()
构造函数,请编写
clients[i] = {};
(这应该可以编译,但我不知道这个赋值是否具有语义意义。看起来很可疑。)
我将如何执行相等测试以确保那里有一个元素,例如,
clients[i] != NULL
?
WiFiClient clients[CLIENT_COUNT]
元素总是存在的。NULL
或nullptr
不代表“无元素”状态。如果您需要“可空”元素,则应更改数组元素类型。您有几个选项:WiFiClient*
、std::unique_ptr<WiFiClient>
和std::optional<WiFiClient>
。
如果 Arduino 编译器支持std::unique_ptr
(C++11) 或std::optional
(C++17),则首选它们而不是原始指针类型。使用原始指针,您应该手动delete
对象以避免内存泄漏,而std::unique_ptr
将自动执行此操作,并且std::optional
根本不会在堆上分配对象,将它们保留在自身内部。在所有情况下,“无元素”状态的测试都是公正的if (!clients[i]) ...
,元素访问是通过*clients[i]
or完成的clients[i]->
。
推荐阅读
- ios - 为什么 ViewController 只占屏幕的一半?
- sql - 如何在oracle中将两个结果列合并到具有交替结果的列中?
- xamarin - 如何使我的 Xamarin 应用程序的 Android 项目以 MainApplication.cs 而不是 MainActivity.cs 开头
- r - 将 xlsx 与 R jupyter notebook 一起使用
- botium-box - 如何在botium中使用带有话语文件的变量
- dataframe - 如何在列上使用 pyspark min max 函数
- linux - 从主机到 docker 容器中的 conan-repository 的连接问题
- c# - 从 EF 视图到 EF 表的关系
- html - 如何在网页中包含示例 html 代码
- python - 我的代码使用超过 25GB 的内存和崩溃