Заменить старых клиентов в массиве на NULL в Arduino - PullRequest
0 голосов
/ 25 апреля 2020

Я пытаюсь создать программу arduino для Node MCU esp-12e, и я хочу обновить 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

И поскольку эта проблема, похоже, связана с контекстом c Google не смог решить мою проблему, поэтому я обратился к хорошему сообществу переполнения стека. Я начал с использования python и постепенно переключился на c ++, что некоторые из вас могут узнать по тому, как я кодирую, так что будьте полегче со мной, лол. Я могу предоставить больше информации, если требуется. Заранее спасибо! =)

1 Ответ

0 голосов
/ 26 апреля 2020

(Я не знаю контекста этой проблемы, но попытаюсь угадать, в чем проблема и каково ее решение.)

Первое замечание: WiFiClient::WiFiClient(ClientContext*) - это защищенный конструктор. Этот конструктор выбирается компилятором для преобразования NULL в WiFiClient (проверка доступа выполняется после выполнения этого выбора).

Когда вы пишете

WiFiClient clients[CLIENT_COUNT] = {NULL};

вы создаете массив clients такой, что его первый (не весь!) Элемент создается с помощью защищенного конструктора WiFiClient(ClientContext*), а остальные - с помощью конструктора publi c WiFiClient(). Эта строка терпит неудачу, потому что защищенный конструктор здесь недоступен.

Вероятно, вам нужно именно это:

WiFiClient clients[CLIENT_COUNT];

Теперь все элементы создаются с помощью WiFiClient(). Вы можете добавить = {}, но здесь это ничего не изменит.

То же самое происходит с

clients[i] = NULL;

Компилятор пытается преобразовать NULL в WiFiClient, используя Защищенный конструктор. Чтобы использовать конструктор publi c WiFiClient(), напишите

clients[i] = {};

(Это должно скомпилироваться, но имеет ли смысл это назначение semanti c, я не знаю. Это выглядит подозрительно.)


Как мне подготовить тест на равенство, чтобы убедиться, что там есть элемент, например, 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] или clients[i]->.

...