Проанализировать большой ответ json с помощью ESP8266

Я выполняю проект, в котором мой Arduino использует внешний модуль Wi-Fi ESP8266 (с прошивкой AT) для отправки http-запросов и получения ответа сервера.

Я пытаюсь получить табло NBA, и вот что мой Arduino отправляет ESP:

> AT+CIPSTART=0,"TCP","data.nba.net",80
0,CONNECT

OK

> AT+CIPSEND=0,103
SEND OK

> GET /data/10s/prod/v1/20190214/scoreboard.json HTTP/1.1
Host: data.nba.net
Connection: keep-alive

+IPD,0,1460:HTTP/1.1 200 OK..
.............................
...here comes the response...
.............................

Теперь код работает нормально, и я могу получить необходимый файл json. Проблема в том, что ответ иногда бывает слишком длинным, и файл усекается, поэтому ответ неполный. Сам файл иногда довольно большой, но мне нужно всего несколько цифр из него (текущие команды + текущий результат). Итак, мои вопросы таковы:

  1. Я использую метод ReadString () (экземпляра объекта SoftwareSerial) для чтения ответа. Есть ли способ проанализировать фрагмент ответа за фрагментом, не загружая все в мою память ESP?
  2. Есть ли способ ограничить реакцию?
  3. Есть ли какие-либо альтернативы методу, который я использую? Может быть, разбор веб-сайта (nba.com или espn.com/nba) напрямую? Если да, то как я могу это сделать?
  4. Может быть, есть способ написать отдельный (бесплатный) онлайн-API для анализа json, ограничения ответа и отправки небольшой версии по запросу?

PS. Я попытался сделать то же самое без прошивки AT, прошив модуль кодом, который напрямую использовал HttpClient (с библиотеками ESP8266WiFi.h и ESP8266HTTPClient.h):

HTTPClient http;
http.begin(request);
if (http.GET() > 0) {
    response = http.getString();
}
http.end();

Это не очень помогло. У меня все еще не хватает памяти, так как я напрямую загружаю ответ в память с помощью getString().

PPS. На всякий случай, если это имеет значение, я использую ATmega2560 и небольшой модуль ESP-01 (похожий на этот).

, 👍1

Обсуждение

большой ... вам нужно будет быть более конкретным, чем "большой", @Jaromanda X

@JaromandaX .... иди сюда.... http://data.nba.net/data/10s/prod/v1/20190214/scoreboard.json, @jsotola

какие данные вы извлекаете? ..... вы, вероятно, уже знаете об этом, но вот API для тех, кто еще читает это https://github.com/kshvmdn/nba.js/blob/master/docs/api/DATA.md .......... может быть, есть другой способ извлечь данные, @jsotola

Нет. Ты иди туда. Насколько сложно количественно оценить большие, @Jaromanda X

@JaromandaX, "> 10 КБ " - это то, что я бы назвал большим. Я думаю выше, что он просто игнорирует остальную часть ответа., @hayk

@jsotola нет, не совсем. Я уже осматривал его раньше. "Scoreboard.json" - это самое близкое, что мне действительно нужно, и оно слишком велико, так как есть данные о потоковой передаче и т. Д. (Кому это все равно нужно :D)., @hayk


2 ответа


Лучший ответ:

1

Хорошо, значит, я смог это сделать. Вот обходной путь.

Допустим, ответ GET представляет собой плоскую строку, содержащую большой файл .json со всеми запятыми и фигурными скобками. Идея состоит в том, чтобы прочитать его байт за байтом и вместо того, чтобы хранить его, просто найдите нужный вам ключ и извлеките его значение.

Мы хотим найти поле, содержащее, скажем, "97,5 FM" из http://data.nba.net/data/10s/prod/v1/20190223/scoreboard.json (это в самом конце), пропустите 3 байта (это просто"|"), а затем прочитайте и последовательно выведите 4 байта (это "1280")*. Мы бы сделали что-то вроде этого (конечно, обязательно подключитесь к Wi-Fi и включите <ESP8266HTTPClient.h><ESP8266HTTPClient.h>:

HTTPClient http;
http.begin("http://data.nba.net/data/10s/prod/v1/20190223/scoreboard.json");
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK)
  Serial.println(getValue(http, "97.5 FM", 3, 4));

где функция GetValue() определяется следующим образом:

String getValue(HTTPClient &http, String key, int skip, int get) {
  bool found = false, look = false;
  int ind = 0;
  String ret_str = "";

  int len = http.getSize();
  char char_buff[1];
  WiFiClient * stream = http.getStreamPtr();
  while (http.connected() && (len > 0 || len == -1)) {
    size_t size = stream->available();
    if (size) {
      int c = stream->readBytes(char_buff, ((size > sizeof(char_buff)) ? sizeof(char_buff) : size));
      if (len > 0)
        len -= c;
      if (found) {
        if (skip == 0) {
          ret_str += char_buff[0];
          get --;
        } else
          skip --;
        if (get <= 0)
          break;
      }
      else if ((!look) && (char_buff[0] == key[0])) {
        look = true;
        ind = 1;
      } else if (look && (char_buff[0] == key[ind])) {
        ind ++;
        if (ind == key.length()) found = true;
      } else if (look && (char_buff[0] != key[ind])) {
        ind = 0;
        look = false;
      }
    }
  }
  return ret_str;
}

Вы можете вызывать эту функцию GetValue() сколько угодно раз, но строки, которые вы ищете, должны быть только друг за другом, потому что ответ на GET считывается байт за байтом только один раз.

Вероятно, есть (т. е., скорее всего) лучший способ сделать это, но этот подход работает, и он довольно быстрый, так что...


* Это, конечно, всего лишь пример, и вы можете перегрузить это определение функции GetValue (), чтобы, скажем, читать, пока оно не получит фигурную скобку или что-то еще.

,

0

Проверьте потоковый анализатор JSON. Он предназначен для анализа больших объектов JSON на процессорах с небольшим объемом памяти. Вы просто будете читать по одному символу за раз из HTTP-соединения, а не весь ответ; передайте символ в библиотеку, и она будет вызывать код, который вы предоставляете каждый раз, когда она видит массив, объект, ключ или значение. Затем вы можете извлечь только те фрагменты, которые вам нужны, без необходимости загружать все это сразу в память.

В библиотеке есть хорошие примеры, которые покажут вам, как ее использовать.

,

Спасибо, я видел это раньше, но я не совсем уверен, как это работает. (a) В примере, в "ESP.getFreeHeap(), является ли" ESP последовательным экземпляром программного обеспечения? (б) Что я должен на самом деле передать в " разбор.разбор (...)"? Это возврат " http.getString ()"?, @hayk

"ESP.getFreeHeap ()" - это диагностическая функция. Он возвращает, сколько памяти осталось выделить. Пример вызывает его, чтобы доказать, что синтаксический анализатор не является утечкой или нехваткой памяти. В примере это должно быть одно и то же до и после запуска синтаксического анализатора. "ESP" - это специальный класс с функциями для работы с процессором ESP8266. Вам не нужно иметь дело ни с одной из функций "ESP". Вам нужно прочитать один символ за раз из вашего HTTP-клиента, а затем передать каждый символ в " parse.parse ()"., @romkey