BG96 Cat.M1 GPS sample for WIZnet IoT Shield

Files at this revision

API Documentation at this revision

Comitter:
hkjung
Date:
Wed Mar 20 05:33:37 2019 +0000
Commit message:
Initial Release

Changed in this revision

.gitignore Show annotated file Show diff for this revision Revisions of this file
CONTRIBUTING.md Show annotated file Show diff for this revision Revisions of this file
README.md Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-os.lib Show annotated file Show diff for this revision Revisions of this file
mbed_app.json Show annotated file Show diff for this revision Revisions of this file
stats_report.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.gitignore	Wed Mar 20 05:33:37 2019 +0000
@@ -0,0 +1,4 @@
+.build
+.mbed
+projectfiles
+*.py*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CONTRIBUTING.md	Wed Mar 20 05:33:37 2019 +0000
@@ -0,0 +1,5 @@
+# Contributing to Mbed OS
+
+Mbed OS is an open-source, device software platform for the Internet of Things. Contributions are an important part of the platform, and our goal is to make it as simple as possible to become a contributor.
+
+To encourage productive collaboration, as well as robust, consistent and maintainable code, we have a set of guidelines for [contributing to Mbed OS](https://os.mbed.com/docs/mbed-os/latest/contributing/index.html).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md	Wed Mar 20 05:33:37 2019 +0000
@@ -0,0 +1,171 @@
+# Getting started example for Mbed OS
+
+This guide reviews the steps required to get Blinky with the addition of dynamic OS statistics working on an Mbed OS platform. (Note: To see a rendered example you can import into the Arm Online Compiler, please see our [quick start](https://os.mbed.com/docs/mbed-os/latest/quick-start/online-with-the-online-compiler.html#importing-the-code).)
+
+Please install [Mbed CLI](https://github.com/ARMmbed/mbed-cli#installing-mbed-cli).
+
+## Import the example application
+
+From the command-line, import the example:
+
+```
+mbed import mbed-os-example-blinky
+cd mbed-os-example-blinky
+```
+
+### Now compile
+
+Invoke `mbed compile`, and specify the name of your platform and your favorite toolchain (`GCC_ARM`, `ARM`, `IAR`). For example, for the ARM Compiler 5:
+
+```
+mbed compile -m K64F -t ARM
+```
+
+Your PC may take a few minutes to compile your code. At the end, you see the following result:
+
+```
+[snip]
++----------------------------+-------+-------+------+
+| Module             |     .text |    .data |     .bss |
+|--------------------|-----------|----------|----------|
+| [fill]             |    98(+0) |    0(+0) | 2211(+0) |
+| [lib]/c.a          | 27835(+0) | 2472(+0) |   89(+0) |
+| [lib]/gcc.a        |  3168(+0) |    0(+0) |    0(+0) |
+| [lib]/misc         |   248(+0) |    8(+0) |   28(+0) |
+| [lib]/nosys.a      |    32(+0) |    0(+0) |    0(+0) |
+| main.o             |   924(+0) |    0(+0) |   12(+0) |
+| mbed-os/components |   134(+0) |    0(+0) |    0(+0) |
+| mbed-os/drivers    |    56(+0) |    0(+0) |    0(+0) |
+| mbed-os/features   |    42(+0) |    0(+0) |  184(+0) |
+| mbed-os/hal        |  2087(+0) |    8(+0) |  152(+0) |
+| mbed-os/platform   |  3633(+0) |  260(+0) |  209(+0) |
+| mbed-os/rtos       |  9370(+0) |  168(+0) | 6053(+0) |
+| mbed-os/targets    |  9536(+0) |   12(+0) |  382(+0) |
+| Subtotals          | 57163(+0) | 2928(+0) | 9320(+0) |
+Total Static RAM memory (data + bss): 12248(+0) bytes
+Total Flash memory (text + data): 60091(+0) bytes
+
+Image: ./BUILD/K64F/GCC_ARM/mbed-os-example-blinky.bin
+```
+
+### Program your board
+
+1. Connect your Mbed device to the computer over USB.
+1. Copy the binary file to the Mbed device.
+1. Press the reset button to start the program.
+
+The LED on your platform turns on and off. The main thread will additionally take a snapshot of the device's runtime statistics and display it over serial to your PC. The snapshot includes:
+
+* System Information:
+    * Mbed OS Version: Will currently default to 999999
+    * Compiler ID
+        * ARM = 1
+        * GCC_ARM = 2
+        * IAR = 3
+    * [CPUID Register Information](#cpuid-register-information)
+    * [Compiler Version](#compiler-version)
+* CPU Statistics
+    * Percentage of runtime that the device has spent awake versus in sleep
+* Heap Statistics
+    * Current heap size
+    * Max heap size which refers to the largest the heap has grown to
+* Thread Statistics
+    * Provides information on all running threads in the OS including
+        * Thread ID
+        * Thread Name
+        * Thread State
+        * Thread Priority
+        * Thread Stack Size
+        * Thread Stack Space
+
+#### Compiler Version
+
+| Compiler | Version Layout |
+| -------- | -------------- |
+| ARM      | PVVbbbb (P = Major; VV = Minor; bbbb = build number) |
+| GCC      | VVRRPP  (VV = Version; RR = Revision; PP = Patch)    |
+| IAR      | VRRRPPP (V = Version; RRR = Revision; PPP = Patch)   |
+
+#### CPUID Register Information
+
+| Bit Field | Field Description | Values |
+| --------- | ----------------- | ------ |
+|[31:24]    | Implementer       | 0x41 = ARM |
+|[23:20]    | Variant           | Major revision 0x0  =  Revision 0 |
+|[19:16]    | Architecture      | 0xC  = Baseline Architecture |
+|           |                   | 0xF  = Constant (Mainline Architecture) |
+|[15:4]     | Part Number       | 0xC20 =  Cortex-M0 |
+|           |                   | 0xC60 = Cortex-M0+ |
+|           |                   | 0xC23 = Cortex-M3  |
+|           |                   | 0xC24 = Cortex-M4  |
+|           |                   | 0xC27 = Cortex-M7  |
+|           |                   | 0xD20 = Cortex-M23 |
+|           |                   | 0xD21 = Cortex-M33 |
+|[3:0]      | Revision          | Minor revision: 0x1 = Patch 1 |
+
+
+
+You can view individual examples and additional API information of the statistics collection tools at the bottom of the page in the [related links section](#related-links).
+
+
+### Output
+
+To view the serial output you can use any terminal client of your choosing such as [PuTTY](http://www.putty.org/) or [CoolTerm](http://freeware.the-meiers.org/). Unless otherwise specified, printf defaults to a baud rate of 9600 on Mbed OS.
+
+You can find more information on the Mbed OS configuration tools and serial communication in Mbed OS in the related [related links section](#related-links).
+
+The output should contain the following block transmitted at the blinking LED frequency (actual values may vary depending on your target, build profile, and toolchain):
+
+```
+=============================== SYSTEM INFO  ================================
+Mbed OS Version: 999999
+CPU ID: 0x410fc241
+Compiler ID: 2
+Compiler Version: 60300
+RAM0: Start 0x20000000 Size: 0x30000
+RAM1: Start 0x1fff0000 Size: 0x10000
+ROM0: Start 0x0 Size: 0x100000
+================= CPU STATS =================
+Idle: 98% Usage: 2%
+================ HEAP STATS =================
+Current heap: 1096
+Max heap size: 1096
+================ THREAD STATS ===============
+ID: 0x20001eac
+Name: main_thread
+State: 2
+Priority: 24
+Stack Size: 4096
+Stack Space: 3296
+
+ID: 0x20000f5c
+Name: idle_thread
+State: 1
+Priority: 1
+Stack Size: 512
+Stack Space: 352
+
+ID: 0x20000f18
+Name: timer_thread
+State: 3
+Priority: 40
+Stack Size: 768
+Stack Space: 664
+
+```
+
+## Troubleshooting
+
+If you have problems, you can review the [documentation](https://os.mbed.com/docs/latest/tutorials/debugging.html) for suggestions on what could be wrong and how to fix it.
+
+## Related Links
+
+* [Mbed OS Stats API](https://os.mbed.com/docs/latest/apis/mbed-statistics.html)
+* [Mbed OS Configuration](https://os.mbed.com/docs/latest/reference/configuration.html)
+* [Mbed OS Serial Communication](https://os.mbed.com/docs/latest/tutorials/serial-communication.html)
+
+### License and contributions
+
+The software is provided under Apache-2.0 license. Contributions to this project are accepted under the same license. Please see contributing.md for more info.
+
+This project contains code from other projects. The original license text is included in those source files. They must comply with our license guide.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Mar 20 05:33:37 2019 +0000
@@ -0,0 +1,402 @@
+/* WIZnet IoT Shield Cat.M1 Sample code for Arm MBED
+ * Copyright (c) 2019 WIZnet Co., Ltd.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+ 
+ /* 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+
+#include <string>
+#include "mbed.h"
+
+#define RET_OK                      1
+#define RET_NOK                     -1
+#define DEBUG_ENABLE                1
+#define DEBUG_DISABLE               0
+#define ON                          1
+#define OFF                         0
+
+#define MAX_BUF_SIZE                1024
+
+#define BG96_APN_PROTOCOL_IPv4      1
+#define BG96_APN_PROTOCOL_IPv6      2
+#define BG96_DEFAULT_TIMEOUT        1000
+#define BG96_CONNECT_TIMEOUT        15000
+#define BG96_SEND_TIMEOUT           500
+#define BG96_RECV_TIMEOUT           500
+
+#define BG96_APN_PROTOCOL           BG96_APN_PROTOCOL_IPv6
+#define BG96_DEFAULT_BAUD_RATE      115200
+#define BG96_PARSER_DELIMITER       "\r\n"
+
+#define CATM1_APN_SKT               "lte-internet.sktelecom.com"
+
+#define CATM1_DEVICE_NAME_BG96      "BG96"
+#define DEVNAME                     CATM1_DEVICE_NAME_BG96
+
+#define devlog(f_, ...)             if(CATM1_DEVICE_DEBUG == DEBUG_ENABLE) { pc.printf("\r\n[%s] ", DEVNAME);  pc.printf((f_), ##__VA_ARGS__); }
+#define myprintf(f_, ...)           {pc.printf("\r\n[MAIN] ");  pc.printf((f_), ##__VA_ARGS__);}
+
+/* Pin configuraiton */
+// Cat.M1
+#define MBED_CONF_IOTSHIELD_CATM1_TX                D8
+#define MBED_CONF_IOTSHIELD_CATM1_RX                D2
+#define MBED_CONF_IOTSHIELD_CATM1_RESET             D7
+#define MBED_CONF_IOTSHIELD_CATM1_PWRKEY            D9
+
+// Sensors
+#define MBED_CONF_IOTSHIELD_SENSOR_CDS              A0
+#define MBED_CONF_IOTSHIELD_SENSOR_TEMP             A1
+
+/* Debug message settings */
+#define BG96_PARSER_DEBUG           DEBUG_DISABLE
+#define CATM1_DEVICE_DEBUG          DEBUG_ENABLE 
+
+
+typedef struct gps_data_t {
+    float utc;      // hhmmss.sss
+    float lat;      // latitude. (-)dd.ddddd
+    float lon;      // longitude. (-)dd.ddddd
+    float hdop;     // Horizontal precision: 0.5-99.9
+    float altitude; // altitude of antenna from sea level (meters) 
+    int fix;        // GNSS position mode 2=2D, 3=3D
+    float cog;      // Course Over Ground ddd.mm
+    float spkm;     // Speed over ground (Km/h) xxxx.x
+    float spkn;     // Speed over ground (knots) xxxx.x
+    char date[7];   // data: ddmmyy
+    int nsat;       // number of satellites 0-12
+} gps_data;
+
+
+// Functions: Module Status
+void waitCatM1Ready(void);
+int8_t setEchoStatus_BG96(bool onoff);
+int8_t getUsimStatus_BG96(void);
+int8_t getNetworkStatus_BG96(void);
+int8_t checknSetApn_BG96(const char * apn);
+int8_t getFirmwareVersion_BG96(char * version);
+int8_t getImeiNumber_BG96(char * imei);
+
+// Functions: GPS
+int8_t setGpsOnOff_BG96(bool onoff);
+int8_t getGpsLocation_BG96(gps_data *data);
+
+
+Serial pc(USBTX, USBRX);    // USB debug
+
+UARTSerial *_serial;        // Cat.M1 module    
+ATCmdParser *_parser;
+
+DigitalOut _RESET_BG96(MBED_CONF_IOTSHIELD_CATM1_RESET);
+DigitalOut _PWRKEY_BG96(MBED_CONF_IOTSHIELD_CATM1_PWRKEY);
+
+void serialPcInit(void)
+{
+    pc.baud(115200);
+    pc.format(8, Serial::None, 1);
+}
+
+void serialDeviceInit(PinName tx, PinName rx, int baudrate) 
+{        
+    _serial = new UARTSerial(tx, rx, baudrate);    
+}
+
+void serialAtParserInit(const char *delimiter, bool debug_en)
+{
+    _parser = new ATCmdParser(_serial);    
+    _parser->debug_on(debug_en);
+    _parser->set_delimiter(delimiter);    
+    _parser->set_timeout(BG96_DEFAULT_TIMEOUT);
+}
+
+void catm1DeviceInit(void)
+{
+    serialDeviceInit(   MBED_CONF_IOTSHIELD_CATM1_TX, 
+                        MBED_CONF_IOTSHIELD_CATM1_RX, 
+                        BG96_DEFAULT_BAUD_RATE);
+                        
+    serialAtParserInit( BG96_PARSER_DELIMITER, 
+                        BG96_PARSER_DEBUG);
+}
+
+void catm1DeviceReset_BG96(void)
+{
+    _RESET_BG96 = 1;
+    _PWRKEY_BG96 = 1;
+    wait_ms(300);
+    
+    _RESET_BG96 = 0;
+    _PWRKEY_BG96 = 0;
+    wait_ms(400);
+    
+    _RESET_BG96 = 1;    
+    wait_ms(1000);
+}
+
+
+// ----------------------------------------------------------------
+// Main routine
+// ----------------------------------------------------------------
+
+int main()
+{
+    serialPcInit();    
+    catm1DeviceInit();
+    
+    myprintf("Waiting for Cat.M1 Module Ready...\r\n");
+    
+    catm1DeviceReset_BG96();
+    
+    waitCatM1Ready();
+    
+    wait_ms(5000);
+            
+    myprintf("System Init Complete\r\n");
+        
+    myprintf("WIZnet IoT Shield for Arm MBED");
+    myprintf("LTE Cat.M1 Version");
+    myprintf("=================================================");
+    myprintf(">> Target Board: WIoT-QC01 (Quectel BG96)");
+    myprintf(">> Sample Code: GPS");
+    myprintf("=================================================\r\n");
+    
+    setEchoStatus_BG96(OFF);
+   
+    getUsimStatus_BG96();
+    
+    getNetworkStatus_BG96();
+    
+    checknSetApn_BG96(CATM1_APN_SKT);
+    
+    // GPS information structure
+    gps_data gps_info;    
+    
+    if(setGpsOnOff_BG96(ON) == RET_OK) {        
+        myprintf("GPS On\r\n")
+
+        while(1) {
+            if(getGpsLocation_BG96(&gps_info) == RET_OK) {
+                myprintf("Get GPS information >>>");
+                myprintf("gps_info - utc: %6.3f", gps_info.utc)             // utc: hhmmss.sss
+                myprintf("gps_info - lat: %2.5f", gps_info.lat)             // latitude: (-)dd.ddddd
+                myprintf("gps_info - lon: %2.5f", gps_info.lon)             // longitude: (-)dd.ddddd
+                myprintf("gps_info - hdop: %2.1f", gps_info.hdop)           // Horizontal precision: 0.5-99.9
+                myprintf("gps_info - altitude: %2.1f", gps_info.altitude)   // altitude of antenna from sea level (meters)
+                myprintf("gps_info - fix: %d", gps_info.fix)                // GNSS position mode: 2=2D, 3=3D
+                myprintf("gps_info - cog: %3.2f", gps_info.cog)             // Course Over Ground: ddd.mm
+                myprintf("gps_info - spkm: %4.1f", gps_info.spkm)           // Speed over ground (Km/h): xxxx.x
+                myprintf("gps_info - spkn: %4.1f", gps_info.spkn)           // Speed over ground (knots): xxxx.x            
+                myprintf("gps_info - date: %s", gps_info.date)              // data: ddmmyy
+                myprintf("gps_info - nsat: %d\r\n", gps_info.nsat)          // number of satellites: 0-12
+            } else {
+                myprintf("Failed to get GPS information\r\n");
+            }
+            wait_ms(1000);
+        }       
+#if 0        
+        if(setGpsOnOff_BG96(OFF) == RET_OK) {
+            myprintf("GPS Off\r\n")
+        }
+#endif
+        
+    } else {
+        myprintf("GPS On failed\r\n")
+    }
+}
+
+// ----------------------------------------------------------------
+// Functions: Cat.M1 Status
+// ----------------------------------------------------------------
+
+void waitCatM1Ready(void)
+{
+    while(1) 
+    {   
+        if(_parser->recv("RDY")) 
+        {
+            myprintf("BG96 ready\r\n");
+            return ;
+        }
+        else if(_parser->send("AT") && _parser->recv("OK"))
+        {
+            myprintf("BG96 already available\r\n");
+            return ;
+        }        
+    }        
+}
+
+int8_t setEchoStatus_BG96(bool onoff)
+{
+    int8_t ret = RET_NOK;
+    char _buf[10];        
+    
+    sprintf((char *)_buf, "ATE%d", onoff);    
+    
+    if(_parser->send(_buf) && _parser->recv("OK")) {        
+        devlog("Turn Echo %s success\r\n", onoff?"ON":"OFF");
+        ret = RET_OK;
+    } else { 
+        devlog("Turn Echo %s failed\r\n", onoff?"ON":"OFF");
+    }    
+    return ret;
+}
+ 
+int8_t getUsimStatus_BG96(void)
+{
+    int8_t ret = RET_NOK;
+    
+    _parser->send("AT+CPIN?");    
+    if(_parser->recv("+CPIN: READY") && _parser->recv("OK")) {
+        devlog("USIM Status: READY\r\n");
+        ret = RET_OK;
+    } else { 
+        devlog("Retrieving USIM Status failed\r\n");        
+    }
+    return ret;
+}
+
+int8_t getNetworkStatus_BG96(void)
+{
+    int8_t ret = RET_NOK;    
+    
+    if(_parser->send("AT+QCDS") && _parser->recv("+QCDS: \"SRV\"") && _parser->recv("OK")) {
+        devlog("Network Status: attached\r\n");
+        ret = RET_OK;
+    } else if (_parser->send("AT+QCDS") && _parser->recv("+QCDS: \"LIMITED\"") && _parser->recv("OK")) {
+        devlog("Network Status: limited\r\n");
+        ret = RET_OK;    
+    } else { 
+        devlog("Network Status: Error\r\n");        
+    }
+    return ret;
+}
+
+int8_t checknSetApn_BG96(const char * apn) // Configure Parameters of a TCP/IP Context
+{       
+    char resp_str[100];
+    
+    uint16_t i = 0;
+    char * search_pt;
+    
+    memset(resp_str, 0, sizeof(resp_str));
+    
+    devlog("Checking APN...\r\n");
+    
+    _parser->send("AT+QICSGP=1");
+    
+    while(1)
+    {
+        _parser->read(&resp_str[i++], 1);        
+        search_pt = strstr(resp_str, "OK\r\n");
+        if (search_pt != 0)
+        {
+            break;
+        }
+    }
+    
+    search_pt = strstr(resp_str, apn);
+    if (search_pt == 0)
+    {
+        devlog("Mismatched APN: %s\r\n", resp_str);
+        devlog("Storing APN %s...\r\n", apn);
+        if(!(_parser->send("AT+QICSGP=1,%d,\"%s\",\"\",\"\",0", BG96_APN_PROTOCOL, apn) && _parser->recv("OK")))
+        {
+            return RET_NOK; // failed
+        }
+    }    
+    devlog("APN Check Done\r\n");    
+    
+    return RET_OK;
+}
+
+int8_t getFirmwareVersion_BG96(char * version)
+{
+    int8_t ret = RET_NOK;
+    
+    if(_parser->send("AT+QGMR") && _parser->recv("%s\n", version) && _parser->recv("OK"))
+    {           
+        ret = RET_OK;
+    } 
+    return ret;
+}
+
+int8_t getImeiNumber_BG96(char * imei)
+{
+    int8_t ret = RET_NOK;    
+    
+    if(_parser->send("AT+CGSN") && _parser->recv("%s\n", imei) && _parser->recv("OK"))
+    { 
+        ret = RET_OK;
+    } 
+    return ret;
+}
+
+// ----------------------------------------------------------------
+// Functions: Cat.M1 GPS
+// ----------------------------------------------------------------
+
+int8_t setGpsOnOff_BG96(bool onoff)
+{
+    int8_t ret = RET_NOK;
+    char _buf[15];        
+    
+    sprintf((char *)_buf, "%s", onoff ? "AT+QGPS=2" : "AT+QGPSEND");
+    
+    if(_parser->send(_buf) && _parser->recv("OK")) {
+        devlog("GPS Power: %s\r\n", onoff ? "On" : "Off");
+        ret = RET_OK;
+    } else { 
+        devlog("Set GPS Power %s failed\r\n", onoff ? "On" : "Off");        
+    }
+    return ret;    
+}
+
+
+int8_t getGpsLocation_BG96(gps_data *data)
+{
+    int8_t ret = RET_NOK;    
+    char _buf[100];
+    
+    bool ok = false;    
+    Timer t;
+    
+    // Structure init: GPS info
+    data->utc = data->lat = data->lon = data->hdop= data->altitude = data->cog = data->spkm = data->spkn = data->nsat=0.0;
+    data->fix=0;
+    memset(&data->date, 0x00, 7);
+    
+    // timer start
+    t.start();
+    
+    while( !ok && (t.read_ms() < BG96_CONNECT_TIMEOUT ) ) {
+        _parser->flush();    
+        _parser->send((char*)"AT+QGPSLOC=2"); // MS-based mode        
+        ok = _parser->recv("+QGPSLOC: ");
+        if(ok) {
+            _parser->recv("%s\r\n", _buf);
+            sscanf(_buf,"%f,%f,%f,%f,%f,%d,%f,%f,%f,%6s,%d",
+                          &data->utc, &data->lat, &data->lon, &data->hdop,
+                          &data->altitude, &data->fix, &data->cog,
+                          &data->spkm, &data->spkn, data->date, &data->nsat);            
+            ok = _parser->recv("OK");
+        }         
+    }
+    
+    if(ok == true) ret = RET_OK;
+    
+    return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-os.lib	Wed Mar 20 05:33:37 2019 +0000
@@ -0,0 +1,1 @@
+https://github.com/ARMmbed/mbed-os/#51d55508e8400b60af467005646c4e2164738d48
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_app.json	Wed Mar 20 05:33:37 2019 +0000
@@ -0,0 +1,11 @@
+{
+    "target_overrides": {
+        "*": {
+            "platform.stack-stats-enabled": true,
+            "platform.heap-stats-enabled": true,
+            "platform.cpu-stats-enabled": true,
+            "platform.thread-stats-enabled": true,
+            "platform.sys-stats-enabled": true
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stats_report.h	Wed Mar 20 05:33:37 2019 +0000
@@ -0,0 +1,132 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2018 ARM Limited
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef STATS_REPORT_H
+#define STATS_REPORT
+
+#include "mbed.h"
+
+/**
+ *  System Reporting library. Provides runtime information on device:
+ *      - CPU sleep, idle, and wake times
+ *      - Heap and stack usage
+ *      - Thread information
+ *      - Static system information
+ */
+class SystemReport {
+    mbed_stats_heap_t   heap_stats;
+    mbed_stats_cpu_t    cpu_stats;
+    mbed_stats_sys_t    sys_stats;
+
+    mbed_stats_thread_t *thread_stats;
+    uint8_t   thread_count;
+    uint8_t   max_thread_count;
+    uint32_t  sample_time_ms;
+
+public:
+    /**
+     *  SystemReport - Sample rate in ms is required to handle the CPU percent awake logic
+     */
+    SystemReport(uint32_t sample_rate) : max_thread_count(8), sample_time_ms(sample_rate)
+    {
+        thread_stats = new mbed_stats_thread_t[max_thread_count];
+
+        // Collect the static system information
+        mbed_stats_sys_get(&sys_stats);
+
+        printf("=============================== SYSTEM INFO  ================================\r\n");
+        printf("Mbed OS Version: %ld \r\n", sys_stats.os_version);
+        printf("CPU ID: 0x%lx \r\n", sys_stats.cpu_id);
+        printf("Compiler ID: %d \r\n", sys_stats.compiler_id);
+        printf("Compiler Version: %ld \r\n", sys_stats.compiler_version);
+
+        for (int i = 0; i < MBED_MAX_MEM_REGIONS; i++) {
+            if (sys_stats.ram_size[i] != 0) {
+                printf("RAM%d: Start 0x%lx Size: 0x%lx \r\n", i, sys_stats.ram_start[i], sys_stats.ram_size[i]);
+            }
+        }
+        for (int i = 0; i < MBED_MAX_MEM_REGIONS; i++) {
+            if (sys_stats.rom_size[i] != 0) {
+                printf("ROM%d: Start 0x%lx Size: 0x%lx \r\n", i, sys_stats.rom_start[i], sys_stats.rom_size[i]);
+            }
+        }
+    }
+
+    ~SystemReport(void)
+    {
+        free(thread_stats);
+    }
+
+    /**
+     *  Report on each Mbed OS Platform stats API
+     */
+    void report_state(void)
+    {
+        report_cpu_stats();
+        report_heap_stats();
+        report_thread_stats();
+
+        // Clear next line to separate subsequent report logs
+        printf("\r\n");
+    }
+
+    /**
+     *  Report CPU idle and awake time in terms of percentage
+     */
+    void report_cpu_stats(void)
+    {
+        static uint64_t prev_idle_time = 0;
+
+        printf("================= CPU STATS =================\r\n");
+
+        // Collect and print cpu stats
+        mbed_stats_cpu_get(&cpu_stats);
+
+        uint64_t diff = (cpu_stats.idle_time - prev_idle_time);
+        uint8_t idle = (diff * 100) / (sample_time_ms * 1000);  // usec;
+        uint8_t usage = 100 - ((diff * 100) / (sample_time_ms * 1000));  // usec;;
+        prev_idle_time = cpu_stats.idle_time;
+
+        printf("Idle: %d%% Usage: %d%% \r\n", idle, usage);
+    }
+
+    /**
+     *  Report current heap stats. Current heap refers to the current amount of
+     *  allocated heap. Max heap refers to the highest amount of heap allocated
+     *  since reset.
+     */
+    void report_heap_stats(void)
+    {
+        printf("================ HEAP STATS =================\r\n");
+
+        // Collect and print heap stats
+        mbed_stats_heap_get(&heap_stats);
+
+        printf("Current heap: %lu\r\n", heap_stats.current_size);
+        printf("Max heap size: %lu\r\n", heap_stats.max_size);
+    }
+
+    /**
+     *  Report active thread stats
+     */
+    void report_thread_stats(void)
+    {
+        printf("================ THREAD STATS ===============\r\n");
+
+        // Collect and print running thread stats
+        int count = mbed_stats_thread_get_each(thread_stats, max_thread_count);
+
+        for (int i = 0; i < count; i++) {
+            printf("ID: 0x%lx \r\n",        thread_stats[i].id);
+            printf("Name: %s \r\n",         thread_stats[i].name);
+            printf("State: %ld \r\n",       thread_stats[i].state);
+            printf("Priority: %ld \r\n",    thread_stats[i].priority);
+            printf("Stack Size: %ld \r\n",  thread_stats[i].stack_size);
+            printf("Stack Space: %ld \r\n", thread_stats[i].stack_space);
+        }
+    }
+};
+
+#endif // STATS_REPORT_H