USB I/O Interface with ATmega168

Project Status

This firmware runs very stable and has all features that I planned to implement. Therefore you should not expect new features in future.


1.4.4 19.02.2015
Update documentation.

1.4.3 28.10.2014
Fix for wrong output on Port Y after oPY command with bit number 15-31. The problem did not occur on all gcc versions.

1.4.2 20.09.2014
Changed fuse settings to enable brown-out detection at 2,7V.

1.4.1 23.04.2013
Fixes compiler warnings introduced by new avr-gcc version.

1.4.0 17.03.2014
Added support for more serial ports.

Start page   Deutsche Fassung dieser Seite

I/O Interface modules, serial with USB or Bluetooth

I/O Interface modules allow you to control and monitor electrical equipment. Because PC don't have classical parallel ports anymore, I show you how to build a replacement with USB or Bluetooth. You get 18 to 84 controllable Pins, depending on the microcontroller type. And you can get even more with the help of some cheap shift registers.

The Bluetooth option is a good choice for makers who don't want to risk damaging their personal computer. But programming Bluetooth is more difficult because the wireless connection is less reliable.

Access to the I/O ports is provided via simple text commands, eliminating the need of special drivers and libraries. It works with almost any programming language and any operating system, like PC, smartphones, tablets, Raspberry Pi, ...

The C Compiler for the microcontroller is free availabe for Windows (e.g. GNU Toolchain), Linux (avr-gcc) and Mac OS. The example app "ioModule" for PC and smartphones can be compiled with Qt Creator.

Download source code, compiled binaries (hex files).

How it works

Open a connection to the I/O module. Then you can send commands in text form and receive answers on this connection. For testing, I recommend the Hammer Terminal program or Cutecom.

For Example, the following command sets the pin PC3 to high:

Send: oPC3,1
Response: Ok

To query the port D in hexadecimal format:

Send: iPD
Response: PD=60

The whole command set is described at the page bottom. You can execute more than 100 commands per second.


As an alternative to raw microcontroller chips, I recommend Arduino Nano as well as AVR Modules from Chip45. They all are available with a pre-installed bootloader, so you don't need an extra programming adapter.

I noticed that some fake Arduino Nano with CH340 chip have problems to receive data from USB. You may work around this design error by removing the "Rx" LED, then it works reliable.

Mobile devices from Apple do not support the Bluetooth SPP protocol. Use WLAN or Ethernet as an alternative.


Test using a terminal program:

The example app "ioModule" controls two LEDs:

Command Set

Digital I/O

You control the module with four basic commands, which are d, p, o and i.

These commands can be used on a single I/O line (PB7), a port (PB) or all ports together by a single command:

PB7 and PB are examples, all ports are accessible in the same way. When accessing all ports together, the size of the hexadecimal number depends on the size of the AVR, referring to the ports: DDCCBBAA or HHGGFFEEDDCCBBAA.

The commands for single pins support characters as an alternative to 0 and 1:

The commands d, p and o respond always with "Ok". The i command responds this way:

Analog Inputs

The available references depend on the capabilities of the Mikrocontroller:

Example for the r command:

The a command expects the channel number of the analog input, for example:

Input Extension

A shift register may be used to enhance the number of inputs. I call these additional inputs "Port X". They can be read using the i command:

The length of the hexadecimal number depends on the size of the shift register (8-32 bits).

Output Extension

The number of outputs can be enhanced with a shift register. I call these additional outputs "Port Y". The o command writes to that port:

Again, the length of the hexadecimal number depends on the size of the shift register (8-32 bits). The current status of port Y can be read out by the command i.

The Commands d and p are not available on the port extensions!

15 Minute Programming Example

I like to show you how simple it is to control the module by a self-written PC program. The effort is only a few minutes.

Preparation of the Hardware

Build an I/O module with USB, based on an Arduino or Crumb module. The pin PD2 (or D2) shall be connected to an LED with a series resitor:

               470 Ohm   LED
      D2 o-----[===]-----|>|----| GND

Install the Firmware into the microcontroller using an ISP programmer (or using a pre-installed bootloader), then attach the module to your PC. The USB driver will be installed automatically. Check out the device manager of Windows to see which COM port has been assigned to the hardware. In case of Linux, use the dmesg command. In my case, it is "COM3".

Install the Qt Creator development environment. It can be used to develop beautiful programs for Windows, Linux and Mac.


Create a new project in Qt Creator, using the template "Application/Qt-Widgets-Application". Enter the name "test" for the application, then click several times on "Next" to complete the assistant. You will get the following screen:

Open the project file by double-click and add "serialport" to the following line:

QT += core gui serialport
Open the form by double-click on the file mainwindow.ui. Drag a push button from the widget catalogue (left) into the form, for example:

Double-click on the label "PushButton", to rename it to "Start".

Now we define what shall happen when somebody clicks the button. Therefore click with the right mouse button onto the push button, then select "Go to slot...".

Select the Slot clicked(). This opens the file mainwindow.cpp in the text editor. Insert one line for QSerialPort at the top:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QSerialPort>
Somewhat below the Qt Creator has created an empty function with name on_pushButton_clicked(). This is the place where you have to insert code that gets executed when the button gets clicked. Copy from the following example:
void MainWindow::on_pushButton_clicked()
    QSerialPort port("COM3");

    port.write("dPD2,o\n");  // configure PD2 as output
    port.write("oPD2,h\n");  // set PD2 to high (5V)

Instead of "COM3" you shall enter the COM port that Windows has assigned to your module. The line below must define the correct baud rate, as defined by the firmware of your hardware.

Try the program by pressing Ctrl-R.

If you click on the Start button now, then the LED on Port D2 goes on. You may add a second button that switches the LED back off.

To read the answer of the module, you may insert this after the flush():

    QTime time;
    while (!serialPort.canReadLine())
        if (time.elapsed()>1000) {
    QByteArray response=serialPort.readAll().trimmed();

The time check ensures that the program does not wait longer than 1 second, so the program does not hang in case of a technical defect.

Take a look at the documentation of QSerialPort as well as the Getting Started Guides of Qt.