Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Application Introduction

...

The Diagnostic Application is designed to help new developers to the Sparrow Application ecosystem understand and trace the behaviors of an application. The Diagnostic application uses all features (albeit trivially) to demonstrate their usage, and generates logs with calling parameters for each callback it enters. This gives the developer a feel for the rhythm and flow of a Sparrow Application.

Expected Behavior

The Diagnostic application has a period of 10 minutes, which means it will wake from “sleep” every 10 minutes to perform work. While awake, it has a polling interval of 5 seconds, which means it will invoke the polling callback every 5 seconds. When a Sparrow running the Diagnostic application is connected to a terminal, it will create an output log as it enters each callback.

The Diagnostic will create a template, register it with the Notecard attached to the Sparrow Gateway and parse the response. Once it has successfully registered a template, the application then performs “real work” by making system calls to collect the runtime memory usage of the Sparrow Runtime, including all applications.

Output to Notehub

The Diagnostic application will generate a Note with the following details. The Note will appear in a Notefile prefixed with the device id (e.g. 012345678901234567890123) and suffixed with #diag.qo.

Code Block
languagejson
"body": {
  "mem.alloc.bytes":928,
  "mem.free.bytes":960,
  "mem.heap.bytes":53488,
  "voltage":3.306
}

This Note can be viewed on Notehub.io, by looking for the project associated with the Notecard attached to the Sparrow Gateway.

Logging Output

The logs generated by the Diagnostic application are verbose by design.

In the logs below, you can see the application send a request to register a template (8), receive a response that the registration was successful (43), generate a diagnostic report (46), and finally receive a response of success (73).

More importantly, you can see the application progress through the callbacks:

  • diagActivate - (1)

  • diagPoll - (4, 6, 16, 20, 31, 38)

  • diagResponse - (35)

  • diagPoll - (38, 49, 53, 65)

  • diagResponse - (69)

  • diagPoll - (72)

Code Block
languagenone
diag: Entered application callback function: diagActivate
	appId: 3
diagnostic activated with 600s activation period and 5s poll interval
diag: Entered application callback function: diagPoll
	appId: 3	state: ONCE
diag: Entered application callback function: diagPoll
	appId: 3	state: ACTIVATED
diag: template registration request
240036 note.template
diagnostic now SENDING_REQUEST
240036 to gateway:5 sensor sending request (152)
240036 to gateway:5 sending ( (152/152) at txp:-17
240036 to gateway:5 waiting 2s to transmit (slot 0s-20s in 20s window)
diagnostic state will be set to 1 on success, or 0 on error
sched: sleeping 3s (next transmit window in 1s)
diag: Entered application callback function: diagPoll
	appId: 3	state: SENDING_REQUEST
sched: sleeping 5s
240036 to gateway:5 waiting for message from gateway
diag: Entered application callback function: diagPoll
	appId: 3	state: SENDING_REQUEST
sched: sleeping 4s (next transmit window in 12s)
240036 fm gateway:5 ack received
ATP: buffered sample 2
ATP: rssi/snr:-34/7 txp:-17
ATP: | [3] - - - - - - - - - - - - 4 - - - - - - - - - - - - - - - - - - - - - - - - - - 
diagnostic now RECEIVING_RESPONSE (waiting for response)
240036 fm gateway:5 waiting for response from gateway
240036 fm gateway:5 now receiving response from gateway
240036 fm gateway:5 sending ACK (0/0) at txp:-17
diag: Entered application callback function: diagPoll
	appId: 3	state: RECEIVING_RESPONSE
sched: sleeping 4s (next transmit window in 8s)
diagnostic now 1 (response completed)
diag: Entered application callback function: diagResponse
	appId: 3	rsp: {"id":2,"bytes":32}
diag: SUCCESSFUL template registration
diag: Entered application callback function: diagPoll
	appId: 3	state: STATE_DIAG_CHECK
diag: generating diagnostic report
240036 note.add
diagnostic now SENDING_REQUEST
240036 to gateway:6 sensor sending request (159)
240036 to gateway:6 sending ( (159/159) at txp:-17
240036 to gateway:6 waiting 3s to transmit (slot 0s-20s in 20s window)
diagnostic state will be set to 1 on success, or 0 on error
diag: note request sent
sched: sleeping 4s (next transmit window in 3s)
diag: Entered application callback function: diagPoll
	appId: 3	state: SENDING_REQUEST
sched: sleeping 4s
240036 to gateway:6 waiting for message from gateway
diag: Entered application callback function: diagPoll
	appId: 3	state: SENDING_REQUEST
sched: sleeping 4s (next transmit window in 13s)
240036 fm gateway:6 ack received
ATP: buffered sample 3
ATP: would decrease power but it's already bottomed-out at -17db
ATP: rssi/snr:-44/5 avg:-37/6 txp:-17
ATP: | [5] - - - - - - - - - - - - 4 - - - - - - - - - - - - - - - - - - - - - - - - - - 
diagnostic now RECEIVING_RESPONSE (waiting for response)
240036 fm gateway:6 waiting for response from gateway
240036 fm gateway:6 now receiving response from gateway
240036 fm gateway:6 sending ACK (0/0) at txp:-17
diag: Entered application callback function: diagPoll
	appId: 3	state: RECEIVING_RESPONSE
sched: sleeping 4s (next transmit window in 8s)
diagnostic now 1 (response completed)
diag: Entered application callback function: diagResponse
	appId: 3	rsp: {"template":true}
diag: SUCCESSFUL Note submission
diag: Entered application callback function: diagPoll
	appId: 3	state: STATE_DIAG_CHECK
diagnostic now DEACTIVATED (diag: completed)
diagnostic deactivated

Implementation Details

Defines

Custom Application States

Code Block
languagec
#define STATE_DIAG_ABORT       0
#define STATE_DIAG_CHECK       01
#define STATE_DIAG_ISR_XFER    12
  • STATE_DIAG_ABORT - This state is reserved for application specific error conditions, and will allow for graceful deactivation of the application.

  • STATE_DIAG_CHECK - When this state is received by the polling callback and the application has not be marked for deactivation, it will generate a diagnostic report, send the results to the Gateway Notecard, and mark itself ready for deactivation. Otherwise, if this state is received by the polling callback and the application has already been marked for deactivation then it will log a completion message and immediately deactivate.

  • STATE_DIAG_ISR_XFER - This state is set from within the ISR, and it allows the polling callback to know the interrupt was invoked.

...

Code Block
languagec
#define REQUESTID_TEMPLATE     219790917
  • REQUESTID_TEMPLATE - This allows An arbitrary number allowing you to match a response to a and request. To In order to keep track of responses a response to specific NotesNote, you need to supply an “id” tag to the Note. If When an “id” was is supplied to a Note, then the response parameter provided to diagResponse() will contain the matching “id”.

...

  • APPLICATION_NOTEFILE - The dynamic filename of the application specific queue.

    NOTE: The Gateway will replace * with the originating node's ID. The resulting transformation will resemble 2037335832365003001b0037#diag012345678901234567890123#diag.qo.

Array and Counter Limits

...

  • ISR_MAX_CALL_RETENTION - The maximum number of ISRs that will be recorded.

  • ISR_COUNTER_MASK - The mask will limit the value of the counter.

Structures

...

languagec
  • isrParameters - Since an APP_PRINT() statement cannot be used from within an interrupt service routine, this structure is used to collect the calling parameters of the ISR for later processing.

    Code Block
    typedef struct 

...

  • isrParameters {
        int appID;
        uint16_t pins;
    } isrParameters;
    • appID - The application ID passed to the ISR callback

    • pins - The pin(s) prompting the ISR invocation.

  • applicationContext - A rather trivial example of application context, but never the less ALL the application state is stored in a single structure which is shared between callbacks.

    Code Block
    typedef struct applicationContext {
        // ISR call ring-buffer
        volatile size_t isrCount;
        volatile bool isrOverflow;
        isrParameters isrParams[ISR

...

  • ISR_parameters - Used to collect information available in ISR for later processing
    _MAX_CALL_RETENTION];
    
        // Application status
        bool templateRegistered;  // Only `true` once we've successfully registered the template
        bool done;
    } applicationContext;
    • isrCount - The number of times the ISR was invoked.

    • isrOverflow - Indicates whether the ISR was invoked more than ISR_MAX_CALL_RETENTION times.

    • isrParams - An array dedicated to recording the calling parameters of each ISR invocation.

    • templateRegistered - Provides a mechanism for the polling callback to only attempt to register a template once.

    • done - Provides a mechanism for the polling callback to indicate when a note has been submitted to the Notecard, so it will know when to deactivate itself.

Variables (a.k.a. Application Specific Context)

none

Callbacks

  • bool diagActivate(int appID, void *appContext);

  • void diagISR(int appID, uint16_t pins, void *appContext);

    Code Block
    diag: ISR callback function called  <7> times.
    diag: call 0:	appId: 3	pins: 128
    diag: call 1:	appId: 3	pins: 128
    diag: call 2:	appId: 3	pins: 128
    diag: call 3:	appId: 3	pins: 128
    diag: call 4:	appId: 3	pins: 8192
    diag: call 5:	appId: 3	pins: 128
    diag: call 6:	appId: 3	pins: 128
  • void diagPoll(int appID, int state, void *appContext);

  • void diagResponse(int appID, J *rsp, void *appContext);

...

  • static void addNote(bool immediate);

  • static const char * diagStateName (int state);

  • static bool registerNotefileTemplate();

  • static inline void resetIsrValues(applicationContext *ctx);