Electric Imp: Faster device.isconnected() function

0

The usage

The device.isconnected() function returns a true or false to tell if the device/impee is connected.

The problem

If it is important you know if the impee is online and ready to receive commands, or is ready to send something right now, the build in function might give you problems. The problem is that the build in function first returns false when the server tried to contact the impee and it has timed out. This can take multiple minutes, but with a simple function you can make your own which is a lot faster.

Some code

Agent

devicePing <- 0;
device.on("ping", function(msg)
{
  devicePing = time();
});

function deviceOnline()
{
  if (time() - devicePing >= 5) return false;
  return true;
}

Device

function ping()
{
  agent.send("ping", "");
  imp.wakeup(1, ping);
}
ping();

How to use

This code will make the impee send a “ping” message to the agent once every second. The agent will then save the time it received the message on.

To check if the device is online you now call deviceOnline(); instead of device.isconnected(); and it will return true or false, depending on if the agent received the ping message within the last 5 seconds or not. This way the device will only be offline for up to 5 seconds before the function will return false. The 5 seconds can most likely be set to something lower too, but it is plenty for what I use it for.

Using Openweathermap with Electric Imp

0

I have not yet found a use for this, but in case anyone else could use it, or maybe I could use it myself later, I am going to post the function here..

OPENWEATHERMAP_ZONE <- "Kolding,DK";
function openWeatherMap()
{
  local url = "http://api.openweathermap.org/data/2.5/weather?q=" + OPENWEATHERMAP_ZONE + "&mode=json&units=metric";
  http.get(url).sendasync(function(resp)
  {
    local data = http.jsondecode(resp.body);
    
    if ("message" in data)
    {
      server.log("OpenWeatherMap returned: " + resp.body);
    }
    else
    {
      server.log("main.temp: " + data.main.temp);
      server.log("main.temp_min: " + data.main.temp_min);
      server.log("main.temp_max: " + data.main.temp_max);
      server.log("main.pressure: " + data.main.pressure);
      server.log("main.humidity: " + data.main.humidity);
      
      server.log("wind.speed: " + data.wind.speed);
      server.log("wind.deg: " + data.wind.deg);
      server.log("wind.var_beg: " + data.wind.var_beg);
      server.log("wind.var_end: " + data.wind.var_end);
      
      server.log("weather.main: " + data.weather[0].main);
      server.log("weather.description: " + data.weather[0].description);
      server.log("weather.icon: " + data.weather[0].icon);
      server.log("weather.id: " + data.weather[0].id);
      
      server.log("data.name: " + data.name);
      server.log("data.dt: " + data.dt);
      
      server.log("clouds.all: " + data.clouds.all);
      
      server.log("sys.country: " + data.sys.country);
      server.log("sys.sunrise: " + data.sys.sunrise);
      server.log("sys.sunset: " + data.sys.sunset);
    }
  });
}
openWeatherMap();

This should return something like

2014-04-18 15:23:15 UTC+2: [Agent] main.temp: 10
2014-04-18 15:23:15 UTC+2: [Agent] main.temp_min: 10
2014-04-18 15:23:15 UTC+2: [Agent] main.temp_max: 10
2014-04-18 15:23:15 UTC+2: [Agent] main.pressure: 1011
2014-04-18 15:23:15 UTC+2: [Agent] main.humidity: 61
2014-04-18 15:23:15 UTC+2: [Agent] wind.speed: 4.1
2014-04-18 15:23:15 UTC+2: [Agent] wind.deg: 360
2014-04-18 15:23:15 UTC+2: [Agent] wind.var_beg: 310
2014-04-18 15:23:15 UTC+2: [Agent] wind.var_end: 60
2014-04-18 15:23:15 UTC+2: [Agent] weather.main: Clouds
2014-04-18 15:23:15 UTC+2: [Agent] weather.description: broken clouds
2014-04-18 15:23:15 UTC+2: [Agent] weather.icon: 04d
2014-04-18 15:23:15 UTC+2: [Agent] weather.id: 803
2014-04-18 15:23:15 UTC+2: [Agent] data.name: Kolding
2014-04-18 15:23:15 UTC+2: [Agent] data.dt: 1397821800
2014-04-18 15:23:15 UTC+2: [Agent] clouds.all: 75
2014-04-18 15:23:15 UTC+2: [Agent] sys.country: Denmark
2014-04-18 15:23:15 UTC+2: [Agent] sys.sunrise: 1397794132
2014-04-18 15:23:15 UTC+2: [Agent] sys.sunset: 1397846030

I hope anyone find a use for it. :)

Using TimezoneDB with Electric Imp

0

Something that can be useful to know when doing some kind of logging, error reporting, or what else it could be, is when it happened. The big problem with time is that it is not the same all over the world, it is not even an absolute offset since some regions got day time saving where the time is offset another hour. To make it even more difficult it is done at different times too… You could either take account for all that yourself, or have someone else do it for you… I am lazy, so I like have others doing things for me, so this is what I do.

Over at timezonedb.com you can create a free account, and then they will tell you what time it is at the location you would like to know more about. What we want is their API, which will return a timestamp with the unix time, and the offset to that, depending on the region you asked about.

In this example I want to use the time in Denmark, which is in Europe, and got Copenhagen as capital city, so I put in “Europe/Copenhagen”. If you are not sure what you should put in, or if you get an error, you can see the supported list here http://timezonedb.com/time-zones

TIMEZONEDB_KEY <- "xxxxxxxxxx";
TIMEZONEDB_ZONE <- "Europe/Copenhagen";
TIMEZONE_OFFSET <- 0;
function timeZoneDB()
{
  local url = "http://api.timezonedb.com/?zone=" + TIMEZONEDB_ZONE + "&format=json&key=" + TIMEZONEDB_KEY;
  http.get(url).sendasync(function(resp)
  {
    server.log(url);
    if (resp.statuscode == 200)
    {
      local data = http.jsondecode(resp.body);
        
      if ("status" in data)
      {
        if (data.status == "OK")
        {
          TIMEZONE_OFFSET = data.gmtOffset.tointeger();
          server.log("TimeZoneDB > Time offset: " + TIMEZONE_OFFSET + " seconds.")
        }
        else
        {
          server.log("TimeZoneDB returned: " + resp.body);
        }
      }
    }
  });
}
timeZoneDB();

This code will contact the timezonedb api, and set TIMEZONE_OFFSET to the offset from the timestamp/unix time.

After having used this function, you can as an example use this code to show the time for the zone you asked timezonedb about.

local t = date(time() + TIMEZONE_OFFSET, 'l');
server.log(format("%02d:%02d:%02d", t.hour, t.min, t.sec));

This will hopefully make things a lot easier for you.

OLED + Electric Imp = <3

0

After reading this post http://www.smovs.dk/blog/2014/03/03/electric-imp-driving-a-small-oled-display/ I decided to try those displays myself too, and oh my do I like them!

To control the display you only need 3 wires. Power, GND and TX, so on the Imp it only takes up one pin.

My first try to just make it show something, was pretty much the same as on the blog post mentioned before. All it does is to tell the ssid it is connected to, and the signal strength.

2014-04-09 21.35.05-1

While making the bar graph I also learnt more about how to make white and black textures, so it is possible to first draw something, then put black over it to turn parts of it off again. In the bar I use this, because the bar is actually two rectangles, one white and one black, just behind it to make sure the bar can both go up and down again.

To make the display show this, I used this code

display <- hardware.uart12;
display.configure(9600, 8, PARITY_NONE, 1, NO_RX);

display.write("CL"); display.write(0x01); //Clear display

display.write("SF"); display.write(0); display.write(0x01); //Set font 6,10,18,51,120,123,0

display.write("CS0"); display.write(0x01); //Cursor off

display.write("SC1"); display.write(0x01); //Set graphics to white

display.write("TP"); display.write(0); display.write(0); display.write(0x01); //Move to collum 0 row 0
display.write("TT" + "SSID: " + imp.getssid()); display.write(0x00); // Write text

display.write("TP"); display.write(2); display.write(1); //Move to collum 2 row 1
display.write("TT" + "dB: "); display.write(0x00); // Write text

function progressBar(barX, barY, barHeight, barWidth, border, percentage)
{
  barSplit <- ((((barWidth-2)-(border*2))/100.0)*percentage).tointeger();

  display.write("SC1"); display.write(0x01);
  display.write("DR"); display.write(barX); display.write(barY); display.write(barX + barWidth); display.write(barY + barHeight); display.write(0x01);

  if (barSplit > 0)
  {
    display.write("FR"); display.write(barX+border+1); display.write(barY+border+1); display.write(barX+1+border+barSplit); display.write(barY+1+border + barHeight-2-(border*2)); display.write(0x01);
  }
  if (percentage < 100)
  {
    display.write("SC0"); display.write(0x01);
    display.write("FR"); display.write((barX+border+1)+(barSplit+1)); display.write(barY+1+border); display.write((barX+barWidth)-1); display.write(barY+barHeight-(border*2)); display.write(0x01);
  }
}

function updateDisplay()
{
  local wifiDB = imp.rssi() + 87;
  wifiDB = (87.0/100.0) * wifiDB;

  progressBar(49, 14, 8, 77, 1, wifiDB)

  imp.wakeup(0.1, updateDisplay);
}
updateDisplay();

This will then update the bar chart every 0.1 second.

i2c controlled amplifier

0

I have for a while wanted to have some speakers out over my terrasse, and my surround receiver got multiple zones, but what if I want different volume levels?

Timing could not have been much better, just a few days after I started looking around for small amplifiers I could mount a stepper to to control volume, or opto to simulate key presses, adafruit posted a new device… A little 20w class D amplifier, with i2c!!!

Next up was to find a way to remote control it, and as I had a few Electric Imp’s laying around, this was an obvious choice… Biggest problem with this was that there were no tutorial on how to do this, and with my totally lack of experience with i2c, I had to turn to the Electric Imp forum, and a few members came to the rescue!

To control the amplifier from an Electric Imp, I can now use this code

hardware.i2c89.configure(CLOCK_SPEED_400_KHZ);
function setVolume(_volume) {

    local address   = 0x4B <<1;
    local volume = _volume;
    if (volume > 63)
    {
        volume = 63;
    }
    else if (volume < 0)
    {
        volume = 0;
    }

    hardware.i2c89.write(address, format("%c%c", volume>>8, volume&0xff));
}

setVolume(35);

And so far the only word I have found for this, is AWESOME! :D

3D printing timelapses

1

What is it?

After having finished my MendelMax2 enough to print with it, I decided to do so, and also make so I can make something I enjoy to watch… Timelapses!

First of, what is a timelapse movie? It is basicly just a video running faster than the frames per second it was captured at. The way I do it is to just take a picture every few seconds instead of the 24fps my camera normally would.

In this first video I set my camera to take a picture every 5th second, then merged them into a movie running at 24fps.

While this works, it is still difficult to see what is actually going on because everything is moving so fast, and the hotend is never at the same place either. To make up for that, you can have the microcontroller controlling the printer, also control the camera, and that is what this post is actually about.

How do I make timelapses like this?

The Camera

To be able to control my camera, I use some software called chdk. It will run alongside the original firmware in the camera, and give it a lot of extra features, where one of the features is how to remotely control it.

On the chdk site you find the instructions on how to put it on the sd card, and how to start it afterwards.

The camera I got is a Canon PowerShot A560, but the A570 is most likely a better choice because it also got focus and exposure lock, so it can trigger faster when receiving the signal to fire.

canon-a560

To make sure it wont run out of power, I also ordered a mains power supply for it, and this is also where I really like this camera. It got a connector for a power supply and a usb b-mini connector. In the software you can adjust if the camera should power off by itself and how long screen should stay on. In some of the newer camera’s I got (not canon powershot’s) I can not connect a power supply, the usb connector isn’t a standard one, and when I connect it the camera goes into file transfer+recharge mode and can’t be used for anything else than that while the cable is connected. In the settings, or lack of, I can’t decide if it should stay on either. So even when this Canon PowerShot camera is old, it seems to be better than some newer cameras from other manufacturers within the same price range.

canonpowersupply

One last thing I needed was a usb cable to put between the controller and camera, this was also easy to find on eBay, and I even found a good quality 5 meter cable.

The connections

2014-03-09 14.07.23 (Large)

The connection on the camera isn’t something that should give any problems, as it is just a normal usb b-mini connector. The tricky part is most likely how to connect it to the controller, so here it goes.

On the picture you can see a 4 wire female pin header connector connected to my Rumba board, which is the controller for my 3D printer. From left to right the pinout is 12V-GND-PIN4-ANALOG9. To trigger the camera to take a picture only GND and a 5V trigger pin is needed, but having the option to expand functionality later is always good to have.

What is left is to cut the usb connector off the usb cable (not the one going into the camera), and solder the wires from the pin header to the usb cable. A good thing would be to check polarity and not rely on the wire colors being correct, in my cable GND was RED and 5V was BLACK.

Trigger it!

After having connected the pin header to the usb cable, and enabled the remote function in chdk (check the instructions on the chdk site), all that is left is to make the controller trigger the camera to take a picture.

The way I do it is to simply use gcode to change one of the pins, in this case PIN 5.

M42 P4 S255
G04 P50
M42 P4 S0

Or even better!

With the last update I got into Marlin, it is possible to use

M240

annd configure it to work with CHDK. It will then result in a seamless printing motion, with the camera triggering while the printer is moving.

This will set pin 4 to HIGH, which means it will be outputting 5V, it will then wait 50mS before setting it to LOW again (0V).

A thing I quickly noticed was that with my camera and the buffer in the controller, it would set the pin high about 5-8 seconds before the layer was actually done, the camera would then start to get focus and exposure lock. The printer would then finish the layer and get to the G04 P50 where it would wait and following set the pin low again. When the pin goes low the camera takes the picture, resulting in it taking a picture at the right time, even when not having manual focus and exposure lock.

2014-03-09 15.53.00 (Large)

Reading JSON from Electric Imp

0

After having some fun reading JSON with JavaScript, I also wanted to read values from my Electric Imp, but I ran into a problem which turned out to be very easy to fix, continue reading to find out what it was and how to deal with it.

JSON

First we have the JSON itself. What we are using here is nothing special and should be fairly easy to read with some not too complicated code. So, what do we want to read? We want something like this

{
	"name": "light",
	"result":"value",
	"coreInfo":
	{
		"connected": true,
		"deviceID": "deviceid"
	}
}

With this we are able to tell what name we give the result, in this case “light”, because I am just going to use the light sensor to get some values to work with. Right after name we got result where the values from the light sensor is put.

After result we got coreInfo, this is actually from SparkCore where sensor readings are returned as JSON by default, so to keep my code simple and universal, I decided to just adapt it to my Electric Imp code too. Inside coreInfo we got connected which can show if the Impee is connected or disconnected (this example is not going to use that, but should be easy to add in)(this example now uses that), and last we got deviceID where we return the device id.

Agent code

To return the JSON we want, we use the Agent for the Electric Imp.

function requestHandler(request, response)
{
  response.header("Access-Control-Allow-Origin", "*");
  
  response.send(200, "{\"name\": \"light\",\"result\": \"" + lightUpdate + "\",\"coreInfo\": {\"connected\": " + device.isconnected() + ",\"deviceID\": \"" + deviceID + "\"}}");
  return;
}
http.onrequest(requestHandler);

deviceID <- "";
device.on("setDeviceID", function(msg)
{
  deviceID = msg;
});

lightUpdate <- 0;
device.on("updateLight", function(msg)
{
  lightUpdate = msg;
});

In here we got a bit of code to receive the light values from the Impee, and also the device id.

Device code

To send the values to the Agent we only need a few lines of code

function updateLight()
{
  agent.send("updateLight", hardware.lightlevel());
  imp.wakeup(1, updateLight);
}
updateLight();

agent.send("setDeviceID", hardware.getdeviceid());

and this should do the trick.

JavaScript

We now got got the Impee sending values to the Agent, and the agent set up to give the reply we want. What we need next is a bit of JavaScript to read that reply and parse the JSON so we can put it into fields on a web page.

<html>
	<head>
		<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">

		<script type="application/javascript">
			function loadJSON()
			{
				var data_file = "https://agent.electricimp.com/xxxxxxxx-xxxxx"; //Insert link to your Agent here
				var http_request = new XMLHttpRequest();
				try
				{
					// Opera 8.0+, Firefox, Chrome, Safari
					http_request = new XMLHttpRequest();
				}
				catch (e)
				{
					// Internet Explorer Browsers
					try
					{
						http_request = new ActiveXObject("Msxml2.XMLHTTP");
					}
					catch (e)
					{
						try
						{
							http_request = new ActiveXObject("Microsoft.XMLHTTP");
						}
						catch (e)
						{
							alert("Something went wrong. :/");
							return false;
						}
					}
				}

				http_request.onreadystatechange = function()
				{
					if (http_request.readyState == 4)
					{
						if (http_request.status == 200)
						{
							var jsonObj = JSON.parse(http_request.responseText);

							if(jsonObj.coreInfo.connected)
							{
								document.getElementById("device").innerHTML =  jsonObj.coreInfo.deviceID;
								document.getElementById("function").innerHTML =  jsonObj.name;
								document.getElementById("result").innerHTML = Math.round((jsonObj.result/65535) * 100) + "%";
							}
							else
							{
								document.getElementById("device").innerHTML =  "offline";
								document.getElementById("function").innerHTML =  "&nbsp;";
								document.getElementById("result").innerHTML = "&nbsp;";
							}
						}

						setTimeout("loadJSON()", 1000);
					}
				}

				http_request.open("GET", data_file, true);
				http_request.send();
			}

			window.onload=function()
			{
				loadJSON();
			};
		</script>

		<style>
			#devices
			{
				font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;
				width:700px;
				border-collapse:collapse;
			}

			#devices td, #devices th
			{
				font-size:1.2em;
				border:1px solid #98bf21;
				padding:3px 7px 2px 7px;
			}

			#devices th
			{
				font-size:1.4em;
				text-align:left;
				padding-top:5px;
				padding-bottom:4px;
				background-color:#A7C942;
				color:#fff;
			}
		</style>

		<title>Electric IMP and JSON</title>
	</head>

	<body>
		<h1>Electric IMP and JSON</h1>

		<table id="devices" class="devices">
			<tr>
				<th width="300px">Device</th>
				<th width="200px">Function</th>
				<th>Result</th>
			</tr>
			<tr>
				<td>
					<div id="device">&nbsp;</div>
				</td>
				<td>
					<div id="function">&nbsp;</div>
				</td>
				<td>
					<div id="result">&nbsp;</div>
				</td>
			</tr>
		</table>
	</body>
</html>

And this should do just that…

However, as mentioned in the beginning of this post, I had a problem reading from the Electric Imp agent. With the exact same code I could read from the SparkCore API just fine, so I started digging a bit. The browser console I got “No ‘Access-Control-Allow-Origin’ header is present on the requested resource.”, and it turned out that I had to send an extra header when sending the reply from the agent, to be able to use it on other pages.

To fix this I simply had to set

response.header("Access-Control-Allow-Origin", "*");

Just before sending the reply from the Electric Imp Agent. A little simple problem, if you know what is wrong. :)

Warning!!!

Yes, a bit dramatic headline, but better safe than sorry… Using this code will expose your agent url to whoever access the page, so if you use it for other things you might want to add some kind of password/token system for that part, find a way to pass the return to the page without using the direct url, or simply not use this at all.

Spark core, first code…

0

As you might have noticed from a previous post, I got a Spark Core.

I haven’t done much with it yet, but what I have done is to make it able to turn pins high/low, using their web api.

TCPClient client;
byte server[]  = { xx, xx, xx, xx };

char tempStr[16];

void setup()
{
    //Register our Spark function here
    Spark.function("SWITCH", pinSwitch);
}

void loop()
{
    nodeSync();
}

int pinSwitch(String command)
{
   int state = 0;
   //find out the pin number and convert the ascii to integer
   int pinNumber = command.charAt(1) - '0';

   //Sanity check to see if the pin numbers are within limits
   if (pinNumber < 0 || pinNumber > 7)
   {
       return -1;
   }

   // find out the state of the led
   if(command.substring(3,7) == "HIGH")
   {
       state = 1;
   }
   else if(command.substring(3,6) == "LOW")
   {
       state = 0;
   }
   else
   {
       return -2;
   }

   // write to the appropriate pin
   pinMode(pinNumber, OUTPUT);
   digitalWrite(pinNumber, state);
   return 1;
}

unsigned long lastSync = 0;
void nodeSync()
{
    if ((millis() - lastSync < 3600000  && lastSync != 0) && millis() >= lastSync) return; //Only check once every hour, unless we havent checked yet, or if time since overflow of millis is less than last time we checked
    lastSync = millis();

    RGB.control(true);

    if (client.connect(server, 80))
    {
        RGB.color(255, 0, 0);
        String tsData = String(Spark.deviceID());

        client.println("GET /nodeSync.php?spark=" + tsData + " HTTP/1.1");
        client.println("Host: server.com");
        client.println("Connection: close");
        client.println();

        while (client.available()) //Read what is returned from the server
        {
            char c = client.read();
            if (millis() - lastSync > 500) break; //Read for maximum 500ms, then stop reading
        }

        client.flush();
        client.stop();
    }

    RGB.control(false);
}

This code actually does a bit more than just changing the pins… I made a file for the different nodes to synchronize with. Calling this file with the id of the node makes the server do the appropriate calls to the node again, making the pins go high/low according to the database. This happens both on initial start of the node and then again every 60 minutes (or every 3600000 millisecond).

 

To use this code for something else you will have to fill in the server ip and change “Host: server.com” to be the same as yours too.

The Spark Core landed!

4

First impression

This is the box it came in, not just a plain normal boring cardboard box, but one with text and graphics on all sides.

2014-02-01 14.57.17 (Large)

Inside we find the Spark Core itself, and a nice visible link telling where to start.

2014-02-01 14.57.55 (Large)

Seems to have everything needed to get going… The Spark Core, a breadboard, USB cable, and a sticker!

2014-02-01 14.58.48 (Large)

The first impression was fine and I looked like they were serious about what they were doing, but as soon as I wanted to get the Spark Core online, the problems started.

Getting it online

To get it connected to the network, all you do is to power it up, open the app on your phone and put the wifi info in, and it then sends it to the core.

However, when I powered mine up it went into the breathing state, and after a long wait (over a minute) the app said it found no core. Repeating the process a few times gave the same result, which is strange, because the breathing should indicate it was online.

Maybe the app just didn’t find it, but it was online? To check I went to the site and it said I had no cores, but if the mobile app failed, I could do it by USB.

So I decided to do it by USB, the guide linked the driver for the core, and said the core had to be in “listening mode”. After some searching around on the site I gave up on finding how to put it into listening mode, and instead tried google to find it. According to the result I got there, I had to hold the mode button down for about 3 seconds, until the led starts to blink blue.

This didn’t seem to do the trick either, my computer did not notice anything new connected, maybe it does not like Windows 8.1? I don’t know, can’t find it on their site.

After messing around with the USB way to do it, I gave up on that too and started to look at what else to do. To do another attempt with the mobile app I cleared the memory on the Spark Core, and it went into the blue flashing state, which means it is waiting for WiFi credentials. After opening the mobile app and putting the needed information in, it found the Spark Core and asked for a name for it, and after this it also showed up on their website.

First examples

The first thing I uploaded to my Spark Core was a classic, just a simple blink.

This just turns D0 HIGH and LOW with a 1000ms delay, and it was an absolute success!

Blinking a led gets boring pretty fast though, and this device is more meant to do things online, so lets try that.

Again I decided to use an example from their site and just did a copy/paste. The code uploaded fine, and I started working on a little PHP snipped to send the device id, access token, and the command.

After a few tries the returned errors was gone, and I seemed like I had a working function to send commands to the Spark Core, but it did not do what I expected it would do, actually it did nothing at all.

Having used a unmodified example from the site, I expected my function to be wrong, but the return from the Spark API seemed like there was nothing wrong.

Turns out, the example is wrong, the comments said

l1,HIGH or l1,LOW
l2,HIGH or l2,LOW

but what actually works is

l0,HIGH or l0,LOW
l1,HIGH or l1,LOW

Now what?

The next I am planning on doing is to make the Spark Core a part of my home automation system. The first thing I need to do is to rewrite a sketch from when I used Arduino’s and RF modules, then modify my PHP based system that is handling all the commands for the different devices I got.

Small final changes to Camille

0

My plan was to order the pcb’s this weekend, but just as I was about to order I changed my mind…

Instead I made a cutout for the Impee so it is easier to push it in to the connector and get it out again, I also moved the copper on both sides down a bit to hopefully disturb the antenna in the Impee a little less.

And for the first time yet, a few measurements!

 

imp_relay2-8

I think this is going to be the one I will order, need to sleep a night while thinking about what else I should change. ;)

Go to Top