forked from pubnub/arduino
-
Notifications
You must be signed in to change notification settings - Fork 0
/
PubNub.h
233 lines (201 loc) · 8.34 KB
/
PubNub.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#ifndef PubNub_h
#define PubNub_h
#include <stdint.h>
/* By default, the PubNub library is built to work with the Ethernet
* shield. WiFi shield support can be enabled by commenting out the
* following line and uncommenting the line after that. Refer
* to the PubNubJsonWifi sketch for a complete example. */
#define PubNub_Ethernet
//#define PubNub_WiFi
#if defined(PubNub_Ethernet)
#include <Ethernet.h>
#define PubNub_BASE_CLIENT EthernetClient
#elif defined(PubNub_WiFi)
#include <WiFi.h>
#define PubNub_BASE_CLIENT WiFiClient
#else
#error PubNub_BASE_CLIENT set to an invalid value!
#endif
/* Some notes:
*
* (i) There is no SSL support on Arduino, it is unfeasible with
* Arduino Uno or even Arduino Mega's computing power and memory limits.
* All the traffic goes on the wire unencrypted and unsigned.
*
* (ii) We re-resolve the origin server IP address before each request.
* This means some slow-down for intensive communication, but we rather
* expect light traffic and very long-running sketches (days, months),
* where refreshing the IP address is quite desirable.
*
* (iii) We let the users read replies at their leisure instead of
* returning an already preloaded string so that (a) they can do that
* in loop() code while taking care of other things as well (b) we don't
* waste precious RAM by pre-allocating buffers that are never needed.
*
* (iv) If you are having problems connecting, maybe you have hit
* a bug in Debian's version of Arduino pertaining the DNS code. Try using
* an IP address as origin and/or upgrading your Arduino package.
*
* (v) The optional timeout parameter allows you to specify a timeout
* period after which the subscribe call shall be retried. Note
* that this timeout is applied only for reading response, not for
* connecting or sending data; use retransmission parameters of
* the Ethernet library to tune this. As a rule of thumb, timeout
* smaller than 30 seconds may still block longer with flaky
* network. Default server-side timeout of PubNub API is 300s.
*
* (vi) If some of the PubNub calls fail with your WiFi shield (e.g. you
* see "subscribe error" and similar messages in serial console), your
* WiFi shield firmware may be buggy - e.g. a WiFi shield bought
* commercially in May 2013 had a firmware that had to be upgaded.
* This is not so difficult to do, simply follow:
*
* http://arduino.cc/en/Hacking/WiFiShieldFirmwareUpgrading
*
* (vii) The vendor firmware for the WiFi shield has dubious TCP implementation;
* for example, TCP ports of outgoing connections are always chosen from the
* same sequence, so if you reset your Arduino, some of the new connections
* may interfere with an outstanding TCP connection that has not been closed
* before the reset; i.e. you will typically see a single failed request
* somewhere down the road after a reset.
*
* (viii) It is essential to use a new enough Arduino version so that
* the WiFi library actually works properly. Most notably, version 1.0.5
* has been confirmed to work while Arduino 1.0.4 is broken.
*/
/* This class is a thin EthernetClient wrapper whose goal is to
* automatically acquire time token information when reading
* subscribe call response.
*
* (i) The user application sees only the JSON body, not the timetoken.
* As soon as the body ends, PubSubclient reads the rest of HTTP reply
* itself and disconnects. The stored timetoken is used in the next call
* to the PubSub::subscribe method then. */
class PubSubClient : public PubNub_BASE_CLIENT {
public:
PubSubClient() :
PubNub_BASE_CLIENT(), json_enabled(false)
{
strcpy(timetoken, "0");
}
/* Customized functions that make reading stop as soon as we
* have hit ',' outside of braces and string, which indicates
* end of JSON body. */
virtual int read();
virtual int read(uint8_t *buf, size_t size);
virtual void stop();
/* Block until data is available. Returns false in case the
* connection goes down or timeout expires. */
bool wait_for_data(int timeout = 310);
/* Enable the JSON state machine. */
void start_body();
inline char *server_timetoken() { return timetoken; }
private:
void _state_input(uint8_t ch, uint8_t *nextbuf, size_t nextsize);
void _grab_timetoken(uint8_t *nextbuf, size_t nextsize);
/* JSON state machine context */
bool json_enabled:1;
bool in_string:1;
bool after_backslash:1;
int braces_depth;
/* Time token acquired during the last subscribe request. */
char timetoken[22];
};
enum PubNub_BH {
PubNub_BH_OK,
PubNub_BH_ERROR,
PubNub_BH_TIMEOUT,
};
class PubNub {
public:
/* Init the Pubnub Client API
*
* This should be called after Ethernet.begin().
* Note that the string parameters are not copied; do not
* overwrite or free the memory where you stored the keys!
* (If you are passing string literals, don't worry about it.)
* Note that you should run only a single publish at once.
*
* @param string publish_key required key to send messages.
* @param string subscribe_key required key to receive messages.
* @param string origin optional setting for cloud origin.
* @return boolean whether begin() was successful. */
bool begin(const char *publish_key, const char *subscribe_key, const char *origin = "pubsub.pubnub.com");
/* Set the UUID identification of PubNub client. This is useful
* e.g. for presence identification.
*
* Pass NULL to unset. The string is not copied over (just like
* in begin()). See the PubNubSubscriber example for simple code
* that generates a random UUID (although not 100% reliable). */
void set_uuid(const char *uuid);
/* Set the authorization key/token of PubNub client. This is useful
* e.g. for access rights validation (PAM).
*
* Pass NULL to unset. The string is not copied over (just like
* in begin()). */
void set_auth(const char *auth);
/* Publish
*
* Send a message (assumed to be well-formed JSON) to a given channel.
*
* Note that the reply can be obtained using code like:
client = publish("demo", "\"lala\"");
if (!client) return; // error
while (client->connected()) {
// More sophisticated code will want to add timeout handling here
while (client->connected() && !client->available()) ; // wait
char c = client->read();
Serial.print(c);
}
client->stop();
* You will get content right away, the header has been already
* skipped inside the function. If you do not care about
* the reply, just call client->stop(); immediately.
*
* It returns an object that is typically EthernetClient (but it
* can be a WiFiClient if you enabled the WiFi shield).
*
* @param string channel required channel name.
* @param string message required message string in JSON format.
* @param string timeout optional timeout in seconds.
* @return string Stream-ish object with reply message or NULL on error. */
PubNub_BASE_CLIENT *publish(const char *channel, const char *message, int timeout = 30);
/**
* Subscribe
*
* Listen for a message on a given channel. The function will block
* and return when a message arrives. Typically, you will run this
* function from loop() function to keep listening for messages
* indefinitely.
*
* As a reply, you will get a JSON array with messages, e.g.:
* ["msg1",{msg2:"x"}]
* and so on. Empty reply [] is also normal and your code must be
* able to handle that. Note that the reply specifically does not
* include the time token present in the raw reply.
*
* @param string channel required channel name.
* @param string timeout optional timeout in seconds.
* @return string Stream-ish object with reply message or NULL on error. */
PubSubClient *subscribe(const char *channel, int timeout = 310);
/**
* History
*
* Receive list of the last N messages on the given channel.
*
* @param string channel required channel name.
* @param int limit optional number of messages to retrieve.
* @param string timeout optional timeout in seconds.
* @return string Stream-ish object with reply message or NULL on error. */
PubNub_BASE_CLIENT *history(const char *channel, int limit = 10, int timeout = 310);
private:
enum PubNub_BH _request_bh(PubNub_BASE_CLIENT &client, unsigned long t_start, int timeout, char qparsep);
const char *publish_key, *subscribe_key;
const char *origin;
const char *uuid;
const char *auth;
PubNub_BASE_CLIENT publish_client, history_client;
PubSubClient subscribe_client;
};
extern class PubNub PubNub;
#endif