五、I2C协议之温湿度采样,JSON格式上报数据

首先进行配置,SYS,RCC,时钟树,串口,前面文章都有详细的讲解了如何进行配置,包括如何添加printf重定向代码,这里就不在讲解。

下面来进行I2C引脚的配置:我的板子上sht30模块是PB13、PB14为I2C模式,所以首先先配置这两个引脚为I2C2_SCL和I2C2_SCL的模式,由于此时还没有使能I2C2功能,所以引脚为黄色状态,接下来使能I2C2,引脚就变为绿色状态,到此配置就完成了,ctrl+s生成代码。(注意:生成代码之前记得勾选每个外面单独生成一个C文件的选项,前面的文章也有说过,这里就不再讲诉)

 接下来就是代码部分,首先先编写sht30.h文件的代码,由于生成的项目中没有该文件,所以这个文件要我们自己创建,Inc--->New--->Header File,如何添加名字即可(由于我已经创建了,所以显示该文件已存在)

 sht30.h文件代码如下:

/*
 * sht30.h
 *
 *  Created on: 2023年5月2日
 *      Author: 86147
 */

#ifndef INC_SHT30_H_
#define INC_SHT30_H_

#include "stm32l4xx_hal.h"

/* chip 7-bits hardware_hal.h */
#define SHT30_ADDR    0x44

/* I2C protocol commuication address */
#define SHT30_ADDR_WR    (SHT30_ADDR<<1)
#define SHT30_ADDR_RD    ((SHT30_ADDR<<1) | 0X01)

#define SHT30_DATA_SIZE   6

typedef enum
{
	/* Soft reset command */
	SOFT_RESET_CMD = 0x30A2,

	HIGH_ENABLED_CMD   = 0X2C06,
	MEDIUM_ENABLED_CMD  = 0X2C0D,
	LOW_ENABLED_CMD   = 0X2C10,
	HIGH_DISABLED_CMD = 0X2400,
	MEDIUM_DISABLED_CMD = 0X240B,
	LOW_DISABLED_CMD = 0X2416,

	HIGH_0_5_CMD  = 0X2032,
	MEDIUM_0_5_CMD = 0X2024,
	LOW_0_5_CMD  = 0X202F,
	HIGH_1_CMD = 0X2130,
	MEDIUM_1_CMD  = 0X2126,
	LOW_1_CMD = 0X212D,
	HIGH_2_CMD = 0X2236,
	MEDIUM_2_CMD  = 0X2220,
	LOW_2_CMD = 0X222B,
	HIGH_4_CMD = 0X2334,
	MEDIUM_4_CMD = 0X2322,
	LOW_4_CMD = 0X2329,
	HIGH_10_CMD = 0X2737,
	MEDIUM_10_CMD = 0X2721,
	LOW_10_CMD = 0X272A,

}SHT30_CMD;


extern int SHT30_SampleData(float *temperature,float *humidity);

#endif /* INC_SHT30_H_ */

同理,编写sht30.c文件代码

/*
 * sht30.c
 *
 *  Created on: May 1, 2023
 *      Author: 86147
 */
#include <stdio.h>
#include "stm32l4xx_hal.h"
#include "i2c.h"
#include "sht30.h"

//#define CONFIG_SHT30_DEBUG

#ifdef CONFIG_SHT30_DEBUG
#define sht30_print(format,args...) printf(format,##args)
#else
#define sht30_print(format,args...) do{} while(0)
#endif

static int sht30_send_cmd(SHT30_CMD cmd)
{
uint8_t buf[2];

buf[0] = cmd >> 8;
buf[1] = cmd & 0xFF;

return HAL_I2C_Master_Transmit(&hi2c2,SHT30_ADDR_WR,(uint8_t*)buf,2,0xFFFF);
}

static void sht30_soft_reset(void)
{
sht30_send_cmd(SOFT_RESET_CMD);
HAL_Delay(1);
}

static int sht30_single_shot_measurement(uint8_t *buf,uint8_t buf_size)
{
uint16_t    cmd = HIGH_ENABLED_CMD;/*High with c1ock stretching */
uint8_t        rv;

if(!buf || buf_size<SHT30_DATA_SIZE )
{
sht30_print("%s(): Invalid input arguments\n",__func__);
return -1;
}

rv = sht30_send_cmd(cmd);
if(rv)
{
sht30_print("ERROR: SHT30 send measurement command failure, rv=%d\n", rv);
sht30_soft_reset();
return -2;
}

rv = HAL_I2C_Master_Receive(&hi2c2,SHT30_ADDR_RD,buf,SHT30_DATA_SIZE, 0xFFFF);
if(rv)
{
sht30_print( "ERROR: SHT30 read measurement result failure,rv=%d\n", rv);
return -3;
}

return 0;
}

static uint8_t sht30_crc8(const uint8_t *data,int len)
{
const uint8_t  POLYNOMIAL = 0x31;   /* SHT30 CRC8 Polynomial: ex31=x8 + x5 + x4 + 1 */
uint8_t     crc = 0xFF;                         /* SHT398 CRC8 Initialization: exFF */
int        i, j;

for (i=0; i<len; ++i)
{
crc ^= *data++;

for (j=0; j<8; ++j)
{
crc = ( crc & 0x80)? (crc << 1)^ POLYNOMIAL: (crc << 1);
}
}

return crc;
}

int SHT30_SampleData(float * temperature, float *humidity)
{
uint8_t  buf[SHT30_DATA_SIZE];
int     rv;
uint16_t    temp;
uint16_t    humd ;
uint8_t      crc;

if( !temperature || !humidity)
{
sht30_print("%s(): Invalid input arguments\n",__func__);
return -1;
}

rv = sht30_single_shot_measurement(buf,SHT30_DATA_SIZE);
if( rv )
{
sht30_print("SHT30 single Short measurement failure,rv=%d\n",rv);
return -2;
}

#ifdef CONFIG_SHT30_DEBUG
{
int    i;
sht30_print("SHT30 get %d bytes sample data: \n",SHT30_DATA_SIZE);
for(i=0; i<SHT30_DATA_SIZE; i++)
{
sht30_print("0x%02x ", buf[i];
}
sht30_print("\n");
}
#endif

/*byte[0-1] is temperature value,and byte[2] is temperature CRC*/
crc = sht30_crc8(buf,2) ;
sht30_print("SHT30 temperature cal_CRC:[%02x] EXP_CRC: [%02x]\n",crc,buf[2]);
if( crc != buf[2])
{
sht30_print("SHT30 measurement temperature got CRC error\n");
return -3;
}

/* byte[3-4] is humidity value,and byte[5] is humidity CRC*/
crc = sht30_crc8(&buf[3],2);
sht30_print("SHT30 humidity Cal_CRC:[%02x] EXP_CRC: [%02x]\n",crc,buf[5]);
if( crc != buf[5])
{
sht30_print( "SHT30 measurement temperature got CRC error\n");
return -4;
}

temp = (buf[0]<<8)| buf[1];
humd = (buf[3]<<8)| buf[4];
* temperature = -45 + 175*((float)temp/65535);
*humidity = 100 * ((float )humd / 65535);
return 0;
}



由于我采用的是JSON格式上报数据,所以项目里面应该要有core_json.h文件和core_json.c文件。

core_json.h文件代码如下

/*
 * coreJSON v3.0.0
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * @file core_json.h
 * @brief Include this header file to use coreJSON in your application.
 */

#ifndef CORE_JSON_H_
#define CORE_JSON_H_

#include <stdbool.h>
#include <stddef.h>

/**
 * @ingroup json_enum_types
 * @brief Return codes from coreJSON library functions.
 */
typedef enum
{
    JSONPartial = 0,      /**< @brief JSON document is valid so far but incomplete. */
    JSONSuccess,          /**< @brief JSON document is valid and complete. */
    JSONIllegalDocument,  /**< @brief JSON document is invalid or malformed. */
    JSONMaxDepthExceeded, /**< @brief JSON document has nesting that exceeds JSON_MAX_DEPTH. */
    JSONNotFound,         /**< @brief Query key could not be found in the JSON document. */
    JSONNullParameter,    /**< @brief Pointer parameter passed to a function is NULL. */
    JSONBadParameter      /**< @brief Query key is empty, or any subpart is empty, or max is 0. */
} JSONStatus_t;

/**
 * @brief Parse a buffer to determine if it contains a valid JSON document.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in] max  The size of the buffer.
 *
 * @note The maximum nesting depth may be specified by defining the macro
 * JSON_MAX_DEPTH.  The default is 32 of sizeof(char).
 *
 * @note By default, a valid JSON document may contain a single element
 * (e.g., string, boolean, number).  To require that a valid document
 * contain an object or array, define JSON_VALIDATE_COLLECTIONS_ONLY.
 *
 * @return #JSONSuccess if the buffer contents are valid JSON;
 * #JSONNullParameter if buf is NULL;
 * #JSONBadParameter if max is 0;
 * #JSONIllegalDocument if the buffer contents are NOT valid JSON;
 * #JSONMaxDepthExceeded if object and array nesting exceeds a threshold;
 * #JSONPartial if the buffer contents are potentially valid but incomplete.
 *
 * <b>Example</b>
 * @code{c}
 *     // Variables used in this example.
 *     JSONStatus_t result;
 *     char buffer[] = "{\"foo\":\"abc\",\"bar\":{\"foo\":\"xyz\"}}";
 *     size_t bufferLength = sizeof( buffer ) - 1;
 *
 *     result = JSON_Validate( buffer, bufferLength );
 *
 *     // JSON document is valid.
 *     assert( result == JSONSuccess );
 * @endcode
 */
/* @[declare_json_validate] */
JSONStatus_t JSON_Validate( const char * buf,
                            size_t max );
/* @[declare_json_validate] */

/**
 * @brief Find a key or array index in a JSON document and output the
 * pointer @p outValue to its value.
 *
 * Any value may also be an object or an array to a maximum depth.  A search
 * may descend through nested objects or arrays when the query contains matching
 * key strings or array indexes joined by a separator.
 *
 * For example, if the provided buffer contains <code>{"foo":"abc","bar":{"foo":"xyz"}}</code>,
 * then a search for 'foo' would output <code>abc</code>, 'bar' would output
 * <code>{"foo":"xyz"}</code>, and a search for 'bar.foo' would output
 * <code>xyz</code>.
 *
 * If the provided buffer contains <code>[123,456,{"foo":"abc","bar":[88,99]}]</code>,
 * then a search for '[1]' would output <code>456</code>, '[2].foo' would output
 * <code>abc</code>, and '[2].bar[0]' would output <code>88</code>.
 *
 * On success, the pointer @p outValue points to a location in buf.  No null
 * termination is done for the value.  For valid JSON it is safe to place
 * a null character at the end of the value, so long as the character
 * replaced is put back before running another search.
 *
 * @param[in] buf  The buffer to search.
 * @param[in] max  size of the buffer.
 * @param[in] query  The object keys and array indexes to search for.
 * @param[in] queryLength  Length of the key.
 * @param[out] outValue  A pointer to receive the address of the value found.
 * @param[out] outValueLength  A pointer to receive the length of the value found.
 *
 * @note The maximum nesting depth may be specified by defining the macro
 * JSON_MAX_DEPTH.  The default is 32 of sizeof(char).
 *
 * @note JSON_Search() performs validation, but stops upon finding a matching
 * key and its value. To validate the entire JSON document, use JSON_Validate().
 *
 * @return #JSONSuccess if the query is matched and the value output;
 * #JSONNullParameter if any pointer parameters are NULL;
 * #JSONBadParameter if the query is empty, or the portion after a separator is empty,
 * or max is 0, or an index is too large to convert to a signed 32-bit integer;
 * #JSONNotFound if the query has no match.
 *
 * <b>Example</b>
 * @code{c}
 *     // Variables used in this example.
 *     JSONStatus_t result;
 *     char buffer[] = "{\"foo\":\"abc\",\"bar\":{\"foo\":\"xyz\"}}";
 *     size_t bufferLength = sizeof( buffer ) - 1;
 *     char query[] = "bar.foo";
 *     size_t queryLength = sizeof( query ) - 1;
 *     char * value;
 *     size_t valueLength;
 *
 *     // Calling JSON_Validate() is not necessary if the document is guaranteed to be valid.
 *     result = JSON_Validate( buffer, bufferLength );
 *
 *     if( result == JSONSuccess )
 *     {
 *         result = JSON_Search( buffer, bufferLength, query, queryLength,
 *                               &value, &valueLength );
 *     }
 *
 *     if( result == JSONSuccess )
 *     {
 *         // The pointer "value" will point to a location in the "buffer".
 *         char save = value[ valueLength ];
 *         // After saving the character, set it to a null byte for printing.
 *         value[ valueLength ] = '\0';
 *         // "Found: bar.foo -> xyz" will be printed.
 *         printf( "Found: %s -> %s\n", query, value );
 *         // Restore the original character.
 *         value[ valueLength ] = save;
 *     }
 * @endcode
 *
 * @note The maximum index value is ~2 billion ( 2^31 - 9 ).
 */
/* @[declare_json_search] */
#define JSON_Search( buf, max, query, queryLength, outValue, outValueLength ) \
    JSON_SearchT( buf, max, query, queryLength, outValue, outValueLength, NULL )
/* @[declare_json_search] */

/**
 * @brief The largest value usable as an array index in a query
 * for JSON_Search(), ~2 billion.
 */
#define MAX_INDEX_VALUE    ( 0x7FFFFFF7 )   /* 2^31 - 9 */

/**
 * @ingroup json_enum_types
 * @brief Value types from the JSON standard.
 */
typedef enum
{
    JSONInvalid = 0, /**< @brief Not a valid JSON type. */
    JSONString,      /**< @brief A quote delimited sequence of Unicode characters. */
    JSONNumber,      /**< @brief A rational number. */
    JSONTrue,        /**< @brief The literal value true. */
    JSONFalse,       /**< @brief The literal value false. */
    JSONNull,        /**< @brief The literal value null. */
    JSONObject,      /**< @brief A collection of zero or more key-value pairs. */
    JSONArray        /**< @brief A collection of zero or more values. */
} JSONTypes_t;

/**
 * @brief Same as JSON_Search(), but also outputs a type for the value found
 *
 * See @ref JSON_Search for documentation of common behavior.
 *
 * @param[in] buf  The buffer to search.
 * @param[in] max  size of the buffer.
 * @param[in] query  The object keys and array indexes to search for.
 * @param[in] queryLength  Length of the key.
 * @param[out] outValue  A pointer to receive the address of the value found.
 * @param[out] outValueLength  A pointer to receive the length of the value found.
 * @param[out] outType  An enum indicating the JSON-specific type of the value.
 */
/* @[declare_json_searcht] */
JSONStatus_t JSON_SearchT( char * buf,
                           size_t max,
                           const char * query,
                           size_t queryLength,
                           char ** outValue,
                           size_t * outValueLength,
                           JSONTypes_t * outType );
/* @[declare_json_searcht] */

/**
 * @brief Same as JSON_SearchT(), but with const qualified buf and outValue arguments.
 *
 * See @ref JSON_Search for documentation of common behavior.
 *
 * @param[in] buf  The buffer to search.
 * @param[in] max  size of the buffer.
 * @param[in] query  The object keys and array indexes to search for.
 * @param[in] queryLength  Length of the key.
 * @param[out] outValue  A pointer to receive the address of the value found.
 * @param[out] outValueLength  A pointer to receive the length of the value found.
 * @param[out] outType  An enum indicating the JSON-specific type of the value.
 */
/* @[declare_json_searchconst] */
JSONStatus_t JSON_SearchConst( const char * buf,
                               size_t max,
                               const char * query,
                               size_t queryLength,
                               const char ** outValue,
                               size_t * outValueLength,
                               JSONTypes_t * outType );
/* @[declare_json_searchconst] */

/**
 * @ingroup json_struct_types
 * @brief Structure to represent a key-value pair.
 */
typedef struct
{
    const char * key;     /**< @brief Pointer to the code point sequence for key. */
    size_t keyLength;     /**< @brief Length of the code point sequence for key. */
    const char * value;   /**< @brief Pointer to the code point sequence for value. */
    size_t valueLength;   /**< @brief Length of the code point sequence for value. */
    JSONTypes_t jsonType; /**< @brief JSON-specific type of the value. */
} JSONPair_t;

/**
 * @brief Output the next key-value pair or value from a collection.
 *
 * This function may be used in a loop to output each key-value pair from an object,
 * or each value from an array.  For the first invocation, the integers pointed to by
 * start and next should be initialized to 0.  These will be updated by the function.
 * If another key-value pair or value is present, the output structure is populated
 * and #JSONSuccess is returned; otherwise the structure is unchanged and #JSONNotFound
 * is returned.
 *
 * @param[in] buf  The buffer to search.
 * @param[in] max  size of the buffer.
 * @param[in,out] start  The index at which the collection begins.
 * @param[in,out] next  The index at which to seek the next value.
 * @param[out] outPair  A pointer to receive the next key-value pair.
 *
 * @note This function expects a valid JSON document; run JSON_Validate() first.
 *
 * @note For an object, the outPair structure will reference a key and its value.
 * For an array, only the value will be referenced (i.e., outPair.key will be NULL).
 *
 * @return #JSONSuccess if a value is output;
 * #JSONIllegalDocument if the buffer does not contain a collection;
 * #JSONNotFound if there are no further values in the collection.
 *
 * <b>Example</b>
 * @code{c}
 *     // Variables used in this example.
 *     static char * json_types[] =
 *     {
 *         "invalid",
 *         "string",
 *         "number",
 *         "true",
 *         "false",
 *         "null",
 *         "object",
 *         "array"
 *     };
 *
 *     void show( const char * json,
 *                size_t length )
 *     {
 *         size_t start = 0, next = 0;
 *         JSONPair_t pair = { 0 };
 *         JSONStatus_t result;
 *
 *         result = JSON_Validate( json, length );
 *         if( result == JSONSuccess )
 *         {
 *             result = JSON_Iterate( json, length, &start, &next, &pair );
 *         }
 *
 *         while( result == JSONSuccess )
 *         {
 *             if( pair.key != NULL )
 *             {
 *                 printf( "key: %.*s\t", ( int ) pair.keyLength, pair.key );
 *             }
 *
 *             printf( "value: (%s) %.*s\n", json_types[ pair.jsonType ],
 *                     ( int ) pair.valueLength, pair.value );
 *
 *             result = JSON_Iterate( json, length, &start, &next, &pair );
 *         }
 *     }
 * @endcode
 */
/* @[declare_json_iterate] */
JSONStatus_t JSON_Iterate( const char * buf,
                           size_t max,
                           size_t * start,
                           size_t * next,
                           JSONPair_t * outPair );
/* @[declare_json_iterate] */
#endif /* ifndef CORE_JSON_H_ */

core_json.c文件代码如下

/*
 * coreJSON v3.0.0
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * @file core_json.c
 * @brief The source file that implements the user-facing functions in core_json.h.
 */

#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "core_json.h"

/** @cond DO_NOT_DOCUMENT */

/* A compromise to satisfy both MISRA and CBMC */
typedef union
{
    char c;
    uint8_t u;
} char_;

#define isdigit_( x )    ( ( ( x ) >= '0' ) && ( ( x ) <= '9' ) )
#define iscntrl_( x )    ( ( ( x ) >= '\0' ) && ( ( x ) < ' ' ) )
/* NB. This is whitespace as defined by the JSON standard (ECMA-404). */
#define isspace_( x )                          \
    ( ( ( x ) == ' ' ) || ( ( x ) == '\t' ) || \
      ( ( x ) == '\n' ) || ( ( x ) == '\r' ) )

#define isOpenBracket_( x )           ( ( ( x ) == '{' ) || ( ( x ) == '[' ) )
#define isCloseBracket_( x )          ( ( ( x ) == '}' ) || ( ( x ) == ']' ) )
#define isCurlyPair_( x, y )          ( ( ( x ) == '{' ) && ( ( y ) == '}' ) )
#define isSquarePair_( x, y )         ( ( ( x ) == '[' ) && ( ( y ) == ']' ) )
#define isMatchingBracket_( x, y )    ( isCurlyPair_( x, y ) || isSquarePair_( x, y ) )
#define isSquareOpen_( x )            ( ( x ) == '[' )
#define isSquareClose_( x )           ( ( x ) == ']' )

/**
 * @brief Advance buffer index beyond whitespace.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 */
static void skipSpace( const char * buf,
                       size_t * start,
                       size_t max )
{
    size_t i;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );

    for( i = *start; i < max; i++ )
    {
        if( !isspace_( buf[ i ] ) )
        {
            break;
        }
    }

    *start = i;
}

/**
 * @brief Count the leading 1s in a byte.
 *
 * The high-order 1 bits of the first byte in a UTF-8 encoding
 * indicate the number of additional bytes to follow.
 *
 * @return the count
 */
static size_t countHighBits( uint8_t c )
{
    uint8_t n = c;
    size_t i = 0;

    while( ( n & 0x80U ) != 0U )
    {
        i++;
        n = ( n & 0x7FU ) << 1U;
    }

    return i;
}

/**
 * @brief Is the value a legal Unicode code point and encoded with
 * the fewest bytes?
 *
 * The last Unicode code point is 0x10FFFF.
 *
 * Unicode 3.1 disallows UTF-8 interpretation of non-shortest form sequences.
 * 1 byte encodes 0 through 7 bits
 * 2 bytes encode 8 through 5+6 = 11 bits
 * 3 bytes encode 12 through 4+6+6 = 16 bits
 * 4 bytes encode 17 through 3+6+6+6 = 21 bits
 *
 * Unicode 3.2 disallows UTF-8 code point values in the surrogate range,
 * [U+D800 to U+DFFF].
 *
 * @note Disallow ASCII, as this is called only for multibyte sequences.
 */
static bool shortestUTF8( size_t length,
                          uint32_t value )
{
    bool ret = false;
    uint32_t min, max;

    assert( ( length >= 2U ) && ( length <= 4U ) );

    switch( length )
    {
        case 2:
            min = ( uint32_t ) 1 << 7U;
            max = ( ( uint32_t ) 1 << 11U ) - 1U;
            break;

        case 3:
            min = ( uint32_t ) 1 << 11U;
            max = ( ( uint32_t ) 1 << 16U ) - 1U;
            break;

        default:
            min = ( uint32_t ) 1 << 16U;
            max = 0x10FFFFU;
            break;
    }

    if( ( value >= min ) && ( value <= max ) &&
        ( ( value < 0xD800U ) || ( value > 0xDFFFU ) ) )
    {
        ret = true;
    }

    return ret;
}

/**
 * @brief Advance buffer index beyond a UTF-8 code point.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 *
 * @return true if a valid code point was present;
 * false otherwise.
 *
 * 00–7F    Single-byte character
 * 80–BF    Trailing byte
 * C0–DF    Leading byte of two-byte character
 * E0–EF    Leading byte of three-byte character
 * F0–F7    Leading byte of four-byte character
 * F8–FB    Illegal (formerly leading byte of five-byte character)
 * FC–FD    Illegal (formerly leading byte of six-byte character)
 * FE–FF    Illegal
 *
 * The octet values C0, C1, and F5 to FF are illegal, since C0 and C1
 * would introduce a non-shortest sequence, and F5 or above would
 * introduce a value greater than the last code point, 0x10FFFF.
 */
static bool skipUTF8MultiByte( const char * buf,
                               size_t * start,
                               size_t max )
{
    bool ret = false;
    size_t i, bitCount, j;
    uint32_t value = 0;
    char_ c;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );

    i = *start;
    assert( i < max );
    assert( buf[ i ] < '\0' );

    c.c = buf[ i ];

    if( ( c.u > 0xC1U ) && ( c.u < 0xF5U ) )
    {
        bitCount = countHighBits( c.u );
        value = ( ( uint32_t ) c.u ) & ( ( ( uint32_t ) 1 << ( 7U - bitCount ) ) - 1U );

        /* The bit count is 1 greater than the number of bytes,
         * e.g., when j is 2, we skip one more byte. */
        for( j = bitCount - 1U; j > 0U; j-- )
        {
            i++;

            if( i >= max )
            {
                break;
            }

            c.c = buf[ i ];

            /* Additional bytes must match 10xxxxxx. */
            if( ( c.u & 0xC0U ) != 0x80U )
            {
                break;
            }

            value = ( value << 6U ) | ( c.u & 0x3FU );
        }

        if( ( j == 0U ) && ( shortestUTF8( bitCount, value ) == true ) )
        {
            *start = i + 1U;
            ret = true;
        }
    }

    return ret;
}

/**
 * @brief Advance buffer index beyond an ASCII or UTF-8 code point.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 *
 * @return true if a valid code point was present;
 * false otherwise.
 */
static bool skipUTF8( const char * buf,
                      size_t * start,
                      size_t max )
{
    bool ret = false;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );

    if( *start < max )
    {
        /* an ASCII byte */
        if( buf[ *start ] >= '\0' )
        {
            *start += 1U;
            ret = true;
        }
        else
        {
            ret = skipUTF8MultiByte( buf, start, max );
        }
    }

    return ret;
}

/**
 * @brief Convert a hexadecimal character to an integer.
 *
 * @param[in] c  The character to convert.
 *
 * @return the integer value upon success or NOT_A_HEX_CHAR on failure.
 */
#define NOT_A_HEX_CHAR    ( 0x10U )
static uint8_t hexToInt( char c )
{
    char_ n;

    n.c = c;

    if( ( c >= 'a' ) && ( c <= 'f' ) )
    {
        n.c -= 'a';
        n.u += 10U;
    }
    else if( ( c >= 'A' ) && ( c <= 'F' ) )
    {
        n.c -= 'A';
        n.u += 10U;
    }
    else if( isdigit_( c ) )
    {
        n.c -= '0';
    }
    else
    {
        n.u = NOT_A_HEX_CHAR;
    }

    return n.u;
}

/**
 * @brief Advance buffer index beyond a single \u Unicode
 * escape sequence and output the value.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 * @param[out] outValue  The value of the hex digits.
 *
 * @return true if a valid escape sequence was present;
 * false otherwise.
 *
 * @note For the sake of security, \u0000 is disallowed.
 */
static bool skipOneHexEscape( const char * buf,
                              size_t * start,
                              size_t max,
                              uint16_t * outValue )
{
    bool ret = false;
    size_t i, end;
    uint16_t value = 0;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
    assert( outValue != NULL );

    i = *start;
#define HEX_ESCAPE_LENGTH    ( 6U )   /* e.g., \u1234 */
    end = i + HEX_ESCAPE_LENGTH;

    if( ( end < max ) && ( buf[ i ] == '\\' ) && ( buf[ i + 1U ] == 'u' ) )
    {
        for( i += 2U; i < end; i++ )
        {
            uint8_t n = hexToInt( buf[ i ] );

            if( n == NOT_A_HEX_CHAR )
            {
                break;
            }

            value = ( value << 4U ) | n;
        }
    }

    if( ( i == end ) && ( value > 0U ) )
    {
        ret = true;
        *outValue = value;
        *start = i;
    }

    return ret;
}

/**
 * @brief Advance buffer index beyond one or a pair of \u Unicode escape sequences.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 *
 * Surrogate pairs are two escape sequences that together denote
 * a code point outside the Basic Multilingual Plane.  They must
 * occur as a pair with the first "high" value in [U+D800, U+DBFF],
 * and the second "low" value in [U+DC00, U+DFFF].
 *
 * @return true if a valid escape sequence was present;
 * false otherwise.
 *
 * @note For the sake of security, \u0000 is disallowed.
 */
#define isHighSurrogate( x )    ( ( ( x ) >= 0xD800U ) && ( ( x ) <= 0xDBFFU ) )
#define isLowSurrogate( x )     ( ( ( x ) >= 0xDC00U ) && ( ( x ) <= 0xDFFFU ) )

static bool skipHexEscape( const char * buf,
                           size_t * start,
                           size_t max )
{
    bool ret = false;
    size_t i;
    uint16_t value;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );

    i = *start;

    if( skipOneHexEscape( buf, &i, max, &value ) == true )
    {
        if( isHighSurrogate( value ) )
        {
            if( ( skipOneHexEscape( buf, &i, max, &value ) == true ) &&
                ( isLowSurrogate( value ) ) )
            {
                ret = true;
            }
        }
        else if( isLowSurrogate( value ) )
        {
            /* premature low surrogate */
        }
        else
        {
            ret = true;
        }
    }

    if( ret == true )
    {
        *start = i;
    }

    return ret;
}

/**
 * @brief Advance buffer index beyond an escape sequence.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 *
 * @return true if a valid escape sequence was present;
 * false otherwise.
 *
 * @note For the sake of security, \NUL is disallowed.
 */
static bool skipEscape( const char * buf,
                        size_t * start,
                        size_t max )
{
    bool ret = false;
    size_t i;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );

    i = *start;

    if( ( i < ( max - 1U ) ) && ( buf[ i ] == '\\' ) )
    {
        char c = buf[ i + 1U ];

        switch( c )
        {
            case '\0':
                break;

            case 'u':
                ret = skipHexEscape( buf, &i, max );
                break;

            case '"':
            case '\\':
            case '/':
            case 'b':
            case 'f':
            case 'n':
            case 'r':
            case 't':
                i += 2U;
                ret = true;
                break;

            default:

                /* a control character: (NUL,SPACE) */
                if( iscntrl_( c ) )
                {
                    i += 2U;
                    ret = true;
                }

                break;
        }
    }

    if( ret == true )
    {
        *start = i;
    }

    return ret;
}

/**
 * @brief Advance buffer index beyond a double-quoted string.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 *
 * @return true if a valid string was present;
 * false otherwise.
 */
static bool skipString( const char * buf,
                        size_t * start,
                        size_t max )
{
    bool ret = false;
    size_t i;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );

    i = *start;

    if( ( i < max ) && ( buf[ i ] == '"' ) )
    {
        i++;

        while( i < max )
        {
            if( buf[ i ] == '"' )
            {
                ret = true;
                i++;
                break;
            }

            if( buf[ i ] == '\\' )
            {
                if( skipEscape( buf, &i, max ) != true )
                {
                    break;
                }
            }
            /* An unescaped control character is not allowed. */
            else if( iscntrl_( buf[ i ] ) )
            {
                break;
            }
            else if( skipUTF8( buf, &i, max ) != true )
            {
                break;
            }
            else
            {
                /* MISRA 15.7 */
            }
        }
    }

    if( ret == true )
    {
        *start = i;
    }

    return ret;
}

/**
 * @brief Compare the leading n bytes of two character sequences.
 *
 * @param[in] a  first character sequence
 * @param[in] b  second character sequence
 * @param[in] n  number of bytes
 *
 * @return true if the sequences are the same;
 * false otherwise
 */
static bool strnEq( const char * a,
                    const char * b,
                    size_t n )
{
    size_t i;

    assert( ( a != NULL ) && ( b != NULL ) );

    for( i = 0; i < n; i++ )
    {
        if( a[ i ] != b[ i ] )
        {
            break;
        }
    }

    return ( i == n ) ? true : false;
}

/**
 * @brief Advance buffer index beyond a literal.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 * @param[in] literal  The type of literal.
 * @param[in] length  The length of the literal.
 *
 * @return true if the literal was present;
 * false otherwise.
 */
static bool skipLiteral( const char * buf,
                         size_t * start,
                         size_t max,
                         const char * literal,
                         size_t length )
{
    bool ret = false;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
    assert( literal != NULL );

    if( ( *start < max ) && ( length <= ( max - *start ) ) )
    {
        ret = strnEq( &buf[ *start ], literal, length );
    }

    if( ret == true )
    {
        *start += length;
    }

    return ret;
}

/**
 * @brief Advance buffer index beyond a JSON literal.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 *
 * @return true if a valid literal was present;
 * false otherwise.
 */
static bool skipAnyLiteral( const char * buf,
                            size_t * start,
                            size_t max )
{
    bool ret = false;

#define skipLit_( x ) \
    ( skipLiteral( buf, start, max, ( x ), ( sizeof( x ) - 1UL ) ) == true )

    if( skipLit_( "true" ) || skipLit_( "false" ) || skipLit_( "null" ) )
    {
        ret = true;
    }

    return ret;
}

/**
 * @brief Advance buffer index beyond one or more digits.
 * Optionally, output the integer value of the digits.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 * @param[out] outValue  The integer value of the digits.
 *
 * @note outValue may be NULL.  If not NULL, and the output
 * exceeds ~2 billion, then -1 is output.
 *
 * @return true if a digit was present;
 * false otherwise.
 */
#define MAX_FACTOR    ( MAX_INDEX_VALUE / 10 )
static bool skipDigits( const char * buf,
                        size_t * start,
                        size_t max,
                        int32_t * outValue )
{
    bool ret = false;
    size_t i, saveStart;
    int32_t value = 0;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );

    saveStart = *start;

    for( i = *start; i < max; i++ )
    {
        if( !isdigit_( buf[ i ] ) )
        {
            break;
        }

        if( ( outValue != NULL ) && ( value > -1 ) )
        {
            int8_t n = ( int8_t ) hexToInt( buf[ i ] );

            if( value <= MAX_FACTOR )
            {
                value = ( value * 10 ) + n;
            }
            else
            {
                value = -1;
            }
        }
    }

    if( i > saveStart )
    {
        ret = true;
        *start = i;

        if( outValue != NULL )
        {
            *outValue = value;
        }
    }

    return ret;
}

/**
 * @brief Advance buffer index beyond the decimal portion of a number.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 */
static void skipDecimals( const char * buf,
                          size_t * start,
                          size_t max )
{
    size_t i;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );

    i = *start;

    if( ( i < max ) && ( buf[ i ] == '.' ) )
    {
        i++;

        if( skipDigits( buf, &i, max, NULL ) == true )
        {
            *start = i;
        }
    }
}

/**
 * @brief Advance buffer index beyond the exponent portion of a number.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 */
static void skipExponent( const char * buf,
                          size_t * start,
                          size_t max )
{
    size_t i;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );

    i = *start;

    if( ( i < max ) && ( ( buf[ i ] == 'e' ) || ( buf[ i ] == 'E' ) ) )
    {
        i++;

        if( ( i < max ) && ( ( buf[ i ] == '-' ) || ( buf[ i ] == '+' ) ) )
        {
            i++;
        }

        if( skipDigits( buf, &i, max, NULL ) == true )
        {
            *start = i;
        }
    }
}

/**
 * @brief Advance buffer index beyond a number.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 *
 * @return true if a valid number was present;
 * false otherwise.
 */
static bool skipNumber( const char * buf,
                        size_t * start,
                        size_t max )
{
    bool ret = false;
    size_t i;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );

    i = *start;

    if( ( i < max ) && ( buf[ i ] == '-' ) )
    {
        i++;
    }

    if( i < max )
    {
        /* JSON disallows superfluous leading zeroes, so an
         * initial zero must either be alone, or followed by
         * a decimal or exponent.
         *
         * Should there be a digit after the zero, that digit
         * will not be skipped by this function, and later parsing
         * will judge this an illegal document. */
        if( buf[ i ] == '0' )
        {
            ret = true;
            i++;
        }
        else
        {
            ret = skipDigits( buf, &i, max, NULL );
        }
    }

    if( ret == true )
    {
        skipDecimals( buf, &i, max );
        skipExponent( buf, &i, max );
        *start = i;
    }

    return ret;
}

/**
 * @brief Advance buffer index beyond a scalar value.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 *
 * @return true if a scalar value was present;
 * false otherwise.
 */
static bool skipAnyScalar( const char * buf,
                           size_t * start,
                           size_t max )
{
    bool ret = false;

    if( ( skipString( buf, start, max ) == true ) ||
        ( skipAnyLiteral( buf, start, max ) == true ) ||
        ( skipNumber( buf, start, max ) == true ) )
    {
        ret = true;
    }

    return ret;
}

/**
 * @brief Advance buffer index beyond a comma separator
 * and surrounding whitespace.
 *
 * JSON uses a comma to separate values in an array and key-value
 * pairs in an object.  JSON does not permit a trailing comma.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 *
 * @return true if a non-terminal comma was present;
 * false otherwise.
 */
static bool skipSpaceAndComma( const char * buf,
                               size_t * start,
                               size_t max )
{
    bool ret = false;
    size_t i;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );

    skipSpace( buf, start, max );
    i = *start;

    if( ( i < max ) && ( buf[ i ] == ',' ) )
    {
        i++;
        skipSpace( buf, &i, max );

        if( ( i < max ) && !isCloseBracket_( buf[ i ] ) )
        {
            ret = true;
            *start = i;
        }
    }

    return ret;
}

/**
 * @brief Advance buffer index beyond the scalar values of an array.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 *
 * @note Stops advance if a value is an object or array.
 */
static void skipArrayScalars( const char * buf,
                              size_t * start,
                              size_t max )
{
    size_t i;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );

    i = *start;

    while( i < max )
    {
        if( skipAnyScalar( buf, &i, max ) != true )
        {
            break;
        }

        if( skipSpaceAndComma( buf, &i, max ) != true )
        {
            break;
        }
    }

    *start = i;
}

/**
 * @brief Advance buffer index beyond the scalar key-value pairs
 * of an object.
 *
 * In JSON, objects consist of comma-separated key-value pairs.
 * A key is always a string (a scalar) while a value may be a
 * scalar, an object, or an array.  A colon must appear between
 * each key and value.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 *
 * @note Stops advance if a value is an object or array.
 */
static void skipObjectScalars( const char * buf,
                               size_t * start,
                               size_t max )
{
    size_t i;
    bool comma;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );

    i = *start;

    while( i < max )
    {
        if( skipString( buf, &i, max ) != true )
        {
            break;
        }

        skipSpace( buf, &i, max );

        if( ( i < max ) && ( buf[ i ] != ':' ) )
        {
            break;
        }

        i++;
        skipSpace( buf, &i, max );

        if( ( i < max ) && isOpenBracket_( buf[ i ] ) )
        {
            *start = i;
            break;
        }

        if( skipAnyScalar( buf, &i, max ) != true )
        {
            break;
        }

        comma = skipSpaceAndComma( buf, &i, max );
        *start = i;

        if( comma != true )
        {
            break;
        }
    }
}

/**
 * @brief Advance buffer index beyond one or more scalars.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 * @param[in] mode  The first character of an array '[' or object '{'.
 */
static void skipScalars( const char * buf,
                         size_t * start,
                         size_t max,
                         char mode )
{
    assert( isOpenBracket_( mode ) );

    skipSpace( buf, start, max );

    if( mode == '[' )
    {
        skipArrayScalars( buf, start, max );
    }
    else
    {
        skipObjectScalars( buf, start, max );
    }
}

/**
 * @brief Advance buffer index beyond a collection and handle nesting.
 *
 * A stack is used to continue parsing the prior collection type
 * when a nested collection is finished.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 *
 * @return #JSONSuccess if the buffer contents are a valid JSON collection;
 * #JSONIllegalDocument if the buffer contents are NOT valid JSON;
 * #JSONMaxDepthExceeded if object and array nesting exceeds a threshold;
 * #JSONPartial if the buffer contents are potentially valid but incomplete.
 */
#ifndef JSON_MAX_DEPTH
    #define JSON_MAX_DEPTH    32
#endif
static JSONStatus_t skipCollection( const char * buf,
                                    size_t * start,
                                    size_t max )
{
    JSONStatus_t ret = JSONPartial;
    char c, stack[ JSON_MAX_DEPTH ];
    int16_t depth = -1;
    size_t i;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );

    i = *start;

    while( i < max )
    {
        c = buf[ i ];
        i++;

        switch( c )
        {
            case '{':
            case '[':
                depth++;

                if( depth == JSON_MAX_DEPTH )
                {
                    ret = JSONMaxDepthExceeded;
                    break;
                }

                stack[ depth ] = c;
                skipScalars( buf, &i, max, stack[ depth ] );
                break;

            case '}':
            case ']':

                if( ( depth > 0 ) && isMatchingBracket_( stack[ depth ], c ) )
                {
                    depth--;

                    if( skipSpaceAndComma( buf, &i, max ) == true )
                    {
                        skipScalars( buf, &i, max, stack[ depth ] );
                    }

                    break;
                }

                ret = ( depth == 0 ) ? JSONSuccess : JSONIllegalDocument;
                break;

            default:
                ret = JSONIllegalDocument;
                break;
        }

        if( ret != JSONPartial )
        {
            break;
        }
    }

    if( ret == JSONSuccess )
    {
        *start = i;
    }

    return ret;
}

/** @endcond */

/**
 * See core_json.h for docs.
 *
 * Verify that the entire buffer contains exactly one scalar
 * or collection within optional whitespace.
 */
JSONStatus_t JSON_Validate( const char * buf,
                            size_t max )
{
    JSONStatus_t ret;
    size_t i = 0;

    if( buf == NULL )
    {
        ret = JSONNullParameter;
    }
    else if( max == 0U )
    {
        ret = JSONBadParameter;
    }
    else
    {
        skipSpace( buf, &i, max );

        /** @cond DO_NOT_DOCUMENT */
        #ifndef JSON_VALIDATE_COLLECTIONS_ONLY
            if( skipAnyScalar( buf, &i, max ) == true )
            {
                ret = JSONSuccess;
            }
            else
        #endif
        /** @endcond */
        {
            ret = skipCollection( buf, &i, max );
        }
    }

    if( ( ret == JSONSuccess ) && ( i < max ) )
    {
        skipSpace( buf, &i, max );

        if( i != max )
        {
            ret = JSONIllegalDocument;
        }
    }

    return ret;
}

/** @cond DO_NOT_DOCUMENT */

/**
 * @brief Output index and length for the next value.
 *
 * Also advances the buffer index beyond the value.
 * The value may be a scalar or a collection.
 * The start index should point to the beginning of the value.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 * @param[out] value  A pointer to receive the index of the value.
 * @param[out] valueLength  A pointer to receive the length of the value.
 *
 * @return true if a value was present;
 * false otherwise.
 */
static bool nextValue( const char * buf,
                       size_t * start,
                       size_t max,
                       size_t * value,
                       size_t * valueLength )
{
    bool ret = true;
    size_t i, valueStart;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
    assert( ( value != NULL ) && ( valueLength != NULL ) );

    i = *start;
    valueStart = i;

    if( ( skipAnyScalar( buf, &i, max ) == true ) ||
        ( skipCollection( buf, &i, max ) == JSONSuccess ) )
    {
        *value = valueStart;
        *valueLength = i - valueStart;
    }
    else
    {
        ret = false;
    }

    if( ret == true )
    {
        *start = i;
    }

    return ret;
}

/**
 * @brief Output indexes for the next key-value pair of an object.
 *
 * Also advances the buffer index beyond the key-value pair.
 * The value may be a scalar or a collection.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 * @param[out] key  A pointer to receive the index of the key.
 * @param[out] keyLength  A pointer to receive the length of the key.
 * @param[out] value  A pointer to receive the index of the value.
 * @param[out] valueLength  A pointer to receive the length of the value.
 *
 * @return true if a key-value pair was present;
 * false otherwise.
 */
static bool nextKeyValuePair( const char * buf,
                              size_t * start,
                              size_t max,
                              size_t * key,
                              size_t * keyLength,
                              size_t * value,
                              size_t * valueLength )
{
    bool ret = true;
    size_t i, keyStart;

    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
    assert( ( key != NULL ) && ( keyLength != NULL ) );
    assert( ( value != NULL ) && ( valueLength != NULL ) );

    i = *start;
    keyStart = i;

    if( skipString( buf, &i, max ) == true )
    {
        *key = keyStart + 1U;
        *keyLength = i - keyStart - 2U;
    }
    else
    {
        ret = false;
    }

    if( ret == true )
    {
        skipSpace( buf, &i, max );

        if( ( i < max ) && ( buf[ i ] == ':' ) )
        {
            i++;
            skipSpace( buf, &i, max );
        }
        else
        {
            ret = false;
        }
    }

    if( ret == true )
    {
        ret = nextValue( buf, &i, max, value, valueLength );
    }

    if( ret == true )
    {
        *start = i;
    }

    return ret;
}

/**
 * @brief Find a key in a JSON object and output a pointer to its value.
 *
 * @param[in] buf  The buffer to search.
 * @param[in] max  size of the buffer.
 * @param[in] query  The object keys and array indexes to search for.
 * @param[in] queryLength  Length of the key.
 * @param[out] outValue  A pointer to receive the index of the value found.
 * @param[out] outValueLength  A pointer to receive the length of the value found.
 *
 * Iterate over the key-value pairs of an object, looking for a matching key.
 *
 * @return true if the query is matched and the value output;
 * false otherwise.
 *
 * @note Parsing stops upon finding a match.
 */
static bool objectSearch( const char * buf,
                          size_t max,
                          const char * query,
                          size_t queryLength,
                          size_t * outValue,
                          size_t * outValueLength )
{
    bool ret = false;

    size_t i = 0, key, keyLength, value = 0, valueLength = 0;

    assert( ( buf != NULL ) && ( query != NULL ) );
    assert( ( outValue != NULL ) && ( outValueLength != NULL ) );

    skipSpace( buf, &i, max );

    if( ( i < max ) && ( buf[ i ] == '{' ) )
    {
        i++;
        skipSpace( buf, &i, max );

        while( i < max )
        {
            if( nextKeyValuePair( buf, &i, max, &key, &keyLength,
                                  &value, &valueLength ) != true )
            {
                break;
            }

            if( ( queryLength == keyLength ) &&
                ( strnEq( query, &buf[ key ], keyLength ) == true ) )
            {
                ret = true;
                break;
            }

            if( skipSpaceAndComma( buf, &i, max ) != true )
            {
                break;
            }
        }
    }

    if( ret == true )
    {
        *outValue = value;
        *outValueLength = valueLength;
    }

    return ret;
}

/**
 * @brief Find an index in a JSON array and output a pointer to its value.
 *
 * @param[in] buf  The buffer to search.
 * @param[in] max  size of the buffer.
 * @param[in] queryIndex  The index to search for.
 * @param[out] outValue  A pointer to receive the index of the value found.
 * @param[out] outValueLength  A pointer to receive the length of the value found.
 *
 * Iterate over the values of an array, looking for a matching index.
 *
 * @return true if the queryIndex is found and the value output;
 * false otherwise.
 *
 * @note Parsing stops upon finding a match.
 */
static bool arraySearch( const char * buf,
                         size_t max,
                         uint32_t queryIndex,
                         size_t * outValue,
                         size_t * outValueLength )
{
    bool ret = false;
    size_t i = 0, value = 0, valueLength = 0;
    uint32_t currentIndex = 0;

    assert( buf != NULL );
    assert( ( outValue != NULL ) && ( outValueLength != NULL ) );

    skipSpace( buf, &i, max );

    if( ( i < max ) && ( buf[ i ] == '[' ) )
    {
        i++;
        skipSpace( buf, &i, max );

        while( i < max )
        {
            if( nextValue( buf, &i, max, &value, &valueLength ) != true )
            {
                break;
            }

            if( currentIndex == queryIndex )
            {
                ret = true;
                break;
            }

            if( skipSpaceAndComma( buf, &i, max ) != true )
            {
                break;
            }

            currentIndex++;
        }
    }

    if( ret == true )
    {
        *outValue = value;
        *outValueLength = valueLength;
    }

    return ret;
}

/**
 * @brief Advance buffer index beyond a query part.
 *
 * The part is the portion of the query which is not
 * a separator or array index.
 *
 * @param[in] buf  The buffer to parse.
 * @param[in,out] start  The index at which to begin.
 * @param[in] max  The size of the buffer.
 * @param[out] outLength  The length of the query part.
 *
 * @return true if a valid string was present;
 * false otherwise.
 */
#define JSON_QUERY_KEY_SEPARATOR    '.'
#define isSeparator_( x )    ( ( x ) == JSON_QUERY_KEY_SEPARATOR )
static bool skipQueryPart( const char * buf,
                           size_t * start,
                           size_t max,
                           size_t * outLength )
{
    bool ret = false;
    size_t i;

    assert( ( buf != NULL ) && ( start != NULL ) && ( outLength != NULL ) );
    assert( max > 0U );

    i = *start;

    while( ( i < max ) &&
           !isSeparator_( buf[ i ] ) &&
           !isSquareOpen_( buf[ i ] ) )
    {
        i++;
    }

    if( i > *start )
    {
        ret = true;
        *outLength = i - *start;
        *start = i;
    }

    return ret;
}

/**
 * @brief Handle a nested search by iterating over the parts of the query.
 *
 * @param[in] buf  The buffer to search.
 * @param[in] max  size of the buffer.
 * @param[in] query  The object keys and array indexes to search for.
 * @param[in] queryLength  Length of the key.
 * @param[out] outValue  A pointer to receive the index of the value found.
 * @param[out] outValueLength  A pointer to receive the length of the value found.
 *
 * @return #JSONSuccess if the query is matched and the value output;
 * #JSONBadParameter if the query is empty, or any part is empty,
 * or an index is too large to convert;
 * #JSONNotFound if the query is NOT found.
 *
 * @note Parsing stops upon finding a match.
 */
static JSONStatus_t multiSearch( const char * buf,
                                 size_t max,
                                 const char * query,
                                 size_t queryLength,
                                 size_t * outValue,
                                 size_t * outValueLength )
{
    JSONStatus_t ret = JSONSuccess;
    size_t i = 0, start = 0, queryStart = 0, value = 0, length = max;

    assert( ( buf != NULL ) && ( query != NULL ) );
    assert( ( outValue != NULL ) && ( outValueLength != NULL ) );
    assert( ( max > 0U ) && ( queryLength > 0U ) );

    while( i < queryLength )
    {
        bool found = false;

        if( isSquareOpen_( query[ i ] ) )
        {
            int32_t queryIndex = -1;
            i++;

            ( void ) skipDigits( query, &i, queryLength, &queryIndex );

            if( ( queryIndex < 0 ) ||
                ( i >= queryLength ) || !isSquareClose_( query[ i ] ) )
            {
                ret = JSONBadParameter;
                break;
            }

            i++;

            found = arraySearch( &buf[ start ], length, ( uint32_t ) queryIndex, &value, &length );
        }
        else
        {
            size_t keyLength = 0;

            queryStart = i;

            if( ( skipQueryPart( query, &i, queryLength, &keyLength ) != true ) ||
                /* catch an empty key part or a trailing separator */
                ( i == ( queryLength - 1U ) ) )
            {
                ret = JSONBadParameter;
                break;
            }

            found = objectSearch( &buf[ start ], length, &query[ queryStart ], keyLength, &value, &length );
        }

        if( found == false )
        {
            ret = JSONNotFound;
            break;
        }

        start += value;

        if( ( i < queryLength ) && isSeparator_( query[ i ] ) )
        {
            i++;
        }
    }

    if( ret == JSONSuccess )
    {
        *outValue = start;
        *outValueLength = length;
    }

    return ret;
}

/**
 * @brief Return a JSON type based on a separator character or
 * the first character of a value.
 *
 * @param[in] c  The character to classify.
 *
 * @return an enum of JSONTypes_t
 */
static JSONTypes_t getType( char c )
{
    JSONTypes_t t;

    switch( c )
    {
        case '"':
            t = JSONString;
            break;

        case '{':
            t = JSONObject;
            break;

        case '[':
            t = JSONArray;
            break;

        case 't':
            t = JSONTrue;
            break;

        case 'f':
            t = JSONFalse;
            break;

        case 'n':
            t = JSONNull;
            break;

        default:
            t = JSONNumber;
            break;
    }

    return t;
}

/** @endcond */

/**
 * See core_json.h for docs.
 */
JSONStatus_t JSON_SearchConst( const char * buf,
                               size_t max,
                               const char * query,
                               size_t queryLength,
                               const char ** outValue,
                               size_t * outValueLength,
                               JSONTypes_t * outType )
{
    JSONStatus_t ret;
    size_t value;

    if( ( buf == NULL ) || ( query == NULL ) ||
        ( outValue == NULL ) || ( outValueLength == NULL ) )
    {
        ret = JSONNullParameter;
    }
    else if( ( max == 0U ) || ( queryLength == 0U ) )
    {
        ret = JSONBadParameter;
    }
    else
    {
        ret = multiSearch( buf, max, query, queryLength, &value, outValueLength );
    }

    if( ret == JSONSuccess )
    {
        JSONTypes_t t = getType( buf[ value ] );

        if( t == JSONString )
        {
            /* strip the surrounding quotes */
            value++;
            *outValueLength -= 2U;
        }

        *outValue = &buf[ value ];

        if( outType != NULL )
        {
            *outType = t;
        }
    }

    return ret;
}

/**
 * See core_json.h for docs.
 */
JSONStatus_t JSON_SearchT( char * buf,
                           size_t max,
                           const char * query,
                           size_t queryLength,
                           char ** outValue,
                           size_t * outValueLength,
                           JSONTypes_t * outType )
{
    /* MISRA Rule 11.3 prohibits casting a pointer to a different type.
     * This instance is a false positive, as the rule permits the
     * addition of a type qualifier. */
    /* coverity[misra_c_2012_rule_11_3_violation] */
    return JSON_SearchConst( ( const char * ) buf, max, query, queryLength,
                             ( const char ** ) outValue, outValueLength, outType );
}

/** @cond DO_NOT_DOCUMENT */

/**
 * @brief Output the next key-value pair or value from a collection.
 *
 * @param[in] buf  The buffer to search.
 * @param[in] max  size of the buffer.
 * @param[in] start  The index at which the collection begins.
 * @param[in,out] next  The index at which to seek the next value.
 * @param[out] outKey  A pointer to receive the index of the value found.
 * @param[out] outKeyLength  A pointer to receive the length of the value found.
 * @param[out] outValue  A pointer to receive the index of the value found.
 * @param[out] outValueLength  A pointer to receive the length of the value found.
 *
 * @return #JSONSuccess if a value is output;
 * #JSONIllegalDocument if the buffer does not begin with '[' or '{';
 * #JSONNotFound if there are no further values in the collection.
 */
static JSONStatus_t iterate( const char * buf,
                             size_t max,
                             size_t * start,
                             size_t * next,
                             size_t * outKey,
                             size_t * outKeyLength,
                             size_t * outValue,
                             size_t * outValueLength )
{
    JSONStatus_t ret = JSONNotFound;
    bool found = false;

    assert( ( buf != NULL ) && ( max > 0U ) );
    assert( ( start != NULL ) && ( next != NULL ) );
    assert( ( outKey != NULL ) && ( outKeyLength != NULL ) );
    assert( ( outValue != NULL ) && ( outValueLength != NULL ) );

    if( *start < max )
    {
        switch( buf[ *start ] )
        {
            case '[':
                found = nextValue( buf, next, max, outValue, outValueLength );

                if( found == true )
                {
                    *outKey = 0;
                    *outKeyLength = 0;
                }

                break;

            case '{':
                found = nextKeyValuePair( buf, next, max, outKey, outKeyLength,
                                          outValue, outValueLength );
                break;

            default:
                ret = JSONIllegalDocument;
                break;
        }
    }

    if( found == true )
    {
        ret = JSONSuccess;
        ( void ) skipSpaceAndComma( buf, next, max );
    }

    return ret;
}

/** @endcond */

/**
 * See core_json.h for docs.
 */
JSONStatus_t JSON_Iterate( const char * buf,
                           size_t max,
                           size_t * start,
                           size_t * next,
                           JSONPair_t * outPair )
{
    JSONStatus_t ret;
    size_t key, keyLength, value, valueLength;

    if( ( buf == NULL ) || ( start == NULL ) || ( next == NULL ) ||
        ( outPair == NULL ) )
    {
        ret = JSONNullParameter;
    }
    else if( ( max == 0U ) || ( *start >= max ) || ( *next > max ) )
    {
        ret = JSONBadParameter;
    }
    else
    {
        skipSpace( buf, start, max );

        if( *next <= *start )
        {
            *next = *start + 1U;
            skipSpace( buf, next, max );
        }

        ret = iterate( buf, max, start, next, &key, &keyLength,
                       &value, &valueLength );
    }

    if( ret == JSONSuccess )
    {
        JSONTypes_t t = getType( buf[ value ] );

        if( t == JSONString )
        {
            /* strip the surrounding quotes */
            value++;
            valueLength -= 2U;
        }

        outPair->key = ( key == 0U ) ? NULL : &buf[ key ];
        outPair->keyLength = keyLength;
        outPair->value = &buf[ value ];
        outPair->valueLength = valueLength;
        outPair->jsonType = t;
    }

    return ret;
}

接下来在main.c文件变成上报温湿度的函数

int report_tempRH_json(void)
{
	char  buf[128];/*声明了一个128字节的缓冲区buf,用于存储JSON数据*/
	float  temperature,humidity;/*声明了两个变量temperature和humidity,用于存储温度和湿度的值*/

	if(SHT30_SampleData(&temperature,&humidity) < 0)
	{
		printf("ERROR:SHT30 Sample data failure\n");
		return -1;
	}

	memset(buf,0,sizeof(buf));/*使用memset函数将buf缓冲区清0*/
	/*使用snprintf函数将温度和湿度的数据格式化为JSON字符串,并存储在buf缓冲区中*/
	snprintf(buf,sizeof(buf),"{\"Temperature\":\"%.2f\",\"Humidity\":\"%.2f\"}",temperature,humidity);

	HAL_UART_Transmit(&huart1,(uint8_t *)buf,strlen(buf),0xFFFF);/*使用HAL_UART_Transmit函数将buf缓冲区中的数据发送给UART设备*/

	return 0;
}

声明函数,同时在要记得添加这两个头文件

#include <string.h>
#include <sht30.h>
extern int report_tempRH_json(void);

在while里调用函数

 while (1)
  {
    /* USER CODE END WHILE */
	  
    /* USER CODE BEGIN 3 */
	  report_tempRH_json();
	  HAL_Delay(1000);
  }
  /* USER CODE END 3 */

编译下载代码,就可以在串口调试助手上看到MCU每隔1s上报一次温湿度了

 

嗯,用户让我分析基于STM32F103C8T6的智能粉尘检测系统所涉及的嵌入式知识点,并拆解原理。首先,我需要理解整个系统的组成部分。系统主控是STM32,传感器包括温湿度和粉尘浓度,数据通过WiFi上传云端。然后要拆解每个部分的原理和涉及的嵌入式技术。 首先,主控单元STM32F103C8T6,属于Cortex-M3内核,需要了解其外设使用,比如ADC、GPIO、定时器等。可能涉及到时钟配置、中断处理、低功耗模式等知识点。还有固件开发流程,比如使用HAL库或标准外设库,以及开发环境如Keil或STM32CubeIDE。 接下来是传感器部分。温湿度传感器可能是DHT11或SHT系列,通信接口可能是单总线或I2C。需要理解传感器数据读取的协议,比如时序要求、数据格式解析。而粉尘传感器,比如GP2Y1010AU0F,可能需要模拟信号处理,使用ADC采集电压,再转换成浓度值。这里涉及到模拟电路设计,比如RC滤波,以及软件上的ADC配置和校准。 然后是WiFi模块,常见的有ESP8266或ESP32,通过AT指令或SPI/SDIO与STM32通信。需要理解串口通信协议,AT指令的发送和响应处理,数据封装成TCP/IP包,连接云端服务器的过程,比如MQTT或HTTP协议。可能还需要处理网络异常,重连机制,数据缓存等。 云端通信部分,数据上传可能使用MQTT、HTTP或者TCP自定义协议。需要了解如何与云平台(如阿里云、AWS IoT)对接,数据格式JSON),以及数据加密(TLS/SSL)。这部分可能涉及嵌入式系统中的安全机制,如加密算法实现,证书管理。 系统整体设计要考虑实时性,多任务处理。可能使用RTOS,如FreeRTOS,进行任务调度,管理传感器采集、数据处理、通信等任务。任务间通信机制,如队列、信号量,确保数据同步和资源共享。 电源管理也是重点,尤其是电池供电的情况。需要低功耗设计,比如STM32的睡眠模式,外设的电源控制,传感器间歇工作,数据上传的间隔优化,以延长续航。 数据处理方面,可能需要对传感器数据进行滤波(如均值、中值滤波),校准(线性或非线性补偿),阈值报警(比如浓度超标触发警报)。这些算法在嵌入式中的实现需要考虑计算效率和资源占用。 硬件设计部分,包括PCB布局,传感器接口的电气特性,抗干扰措施,比如去耦电容、屏蔽罩。可能还需要考虑工业环境中的EMC问题,确保稳定运行。 调试与测试涉及日志输出(通过UART或SWD),单元测试(传感器模块、通信模块),系统集成测试,以及现场调试工具的使用,如逻辑分析仪、示波器,确保各模块协同工作。 用户可能还希望了解如何保证系统的可靠性和实时性,如何处理传感器误差,以及如何优化网络通信的稳定性。需要覆盖嵌入式系统的各个层面,从硬件到软件,再到通信协议和云端交互。 最后,要确保回答结构清晰,分点讲解每个模块的原理和对应的嵌入式技术,帮助用户逐步理解整个系统的工作流程和关键技术点。 好的!我将从**嵌入式系统设计的角度**,拆解基于STM32F103C8T6的智能粉尘检测系统的核心原理和关键知识点,分模块进行说明: --- ### 一、系统整体架构 ``` [STM32主控] → [传感器数据采集] → [数据处理] → [WiFi传输] → [云端存储] ↑ ↑ ↑ 温湿度传感器 粉尘传感器 本地报警/显示 ``` **核心逻辑**:STM32通过外设接口获取传感器数据,处理后通过WiFi模块封装成网络数据包,上传至云端。 --- ### 二、关键嵌入式技术分解 #### 1. **STM32外设驱动** - **ADC模块**(模拟信号采集) - 原理:粉尘传感器(如GP2Y1010AU0F)输出模拟电压信号,STM32的12位ADC将其转换为数字量 - 代码关键点:ADC校准、采样率设置、DMA传输优化 ```c HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adc_value, 1); // 使用DMA提高采集效率 ``` - **GPIO与定时器** - 温湿度传感器(如DHT11)需严格时序控制 - 代码关键点:微秒级延时(TIM定时器实现)、中断响应 #### 2. **通信协议解析** - **UART通信** - WiFi模块(如ESP8266)通过AT指令控制 - 协议解析:状态机实现AT指令响应处理 ```c if(strstr(recv_buf, "OK")) { /* 指令成功响应处理 */ } ``` - **SPI/I2C接口** - 部分高精度传感器(如SHT30温湿度)使用I2C通信 - 代码关键点:I2C地址配置、CRC校验 #### 3. **实时操作系统(RTOS)** - **任务调度** - 使用FreeRTOS创建多任务: - 任务1:传感器数据采集(高优先级) - 任务2:网络通信(中优先级) - 任务3:本地显示/报警(低优先级) ```c xTaskCreate(sensor_task, "SENSOR", 128, NULL, 3, NULL); ``` - **同步机制** - 使用信号量(Semaphore)保护共享资源(如传感器数据缓冲区) #### 4. **低功耗设计** - **电源管理** - 进入Stop模式:在数据上传间隔期关闭非必要外设 ```c HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); ``` - **动态频率调节** - 根据任务需求调整系统时钟(HSI/HSE切换) #### 5. **数据处理算法** - **传感器校准** - 粉尘浓度计算公式(需实验标定): $$ C = k \cdot V_{ADC} + b $$ - 温湿度补偿:通过查表法修正粉尘浓度值 - **滑动平均滤波** ```c #define FILTER_LEN 5 uint16_t filter_buf[FILTER_LEN]; uint16_t adc_filter() { static uint8_t idx = 0; filter_buf[idx++] = ADC_Value; if(idx >= FILTER_LEN) idx = 0; return (filter_buf[0]+...+filter_buf[4])/FILTER_LEN; } ``` #### 6. **网络协议栈** - **TCP/IP协议封装** - ESP8266透传模式下直接发送HTTP POST请求: ``` POST /api/data HTTP/1.1 Host: cloud.example.com Content-Type: application/json {"dust": 25.6, "temp": 28.5, "humidity": 45} ``` - **断网重连机制** - 心跳包检测(每30秒发送ping指令) --- ### 三、工业场景特殊处理 1. **抗干扰设计** - 传感器信号线增加RC滤波电路 - PCB布局隔离数字/模拟地 2. **异常处理** - 看门狗(IWDG)防止程序跑飞 ```c HAL_IWDG_Refresh(&hiwdg); // 主循环中喂狗 ``` 3. **数据安全** - AES加密敏感数据后再上传 --- ### 四、开发工具链 1. **硬件调试**: - 逻辑分析仪抓取传感器时序 - J-Link通过SWD接口调试STM32 2. **软件工具**: - STM32CubeMX配置外设时钟树 - Wireshark抓包分析网络通信 --- ### 、扩展优化方向 1. **边缘计算**:在本地实现浓度超标预警算法 2. **OTA升级**:通过WiFi更新固件 3. **多节点组网**:LoRa扩展覆盖范围 在我面试时,我改如何介绍这个项目
03-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值