Thursday, August 06, 2015

Internet of things - Rasberry Pi - Alamode Arduino Shield - Bluetooth Mate RN41/RN42 - SPP

re you ready to hit the airwaves and add Bluetooth to your project? With the BlueSMiRF and Bluetooth Mate line of products, you’re much closer than you think to replacing those pesky, tangled RX and TX wires with 2.4GHz wireless communication.
Bluetooth Mate Gold and Silvers
Each of these modules has a Bluetooth transceiver on it, meaning they’re capable of both sending and receiving data. They’re perfect for directly replacing a wired asynchronous serial interface. Free of wires, your devices can be up to 100 meters away (RN41 Gold) from each other. On top of those benefits, these modules are also very easy to use. There’s no messing with Bluetooth protocols or the stack, just send data over a serial interface, and it’s piped through to whatever Bluetooth module to which it’s connected.

Materials and Tools

For starters, you’ll need one of the four Bluetooth modems we’ll be covering in this tutorial: the Bluetooth Mate Silver,BlueSMiRF SilverBluetooth Mate Gold, or BlueSMiRF Gold. The modules all function in the same way, so this tutorial is applicable to all four.
Wireless communication won’t do you any good unless you have two devices that can talk to each other! These Bluetooth modems can talk to any other Bluetooth device that supports SPP. That (long) list includes other BlueSMiRFs or Bluetooth Mates, or Bluetooth modules embedded into your computer, or even your smartphone. If your computer doesn’t already have a Bluetooth module in it, you can plug a Bluetooth USB Module into an available USB slot.
We’ll also need something to talk to the Bluetooth modem on the serial end. This will usually be a microcontroller of some sort. In this tutorial we’ll be using the Alamode Arduino Shield seating on top of the Rasberry pi

SMiRF? Mate? Silver? Gold? What’s the Difference?

The “Silver” and “Gold” designations of these modules indicates whether they use an RN-42 Bluetooth module or an RN-41. The Silvers use the RN-42, and the Gold uses an RN-41. The difference between those two modules? Range and transmit power. The RN-41 is a class 1 Bluetooth module, so it can communicate at up to 100 meters, but it also transmits at a higher power (meaning shorter battery life). The RN-42 is class 2, which limits the transmit range to about 10 meters.
The difference between Mate and SMiRF all comes down to the pin-out of the six-pin header. If you flip each of the boards over, and look at the pin labels, this is what you’ll see:
Pinout of BlueSMiRF and Bluetooth Mate

Design Overview

The RN-42 and RN-41 are pin-for-pin compatible, so the schematic for each of these boards is the same. The only difference exists at the connector pin-out for the Mate and SMiRF. Click the image below to see a bigger view of the schematic (or click here to see it in PDF form).
Bluetooth Mate Silver Schematic
Key to the design are level shifting circuits between the RN-41/42’s serial pins, and the output header. The maximum operating voltage of the Roving Networks modules is 3.3V, so these enable a device operating at 5V (like an Arduino) to safely communicate with the Bluetooth modems. There is also a linear 3.3V regulator on the board, so a voltage from 3.3V to 6V can be used to supply power to the module.
The boards also include two LEDs. There’s a red “Stat” LED, and a green “Connect” LED. These can be used to determine what state the Bluetooth module is in.
Annotated Board
Finally, be aware of where the antenna is – give it some room to breathe. Don’t place it near any big chunks of metal or enclose it in a Faraday cage, and you should be just fine.

The Pinouts

Each of the four Bluetooth boards breaks out six pins. Four pins are devoted to the serial interface, and the other two are for power.
Pin LabelPin FunctionInput, Output, Power?Description
RTS-ORequest to sendOutputRTS is used for hardware flow control in some serial interfaces. This output is not critical for simple serial communication.
RX-ISerial receiveInputThis pin receives serial data from another device. It should be connected to the TX of the other device.
TX-OSerial transmitOutputThis pin sends serial data to another device. It should be connected to the RX of the other device.
VCCVoltage supplyPower InThis voltage supply signal is routed through a 3.3V regulator, then routed to the Bluetooth module. It should range from 3.3V to 6V.
CTS-IClear to sendInputCTS is another serial flow control signal. Like RTS, it's not required for most, simple serial interfaces.
GNDGroundPower InThe 0V reference voltage, common to any other device connected to the Bluetooth modem.

Powering the Modules

These Bluetooth devices are designed to work seamlessly in both 3.3V and 5V systems. The voltage supplied to the VCC/GND pins can be anywhere between 3.3V and 6V. Voltages on the input serial and control signals (RX-I and CTS-I) can be anywhere between 3.3V and 5V. The output signals (TX-O and RTS-O) will range from 0V for a LOW logic level, and VCC for a HIGH. That means if you power them at 6V, the TX and RTS signals will output up to 6V.
The current consumption of a modem depends on what it’s doing at the time. It can be as low as 0.026mA when the device is asleep, and as high as 50mA when data is being transmitted. This table from the datasheet provides some good estimates:
Current consumption characteristics

Connecting a device up to the Bluetooth modems is as easy as applying power and wiring up the serial RX and TX pins. What do we send over that serial interface, though? That’s where we need to look at the the firmware and the Bluetooth module’s operation modes.

Hardware Hookup

Assembly

Happily, most of the assembly on these modules is done for you; you don’t need to learn how to solder SMD components just yet. However, before you can begin using these Bluetooth modules, you’ll need to solder somethinginto the six plated-through-holes to form a solid electrical connection.
What you solder into the holes depends mostly on what you’re going to connect the device to. If you’ve got a Bluetooth Mate, and want to connect it directly to an Arduino Pro, you may want to throw a right-angle female header on there. Another good option, which makes the board breadboard-compatible, is male-headers. A third, ever-reliable option is to solder wires directly to the holes.

Connecting Everything Together

We need to connect the Bluetooth modems to devices that can send and receive serial signals. These are TTL-levelserial signals, make sure you don’t confuse that with RS-232! Voltages should be between 3.3V and 5V. There are loads of options here, for this tutorial we’ll use an Arduino.
Instead of connecting the Bluetooth modem to the Arduino’s lone hardware UART, we’ll use SoftwareSerial and connect the modem’s RX and TX pins to any of the Arduino’s free digital pins. This will help to avoid bus contention and will make sure the Bluetooth modem doesn’t receive any spurious data during a sketch upload. Here’s the connections we’ll make for the example code later in this tutorial:
Fritzing diagram

Note that this is a Bluetooth Mate shown in the Fritzing diagram, the BlueSMiRF will have a different pinout.

For Alamode


TX-O is connected to D2 of the Alamode (blue), RX-I is connected to D3 (white), GND goes to GND, and VCC goes to 5V. The CTS-I and RTS-O pins are left floating. The TX-O and RX-I pins could really be connected to any digital pin (besides 0 and 1), so if you need 2 and 3 for something else, feel free to move those around.

Half of the hardware hookup is done. We still need to create a wireless connection to another Bluetooth device. Before we can delve further into that, though, we need to understand more about the Bluetooth modem’s firmware.

Firmware Overview

serial interface is all it takes to control these Bluetooth modules and send data through them. They act, essentially, like a data pipeline. Serial data that goes into the module (from the RX-I pin), is passed out the Bluetooth connection. Data coming in from the Bluetooth side is passed out the serial side (out the TX-O pin).
How data passes through modem
Establishing this data pipeline is a two step process. First, we need to connect something capable of sending and receiving serial data to the header of the Bluetooth modem. We achieved this in the Hardware Hookup phase by connecting an Arduino to the serial header, but any microcontroller with a UART could work. With the device connected we need to configure the serial port to work at the same baud rate the the modem is configured to – they default to 115200 bps (8-N-1).
Secondly, on the Bluetooth end of things, we need to establish a wireless connection between the modem and another Bluetooth device. The only stipulation here is the other Bluetooth device must support SPP (which most do). This connection involves a pairing process similar to connecting any other Bluetooth devices together. More on that later. Let’s talk a bit more about the serial interface.

Data and Command Modes

Controlling the Bluetooth module and sending data through it are two very separate operations, but they’re both done via the serial interface. To differentiate between these two forms of data, the Bluetooth modules implement two different communication modes.
Command mode is used to configure the Bluetooth module. Characteristics like the device name, baud rate, PIN code, and data rate can be adjusted in command mode. This is also where action commands are sent to the module, which can tell it to connect to a device or scan for other modules.
In data mode, the Bluetooth module acts as a transparent data gateway. Any data received over the Bluetooth connection is routed out the module’s TX pin. And data sent to the module’s RX pin is piped out over the Bluetooth connection.
Data and command mode examples
To enter command mode from data mode, the host controller needs to send a string of three $ symbols ($$$).

Configuration Timer

The configuration timer is the one obstacle to watch out for when entering command mode. The config timer begins counting as soon as the Bluetooth modem is turned on, and once it’s done counting, you’ll be unable to enter config mode unless you cycle power. By default the config timer is set to 60 seconds, however this can be adjusted, or even turned off (that’s the ticket!).

Deciphering the LEDs

There are two LEDs on the Bluetooth modems: a red one labeled “Stat”, and a green one labeled “Connect”. These help to indicate the status of the module. Never forget the importance of blinkies! The green LED will illuminate when a wireless connection is formed. The “Stat” LED can indicate that the module is in one of three states, depending on how fast it blinks:
ModeStat Blink RateNotes
Configuration10 per secondModule is in config mode.
Startup/Config Timer2 per secondModule is not in config mode, but the configuration timer is still counting.
Discoverable/Inquiring/Idle1 per secondNot in config mode, and the config timer has run out.

If you’re having trouble getting the module to enter configuration mode, make sure the timer hasn’t run out by checking for a very slow blink rate.

Bridge Mode 

 In bridge mode the BT is used to foward packet received over BT to Serial port and then the data is send over debug serial port of Alamode which is connected to the Rasberry PI serial port. This means that app running on the Rasberry pi will also receive the data transiting  through the running Arduino Sketch on the ATMEL MCU. pretty Sweet!

/*
  Example Bluetooth Serial Passthrough Sketch
 by: Jim Lindblom
 SparkFun Electronics
 date: February 26, 2013
 license: Public domain

 This example sketch converts an RN-42 bluetooth module to
 communicate at 9600 bps (from 115200), and passes any serial
 data between Serial Monitor and bluetooth module.
 */
#include   

int bluetoothTx = 2;  // TX-O pin of bluetooth mate, Arduino D2
int bluetoothRx = 3;  // RX-I pin of bluetooth mate, Arduino D3

SoftwareSerial bluetooth(bluetoothTx, bluetoothRx);

void setup()
{
  Serial.begin(9600);  // Begin the serial monitor at 9600bps

  bluetooth.begin(115200);  // The Bluetooth Mate defaults to 115200bps
  bluetooth.print("$");  // Print three times individually
  bluetooth.print("$");
  bluetooth.print("$");  // Enter command mode
  delay(100);  // Short delay, wait for the Mate to send back CMD
  bluetooth.println("U,9600,N");  // Temporarily Change the baudrate to 9600, no parity
  // 115200 can be too fast at times for NewSoftSerial to relay the data reliably
  bluetooth.begin(9600);  // Start bluetooth serial at 9600
}

void loop()
{
  if(bluetooth.available())  // If the bluetooth sent any characters
  {
    // Send any characters the bluetooth prints to the serial monitor
    Serial.print((char)bluetooth.read());  
  }
  if(Serial.available())  // If stuff was typed in the serial monitor
  {
    // Send any characters the Serial monitor prints to the bluetooth
    bluetooth.print((char)Serial.read());
  }
  // and loop forever and ever!
}


Now you are good to use aRest.io with BluetoothMate !!

This sketch makes use of the SoftwareSerial library, which should be included with most of the recent versions of Arduino.
At the beginning of the sketch, the Arduino enters the command mode string and temporarily changes the Bluetooth modem’s baud rate to 9600 bps (using the U,9600,N command). Remember this is temporary, so when power is cycled, the modem will default back to 115200 bps.
The loop of the sketch simply checks to see if either the Bluetooth modem or the Serial Monitor have sent any data to the Arduino. If so, it’ll relay the data sent from one device to the other.

Using the Passthrough Sketch

With the code uploaded, and everything hooked up accordingly, open up the Serial Monitor. Make sure the baud rate is set to 9600. Throughout this process you’ll have to fudge around with the dropdown menu to the left of the baud rate selection. It should initially be set to “No line ending”.
First, let’s enter command mode by typing $$$, and click “Send”. You should see the Bluetooth modem respond with CMD, and you’ll notice the red Stat LED blinking much faster, this all indicates that the device is in command mode.
Once you’re in command mode, you’ll need to change the line ending dropdown to “Newline”. The basis of all this is that the RN-42 module expects a newline character after every command except for the command mode string. Annoying, but we’ll deal.

Using GET Commands

The GET commands are a good place to start using command mode, they’ll display settings, status, or other information that might be helpful. Try sending the “Display Basic Settings” command by typing “D”, and pressing “Send”. This will trigger a response from the Bluetooth modem that details, among other things, the baud rate settings, the name, and the address (BTA) of the device. The address is something you should take note of, it can either be identified from this command, or by taking a gander at the module’s label, next to the “MAC NO”. Each Bluetooth moule has a unique address which can’t be changed. Try sending the other get commands, and see what information you can retrieve from the modem.
Displaying Settings from Command Mode

Using SET Commands

After sending the “D” command, you may have noticed your Bluetooth modem has it’s own name, in addition to the address. Unlike the address, this name can be changed to whatever you’d like. By default it’ll be RN42-XXXX, where XXXX is the last four digits of the address. Let’s give a SET command a whirl. The SN, command is used to set the name, where is any collection of up to 20 characters. Think up a unique name, and assign it to your device. After sending the SN command, the modem should respond with an “AOK”. Now if you send the D command, you should see your new name listed next to the “BTName” setting.
Using the SET name command
Be careful with the SET commands, only change something if you’re sure it won’t negatively affect the modem, or your ability to communicate with it. If you change something you don’t think you should have, send the SF, 1 command to reset everything back to its factory default value. Another handy command, if you’re lazy like me, is ST,0, which turns the config timer off. Remember that any setting you modify will be saved to the Bluetooth modem’s memory, and will be retained upon loss of power.

ACTION Commands

Finally, it’s time for some action. Among other things the Bluetooth modem’s ACTION commands can be used to find other Bluetooth devices, connect to them, and disconnect from them.
Begin by sending the inquiry scan command – I, – to search for other Bluetooth modules in range. The parameter defines the number of seconds the modem will take to look for other modules. It’ll default to 10 if not defined. If you just type “I” and click send, the device should respond with a “Inquiry, COD=0”, and then after ten seconds it’ll respond with any Bluetooth modules it found. It will print their information as “BT address, BT name, COD” (COD is class of device).
Connecting via command mode
If the modem finds any modules, you can try sending the connect command – C,
 – to connect to one of them. The modem in the example above found two devices in range, by sending the C,000666421B01 command, we can attempt to connect to one of them.
After sending the connect command, the device will respond with “TRYING”, which will be followed by either “CONNECT failed” (the meaning of which should be pretty apparent) or the connection will be successful! After a successful connection we immediately enter data mode, and the modem becomes a pipeline. Any characters sent from one Bluetooth device will be sent to the other, and vice-versa. To disconnect, you’ll need to re-enter command mode (don’t forget to set to “No new line”), and send the “K,” command (with Newline selected, bleh).

There are a lot of other commands to explore! Thumb through the User’s Manual and familiarize yourself with all of the power at your Bluetooth modems’s fingertips!

Connecting From Another Device

In the example code section we attempted to connect to a device from the Bluetooth modem, but what if you wanted to initiate the connection from another Bluetooth device? This process varies by operating system and device, but most of the steps involved are pretty similar.
If your device (computer, phone, etc.) doesn’t already have an Bluetooth modem, you’ll need to connect an external module to it. The Bluetooth USB Module works for any computer with an available USB slot.

Connecting to the Modem in Windows

Go to the Control Panel and navigate to the Devices and Printers window. In the top-left section of that window, there should be an Add a device button. Click that.
Add a device window
When the Add a device window opens your computer’s Bluetooth module should automatically search for any in-range, available Bluetooth devices. Those it finds should show up in the window (give the window a few seconds to search).
If you see your device in this window, double-click it to initiate a connection. You’ll then be presented with the Select a pairing option window. Since the modems don’t have an attached keypad, select the Enter the device’s pairing code option.
Pairing options menu
On the next window, enter 1234 as the PIN code. This is the default PIN value for every RN-42 and RN-41.
Entering the PIN code
Windows will take a few moments to install drivers for your device. Once it’s done, it’ll pop up a notification to let you know that your device is ready to use!
But how do you actually use it? You’ll need to open up a terminal emulator (check out our Serial Terminal Basics tutorial for help!). When Windows installed drivers for your new Bluetooth device, it created a new COM port for it. Opening up your device manager, and looking in the “Ports (COM & LPT)” tree, you’ll find a new port named “Standard Serial over Bluetooth link (COM##)” (there may be two of them).
Device manager
To open up a connection between the Bluetooth devices, open up a terminal to that COM port at 9600 bps (8-N-1). (If you see two ports, try the lower number first). When the terminal opens up, your Bluetooth modem’s green connect LED should light up. Connection successful!
If you have the sketch from the last example (the serial passthrough) still loaded up on your Arduino, you can open up a second terminal window to communicate between devices.
Bluetooth communication example
If you’re within the config timer window (cycle power on the modem if you’re not), you can even remotely enter command mode by sending the “$$$” string. Now you can remotely alter the settings of your Bluetooth modem. Nifty!

If your using a Mac, Linux, or even a smartphone, pairing and connecting should involve a similar process. If authentication is required, you’ll want to use the PIN-code option, and enter the default PIN of “1234”. Open up a serial terminal emulator – Terminal or CoolTerm on Mac OSX, a variety of apps are available for smartphones – to initiate the connection and start passing data.

aRest API Mode 




The aREST framework was created to give RESTful interface to several embedded boards & platforms. In a nutshell, the library allows you to send commands to a given board running aREST, provoke an action (or just get some data), and send data back in a JSON container.

For example, to set the state of pin 6 to HIGH on an Arduino board running aREST, connected to your local network via Ethernet, and with the IP address 192.168.1.101, you would send the command:

192.168.1.101/digital/6/1
It’s that simple. As an answer, the board will then send:

{“message”: “Pin D6 set to 1?, “id”: “1”, “name”: “arduino”, “connected”: true}
This makes aREST really easy to use for your connected project. You don’t have to change the code on your embedded boards anymore: set it once, and then interact with your boards using a RESTful interface.

let's modify aRest to support SoftwareSerial

edit aREST.h and merge with following version
/* 
  aREST Library for Arduino
  See the README file for more details.
 
  Written in 2014 by Marco Schwartz under a GPL license. 
  Version 1.9.8
  Changelog:
  
  Version 1.9.8: Added support for ESP8266 chip
  Version 1.9.7: Added support for Arduino 1.6.2
  Version 1.9.6: Added support for float variables for Arduino Mega
  Version 1.9.5: Added compatibility with Arduino IDE 1.5.8
  Version 1.9.4: Bug fixes & added support for configuring analog pints as digital outputs
  Version 1.9.3: Added description of available variables for the /id and / routes
  Version 1.9.2: Added compatibility with the Arduino WiFi library
  Version 1.9.1: Added compatibility with CORS
  Version 1.9: New speedup of the library (answers 2x faster in HTTP compared to version 1.8)
  Version 1.8: Speedup of the library (answers 2.5x faster with the CC3000 WiFi chip)
  Version 1.7.5: Reduced memory footprint of the library
  Version 1.7.4: Added a function to read all analog & digital inputs at once
  Version 1.7.3: Added LIGHTWEIGHT mode to only send limited data back
  Version 1.7.2: Added possibility to assign a status pin connected to a LED
  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
*/

#ifndef aRest_h
#define aRest_h

// Include Arduino header
#include "Arduino.h"


#include 

// Using ESP8266 ?
#if defined(ESP8266)
#include "stdlib_noniso.h"
#endif

// Which board?
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(CORE_WILDFIRE) || defined(ESP8266)
#define NUMBER_ANALOG_PINS 16
#define NUMBER_DIGITAL_PINS 54
#define OUTPUT_BUFFER_SIZE 2000
#elif defined(__AVR_ATmega328P__) && !defined(ADAFRUIT_CC3000_H)
#define NUMBER_ANALOG_PINS 6
#define NUMBER_DIGITAL_PINS 14
#define OUTPUT_BUFFER_SIZE 350
#elif defined(ADAFRUIT_CC3000_H)
#define NUMBER_ANALOG_PINS 6
#define NUMBER_DIGITAL_PINS 14
#define OUTPUT_BUFFER_SIZE 275  
#else
#define NUMBER_ANALOG_PINS 6
#define NUMBER_DIGITAL_PINS 14
#define OUTPUT_BUFFER_SIZE 350
#endif

// Size of name & ID
#define NAME_SIZE 20
#define ID_SIZE 10

// Debug mode
#ifndef DEBUG_MODE
#define DEBUG_MODE 0
#endif

// Use light answer mode
#ifndef LIGHTWEIGHT
#define LIGHTWEIGHT 0
#endif

// Default number of max. exposed variables
#ifndef NUMBER_VARIABLES
  #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(CORE_WILDFIRE) || defined(ESP8266)
  #define NUMBER_VARIABLES 10
  #else
  #define NUMBER_VARIABLES 5
  #endif
#endif

// Default number of max. exposed functions
#ifndef NUMBER_FUNCTIONS
  #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(CORE_WILDFIRE) || defined(ESP8266)
  #define NUMBER_FUNCTIONS 10
  #else
  #define NUMBER_FUNCTIONS 5
  #endif
#endif

class aREST {

public:

aREST() {
  
  command = 'u';
  pin_selected = false;
 
  status_led_pin = 255;
  state = 'u';

}

// 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);
}

// Glow status LED
void glow_led() {

  if(status_led_pin != 255){
    unsigned long i = millis();
    int j = i % 4096;
    if (j > 2048) { j = 4096 - j;}
      analogWrite(status_led_pin,j/8);
    }
}

// Send HTTP headers for Ethernet & WiFi
void send_http_headers(){

  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"));
}

// Reset variables after a request
void reset_status() {
  answer = "";
  command = 'u';
  pin_selected = false;
  state = 'u';
  arguments = "";

  index = 0;
  //memset(&buffer[0], 0, sizeof(buffer));

}

// 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();  

    // Reset variables for the next command
    reset_status();
  } 
}

// Handle request with the Arduino Yun
#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
#elif defined(ethernet_h)
void handle(EthernetClient& client){

  if (client.available()) {

    // Handle request
    handle_proto(client,true,0);

    // Answer
    sendBuffer(client,50,0);
    client.stop();  
   
    // Reset variables for the next command
    reset_status();   
  }
}

// Handle request for the ESP8266 chip
#elif defined(ESP8266)
void handle(WiFiClient& client){

  if (client.available()) {

    if (DEBUG_MODE) {Serial.println("Request received");}

    // Handle request
    handle_proto(client,true,0);

    // Answer
    sendBuffer(client,0,0);
    client.stop();  
   
    // Reset variables for the next command
    reset_status();   
  }
}

// Handle request for the Arduino WiFi shield
#elif defined(WiFi_h)
void handle(WiFiClient& client){

  if (client.available()) {

    if (DEBUG_MODE) {Serial.println("Request received");}

    // Handle request
    handle_proto(client,true,0);

    // Answer
    sendBuffer(client,50,1);
    client.stop();  
   
    // Reset variables for the next command
    reset_status();   
  }
}

#elif defined(CORE_TEENSY)
// Handle request on the Serial port
void handle(usb_serial_class& serial){

  if (serial.available()) {

    // Handle request
    handle_proto(serial,false,1);

    // Answer
    sendBuffer(serial,25,1);

    // Reset variables for the next command
    reset_status();     
  }
}

#elif defined(__AVR_ATmega32U4__)
// Handle request on the Serial port
void handle(Serial_& serial){

  if (serial.available()) {

    // Handle request
    handle_proto(serial,false,1);

    // Answer
    sendBuffer(serial,25,1);

    // Reset variables for the next command
    reset_status();     
  }
}

#elif defined(__RN41__)

// Handle request on the SoftwareSerial port
void handle(SoftwareSerial & client){

  if (client.available()) {

    // Handle request
    handle_proto(client,false,1);

    // Answer
    sendBuffer(client,25,1);

    // Reset variables for the next command
    reset_status();     
  }
}
#else
// Handle request on the Serial port
void handle(HardwareSerial& serial){

  if (serial.available()) {

    // Handle request
    handle_proto(serial,false,1);

    // Answer
    sendBuffer(serial,25,1);

    // Reset variables for the next command
    reset_status();     
  }
}
#endif

void handle(char * string) {

  // Process String
  handle_proto(string);

  // Reset variables for the next command
  reset_status();     
}

void handle_proto(char * string) {
  // Check if there is data available to read
  for (int i = 0; i < strlen(string); i++){

    char c = string[i];
    answer = answer + c;

    // Process data
    process(c);
    
  }

  // Send command
  send_command(false);
}

template <typename T>
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);
    answer = answer + c;
    //if (DEBUG_MODE) {Serial.print(c);}

    // Process data
    process(c);
    
   }

   // Send command
   send_command(headers);  
}

void process(char c){

  // Check if we are receveing useful data and process it
  if ((c == '/' || c == '\r') && state == 'u') {

      if (DEBUG_MODE) {Serial.println(answer);}

      // 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 (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        
       else {value = answer.toInt(); state = 'w';}
     }
     
     // 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 {value = answer.toInt(); state = 'w';}
     }
     
     // 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';  
       }
       else {
         pin = answer.toInt();
       }
       if (DEBUG_MODE) {
        Serial.print("Selected pin: ");
        Serial.println(pin);
       }
       pin_selected = true;

       // Nothing more ?
       if ((answer[1] != '/' && answer[2] != '/') 
        || (answer[1] == ' ' && answer[2] == '/')
        || (answer[2] == ' ' && answer[3] == '/')) {
     
        // Nothing more & digital ?
        if (command == 'd') {

          // Read all digital ?
          if (answer[0] == 'a') {state = 'a';}

          // Save state & end there
          else {state = 'r';}
        }

       // Nothing more & analog ?
       if (command == 'a') {

         // Read all analog ?
         if (answer[0] == 'a') {state = 'a';}
        
         // Save state & end there
         else {state = 'r';}
       }
     }  

   }
     
     // 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';

           // Set state
           command = 'v';
           value = i;
         }
       }

       // Check if variable name is in float array (Mega & ESP8266 only)
       #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE)
       for (uint8_t i = 0; i < float_variables_index; i++){
         if(answer.startsWith(float_variables_names[i])) {
           
           // End here
           pin_selected = true;
           state = 'x';

           // Set state
           command = 'l';
           value = i;
         }
       }
       #endif

       // Check if variable name is in float array (Mega & ESP8266 only)
       #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE)
       for (uint8_t i = 0; i < string_variables_index; i++){
         if(answer.startsWith(string_variables_names[i])) {
           
           // End here
           pin_selected = true;
           state = 'x';

           // Set state
           command = 's';
           value = i;
         }
       }
       #endif

       // 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';

           // Set state
           command = 'f';
           value = i;

           // Get command
           arguments = "";
           uint8_t header_length = strlen(functions_names[i]);
           if (answer.substring(header_length, header_length + 1) == "?") {
             uint8_t footer_start = answer.length();
             if (answer.endsWith(" HTTP/"))
               footer_start -= 6; // length of " HTTP/"
             arguments = answer.substring(header_length + 8, footer_start);
           }
         }
       }

       // If the command is "id", return device id, name and status
       if ( (answer[0] == 'i' && answer[1] == 'd') ){

           // Set state
           command = 'i';

           // End here
           pin_selected = true;
           state = 'x';
       }

       if (answer[0] == ' '){

           // Set state
           command = 'r';

           // End here
           pin_selected = true;
           state = 'x';
       }

       // Check the type of HTTP request
       // if (answer.startsWith("GET")) {method = "GET";}
       // 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);
       // }

     }

     answer = "";
    }
}

bool send_command(bool headers) {

   if (DEBUG_MODE) {
     Serial.println(F("Sending command"));
     Serial.print(F("Command: "));
     Serial.println(command);
     Serial.print(F("State: "));
     Serial.println(state);
     Serial.print(F("State of buffer at the start: "));
     Serial.println(buffer);
   }

   // Start of message
   if (headers && command != 'r') {send_http_headers();}

   // Mode selected
   if (command == 'm'){

     // Send feedback to client 
     if (!LIGHTWEIGHT){
       addToBuffer(F("{\"message\": \"Pin D"));
       addToBuffer(pin); 
     } 
     
     // Input
     if (state == 'i'){
      
      // Set pin to Input     
      pinMode(pin,INPUT);
          
      // Send feedback to client
      if (!LIGHTWEIGHT){addToBuffer(F(" set to input\", "));}
     }

     // Output
     if (state == 'o'){

       // Set to Output  
       pinMode(pin,OUTPUT);
          
       // Send feedback to client
       if (!LIGHTWEIGHT){addToBuffer(F(" set to output\", "));}
     }

   }

   // Digital selected
   if (command == 'd') {
     if (state == 'r'){

       // Read from pin
       value = digitalRead(pin);

       // Send answer
       if (LIGHTWEIGHT){addToBuffer(value);}
       else {
        addToBuffer(F("{\"return_value\": "));
        addToBuffer(value);
        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++) {       
         
         // Read analog value
         value = digitalRead(i);
      
         // Send feedback to client
         if (LIGHTWEIGHT){
           addToBuffer(value);
           addToBuffer(F(","));
         }
         else {
           addToBuffer(F("\"D"));
           addToBuffer(i);
           addToBuffer(F("\": "));
           addToBuffer(value);
           addToBuffer(F(", "));
         } 
     }
    }
    #endif

     if (state == 'w') {

       // Apply on the pin      
       digitalWrite(pin,value);

       // Send feedback to client
       if (!LIGHTWEIGHT){
        addToBuffer(F("{\"message\": \"Pin D"));
        addToBuffer(pin);
        addToBuffer(F(" set to "));
        addToBuffer(value);
        addToBuffer(F("\", "));
       }
     }
   }

   // Analog selected
   if (command == 'a') {
     if (state == 'r'){
       
       // Read analog value
       value = analogRead(pin);
      
       // Send feedback to client
       if (LIGHTWEIGHT){addToBuffer(value);}
       else {
        addToBuffer(F("{\"return_value\": "));
        addToBuffer(value);
        addToBuffer(F(", "));
       }
     }
     #if !defined(__AVR_ATmega32U4__)
     if (state == 'a') {
       if (!LIGHTWEIGHT) {addToBuffer(F("{"));}
       
       for (uint8_t i = 0; i < NUMBER_ANALOG_PINS; i++) {       
         
         // Read analog value
         value = analogRead(i);
      
         // Send feedback to client
         if (LIGHTWEIGHT){
           addToBuffer(value);
           addToBuffer(F(","));
         }
         else {
           addToBuffer(F("\"A"));
           addToBuffer(i);
           addToBuffer(F("\": "));
           addToBuffer(value);
           addToBuffer(F(", "));
         } 
     }
   }
   #endif
   if (state == 'w') {

     // Write output value
     analogWrite(pin,value);

     // Send feedback to client
     addToBuffer(F("{\"message\": \"Pin D"));
     addToBuffer(pin);
     addToBuffer(F(" set to "));
     addToBuffer(value);
     addToBuffer(F("\", "));

   }
  }

  // Variable selected
  if (command == 'v') {          

       // Send feedback to client
       if (LIGHTWEIGHT){addToBuffer(*int_variables[value]);}
       else {
        addToBuffer(F("{\""));
        addToBuffer(int_variables_names[value]);
        addToBuffer(F("\": "));
        addToBuffer(*int_variables[value]);
        addToBuffer(F(", ")); 
       }
  }

  // Float ariable selected (Mega only)
  #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE)
  if (command == 'l') {          

       // Send feedback to client
       if (LIGHTWEIGHT){addToBuffer(*float_variables[value]);}
       else {
        addToBuffer(F("{\""));
        addToBuffer(float_variables_names[value]);
        addToBuffer(F("\": "));
        addToBuffer(*float_variables[value]);
        addToBuffer(F(", ")); 
       }
  }
  #endif

  // String variable selected (Mega & ESP8266 only)
  #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE)
  if (command == 's') {          

       // Send feedback to client
       if (LIGHTWEIGHT){addToBuffer(*string_variables[value]);}
       else {
        addToBuffer(F("{\""));
        addToBuffer(string_variables_names[value]);
        addToBuffer(F("\": \""));
        addToBuffer(*string_variables[value]);
        addToBuffer(F("\", ")); 
       }
  }
  #endif

  // Function selected
  if (command == 'f') {

    // Execute function
    uint8_t result = functions[value](arguments);

    // Send feedback to client
    if (!LIGHTWEIGHT) {
     addToBuffer(F("{\"return_value\": "));
     addToBuffer(result);
     addToBuffer(F(", "));
     //addToBuffer(F(", \"message\": \""));
     //addToBuffer(functions_names[value]);
     //addToBuffer(F(" executed\", "));
    }
  }

  if (command == 'r') {
    root_answer();
  }

  if (command == 'i') {
    if (LIGHTWEIGHT) {addToBuffer(id);}
    else {
      addToBuffer(F("{"));
    }
  }

   // End of message
   if (LIGHTWEIGHT){
     addToBuffer(F("\r\n"));
   }

   else {

     if (command != 'r') {
      addToBuffer(F("\"id\": \""));
       addToBuffer(id);
       addToBuffer(F("\", \"name\": \""));
       addToBuffer(name);
       addToBuffer(F("\", \"connected\": true}\r\n"));
     }
   }

   if (DEBUG_MODE) {
     Serial.print(F("State of buffer at the end: "));
     Serial.println(buffer);
   }
   
   // End here
   return true;
}

virtual void root_answer() {
  if (LIGHTWEIGHT) {addToBuffer(id);}
    else {
      addToBuffer(F("{\"variables\": {"));

      // Int variables
      if (variables_index > 0){
        
        for (uint8_t i = 0; i < variables_index-1; i++){
          addToBuffer(F("\""));
          addToBuffer(int_variables_names[i]);
          addToBuffer(F("\": "));
          addToBuffer(*int_variables[i]);
          addToBuffer(F(", "));
        }
        addToBuffer(F("\""));
        addToBuffer(int_variables_names[variables_index-1]);
        addToBuffer(F("\": "));
        addToBuffer(*int_variables[variables_index-1]);
        addToBuffer(F("}, "));
      }
      else {
        addToBuffer(F(" }, "));
      }
      
    }
}

void variable(char * variable_name, int *variable){

  int_variables[variables_index] = variable;
  int_variables_names[variables_index] = variable_name;
  variables_index++;

}

// Float variables (Mega & ESP only)
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE)
void variable(char * variable_name, float *variable){

  float_variables[float_variables_index] = variable;
  float_variables_names[float_variables_index] = variable_name;
  float_variables_index++;

}
#endif

// String variables (Mega & ESP only)
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE)
void variable(char * variable_name, String *variable){

  string_variables[string_variables_index] = variable;
  string_variables_names[string_variables_index] = variable_name;
  string_variables_index++;

}
#endif

void function(char * function_name, int (*f)(String)){

  functions_names[functions_index] = function_name;
  functions[functions_index] = f;
  functions_index++;
}

// Set device ID
void set_id(char *device_id){

  strcpy(id,device_id);
}

// Set device name
void set_name(char *device_name){
  
  strcpy(name, device_name);
}

// Set device name
void set_name(String device_name){

  device_name.toCharArray(name, NAME_SIZE);
}

// Set device ID
void set_id(String device_id){

  device_id.toCharArray(id, NAME_SIZE);
}

// Add to output buffer
void addToBuffer(char * toAdd){

  if (DEBUG_MODE) {
    Serial.print(F("Added to buffer: "));
    Serial.println(toAdd);
  }
  
  for (int i = 0; i < strlen(toAdd); i++){
    buffer[index+i] = toAdd[i];  
  }
  index = index + strlen(toAdd);
}

// Add to output buffer
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE)
void addToBuffer(String toAdd){

  if (DEBUG_MODE) {
    Serial.print(F("Added to buffer: "));
    Serial.println(toAdd);
  }
  
  for (int i = 0; i < toAdd.length(); i++){
    buffer[index+i] = toAdd[i];  
  }
  index = index + toAdd.length();
}
#endif

// Add to output buffer
void addToBuffer(uint16_t toAdd){

  char number[10];
  itoa(toAdd,number,10);
  
  addToBuffer(number);
}

// Add to output buffer
void addToBuffer(int toAdd){

  char number[10];
  itoa(toAdd,number,10);
  
  addToBuffer(number);
}

// Add to output buffer (Mega & ESP only)
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE)
void addToBuffer(float toAdd){

  char number[10];
  dtostrf(toAdd, 5, 2, number);
  
  addToBuffer(number);
}
#endif

// Add to output buffer
void addToBuffer(const __FlashStringHelper *toAdd){

  // if (DEBUG_MODE) {
  //   Serial.print(F("Added to buffer: "));
  //   Serial.println(toAdd);
  // }

  uint8_t idx = 0;

  PGM_P p = reinterpret_cast<PGM_P>(toAdd);

  while (1) {
    unsigned char c = pgm_read_byte(p++);
    if (c == 0) break;
    buffer[index + idx] = c;
    idx++;
  }
  index = index + idx;
}

template <typename T>
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) {
    client.print(buffer);
  }

  // Send chunk by chunk
  else {
    
    // Max iteration
    uint8_t max_iteration = (int)(index/chunkSize) + 1;

    // Send data
    for (uint8_t i = 0; i < max_iteration; i++) {
      char intermediate_buffer[chunkSize+1];
      memcpy(intermediate_buffer, buffer + i*chunkSize, chunkSize);
      intermediate_buffer[chunkSize] = '\0';

      // Send intermediate buffer
      #ifdef ADAFRUIT_CC3000_H
      client.fastrprint(intermediate_buffer);
      #else
      client.print(intermediate_buffer);
      #endif

      // Wait for client to get data
      delay(wait_time);

      if (DEBUG_MODE) {
        Serial.print(F("Sent buffer: "));
        Serial.println(intermediate_buffer);
      }
    }
  }
    
    // Reset the buffer
    resetBuffer();
}

char * getBuffer() {
  return buffer;
}

void resetBuffer(){
  memset(&buffer[0], 0, sizeof(buffer));
}


private:
  String answer;
  char command;
  uint8_t pin;
  char state;
  uint16_t value;
  boolean pin_selected;

  //char * method;

  char name[NAME_SIZE];
  char id[ID_SIZE];
  String arguments;

  // Output uffer
  char buffer[OUTPUT_BUFFER_SIZE];
  uint16_t index;

  // Status LED
  uint8_t status_led_pin;

  // Int variables arrays
  uint8_t variables_index;
  int * int_variables[NUMBER_VARIABLES];
  char * int_variables_names[NUMBER_VARIABLES];

  // Float variables arrays (Mega & ESP8266 only)
  #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE)
  uint8_t float_variables_index;
  float * float_variables[NUMBER_VARIABLES];
  char * float_variables_names[NUMBER_VARIABLES];
  #endif

  // String variables arrays (Mega & ESP8266 only)
  #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP8266) || defined(CORE_WILDFIRE)
  uint8_t string_variables_index;
  String * string_variables[NUMBER_VARIABLES];
  char * string_variables_names[NUMBER_VARIABLES];
  #endif

  // Functions array
  uint8_t functions_index;
  int (*functions[NUMBER_FUNCTIONS])(String);
  char * functions_names[NUMBER_FUNCTIONS];

};

#endif

/*
  Example Bluetooth Serial aRest Sketch
 by: Thomas Younsi
 date: August 7, 2015
 license: Public domain

 This example sketch converts an RN-42 bluetooth module to
 communicate at 9600 bps (from 115200), and Handle REST calls 
 send over bluetooth.
 */
#define __RN41__ 1

#include <SoftwareSerial.h> 
// Create aREST instance
aREST rest = aREST();
// Variables to be exposed to the API
int temperature;
int humidity;
int led = 13;


#include  
int bluetoothTx = 2;  // TX-O pin of bluetooth mate, Arduino D2
int bluetoothRx = 3;  // RX-I pin of bluetooth mate, Arduino D3

SoftwareSerial bluetooth(bluetoothTx, bluetoothRx);

void setup()
{
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);

  // Start Serial
#ifdef BRIDGE
  Serial.begin(9600);  // Begin the serial monitor at 9600bps
#else
  Serial.begin(115200);
#endif

  bluetooth.begin(115200);  // The Bluetooth Mate defaults to 115200bps
  bluetooth.print("$");  // Print three times individually
  bluetooth.print("$");
  bluetooth.print("$");  // Enter command mode
  delay(100);  // Short delay, wait for the Mate to send back CMD
  bluetooth.println("U,9600,N");  // Temporarily Change the baudrate to 9600, no parity
  // 115200 can be too fast at times for NewSoftSerial to relay the data reliably
  bluetooth.begin(9600);  // Start bluetooth serial at 9600

  // Init variables and expose them to REST API
  temperature = 24;
  humidity = 40;
  rest.variable("temperature",&temperature);
  rest.variable("humidity",&humidity);
  // Function to be exposed
  rest.function("led",ledControl);
  // Give name and ID to device
  rest.set_id("008");
  rest.set_name("ble_drake");
}

void loop()
{
  if(bluetooth.available())  // If the bluetooth sent any characters
  {
    // Handle REST calls send over bluetooth
    rest.handle(bluetooth);

    // Send any characters the bluetooth prints to the serial monitor
    //Serial.print((char)bluetooth.read());
  }
#ifdef BRIDGE
  if(Serial.available())  // If stuff was typed in the serial monitor
  {
    // Send any characters the Serial monitor prints to the bluetooth
    bluetooth.print((char)Serial.read());
  }
#endif
  // and loop forever and ever!
}


// Custom function accessible by the REST API
int ledControl(String command) 
{
  // Get state from command
  int state = command.toInt();

  digitalWrite(led,state);
  return 1;
}

Android Client TODO


iOS Client TODO


Resources and Going Further

Hopefully this tutorial has prepared you for an exciting foray into the world of wireless communication. Now that you have a good idea of how to command these Bluetooth modems, and connect them to other devices, the rest is up to you. How are you going to make use of your pleasant lack of wire? Go hit the airwaves!

Resources



No comments: