Introduction: ImPrinter: the Electric Imp Wireless, Internet-connected Thermal Printer

About: Developer at Electric Imp

Thermal printers have gotten (again) very popular in the last few months; lots of people seem to be quite interested in having relevant info pop up on a little reel of thermal paper instead of in phone alerts or the like. Take a look:

BERG made the Little Printer, which is sort of unbearably cute. You need a little hub to get your printer connected to the internet.

Adafruit did an ethernet printer project kit with a slick enclosure, built on arduino with an ethernet shield. Then they built a wireless one built on Raspberry Pi.

Sparkfun's done a component pack for a USB-connected thermal printer, too.

Lots of printers. And as objectively silly as it feels to channel various data away from the tailored interfaces I'm used to on my phone or computer, I found myself really wanting one as well. Why not have the weather forecast waiting on a little slip of paper by the closet door, or build an old-school stock ticker that reels off prices onto a big heap of paper tape, or have my desk spit out a list of my meetings for the day when I get in in the morning? Sure, it's technically old-school, but something about it just feels so... future.

Not that I needed much more of a push at this point, but here it does come into play that building one of these with an electric imp is easier that falling off a log. I mean solder-five-wires easy. I mean no external components. Don't cancel your weekend project in favor of this one. Do this one on saturday morning while you're having your coffee, and have it print encouraging messages for you while you're refinishing the deck or changing your oil or building an arc or what have you. You'll have time.

Step 1: ImPrinter: What You'll Need

Let's have a look at what you'll need to build the imPrinter. This one's a short list.
  1. The printer (obviously). You probably need paper and a power supply, too. You'll need a 5V, 2A wall wart to run your printer, and Adafruit's got that packed up with paper and a printer and cables that conveniently already match the sockets on the printer. You even get a nice barrel jack adapter, so you don't need to lop the end off your power supply and solder it to the imp breakout board. $61.95 for this, or you can get the pieces separately for a bit more
  2. The imp. If you're already putting in an Adafruit order, you can add this to it.Sparkfun's got them too. $29.95.
  3. The imp breakout board. This essential bit provides the imp with it's 3.3V supply (the breakout runs off of 6 to 17 volts DC), gives you access to the imp's pins, and has nice 0.1" pitch holes, which fits perfectly into a standard breadboard, if you're into that sort of thing. Adafruit and Sparkfun both have them. $12.50
That was a quick shopping trip. You'll need some tools, too - make sure you've got:
  1. Wire cutters
  2. Wire strippers (probably you've got one tool for 1 and 2, and that's just fine)
  3. A soldering iron and some solder
  4. Perhaps a bit of foam tape, if you want to pack it all up nicely.
  5. A computer to program the imp with (any OS is fine; you just need a browser other than IE)
  6. A smartphone, android or iOS, to use blinkUp to configure your imp.
Alright, let's build this thing before your coffee gets cold.

Step 2: ImPrinter: Wire It Up

We've got parts, we've got tools, let's wire up the printer. The printer needs two things:
  1. Power - minimum 2A at 5V
  2. Signal - 3.3V 19200-baud serial, which the imp is happy to send and receive
This adds up to a total of five solder connections for the printer. You'll need to make two more connections, though, because the imp breakout board needs power, too. What we'll actually do is connect the power (via the barrel jack adapter) to your electric imp breakout board, then connect Vin and Ground on the breakout board to Vin and Ground on the printer. Then we'll hook up the serial interface. Let's start with the power:
  1. Cut two short (1" to 3") pieces of wire and strip both ends.
    1. If you don't have any wire, you can steal some from the printer cables you got with the printer, because we're going to cut one of the connector ends off. Read through and you'll see where you can take it from.
  2. Solder the wires to the power pads on your electric imp breakout board. Red to P+, Black to P-. If your wires are very short, you may want to use tweezers or small pliers to hold them down when soldering, as they'll conduct heat pretty well.
    1. Tape your breakout board to something solid
    2. Heat the desired power pad with the soldering iron
    3. Add a bit of solder to the pad (not to the iron) to make a nice little solder bead
    4. remove the solder and iron from the pad
    5. Hold the stripped end of the wire you want to solder down onto the solder bead
    6. Heat the wire and the pad (just heating the solder will make a cold joint, which will break!) with the soldering iron until the solder flows over both
    7. Remove the iron, holding the wire in place for a moment to let the solder cool
  3. Connect the wires to the barrel jack adapter.
    1. The black wire should go to the terminal that is connected to the outside of the barrel jack. This is ground.
    2. The red wire should go to the center pin of the barrel jack. This is +5V.
    3. You can use a multimeter to test. I recommend doing so.
    4. Don't sweat getting it backwards. The electric imp breakout board has reverse-voltage protection on the P+ and P- pads (but not on the Vin pad, so be careful if you're ever using that one!)
  4. Set the jumper on your electric imp breakout board to connect "BAT" and the center pin of the three-pin header. If your board doesn't have a header populated, you can use a little piece of wire to connect the center pad of the three power select pads with the pad marked "BAT"
You can test out your imp at this point if you want; if you plug in the wall wart, connect it to the barrel jack adapter, and put an imp into the socket, it should power on (and if you've previously configured this imp for your network, it will even connect). If it doesn't, check that you're actually sending 5V to P+, ground is connected to P-, and that you've got the power select jumper set correctly. Don't worry about configuring and programming the imp now, we'll get to that a bit later.

Back to power: we've got to connect the printer, too. We'll wire the printer's Vin and Ground right up to the Vin and Ground pins on the electric imp breakout board, which will pass the 5V supply through for the printer.
  1. Plug the power cable you got with your printer into the printer. This will prevent you from cutting the wrong end off of it.
  2. Cut off the end that not connected to the printer. I recommend cutting it off 4" to 6" away from the printer end, which gives you enough wire to work with, but not a ridiculous, messy amount. Strip the ends that you've just cut.
  3. Solder the red wire from the printer's Vin to the "Vin" pad on your electric imp breakout board.
  4. Solder the black wire from the printer's Ground (on the same connector as Vin) to the GND pad next to Vin on your electric imp breakout board.
You can test the printer now, too. If you hold the printer button down while you plug the 5V supply in, the printer should print a test page for you when it powers on. This will tell you various things about the printer's default settings, including the default baud rate, which should be 19200 (apparently just a few are set to 9600 - you can make a one-line change in your device code later if yours runs at 9600, so don't worry).

Now let's go ahead and connect the signal lines.
  1. Just like you did with the power cable, plug the signal cable (this one's got three wires) into the printer. This, again, prevents you cutting off the wrong end. Leave about the same amount of cable.
  2. We'll be using the imp's serial interface on pins 5 and 7 for this job. The imp transmits on pin 5 and receives on pin 7. See the imp pin mux for all the different pin configurations the imp supports. The TX and RX pins on the printer are labeled on the sticker on the bottom of the printer; refer to the diagram above. The center pin is RX; connect this to the imp's TX pin. Strip the end of the yellow wire and solder it to pin 5 on your imp breakout board.
  3. This example doesn't actually listen to serial data from the printer, but you certainly can; it can tell you things like whether it's working on a write, or if it's out of paper. Strip the end of the green wire and solder it to pin 7 on your breakout board.
  4. The serial interface also includes a ground pin. This acts as a reference voltage; the other ground wire is going to be passing a lot of current, as the printer uses a good bit of power. Because that wire has a small but finite resistance, that means that the voltage isn't always going to be zero; this leads to noise in your signal, which is bad. Hence, the second ground pin. Strip the end of the black wire and connect it to the second GND pin on your imp breakout board.
And. You're. Done. That is an internet-connected printer.

Of course, you'll need some software to get it working. Head to https://github.com/electricimp/examples/tree/master/thermalprinter in another tab and let's get the printer working.

Step 3: ImPrinter: Configure Your Imp

Next step: get your imp online. You'll need a wireless network connected to the internet for this. Presumably you do, or you wouldn't have much interest in a wireless internet-connected printer.

If you haven't already, now is the time to go to ide.electricimp.com and register (for free) for an account. If you've never configured an imp before, you'll get a page showing that you have no devices, and informing you that you'll need to configure one.

You'll also need the electric imp app for your smartphone to configure the imp using blinkUp. This nifty bit of tech sends your wifi SSID and password to the imp optically with a series of flashes on the phone screen. The imp picks up the blinks with a little phototransistor, decodes them, joins your wifi network, phones home to the imp server and appears in the imp IDE.

To configure your imp, open the electric imp app and log in with the credentials you used to create your account at plan.electricimp.com. Once you're in, tap "add network" in the app, and enter your wifi SSID and password. Then, power up your imp. If you imp has been powered up for a while, it's probably stopped listening (this is done on purpose), so power cycle it so it's blinking.

Hold the screen of your phone up to the end of the imp. I recommend holding a finger over the top of the imp (over the imp logo and "developer edition", if your imp is so marked) to shield the phototransistor from ambient light. Press "send blinkup" on the app, and the screen will begin to flash for a few seconds. When it's done, the imp will blink green once to show that it got the message, then it will blink red (offline), then red-orange (doing DHCP), then orange-red (doing DNS to find the electric imp server), then green (connected). Check out the LED blink codes here.

Once your imp is connected and happily blinking green, take a look at ide.electricimp.com in your browser, where you can program your imps and agents and view the logs.

Step 4: ImPrinter: Program Your Imp

In the electric imp IDE, your new breakout board will appear under "new devices" in the left panel. If you've used this breakout board for something else in the past, it will appear under the model that it was last configured to be. Assuming this is a new board (if it isn't, you've done this before), click "new devices" to show the list of new devices. You'll see a box with a long hex code in it - this is your device's ID. Click it, then click "device settings" on the right to assign your printer a name and a model.

In the imp IDE, devices belong to model. A model refers to what sort of thing your device is. For instance, on my plan, I have a thermometer model for the three internet-connected thermometers in my house, one for my printer, one for my controlBox, one for the imp conference room lights, etc.

In "device settings", enter a name for your new printer. Before you save and go back to the code editor, click the drop down to show your available models and pick "create a new model". Enter a name for this new model in the box. Click save and you'll go back to the code editor.

Now it's time to add some code. You should have a browser open and pointed to https://github.com/electricimp/examples/tree/master/thermalprinter, where I've posted the code you'll need to get your printer working. In the IDE, you'll see three panes: "agent", "device", and "logs". The "device" pane is where you'll place code to be run on the imp card. The "agent" is a little VM, just like the one running in the imp card, that runs inside the electric imp servers and has a direct line to your imp card. The agent allows you to do lots of things you couldn't do with just an imp card, like respond to HTTP requests while the imp card is asleep to save batteries, send HTTP requests and parse JSON to interact with all sorts of APIs, and hang onto bigger sets of data than the imp can keep in memory.

Grab the device firmware, "thermalprinter.device.nut" from github and paste it into the "device" window in the IDE. The file extension is not some juvenile attempt a humor, it's actually the proper file extension for source files written in squirrel, the lightweight javascript-like language that the imp uses. The agent code is "thermalprinter.agent.nut"; paste it into the agent window.

You can set the IDE up in a lot of different ways to best suit your style. The buttons in the top-left corner of the window control the look and feel of the IDE. The toggle buttons with the stacked rectangles allow you to view the device and agent windows either top-and-bottom or side-by-side. The gear icon opens a small window where you can choose a light-on-dark or dark-on-light color scheme, as well as set the tab size in your editor window. There is also a small expand/collapse icon inside the model window, just under the gear icon - clicking this will toggle a view where the left-hand navigation pane is hidden to give you more space to work. The IDE auto-saves while you're editing, and running your code also saves it.

Once your code is pasted in, press the "Build and Run" button to send your code to the imp and start running. You'll see the imp boot and start running and print some messages to the log, and then signal to the agent that it would like to print the imp logo. Here, some magic happens. NERD ALERT: If you don't care about magic, head to the next step now.

The imp logo needs to be formatted as a 384-pixel wide .bmp for the printer to print it, and it needs to be passed to the printer row-by-row. This is a lot of data for the imp to hold in memory, so we have the agent do the heavy lifting for us. The imp sends a signal ("logo") to the agent. When the agent gets this, it heads to the imp server, where I've stashed away the file we need for the job, and downloads it (just a simple HTTP GET). The agent reads through the headers on the file to grab a couple important parameters (since the file doesn't change, this isn't really necessary, but it's useful to get you started printing other things), then signals to the imp that it has data ready for printing. The imp then sends a new signal, "pull", to the agent. The agent chops off a chunk of image data and hands it back to the imp, which then hands it off to the printer. With the chunk handed off to the printer and printing, the imp sends another "pull" to the agent, who chops off and serves another chunk of image; this goes on until the image is done.

And if that doesn't leave you feeling pretty pumped, take up skydiving.

Step 5: ImPrinter: Sending Other Messages

While you're on github, grab the index.html file from the folder with your imp code. This is a tiny little website with some javascript in it that I've cooked up to send new messages to the agent. Tear it up and reuse bits at will!

The page uses twitter bootstrap version 2.3.2 (http://twitter.github.com/bootstrap/), which it picks up from a CDN (http://twitter.github.com/bootstrap/):

Add your agent URL to index.html on line 103. You can find your agent URL in the imp IDE at the top of the Agent Window.
  // this is where you put your agent URL. 
  impURL = "http://agent.electricimp.com/YOUR_UNIQUE_URL";
You can change the script source tags in index.html if you want to move things around. Now you can open the page with your favorite browser right on your local machine and use it as an interface for your printer. If you've got a webserver (or a friend with one), you can post your page up there, allowing you to access it from anywhere and send messages back to your printer, no matter where you are (or where the printer is, provided it's online).

Step 6: ImPrinter: What's in the Code?

If the imp and agent code looks a little odd to you, don't worry! Programming the imp involves a few new ideas, but the API reference is a big help, and the rich API makes most hardware tasks into very intuitive one-liners. To really understand what's going on in your imp firmware, you can't beat just reading the code and referring to the API reference, but let's take a quick look at some of the really interesting bits to help you get started. We'll start with the imp firmware (thermalprinter.device.nut).

Our device firmware starts with a class definition for the "printer" class. Classes are a bit more useful in situations where you want to create multiple instances of something, but this does provide a nice way to structure our code. Most of the things the device firmware needs to do here are created as methods of the printer class. The printer also keeps track of its own current settings.

Inside the printer class's constructor, we see a cool one-liner to configure the UART (Universal Asynchronous Receive and Transmit) hardware in the imp:
hardware.uart57.configure(19200, 8, PARITY_NONE, 1, NO_CTSRTS);
This sets up the UART hardware on pins 5 and 7, configures the baud rate to 19200 (bits per second), 8 data bits per word, no parity bits, one stop bit, and no flow control; just about the simplest UART settings you can use.

Below the class definition for the printer, the device registers some callbacks for the agent. We can think of these as hooks, each one giving the agent a way to tell the device to do something. Agent callbacks are registered with "agent.on()", which takes two arguments: a string, which will be passed by the agent to tell the device which callback to use; and a function, which will be called when the agent sends that string:
agent.on("print", function(buffer) {<br>   server.log("Device: printing new buffer from agent: "+buffer);<br>   myPrinter.print(buffer);<br>});
Looking at the firmware, we can see that we've registered callbacks for all sorts of things; toggling bold or underlined text, telling the imp to start downloading an image from the agent using the "pull" callback (which you'll find as a method of the printer class), feeding a certain number of lines, etc.

When the agent callbacks are all registered, and the classes are defined, we instantiate the printer class to create a printer object, and we're ready to go. The device firmware is primarily event-driven; bits of it are called into action when the agent signals it's time to do something.

// instantiate the printer object at global scope
myPrinter <- printer(hardware.uart57, 19200);<br>
The agent firmware is much the same, but even shorter, with a few interesting bits thrown in for HTTP interactions. Let's take a look:

We kick the whole thing off with a definition of the printLogo function - we'll see this registered as a callback for the "logo" event a little further down. One very useful bit here is the illustration of how to make an HTTP GET request from your agent:
local reqURL = "http://electricimp.com/docs/attachments/images/examples/resources/ei_logo_tinyprinter.bmp";<br>local req = http.get(reqURL);<br>imageData = req.sendsync().body;
We make a request object with "http.get(reqURL), then send it, which returns an http response object. The body of that response object contains the image data - it's as simple as that!

After doing a bit of work with the image data, we see the agent sending an event to the device. Here, we need to send multiple parameters to the device, so we pack them up into an array and then send that array with the event:
local imageParams = [imageSize, imageWidth, imageHeight];
device.send("downloadImage", imageParams);
Further down, we see callbacks registered for the "pull" and "imageDone" events from the device, then a callback registered with "http.onrequest". This is a very useful bit of agent API: the function registered here will be called whenever an HTTP request (of any method) is made to the agent URL listed in device settings. This is how the agent handles new data from the printer demo website:
http.onrequest(function(request,res){
One very important part here, for sending POSTs to the agent: when your browser sends a POST, it sends a preflight check first to see what methods the recipient supports. The recipient provides this information with its response headers. If these don't show that the agent supports POST, the page won't send the message body, and the request to the agent will be empty! Here's where we set these headers to allow our page to work:

res.header("Access-Control-Allow-Origin", "*");<br>res.header("Access-Control-Allow-Headers","Origin, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
The agent does another useful thing after the headers are set, but before sending a response: it determines whether to use the text message or image message handler based on the URL path to which the request was made:
if (request.path == "/text") {
Last in the agent code, we see the callback registered for the "logo" event from the device:
device.on("logo", function(value) {<br>    printLogo();<br>});
That's the whole shooting match! The whole internet-connected printer, in less than 600 lines!

Step 7: You're Done. Go Varnish the Deck.

If you read step six, you likely see that this is just a skeleton: a little tweaking to your agent code can point it toward virtually any API on the internet. A webpage to send messages is just the very beginning.

As for me, you've probably not seen the last of the printer. Stay tuned for more special-purpose agents!

And by now, you've probably finished your coffee, so it's back to that weekend project for you.