Используем JSON в Quectel OpenCPU

Исторически так сложилось, что JSON как текстовый формат используется в основном в веб, клиент-серверном обмене данными, межсерверном обмене данными и т.п., но никто не мешает его использовать и в embedded, тем более что я нашел достаточно много материалов по этой теме в интернете и смог разобраться, попробую здесь описать свой опыт использования JSON в программировании для модемов Quectel на OpenCPU.

Для начала небольшая справка.

Цитата из википедии:

JSON (JavaScript Object Notation) — текстовый формат обмена данными, основанный на JavaScript. Как и многие другие текстовые форматы, JSON легко читается людьми. За счёт своей лаконичности по сравнению с XML, формат JSON может быть более подходящим для сериализации сложных структур.

Так уж вышло, что OpenCPU написан полностью на Си, использовать Си++ и какие-нибудь библиотеки с многими зависимостями для JSON здесь не получится, да и ресурсы весьма ограничены, здесь вам не компьютер и памяти в том же модуле M66 выделяется всего 360kB для программы и 100kB RAM.

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

Исходя из всего выше изложенного, я искал код для использования JSON по следующим критериям:

  1. Не должно быть практически никаких зависимостей;
  2. Написан на чистом Си;
  3. Должен легко портироваться под мое применение;
  4. Кроме парсера должна быть возможность формирования JSON объектов и сериализации.

И я нашел JFES — Json For Embedded Systems. Документация у этого проекта вся описана в README репозитория, прочитать ее минутное дело, я не буду здесь делать выкладки из нее. Остановлюсь на применении в OpenCPU.

Чтобы начать работать с JFES необходимо скопировать файлы jfes.c и jfes.h в папку /custom проекта Opencpu и подключить хэдер в файле main.c:

#include "jfes.h"

Чтобы инициализировать библиотеку необходимо инициализировать объект jfes_config_t:

jfes_config_t config;

Теперь самое важное JFES не умеет выделять память сам, поэтому необходимо указать функции для выделения и освобождения памяти, которые предоставляются Opencpu:

config.jfes_malloc = Ql_MEM_Alloc;
config.jfes_free = Ql_MEM_Free;

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

{
    "id": 12345,
    "imei": "759304218643261",
    "status": true
}

Код будет выглядеть следующим образом:

jfes_value_t * value = jfes_create_object_value ( &config );

jfes_set_object_property ( &config, value, jfes_create_integer_value ( &config, 12345 ), "id", 0 );
jfes_set_object_property ( &config, value, jfes_create_string_value ( &config, "759304218643261", 0 ), "imei", 0 );
jfes_set_object_property ( &config, value, jfes_create_boolean_value ( &config, true ), "status", 0 );

При создании объекта строкового типа указывается последним аргументом длина строки или 0, если строка заканчивается нулевым символом.

Далее сериализуем полученный объект в строку для отправки. В dump_size будет лежать длина полученной строки:

char beauty_dump[1024];
jfes_size_t dump_size = 1024;
jfes_value_to_string ( value, &beauty_dump[0], &dump_size, 1 );

Если последним параметром передать не 1, а 0, то вывод в строку будет одной строкой, без переносов, пробелов и т.п. (ugly json).

Полученная строка не будет нуль-терминированной, поэтому обязательно в конец необходимо добавить ноль:

beauty_dump[dump_size] = '\0';

Можно вывести полученный JSON в отладочный интерфейс:

APP_DEBUG ( "[ JSON ] %s \r\n",  &beauty_dump[0] );

На этом все.