From 6c7b6c910be1dcdc0bb786e02be648b1a56faa5e Mon Sep 17 00:00:00 2001
From: guowenxue <guowenxue@gmail.com>
Date: Thu, 09 Oct 2025 00:50:02 +0800
Subject: [PATCH] 添加串口远程控制项目代码

---
 Core/Src/board/isl1208.c     |  343 +++++
 ISKBoard.ioc                 |    3 
 Core/Src/board/ws2812b.h     |   55 
 Core/Src/board/ringbuf.c     |  102 +
 Core/Src/board/ws2812b.c     |  183 ++
 Core/Src/main.c              |  103 +
 Core/Src/board/ringbuf.h     |   52 
 Core/Src/board/i2c_bitbang.c |    4 
 Core/Src/board/hal_oled.h    |   52 
 Core/Src/board/hal_oled.c    |  280 ++++
 Core/Inc/stm32l4xx_it.h      |    1 
 Core/Src/board/font_oled.h   |  334 +++++
 Core/Src/usart.c             |    5 
 Core/Src/board/sht20.c       |    2 
 Core/Src/board/core_json.h   |  349 +++++
 Core/Src/stm32l4xx_it.c      |   16 
 Core/Src/board/isl1208.h     |   46 
 Core/Src/board/core_json.c   | 1853 ++++++++++++++++++++++++++++
 18 files changed, 3,766 insertions(+), 17 deletions(-)

diff --git a/Core/Inc/stm32l4xx_it.h b/Core/Inc/stm32l4xx_it.h
index cfba83e..953c972 100644
--- a/Core/Inc/stm32l4xx_it.h
+++ b/Core/Inc/stm32l4xx_it.h
@@ -55,6 +55,7 @@
 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 */
 
diff --git a/Core/Src/board/core_json.c b/Core/Src/board/core_json.c
new file mode 100644
index 0000000..b6d4d99
--- /dev/null
+++ b/Core/Src/board/core_json.c
@@ -0,0 +1,1853 @@
+/*
+ * 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;
+}
diff --git a/Core/Src/board/core_json.h b/Core/Src/board/core_json.h
new file mode 100644
index 0000000..6590ddf
--- /dev/null
+++ b/Core/Src/board/core_json.h
@@ -0,0 +1,349 @@
+/*
+ * 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_ */
diff --git a/Core/Src/board/font_oled.h b/Core/Src/board/font_oled.h
new file mode 100644
index 0000000..144b348
--- /dev/null
+++ b/Core/Src/board/font_oled.h
@@ -0,0 +1,334 @@
+/**********************************************************************
+*   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
diff --git a/Core/Src/board/hal_oled.c b/Core/Src/board/hal_oled.c
new file mode 100644
index 0000000..8f666d0
--- /dev/null
+++ b/Core/Src/board/hal_oled.c
@@ -0,0 +1,280 @@
+/**********************************************************************
+*   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);
+        }
+    }
+}
diff --git a/Core/Src/board/hal_oled.h b/Core/Src/board/hal_oled.h
new file mode 100644
index 0000000..274260e
--- /dev/null
+++ b/Core/Src/board/hal_oled.h
@@ -0,0 +1,52 @@
+/**********************************************************************
+*   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_ */
diff --git a/Core/Src/board/i2c_bitbang.c b/Core/Src/board/i2c_bitbang.c
index b2040cd..769eec6 100644
--- a/Core/Src/board/i2c_bitbang.c
+++ b/Core/Src/board/i2c_bitbang.c
@@ -21,7 +21,7 @@
  *+---------------------------------+
  */
 
-#define mdelay(x)       HAL_Delay(x)
+#define mdelay(ms) HAL_Delay(ms)
 
 enum
 {
@@ -32,7 +32,7 @@
 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)
diff --git a/Core/Src/board/isl1208.c b/Core/Src/board/isl1208.c
new file mode 100644
index 0000000..054a92a
--- /dev/null
+++ b/Core/Src/board/isl1208.c
@@ -0,0 +1,343 @@
+/**********************************************************************
+ *   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);
+}
diff --git a/Core/Src/board/isl1208.h b/Core/Src/board/isl1208.h
new file mode 100644
index 0000000..2fe532e
--- /dev/null
+++ b/Core/Src/board/isl1208.h
@@ -0,0 +1,46 @@
+/**********************************************************************
+ *   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_ */
diff --git a/Core/Src/board/ringbuf.c b/Core/Src/board/ringbuf.c
new file mode 100644
index 0000000..46fe52e
--- /dev/null
+++ b/Core/Src/board/ringbuf.c
@@ -0,0 +1,102 @@
+/**********************************************************************
+ *   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)];
+}
diff --git a/Core/Src/board/ringbuf.h b/Core/Src/board/ringbuf.h
new file mode 100644
index 0000000..faef295
--- /dev/null
+++ b/Core/Src/board/ringbuf.h
@@ -0,0 +1,52 @@
+/**********************************************************************
+ *   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_ */
diff --git a/Core/Src/board/sht20.c b/Core/Src/board/sht20.c
index 92f9780..7e618da 100644
--- a/Core/Src/board/sht20.c
+++ b/Core/Src/board/sht20.c
@@ -14,7 +14,7 @@
 #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 */
 
diff --git a/Core/Src/board/ws2812b.c b/Core/Src/board/ws2812b.c
new file mode 100644
index 0000000..074992a
--- /dev/null
+++ b/Core/Src/board/ws2812b.c
@@ -0,0 +1,183 @@
+/**********************************************************************
+ *   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);
+        }
+    }
+}
+
diff --git a/Core/Src/board/ws2812b.h b/Core/Src/board/ws2812b.h
new file mode 100644
index 0000000..2f48bb5
--- /dev/null
+++ b/Core/Src/board/ws2812b.h
@@ -0,0 +1,55 @@
+/**********************************************************************
+ *   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_ */
diff --git a/Core/Src/main.c b/Core/Src/main.c
index 3344439..91e9187 100644
--- a/Core/Src/main.c
+++ b/Core/Src/main.c
@@ -26,9 +26,11 @@
 
 /* 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 -----------------------------------------------------------*/
@@ -60,7 +62,68 @@
 
 /* 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 */
 
 /**
@@ -73,6 +136,7 @@
   /* USER CODE BEGIN 1 */
   int      rv;
   float    temperature, humidity;
+  char     pack_buf[128];
   /* USER CODE END 1 */
 
   /* MCU Configuration--------------------------------------------------------*/
@@ -101,25 +165,40 @@
   /* 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)
   {
-	rv = sht20_sample_TrH(&temperature, &humidity);
-	if( rv )
-	{
-		printf("ERROR: SHT20 sample data failure, rv=%d\r\n", rv);
-	}
-	else
-	{
-		printf("SHT20 sample temperature: %.2f relative humidity: %.1f%%\r\n", temperature, humidity);
-	}
-	HAL_Delay(1000);
+      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 )
+            {
+                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);
+            }
+      }
+
+      /* UART receive data and receive over */
+      if( g_rxbytes > 0 && (HAL_GetTick()-g_rxTick)>=2)
+      {
+          parser_command((char *)g_rxbuf);
+          memset(g_rxbuf, 0, sizeof(g_rxbuf));
+          g_rxbytes = 0;
+      }
 
     /* USER CODE END WHILE */
 
diff --git a/Core/Src/stm32l4xx_it.c b/Core/Src/stm32l4xx_it.c
index d34011e..594f31c 100644
--- a/Core/Src/stm32l4xx_it.c
+++ b/Core/Src/stm32l4xx_it.c
@@ -55,7 +55,7 @@
 /* USER CODE END 0 */
 
 /* External variables --------------------------------------------------------*/
-
+extern UART_HandleTypeDef huart1;
 /* USER CODE BEGIN EV */
 
 /* USER CODE END EV */
@@ -199,6 +199,20 @@
 /******************************************************************************/
 
 /**
+  * @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)
diff --git a/Core/Src/usart.c b/Core/Src/usart.c
index 26436d2..e1b680d 100644
--- a/Core/Src/usart.c
+++ b/Core/Src/usart.c
@@ -93,6 +93,9 @@
     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 */
@@ -116,6 +119,8 @@
     */
     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 */
diff --git a/ISKBoard.ioc b/ISKBoard.ioc
index 02d3eb4..3d6ca2e 100644
--- a/ISKBoard.ioc
+++ b/ISKBoard.ioc
@@ -68,6 +68,7 @@
 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
@@ -156,7 +157,7 @@
 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

--
Gitblit v1.9.1