• Уважаемые посетители сайта esp8266.ru!
    Мы отказались от размещения рекламы на страницах форума для большего комфорта пользователей.
    Вы можете оказать посильную поддержку администрации форума. Данные средства пойдут на оплату услуг облачных провайдеров для сайта esp8266.ru
  • Система автоматизации с открытым исходным кодом на базе 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 (как пример).
 
Сверху Снизу