MCU & FPGA C,C++ Usando JSON e cJSON em Microcontroladores

Usando JSON e cJSON em Microcontroladores


Trabalhando com arrays em JSON usando cJSON

Agora vamos adicionar um elemento muito comum em mensagens JSON: o array. Um array representa uma lista de valores. Em sistemas embarcados, arrays aparecem quando precisamos enviar várias amostras de sensores, uma lista de eventos, leituras de canais ADC, estados de entradas digitais ou registros de diagnóstico.

Até agora, geramos uma estrutura assim:

{
  "device": {
    "id": "mcu-001",
    "model": "generic-mcu",
    "firmware": "1.0.0"
  },
  "telemetry": {
    "temperature": 28.75,
    "voltage": 3.29,
    "uptime": 15240
  }
}

Agora vamos evoluir para uma mensagem onde telemetry possui um array chamado adc_samples:

{
  "device": {
    "id": "mcu-001",
    "model": "generic-mcu",
    "firmware": "1.0.0"
  },
  "telemetry": {
    "temperature": 28.75,
    "voltage": 3.29,
    "uptime": 15240,
    "adc_samples": [1023, 1040, 1035, 1051, 1062]
  }
}

Visualmente, a árvore fica assim:

root
├── device
│   ├── id
│   ├── model
│   └── firmware
└── telemetry
    ├── temperature
    ├── voltage
    ├── uptime
    └── adc_samples
        ├── 1023
        ├── 1040
        ├── 1035
        ├── 1051
        └── 1062

Em cJSON, criamos um array com:

cJSON *array = cJSON_CreateArray();

Depois adicionamos itens ao array usando:

cJSON_AddItemToArray(array, cJSON_CreateNumber(valor));

Vamos primeiro criar uma função mock para simular amostras de ADC.

sensor_mock.h

#ifndef SENSOR_MOCK_H
#define SENSOR_MOCK_H

#include <stddef.h>
#include <stdint.h>

float sensor_mock_read_temperature(void);
float sensor_mock_read_voltage(void);
unsigned long sensor_mock_get_uptime(void);

/**
 * @brief Preenche um buffer com amostras simuladas de ADC.
 *
 * @param samples Ponteiro para o buffer onde as amostras serão gravadas.
 * @param count Quantidade máxima de amostras no buffer.
 * @return Quantidade real de amostras preenchidas.
 */
size_t sensor_mock_read_adc_samples(uint16_t *samples, size_t count);

#endif

sensor_mock.c

#include "sensor_mock.h"

/**
 * @brief Simula a leitura de temperatura.
 *
 * @return Temperatura simulada em graus Celsius.
 */
float sensor_mock_read_temperature(void)
{
    return 28.75f;
}

/**
 * @brief Simula a leitura da tensão de alimentação.
 *
 * @return Tensão simulada em volts.
 */
float sensor_mock_read_voltage(void)
{
    return 3.29f;
}

/**
 * @brief Simula o tempo de atividade do sistema.
 *
 * @return Tempo de atividade simulado em segundos.
 */
unsigned long sensor_mock_get_uptime(void)
{
    return 15240UL;
}

/**
 * @brief Preenche um buffer com valores simulados de ADC.
 *
 * Em um microcontrolador real, esses dados poderiam vir de:
 * ADC com polling, ADC com interrupção, ADC com DMA ou leitura filtrada.
 *
 * @param samples Buffer de destino.
 * @param count Quantidade máxima de amostras.
 * @return Quantidade de amostras realmente preenchidas.
 */
size_t sensor_mock_read_adc_samples(uint16_t *samples, size_t count)
{
    static const uint16_t mock_values[] = {
        1023, 1040, 1035, 1051, 1062
    };

    size_t mock_count = sizeof(mock_values) / sizeof(mock_values[0]);
    size_t limit = (count < mock_count) ? count : mock_count;

    for (size_t i = 0; i < limit; i++)
    {
        samples[i] = mock_values[i];
    }

    return limit;
}

Agora vamos alterar o json_builder.c para incluir o array adc_samples.

json_builder.c com array

#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>

#include "cJSON.h"
#include "sensor_mock.h"
#include "json_builder.h"

#define ADC_SAMPLE_MAX_COUNT 5

/**
 * @brief Cria uma mensagem JSON de telemetria com hierarquia e array.
 *
 * Estrutura gerada:
 *
 * {
 *   "device": {
 *     "id": "mcu-001",
 *     "model": "generic-mcu",
 *     "firmware": "1.0.0"
 *   },
 *   "telemetry": {
 *     "temperature": 28.75,
 *     "voltage": 3.29,
 *     "uptime": 15240,
 *     "adc_samples": [1023, 1040, 1035, 1051, 1062]
 *   }
 * }
 *
 * @return String JSON alocada dinamicamente.
 *         O chamador deve liberar com free().
 *         Retorna NULL em caso de erro.
 */
char *json_builder_create_telemetry_message(void)
{
    char *json_string = NULL;

    uint16_t adc_samples[ADC_SAMPLE_MAX_COUNT];
    size_t adc_count = sensor_mock_read_adc_samples(
        adc_samples,
        ADC_SAMPLE_MAX_COUNT
    );

    cJSON *root = cJSON_CreateObject();
    if (root == NULL)
    {
        return NULL;
    }

    cJSON *device = cJSON_CreateObject();
    if (device == NULL)
    {
        cJSON_Delete(root);
        return NULL;
    }

    cJSON *telemetry = cJSON_CreateObject();
    if (telemetry == NULL)
    {
        cJSON_Delete(device);
        cJSON_Delete(root);
        return NULL;
    }

    cJSON *adc_array = cJSON_CreateArray();
    if (adc_array == NULL)
    {
        cJSON_Delete(telemetry);
        cJSON_Delete(device);
        cJSON_Delete(root);
        return NULL;
    }

    cJSON_AddStringToObject(device, "id", "mcu-001");
    cJSON_AddStringToObject(device, "model", "generic-mcu");
    cJSON_AddStringToObject(device, "firmware", "1.0.0");

    cJSON_AddNumberToObject(telemetry, "temperature", sensor_mock_read_temperature());
    cJSON_AddNumberToObject(telemetry, "voltage", sensor_mock_read_voltage());
    cJSON_AddNumberToObject(telemetry, "uptime", sensor_mock_get_uptime());

    for (size_t i = 0; i < adc_count; i++)
    {
        cJSON *sample_item = cJSON_CreateNumber(adc_samples[i]);

        if (sample_item == NULL)
        {
            cJSON_Delete(adc_array);
            cJSON_Delete(telemetry);
            cJSON_Delete(device);
            cJSON_Delete(root);
            return NULL;
        }

        cJSON_AddItemToArray(adc_array, sample_item);
    }

    cJSON_AddItemToObject(telemetry, "adc_samples", adc_array);
    cJSON_AddItemToObject(root, "device", device);
    cJSON_AddItemToObject(root, "telemetry", telemetry);

    json_string = cJSON_PrintUnformatted(root);

    cJSON_Delete(root);

    return json_string;
}

Existe um ponto importante sobre propriedade de memória. Antes de adicionarmos adc_array ao objeto telemetry, ele ainda é independente. Se ocorrer erro antes disso, precisamos chamar:

cJSON_Delete(adc_array);

Depois que fazemos:

cJSON_AddItemToObject(telemetry, "adc_samples", adc_array);

o array passa a pertencer ao objeto telemetry.

Depois que fazemos:

cJSON_AddItemToObject(root, "telemetry", telemetry);

o objeto telemetry passa a pertencer ao objeto root.

Por isso, no final basta chamar:

cJSON_Delete(root);

e toda a árvore será liberada.

O main.c continua praticamente igual:

#include <stdio.h>
#include <stdlib.h>
#include "json_builder.h"

int main(void)
{
    char *message = json_builder_create_telemetry_message();

    if (message == NULL)
    {
        printf("Erro ao criar mensagem JSON.\n");
        return 1;
    }

    printf("Mensagem JSON gerada:\n%s\n", message);

    free(message);

    return 0;
}

A saída esperada será semelhante a:

{"device":{"id":"mcu-001","model":"generic-mcu","firmware":"1.0.0"},"telemetry":{"temperature":28.75,"voltage":3.29,"uptime":15240,"adc_samples":[1023,1040,1035,1051,1062]}}

Em microcontroladores, arrays precisam de atenção especial. Um array pequeno, como cinco amostras de ADC, é tranquilo. Porém, um array com centenas ou milhares de amostras pode consumir muita RAM, gerar uma string JSON grande e causar atraso na transmissão. Para aquisição de sinais, como áudio, vibração ou corrente elétrica, normalmente é melhor enviar blocos pequenos ou usar protocolo binário. JSON é excelente para telemetria, configuração e diagnóstico, mas nem sempre é a melhor escolha para fluxo contínuo de alta taxa.

0 0 votos
Classificação do artigo
Inscrever-se
Notificar de
guest
0 Comentários
mais antigos
mais recentes Mais votado
Feedbacks embutidos
Ver todos os comentários

Related Post

0
Adoraria saber sua opinião, comente.x