Subscribe via RSS

Arduino + Thermal Printers (Sparkfun, IBM 4160-TF6)

I've always wanted to print my own receipts... devious activities come to mind; but the following usage of receipt printers is nothing sinister at all. My goal is to print out activities for trains on the layout; certain locations will have a new reporting mechanism!

I'd seen a few printers on eBay, most being USB. Serial was always to be my preferred method and I had the components on hand. I then saw the Thermal Printer at Sparkfun and decided that it'd be my first guinea pig.

Sparkfun Thermal Printer

The Sparkfun themal printer is, like most of their products, targetted at the Arduino. Thanks to this there is a wealth of information on their page on how to connect and use it. After hooking up a proper power supply (they recommend 9v @ 2Amps, so use a 7809 with a heatsink!) the printer just started spewing out whatever I threw at it.

Sparkfun Thermal Printer

The easiest way to use this is via the library found here: Displaying on Paper – Thermal Printer + Arduino. bildr.org is actually a really cool site full of interesting projects for the Arduino, check it out when you can!

There's a forum for discussing the above library, in which I've already posted my praises to the developer. If you need a hand then go over and ask away... they're always open to feedback and improvements to the library too!

Note that this printer uses small rolls of 58mm paper. I found a 10-pack of these at OfficeWorks (Australia) for AUD$9.95.

IBM 4610-TF6 (on Windows)

I'd finally found a dirt cheap printer on eBay that was RS-232. It was a chunky/retro IBM thermal printer and really was just a larger, more robust version of the Sparkfun thermal printer above. It didn't come with a power supply and, after purchasing, I realised that it wanted 38v! What the hell? It seems that the 'thermal' side of it uses a lot of current to burn the paper! It also wanted 3 AMPS at 38v... where the hell would I find that?

Split open IBM SureMark 4610-TF6 Bypassing the power socket 58mm paper output!

I installed the printer drivers here on a Windows XP 32-Bit machine (DOES NOT WORK WITH 64-Bit!) and provided it 12v @ 2A. The LED came on, but the printer showed up as 'offline'. All attempted connections via Hyper-Terminal showed the port as 'already open'; the printer driver would've been the cause. I uninstalled it and rebooted, but Hyper-Terminal wasn't receiving any responses after connecting successfully.

Middle bar not printing! Bit Switches

I then found a second 12v power supply and chained them together... Prior to this I'd re-installed the printer drivers and, upon the boost to 24v, the printer flicked to 'online' and the LED glowed slightly brighter. I could now also use the line-feed button on the top of the printer! I opened notepad, loaded a text file from the Arduino directory and, without thinking, hit print. The printer control panel window showed the job spooling up to 20 pages and then the printer started .... and kept going ... for an hour. It printed at a rate of about 1 line per 5 seconds.

Testing output

I had no idea how to cancel it... so I had to let it go.

IBM 4610-TF6 (on the Arduino)

It was now time to make this printer talk to my Arduino Mega. I hooked it all up based on how the Sparkfun printer wiring and attempted to use the same code; nothing happened. I then used the code from Tom Taylor's Microprinter blog post. Once uploaded, I had junk coming out of the printer... It occurred to me that I probably needed a MAX232 in the middle to raise the voltages to proper RS232 levels (as per everyone elses examples!)

I hooked up the MAX232 as per the schematic below and then had a functional printer from my Arduino! Determining the actual commands to send it came next.

Printer Connection to Arduino Proper cabling Initial DB9 Connections
MAX232 Setup Two RS232 cables for the Arduino

Fonts, spacing, etc...

Right, this gets tricky... you can either put 58mm or 80mm paper in this printer. 80mm is recommended as it has better chances of staying aligned with the paper cutter (coming out diagonally is actually an issue.) I had started with the 58mm but quickly went and bought 80mm paper (AUD$12 for 4 80x80 rolls) as I also wanted the extra printing space.

You can work out how many characters per line based on the font chosen, character spacing. It is pretty much expected that you're using 80mm paper. Font A is 10x20, Font B is 12x24 and Font C is 8x16. The Cash Receipt print line is 72 mm (2.83 inches) long. There are 576 dots per line and 203 dots per inch.

The Application Guide provides the following calculations:

  • 20 CPI ⇒ 8-dot wide character + 2-dot space (Font C) ⇒ 57 characters/line
  • 17 CPI ⇒ 10-dot wide character + 2-dot space (Font A) ⇒ 48 characters/line
  • 15 CPI ⇒ 10-dot wide character + 3-dot space (Font A) ⇒ 44 characters/line
  • 12 CPI ⇒ 12-dot wide character + 5-dot space (Font B) ⇒ 33 characters/line

More test output

Printing a Barcode

In a comment below, Jonas has asked how to print a barcode from any programming language via the serial port... Here's a list of instructions to do so:

  1. Set the Font (0x00 or 0x01):
    0x1d 0x66 FONT 0x0a
  2. Set the Text Location:
    • 0x00 - None
    • 0x01 - Above
    • 0x02 - Below
    • 0x03 - Both
    0x1d 0x66 POSITION 0x0a
  3. Set Barcode Width:
    0x1d 0x68 WIDTH 0x0a
  4. Set Barcode Height:
    0x1d 0x77 HEIGHT 0x0a
  5. Print a Barcode. 'CHARS' is the list of characters to print.
    • 0x00 - UPC-A
    • 0x01 - UPC-E
    • 0x02 - JAN13 (EAN)
    • 0x03 - JAN8 (EAN)
    • 0x04 - CODE 39
    • 0x05 - ITF
    • 0x06 - CODABAR
    • 0x07 - CODE 128 (c)
    • 0x08 - CODE 93
    • 0x09 - CODE 128 (a, b)
    0x1d 0x6b BARCODE_TYPE CHARS 0x00

A 'Hello World' example of point 4 would be:

0x1d 0x6b 0x00 0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x72 0x6C 0x64 0x00

IBM SureMark Thermal Printer Library for Arduino 1.0

IBM provides a great reference document for this collection of printers: Application interface specification for IBM 4610 printers. I found it to be a little hit-and-miss as to what commands are available on the TF6, but most worked well. Either way, I built the following library which provides the following functionality:

  • Text Styles: Bold, Underline, Overline, Inverted, Arbitrary Font Scaling, Double Height/Width, Rotated, Upside-down
  • Barcode Printing: UPC-A, UPC-E, JAN13 (EAN), JAN8 (EAN), CODE 39, ITF, CODABAR, CODE 128 (c), CODE 93, CODE 128 (a, b)
  • Image Printing from data stream, image storing to printer RAM and image printing from printer RAM
  • Message storing to printer RAM and printing from printer RAM
  • Beeper sounds. (Example below has 'Mary had a little lamb')
  • Paper cutting, line spacing, line feeding, etc...
Download the library and example here.

Other references

It turns out that, if I'd google'd more, I would've found a lot more help around the traps... here's a few locations to check out:

20Feb/121

Controlling points/turnouts with Servos via the Arduino

I've managed to cook many Tomix Turnouts during my tinkering with the Arduino. The main issue has been applying too much current for too long. The actual switches that come with Tomix points are momentary and, internally, the circuit is only completed very quickly via a spring mechanism. The goal, of course, is to prevent the user from holding the power to the turnout magnet for too long. Unfortunately, I've managed to (via both coding and circuitry mistakes) apply too much power in the past and it takes very little time to melt the roadbase of Tomix Finetrack.

Due to all this, and the desire to use Peco Setrack, I'd decided that instead of Peco Turnout Motors (which also require large amounts of voltage) I'd use the smallest RC servos I could find. Off to eBay I went and the following arrived not so long ago.

9g Servo 9g Servo 9g Servo

I had no idea what servos to purchase: they seem to be rated in grams as to how much they can lift? I read in a few random locations across the web that 9g would be more than enough for switching points.

Hooking up Servos to your Arduino

This could not be easier. Arduino 1.0 already comes with the Servo Library in-built. Simply include this header and then implement the following code. The basic idea is to initialise the Servo on a specific pin (they only require one pin to be controlled) and then hook up the external power source. As per usual, it's not recommended to power too many off the USB 5v.

BareBonesWithServoAndEncoder

Mounting Servos to control Turnouts

Got some piano wire? A paperclip? Resistor legs? Any solid piece of wire/metal/rod will work to connect your turnout to a Servo. As you can see below, I've used a .55mm 'hobby wire' to connect everything up as I don't need flexibility and I want it to be robust.

Prototype Arduino + Servo controlling point Prototype Arduino + Servo controlling point

You could also be very tricky and build a full interlocking frame to control multiple points at once. I bought a few 'hinges' (no idea what the real word should be) to allow the rodding to turn corners but thought it easier in the end to just install another Servo :).

Rotary Encoders allow you to switch the Turnout yourself...

Rotary Encoders are 'endless' switches which usually come with 5 pins + GND. You can continually turn them, allowing for applications where you want an inifinitely adjustable value. The pins are as follows: one side has two pins which are for the 'pushbutton', as the actual stem can be pushed into the base and provides a momentary switch. The other three on the other side are for the rotor location. The inside pin is 'common' and needs to go to ground; the outside pins are 'data' and need to be hooked into digital inputs somewhere on your Arduino.

Rotary Encoder Rotary Encoder

You then simply download the rotary encoder library from PJRC, drop the main Encoder.cpp and 'utils' folder into your sketch folder and include the following source lines.

#define ENCODER_DO_NOT_USE_INTERRUPTS

#include "Encoder.h"
#include <Servo.h>

Encoder myEnc(7, 8);

Servo myservo;
long position = -999;
long srv = 0;
void setup() {                
  myservo.attach(5);
}

void loop() {
  long newPos = myEnc.read();
  if (newPos != position) {
    if (newPos < position) {
      srv += 1;
      if (srv > 180) srv = 180;
    } else {
      srv -= 1;
      if (srv < 0) srv = 0;
    }
    position = newPos;
    myservo.write(srv);    
  }
}

The code above will check if the rotary encoder has moved and, if it has, then check which direction and adjust the servo accordingly. Note that the servos will hum/jam if you try to turn them past any restrictions: i.e. once hooked to a turnout, the servo's movement will be limited and you should only move them as much as required... don't try and move them past the limits of the turnout. I'm quite sure that you will ruin either the servo or the turnout if you let it hum for too long past the movement of the switch.

What to do next?

Control your points based on timing? Or even based on track occupancy detection. Computerised turnout control will allow you to automate any movement over your layout. Of course, my current goal is to build a node for the OpenLCB project to control points via servos. This will need to store data, allow max/min settings per point, etc... but more as it gets built!

Tagged as: 1 Comment
17Feb/124

Persistent Data on the Arduino (EEPROM)

It's taken me a year to realise that you can actually store data at runtime on the Arduino and happily turn it off, expecting the data to still be there when you turn it on. By this, I don't mean the code you've uploaded; I mean the actual values you've created/calculated/determined whilst your code has been executing on the Arduino.

Actually, I lie... it hasn't taken a year to 'realise'... it's taken a year to actually need the ability to store information. It occurred to me, whilst looking at Don's OpenLCB railstars products, that they'd need to store everything they 'learn' as you set them up with controller nodes. All of my previous projects would've forgotten all settings once you disconnect the power!

Memory Types on the Arduino

After a little research, it turns out that Arduinos have three types of memory areas. These would be the flash, EEPROM and SRAM. The former is the location that all 'sketches' and other compiled program code go, therefore being the largest area. The EEPROM is, depending on your chip, an area around 1k to 4k in size for storing data to be persisted. Finally the SRAM is the 'running' area where data is stored during runtime of your code.

Memory Type ATMega168 ATMega328P ATmega1280 ATmega2560
Flash 16k 32k 128k 256k
SRAM 1k 2k 8k 8k
EEPROM 512 bytes 1k 4k 4k

So, as you can see, the more you pay for the microprocessor, the more space you get to play with. I have used the Arduino Mega 1280 for a while and had never used the space available in the EEPROM... what a waste. Now I'm tinkering with the Atmega328P and, as it shows, there's a lot less space available to play with. Fortunately, depending on how frugal you are with data storage, there's more than enough for creating our OpenLCB nodes.

Working with the EEPROM

Arduino 1.0 (and all previous versions) include the EEPROM Library. This library includes two calls, being read() and write(). For the Atmega328P, I'm able to store a byte in 1024 areas. This expands to 4096 areas for the Mega.

By the way, for time-critical apps, an EEPROM write takes 3.3 ms to complete.

NOTE: As the Arduino page warns, EEPROMs are only good for 100000 writes! Please only write/update your EEPROM areas sparingly and when absolutely required.

Efficient storage of Bits/Bytes

Depending on your requirements, you may want to be more efficient in the way you store certain values. We'll start with booleans: if you're lazy and wont need to store over 1024 booleans on an Atmega328p then you can simply check the boolean and store a '1' or '0' in any of the 1024 areas. Of course, if you need more, then you'd want to efficiently use the 8 bits per byte that you have available. As each of those 8 bits can be a '1' or a '0', you can then actually store 8 booleans in each byte. It's simply a matter of 'or'ing 8 booleans together and left-shifting to ensure you set the correct bit.

byte setBit(store, bit) { //bit 1 is right-most
      store |= (1 << (bit - 1)); //set bit 5 to '1'.
}

byte clearBit(store, bit) {
      store &= !(1 << (bit - 1));
}

bool getBit(store, bit) {
      byte b = (1 << (bit - 1));
      return (store & b);
}

Arduino has a good bit of information on BitMasks and BitMath for those interested.

Using PROGMEM to store 'known' data

So, as previously mentioned, the Flash area has the most space available. The Arduino comes with the PROGMEM library for storing variables in this area. Note that you cannot easily write to this at run-time (I haven't dug far enough to work out if you really can) ... the goal is to just store large data in the flash and use it from there at runtime rather than copying to your limited SRAM first.

Firstly, you need to select from a datatype below:

Data Type Description
prog_char a signed char (1 byte) -127 to 128
prog_uchar an unsigned char (1 byte) 0 to 255
prog_int16_t a signed int (2 bytes) -32,767 to 32,768
prog_uint16_t an unsigned int (2 bytes) 0 to 65,535
prog_int32_t a signed long (4 bytes) -2,147,483,648 to * 2,147,483,647.
prog_uint32_t an unsigned long (4 bytes) 0 to 4,294,967,295

Now, declare it in the PROGMEM 'space'. It seems that the Arduino devs recommended it to be stored as an array as you'd usually only use this space for large amount of data.
I've chosen the prog_uint16_t (note that this var size is a 'word'), the code below stores two of these values and then uses them during execution.

#include 
PROGMEM prog_uint16_t myValues[] = { 123, 456 };

int k; //counter? don't quite know what for.
int readValue1, readValue2;
void setup() {
      k = 0; //read the first word. 
      readValue1 = pgm_read_word_near(charSet + k);
      k = 1;
      readValue2 = pgm_read_word_near(charSet + k);
}

void loop() {
      //now you should probably do something with these values...
}

And that's it.. I hope this helps some of you to limit your SRAM requirements and also to store data for users each time your device is switched off!

14Feb/122

Building an Arduino from scratch

OK, OpenLCB is the new cool... to make 'nodes' for it, we need a skeleton Arduino to build from. This article will define how to go about this. The goal in the end is to have a prototype that can, via the Arduino IDE, have a sketch uploaded to do as you wish.

References

Firstly, a list of random links for random information on building your own Arduino:

Serial or USB?

I know the DB-9 Serial Port is archaic... needs an Interrupt Request (IRQ) assigned to it etc, etc... but there's something nostalgic about plugging in a chunky cable instead of a new USB-B connector. It's also cheaper and easier to use parts on hand and I had a few MAX232s sitting around.

To use USB you'll need an FTDI chip to convert the USB signal from your PC to the TTL protocol of which the Arduino understands. Note that there are also FTDI chips which convert USB to RS232, the serial protocol that allows devices (specifically your old dial-up modems) to communicate over serial cables. You don't want one of these as you'd then have to convert the RS232 again to TTL.

Which Microcontroller?

Good question! Your choice of microcontroller comes down to size and quantity of digital/analogue inputs and outputs. I decided on the Atmega328P as it has enough IO lines for what I need on each OpenLCB node; I'll produce more node types and have them specialise rather than one node that does everything from one board. They're cheap on eBay and they are very similar to the MCU used in the Arduino Duemilanove (5 analogue and ~13 digital IOs.)

Schematic

You'll find schematics all over the web, although none seem to agree on what components are the exact bare-minimum for an Arduino... it actually makes it quite difficult to know exactly what you need. Below is a solid basis to start your Arduino-based invention, containing everything required to program over the serial port.

BareBones

Building the prototype

There was not much out of the ordinary on the breadboarding of this project. It's actually quite simple and the standard rules of check, check and double-check your wire placement, IC pin numbers, etc... as you go is imperative. The serial cable connection on this project, due to the cable being rigid, can cause issues of dragging the board around. I'd recommend to seat connection and cable first and secure everything to your workbench... You'll then prevent your breadboard from upending when the serial cable chooses to move around.

Programming from the IDE

The MCU chosen is the Atmega328P and, as mentioned, it's closest match is the "Arduino Duemilanove w/ Atmega328". Once everything was connected I attempted to flash using this board, selected in the Arduino GUI, but it didn't work straight away. The initial error was that the chip device ID didn't match. It turns out that the Atmega328 has as a different code to the Atmega328P (something to do with Pico Power.) I had to hack the 'avrdude.conf' file buried in the arduino folder and then it just worked.

Before I knew it I had the servo library in and a mini servo from eBay controlling a Peco point on my layout.

Re-programming an Atmega MCU

I accidentally learnt how to do this... At one point I'd put the MCU in the wrong way around and expected that I'd killed it. The circuit then failed to work after numerous attempts with the TX and/or RX lights constantly lit. It wouldn't accept sketches and I therefore thought the best bet was to re-flash the bootloader + program.

It turns out that my Arduino Mega can be used as an In-Serial Programmer and so I set this up. This can be selected via the "Arduino ISP" option in the "Programmer" menu under the new Arduino 1.0 software.

ArduinoMegaISP
(Note that the capacitor from +5v to RESET is a 100n non-polarised.)

After the re-flash and a reconfigure of the MAX232 in/outs (the schematic above is the final version that now works perfectly) it all just worked again... for one of the MCUs anyway. The second simply refused to receive sketches; the RX light would not turn off. I took this as a fundamental failure in the chip (I blame my bread-boarding skills) and therefore tried something sneaky: I copied the entire code off the MCU that worked and flashed it to the 'broken' one with avrdude and it's associated avrdude-GUI [direct download link]. It turns out this worked... the chip now did what the previous one did... but in the MAX232 breadboard it still would not accept a new sketch from the Arduino GUI. I attempted to find other methods to 'completely erase/reset' an Atmega, but I couldn't get it to work and so just accepted it's fate of having to copy it's brother.

9Feb/120

OpenLCB: Controlling Your Model Railroad

It's been a while since anything remotely home-brew/electronic for model railways has appeared on this blog... but this is about to change. It's now time to try a third method of electronic control after previous attempts of controlling via DCC Boosters and then Arduino Microprocessors.

Don E. Goodman-Wilson, who you would've seen commenting on a few of my posts here regarding DCC and Arduino, has gone all-out and started production of some pretty amazing technology. He's started a company called railstars which is currently providing DCC boosters, central units and throttles. Not only are these perfectly marketed and designed, they also now incorporate a new concept known as OpenLCB.

OpenLCB

OpenLCB is an initiative by the OpenLCB team to define a network (protocol and required hardware) to control pretty much everything on your model railway. The end result is layout control and automation which is easy to install, configure and extend. As of the 9th of February 2012, there are motions for NMRA to work closely with the OpenLCB project in a venture named NMRAnet... stay tuned for more information on that!

It should be noted that the OpenLCB team is still in the early stages of design work and, although prototypes have been built and tested, there is a lot of work to do to get final products to market.

The basic concept consists of a control bus with nodes located along a network bus, terminated at each end. Each node has it's own processor that can create and respond to events. Events can be anything from controlling accessories and trains on your layout to sending informational messages to remote displays. With this, you can have a node controlling points around your layout, a node controlling trains indirectly via DCC and a final node acting as a control panel with buttons and switches configured to send specific events.

In case you were wondering, the OpenLCB venture has no intention to interact with DCC. Other parties, such as Don with his railstars products, are currently bringing DCC in as an option, but do not intend to outright replace it! Don's efforts with railstars OpenLCB components will allow DCC commands to be transmitted over the OpenLCB protocol allowing each node on the network to respond appropriately. A lot of opportunities will open up with this as you will then be able to use the OpenLCB network to transmit DCC information, allowing completely separate DCC blocks on your layout.

You can find a lot more information about OpenLCB here at their 'First Look' page.

I want to control my DC Layout?

There are currently no nodes designed for use as standard DC throttles; fortunately, controlling DC layouts via the OpenLCB bus should be completely feasible! As per my previous articles on Arduino control of model railways, a simple PWM motor controller is all that is required to supply a model railway with the appropriate power levels. Since OpenLCB is also very compatible with Arduino, it would also therefore be quite practical to transform an Arduino+Motor Controller into an OpenLCB 'node'. This would mean that any 'controller node' could then send 'events' to the 'pwm node' which would then act accordingly... be that 'supply 50% throttle'.

I'd spoken to Don on this concept and he indicated that the OpenLCB key concept was to control 'Trains' rather than 'Blocks'. This is the same as DCC... as you supply the same power+data to all rails on your layout and the locomotives respond only when called. The issue then with DC control is that trains, sitting in blocks, will respond as soon as you apply power. One answer would be to have detection circuits around the layout which would follow movement and assume where each train was. Another would be to have the 'pwm nodes' act simply as 'accessories' that supply power to each block. This concept is still under discussion.

What's Next...

I really do want to get into development and testing of the OpenLCB system. Fortunately the whole lot is open source and one can even use standard Arduinos as 'nodes' based on the code available in the OpenLCB repository. Once I have enough components I'll be building and connecting a few nodes to test the communications functionality. After that I'll go ahead and design a block-controller and pwm node. At the least this will allow me to prototype some ideas whilst controlling my latest n-scale-in-a-table layout.

The OpenLCB project will also shortly be offering a 'dev kit' to parties interested in developing for the OpenLCB project. It will have enough components to create a local network able to control a layout. You can find out more information on the Japanese Modelling & Japan Rail Enthusiasts Forum.

I intend on keeping everyone updated as I tackle the concept of DC in the newly formed OpenLCB realm.