Arduino Teaches Old Coder New Tricks

In September, 2012, my article of this title was published by Linux Journal magazine. A blog version of the article is below.


The first significant electronic circuit that I designed and implemented was in 1973 and consisted of several input, outputs and flip-flop logic circuits – all implemented with relays. At the time, I worked as a training instructor for AT&T-Long Lines and relays were much more obtainable than was the nascent 7400 series logic integrated circuits. Soon thereafter, however, after I was able to obtain some 7400 series ICs and started designing and implementing various circuits using wire-wrap for connections. Wire-wrap was another readily available technology in the telephony field and the circuits were simple – timers and the like. Although the Intel 4004 was in the news at that time, my first microprocessor project was with an Intel 8008. Later I worked with the Intel 8080 and especially the Zilog Z80. I loved the Z80. By the time that IPM-PC came along and I migrated over to the Intel 8086, my hardware development days were numbered. The market had matured and the availability of just about any hardware device that I needed had arrived and thereafter I stuck mostly to software development.

While my work was never officially in hardware design, I occasionally did some simple hardware design in order to implement aspects of software that I was working on. I was a software developer for only a few years in my career although my entire career was involved in and around computer based systems design and support. Nonetheless, I have always dabbled in software and hardware design and implementation on the side. The further my career moved away from actual code production, the more difficult it was for me to justify spending time on such activity. Occasionally, I would need something that didn’t exist and I would build it, but there were large gaps between projects. Eventually years would pass and my soldering irons collected dust.

Around this same time period, the mid-1970s, I had my first exposure to Unix when I obtained a login on a machine running an assembly language version of Unix (Dennis Ritchie had not yet finished creating C), running on a DEC PDP-11/30, aka 11/20. That was the first of many Unix systems that I would work with over time. I fell in love with Unix in these early days and it has never left me and now I use Linux exclusively on my desktop and laptops. At that time, very little application software existed to accomplish much of anything and I became used to having to create my own programs and, occasionally exchange code with other employees. The seeds of my commitment to open source were planted at this time and that too has never left. While I was forced to use closed source systems for years (CP/M, MSDOS and Windows) once Linux matured to a usable desktop platform, I switched to Linux 100% and left proprietary systems in my rear view mirror.

Rebirth of Development Interest

Fast forward to the present. I became aware of the Arduino project from occasional media reports and a presentation at Atlanta Linux Fest 2009. I was impressed with what the Arduino community was doing but, at that time, I saw no personal use for it. It took a grandson to change things for me. This grandson is heavily involved in a competitive Robotics program at his high school. During a 2011 thanksgiving family gathering he asked me some questions about robotics related electronics and I told him that he should Google Arduino. He did, and Arduino ended up on his parents’ Christmas list for him, and Santa delivered.

I decided that I could be more helpful in assisting the grandson’s Arduino efforts if I understood more about it myself, so I ordered a couple of Arduino Nanos and some peripherals, such as rotors, servos, ultrasonic sensors and LCD displays, and dug in. I now had a purpose for using the Arduino and a reason to dust off my soldering iron. I used a bread board for testing, as shown in Figure 1, below.

Figure 1. Arduino Pro Mini in Breadboard Tests

Figure 1. Arduino Pro Mini in Breadboard Tests

It didn’t take very long to remove the mental cobwebs and get into the elegant simplicity of the Arduino project. Years ago, when I built microprocessor projects, the underlying system code was always the problem. Before I could actually write my application I had to develop or adapt systems level code to interface the application level code with the underlying hardware. It was always a major pain and, quite frankly, drudgery. The Arduino project does away with worrying with most of the low level systems code, leaving you with the now much simplified task of creating your application. Using the Arduino IDE and included or contributed libraries enables you to easily interface to a plethora of hardware. Anyone that has developed in the C and C++ languages will find the Arduino platform easy to quickly master. Although Arduino is actually based upon the Wiring Project, compatibility with the C, C++ languages and Linux are very high.

After implementing and testing code for the various peripherals that I had accumulated and generally mastering the Arduino platform I said to myself – “now what?” So, I abandoned the nice Arduino IDE and switched over to developing code using Linux tools, such as Make. I also wanted to get closer to the hardware so I abandoned the Arduino boards and did my implementations on the underlying ICs used by all Arduino boards, the Atmel 8-bit series of microcontrollers. Using the Arduino libraries with the Atmel microcontrollers is a joy to behold. I am so thrilled that the drudgery of systems code can be mostly ignored as it is mostly handled by the hardware abstraction features of Arduino’s built-in libraries. It is important to note that the Atmel ICs are microcontrollers, not microprocessors. In other words they are almost complete computers equipped with RAM, EPROM and FLASH memory, multidirectional I/O, serial ports (in some cases), interface circuitry, such as pull-up resistors, etc. Just adding a power source will yield a computer in a chip.

The hardware’s interfaces of the Atmel microcontroller are abstracted by Arduino in a uniform way – at least uniform for those Atmel micrcontrollers implemented by the Arduino group. Arduino libraries uses internal code, generically called the “core”, to define the available I/O pins on a given Atmel microcontroller, assigned pin number. For example, the Atmel ATMega168 physical pin 4 is defined as Arduino I/O pin 2, yet with the Atmal ATMega32u4 microcontroller the same Arduino pin 2 is matched to physical pin 19. Thus the Arduino syntax of “pinMode(2, OUTPUT)” defines, in software, an abstracted hardware pin as a digital output. Since the Arduino module pins are labeled with Arduino pin numbers, the abstraction is becomes physical, at lease on the module level. Nonetheless, it is this abstraction as well as robust libraries that enable the Arduino to be so easy to work with. One caveat alluded to above is that Atmel microcontrollers not implemented in Arduino modules don’t have uniform core definitions – for example the Atmel Attiny series. You can still use the Arduino libraries and tools but the cores must be obtained elsewhere. For the Atmel ATtiny85 and Attiny 84 microcontrollers I use the core from the project named arduino-tiny. However, there are other, competing cores around for these chips and they are NOT necessarily compatible.

Burning your program into an Arduino module is extremely easy to accomplish. The USB connection can not only power the module as well as serve as the serial communications interface, but the Arduino IDE uses it to install your program into the FLASH memory. It is more complex with the Atmel ATtiny series since they have no USB port or even a hardware serial port, for that matter. For the ATtiny series you must use an external programmer. Many people use an Arduino board as the programmer once they have loaded the readily ArduinoISP software or sketch as programs are named in the Arduino world. In my case I chose to use a dedicated programmer called a USBasp. It is readily available on eBay or you can even make your own with plans from its creator, Thomas Fischl. I purchased mine on eBay because it was cheaper than the parts cost to make my own. The USBasp uses the open-source AVRdude software.

The Project

I have never been able to work on any software or hardware project unless I perceived a need for a deliverable. Now that I had invested a lot of time into learning the Arduino system and the Atmel microcontrollers I wanted to take the next logical step – move a design from the breadboard to a printed circuit board. There are some interesting projects in this area such as Fritzing, which is designed to facilitate doing just that – move a design from the breadboard to a printed circuit board. It is a very clever project and I encourage the reader to check it out, but I took a different path, using the gEDA open source Linux software suite for printed circuit development.

I looked at my inventory of parts and started thinking of what I could create that wasn’t already readily available. I settled upon the LCD display. The displays being used in Arduino projects were interfaced with a lot of I/O pins and code space, neither of which are in great supply on the Atmel chips. I felt that if I could create a same size daughter-board that I could attach onto the back of the display and put the smarts into the board that would communicate with the LCD display via an ASCII serial interface, then I would have something that was useful and didn’t exist in the marketplace in an affordable form. This is commonly called a Serial LCD.

Being somewhat of an old-timer, I spent a lot of time using and coding for the DEC VT100 display terminals upon which the ECMA-48/ANSI X3.64 standards are based. I felt that if I coded the daughter-board to turn an LCD display into a tiny, affordable DEC-VT100, then I would have something reasonably unique and useful. Serial driven LCD displays do exist but they typically have proprietary protocols and some are rather expensive. As far as I have been able to determine, there exists no open-source (software and hardware) serial LCD display with VT100 protocol. I found my project!

Gathering the Parts

I selected parts for the VT100-LCD project, such that the parts would be as affordable as possible. In fact I purchased all of the parts from two sources – eBay and Digi-Key, based on who had the lowest price for a particular part. Below is a list of the required materials to build one vt100lcd. Costs are shown on a per item basis, however, I purchased most of these items in quantities of 5 or more.



Source Cost
1602 HD44780 LCD


Ebay – Chinese seller $2.95
Atmel ATtiny84


Digi-Key ATTINY84-20PU-ND $3.01
Switch, tactile FSM4JH


Digi-Key 450-1650-ND $0.80
Socket, IC, 14-pin


Ebay seller, USA $0.15
Header, 1X20, Female, 2.54mm


Ebay seller, Chinese $0.39
Header, 1X40, Male, 2.54mm


Ebay seller, Chinese $0.20
Resistor, 330 ohm, 1/4W


Ebay seller, Thai seller $0.02
Resistor, 10k ohm, 1/4W


Ebay seller, Hong Kong $0.02
Pot, trim, 5k, RM-065


Ebay seller, USA $0.30
Capacitor, .1uf, ceramic disc, 50V


Ebay seller, Hong Kong $0.05
Transistor, 2N3906


Ebay seller, Thai $.01
Diode, 1N4148


Ebay seller, Thai $.01




Commercial PCB


Panel Aggregator $7.43
Capacitor, 22pf, ceramic disc, 50V


Ebay seller, USA $0.40
Crystal, 20MHZ, ATS200-E


Digi-Key CTX1105-ND $0.64

Schematic Design

To design the circuitry for the VT100-LCD I chose gschem of the gEDA project by This suite includes not only the schematic design program but also a PCB layout program, as well as various helper programs. There are a number of schematic/PCB design software programs but this article focuses on the open source software of the gEDA project by Other open source projects that run on Linux, include KiCad, as well as several commercial products, the most popular of which is Eagle PCB by CadSoft, which runs pretty well under WINE.

Gschem is fairly straight forward and many functions are intuitive but there are a few, useful but arcane commands that necessitate printing out a cheat-sheet – hey, I’m getting older and I can’t memorize all of those keystrokes. Yes, although gschem is a GUI program, there are useful keyboard shortcuts that appear nowhere in the GUI’s menus. This is especially true of the PCB layout program that I discuss later.

The process consists of inserting electronic component symbols into the schematic drawing either from the built-in library or from your private library and then connecting the pins by drawing traces. I highly recommend reviewing the gEDA project’s on-line documentation before starting your own schematic. There are a few tutorials on the web about using the gEDA suite and Stuart Brorson wrote a tutorial article in the November, 2005 issue of Linux Journal. Rather than duplicate their work, I refer you to these alternate sources.

I created two versions of my VT100-LCD project, one using the eight pin ATtiny85 microcontroller and another using the fourteen pin ATtiny84 microcontroller. The schematic for the ATtiny84 microcontroller version is shown in Figure 2, below.

Since some of the components that I was using do not exist in the built-in library I scoured the Internet for contributed symbols and in a few cases I had to design my own symbols. A good source for contributed symbols and footprints is For creating your own symbols I refer you to David Weber’s Online Symbol Creation Tool at Symbols are actually text files. Figure 3, below, illustrates a symbol along with a portion of the text file used to draw it. Symbol files are not just an image. They also hold important pin definitions and the name of the footprint file that the gEDA PCB program will ultimately use to represent the component on the circuit board.

A gEDA schematic is a text file interpreted for GUI presentation by gschem but it also serves as the source for gEDA’s PCB program. An intermediary helper program named gsch2pcb is used to prepare the schematic file for use as input to the PCB program. While xgsch2pcb is a GUI version of gsch2pcb for gsch2pcb, I use the gsch2pcb command line version. For example, given the schematic file vt100lcd84.sch as an input, gsch2pcb creates vt100lcd84.pcb, and vt100lcd84.cmd, all necessary files for PCB creation. Gsch2pcb also displays important instructions as part of its command line text output. To make the process a little easier, I use a file named “project” in the project folder for the current design. Figure 4, below, shows my project folder for the vt100lcd84 project, the “project” file and the command line with the gsch2pcb command just before execution.

Figure 2: Schematic for VT100LCD e/w ATtiny84

Figure 2: Schematic for VT100LCD e/w ATtiny84

Figure 3: Symbol Example1

Figure 3: Symbol Example1

Figure 4: Example of gsch2pcb Project File

Figure 4: Example of gsch2pcb Project File

It is worth noting that the gEDA suite includes circuit simulation capability (spice), enabling virtual design testing. I did not use SPICE with my VT100-LCD project but please do explore the Resources list if you are interested.

Software Design

Now that I had the circuitry designed for the project, it was time for the software. I wrote the software as a simple state machine that parses each character received on a character by character basis, meaning that there is no buffer. Characters are handled differently based upon the current state of the machine. If the state is NOTSPECIAL then the character is simply passed to the LCD screen for display. However, if the state is GOTESCAPE, GOTBRACKET or INNUM then the character is further processed. For example, if the state is GOTBRACKET then both an escape and left-bracket character have been previously received and the current character must be parsed in that context. For illustration, the VT100 sequence for Screen-Clear is ESC[2J and if the current character being parsed was the ‘2’, then the state would be GOTBRACKET and the next state would be INNUM (number collection).

This method of parsing has the advantage of simplicity, which is suitable for the limited capacity microcontrollers but with the disadvantage not being able to scroll the screen due the absence of a buffer holding a copy what is on the screen. A copy of the software source is at the end of the article.

I used Arduino libraries to build the code. While the source can be compiled using the Arduino IDE, I use Linux make. Using the Arduino libraries makes the project extremely to to build. Most of the drudgery of low-level code and the bootloader is hidden away within the Arduino libraries, freeing me to focus solely on my project. Even main() is hidden away such that Arduino code contains two required routines – setup() and loop(). Main actually does exist deep in the Arduino directory structure in ~/arduino/arduino-1.0/hardware/arduino/cores/arduino/main.cpp and is automatically linked in at compile time.

Supported VT100 Commands:

vt100lcd valid VT100/ANSI sequences (NOTE: No spaces – included just for easy reading)
RETURN Cursor to leftmost of current line
LINEFEED Cursor down
ESC c Resets LCD
ESC D Cursor Down
ESC M Cursor Up
ESC E Move cursor to start of next line
ESC [ A Cursor up one line (arrow key)
ESC [ B Cursor down one line (arrow key)
ESC [ C Cursor right one column (arrow key)
ESC [ D Cursor left one column (arrow key)
ESC [ H Cursor to HOME 1;1
ESC [ s Save cursor position
ESC [ u Restore to saved cursor position
ESC [ m All attributes off
ESC [ Pn A Cursor up Pn lines
ESC [ Pn F Cursor up to column 1 of Pn lines
ESC [ Pn B Cursor down Pn lines
ESC [ Pn E Cursor down column 1 of Pn lines
ESC [ Pn C Cursor right Pn characters
ESC [ Pn D Cursor left Pn characters
ESC [ Pn G Cursor to column Pn of current line
ESC [ 2 J Erase Screen and home cursor
ESC [ Pl;PcH Direct cursor addressing, where Pl is line#,
ESC [ Pl;Pcf Same as above
ESC [ = Pn h Set Mode (LCD lines), Pn 2 = 16X2, 4 = 16X4
ESC [ 0 m All attributes off (underscore cursor off)
ESC [ 4 m Underscore on
ESC [ 0 c Report terminal type
ESC [ 5 n Device Status Report, reports max qty lcd lines (1s based)
ESC [ 6 n Device Status Report, reports cursor position (1s based)
ESC [ 0 q Turn LCD’s LED 1 off
ESC [ 1 q Turn LCD’s LED 1 on

PCB Layout

When the .pcb file is opened with gEDA’s PCB program and the commands invoked as listed in the gsch2pcb command line text output, you are presented with a jumble of components. I first manually dispersed the components with an approximate placement and then activated the “rats-nest” display. The “rats-nest” is the connections that must be converted to copper traces. After shifting around components to visually bring the “rats-nest” connections to their shortest routes, I was presented with what you see in Figure 5, below.

Figure 5: Components with Associated Rats-Nest

Figure 5: Components with Associated Rats-Nest

PCB possesses the ability to auto-route the traces, namely convert the “rats-next” into copper trace representations. This tends to do some odd things but produces a workable PCB design once a cleanup is done. I chose a semi-manual layout so that I could control the placement and appearance. Basically, I used the auto-route for the power traces, did some manual cleanup, then used auto-route for the signal traces followed by more cleanup. The result was similar to Figure 6, below, which is my second, and final version of the layout for the ATtiny84 version of my VT100LCD project.

Figure 6: Final Layout of vt100lcd84 Project

Figure 6: Final Layout of vt100lcd84 Project

PCB Manufacture

Printed Circuit Board layout consists of applying upon a copper laminated board, an acid-resistant pattern that represents the areas that are to retain copper after etching in an acid solution (etchant). Areas of the copper laminated board that are exposed to the etchant will be dissolved away, leaving the areas under the acid-resistant pattern intact.

Years ago, I used to occasionally make Printed Circuit Boards using a photographic method that is less common in the Do It Yourself (DIY) community today. The acid-resistant pattern was laid out by hand onto translucent or clear drafting paper using fine black tape for circuit paths and dry transfer patterns for components. This pattern is typically a positive, similar to Figure 7, below, so a negative must be photographically made for the process to work. The end result is that the negative’s acetate sheet is clear where copper should remain after etching. This photographic work was formally done in a darkroom but today creating the negative can be done using a computer printing to a transparency sheet. I will not discuss the process here, but an example is shown in Figure 8, below.

Figure 7: Layout Positive

Figure 7: Layout Positive

Figure 8: Layout Negative

Figure 8: Layout Negative

Next the prepared negative pattern would be affixed on a copper laminated board that has a light sensitive diazo-type emulsion as a top layer. Exposing the prepared PCB to ultra-violet light would alter the properties of the exposed (clear) areas that received the ultra-violet light. Washing the exposed board in a chemical developer dissolved the exposed portions of the emulsion, leaving intact the emulsion that was under the black portions of pattern on prepared negative sheet. Many commercial systems still do a modernized variation of this process as do some serious Do It Yourselfers.

The casual DIY community has, thankfully, adopted a new and much easier method of PCB layout for medium density layouts. High density layouts should still use the photographic process. The new DIY PCB layout process is commonly called the “toner transfer method”, because a laser printer is involved. Thankfully the old pasteup tape and dry-transfer component patterns are a thing of the past. Computer software is now available for the DIY community that takes software designed schematics as input and produces a representative PCB layout. Figure 7, above, was produced by such software.

As I mentioned earlier, there are a number of PCB design software programs but this article focuses on the open source PCB program of the gEDA project by An example of an in-progress gEDA PCB layout is shown in Figure 9, below and its final output is a positive similar to Figure 7, above.

The positive of Figure 7 needs to be printed in reverse onto a paper that will easily release the toner when heated. Laser printer toner is a finely ground polymer plastic that is fused to the paper by heat. The trick of the “toner method” is to get the toner to transfer from the paper to the copper laminated board once it is re-heated. The type of paper used is a big part of the secret here.

There are several paper solutions for the “toner method”, some better than others. Regardless of the type of paper used, the process is to place the reverse image positive laser print with the toner touching the metal surface of a clean copper laminated board and then apply heat and pressure to loosen the toner from the paper, permitting it to transfer to and adhere to the copper laminated board. Most DIYers use a common clothes iron as the heat source, although a laminating machine designed for identification cards is successfully used with one commercial product that I’ll talk some about later.

The cheapest and simplest method is to simply use ordinary copier paper. Once heated under pressure, the toner ends up adhering to both the paper and the copper laminated board. The paper/copper laminated board is then soaked under water and the waterlogged paper is rubbed off with your fingers. This method leaves a lot of paper residue embedded in the toner’s surface, this is undesirable for reasons explained later.

Figure 9: gEDA PCB Layout in Progress

Figure 9: gEDA PCB Layout in Progress

Many other paper types are used by various DIYers. One of the most popular is to use a high quality magazine page that has a smooth, glossy appearance. The gloss is caused by a white clay (kaolin) coating. Since the kaolin fills in many of the pores of the paper, the toner is less firmly bonded to the paper. Secondly, the kaolin dissolves in water, thus freeing the toner more readily than plain paper. This method is superior to using plain paper but it still leaves too much paper residue embedded in the toner’s surface.

Another popular method is to print onto the glossy side of photo paper or backing paper for labels. This method is superior either plain paper or high quality magazine page in that there is no paper residue embedded in the toner’s surface. However, a significant problem with this method is actually getting the toner to evenly stick to the paper. Quite often PCB traces will simply fall off of the slick surface while the paper works its way through the printer. Obviously there is a lot of variability amongst laser printers and glossy paper types. I don’t like variability – I like dependable repeatability.

This leads me to the paper and method that I use that has dependable, predictable results. The paper is colloquially known as dextrin coated paper. Some DIYers actually make their own by making dextrin and coat paper with it. Dextrin is simply cooked corn starch and the process is easy albeit a bit labor and time intensive. Also, getting an even coat is a challenge. If you are interested, there are numerous articles and videos on the subject – simply Google “make dextrin paper”. I, however, feel that purchasing commercial dextrin paper is worth the cost. My preferred product is made by PulsarProFX ( They primarily sell a kit, called “Fab-In-A-Box” but the entire kit isn’t really necessary. Instead buy their refill package of Transfer Paper. Also buy their Green Toner Foil. DigiKey sells refill kits of both. Pulsar really pushes use of a laminator but cautions you that their laminator isn’t hot enough to melt the toner used in Brother laser printers. My printer is a Brother HL-2140 so I simply use a clothes iron. A word of caution here – use genuine Brother toner. After-market toner cartridges may contain fuser oil that prevents the toner from adhering to copper. After several failed boards I figured out that the problem was my new Rosewill brand toner cartridge. When I put in a genuine Brother cartridge my boards were successful again.

The reason that you need the Green Toner Foil is because when the toner adhering to the copper laminated board is porous and, even though you cannot see it with the naked eye, there are sufficient holes for the etchant to penetrate the toner traces and remove metal that you do not want removed. The Green Toner Foil is ironed onto the copper laminated board resident toner, creating a smooth, impervious surface on the top of the toner traces, resulting in superior board etches. Now, remember that I said that the aforementioned transfer methods were deficient due to paper residue embedded in the toner’s surface? This is because the paper residue prevents the Green Toner Foil from making a good bond to the toner.

How do I make my own single-sided PCBs? It is fairly simple:

  1. Print a reverse image positive of the PCB pattern onto the shiny side of Pulsar dextrin Transfer paper.
  2. Place the Transfer paper’s toner side against a copper laminate board that has been cleaned with steel wool
  3. Place a sheet of ordinary paper above the Transfer paper to help prevent slippage
  4. For two minutes apply, with a few of pounds of pressure, a common clothes iron set to the highest “cotton” setting.
  5. Immerse into water, the ironed together paper/copper laminate board. After a couple of minutes the paper will probably float off. If not lift it off.
  6. Dry the board and with the toner side up, lay the dull side of the Green Toner Foil against the toner and another piece of ordinary paper above that.
  7. Using the same clothes iron set slightly cooler, to “wool”, iron for one minute with a few pounds of pressure.
  8. Peel off the Green Toner Foil.
  9. Etch the board as described below.

I only make single sided boards. If you’d like to make a double-sided board, watch the video at This DIYer uses HP’s glossy brochure paper and seems to get pretty good results.

Etching the PCB

Having read much of what is readily available on the web concerning DIY PCB Etching, when the need arose, I decided to etch a single sided board two different ways – first with the Vinegar & Salt method and second with the sponge and Ferric Chloride method. Some DIYers are using Muriatic Acid but I have not tried that.

The Vinegar & Salt method does work, albeit slowly. Etching my small board took two hours. The formula that I used was equal parts of Vinegar and Hydrogen Peroxide and a few tablespoons of table salt. Keep adding salt until the “fizzing” continues all by itself. The liquid starts out clear but then turns an attractive shade of blue. See Figure 10, below.

Figure 10: Vinegar Salt Etchant

Figure 10: Vinegar Salt Etchant

The sponge and Ferric Chloride method works extremely well, etching the same board in a couple of minutes. In the past, I used Ferric Chloride to etch boards by placing them into a bath of Ferric Chloride. Even with agitation, etching a board could take ten minutes, or so. The sponge and Ferric Chloride method accelerates the etching by continuously rubbing the surface with a sponge soaked in Ferric Chloride. The rubbing removes the oxide layer that continuously builds up, permitting the Ferric Chloride to get to the raw metal and thus accelerate etching. Instead of a tub of etchant, a couple of tablespoons is all that is needed, which will make a bottle of Ferric Chloride last for a very, very long time. The technique is simple. Don plastic gloves; pour a couple of tablespoons of Ferric Chloride into a small container; soak a small piece of soft sponge in the Ferric Chloride; continuously, lightly rub the saturated sponge on the PCB. In a couple of minutes the board will be finished with little mess and little Ferric Chloride to dispose of.

My final product (after 3 versions), a single-sided ATtiny84 version of the project, is shown in Figure 11, below. Given that the board was single sided, nine jumpers were required, which are the wires that you can see on the component side of the board.

Figure 11: Final etched ATtiny84 Board

Figure 11: Final etched ATtiny84 Board

Commercially Made PCBs

In addition to making my own PCBs, I also had commercial boards made by a panel aggregator. A panel aggregator is a service that aggregates boards from many sources, filling up a cost efficient size printed circuit board panel and then breaking up the completed panel for delivery. There are several such companies supporting the hobbyist community.

Figure 12, below, shows my Attiny85 design mounted to a 16×2 LCD.

Figure 12: Commercially made ATtiny85 Board

Figure 12: Commercially made ATtiny85 Board

Figure 13, below, shows my Attiny84 design mounted to a 16×4 LCD.

Figure 13: Commercially made ATtiny84 Board

Figure 13: Commercially made ATtiny84 Board


  1. The Arduino Project:
  2. The Wiring Project:
  3. The arduino-tiny project:
  4. Thomas Fischl’s USBasp website:
  5. Avrdude device programming software:
  6. The Fritzing Project:
  7. The gEDA PCB development project:
  8. Symbol creation:
  9. Footprint creation by Stefan Salewski:
  10. Circuit Design on Your Linux Box Using gEDA,by Stuart Brorson, published in the November, 2005 Linux Journal
  11. Using gEDA, by Iznogood,
  12. Getting Started With PCB,
  13. gsch2pcb Tutorial:
  14. gschem -> gsch2pcb -> PCB:
  15. Circuit simulation using gEDA and SPICE – HOWTO, by Stuart Brorson:
  16. Project source:
 * vt100lcd.ino
 * Copyright 2012 Edward I. Comer <>
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 3, as
 * published by the Free Software Foundation.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA or browse to
 * WARNING: Pin assignments are per the Arduino-Tiny open source set of
 * ATtiny "cores".
 * Use of a different core may have different pin assignments.
 * +-\/-+
 * VCC 1| |14 GND
 * XTAL (D 10) PB0 2| |13 PA0 (D 0) AREF
 * XTAL (D 9) PB1 3| |12 PA1 (D 1)
 * RESET PB3 4| |11 PA2 (D 2)
 * PWM INT0 (D 8) PB2 5| |10 PA3 (D 3)
 * PWM (D 7) PA7 6| |9 PA4 (D 4)
 * PWM (D 6) PA6 7| |8 PA5 (D 5) PWM
 * +----+
 * +-\/-+
 * RESET (D 5) PB5 1| |8 Vcc
 * Ain3 (D 3) PB3 2| |7 PB2 (D 2) Ain1
 * Ain2 (D 4) PB4 3| |6 PB1 (D 1) pwm1
 * GND 4| |5 PB0 (D 0) pwm0
 * +----+
 * Employs a state machine to parse special escape sequences. If
 * the character passed to parse is not special and the state
 * machine is not in a "special" state, then the character is returned
 * as passed. Otherwise, a zero value is returned.
 #include "vt100lcd.h"
#ifdef ATTINY85
 #include <LiquidCrystal_SR.h> #else
 #include  #endif
 #define SerialPort SoftSerial
 #define SerialPort Serial
int parsechar(unsigned char current_char);
#ifdef ATTINY84
 #define rxPin 0 //ATtiny84 PA0 physical pin 13
 #define txPin 1 //ATtiny84 PA1 physical pin 12
 #define ledPin 2 //ATtiny84 PA2 physical pin 11
 #define LCD_RS_Pin 3 //ATtiny84 PA3 physical pin 10
 #define LCD_E_Pin 4 //ATtiny84 PA4 physical pin 9
 #define LCD_d4_Pin 5 //ATTiny84 PA5 physical pin 8
 #define LCD_d5_Pin 6 //ATTiny84 PA6 physical pin 7
 #define LCD_d6_Pin 7 //ATTiny84 PA7 physical pin 6
 #define LCD_d7_Pin 8 //ATTiny84 PA2 physical pin 5
 #elif ATTINY85
 #define rxPin 0 //ATtiny85 PB0 physical pin 5
 #define txPin 1 //ATtiny85 PB1 physical pin 6
 #define LCDdataPin 2 //ATtiny85 PB2 physical pin 7
 #define LCDclockPin 3 //ATtiny85 PB3 physical pin 2
 #define ledPin 4 //ATtiny85 PB4 physical pin 3
 #define ledPin 2 //ArduinoNano D2 physical pin 5
 #define LCD_RS_Pin 3 //ArduinoNano D3 physical pin 6
 #define LCD_E_Pin 4 //ArduinoNano D4 physical pin 7
 #define LCD_d4_Pin 5 //ArduinoNano D5 physical pin 8
 #define LCD_d5_Pin 6 //ArduinoNano D6 physical pin 9
 #define LCD_d6_Pin 7 //ArduinoNano D7 physical pin 10
 #define LCD_d7_Pin 8 //ArduinoNano D8 physical pin 11
// Software default is 16X2 HJ1602 LCD display
 // lcd.begin() default is 16X4 JHD539 LCD display
 #define MAXCOLUMNS 16
 #define MAXLINES 4 // Default Hardware to 4 Lines, software to 2
#define NOTSPECIAL 1
 #define GOTESCAPE 2
 #define GOTBRACKET 3
 #define INNUM 4
// 0 based defines
 #define LEFT_EDGE0 0
 #define RIGHT_EDGE16COL_0 15
 #define TOP_EDGE0 0
 #define BOTTOM_EDGE2LINE_0 1
 #define BOTTOM_EDGE4LINE_0 3
#define DEFBAUD 9600
struct cursor { unsigned int row; unsigned int col; };
int current_state = NOTSPECIAL;
 int previous_state = NOTSPECIAL;
 int tmpnum; // number accumulator
 int n, c;
 unsigned int num, row, col;
 int bottom_edge0 = BOTTOM_EDGE2LINE_0; //Startup default
 int right_edge0 = RIGHT_EDGE16COL_0; //Startup default
 struct cursor cursor_pos = { 0, 0 }; // VT100 is 1 based but
 struct cursor cursor_sav = { 0, 0 }; // cursor values 0 based
#ifdef ATTINY85
 LiquidCrystal_SR lcd(LCDdataPin,LCDclockPin,TWO_WIRE);
 LiquidCrystal lcd(LCD_RS_Pin,LCD_E_Pin,LCD_d4_Pin, LCD_d5_Pin, LCD_d6_Pin, LCD_d7_Pin);
 SoftwareSerial SoftSerial = SoftwareSerial(rxPin, txPin);
// Write char to LCD and update col position
 void writelcd(int c) {
 cursor_pos.col = (right_edge0 > (col = cursor_pos.col + 1) ? col : right_edge0);
void setup() {
 pinMode(ledPin, OUTPUT);
 digitalWrite(ledPin, LOW); // low activates LED
 lcd.begin(MAXCOLUMNS, MAXLINES); // set up the LCD's number of rows and columns:
 lcd.print(FILENME); // Print a message to the LCD.
 // set the cursor to column 0, line 1
 // (note: line 1 is the second row,
 // since counting begins with 0):
 lcd.setCursor(0, 1); //lcd.setCursor(column, row);
 lcd.print(COPYRIGHT); // Print a message to the LCD.
 lcd.setCursor(0, 0); //lcd.setCursor(column, row);
// set the data rate for the Serial port
 SerialPort.println("Init Serial Port");
void loop() {
 // fetch softserial and send/parse to LCD
 if (SerialPort.available())
 if((c = > 0)
 if(parsechar(c) > 0)
void switchstate(int newstate) { // TODO not sure prev-state needed
 previous_state = current_state;
 current_state = newstate;
void cursorDown()
 if(cursor_pos.row < bottom_edge0){ n = cursor_pos.row++; lcd.setCursor(cursor_pos.col, cursor_pos.row); } } /* * call with char to parse for VT100 escape sequence. * Returns 0 for chars within a parse or the * passed char if the char is not special */ int parsechar(unsigned char current_char) { switch(current_state) { case NOTSPECIAL: if(current_char == 033) { // Escape switchstate(GOTESCAPE); return(0); } else if(current_char == 015) { // CR lcd.setCursor(cursor_pos.col=0, cursor_pos.row); return(0); } else if(current_char == 012) { // LF cursorDown(); return(0); } else { switchstate(NOTSPECIAL); return(current_char); } break; case GOTESCAPE: switch(current_char) { case '[': // Bracket switchstate(GOTBRACKET); return(0); case 'D': // Cursor Down cursorDown(); break; case 'M': // Cursor Up if(cursor_pos.row == TOP_EDGE0){ break; // already at top } lcd.setCursor(cursor_pos.col, cursor_pos.row -= 1); break; case 'E': // Cursor down to col 1 cursor_pos.col = 0; cursorDown(); switchstate(NOTSPECIAL); return(0); case 'c': // Reset lcd.clear(); lcd.noCursor(); cursor_pos.col = cursor_pos.row =0; switchstate(NOTSPECIAL); return(0); default: switchstate(NOTSPECIAL); return(current_char); } switchstate(NOTSPECIAL); return(0); /* * Previous ESC[ should be followed by a decimal number, * for curdor movement, keyboard arrow or a row number */ case GOTBRACKET: if(isdigit(current_char)){ switchstate(INNUM); // accumulate number tmpnum = 0; tmpnum = tmpnum*10 + (current_char-'0'); return(0); } else { // Here if non-numeric char after bracket // Check for Keyboard Arrows switch(current_char) { case 'A': // Keyboard UP Arrow if(cursor_pos.row == TOP_EDGE0){ break; // already at top } lcd.setCursor(cursor_pos.col, cursor_pos.row -= 1); break; case 'B': // Keyboard Down Arrow cursorDown(); break; case 'C': // Keyboard Right Arrow col = (right_edge0 > (col = cursor_pos.col + 1)) ? col : right_edge0;
 lcd.setCursor(cursor_pos.col=col, cursor_pos.row=row);
case 'D': // Keyboard Left Arrow
 col = (LEFT_EDGE0 < (col = cursor_pos.col - 1)) ? col : LEFT_EDGE0; lcd.setCursor(cursor_pos.col=col, cursor_pos.row=row); break; case 'H': // Cursor to Home lcd.setCursor(cursor_pos.col=0, cursor_pos.row=0); break; case 'm': // turn off attributes lcd.noCursor(); break; case 's': // Save cursor pos cursor_sav.col = cursor_pos.col; cursor_sav.row = cursor_pos.row; break; case 'u': // Restore cursor pos cursor_pos.col = cursor_sav.col; cursor_pos.row = cursor_sav.row; lcd.setCursor(cursor_pos.col, cursor_pos.row); break; case '=': // Set screen size switchstate(INNUM); // accumulate screen size number tmpnum = 0; return(0); // discard '=' char default: // ESC[ was fluke break; } switchstate(NOTSPECIAL); return(0); } break; case INNUM: // intermediate number accumulation if(isdigit(current_char)){ // accumulate number tmpnum = tmpnum*10 + (current_char-'0'); return(0); // stay in INNUM state } else { // must be a post numeric delimiter or command switch(current_char){ case ';': //Delimiter between row, col tmpnum = (tmpnum > 0) ? tmpnum-1 : 0; //set base 0
 row = (tmpnum > bottom_edge0) ? bottom_edge0 : tmpnum;
 tmpnum = 0;
 return(0); // stay in INNUM state
// Case for ESC [ num1 ; num2 H|f
 case 'H': // Move cursor to r,c
 case 'f': // ditto
 tmpnum = (tmpnum > 0) ? tmpnum-1 : 0; //set base 0
 col = (tmpnum > right_edge0) ? right_edge0 : tmpnum;
 lcd.setCursor(cursor_pos.col=col, cursor_pos.row=row);
// Case for ESC [ num A|F
 case 'A': // Cursor up n lines
 case 'F': // Cursor up n lines to col 1
 tmpnum = (tmpnum > 0) ? tmpnum : 1; // min val of 1
 row = (TOP_EDGE0 < (row = cursor_pos.row - tmpnum)) ? row : TOP_EDGE0; lcd.setCursor(cursor_pos.col=(current_char=='A')?col:LEFT_EDGE0, cursor_pos.row=row); break; // Case for ESC [ num B|E case 'B': // Cursor down n lines case 'E': // Cursor down n lines to col 1 tmpnum = (tmpnum > 0) ? tmpnum : 1; // min val of 1
 row = (bottom_edge0 > (row = cursor_pos.row + tmpnum)) ? row : bottom_edge0;
 lcd.setCursor(cursor_pos.col=(current_char=='B')?col:LEFT_EDGE0, cursor_pos.row=row);
// Case for ESC [ num C
 case 'C': // Cursor right n chars
 tmpnum = (tmpnum > 0) ? tmpnum : 1; // min val of 1
 col = (right_edge0 > (col = cursor_pos.col + tmpnum)) ? col : right_edge0;
 lcd.setCursor(cursor_pos.col=col, cursor_pos.row=row);
// Case for ESC [ num D
 case 'D': // Cursor left n chars
 tmpnum = (tmpnum > 0) ? tmpnum : 1; // min val of 1
 col = (LEFT_EDGE0 < (col = cursor_pos.col - tmpnum)) ? col : LEFT_EDGE0; lcd.setCursor(cursor_pos.col=col, cursor_pos.row=row); break; // Case for ESC [ num G case 'G': // Cursor to pos n on cur line tmpnum = (tmpnum > 0) ? tmpnum-1 : 0; // base 0
 col = (tmpnum > right_edge0) ? right_edge0 : tmpnum;
 lcd.setCursor(cursor_pos.col=col, cursor_pos.row);
// Case for ESC [ 0|4 m
 case 'm': // turn off attributes
 if(tmpnum == 0)
 else if(tmpnum == 4)
// Case for ESC [ num c
 case 'c': // report terminal type
// Case for ESC [ 2 J
 case 'J': // Erase screen and home cursor
 if(tmpnum == 2){
 cursor_pos.col = cursor_pos.row =0;
// Case for ESC [ 0|1 q
 case 'q': // LED Operation
 if(tmpnum == 0) {
 digitalWrite(ledPin, HIGH);
 } else {
 digitalWrite(ledPin, LOW); // Low activates LED
// case for ESC [ 5|6 n
 // NOTE: DSR response differs in format from VT100
 case 'n': // Device Status Report
 if(tmpnum == 5){ // Request DSR
 SerialPort.println(bottom_edge0+1, DEC);
 else if(tmpnum == 6){ // Request active position
 SerialPort.print(", row=");
 SerialPort.println(cursor_pos.row+1, DEC);
// case for ESC [ = num h
 case 'h': // Set screen size
 if(tmpnum == 4){
 bottom_edge0 = 3;
 bottom_edge0 = BOTTOM_EDGE2LINE_0;
default: // not supposed to happen
default: // not supposed to happen
This entry was posted in Arduino, Electronics, Linux, PCB Etching, Programming and tagged , , , , , , , . Bookmark the permalink.

2 Responses to Arduino Teaches Old Coder New Tricks

  1. Zohar says:

    Thank you for an amazing article


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s