首先进行配置,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;
}
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上报一次温湿度了
————————————————
版权声明:本文为CSDN博主「每天都在摸爬滚打」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_66273027/article/details/131813106
|
|