• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

MQTT

kharlashkin

New member
Добрый день.
Есть проблема- второй день бьюсь над решением, гугль молчит (никакого решения найти не могу), сам пока что не могу додуматься.
Есть плата Wemos D1 mini, к нему контроллер аккумулятора и датчик MPU-9250 распаенный на макетке. Контроллер считывает показания датчика и отправляет их на ПК протоколом MQTT.
Что хочу сделать - отправлять команды на контроллер о начале передачи, окончании передачи, глубокого сна (экономить заряд).
Код:
#include <I2Cdev.h>
#include <MPU9250_9Axis_MotionApps41.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoOTA.h>

MPU9250 mpu;

#define SDA 4
#define SCL 5

#define LED_PIN 2

IPAddress ip(192, 168, 50, 24);
IPAddress gateway(192, 168, 50, 1);
IPAddress subnet(255, 255, 255, 0);

const char* ssid = "ссид";
const char* password = "пароль";
const char* mqtt_server = "192.168.50.10";
String messageMQTT;

WiFiClient espClient;
PubSubClient client(espClient);

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
bool blinkState = false;
bool sendData = false;
int good = 1;

Quaternion q;           // [w, x, y, z]         quaternion container

void setup_wifi() {
  delay(10);
  WiFi.setSleepMode(WIFI_NONE_SLEEP);
  WiFi.config(ip, gateway, subnet);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(50);
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived ");
  Serial.print(topic);
  Serial.print(" ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
    messageMQTT += (char)payload[i];
  }
  if (messageMQTT == "start"){
      Serial.println(messageMQTT);
      sendData = !sendData;
  }
  if (messageMQTT == "sleep"){
      Serial.println(messageMQTT);
      mpu.setSleepEnabled(true);
      ESP.deepSleep(-1);
  }
  Serial.println();
}

void reconnect() {
  while (!client.connected()) {
    String clientId = "head-";
    clientId += String(random(0xffff), HEX);
    if (client.connect(clientId.c_str())) {
      client.subscribe("main");
    }
  }
}


void setup() {
  Wire.begin(SDA, SCL);
  Wire.setClock(400000);
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  mpu.initialize();
  mpu.dmpInitialize();
  mpu.setDMPEnabled(true);
  packetSize = mpu.dmpGetFIFOPacketSize();
  fifoCount = mpu.getFIFOCount();
  ArduinoOTA.onStart([]() {
    Serial.println("Start");  //  "Начало OTA-апдейта"
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");  //  "Завершение OTA-апдейта"
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    //  "Ошибка при аутентификации"
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    //  "Ошибка при начале OTA-апдейта"
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    //  "Ошибка при подключении"
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    //  "Ошибка при получении данных"
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
    //  "Ошибка при завершении OTA-апдейта"
  });
  ArduinoOTA.begin();
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
    ArduinoOTA.handle();
    if (!client.connected()) {
        reconnect();
    }
    client.loop();
      if (fifoCount < packetSize) {
        fifoCount = mpu.getFIFOCount();
      }
      else if (fifoCount == 1024) {
        mpu.resetFIFO();
          }
      else if (fifoCount % packetSize != 0) {
          mpu.resetFIFO();
        }
    else if (fifoCount >= packetSize) {
           mpu.getFIFOBytes(fifoBuffer, packetSize);
           fifoCount -= packetSize;
           mpu.dmpGetQuaternion(&q, fifoBuffer);
               if (sendData == true) {
                client.publish("adam/head", (uint8_t*)&q, sizeof(q));
                   good = good + 1;
                   Serial.println(good);
                   yield();
               }
           blinkState = !blinkState;
           digitalWrite(LED_PIN, blinkState);
        mpu.resetFIFO();
    }
}
При загрузке контроллера я вижу по индикации светодиода, что данные считываются, отправляю "start", начинается отправка и прием данных, но после этого никакими другими командами ничего сделать не получается. Если же после загрузки отправляю "sleep" датчик засыпает. Может кто сталкивался с похожей проблемой или укажет в какую сторону смотреть.
 

pvvx

Активный участник сообщества
Добрый день.
Есть проблема- второй день бьюсь над решением, гугль молчит (никакого решения найти не могу), сам пока что не могу додуматься.
......
При загрузке контроллера я вижу по индикации светодиода, что данные считываются, отправляю "start", начинается отправка и прием данных, но после этого никакими другими командами ничего сделать не получается. Если же после загрузки отправляю "sleep" датчик засыпает. Может кто сталкивался с похожей проблемой или укажет в какую сторону смотреть.
У вас кроме обработки "start" и "sleep" ничего не описано.
 

kharlashkin

New member
У вас кроме обработки "start" и "sleep" ничего не описано.
Если получаю старт, "sendData" становится "true" тогда начинается передача данных с датчика - она действительно начинается, но при повторной отправке этого сообщения параметр переходит в "false" о передача продолжается, так же контроллер не реагирует на "sleep". Или что имеется ввиду под "не описано"?
 

pvvx

Активный участник сообщества
У вас было описано "но после этого никакими другими командами ничего сделать", про это и ответ - у вас других команд то и нет.
А так-же непонятные "Если же после загрузки отправляю "sleep" датчик засыпает." После какой загрузки и кого грузят? Прошивку? MQTT сервeр?
И тут надо догадываться, что наверно это после рестарта модуля?

Для отладки замените обращение к функциям датчика на sleep(), а отправляемый результат на random. Тогда определитесь работает ли ваш MQTT.
Эмуляция i2с шины грузит CPU на полную и что там будет с Wifi и прочим - неизвестно.
 

kharlashkin

New member
У вас было описано "но после этого никакими другими командами ничего сделать", про это и ответ - у вас других команд то и нет.
А так-же непонятные "Если же после загрузки отправляю "sleep" датчик засыпает." После какой загрузки и кого грузят? Прошивку? MQTT сервeр?
И тут надо догадываться, что наверно это после рестарта модуля?

Для отладки замените обращение к функциям датчика на sleep(), а отправляемый результат на random. Тогда определитесь работает ли ваш MQTT.
Эмуляция i2с шины грузит CPU на полную и что там будет с Wifi и прочим - неизвестно.
Прошу прощения за некоторую недосказанность. У меня в коде в этих местах "Serial.println" не просто так стоит - сообщения приходят и я это вижу, но контроллер их не обрабатывает.
 

pvvx

Активный участник сообщества
Прошу прощения за некоторую недосказанность. У меня в коде в этих местах "Serial.println" не просто так стоит - сообщения приходят и я это вижу, но контроллер их не обрабатывает.
Дык что не отрабатывает? Не выключает client.publish("adam/head", (uint8_t*)&q, sizeof(q));?
И что там такое накручено с fifoCount ?
Он у вас обновляется только если fifoCount < packetSize, а если от станет равным fifoCount > packetSize, то вызовется mpu.resetFIFO(), но значение fifoCount не обновится и будет вечно вызываться mpu.resetFIFO().
 

kharlashkin

New member
Дык что не отрабатывает? Не выключает client.publish("adam/head", (uint8_t*)&q, sizeof(q));?
И что там такое накручено с fifoCount ?
Он у вас обновляется только если fifoCount < packetSize, а если от станет равным fifoCount > packetSize, то вызовется mpu.resetFIFO(), но значение fifoCount не обновится и будет вечно вызываться mpu.resetFIFO().
В том то и дело что включает, но после включения в true и старта отправки данных, датчик получает сообщения о прекращении передачи или уход в сон (я это вижу в терминале) но не реагирует, то есть не прекращает передачу или не уходит в сон.
fifoCount - проверка буфера на датчике, если не готово - занимаемся дальше делами, если готово - считываем и чистим буфер, если есть мусор - чистим буфер (лишние данные больше размера буфера), если переполнение - чистим. Буфер размером 1 КБ. Кстати учитывая размер данных забираемых из датчика - время на чтение около 10 мс, а частота 50 Гц, т.е. новые данные каждые 20 мс - может здесь есть подвох...
 

pvvx

Активный участник сообщества
Кстати учитывая размер данных забираемых из датчика - время на чтение около 10 мс, а частота 50 Гц, т.е. новые данные каждые 20 мс - может здесь есть подвох...
Там вроде 42 байта блок... и по умолчанию 200 Гц опрос и помещение в FIFO (делитель задан с 1000 Гц на 5)... это по памяти и для другого CPU и могу путать...
По этому первое предложение и было - влепить sleep() и проверить.
А fifoCount у вас не переписывается и если заглючит, то навсегда.
 

kharlashkin

New member
DEBUG_PRINTLN(F("Setting up internal 42-byte (default) DMP packet buffer...")); dmpPacketSize = 42; // 42 байта
...
DEBUG_PRINTLN(F("Setting sample rate to 200Hz..."));
Я использую 9250 с магнитометром, там размер буфера 48 байт.
Код:
/* ================================================================================================ *
| Default MotionApps v4.1 48-byte FIFO packet structure:                                           |
|                                                                                                  |
| [QUAT W][      ][QUAT X][      ][QUAT Y][      ][QUAT Z][      ][GYRO X][      ][GYRO Y][      ] |
|   0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  |
|                                                                                                  |
| [GYRO Z][      ][MAG X ][MAG Y ][MAG Z ][ACC X ][      ][ACC Y ][      ][ACC Z ][      ][      ] |
|  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  |
* ================================================================================================ */
Блин а в коде стоит размер 64... Возможно стоит подправить/проверить.
48 байт передать на скорости 400000 бод по I2C занимает около 10 мс - где-то встречал расчеты и проверял их. Если использовать SPI и новые датчики там до 4 Мбит скорость.
Я использую 50 Гц опрос, можно до 200. Но встает вопрос с временем передачи данных из буфера в контроллер.
 

pvvx

Активный участник сообщества
48 байт передать на скорости 400000 бод по I2C занимает около 10 мс - где-то встречал расчеты и проверял их.
Не более 2 ms, т.к. на символ идет 9 бит + заголовок с адресом регистра + старт/стоп -> немного более чем (48*9+9*3+2)/400000=0.001153 сек
Но у ESP I2C не аппаратный и там задержки болтаются для каждой стадии бита в большую сторону...
Если использовать SPI и новые датчики там до 4 Мбит скорость.
На SPI с него и читают тысячу отсчетов в сек.
Я использую 50 Гц опрос, можно до 200. Но встает вопрос с временем передачи данных из буфера в контроллер.
MQTT то успеет? Оно никогда на такое не рассчитывалось :) Ну десяток транзакций в сек на сервере с дцать ядер по 3+ГГц... :)
 

kharlashkin

New member
Не более 2 ms, т.к. на символ идет 9 бит + заголовок с адресом регистра + старт/стоп -> немного более чем (48*9+9*3+2)/400000=0.001153 сек
Но у ESP I2C не аппаратный и там задержки болтаются для каждой стадии бита в большую сторону...
На SPI с него и читают тысячу отсчетов в сек.

MQTT то успеет? Оно никогда на такое не рассчитывалось :) Ну десяток транзакций в сек на сервере с дцать ядер по 3+ГГц... :)
Нужно пробовать, по этому документу от Гугль протокол шустрый, я гонял поток сообщений с ESP - получалось что-то около 200 сообщений за 20 мс - замер делался просто копипастом из консоли количества принятых сообщений с одними данными от датчика в редактор и подсчетом кол-ва строк что получилось. К тому же сейчас дополнительно делаю сверку предыдущих данных датчика с полученными на контроллере и если до 3-го знака после запятой изменений нет, то новое сообщение не отправляется. Тут 3 датчика, не особо тупит (больше затормозила запись видео), но у меня вопрос к точке доступа используемой, ESP цепляются только на 802.11g.
 

pvvx

Активный участник сообщества
Нужно пробовать, по этому документу от Гугль протокол шустрый,
На одну транзакцию 250 мс. Как и привел - 4 шт в секунду. Это шустрый?
Древний modbus tcp на древнем одно-ядерном mips в 200 МГц - более 10 тысяч транзакций в сек (до 256 байт туда - сюда). Это не призыв к действию, а просто сравнение.
больше затормозила запись видео
А что его затормозило? Весь ресурс ушел на MQTT.
 

kharlashkin

New member
На одну транзакцию 250 мс. Как и привел - 4 шт в секунду. Это шустрый?
Древний modbus tcp на древнем одно-ядерном mips в 200 МГц - более 10 тысяч транзакций в сек (до 256 байт туда - сюда). Это не призыв к действию, а просто сравнение.
А что его затормозило? Весь ресурс ушел на MQTT.
Не знаю сейчас много значений получаю реал-тайм.
Нет затупил Blender, если в консоли просто вывожу полученные значения - загрузки процессора нет.
 

pvvx

Активный участник сообщества
Нет затупил Blender, если в консоли просто вывожу полученные значения - загрузки процессора нет.
Она системная, а не пользовательская.
Нагружена система, а не типа CPU не успевает...
MQTT имеет много переключений контекста и прочих семафоров. Система и занята этим обслуживанием и ей не до ваших других задач.
Иначе от куда намеряли 250 ms на запрос-ответ? Что там процу с многими ядрами на нескольких ГГц делать четверть секунды?
 

pvvx

Активный участник сообщества

kharlashkin

New member
Вы не на этом пишите?
Хотелось бы прилепить к UBIA в WebBluetooth или записать в смарт-часы что-то примерно аналогичное... А то выводить просто графики не очень прикольно, а в случае со смарт-часами и паять ничего не требуется...
Нет на python. JS только учусь. Кстати победил проблему, все довольно тривиально. Сбрасывать сообщение в 0, после получения и совершения всего необходимого, отрабатывают команды нормально.
 

kharlashkin

New member
Откройте тайну: Где и что "Сбрасывать сообщение в 0"?
Да все просто на самом деле - детская ошибка. В первоначальном коде, строка messageMQTT объявлялась внутри цикла, я вынес её в глобальную, теперь все отрабатывает как надо.
Код:
   if (messageMQTT == "start"){
    Serial.println(messageMQTT);
    sendData = true;
    messageMQTT = "";
    Serial.print("sendData ");
    Serial.println(sendData);
  }
  else if (messageMQTT == "stop") {
      Serial.println(messageMQTT);
    sendData = false;
    messageMQTT = "";
    Serial.print("sendData ");
    Serial.println(sendData);
}
  else if (messageMQTT == "sleep") {
      Serial.println(messageMQTT);
      messageMQTT = "";
    digitalWrite(LED_BUILTIN, LOW);
    mpu.setSleepEnabled(true);
    ESP.deepSleep(0);
}
Кстати видно как Blender грузит систему :)
 

kharlashkin

New member
В первоначальном коде, строка messageMQTT объявлялась внутри цикла
Имеется ввиду, что первоначальный, тот который я брал для примера и там не отрабатывались команды нормальна, я сделал глобальную переменную, но отрабатывалась только первая команда, после того как мы "подчищаем" эту строку все норм. И правда ведь - у меня строка была startstopstartstartstopsleepstartstop (как пример).
 
Сверху Снизу