SharkSSL C/C++ Reference
TLS, cryptography, certificates, and PSA integration
SharkSSL Examples

Use an example to establish a working TLS connection or verify a firmware signature before adapting the code to your product. Client examples initiate connections; server examples accept them. The ECDSA verification example runs without a network connection.

Choose a first example

Goal Example
Inspect a server certificate and trust result CertCheck.c
Connect a device to an MQTT broker with a client certificate AWS-MQTT.c
Exchange messages over WebSocket WsEchoClient.c
Compare full and resumed connections SessionCache.c
Accept HTTPS and WebSocket connections on a device Minnow Server examples below
Verify a firmware signature made with OpenSSL ECDSA-Verify

Before running a network example, check its configured endpoint, port, trust store, and any required account credentials. Online endpoints and cloud setup steps can change. Treat supplied certificates and private keys as demonstration credentials and provision your own identities for a product.

A client must check both certificate trust and the expected server name before exchanging sensitive data. Review the application's handling of seSec_handshake results; completion alone does not mean that its trust policy accepted the server.

Initialize the random number generator

Examples that initialize the random number generator (RNG) use a function address as a demonstration seed, for example:

/* Demonstration only. Replace with proper platform entropy in a product. */
sharkssl_entropy((U32)(ptrdiff_t)mainTask);
SHARKSSL_API int sharkssl_entropy(U32)
Mix 32 application-supplied bits into the default RNG state.
void mainTask(SeCtx *ctx)
Main entry for all example programs.

This passes a U32 value derived from the function address; it does not register or call an entropy callback. The pattern avoids a desktop-specific entropy API in the example, but a function address is predictable and provides no cryptographic entropy. Repeating this call does not make the seed secure.

Before using the code in a product, replace the demonstration seed with proper seeding from the platform's hardware entropy source or operating-system cryptographic RNG. Follow the selected RNG's initialization and reseeding requirements, and handle failures before TLS, key generation, signing, or WebSocket client masking. In a PSA consumer build, sharkssl_entropy initializes the provider and ignores the supplied value; configure entropy in that provider. See the RNG declarations in SharkSslCrypto.h for the API contracts.

SharkSSL client examples

A TLS client authenticates the server against its configured trust store and expected domain name. SharkSslCon_trusted checks the name against the certificate; its date checks depend on SHARKSSL_CHECK_DATE. Preserve the example's trust-result handling when changing the server or certificate configuration.

Client Examples

  • E-Mail (SMTP) Example: SMTP/SMTP-example.c - Send emails securely by using the included secure SMTP library.
  • LED SMQ: LED-SMQ.c - IoT example that uses the secure SMQ library SharkMQ for connecting to our SMQ cluster. The example lets you control LEDs in a device in realtime via a browser. See the Browser to Device LED Control using SMQ tutorial for details. See also the SharkMQ API documentation.
  • LED JSON: LED-JSON.c - IoT example that shows how to send and receive JSON IoT messages over a raw socket connection. The example requires that you have installed our free JSON library in the root path.
  • SharkMQTT Chat Example: Chat-MQTT.c - implements a basic chat client by using the MQTT protocol and the SharkMQTT library. The example, by default, connects to an online MQTT broker we have setup. We usually have a machine chat client also connected to the online MQTT broker, and you may chat with this machine or start a second instance of the MQTT client. Our chat machine is a basic implementation of ELIZA the therapist.
  • AWS MQTT - shows how to connect to Amazon's AWS MQTT cloud. You must have an Amazon AWS account in order to use this example. Navigate to your AWS IoT console and register a new device (Register a thing). Take note of the AWS MQTT broker name, open AWS-MQTT.c in an editor, navigate to the line where you find #define AWS_IOT_MQTT_HOST "", and paste the broker name into the name string. Follow the instructions for creating a certificate for the device. On the "Certificate created" page, chose Download for the certificate, private key, and the root CA for AWS IoT. Name these files aws-certificate.pem, aws-private.pem.key, and aws-iot-rootCA.crt. Copy these three files to the directory where you have the example executable. Run the example. You may alternatively embed the certificate, key, and root CA in the executable. To embed the three files, run the two following command line programs as follows:
    SharkSslParseCert aws-certificate.pem aws-private.pem.key > aws-certificate.h
    SharkSSLParseCAList aws-iot-rootCA.crt > aws-iot-rootCA.h
    SharkSslParseCert and SharkSSLParseCAList are command line tools included with SharkSSL. Compile the example with the macro EMBED_CERTIFICATES. This macro makes the example include the two generated C header files aws-certificate.h and aws-iot-rootCA.h.
  • WebSocket Echo Client: WsEchoClient.c - connects either to our online ELIZA the therapist WebSocket server (default) or to an online public WebSocket echo server. A #define in the example C code changes what server the client connects to. For the public echo WebSocket server mode, all data entered at the command line is sent to the server which in turn sends or (echoes) the data back to the client.
  • SSL Certificate Check: CertCheck.c - a client example that shows how to validate several certificates returned by SSL enabled servers.
  • SSL Session Cache Test: SessionCache.c - connects to various servers twice and measures the time difference between non-cached and cached sessions. The first connection is not cached. The second connection uses the SSL session key from the first connection if the server supports this feature.

SharkSSL server examples

The following examples are designed for the Minnow Server. Refer to the online tutorial Creating Single-Page Apps with the Minnow Server for how to best design WebSocket server compatible applications.

  • WebSocket Chat Server: a basic chat example that echoes characters entered in the server's console window to the browser window and vice versa.
  • WebSocket LED Server: a Single Page Application (SPA) designed for controlling LEDs in a device. This example functions similar to the LED-SMQ example except for that you connect directly to the device from your browser instead of controlling the device via an online SMQ broker (proxy server).
  • Minnow Server Reference Platform: a more complete WebSocket example that follows the design guidelines in the tutorial Creating Single-Page Apps with the Minnow Server. The example may be downloaded from GitHub and used with the SharkSSL enabled Minnow Server included in the SharkSSL package.
  • SharkTrust: an example that shows how to automate certificate management by using SharkTrust. The example shows how to auto update and maintain a valid certificate for the Minnow Server.

Important information

All server examples include a certificate signed by Real Time Logic's root (CA) certificate. You will receive a browser warning if you do not install Real Time Logic's CA certificate in your browser/certificate store. An explanation on how to install a CA certificate is available online. We recommend reading our certificate management tutorial if you are new to certificates. The server examples only respond to HTTPS connections. You must make sure you enter your https://address:port in the browser when connecting to the server examples.

You may still get a browser SSL warning when connecting to the example server after installing the CA certificate in your browser if the examples are not used correctly. Not only does the browser validate that the certificate is from a trusted source; it also checks that the certificate identifies the name entered in the browser's address bar, normally through a DNS Subject Alternative Name (SAN). Check the certificate supplied with your package. The documented test name for the WebSocket Chat Server is "localhost", and the common name for the WebSocket LED Server is "device". The WebSocket Chat Server must, for this reason, run on the same computer as the browser such that you connect to "localhost". The WebSocket LED Server requires an entry in your computer's hosts file so you can enter "device" as the name in the browser's address bar.

SharkSSL example libraries

SharkSSL includes a number of example libraries. These libraries include functionality such as WebSockets and SMTP. These libraries are fully functional, but are delivered as examples since they are not part of the SharkSSL core functionality.

SharkSSL Example Libraries

See the following SharkSSL product pages: SMTP library, WebSocket client library, and Minnow Server for an introduction to these products.

The following figure shows the relationship between the core SharkSSL library (red), the example libraries (blue), and the provided example programs (green).

Figure 1 shows the relationship between examples, example libraries, and SharkSSL

SharkSSL example program features

Demonstration Attributes

The (example) libraries above include special features, which may easily go unnoticed without reading the references contained in this section.

  • Single Active User: The WebSocket Chat Server and WebSocket LED Server examples are limited to one active user. You may modify these examples such that they will accept more than one active user.
  • Forced Browser Closure: The WebSocket Chat Server example and WebSocket LED Server example require that you close the current browser window before connecting a new window.
  • Multiple Connected Users: The SMQ-LED and LED-JSON examples are designed to manage multiple connected users. A user is an active browser session that is controlling a device. The two examples include resource management on the server side to handle multiple users. You should try to connect at least two browser windows to one device, and watch both browser windows as you toggle an LED button in the (simulated) device or as you click on an LED in one of the browser Windows.
  • Browser Multiple Connection Conflict: The WebSocket Chat Server and WebSocket LED Server examples include dual HTTPS server/WebSocket server functionality. The web page and resource files are initially loaded using HTTPS via the Minnow Server library. JavaScript code in the browser is activated when all resources are loaded, and this JavaScript code initiates a persistent WebSocket connection with the server. The Minnow Server includes both a basic HTTPS server and WebSocket server. The basic HTTPS server is constructed to be as small as possible and can only handle one connection at a time. This creates a conflict with modern browsers that attempt to open multiple HTTPS connections when connecting to an HTTPS server. The connections will remain pending on the server side and most browsers will automatically time out and cause an initial delay before the WebSocket connection is established. The Minnow Server includes information on how this initial delay can be eliminated.

Tinkering with the examples

Client Side Modifications

The Certificate Management for IoT tutorial shows how to create your own Certificate Authority (CA) and how to sign certificates for the SharkSSL client examples by using the CA (root) certificate.

You can modify the client examples and connect to your own server. The server side code that manages the connections from all client examples can be downloaded and executed on your own computer. The Certificate Management for IoT tutorial shows you how to modify the client examples and how to install and operate your own server.

<em>Server Side Modifications</em>

Let's say you want to change the certificate in the WebSocket LED Server (WsLedServer.c) to your own certificate signed by your own Certificate Authority (CA). The below step-by-step instructions will use the Certificate management tool to illustrate the process for creating a complete new chain of trust:

Steps

  1. Download and install our Certificate Management Tool.
  2. Use the tool to create the CA certificate. We suggest that you initially create an RSA CA certificate.
  3. Install the CA certificate in your browser's certificate store. You can use the instructions we provide for our certificates, but remember to install your own CA certificate and not ours.
  4. Use the Certificate Management tool to create a certificate for the WebSocket LED Server. When you create your own certificates, you can use an IP address as the certificate's "common name"; thus you can use the IP address of the computer where the server will be running. You may also consider creating a Subject Alternative Name (SAN) certificate and use your computer's IP address and a name such as "device" as the alternative names. In this way, you can access the server securely without getting browser warnings by: (1) using the IP address, (2) using the name "device". For example, you can enter one of the following in your browser: https://192.168.1.100 or https://device.
  5. Add the certificate to the WebSocket LED server example.
  6. Compile and run the code.
  7. Navigate to the server by entering https://ip-address in the browser. You should not get any certificate warnings.

In step 5, we convert an X.509 certificate and key to the optimized SharkSSL format. This provides the appropriate APIs that allow you to load a standard X.509 certificate (or) the optimized SharkSSL certificate format. The LED server demo is using the optimized format, but you may change the example and use the SharkSSL APIs to allow a direct load of an X.509 certificate. The SharkSSL source code can be compiled without the X.509 processing code, thus saving memory in constrained devices. See certificate Management with sharkSSL for a detailed explanation.

Signing and Verifying Binaries

Use a digital signature to check that firmware matches an image signed with your trusted signing key before accepting an update. The signing computer holds the private key; the device verifies the signature with the matching public key. The following examples use RayCrypto, the cryptographic engine in SharkSSL. See also the Securing Edge Nodes Whitepaper.

Start with Verify firmware signatures with SharkSSL in examples/ECDSA-Verify. It includes a small C verifier, matching OpenSSL test data, a Windows build script, and instructions for signing your own firmware. The demo checks both a valid signature and a deliberately changed digest.

For signing and verification within SharkSSL, see examples/RayCrypto-ECDSA.c. The integration sketches below show where hashing and verification belong in a firmware workflow. Their disk, network and flash functions are application placeholders, not SharkSSL APIs; read errors and installation policy must be handled by the application.

Both APIs take a binary digest and a hash identifier as the final argument. For SHA-256, use SHARKSSL_HASHID_SHA256. SHARKSSL_SHA256_HASH_LEN is the 32-byte digest size and is only used to size the digest buffer. The signature uses binary DER encoding, and verification needs its actual byte length.

Signature verification is one part of secure boot or a firmware updater. Keep the verification public key in trusted device storage and apply your version/rollback and safe-installation policies before activating an image.

Figure 2 shows the complete signing and verification process

Signing Binaries

Sign firmware on a host computer as part of the release process. The following sketch embeds a demonstration private key converted with SharkSslParseKey. A signing application can instead load PEM text from a file and convert it with sharkssl_PEM_to_ECCKey; release the converted key with SharkSslECCKey_free when finished. A production signer must protect its own private key and initialize SharkSSL's cryptographic random number generator with suitable entropy before signing. Keep the private key off the device that only verifies updates.

/* Demonstration private key converted by SharkSslParseKey. */
const U8 sharkSslPrivECKey256[104] =
{
0x30, 0x82, 0x00, 0x00, 0x02, 0x20, 0x17, 0x20,
0xF6, 0x9C, 0x88, 0x3D, 0x9C, 0x32, 0x41, 0xDE,
0x25, 0x34, 0xD9, 0x23, 0xDD, 0x71, 0x0E, 0xF2,
0x37, 0x83, 0xAE, 0xC6, 0xD5, 0xBF, 0xEA, 0x61,
0xC9, 0x81, 0xE5, 0x0F, 0x84, 0x83, 0xFA, 0x3F,
0x11, 0x23, 0xE8, 0xEA, 0x05, 0x14, 0x28, 0xE0,
0x37, 0xAC, 0xF3, 0x0E, 0x35, 0x58, 0x58, 0xA8,
0x16, 0x0B, 0x62, 0xEB, 0xEE, 0xC9, 0x06, 0x2F,
0x2A, 0xCF, 0x83, 0x1F, 0x93, 0x3C, 0xDF, 0x84,
0x04, 0xBF, 0x4E, 0x80, 0x5D, 0xA8, 0x0D, 0x15,
0xD7, 0x02, 0xAD, 0x8E, 0xD0, 0xFA, 0xE2, 0xB5,
0x69, 0x8C, 0x92, 0x00, 0xF5, 0xB6, 0xA8, 0x1C,
0x0D, 0x7F, 0x9E, 0x5C, 0x27, 0x93, 0x5C, 0x57
};
U8 dataChunk[256];
U8 sha256digest[SHARKSSL_SHA256_HASH_LEN];
U8 *signature;
U32 len;
U16 sl;
SharkSslSha256Ctx ctx; /* Hash engine */
/* Read firmware in chunks from local file system
and compute hash.
*/
while(readFirmwareChunkFromDisk(dataChunk,&len))
{
SharkSslSha256Ctx_append(&ctx, dataChunk, len);
}
/* All firmware data chunks read: Finish the hash calculation */
SharkSslSha256Ctx_finish(&ctx, sha256digest);
/* Determine the maximum length for the signature
in order to allocate memory for it.
*/
sl = sharkssl_ECDSA_siglen((SharkSslECCKey)sharkSslPrivECKey256);
signature = (U8*)malloc(sl);
/* Check key sizing/allocation before signing the binary digest. */
if(sl && signature && sharkssl_ECDSA_sign_hash(
(SharkSslECCKey) sharkSslPrivECKey256, /* ECC private key */
signature, &sl, /* Buffer capacity in; actual signature length out */
sha256digest, /* Firmware fingerprint (hash) */
{
/* Success:
Store the signature and its actual byte length in the update package.
Define the package layout so the receiver can separate firmware bytes
from signature bytes. Publish only when signing succeeds.
*/
}
free(signature);
#define SHARKSSL_HASHID_SHA256
Definition: SharkSslCrypto.h:66
#define SHARKSSL_SHA256_HASH_LEN
Definition: SharkSslCrypto.h:47
SHARKSSL_API U16 sharkssl_ECDSA_siglen(SharkSslECCKey privkey)
Returns the maximum length (in bytes) of a DER-encoded ECDSA signature generated with the private key...
SHARKSSL_API sharkssl_ECDSA_RetVal sharkssl_ECDSA_sign_hash(SharkSslECCKey privkey, U8 *sig, U16 *siglen, const U8 *hash, U8 hashID)
Generate the signature using the ECC private key and a hash.
U8 * SharkSslECCKey
SharkSslECCKey is an alias for the SharkSslCert type and is a private/public key converted by sharkss...
Definition: SharkSSL.h:2425
SHARKSSL_API int SharkSslSha256Ctx_constructor(SharkSslSha256Ctx *ctx)
Initialize.
SHARKSSL_API int SharkSslSha256Ctx_finish(SharkSslSha256Ctx *, U8 digest[SHARKSSL_SHA256_HASH_LEN])
finish
SHARKSSL_API int SharkSslSha256Ctx_append(SharkSslSha256Ctx *, const U8 *data, U32 len)
append
@ SHARKSSL_ECDSA_OK
OK.
Definition: SharkSSL.h:2579
SHA256.
Definition: SharkSslCrypto.h:116

Verifying Binaries

On the device, hash the original firmware bytes and verify the received signature against that digest. The embedded public key below matches the demonstration signing key above. Stage incoming data in an inactive area; activate it only after verification and the application's other update checks succeed. A read or signature-extraction error must abort the update.

/* Matching public key converted by SharkSslParseKey. */
const U8 sharkSslPubECKey256[72] =
{
0x30, 0x82, 0x00, 0x00, 0x0A, 0x00, 0x17, 0x20,
0x11, 0x23, 0xE8, 0xEA, 0x05, 0x14, 0x28, 0xE0,
0x37, 0xAC, 0xF3, 0x0E, 0x35, 0x58, 0x58, 0xA8,
0x16, 0x0B, 0x62, 0xEB, 0xEE, 0xC9, 0x06, 0x2F,
0x2A, 0xCF, 0x83, 0x1F, 0x93, 0x3C, 0xDF, 0x84,
0x04, 0xBF, 0x4E, 0x80, 0x5D, 0xA8, 0x0D, 0x15,
0xD7, 0x02, 0xAD, 0x8E, 0xD0, 0xFA, 0xE2, 0xB5,
0x69, 0x8C, 0x92, 0x00, 0xF5, 0xB6, 0xA8, 0x1C,
0x0D, 0x7F, 0x9E, 0x5C, 0x27, 0x93, 0x5C, 0x57
};
U8 dataChunk[256];
U8 sha256digest[SHARKSSL_SHA256_HASH_LEN];
U8 *signature;
U32 len;
U16 sl;
SharkSslSha256Ctx ctx; /* Hash engine */
/* Hash each firmware block in order as it arrives.
Note: Function receiveFirmwareChunk is assumed to stop at the end
of the firmware and just before the attached signature starts.
The application must distinguish normal EOF from a read error.
*/
while(receiveFirmwareChunk(dataChunk,&len))
{
SharkSslSha256Ctx_append(&ctx, dataChunk, len);
copyFirmwareChunk2FlashMemory(dataChunk, len); /* Inactive staging area */
}
/* All firmware data chunks read: Finish the hash calculation */
SharkSslSha256Ctx_finish(&ctx, sha256digest);
/* The following device specific function extracts the signature from
the TCP stream -- the binary data immediately following the firmware.
*/
signature = getSignatureFromStream(&sl);
/* Verify the received DER signature using the public key and binary digest.
getSignatureFromStream must return NULL on failure and set sl on success.
Pass SHARKSSL_HASHID_SHA256, not the digest size.
*/
(SharkSslECCKey) sharkSslPubECKey256, /* ECC public key */
signature, /* extracted from TCP stream */
sl, /* extracted signature length */
sha256digest, /* Computed digest calculated from received firmware */
{
/* Signature verified. Continue with the application's activation policy. */
}
else
{
/* Reject the update. Do not activate the staged image. */
}
SHARKSSL_API sharkssl_ECDSA_RetVal sharkssl_ECDSA_verify_hash(SharkSslECCKey pubkey, U8 *sig, U16 siglen, const U8 *hash, U8 hashID)
Verify a message using the ECC public key and a hash algorithm.

Interfacing LED Demo Programs to Hardware

The SMQ LED demo and the Minnow Server LED demo require that you interface the example code to your development board.

The following code is extracted from the mbed platform example. The code example shows how the required interface functions are connected to the LEDS on an mbed board with four LEDS.

#include "mbed.h"
#include "ledctrl.h"
#include "EthernetInterface.h"
DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);
static const LedInfo ledInfo[] = {
{
"LED 1",
LedColor_yellow,
1
},
{
"LED 2",
LedColor_red,
2
},
{
"LED 3",
LedColor_blue,
3
},
{
"LED 4",
LedColor_yellow,
4
}
};
static int leds[sizeof(ledInfo)/sizeof(ledInfo[1])];
/* Returns the LED on/off state for led with ID 'ledId'.
*/
int getLedState(int ledId)
{
baAssert(ledId >= 1 && ledId <= sizeof(ledInfo)/sizeof(ledInfo[1]));
return leds[ledId-1];
}
/*
Return an array of LedInfo (struct). Each element in the array
provides information for one LED. The 'len' argument must be set by
function getLedInfo. The out argument 'len' specifies the length of
the returned array, that is, number of LEDs in the device. Each LED
has a name, color, and ID. The ID, which provides information about
which LED to turn on/off, is used by control messages sent between
device code and UI clients. The IDs for a four LED device can for
example be 1,2,3,4.
*/
const LedInfo* getLedInfo(int* len)
{
*len = sizeof(ledInfo) / sizeof(ledInfo[0]);
return ledInfo;
}
/* Returns the name of this device. The name is presented by UI
clients such as browsers.
*/
const char* getDevName(void)
{
return "mbed";
}
static void blinkAll(int fast)
{
int i;
for(i = 1 ; i <= 4 ; i++) setLed(i, FALSE);
for(i = 1 ; i <= 4 ; i++)
{
setLed(i, TRUE);
wait_ms(fast ? 120: 400);
}
for(i = 1 ; i <= 4 ; i++)
{
setLed(i, FALSE);
wait_ms(fast ? 120: 400);
}
}
static void blink(int led)
{
int i;
for(i = 0 ; i < 30; i++)
{
setLed(led, 1);
wait_ms(120);
setLed(led, 0);
wait_ms(120);
}
}
/* Command sent by UI client to turn LED with ID on or off. This
function must set the LED to on if 'on' is TRUE and off if 'on' is FALSE.
*/
int setLed(int ledId, int on)
{
baAssert(ledId >= 1 && ledId <= sizeof(ledInfo)/sizeof(ledInfo[1]));
leds[ledId-1] = on;
on = on ? 0 : 1; /* Invert */
switch(ledId)
{
case 1: led1 = on; break;
case 2: led2 = on; break;
case 3: led3 = on; break;
case 4: led4 = on; break;
}
return 0;
}
/*
An optional function that enables LEDs to be set directly by the
device. This function is typically used by devices that include one
or more buttons. A button click may for example turn on a specific
LED. The function is called at intervals (polled) by the LED device
code. The function may for example detect a button click and return
the information to the caller. Arguments 'ledId' and 'on' are out
arguments, where 'ledId' is set to the LED ID and 'on' is set to
TRUE for on and FALSE for off. The function must return TRUE (a non
zero value) if the LED is to be set on/off and zero on no
change. Create an empty function returning zero if you do not plan
on implementing this feature.
*/
int setLedFromDevice(int* ledId, int* on)
{
return FALSE;
}
/*
The purpose with program status is to provide visible program
connection state information during startup. The function is typically
used to signal information via the LEDs. Simply create an empty
function if you do not want to set program status.
*/
void setProgramStatus(ProgramStatus s)
{
int i;
for(i = 1 ; i <= 4 ; i++) setLed(i, FALSE);
switch(s)
{
case ProgramStatus_Restarting:
blinkAll(FALSE);
case ProgramStatus_Starting:
blinkAll(TRUE);
break;
case ProgramStatus_Connecting:
setLed(1, TRUE);
break;
case ProgramStatus_SslHandshake:
setLed(2, TRUE);
break;
case ProgramStatus_DeviceReady:
for(i = 1 ; i <= 4 ; i++) setLed(i, TRUE);
wait(1);
for(i = 1 ; i <= 4 ; i++) setLed(i, FALSE);
break;
case ProgramStatus_CloseCommandReceived:
blink(3);
break;
default:
blinkAll(FALSE);
switch(s)
{
case ProgramStatus_SocketError:
case ProgramStatus_DnsError:
case ProgramStatus_WebServiceNotAvailError:
case ProgramStatus_InvalidCommandError:
case ProgramStatus_PongResponseError:
blink(1);
break;
case ProgramStatus_ConnectionError:
blink(2);
break;
case ProgramStatus_CertificateNotTrustedError:
case ProgramStatus_SslHandshakeError:
blink(3);
break;
case ProgramStatus_MemoryError:
for(i = 1 ; i <= 4 ; i++) blink(i);
break;
}
}
}
/* Required by SMQ examples.
The unique ID is used when calling the SMQ constructor.
The unique ID is typically set to the MAC address. See the SMQ
documentation for details:
https://realtimelogic.com/ba/doc/en/C/shark/structSharkMQ.html
*/
int getUniqueId(const char** id)
{
#if NO_MAC
*id = "ID: " __DATE__ __TIME__;
return strlen(*id);
#else
*id=eth.getMACAddress();
return 6;
#endif
}