PubNub Mbed SDK 4.14.1
Get Code: Source
https://os.mbed.com/users/sveljko/code/Pubnub_mbed2_sync/
You can use the library in the mbed Online Compiler, or clone the Mercurial repository and use it in any other way you wish.
Hello World
You can make your own program, or start by importing the sample program.
The sample is for the AT&T IoT Starter Kit, which uses the Freescale (now NXP) K64F and WNC modem, so if you use some other hardware you would need to change the library/driver you use for networking, but, as far as Pubnub API is concerned, the sample is usable on other hardware.
If you want to make your own program from scratch, then:
-
Import the Pubnub library.
For now, there is only the sync library
-
If your MBed project/program doesn't already use it, import the network library.
The sample uses the WNC modem library. The latest revision at the time of this writing has build issues, but we don't need it, so use the revision from the URL.
-
Add a
pubnub_config.h
to your program.You can copy the contents from the sample program and then tinker with it, if you wish. All the
knobs
are documented, so here we'll look at only thewhere to log
configuration, near the end ofpubnub_config.h
:#include <MODSERIAL.h>
extern MODSERIAL pc;
#define PUBNUB_LOG_PRINTF(...) pc.printf(__VA_ARGS__)This expects that in your program somewhere you define the
MODSERIAL pc
global object. In the sample program, it's defined inmain.cpp
:MODSERIAL pc(USBTX,USBRX,256,256);
Of course, you can
#define PUBNUB_LOG_PRINTF(...)
to whatever you wish to use for Pubnub logging. -
If your MBed project/program doesn't already use/initialze it, initialize the networking library.
The sample does this by defining an object of class
WNCInterface
and then calls itsinit
andconnect
member functionsWNCInterface eth;
int main() {
// .... some other init code, like serial port (`pc`)
pc.printf("init() returned 0x%04X" CRLF, eth.init(NULL,&pc));
eth.connect();
pc.printf("IP Address: %s" CRLF, eth.getIPAddress());
// now modem init is done, you can use Pubnub C-core APIs
} -
After the modem has been initialized, you can use the PubNub C-core APIs like on any other supported platform.
Including the header
The calling pattern you choose to use with PubNub (Synchronous vs Callback) will determine which header to import. More information on these calling patterns can be found later on in this guide.
At this time, only the sync
pattern is supported on MBed, so import only pubnub_sync.h
:
#include "pubnub_sync.h"
Memory allocation
This client uses dynamic memory allocation for the PubNub contexts, but the usage is the same as for any other Pubnub C client - always use pubnub_alloc()
to create a context (and check its return value) and always use pubnub_free()
to dispose of a context.
Required UUID
Always set the UUID
to uniquely identify the user or device that connects to PubNub. This UUID
should be persisted, and should remain unchanged for the lifetime of the user or the device. If you don't set the UUID
, you won't be able to connect to PubNub.
#include "pubnub_alloc.h"
int main() {
pubnub_t *ctx = pubnub_alloc();
if (NULL == ctx) {
return -1;
}
/* Do something with ctx...
and then: */
pubnub_free(ctx);
return 0
}
Timers
We only provide one timer - the (total) transaction
timer. In general, it is started when a transaction is started and stopped when a transaction is finished. If it expires, the transaction will be cancelled. Keep in mind that this canceling is local, so, for example, if you already published a message, but, for some reason, the HTTP response didn't arrive in time, this canceling will not revoke
the publish - it will just stop the wait for response.
If the transaction timer expires, the outcome of the transaction will be timeout
- different than when you cancel a transaction yourself.
The actual duration of the timer is at least as long as you set it. It could be significantly higher, depending on various platform issues. But, in general, it will be close to what you set.
You should set the timer after initializing the context and before starting a transaction. The duration you set will be used for all subsequent transactions, regardless of their type (that is, for publish and subscribe and all other).
Thread safety
C-core supports thread-safe operation, though, for performance, you may think about not using it. To use thread-safety support, define the preprocessor symbol PUBNUB_THREADSAFE
(just define it, the value does not matter). The recommended place to do it is in pubnub_config.h
.
Thread-safe usage
Thread safety is internal. Just because you can access the PubNub context through the PubNub C-core SDK API from different threads safely, doesn't mean you're off the hook
for your own data that is related to a context. For example, if you're using the callback interface and signalling an event from it to other (worker
) thread(s), you have to synchronise that data transfer yourself.
If you compiled thread-safety support in, you are free to access the same context from different threads, pretty much in any way you wish. However, there are some advised guidelines you should follow:
- If you're using the sync blocking interface, threads that come to wait on the context may wait a long time, so try to avoid it (also, re-think your whole need for a thread-safe C-core)
- If you're using the sync non-blocking interface by calling
pubnub_await
, things are pretty much the same as for sync blocking interface - If you're using the sync non-blocking interface and avoid
pubnub_await
, waiting threads will not block so long, but, pretty much the only useful thing you can do is cancel a transaction from another thread. - Using the sync interface, it's perfectly fine to call
pubnub_await
orpubnub_last_result
in different threads, but, you probably shouldn't do that, as it will make debugging harder. - If you're using the callback interface, it's perfectly fine to call Functions from your callback, but, you should avoid doing that, except for some helper functions. Following this guideline will make your debugging, thus life, a lot easier
Thread-unsafe usage
If you compile without thread-safety support, obviously, you will have an SDK which is not thread safe - that is, it is not safe to use a single context from more than one thread at the same time. So, if you're using such SDK configuration in a multithreaded code, then:
- If at all possible, use a single context from only one thread - the one that created it.
- If this is not possible, provide some synchronization yourself, for example, using pthread condition variables, or just mutexes, or some higher abstraction, like message queues.
- As a special case, if you're using the callback interface, you can start a transaction in one thread and then don't touch the context from that thread any more - use it only in the callback. This is safe.
Context usage
Keep in mind that it is perfectly safe to use different contexts from different threads at the same time. To each (thread) its own (context).
Transaction and operation
The MBed C SDK operates as a set of transactions. A transaction is initiated by the client SDK and is defined as a single message exchange between the SDK and PubNub service. Every interaction that the client SDK initiates with PubNub is sequenced as a series of transactions which ultimately results in a PubNub service-specific operation.
Status and events
The SDK provides a set of status and event identifiers which can help developers interact with the library. The status identifier codes are returned as part of the SDK's API invocation. These are used by the developer to check for status of transactions or for detecting normal / abnormal conditions in an API call. Some of the commonly used status codes are as follows:
PNR_OK
: Success, the transaction finished successfullyPNR_STARTED
: The previously initiated transaction has started.PNR_IN_PROGRESS
: Indicates that the previous transaction with PubNub service is still in progress.
Refer to the API docs for a complete list of status identifiers supported by the library.
Events
refer to the PubNub REST operations which are initiated by the client SDK. The most common example of events are subscribe and publish. A client subscribing for a channel is a subscribe event and a client publishing a message on a channel is a publish event.
Some of the common event identifiers are as follows:
PBTT_SUBSCRIBE
: Subscriber operationPBTT_PUBLISH
: Publish operation
Refer to the API docs for a complete list of operations supported by the SDK.
Sync calling pattern
This SDK provides sync
interface for retrieving the outcome of a Pubnub request/transaction/operation.
The sync
interface works like this:
- Start a transaction (say, publish - using
pubnub_publish()
) - Either
pubnub_await()
the outcome, or use your own loop in which you checkif (PNR_STARTED != pubnub_last_result())
- Handle the outcome as you wish
This is illustrated in the Hello World example below (which is the same for any platform that supports sync
interface).
Sync code sample
Required UUID
Always set the UUID
to uniquely identify the user or device that connects to PubNub. This UUID
should be persisted, and should remain unchanged for the lifetime of the user or the device. If you don't set the UUID
, you won't be able to connect to PubNub.
#include "mbed.h"
#include "WNCInterface.h" /* or other network lib/driver */
#include "pubnub_sync.h"
WNCInterface eth; /* Or another network interface */
MODSERIAL pc(USBTX,USBRX,256,256); /* Or other log/debug interface */
int main()
{
pc.baud(115200);
pc.printf("init() returned 0x%04X" CRLF, eth.init(NULL,&pc));
eth.connect();
pc.printf("IP Address: %s" CRLF, eth.getIPAddress());
show all 57 linesCopy and paste examples
In addition to the Hello World sample code, we also provide some copy and paste snippets of common API functions:
Init
Instantiate a new Pubnub instance. Only the subscribe_key
is mandatory. Also include publish_key
if you intend to publish from this instance.
Required UUID
Always set the UUID
to uniquely identify the user or device that connects to PubNub. This UUID
should be persisted, and should remain unchanged for the lifetime of the user or the device. If you don't set the UUID
, you won't be able to connect to PubNub.
pubnub_init(
ctx,
/*publish key*/"demo",
/*subscribe key*/"demo"
);
Time
Call pubnub_time()
to verify the client connectivity to the origin:
pubnub_time(ctx);
pbresult = pubnub_await(ctx);
if (PNR_OK == pbresult) {
char const *gotten_time = pubnub_get();
}
Subscribe
Subscribe (listen on) a channel (it's async!):
Message retrieval
Typically, you will want two separate contexts for publish and subscribe. When changing the active set of subscribed channels, first call pubnub_leave()
on the old set.
The pubnub_subscribe()
interface is essentially a transaction to start listening on the channel for arrival of next message. This has to be followed by pubnub_get()
call to retrieve the actual message, once the subscribe transaction completes successfully. This needs to be performed every time it is desired to retrieve a message from the channel.
pubnub_subscribe(
ctx,
"my_channel",
NULL
);
pbresult = pubnub_await(ctx);
if (PNR_OK == pbresult) {
char const *message = pubnub_get(ctx);
while (message != NULL) {
message = pubnub_get(ctx);
}
}
Publish
Publish a message to a channel:
pubnub_publish(
ctx,
"my_channel",
"\"message\""
);
pbresult = pubnub_await(ctx);
if (PNR_OK == pbresult) {
/* Published successfully */
}
Here Now
Get occupancy of who's here now
on the channel by UUID:
Requires Presence add-on
This method requires that the Presence add-on is enabled for your key in the Admin Portal. Read the support page on enabling add-on features on your keys.
// Sync
pubnub_here_now(
ctx,
"my_channel",
NULL
);
pbresult = pubnub_await(ctx);
if (PNR_OK == pbresult) {
char const *json_response = pubnub_get(ctx);
}
Presence
Subscribe to real-time Presence events, such as join
, leave
, and timeout
, by UUID. Setting the presence attribute to a callback will subscribe to presents events on my_channel
:
Requires Presence add-on
This method requires that the Presence add-on is enabled for your key in the Admin Portal. Read the support page on enabling add-on features on your keys.
// Sync
char *presence_channel = malloc(strlen(channel) + strlen(PUBNUB_PRESENCE_SUFFIX) + 1);
strcpy(presence_channel, channel);
strcat(presence_channel, PUBNUB_PRESENCE_SUFFIX);
pubnub_subscribe(
ctx,
presence_channel,
NULL
);
pbresult = pubnub_await(ctx);
if (PNR_OK == pbresult) {
char const *presence_event = pubnub_get(ctx);
while (presnce_event != NULL) {
presence_event = pubnub_get(ctx);
show all 17 linesHistory
Retrieve published messages from archival storage:
Requires Message Persistence
This method requires that Message Persistence is enabled for your key in the Admin Portal. Read the support page on enabling add-on features on your keys.
// Sync
enum pubnub_res res;
pubnub_history(
pn,
"history_channel",
10,
false
);
res = pubnub_await(pn);
if (PNR_OK == res) {
puts("Got history! Messages:");
for (;;) {
const char *msg = pubnub_get(pn);
show all 23 linesUnsubscribe
Stop subscribing (listening) to a channel.
To unsubscribe
, you need to cancel a subscribe transaction.
-
If you configured SDK to be thread-safe, you can cancel at any time, but, the cancelling may actually fail - that is, your thread may wait for another thread to finish working with the context, and by the time your cancel request gets processed, the transaction may finish.
-
If you configured SDK to not be thread-safe, the only safe way to do it is to use the
sync
interface and:- Set the context to use
non-blocking I/O
. - Wait for the outcome in a loop, checking for
pubnub_last_result()
- rather than callingpubnub_await()
- If a condition occurs that prompts you to
unsubscribe
, callpubnub_cancel()
- Wait for the cancellation to finish (here you can call
pubnub_await()
, unless you want to do other stuff while you wait)
- Set the context to use
pbresult = pubnub_subscribe(
ctx,
"my_channel",
NULL
);
/* If we don't set non-blocking I/O, we can't get out of a blocked read */
pubnub_set_non_blocking_io(ctx);
/* Can't use pubnub_await() here, it will block */
while (PNR_STARTED == pbresult) {
pbresult = pubnub_last_result(ctx);
/* Somehow decide we want to quit / unsubscribe */
if (should_stop()) {
pubnub_cancel(ctx);
show all 25 lines