Thursday, November 10, 2016

Looping in Node.Red



I’ve grown pretty fond of Node-Red lately. I like to consider myself a technical person - someone that needs to know HOW things work.  But  the simplicity of the Node-Red environment and the ability to drag/drop function nodes onto a palette without having to get into the gory details of how things function is really appealing to me.

That said, most of the examples I find with Node.Red seem to treat flows as transactions, rather than long running processes or programs.  That is, it’s kind of like this:

  • Some external events happens
  • A flow is activated
  • Some  data is processed
  • Something else occurs as a result
  • The Flow ends

I may be wrong, and I’m still a newbie at this – and I don’t mean to sound critical (in fact I’m not) – but in my case I wanted something a little more complex than a simple flow - I wanted to kick off a flow and have it run (processing data) continuously.

Here’s my problem – I have an IoT device that watches for data on Twitter and when it receives something, I want to take an action (check out http://mganis.blogspot.com/2016/08/my-latest-internet-of-thing.html)  This device was going to installed in one the lobby of one of our buildings Lobbies to generate interest in IoT and it would be kinda boring if it didn’t move due to lack of twitter activity.  So, I wanted to have a set of pre-built statements (tweets) prepared, so when I put it into demo mode, these messages would be accessed in random order and sent to the device (like a simulator).  Oh, and I wanted to do it in Node.Red

So my first reaction was that I needed a loop of some kind.   But Node-Red doesn’t provide that kind of functionality natively.   So this is what I built:




Everything starts at the “Bottom” of the flow with the node labelled “start continuous loop” – it’s just an “inject node” that sends a timestamp into the flow, it could inject anything - it doesn't matter - I actually just ignore it anyway, but use it as a way to “get things started”

Now, I had thought I might want to keep track of the number of times I went through the loop, so when things start – I guess I’d call this the initialization part of the flow - I set up a counter by adding a new property called ‘i’ for a counter.  If it doesn’t exist, I create and and set it to zero.  The “Start Loop” javascript simply looks like this:


1
2
if( msg.i == undefined ) msg.i = 0;
return msg;

After that we enter the looping part of the code.  The first node encountered is the “Bump Counter” node.  Since this flow was going to run forever I figured I’d save some work, but not processing new messages before 8am and after 6pm.  Also, I figured I wouldn’t process anything on the weekends.  So to do this, “Bump Counter” looks at the date and based on the hour and time of time, it sets another property called msg.process.  A value of “1” means process a new tweet and msg.process=0 means just skip it for now (ie, don’t grab and process a new tweet).  Here’s the code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
var d = new Date();

// ONLY PROCESS BETWEEN 8AM AND 6PM ET
//

if ((d.getHours()>6) && (d.getHours()<18)) msg.process=1;

// check now - if it's a Sat or Sun - don't process


if (msg.process == 1) {
                       if (d.getDay() == 5) msg.process = 0;
                       if (d.getDay() == 6) msg.process = 0;
}

msg.i = msg.i + 1; 
return msg;


Then we enter a switch node.  This basically looks at the value of msg.process.  If it’s a ‘1’ the message gets routed one way (to go grab a tweet from the DB) or if it’s a ‘0’ it goes another (to just delay until its time to do it again) –- basically checking to see if the time is within our parameters.  It simply looks like this:

So if we’re at the right day and time, the message flows to the “Grab a Random message from DB” node.  This is a SQL node that does a simple query.  Now, a bit about this.  The idea was that I wanted to have a never-ending loop that would send a random message over twitter so that my device would act on it.  I also didn’t want to have to go into the code if I wanted to add more messages (canned messages).  So I figured I would just stuff them all into a simple database (the schema is simple: just one column) and then if the Node-Red code was simply querying the Database, I wouldn’t have to change the code.   The node simply has this SQL in it:


1
2
3
SELECT tweets2 FROM sample
ORDER BY RANDOM()
LIMIT 1


Basically this query will select all of the records from my sample database, randomly order them but only select the top 1 (thus I get a random record).  Simple, huh ?

The next node in the flow, “move text into payload” simply moves the result of the query into the msg.payload property - which is the property that most node operate on.    The flow is going to be passed to a standard Node.Red node called "Compute Sentiment" and it expects the payload to have a set of text to be analyzed.

The next node, “create Thing String” is specific to my application, and not really applicable to the point of this blog posting, which is looping in Node-Red, but for completeness, here it is:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
sent = "";
marker = "";

val  = msg.sentiment.score;
save = msg.payload;
msg.payload = {};

if (msg.sentiment.score<0) {
                              marker="n";
                           }
else { 
       marker='p';
}

for (i=0; i<Math.abs(msg.sentiment.score);i++)
{
    sent=sent+marker;
}

var regexp = new RegExp('#([^\\s]*)','g');
save = save.replace(regexp, '');

sent = sent + "," + save +"\n";
msg.payload = sent;

return msg;


The idea is to take a message, for example:  “#thumbsup this is awesome - happy spinning” and compute the sentiment (which in this case is a +7).   This code then creates a string that looks like this:

“ppppppp,this is awesome – happy spinning” 

and posts it to an MQTT channel where my device will listen and spin my thumb 7 divisions upward (and display the message on the LCD).

But the real “magic” is that the output of the “Create Thing String” node is passed to two (really three) places: 

  1. To MQTT to post it to the network
  2. To a debug node so I can see that it’s correct
  3. And also to the node “wait 10 seconds”

It’s the flow that goes to “wait 10 seconds” that will continue the loop;  the other nodes will just “do their thing then go away.  Now, after a slight delay, we flow back to “bump counter” and have in effect completed our loop.

Hindsight (always 50/50)

Of course they say “Hindsight is 50/50” – I didn’t realize I could do this same thing without the hassle of my loop.  The Inject node could have done this for me just as easy.  When you look at the node, you have some options which could have been useful:



Notice I could set the day of the week (Monday – Friday) and uncheck Saturday and Sunday, and I could have sent the message once every 10 seconds – exactly as I did in my loop.  Sigh.  My only solace in figuring this out was that when I build my code, I wasn’t planning on delaying a specific fixed amount of time, I wanted a random delay of somewhere between 30-90 seconds (I just used a delay node for testing, but really was planning to make it a javascript node with a random number – makes me feel a little better)

If you want my flow I have in a git repository here: https://github.com/mattganis/Node-Red-Loop

Footnotes 

I have to leave a footnote for a great site I found: http://rusya7.blogspot.com/2015/02/how-to-insert-code-blocks-in-blogger.html that allows me to easily create the code windows in this blog

and

A great resource for Node.Red patterns:  https://medium.com/node-red/node-red-design-patterns-893331422f42#.esgrmjs6y which also has a while/loop example (tho I didn't find it till after this code)

Tuesday, August 2, 2016

My latest (Internet of) Thing

So my Internet of Things "hand" (my Thing) seems to be getting some attention.  I was asked to "clean" it up a bit - to make it more presentable - so it could be used as a demo in our new IBM IoT HQ in Munich Germany !

So here's the latest version of the device (thanks largely to the expert model builders in our IBM Yorktown Research Lab):


I had to shrink it down a little since the maximum height I had was 5.5" (because of the glass case that will hold the demos).   In this new version, I did a few things:

  • Added an LCD screen to display the content (tweet) and the sentiment value
  • In the original I had a pulley spinning the thumb, this time it's a sprocket/chain
The basic functions are still in place (see:  http://mganis.blogspot.com/2016/04/my-arduino-thing.html)

  • There's a node.red flow in Bluemix that watches twitter for a specific hashtag
  • When we get a tweet, the node.red flow computes the sentiment (-5 to +5)
  • Convert the sentiment to a string (-3 becomes "nnn"  - a negative sentiment of 3 or +2 would be "pp")
  • The tweet and the sentiment string is published to MQTT (this is new - last time I just published the sentiment string, but since I want to display the tweet text I had to publish that as well)
At the device:
  • I Use an ESP8266 to connect to the Internet for the MQTT message
  • When we get a message, we display the text on the LCD and "spin" the thumb based on the sentiment 
One of the bigger changes I made was to move to a sprocket/chain to spin the thumb. Thanks to my good friends at ServoCity  I was able to find a great selection of gears/shafts and chains.  Great stuff.  The problem was in the previous version the pulley and rubber "O" ring I used would "slip" causing a gradual error (ie, a "thumbs up" eventually became a "thumbs sideways").  With the sprocket/chain, the movement stays consistent.

Because the LCD display needs +5v and so does the Servo, the little Arduino couldn't supply enough power.  So to power the display I used a separate power source along with an Adafruit USB breakout.  The big thing was to be sure the breakout, the arduino and the ESP8266 were all grounded together. Here's the Fritzing diagram:





The LCD is from Parallax (not the one shown).  It's a three wire implementation (+5v, Ground and Serial input)


Here's an example of the device in action.  I set the Node.red flow to watch for "Hillary" since I figured there would be positive/negative thoughts flowing around that as a topic

One disclaimer:  People have asked why I didn't use "trump" - I did the first time - and he does produce ALOT of controversy (ie, positive AND negative sentiment) - the problem is, I couldn't handle the volume - the communication between the arduino (to spin the thumb) and the network device is a serial connection - I just didn't want to get into the complexity of creating a queuing system


Thursday, April 21, 2016

My Arduino "Thing"

-->
My latest Arduino project is a sentiment visualization experiment (sort of).  Basically what I wanted to do was to watch twitter (say for a hashtag) and indicate the basic sentiment with a rotating “thumb” – a “thumbs up” means there’s a positive sentiment and a    
 A quick stop in my local art store (Michaels) turned up exactly what I was looking for:  a wooden hand, that could be “posed” any way I wanted (in my case to a “thumbs up”).
“thumb’s down” indicates a negative.


Then I attached a metal dowel to the end and mounted it (horizontally) to two boards so that it could rotate.  You can see in the picture below, I wrapped a rubber band (actually it was a large rubber “o” ring) around a servo motor around the arm.  So now when the servo spins, it will spin the arm.  One thing – initially it wasn’t working exactly as I had wanted: when the servo would spin the rubber “O” ring would “slip” and it wouldn’t rotate the arm.   So if you look close, you’ll see some double sided tape around the wrist.  That provided a sticky surface that would keep the “O” ring from slipping and made for a perfect (cheap) solution

I probably made this project a lot more complicated than I needed to.  Since this project had to get input from Twitter, I had to connect to the Internet.  I didn’t have a WiFi shield for my Arduino, but I wanted to figure out how to connect my ESP8266 to an Arduino UNO (for a “poor man’s” wireless device).   Turns out it’s pretty easy – but it made this project a bit more complicated since I had to maintain code for two devices.

The system looks like this:


Figure 1 System diagram for the "Thing"

All of the actions starts in IBM’s Cloud Service (Bluemix).  I have a Node.red flow defined there that watches Twitter for any mention of a particular string (you can watch for specific Twitter handles or hashtags, etc).   When we “see” a tweet, the node.red flow does a little processing (more on that later) and then publishes the result to mqtt (I use channel “MrG-Thing” – yes, I called this project “Thing” after the character in the Adam’s Family).  My ESP8266 is also running an MQTT client which is subscribed to the same broker and channel.  When it “sees” something published from MQTT it grabs it and then writes it out verbatim the ESP8266’s standard output (serial) connection which is connected to the Arduino.   The Arduino gets the message and moves the servo - either pointing the thumb up or down based on how positive or negative the sentiment is.

Simple right ?




Let’s take it step-by-step

Let’s start with the Node.red application. This is really where all the work happens.   First off, here’s the flow:



Figure 2 Flow for node.red application


The flow starts at the bottom with “Fetch Tweets”.  When a tweet matches my criteria (in this case I’m matching on “sanders” since I want to see the thumbs up/down on tweet related to the presidential race) I pass it onto the next node (compute sentiment).  I do have a number of debugging nodes in there so I can watch what’s happening (ie, the “show tweet received” and the “show sentiment”) but I’ll just ignore those here.

The compute sentiment simply takes the payload from the previous node (the tweet it received) and does a basic dictionary lookup on positive and negative words – it’s code that is included with basic node.red.  At the end of “compute sentiment” the data structure “msg” is modified to include “msg.sentiment” and therefore something called “msg.sentiment.score” that provides a numeric value that represents the overall “tone” or sentiment of the message.  According to the documentation the number will be between +5 (very positive) and -5 (very negative).

A quick look at the json shows:


{ "score": -1, "comparative": -0.058823529411764705, "tokens": [ "rt", "ibmfacts", "ibm", "an", "ugly", "quarter", "but", "a", "beautiful", "future", "awaits", "the", "patient", "investor", "httpstcozj9ldtkgsn", "via", "seekingalpha" ], "words": [ "awaits", "beautiful", "ugly" ], "positive": [ "beautiful" ], "negative": [ "awaits", "ugly" ] }

Figure 3 json addition from the sentiment node
So in this case, the tweet was: 

“IBM an ugly quarter, but a beautiful future awaits the patient investor”

Which produced an overall sentiment score of -1 (negative 1).

Ultimately this score needs to be sent to the Arduino which controls the servo that will rotate the arm.   So once the score is computed, the msg construct is passed to a node which is essentially a “case” statement.  If the sentiment score is greater than zero or less than zero, we want to send the score to the Arduino.  If it’s zero, meaning the tweet had no real sentiment we just want to ignore it.  This is what the node labelled “send only valid sentiments” does.   Really it’s just a way to avoid sending messages to the Arduino that are meaningless (a sentiment score of zero wouldn’t cause the arm to move positively or negatively, so why send it).

Since the Arduino is just a microcontroller, it’s somewhat limited in what it can do.  Rather than send a score of -4 to the Arduino over a serial line and having to convert that ascii representation of the characters “-“ and “4” into a numeric value, I chose a different method.  The node “create string” simply takes the score from msg.sentiment.score and builds a string of characters to represent the number.  So if the score was +2, the “create score” node would build a string of 2 “p’s” (ie, “pp”).  If the score was negative 4 it would build “nnnn”.  The code is pretty straight forward, it looks like:


 
-->
sent = "";

marker = "";



val = msg.sentiment.score;



msg.payload = {};

if (msg.sentiment.score<0) {

                             marker="n"

                           }

else {

       marker='p';

}



for (i=0; i<Math.abs(msg.sentiment.score);i++)

{

    sent=sent+marker;

}

sent = sent +"\n"

msg.payload = sent;



return msg;



So when the Arduino receives a string of “ppp” it simply moves the servo 3 units in the positive direction.  Conversely if the string received was “nn” it would move the servo 2 units in the negative direction.

Once the string is formed, it’s simply published on MQTT (in this case I use a channel name: “MrG-Thing” and we wait for the next tweet.

And that’s it from the node.red side

Looking back at the System Diagram in Figure 1, we have the Arduino and the ESP8266 driving the servo (and therefore the arm).   So let’s look at how they are wired up:



Figure 5 Wiring for ESP8266 (Huzzah) and Arduino Uno

 As I said earlier, the ESP8266 (in this case an Adafuit Huzzah) is used mainly for the wifi connection.  The code running there is pretty straight forward.  It connects to my wifi network (in this case I’m using an SSID of  “ws2” so I don’t need to expose my own SSID/password to the rest of the world).  Then we connect to an MQTT broker (the same one that the node.red node is using) and listen on channel “MrG-Thing”.  If anything is received, we simply send it to the ESP8266’s Serial (with a Serial.print) which is also exposed on the TX/RX pins.  The Arduino on the other side will do a Serial.read() and grab the data for further processing.  Here’s the code:

#define STANDALONE

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// Update these with values suitable for your network.

const char* ssid = "ws2";
const char* password = "";
const char* mqtt_server = "realtime.ngi.ibm.com";

WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[160];
int value = 0;


void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  #ifndef STANDALONE
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);
  #endif

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    #ifndef STANDALONE
      Serial.print(".");
    #endif
  }
#ifndef STANDALONE
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
#endif
}


void callback(char* topic, byte* payload, unsigned int length) {
#ifndef STANDALONE
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  Serial.println();
#endif

for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
   #ifndef STANDALONE
    Serial.print("Attempting MQTT connection...");
   #endif
    // Attempt to connect
    if (client.connect("MrG")) {
     #ifndef STANDALONE
      Serial.println("connected");
     #endif
      // Once connected, publish a msg saying we're up
      client.publish("MrG-debug", "esp8266 up!");
      // ... and resubscribe
      client.subscribe("MrG-Thing");
    } else {
      #ifndef STANDALONE
        Serial.print("failed, rc=");
        Serial.print(client.state());
        Serial.println(" try again in 5 seconds");
      #endif
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
 
  pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
  #ifndef STANDALONE
    Serial.begin(115200);
  #endif
    Serial.begin(4800);  // Software Serial to Arduino at 4800
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void loop() {

  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  
  }

Note, in the code I have a statement “#define STANDALONE”.  If that’s coded, there are lots of blocks of code not included in the compile that print out debugging information.  Since I’m using the serial connection to talk to the Arduino, the only thing expected out of that connection is representation of the sentiment (ie, “ppp” or “nn”, etc).   If you take out the define for debugging, be sure to put it back or things won’t work when you add the Arduino to the mix.

Finally the Arduino code.   Really all this code needs to do is watch the serial connection (in this case I used a Software Serial library that defines a serial connection on Arduino pins 10 and 11).   Because I do this, I can use the standard Arduino Serial monitor for debugging messages from the Arduino.   Once there is something on the serial line, the code determines how long it is (the number of p’s or n’s in the string) and changes the index into an array of angles that represent the movement of the servo.  If the string has p’s (for positive sentiment) we add the string length to the index and look up the angle for the servo’s motion.  If the string is made up of “n’s” we simply subtract and then look up the angle in the array.

And that’s really it.  Here’s the Arduino code:



/*
  The Arduino code for Thing (driving the servo)


This example is basically one-way communication:

From the ESP8266 (and wifi) to the Arduino.  There is NO
communication the other way.  Note, if we wanted to do that
we would have to use a voltage divider in the input to the
ESP8266 to bring the +5v from the arduino to +3.3v on the ESP8266

 The circuit:

 * TX on the ESP8266 is connected to pin 10 of the Arduino
 * RX on the ESP8266 is NOT connected (would be pin 11)

 */


#include <SoftwareSerial.h>


SoftwareSerial mySerial(10, 11); // RX, TX
String fromMQTT;
byte index=0;

  
  int dir;     // direction to rotate
  int rot=0;   // Where we will want to rotate too

#include <Servo.h>

Servo myservo;  // create servo object to control a servo
                // twelve servo objects can be created on most boards

int thumb[] = {0,22,44,66,88,110,132,154,176};

int pos = 0;    // variable to store the servo position
int thumb_entries = (sizeof(thumb)/sizeof(int *)) - 1; 

void setup() {

  /* This allows us to use the Serial Monitor on the Arduino
   *  to watch what's happening
   * 
   *  Just the standard Serial stuff
   */

  
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.println("Running on the Arduino");

 
  pinMode(13,OUTPUT);
 
  // set the data rate for the SoftwareSerial port
  mySerial.begin(4800);


 
   myservo.attach(2);  // attaches the servo on pin 2 to the servo object
   myservo.write(thumb[thumb_entries]);
   rot = 8; // Start at 180 degrees (thumbs UP)
 
}

void loop() { // run over and over

  char input;
  byte incoming;
  unsigned long currentMillis = millis();

  incoming = 0;
  index = 0;

  while (mySerial.available()==0) {            
 
  }
 
 
       fromMQTT= mySerial.readString();
       Serial.println(fromMQTT);
                             

        if (fromMQTT[0]=='p') {
                                dir = 1;
        }
        else {
               dir = -1;
        }
        
   rot = rot + ((fromMQTT.length() - 1) * dir );
       // note, we have to subtract 1 from the length due to CR
   if (rot > 8) {
                   rot = 8;
   }
   if (rot < 0) {
                   rot = 0;
   }

  
   myservo.write(thumb[rot]);
   Serial.print("rotation at: ");
   Serial.print(rot);
   Serial.print(" or: :");
   Serial.println(thumb[rot]);

        
    }



Nothing special in this code really.  As I said, I used Software Serial to create a RX/TX on Arduino pins 10 and 11 – that was handy for having the Serial monitor window available as thing runs.  What I did was define an array of angles for the thumb to rotate – there are 8 angles – that can be changed for a “finer tuned” rotation – but I like seeing the bigger jumps


Here’s an example of “thing” in action.  For this one, I was watching twitter for mentions of Bernie Sanders – enjoy -