Декодирование и кодирование JSON с Arduino или ESP8266

В этом руководстве вы узнаете, как декодировать (парсить JSON-строку) и кодировать (генерировать JSON-строку) с помощью библиотеки ArduinoJson, используя Arduino с Ethernet-шилдом. Это руководство также работает с Wi-Fi модулями ESP8266 и ESP32 с небольшими изменениями.

Декодирование и кодирование JSON с Arduino или ESP8266

Примечание

Важно: данное руководство совместимо только с библиотекой ArduinoJSON версии 5.13.5.

Установленная библиотека ArduinoJson 5.13.5

Что такое JSON?

JSON расшифровывается как J ava S cript O bject N otation. JSON — это легковесный текстовый открытый стандарт, предназначенный для обмена данными.

JSON в основном используется для сериализации и передачи структурированных данных по сетевому соединению — передача данных между сервером и клиентом. Он часто используется в сервисах, таких как API (Application Programming Interfaces — интерфейсы прикладного программирования) и веб-сервисы, предоставляющие публичные данные.

Основы синтаксиса JSON

В JSON данные структурированы определённым образом. JSON использует символы { } , : « « [ ] и имеет следующий синтаксис:

  • Данные представлены в виде пар ключ/значение

  • Двоеточие (:) присваивает значение ключу

  • Пары ключ/значение разделяются запятыми (,)

  • Фигурные скобки содержат объекты ({ })

  • Квадратные скобки содержат массивы ([ ])

Например, для представления данных в JSON пары ключ/значение выглядят следующим образом:

{"key1":"value1", "key2":"value2", "key3":"value3"}

Примеры JSON

В реальном примере вы можете захотеть структурировать данные о пользователе:

{"name":"Rui", "country": "Portugal", "age":24}

Или в IoT-проекте вы можете захотеть структурировать данные с ваших датчиков:

{"temperature":27.23, "humidity":62.05, "pressure":1013.25}

В JSON значениями могут быть другие JSON-объекты (sports) или массивы (pets). Например:

{
  "name": "Rui",
  "sports": {
    "outdoor": "hiking",
    "indoor": "swimming"
  },
  "pets": [
    "Max",
    "Dique"
  ]
}

Здесь мы структурируем данные о пользователе, и у нас есть несколько ключей: «name», «sports» и «pets».

Ключу name присвоено значение Rui. Rui может заниматься различными видами спорта в зависимости от места. Поэтому мы создаём ещё один JSON-объект для сохранения любимых видов спорта Rui. Этот JSON-объект является значением ключа «sports».

Ключ «pets» содержит массив с именами питомцев Rui, и он содержит значения «Max» и «Dique» внутри.

Большинство API возвращают данные в формате JSON, и большинство значений сами являются JSON-объектами. Следующий пример показывает данные, предоставляемые API погоды.

{
  "coord": {
    "lon": -8.61,
    "lat": 41.15
  },
  "weather": [
    {
      "id": 803,
      "main": "Clouds",
      "description": "broken clouds",
      "icon": "04d"
    }
  ],
  "base": "stations",
  "main": {
    "temp": 288.15,
    "pressure": 1020,
    "humidity": 93,
    "temp_min": 288.15,
    "temp_max": 288.15
  },
  (...)
}

Этот API предоставляет большой объём информации. Например, первые строки хранят координаты с долготой и широтой.

Arduino с Ethernet-шилдом

Примеры в этом руководстве используют Arduino с Ethernet-шилдом. Просто установите шилд на вашу плату Arduino и подключите его к сети кабелем RJ45 для установки интернет-соединения (как показано на рисунке ниже).

Arduino с Ethernet-шилдом

Примечание: примеры, приведённые в этом руководстве, также работают с ESP8266 и ESP32 с небольшими изменениями.

Подготовка Arduino IDE

Самый простой способ декодировать и кодировать JSON-строки в Arduino IDE — использовать библиотеку ArduinoJson версии 5.13.5, которая была разработана как наиболее интуитивно понятная JSON-библиотека с минимальным размером и наиболее эффективным управлением памятью для Arduino.

Она была написана с учётом Arduino, но не привязана к библиотекам Arduino, поэтому вы можете использовать эту библиотеку в любом другом C++ проекте. Также существует документация библиотеки с примерами и справочником по API.

Возможности

  • Декодирование JSON (поддерживаются комментарии)

  • Кодирование JSON (с опциональными отступами)

  • Элегантный API, очень простой в использовании

  • Фиксированное выделение памяти (zero malloc)

  • Без дублирования данных (zero copy)

  • Портативная (написана на C++98)

  • Самодостаточная (без внешних зависимостей)

  • Малый размер

  • Библиотека только из заголовочных файлов

  • Лицензия MIT

Совместимость

  • Платы Arduino: Uno, Due, Mini, Micro, Yun…

  • ESP8266, ESP32 и платы WeMos

  • Teensy, RedBearLab, Intel Edison и Galileo

  • PlatformIO, Particle и Energia

Установка библиотеки ArduinoJson

Для этого проекта вам нужно установить библиотеку ArduinoJson в вашу Arduino IDE:

  1. Нажмите здесь, чтобы скачать ArduinoJson версии 5.13.5. У вас должен появиться файл .zip в папке Downloads

  2. Распакуйте .zip файл, и вы получите папку ArduinoJson-master

  3. Переименуйте папку из ~~ArduinoJson-master~~ в ArduinoJson

  4. Переместите папку ArduinoJson в папку библиотек вашей Arduino IDE

  5. Наконец, перезапустите Arduino IDE

Декодирование JSON — Парсинг JSON-строки

Давайте начнём с декодирования/парсинга следующей JSON-строки:

{"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}

Импортируйте библиотеку ArduinoJson:

#include <ArduinoJson.h>

Arduino JSON использует предварительно выделенный пул памяти для хранения дерева JsonObject, это делается с помощью StaticJsonBuffer. Вы можете использовать ArduinoJson Assistant для вычисления точного размера буфера, но для этого примера 200 достаточно.

StaticJsonBuffer<200> jsonBuffer;

Создайте массив символов с именем json[] для хранения примера JSON-строки:

char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";

Используйте функцию parseObject() для декодирования/парсинга JSON-строки в JsonObject с именем root.

JsonObject& root = jsonBuffer.parseObject(json);

Чтобы проверить, было ли декодирование/парсинг успешным, вы можете вызвать root.success():

if(!root.success()) {
  Serial.println("parseObject() failed");
  return false;
}

Результат может быть false по трём причинам:

  • JSON-строка имеет недопустимый синтаксис;

  • JSON-строка не представляет объект;

  • StaticJsonBuffer слишком мал — используйте ArduinoJson Assistant для вычисления размера буфера.

Теперь, когда объект или массив находится в памяти, вы можете легко извлечь данные. Самый простой способ — использовать JsonObject root:

const char* sensor = root["sensor"];
long time = root["time"];
double latitude = root["data"][0];
double longitude = root["data"][1];

Вы можете использовать декодированные переменные sensor, time, latitude или longitude в логике вашего кода.

API OpenWeatherMap

Для реального примера использования Arduino с Ethernet-шилдом мы собираемся использовать бесплатный API от OpenWeatherMap для запроса прогноза погоды на текущий день для выбранного вами местоположения.

Умение работать с API — это отличный навык, потому что он предоставляет вам доступ к широкому спектру постоянно обновляемой информации, такой как текущая цена акций, курс обмена валют, последние новости, информация о трафике и многое другое.

Использование API

Бесплатный план OpenWeatherMap предоставляет всё необходимое для этого примера. Для использования API вам нужен API-ключ, известный как APIID. Чтобы получить APIID:

  1. Откройте браузер и перейдите на OpenWeatherMap

  2. Нажмите кнопку Sign up и создайте бесплатный аккаунт

  3. После создания аккаунта вам будет представлена панель управления с несколькими вкладками (см. рисунок ниже)

  4. Выберите вкладку API Keys и скопируйте ваш уникальный Key

API-ключ OpenWeatherMap

Это уникальный ключ, который вам нужен для получения информации с сайта. Скопируйте и сохраните этот ключ, он вам скоро понадобится.

Чтобы получить информацию о погоде в выбранном вами местоположении, введите следующий URL, заменив разделы в фигурных скобках на информацию о вашем местоположении и ваш уникальный API-ключ:

http://api.openweathermap.org/data/2.5/weather?q={your city},{your country code}&APPID={your API Key}

Замените {your city} на город, для которого вам нужны данные, {your country code} на код страны для этого города, и {your API key} на ваш уникальный API-ключ, который мы нашли ранее. Например, наш URL API для города Порту в Португалии после замены деталей будет выглядеть так:

http://api.openweathermap.org/data/2.5/weather?q=Porto,PT&APPID=801d2603e9f2e1c70e042e4------

Примечание: дополнительная информация об использовании API для получения данных о погоде доступна здесь.

Скопируйте ваш URL в браузер, и он должен выдать кучу информации, соответствующей данным о погоде в вашем регионе.

Тестирование API OpenWeatherMap в браузере

В нашем случае он возвращает погоду в Порту, Португалия, на день написания:

{
  "coord": {
    "lon": -8.61,
    "lat": 41.15
  },
  "weather": [
    {
      "id": 701,
      "main": "Mist",
      "description": "mist",
      "icon": "50d"
    }
  ],
  "base": "stations",
  "main": {
    "temp": 290.86,
    "pressure": 1014,
    "humidity": 88,
    "temp_min": 290.15,
    "temp_max": 292.15
  },
  (...)
}

Выполнение API-запроса с Arduino

Теперь, когда у вас есть URL, который возвращает данные о погоде в вашем регионе, вы можете автоматизировать эту задачу и получить доступ к этим данным в ваших проектах Arduino или ESP8266. Вот полный скрипт, который нужно загрузить на ваш Arduino с Ethernet-шилдом для получения температуры в Кельвинах и влажности:

/*
 * Rui Santos
 * Complete Project Details https://randomnerdtutorials.com
 * Based on the Arduino Ethernet Web Client Example
 * and on the sketch "Sample Arduino Json Web Client"
 * of the Arduino JSON library by Benoit Blanchon
 * (bblanchon.github.io/ArduinoJson)
 */

#include <ArduinoJson.h>
#include <Ethernet.h>
#include <SPI.h>

EthernetClient client;

// Name address for Open Weather Map API
const char* server = "api.openweathermap.org";

// Replace with your unique URL resource
const char* resource = "REPLACE_WITH_YOUR_URL_RESOURCE";

// How your resource variable should look like, but with your own
// COUNTRY CODE, CITY and API KEY (that API KEY below is just an example):
//const char* resource = "/data/2.5/weather?q=Porto,pt&appid=bd939aa3d23ff33d3c8f5dd1";

const unsigned long HTTP_TIMEOUT = 10000;  // max respone time from server
const size_t MAX_CONTENT_SIZE = 512;       // max size of the HTTP response

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};

// The type of data that we want to extract from the page
struct clientData {
  char temp[8];
  char humidity[8];
};

// ARDUINO entry point #1: runs once when you press reset or power the board
void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ;  // wait for serial port to initialize
  }
  Serial.println("Serial ready");
  if(!Ethernet.begin(mac)) {
    Serial.println("Failed to configure Ethernet");
    return;
  }
  Serial.println("Ethernet ready");
  delay(1000);
}

// ARDUINO entry point #2: runs over and over again forever
void loop() {
  if(connect(server)) {
    if(sendRequest(server, resource) && skipResponseHeaders()) {
      clientData clientData;
      if(readReponseContent(&clientData)) {
        printclientData(&clientData);
      }
    }
  }
  disconnect();
  wait();
}

// Open connection to the HTTP server
bool connect(const char* hostName) {
  Serial.print("Connect to ");
  Serial.println(hostName);

  bool ok = client.connect(hostName, 80);

  Serial.println(ok ? "Connected" : "Connection Failed!");
  return ok;
}

// Send the HTTP GET request to the server
bool sendRequest(const char* host, const char* resource) {
  Serial.print("GET ");
  Serial.println(resource);

  client.print("GET ");
  client.print(resource);
  client.println(" HTTP/1.1");
  client.print("Host: ");
  client.println(host);
  client.println("Connection: close");
  client.println();

  return true;
}

// Skip HTTP headers so that we are at the beginning of the response's body
bool skipResponseHeaders() {
  // HTTP headers end with an empty line
  char endOfHeaders[] = "\r\n\r\n";

  client.setTimeout(HTTP_TIMEOUT);
  bool ok = client.find(endOfHeaders);

  if (!ok) {
    Serial.println("No response or invalid response!");
  }
  return ok;
}

// Parse the JSON from the input string and extract the interesting values
// Here is the JSON we need to parse
/*{
    "coord": {
        "lon": -8.61,
        "lat": 41.15
    },
    "weather": [
        {
            "id": 800,
            "main": "Clear",
            "description": "clear sky",
            "icon": "01d"
        }
    ],
    "base": "stations",
    "main": {
        "temp": 296.15,
        "pressure": 1020,
        "humidity": 69,
        "temp_min": 296.15,
        "temp_max": 296.15
    },
    "visibility": 10000,
    "wind": {
        "speed": 4.6,
        "deg": 320
    },
    "clouds": {
        "all": 0
    },
    "dt": 1499869800,
    "sys": {
        "type": 1,
        "id": 5959,
        "message": 0.0022,
        "country": "PT",
        "sunrise": 1499836380,
        "sunset": 1499890019
    },
    "id": 2735943,
    "name": "Porto",
    "cod": 200
}*/

bool readReponseContent(struct clientData* clientData) {
  // Compute optimal size of the JSON buffer according to what we need to parse.
  // See https://bblanchon.github.io/ArduinoJson/assistant/
  const size_t bufferSize = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) +
      2*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) +
      JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(12) + 390;
  DynamicJsonBuffer jsonBuffer(bufferSize);

  JsonObject& root = jsonBuffer.parseObject(client);

  if (!root.success()) {
    Serial.println("JSON parsing failed!");
    return false;
  }

  // Here were copy the strings we're interested in using to your struct data
  strcpy(clientData->temp, root["main"]["temp"]);
  strcpy(clientData->humidity, root["main"]["humidity"]);
  // It's not mandatory to make a copy, you could just use the pointers
  // Since, they are pointing inside the "content" buffer, so you need to make
  // sure it's still in memory when you read the string

  return true;
}

// Print the data extracted from the JSON
void printclientData(const struct clientData* clientData) {
  Serial.print("Temp = ");
  Serial.println(clientData->temp);
  Serial.print("Humidity = ");
  Serial.println(clientData->humidity);
}

// Close the connection with the HTTP server
void disconnect() {
  Serial.println("Disconnect");
  client.stop();
}

// Pause for a 1 minute
void wait() {
  Serial.println("Wait 60 seconds");
  delay(60000);
}

Посмотреть исходный код

Примечание: убедитесь, что вы заменили переменную resource на ваш уникальный URL-ресурс OpenWeatherMap:

const char* resource = "REPLACE_WITH_YOUR_URL_RESOURCE";

Модификация кода для вашего проекта

В этом примере Arduino выполняет HTTP GET запрос к нужному сервису (в данном случае API OpenWeatherMap), но вы можете изменить его для запроса к любому другому веб-сервису. Мы не будем объяснять код Arduino построчно.

Для этого проекта важно, чтобы вы понимали, что нужно изменить в коде Arduino для декодирования/парсинга любого JSON-ответа. Следуйте следующим трём шагам.

ШАГ #1 — struct

Создайте структуру данных, которая может хранить информацию, которую вы хотите извлечь из API. В данном случае мы хотим хранить температуру и влажность в массивах символов:

struct clientData {
  char temp[8];
  char humidity[8];
};

ШАГ #2 — размер JsonBuffer

Перейдите в ArduinoJson Assistant и скопируйте полный ответ API OpenWeatherMap в поле Input.

Пример ArduinoJson Assistant

Скопируйте сгенерированное Expression (см. предыдущий рисунок), в нашем случае:

JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(12)

Вам нужно отредактировать функцию readReponseContent() с вашим сгенерированным размером JsonBuffer из ArduinoJson Assistant, чтобы выделить соответствующий объём памяти для декодирования JSON-ответа от API:

bool readReponseContent(struct clientData* clientData) {
  const size_t bufferSize = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) +
      2*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) +
      JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(12) + 390;

  DynamicJsonBuffer jsonBuffer(bufferSize);
  JsonObject& root = jsonBuffer.parseObject(client);

Всё ещё внутри функции readReponseContent() вам нужно скопировать переменные, которые нужны для вашего проекта, в вашу структуру данных:

strcpy(clientData->temp, root["main"]["temp"]);
strcpy(clientData->humidity, root["main"]["humidity"]);

ШАГ #3 — доступ к декодированным данным

Затем вы можете легко получить доступ к декодированным JSON-данным в вашем коде Arduino и сделать что-нибудь с ними. В этом примере мы просто выводим температуру в Кельвинах и влажность в серийный монитор Arduino IDE:

void printclientData(const struct clientData* clientData) {
  Serial.print("Temp = ");
  Serial.println(clientData->temp);
  Serial.print("Humidity = ");
  Serial.println(clientData->humidity);
}

Демонстрация

Откройте серийный монитор Arduino IDE на скорости 9600 бод, и вы увидите температуру в Кельвинах и влажность в процентах, выводимые в серийный монитор каждые 60 секунд.

Серийный монитор Arduino IDE с данными OpenWeatherMap

Вы можете получить доступ к остальной информации в ответе API OpenWeatherMap, но для целей демонстрации мы декодировали только температуру и влажность.

Кодирование JSON — Генерация JSON-строки

Давайте узнаем, как кодировать/генерировать следующую JSON-строку:

{"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}

Вы можете прочитать документацию по кодированию здесь.

Импортируйте библиотеку ArduinoJson:

#include <ArduinoJson.h>

Arduino JSON использует предварительно выделенный пул памяти для хранения дерева объектов, это делается с помощью StaticJsonBuffer. Вы можете использовать ArduinoJson Assistant для вычисления точного размера буфера, но для этого примера 200 достаточно.

StaticJsonBuffer<200> jsonBuffer;

Создайте JsonObject с именем root, который будет хранить ваши данные. Затем присвойте значения gps и 1351824120 ключам sensor и time соответственно:

JsonObject& root = jsonBuffer.createObject();
root["sensor"] = "gps";
root["time"] = 1351824120;

Затем, чтобы поместить массив в ключ data, сделайте следующее:

JsonArray& data = root.createNestedArray("data");
data.add(48.756080);
data.add(2.302038);

Очень вероятно, что вам потребуется вывести сгенерированный JSON в серийный монитор для целей отладки, для этого:

root.printTo(Serial);

После кодирования вашей информации в JSON-строку вы можете отправить её на другое устройство или веб-сервис, как показано в следующем примере.

Пример кодирования с Arduino и Node-RED

Для этого примера вам нужен Node-RED или аналогичное программное обеспечение, которое может принимать HTTP POST запросы. Вы можете установить Node-RED на свой компьютер, но мы рекомендуем запуск Node-RED на Raspberry Pi.

Создание потока (flow)

В этом потоке вы будете получать HTTP POST запрос и выводить полученные данные в окне Debug. Следуйте следующим 6 шагам для создания вашего потока:

1) Откройте программу Node-RED в вашем браузере

Доступ к программе Node-RED

2) Перетащите узел HTTP input и узел debug

Узлы HTTP input и debug в Node-RED

3) Отредактируйте HTTP input, добавив метод POST и URL /json-post-example

Настройка HTTP input в Node-RED

4) Вы можете оставить настройки по умолчанию для узла debug

Настройка узла debug в Node-RED

5) Соедините ваши узлы

Соединённые узлы в Node-RED

6) Чтобы сохранить ваше приложение, нужно нажать кнопку deploy в правом верхнем углу

Кнопка Deploy в Node-RED

Ваше приложение сохранено и готово к работе.

Отправка JSON-данных с Arduino

После того как Node-RED подготовлен к приёму POST запросов по URL /json-post-example, вы можете использовать следующий пример кода на Arduino с Ethernet-шилдом для отправки данных.

/*
 * Rui Santos
 * Complete Project Details https://randomnerdtutorials.com
 * Based on the Arduino Ethernet Web Client Example
 * and on the sketch "Sample Arduino Json Web Client"
 * of the Arduino JSON library by Benoit Blanchon
 * (bblanchon.github.io/ArduinoJson)
 */

#include <ArduinoJson.h>
#include <Ethernet.h>
#include <SPI.h>

EthernetClient client;

// Replace with your Raspberry Pi IP address
const char* server = "REPLACE_WITH_YOUR_RASPBERRY_PI_IP_ADDRESS";

// Replace with your server port number frequently port 80
// with Node-RED you need to use port 1880
int portNumber = 1880;

// Replace with your unique URL resource
const char* resource = "/json-post-example";

const unsigned long HTTP_TIMEOUT = 10000;  // max respone time from server
const size_t MAX_CONTENT_SIZE = 512;       // max size of the HTTP response

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};

// ARDUINO entry point #1: runs once when you press reset or power the board
void setup() {
  Serial.begin(9600);
  while(!Serial) {
    ;  // wait for serial port to initialize
  }
  Serial.println("Serial ready");
  if(!Ethernet.begin(mac)) {
    Serial.println("Failed to configure Ethernet");
    return;
  }
  Serial.println("Ethernet ready");
  delay(1000);
}

// ARDUINO entry point #2: runs over and over again forever
void loop() {
  if(connect(server, portNumber)) {
    if(sendRequest(server, resource) && skipResponseHeaders()) {
      Serial.print("HTTP POST request finished.");
    }
  }
  disconnect();
  wait();
}

// Open connection to the HTTP server (Node-RED running on Raspberry Pi)
bool connect(const char* hostName, int portNumber) {
  Serial.print("Connect to ");
  Serial.println(hostName);

  bool ok = client.connect(hostName, portNumber);

  Serial.println(ok ? "Connected" : "Connection Failed!");
  return ok;
}

// Send the HTTP POST request to the server
bool sendRequest(const char* host, const char* resource) {
  // Reserve memory space for your JSON data
  StaticJsonBuffer<200> jsonBuffer;

  // Build your own object tree in memory to store the data
  // you want to send in the request
  JsonObject& root = jsonBuffer.createObject();
  root["sensor"] = "dht11";

  JsonObject& data = root.createNestedObject("data");
  data.set("temperature", "30.1");
  data.set("humidity", "70.1");

  // Generate the JSON string
  root.printTo(Serial);

  Serial.print("POST ");
  Serial.println(resource);

  client.print("POST ");
  client.print(resource);
  client.println(" HTTP/1.1");
  client.print("Host: ");
  client.println(host);
  client.println("Connection: close\r\nContent-Type: application/json");
  client.print("Content-Length: ");
  client.print(root.measureLength());
  client.print("\r\n");
  client.println();
  root.printTo(client);

  return true;
}

// Skip HTTP headers so that we are at the beginning of the response's body
bool skipResponseHeaders() {
  // HTTP headers end with an empty line
  char endOfHeaders[] = "\r\n\r\n";

  client.setTimeout(HTTP_TIMEOUT);
  bool ok = client.find(endOfHeaders);

  if(!ok) {
    Serial.println("No response or invalid response!");
  }
  return ok;
}

// Close the connection with the HTTP server
void disconnect() {
  Serial.println("Disconnect");
  client.stop();
}

// Pause for a 1 minute
void wait() {
  Serial.println("Wait 60 seconds");
  delay(60000);
}

Посмотреть исходный код

Примечание: убедитесь, что вы заменили переменную server вашим IP-адресом Raspberry Pi:

const char* server = "REPLACE_WITH_YOUR_RASPBERRY_PI_IP_ADDRESS";

Модификация кода для вашего проекта

В этом примере Arduino выполняет HTTP POST запрос к Node-RED, но вы можете изменить его для выполнения запроса к другому веб-сервису или серверу. Мы не будем объяснять код Arduino построчно. Для этого проекта важно, чтобы вы понимали, что нужно изменить в коде Arduino для кодирования/генерации JSON-запроса.

Функция sendRequest()

Для этого проекта вы можете модифицировать функцию sendRequest() с вашей собственной JSON-структурой данных:

bool sendRequest(const char* host, const char* resource) {

Начните с резервирования места в памяти для ваших JSON-данных. Вы можете использовать ArduinoJson Assistant для вычисления точного размера буфера, но для этого примера 200 достаточно.

StaticJsonBuffer<200> jsonBuffer;

Создайте JsonObject с именем root, который будет хранить ваши данные, и присвойте значения вашим ключам (в этом примере у нас есть ключ sensor):

JsonObject& root = jsonBuffer.createObject();
root["sensor"] = "dht11";

Чтобы поместить данные в массив, сделайте следующее:

JsonObject& data = root.createNestedObject("data");
data.set("temperature", "30.1");
data.set("humidity", "70.1");

Выведите сгенерированную JSON-строку в серийный монитор Arduino IDE для целей отладки:

root.printTo(Serial);

Остальная часть функции sendRequest() — это POST-запрос.

client.print("POST ");
client.print(resource);
client.println(" HTTP/1.1");
client.print("Host: ");
client.println(host);
client.println("Connection: close\r\nContent-Type: application/json");
client.print("Content-Length: ");
client.print(root.measureLength());
client.print("\r\n");
client.println();
root.printTo(client);

Обратите внимание, что вы можете использовать root.measureLength() для определения длины вашего сгенерированного JSON. Функция root.printTo(client) отправляет JSON-данные Ethernet-клиенту.

Демонстрация

Откройте серийный монитор Arduino IDE на скорости 9600 бод, и вы увидите JSON-объект, выводимый в серийный монитор каждые 60 секунд.

Серийный монитор Arduino IDE с JSON POST

Во вкладке debug Node-RED вы увидите тот же JSON-объект, получаемый каждые 60 секунд:

Вкладка debug в Node-RED с полученным JSON

Наконец, вы можете создать функции в Node-RED, которые делают что-то полезное с полученными данными, но для целей демонстрации мы просто выводим примеры данных.

Подведение итогов

В этом руководстве мы показали вам несколько примеров декодирования и кодирования JSON-данных. Вы можете следовать этим базовым шагам для создания более сложных проектов, требующих обмена данными между устройствами.

Надеемся, что это руководство было для вас полезным.

Если вам понравился этот проект и домашняя автоматизация, обязательно ознакомьтесь с нашим курсом: Создайте систему домашней автоматизации за $100.