Wunderlist Task Printer

Digital Lists (such as Wunderlist) are great but sometimes you just want a paper copy. In this project we will be making a Wunderlist Task Printer. To accomplish this we will be creating a nice 3D printed case for the Adafruit Mini Thermal Printer and connecting it to the Particle Photon. The photon will then listen for HTTP POST requests from a Microsoft Flow (other services can be used) task, that monitors Wunderlist for tasks that have been added.

Download Project Source

Licensed under the MIT license, you can download the code and 3D files for this project from here



ADVERTISMENT


The Build

Parts Needed

To complete this build you will need:

Hardware

Making the case

The enclosure for this project was 3D printed and holds the thermal printer and required electronics. The plastic used is the Taulman In-PLA. This version took ~8 hours to print the 3 parts.
Printer settings:

  • 0.2mm Layer Height
  • Sliced in Astroprint
  • 220°C Extruder temperature
  • 50°C Bed temperature

Constructed Front view
The STLs for this build can be downloaded from Thingiverse.

You may also be interested in this remix by Thingiverse user bmjbmj. This version has several improvements, being designed in openSCAD with sturdier 5mm walls and is printed in two pieces. Mathias says it took ~24 hours to print.
bmjbmj remix
After printing you will have 3 parts:

The Front panel
Printed front panel

The Main body
Main Body

The Side panel
Side panel

You will need to attach the main body and side panels together. Here we carefully melted the two pieces using a soldering iron, however you may like to use hot glue or your own preferred method. There is an area on the body and side panel for a mounting screw to provide additional support.

Constructed body

Once the body is completed you need to attach the power jack. This can be done using the same method as before (if soldering, the barrel jack needs to have plastic where you are going to make the join). Hot glue would be ideal for this, however I didn’t have any available so off with the soldering iron again to make the connection.

Power connector closeup

Now that the body is constructed and has the power jack in place, construction on the front panel can begin. Start by installing the 3, 5mm LEDs. These can be kept in place with hot glue, melting the plastic or with LED mounting clips (ideal). Then install the button in the middle. Depending on your button, it may be best to attach wires to it before installing. Same goes for the buzzer. If you used the Freetronics buzzer module, it can be pressure fitted in place, otherwise use hot glue on the outside to keep it secure.

The thermal printer can now be installed. Remove the side mounts from the printer and slide it in. The reinstall the mounting hardware to secure the printer to the frame.

Install the electronics

With the case constructed and parts mounted, its time to wire everything up. Start by soldering wires to the LEDs. All the cathodes (the flat side) of the LED can be soldered together. Attach wires to the button, if you haven’t already. If your using a mini breadboard then it may come with an adhesive back. Use this to attach it to the back of the thermal printer. Then install the photon.
Use this guide to wire every thing together.

From To
Buzzer Pin 0 and GND
LED1 + Pin 3
LED2 + Pin 2
LED3 + Pin 1
LED Cathodes GND
Button Pin 4 and GND
Printer GND GND
Printer RX Photon TX
Printer TX Not Connected
PWR IN Printer HV & Photon VIN
GND IN Printer GND & GND
Printer Wiring

Before mounting the front panel, tidy up the wiring. Its also a good idea to setup the photon if you haven’t already. Then attach the front panel and screw it all together.

Constructed front

Congratulations, now that the device is constructed lets give it some life with the software.

Software

Before uploading the code to the Photon we need to know the baud rate of our printer. To find out this hold in the button on the thermal printer while attaching power. The printer will preform a test print and on it will be the printer baud rate.

Baud printout

Now we need to flash the photon with the code. Go to build.particle.io and sign in using the same account used during the Photon setup.

Copy and paste a version of the code below and modify the baud rate to match what was on the test print. The Buffered FlowPrinter version will store the results in memory until the button is pressed, where it will print all the results at once. Then flash the code to the Photon.

The software will wait for a POST request and will print the value to the printer. To test that it is setup and listening follow the guide on the particle site. The code has three functions that it can accept; headerbody and task. The header function will print the text larger and center aligned. body will print the text in default size and alignment, while task will add the string [] to make a checkbox before the item. If the input text to any of these functions is too long, then it will be trimmed and the last 3 characters will be replace with ....

The code is licensed under the MIT Open Source license. Feel free to modify it to suit your needs. All thats left is creating a Microsoft Flow that will monitor Wunderlist for added tasks and print it!

Printer Listener.ino

Code that runs on photon to listen for HTTP POST requests from MS Flow


/*
 * Code to power the Particle Photon connected to a thermal printer.
 * Waits for a HTTP POST request then  prints it on the thermal printer
 *
 * Project details at
 * prototypingcorner.io/projects/wunderlist-task-printer
 *
 * MIT License
 *
 * Copyright (c) 2016 Prototyping Corner
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

// Include some libraies for thermal printer managment
#include "Adafruit_Thermal/Adafruit_Thermal.h"

// Configure Pins
#define BUZZER  0   // Piezo Buzzer
#define PWRLED  3   // Power Status LED
#define HBLED   2   // Heart Beat LED
#define STATLED 1   // Thermal Printer Status LED
#define BTN     4   // Input Button pin

// Define Printer in Adafruit Thermal printer Library (for sending commands)
Adafruit_Thermal printer;

// Misc. Variables to use globaly in code
const int printerBAUD = 19200;  // Printer Baud rate, see test page (9600 / 19200)
const String sepLine = "________________________________";  // 32 hypens to create boarder line
const int charactersPerLine = 16; // Characters that fit on one line of thermal paper
const int maxBodyLength = 255;

const String boxChar = "[]"; // Character to display at begining of task print mode (printTask)

void setup()
{
    // Configure Pins
    pinMode(BUZZER, OUTPUT);
    pinMode(PWRLED, OUTPUT);
    pinMode(HBLED, OUTPUT);
    pinMode(STATLED, OUTPUT);
    pinMode(BTN, INPUT_PULLUP);

    switchPWR(); // Indicate System is Starting by turning on PWRLED

    initPrinter(); // Initiate Thermal Printer

    // Initialize Particle Functions
    Particle.function("header", printHeading);  // For printing large Headers
    Particle.function("body", printBody);       // Normal text
    Particle.function("task", printTask);       // Printing Lists or Tasks - Includes boxChar [] at beginging of print
}

void loop()
{

    HB(); // Change Heart Beat to indicate system is responding
    delay(50); // Add small delay for HB
}

// Particle Functions
// For printing large headings
int printHeading(String input)
{
    // Set Status LED to indicate Proccessing state
    switchStat();

    fastPrint(sepLine); // Print Seperator Line at header
    //feed(2);

    // Set Text to HEADING
    resetText();
    printer.setSize('L');
    printer.justify('C');

    fastPrint(shorten(charactersPerLine, input));

    resetText();

    // Set Status LED to indicate end of proccessing state
    switchStat();

    return 1;
}

// For Task printer mode. Adds boxChar to begining of String.
int printTask(String input)
{
    // Initiate Printing
    switchStat();
    resetText();

    String toPrint = "";
    toPrint += boxChar;
    toPrint += " ";
    toPrint += input;

    toPrint = shorten(maxBodyLength, toPrint); // Comment out this line for text overflow

    fastPrint(toPrint); // Print string

    switchStat(); // Change Status indicator

    return 1;
}

// For printing ordinary text
int printBody(String input)
{
    // Set Status LED to indicate Proccessing state
    switchStat();

    resetText();

    fastPrint(shorten(maxBodyLength, input)); // Print Shortend input value

    fastPrint(sepLine); // Print Seperator Line

    // Set Status LED to indicate end of proccessing state
    switchStat();

    return 1;
}

// shorten function. Accepts input length (int) and input string (String)
String shorten(int length, String inputString)
{
    String outputString = "";
    int inputSize = inputString.length(); // Get length of String in characters

    // if length of String exceeds threshold
    if(inputSize > length)
    {
        char buffer[inputSize];
        inputString.toCharArray(buffer, inputSize); // Convert input string to char array

        for(int i = 0; i < (length - 3); i++)
        {
            outputString += buffer[i];
        }
        outputString += "..."; // Trailing dots
    }
    else
    {
        outputString = inputString;
    }

    return outputString; // Return formated String
}

// Power LED state switcher
bool pwrState = false; // State of PWRLED
void switchPWR()
{
    if(pwrState)
    {
        pwrState = false;
        digitalWrite(PWRLED, HIGH);
    }
    else
    {
        pwrState = true;
        digitalWrite(PWRLED, LOW);
    }
}

// Status LED state switcher
bool statState = false;
void switchStat()
{
    if(statState)
    {
        statState = false;
        digitalWrite(STATLED, HIGH);
    }
    else
    {
        statState = true;
        digitalWrite(STATLED, LOW);
    }
}

// Heartbeat LED changer (switch state of HBLED to indicate software is responding)
bool heartState = false;
void HB()
{
    if(heartState)
    {
        heartState = false;
        digitalWrite(HBLED, HIGH);
    }
    else
    {
        heartState = true;
        digitalWrite(HBLED, LOW);
    }
}

// Thermal Printer Methods
// Directly send string to thermal printer (Serial1)
void fastPrint(String toPrint)
{
    Serial1.println(toPrint);
}
// fastPrint overload to add feed at end of print
void fastPrint(String toPrint, bool feedAtEnd)
{
    fastPrint(toPrint);
    feed(2);
}

// Print Empty Paper (Feed)
void feed(int amount)
{
    for(int i = 0; i < amount; i++)
    {
        fastPrint(" ");  // Send blank lines to printer
    }
}

// Initiate Printer
void initPrinter()
{
    switchStat();                   // Turn on status LED while setting up printer
    Serial1.begin(printerBAUD);     // Setup Serial1 - RX/TX pins (Serial is USB)
    printer.begin(&Serial1);        // Configure printer in Adafruit Thermal library
    switchStat();                   // Turn off status LED (will turn on again in printerDefault method)

    printerDefault();   // Restore printer to defaults

    char printDensity = 15;
    char printBreakTime = 15;

    Serial1.write(27);
    Serial1.write(55);
    Serial1.write(7);
    Serial1.write(200); // Heat time
    Serial1.write(255); // Heat interval
    Serial1.write(30);
    Serial1.write(35);

    int printSettings = (printDensity<<4) | printBreakTime;
    Serial1.write(printSettings);

}

// Set printer to default config
void printerDefault()
{
    // Turn on status LED while preforming actions on thermal printer
    switchStat();
    printer.setDefault();
    switchStat();
}

// Reset Text Styles
void resetText()
{
    switchStat(); // Switch Status LED

    // Reset all text styles on printer
    printer.inverseOff();
    printer.justify('L');
    printer.doubleHeightOff();
    printer.underlineOff();
    printer.setSize('S');

    switchStat(); // Switch Status LED
}

// END OF CODE
  

Printer listener - buffered

Buffers POST requests until button is pressed


/*
 * Code to power the Particle Photon connected to a thermal printer.
 * Waits for a HTTP POST request then adds to to a queue to prints it on the
 * thermal printer
 *
 * Project details at
 * prototypingcorner.io/projects/wunderlist-task-printer
 *
 * MIT License
 *
 * Copyright (c) 2016 Prototyping Corner
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

// Include some libraies for thermal printer managment
#include "Adafruit_Thermal/Adafruit_Thermal.h"

// Configure Pins
#define BUZZER  0   // Piezo Buzzer
#define PWRLED  3   // Power Status LED
#define HBLED   2   // Heart Beat LED
#define STATLED 1   // Thermal Printer Status LED
#define BTN     4   // Input Button pin

// Define Printer in Adafruit Thermal printer Library (for sending commands)
Adafruit_Thermal printer;

// Misc. Variables to use globaly in code
const int printerBAUD = 19200;  // Printer Baud rate, see test page (9600 / 19200)
const String sepLine = "________________________________";  // 32 hypens to create boarder line
const int charactersPerLine = 16; // Characters that fit on one line of thermal paper
const int maxBodyLength = 255;

const String boxChar = "[]"; // Character to display at begining of task print mode (printTask)

// Array to store values in
const int bufferSize = 50;
String store[bufferSize];
int currentBuffPos = 0;

void setup()
{
    // Configure Pins
    pinMode(BUZZER, OUTPUT);
    pinMode(PWRLED, OUTPUT);
    pinMode(HBLED, OUTPUT);
    pinMode(STATLED, OUTPUT);
    pinMode(BTN, INPUT_PULLUP);

    switchPWR(); // Indicate System is Starting by turning on PWRLED

    initPrinter(); // Initiate Thermal Printer

    // Initialize Particle Functions
    Particle.function("header", printHeading);  // For printing large Headers
    Particle.function("body", printBody);       // Normal text
    Particle.function("task", printTask);       // Printing Lists or Tasks - Includes boxChar [] at beginging of print
}

void loop()
{

    HB(); // Change Heart Beat to indicate system is responding
    delay(50); // Add small delay for HB

    // Check Button for press
    if(digitalRead(BTN) == LOW) printBuffer();
}

// Print content of Buffer to thermal printer
void printBuffer()
{
    switchStat();

    for(int i = 0; i < currentBuffPos + 1; i++)
    {
        fastPrint(store[i]);
    }

    clearBuffer();

    switchStat();
}

// Particle Functions
// For printing large headings
int printHeading(String input)
{
    // Set Status LED to indicate Proccessing state
    switchStat();

    fastPrint(sepLine); // Print Seperator Line at header
    //feed(2);

    // Set Text to HEADING
    resetText();
    printer.setSize('L');
    printer.justify('C');

    fastPrint(shorten(charactersPerLine, input));

    resetText();

    // Set Status LED to indicate end of proccessing state
    switchStat();

    return 1;
}

// Adds input to buffer
int printTask(String input)
{
    // Initiate Printing
    switchStat();

    // Store value in buffer
    String toStore = "[] " + input;
    store[currentBuffPos] = toStore;
    currentBuffPos++;

    switchStat(); // Change Status indicator

    return 1;
}

void clearBuffer()
{
    for(int i = 0; i < bufferSize; i++)
    {
        store[i] = "";
    }

    currentBuffPos = 0;
}

// For printing ordinary text
int printBody(String input)
{
    // Set Status LED to indicate Proccessing state
    switchStat();

    resetText();

    fastPrint(shorten(maxBodyLength, input)); // Print Shortend input value

    fastPrint(sepLine); // Print Seperator Line

    // Set Status LED to indicate end of proccessing state
    switchStat();

    return 1;
}

// shorten function. Accepts input length (int) and input string (String)
String shorten(int length, String inputString)
{
    String outputString = "";
    int inputSize = inputString.length(); // Get length of String in characters

    // if length of String exceeds threshold
    if(inputSize > length)
    {
        char buffer[inputSize];
        inputString.toCharArray(buffer, inputSize); // Convert input string to char array

        for(int i = 0; i < (length - 3); i++)
        {
            outputString += buffer[i];
        }
        outputString += "..."; // Trailing dots
    }
    else
    {
        outputString = inputString;
    }

    return outputString; // Return formated String
}

// Power LED state switcher
bool pwrState = false; // State of PWRLED
void switchPWR()
{
    if(pwrState)
    {
        pwrState = false;
        digitalWrite(PWRLED, HIGH);
    }
    else
    {
        pwrState = true;
        digitalWrite(PWRLED, LOW);
    }
}

// Status LED state switcher
bool statState = false;
void switchStat()
{
    if(statState)
    {
        statState = false;
        digitalWrite(STATLED, HIGH);
    }
    else
    {
        statState = true;
        digitalWrite(STATLED, LOW);
    }
}

// Heartbeat LED changer (switch state of HBLED to indicate software is responding)
bool heartState = false;
void HB()
{
    if(heartState)
    {
        heartState = false;
        digitalWrite(HBLED, HIGH);
    }
    else
    {
        heartState = true;
        digitalWrite(HBLED, LOW);
    }
}

// Thermal Printer Methods
// Directly send string to thermal printer (Serial1)
void fastPrint(String toPrint)
{
    Serial1.println(toPrint);
}
// fastPrint overload to add feed at end of print
void fastPrint(String toPrint, bool feedAtEnd)
{
    fastPrint(toPrint);
    feed(2);
}

// Print Empty Paper (Feed)
void feed(int amount)
{
    for(int i = 0; i < amount; i++)
    {
        fastPrint(" ");  // Send blank lines to printer
    }
}

// Initiate Printer
void initPrinter()
{
    switchStat();                   // Turn on status LED while setting up printer
    Serial1.begin(printerBAUD);     // Setup Serial1 - RX/TX pins (Serial is USB)
    printer.begin(&Serial1);        // Configure printer in Adafruit Thermal library
    switchStat();                   // Turn off status LED (will turn on again in printerDefault method)

    printerDefault();   // Restore printer to defaults

    char printDensity = 15;
    char printBreakTime = 15;

    Serial1.write(27);
    Serial1.write(55);
    Serial1.write(7);
    Serial1.write(200); // Heat time
    Serial1.write(255); // Heat interval
    Serial1.write(30);
    Serial1.write(35);

    int printSettings = (printDensity<<4) | printBreakTime;
    Serial1.write(printSettings);

}

// Set printer to default config
void printerDefault()
{
    // Turn on status LED while preforming actions on thermal printer
    switchStat();
    printer.setDefault();
    switchStat();
}

// Reset Text Styles
void resetText()
{
    switchStat(); // Switch Status LED

    // Reset all text styles on printer
    printer.inverseOff();
    printer.justify('L');
    printer.doubleHeightOff();
    printer.underlineOff();
    printer.setSize('S');

    switchStat(); // Switch Status LED
}

// END OF CODE

  

Creating the Flow

This project uses Microsoft Flow to monitor Wunderlist for added items and send the POST request to the Photon. Other services such as Zapier could be configured to achieve the same results.

Start by going to flow.microsoft.com and signing in. Then go to 'My Flows' and press Create from blank. Type Wunderlist and choose Wunderlist - When a new task is created as the trigger. You will need to connect you Wunderlist account and choose a list to monitor.

Flow menu 1

Click + New step > Add an action. Type HTTP to select the HTTP option. For method select POST. We will need to create our URI. To do this you need the device id and access token for your Photon. You can get these from the particle site. Enter in https://api.particle.io/v1/devices/{device}/task?access_token={token}, replacing {device}with the device id and {token} with the access token from particle. In the body enter
{ "args":"TITLE" }
TITLE is dynamic content that can be selected from the Add dynamic content menu (see image).

Flow menu 2

All that's left is to give the flow a name and press Create Flow. Wait for the flow to be created then open Wunderlist and create a new task. You may need to wait a moment for Flow to register the new task and send it to the Photon.

Done

Completed build

That's it! Now you have a live Wunderlist task printer for the occasion when you want a physical copy of your tasks. Let me know your thoughts in the comments below.

Support Prototyping Corner

Consider supporting Prototyping Corner by making a small contribution to our work or take a look at other ways to support us


Leave a Reply

Your email address will not be published. Required fields are marked *