├── .gitignore ├── Adafruit_MQTT.cpp ├── Adafruit_MQTT_Client.cpp ├── Milkcocoa.cpp ├── Milkcocoa.h ├── README.md ├── aJSON.cpp ├── aJson ├── .gitignore ├── README.md ├── keywords.txt └── library.json ├── examples ├── milkcocoa_esp8266 │ └── milkcocoa_esp8266.ino ├── milkcocoa_esp8266_apikey_auth │ └── milkcocoa_esp8266_apikey_auth.ino ├── milkcocoa_esp8266_tout │ └── milkcocoa_esp8266_tout.ino └── milkcocoa_esp8266_tout_by_half_hour │ └── milkcocoa_esp8266_tout_by_half_hour.ino ├── include ├── Adafruit │ ├── Adafruit_MQTT.h │ ├── Adafruit_MQTT_CC3000.h │ ├── Adafruit_MQTT_Client.h │ └── Adafruit_MQTT_FONA.h └── aJson │ ├── aJSON.h │ └── stringbuffer.h ├── library.properties └── stringbuffer.c /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | -------------------------------------------------------------------------------- /Adafruit_MQTT.cpp: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Adafruit Industries 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | #include "include/Adafruit/Adafruit_MQTT.h" 23 | 24 | 25 | void printBuffer(uint8_t *buffer, uint8_t len) { 26 | for (uint8_t i=0; i> 8; p++; 45 | p[0] = len & 0xFF; p++; 46 | memcpy(p, s, len); 47 | return p+len; 48 | } 49 | */ 50 | 51 | static uint8_t *stringprint_P(uint8_t *p, const char *s, uint16_t maxlen=0) { 52 | // If maxlen is specified (has a non-zero value) then use it as the maximum 53 | // length of the source string to write to the buffer. Otherwise write 54 | // the entire source string. 55 | uint16_t len = strlen_P(s); 56 | if (maxlen > 0 && len > maxlen) { 57 | len = maxlen; 58 | } 59 | /* 60 | for (uint8_t i=0; i> 8; p++; 65 | p[0] = len & 0xFF; p++; 66 | strncpy_P((char *)p, s, len); 67 | return p+len; 68 | } 69 | 70 | 71 | // Adafruit_MQTT Definition //////////////////////////////////////////////////// 72 | 73 | Adafruit_MQTT::Adafruit_MQTT(const char *server, uint16_t port, const char *cid, 74 | const char *user, const char *pass) { 75 | servername = server; 76 | portnum = port; 77 | clientid = cid; 78 | username = user; 79 | password = pass; 80 | 81 | for (uint8_t i=0; itopic, subscriptions[i]->qos); 144 | if (!sendPacket(buffer, len)) 145 | return -1; 146 | 147 | // Get SUBACK 148 | len = readPacket(buffer, 5, CONNECT_TIMEOUT_MS); 149 | // DEBUG_PRINT(F("SUBACK:\t")); 150 | // DEBUG_PRINTBUFFER(buffer, len); 151 | // if (false) { 152 | // return 6; // failure to subscribe 153 | // } 154 | } 155 | 156 | return 0; 157 | } 158 | 159 | const __FlashStringHelper* Adafruit_MQTT::connectErrorString(int8_t code) { 160 | switch (code) { 161 | case 1: return F("Wrong protocol"); 162 | case 2: return F("ID rejected"); 163 | case 3: return F("Server unavail"); 164 | case 4: return F("Bad user/pass"); 165 | case 5: return F("Not authed"); 166 | case 6: return F("Failed to subscribe"); 167 | case -1: return F("Connection failed"); 168 | default: return F("Unknown error"); 169 | } 170 | } 171 | 172 | bool Adafruit_MQTT::publish(const char *topic, const char *data, uint8_t qos) { 173 | // Construct and send publish packet. 174 | uint8_t len = publishPacket(buffer, topic, data, qos); 175 | if (!sendPacket(buffer, len)) 176 | return false; 177 | 178 | // If QOS level is high enough verify the response packet. 179 | if (qos > 0) { 180 | len = readPacket(buffer, 4, PUBLISH_TIMEOUT_MS); 181 | DEBUG_PRINT(F("Publish QOS1+ reply:\t")); 182 | DEBUG_PRINTBUFFER(buffer, len); 183 | //TODO: Verify response packet? 184 | } 185 | 186 | return true; 187 | } 188 | 189 | bool Adafruit_MQTT::subscribe(Adafruit_MQTT_Subscribe *sub) { 190 | uint8_t i; 191 | // see if we are already subscribed 192 | for (i=0; itopic) != topiclen) 231 | continue; 232 | // Stop if the subscription topic matches the received topic. Be careful 233 | // to make comparison case insensitive. 234 | if (strncasecmp_P((char*)buffer+4, subscriptions[i]->topic, topiclen) == 0) { 235 | DEBUG_PRINT(F("Found sub #")); DEBUG_PRINTLN(i); 236 | break; 237 | } 238 | } 239 | } 240 | if (i==MAXSUBSCRIPTIONS) return NULL; // matching sub not found ??? 241 | 242 | // zero out the old data 243 | memset(subscriptions[i]->lastread, 0, SUBSCRIPTIONDATALEN); 244 | 245 | datalen = len - topiclen - 4; 246 | // if (datalen > SUBSCRIPTIONDATALEN) { 247 | // datalen = SUBSCRIPTIONDATALEN-1; // cut it off 248 | // } 249 | // extract out just the data, into the subscription object itself 250 | memcpy(subscriptions[i]->lastread, buffer+4+topiclen, datalen); 251 | subscriptions[i]->datalen = datalen; 252 | DEBUG_PRINT(F("Data len: ")); DEBUG_PRINTLN(datalen); 253 | DEBUG_PRINT(F("Data: ")); DEBUG_PRINTLN((char *)subscriptions[i]->lastread); 254 | 255 | // return the valid matching subscription 256 | return subscriptions[i]; 257 | } 258 | 259 | bool Adafruit_MQTT::ping(uint8_t times) { 260 | while (times) { 261 | // Construct and send ping packet. 262 | uint8_t len = pingPacket(buffer); 263 | if (!sendPacket(buffer, len)) 264 | return false; 265 | 266 | // Process ping reply. 267 | len = readPacket(buffer, 2, PING_TIMEOUT_MS); 268 | if (buffer[0] == (MQTT_CTRL_PINGRESP << 4)) 269 | return true; 270 | } 271 | return false; 272 | } 273 | 274 | // Packet Generation Functions ///////////////////////////////////////////////// 275 | 276 | // The current MQTT spec is 3.1.1 and available here: 277 | // http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718028 278 | // However this connect packet and code follows the MQTT 3.1 spec here (some 279 | // small differences in the protocol): 280 | // http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#connect 281 | uint8_t Adafruit_MQTT::connectPacket(uint8_t *packet) { 282 | uint8_t *p = packet; 283 | uint16_t len; 284 | 285 | // fixed header, connection messsage no flags 286 | p[0] = (MQTT_CTRL_CONNECT << 4) | 0x0; 287 | p+=2; 288 | // fill in packet[1] last 289 | 290 | p = stringprint_P(p, PSTR("MQTT")); 291 | 292 | p[0] = MQTT_PROTOCOL_LEVEL; 293 | p++; 294 | 295 | p[0] = MQTT_CONN_CLEANSESSION; 296 | if (pgm_read_byte(username) != 0) 297 | p[0] |= MQTT_CONN_USERNAMEFLAG; 298 | if (pgm_read_byte(password) != 0) 299 | p[0] |= MQTT_CONN_PASSWORDFLAG; 300 | p++; 301 | // TODO: add WILL support? 302 | 303 | p[0] = MQTT_CONN_KEEPALIVE >> 8; 304 | p++; 305 | p[0] = MQTT_CONN_KEEPALIVE & 0xFF; 306 | p++; 307 | 308 | p = stringprint_P(p, clientid, 23); // Limit client ID to first 23 characters. 309 | 310 | if (pgm_read_byte(username) != 0) { 311 | p = stringprint_P(p, username); 312 | } 313 | if (pgm_read_byte(password) != 0) { 314 | p = stringprint_P(p, password); 315 | } 316 | 317 | len = p - packet; 318 | 319 | packet[1] = len-2; // don't include the 2 bytes of fixed header data 320 | DEBUG_PRINTLN(F("MQTT connect packet:")); 321 | DEBUG_PRINTBUFFER(buffer, len); 322 | return len; 323 | } 324 | 325 | uint8_t Adafruit_MQTT::publishPacket(uint8_t *packet, const char *topic, 326 | const char *data, uint8_t qos) { 327 | uint8_t *p = packet; 328 | uint16_t len; 329 | 330 | p[0] = MQTT_CTRL_PUBLISH << 4 | qos << 1; 331 | // fill in packet[1] last 332 | p+=2; 333 | 334 | p = stringprint_P(p, topic); 335 | 336 | memcpy(p, data, strlen(data)); 337 | p+=strlen(data); 338 | len = p - packet; 339 | packet[1] = len-2; // don't include the 2 bytes of fixed header data 340 | DEBUG_PRINTLN(F("MQTT publish packet:")); 341 | DEBUG_PRINTBUFFER(buffer, len); 342 | return len; 343 | } 344 | 345 | uint8_t Adafruit_MQTT::subscribePacket(uint8_t *packet, const char *topic, 346 | uint8_t qos) { 347 | uint8_t *p = packet; 348 | uint16_t len; 349 | 350 | p[0] = MQTT_CTRL_SUBSCRIBE << 4 | MQTT_QOS_1 << 1; 351 | // fill in packet[1] last 352 | p+=2; 353 | 354 | // put in a message id, 355 | p[0] = 0xAD; 356 | p[1] = 0xAF; 357 | p+=2; 358 | 359 | p = stringprint_P(p, topic); 360 | 361 | p[0] = qos; 362 | p++; 363 | 364 | len = p - packet; 365 | packet[1] = len-2; // don't include the 2 bytes of fixed header data 366 | DEBUG_PRINTLN(F("MQTT subscription packet:")); 367 | DEBUG_PRINTBUFFER(buffer, len); 368 | return len; 369 | } 370 | 371 | uint8_t Adafruit_MQTT::pingPacket(uint8_t *packet) { 372 | packet[0] = MQTT_CTRL_PINGREQ << 4; 373 | packet[1] = 0; 374 | DEBUG_PRINTLN(F("MQTT ping packet:")); 375 | DEBUG_PRINTBUFFER(buffer, 2); 376 | return 2; 377 | } 378 | 379 | 380 | // Adafruit_MQTT_Publish Definition //////////////////////////////////////////// 381 | 382 | Adafruit_MQTT_Publish::Adafruit_MQTT_Publish(Adafruit_MQTT *mqttserver, 383 | const char *feed, uint8_t q) { 384 | mqtt = mqttserver; 385 | topic = feed; 386 | qos = q; 387 | } 388 | 389 | Adafruit_MQTT_Publish::Adafruit_MQTT_Publish(Adafruit_MQTT *mqttserver, 390 | const __FlashStringHelper *feed, uint8_t q) { 391 | mqtt = mqttserver; 392 | topic = (const char *)feed; 393 | qos = q; 394 | } 395 | 396 | bool Adafruit_MQTT_Publish::publish(int32_t i) { 397 | char payload[18]; 398 | itoa(i, payload, 10); 399 | return mqtt->publish(topic, payload, qos); 400 | } 401 | 402 | bool Adafruit_MQTT_Publish::publish(double f, uint8_t precision) { 403 | char payload[40]; // Need to technically hold float max, 39 digits and minus sign. 404 | dtostrf(f, 0, precision, payload); 405 | return mqtt->publish(topic, payload, qos); 406 | } 407 | 408 | bool Adafruit_MQTT_Publish::publish(uint32_t i) { 409 | char payload[18]; 410 | itoa(i, payload, 10); 411 | return mqtt->publish(topic, payload, qos); 412 | } 413 | 414 | bool Adafruit_MQTT_Publish::publish(const char *payload) { 415 | return mqtt->publish(topic, payload, qos); 416 | } 417 | 418 | 419 | // Adafruit_MQTT_Subscribe Definition ////////////////////////////////////////// 420 | 421 | Adafruit_MQTT_Subscribe::Adafruit_MQTT_Subscribe(Adafruit_MQTT *mqttserver, 422 | const char *feed, uint8_t q) { 423 | mqtt = mqttserver; 424 | topic = feed; 425 | qos = q; 426 | } 427 | 428 | Adafruit_MQTT_Subscribe::Adafruit_MQTT_Subscribe(Adafruit_MQTT *mqttserver, 429 | const __FlashStringHelper *feed, uint8_t q) { 430 | mqtt = mqttserver; 431 | topic = (const char *)feed; 432 | qos = q; 433 | } 434 | -------------------------------------------------------------------------------- /Adafruit_MQTT_Client.cpp: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Adafruit Industries 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | #include "include/Adafruit/Adafruit_MQTT_Client.h" 23 | 24 | 25 | bool Adafruit_MQTT_Client::connectServer() { 26 | // Grab server name from flash and copy to buffer for name resolution. 27 | memset(buffer, 0, sizeof(buffer)); 28 | strcpy_P((char *)buffer, servername); 29 | DEBUG_PRINT(F("Connecting to: ")); DEBUG_PRINTLN((char *)buffer); 30 | // Connect and check for success (0 result). 31 | int r = client->connect((char *)buffer, portnum); 32 | DEBUG_PRINT(F("Connect result: ")); DEBUG_PRINTLN(r); 33 | return r != 0; 34 | } 35 | 36 | bool Adafruit_MQTT_Client::disconnect() { 37 | // Stop connection if connected and return success (stop has no indication of 38 | // failure). 39 | if (client->connected()) { 40 | client->stop(); 41 | } 42 | return true; 43 | } 44 | 45 | bool Adafruit_MQTT_Client::connected() { 46 | // Return true if connected, false if not connected. 47 | return client->connected(); 48 | } 49 | 50 | uint16_t Adafruit_MQTT_Client::readPacket(uint8_t *buffer, uint8_t maxlen, 51 | int16_t timeout, 52 | bool checkForValidPubPacket) { 53 | /* Read data until either the connection is closed, or the idle timeout is reached. */ 54 | uint16_t len = 0; 55 | int16_t t = timeout; 56 | 57 | while (client->connected() && (timeout >= 0)) { 58 | //DEBUG_PRINT('.'); 59 | while (client->available()) { 60 | //DEBUG_PRINT('!'); 61 | char c = client->read(); 62 | timeout = t; // reset the timeout 63 | buffer[len] = c; 64 | //DEBUG_PRINTLN((uint8_t)c, HEX); 65 | len++; 66 | if (len == maxlen) { // we read all we want, bail 67 | DEBUG_PRINT(F("Read packet:\t")); 68 | DEBUG_PRINTBUFFER(buffer, len); 69 | return len; 70 | } 71 | 72 | // special case where we just one one publication packet at a time 73 | if (checkForValidPubPacket) { 74 | if ((buffer[0] == (MQTT_CTRL_PUBLISH << 4)) && (buffer[1] == len-2)) { 75 | // oooh a valid publish packet! 76 | DEBUG_PRINT(F("Read PUBLISH packet:\t")); 77 | DEBUG_PRINTBUFFER(buffer, len); 78 | return len; 79 | } 80 | } 81 | } 82 | timeout -= MQTT_CLIENT_READINTERVAL_MS; 83 | delay(MQTT_CLIENT_READINTERVAL_MS); 84 | } 85 | return len; 86 | } 87 | 88 | bool Adafruit_MQTT_Client::sendPacket(uint8_t *buffer, uint8_t len) { 89 | if (client->connected()) { 90 | uint16_t ret = client->write(buffer, len); 91 | DEBUG_PRINT(F("sendPacket returned: ")); DEBUG_PRINTLN(ret); 92 | if (ret != len) { 93 | DEBUG_PRINTLN("Failed to send complete packet.") 94 | return false; 95 | } 96 | } else { 97 | DEBUG_PRINTLN(F("Connection failed!")); 98 | return false; 99 | } 100 | return true; 101 | } 102 | -------------------------------------------------------------------------------- /Milkcocoa.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Technical Rockstars 5 | Copyright (C) 2015 Embedded and Real-Time Systems Laboratory 6 | Graduate School of Information Science, Nagoya Univ., JAPAN 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | #include "Milkcocoa.h" 26 | #include "stdio.h" 27 | 28 | 29 | DataElement::DataElement() { 30 | params = aJson.createObject(); 31 | paJsonObj = aJson.createObject(); 32 | aJson.addItemToObject(paJsonObj, "params", params); 33 | } 34 | 35 | DataElement::DataElement(char *json_string) { 36 | paJsonObj = aJson.parse(json_string); 37 | params = aJson.getObjectItem(paJsonObj, "params"); 38 | } 39 | 40 | DataElement::~DataElement() { 41 | aJson.deleteItem(paJsonObj); 42 | paJsonObj = NULL; 43 | params = NULL; 44 | } 45 | 46 | void DataElement::setValue(const char *key, const char *v) { 47 | aJson.addStringToObject(params, key, v); 48 | } 49 | 50 | void DataElement::setValue(const char *key, int v) { 51 | aJson.addNumberToObject(params, key, v); 52 | } 53 | 54 | void DataElement::setValue(const char *key, double v) { 55 | aJson.addNumberToObject(params, key, v); 56 | } 57 | 58 | char *DataElement::getString(const char *key) { 59 | aJsonObject* obj = aJson.getObjectItem(params, key); 60 | return obj->valuestring; 61 | } 62 | 63 | int DataElement::getInt(const char *key) { 64 | aJsonObject* obj = aJson.getObjectItem(params, key); 65 | if(obj == NULL) 66 | Serial.println("obj is NULL"); 67 | return obj->valueint; 68 | } 69 | 70 | float DataElement::getFloat(const char *key) { 71 | aJsonObject* obj = aJson.getObjectItem(params, key); 72 | return obj->valuefloat; 73 | } 74 | 75 | 76 | char *DataElement::toCharArray() { 77 | return aJson.print(paJsonObj); 78 | } 79 | 80 | Milkcocoa::Milkcocoa(Client *client, const char *host, uint16_t port, const char *_app_id, const char *client_id) { 81 | app_id = _app_id; 82 | mqtt = new Adafruit_MQTT_Client(client, host, port, client_id, "sdammy", app_id); 83 | 84 | for (uint8_t i=0; iconnected()) { 113 | return true; 114 | } 115 | 116 | Serial.print("Connecting to MQTT... "); 117 | 118 | while ((ret = mqtt->connect()) != 0) { // connect will return 0 for connected 119 | Serial.println(mqtt->connectErrorString(ret)); 120 | Serial.println(ret); 121 | Serial.println("Retrying MQTT connection in 5 seconds..."); 122 | mqtt->disconnect(); 123 | delay(5000); // wait 5 seconds 124 | if(timeout > 0){ 125 | cnt++; 126 | if(cnt*5000 >= timeout) return false; 127 | } 128 | } 129 | Serial.println("MQTT Connected!"); 130 | return true; 131 | } 132 | 133 | bool Milkcocoa::ping() { 134 | return mqtt->ping(1); 135 | } 136 | 137 | bool Milkcocoa::push(const char *path, DataElement *pdataelement) { 138 | char topic[100]; 139 | bool ret; 140 | char *send_array; 141 | sprintf(topic, "%s/%s/push", app_id, path); 142 | Adafruit_MQTT_Publish pushPublisher = Adafruit_MQTT_Publish(mqtt, topic); 143 | send_array = pdataelement->toCharArray(); 144 | ret = pushPublisher.publish(send_array); 145 | free(send_array); 146 | return ret; 147 | } 148 | 149 | bool Milkcocoa::send(const char *path, DataElement *pdataelement) { 150 | char topic[100]; 151 | bool ret; 152 | char *send_array; 153 | sprintf(topic, "%s/%s/send", app_id, path); 154 | Adafruit_MQTT_Publish pushPublisher = Adafruit_MQTT_Publish(mqtt, topic); 155 | send_array = pdataelement->toCharArray(); 156 | ret = pushPublisher.publish(send_array); 157 | free(send_array); 158 | return ret; 159 | } 160 | 161 | bool Milkcocoa::loop(uint16_t timeout) { 162 | if(!connect(timeout)){ 163 | return false; 164 | } 165 | Adafruit_MQTT_Subscribe *subscription; 166 | while ((subscription = mqtt->readSubscription(1000))) { 167 | for (uint8_t i=0; imqtt_sub) ) { 170 | DataElement de = DataElement((char *)milkcocoaSubscribers[i]->mqtt_sub->lastread); 171 | milkcocoaSubscribers[i]->cb( &de ); 172 | } 173 | } 174 | } 175 | return true; 176 | } 177 | 178 | bool Milkcocoa::on(const char *path, const char *event, GeneralFunction cb) { 179 | MilkcocoaSubscriber *sub = new MilkcocoaSubscriber(cb); 180 | sprintf(sub->topic, "%s/%s/%s", app_id, path, event); 181 | 182 | uint8_t i; 183 | Adafruit_MQTT_Subscribe *mqtt_sub = new Adafruit_MQTT_Subscribe(mqtt, sub->topic); 184 | sub->set_mqtt_sub(mqtt_sub); 185 | if(!mqtt->subscribe(mqtt_sub)) { 186 | return false; 187 | } 188 | for (i=0; i`), and write a code like below. 12 | 13 | ``` 14 | // 'client' is Ethernet/WiFi Client 15 | Milkcocoa milkcocoa = Milkcocoa(&client, "milkcocoa_app_id.mlkcca.com", 1883, "milkcocoa_app_id", "mqtt_client_id"); 16 | 17 | void setup() { 18 | //"on" API was able to call in setup 19 | milkcocoa.on("milkcocoa_datastore_name", "push", onpush); 20 | } 21 | 22 | void loop() { 23 | //milkcocoa.loop must be called in loop() 24 | milkcocoa.loop(); 25 | 26 | //push 27 | DataElement elem = DataElement(); 28 | elem.setValue("name", "Milk"); 29 | elem.setValue("age", 35); 30 | milkcocoa.push("milkcocoa_datastore_name", &elem); 31 | 32 | delay(10000); 33 | } 34 | 35 | void onpush(DataElement *elem) { 36 | Serial.println(elem->getString("name")); 37 | Serial.println(elem->getInt("age")); 38 | // Output: 39 | // Milk 40 | // 35 41 | }; 42 | ``` 43 | 44 | ### Using API Key 45 | 46 | If you use Milkcocoa API Key Authantication, please use `createWithApiKey`. 47 | 48 | ``` 49 | Milkcocoa *milkcocoa = Milkcocoa::createWithApiKey(&client, "milkcocoa_app_id.mlkcca.com", 1883, "milkcocoa_app_id", "mqtt_client_id", "API_KEY", "API_SECRET"); 50 | ``` 51 | 52 | ### onメソッドでイベント監視だけ行う場合 53 | 54 | 接続を維持するために、10秒から20秒間隔でpingを送信する必要があります。 55 | 56 | ``` 57 | void setup() { 58 | //"on" API was able to call in setup 59 | milkcocoa.on("milkcocoa_datastore_name", "push", onpush); 60 | } 61 | 62 | void loop() { 63 | //milkcocoa.loop must be called in loop() 64 | milkcocoa.loop(); 65 | milkcocoa.ping() 66 | delay(10000); 67 | } 68 | 69 | void onpush(DataElement *elem) { 70 | Serial.println(elem->getString("name")); 71 | Serial.println(elem->getInt("age")); 72 | // Output: 73 | // Milk 74 | // 35 75 | }; 76 | 77 | ``` 78 | 79 | ## Examples 80 | 81 | - [Simple ESP8266 Example](https://github.com/milk-cocoa/Milkcocoa_ESP8266_SDK/blob/master/examples/milkcocoa_esp8266/milkcocoa_esp8266.ino): simple test of `push()` and `on("push")`. 82 | - [ESP8266 TOUT Example](https://github.com/milk-cocoa/Milkcocoa_ESP8266_SDK/blob/master/examples/milkcocoa_esp8266_tout/milkcocoa_esp8266_tout.ino): get sensor data from TOUT of ESP8266 83 | - [ESP8266 API Key Authantication Example](https://github.com/milk-cocoa/Milkcocoa_ESP8266_SDK/blob/master/examples/milkcocoa_esp8266_apikey_auth/milkcocoa_esp8266_apikey_auth.ino): auth with Milkcocoa API Key 84 | 85 | 86 | ## LICENSE 87 | 88 | MIT 89 | 90 | 91 | 92 | 以下はCopyright (c) 2015 Technical Rockstars. 93 | 94 | - Milkcocoa.h 95 | - Milkcocoa.cpp 96 | 97 | ### Using 98 | 99 | - https://github.com/adafruit/Adafruit_MQTT_Library 100 | - https://github.com/interactive-matter/aJson 101 | 102 | -------------------------------------------------------------------------------- /aJSON.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2001, Interactive Matter, Marcus Nowotny 3 | 4 | Based on the cJSON Library, Copyright (C) 2009 Dave Gamble 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | // aJSON 26 | // aJson Library for Arduino. 27 | // This library is suited for Atmega328 based Arduinos. 28 | // The RAM on ATmega168 based Arduinos is too limited 29 | 30 | /****************************************************************************** 31 | * Includes 32 | ******************************************************************************/ 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #ifdef __AVR__ 40 | #include 41 | #else 42 | #include 43 | #endif 44 | #include "include/aJson/aJSON.h" 45 | #include "include/aJson/stringbuffer.h" 46 | 47 | /****************************************************************************** 48 | * Definitions 49 | ******************************************************************************/ 50 | //Default buffer sizes - buffers get initialized and grow acc to that size 51 | #define BUFFER_DEFAULT_SIZE 4 52 | 53 | //how much digits after . for float 54 | #define FLOAT_PRECISION 5 55 | 56 | 57 | bool 58 | aJsonStream::available() 59 | { 60 | if (bucket != EOF) 61 | return true; 62 | while (stream()->available()) 63 | { 64 | /* Make an effort to skip whitespace. */ 65 | int ch = this->getch(); 66 | if (ch > 32) 67 | { 68 | this->ungetch(ch); 69 | return true; 70 | } 71 | } 72 | return false; 73 | } 74 | 75 | int 76 | aJsonStream::getch() 77 | { 78 | if (bucket != EOF) 79 | { 80 | int ret = bucket; 81 | bucket = EOF; 82 | return ret; 83 | } 84 | // In case input was malformed - can happen, this is the 85 | // real world, we can end up in a situation where the parser 86 | // would expect another character and end up stuck on 87 | // stream()->available() forever, hence the 500ms timeout. 88 | unsigned long i= millis()+500; 89 | while ((!stream()->available()) && (millis() < i)) /* spin with a timeout*/; 90 | return stream()->read(); 91 | } 92 | 93 | void 94 | aJsonStream::ungetch(char ch) 95 | { 96 | bucket = ch; 97 | } 98 | 99 | size_t 100 | aJsonStream::write(uint8_t ch) 101 | { 102 | return stream()->write(ch); 103 | } 104 | 105 | size_t 106 | aJsonStream::readBytes(uint8_t *buffer, size_t len) 107 | { 108 | for (size_t i = 0; i < len; i++) 109 | { 110 | int ch = this->getch(); 111 | if (ch == EOF) 112 | { 113 | return i; 114 | } 115 | buffer[i] = ch; 116 | } 117 | return len; 118 | } 119 | 120 | 121 | int 122 | aJsonClientStream::getch() 123 | { 124 | if (bucket != EOF) 125 | { 126 | int ret = bucket; 127 | bucket = EOF; 128 | return ret; 129 | } 130 | while (!stream()->available() && stream()->connected()) /* spin */; 131 | if (!stream()->available()) // therefore, !stream()->connected() 132 | { 133 | stream()->stop(); 134 | return EOF; 135 | } 136 | return stream()->read(); 137 | } 138 | 139 | bool 140 | aJsonStringStream::available() 141 | { 142 | if (bucket != EOF) 143 | return true; 144 | return inbuf_len > 0; 145 | } 146 | 147 | int 148 | aJsonStringStream::getch() 149 | { 150 | if (bucket != EOF) 151 | { 152 | int ret = bucket; 153 | bucket = EOF; 154 | return ret; 155 | } 156 | if (!inbuf || !inbuf_len) 157 | { 158 | return EOF; 159 | } 160 | char ch = *inbuf++; 161 | inbuf_len--; 162 | return ch; 163 | } 164 | 165 | size_t 166 | aJsonStringStream::write(uint8_t ch) 167 | { 168 | if (!outbuf || outbuf_len <= 1) 169 | { 170 | return 0; 171 | } 172 | *outbuf++ = ch; outbuf_len--; 173 | *outbuf = 0; 174 | return 1; 175 | } 176 | 177 | 178 | // Internal constructor. 179 | aJsonObject* 180 | aJsonClass::newItem() 181 | { 182 | aJsonObject* node = (aJsonObject*) malloc(sizeof(aJsonObject)); 183 | if (node) 184 | memset(node, 0, sizeof(aJsonObject)); 185 | return node; 186 | } 187 | 188 | // Delete a aJsonObject structure. 189 | void 190 | aJsonClass::deleteItem(aJsonObject *c) 191 | { 192 | aJsonObject *next; 193 | while (c) 194 | { 195 | next = c->next; 196 | if (!(c->type & aJson_IsReference) && c->child) 197 | { 198 | deleteItem(c->child); 199 | } 200 | if ((c->type == aJson_String) && c->valuestring) 201 | { 202 | free(c->valuestring); 203 | } 204 | if (c->name) 205 | { 206 | free(c->name); 207 | } 208 | free(c); 209 | c = next; 210 | } 211 | } 212 | 213 | // Parse the input text to generate a number, and populate the result into item. 214 | int 215 | aJsonStream::parseNumber(aJsonObject *item) 216 | { 217 | int i = 0; 218 | int sign = 1; 219 | 220 | int in = this->getch(); 221 | if (in == EOF) 222 | { 223 | return EOF; 224 | } 225 | // It is easier to decode ourselves than to use sscnaf, 226 | // since so we can easier decide between int & double 227 | if (in == '-') 228 | { 229 | //it is a negative number 230 | sign = -1; 231 | in = this->getch(); 232 | if (in == EOF) 233 | { 234 | return EOF; 235 | } 236 | } 237 | if (in >= '0' && in <= '9') 238 | do 239 | { 240 | i = (i * 10) + (in - '0'); 241 | in = this->getch(); 242 | } 243 | while (in >= '0' && in <= '9'); // Number? 244 | //end of integer part � or isn't it? 245 | if (!(in == '.' || in == 'e' || in == 'E')) 246 | { 247 | item->valueint = i * (int) sign; 248 | item->type = aJson_Int; 249 | } 250 | //ok it seems to be a double 251 | else 252 | { 253 | double n = (double) i; 254 | int scale = 0; 255 | int subscale = 0; 256 | char signsubscale = 1; 257 | if (in == '.') 258 | { 259 | in = this->getch(); 260 | do 261 | { 262 | n = (n * 10.0) + (in - '0'), scale--; 263 | in = this->getch(); 264 | } 265 | while (in >= '0' && in <= '9'); 266 | } // Fractional part? 267 | if (in == 'e' || in == 'E') // Exponent? 268 | { 269 | in = this->getch(); 270 | if (in == '+') 271 | { 272 | in = this->getch(); 273 | } 274 | else if (in == '-') 275 | { 276 | signsubscale = -1; 277 | in = this->getch(); 278 | } 279 | while (in >= '0' && in <= '9') 280 | { 281 | subscale = (subscale * 10) + (in - '0'); // Number? 282 | in = this->getch(); 283 | } 284 | } 285 | 286 | n = sign * n * pow(10.0, ((double) scale + (double) subscale 287 | * (double) signsubscale)); // number = +/- number.fraction * 10^+/- exponent 288 | 289 | item->valuefloat = n; 290 | item->type = aJson_Float; 291 | } 292 | //preserve the last character for the next routine 293 | this->ungetch(in); 294 | return 0; 295 | } 296 | 297 | // Render the number nicely from the given item into a string. 298 | int 299 | aJsonStream::printInt(aJsonObject *item) 300 | { 301 | if (item != NULL) 302 | { 303 | return this->print(item->valueint, DEC); 304 | } 305 | //printing nothing is ok 306 | return 0; 307 | } 308 | 309 | int 310 | aJsonStream::printFloat(aJsonObject *item) 311 | { 312 | if (item != NULL) 313 | { 314 | double d = item->valuefloat; 315 | if (d<0.0) { 316 | this->print("-"); 317 | d=-d; 318 | } 319 | //print the integer part 320 | unsigned long integer_number = (unsigned long)d; 321 | this->print(integer_number, DEC); 322 | this->print("."); 323 | //print the fractional part 324 | double fractional_part = d - ((double)integer_number); 325 | //we do a do-while since we want to print at least one zero 326 | //we just support a certain number of digits after the '.' 327 | int n = FLOAT_PRECISION; 328 | fractional_part += 0.5/pow(10.0, FLOAT_PRECISION); 329 | do { 330 | //make the first digit non fractional(shift it before the '.' 331 | fractional_part *= 10.0; 332 | //create an int out of it 333 | unsigned int digit = (unsigned int) fractional_part; 334 | //print it 335 | this->print(digit, DEC); 336 | //remove it from the number 337 | fractional_part -= (double)digit; 338 | n--; 339 | } while ((fractional_part!=0) && (n>0)); 340 | } 341 | //printing nothing is ok 342 | return 0; 343 | } 344 | 345 | // Parse the input text into an unescaped cstring, and populate item. 346 | int 347 | aJsonStream::parseString(aJsonObject *item) 348 | { 349 | //we do not need to skip here since the first byte should be '\"' 350 | int in = this->getch(); 351 | if (in != '\"') 352 | { 353 | return EOF; // not a string! 354 | } 355 | item->type = aJson_String; 356 | //allocate a buffer & track how long it is and how much we have read 357 | string_buffer* buffer = stringBufferCreate(); 358 | if (buffer == NULL) 359 | { 360 | //unable to allocate the string 361 | return EOF; 362 | } 363 | in = this->getch(); 364 | if (in == EOF) 365 | { 366 | stringBufferFree(buffer); 367 | return EOF; 368 | } 369 | while (in != EOF) 370 | { 371 | while (in != '\"' && in >= 32) 372 | { 373 | if (in != '\\') 374 | { 375 | stringBufferAdd((char) in, buffer); 376 | } 377 | else 378 | { 379 | in = this->getch(); 380 | if (in == EOF) 381 | { 382 | stringBufferFree(buffer); 383 | return EOF; 384 | } 385 | switch (in) 386 | { 387 | case '\\': 388 | stringBufferAdd('\\', buffer); 389 | break; 390 | case '\"': 391 | stringBufferAdd('\"', buffer); 392 | break; 393 | case '/': 394 | stringBufferAdd('/', buffer); 395 | break; 396 | case 'b': 397 | stringBufferAdd('\b', buffer); 398 | break; 399 | case 'f': 400 | stringBufferAdd('\f', buffer); 401 | break; 402 | case 'n': 403 | stringBufferAdd('\n', buffer); 404 | break; 405 | case 'r': 406 | stringBufferAdd('\r', buffer); 407 | break; 408 | case 't': 409 | stringBufferAdd('\t', buffer); 410 | break; 411 | default: 412 | //we do not understand it so we skip it 413 | break; 414 | } 415 | } 416 | in = this->getch(); 417 | if (in == EOF) 418 | { 419 | stringBufferFree(buffer); 420 | return EOF; 421 | } 422 | } 423 | //the string ends here 424 | item->valuestring = stringBufferToString(buffer); 425 | return 0; 426 | } 427 | //we should not be here but it is ok 428 | return 0; 429 | } 430 | 431 | // Render the cstring provided to an escaped version that can be printed. 432 | int 433 | aJsonStream::printStringPtr(const char *str) 434 | { 435 | this->print("\""); 436 | char* ptr = (char*) str; 437 | if (ptr != NULL) 438 | { 439 | while (*ptr != 0) 440 | { 441 | if ((unsigned char) *ptr > 31 && *ptr != '\"' && *ptr != '\\') 442 | { 443 | this->print(*ptr); 444 | ptr++; 445 | } 446 | else 447 | { 448 | this->print('\\'); 449 | switch (*ptr++) 450 | { 451 | case '\\': 452 | this->print('\\'); 453 | break; 454 | case '\"': 455 | this->print('\"'); 456 | break; 457 | case '/': 458 | this->print('/'); 459 | break; 460 | case '\b': 461 | this->print('b'); 462 | break; 463 | case '\f': 464 | this->print('f'); 465 | break; 466 | case '\n': 467 | this->print('n'); 468 | break; 469 | case '\r': 470 | this->print('r'); 471 | break; 472 | case '\t': 473 | this->print('t'); 474 | break; 475 | default: 476 | break; // eviscerate with prejudice. 477 | } 478 | } 479 | 480 | } 481 | } 482 | this->print('\"'); 483 | return 0; 484 | } 485 | 486 | // Invote print_string_ptr (which is useful) on an item. 487 | int 488 | aJsonStream::printString(aJsonObject *item) 489 | { 490 | return this->printStringPtr(item->valuestring); 491 | } 492 | 493 | // Utility to jump whitespace and cr/lf 494 | int 495 | aJsonStream::skip() 496 | { 497 | int in = this->getch(); 498 | while (in != EOF && (in <= 32)) 499 | { 500 | in = this->getch(); 501 | } 502 | if (in != EOF) 503 | { 504 | this->ungetch(in); 505 | return 0; 506 | } 507 | return EOF; 508 | } 509 | 510 | // Utility to flush our buffer in case it contains garbage 511 | // since the parser will return the buffer untouched if it 512 | // cannot understand it. 513 | int 514 | aJsonStream::flush() 515 | { 516 | int in = this->getch(); 517 | while(in != EOF) 518 | { 519 | in = this->getch(); 520 | } 521 | return EOF; 522 | } 523 | 524 | 525 | // Parse an object - create a new root, and populate. 526 | aJsonObject* 527 | aJsonClass::parse(char *value) 528 | { 529 | aJsonStringStream stringStream(value, NULL); 530 | aJsonObject* result = parse(&stringStream); 531 | return result; 532 | } 533 | 534 | // Parse an object - create a new root, and populate. 535 | aJsonObject* 536 | aJsonClass::parse(aJsonStream* stream) 537 | { 538 | return parse(stream, NULL); 539 | } 540 | 541 | // Parse an object - create a new root, and populate. 542 | aJsonObject* 543 | aJsonClass::parse(aJsonStream* stream, char** filter) 544 | { 545 | if (stream == NULL) 546 | { 547 | return NULL; 548 | } 549 | aJsonObject *c = newItem(); 550 | if (!c) 551 | return NULL; /* memory fail */ 552 | 553 | stream->skip(); 554 | if (stream->parseValue(c, filter) == EOF) 555 | { 556 | deleteItem(c); 557 | return NULL; 558 | } 559 | return c; 560 | } 561 | 562 | // Render a aJsonObject item/entity/structure to text. 563 | int 564 | aJsonClass::print(aJsonObject* item, aJsonStream* stream) 565 | { 566 | return stream->printValue(item); 567 | } 568 | 569 | char* 570 | aJsonClass::print(aJsonObject* item) 571 | { 572 | char* outBuf = (char*) malloc(PRINT_BUFFER_LEN); /* XXX: Dynamic size. */ 573 | if (outBuf == NULL) 574 | { 575 | return NULL; 576 | } 577 | aJsonStringStream stringStream(NULL, outBuf, PRINT_BUFFER_LEN); 578 | print(item, &stringStream); 579 | return outBuf; 580 | } 581 | 582 | // Parser core - when encountering text, process appropriately. 583 | int 584 | aJsonStream::parseValue(aJsonObject *item, char** filter) 585 | { 586 | if (this->skip() == EOF) 587 | { 588 | return EOF; 589 | } 590 | //read the first byte from the stream 591 | int in = this->getch(); 592 | if (in == EOF) 593 | { 594 | return EOF; 595 | } 596 | this->ungetch(in); 597 | if (in == '\"') 598 | { 599 | return this->parseString(item); 600 | } 601 | else if (in == '-' || (in >= '0' && in <= '9')) 602 | { 603 | return this->parseNumber(item); 604 | } 605 | else if (in == '[') 606 | { 607 | return this->parseArray(item, filter); 608 | } 609 | else if (in == '{') 610 | { 611 | return this->parseObject(item, filter); 612 | } 613 | //it can only be null, false or true 614 | else if (in == 'n') 615 | { 616 | //a buffer to read the value 617 | char buffer[] = 618 | { 0, 0, 0, 0 }; 619 | if (this->readBytes((uint8_t*) buffer, 4) != 4) 620 | { 621 | return EOF; 622 | } 623 | if (!strncmp(buffer, "null", 4)) 624 | { 625 | item->type = aJson_NULL; 626 | return 0; 627 | } 628 | else 629 | { 630 | return EOF; 631 | } 632 | } 633 | else if (in == 'f') 634 | { 635 | //a buffer to read the value 636 | char buffer[] = 637 | { 0, 0, 0, 0, 0 }; 638 | if (this->readBytes((uint8_t*) buffer, 5) != 5) 639 | { 640 | return EOF; 641 | } 642 | if (!strncmp(buffer, "false", 5)) 643 | { 644 | item->type = aJson_Boolean; 645 | item->valuebool = false; 646 | return 0; 647 | } 648 | } 649 | else if (in == 't') 650 | { 651 | //a buffer to read the value 652 | char buffer[] = 653 | { 0, 0, 0, 0 }; 654 | if (this->readBytes((uint8_t*) buffer, 4) != 4) 655 | { 656 | return EOF; 657 | } 658 | if (!strncmp(buffer, "true", 4)) 659 | { 660 | item->type = aJson_Boolean; 661 | item->valuebool = true; 662 | return 0; 663 | } 664 | } 665 | 666 | return EOF; // failure. 667 | } 668 | 669 | // Render a value to text. 670 | int 671 | aJsonStream::printValue(aJsonObject *item) 672 | { 673 | int result = 0; 674 | if (item == NULL) 675 | { 676 | //nothing to do 677 | return 0; 678 | } 679 | switch (item->type) 680 | { 681 | case aJson_NULL: 682 | result = this->print("null"); 683 | break; 684 | case aJson_Boolean: 685 | if(item->valuebool){ 686 | result = this->print("true"); 687 | } 688 | else{ 689 | result = this->print("false"); 690 | } 691 | break; 692 | case aJson_Int: 693 | result = this->printInt(item); 694 | break; 695 | case aJson_Float: 696 | result = this->printFloat(item); 697 | break; 698 | case aJson_String: 699 | result = this->printString(item); 700 | break; 701 | case aJson_Array: 702 | result = this->printArray(item); 703 | break; 704 | case aJson_Object: 705 | result = this->printObject(item); 706 | break; 707 | } 708 | return result; 709 | } 710 | 711 | // Build an array from input text. 712 | int 713 | aJsonStream::parseArray(aJsonObject *item, char** filter) 714 | { 715 | int in = this->getch(); 716 | if (in != '[') 717 | { 718 | return EOF; // not an array! 719 | } 720 | 721 | item->type = aJson_Array; 722 | this->skip(); 723 | in = this->getch(); 724 | //check for empty array 725 | if (in == ']') 726 | { 727 | return 0; // empty array. 728 | } 729 | //now put back the last character 730 | this->ungetch(in); 731 | aJsonObject *child; 732 | char first = -1; 733 | while ((first) || (in == ',')) 734 | { 735 | aJsonObject *new_item = aJsonClass::newItem(); 736 | if (new_item == NULL) 737 | { 738 | return EOF; // memory fail 739 | } 740 | if (first) 741 | { 742 | item->child = new_item; 743 | first = 0; 744 | } 745 | else 746 | { 747 | child->next = new_item; 748 | new_item->prev = child; 749 | } 750 | child = new_item; 751 | this->skip(); 752 | if (this->parseValue(child, filter)) 753 | { 754 | return EOF; 755 | } 756 | this->skip(); 757 | in = this->getch(); 758 | } 759 | if (in == ']') 760 | { 761 | return 0; // end of array 762 | } 763 | else 764 | { 765 | return EOF; // malformed. 766 | } 767 | } 768 | 769 | // Render an array to text 770 | int 771 | aJsonStream::printArray(aJsonObject *item) 772 | { 773 | if (item == NULL) 774 | { 775 | //nothing to do 776 | return 0; 777 | } 778 | aJsonObject *child = item->child; 779 | if (this->print('[') == EOF) 780 | { 781 | return EOF; 782 | } 783 | while (child) 784 | { 785 | if (this->printValue(child) == EOF) 786 | { 787 | return EOF; 788 | } 789 | child = child->next; 790 | if (child) 791 | { 792 | if (this->print(',') == EOF) 793 | { 794 | return EOF; 795 | } 796 | } 797 | } 798 | if (this->print(']') == EOF) 799 | { 800 | return EOF; 801 | } 802 | return 0; 803 | } 804 | 805 | // Build an object from the text. 806 | int 807 | aJsonStream::parseObject(aJsonObject *item, char** filter) 808 | { 809 | int in = this->getch(); 810 | if (in != '{') 811 | { 812 | return EOF; // not an object! 813 | } 814 | 815 | item->type = aJson_Object; 816 | this->skip(); 817 | //check for an empty object 818 | in = this->getch(); 819 | if (in == '}') 820 | { 821 | return 0; // empty object. 822 | } 823 | //preserve the char for the next parser 824 | this->ungetch(in); 825 | 826 | aJsonObject* child; 827 | char first = -1; 828 | while ((first) || (in == ',')) 829 | { 830 | aJsonObject* new_item = aJsonClass::newItem(); 831 | if (new_item == NULL) 832 | { 833 | return EOF; // memory fail 834 | } 835 | if (first) 836 | { 837 | first = 0; 838 | item->child = new_item; 839 | } 840 | else 841 | { 842 | child->next = new_item; 843 | new_item->prev = child; 844 | } 845 | child = new_item; 846 | this->skip(); 847 | if (this->parseString(child) == EOF) 848 | { 849 | return EOF; 850 | } 851 | this->skip(); 852 | child->name = child->valuestring; 853 | child->valuestring = NULL; 854 | 855 | in = this->getch(); 856 | if (in != ':') 857 | { 858 | return EOF; // fail! 859 | } 860 | // skip any spacing, get the value. 861 | this->skip(); 862 | if (this->parseValue(child, filter) == EOF) 863 | { 864 | return EOF; 865 | } 866 | this->skip(); 867 | in = this->getch(); 868 | } 869 | 870 | if (in == '}') 871 | { 872 | return 0; // end of array 873 | } 874 | else 875 | { 876 | return EOF; // malformed. 877 | } 878 | } 879 | 880 | // Render an object to text. 881 | int 882 | aJsonStream::printObject(aJsonObject *item) 883 | { 884 | if (item == NULL) 885 | { 886 | //nothing to do 887 | return 0; 888 | } 889 | aJsonObject *child = item->child; 890 | if (this->print('{') == EOF) 891 | { 892 | return EOF; 893 | } 894 | while (child) 895 | { 896 | if (this->printStringPtr(child->name) == EOF) 897 | { 898 | return EOF; 899 | } 900 | if (this->print(':') == EOF) 901 | { 902 | return EOF; 903 | } 904 | if (this->printValue(child) == EOF) 905 | { 906 | return EOF; 907 | } 908 | child = child->next; 909 | if (child) 910 | { 911 | if (this->print(',') == EOF) 912 | { 913 | return EOF; 914 | } 915 | } 916 | } 917 | if (this->print('}') == EOF) 918 | { 919 | return EOF; 920 | } 921 | return 0; 922 | } 923 | 924 | // Get Array size/item / object item. 925 | unsigned char 926 | aJsonClass::getArraySize(aJsonObject *array) 927 | { 928 | aJsonObject *c = array->child; 929 | unsigned char i = 0; 930 | while (c) 931 | i++, c = c->next; 932 | return i; 933 | } 934 | aJsonObject* 935 | aJsonClass::getArrayItem(aJsonObject *array, unsigned char item) 936 | { 937 | aJsonObject *c = array->child; 938 | while (c && item > 0) 939 | item--, c = c->next; 940 | return c; 941 | } 942 | aJsonObject* 943 | aJsonClass::getObjectItem(aJsonObject *object, const char *string) 944 | { 945 | aJsonObject *c = object->child; 946 | while (c && strcasecmp(c->name, string)) 947 | c = c->next; 948 | return c; 949 | } 950 | 951 | // Utility for array list handling. 952 | void 953 | aJsonClass::suffixObject(aJsonObject *prev, aJsonObject *item) 954 | { 955 | prev->next = item; 956 | item->prev = prev; 957 | } 958 | // Utility for handling references. 959 | aJsonObject* 960 | aJsonClass::createReference(aJsonObject *item) 961 | { 962 | aJsonObject *ref = newItem(); 963 | if (!ref) 964 | return 0; 965 | memcpy(ref, item, sizeof(aJsonObject)); 966 | ref->name = 0; 967 | ref->type |= aJson_IsReference; 968 | ref->next = ref->prev = 0; 969 | return ref; 970 | } 971 | 972 | // Add item to array/object. 973 | void 974 | aJsonClass::addItemToArray(aJsonObject *array, aJsonObject *item) 975 | { 976 | aJsonObject *c = array->child; 977 | if (!item) 978 | return; 979 | if (!c) 980 | { 981 | array->child = item; 982 | } 983 | else 984 | { 985 | while (c && c->next) 986 | c = c->next; 987 | suffixObject(c, item); 988 | } 989 | } 990 | void 991 | aJsonClass::addItemToObject(aJsonObject *object, const char *string, 992 | aJsonObject *item) 993 | { 994 | if (!item) 995 | return; 996 | if (item->name) 997 | free(item->name); 998 | item->name = strdup(string); 999 | addItemToArray(object, item); 1000 | } 1001 | void 1002 | aJsonClass::addItemReferenceToArray(aJsonObject *array, aJsonObject *item) 1003 | { 1004 | addItemToArray(array, createReference(item)); 1005 | } 1006 | void 1007 | aJsonClass::addItemReferenceToObject(aJsonObject *object, const char *string, 1008 | aJsonObject *item) 1009 | { 1010 | addItemToObject(object, string, createReference(item)); 1011 | } 1012 | 1013 | aJsonObject* 1014 | aJsonClass::detachItemFromArray(aJsonObject *array, unsigned char which) 1015 | { 1016 | aJsonObject *c = array->child; 1017 | while (c && which > 0) 1018 | c = c->next, which--; 1019 | if (!c) 1020 | return 0; 1021 | if (c->prev) 1022 | c->prev->next = c->next; 1023 | if (c->next) 1024 | c->next->prev = c->prev; 1025 | if (c == array->child) 1026 | array->child = c->next; 1027 | c->prev = c->next = 0; 1028 | return c; 1029 | } 1030 | void 1031 | aJsonClass::deleteItemFromArray(aJsonObject *array, unsigned char which) 1032 | { 1033 | deleteItem(detachItemFromArray(array, which)); 1034 | } 1035 | aJsonObject* 1036 | aJsonClass::detachItemFromObject(aJsonObject *object, const char *string) 1037 | { 1038 | unsigned char i = 0; 1039 | aJsonObject *c = object->child; 1040 | while (c && strcasecmp(c->name, string)) 1041 | i++, c = c->next; 1042 | if (c) 1043 | return detachItemFromArray(object, i); 1044 | return 0; 1045 | } 1046 | void 1047 | aJsonClass::deleteItemFromObject(aJsonObject *object, const char *string) 1048 | { 1049 | deleteItem(detachItemFromObject(object, string)); 1050 | } 1051 | 1052 | // Replace array/object items with new ones. 1053 | void 1054 | aJsonClass::replaceItemInArray(aJsonObject *array, unsigned char which, 1055 | aJsonObject *newitem) 1056 | { 1057 | aJsonObject *c = array->child; 1058 | while (c && which > 0) 1059 | c = c->next, which--; 1060 | if (!c) 1061 | return; 1062 | newitem->next = c->next; 1063 | newitem->prev = c->prev; 1064 | if (newitem->next) 1065 | newitem->next->prev = newitem; 1066 | if (c == array->child) 1067 | array->child = newitem; 1068 | else 1069 | newitem->prev->next = newitem; 1070 | c->next = c->prev = 0; 1071 | deleteItem(c); 1072 | } 1073 | void 1074 | aJsonClass::replaceItemInObject(aJsonObject *object, const char *string, 1075 | aJsonObject *newitem) 1076 | { 1077 | unsigned char i = 0; 1078 | aJsonObject *c = object->child; 1079 | while (c && strcasecmp(c->name, string)) 1080 | i++, c = c->next; 1081 | if (c) 1082 | { 1083 | newitem->name = strdup(string); 1084 | replaceItemInArray(object, i, newitem); 1085 | } 1086 | } 1087 | 1088 | // Create basic types: 1089 | aJsonObject* 1090 | aJsonClass::createNull() 1091 | { 1092 | aJsonObject *item = newItem(); 1093 | if (item) 1094 | item->type = aJson_NULL; 1095 | return item; 1096 | } 1097 | 1098 | aJsonObject* 1099 | aJsonClass::createItem(bool b) 1100 | { 1101 | aJsonObject *item = newItem(); 1102 | if (item){ 1103 | item->type = aJson_Boolean; 1104 | item->valuebool = b; 1105 | } 1106 | 1107 | return item; 1108 | } 1109 | 1110 | aJsonObject* 1111 | aJsonClass::createItem(char b) 1112 | { 1113 | aJsonObject *item = newItem(); 1114 | if (item) 1115 | { 1116 | item->type = aJson_Boolean; 1117 | item->valuebool = b ? -1 : 0; 1118 | } 1119 | return item; 1120 | } 1121 | 1122 | aJsonObject* 1123 | aJsonClass::createItem(int num) 1124 | { 1125 | aJsonObject *item = newItem(); 1126 | if (item) 1127 | { 1128 | item->type = aJson_Int; 1129 | item->valueint = (int) num; 1130 | } 1131 | return item; 1132 | } 1133 | 1134 | aJsonObject* 1135 | aJsonClass::createItem(double num) 1136 | { 1137 | aJsonObject *item = newItem(); 1138 | if (item) 1139 | { 1140 | item->type = aJson_Float; 1141 | item->valuefloat = num; 1142 | } 1143 | return item; 1144 | } 1145 | 1146 | aJsonObject* 1147 | aJsonClass::createItem(const char *string) 1148 | { 1149 | aJsonObject *item = newItem(); 1150 | if (item) 1151 | { 1152 | item->type = aJson_String; 1153 | item->valuestring = strdup(string); 1154 | } 1155 | return item; 1156 | } 1157 | 1158 | aJsonObject* 1159 | aJsonClass::createArray() 1160 | { 1161 | aJsonObject *item = newItem(); 1162 | if (item) 1163 | item->type = aJson_Array; 1164 | return item; 1165 | } 1166 | aJsonObject* 1167 | aJsonClass::createObject() 1168 | { 1169 | aJsonObject *item = newItem(); 1170 | if (item) 1171 | item->type = aJson_Object; 1172 | return item; 1173 | } 1174 | 1175 | // Create Arrays: 1176 | aJsonObject* 1177 | aJsonClass::createIntArray(int *numbers, unsigned char count) 1178 | { 1179 | unsigned char i; 1180 | aJsonObject *n = 0, *p = 0, *a = createArray(); 1181 | for (i = 0; a && i < count; i++) 1182 | { 1183 | n = createItem(numbers[i]); 1184 | if (!i) 1185 | a->child = n; 1186 | else 1187 | suffixObject(p, n); 1188 | p = n; 1189 | } 1190 | return a; 1191 | } 1192 | 1193 | aJsonObject* 1194 | aJsonClass::createFloatArray(double *numbers, unsigned char count) 1195 | { 1196 | unsigned char i; 1197 | aJsonObject *n = 0, *p = 0, *a = createArray(); 1198 | for (i = 0; a && i < count; i++) 1199 | { 1200 | n = createItem(numbers[i]); 1201 | if (!i) 1202 | a->child = n; 1203 | else 1204 | suffixObject(p, n); 1205 | p = n; 1206 | } 1207 | return a; 1208 | } 1209 | 1210 | aJsonObject* 1211 | aJsonClass::createDoubleArray(double *numbers, unsigned char count) 1212 | { 1213 | unsigned char i; 1214 | aJsonObject *n = 0, *p = 0, *a = createArray(); 1215 | for (i = 0; a && i < count; i++) 1216 | { 1217 | n = createItem(numbers[i]); 1218 | if (!i) 1219 | a->child = n; 1220 | else 1221 | suffixObject(p, n); 1222 | p = n; 1223 | } 1224 | return a; 1225 | } 1226 | 1227 | aJsonObject* 1228 | aJsonClass::createStringArray(const char **strings, unsigned char count) 1229 | { 1230 | unsigned char i; 1231 | aJsonObject *n = 0, *p = 0, *a = createArray(); 1232 | for (i = 0; a && i < count; i++) 1233 | { 1234 | n = createItem(strings[i]); 1235 | if (!i) 1236 | a->child = n; 1237 | else 1238 | suffixObject(p, n); 1239 | p = n; 1240 | } 1241 | return a; 1242 | } 1243 | 1244 | void 1245 | aJsonClass::addNullToObject(aJsonObject* object, const char* name) 1246 | { 1247 | addItemToObject(object, name, createNull()); 1248 | } 1249 | 1250 | void 1251 | aJsonClass::addBooleanToObject(aJsonObject* object, const char* name, bool b) 1252 | { 1253 | addItemToObject(object, name, createItem(b)); 1254 | } 1255 | 1256 | void 1257 | aJsonClass::addNumberToObject(aJsonObject* object, const char* name, int n) 1258 | { 1259 | addItemToObject(object, name, createItem(n)); 1260 | } 1261 | 1262 | void 1263 | aJsonClass::addNumberToObject(aJsonObject* object, const char* name, double n) 1264 | { 1265 | addItemToObject(object, name, createItem(n)); 1266 | } 1267 | 1268 | void 1269 | aJsonClass::addStringToObject(aJsonObject* object, const char* name, 1270 | const char* s) 1271 | { 1272 | addItemToObject(object, name, createItem(s)); 1273 | } 1274 | 1275 | //TODO conversion routines btw. float & int types? 1276 | 1277 | aJsonClass aJson; 1278 | -------------------------------------------------------------------------------- /aJson/.gitignore: -------------------------------------------------------------------------------- 1 | .cproject 2 | .project 3 | .settings/ 4 | Debug/ 5 | Release/ 6 | /Debug 7 | /Release 8 | 9 | #some Mac stuff 10 | .DS_Store 11 | -------------------------------------------------------------------------------- /aJson/README.md: -------------------------------------------------------------------------------- 1 | aJson v1.0 2 | ================ 3 | Copyright (c) 2010, Interactive Matter, Marcus Nowotny 4 | 5 | Based on the cJSON Library, Copyright (C) 2009 Dave Gamble 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | 25 | Welcome to aJson. 26 | ================ 27 | 28 | aJson is the attempt to port a complete JSON implementation to Arduino. It is based on the cJSON implementation, reduced in size and removing one or two features: 29 | 30 | - The code has very limited support on ATmega168 - there is just not enough memory and 31 | memory fragmentation is a serious problem 32 | - Arrays and Lists are max 255 elements big 33 | - There is no proper Unicode handling in this code 34 | - There is an internal buffer eating up 256 bytes of ram 35 | 36 | Most of the limitation will be gone in one of the future releases. 37 | 38 | JSON is described best here: http://www.json.org/ 39 | It's like XML, but fat-free. You use it to move data around, store things, or just 40 | generally represent your program's state. 41 | JSON is especially useful to exchange data efficiently with e.g. JavaScript, Java, C++, 42 | Processing or anything else 43 | 44 | aJson is a library to receive, understand, create or modify JSON strings directly in the 45 | Arduino. JSON is quite a standard, so that is perfect for exchanging data with other 46 | applications. I combination with HTTP it is suitable to implement REST Web Services. 47 | 48 | aJson provides functions to parse JSON strings to object models. Handle, search and 49 | create and modify JSON Object structures. 50 | 51 | This is some JSON from this page: http://www.json.org/fatfree.html 52 | 53 | ```javascript 54 | { 55 | "name": "Jack (\"Bee\") Nimble", 56 | "format": { 57 | "type": "rect", 58 | "width": 1920, 59 | "height": 1080, 60 | "interlace": false, 61 | "frame rate": 24 62 | } 63 | } 64 | 65 | ``` 66 | 67 | Parsing JSON 68 | ================ 69 | 70 | To parse such a structure with aJson you simply convert it to a object tree: 71 | 72 | ```c 73 | aJsonObject* jsonObject = aJson.parse(json_string); 74 | ``` 75 | 76 | (assuming you got the JSON string in the variable json_string - as a char*) 77 | 78 | This is an object. We're in C. We don't have objects. But we do have structs. 79 | Therefore the objects are translated into structs, with all the drawbacks it brings.s 80 | 81 | Now we can e.g. retrieve the value for name: 82 | 83 | ```c 84 | aJsonObject* name = aJson.getObjectItem(root, "name"); 85 | ``` 86 | 87 | The value of name can be retrieved via: 88 | 89 | ```c 90 | Serial.println(name->valuestring); 91 | ``` 92 | 93 | Note that the aJsonObject has a union which holds all possible value types as 94 | overlays - you can get only useful data for the type which you have at hand. You can get 95 | the type as 96 | 97 | ```c 98 | name->type 99 | ``` 100 | 101 | which can be either aJson_False, aJson_True, aJson_NULL, aJson_Number, aJson_String, aJson_Array 102 | or aJson_Object. For aJson_Number you can use value.number.valueint or value.number.valuedouble, for aJson_String 103 | you can use value.valuestring, for True or False, you can use value.valuebool. 104 | 105 | To render the object back to a string you can simply call 106 | 107 | ```c 108 | char *json_String=aJson.print(jsonObject); 109 | ``` 110 | 111 | Finished? Delete the root (this takes care of everything else). 112 | 113 | ```c 114 | aJson.deleteItem(root); 115 | ``` 116 | 117 | This deletes the objects and all values referenced by it. 118 | 119 | Parsing streams 120 | -------------- 121 | 122 | As you can see this will eat up lots of memory. Storing the original string and the JSON object is a bit too much 123 | for your Arduino - it will most likely use up all the memory. Therefore it is better to parse streams instead of strings. 124 | A stream in C is a FILE* - on Arduino there are some special streams, but later adapters will be provided. 125 | So if you for example read from a FILE* stream you can simply call 126 | 127 | ```c 128 | aJsonObject* jsonObject = aJson.parse(file); 129 | ``` 130 | 131 | By that you will not have to store the JSON string in memory. 132 | 133 | Filtering while parsing 134 | -------------- 135 | 136 | Any JSON respond can have object name/value pairs your code either does not understand or is not interested in. 137 | To avoid those values going into your memory you can simply add filters to your parsing request. 138 | A set of filter is just a list of names you are interested in, ended by a null value. If you are 139 | only interested in "name", "format", "height" and "width" in the above example you can do it like: 140 | 141 | ```c 142 | char** jsonFilter = {"name,"format","height","width",NULL}; 143 | aJsonObject* jsonObject = aJson.parse(json_string,jsonFilter); 144 | ``` 145 | (assuming you got the JSON string in the variable json_string - as a char*) 146 | 147 | By that only the following structure is parsed - the rest will be ignored: 148 | 149 | ```javascript 150 | { 151 | "name": "Jack (\"Bee\") Nimble", 152 | "format": { 153 | "width": 1920, 154 | "height": 1080, 155 | } 156 | } 157 | ``` 158 | 159 | It is good practice to always use the filtering feature to parse JSON answers, to avoid unknown objects swamping your 160 | memory. 161 | 162 | Creating JSON Objects from code 163 | ================ 164 | 165 | If you want to see how you'd build this struct in code? 166 | 167 | ```c 168 | aJsonObject *root,*fmt; 169 | root=aJson.createObject(); 170 | aJson.addItemToObject(root, "name", aJson.createItem("Jack (\"Bee\") Nimble")); 171 | aJson.addItemToObject(root, "format", fmt = aJson.createObject()); 172 | aJson.addStringToObject(fmt,"type", "rect"); 173 | aJson.addNumberToObject(fmt,"width", 1920); 174 | aJson.addNumberToObject(fmt,"height", 1080); 175 | aJson.addFalseToObject (fmt,"interlace"); 176 | aJson.addNumberToObject(fmt,"frame rate", 24); 177 | ``` 178 | 179 | The root object has: Object Type and a Child 180 | The Child has name "name", with value "Jack ("Bee") Nimble", and a sibling: 181 | Sibling has type Object, name "format", and a child. 182 | That child has type String, name "type", value "rect", and a sibling: 183 | Sibling has type Number, name "width", value 1920, and a sibling: 184 | Sibling has type Number, name "height", value 1080, and a sibling: 185 | Sibling hs type False, name "interlace", and a sibling: 186 | Sibling has type Number, name "frame rate", value 24 187 | 188 | If you want to create an array it works nearly the same way: 189 | 190 | ```c 191 | aJsonObject* root = aJson.createArray(); 192 | 193 | aJsonObject* day; 194 | day=aJson.createItem("Monday"); 195 | aJson.addItemToArray(root, day); 196 | day=aJson.createItem("Tuesday"); 197 | aJson.addItemToArray(root, day); 198 | day=aJson.createItem("Wednesday"); 199 | aJson.addItemToArray(root, day); 200 | day=aJson.createItem("Thursday"); 201 | aJson.addItemToArray(root, day); 202 | day=aJson.createItem("Friday"); 203 | aJson.addItemToArray(root, day); 204 | day=aJson.createItem("Saturday"); 205 | aJson.addItemToArray(root, day); 206 | day=aJson.createItem("Sunday"); 207 | aJson.addItemToArray(root, day); 208 | ``` 209 | 210 | 211 | The whole library (nicely provided by cJSON) is optimized for easy usage. You can create and modify 212 | the object as easy as possible. 213 | 214 | aJson Data Structures 215 | ================ 216 | 217 | aJson stores JSON objects in struct objects: 218 | 219 | ```c 220 | // The aJson structure: 221 | typedef struct aJsonObject { 222 | char *name; // The item's name string, if this item is the child of, or is in the list of subitems of an object. 223 | struct aJsonObject *next, *prev; // next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem 224 | struct aJsonObject *child; // An array or object item will have a child pointer pointing to a chain of the items in the array/object. 225 | 226 | char type; // The type of the item, as above. 227 | 228 | union { 229 | char *valuestring; // The item's string, if type==aJson_String 230 | char valuebool; //the items value for true & false 231 | int valueint; // The item's number, if type==aJson_Number 232 | float valuefloat; // The item's number, if type==aJson_Number 233 | }; 234 | } aJsonObject; 235 | ``` 236 | 237 | By default all values are 0 unless set by virtue of being meaningful. 238 | 239 | Note that the aJsonObject has a union 'value' which holds all possible value types as 240 | overlays - you can get only useful data for the type which you have at hand. You can get 241 | the type as 242 | 243 | ```c 244 | name->type 245 | ``` 246 | 247 | which can be either aJson_False, aJson_True, aJson_NULL, aJson_Number, aJson_String, aJson_Array 248 | or aJson_Object. For aJson_Number you can use value.number.valueint or value.number.valuedouble. 249 | If you're expecting an int, read valueint, if not read valuedouble. For aJson_String 250 | you can use value.valuestring, for True or False, you can use value.valuebool. 251 | 252 | next/prev is a doubly linked list of siblings. next takes you to your sibling, 253 | prev takes you back from your sibling to you. 254 | Only objects and arrays have a "child", and it's the head of the doubly linked list. 255 | A "child" entry will have prev==0, but next potentially points on. The last sibling has next=0. 256 | The type expresses Null/True/False/Number/String/Array/Object, all of which are #defined in 257 | aJson.h 258 | 259 | Any entry which is in the linked list which is the child of an object will have a "string" 260 | which is the "name" of the entry. When I said "name" in the above example, that's "string". 261 | "string" is the JSON name for the 'variable name' if you will. 262 | 263 | Now you can trivially walk the lists, recursively, and parse as you please. 264 | You can invoke aJson.parse to get aJson to parse for you, and then you can take 265 | the root object, and traverse the structure (which is, formally, an N-tree), 266 | and tokenise as you please. 267 | 268 | Lists in aJson 269 | ================ 270 | 271 | Lists are easily handled in aJson, to create a list you can simply use the provided API functions: 272 | 273 | ```c 274 | aJson.createArray(objects,24); 275 | ``` 276 | 277 | You simply pass a array of the respective type: char*[], int[] and so on. 278 | 279 | aJSON doesn't make any assumptions about what order you create things in. 280 | You can attach the objects, as above, and later add children to each 281 | of those objects with 282 | 283 | ```c 284 | aJson.addItemToArray() 285 | ``` 286 | 287 | or remove them with 288 | 289 | ```c 290 | aJson.deleteItemFromArray() - which also deletes the objects, or 291 | aJson.detachItemFromArray() - which does not free the memory 292 | ``` 293 | 294 | As soon as you call aJson.print(), it renders the structure to text. 295 | 296 | 297 | Have Fun! 298 | -------------------------------------------------------------------------------- /aJson/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For aJson 3 | # Use TAB to seperate identifiers 4 | # and KEYWORD specifiers! 5 | ####################################### 6 | 7 | ####################################### 8 | # Datatypes (KEYWORD1) 9 | ####################################### 10 | 11 | aJson KEYWORD1 12 | aJsonObject KEYWORD1 13 | aJsonStream KEYWORD1 14 | aJsonClientStream KEYWORD1 15 | aJsonStringStream KEYWORD1 16 | 17 | ####################################### 18 | # Methods and Functions (KEYWORD2) 19 | ####################################### 20 | 21 | parse KEYWORD2 22 | print KEYWORD2 23 | deleteItem KEYWORD2 24 | getArraySize KEYWORD2 25 | getArrayItem KEYWORD2 26 | getObjectItem KEYWORD2 27 | createNull KEYWORD2 28 | createTrue KEYWORD2 29 | createFalse KEYWORD2 30 | createBool KEYWORD2 31 | createNumber KEYWORD2 32 | createString KEYWORD2 33 | createArray KEYWORD2 34 | createObject KEYWORD2 35 | createIntArray KEYWORD2 36 | createFloatArray KEYWORD2 37 | createDoubleArray KEYWORD2 38 | createStringArray KEYWORD2 39 | addItemToArray KEYWORD2 40 | addItemToObject KEYWORD2 41 | addItemReferenceToArray KEYWORD2 42 | addItemReferenceToObject KEYWORD2 43 | detachItemFromArray KEYWORD2 44 | deleteItemFromArray KEYWORD2 45 | detachItemFromObject KEYWORD2 46 | deleteItemFromObject KEYWORD2 47 | replaceItemInArray KEYWORD2 48 | replaceItemInObject KEYWORD2 49 | addNullToObject KEYWORD2 50 | addTrueToObject KEYWORD2 51 | addFalseToObject KEYWORD2 52 | addNumberToObject KEYWORD2 53 | addStringToObject KEYWORD2 54 | 55 | 56 | ####################################### 57 | # Constants (LITERAL1) 58 | ####################################### 59 | 60 | aJson_False LITERAL1 61 | aJson_True LITERAL1 62 | aJson_NULL LITERAL1 63 | aJson_Number LITERAL1 64 | aJson_String LITERAL1 65 | aJson_Array LITERAL1 66 | aJson_Object LITERAL1 67 | aJson_IsReference LITERAL1 68 | -------------------------------------------------------------------------------- /aJson/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aJson", 3 | "keywords": "json, rest, http, web", 4 | "description": "An Arduino library to enable JSON processing with Arduino", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/interactive-matter/aJson.git" 9 | }, 10 | "frameworks": "arduino", 11 | "platforms": "atmelavr" 12 | } 13 | -------------------------------------------------------------------------------- /examples/milkcocoa_esp8266/milkcocoa_esp8266.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /************************* WiFi Access Point *********************************/ 5 | 6 | #define WLAN_SSID "...SSID..." 7 | #define WLAN_PASS "...PASS..." 8 | 9 | 10 | /************************* Your Milkcocoa Setup *********************************/ 11 | 12 | #define MILKCOCOA_APP_ID "...YOUR_MILKCOCOA_APP_ID..." 13 | #define MILKCOCOA_DATASTORE "esp8266" 14 | 15 | /************* Milkcocoa Setup (you don't need to change this!) ******************/ 16 | 17 | #define MILKCOCOA_SERVERPORT 1883 18 | 19 | /************ Global State (you don't need to change this!) ******************/ 20 | 21 | // Create an ESP8266 WiFiClient class to connect to the MQTT server. 22 | WiFiClient client; 23 | 24 | const char MQTT_SERVER[] PROGMEM = MILKCOCOA_APP_ID ".mlkcca.com"; 25 | const char MQTT_CLIENTID[] PROGMEM = __TIME__ MILKCOCOA_APP_ID; 26 | 27 | Milkcocoa milkcocoa = Milkcocoa(&client, MQTT_SERVER, MILKCOCOA_SERVERPORT, MILKCOCOA_APP_ID, MQTT_CLIENTID); 28 | 29 | void onpush(DataElement *elem) { 30 | Serial.println("onpush"); 31 | Serial.println(elem->getInt("v")); 32 | }; 33 | 34 | void setupWiFi() { 35 | Serial.println(); Serial.println(); 36 | Serial.print("Connecting to "); 37 | Serial.println(WLAN_SSID); 38 | 39 | WiFi.begin(WLAN_SSID, WLAN_PASS); 40 | while (WiFi.status() != WL_CONNECTED) { 41 | delay(500); 42 | Serial.print("."); 43 | } 44 | Serial.println(); 45 | 46 | Serial.println("WiFi connected"); 47 | Serial.println("IP address: "); 48 | Serial.println(WiFi.localIP()); 49 | } 50 | 51 | void setup() { 52 | Serial.begin(115200); 53 | delay(10); 54 | Serial.println(F("Milkcocoa SDK demo")); 55 | 56 | setupWiFi(); 57 | 58 | Serial.println( milkcocoa.on(MILKCOCOA_DATASTORE, "push", onpush) ); 59 | }; 60 | 61 | void loop() { 62 | milkcocoa.loop(); 63 | 64 | DataElement elem = DataElement(); 65 | elem.setValue("v", 1); 66 | 67 | milkcocoa.push(MILKCOCOA_DATASTORE, &elem); 68 | delay(7000); 69 | }; 70 | 71 | -------------------------------------------------------------------------------- /examples/milkcocoa_esp8266_apikey_auth/milkcocoa_esp8266_apikey_auth.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /************************* WiFi Access Point *********************************/ 5 | 6 | #define WLAN_SSID "...SSID..." 7 | #define WLAN_PASS "...PASS..." 8 | 9 | 10 | /************************* Your Milkcocoa Setup *********************************/ 11 | 12 | #define MILKCOCOA_APP_ID "...YOUR_MILKCOCOA_APP_ID..." 13 | #define MILKCOCOA_DATASTORE "esp8266" 14 | #define MILKCOCOA_API_KEY "...YOUR_MILKCOCOA_API_KEY..." 15 | #define MILKCOCOA_API_SECRET "...YOUR_MILKCOCOA_API_SECRET..." 16 | 17 | /************* Milkcocoa Setup (you don't need to change this!) ******************/ 18 | 19 | #define MILKCOCOA_SERVERPORT 1883 20 | 21 | /************ Global State (you don't need to change this!) ******************/ 22 | 23 | // Create an ESP8266 WiFiClient class to connect to the MQTT server. 24 | WiFiClient client; 25 | 26 | const char MQTT_SERVER[] PROGMEM = MILKCOCOA_APP_ID ".mlkcca.com"; 27 | const char MQTT_CLIENTID[] PROGMEM = __TIME__ MILKCOCOA_APP_ID; 28 | 29 | Milkcocoa *milkcocoa = Milkcocoa::createWithApiKey(&client, MQTT_SERVER, MILKCOCOA_SERVERPORT, MILKCOCOA_APP_ID, MQTT_CLIENTID, MILKCOCOA_API_KEY, MILKCOCOA_API_SECRET); 30 | 31 | void onpush(DataElement *elem) { 32 | Serial.println("onpush"); 33 | Serial.println(elem->getInt("v")); 34 | }; 35 | 36 | void setupWiFi() { 37 | Serial.println(); Serial.println(); 38 | Serial.print("Connecting to "); 39 | Serial.println(WLAN_SSID); 40 | 41 | WiFi.begin(WLAN_SSID, WLAN_PASS); 42 | while (WiFi.status() != WL_CONNECTED) { 43 | delay(500); 44 | Serial.print("."); 45 | } 46 | Serial.println(); 47 | 48 | Serial.println("WiFi connected"); 49 | Serial.println("IP address: "); 50 | Serial.println(WiFi.localIP()); 51 | } 52 | 53 | void setup() { 54 | Serial.begin(115200); 55 | delay(10); 56 | 57 | Serial.println(F("Milkcocoa SDK demo")); 58 | 59 | setupWiFi(); 60 | 61 | Serial.println( milkcocoa->on(MILKCOCOA_DATASTORE, "push", onpush) ); 62 | }; 63 | 64 | void loop() { 65 | milkcocoa->loop(); 66 | 67 | DataElement elem = DataElement(); 68 | elem.setValue("v", 1); 69 | 70 | milkcocoa->push(MILKCOCOA_DATASTORE, &elem); 71 | delay(7000); 72 | }; 73 | -------------------------------------------------------------------------------- /examples/milkcocoa_esp8266_tout/milkcocoa_esp8266_tout.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /************************* WiFi Access Point *********************************/ 5 | 6 | #define WLAN_SSID "...SSID..." 7 | #define WLAN_PASS "...PASS..." 8 | 9 | 10 | /************************* Your Milkcocoa Setup *********************************/ 11 | 12 | #define MILKCOCOA_APP_ID "...YOUR_MILKCOCOA_APP_ID..." 13 | #define MILKCOCOA_DATASTORE "esp8266/tout" 14 | 15 | /************* Milkcocoa Setup (you don't need to change this!) ******************/ 16 | 17 | #define MILKCOCOA_SERVERPORT 1883 18 | 19 | /************ Global State (you don't need to change this!) ******************/ 20 | 21 | // Create an ESP8266 WiFiClient class to connect to the MQTT server. 22 | WiFiClient client; 23 | 24 | const char MQTT_SERVER[] PROGMEM = MILKCOCOA_APP_ID ".mlkcca.com"; 25 | const char MQTT_CLIENTID[] PROGMEM = __TIME__ MILKCOCOA_APP_ID; 26 | 27 | Milkcocoa milkcocoa = Milkcocoa(&client, MQTT_SERVER, MILKCOCOA_SERVERPORT, MILKCOCOA_APP_ID, MQTT_CLIENTID); 28 | 29 | void onpush(DataElement *elem) { 30 | Serial.println("onpush"); 31 | Serial.println(elem->getInt("v")); 32 | }; 33 | 34 | void setupWiFi() { 35 | Serial.println(); Serial.println(); 36 | Serial.print("Connecting to "); 37 | Serial.println(WLAN_SSID); 38 | 39 | WiFi.begin(WLAN_SSID, WLAN_PASS); 40 | while (WiFi.status() != WL_CONNECTED) { 41 | delay(500); 42 | Serial.print("."); 43 | } 44 | Serial.println(); 45 | 46 | Serial.println("WiFi connected"); 47 | Serial.println("IP address: "); 48 | Serial.println(WiFi.localIP()); 49 | } 50 | 51 | void setup() { 52 | Serial.begin(115200); 53 | delay(10); 54 | Serial.println(F("Milkcocoa SDK demo")); 55 | 56 | setupWiFi(); 57 | 58 | Serial.println( milkcocoa.on(MILKCOCOA_DATASTORE, "push", onpush) ); 59 | }; 60 | 61 | void loop() { 62 | milkcocoa.loop(); 63 | 64 | int val = analogRead(A0); 65 | Serial.println(String(val)); 66 | 67 | DataElement elem = DataElement(); 68 | elem.setValue("v", val); 69 | 70 | milkcocoa.push(MILKCOCOA_DATASTORE, &elem); 71 | delay(7000); 72 | }; 73 | -------------------------------------------------------------------------------- /examples/milkcocoa_esp8266_tout_by_half_hour/milkcocoa_esp8266_tout_by_half_hour.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: 3 | * When you use this sketch, 4 | * please connect WAKE-pin(ESP-WROOM-02's IO16-pin) to RESET-pin. 5 | * 6 | **/ 7 | 8 | #include 9 | #include 10 | 11 | /************************* WiFi Access Point *********************************/ 12 | 13 | #define WLAN_SSID "...SSID..." 14 | #define WLAN_PASS "...PASS..." 15 | 16 | 17 | /************************* Your Milkcocoa Setup *********************************/ 18 | 19 | #define MILKCOCOA_APP_ID "...YOUR_MILKCOCOA_APP_ID..." 20 | #define MILKCOCOA_DATASTORE "esp8266/tout/half-hour" 21 | 22 | /************* Milkcocoa Setup (you don't need to change this!) ******************/ 23 | 24 | #define MILKCOCOA_SERVERPORT 1883 25 | 26 | /************ Global State (you don't need to change this!) ******************/ 27 | 28 | // Create an ESP8266 WiFiClient class to connect to the MQTT server. 29 | WiFiClient client; 30 | 31 | const char MQTT_SERVER[] PROGMEM = MILKCOCOA_APP_ID ".mlkcca.com"; 32 | const char MQTT_CLIENTID[] PROGMEM = __TIME__ MILKCOCOA_APP_ID; 33 | 34 | Milkcocoa milkcocoa = Milkcocoa(&client, MQTT_SERVER, MILKCOCOA_SERVERPORT, MILKCOCOA_APP_ID, MQTT_CLIENTID); 35 | 36 | 37 | /** 38 | * Functions 39 | **/ 40 | void setupWiFi() { 41 | Serial.println(); Serial.println(); 42 | Serial.println("WiFi Connection->AP=================="); 43 | Serial.print("Connecting to "); Serial.print(WLAN_SSID); 44 | 45 | WiFi.begin(WLAN_SSID, WLAN_PASS); 46 | int wifiCounter = 0; 47 | while (WiFi.status() != WL_CONNECTED) { 48 | delay(450); 49 | Serial.print("."); 50 | if(wifiCounter > 20) { 51 | Serial.println("Failed to connect WiFi. Reset now."); 52 | delay(500); 53 | ESP.deepSleep(2 * 1000 * 1000); 54 | delay(1000); 55 | } 56 | delay(50); 57 | wifiCounter++; 58 | } 59 | Serial.println(); 60 | 61 | Serial.println("WiFi connected"); 62 | Serial.print("IP address: "); Serial.println(WiFi.localIP()); 63 | Serial.println("====================================="); 64 | }; 65 | 66 | void connectToMilkcocoa() { 67 | if(!milkcocoa.loop(10*5000)){ 68 | Serial.println("Failed to connect Milkcocoa. Reset now."); 69 | delay(500); 70 | ESP.deepSleep(2 * 1000 * 1000); 71 | delay(1000); 72 | } 73 | }; 74 | 75 | void onpush(DataElement *elem) { 76 | Serial.println("Onpush comming."); 77 | Serial.print("Got value: "); Serial.println(elem->getInt("v")); 78 | 79 | Serial.println("Sleep start, reset after 30 minutes."); 80 | delay(500); 81 | ESP.deepSleep(1800000000); 82 | delay(1000); 83 | }; 84 | 85 | 86 | /** 87 | * Main 88 | **/ 89 | void setup() { 90 | Serial.begin(115200); 91 | delay(10); 92 | 93 | setupWiFi(); 94 | Serial.println(); 95 | 96 | if( milkcocoa.on(MILKCOCOA_DATASTORE, "push", onpush) ) Serial.println("Milkcocoa on sucesss"); 97 | else Serial.println("Milkcocoa on failure"); 98 | Serial.println(); 99 | }; 100 | 101 | int loopCounter = 0; 102 | 103 | void loop() { 104 | connectToMilkcocoa(); 105 | 106 | int sensorValue = analogRead(A0); 107 | Serial.println("Read sensor value: " + String(sensorValue)); 108 | 109 | DataElement elem = DataElement(); 110 | elem.setValue("v", sensorValue); 111 | Serial.println(); 112 | 113 | if(loopCounter == 0) milkcocoa.push(MILKCOCOA_DATASTORE, &elem); 114 | 115 | delay(100); 116 | 117 | if(loopCounter > 50){ 118 | Serial.println("Failed to push data to Milkcocoa. Reset now."); 119 | delay(500); 120 | ESP.deepSleep(2 * 1000 * 1000); 121 | delay(1000); 122 | } 123 | loopCounter++; 124 | }; 125 | -------------------------------------------------------------------------------- /include/Adafruit/Adafruit_MQTT.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Adafruit Industries 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | #ifndef _ADAFRUIT_MQTT_H_ 23 | #define _ADAFRUIT_MQTT_H_ 24 | 25 | #if ARDUINO >= 100 26 | #include "Arduino.h" 27 | #else 28 | #include "WProgram.h" 29 | #endif 30 | 31 | // Uncomment/comment to turn on/off debug output messages. 32 | //#define MQTT_DEBUG 33 | 34 | // Set where debug messages will be printed. 35 | #define DEBUG_PRINTER Serial 36 | 37 | // Define actual debug output functions when necessary. 38 | #ifdef MQTT_DEBUG 39 | #define DEBUG_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); } 40 | #define DEBUG_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); } 41 | #define DEBUG_PRINTBUFFER(buffer, len) { printBuffer(buffer, len); } 42 | #else 43 | #define DEBUG_PRINT(...) {} 44 | #define DEBUG_PRINTLN(...) {} 45 | #define DEBUG_PRINTBUFFER(buffer, len) {} 46 | #endif 47 | 48 | #define MQTT_PROTOCOL_LEVEL 4 49 | 50 | #define MQTT_CTRL_CONNECT 0x01 51 | #define MQTT_CTRL_CONNECTACK 0x02 52 | #define MQTT_CTRL_PUBLISH 0x03 53 | #define MQTT_CTRL_SUBSCRIBE 0x08 54 | #define MQTT_CTRL_SUBACK 0x09 55 | #define MQTT_CTRL_PINGREQ 0x0C 56 | #define MQTT_CTRL_PINGRESP 0x0D 57 | 58 | #define MQTT_QOS_1 0x1 59 | #define MQTT_QOS_0 0x0 60 | 61 | #define CONNECT_TIMEOUT_MS 3000 62 | #define PUBLISH_TIMEOUT_MS 500 63 | #define PING_TIMEOUT_MS 500 64 | 65 | // Adjust as necessary, in seconds. Default to 5 minutes. 66 | #define MQTT_CONN_KEEPALIVE 30 67 | 68 | // Largest full packet we're able to send. 69 | // Need to be able to store at least ~90 chars for a connect packet with full 70 | // 23 char client ID. 71 | #define MAXBUFFERSIZE (125) 72 | 73 | #define MQTT_CONN_USERNAMEFLAG 0x80 74 | #define MQTT_CONN_PASSWORDFLAG 0x40 75 | #define MQTT_CONN_WILLRETAIN 0x20 76 | #define MQTT_CONN_WILLQOS 0x08 77 | #define MQTT_CONN_WILLFLAG 0x04 78 | #define MQTT_CONN_CLEANSESSION 0x02 79 | 80 | // how many subscriptions we want to be able to 81 | // track 82 | #define MAXSUBSCRIPTIONS 5 83 | 84 | // how much data we save in a subscription object 85 | // eg max-subscription-payload-size 86 | #define SUBSCRIPTIONDATALEN 1024 87 | 88 | 89 | extern void printBuffer(uint8_t *buffer, uint8_t len); 90 | 91 | class Adafruit_MQTT_Subscribe; // forward decl 92 | 93 | class Adafruit_MQTT { 94 | public: 95 | Adafruit_MQTT(const char *server, uint16_t port, const char *cid, 96 | const char *user, const char *pass); 97 | Adafruit_MQTT(const __FlashStringHelper *server, uint16_t port, const __FlashStringHelper *cid, 98 | const __FlashStringHelper *user, const __FlashStringHelper *pass); 99 | virtual ~Adafruit_MQTT() {} 100 | 101 | // Connect to the MQTT server. Returns 0 on success, otherwise an error code 102 | // that indicates something went wrong: 103 | // -1 = Error connecting to server 104 | // 1 = Wrong protocol 105 | // 2 = ID rejected 106 | // 3 = Server unavailable 107 | // 4 = Bad username or password 108 | // 5 = Not authenticated 109 | // 6 = Failed to subscribe 110 | // Use connectErrorString() to get a printable string version of the 111 | // error. 112 | int8_t connect(); 113 | 114 | // Return a printable string version of the error code returned by 115 | // connect(). This returns a __FlashStringHelper*, which points to a 116 | // string stored in flash, but can be directly passed to e.g. 117 | // Serial.println without any further processing. 118 | const __FlashStringHelper* connectErrorString(int8_t code); 119 | 120 | // Disconnect from the MQTT server. Returns true if disconnected, false 121 | // otherwise. 122 | virtual bool disconnect() = 0; // Subclasses need to fill this in! 123 | 124 | // Return true if connected to the MQTT server, otherwise false. 125 | virtual bool connected() = 0; // Subclasses need to fill this in! 126 | 127 | // Publish a message to a topic using the specified QoS level. Returns true 128 | // if the message was published, false otherwise. 129 | // The topic must be stored in PROGMEM. It can either be a 130 | // char*, or a __FlashStringHelper* (the result of the F() macro). 131 | bool publish(const char *topic, const char *payload, uint8_t qos = 0); 132 | bool publish(const __FlashStringHelper *topic, const char *payload, uint8_t qos = 0) { 133 | return publish((const char *)topic, payload, qos); 134 | } 135 | 136 | // Add a subscription to receive messages for a topic. Returns true if the 137 | // subscription could be added or was already present, false otherwise. 138 | // Must be called before connect(), subscribing after the connection 139 | // is made is not currently supported. 140 | bool subscribe(Adafruit_MQTT_Subscribe *sub); 141 | 142 | // Check if any subscriptions have new messages. Will return a reference to 143 | // an Adafruit_MQTT_Subscribe object which has a new message. Should be called 144 | // in the sketch's loop function to ensure new messages are recevied. Note 145 | // that subscribe should be called first for each topic that receives messages! 146 | Adafruit_MQTT_Subscribe *readSubscription(int16_t timeout=0); 147 | 148 | // Ping the server to ensure the connection is still alive. Returns true if 149 | // successful, otherwise false. 150 | bool ping(uint8_t t); 151 | 152 | protected: 153 | // Interface that subclasses need to implement: 154 | 155 | // Connect to the server and return true if successful, false otherwise. 156 | virtual bool connectServer() = 0; 157 | 158 | // Send data to the server specified by the buffer and length of data. 159 | virtual bool sendPacket(uint8_t *buffer, uint8_t len) = 0; 160 | 161 | // Read MQTT packet from the server. Will read up to maxlen bytes and store 162 | // the data in the provided buffer. Waits up to the specified timeout (in 163 | // milliseconds) for data to be available. If checkForValidPubPacket is true 164 | // then the received data is verified to make sure it's a complete packet. 165 | virtual uint16_t readPacket(uint8_t *buffer, uint8_t maxlen, int16_t timeout, 166 | bool checkForValidPubPacket = false) = 0; 167 | 168 | // Shared state that subclasses can use: 169 | const char *servername; 170 | int16_t portnum; 171 | const char *clientid; 172 | const char *username; 173 | const char *password; 174 | uint8_t buffer[MAXBUFFERSIZE]; // one buffer, used for all incoming/outgoing 175 | 176 | private: 177 | Adafruit_MQTT_Subscribe *subscriptions[MAXSUBSCRIPTIONS]; 178 | 179 | // Functions to generate MQTT packets. 180 | uint8_t connectPacket(uint8_t *packet); 181 | uint8_t publishPacket(uint8_t *packet, const char *topic, const char *payload, uint8_t qos); 182 | uint8_t subscribePacket(uint8_t *packet, const char *topic, uint8_t qos); 183 | uint8_t pingPacket(uint8_t *packet); 184 | }; 185 | 186 | 187 | class Adafruit_MQTT_Publish { 188 | public: 189 | Adafruit_MQTT_Publish(Adafruit_MQTT *mqttserver, const char *feed, uint8_t qos = 0); 190 | Adafruit_MQTT_Publish(Adafruit_MQTT *mqttserver, const __FlashStringHelper *feed, uint8_t qos = 0); 191 | 192 | bool publish(const char *s); 193 | bool publish(double f, uint8_t precision=2); // Precision controls the minimum number of digits after decimal. 194 | // This might be ignored and a higher precision value sent. 195 | bool publish(int32_t i); 196 | bool publish(uint32_t i); 197 | 198 | private: 199 | Adafruit_MQTT *mqtt; 200 | const char *topic; 201 | uint8_t qos; 202 | }; 203 | 204 | class Adafruit_MQTT_Subscribe { 205 | public: 206 | Adafruit_MQTT_Subscribe(Adafruit_MQTT *mqttserver, const char *feedname, uint8_t q=0); 207 | Adafruit_MQTT_Subscribe(Adafruit_MQTT *mqttserver, const __FlashStringHelper *feedname, uint8_t q=0); 208 | 209 | bool setCallback(void (*callback)(char *)); 210 | 211 | const char *topic; 212 | uint8_t qos; 213 | 214 | uint8_t lastread[SUBSCRIPTIONDATALEN]; 215 | // Number valid bytes in lastread. Limited to SUBSCRIPTIONDATALEN-1 to 216 | // ensure nul terminating lastread. 217 | uint8_t datalen; 218 | private: 219 | Adafruit_MQTT *mqtt; 220 | }; 221 | 222 | 223 | #endif 224 | -------------------------------------------------------------------------------- /include/Adafruit/Adafruit_MQTT_CC3000.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Adafruit Industries 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | #ifndef _ADAFRUIT_MQTT_CC3000_H_ 23 | #define _ADAFRUIT_MQTT_CC3000_H_ 24 | 25 | #include 26 | #include 27 | #include "Adafruit_MQTT.h" 28 | 29 | 30 | // delay in ms between calls of available() 31 | #define MQTT_CC3000_INTERAVAILDELAY 10 32 | 33 | 34 | // CC3000-specific version of the Adafruit_MQTT class. 35 | // Note that this is defined as a header-only class to prevent issues with using 36 | // the library on non-CC3000 platforms (since Arduino will include all .cpp files 37 | // in the compilation of the library). 38 | class Adafruit_MQTT_CC3000 : public Adafruit_MQTT { 39 | public: 40 | Adafruit_MQTT_CC3000(Adafruit_CC3000 *cc3k, const char *server, uint16_t port, 41 | const char *cid, const char *user, const char *pass): 42 | Adafruit_MQTT(server, port, cid, user, pass), 43 | cc3000(cc3k) 44 | {} 45 | 46 | bool connectServer() { 47 | uint32_t ip = 0; 48 | 49 | Watchdog.reset(); 50 | 51 | // look up IP address 52 | if (serverip == 0) { 53 | // Try looking up the website's IP address using CC3K's built in getHostByName 54 | strcpy_P((char *)buffer, servername); 55 | Serial.print((char *)buffer); Serial.print(F(" -> ")); 56 | uint8_t dnsretries = 5; 57 | 58 | Watchdog.reset(); 59 | while (ip == 0) { 60 | if (! cc3000->getHostByName((char *)buffer, &ip)) { 61 | Serial.println(F("Couldn't resolve!")); 62 | dnsretries--; 63 | Watchdog.reset(); 64 | } 65 | //Serial.println("OK"); Serial.println(ip, HEX); 66 | if (!dnsretries) return false; 67 | delay(500); 68 | } 69 | 70 | serverip = ip; 71 | cc3000->printIPdotsRev(serverip); 72 | Serial.println(); 73 | } 74 | 75 | Watchdog.reset(); 76 | 77 | // connect to server 78 | DEBUG_PRINTLN(F("Connecting to TCP")); 79 | mqttclient = cc3000->connectTCP(serverip, portnum); 80 | 81 | return mqttclient.connected(); 82 | } 83 | 84 | bool disconnect() { 85 | if (connected()) { 86 | return (mqttclient.close() == 0); 87 | } 88 | else { 89 | return true; 90 | } 91 | } 92 | 93 | bool connected() { 94 | return mqttclient.connected(); 95 | } 96 | 97 | uint16_t readPacket(uint8_t *buffer, uint8_t maxlen, int16_t timeout, 98 | bool checkForValidPubPacket = false) { 99 | /* Read data until either the connection is closed, or the idle timeout is reached. */ 100 | uint16_t len = 0; 101 | int16_t t = timeout; 102 | 103 | while (mqttclient.connected() && (timeout >= 0)) { 104 | //DEBUG_PRINT('.'); 105 | while (mqttclient.available()) { 106 | //DEBUG_PRINT('!'); 107 | char c = mqttclient.read(); 108 | timeout = t; // reset the timeout 109 | buffer[len] = c; 110 | //DEBUG_PRINTLN((uint8_t)c, HEX); 111 | len++; 112 | if (len == maxlen) { // we read all we want, bail 113 | DEBUG_PRINT(F("Read packet:\t")); 114 | DEBUG_PRINTBUFFER(buffer, len); 115 | return len; 116 | } 117 | 118 | // special case where we just one one publication packet at a time 119 | if (checkForValidPubPacket) { 120 | if ((buffer[0] == (MQTT_CTRL_PUBLISH << 4)) && (buffer[1] == len-2)) { 121 | // oooh a valid publish packet! 122 | DEBUG_PRINT(F("Read PUBLISH packet:\t")); 123 | DEBUG_PRINTBUFFER(buffer, len); 124 | return len; 125 | } 126 | } 127 | } 128 | Watchdog.reset(); 129 | timeout -= MQTT_CC3000_INTERAVAILDELAY; 130 | delay(MQTT_CC3000_INTERAVAILDELAY); 131 | } 132 | return len; 133 | } 134 | 135 | bool sendPacket(uint8_t *buffer, uint8_t len) { 136 | if (mqttclient.connected()) { 137 | uint16_t ret = mqttclient.write(buffer, len); 138 | DEBUG_PRINT(F("sendPacket returned: ")); DEBUG_PRINTLN(ret); 139 | if (ret != len) { 140 | DEBUG_PRINTLN("Failed to send complete packet.") 141 | return false; 142 | } 143 | } else { 144 | DEBUG_PRINTLN(F("Connection failed!")); 145 | return false; 146 | } 147 | return true; 148 | } 149 | 150 | private: 151 | uint32_t serverip; 152 | Adafruit_CC3000 *cc3000; 153 | Adafruit_CC3000_Client mqttclient; 154 | }; 155 | 156 | 157 | #endif 158 | -------------------------------------------------------------------------------- /include/Adafruit/Adafruit_MQTT_Client.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Adafruit Industries 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | #ifndef _ADAFRUIT_MQTT_CLIENT_H_ 23 | #define _ADAFRUIT_MQTT_CLIENT_H_ 24 | 25 | #include "Client.h" 26 | #include "Adafruit_MQTT.h" 27 | 28 | 29 | // How long to delay waiting for new data to be available in readPacket. 30 | #define MQTT_CLIENT_READINTERVAL_MS 10 31 | 32 | 33 | // MQTT client implementation for a generic Arduino Client interface. Can work 34 | // with almost all Arduino network hardware like ethernet shield, wifi shield, 35 | // and even other platforms like ESP8266. 36 | class Adafruit_MQTT_Client : public Adafruit_MQTT { 37 | public: 38 | Adafruit_MQTT_Client(Client *client, const char *server, uint16_t port, 39 | const char *cid, const char *user, 40 | const char *pass): 41 | Adafruit_MQTT(server, port, cid, user, pass), 42 | client(client) 43 | {} 44 | 45 | bool connectServer(); 46 | bool disconnect(); 47 | bool connected(); 48 | uint16_t readPacket(uint8_t *buffer, uint8_t maxlen, int16_t timeout, 49 | bool checkForValidPubPacket = false); 50 | bool sendPacket(uint8_t *buffer, uint8_t len); 51 | 52 | private: 53 | Client* client; 54 | }; 55 | 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /include/Adafruit/Adafruit_MQTT_FONA.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Adafruit Industries 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | #ifndef _ADAFRUIT_MQTT_FONA_H_ 23 | #define _ADAFRUIT_MQTT_FONA_H_ 24 | 25 | #include 26 | #include "Adafruit_MQTT.h" 27 | 28 | #define MQTT_FONA_INTERAVAILDELAY 100 29 | #define MQTT_FONA_QUERYDELAY 500 30 | 31 | 32 | // FONA-specific version of the Adafruit_MQTT class. 33 | // Note that this is defined as a header-only class to prevent issues with using 34 | // the library on non-FONA platforms (since Arduino will include all .cpp files 35 | // in the compilation of the library). 36 | class Adafruit_MQTT_FONA : public Adafruit_MQTT { 37 | public: 38 | Adafruit_MQTT_FONA(Adafruit_FONA *f, const char *server, uint16_t port, 39 | const char *cid, const char *user, const char *pass): 40 | Adafruit_MQTT(server, port, cid, user, pass), 41 | fona(f) 42 | {} 43 | 44 | bool connectServer() { 45 | char server[40]; 46 | strncpy_P(server, servername, 40); 47 | Watchdog.reset(); 48 | 49 | // connect to server 50 | DEBUG_PRINTLN(F("Connecting to TCP")); 51 | return fona->TCPconnect(server, portnum); 52 | } 53 | 54 | bool disconnect() { 55 | return fona->TCPclose(); 56 | } 57 | 58 | bool connected() { 59 | // Return true if connected, false if not connected. 60 | return fona->TCPconnected(); 61 | } 62 | 63 | uint16_t readPacket(uint8_t *buffer, uint8_t maxlen, int16_t timeout, 64 | bool checkForValidPubPacket = false) { 65 | uint8_t *buffp = buffer; 66 | DEBUG_PRINTLN(F("Reading a packet..")); 67 | 68 | if (!fona->TCPconnected()) return 0; 69 | 70 | 71 | /* Read data until either the connection is closed, or the idle timeout is reached. */ 72 | uint16_t len = 0; 73 | int16_t t = timeout; 74 | uint16_t avail; 75 | 76 | while (fona->TCPconnected() && (timeout >= 0)) { 77 | DEBUG_PRINT('.'); 78 | while (avail = fona->TCPavailable()) { 79 | DEBUG_PRINT('!'); 80 | 81 | if (len + avail > maxlen) { 82 | avail = maxlen - len; 83 | if (avail == 0) return len; 84 | } 85 | 86 | // try to read the data into the end of the pointer 87 | if (! fona->TCPread(buffp, avail)) return len; 88 | 89 | // read it! advance pointer 90 | buffp += avail; 91 | len += avail; 92 | timeout = t; // reset the timeout 93 | 94 | //DEBUG_PRINTLN((uint8_t)c, HEX); 95 | 96 | if (len == maxlen) { // we read all we want, bail 97 | DEBUG_PRINT(F("Read packet:\t")); 98 | DEBUG_PRINTBUFFER(buffer, len); 99 | return len; 100 | } 101 | 102 | // special case where we just one one publication packet at a time 103 | if (checkForValidPubPacket) { 104 | if ((buffer[0] == (MQTT_CTRL_PUBLISH << 4)) && (buffer[1] == len-2)) { 105 | // oooh a valid publish packet! 106 | DEBUG_PRINT(F("Read PUBLISH packet:\t")); 107 | DEBUG_PRINTBUFFER(buffer, len); 108 | return len; 109 | } 110 | } 111 | 112 | } 113 | Watchdog.reset(); 114 | timeout -= MQTT_FONA_INTERAVAILDELAY; 115 | timeout -= MQTT_FONA_QUERYDELAY; // this is how long it takes to query the FONA for avail() 116 | delay(MQTT_FONA_INTERAVAILDELAY); 117 | } 118 | 119 | return len; 120 | } 121 | 122 | bool sendPacket(uint8_t *buffer, uint8_t len) { 123 | DEBUG_PRINTLN(F("Writing packet")); 124 | if (fona->TCPconnected()) { 125 | boolean ret = fona->TCPsend((char *)buffer, len); 126 | //DEBUG_PRINT(F("sendPacket returned: ")); DEBUG_PRINTLN(ret); 127 | if (!ret) { 128 | DEBUG_PRINTLN("Failed to send packet.") 129 | return false; 130 | } 131 | } else { 132 | DEBUG_PRINTLN(F("Connection failed!")); 133 | return false; 134 | } 135 | return true; 136 | } 137 | 138 | private: 139 | uint32_t serverip; 140 | Adafruit_FONA *fona; 141 | }; 142 | 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /include/aJson/aJSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2001, Interactive Matter, Marcus Nowotny 3 | 4 | Based on the cJSON Library, Copyright (C) 2009 Dave Gamble 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | #ifndef aJson__h 26 | #define aJson__h 27 | 28 | #include 29 | #include 30 | #include 31 | #include // To get access to the Arduino millis() function 32 | 33 | /****************************************************************************** 34 | * Definitions 35 | ******************************************************************************/ 36 | // aJson Types: 37 | #define aJson_NULL 0 38 | #define aJson_Boolean 1 39 | #define aJson_Int 2 40 | #define aJson_Float 3 41 | #define aJson_String 4 42 | #define aJson_Array 5 43 | #define aJson_Object 6 44 | 45 | #define aJson_IsReference 128 46 | 47 | #ifndef EOF 48 | #define EOF -1 49 | #endif 50 | 51 | #define PRINT_BUFFER_LEN 256 52 | 53 | // The aJson structure: 54 | typedef struct aJsonObject { 55 | char *name; // The item's name string, if this item is the child of, or is in the list of subitems of an object. 56 | struct aJsonObject *next, *prev; // next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem 57 | struct aJsonObject *child; // An array or object item will have a child pointer pointing to a chain of the items in the array/object. 58 | 59 | char type; // The type of the item, as above. 60 | 61 | union { 62 | char *valuestring; // The item's string, if type==aJson_String 63 | char valuebool; //the items value for true & false 64 | int valueint; // The item's value, if type==aJson_Int 65 | double valuefloat; // The item's value, if type==aJson_Float 66 | }; 67 | } aJsonObject; 68 | 69 | /* aJsonStream is stream representation of aJson for its internal use; 70 | * it is meant to abstract out differences between Stream (e.g. serial 71 | * stream) and Client (which may or may not be connected) or provide even 72 | * stream-ish interface to string buffers. */ 73 | class aJsonStream : public Print { 74 | public: 75 | aJsonStream(Stream *stream_) 76 | : stream_obj(stream_), bucket(EOF) 77 | {} 78 | /* Use this to check if more data is available, as aJsonStream 79 | * can read some more data than really consumed and automatically 80 | * skips separating whitespace if you use this method. */ 81 | virtual bool available(); 82 | 83 | int parseNumber(aJsonObject *item); 84 | int printInt(aJsonObject *item); 85 | int printFloat(aJsonObject *item); 86 | 87 | int parseString(aJsonObject *item); 88 | int printStringPtr(const char *str); 89 | int printString(aJsonObject *item); 90 | 91 | int skip(); 92 | int flush(); 93 | 94 | int parseValue(aJsonObject *item, char** filter); 95 | int printValue(aJsonObject *item); 96 | 97 | int parseArray(aJsonObject *item, char** filter); 98 | int printArray(aJsonObject *item); 99 | 100 | int parseObject(aJsonObject *item, char** filter); 101 | int printObject(aJsonObject *item); 102 | 103 | protected: 104 | /* Blocking load of character, returning EOF if the stream 105 | * is exhausted. */ 106 | /* Base implementation just looks at bucket, returns EOF 107 | * otherwise; descendats take care of the real reading. */ 108 | virtual int getch(); 109 | virtual size_t readBytes(uint8_t *buffer, size_t len); 110 | /* Return the character back to the front of the stream 111 | * after loading it with getch(). Only returning a single 112 | * character is supported. */ 113 | virtual void ungetch(char ch); 114 | 115 | /* Inherited from class Print. */ 116 | virtual size_t write(uint8_t ch); 117 | 118 | /* stream attribute is used only from virtual functions, 119 | * therefore an object inheriting aJsonStream may avoid 120 | * using streams completely. */ 121 | Stream *stream_obj; 122 | /* Use this accessor for stream retrieval; some subclasses 123 | * may use their own stream subclass. */ 124 | virtual inline Stream *stream() { return stream_obj; } 125 | 126 | /* bucket is EOF by default. Otherwise, it is a character 127 | * to be returned by next getch() - returned by a call 128 | * to ungetch(). */ 129 | int bucket; 130 | }; 131 | 132 | /* JSON stream that consumes data from a connection (usually 133 | * Ethernet client) until the connection is closed. */ 134 | class aJsonClientStream : public aJsonStream { 135 | public: 136 | aJsonClientStream(Client *stream_) 137 | : aJsonStream(NULL), client_obj(stream_) 138 | {} 139 | 140 | private: 141 | virtual int getch(); 142 | 143 | Client *client_obj; 144 | virtual inline Client *stream() { return client_obj; } 145 | }; 146 | 147 | /* JSON stream that is bound to input and output string buffer. This is 148 | * for internal usage by string-based aJsonClass methods. */ 149 | /* TODO: Elastic output buffer support. */ 150 | class aJsonStringStream : public aJsonStream { 151 | public: 152 | /* Either of inbuf, outbuf can be NULL if you do not care about 153 | * particular I/O direction. */ 154 | aJsonStringStream(char *inbuf_, char *outbuf_ = NULL, size_t outbuf_len_ = 0) 155 | : aJsonStream(NULL), inbuf(inbuf_), outbuf(outbuf_), outbuf_len(outbuf_len_) 156 | { 157 | inbuf_len = inbuf ? strlen(inbuf) : 0; 158 | } 159 | 160 | virtual bool available(); 161 | 162 | private: 163 | virtual int getch(); 164 | virtual size_t write(uint8_t ch); 165 | 166 | char *inbuf, *outbuf; 167 | size_t inbuf_len, outbuf_len; 168 | }; 169 | 170 | class aJsonClass { 171 | /****************************************************************************** 172 | * Constructors 173 | ******************************************************************************/ 174 | 175 | /****************************************************************************** 176 | * User API 177 | ******************************************************************************/ 178 | public: 179 | // Supply a block of JSON, and this returns a aJson object you can interrogate. Call aJson.deleteItem when finished. 180 | aJsonObject* parse(aJsonStream* stream); //Reads from a stream 181 | aJsonObject* parse(aJsonStream* stream,char** filter_values); //Read from a file, but only return values include in the char* array filter_values 182 | aJsonObject* parse(char *value); //Reads from a string 183 | // Render a aJsonObject entity to text for transfer/storage. Free the char* when finished. 184 | int print(aJsonObject *item, aJsonStream* stream); 185 | char* print(aJsonObject* item); 186 | //Renders a aJsonObject directly to a output stream 187 | char stream(aJsonObject *item, aJsonStream* stream); 188 | // Delete a aJsonObject entity and all sub-entities. 189 | void deleteItem(aJsonObject *c); 190 | 191 | // Returns the number of items in an array (or object). 192 | unsigned char getArraySize(aJsonObject *array); 193 | // Retrieve item number "item" from array "array". Returns NULL if unsuccessful. 194 | aJsonObject* getArrayItem(aJsonObject *array, unsigned char item); 195 | // Get item "string" from object. Case insensitive. 196 | aJsonObject* getObjectItem(aJsonObject *object, const char *string); 197 | 198 | // These calls create a aJsonObject item of the appropriate type. 199 | aJsonObject* createNull(); 200 | aJsonObject* createItem(bool b); 201 | aJsonObject* createItem(char b); 202 | aJsonObject* createItem(int num); 203 | aJsonObject* createItem(double num); 204 | aJsonObject* createItem(const char *string); 205 | aJsonObject* createArray(); 206 | aJsonObject* createObject(); 207 | 208 | // These utilities create an Array of count items. 209 | aJsonObject* createIntArray(int *numbers, unsigned char count); 210 | aJsonObject* createFloatArray(double *numbers, unsigned char count); 211 | aJsonObject* createDoubleArray(double *numbers, unsigned char count); 212 | aJsonObject* createStringArray(const char **strings, unsigned char count); 213 | 214 | // Append item to the specified array/object. 215 | void addItemToArray(aJsonObject *array, aJsonObject *item); 216 | void addItemToObject(aJsonObject *object, const char *string, 217 | aJsonObject *item); 218 | // Append reference to item to the specified array/object. Use this when you want to add an existing aJsonObject to a new aJsonObject, but don't want to corrupt your existing aJsonObject. 219 | void addItemReferenceToArray(aJsonObject *array, aJsonObject *item); 220 | void addItemReferenceToObject(aJsonObject *object, const char *string, 221 | aJsonObject *item); 222 | 223 | // Remove/Detach items from Arrays/Objects. 224 | aJsonObject* detachItemFromArray(aJsonObject *array, unsigned char which); 225 | void deleteItemFromArray(aJsonObject *array, unsigned char which); 226 | aJsonObject* detachItemFromObject(aJsonObject *object, const char *string); 227 | void deleteItemFromObject(aJsonObject *object, const char *string); 228 | 229 | // Update array items. 230 | void replaceItemInArray(aJsonObject *array, unsigned char which, 231 | aJsonObject *newitem); 232 | void replaceItemInObject(aJsonObject *object, const char *string, 233 | aJsonObject *newitem); 234 | 235 | void addNullToObject(aJsonObject* object, const char* name); 236 | void addBooleanToObject(aJsonObject* object, const char* name, bool b); 237 | void addNumberToObject(aJsonObject* object, const char* name, int n); 238 | void addNumberToObject(aJsonObject* object, const char* name, double n); 239 | void addStringToObject(aJsonObject* object, const char* name, 240 | const char* s); 241 | 242 | protected: 243 | friend class aJsonStream; 244 | static aJsonObject* newItem(); 245 | 246 | private: 247 | void suffixObject(aJsonObject *prev, aJsonObject *item); 248 | 249 | aJsonObject* createReference(aJsonObject *item); 250 | }; 251 | 252 | extern aJsonClass aJson; 253 | 254 | #endif 255 | -------------------------------------------------------------------------------- /include/aJson/stringbuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aJson 3 | * stringbuffer.h 4 | * 5 | * http://interactive-matter.org/ 6 | * 7 | * This file is part of aJson. 8 | * 9 | * aJson is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * aJson is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * You should have received a copy of the GNU General Public License 19 | * along with aJson. If not, see . 20 | * 21 | * Created on: 14.10.2010 22 | * Author: marcus 23 | */ 24 | 25 | #ifndef STRINGBUFFER_H_ 26 | #define STRINGBUFFER_H_ 27 | 28 | typedef struct 29 | { 30 | char* string; 31 | unsigned int memory; 32 | unsigned int string_length; 33 | } string_buffer; 34 | 35 | #ifdef __cplusplus 36 | extern "C" 37 | { 38 | #endif 39 | 40 | string_buffer* 41 | stringBufferCreate(void); 42 | 43 | char 44 | stringBufferAdd(char value, string_buffer* buffer); 45 | 46 | char* 47 | stringBufferToString(string_buffer* buffer); 48 | 49 | void 50 | stringBufferFree(string_buffer* buffer); 51 | 52 | #ifdef __cplusplus 53 | } 54 | #endif 55 | #endif /* STRINGBUFFER_H_ */ 56 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Milkcocoa ESP8266 SDK 2 | version=0.0.1 3 | author=Technical Rockstars 4 | maintainer=Technical Rockstars 5 | sentence=Milkcocoa SDK for Arduino & ESP8266. 6 | paragraph=Milkcocoa SDK for Arduino & ESP8266. 7 | category=Communication 8 | url=https://github.com/milk-cocoa/Milkcocoa_ESP8266_SDK 9 | architectures=* 10 | -------------------------------------------------------------------------------- /stringbuffer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * aJson 3 | * stringbuffer.c 4 | * 5 | * http://interactive-matter.org/ 6 | * 7 | * This file is part of aJson. 8 | * 9 | * aJson is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * aJson is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * You should have received a copy of the GNU General Public License 19 | * along with aJson. If not, see . 20 | * 21 | * Created on: 14.10.2010 22 | * Author: marcus 23 | */ 24 | #include 25 | #include 26 | #include "include/aJson/stringbuffer.h" 27 | 28 | //Default buffer size for strings 29 | #define BUFFER_SIZE 256 30 | //there is a static buffer allocated, which is used to decode strings to 31 | //strings cannot be longer than the buffer 32 | char global_buffer[BUFFER_SIZE]; 33 | 34 | string_buffer* 35 | stringBufferCreate(void) 36 | { 37 | string_buffer* result = malloc(sizeof(string_buffer)); 38 | if (result == NULL) 39 | { 40 | return NULL; 41 | } 42 | result->string = global_buffer; 43 | memset((void*) global_buffer, 0, BUFFER_SIZE); 44 | //unused - but will be usefull after realloc got fixd 45 | /* if (result->string==NULL) { 46 | free(result); 47 | return NULL; 48 | } 49 | result->memory=BUFFER_DEFAULT_SIZE;*/ 50 | result->memory = BUFFER_SIZE; 51 | result->string_length = 0; 52 | return result; 53 | } 54 | 55 | char 56 | stringBufferAdd(char value, string_buffer* buffer) 57 | { 58 | if (buffer->string_length >= buffer->memory) 59 | { 60 | //this has to be enabled after realloc works 61 | /*char* new_string = (char*) realloc((void*) buffer->string, (buffer->memory 62 | + BUFFER_DEFAULT_SIZE) * sizeof(char)); 63 | if (new_string == NULL) 64 | { 65 | free(buffer->string); 66 | buffer->string = NULL; 67 | return -1; 68 | } else { 69 | buffer->string = new_string; 70 | } 71 | buffer->memory += BUFFER_DEFAULT_SIZE;*/ 72 | //in the meantime we just drop it 73 | return 0; //EOF would be a better choice - but that breaks json decoding 74 | } 75 | buffer->string[buffer->string_length] = value; 76 | buffer->string_length += 1; 77 | return 0; 78 | } 79 | 80 | char* 81 | stringBufferToString(string_buffer* buffer) 82 | { 83 | //this is the realloc dependent function - it does not work 84 | // char* result = buffer->string; 85 | //ensure that the string ends with 0 86 | if (buffer->string_length == 0 || buffer->string[(buffer->string_length - 1)] 87 | != 0) 88 | { 89 | stringBufferAdd(0, buffer); 90 | } 91 | /* char* string = realloc(result, buffer->string_length); 92 | if (string==NULL) { 93 | free(result); 94 | } 95 | buffer->string=NULL; 96 | free(buffer); 97 | return string;*/ 98 | char* result = malloc(buffer->string_length * sizeof(char)); 99 | if (result == NULL) 100 | { 101 | return NULL; 102 | } 103 | strcpy(result, global_buffer); 104 | buffer->string = NULL; 105 | free(buffer); 106 | return result; 107 | } 108 | 109 | void 110 | stringBufferFree(string_buffer* buffer) 111 | { 112 | if (buffer == NULL) 113 | { 114 | //hmm it was null before - whatever 115 | return; 116 | } 117 | //this is not needed in this realloc free concept 118 | /* 119 | if (buffer->string!=NULL) { 120 | free(buffer->string); 121 | } 122 | */ 123 | free(buffer); 124 | } 125 | 126 | --------------------------------------------------------------------------------