It worked and now it doesn't [Planner to Agent, Twitter]

Ah, progress… I knew the move from planner would kill my “Tweeting Cat Door” project. I am, admittedly; an Imp novice and would have never gotten the project going without the help of brendandawes and Hugo. (thread here: http://forums.electricimp.com/discussion/comment/1262)

I’m lost. I was hoping to find a Twitter example in the forums after the Planner RIP date, but no luck. I openly throw myself on the mercy and criticism of the forum for help. ;(

Here is what the project did: 1) The Imp detects a reed switch when a cat opens the door flap. 2) A PHP script communicates with the open.se API to send a Tweet and update a usage graph. All the glory of the project is documented here: http://whiskeytangohotel.com/ourcatdoor

Of, course, my Imp still reads the door switch fine. My planner code used a ‘noodle’ to call a PHP script running on a Raspberry PI at my house. Below is a planner screen shot, my PHP code, and a cut down version of my Imp code.

I am looking for insight (or a flat out solution) on how to move this to the new Imp dev platform. As always, thanks for the help.

`/*
Tweeting Cat Door
Dec 2012. Rev Aug 8, 2013

*** Many Many thanks to the Electric Imp forum! ***

Read switch on PIN7 and GND
Normally Open or Normally Closed can be adjusted in swEvent function
Blue LED PIN9 to V3V is ON if the code in running
Blinks off for 0.5 secs when door switch dectected
*/

hardware.pin9.write(0); // LED ON, active LOW
local channelOutput = [ OutputPort(“Ch 1”, “string”)]; // String var that gets sent to PHP script

//Watchdog code below send Ping to Imp server every xx secs
//I added this because the Imp was not Tweeting all switch closures and
//power up/down seems to correct it. Assuming lost cost with Imp server?
function watchdog() {
imp.wakeup(300,watchdog); //xxx is seconds between pings
//server.log(“WD ping”); // this will echo “text” to debug window
}
watchdog();
//End of my watchdog code experiment

// function swEvent handles looking for action on the door switch
function swEvent() {
local d = date(); // Date info not sent to twitter. Used only for planner debug
local min = d[“min”];
local hour = d[“hour”];
local sec = d[“sec”];
local state = hardware.pin7.read();

    if (state == 1) {   // "1" Tweets when switch opens (for a NC swtich).  "0" Tweets when switch closes (for a NO swtich).
    
    // Select Random Msg
        local r = math.rand()%80;   // .rand(xx) should r== + 1.  this generate random number (min val is 0.  max val is %xxx in Decimal)

        if (r == 2) {
            channelOutput[0].set("Tweet random message #2");
        }
        
        if (r == 1) {
            channelOutput[0].set("Tweet random message #1");
        }
        
        if (r == 0) {
            channelOutput[0].set("Tweet random message #0");
        } 
         
    // end Random Msg selecton
    
    // Blink the LED Off to show a door swing detected
    hardware.pin9.write(1);  // LED OFF
    //imp.sleep(0.5)          // 500mS delay  ((this line and next line)
    //hardware.pin9.write(0);  //  LED back ON ((replaced with line below.  Suggestion from Hugo May 5, 2013)
    imp.wakeup(0.5, function() { hardware.pin9.write(0); } ); // turn LED on again after 500ms (Hugo's line)
    
    server.show("r="+r+" @ "+hour+":"+min+":"+sec);  //echo to the Imp Planner Blue BOX the generated RANDOM# and time.  Used for debug.
    server.log("r="+r);  //echo to the debug planner window the generated RANDOM# and time.  Used for debug.
    }   // end if state ==0 (or state ==1 depending on NO or NC switch type)
     
    imp.sleep(10.0);  // switch settle time of x.x seconds (long due you door swing back and forth)       
    server.log(state);  //  echo switch state (O or 1) to planner window for debug pane

} // End function swEvent

// Configure pin 9 as an open drain output with internal pull up
// Configure pin 7 as switch
//hardware.pin9.configure(DIGITAL_OUT_OD_PULLUP); //((replaced with line below. Suggestion from Hugo May5, 2013)
hardware.pin9.configure(DIGITAL_OUT); // (Hugo’s line)
hardware.pin7.configure(DIGITAL_IN_PULLUP, swEvent);

imp.configure( “TCD w-Blink”, [], channelOutput);`

PHP Script running on the RaspPI:
`<?php

$feed_id = “open.se FEEDID# here”;

$api_key = “my personal open.se API key here”;

if(isset($_POST[‘value’])) {

$data = array(“feed_id” => $feed_id, “value” => $_POST[‘value’]);
$data_string = json_encode($data);

$ch = curl_init(‘http://api.sen.se/events/?sense_key=’.$api_key);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, “POST”);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
‘Content-Type: application/json’,
'Content-Length: ’ . strlen($data_string))
);

$result = curl_exec($ch);

Print $data_string;

}
?>`

I think… maybe… you would use the twitter API and post to it from the agent code

twitter api

I have not written any code for twitter but I did fumble through doing this for TempoDB.

You need to revise your code and make use of the agent.send() and device.on() functions, in the imp and the agent code respectively. They are tied together so that data sent using agent.send() will emerge within the agent in the corresponding device.on(). In the agent you then do a http request to the pi.

Hopefully this code should give you some pointers…

`
/*
*

  • DEVICE CODE

*/

// function swEvent handles looking for action on the door switch
function swEvent() {
local d = date();
local min = d[“min”];
local hour = d[“hour”];
local sec = d[“sec”];
local state = hardware.pin7.read();

    if (state == 1) {   // "1" Tweets when switch opens (for a NC swtich).  "0" Tweets when switch closes (for a NO swtich).
    
    // Select Random Msg
        local r = math.rand()%80;   // .rand(xx) should r== + 1.  this generate random number (min val is 0.  max val is %xxx in Decimal)

        if (r == 2) {
            channelOutput[0].set("Tweet random message #2");
        }
        
        if (r == 1) {
            channelOutput[0].set("Tweet random message #1");
        }
        
        if (r == 0) {
            channelOutput[0].set("Tweet random message #0");
        } 
         
    hardware.pin9.write(1);  // LED OFF
    imp.wakeup(0.5, function() { hardware.pin9.write(0); } );
    
    server.log("r="+r);  //echo to the debug planner window the generated RANDOM# and time.  Used for debug.
    }   // end if state ==0 (or state ==1 depending on NO or NC switch type)
     
    imp.sleep(10.0);  // switch settle time of x.x seconds (long due you door swing back and forth)       
    server.log(state);  //  echo switch state (O or 1) to planner window for debug pane

    stringForAgent = "r="+r+" @ "+hour+":"+min+":"+sec;

    // this agent.send call, with the name "catFlap" will trigger the corresponding "device.on"
    // call in the agent code. We send it a simple table with our string we created.
    agent.send("catFlap", { "dateString" : stringForAgent });

} // End function swEvent

hardware.pin9.configure(DIGITAL_OUT);
hardware.pin9.write(0);
hardware.pin7.configure(DIGITAL_IN_PULLUP, swEvent);

imp.configure( “TCD w-Blink”, [], []);

/*
*

  • AGENT CODE

*/

pi_url <- “http://url/to/catdoor.php”;

// function to send a table of keys and values as POST data
function pi_post(post_table)
{
local headers = {};
local req = http.post(pi_url, headers, http.urlencode(post_table));
local resp = req.sendsync();
if(resp.statuscode == 200)
{
server.log(“pi request OK”);
return true;
}
else
{
server.log("pi POST error, status code = "+resp.statuscode);
return false;
}
}

// This is called on the agent when the device sends the “catFlap” event

device.on(“catFlap”, function(tableSentByDevice){
pi_post({“postVarName”: tableSentByDevice.stringForAgent});
});
`

opb:

Many thanks. I know that took some time and effort to do. I should be able to give it a test run this weekend and I’ll report back.

I was able to quickly make the edits opb provided. Honestly, the code is above my amateur ability so i really don’t understand exactly what is going on. That said…

I am getting:
ERROR: the index ‘r’ does not exist
ERROR: at swEvent:377

Line 377 is: stringForAgent = “r=”+r+" @ “+hour+”:"+min+":"+sec;

Does this mean ‘r’ needs to be defined in some way in the device section?
‘r’ echoes back to the log window just fine.

Thanks again.

Can you post the full code (or put it on gist.github.com)?

‘r’ is declared inside the if block, so it’s in scope for server.log("r="+r); but not beyond the following ‘}’

sorry, that’s my bad…

Hugo wrote:>>>
Can you post the full code (or put it on gist.github.com)?
<<<

The complete code as I had it originally is posted above. I just removed the 80 or so moderately amusing post to Twitter strings and replaced them with three “Tweet random message #…” to save space.

I will search for a way to try and globally define the var ‘r’. I guess that will get me to the next step?

Absolutely I will repost the working code when I get it going!

Thanks!

Add on a line by itself…
local r;

just below where you’ve set “local state”

Then where you’ve got “local r =…” later on, remove the word “local”.

What this does is declare the variable “r” outside of the “if” statement, so that it is available to other bit of code outside the “if” statement too.

All right!! Things are happening… but not all the way.

I was getting the error: [Device] ERROR: the index ‘stringForAgent’ does not exist

Being the learning machine I am (thanks to the above), I fixed this with the Device line:
local stringForAgent;

Then a new error. However, this time in Agent:
[Agent] at unknown:32
[Agent] the index ‘stringForAgent’ does not exist

where line 32 is:
pi_post({"postVarName": tableSentByDevice.stringForAgent});

Again, being the clever guy I am, I added the line below to Agent. Sadly, it did not correct the problem:
local stringForAgent;

I even tried the line in a few different parts of the Agent code. Basically ‘shotgunning’. A few web searches suggested I have an “old rev”, but I don’t think that is the issue?

Help (again), please. :slight_smile:

I misread the error you posted. Will post something useful when I look at the code properly.

Oh, ok - here we go :slight_smile:

The table you sent from the device doesn’t have a key called “stringForAgent”:

agent.send("catFlap", { "dateString" : stringForAgent });

It has a key called dateString, which you assingned stringForAgent to. You want to change line 32 of your agent code to:

pi_post({"postVarName": tableSentByDevice.dateString});

Again, my shoddy code. sorry guys!

No worries!

The forums are all about learning - part of that is doing really silly things sometimes :slight_smile:

OK. No errors!!!

The PHP is getting accessed on the PI. I know this because I get the message:
"[Agent] pi request OK" as programmed into the Agent.

If I change the PHP URL to something none existent an error # gets returned.

Bravo!

Something is not getting passed to the PHP (I think). The open.se site is not responding with a Tweet, door count update, etc. I have verified the open.se site is up with code on my RaspPI.

I’ll look some more at what is going on. I haven’t changed the PHP, so I am thinking the Imp code still needs for work. I’ll experiment with some stuff when I can get to my computer at home vs working through my VPN. Of course, any suggestions are welcome.

Here is the PHP the Imp is pointing to on my RasPI.

Thanks all, for getting me this close!

`<?php

$feed_id = “open.se FEEDID# here”;

$api_key = “my personal open.se API key here”;

if(isset($_POST[‘value’])) {

$data = array(“feed_id” => $feed_id, “value” => $_POST[‘value’]);
$data_string = json_encode($data);

$ch = curl_init(‘http://api.sen.se/events/?sense_key=’.$api_key);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, “POST”);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
‘Content-Type: application/json’,
'Content-Length: ’ . strlen($data_string))
);

$result = curl_exec($ch);

Print $data_string;

}
?>`

Well, unless you’re also using it for something else (logging?), why do you need the Pi at all? Why not just post straight to api.sen.se from the agent code?

Peter

Peter… yes, I do some logging.

OK. Every thing is back on track and our two cats are Tweeting away!
Thanks for everyone for all the help.

I took a little different approach. The only thing I understand less that PHP code is Imp code. So, I really cut down the Imp code and let the PHP script on the PI do more of the work when it is called. This basically kept me from passing string vars from the Imp to the PHP. Now the PHP contains the Tweet strings.

Hopefully, this thread can serve as some help to others that have an Imp and want to connect it to sen.se and Twitter. The Device, Agent, and PHP code is below. Some, now non-related, comments are still in the code just for my benefit.

Again… Thanks all. Absolutely would not have gotten back on line without the generous help of the forum.

Device code:
`
/*
Tweeting Cat Door
Dec 2012. Rev Aug 8, 2013

*** Many Many thanks to the Electric Imp forum! ***
Special thanks to brendandawes, Hugo, odb, beardedinventor

Read switch on PIN7 and GND
Normally Open or Normally Closed can be adjusted in swEvent function
Blue LED PIN9 to V3V is ON if the code in running
Blinks off when door switch detected
*/

hardware.pin9.write(0); // LED ON, active LOW
local channelOutput = [ OutputPort(“Ch 1”, “string”)]; // String var that gets sent to PHP script

//Watchdog code below send Ping to Imp server every xx secs
//I added this because the Imp was not Tweeting all switch closures and
//power up/down seems to correct it. Assuming lost cost with Imp server?
function watchdog() {
imp.wakeup(300,watchdog); //xxx is seconds between pings
//server.log(“WD ping”); // this will echo “text” to debug window
}

watchdog();
//End of my watchdog code experiment

local stringForAgent;

// function swEvent handles looking for action on the door switch
function swEvent() {
local d = date(); // Date info not sent to twitter. Used only for planner debug
local min = d[“min”];
local hour = d[“hour”];
local sec = d[“sec”];
local state = hardware.pin7.read();

    if (state == 1) {   // "1" Tweets when switch opens (for a NC swtich).  "0" Tweets when switch closes (for a NO swtich).
    
    // Blink the LED Off to show a door swing detected
    hardware.pin9.write(1);  // LED OFF
    //imp.sleep(0.5)          // 500mS delay  ((this line and next line)
    //hardware.pin9.write(0);  //  LED back ON ((replaced with line below.  Suggestion from Hugo May 5, 2013)
    imp.wakeup(0.5, function() { hardware.pin9.write(0); } ); // turn LED on again after 500ms (Hugo's line)
    
    server.show("Door Active at: "+hour+":"+min+":"+sec);  //echo to the Imp Planner Blue BOX the generated RANDOM# and time.  Used for debug.
    server.log("Door Active at: "+hour+":"+min+":"+sec);  //echo to the debug planner window the generated RANDOM# and time.  Used for debug.
    }   // end if state ==0 (or state ==1 depending on NO or NC switch type)
     
    imp.sleep(10.0);  // switch settle time of x.x seconds (long due you door swing back and forth)       
    server.log(state);  //  echo switch state (O or 1) to planner window for debug pane
    
    // start new code from 'opd' in Imp forum
    //stringForAgent = "r="+r+" @ "+hour+":"+min+":"+sec;

    // this agent.send call, with the name "catFlap" will trigger the corresponding "device.on"
    // call in the agent code. We send it a simple table with our string we created.
    agent.send("catFlap", { "dateString" : stringForAgent });
    // End new code from odp...

} // End function swEvent

// Configure pin 9 as an open drain output with internal pull up
// Configure pin 7 as switch
//hardware.pin9.configure(DIGITAL_OUT_OD_PULLUP); //((replaced with line below. Suggestion from Hugo May5, 2013)
hardware.pin9.configure(DIGITAL_OUT); // (Hugo’s line)
hardware.pin7.configure(DIGITAL_IN_PULLUP, swEvent);

imp.configure( “TCD w-Blink”, [], channelOutput);
`

Device code:
`
/*
*

  • AGENT CODE for @OurCatDoor tweeting cat door
  • *** Many Many thanks to the Electric Imp forum! ***
  • Special thanks to brendandawes, Hugo, odb, beardedinventor

*/

pi_url <- “…new_cat_door.php”;

// function to send a table of keys and values as POST data
function pi_post(post_table)
{
local headers = {};
local req = http.post(pi_url, headers, http.urlencode(post_table));
local resp = req.sendsync();
if(resp.statuscode == 200)
{
server.log(“new_cat_door.php request = 200/OK/Successful”);
return true;
}
else
{
server.log("new_cat_door.php request error, status code = "+resp.statuscode);
return false;
}
}

// This is called on the agent when the device sends the “catFlap” event

device.on(“catFlap”, function(tableSentByDevice){
pi_post({“postVarName”: tableSentByDevice.dateString});
//pi_post({“postVarName”: tableSentByDevice.stringForAgent});
});
`

My sloppy PHP code:
`

<?php Print date("h:m:s"); Print "
ECHO: Start of PHP..........

"; $feed_id = "....my sen.se device feedID here...."; $api_key = "....my personal sen.se API key here....."; $x = rand(0, 3);; // (0,xx) where xx is the max tweet msg value Print "Random # generated by PHP was: " . $x . "
"; if ($x == 0) { $Tweet_Msg = "Random message #1"; echo $Tweet_Msg; } elseif ($x == 1) { $Tweet_Msg = "Random message #2"; echo $Tweet_Msg; } elseif ($x == 2) { $Tweet_Msg = "Random message #3"; echo $Tweet_Msg; } else { $Tweet_Msg = "I do not understand what you are asking me to do."; echo "I do not understand what you are asking me to do."; } //end random if $data = array("feed_id" => $feed_id, "value" => $Tweet_Msg ); $data_string = json_encode($data); $ch = curl_init('http://api.sen.se/events/?sense_key='.$api_key); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json', 'Content-Length: ' . strlen($data_string)) ); $result = curl_exec($ch); //} Print "

ECHO: End of PHP..........
"; `