Визуальная электроника

Для реализации одного из наших проектов нам понадобилось найти способы наладить взаимодействие между «взрослыми» серверами, служащих для накопления и обработки больших массивов информации и нашим устройством с микроконтроллером «STM32», эту самую информацию собирающим. Одним из вопросов, возникающих при проектировании радиоэлектронных систем, является передача данных (например, первоначальных настроек) от сервера к контроллеру.

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

 

Давайте реализуем прием информации в формате JSON устройством на базе STM32 (конкретно, на STM32F107RB). Для меньшего расхода ресурсов микроконтроллера будем использовать JSON парсер JSMN. Конечно, STM32 потянет и куда более тяжелый софт, но, учитывая то, что JSON парсинг, как правило, отнюдь не основная задача микроконтроллера, не будем отъедать ресурсов более необходимого и используем наиболее легковесное решение.

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

Давайте попробуем считать с сервера JSON строку, обработать ее JSON парсером JSMN и, используя полученные данные, сформировать «нормальные» пары ключ-значение.

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

Java Code:
  1. {"REQUEST":"SET","PARAMETR":"Device","NAME":"VE-MS001","RS485ADR":0,"IP_ADDRESS":[192,168,1,254],"NETMASK":[255,255,255,0],"GATEWAY":[192,168,1,1]}

Итак, скачиваем и подключаем к своему проекту собственно парсер JSMN. Проблем быть не должно, так как весь парсер умещается всего в двух файлах, jsmn.c и jsmn.h, все остальное — документация, примеры использования и тесты.

Создаем структуру, в которую в конце концов попадут все декодированные данные:

C++ Code:
  1. #include "jsmn.h"
  2.  
  3. #define MAXANSWERLENGTH 256
  4. #define MAXNUMBER_OF_TOKENS 32
  5. #define STR_LENGTH 8
  6. #define NUM_LENGTH 15
  7.  
  8. typedef struct
  9. {
  10. uint8_t REQUEST[STR_LENGTH];
  11. uint8_t PARAMETR[STR_LENGTH];
  12. uint8_t NAME[STR_LENGTH];
  13. uint8_t RS485ADR[NUM_LENGTH];
  14. uint8_t IP_ADDRESS[STR_LENGTH];
  15. uint8_t NETMASK[STR_LENGTH];
  16. uint8_t GATEWAY[STR_LENGTH];
  17. }tmStruct;
  18.  
  19. tmStruct RAMStruct;

Подготавливаем переменные для парсера:

C++ Code:
  1. int resultCode;
  2. jsmn_parser p;
  3. jsmntok_t tokens[MAXNUMBER_OF_TOKENS];

Натравливаем JSON парсер на наши данные:

C++ Code:
  1. unsigned char ptr[]="{\"REQUEST\":\"SET\",\"PARAMETR\":\"Device\",\"NAME\":\"VE-MS001\",\"RS485ADR\":0,\"IP_ADDRESS\":[192,168,1,254],\"NETMASK\":[255,255,255,0],\"GATEWAY\":[192,168,1,1]}";
  2. unsigned int len=sizeof(ptr);
  3.  
  4. jsmn_init(&p);
  5. resultCode = jsmn_parse(&p, ptr, len, tokens, sizeof(tokens) / sizeof(tokens[0]));
  6.  
  7. if (resultCode > 0)
  8. {
  9. char keyString[MAX_TOKEN_LENGTH];
  10. char Prev_keyString[MAX_TOKEN_LENGTH];
  11.  
  12. for (int i = 1; i <= resultCode - 1; i++) // resultCode == 0 => whole json string
  13. {
  14. jsmntok_t key = tokens[i];
  15. uint16_t length = key.end - key.start;
  16.  
  17. if (length < MAX_TOKEN_LENGTH)
  18. {
  19. memcpy(keyString, &ptr[key.start], length);
  20. keyString[length] = '\0';
  21.  
  22. if (strcmp(Prev_keyString, "REQUEST") == 0)
  23. strcpy(RAMStruct.REQUEST, keyString);
  24. else if (strcmp(Prev_keyString, "PARAMETR") == 0)
  25. strcpy(RAMStruct.PARAMETR, keyString);
  26. else if (strcmp(Prev_keyString, "NAME") == 0)
  27. strcpy(RAMStruct.Name, keyString);
  28. else if (strcmp(Prev_keyString, "RS485ADR") == 0)
  29. strcpy(RAMStruct.RS485ADR, keyString);
  30. else if (strcmp(Prev_keyString, "IP_ADDRESS") == 0)
  31. strcpy(RAMStruct.IP_ADDRESS, keyString);
  32. else if (strcmp(Prev_keyString, "NETMASK") == 0)
  33. strcpy(RAMStruct.NETMASK, keyString);
  34. else if (strcmp(Prev_keyString, "GATEWAY") == 0)
  35. strcpy(RAMStruct.GATEWAY, keyString);
  36.  
  37. strcpy(Prev_keyString, keyString);
  38. }
  39. }
  40. }

В результате всех этих программных манипуляций в отладчике вы должны увидеть примерно следующую картину:

pic1

Сразу же возникает закономерный вопрос: а как же сформировать JSON в ответ? Тут ситуация еще проще. Нам поможет библиотека под названием JWrite. Вот пример, формирующий строку с текущими настройками нашего устройства:

C++ Code:
  1. #include "jWrite.h"
  2.  
  3. unsigned char ptr[MAXANSWERLENGTH];
  4.  
  5. if(type==1) //Answer Device
  6. {
  7. jwOpen( ptr, MAXANSWERLENGTH, JW_OBJECT, JW_PRETTY ); // open root node as object
  8. jwObj_string("ANSWER", "Device"); // writes "key":"value"
  9. jwObj_string("NAME", device.name); // writes "key":"value"
  10. jwObj_int("SENSORS", device.number_sensors); // writes "int":1
  11. jwObj_int("RS485_ADRESS", device.rs485_addr); // writes "int":1
  12. jwObj_array("IP_ADDRESS"); // start "anArray": [...]
  13. jwArr_int(device.ip_addr[0]); // add a few integers to the array
  14. jwArr_int(device.ip_addr[1]);
  15. jwArr_int(device.ip_addr[2]);
  16. jwArr_int(device.ip_addr[3]);
  17. jwEnd();
  18. jwObj_array("NETMASK"); // start "anArray": [...]
  19. jwArr_int(device.netmask[0]); // add a few integers to the array
  20. jwArr_int(device.netmask[1]);
  21. jwArr_int(device.netmask[2]);
  22. jwArr_int(device.netmask[3]);
  23. jwEnd();
  24. jwObj_array("GATEWAY"); // start "anArray": [...]
  25. jwArr_int(device.gw[0]); // add a few integers to the array
  26. jwArr_int(device.gw[1]);
  27. jwArr_int(device.gw[2]);
  28. jwArr_int(device.gw[3]);
  29. jwEnd(); // end the array
  30. resultCode=jwClose();
  31. }

Результатом этих действий будет являться следущая строка с JSON:

Java Code:
  1. {"ANSWER":"Device","NAME":"VE-MS001","SENSORS":2,"RS485_ADRESS":0,"IP_ADDRESS":[192,168,1,254],"NETMASK":[255,255,255,0],"GATEWAY":[192,168,1,1]}
Добавить комментарий