In Internet of Customers – “Jarvis” Style Personal Agenda with Arduino, Bluetooth and Salesforce we used an Arduino to scan for Bluetooth MAC addresses, identify a guest and read aloud the tasks for that user. In this post, we will cover the approach & code used within the proof, as well as supply it for download.
Overview & Bluetooth Module Challenges
The toughest part of this proof was getting the Bluetooth detection working, as my first efforts were potentially complicated with a bad Bluetooth module. I ended up using a Sparkfun Bluetooth Mate Silver with terrific results. It did require soldering, but it ended up being a pretty straightforward process.
Once the Bluetooth was working, I was able to use some excellent Bluetooth detection sample code from Jan Stevens to listen for Bluetooth IDs. Merging this with some code from our Internet of Customers – Heroku, Arduino and Salesforce.com for Customer Service example, we are easily able to send any matches to a Node.js application to evaluate against Salesforce for guest identification.
Communication Between Systems
The screens below show console output from the Node.js application, showing any data it has received from the Arduino, as well as some response data coming back from Salesforce1 APIs. The Node.js application then uses Web Sockets to communicate to the client dashboard with our greeting UI.
Salesforce Field Setup
This is the easiest part of the proof and adding custom fields in explained here. Our Node.js will need to query fields to see if the ID sent by the Arduino device will match a user’s Bluetooth ID in Salesforce. If so, retrieve the user photo, as well as a speaking voice preference.
Greeting Dashboard
Using Web Sockets, the dashboard is able to respond to the Node.js application to update as needed with a visitor photo and name. When the Node.js application is done with the audio greeting and Tasks, it sends another update to reset the UI.
Arduino Code
This Arduino code combines a Bluetooth scan using code from Jan Stevens, combined with our code from prior proofs connecting the Arduino to Wifi.
/* Code sample based on the following resources - Bluetooth Detection http://www.fritz-hut.com/2012/08/26/bluetooth-presence-detection/ - WiFi http://arduino.cc/en/Tutorial/WiFiWebClientRepeating - HTTP PUT https://www.johnbrunswick.com/2014/01/internet-of-customers-arduino-rfid-http-put-node-js/ */ #include #include #include #include char ssid[] = "xxxxx"; // your network SSID (name) char key[] = "xxxxx"; // your network key // Keep track of web connectivity boolean lastConnected = false; boolean incomingData = false; int status = WL_IDLE_STATUS; // the Wifi radio's status int keyIndex = 0; // your network key Index number // Take care of advoiding duplicate posts unsigned long lastConnectionTime = 0; const unsigned long postingInterval = 10*1000; // Removing duplicate posts to Node.js service within certain interval unsigned long lastReadTime = 0; const unsigned long pollingInterval = 10*1000; IPAddress server(192,168,1,103); // numeric IP (no DNS) // Initialize the Ethernet client library // with the IP address and port of the server // that you want to connect to (port 80 is default for HTTP): WiFiClient client; unsigned long lastTime; unsigned long interval = 10000; 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); String btid; 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 Serial.println("------------------"); Serial.println("Starting - RFID and WiFi POC Test"); while (!Serial) { ; // wait for serial port to connect. Needed for Leonardo only } // check for the presence of the shield: if (WiFi.status() == WL_NO_SHIELD) { Serial.println("WiFi shield not present"); // don't continue: while(true); } // attempt to connect to Wifi network: while ( status != WL_CONNECTED) { Serial.print("Attempting to connect to WEP network, SSID: "); Serial.println(ssid); status = WiFi.begin(ssid, keyIndex, key); // wait 10 seconds for connection: delay(10000); } Serial.println("Connected to WiFi..."); Serial.println("------------------"); Serial.println(""); printWifiStatus(); } void loop() { // Get current millis(); unsigned long currentTime = millis(); // Check if its already time for our process if((currentTime - lastTime) > interval) { lastTime = currentTime; // Enter command mode bluetooth.print("$$"); //Wait a bit for the module to respond delay(200); // Start the inquiry scan bluetooth.println("IN,2"); delay(5000); delay(500); int h = bluetooth.available(); if(h >= 42) { // we know there is a bluetooth device Serial.println("Someone is near..."); char *id = readSerial(h); char *str; while((str = strtok_r(id, "n", &id)) != NULL) { Serial.println(str); // TODO - find better way to deal with null at end of str // variable to assign for later use as string btid = (String)str; btid = btid.substring(0, 19); while (client.available()) { char c = client.read(); Serial.write(c); } if (!client.connected() && lastConnected) { Serial.println(); Serial.println("disconnecting."); client.stop(); } if(!client.connected() && (millis() - lastConnectionTime > postingInterval)) { sendData(); } lastConnected = client.connected(); } } else if(h <= 21) { // we know there is no bluetooth device Serial.println("No device found"); } bluetooth.println("---"); } clearAll(); } void clearAll() { for(int i = 0; i < bluetooth.available(); i++) { bluetooth.read(); } } // a bit hacked... char* readSerial(int h) { char input; char buffer[100]; if (bluetooth.available() > 0) { int j = 0; for (int i = 0; i < h; i++) { // the id's start at char 21, se we copy from there input = (char)bluetooth.read(); if(i >= 21) { buffer[j] = input; buffer[j+1] = ''; j++; } } return buffer; } else { return "No Data"; } } void sendData() { Serial.println("----------------->"); Serial.println("Trying to send card data to server for card " + btid + "..."); // if you get a connection, report back via serial: // change port info as needed, this was used with a local instance via Heroku command line if (client.connect(server, 3001)) { Serial.println("Connected to server..."); String feedData = "n{"mobiledevice" : {"btid" : "" + btid + ""}}"; Serial.println("Sending: " + feedData + "..."); client.println("PUT /checkin/ HTTP/1.0"); client.println("Host: 192.168.1.103"); client.println("Content-Type: application/json"); client.println("Content-Length: " + String(feedData.length())); client.print("Connection: close"); client.println(); client.print(feedData); client.println(); Serial.println("Data has been sent..."); Serial.println("----------------->"); delay(2000); lastConnectionTime = millis(); while (client.available() && status == WL_CONNECTED) { if (incomingData == false) { Serial.println(); Serial.println("--- Incoming data Start ---"); incomingData = true; } char c = client.read(); Serial.write(c); client.flush(); client.stop(); } if (incomingData == true) { Serial.println("--- Incoming data End"); incomingData = false; } if (status == WL_CONNECTED) { Serial.println("--- WiFi connected"); } else { Serial.println("--- WiFi not connected"); } } else { // if you couldn't make a connection: Serial.println("connection failed"); Serial.println("disconnecting."); client.stop(); } // Reset Bluetooth ID btid = ""; } void printWifiStatus() { // Print the SSID of the network you're attached to: Serial.println("------------------"); Serial.println("WiFi Status..."); Serial.print("SSID: "); Serial.println(WiFi.SSID()); // Print your WiFi shield's IP address: IPAddress ip = WiFi.localIP(); Serial.print("IP Address: "); Serial.println(ip); // Print the received signal strength: long rssi = WiFi.RSSI(); Serial.print("Signal Strength (RSSI):"); Serial.print(rssi); Serial.println(" dBm"); Serial.println("------------------"); Serial.println(""); }
Node.js Application
The following is the featured method from our Node.js application that uses the Say.js package to provide the audio that speaks a user’s Tasks. We evaluate if any Tasks exist on the basis of a user account containing a Bluetooth ID that matches one that the Arduino has sent.
app.put('/checkin', function(req, res) { console.log('PUT received'); var btid = req.body.mobiledevice.btid; console.log("Last guest: " + lastguest); console.log("New guest : " + btid) // check to see if speech is already in progress if (readytospeak == 1) { if (btid != "001FF3AD6525,380104") { guests.push(btid); // lock the speaking, so we only address 1 guest at a time readytospeak = 0 console.log("Guests so far - "); //console.log(btid); guests.forEach(function(entry) { console.log("Guest: " + entry); }); var taskspeech = ''; var greetinghour = ''; var greetinghour = 'afternoon'; // could be dynamic based on time and SFDC user timezone var greetingname = ''; var speechvoice = ''; // Get user info for photo var quser = "SELECT Id, FirstName, LastName, Profile_Photo__c, Greeting_Voice__c FROM User WHERE Phone_Code__c = '" + btid + "'"; org.query(quser, oauth, function(err, resp){ if(!err && resp.records) { // get speech defaults used for audio greeting greetingname = resp.records[0].FirstName; speechvoice = resp.records[0].Greeting_Voice__c; io.sockets.emit("proximity", resp.records[0]); console.log("greetingname: " + greetingname); console.log("speechvoice: " + speechvoice); // User the MAC address from the Bluetooth var q = "SELECT Id, OwnerId, Description FROM Task WHERE OwnerId IN (SELECT Id FROM User WHERE Phone_Code__c = '" + btid + "') AND Status = 'Not Started'"; org.query(q, oauth, function(err, resp){ if(!err && resp.records) { var opentasks = resp.records; var say = require('say'), colors = require('colors'), sys = require('sys'); var plural = ''; if (opentasks.length > 1) { plural = 's'; } // no callback, fire and forget say.speak(speechvoice, "Good " + greetinghour + " " + greetingname + ". You currently have " + opentasks.length + " task" + plural + ".", function () { var taskcount = 0; opentasks.forEach(function(item) { taskcount += 1; // commas add a pause during the speech taskspeech += "Task " + taskcount + ", " + item.Description + ", "; }) // no callback, fire and forget say.speak(speechvoice, taskspeech, function () { // close the welcome photo on the client side io.sockets.emit("closephoto", true); // ready to greet another guest readytospeak = 1; }); }); } }); } }); } } else{ console.log("Speaking is already in process..."); } res.send({}); });
Download Source
Download Source Code from Github
rushabhbadani07
John would u considering, making a step by step video tutorial for this