11 files added
7 files modified
| | |
| | | void DebugMon_Handler(void); |
| | | void PendSV_Handler(void); |
| | | void SysTick_Handler(void); |
| | | void USART1_IRQHandler(void); |
| | | void EXTI15_10_IRQHandler(void); |
| | | /* USER CODE BEGIN EFP */ |
| | | |
New file |
| | |
| | | /* |
| | | * coreJSON v3.3.0 |
| | | * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| | | * |
| | | * SPDX-License-Identifier: MIT |
| | | * |
| | | * 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 <limits.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_; |
| | | |
| | | #if ( CHAR_MIN == 0 ) |
| | | #define isascii_( x ) ( ( x ) <= '\x7F' ) |
| | | #else |
| | | #define isascii_( x ) ( ( x ) >= '\0' ) |
| | | #endif |
| | | #define iscntrl_( x ) ( isascii_( x ) && ( ( x ) < ' ' ) ) |
| | | #define isdigit_( x ) ( ( ( x ) >= '0' ) && ( ( x ) <= '9' ) ) |
| | | /* 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 = 0U; |
| | | |
| | | coreJSON_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 = 0U, max = 0U; |
| | | |
| | | coreJSON_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 = 0U, bitCount = 0U, j = 0U; |
| | | uint32_t value = 0U; |
| | | char_ c; |
| | | |
| | | coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) ); |
| | | |
| | | i = *start; |
| | | coreJSON_ASSERT( i < max ); |
| | | coreJSON_ASSERT( !isascii_( buf[ i ] ) ); |
| | | |
| | | 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; |
| | | |
| | | coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) ); |
| | | |
| | | if( *start < max ) |
| | | { |
| | | if( isascii_( buf[ *start ] ) ) |
| | | { |
| | | *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 = 0U, end = 0U; |
| | | uint16_t value = 0U; |
| | | |
| | | coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) ); |
| | | coreJSON_ASSERT( outValue != NULL ); |
| | | |
| | | i = *start; |
| | | #define HEX_ESCAPE_LENGTH ( 6U ) /* e.g., \u1234 */ |
| | | /* MISRA Ref 14.3.1 [Configuration dependent invariant] */ |
| | | /* More details at: https://github.com/FreeRTOS/coreJSON/blob/main/MISRA.md#rule-143 */ |
| | | /* coverity[misra_c_2012_rule_14_3_violation] */ |
| | | end = ( i <= ( SIZE_MAX - HEX_ESCAPE_LENGTH ) ) ? ( i + HEX_ESCAPE_LENGTH ) : SIZE_MAX; |
| | | |
| | | 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 = 0U; |
| | | uint16_t value = 0U; |
| | | |
| | | coreJSON_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 = 0U; |
| | | |
| | | coreJSON_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 = 0; |
| | | |
| | | coreJSON_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 = 0U; |
| | | |
| | | coreJSON_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; |
| | | |
| | | coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) ); |
| | | coreJSON_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" ) ) |
| | | { |
| | | ret = true; |
| | | } |
| | | else if( skipLit_( "false" ) ) |
| | | { |
| | | ret = true; |
| | | } |
| | | else if( skipLit_( "null" ) ) |
| | | { |
| | | ret = true; |
| | | } |
| | | else |
| | | { |
| | | ret = false; |
| | | } |
| | | |
| | | 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 = 0U, saveStart = 0U; |
| | | int32_t value = 0; |
| | | |
| | | coreJSON_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 = 0U; |
| | | |
| | | coreJSON_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 = 0U; |
| | | |
| | | coreJSON_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 = 0U; |
| | | |
| | | coreJSON_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 ) |
| | | { |
| | | ret = true; |
| | | } |
| | | else if( skipAnyLiteral( buf, start, max ) == true ) |
| | | { |
| | | ret = true; |
| | | } |
| | | else if( skipNumber( buf, start, max ) == true ) |
| | | { |
| | | ret = true; |
| | | } |
| | | else |
| | | { |
| | | ret = false; |
| | | } |
| | | |
| | | 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 = 0U; |
| | | |
| | | coreJSON_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 = 0U; |
| | | |
| | | coreJSON_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 = 0U; |
| | | bool comma = false; |
| | | |
| | | coreJSON_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 ) |
| | | { |
| | | bool modeIsOpenBracket = ( bool ) isOpenBracket_( mode ); |
| | | |
| | | /* assert function may be implemented in macro using a # or ## operator. |
| | | * Using a local variable here to prevent macro replacement is subjected |
| | | * to macro itself. */ |
| | | coreJSON_ASSERT( modeIsOpenBracket != false ); |
| | | |
| | | /* Adding this line to avoid unused variable warning in release mode. */ |
| | | ( void ) modeIsOpenBracket; |
| | | |
| | | 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 = 0U; |
| | | |
| | | coreJSON_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 ) && ( depth < JSON_MAX_DEPTH ) && |
| | | isMatchingBracket_( stack[ depth ], c ) ) |
| | | { |
| | | depth--; |
| | | |
| | | if( ( skipSpaceAndComma( buf, &i, max ) == true ) && |
| | | isOpenBracket_( stack[ depth ] ) ) |
| | | { |
| | | skipScalars( buf, &i, max, stack[ depth ] ); |
| | | } |
| | | |
| | | break; |
| | | } |
| | | |
| | | ret = ( ( depth == 0 ) && isMatchingBracket_( stack[ depth ], c ) ) ? |
| | | 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 = 0U; |
| | | |
| | | 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 = 0U, valueStart = 0U; |
| | | |
| | | coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) ); |
| | | coreJSON_ASSERT( ( value != NULL ) && ( valueLength != NULL ) ); |
| | | |
| | | i = *start; |
| | | valueStart = i; |
| | | |
| | | if( skipAnyScalar( buf, &i, max ) == true ) |
| | | { |
| | | *value = valueStart; |
| | | *valueLength = i - valueStart; |
| | | } |
| | | else if( 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 = 0U, keyStart = 0U; |
| | | |
| | | coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) ); |
| | | coreJSON_ASSERT( ( key != NULL ) && ( keyLength != NULL ) ); |
| | | coreJSON_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 = 0U, key = 0U, keyLength = 0U, value = 0U, valueLength = 0U; |
| | | |
| | | coreJSON_ASSERT( ( buf != NULL ) && ( query != NULL ) ); |
| | | coreJSON_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 = 0U, value = 0U, valueLength = 0U; |
| | | uint32_t currentIndex = 0U; |
| | | |
| | | coreJSON_ASSERT( buf != NULL ); |
| | | coreJSON_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 ) || |
| | | ( currentIndex == UINT32_MAX ) ) |
| | | { |
| | | 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. |
| | | */ |
| | | #ifndef JSON_QUERY_KEY_SEPARATOR |
| | | #define JSON_QUERY_KEY_SEPARATOR '.' |
| | | #endif |
| | | #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 = 0U; |
| | | |
| | | coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( outLength != NULL ) ); |
| | | coreJSON_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 = 0U, start = 0U, queryStart = 0U, value = 0U, length = max; |
| | | |
| | | coreJSON_ASSERT( ( buf != NULL ) && ( query != NULL ) ); |
| | | coreJSON_ASSERT( ( outValue != NULL ) && ( outValueLength != NULL ) ); |
| | | coreJSON_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 = 0U; |
| | | |
| | | 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 Ref 11.3.1 [Pointer conversion] */ |
| | | /* More details at: https://github.com/FreeRTOS/coreJSON/blob/main/MISRA.md#rule-113 */ |
| | | /* 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; |
| | | |
| | | coreJSON_ASSERT( ( buf != NULL ) && ( max > 0U ) ); |
| | | coreJSON_ASSERT( ( start != NULL ) && ( next != NULL ) ); |
| | | coreJSON_ASSERT( ( outKey != NULL ) && ( outKeyLength != NULL ) ); |
| | | coreJSON_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 = 0U, keyLength = 0U, value = 0U, valueLength = 0U; |
| | | |
| | | 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; |
| | | } |
New file |
| | |
| | | /* |
| | | * coreJSON v3.3.0 |
| | | * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| | | * |
| | | * SPDX-License-Identifier: MIT |
| | | * |
| | | * 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 <assert.h> |
| | | #include <stdbool.h> |
| | | #include <stddef.h> |
| | | |
| | | /* *INDENT-OFF* */ |
| | | #ifdef __cplusplus |
| | | extern "C" { |
| | | #endif |
| | | /* *INDENT-ON* */ |
| | | |
| | | /** |
| | | * @brief By default, has the stand behavior of assert() for |
| | | * parameter checking. To swap out the assert(), define this |
| | | * macro with the desired behavior. */ |
| | | #ifndef coreJSON_ASSERT |
| | | #define coreJSON_ASSERT( expr ) assert( expr ) |
| | | #endif |
| | | |
| | | |
| | | /** |
| | | * @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] */ |
| | | |
| | | /* *INDENT-OFF* */ |
| | | #ifdef __cplusplus |
| | | } |
| | | #endif |
| | | /* *INDENT-ON* */ |
| | | |
| | | #endif /* ifndef CORE_JSON_H_ */ |
New file |
| | |
| | | /********************************************************************** |
| | | * Copyright: (C)2021 LingYun IoT System Studio <www.weike-iot.com> |
| | | * Author: GuoWenxue<guowenxue@gmail.com> QQ: 281143292 |
| | | * Description: ISKBoard OLED font generated by PCtoLCD.exe |
| | | * |
| | | * ChangeLog: |
| | | * Version Date Author Description |
| | | * V1.0.0 2021.08.10 GuoWenxue Release initial version |
| | | ***********************************************************************/ |
| | | #ifndef __FONT_OLED_H |
| | | #define __FONT_OLED_H |
| | | |
| | | #include <stdint.h> |
| | | |
| | | /*+--------------------------------+ |
| | | *| ASCII font size 6x8 | |
| | | *+--------------------------------+*/ |
| | | |
| | | static const uint8_t F6x8[][6] = |
| | | { |
| | | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},// sp |
| | | {0x00, 0x00, 0x00, 0x2f, 0x00, 0x00},// ! |
| | | {0x00, 0x00, 0x07, 0x00, 0x07, 0x00},// " |
| | | {0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14},// # |
| | | {0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12},// $ |
| | | {0x00, 0x62, 0x64, 0x08, 0x13, 0x23},// % |
| | | {0x00, 0x36, 0x49, 0x55, 0x22, 0x50},// & |
| | | {0x00, 0x00, 0x05, 0x03, 0x00, 0x00},// ' |
| | | {0x00, 0x00, 0x1c, 0x22, 0x41, 0x00},// ( |
| | | {0x00, 0x00, 0x41, 0x22, 0x1c, 0x00},// ) |
| | | {0x00, 0x14, 0x08, 0x3E, 0x08, 0x14},// * |
| | | {0x00, 0x08, 0x08, 0x3E, 0x08, 0x08},// + |
| | | {0x00, 0x00, 0x00, 0xA0, 0x60, 0x00},// , |
| | | {0x00, 0x08, 0x08, 0x08, 0x08, 0x08},// - |
| | | {0x00, 0x00, 0x60, 0x60, 0x00, 0x00},// . |
| | | {0x00, 0x20, 0x10, 0x08, 0x04, 0x02},// / |
| | | {0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E},// 0 |
| | | {0x00, 0x00, 0x42, 0x7F, 0x40, 0x00},// 1 |
| | | {0x00, 0x42, 0x61, 0x51, 0x49, 0x46},// 2 |
| | | {0x00, 0x21, 0x41, 0x45, 0x4B, 0x31},// 3 |
| | | {0x00, 0x18, 0x14, 0x12, 0x7F, 0x10},// 4 |
| | | {0x00, 0x27, 0x45, 0x45, 0x45, 0x39},// 5 |
| | | {0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30},// 6 |
| | | {0x00, 0x01, 0x71, 0x09, 0x05, 0x03},// 7 |
| | | {0x00, 0x36, 0x49, 0x49, 0x49, 0x36},// 8 |
| | | {0x00, 0x06, 0x49, 0x49, 0x29, 0x1E},// 9 |
| | | {0x00, 0x00, 0x36, 0x36, 0x00, 0x00},// : |
| | | {0x00, 0x00, 0x56, 0x36, 0x00, 0x00},// ; |
| | | {0x00, 0x08, 0x14, 0x22, 0x41, 0x00},// < |
| | | {0x00, 0x14, 0x14, 0x14, 0x14, 0x14},// = |
| | | {0x00, 0x00, 0x41, 0x22, 0x14, 0x08},// > |
| | | {0x00, 0x02, 0x01, 0x51, 0x09, 0x06},// ? |
| | | {0x00, 0x32, 0x49, 0x59, 0x51, 0x3E},// @ |
| | | {0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C},// A |
| | | {0x00, 0x7F, 0x49, 0x49, 0x49, 0x36},// B |
| | | {0x00, 0x3E, 0x41, 0x41, 0x41, 0x22},// C |
| | | {0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C},// D |
| | | {0x00, 0x7F, 0x49, 0x49, 0x49, 0x41},// E |
| | | {0x00, 0x7F, 0x09, 0x09, 0x09, 0x01},// F |
| | | {0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A},// G |
| | | {0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F},// H |
| | | {0x00, 0x00, 0x41, 0x7F, 0x41, 0x00},// I |
| | | {0x00, 0x20, 0x40, 0x41, 0x3F, 0x01},// J |
| | | {0x00, 0x7F, 0x08, 0x14, 0x22, 0x41},// K |
| | | {0x00, 0x7F, 0x40, 0x40, 0x40, 0x40},// L |
| | | {0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F},// M |
| | | {0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F},// N |
| | | {0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E},// O |
| | | {0x00, 0x7F, 0x09, 0x09, 0x09, 0x06},// P |
| | | {0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E},// Q |
| | | {0x00, 0x7F, 0x09, 0x19, 0x29, 0x46},// R |
| | | {0x00, 0x46, 0x49, 0x49, 0x49, 0x31},// S |
| | | {0x00, 0x01, 0x01, 0x7F, 0x01, 0x01},// T |
| | | {0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F},// U |
| | | {0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F},// V |
| | | {0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F},// W |
| | | {0x00, 0x63, 0x14, 0x08, 0x14, 0x63},// X |
| | | {0x00, 0x07, 0x08, 0x70, 0x08, 0x07},// Y |
| | | {0x00, 0x61, 0x51, 0x49, 0x45, 0x43},// Z |
| | | {0x00, 0x00, 0x7F, 0x41, 0x41, 0x00},// [ |
| | | {0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55},// 55 |
| | | {0x00, 0x00, 0x41, 0x41, 0x7F, 0x00},// ] |
| | | {0x00, 0x04, 0x02, 0x01, 0x02, 0x04},// ^ |
| | | {0x00, 0x40, 0x40, 0x40, 0x40, 0x40},// _ |
| | | {0x00, 0x00, 0x01, 0x02, 0x04, 0x00},// ' |
| | | {0x00, 0x20, 0x54, 0x54, 0x54, 0x78},// a |
| | | {0x00, 0x7F, 0x48, 0x44, 0x44, 0x38},// b |
| | | {0x00, 0x38, 0x44, 0x44, 0x44, 0x20},// c |
| | | {0x00, 0x38, 0x44, 0x44, 0x48, 0x7F},// d |
| | | {0x00, 0x38, 0x54, 0x54, 0x54, 0x18},// e |
| | | {0x00, 0x08, 0x7E, 0x09, 0x01, 0x02},// f |
| | | {0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C},// g |
| | | {0x00, 0x7F, 0x08, 0x04, 0x04, 0x78},// h |
| | | {0x00, 0x00, 0x44, 0x7D, 0x40, 0x00},// i |
| | | {0x00, 0x40, 0x80, 0x84, 0x7D, 0x00},// j |
| | | {0x00, 0x7F, 0x10, 0x28, 0x44, 0x00},// k |
| | | {0x00, 0x00, 0x41, 0x7F, 0x40, 0x00},// l |
| | | {0x00, 0x7C, 0x04, 0x18, 0x04, 0x78},// m |
| | | {0x00, 0x7C, 0x08, 0x04, 0x04, 0x78},// n |
| | | {0x00, 0x38, 0x44, 0x44, 0x44, 0x38},// o |
| | | {0x00, 0xFC, 0x24, 0x24, 0x24, 0x18},// p |
| | | {0x00, 0x18, 0x24, 0x24, 0x18, 0xFC},// q |
| | | {0x00, 0x7C, 0x08, 0x04, 0x04, 0x08},// r |
| | | {0x00, 0x48, 0x54, 0x54, 0x54, 0x20},// s |
| | | {0x00, 0x04, 0x3F, 0x44, 0x40, 0x20},// t |
| | | {0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C},// u |
| | | {0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C},// v |
| | | {0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C},// w |
| | | {0x00, 0x44, 0x28, 0x10, 0x28, 0x44},// x |
| | | {0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C},// y |
| | | {0x00, 0x44, 0x64, 0x54, 0x4C, 0x44},// z |
| | | {0x14, 0x14, 0x14, 0x14, 0x14, 0x14},// horiz lines |
| | | }; |
| | | |
| | | /*+--------------------------------+ |
| | | *| ASCII font size 8x16 | |
| | | *+--------------------------------+*/ |
| | | |
| | | static const uint8_t F8X16[]= |
| | | { |
| | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// 0 |
| | | 0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//! 1 |
| | | 0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//" 2 |
| | | 0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//# 3 |
| | | 0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,//$ 4 |
| | | 0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00,//% 5 |
| | | 0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10,//& 6 |
| | | 0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//' 7 |
| | | 0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00,//( 8 |
| | | 0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00,//) 9 |
| | | 0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00,//* 10 |
| | | 0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00,//+ 11 |
| | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00,//, 12 |
| | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,//- 13 |
| | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,//. 14 |
| | | 0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00,/// 15 |
| | | 0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,//0 16 |
| | | 0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//1 17 |
| | | 0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,//2 18 |
| | | 0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,//3 19 |
| | | 0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,//4 20 |
| | | 0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,//5 21 |
| | | 0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,//6 22 |
| | | 0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,//7 23 |
| | | 0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,//8 24 |
| | | 0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,//9 25 |
| | | 0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,//: 26 |
| | | 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00,//; 27 |
| | | 0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00,//< 28 |
| | | 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,//= 29 |
| | | 0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00,//> 30 |
| | | 0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00,//? 31 |
| | | 0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00,//@ 32 |
| | | 0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,//A 33 |
| | | 0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00,//B 34 |
| | | 0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00,//C 35 |
| | | 0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00,//D 36 |
| | | 0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00,//E 37 |
| | | 0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00,//F 38 |
| | | 0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00,//G 39 |
| | | 0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20,//H 40 |
| | | 0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//I 41 |
| | | 0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,//J 42 |
| | | 0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,//K 43 |
| | | 0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00,//L 44 |
| | | 0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00,//M 45 |
| | | 0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00,//N 46 |
| | | 0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00,//O 47 |
| | | 0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00,//P 48 |
| | | 0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00,//Q 49 |
| | | 0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20,//R 50 |
| | | 0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00,//S 51 |
| | | 0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//T 52 |
| | | 0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//U 53 |
| | | 0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00,//V 54 |
| | | 0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00,//W 55 |
| | | 0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20,//X 56 |
| | | 0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//Y 57 |
| | | 0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00,//Z 58 |
| | | 0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00,//[ 59 |
| | | 0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00,//\ 60 |
| | | 0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00,//] 61 |
| | | 0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//^ 62 |
| | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,//_ 63 |
| | | 0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//` 64 |
| | | 0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20,//a 65 |
| | | 0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00,//b 66 |
| | | 0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00,//c 67 |
| | | 0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20,//d 68 |
| | | 0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00,//e 69 |
| | | 0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//f 70 |
| | | 0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00,//g 71 |
| | | 0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//h 72 |
| | | 0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//i 73 |
| | | 0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,//j 74 |
| | | 0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00,//k 75 |
| | | 0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//l 76 |
| | | 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F,//m 77 |
| | | 0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//n 78 |
| | | 0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//o 79 |
| | | 0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00,//p 80 |
| | | 0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80,//q 81 |
| | | 0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00,//r 82 |
| | | 0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00,//s 83 |
| | | 0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00,//t 84 |
| | | 0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20,//u 85 |
| | | 0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00,//v 86 |
| | | 0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00,//w 87 |
| | | 0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00,//x 88 |
| | | 0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00,//y 89 |
| | | 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00,//z 90 |
| | | 0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40,//{ 91 |
| | | 0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,//| 92 |
| | | 0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00,//} 93 |
| | | 0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//~ 94 |
| | | }; |
| | | |
| | | |
| | | |
| | | /*+--------------------------------+ |
| | | *| BMP Logo for 128x32 | |
| | | *+--------------------------------+*/ |
| | | |
| | | /* lingyun-128x32.bmp 使用 PCtoLCD.exe 取模软件生成配置(图形模式): 阴码、列行式、逆向、十六进制数、C51格式 */ |
| | | |
| | | static const uint8_t bmp_logo[] = { |
| | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0xFF,0xFF,0x03,0x03,0x00,0x00, |
| | | 0x00,0xFC,0xFE,0x87,0x03,0x03,0x87,0xFE,0xFC,0x00,0x03,0x03,0xFF,0xFF,0x03,0x03, |
| | | 0x00,0x00,0xF8,0xF8,0xF8,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| | | 0x78,0x78,0x78,0x78,0x00,0x00,0x00,0x80,0xC0,0xE0,0xF0,0xF8,0xF8,0x78,0x78,0x78, |
| | | 0xF8,0xF8,0xF8,0xF0,0xE0,0x80,0x00,0x00,0x00,0x80,0x80,0xE0,0xE0,0xF0,0xF0,0x78, |
| | | 0x78,0x78,0x78,0x78,0x78,0xF0,0xF0,0xE0,0xE0,0xC0,0x80,0x00,0x00,0xF0,0xF0,0xF0, |
| | | 0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xF0,0xF0,0xF0,0xF0,0x00,0x00,0xF0,0xF0, |
| | | 0xF0,0xF0,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xF0,0xF0,0xF0,0x00,0x00,0x00, |
| | | 0xC0,0xE0,0xF0,0xF8,0xF8,0xF8,0xF8,0xF8,0xFB,0xFB,0xF3,0xF3,0xE3,0xC3,0x00,0x03, |
| | | 0x03,0x00,0x01,0x03,0x03,0x03,0x03,0x01,0x00,0x03,0x03,0x00,0x03,0x03,0x00,0x00, |
| | | 0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| | | 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x00,0x00,0x00, |
| | | 0x00,0x01,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x07,0x03,0x00,0x00, |
| | | 0x00,0x00,0xE0,0xE0,0xE0,0xE0,0xE1,0xE3,0xE3,0xE3,0xE3,0xE0,0x00,0x1F,0x3F,0x7F, |
| | | 0xFF,0xFC,0xF0,0xE0,0xF0,0xF0,0xF8,0xFF,0xFF,0xFF,0x3F,0x1F,0x00,0x00,0xFF,0xFF, |
| | | 0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00, |
| | | 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00, |
| | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| | | 0x00,0x00,0x7F,0x7F,0x7F,0x7F,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x00,0x00, |
| | | 0x7F,0x7F,0x7F,0x7F,0x00,0x00,0x00,0x7F,0x7F,0x7F,0x7F,0x00,0x00,0x00,0x00,0x00, |
| | | 0x00,0x00,0x7F,0x7F,0x7F,0x7F,0x00,0x00,0x03,0x0F,0x1F,0x3F,0x7E,0x7C,0x78,0x70, |
| | | 0x70,0x60,0x61,0x71,0x71,0x7F,0x7F,0x7F,0x3F,0x1F,0x07,0x03,0x00,0x00,0x00,0x00, |
| | | 0x00,0x01,0x7F,0x7F,0x7F,0x7F,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x1F, |
| | | 0x3F,0x7F,0x7F,0x78,0x70,0x70,0x70,0x70,0x78,0x7F,0x3F,0x1F,0x0F,0x00,0x00,0x00, |
| | | 0x7F,0x7F,0x7F,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x7F,0x7F,0x7F,0x00,0x00, |
| | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 |
| | | }; |
| | | |
| | | |
| | | /*+--------------------------------+ |
| | | *| Chines Font 16x16 SongTi | |
| | | *+--------------------------------+*/ |
| | | |
| | | /* 汉字生成使用 PCtoLCD.exe(字符模式):宋体、16x16、阴码、列行式、逆向、十六进制数、C51格式 */ |
| | | |
| | | /* 宋体 16X16: 凌云实验室 */ |
| | | |
| | | #define HZK_LEN_LINGYUN 5 |
| | | static const uint8_t Hzk_LingYun[][32]= |
| | | { |
| | | {0x00,0x02,0x0C,0xC0,0x00,0x20,0xA4,0x64,0xA4,0x3F,0x24,0x64,0xA4,0x20,0x20,0x00}, |
| | | {0x02,0x02,0x7F,0x00,0x80,0x89,0x44,0x46,0x2B,0x12,0x2A,0x46,0x80,0x81,0x00,0x00},/*"凌",0*/ |
| | | |
| | | {0x40,0x40,0x42,0x42,0x42,0x42,0xC2,0x42,0x42,0x42,0x42,0x42,0x42,0x40,0x40,0x00}, |
| | | {0x00,0x20,0x70,0x28,0x24,0x23,0x20,0x20,0x20,0x24,0x28,0x30,0xE0,0x00,0x00,0x00},/*"云",1*/ |
| | | |
| | | {0x10,0x0C,0x04,0x84,0x14,0x64,0x05,0x06,0xF4,0x04,0x04,0x04,0x04,0x14,0x0C,0x00}, |
| | | {0x04,0x84,0x84,0x44,0x47,0x24,0x14,0x0C,0x07,0x0C,0x14,0x24,0x44,0x84,0x04,0x00},/*"实",2*/ |
| | | |
| | | {0x02,0xFA,0x82,0x82,0xFE,0x80,0x40,0x20,0x50,0x4C,0x43,0x4C,0x50,0x20,0x40,0x00}, |
| | | {0x08,0x18,0x48,0x84,0x44,0x3F,0x40,0x44,0x58,0x41,0x4E,0x60,0x58,0x47,0x40,0x00},/*"验",3*/ |
| | | |
| | | {0x10,0x0C,0x24,0x24,0xA4,0x64,0x25,0x26,0x24,0x24,0xA4,0x24,0x24,0x14,0x0C,0x00}, |
| | | {0x40,0x40,0x48,0x49,0x49,0x49,0x49,0x7F,0x49,0x49,0x49,0x4B,0x48,0x40,0x40,0x00},/*"室",4*/ |
| | | }; |
| | | |
| | | /* 宋体 16x16: 温度 */ |
| | | #define HZK_LEN_TEMP 2 |
| | | static const uint8_t Hzk_Temp[][32]= |
| | | { |
| | | {0x10,0x60,0x02,0x8C,0x00,0x00,0xFE,0x92,0x92,0x92,0x92,0x92,0xFE,0x00,0x00,0x00}, |
| | | {0x04,0x04,0x7E,0x01,0x40,0x7E,0x42,0x42,0x7E,0x42,0x7E,0x42,0x42,0x7E,0x40,0x00},/*"温",0*/ |
| | | |
| | | {0x00,0x00,0xFC,0x24,0x24,0x24,0xFC,0x25,0x26,0x24,0xFC,0x24,0x24,0x24,0x04,0x00}, |
| | | {0x40,0x30,0x8F,0x80,0x84,0x4C,0x55,0x25,0x25,0x25,0x55,0x4C,0x80,0x80,0x80,0x00},/*"度",1*/ |
| | | }; |
| | | |
| | | /* 宋体 16x16: 湿度 */ |
| | | #define HZK_LEN_HUMD 2 |
| | | static const uint8_t Hzk_Humd[][32]= |
| | | { |
| | | {0x10,0x60,0x02,0x8C,0x00,0xFE,0x92,0x92,0x92,0x92,0x92,0x92,0xFE,0x00,0x00,0x00}, |
| | | {0x04,0x04,0x7E,0x01,0x44,0x48,0x50,0x7F,0x40,0x40,0x7F,0x50,0x48,0x44,0x40,0x00},/*"湿",0*/ |
| | | |
| | | {0x00,0x00,0xFC,0x24,0x24,0x24,0xFC,0x25,0x26,0x24,0xFC,0x24,0x24,0x24,0x04,0x00}, |
| | | {0x40,0x30,0x8F,0x80,0x84,0x4C,0x55,0x25,0x25,0x25,0x55,0x4C,0x80,0x80,0x80,0x00},/*"度",1*/ |
| | | }; |
| | | |
| | | /* 宋体 16X16: 光强 */ |
| | | #define HZK_LEN_LIGHT 2 |
| | | static const uint8_t Hzk_Light[][32]= |
| | | { |
| | | {0x40,0x40,0x42,0x44,0x58,0xC0,0x40,0x7F,0x40,0xC0,0x50,0x48,0x46,0x40,0x40,0x00}, |
| | | {0x80,0x80,0x40,0x20,0x18,0x07,0x00,0x00,0x00,0x3F,0x40,0x40,0x40,0x40,0x78,0x00},/*"光",0*/ |
| | | |
| | | {0x02,0xE2,0x22,0x22,0x3E,0x00,0x80,0x9E,0x92,0x92,0xF2,0x92,0x92,0x9E,0x80,0x00}, |
| | | {0x00,0x43,0x82,0x42,0x3E,0x40,0x47,0x44,0x44,0x44,0x7F,0x44,0x44,0x54,0xE7,0x00},/*"强",1*/ |
| | | }; |
| | | |
| | | |
| | | /* 宋体 16X16: 声音 */ |
| | | #define HZK_LEN_NOISY 2 |
| | | static const uint8_t Hzk_Noisy[][32]= |
| | | { |
| | | {0x04,0x14,0xD4,0x54,0x54,0x54,0x54,0xDF,0x54,0x54,0x54,0x54,0xD4,0x14,0x04,0x00}, |
| | | {0x80,0x60,0x1F,0x02,0x02,0x02,0x02,0x03,0x02,0x02,0x02,0x02,0x03,0x00,0x00,0x00},/*"声",0*/ |
| | | |
| | | {0x40,0x40,0x44,0x44,0x54,0x64,0x45,0x46,0x44,0x64,0x54,0x44,0x44,0x40,0x40,0x00}, |
| | | {0x00,0x00,0x00,0xFF,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0xFF,0x00,0x00,0x00,0x00},/*"音",1*/ |
| | | }; |
| | | |
| | | #endif |
New file |
| | |
| | | /********************************************************************** |
| | | * Copyright: (C)2021 LingYun IoT System Studio <www.weike-iot.com> |
| | | * Author: GuoWenxue<guowenxue@gmail.com> QQ: 281143292 |
| | | * Description: ISKBoard OLED(N091-2832TSWFG02-H14, 128x32) driver |
| | | * |
| | | * ChangeLog: |
| | | * Version Date Author Description |
| | | * V1.0.0 2021.08.10 GuoWenxue Release initial version |
| | | ***********************************************************************/ |
| | | |
| | | #include "hal_oled.h" |
| | | |
| | | #define mdelay(ms) HAL_Delay(ms) |
| | | |
| | | enum |
| | | { |
| | | OLED_CMD = 0, |
| | | OLED_DATA, |
| | | }; |
| | | |
| | | static void IIC_Write_Command(uint8_t command) |
| | | { |
| | | /* Send I2C start signal */ |
| | | I2C_StartCondition(); |
| | | |
| | | /* Send OLED I2C slave write address */ |
| | | I2C_SendAddress(I2C_WR); |
| | | |
| | | /* send command value */ |
| | | I2C_WriteByte(0x00); |
| | | I2C_WriteByte(command); |
| | | |
| | | /* Send I2C stop signal */ |
| | | I2C_StopCondition(); |
| | | } |
| | | |
| | | static void IIC_Write_Data(uint8_t data) |
| | | { |
| | | /* Send I2C start signal */ |
| | | I2C_StartCondition(); |
| | | |
| | | /* Send OLED I2C slave write address */ |
| | | I2C_SendAddress(I2C_WR); |
| | | |
| | | /* send data value */ |
| | | I2C_WriteByte(0x40); |
| | | I2C_WriteByte(data); |
| | | |
| | | /* Send I2C stop signal */ |
| | | I2C_StopCondition(); |
| | | } |
| | | |
| | | |
| | | /* OLED write a byte command data or command */ |
| | | void OLED_WR_Byte(uint8_t data, uint8_t type) |
| | | { |
| | | if( OLED_CMD==type ) |
| | | { |
| | | IIC_Write_Command(data); |
| | | } |
| | | else |
| | | { |
| | | IIC_Write_Data(data); |
| | | } |
| | | } |
| | | |
| | | /* |
| | | *+-------------------------------------------------+ |
| | | *| OLED initial/control function API | |
| | | *+-------------------------------------------------+ |
| | | */ |
| | | |
| | | /* Turn OLED display onʾ */ |
| | | void OLED_Display_On(void) |
| | | { |
| | | OLED_WR_Byte(0X8D, OLED_CMD); //SET DCDC command |
| | | OLED_WR_Byte(0X14, OLED_CMD); //DCDC ON |
| | | OLED_WR_Byte(0XAF, OLED_CMD); //DISPLAY ON |
| | | } |
| | | |
| | | /* Turn OLED display off */ |
| | | void OLED_Display_Off(void) |
| | | { |
| | | OLED_WR_Byte(0X8D, OLED_CMD); //SET DCDC command |
| | | OLED_WR_Byte(0X10, OLED_CMD); //DCDC OFF |
| | | OLED_WR_Byte(0XAE, OLED_CMD); //DISPLAY OFF |
| | | } |
| | | |
| | | /* Clear OLED, it will be black */ |
| | | void OLED_Clear(void) |
| | | { |
| | | uint8_t i, j; |
| | | |
| | | /* update display */ |
| | | for(i=0;i<8;i++) |
| | | { |
| | | OLED_WR_Byte (0xb0+i, OLED_CMD); // set page address: 0~7 |
| | | OLED_WR_Byte (0x00, OLED_CMD); // set display address, column address lower bytes;ַ |
| | | OLED_WR_Byte (0x10, OLED_CMD); // set display address, column address higher bytes; |
| | | |
| | | for(j=0; j<128; j++) |
| | | OLED_WR_Byte(0, OLED_DATA); |
| | | } |
| | | |
| | | OLED_Set_Pos(0, 0); |
| | | } |
| | | |
| | | void OLED_On(void) |
| | | { |
| | | uint8_t i, j; |
| | | |
| | | /* update display */ |
| | | for(i=0; i<8; i++) |
| | | { |
| | | OLED_WR_Byte (0xb0+i, OLED_CMD); // set page address: 0~7 |
| | | OLED_WR_Byte (0x00, OLED_CMD); // set display address, row address lower bytes;ַ |
| | | OLED_WR_Byte (0x10, OLED_CMD); // set display address, row address higher bytes; |
| | | |
| | | for(j=0; j<128; j++) |
| | | OLED_WR_Byte(1, OLED_DATA); |
| | | } |
| | | } |
| | | |
| | | void OLED_Init(void) |
| | | { |
| | | if( i2c_lock(OLED_CHIPADDR) ) |
| | | { |
| | | return ; |
| | | } |
| | | |
| | | mdelay(10); |
| | | |
| | | OLED_WR_Byte(0xAE,OLED_CMD); // Disable |
| | | |
| | | OLED_WR_Byte(0x40,OLED_CMD); //---set low column address |
| | | OLED_WR_Byte(0xB0,OLED_CMD); //---set high column address |
| | | |
| | | OLED_WR_Byte(0xC8,OLED_CMD); |
| | | |
| | | OLED_WR_Byte(0x81,OLED_CMD); |
| | | OLED_WR_Byte(0xff,OLED_CMD); |
| | | |
| | | OLED_WR_Byte(0xa1,OLED_CMD); |
| | | |
| | | OLED_WR_Byte(0xa6,OLED_CMD); |
| | | |
| | | OLED_WR_Byte(0xa8,OLED_CMD); |
| | | OLED_WR_Byte(0x1f,OLED_CMD); |
| | | |
| | | OLED_WR_Byte(0xd3,OLED_CMD); |
| | | OLED_WR_Byte(0x00,OLED_CMD); |
| | | |
| | | OLED_WR_Byte(0xd5,OLED_CMD); |
| | | OLED_WR_Byte(0xf0,OLED_CMD); |
| | | |
| | | OLED_WR_Byte(0xd9,OLED_CMD); |
| | | OLED_WR_Byte(0x22,OLED_CMD); |
| | | |
| | | OLED_WR_Byte(0xda,OLED_CMD); |
| | | OLED_WR_Byte(0x02,OLED_CMD); |
| | | |
| | | OLED_WR_Byte(0xdb,OLED_CMD); |
| | | OLED_WR_Byte(0x49,OLED_CMD); |
| | | |
| | | OLED_WR_Byte(0x8d,OLED_CMD); |
| | | OLED_WR_Byte(0x14,OLED_CMD); |
| | | |
| | | OLED_WR_Byte(0xaf,OLED_CMD); |
| | | |
| | | OLED_Clear(); |
| | | } |
| | | |
| | | /* |
| | | *+-------------------------------------------------+ |
| | | *| OLED display function API | |
| | | *+-------------------------------------------------+ |
| | | */ |
| | | |
| | | /* set OLED display position */ |
| | | void OLED_Set_Pos(uint8_t x, uint8_t y) |
| | | { |
| | | OLED_WR_Byte(0xb0+y, OLED_CMD); |
| | | OLED_WR_Byte(((x&0xf0)>>4)|0x10, OLED_CMD); |
| | | OLED_WR_Byte((x&0x0f), OLED_CMD); |
| | | } |
| | | |
| | | /* show a character on OLED as $Char_Size */ |
| | | void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t char_Size) |
| | | { |
| | | uint8_t c=0,i=0; |
| | | |
| | | c=chr-' '; // get offset value |
| | | |
| | | if( x>X_WIDTH-1 ) |
| | | { |
| | | x=0; |
| | | y=y+2; |
| | | } |
| | | |
| | | if(char_Size ==16) |
| | | { |
| | | OLED_Set_Pos(x,y); |
| | | |
| | | for(i=0; i<8; i++) |
| | | OLED_WR_Byte(F8X16[c*16+i],OLED_DATA); |
| | | |
| | | OLED_Set_Pos(x,y+1); |
| | | |
| | | for(i=0;i<8;i++) |
| | | OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA); |
| | | } |
| | | else |
| | | { |
| | | OLED_Set_Pos(x,y); |
| | | for(i=0;i<6;i++) |
| | | OLED_WR_Byte(F6x8[c][i],OLED_DATA); |
| | | } |
| | | } |
| | | |
| | | /* show a string on OLED */ |
| | | void OLED_ShowString(uint8_t x, uint8_t y, char *chr, uint8_t font_size) |
| | | { |
| | | uint8_t j=0; |
| | | |
| | | while( chr[j]!='\0' ) |
| | | { |
| | | OLED_ShowChar(x, y, chr[j], font_size); |
| | | |
| | | x+=8; |
| | | |
| | | if(x>120) |
| | | { |
| | | x=0; |
| | | y+=2; |
| | | } |
| | | |
| | | j++; |
| | | } |
| | | } |
| | | |
| | | /* Show Chinese on OLED */ |
| | | void OLED_ShowChinese(const uint8_t (*Hzk)[32], uint8_t x, uint8_t y, uint8_t number) |
| | | { |
| | | uint8_t t,adder=0; |
| | | |
| | | OLED_Set_Pos(x, y); |
| | | for(t=0;t<16;t++) |
| | | { |
| | | OLED_WR_Byte(Hzk[2*number][t],OLED_DATA); |
| | | adder+=1; |
| | | } |
| | | |
| | | OLED_Set_Pos(x,y+1); |
| | | for(t=0;t<16;t++) |
| | | { |
| | | OLED_WR_Byte(Hzk[2*number+1][t],OLED_DATA); |
| | | adder+=1; |
| | | } |
| | | } |
| | | |
| | | /* Show BMP images(32) on OLED, x: 0~127 y:0~7 */ |
| | | void OLED_DrawBMP(uint8_t x0, uint8_t y0, uint8_t x_width, uint8_t y_width, const uint8_t *bmp) |
| | | { |
| | | uint32_t j=0; |
| | | uint8_t x,y; |
| | | |
| | | if( y_width%8==0 ) |
| | | y = y_width/8; |
| | | else |
| | | y = y_width/8+1; |
| | | |
| | | for(y=y0; y<y_width; y++) |
| | | { |
| | | OLED_Set_Pos(x0, y); |
| | | for(x=x0; x<x_width; x++) |
| | | { |
| | | OLED_WR_Byte(bmp[j++],OLED_DATA); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | /********************************************************************** |
| | | * Copyright: (C)2021 LingYun IoT System Studio <www.weike-iot.com> |
| | | * Author: GuoWenxue<guowenxue@gmail.com> QQ: 281143292 |
| | | * Description: ISKBoard OLED(N091-2832TSWFG02-H14, 128x32) driver |
| | | * |
| | | * ChangeLog: |
| | | * Version Date Author Description |
| | | * V1.0.0 2021.08.10 GuoWenxue Release initial version |
| | | ***********************************************************************/ |
| | | |
| | | #ifndef INC_HAL_OLED_H_ |
| | | #define INC_HAL_OLED_H_ |
| | | |
| | | #include "stm32l4xx_hal.h" |
| | | #include "i2c_bitbang.h" |
| | | #include "font_oled.h" |
| | | |
| | | #define OLED_CHIPADDR 0x3C /* OLED chip address */ |
| | | |
| | | #define X_WIDTH 128 |
| | | #define Y_WIDTH 32 |
| | | |
| | | #define OLED_FONT16 16 |
| | | #define OLED_FONT8 8 |
| | | |
| | | /* |
| | | *+-------------------------------------------------+ |
| | | *| OLED initial/control function API | |
| | | *+-------------------------------------------------+ |
| | | */ |
| | | void OLED_Init(void); |
| | | void OLED_On(void); |
| | | void OLED_Clear(void); |
| | | void OLED_Display_On(void); |
| | | void OLED_Display_Off(void); |
| | | |
| | | /* |
| | | *+-------------------------------------------------+ |
| | | *| OLED display function API | |
| | | *+-------------------------------------------------+ |
| | | */ |
| | | void OLED_Set_Pos(uint8_t x, uint8_t y); |
| | | void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size); |
| | | void OLED_ShowString(uint8_t x,uint8_t y, char *p,uint8_t font_size); |
| | | |
| | | /* Show Chinese on OLED */ |
| | | void OLED_ShowChinese(const uint8_t (*Hzk)[32], uint8_t x, uint8_t y, uint8_t number); |
| | | |
| | | /* Show BMP images(128x64) on OLED, x: 0~127 y:0~7 */ |
| | | void OLED_DrawBMP(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t * bmp); |
| | | |
| | | #endif /* INC_HAL_OLED_H_ */ |
| | |
| | | *+---------------------------------+ |
| | | */ |
| | | |
| | | #define mdelay(x) HAL_Delay(x) |
| | | #define mdelay(ms) HAL_Delay(ms) |
| | | |
| | | enum |
| | | { |
| | |
| | | i2c_bus_t i2c_bus = |
| | | { |
| | | /* Addr SCL_Pin SDA_Pin */ |
| | | 0x00, {GPIOB, GPIO_PIN_6 }, {GPIOB, GPIO_PIN_7 }, |
| | | 0x00, {GPIOB, GPIO_PIN_6 }, {GPIOB, GPIO_PIN_7 }, /* SHT20, RTC, OLED */ |
| | | }; |
| | | |
| | | static inline void SDA_IN(void) |
New file |
| | |
| | | /********************************************************************** |
| | | * Copyright: (C)2024 LingYun IoT System Studio |
| | | * Author: GuoWenxue<guowenxue@gmail.com> |
| | | * |
| | | * Description: ISL1208 RTC driver on ISKBoard |
| | | * |
| | | * ChangeLog: |
| | | * Version Date Author Description |
| | | * V1.0.0 2024.08.29 GuoWenxue Release initial version |
| | | * |
| | | ***********************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | #include <string.h> |
| | | #include "isl1208.h" |
| | | #include "i2c_bitbang.h" |
| | | |
| | | //#define CONFIG_DEBUG_RTC /* Enable ISL1208 RTC debug */ |
| | | |
| | | #define ISL1208_CHIPADDR 0x6F /* ISL1208 7-Bits Chip address */ |
| | | |
| | | |
| | | #ifdef CONFIG_DEBUG_RTC |
| | | #define rtc_print(format,args...) printf(format, ##args) |
| | | #else |
| | | #define rtc_print(format,args...) do{} while(0) |
| | | #endif |
| | | |
| | | |
| | | /*+--------------------------------------+ |
| | | *| RTC Chipset ISL1208 Register | |
| | | *+--------------------------------------+*/ |
| | | |
| | | /* rtc section */ |
| | | #define ISL1208_REG_SC 0x00 |
| | | #define ISL1208_REG_MN 0x01 |
| | | #define ISL1208_REG_HR 0x02 |
| | | #define ISL1208_REG_HR_MIL (1<<7) /* 24h/12h mode */ |
| | | #define ISL1208_REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */ |
| | | #define ISL1208_REG_DT 0x03 |
| | | #define ISL1208_REG_MO 0x04 |
| | | #define ISL1208_REG_YR 0x05 |
| | | #define ISL1208_REG_DW 0x06 |
| | | #define ISL1208_RTC_SECTION_LEN 7 /* RTC Section register */ |
| | | #define REGS_RTC_SR_LEN 8 /* RTC Section and status register */ |
| | | |
| | | /* control/status section */ |
| | | #define ISL1208_REG_SR 0x07 |
| | | #define ISL1208_REG_SR_ARST (1<<7) /* auto reset */ |
| | | #define ISL1208_REG_SR_XTOSCB (1<<6) /* crystal oscillator */ |
| | | #define ISL1208_REG_SR_WRTC (1<<4) /* write rtc */ |
| | | #define ISL1208_REG_SR_EVT (1<<3) /* event */ |
| | | #define ISL1208_REG_SR_ALM (1<<2) /* alarm */ |
| | | #define ISL1208_REG_SR_BAT (1<<1) /* battery */ |
| | | #define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */ |
| | | #define ISL1208_REG_INT 0x08 |
| | | #define ISL1208_REG_INT_ALME (1<<6) /* alarm enable */ |
| | | #define ISL1208_REG_INT_IM (1<<7) /* interrupt/alarm mode */ |
| | | #define ISL1219_REG_EV 0x09 |
| | | #define ISL1219_REG_EV_EVEN (1<<4) /* event detection enable */ |
| | | #define ISL1219_REG_EV_EVIENB (1<<7) /* event in pull-up disable */ |
| | | #define ISL1208_REG_ATR 0x0a |
| | | #define ISL1208_REG_DTR 0x0b |
| | | |
| | | /* alarm section */ |
| | | #define ISL1208_REG_SCA 0x0c |
| | | #define ISL1208_REG_MNA 0x0d |
| | | #define ISL1208_REG_HRA 0x0e |
| | | #define ISL1208_REG_DTA 0x0f |
| | | #define ISL1208_REG_MOA 0x10 |
| | | #define ISL1208_REG_DWA 0x11 |
| | | #define ISL1208_ALARM_SECTION_LEN 6 |
| | | |
| | | /* user section */ |
| | | #define ISL1208_REG_USR1 0x12 |
| | | #define ISL1208_REG_USR2 0x13 |
| | | #define ISL1208_USR_SECTION_LEN 2 |
| | | |
| | | #define ISL1208_REGS_MAX 0x13 |
| | | |
| | | const char *weekday[7]={"Sun.","Mon.","Tue.","Wed.","Thu.","Fri.","Sat." }; |
| | | |
| | | |
| | | /* |
| | | *+----------------------------------+ |
| | | *| ISL1208 Low level API | |
| | | *+----------------------------------+ |
| | | */ |
| | | |
| | | static int isl1208_i2c_read_regs(uint8_t regaddr, uint8_t *regs, uint8_t len) |
| | | { |
| | | uint8_t i; |
| | | int rv = 0; |
| | | uint8_t byte; |
| | | uint8_t ack; |
| | | |
| | | if( !regs || len<=0 ) |
| | | { |
| | | rtc_print("ISL1208: Invalid input arguments\r\n"); |
| | | return -1; |
| | | } |
| | | |
| | | I2C_StartCondition(); |
| | | |
| | | if( (rv=I2C_SendAddress(I2C_WR)) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Send chipset address[W] failure: rv=0x%02x\r\n", rv); |
| | | goto OUT; |
| | | } |
| | | |
| | | if( (rv=I2C_WriteByte(regaddr)) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Set register[0x%02x] failure: rv=0x%02x\r\n", regaddr, rv); |
| | | goto OUT; |
| | | } |
| | | |
| | | I2C_StartCondition(); |
| | | |
| | | if( (rv=I2C_SendAddress( I2C_RD )) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Send chipset address[W] failure: rv=0x%02x\r\n", rv); |
| | | goto OUT; |
| | | } |
| | | |
| | | for (i=0; i<len; i++) |
| | | { |
| | | if( i == (len-1) ) |
| | | ack = ACK_NONE; |
| | | else |
| | | ack = ACK; |
| | | |
| | | if( (rv=I2C_ReadByte(&byte, ack, I2C_CLK_STRETCH_TIMEOUT)) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Read register data failure: rv=0x%02x\r\n", rv); |
| | | goto OUT; |
| | | } |
| | | |
| | | regs[i] = byte; |
| | | } |
| | | |
| | | OUT: |
| | | I2C_StopCondition(); |
| | | return rv; |
| | | } |
| | | |
| | | static int isl1208_i2c_write_regs(uint8_t regaddr, uint8_t *regs, uint8_t len) |
| | | { |
| | | uint8_t i = 0; |
| | | int rv = 0; |
| | | |
| | | I2C_StartCondition(); |
| | | |
| | | if( (rv=I2C_SendAddress(I2C_WR)) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Send chipset address[W] failure: rv=0x%02x\r\n", rv); |
| | | goto OUT; |
| | | } |
| | | |
| | | if( (rv=I2C_WriteByte(regaddr)) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Set register[0x%02x] failure: rv=0x%02x\r\n", regaddr, rv); |
| | | goto OUT; |
| | | } |
| | | |
| | | for (i=0; i<len; i++) |
| | | { |
| | | if( (rv=I2C_WriteByte(regs[i])) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Write register data failure: rv=0x%02x\r\n", rv); |
| | | goto OUT; |
| | | } |
| | | } |
| | | |
| | | rv = 0; |
| | | |
| | | OUT: |
| | | I2C_StopCondition(); |
| | | return rv; |
| | | } |
| | | |
| | | |
| | | #define bcd2bin(x) (((x) & 0x0f) + ((x) >> 4) * 10) |
| | | #define bin2bcd(x) ((((x) / 10) << 4) + (x) % 10) |
| | | |
| | | int set_hwclock(hwclock_t tm) |
| | | { |
| | | int rv; |
| | | uint8_t regs[ISL1208_RTC_SECTION_LEN] = { 0, }; |
| | | uint8_t sr; |
| | | |
| | | regs[ISL1208_REG_SC] = bin2bcd(tm.tm_sec); |
| | | regs[ISL1208_REG_MN] = bin2bcd(tm.tm_min); |
| | | regs[ISL1208_REG_HR] = bin2bcd(tm.tm_hour) | ISL1208_REG_HR_MIL; |
| | | |
| | | regs[ISL1208_REG_DT] = bin2bcd(tm.tm_mday); |
| | | regs[ISL1208_REG_MO] = bin2bcd(tm.tm_mon); |
| | | regs[ISL1208_REG_YR] = bin2bcd(tm.tm_year - 2000); |
| | | //regs[ISL1208_REG_DW] = bin2bcd(tm.tm_wday & 7); |
| | | |
| | | if( i2c_lock(ISL1208_CHIPADDR) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Lock I2C bus failure!\r\n"); |
| | | return -2; |
| | | } |
| | | |
| | | rv = isl1208_i2c_read_regs(ISL1208_REG_SR, &sr, 1); |
| | | if( rv < 0 ) |
| | | { |
| | | rtc_print("ISL1208: read Status Register failure, rv=%d!\r\n", rv); |
| | | rv = -3; |
| | | goto OUT; |
| | | } |
| | | sr |= ISL1208_REG_SR_WRTC; |
| | | rv = isl1208_i2c_write_regs(ISL1208_REG_SR, &sr, 1); |
| | | if( rv < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Set Status Register WRTC failure, rv=%d!\r\n", rv); |
| | | rv = -4; |
| | | goto OUT; |
| | | } |
| | | |
| | | rv=isl1208_i2c_write_regs(ISL1208_REG_SC, regs, ISL1208_RTC_SECTION_LEN); |
| | | if(rv < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Set RTC section registeres failure, rv=%d!\r\n", rv); |
| | | rv = -5; |
| | | goto OUT; |
| | | } |
| | | |
| | | sr &= (~ISL1208_REG_SR_WRTC); |
| | | rv = isl1208_i2c_write_regs(ISL1208_REG_SR, &sr, 1); |
| | | if( rv < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Clear Status Register WRTC failure, rv=%d!\r\n", rv); |
| | | rv = -6; |
| | | goto OUT; |
| | | } |
| | | |
| | | OUT: |
| | | i2c_free(); |
| | | return rv; |
| | | } |
| | | |
| | | int get_hwclock(hwclock_t *tm) |
| | | { |
| | | int rv = 0; |
| | | uint8_t regs[REGS_RTC_SR_LEN] = { 0, }; |
| | | |
| | | if( !tm ) |
| | | { |
| | | rtc_print("ISL1208: Invalid input arugments!\r\n"); |
| | | return -1; |
| | | } |
| | | |
| | | if( i2c_lock(ISL1208_CHIPADDR) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Initial I2C bus failure!\r\n"); |
| | | return -2; |
| | | } |
| | | |
| | | rv = isl1208_i2c_read_regs(ISL1208_REG_SC, regs, REGS_RTC_SR_LEN); |
| | | if (rv < 0) |
| | | { |
| | | rtc_print("ISL1208: read RTC_SECTION and SR registeres failure, rv=%d!\r\n", rv); |
| | | rv = -3; |
| | | goto OUT; |
| | | } |
| | | |
| | | if( regs[ISL1208_REG_SR] & ISL1208_REG_SR_RTCF ) |
| | | { |
| | | rtc_print("ISL1208: Initialize RTC time after power failure!\r\n"); |
| | | rv = -4; |
| | | goto OUT; |
| | | } |
| | | |
| | | tm->tm_sec = bcd2bin(regs[ISL1208_REG_SC]); |
| | | tm->tm_min = bcd2bin(regs[ISL1208_REG_MN]); |
| | | |
| | | { |
| | | const uint8_t _hr = regs[ISL1208_REG_HR]; |
| | | |
| | | if (_hr & ISL1208_REG_HR_MIL) /* 24H */ |
| | | { |
| | | tm->tm_hour = bcd2bin(_hr & 0x3f); |
| | | } |
| | | else /* 12H */ |
| | | { |
| | | tm->tm_hour = bcd2bin(_hr & 0x1f); |
| | | if (_hr & ISL1208_REG_HR_PM) |
| | | tm->tm_hour += 12; |
| | | } |
| | | } |
| | | |
| | | tm->tm_mday = bcd2bin(regs[ISL1208_REG_DT]); |
| | | tm->tm_mon = bcd2bin(regs[ISL1208_REG_MO]); |
| | | tm->tm_year = bcd2bin(regs[ISL1208_REG_YR]) + 2000; |
| | | tm->tm_wday = bcd2bin(regs[ISL1208_REG_DW]); |
| | | |
| | | OUT: |
| | | i2c_free(); |
| | | return rv; |
| | | } |
| | | |
| | | int set_rtctime(char *time) |
| | | { |
| | | hwclock_t tm; |
| | | |
| | | if( 6 != sscanf(time, "%d-%d-%d %d:%d:%d", |
| | | &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec)) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | set_hwclock(tm); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int get_rtctime(char *buf, int size) |
| | | { |
| | | hwclock_t tm; |
| | | |
| | | memset(buf, 0, size); |
| | | |
| | | if( get_hwclock(&tm) < 0 ) |
| | | return -1; |
| | | |
| | | snprintf(buf, size, "%04d-%02d-%02d %02d:%02d:%02d\r\n", |
| | | tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | void print_rtctime(void) |
| | | { |
| | | hwclock_t tm; |
| | | |
| | | if( get_hwclock(&tm) < 0 ) |
| | | return ; |
| | | |
| | | printf("%04d-%02d-%02d %02d:%02d:%02d\r\n", |
| | | tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); |
| | | } |
New file |
| | |
| | | /********************************************************************** |
| | | * Copyright: (C)2024 LingYun IoT System Studio |
| | | * Author: GuoWenxue<guowenxue@gmail.com> |
| | | * |
| | | * Description: ISL1208 RTC driver on ISKBoard |
| | | * |
| | | * ChangeLog: |
| | | * Version Date Author Description |
| | | * V1.0.0 2024.08.29 GuoWenxue Release initial version |
| | | * |
| | | ***********************************************************************/ |
| | | |
| | | #ifndef __ISL1208_H_ |
| | | #define __ISL1208_H_ |
| | | |
| | | typedef struct hwclock_s |
| | | { |
| | | int tm_sec; /* [0 ~ 59/60 ] */ |
| | | int tm_min; /* [0 ~ 59 ] */ |
| | | int tm_hour; /* [0 ~ 23 ] */ |
| | | |
| | | int tm_mday; /* [1 ~ 31] */ |
| | | int tm_mon; /* [1 ~ 12] */ |
| | | int tm_year; /* [ 2000~2099 ] */ |
| | | |
| | | int tm_wday; /* [0 ~ 6 ] */ |
| | | } hwclock_t; |
| | | |
| | | extern const char *weekday[7]; |
| | | |
| | | /* Set RTC time by struct $tm */ |
| | | extern int set_hwclock(hwclock_t tm); |
| | | |
| | | /* Get RTC time by struct $tm */ |
| | | extern int get_hwclock(hwclock_t *tm); |
| | | |
| | | /* Set RTC time by time string format: "2024-08-29 12:08:08" */ |
| | | extern int set_rtctime(char *time); |
| | | |
| | | /* Get RTC time by time string format: "2024-08-29 12:08:08" */ |
| | | extern int get_rtctime(char *buf, int size); |
| | | |
| | | /* Print RTC time by time string format: "2024-08-29 12:08:08" */ |
| | | extern void print_rtctime(void); |
| | | |
| | | #endif /* __ISL1208_H_ */ |
New file |
| | |
| | | /********************************************************************** |
| | | * Copyright: (C)2024 LingYun IoT System Studio |
| | | * Author: GuoWenxue<guowenxue@gmail.com> |
| | | * |
| | | * Description: The purpose of this code is to provide a simple ring buffer |
| | | * C library for ISKBoard. |
| | | * |
| | | * ChangeLog: |
| | | * Version Date Author Description |
| | | * V1.0.0 2024.08.29 GuoWenxue Release initial version |
| | | * |
| | | ***********************************************************************/ |
| | | |
| | | #include <string.h> |
| | | #include "ringbuf.h" |
| | | |
| | | void rb_init (ring_buffer_t *rb, uint8_t *buf, int size) |
| | | { |
| | | rb->rd_pointer = 0; |
| | | rb->wr_pointer = 0; |
| | | |
| | | rb->buffer= buf; |
| | | rb->size = size; |
| | | memset(buf, 0, size); |
| | | |
| | | } |
| | | |
| | | void rb_clear (ring_buffer_t *rb) |
| | | { |
| | | rb->rd_pointer=0; |
| | | rb->wr_pointer=0; |
| | | |
| | | memset(rb->buffer,0,rb->size); |
| | | } |
| | | |
| | | int rb_data_size (ring_buffer_t *rb) |
| | | { |
| | | return ((rb->wr_pointer - rb->rd_pointer) & (rb->size-1)); |
| | | } |
| | | |
| | | int rb_free_size (ring_buffer_t *rb) |
| | | { |
| | | return (rb->size - 1 - rb_data_size(rb)); |
| | | } |
| | | |
| | | int rb_write (ring_buffer_t *rb, uint8_t *buf, int len) |
| | | { |
| | | int total; |
| | | int i; |
| | | |
| | | /* total = len = min(space, len) */ |
| | | total = rb_free_size(rb); |
| | | if(len > total) |
| | | len = total; |
| | | else |
| | | total = len; |
| | | |
| | | i = rb->wr_pointer; |
| | | if(i + len > rb->size) |
| | | { |
| | | memcpy(rb->buffer + i, buf, rb->size - i); |
| | | buf += rb->size - i; |
| | | len -= rb->size - i; |
| | | i = 0; |
| | | } |
| | | memcpy(rb->buffer + i, buf, len); |
| | | rb->wr_pointer = i + len; |
| | | return total; |
| | | } |
| | | |
| | | int rb_read (ring_buffer_t *rb, uint8_t *buf, int max) |
| | | { |
| | | int total; |
| | | int i; |
| | | /* total = len = min(used, len) */ |
| | | total = rb_data_size(rb); |
| | | |
| | | if(max > total) |
| | | max = total; |
| | | else |
| | | total = max; |
| | | |
| | | i = rb->rd_pointer; |
| | | if(i + max > rb->size) |
| | | { |
| | | memcpy(buf, rb->buffer + i, rb->size - i); |
| | | buf += rb->size - i; |
| | | max -= rb->size - i; |
| | | i = 0; |
| | | } |
| | | memcpy(buf, rb->buffer + i, max); |
| | | rb->rd_pointer = i + max; |
| | | |
| | | return total; |
| | | } |
| | | |
| | | uint8_t rb_peek(ring_buffer_t *rb, int index) |
| | | { |
| | | assert(index < rb_data_size(rb)); |
| | | |
| | | return rb->buffer[((rb->rd_pointer + index) % rb->size)]; |
| | | } |
New file |
| | |
| | | /********************************************************************** |
| | | * Copyright: (C)2024 LingYun IoT System Studio |
| | | * Author: GuoWenxue<guowenxue@gmail.com> |
| | | * |
| | | * Description: The purpose of this code is to provide a simple ring buffer |
| | | * C library for ISKBoard. |
| | | * |
| | | * ChangeLog: |
| | | * Version Date Author Description |
| | | * V1.0.0 2024.08.29 GuoWenxue Release initial version |
| | | * |
| | | ***********************************************************************/ |
| | | |
| | | |
| | | #ifndef __RINGBUF_H_ |
| | | #define __RINGBUF_H_ |
| | | |
| | | #include <sys/types.h> |
| | | #include <stdint.h> |
| | | |
| | | typedef struct ring_buffer_s { |
| | | uint8_t *buffer; |
| | | int wr_pointer; |
| | | int rd_pointer; |
| | | int size; |
| | | } ring_buffer_t; |
| | | |
| | | /* Initial the ring buffer */ |
| | | void rb_init (ring_buffer_t *ring, uint8_t *buf, int size) ; |
| | | |
| | | /* Clear the ring buffer data */ |
| | | void rb_clear (ring_buffer_t *rb) ; |
| | | |
| | | /* Get ring buffer left free size */ |
| | | int rb_free_size (ring_buffer_t *rb); |
| | | |
| | | /* Get data size in the ring buffer */ |
| | | int rb_data_size (ring_buffer_t *rb); |
| | | |
| | | /* Read $max bytes data from ring buffer $rb to $buf */ |
| | | int rb_read (ring_buffer_t *rb, uint8_t *buf, int max); |
| | | |
| | | /* Description: Write $len bytes data in $buf into ring buffer $rb |
| | | * Return Value: The actual written into ring buffer data size, if ring buffer |
| | | * left space size small than $len, then only part of the data be written into. |
| | | */ |
| | | int rb_write (ring_buffer_t *rb, uint8_t *buf, int len) ; |
| | | |
| | | /* Read a specify $index byte data in ring buffer $rb */ |
| | | uint8_t rb_peek(ring_buffer_t *rb, int index); |
| | | |
| | | #endif /* __RINGBUF_H_ */ |
| | |
| | | #include "i2c_bitbang.h" |
| | | #include "sht20.h" |
| | | |
| | | #define CONFIG_DEBUG_SHT2X /* Enable SHT20 debug */ |
| | | //#define CONFIG_DEBUG_SHT2X /* Enable SHT20 debug */ |
| | | |
| | | #define SHT2X_CHIPADDR 0x40 /* SHT20 7-Bits Chip address */ |
| | | |
New file |
| | |
| | | /********************************************************************** |
| | | * Copyright: (C)2024 LingYun IoT System Studio |
| | | * Author: GuoWenxue<guowenxue@gmail.com> |
| | | * |
| | | * Description: WS2812B strip lights driver on ISKBoard |
| | | * |
| | | * Reference : https://blog.csdn.net/Lennon8_8/article/details/108980808 |
| | | * |
| | | * ChangeLog: |
| | | * Version Date Author Description |
| | | * V1.0.0 2024.08.29 GuoWenxue Release initial version |
| | | * |
| | | ***********************************************************************/ |
| | | #include <string.h> |
| | | #include <stdint.h> |
| | | #include "ws2812b.h" |
| | | #include "miscdev.h" |
| | | |
| | | #ifdef USE_FREERTOS |
| | | #include "cmsis_os.h" |
| | | #define mdelay(ms) osDelay(ms) |
| | | #else |
| | | #define mdelay(ms) HAL_Delay(ms) |
| | | #endif |
| | | |
| | | static color_t g_ColorPanel[WS2812_NUM]; |
| | | |
| | | typedef struct ws_gpio_s |
| | | { |
| | | GPIO_TypeDef *group; |
| | | uint16_t pin; |
| | | } ws_gpio_t; |
| | | |
| | | static ws_gpio_t ws_gpio = /* IO pin connected to PC2 */ |
| | | { |
| | | .group = GPIOC, |
| | | .pin = GPIO_PIN_2, |
| | | }; |
| | | |
| | | #define ws_setpin(x) if(!x) ws_gpio.group->BRR=ws_gpio.pin; else ws_gpio.group->BSRR=ws_gpio.pin |
| | | |
| | | /* |
| | | * 1 NOP = 1/80MHz = 12.5ns => 200ns = 8 * NOP |
| | | * |
| | | * */ |
| | | #define delay_200ns() { __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP(); \ |
| | | __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP(); } |
| | | |
| | | void ws2812b_init(void) |
| | | { |
| | | GPIO_InitTypeDef GPIO_InitStruct = {0}; |
| | | |
| | | GPIO_InitStruct.Pin = ws_gpio.pin; |
| | | GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; |
| | | GPIO_InitStruct.Pull = GPIO_NOPULL; |
| | | GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; |
| | | |
| | | HAL_GPIO_Init(ws_gpio.group, &GPIO_InitStruct); |
| | | |
| | | ws2812b_reset(); |
| | | |
| | | return ; |
| | | } |
| | | |
| | | static inline void ws2812b_WriteByte(uint8_t byte) |
| | | { |
| | | int i; |
| | | |
| | | for(i=0; i<8; i++) |
| | | { |
| | | if(byte & 0x80) |
| | | { |
| | | /* Sequence Send 1: T1H: 580ns~1.6us T1L: 200ns~420ns: |
| | | * |
| | | * +--------------+ + |
| | | * |<- T1H ->|<- T1L ->| |
| | | * | +---------+ |
| | | */ |
| | | ws_setpin(1); // T1H |
| | | udelay(1); |
| | | |
| | | ws_setpin(0); // T1L |
| | | delay_200ns(); //200ns test okay |
| | | } |
| | | else |
| | | { |
| | | /* Sequence Send 0: T0H: 220ns~380ns T0L: 580ns~1.6ns: |
| | | * |
| | | * +---------+ + |
| | | * |<- T0H ->|<- T0L ->| |
| | | * | +--------------+ |
| | | */ |
| | | ws_setpin(1); // T0H |
| | | delay_200ns(); //200ns test okay |
| | | |
| | | ws_setpin(0); // T0L |
| | | udelay(1); |
| | | } |
| | | |
| | | byte<<=1; |
| | | } |
| | | |
| | | return ; |
| | | } |
| | | |
| | | void ws2812b_reset(void) |
| | | { |
| | | /* Sequence Reset: Treset >280us: |
| | | * |
| | | * + + |
| | | * |<- Treset ->| |
| | | * +--------------+ |
| | | */ |
| | | ws_setpin(0); |
| | | udelay(300); |
| | | |
| | | memset(g_ColorPanel, 0, sizeof(g_ColorPanel)); |
| | | |
| | | return ; |
| | | } |
| | | |
| | | static inline void ws2812b_SendColor(color_t color) |
| | | { |
| | | ws2812b_WriteByte(color.pixel.green); |
| | | ws2812b_WriteByte(color.pixel.red); |
| | | ws2812b_WriteByte(color.pixel.blue); |
| | | |
| | | return ; |
| | | } |
| | | |
| | | static inline void ws2812b_Refresh(void) |
| | | { |
| | | int i; |
| | | |
| | | for(i=0; i<WS2812_NUM; i++) |
| | | { |
| | | ws2812b_SendColor(g_ColorPanel[i]); |
| | | } |
| | | |
| | | return ; |
| | | } |
| | | |
| | | static inline int ws2812b_SetPixel(int which, color_t color) |
| | | { |
| | | if( which<0 || which>=WS2812_NUM ) |
| | | return -1; |
| | | |
| | | g_ColorPanel[which].pixel.red = color.pixel.red; |
| | | g_ColorPanel[which].pixel.green = color.pixel.green; |
| | | g_ColorPanel[which].pixel.blue = color.pixel.blue; |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int ws2812b_turn(int which, color_t color) |
| | | { |
| | | if( which<0 || which>=WS2812_NUM ) |
| | | return -1; |
| | | |
| | | ws2812b_SetPixel(which, color); |
| | | ws2812b_Refresh(); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | void ws2812b_blink(void) |
| | | { |
| | | int c, i; |
| | | color_t colors[4] = {[0].data=PIXEL_B, [1].data=PIXEL_R, [2].data=PIXEL_G, [3].data=PIXEL_W}; |
| | | color_t color_off = {.data = PIXEL_OFF}; |
| | | |
| | | for(c=0; c<4; c++) /* color */ |
| | | { |
| | | for(i=0; i<WS2812_NUM; i++) |
| | | { |
| | | ws2812b_turn(i, colors[c]); |
| | | mdelay(200); |
| | | ws2812b_turn(i, (color_t)color_off); |
| | | mdelay(200); |
| | | } |
| | | } |
| | | } |
| | | |
New file |
| | |
| | | /********************************************************************** |
| | | * Copyright: (C)2024 LingYun IoT System Studio |
| | | * Author: GuoWenxue<guowenxue@gmail.com> |
| | | * |
| | | * Description: WS2812B strip lights driver on ISKBoard |
| | | * |
| | | * Reference : https://blog.csdn.net/Lennon8_8/article/details/108980808 |
| | | * |
| | | * ChangeLog: |
| | | * Version Date Author Description |
| | | * V1.0.0 2024.08.29 GuoWenxue Release initial version |
| | | * |
| | | ***********************************************************************/ |
| | | |
| | | #ifndef __WS2812B_H_ |
| | | #define __WS2812B_H_ |
| | | |
| | | #include "stm32l4xx_hal.h" |
| | | |
| | | #define WS2812_NUM 3 |
| | | |
| | | #define PIXEL_OFF 0 /* off */ |
| | | #define PIXEL_B (0xFF<<0) /* blue */ |
| | | #define PIXEL_R (0xFF<<8) /* red */ |
| | | #define PIXEL_G (0xFF<<16) /* green */ |
| | | #define PIXEL_W (0xFFFFFF) /* white */ |
| | | |
| | | /* Composition of 24 bits data: GRB888 |
| | | * +----+----+----+----+----+----+----+----+----+ |
| | | * | G7 | ...| G0 | R7 | ...| R0 | B7 | ...| B0 | |
| | | * +----+----+----+----+----+----+----+----+----+ |
| | | */ |
| | | typedef struct pixel_s |
| | | { |
| | | uint32_t blue:8; |
| | | uint32_t red:8; |
| | | uint32_t green:8; |
| | | } pixel_t; |
| | | |
| | | typedef union color_u |
| | | { |
| | | pixel_t pixel; |
| | | uint32_t data; |
| | | } color_t; |
| | | |
| | | |
| | | extern void ws2812b_init(void); |
| | | |
| | | extern void ws2812b_reset(void); |
| | | |
| | | extern int ws2812b_turn(int which, color_t color); |
| | | |
| | | extern void ws2812b_blink(void); |
| | | |
| | | #endif /* __WS2812B_H_ */ |
| | |
| | | |
| | | /* Private includes ----------------------------------------------------------*/ |
| | | /* USER CODE BEGIN Includes */ |
| | | #include <string.h> |
| | | #include "miscdev.h" |
| | | #include "sht20.h" |
| | | #include "w25q.h" |
| | | #include "core_json.h" |
| | | /* USER CODE END Includes */ |
| | | |
| | | /* Private typedef -----------------------------------------------------------*/ |
| | |
| | | |
| | | /* Private user code ---------------------------------------------------------*/ |
| | | /* USER CODE BEGIN 0 */ |
| | | uint8_t g_rxch; /* HAL_UART_Receive_IT() receive 1 byte buffer */ |
| | | uint8_t g_rxbuf[256]; /* UART1 receive data buffer */ |
| | | uint8_t g_rxbytes; /* UART1 receive data bytes */ |
| | | uint32_t g_rxTick; |
| | | |
| | | void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) |
| | | { |
| | | if( huart == &huart1 ) |
| | | { |
| | | /* clear buffer if it's full */ |
| | | if( g_rxbytes >= sizeof(g_rxbuf) ) |
| | | g_rxbytes = 0; |
| | | |
| | | /* Receive a byte data by interrupter */ |
| | | HAL_UART_Receive_IT(huart, &g_rxch, 1); |
| | | |
| | | /* Put this data into receive buffer */ |
| | | g_rxbuf[g_rxbytes++] = g_rxch; |
| | | |
| | | /* Get current receive data tick */ |
| | | g_rxTick = HAL_GetTick(); |
| | | } |
| | | } |
| | | |
| | | /* parser and control the Leds and relay */ |
| | | int parser_command(char *json_str) |
| | | { |
| | | const char *fields[] = {"RedLed", "GreenLed", "BlueLed", "Relay"}; |
| | | char *value; |
| | | size_t value_len = 0; |
| | | int status; |
| | | |
| | | if (JSON_Validate(json_str, strlen(json_str) ) != JSONSuccess) |
| | | { |
| | | printf("Invalid JSON: %s\r\n", json_str); |
| | | return -1; |
| | | } |
| | | |
| | | for (int i = 0; i < sizeof(fields) / sizeof(fields[0]); i++) |
| | | { |
| | | if (JSONSuccess == JSON_Search(json_str, strlen(json_str), fields[i], strlen(fields[i]), &value, &value_len)) |
| | | { |
| | | if ( !strncasecmp(value, "On", value_len) ) |
| | | status = 1; |
| | | |
| | | else if ( !strncasecmp(value, "Off", value_len) ) |
| | | status = 0; |
| | | |
| | | if ( !strcmp(fields[i], "RedLed") ) { |
| | | turn_led(Led_R, status); |
| | | } else if ( !strcmp(fields[i], "GreenLed") ) { |
| | | turn_led(Led_G, status); |
| | | } else if ( !strcmp(fields[i], "BlueLed") ) { |
| | | turn_led(Led_B, status); |
| | | } else if ( !strcmp(fields[i], "Relay") ) { |
| | | turn_relay(Relay1, status); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | /* USER CODE END 0 */ |
| | | |
| | | /** |
| | |
| | | /* USER CODE BEGIN 1 */ |
| | | int rv; |
| | | float temperature, humidity; |
| | | char pack_buf[128]; |
| | | /* USER CODE END 1 */ |
| | | |
| | | /* MCU Configuration--------------------------------------------------------*/ |
| | |
| | | /* USER CODE BEGIN 2 */ |
| | | beep_start(2, 300); |
| | | |
| | | spinor_test(); |
| | | /* Must Enable UART3 receive interrupter here */ |
| | | HAL_UART_Receive_IT(&huart1 , &g_rxch, 1); |
| | | |
| | | do {} while(1); |
| | | printf("Remote monitor and control program\r\n"); |
| | | /* USER CODE END 2 */ |
| | | |
| | | /* Infinite loop */ |
| | | /* USER CODE BEGIN WHILE */ |
| | | while (1) |
| | | { |
| | | const uint32_t sample_interval = 5000; /* sample interval time 5000ms */ |
| | | static uint32_t last_tick = 0; /* last sample time */ |
| | | |
| | | /* check sample time arrive and start report */ |
| | | if ((HAL_GetTick() - last_tick) >= sample_interval) |
| | | { |
| | | rv = sht20_sample_TrH(&temperature, &humidity); |
| | | if( rv ) |
| | | if( !rv ) |
| | | { |
| | | printf("ERROR: SHT20 sample data failure, rv=%d\r\n", rv); |
| | | last_tick = HAL_GetTick(); |
| | | |
| | | memset(pack_buf, 0, sizeof(pack_buf)); |
| | | snprintf(pack_buf, sizeof(pack_buf), "{\"Temperature\":\"%.02f\", \"Humidity\":\"%.02f\"}\r\n", temperature, humidity); |
| | | HAL_UART_Transmit(&huart1, (uint8_t *)pack_buf, strlen(pack_buf), 0xFFFF); |
| | | } |
| | | else |
| | | } |
| | | |
| | | /* UART receive data and receive over */ |
| | | if( g_rxbytes > 0 && (HAL_GetTick()-g_rxTick)>=2) |
| | | { |
| | | printf("SHT20 sample temperature: %.2f relative humidity: %.1f%%\r\n", temperature, humidity); |
| | | parser_command((char *)g_rxbuf); |
| | | memset(g_rxbuf, 0, sizeof(g_rxbuf)); |
| | | g_rxbytes = 0; |
| | | } |
| | | HAL_Delay(1000); |
| | | |
| | | /* USER CODE END WHILE */ |
| | | |
| | |
| | | /* USER CODE END 0 */ |
| | | |
| | | /* External variables --------------------------------------------------------*/ |
| | | |
| | | extern UART_HandleTypeDef huart1; |
| | | /* USER CODE BEGIN EV */ |
| | | |
| | | /* USER CODE END EV */ |
| | |
| | | /******************************************************************************/ |
| | | |
| | | /** |
| | | * @brief This function handles USART1 global interrupt. |
| | | */ |
| | | void USART1_IRQHandler(void) |
| | | { |
| | | /* USER CODE BEGIN USART1_IRQn 0 */ |
| | | |
| | | /* USER CODE END USART1_IRQn 0 */ |
| | | HAL_UART_IRQHandler(&huart1); |
| | | /* USER CODE BEGIN USART1_IRQn 1 */ |
| | | |
| | | /* USER CODE END USART1_IRQn 1 */ |
| | | } |
| | | |
| | | /** |
| | | * @brief This function handles EXTI line[15:10] interrupts. |
| | | */ |
| | | void EXTI15_10_IRQHandler(void) |
| | |
| | | GPIO_InitStruct.Alternate = GPIO_AF7_USART1; |
| | | HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); |
| | | |
| | | /* USART1 interrupt Init */ |
| | | HAL_NVIC_SetPriority(USART1_IRQn, 0, 0); |
| | | HAL_NVIC_EnableIRQ(USART1_IRQn); |
| | | /* USER CODE BEGIN USART1_MspInit 1 */ |
| | | |
| | | /* USER CODE END USART1_MspInit 1 */ |
| | |
| | | */ |
| | | HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10); |
| | | |
| | | /* USART1 interrupt Deinit */ |
| | | HAL_NVIC_DisableIRQ(USART1_IRQn); |
| | | /* USER CODE BEGIN USART1_MspDeInit 1 */ |
| | | |
| | | /* USER CODE END USART1_MspDeInit 1 */ |
| | |
| | | NVIC.PriorityGroup=NVIC_PRIORITYGROUP_4 |
| | | NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false |
| | | NVIC.SysTick_IRQn=true\:1\:0\:true\:false\:true\:false\:true\:false |
| | | NVIC.USART1_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true |
| | | NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false |
| | | PA10.Mode=Asynchronous |
| | | PA10.Signal=USART1_RX |
| | |
| | | ProjectManager.UAScriptAfterPath= |
| | | ProjectManager.UAScriptBeforePath= |
| | | ProjectManager.UnderRoot=true |
| | | ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_USART1_UART_Init-USART1-false-HAL-true,4-MX_ADC1_Init-ADC1-false-HAL-true,5-MX_TIM6_Init-TIM6-false-HAL-true,6-MX_TIM1_Init-TIM1-false-HAL-true |
| | | ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_USART1_UART_Init-USART1-false-HAL-true,4-MX_ADC1_Init-ADC1-false-HAL-true,5-MX_TIM6_Init-TIM6-false-HAL-true,6-MX_TIM1_Init-TIM1-false-HAL-true,7-MX_SPI1_Init-SPI1-false-HAL-true |
| | | RCC.ADCFreq_Value=12000000 |
| | | RCC.AHBFreq_Value=80000000 |
| | | RCC.APB1Freq_Value=80000000 |