diff --git a/README.md b/README.md index 2ec6f44..041b98f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # aREST [![Build Status](https://travis-ci.org/marcoschwartz/aREST.svg?branch=master)](https://travis-ci.org/marcoschwartz/aREST) -Version 1.9.11 +Version 2.0.0 ## Overview @@ -44,7 +44,7 @@ For Bluetooth Low Energy communications, the library has been tested with the Ad To use the library with Arduino boards you will need the latest version of the Arduino IDE: -- [Arduino IDE 1.6.5](http://arduino.cc/en/main/software) +- [Arduino IDE 1.6.6](http://arduino.cc/en/main/software) ### For WiFi using the ESP8266 chip @@ -101,7 +101,7 @@ To install the library, simply clone this repository in the /libraries folder of 1. Connect a LED & resistor to pin number 8 of your Arduino board 2. Open the BLE example sketch 3. Upload the sketch -4. Use the [BlueFruit LE Connect app](https://itunes.apple.com/fr/app/adafruit-bluefruit-le-connect/id830125974?mt=8) to connect to the BLE chip +4. Use the [BlueFruit LE Connect app](https://itunes.apple.com/fr/app/adafruit-bluefruit-le-connect/id830125974?mt=8) to connect to the BLE chip 5. Type `/mode/8/o /` to set the pin as an output 6. Now type `/digital/8/1 /` and the LED should turn on @@ -167,7 +167,7 @@ rest.set_status_led(led_pin); ### Lightweight mode (BETA) -There is the possibility to use a lightweight mode for aREST. This means that for commands to control the Arduino board (like digitalWrite commands), no data is returned at all. For commands that ask for data to be sent back (like asking for a variable), in this mode the library will only return the value of the data that was requested. +There is the possibility to use a lightweight mode for aREST. This means that for commands to control the Arduino board (like digitalWrite commands), no data is returned at all. For commands that ask for data to be sent back (like asking for a variable), in this mode the library will only return the value of the data that was requested. This mode was made for cases where the memory footprint of the aREST library has to be as small as possible, or with devices that can't send/receive a lot of data at the same time, like Bluetooth LE. To enable this lightweight mode, simply start your sketch with: diff --git a/aREST.h b/aREST.h index be5a6b3..68c4425 100644 --- a/aREST.h +++ b/aREST.h @@ -1,11 +1,12 @@ -/* +/* aREST Library for Arduino See the README file for more details. - - Written in 2014 by Marco Schwartz under a GPL license. - Version 1.9.10 + + Written in 2014 by Marco Schwartz under a GPL license. + Version 2.0.0 Changelog: - + + Version 2.0.0: Added beta support for MQTT communications Version 1.9.10: Added support for floats & Strings for Uno (without the CC3000 chip) Version 1.9.8: Added support for ESP8266 chip Version 1.9.7: Added support for Arduino 1.6.2 @@ -24,17 +25,17 @@ Version 1.7.1: Added possibility to change number of exposed variables & functions Version 1.7: Added compatibility with the Arduino Due & Teensy 3.x Version 1.6: Added compatibility with the Arduino Yun - + Version 1.5: Size reduction, and added compatibility with Adafruit BLE - + Version 1.4: Added authentification with API key - + Version 1.3: Added support for the Ethernet shield - + Version 1.2: Added support of Serial communications - + Version 1.1: Added variables & functions support - + Version 1.0: First working version of the library */ @@ -61,7 +62,7 @@ #elif defined(ADAFRUIT_CC3000_H) #define NUMBER_ANALOG_PINS 6 #define NUMBER_DIGITAL_PINS 14 -#define OUTPUT_BUFFER_SIZE 275 +#define OUTPUT_BUFFER_SIZE 275 #else #define NUMBER_ANALOG_PINS 6 #define NUMBER_DIGITAL_PINS 14 @@ -105,21 +106,39 @@ class aREST { public: aREST() { - + command = 'u'; pin_selected = false; - + status_led_pin = 255; state = 'u'; } +#if defined(PubSubClient_h) +aREST(PubSubClient& client) { + + command = 'u'; + pin_selected = false; + + status_led_pin = 255; + state = 'u'; + + client.setServer(mqtt_server, 1883); + +} +char* get_topic() { + return out_topic; +} + +#endif + // Set status LED void set_status_led(uint8_t pin){ - + // Set variables status_led_pin = pin; - + // Set pin as output pinMode(status_led_pin,OUTPUT); } @@ -157,54 +176,54 @@ void reset_status() { // Handle request with the Adafruit CC3000 WiFi library #ifdef ADAFRUIT_CC3000_H void handle(Adafruit_CC3000_ClientRef& client) { - + if (client.available()) { // Handle request handle_proto(client,true,0); - + // Answer sendBuffer(client,32,20); - client.stop(); + client.stop(); // Reset variables for the next command reset_status(); - } + } } // Handle request with the Arduino Yun -#elif defined(_YUN_CLIENT_H_) +#elif defined(_YUN_CLIENT_H_) void handle(YunClient& client) { - + if (client.available()) { // Handle request handle_proto(client,false,0); - + // Answer sendBuffer(client,25,10); client.stop(); - + // Reset variables for the next command reset_status(); - } + } } // Handle request with the Adafruit BLE board #elif defined(_ADAFRUIT_BLE_UART_H_) void handle(Adafruit_BLE_UART& serial) { - + if (serial.available()) { // Handle request handle_proto(serial,false,0); - + // Answer sendBuffer(serial,100,1); // Reset variables for the next command reset_status(); - } + } } // Handle request for the Arduino Ethernet shield @@ -218,10 +237,10 @@ void handle(EthernetClient& client){ // Answer sendBuffer(client,50,0); - client.stop(); - + client.stop(); + // Reset variables for the next command - reset_status(); + reset_status(); } } @@ -238,10 +257,10 @@ void handle(WiFiClient& client){ // Answer sendBuffer(client,0,0); - client.stop(); - + client.stop(); + // Reset variables for the next command - reset_status(); + reset_status(); } } @@ -258,10 +277,10 @@ void handle(WiFiClient& client){ // Answer sendBuffer(client,50,1); - client.stop(); - + client.stop(); + // Reset variables for the next command - reset_status(); + reset_status(); } } @@ -278,7 +297,7 @@ void handle(usb_serial_class& serial){ sendBuffer(serial,25,1); // Reset variables for the next command - reset_status(); + reset_status(); } } @@ -295,7 +314,7 @@ void handle(Serial_& serial){ sendBuffer(serial,25,1); // Reset variables for the next command - reset_status(); + reset_status(); } } @@ -312,7 +331,7 @@ void handle(HardwareSerial& serial){ sendBuffer(serial,25,1); // Reset variables for the next command - reset_status(); + reset_status(); } } #endif @@ -323,7 +342,7 @@ void handle(char * string) { handle_proto(string); // Reset variables for the next command - reset_status(); + reset_status(); } void handle_proto(char * string) { @@ -335,7 +354,7 @@ void handle_proto(char * string) { // Process data process(c); - + } // Send command @@ -343,12 +362,12 @@ void handle_proto(char * string) { } template -void handle_proto(T& serial, bool headers, uint8_t read_delay) +void handle_proto(T& serial, bool headers, uint8_t read_delay) { // Check if there is data available to read while (serial.available()) { - + // Get the server answer char c = serial.read(); delay(read_delay); @@ -357,13 +376,77 @@ void handle_proto(T& serial, bool headers, uint8_t read_delay) // Process data process(c); - + } // Send command - send_command(headers); + send_command(headers); +} + +#if defined(PubSubClient_h) + +// Process callback +void handle_callback(PubSubClient& client, char* topic, byte* payload, unsigned int length) { + + int i; + char mqtt_msg[100]; + // Create character buffer with ending null terminator (string) + for(i = 0; i < length; i++) { + mqtt_msg[i] = payload[i]; + } + mqtt_msg[i] = '\0'; + + String msgString = String(mqtt_msg); + + Serial.print("Received message via MQTT: "); + Serial.println(msgString); + + String modified_message = String(msgString) + " /"; + char char_message[50]; + modified_message.toCharArray(char_message, 50); + + handle(char_message); + char * answer = getBuffer(); + + String output = String(answer); + output.trim(); + + Serial.print("Sending message via MQTT: "); + Serial.println(output); + client.publish(out_topic, (char *) output.c_str()); + resetBuffer(); +} + +// Handle request on the Serial port +void loop(PubSubClient& client){ + + // Connect to cloud + if (!client.connected()) { + reconnect(client); + } + client.loop(); + } +void reconnect(PubSubClient& client) { + // Loop until we're reconnected + while (!client.connected()) { + Serial.print("Attempting MQTT connection..."); + // Attempt to connect + if (client.connect(id)) { + Serial.println("connected"); + client.subscribe(in_topic); + } else { + Serial.print("failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + // Wait 5 seconds before retrying + delay(5000); + } + } +} +#endif + void process(char c){ // Check if we are receveing useful data and process it @@ -371,40 +454,40 @@ void process(char c){ if (DEBUG_MODE) {Serial.println(answer);} - // If the command is mode, and the pin is already selected + // If the command is mode, and the pin is already selected if (command == 'm' && pin_selected && state == 'u') { - + // Get state state = answer[0]; - + } - - // If a digital command has been received, process the data accordingly + + // If a digital command has been received, process the data accordingly if (command == 'd' && pin_selected && state == 'u') { - + // If it's a read command, read from the pin and send data back if (answer[0] == 'r') {state = 'r';} - - // If not, get value we want to apply to the pin + + // If not, get value we want to apply to the pin else {value = answer.toInt(); state = 'w';} } - - // If analog command has been selected, process the data accordingly + + // If analog command has been selected, process the data accordingly if (command == 'a' && pin_selected && state == 'u') { - + // If it's a read, read from the correct pin if (answer[0] == 'r') {state = 'r';} - - // Else, write analog value + + // Else, write analog value else {value = answer.toInt(); state = 'w';} } - - // If the command is already selected, get the pin + + // If the command is already selected, get the pin if (command != 'u' && pin_selected == false) { - + // Get pin if (answer[0] == 'A') { - pin = 14 + answer[1] - '0'; + pin = 14 + answer[1] - '0'; } else { pin = answer.toInt(); @@ -416,10 +499,10 @@ void process(char c){ pin_selected = true; // Nothing more ? - if ((answer[1] != '/' && answer[2] != '/') + if ((answer[1] != '/' && answer[2] != '/') || (answer[1] == ' ' && answer[2] == '/') || (answer[2] == ' ' && answer[3] == '/')) { - + // Nothing more & digital ? if (command == 'd') { @@ -435,30 +518,30 @@ void process(char c){ // Read all analog ? if (answer[0] == 'a') {state = 'a';} - + // Save state & end there else {state = 'r';} } - } + } } - - // Digital command received ? + + // Digital command received ? if (answer.startsWith("digital")) {command = 'd';} - + // Mode command received ? if (answer.startsWith("mode")) {command = 'm';} - + // Analog command received ? if (answer.startsWith("analog")) {command = 'a';} // Variable or function request received ? if (command == 'u') { - + // Check if variable name is in int array for (uint8_t i = 0; i < variables_index; i++){ if(answer.startsWith(int_variables_names[i])) { - + // End here pin_selected = true; state = 'x'; @@ -473,7 +556,7 @@ void process(char c){ #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE) || !defined(ADAFRUIT_CC3000_H) for (uint8_t i = 0; i < float_variables_index; i++){ if(answer.startsWith(float_variables_names[i])) { - + // End here pin_selected = true; state = 'x'; @@ -489,7 +572,7 @@ void process(char c){ #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE) || !defined(ADAFRUIT_CC3000_H) for (uint8_t i = 0; i < string_variables_index; i++){ if(answer.startsWith(string_variables_names[i])) { - + // End here pin_selected = true; state = 'x'; @@ -504,7 +587,7 @@ void process(char c){ // Check if function name is in array for (uint8_t i = 0; i < functions_index; i++){ if(answer.startsWith(functions_names[i])) { - + // End here pin_selected = true; state = 'x'; @@ -551,7 +634,7 @@ void process(char c){ // if (answer.startsWith("POST")) {method = "POST";} // if (answer.startsWith("PUT")) {method = "PUT";} // if (answer.startsWith("DELETE")) {method = "DELETE";} - + // if (DEBUG_MODE && method != "") { // Serial.print("Selected method: "); // Serial.println(method); @@ -581,18 +664,18 @@ bool send_command(bool headers) { // Mode selected if (command == 'm'){ - // Send feedback to client + // Send feedback to client if (!LIGHTWEIGHT){ addToBuffer(F("{\"message\": \"Pin D")); - addToBuffer(pin); - } - + addToBuffer(pin); + } + // Input if (state == 'i'){ - - // Set pin to Input + + // Set pin to Input pinMode(pin,INPUT); - + // Send feedback to client if (!LIGHTWEIGHT){addToBuffer(F(" set to input\", "));} } @@ -600,9 +683,9 @@ bool send_command(bool headers) { // Output if (state == 'o'){ - // Set to Output + // Set to Output pinMode(pin,OUTPUT); - + // Send feedback to client if (!LIGHTWEIGHT){addToBuffer(F(" set to output\", "));} } @@ -624,16 +707,16 @@ bool send_command(bool headers) { addToBuffer(F(", ")); } } - + #if !defined(__AVR_ATmega32U4__) || !defined(ADAFRUIT_CC3000_H) if (state == 'a') { if (!LIGHTWEIGHT) {addToBuffer(F("{"));} - - for (uint8_t i = 0; i < NUMBER_DIGITAL_PINS; i++) { - + + for (uint8_t i = 0; i < NUMBER_DIGITAL_PINS; i++) { + // Read analog value value = digitalRead(i); - + // Send feedback to client if (LIGHTWEIGHT){ addToBuffer(value); @@ -645,14 +728,14 @@ bool send_command(bool headers) { addToBuffer(F("\": ")); addToBuffer(value); addToBuffer(F(", ")); - } + } } } #endif if (state == 'w') { - // Apply on the pin + // Apply on the pin digitalWrite(pin,value); // Send feedback to client @@ -669,10 +752,10 @@ bool send_command(bool headers) { // Analog selected if (command == 'a') { if (state == 'r'){ - + // Read analog value value = analogRead(pin); - + // Send feedback to client if (LIGHTWEIGHT){addToBuffer(value);} else { @@ -684,12 +767,12 @@ bool send_command(bool headers) { #if !defined(__AVR_ATmega32U4__) if (state == 'a') { if (!LIGHTWEIGHT) {addToBuffer(F("{"));} - - for (uint8_t i = 0; i < NUMBER_ANALOG_PINS; i++) { - + + for (uint8_t i = 0; i < NUMBER_ANALOG_PINS; i++) { + // Read analog value value = analogRead(i); - + // Send feedback to client if (LIGHTWEIGHT){ addToBuffer(value); @@ -701,7 +784,7 @@ bool send_command(bool headers) { addToBuffer(F("\": ")); addToBuffer(value); addToBuffer(F(", ")); - } + } } } #endif @@ -721,7 +804,7 @@ bool send_command(bool headers) { } // Variable selected - if (command == 'v') { + if (command == 'v') { // Send feedback to client if (LIGHTWEIGHT){addToBuffer(*int_variables[value]);} @@ -730,13 +813,13 @@ bool send_command(bool headers) { addToBuffer(int_variables_names[value]); addToBuffer(F("\": ")); addToBuffer(*int_variables[value]); - addToBuffer(F(", ")); + addToBuffer(F(", ")); } } // Float ariable selected (Mega only) #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE) || !defined(ADAFRUIT_CC3000_H) - if (command == 'l') { + if (command == 'l') { // Send feedback to client if (LIGHTWEIGHT){addToBuffer(*float_variables[value]);} @@ -745,14 +828,14 @@ bool send_command(bool headers) { addToBuffer(float_variables_names[value]); addToBuffer(F("\": ")); addToBuffer(*float_variables[value]); - addToBuffer(F(", ")); + addToBuffer(F(", ")); } } #endif // String variable selected (Mega & ESP8266 only) #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE) || !defined(ADAFRUIT_CC3000_H) - if (command == 's') { + if (command == 's') { // Send feedback to client if (LIGHTWEIGHT){addToBuffer(*string_variables[value]);} @@ -761,7 +844,7 @@ bool send_command(bool headers) { addToBuffer(string_variables_names[value]); addToBuffer(F("\": \"")); addToBuffer(*string_variables[value]); - addToBuffer(F("\", ")); + addToBuffer(F("\", ")); } } #endif @@ -783,7 +866,7 @@ bool send_command(bool headers) { } } - if (command == 'r') { + if (command == 'r' || command == 'u') { root_answer(); } @@ -801,7 +884,7 @@ bool send_command(bool headers) { else { - if (command != 'r') { + if (command != 'r' && command != 'u') { addToBuffer(F("\"id\": \"")); addToBuffer(id); addToBuffer(F("\", \"name\": \"")); @@ -814,7 +897,7 @@ bool send_command(bool headers) { Serial.print(F("State of buffer at the end: ")); Serial.println(buffer); } - + // End here return true; } @@ -822,17 +905,19 @@ bool send_command(bool headers) { virtual void root_answer() { #if defined(ADAFRUIT_CC3000_H) || defined(ESP8266) || defined(ethernet_h) || defined(WiFi_h) - addToBuffer(F("HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: POST, GET, PUT, OPTIONS\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n")); + if (command != 'u') { + addToBuffer(F("HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: POST, GET, PUT, OPTIONS\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n")); + } #endif if (LIGHTWEIGHT) {addToBuffer(id);} else { - + // Start addToBuffer(F("{\"variables\": {")); #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE) || !defined(ADAFRUIT_CC3000_H) - + // Int variables if (variables_index == 0 && string_variables_index == 0 && float_variables_index == 0){ addToBuffer(F(" }, ")); @@ -859,7 +944,7 @@ virtual void root_answer() { addToBuffer(*string_variables[i]); addToBuffer(F("\", ")); } - + } if (float_variables_index > 0){ @@ -870,7 +955,7 @@ virtual void root_answer() { addToBuffer(*float_variables[i]); addToBuffer(F(", ")); } - + } removeLastBufferChar(); removeLastBufferChar(); @@ -880,7 +965,7 @@ virtual void root_answer() { #else // Int variables if (variables_index > 0){ - + for (uint8_t i = 0; i < variables_index-1; i++){ addToBuffer(F("\"")); addToBuffer(int_variables_names[i]); @@ -888,7 +973,7 @@ virtual void root_answer() { addToBuffer(*int_variables[i]); addToBuffer(F(", ")); } - + // End addToBuffer(F("\"")); addToBuffer(int_variables_names[variables_index-1]); @@ -898,9 +983,9 @@ virtual void root_answer() { } else { addToBuffer(F(" }, ")); - } + } #endif - + } // End @@ -952,11 +1037,24 @@ void function(char * function_name, int (*f)(String)){ void set_id(char *device_id){ strcpy(id,device_id); + + #if defined(PubSubClient_h) + char bufferone[5]; + strcpy(bufferone, "_in"); + strcpy(in_topic, id); + strcat(in_topic, bufferone); + + char bufferbis[5]; + strcpy(bufferbis, "_out"); + strcpy(out_topic, id); + strcat(out_topic, bufferbis); + #endif + } // Set device name void set_name(char *device_name){ - + strcpy(name, device_name); } @@ -986,9 +1084,9 @@ void addToBuffer(char * toAdd){ Serial.print(F("Added to buffer: ")); Serial.println(toAdd); } - + for (int i = 0; i < strlen(toAdd); i++){ - buffer[index+i] = toAdd[i]; + buffer[index+i] = toAdd[i]; } index = index + strlen(toAdd); } @@ -1001,9 +1099,9 @@ void addToBuffer(String toAdd){ Serial.print(F("Added to buffer: ")); Serial.println(toAdd); } - + for (int i = 0; i < toAdd.length(); i++){ - buffer[index+i] = toAdd[i]; + buffer[index+i] = toAdd[i]; } index = index + toAdd.length(); } @@ -1014,7 +1112,7 @@ void addToBuffer(uint16_t toAdd){ char number[10]; itoa(toAdd,number,10); - + addToBuffer(number); } @@ -1023,7 +1121,7 @@ void addToBuffer(int toAdd){ char number[10]; itoa(toAdd,number,10); - + addToBuffer(number); } @@ -1033,7 +1131,7 @@ void addToBuffer(float toAdd){ char number[10]; dtostrf(toAdd, 5, 2, number); - + addToBuffer(number); } #endif @@ -1065,7 +1163,7 @@ void sendBuffer(T& client, uint8_t chunkSize, uint8_t wait_time) { if (DEBUG_MODE) { Serial.print(F("Buffer size: ")); Serial.println(index); - } + } // Send all of it if (chunkSize == 0) { @@ -1074,7 +1172,7 @@ void sendBuffer(T& client, uint8_t chunkSize, uint8_t wait_time) { // Send chunk by chunk else { - + // Max iteration uint8_t max_iteration = (int)(index/chunkSize) + 1; @@ -1100,7 +1198,7 @@ void sendBuffer(T& client, uint8_t chunkSize, uint8_t wait_time) { } } } - + // Reset the buffer resetBuffer(); } @@ -1113,7 +1211,6 @@ void resetBuffer(){ memset(&buffer[0], 0, sizeof(buffer)); } - private: String answer; char command; @@ -1122,8 +1219,6 @@ void resetBuffer(){ uint16_t value; boolean pin_selected; - //char * method; - char name[NAME_SIZE]; char id[ID_SIZE]; String arguments; @@ -1140,6 +1235,16 @@ void resetBuffer(){ int * int_variables[NUMBER_VARIABLES]; char * int_variables_names[NUMBER_VARIABLES]; + // MQTT client + #if defined(PubSubClient_h) + + char in_topic[50]; + char out_topic[50]; + + const char* mqtt_server = "192.168.0.101"; + //const char* mqtt_server = "45.55.79.41"; + #endif + // Float variables arrays (Mega & ESP8266 only) #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE) || !defined(ADAFRUIT_CC3000_H) uint8_t float_variables_index; @@ -1161,4 +1266,4 @@ void resetBuffer(){ }; -#endif \ No newline at end of file +#endif diff --git a/library.properties b/library.properties index afef172..b26217d 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=aREST -version=1.9.11 +version=2.0.0 author=Marco Schwartz maintainer=Marco Schwartz sentence=RESTful API for the Arduino platform. paragraph=A simple library that implements a REST API for Arduino. It is designed to be universal and currently supports REST calls via HTTP, Serial & BLE. See more at: http://arest.io/ category=Communication url=https://github.com/marcoschwartz/aREST -architectures=* \ No newline at end of file +architectures=*