raspberry_printer_featured

Raspberry Pi Printer server for LabelWriter

Introduction

So I have finally come around and started playing with a Raspberry Pi. If you dont know what it is, its a one card arm mini computer, read more at raspberrypi.org. What i tried to accomplish here was to get the pi to work as remote accessible printer server for a Dymo LabelWriter 450.

The goal was to build a web application that could be used from a tablet and on a click on a button in the webapp ui send a request to the pi with a number of paramterers that would for two labels and be printed. The whole thing used a a checkin system for children whit a label for the child and the parent dropping of the child.

Overview

After some thouhts i desicided to build the system with the following components and parts:

  • Dymo LabelWriter 450
  • Raspberry Pi, model B with Rasbian Wheezy
  • Apache server (with PHP) on the raspberry reciving the printing requests
  • C++ application on the raspberry doing the actual printing

Getting the printer up and running

Install cups libraries and other necessary libraries to build

pi@raspberrypi ~ $ sudo apt-get install libcups2-dev libcupsimage2-dev g++ cups cups-client

Download Dymo SDK and install the drivers

Download the Dymo LabelWriter SDK for Linux located at http://sites.dymo.com/DeveloperProgram/Pages/LW_SDK_Linux.aspx

pi@raspberrypi ~ $ wget http://download.dymo.com/Download%20Drivers/Linux/Download/dymo-cups-drivers-1.4.0.tar.gz

Now we need to unpack, configure and install the drivers. This is done with the autoconfigure file that is packed with the sdk. Change directory to the unpacked foleder and run ./configure

pi@raspberrypi ~ $ tar xvf dymo-cups-drivers-1.4.0.tar.gz
pi@raspberrypi ~ $ cd dymo-cups-drivers-1.4.0.5/
pi@raspberrypi ~/dymo-cups-drivers-1.4.0.5 $ sudo ./configure
pi@raspberrypi ~/dymo-cups-drivers-1.4.0.5 $ sudo make
pi@raspberrypi ~/dymo-cups-drivers-1.4.0.5 $ sudo make install

That is everything to get the drivers for the printers installed on the system.

Add the printer

To use the printer, we need to add it in the system first, this is easiest done with cups webadmin. And since the raspberry is only aceessed remote with ssh, we need to install a command line based web browser , I'm using links2. You could use the graphical interface as well. But since the rasperry is going to be a embedded system i'll do everything with command line.

pi@raspberrypi ~ $ sudo apt-get install links2

And the we need to add the pi user to the printer admin group to be allow to login to the printer admin.

pi@raspberrypi ~ $ sudo usermod -a -G lpadmin pi

Navigate to http://localhost:631/admin with links2

pi@raspberrypi ~ $ links2 http://localhost:631/admin

cups_amin
Select "Add printer".

cups_login
Log in with pi user. Default user pi and password raspberry.

cups_local_printers
In the list of local printers, the "Dymo LabelWriter 450" should appear, mark and continue.

cups_add_printerGive printer a name and description, and continue.

Select the Dymo LabelWriter 450 drivers if found. otherwise you need to locate the file that make install created earlier.

cups_manageThat should be everything for the printer. To verify, go the admin interface and click Manage Printers ant the LabelWriter should appear in the list.

The Code

The printing application

So I built the printing application in c++ from the example code supplied by DYMO.
The includes used for the application, some standard c++ libraries and the cups libraries:

#include <iostream>
#include <cups/cups.h>
#include <cups/ppd.h>
#include <string>
#include <stdio.h>
#include <map>
#include <exception>

This code lists all printers and can be useful to see if your program can find the LabelWriter.

  1. int i;
  2. cups_dest_t *dests, *dest;
  3. int num_dests = cupsGetDests(&dests);
  4. for (i = num_dests, dest = dests; i > 0; i --, dest ++)
  5. {
  6. if (dest->instance)
  7. printf("%s/%s\n", dest->name, dest->instance);
  8. else
  9. puts(dest->name);
  10. }

The actual printing does not require that much code. PrinterName is a string with the name of the LabelWriter you chose when you added it before. ImageName is a string with image to be printed, for me it's a png image that is generated by the web server in the next section.

  1. int num_options = 0;
  2. cups_option_t* options = NULL;
  3.  
  4. num_options = cupsAddOption("PageSize", "w167h288", num_options, &options);
  5. num_options = cupsAddOption("scaling", "100", num_options, &options);
  6. cupsPrintFile(PrinterName, ImageName, "Print Label", num_options, options);
  7. cupsFreeOptions(num_options, options);

The options for the PageSize parameter can be found in the lw450.ppd file (/dymo-cups-drivers-1.4.0.5/ppd/lw450.ppd). It lists all paper types and sizes available for that printer.

Compiling

For compiling the application these flags is used:

g++ `cups-config --cflags` PrintLabel.cpp `cups-config --libs` -o PrintLabel

The receiving HTTP server

The web server, running apache with PHP, is just used for receiving remote calls with parameters and generate an image and then execute the previous c++ application  to print that image.

  1. <?php
  2. $firstname = $_GET['firstname'];
  3. $id = $_GET['id'];
  4. $fileName = "kid_".$id.".png";
  5. ... (image generation with GD and write image to disk) ...
  6. $printOutput = array();
  7. exec("./PrintLabel " . $fileName, $printOutput, $status);

Testing Geocoding on Android

Intro

Recently i developed an application for Android that used reverse geocoding, ie. get a human readable address from a latitude/longitude position. To test what position i got for different location and to test what addresses that translated to i developed this little neat application.

Application info and download

Application to test Positioning and Reverse geocoding (Address from lat/lng). Optionally Save results to sdcard.

Download application (.apk)

Download source-code (.rar)

Explanation of code

Permissions

First off, we need to add the needed permissions to AndroidManifest.xml

  1. <uses-permission android:name="android.permission.INTERNET"></uses-permission>
  2. <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
  3. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>

The permissions are for; accessing internet which is used by the geocoder, using GPS and Network location, and writing files to SD-card.

Get user location

Getting the location of the user is done with the LocationManager and LocationListener. Start with getting in instance of the LocationManager.

  1. locationManager = (LocationManager)
  2. this.getSystemService(Context.LOCATION_SERVICE);
  3.  

Then I start requesting location updates, we also supply which listener that will handle location events.

  1. locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
  2.  0, 0, locationListener);
  3. locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
  4.  0, 0, locationListener);

The listener i'm using is very simple and looks like this:

  1. LocationListener locationListener = new LocationListener() {
  2. public void onLocationChanged(Location location) {
  3. txt_lat.setText("" + location.getLatitude());
  4. txt_lng.setText("" + location.getLongitude());
  5. }
  6. public void onStatusChanged(String provider, int status, Bundle extras) {}
  7. public void onProviderEnabled(String provider) {}
  8. public void onProviderDisabled(String provider) {}
  9. };

Reverse geocoding

Now when we have a lat/lng position, either from a location update or manually inputed in the text boxes, its time to do some reverse geocoding. This is done with the Geocoder class, and looks like this:

  1. Geocoder myLocation = new Geocoder(getApplicationContext(), Locale.getDefault());
  2. List<Address> geo_adresses = myLocation.getFromLocation(lat, lng, 1);

now check if there was a resulting address, in that case get it. And in this application I wanted to see all different address fields that returned.

  1. if(geo_adresses.size()>0){
  2. Address add = geo_adresses.get(0);
  3. for(int i = 0; i <= add.getMaxAddressLineIndex(); ++i){
  4. sb.append("AdressLine" + i + ": " + add.getAddressLine(i)+ "\n");
  5. }
  6. sb.append("AddminArea: "+ add.getAdminArea()+ "\n");
  7. sb.append("CoutryName: "+ add.getCountryName()+ "\n");
  8. sb.append("Premises: "+ add.getPremises()+ "\n");
  9. sb.append("SubAdminArea: "+ add.getSubAdminArea()+ "\n");
  10. sb.append("SubLocality: "+ add.getSubLocality()+ "\n");
  11. sb.append("Thoroughfare: "+ add.getThoroughfare()+ "\n");
  12. sb.append("SubThoroughfare: "+ add.getSubThoroughfare()+ "\n");
  13.  
  14. txt_out.setText(sb.toString());
  15. }

Conclusion

I found this little application very handy to test situations that don't occur during testing at home/office or with the emulator, to see what values you can expect to get from users when the application is live.

New server and new domain

Today have been a good day, i have bought and set up a new web/database server and I have acquired the domain hmazter.com and directed it to the new server. As a part of this there has been a re-release of this site as the old one was down because of the old server broke.

//Kristoffer

Site launched

I'm glad to announce that this site has been launched.

This site will show the portfolio of some of my work I'm doing with design and development. You're welcome to browse and look trough my work that are on display here. Most of the work I'm doing is for myself or friends so don't expect to find any work done for any corporate or something like that =)

Happy browsing

//Hmazter