I have an old PC that I use to control my christmas lights. They are driven by an electronic box that I build that plugs into the parallel port which then drives 8 outputs to switch them on or off.
I had written a C++ program that I was previously running on an old Dos box, but the machine this year. I opted to use a slightly newer spare machine and to run Ubuntu on it. I was keen to use Mono to learn a bit about it (as I program in C#.Net on Windows for a living).
Unfortunately there’s no libraries in C# for accessing parallel ports (at least not that I could find). After much looking it transpired that I needed to use P/Invoke to access some managed code to address LPT1.
As I couldn’t find any examples of doing this in the wild, I decided to document what I did here in case others need to do the same thing (though parallel ports are really redundant these days with USB taking over).
First of all, I had to convert my existing C++ code to Linux so that I could compile it into a shared library. Here’s the basic code for addressing the LPT1 port in Ubuntu:
// ------ Include files
#include <stdio.h>
#include <sys/io.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
// ------ Global Variables
#define ON 1
#define OFF 0
unsigned int LPT1 = 0x378;
unsigned int PORTFLAGS = 0x00; // Integer to send to LPT1 to set ports
// ----- Function Declarations
extern "C" void output(int o_port_stat, int o_port);
extern "C" void output(int o_port_stat, int o_port)
{
// Switches a port on or off based on the port status and port number
// o_port_stat = ON or o_port_stat = OFF
// o_port = 1 to 8 for the 8 ports to turn on/off
// write_val is an integer representing an 8 bit value with the bits
// being on or off in line with the 8 ports, i.e bit 0 on = light 1 on
if (o_port_stat == ON)
PORTFLAGS = PORTFLAGS | int(pow(2, o_port));
else
PORTFLAGS = PORTFLAGS & ~int(pow(2, o_port));
//set permissions to access port
if (ioperm(LPT1, 3, 1)) {perror("ioperm"); exit(1);}
outb(PORTFLAGS, LPT1);
//take away permissions to access port
if (ioperm(BASEPORT, 3, 0)) {perror("ioperm"); exit(1);}
}
A couple of notes about the above:
- The extern “C” is required on the functions so they don’t get renamed by the compiler to some random name;
- ioperm is required to grant permissions to access the LPT port
I compiled this using the command below to create a shared library to DLLImport:
sudo gcc -g -fPIC -shared ~/Documents/Programming/ChristmasLights/LPTDriver.C -o /usr/lib/LPTDriver.so
Then it is a matter of importing the resulting shared library into the Mono C# project. I set up a separate class to do this:
using System;
using System.Runtime.InteropServices;
namespace ChristmasLights
{
public class LPTAccess
{
[DllImport("LPTDriver.so", EntryPoint="output")]
public static extern void output(int address, int value);
[DllImport("LPTDriver.so", EntryPoint="set_all_ports_off")]
public static extern void set_all_ports_off();
[DllImport("LPTDriver.so", EntryPoint="set_all_ports_on")]
public static extern void set_all_ports_on();
public LPTAccess ()
{
}
}
}
Note that I have a couple of extra functions in my external library that I set up to simplify turning all 8 ports on or off at once – I didn’t include that code in my listing just to save space.
Finally, in my main program I call the class above and set up a loop to make the lights flash on and off once per second:
using System;
using System.Threading;
namespace ChristmasLights
{
class MainClass
{
public static void Main (string[] args)
{
DoFlash(30);
}
private static void DoFlash (int pTimeFor)
{
int lStatus = 1;
DateTime lEndTime = DateTime.Now.AddSeconds(pTimeFor);
while (lEndTime >= DateTime.Now) {
if(lStatus == 1)
{
LPTAccess.set_all_ports_off();
lStatus = 0;
}
else
{
LPTAccess.set_all_ports_on();
lStatus = 1;
}
Console.Write("f");
Thread.Sleep (1000);
}
LPTAccess.set_all_ports_off();
Console.WriteLine (" Done Flashing");
}
}
}
Note that when running the above Mono code, it needs to execute as su in order to access the parallel port, hence I execute it with the command:
sudo mono ChristmasLights.exe
In reality there’s more to it than that, I have various patterns set up to run, but that gives the general gist of what is required to control the parallel port from Mono, using C# in Ubuntu.