Subscribe via RSS
4Apr/140

Ex-Japanese Rolling Stock in Manila

Manila was nothing like I'd expected... I suppose I had no idea what to expect, but after hearing of kidnappings, dangerous weather and chaotic traffic, I hadn't painted the best picture. Turns out it's all crap. I mean, they've had a few blows from Mother Nature in the past, but this hasn't hampered what is a bustling and vibrant city.

Apart from shopping for cheap electronic goods, I had a goal to check out as many of their modes of transport as possible. Google helped me find the Philippine National Railways ... and this then lead me down a rabbit-warren of information. It turns out the railway has been restarted many times. It used to expand all over the country, but squatters came in each time the railway closed/suspended lines. This has now lead to difficulties reclaiming the right-of-way and resuming the services. The result, currently, is that there are a limited number of services to catch and I chose one that worked out nicely when transferring from other modes of transport.

Another reason to check out PNR was that they utilise hand-me-downs from Japan. Quoting the PNR Wikipedia Page:

Surplus sleeper coaches from Japan Railways were recently acquired by PNR, and were delivered on November 2010. More used rolling stock from Japan Railways was recently acquired by PNR, and arrived in 2011 which included some 203-series EMU, Kiha 52 and Kogane Train (Kiha 59).

Judging by Google Maps and Wikipedia's page on Rail Transport in the Philippines, I worked out a path that would allow me to ride a few of the 'light rail' systems and the PNR. This meant catching the LRT from EDSA Station to Carrieda Station and then walking to Tutuban. It turns out the area from Carriedo to Tutuban is Chinatown and therefore full of markets. From Tutuban, the PNR was traversed to end up at PNR EDSA Station. This was a 45min suburban route and allowed viewing of the real Manila. A quick walk to MRT-3 Magallanes Station then allowed a trip north to Santolan Station near Greenhills shopping center for some much needed retail therapy.

LRT-1

The Manila Light Rail Transit System - Line 1 is the first of the LRTs in Manilla and runs from Baclaran to North Avenue.

DSC02252

DSC02261 DSC02266 DSC02283

DSC02267

LRT-2

Walking north along Rizalo Avenue until Recto Avenue allowed the viewing of the end of LRT-2. The weather was a little temperamental and (especially at night) the high overhead railway gave the whole neighbourhood a Blade Runner feel.

DSC02293

I couldn't quite work out if there were plans for expansion or if the track was simply extended to allow for a turn-back.

DSC02298

PNR Tutuban Station

DSC02314 DSC02315 DSC02320

PNR EDSA Station

A quick jog to the bridge let me get a few departing photos of the service I arrived on.

DSC02332

DSC02342

PNR 203-series KIHA Rollingstock

Ex Japanese Railway stock. Towed along by a General Electic 900-Class diesel (build in GE Montreal, Canada.) I love it that they've left the pantographs on the roof.

DSC02321 DSC02335 DSC02338
DSC02324
DSC02328

MRT-3

Do note to be mindful of the station staff. Don't take direct photos of them and if they tell you not to take photos, do respect their wishes!

DSC02204

DSC02207 DSC02345 DSC02348

DSC02351

...must get back there...

20Mar/140

Table Railway – Continued

It's been green for a while.. but I thought I might as well provide a long-overdue update. The table layout has received a fresh coating of grass and it's in quite a reasonable state after moving house. It coped with being held upright through doorways and thrown around in the back of a car... so it seems my process of painting, gluing and spraying ballast and foliage worked well.

Painting the base scenery

The last thing one needs is white plaster showing through the scenery base. It really does ruin all of your hard work very quickly. To prevent this I coated the entire base with an appropriately-coloured paint. Japan's scenery is ultra-lush, so a dark green here will work well.

DSC01604 DSC01602 DSC01609

Adding the grass

Adding grass to the green paint brings it to life. Texture is the key here and un-even-ness is to be achieved. Don't be scared to glue layer upon layer upon layer. I've used a glue/water ratio of around 3-10 to make sure everything sticks. It's a little thicker than you'll need for a layout that won't be thrown around as much as this one will.

DSC01611 DSC01614 DSC01643

Sinking and Ballasting the track

I'd done this before on my previous railway and the effect is much more realistic. Although the plastic ballast that comes with Unitrack isn't ugly, it easily removes from the realism of a layout. To get around this I've cut a trench for the track and glued ballast down the sides. Be careful not to completely cover the track with glue...

DSC01619 DSC01621 DSC01616
DSC01635 DSC01644 DSC01647
DSC01735 DSC01728 DSC01722

The best part? My 300 Series Shinkansen bolted round the loops at full-tilt... I really wasn't expecting it to cope. It's about time to add a city.

12Dec/130

Yet another layout…

It was about time to fill the table I'd bought from an op-shop a long time ago. I'd attempted a layout for this prior using the Arduino and CAN Bus to control it... but I somehow lost interest and demolished it. Hence we begin with the 810mm(Squared) Table Layout Version 2.0.

The space was relatively small and, thanks to my previous attempt, I knew there wasn't going to be much more than a loop-the-loop. The whole reason for building this was to run my 300 Series Shinkasen and that meant wide curves and wide clearances. For some reason I then decided that a loop-the-loop was boring and that I could fit a loop-the-loop-the-loop in.

I set to work on Anyrail.Net and found that a triple-loop was going to be tight. Unitrack had enough different radii curves to get the loops in, but I'd have to be using the tightest available... not too good for a 7-car Shinkansen. So... I started breaking the mould and making everything not-quite-fit together in the layout software. This meant slightly wider curves but potentially dangerous track joins where I would be 'stretching' the limits of unitrack to fit. Fortunately it turns out to be pretty forgiving.

I went to a not-so-local hobby store and found they had a HUGE selection of Japanese stock. I had my list printed from Anyrail and went about collecting. I also got some Walthers gradient foam for my crazy layout.

DSC01569 DSC01570 DSC01571

After a little fiddling the track was laid out and temporarily elevated into place. It all worked... but was a squeeze. With nothing stuck down you'd attempt to get track to connect in one spot and it'd fall out of alignment in another... I don't really recommend jamming Unitrack together in odd formations!

DSC01574 DSC01576 DSC01578

The result was a successful session of test running with all of the stock I could find!

21Nov/132

Sleeper Trains to be retired by 2015

This was sad news... Japan has a brilliant selection of sleeper trains and, although they have been phasing a lot of them out over the years, to hear that they're getting rid of the top-notch services was heart-breaking.

My experiences on sleeper trains in Japan has always been fantastic. The staff are amazing, dedicated to their work and more than happy to help out a non-fluent Japanese speaker. Currently my checklist includes the Twilight Express, Nihonkai, Kitaguni and Hokutosei, but I'm think I need to get the others ticked before they're scrapped.

So, here's the original article and here's the english version. Noteable points:

The Japanese Fiscal Year runs from April of said year to March 31st of the next. So 'End of Fiscal 2014' would be March 31st 2015 and 'End of Fiscal 2015' would be March 31st 2016.

Note that the Sunrise Seto and Sunrise Izumo will continue to run.

I can understand that they're scrapping the older (although very well maintained) services ... but it's astonishing that JR East is scrapping the services that have just received brand new locomotives to haul them! The EF510-500 Series locomotives were only introduced in 2010 to pull both the Casseiopeia and Hokutosei services.

Something random to check out: Here's a link to EF81-104 (one of the Twilight Express Locomotives) being chopped up. Turns out that's the one I travelled behind back in July 2009. RIP.

Pictures of the Sleeper Trains

Nihon-Kai

NihonKai pauses at ShinOsaka Nihonkai at Osaka Nihonkai Headmark
Nihonkai B-Class Nihonkai at Shinosaka Nihonkai paused Goodbye Nihonkai

Twilight Express

The Twilight Express at Shin Osaka The Twilight Express pulls into ShinOsaka Twilight Express en-route to ShinOsaka
Twilight Express heads to Osaka Station Twilight Express enters Higashi-Muroran Twilight Express A-Class
My room on the Twilight Express Twilight Express Salon du Nord Twilight Express dirty after travel from Sapporo to Tsuruga
Twilight Express EF81 104 joins (passing Thunderbird) Twilight Express EF81 104

Hokutosei

Hokutosei arrives DD51s on Hokutosei Grand Chariot in Hokutosei
Hokutosei at Fukushima Hokutosei at Fukushima

Kitaguni

Kitaguni at Osaka Station Kitaguni at Osaka Station Kitaguni at Osaka Station
Internal view from top bunk of Kitaguni External view from top bunk of Kitaguni Kitaguni at Naoetsu

Unfortunately, I've never seen the Casseiopeia in the flesh... Will need to do so before they cut that up too.

16Oct/130

Kato Unitrack vs. Moisture/Humidity from setting plaster

It probably should've been obvious, but I've just found out the hard way of the effects of drying plaster (Woodland Scenics Plaster Cloth) and Kato Unitrack when kept in a small enclosed space.

DSC01599

I've been building a new layout recently in a coffee table I acquired from a secondhand store. It's around 80x80cm and fits a nice loop-the-loop-the-loop layout.

DSC01588 DSC01590 DSC01591

After planning and purchasing the track, I started to build up the scenery. Once the foam was down, the plaster was laid. Due to wanting to be neat, I returned the setting/drying scenery back into the table each night to dry. I noticed on the second day that the plaster hadn't really dried that much, so I chose not to run any locos (there had been a C50 steamer sitting in the table overnight too!.)
The next day I then noticed that the track had a strange tinge to it. I attempted to run the steamer, but the performance was terrible (although this was second hand and hadn't run 100% in the first place.) Another locomotive didn't do much better.

The cause was obvious on closer inspection...
(First image: notice that left track is shiny and right is dull. Second image: see mould.)

20131011 085239

20131011 085153

After 'phoning a friend' on the JNS Forums, a few possible options came up... the most probably one being the chemicals used in the plaster cloth. These could well have hung around inside the glass table whilst the plaster was drying and adversely affected the track.

I'm still in the process of cleaning it all up (a quick once-over with a cleaning block worked fine)... the sides of the rails are still tarnished. Currently alcohol-wipes are doing the best job of cleaning this mess up!

7Jun/130

Misfiring AW11 4AGZE Supercharger

Just got my car back from the mechanic... he does a really good job, but I'll leave the name out of it for all intents and purposes.
Turns out I'd done too much off-roading in my poor little MR2 and had filled the engine bay with dirt/dust/grime. Mechanic took the liberty to wash it all out... looks great!

Unfortunately, it didn't run so good anymore.

Symptom

Upon acceleration, the tacho would get to 1,500rpm and then it felt like the rev-limiter kicked in. The engine would burst about 100rpm, then drop back to 1,500, then back up and down, on and off.

It felt terrible to drive... if you timed it right, you could stop accelerating at 1,459rpm then gun it and it'd jump to 2,000rpm and continue... BUT THE SUPERCHARGER LIGHT STAYED OFF.

Something had gotten into the wiring and was going haywire. Stopped the car, got a torch and checked everything. All was connected apart from a cracked up vacuum tube. Thought it was the culprit, but upon sealing the connection it was still failing.

20130607 174807

I could see the supercharger clutch engaging when I feathered the throttle, but it would pop back out just as quick when the engine dropped revs.

So... resorted to Google and found this: http://forums.club4ag.com/zerothread?id=89527
Reading the second post, the user indicates that the TPS was causing his engine to start/fail/start/fail... same symptom as mine.

So, went back to the engine and popped off the TPS connector.

20130607 181739

FULL OF WATER.

Solution

Dried it up and ... the MR2 is back to bat-out-of-hell status... I love my little green Supercharger light!

I'm only posting this so that other people can find this information easier.

22Aug/121

Eizan Dentetsu (Eiden) Kyoto, Japan

Was just travelling via Google Earth and decided to check out North Kyoto. The Eizan Electric Railway runs one and two-car EMUs from Demachiyanagi Station up to either Mount Kurama or Mount Hiei. I haven't travelled up to Hiei-san yet, but I do love the trains that travel up to Kurama.

Usually google maps satellite photography is taken at random hours and doesn't capture anything interesting, but it seems this time they have captured a Kirara 900 Series about to pass an 800 Series at Ninose Station.

Just thought I'd take a snapshot and record it... it's a beautiful area of Kyoto and everyone must visit at least once!

Meanwhile... here's a few old shots of my visits there including these two EMUs.

Eizan Dentetsu Kirara 900 at Iwakura Station Kirara 900 at Iwakura Station
Kirara 900 paused at Demachiyanagi Station Kirara 900 Kirara 900
Eizan Dentetsu 800 Series Eizan Dentetsu Yards Eizan Dentetsu
Kirara 900 Eizan 800 Series Eizan Dentetsu - Demachiyanagi
Eizan Dentetsu Colourful Train Eizan Dentetsu Kirara 900 Maple Orange Eizan Dentetsu Kirara 900 Maple Red_001
Eizan Dentetsu Kirara 900 Maple Red_002

I could go on about this railway forever .... go and visit it now.

15Apr/124

First CAN Node: LEDs and Sensors

Despite my layout being tiny, I've decided that my layout will need two nodes. One will do the sensing of trains + management of LEDs and the other will control the throttle, points and isolation. I've now finished the building and wiring of the first node. One note for below: I've used Frame IDs to differentiate the messages across the network; unique numbers are used to define the message type and each node will have filters applied to only receive nodes that are specifically for them.

Detecting Trains

Train sensing has been implemented via light sensors. I needed 24 sensors around the track and decided that multiplexing was the only way to read them all. 3x 4051 ICs came in handy and were configured as per the schematic below. You can find a better explanation on light sensor implementation in the article here.

LED+Sensors CAN Node

I've simplified the code for reading the sensors as I'll ensure all of them have sufficient lighting and therefore 'stable' values. The circuit waits a second after power-up to read the initial sensor values. It then marks each one as 'active' if the value is greater than 20 'points' above the initial value. If a reading comes in lower then the initially read value then that value is lowered accordingly. I've also created a special command (frame id: 0x333) to allow a reset of all sensor values (specifically if ambient light proves a problem.)

The node sends out the sensor values and then pauses 100ms. The CAN packet with sensor data has the frame ID 0x111 and then fills three bytes with the 24 sensors states. A '1' is used for occupied and a '0' for vacant. The message will be received by any node on the network with the appropriate filters set.

Controlling LEDs

To control the LEDs I've re-used the previous MAX7219 ICs that I had lying around. There's a much more in-depth article here on how to use this chip. The MAX7219 requires three wires to the Arduino... but I'd already used up all the digital pins. Fortunately you can use the Analog pins just as easily.

LEDs on new layout

LEDs on new layout

As the MAX7219 can receive bytes per row to control the LEDs I simply pass through the data from the CAN bus when appropriate. It would only take one CAN message as they contain 8 bytes of data. The Frame ID for this message is 0x222. You can see that I'm being pretty wasteful with Frame IDs but, fortunately, I don't have many specific messages to send.

Completed Sensor+LED Node

So, it was soldered and wired... quite a mess really. The CAN Controller was on-board but there was no 'direct communications' interface to the PC. This meant I had to pop the chip out (atmega328) each time I wanted to change the code... tedious and dangerous!! I managed to bend pins and confuse myself every now and then. Don't do this at home!

SENSOR+LED Node

Controller Node

I've got a third node hooked up to the computer via my trusty old Arduino Mega. This node transmits data from the CAN Bus to the PC via the standard serial-over-USB connection. A .NET application then shows the layout and allows you to interact with the LEDs and sensors accordingly. It shows the status of the sensors at any point in time and allows you to create rules in a sequence which can determine the speed and direction of the train.

The physical design of this node is simply the Arduino Mega plus the CAN schematic as seen in my previous blog post on implementing the MCP2515 controller.

  if (CAN.buffer0DataWaiting()) {
    CAN.readDATA_ff_0(&length,frame_data,&frame_id, &ext, &filter);
    Serial.write("X");
    Serial.write(frame_data[0]);
    Serial.write(frame_data[1]);
    Serial.write(frame_data[2]);
  }
  
  while (Serial.available() >= 9) {
    // read the incoming byte:
    b = Serial.read();
    if (b == 'L') {
      for (int i = 0; i < 8; i++) frame_data[i] = Serial.read();
      frame_id = 0x222;
      CAN.load_ff_0(8, &frame_id, frame_data, false);
    } else if (b == 'D') {
      for (int i = 0; i < 8; i++) frame_data[i] = Serial.read();
      frame_id = 0x666;
      CAN.load_ff_0(8, &frame_id, frame_data, false);
      //printBuf(frame_id, frame_data);
    } else if (b == 'R') {
      for (int i = 0; i < 8; i++) frame_data[i] = Serial.read();
      frame_id = 0x333;
      CAN.load_ff_0(8, &frame_id, frame_data, false);      
    } else if (b == 'P') {
      for (int i = 0; i < 8; i++) frame_data[i] = Serial.read();
      frame_id = 0x777;
      CAN.load_ff_0(8, &frame_id, frame_data, false);            
    }
  }

LED+Sensor Node Code

The sensor code consisted of an initial sensor read and then constant sensor checking. The sensors were then determined to be either 'occupied' or 'vacant' and then the data was sent. There are no 'smarts' here... the sensor data is simply hammered over the network.

The receive buffers are also checked for a Frame ID of 0x222 or 0x333. The filters are set appropriately to ensure that only these messages trigger the interrupts. The LED data is sent to the MAX7219 controller on the 0x222 message and the sensors are reset on 0x333.

void loop() {	
  send_data[0] = 0;
  send_data[1] = 0;
  send_data[2] = 0;
  for (sens = 0; sens < 8; sens++) {
    setOutputBit(sens);
    sensor_read = readInput(0);
    if (sensor_read < sensor[sens]) sensor[sens] = sensor_read;
    send_data[0] |= (((20 + sensor[sens])      <= sensor_read) << sens);
    sensor_read = readInput(1);
    if (sensor_read < sensor[sens]) sensor[sens] = sensor_read;
    send_data[1] |= (((20 + sensor[sens + 8])  <= sensor_read) << sens);
    sensor_read = readInput(2);
    if (sensor_read < sensor[sens]) sensor[sens] = sensor_read;
    send_data[2] |= (((20 + sensor[sens + 16]) <= sensor_read) << sens);
  }
  frame_id = 0x111;
  CAN.load_ff_0(3, &frame_id, send_data, false);

  frame_data[0] = 0;

  data1 = CAN.buffer0DataWaiting();
  data2 = CAN.buffer1DataWaiting();

  if (data1 || data2) {
    if (data1) CAN.readDATA_ff_0(&length, frame_data, &frame_id, &ext, &filter);
    if (data2) CAN.readDATA_ff_1(&length, frame_data, &frame_id, &ext, &filter);

    if (frame_id == 0x222) {
      for (int i = 0; i < 8; i++) lcl.setRow(0, i, frame_data[i]);
    } else if (frame_id == 0x333) {
      for (sens = 0; sens < 8; sens++) {
        setOutputBit(sens);
        sensor[sens] = readInput(0);
        sensor[sens + 8] = readInput(1);
        sensor[sens + 16] = readInput(2);
      }   
    }
 }
 delay(100);
}

Controller Node Code

The controller was the middle-man for translating PC code to the CAN bus and vice-versa. Sensor data from the CAN bus was converted to serial data with a leading 'X' byte. On the contrary LED commands were received from the PC with a leading 'L' byte. The following 8 bytes were sent as a single message onto the CAN bus once this character was seen.

    using System.IO.Ports;
    SerialPort _serialPort;

    private void SetupComPort() {
        _serialPort = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);
        _serialPort.Handshake = Handshake.None;
        _serialPort.DataReceived += new SerialDataReceivedEventHandler(HandleData);
        _serialPort.Open();
    }

    private void cb_CheckedChanged(object sender, EventArgs e)
    {
        Byte[] bytesToSend = new Byte[9];
        bytesToSend[0] = (byte)'L';
        for (int i = 0; i < 8; i++) {
            for (int z = 0; z < 8; z++) bytesToSend[i+1] |= (byte)((LEDs[(i*8) + z].Checked ? 1 : 0) << z);
        }
        _serialPort.Write(bytesToSend, 0, 9);
    }

    private void UpdateSpeed()
    {
        Byte[] bytesToSend = new Byte[9];
        bytesToSend[0] = (byte)'D';
        bytesToSend[1] = (byte)(direction ? 1 : 0);
        bytesToSend[2] = (byte)hSpeed.Value;
        bytesToSend[3] = (byte)points[0]; //for loop? probably should.
        bytesToSend[4] = (byte)points[1];
        bytesToSend[5] = (byte)points[2];
        bytesToSend[6] = (byte)points[3];
        bytesToSend[7] = (byte)points[4];
        bytesToSend[8] = (byte)points[5];
        _serialPort.Write(bytesToSend, 0, 9);
        Console.WriteLine("Sent: D" + bytesToSend[1] + "-" + bytesToSend[2]);
    }

Notes learnt from hooking all this up

  • CAN Filters work, but you can still see messages
    On the CAN controller you can set filters to only allow specific messages through. It actually turns out that, regardless of the filter being set, if you constantly poll either buffer for a message that, if there has been a message sent, it will be available and can be received from the network. Filters simply prevent the message from triggering the 'message waiting in buffer' interrupts. So, the best bet is that you check for a message in a buffer first rather than simply constantly attempt to receive messages. Let the CAN IC tell you that a message is waiting!
  • You can easily spam a CAN network
    Sending too many messages over the network will delay or prevent transmission of other nodes' messages. One node can send too many messages over the network preventing another from successfully getting messages into a third nodes' buffers. It's all down to how quickly the third node can receive the messages. If both receiving buffers are full then the message will fly on past and not be seen.
  • Inserting and removing ICs
    PLEASE only do this when you need to and do this with care! Lever them out from both ends and DO NOT just use your fingers. Insert them evenly as well. It's too damn easy to destroy these delicate and expensive components.

...next will be a node to control the thottle, isolated blocks and point-servos.

3Mar/1274

Arduino + Controller Area Network (CAN)

Stolen from Wikipedia: The CAN (controller area network) bus is a vehicle bus standard designed to allow microcontrollers and devices to communicate with each other within a vehicle without a host computer. This bus standard includes it's own messaging protocol for communications between nodes on the network.

One of the most popular uses for this technology is in the automobile where the network provides a communication channel between the ECU, Transmission, airbags, braking systems, etc...

As you'll have noticed, I've recently been investigating OpenLCB which uses this technology for communications between its nodes on a model railway. The article below will show how the CAN bus can be incorporated with an Arduino to allow communications between different microcontrollers/nodes on your network.

Topography

The OpenLCB project dictates that the CAN bus implemented via their standards must not be in the form of a ring. When I initially saw the Railstars:io prototype I expected that a ring would be required; the prototype shows an 'in' and 'out' communications port. Little did I realise that, upon closer inspection, the io could act as a termination point for the CAN bus that it connects to. We therefore will implement a layout as follows on our railways...

can-setup

The OpenLCB project will use standard RJ45 cables (i.e. ethernet cables) due to the requirement of twisted pair technology to guarantee data transmission.

Interfacing with the Arduino via SPI

The Serial Peripheral Interface is a communication standard used to provide communications between integrated circuits. The Arduino has an SPI library and therefore allows us to easily communicate to the CAN interface.

We'll be using the MCP2515 and MCP2551 integrated circuits to transmit/receive our data from SPI onto the CAN bus. This setup has been done before in Sparkfun's CAN-BUS Shield, the CAN-CAN, the Universal CAN Library (Universelle CAN Bibliothek) and the CANduino. The CAN-CAN Schematic and Sparkfun's schematics provide a great reference point for hooking the whole lot up. Of course, you could also just grab their shield if you don't want to build everything yourself.

Schematic

The image below should be pretty self-explanatory. See the next section for the exact pins for the SPI interface. RESET and INT can go to any digital pin.

MCP2515

Building, troubleshooting and talking to one's self

I built two at the same time to be able to test node-to-node communications. There was nothing overly tricky in the construction... I just chose to use relatively tight pcb prototyping boards which didn't leave much room for error. The above schematic was followed and the devices were hooked up to my Arduino(s).

Initial board

I tried the loopback test from the canduino project and got random responses... the data going in was not the same as the data coming back. I started the debugging process after letting out a little sigh.

It turns out that, on all Arduinos, the SPI interface can only be used on specific pins:

SPI Pin Arduino Mega Smaller Arduinos (168,328)
SS/CS 53 10
MOSI 51 11
MISO 50 12
SCK 52 13

Note that the CAN library also then required a change as the pins in it are hard-coded to a 168/328 Arduino and wouldn't have ever worked with the Mega. You can find my fix for this down below.

I finally had responses after the pins were in the correct locations... but not the responses I wanted to see; I looked at the crystals next. My Arduinos all used 16.000MHZ crystals, but my local hobby shop only had 4mhz, 8mhz or 20mhz. This concerned me and I google'd and google'd to work out if, in the same circuit, multiple ICs could be driven off different oscillation rates. I decided that, since the 'SPI' interface had it's own 'bus rate' and the CAN bus also had it's own 'bus rate' that the crystals therefore did not affect these speeds. Also, the Arduino would dictate the speed of the SPI interface since it was the master (and slower.) I therefore bought 20mhz crystals for the MCP2515 chips, with my fingers crossed. Of course, this could have been the next issue.

Fortunately, from my previous attempts at barebones Arduinos, I had a 16mhz crystal on-hand. I swapped out the 2 capacitors and the 20mhz for the 16mhz and hooked it all up again. Nothing...

So, I then whipped out my Atmega328p 'barebones' Arduino and hooked it up to that. The SPI pins were different, as per above, so I made sure they were correct... and the CAN library from the canduino source had to be modified back to the required pins. After putting it all together I checked the serial output in the Arduino Serial Monitor and saw the correct response. The loop back test was working!

WTH... there was no reason that it should... except that maybe the SPI interface on my Mega was fried? I then, just because I hate caution and feed it to the wind all the time, swapped the 20mhz crystal and capacitors back in. It STILL WORKED! Good... I could keep the 16mhz for my other barebones Arduino.

So... with a known-working CAN node, I swapped it back onto the Mega. WTH... it worked. I now had my CAN nodes (by this time the other node built was also functioning thanks to it's guinea pig brother) talking to themselves. It was time to get them to talk to each other!

Talking in the CAN

Hah... there's an unwritten rule to not talk whilst in the toilet, but in our situation we'll make an exception. From the start I'd built the bus and was just itching to get the damn thing transmitting messages. The setup was simple: terminal blocks were used at junctions and standard dodgy speaker wire was the main network cable. 120ohm resistors were the terminators as per the standard CAN specification.

Terminators One node setup

The only real issues here were the source code (or my lack of knowledge of how it worked) and the usage of crystals. Yes, they came back to haunt me... it turns out that, whilst I had the loopback going, I'd left a 16mhz on one node and a 20mhz on the other. In the source code you specify the 'CAN Bus Speed' which, of course, was configured the same on both Arduinos. The actual issue was that, when the Arduino told the CAN controller to run at bus speed '1000', the CAN nodes did this but, due to their differing crystals, their calculations of what '1000' meant were incorrect! Since one was running 4mhz faster (20mhz vs. 16mhz) it must've been communicating on the bus at a different rate and therefore confusing the hell out of the other node.

The next issue was the code... I looked over it and thought that each node would take turns in transmitting and recieving... I was wrong: you had to actually set one as the sender and one as the receiver. I configured this based on the Arduino class (by #IFDEF just as the SPI pins were configured) and then had communication! My nodes were live and functional!

Communicating More communications

RX/TX LEDs

The final step was wanting RX/TX activity LEDs. It turns out that the MCP2515 supports this, but you need to do a little configuration. If you view the datasheet at microchip you'll note that the RXnBF Pins can be configured as 'nothing', 'general outputs' or 'low on RX buffer full'. I was hoping that obe would be 'high on TX buffer', but no such luck. It actually turns out that the chip has two RX buffers and therefore two RXnBF Pins. LEDs attached to these pins will, once configured as per below, illuminate once the associated RX buffer is full. If you ever see both full then you might be losing data?

RX TX LEDs

Second option: It turns out that in the MCP2515 Demo Board PDF that you can just put two LEDs on the RXCAN(1)/TXCAN(2) pins of the MCP2515.

Third option: It seems that you can also configure the INT pin to fire on TX buffer 'emptying'. You'll have to disable the pin firing on all other error situations to do this. By default, this pin is usually pulled 'high' by the MCU, so you must either disconnect that, use a resistor between the MCU and MCP2515 or just pull the pin high normally via a pullup resistor and then connect an LED in parallel with this to the INT pin. I imagine that in more complicated scenarios you'd actually want to know once the MCP2515 has generated an interrupt.

IDs: Standard or Extended?

All messages sent on the network have a 'frame id' of either (standard mode) 11-bits or (extended mode) 29-bits. It must be understood that this 'frame id' does not actually identify a node! It is simply an identifier for the actual message being transmitted. This identifier can contain any form of information you want; the bits are yours to play with.

One main use for this identifier is to record the intended recipient of the message you are sending. Depending on the amount of nodes in your network (don't forget to think of future scalability!) you can use anywhere up to 29-bits to uniquely identify them. Of course, this identifier could just be used to identify the 'class' of node and then you could put further data in the message to indicate the exact recipient. You could also go the other way and only use a portion of the identifier for the recipient code and have the rest for other uses.

The MCP2515 has in-built filtering to work on the identifier of incoming messages. If you're only using standard mode then the filters also apply to the first two bytes of the message. The filtering works inside the MCP2515 and therefore doesn't saturate your SPI link as the messages are stopped prior to being put into the RX buffer.

MCP2515 Modes

There's 5 modes that you can set the MCP2515 into. These are as follows:

Mode Description
Configuration This mode allows the developer to update settings. The following settings require this mode to be set first: CNF1,CNF2,CNF3,TXRTSCTRL,MASKS,FILTERS.
Normal Standard operating mode. RXM0 and RXM1 per buffer do not apply in this mode. All filters and Masks do apply.
Sleep Low-power mode. SPI remains active, and the MCP2515 will wake up if there is activity on the CAN bus if the WAKIE/WAKIF interrupts are enabled. After woken, the chip will be in Listen Mode.
Listen Make sure there are two or more nodes on the CAN bus. Whilst in Listen mode, the chip won't output a thing, so if another chip sends a message then the message will constantly circulate on the bus if there is no other node to 'ACK' it. Another node must be there to stop the message transmission. RXM0 and RXM1 per buffer apply in this mode and the masks/filters also do too.
Loopback All messages sent are returned via the RX buffers, filtered first. No messages are sent out over the CAN. No messages are received from the CAN.

Buffers

It needs to be noted that the MCP2515 has two receiving buffers known as Buffer 0 and Buffer 1. Messages will go to the first if it's free (and not filtered) and then the second. Filters can be set on both, but the second buffer is much more flexible with four filters.

I don't know the exact reason for the MCP2515 containing two buffers, and then why one is more flexibile than the other, but in the end it's up to the developer as to how to use them. Filters can be configured to allow specific message 'types' on each buffer. This then means that, for example, the developer could configure the second buffer to only allow emergency messages such as 'STOP THAT TRAIN' whilst the first deals with the standard communications.

Filtering CAN Messages via the MCP2515

With the base configuration, an MCP2515 controller will accept any messages sent across the network. This therefore means that your nodes need to filter each individual message coming across the bus to determine if they themselves are the intended recipient. Fortunately the MCP2515 has in-built configurable filtering to allow only messages intended for the node to be passed through its RX buffers.

The MCP2515 has 6 configurable filters with 2 masks. The first mask works in conjunction with the first two filters and applies to the first receiving buffer whereas the second mask works with the final 4 filters and works with the second receiving buffer. Filtering is enabled when any bit of a mask is '1' and disabled when the masks are completely zero'd out.

You may be wondering why both a mask and a filter are needed? As mentioned above, you may not have used the entire 'identifier' area of the sent message to indicate the recipient. Therefore, when you're checking an incoming message, you wont want to filter the entire identifier. As the identifier bits can be a '1' or '0', you need to initially specify which bits you want to check (mask) and then whether or not they are a '1' or '0' (filter).

Node 1 ('101') Node 2 ('110')
Identifier 10110 10110
Mask 00111 00110
AND'd 00110 00110
Filter 00101 00110
Match? False True

In the table above, you can see that Node 1 failed to match. The filter applied was looking for '101' (5 in decimal) but the lower 3 bits identifier (that's what the mask was looking for) was actually '110' (6 in decimal). You can see that the actual result was '110' after the mask was applied to the identifier. The second node matched as the filter is actually looking for '110'.

Now, we have to use the full registers when we apply this to the MCP2515. This means filling out 29bits of data. When in doubt, fill everything else with zeroes. Each register in the MCP2515 is 8bytes, this therefore means that you need 4 bytes per filter and 4 bytes per mask. The 6 filters and 2 masks that were mentioned above are located in the following areas:

Mask 0 Filter 0 Filter 1 Mask 1 Filter 2 Filter 3 Filter 4 Filter 5
0x20 0x00 0x04 0x24 0x08 0x10 0x14 0x18
0x21 0x01 0x05 0x25 0x09 0x11 0x15 0x19
0x22 0x02 0x06 0x26 0x0a 0x12 0x16 0x1a
0x23 0x03 0x07 0x27 0x0b 0x13 0x17 0x1b

Right, now that you know you need 4 bytes for a filter and then 4 bytes for a mask... and you know where to store them... you'll probably now need to know how to construct a mask and filter. We'll start with a mask that looks for the value 11 (decimal!) [0x0b in hex, 1011 in binary] in the standard 11-bit identifier. As you can guess, I've just provided the filter by representing the value in binary; we simply need to zero-out the bits to the left to ensure we provide the correct number. Have I provided the mask? No! Look at the truth table below if we were to use 1011 as the mask.

Node 1 ('11011') Node 2 ('01111') Node 3 ('01011')
Identifier 11111 11011 01011
Mask 01011 01011 01011
AND'd 01011 01011 01011
Filter 01011 01011 01011
Match? True True True

They all matched!?!?!? Since we were only checking the 'exact' value via the mask, we weren't actually looking to see if any of the bits around the value were set. The basic principal is that we need to know the maximum length of the value we could be looking for and then use that as the mask. For example, if you have up to '11111' (32 including '0') ids on your network, then you want to make sure that your mask is '11111' and that your filter is the exact number of the node checking if the message is theirs.

Node 1 ('01011') Node 2 ('01111') Node 3 ('11011')
Identifier 11111 11011 01011
Mask 11111 11111 11111
AND'd 11111 11011 01011
Filter 01011 01011 01011
Match? False False True

That's better! Make sure your values are correct! So, in the example below we'll use '11111' as the max value and therefore the mask. We'll then use 01011 as the value of the node we're pretending to be and we'll insert these into the correct registers (being Mask 0 and Filter 0.)

Message Acceptance Process

So, two buffers with differing filters... what happens when a message arrives? The basic idea is that a message will hit the first buffer and, if not accepted, attempt to hit the second buffer. The message is then discarded if the second buffer is also configured to not accept this message.

Note: There are also two bits (RXM0 and RXM1) in the RXBnCTRL (where n is either 0 or 1 depending on the buffer) that determine how messages are accepted. Note: My code currently has the ability to set these registers; for the life of me I cannot actually get the chip to function as per the datasheet when these registers are set. If anyone else has succeeded in using the RXM bits successfully then please leave a comment and tell how you did it!

RXM0 RXM0 Description
0 0 [Default] Enable reception of all valid messages as determined by the appropriate acceptance filters. (See the pseudo-code below as to how the masks and filters apply.)
0 1 Only accept standard messages. Filters are useless if the EXIDE bit does not match with this value.
For example, if you put an extended filter on buffer 0 but you disable extended messages to buffer zero with this configuration then that filter will never be used.
1 0 Only accept extended messages. The same rules apply that any standard filters on a buffer with this setting will not be used.
1 1 Receive ALL messages regardless of filters. Also recieve messages that have caused an error during transmission. The fragment of message received in the buffer prior to the error will be brought through. This mode should really only be used for debugging a CAN system.

So, as long as the RXM bits are both set to '0', the message buffers will then process through the following procedure to accept or deny messages.

  for EACH BUFFER b: (initially 0)
    if (MASK 'b' is ZERO) { //mask 0 for buffer 0 and mask 1 for buffer 1
      Accept new message on Buffer b
    } else {
      using FILTERs 0,1 (if b equals 0) OR FILTERs 2,3,4,5 (if b equals 1)
        if (both the message and filter are 'extended') or (both the message and filter are 'standard') then
          for each '1' in MASK as 'x', does FILTER[x] equal message[x]?
            if all match then accept message
            else check next filter as this filters bits do not match the relevant message bits.
        else check next filter as this filter type does not match this message type.
      if still not accepted then TRY BUFFER 1
    }

A mask is disabled when all its bits are '0'. As soon as a bit is '1' then that mask applies to the relevant buffer. As per the section above, the bits in the filter are then checked against the relevant message bits based on what bits the mask specifies the process to check.

On the contrary, a filter is not 'disabled' when all bits are '0'; instead you have configured two filters, on the buffer related to the mask, which will only permit standard messages where the identifier is zero. If you set the mask on a buffer to anything other than 0 and then don't touch the filters, this will prevent extended messages from coming through (as both filters have '0' as the EXIDE bit) and also prevent any standard messages that do not have an identifier of zero.

In the event that you don't want to receive a specific message type, you must put a mask on both receiving buffers and then an appropriate filter to block out the specific identifier bits. On the contrary, make sure that your masks and filters are smart enough to capture all other data required for your CAN node.

If in doubt, write an 'all-seeing' debugging application to view all traffic on your bus... it'll allow you to diagnose why messages are landing where you might not want them to. As per above, setting both RXM bits to 1 will bypass your filters; this way you don't have to muck around with your own configuration too much. Just make sure you have more than two other nodes on the bus if you want another in Listen mode. Having one node on the bus, or two with one in listen mode, will mean that any message transmitted will stay perpetually on the wire. A message must be consumed by another node for it to disappear.

Storing Masks and Filters on the MCP2515

Scroll up to see the register addresses for the masks and filters. The contents of each of these registers looks as follows:

Filter/Mask Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
Byte 0 (RX[F|M]nSIDH)* SID10 SID09 SID08 SID07 SID06 SID05 SID04 SID03
Byte 1 (RX[F|M]nSIDL)* SID02 SID01 SID00 –– EXIDE/––** –– EID17^ EID16^
Byte 2 (RX[F|M]nEID8)* EID15 EID14 EID13 EID12 EID11 EID10 EID09 EID08
Byte 3 (RX[F|M]nEID0)* EID07 EID06 EID05 EID04 EID03 EID02 EID01 EID00
  • *: 'n' is the filter number, i.e. RXF0SIDH is the 'Standard ID High Receive Register Filter 0'.
    [F|M] means that the name is 'RXF1SIDH' for Filter 1 and 'RXM1SIDH' for Mask 1.
  • **: 'EXIDE': set this to '1' and this filter will only apply to Extended IDs, '0' means it will only apply to standard IDs.
    This byte is unimplemented for Masks. Just leave it as zero.
  • ^: These two bits are not used if you are in standard mode as you only need 16 bits to check the first two bytes of data.

Have I lost you yet? Each filter and mask needs to be able to store both the Standard ID bits and the Extended ID bits. As mentioned before, if you're in standard mode then the 'lower' two bytes (i.e. bits 16 to 0 [17,18 are ignored!]) of the extended area are for matching against the first two bytes of data. You can disable this by ensuring that the associated mask has zeroes in the extended area. On the contrary, if you wanted to check the first byte in the data for a specific value then you would pass in '001111111100000000' as the extended area of the mask with the relevant extended bits set in filter.

Somewhere up above I mentioned we'd use '11111' for the mask and '01011' for the node id. We'll now store this in the registers. We'll use Mask 0 and Filter 0 for this and we'll make sure the rest are all zeroes so they don't apply. You'll note that the first register (RXM0SIDH) starts with zeros; as per above this means that the identifier could actually contain any data in this area and, since the mask is '0' for these bits, the acceptance process wont care at all! The registers will need to be set as follows:

Register Data Register Data
RXM0SIDH [0x20] 0b00000011 RXF0SIDH [0x00] 0b00000001
RXM0SIDL [0x21] 0b11100000 RXF0SIDL [0x01] 0b01100000
RXM0EID8 [0x22] 0b00000000 RXF0EID8 [0x02] 0b00000000
RXM0EID0 [0x23] 0b00000000 RXF0EID0 [0x03] 0b00000000
Note: All other Registers in the RXF/RXM space need to be zero'd out!

So, as you can see above, we've put the mask 0b00000011111 in the 'standard id area' of Mask 0 and 0b00000001011 in the 'standard id area' of filter 0. Based on our logic above this will then only allow messages through that match the above requirements. Below is the code to actually do this on the Arduino.

#define MASK_0 0x20
#define FILTER_0 0x00
void CANClass::setMaskOrFilter(byte mask, byte b0, byte b1, byte b2, byte b3) {
	setMode(CONFIGURATION);  
	setRegister(mask, b0);
	setRegister(mask+1, b1);
	setRegister(mask+2, b2);
	setRegister(mask+3, b3);
	setMode(NORMAL);
}

//set MASK 0 for RXB0 a mask checks all bits of the standard id.
CAN.setMaskOrFilter(MASK_0,   0b11111111, 0b11100000, 0b00000000, 0b00000000);
//set Filter 0 to 0x555. Therefore only messages with frame id: 0x555 are allowed through this buffer. 
CAN.setMaskOrFilter(FILTER_0, 0b10101010, 0b10100000, 0b00000000, 0b00000000);

Note that only setting this for buffer 0 will mean that any message that doesn't match will come through on buffer 1. You'll need to also set a mask on buffer 1 to stop the messages completely. Of course, you can also only ever check buffer 0 for messages and not care if buffer 1 has anything waiting.

Filters in action: Guessing game.

Ok, who would've thought you could make microcontrollers play a game together. You know the old trick, someone else chooses a 'random' number and you get to 'randomly' guess it. Why don't we make our Arduino's play the same game?

We'll set the filter on buffer 0 to the number the receiver is thinking of. This means that the sender will only actually get a message through to the receiver if they guess the correct identifier. Both controllers will then swap roles. Just to make life easier we'll limit the range of numbers allowed, but we'll still make the guessing as 'random' as possible.

I could paste the full code here, but instead I'll just post the general idea.

Main setup
Set Mask of Buffer 0 to the 11-bit standard identifier
Set Mask of buffer 1 to the same 11-bit id
set filter 2 (first filter of buffer 1) to a specific ID for messages that aren't guesses.
Set one node to RX and one to TX
Initialise the random generator.
Receiver Role Transmitter Role
  1. Randomly set 'guess' number from known range.
  2. Set Filter 0 to guess number.
  3. Sit and wait for transmitter to guess...
    - if they send a message on buffer 1 then pass it on to the host.
    - if they get through on buffer 0 then it's their turn.
  1. Randomly choose a number to guess.
  2. Set the frame id of the message and send.
  3. Wait for response, if found then we won, swap to receiver.
  4. If no response, send guessed number to buffer 1 of receiver.
  5. Loop and guess another number if required.

Source code (including my version of the CAN Library)

The final piece of the puzzle is always the source code. I've included the guessing game example as well. Note that my code is based off the canduino and I intend to send changes upstream where possible to make life easier for everyone.

Download the library and example here.
29Feb/129

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 Two RS232 cables for the Arduino

MAX232 Setup

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: