Device as part of HTTPrequest and response - Arduino?

Hey everyone,

My first time posting, and I’m a bit of a newb with Squirrel, but here goes:

I’ve built a device that is connected to the web via Imp. This device is posting data to Parse every so often, and is controlled from a web app. For now I’m able to send all commands to the imp, then pass off to my arduino via TX/RX, where the device then performs its function.

What I would like is a callback that verifies the physical pin status on the Arduino and returns it to my app. Right now the flow is like this:

Web App -> User input (button) -> Command sent to Imp -> HTTPrequest return success -> App receives 200, ok.

What I want is to then track the command as it moves along to the Arduino over TX/RX, and then have the arduino send back a “200,ok” of sorts. Have the IMP device pick that up, pass to Agent HTTPrequest, and include in the response back to app. God I hope that makes sense, lol.

I have TX/RX working on all ends and the devices are all able to communicate right now… but not all the way as a request/response type format. After the IMP request, it switches over to TX/RX to communicate with Arduino…

How do you get the Agent to wait for a response from the device before sending the Response to the httpRequest?

In a nut shell, I want to poll the actual pin state on Arduino, and have that returned as part of an HTTPrequest/response.

Any help would be greatly appreciated.

AGENT CODE ------

function requestHandler(request, response) { try { // check if the user myCommand led as a query parameter if ("myCommand" in request.query) { local myVar = request.query.myCommand; device.send("myCommand", myVar); ---->WAIT FOR DEVICE RESPONSE response.header("Access-Control-Allow-Origin", "*"); response.send(200, "OK, the command was sent: "+myVar); }else if ("light" in request.query) { local myVar = request.query.light; device.send("light", myVar); ---->WAIT FOR DEVICE RESPONSE response.header("Access-Control-Allow-Origin", "*"); response.send(200,"Command Sent to Arduino: Light "+myVar); }else if ("heat" in request.query) { local myVar = request.query.heat; device.send("heat", myVar); ---->WAIT FOR DEVICE RESPONSE response.header("Access-Control-Allow-Origin", "*"); response.send(200,"Command Sent to Arduino: Heat "+myVar); }else if ("water" in request.query) { local myVar = request.query.water; device.send("water", myVar); ---->WAIT FOR DEVICE RESPONSE response.header("Access-Control-Allow-Origin", "*"); response.send(200,"Command Sent to Arduino: Water "+myVar); }else{ response.header("Access-Control-Allow-Origin", "*"); response.send(500, "Invalid Command Sent"); } } catch (ex) { response.send(500, "Invalid Command Sent: " + ex); } }

How do you get the Agent to wait for a response from the device before sending the Response to the httpRequest?

Store response in a global variable.
Send the command to the device.
The device does what it needs to.
The device signals the agent.
The agent used the preserved response to signal the app.

The app-agent connection will stay open until you call response.send(), so don’t send to straight away.

See ‘The Interative imp’

I think I kind of understand… I’m just not sure I know how to write it.

I guess I don’t understand how the global var is paired to the request doing it that way. Modified code below… is this right?

-------AGENT CODE

`local response;
response.header(“Access-Control-Allow-Origin”, “*”);

function requestHandler(request, response) {
try {
// check if the user myCommand led as a query parameter
if (“myCommand” in request.query) {
local myVar = request.query.myCommand;
device.send(“myCommand”, myVar);
}else if (“lights” in request.query) {
local myVar = request.query.lights;
device.send(“lights”, myVar);
}else if (“heat” in request.query) {
local myVar = request.query.heat;
device.send(“heat”, myVar);
}else if (“water” in request.query) {
local myVar = request.query.water;
device.send(“water”, myVar);
}else{
response.header(“Access-Control-Allow-Origin”, “*”);
response.send(500, “Invalid Command Sent”);
}
}
catch (ex) {
response.send(500, "Invalid Command Sent: " + ex);
}
}

device.onconnect(function() {
server.log(“Alpha Imp CONNECTED to agent.”);
});

device.ondisconnect(function() {
server.log(“Alpha Imp DISCONNECTED from agent”);
});
function parsePost(transString){
// POST data to server
// Using Parse.com
local substrings = split(transString,"=");
local sensorName = substrings[0];
local sensorValue = substrings[1];
local parse = http.post(
https://api.parse.com/1/classes/Log/”,
{
“X-Parse-Application-Id”: “#################################”,
“X-Parse-REST-API-Key”: “#################################”,
“Content-Type”: “application/json”
},
http.jsonencode({
“deviceName”: sensorName,
“logValue”: sensorValue.tointeger(),
“eventType”: “serial”
})
).sendasync(
// Log server response
function( response ) {
server.log( response.body );
response.send(200, "OK, the command was sent: "+response.body); <------ LIKE THIS? *********
}
);
}
// register the HTTP handler
http.onrequest(requestHandler);
device.on(“ping”,parsePost); <---- UART Response from Arduino and device ****`

------ DEVICE CODE

`server.log(“Device Started”);
local index = 0;
local masterUART = array(100); //serial buffer
//these should be changed to non character values
local startbit = 124; //which is |
local endbit = 126; //which is ~
arduino <- hardware.uart12;
//Port 5=TX , Port 7=RX
//remember: Arduino TX = Imp RX

// Write to Arduino UART
function writeCommand(command){
server.log("Sending Command to Arduino: " + command);
arduino.write(command);
}
function lightCommand(command){
server.log("Sending Command to Arduino: Light " + command);
arduino.write(command);
}
function heatCommand(command){
server.log("Sending Command to Arduino: Heat " + command);
arduino.write(command);
}
function waterCommand(command){
server.log("Sending Command to Arduino: Water " + command);
arduino.write(command);
}
// register a handler for “myCommand” messages from the agent
agent.on(“myCommand”,writeCommand);
agent.on(“lights”,lightCommand);
agent.on(“heat”,heatCommand);
agent.on(“water”,waterCommand);

function checkUART()
{
//imp.wakeup(0.001, checkUART.bindenv(this));
imp.wakeup(0.3, checkUART);

local byte = arduino.read();    //read uart 
while (byte != -1)  
{
    if(byte == endbit){     //check if endbit
        local startint = masterUART.find(startbit);     //find startbit
        if(startint != null){   
            local fullTrans = masterUART.slice((startint + 1),index);   //slice transmission from checkbits
            local transString = "";
            for(local a=0;a<fullTrans.len();a++){   //convert char array to string
                // server.log(fullTrans[a]);
                transString = transString + format("%c",fullTrans[a]); 
            }
            server.log(transString);    //send string to agent
            agent.send("ping",transString); <-------- SEND RESPONSE TO AGENT ****
            fullTrans.clear();
            masterUART.clear();
            index = 0;
        }else server.log("UART ERROR");
    }else{  //if not endbit, add byte to array
        masterUART.insert(index,byte);
        index ++;
    }
    byte = hardware.uart12.read();         
}

}
arduino.configure(115200, 8, PARITY_NONE, 1, NO_CTSRTS, checkUART);
checkUART(); //begin uart polling`

I gave it a whirl… and for the most part its working… just getting this error:

ERROR: Attempt to send same httpresponse twice. Not sending.

Do I have to destroy the loval var or something after?

`// Log the URLs we need
server.log("Turn ATX Power On: " + http.agenturl() + “?myCommand=Jeff”);
local myResponse;
function requestHandler(request, response) {
try {
// check if the user myCommand led as a query parameter
if (“myCommand” in request.query) {
local myVar = request.query.myCommand;
myResponse = response;
device.send(“myCommand”, myVar);
}else if (“lights” in request.query) {
local myVar = request.query.lights;
myResponse = response;
device.send(“lights”, myVar);
}else if (“heat” in request.query) {
local myVar = request.query.heat;
myResponse = response;
device.send(“heat”, myVar);
}else if (“water” in request.query) {
local myVar = request.query.water;
myResponse = response;
device.send(“water”, myVar);
}else{
response.send(500, “Invalid Command Sent”);
}
}
catch (ex) {
response.send(500, "Invalid Command Sent: " + ex);
}
}

device.onconnect(function() {
server.log(“Alpha Imp CONNECTED to agent.”);
});

device.ondisconnect(function() {
server.log(“Alpha Imp DISCONNECTED from agent”);
});
function parsePost(transString){
// POST data to server
// Using Parse.com
local substrings = split(transString,"=");
local sensorName = substrings[0];
local sensorValue = substrings[1];
local parse = http.post(
https://api.parse.com/1/classes/Log/”,
{
“X-Parse-Application-Id”: “###############”,
“X-Parse-REST-API-Key”: “###############”,
“Content-Type”: “application/json”
},
http.jsonencode({
“deviceName”: sensorName,
“logValue”: sensorValue.tointeger(),
“eventType”: “serial”
})
).sendasync(
// Log server response
function( response ) {
server.log( response.body );
myResponse.header(“Access-Control-Allow-Origin”, “*”);
myResponse.send(200, "OK, the command was sent: "+response.body);
}
);
}
// register the HTTP handler
http.onrequest(requestHandler);
device.on(“ping”,parsePost);`

You’re only storing a single response object, which could be an issue if you get multiple commands at once (i.e. - you get a second command before the first command finishes).

Take a look at this old post which describes a good way of handling this (basically, you keep an array of response objects, and pass an ID back and forth to tell which one you should be responding to):

https://discourse.electricimp.com/discussion/1650/web-agent-device-agent-web-communication/p1

Ahhh… that makes perfect sense. I got it working after all. My arduino was sending out rouge TX/RX, invoking one call too many. It’s working like a charm now. :slight_smile: Thanks so much for the help, I’ll be sure to keep the response array idea in mind as this project grows.

~ learn something new everyday

Huzzah!

lol, thanks again for the help. I’m having a hard time getting my responses to send back correctly. looks like that response array thing you mentioned…

do you have an example?

`// Log the URLs we need
server.log("Turn ATX Power On: " + http.agenturl() + “?myCommand=Jeff”);
local reqCount = 0;
local myResponse = [5];
function requestHandler(request, response) {
try {
// check if the user myCommand led as a query parameter
if (“myCommand” in request.query) {
local myVar = request.query.myCommand;
myResponse = response;
device.send(“myCommand”, myVar);
}else if (“lights” in request.query) {
local myVar = request.query.lights;
myResponse = response;
device.send(“lights”, myVar);
}else if (“heat” in request.query) {
local myVar = request.query.heat;
myResponse = response;
device.send(“heat”, myVar);
}else if (“water” in request.query) {
local myVar = request.query.water;
myResponse = response;
device.send(“water”, myVar);
}else if (“checkSensor” in request.query) {
local myVar = request.query.checkSensor;
response.header(“Access-Control-Allow-Origin”, “*”);
myResponse[reqCount] = response;
server.log(reqCount);
device.send(“checkSensor”, myVar);
}else{
response.send(500, “Invalid Command Sent”);
}
}
catch (ex) {
response.send(500, "Invalid Command Sent: " + ex);
}
}

device.onconnect(function() {
server.log(“Alpha Imp CONNECTED to agent.”);
});

device.ondisconnect(function() {
server.log(“Alpha Imp DISCONNECTED from agent”);
});
function parsePost(transString){
// POST data to server
// Using Parse.com
local substrings = split(transString,"=");
local sensorName = substrings[0];
local sensorValue = substrings[1];

local parse = http.post(
https://api.parse.com/1/classes/Log/”,
{
“X-Parse-Application-Id”: “#######################”,
“X-Parse-REST-API-Key”: “#######################”,
“Content-Type”: “application/json”
},
http.jsonencode({
“deviceName”: sensorName,
“logValue”: sensorValue.tointeger(),
“eventType”: “serial”
})
).sendasync(
// Log server response
function( response ) {
server.log( "Array POS - "+myResponse[0] );
server.log( "IMP AGENT - "+response.body );
myResponse[reqCount].send(200,sensorValue);
}
);
}
// register the HTTP handler
http.onrequest(requestHandler);
device.on(“ping”,parsePost);`

Looks to me like you set myResponse to an array in line4, and then re-set it to an object in line 9.

You want something like this:

`// Global array, initially empty, for responses

myResponses <- []`

In your requestHandler() you want to add the response to the end of the array

myResponses.append(response)

When you message the device, include in the data you pass to the device the position of the response in the array - it’s at the end so it’ll be the length of the array - 1, ie.

myResponses.len() - 1

Make sure the device sends this value back so that you can pull the correct response from the array:

local response = myResponses[valueReturnedByDevice] response.send(200, "OK")

There’s a problem here, of course: you have to maintain the array as is, otherwise in-flight agent-device communications will end up with a value that no longer indicates the correct index of their response in the myResponses array, ie. if the responses is at index 5 and then index 2 is removed, the response you want is now at index 4. So it’s better to use some other ID value than the array index, but you get the idea.

Awesome, thanks again! I’ll give it a go now.

Setting the array like this:

myResponses <- []

gives me an error. Any idea why? Sorry, total newb question I’m sure.

okay, so only part I seem to be stuck on, is sending the index value of the array to the device.

... local myVar = request.query.checkSensor; response.header("Access-Control-Allow-Origin", "*"); myResponse.append(response); device.send("checkSensor", myVar);

Says I can only pass one variable along in eventListener…?

Can you share your agent.on device code?

You can only pass one variable, however you can pass a table containing multiple variables… here’s a quick example:

Agent Code:
`function test(myKey, myData) {
local packet = { key = myKey, data = myData }l
device.send(“someEvent”, packet);
}

test(“abc”, true);`

Device Code:
agent.on("someEvent", function(packet) { local packetKey = packet.key; local packetData = packet.data; });

@alphanerdsmith, you can only pass one value via device.send(), however, that valuue can be a table (or array), so you can do something like:

local dataToSend = {} dataToSend.id <- myResponses.len() dataToSend.data <- myVar device.send("myCommand", dataToSend)

At the other end, your agent.on() handler function goes:

`handler(receivedData)
{
myVar = receivedData.data
valueToReturnToAgent = receivedData.id

. . . 

}`

Note the use of the new slot operator (<-) in the first block - it signifies you’re creating a new key-value pair in the table dataToSend. Table values are indexed by key, so can be read and written in any order (which is why they can be read ‘backwards’ in the second block). receivedData is a copy of dataToSend.