Controlling a relay and motor with a serial port
For a while I have wanted to control things with a serial port. It was pretty easy to control a relay with a serial port. With a standard serial port you can control 2 relays. (with a parallel port you can control 8 relays, but I don't have a parallel port on my system).
A standard PC serial port has 9 pins. Pin 4 - DTR (data terminal ready) and Pin 7 - RTS (request to send) can be used to control a relay. These two ports don't actually send data. They are used to signal the other device to tell it when to send data.

These pins can be set high or low. When set high, they each go to about +9 volts. When set low they go to about -9 volts. This voltage swing is what is used to run the the relay.
A serial port pin does not have enough current to drive a relay by itself. You must build a simple cicuit to drive the relay. You have the DTR pin drive a transistor, which in turn drives the relay. The parts needed are:
1. NPN transistor, Radio Shack part #276-1617
2. 2 diodes, Radio Shack part #276-1103
3. 4.7k resistor
4. reed relay, Radio Shack part #275-233
5. 9 volt battery
Here is a diagram of the circuit:
To drive the pin, I wrote a small C program. It loops and turns DTR high then low then repeats itself.
You have to run this program as root. To compile it, just download relay.c and do gcc relay -o relay.c
To run type ./relay /dev/ttyS0 (or whatever serial port you use)
This circuit worked well with my built in serial port. I was able to run a small dc motor. I also used a USB to Serial adapter and it worked too. If I had the relay switch on/off quickly (say on/off ten times in a row within 5 seconds) the USB serial port would lock up. I would have to unplug and plug it back in. I am guessing the motor is generating some interference that causes the usb serial port to lock up. I haven't had any problem with my built in serial port though.
The final product:

Update: Joe sent in his version of a
serial port relay. He uses a solid state relay instead of the transitor/relay setup I have. That makes things a lot simpler.
Resources:
Serial Programming Guide for POSIX Operating Systems
Controlling Hardware with ioctl
Controlling Devices with Relays
Comments
Peter:
Thanks for detalized explainments and pictures
work at home writing
Anonymous:
Well this is quite an interesting theory and it really works, thanks a lot
Java Worm [PRITAM]:
Thankyou very much man !!
It works !!
Really got it what i was searchin for my project !!!!
Great Resource !!!!
leopard:
Hi there!!
Here is a small Java code to control the relay with this circuit. Needs the rxtx library.
The API is in spanish, but some google translate can help. I'm apologyze for my Tarzan-Like english.
greetings from Chile.
----------------
import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
/**
* Permite controlar un dispositivo externo mediante el puerto serial.
* El control se realiza llevando a ON el DTR (pin numero 4 en un conector
* serial de 9 pines). Ver http://www.windmeadow.com/node/4 para un circuito
* que utiliza esta señal. Contiene un
booleanque indica el estado (encendido/apagado)* y puede agregarsele uno o mas
ActionListenerque se gatilla(n) cada vez que* se cambia el estado.
*
* @author pedro
*/
public class Interruptor {
private Puerto puerto;
private CommPortIdentifier portId;
private CommPort commPort;
private boolean encendido;
private Collection listeners;
/**
* Crea una nueva instancia del interruptor en el puerto indicado
* @param puerto
*/
public Interruptor(Puerto puerto) throws ErrorInterruptor {
if (!puerto.getTipoPuerto().equals(TipoPuerto.SERIAL)) {
throw new ErrorInterruptor("Debe utilizarse un puerto serial");
}
this.puerto = puerto;
encendido = false;
listeners = new ArrayList();
}
/**
* Agrega un
ActionListeneral listado de listeners del* interruptor. Cada vez que se gatilla una accion en el interruptor se envia
* una señal a los Listener. Cuando el interruptor se activa, el comando
* es on, mientras que cuando se apaga el comando es off
* @param al ActionListener que se agregara al listado
*/
public void addListener(ActionListener al) {
listeners.add(al);
}
/**
* Indica si el interruptor esta encendido
* @return boolean con el estado
*/
public boolean isEncendido() {
return encendido;
}
/**
* Envia un pulso que mantiene encendido el
* interruptor por 0,5 segundo
*/
public void pulsar() {
pulsar(500L);
}
/**
* Envia un pulso que mantiene encendido el
* interruptor por el tiempo indicado
* @param milis Duracion del pulso en milisegundos
*/
public void pulsar(final Long millis) {
Runnable pulsar = new Runnable() {
public void run() {
try {
encender();
Thread.sleep(millis);
apagar();
} catch (Exception ex) {
ManejadorExcepciones.traza(ex);
}
}
};
if (!encendido) {
new Thread(pulsar).start();
}
}
/**
* Enciende el interruptor. Se mantendra encendido indefinidamente hasta que
* se invoque el metodo
apagar()oconmutar();*/
public void encender() throws ErrorInterruptor {
try {
portId = CommPortIdentifier.getPortIdentifier(puerto.getNombrePuerto());
} catch (NoSuchPortException ex) {
throw new ErrorInterruptor("El puerto " + puerto.getNombrePuerto() + " no existe", ex);
}
try {
commPort = portId.open(getClass().getName(), 5000);
} catch (PortInUseException ex) {
throw new ErrorInterruptor("El puerto esta en uso", ex);
}
encendido = true;
fireActionPerformed("on");
}
/**
* Apaga el interruptor
*/
public void apagar() {
if (commPort != null) {
commPort.close();
}
encendido = false;
fireActionPerformed("off");
}
/**
* Cada vez que se invoca este metodo conmuta el estado anterior. Si
* estaba encendido se apaga y si estaba apagado se enciende.
*/
public void conmutar() throws ErrorInterruptor {
if (encendido) {
apagar();
} else {
encender();
}
}
/**
* Gatilla los
ActionListenercon la señal on si el estado* cambia a encendido u off si el estado cambia a apagado.
* @param action
*/
private void fireActionPerformed(String action) {
for (ActionListener al : listeners) {
al.actionPerformed(new ActionEvent(this, 0, action));
}
}
}
-----
Class Puerto:
import java.util.Vector;
public enum Puerto {
NINGUNO("NINGUNO", TipoPuerto.NINGUNO),
COM1("COM1", TipoPuerto.SERIAL),
COM2("COM2", TipoPuerto.SERIAL),
COM3("COM3", TipoPuerto.SERIAL),
COM4("COM4", TipoPuerto.SERIAL),
COM5("COM5", TipoPuerto.SERIAL),
COM6("COM6", TipoPuerto.SERIAL),
COM7("COM7", TipoPuerto.SERIAL),
COM8("COM8", TipoPuerto.SERIAL),
COM9("COM9", TipoPuerto.SERIAL),
COM10("COM10", TipoPuerto.SERIAL),
COM11("COM11", TipoPuerto.SERIAL),
COM12("COM12", TipoPuerto.SERIAL),
COM13("COM13", TipoPuerto.SERIAL),
ttyS0("/dev/ttyS0", TipoPuerto.SERIAL),
ttyS1("/dev/ttyS1", TipoPuerto.SERIAL),
ttyS2("/dev/ttyS2", TipoPuerto.SERIAL),
ttyS3("/dev/ttyS3", TipoPuerto.SERIAL),
ttyS4("/dev/ttyS4", TipoPuerto.SERIAL),
ttyS5("/dev/ttyS5", TipoPuerto.SERIAL),
ttyS6("/dev/ttyS6", TipoPuerto.SERIAL),
ttyS7("/dev/ttyS7", TipoPuerto.SERIAL),
ttyUSB0("/dev/ttyUSB0", TipoPuerto.SERIAL),
ttyUSB1("/dev/ttyUSB1", TipoPuerto.SERIAL),
ttyUSB2("/dev/ttyUSB2", TipoPuerto.SERIAL),
ttyUSB3("/dev/ttyUSB3", TipoPuerto.SERIAL),
LPT1("LPT1", TipoPuerto.PARALELO),
LPT2("LPT2", TipoPuerto.PARALELO),
LPT3("LPT3", TipoPuerto.PARALELO),
LPT4("LPT4", TipoPuerto.PARALELO),
lp0("/dev/lp0", TipoPuerto.PARALELO),
lp1("/dev/lp1", TipoPuerto.PARALELO),
lp2("/dev/lp2", TipoPuerto.PARALELO),
lp3("/dev/lp3", TipoPuerto.PARALELO),
usblp0("/dev/usblp0", TipoPuerto.PARALELO),
usblp1("/dev/usblp1", TipoPuerto.PARALELO);
private String nombrePuerto;
private TipoPuerto tipoPuerto;
Puerto(String port, TipoPuerto tp) {
this.nombrePuerto = port;
this.tipoPuerto = tp;
}
public String getNombrePuerto() {
return this.nombrePuerto;
}
public TipoPuerto getTipoPuerto() {
return tipoPuerto;
}
public boolean isPuertoParalelo() {
return getTipoPuerto().equals(TipoPuerto.PARALELO);
}
public boolean isPuertoSerial() {
return getTipoPuerto().equals(TipoPuerto.SERIAL);
}
public static Vector getPuertos(TipoPuerto tp) {
Vector vp = new Vector();
if (tp == null) {
for (Puerto p : Puerto.values()) {
if (p.getTipoPuerto().equals(TipoPuerto.NINGUNO)) {
vp.add(p);
}
}
} else {
for (Puerto p : Puerto.values()) {
if (p.getTipoPuerto().equals(tp)) {
vp.add(p);
}
}
}
return vp;
}
}
------
Class TipoPuerto
public enum TipoPuerto {
NINGUNO,SERIAL, PARALELO
}
dev095:
Thanks for sharing useful and informative knowledge. Networking is always attracts me and i am always ready to take ccsp, ccna training and online courses for networking.
AZsid:
Where did you get the value 4.7K? I accidentally bought a pack of 470 ohms and was wondering what my odds were.
Ken:
Thanks for the circuit + c prog... this is exatly what I needed. I spent untold hours trying to get the GPIO pins on my soekris net4801 (small embedded system running linux) to work, but to no avail. I just needed a simple circuit that I could use to programatically power cycle an external device (a camera). This works perfectly!
I also needed my circuit to be powered up by default, and off only when the coil is charged, so I used a 5V SPDT relay (Radio Shack part #275-240) instead of the reed relay you described, which I assume is SPST.
Here's the test setup (using the multimeter to test continuity):
http://www.flickr.com/photos/obeyken/3673426938/
Thanks again and cheers!
Ken
kd:
Just curious, but is there a way you can control each pin individually? ie a class like, serail->pin1=true; for pin 1 high? .. i have a need for controlling switched input, 8 pins of switched input... and if i could find a way to control each pin individually without having to purchase another chip i would appreciate it a lot. Linux or windows, either one. i run mandriva and xp. Ty.
Online drugs are addictive:
After several attempts and failures, It is finally working!The solid state relay definitely makes thing MUCH simplier!
Anonymous:
The circuit works, Its a cool project!! Must follow the circuit diagram carefully especially the transistor pin positions.
Anonymous:
I tried to construct the circuit mentioned above but failed to make it work properly. Does this circuit really works?
Greg:
Our company produces a line of serial controlled relays in which you can link 16 units together for a total of 64 relays that can be controlled by any unit that sends rs232 data. Please visit the website www.1st-choiceav.com to answer all your questions.
Panagiotis:
Hello everyone ... great work
I ve created the board and i heve checked it many times..!!
My problem is that the relay stays on whatever choise i made from the software.
the software works fine because i get 12 and -12 volts when i make the change but the relay always gets the 9 volts from the battery. plz helppppppppp
thanks
Anonymous:
can smbody tell me da code for controlling teh forward n reverse movement of a motor through serail port in c or c#
Ed:
the link to relay.c is broken, could you check it?
Thanks,
ed
Ed:
the link to relay.c is broken, could you check it?
Thanks,
ed
kesavamoorthi:
The given circuit diagram used to control only one relay. but how to use it to control 2 relays? also pls send the VB version and c Version of program to my id to use it with windows not unix
Anonymous:
Please send more pictures
thank you
Anonymous:
I am totally new to this and i am not sure how to set this up there is a few miner changes the only connection that i am using is a usb to 15 pin 2 column connector and i am having a bit of trouble with c++ programing please help- this is the error log after compiling in dev-c++
-\Documents and Settings\Administrator\Desktop\relay.c C:\Documents and Settings\Administrator\Desktop\C sys/ioctl.h: No such file or directory.
-\Documents and Settings\Administrator\Desktop\relay.c C:\Documents and Settings\Administrator\Desktop\C termios.h: No such file or directory.
- C:\Documents and Settings\Administrator\Desktop\relay.c In function `main':
-18 C:\Documents and Settings\Administrator\Desktop\relay.c `O_NDELAY' undeclared (first use in this function)
- (Each undeclared identifier is reported only once
- for each function it appears in.)
-31 C:\Documents and Settings\Administrator\Desktop\relay.c `TIOCMSET' undeclared (first use in this function)
-32 C:\Documents and Settings\Administrator\Desktop\relay.c `TIOCMGET' undeclared (first use in this function)
-33 C:\Documents and Settings\Administrator\Desktop\relay.c `TIOCM_DTR' undeclared (first use in this function)
-37 C:\Documents and Settings\Administrator\Desktop\relay.c `TIOCM_RTS' undeclared (first use in this function)
I am using a mac with vmware fusion running xp
redriderusa96@hotmail.com
chad:
The code was written to run on linux with the gcc compiler. It looks look you are running windows.
Your best bet is to try to find a library that will let you directly control the lines on windows. You might try:
http://www.wcscnet.com/CdrvLNBro.htm
I have found that it is usually easier to use an 8 bit chip like an arduino. You talk to the chip over standard serial and the chip controls your relays.
thanks
chad
Anonymous:
No need to purchase an expensive 3rd party library! If you're coding in "c" on a more recent NT-based Windows OS (e.g. Windows 2000, XP, Vista), you'll need to use the win32 APIs to use the serial port; normal "user-land" programs are not allowed to talk directly to the serial-port in NT-based Windows OSes, only the kernel is allowed to do that. This means that functions like "outp", "outportb", "bios_serialcom" etc. don't work. Instead, you have to #include windows.h and winbase.h, then use win32 APIs such as "CreateFile" and "EscapeCommFunction". For example, to control the DTR line, you could use this code for a win32 console application:
#include stdio.h
#include conio.h
#include twindows.h
#include winbase.h
void main ()
{
HANDLE SerialPort;
int Error, CallStatus;
SerialPort = CreateFile(TEXT("COM1"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (SerialPort==INVALID_HANDLE_VALUE)
{
Error=GetLastError();
printf("error opening serial port! :-(\nError code: %i\nPress any key to exit...\n", Error);
getch();
}
else
printf("serial port open\n");
CallStatus=EscapeCommFunction(SerialPort,CLRDTR);
if (CallStatus==0)
{
printf("error clearing DTR of serial port\nPress any key to exit...\n");
getch();
}
else
{
printf("DTR cleared\nPress any key to exit...\n");
getch();
}
}
more info can be found at MSDN
Steve:
Hi Guys.
This thing looks cool, but i wanted to know if any of you knows how to timer it.
example. i want the motor run for 5 seconds and then off. and i want to control it in both ways the motor, forward and backward.
i would appreciate it if any of you guys can teach me how.
thanks a lot. cool page. cool people.
Steve
FREMONT, CA, USA
estchen@gmail.com
rausted:
In the source, Just add a delay command with the Millisecs you want the pin to stay high, between the set high and set low commands.
Anonymous:
I built one too: http://www.nsit.info/commcontrol/
The VB.Net 2005 code is there as well.
Belfry:
Ahhh--serial ports. They're going away, but most of us still have at least one in the form of a header on our motherboards--which I'm making use of thanks to this blog! Special thanks to the anonymous poster who provided this: http://www.nsit.info/commcontrol/. The program I wrote is largely based on it and controls an Elk-924 sensitive-trigger relay ($12.14 through Amazon) which sends power to a hard disk array within my computer case.
One has to apply a registry tweak to prevent the triggering action during boot up:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\ACPI\PNP0501\4&2658d0a0&0\Device Parameters]
"SkipEnumerations"=dword:ffffffff
Before you apply this make sure the "4&2658d0a0&0" key name is the same on your machine. Apply it only to the key corresponding to the port you wish to use (the name "com1" or "com2" will appear within the values.)
This registry tweak disables the serial port plug-and-play enumeration, which raises and lowers RTS and DTR during boot-up and trips the relay. So don't use it if you need to use other devices with the serial port you intend to use.
I use a raised RTS signal to trigger my relay (no looping required in VB.) This is pin 7 on my motherboard header and all db-9 serial ports.
The code was compiled on Visual Basic Express 2008 (free from Microsoft). This package will install about 700 MB worth of stuff, including .Net 3.5, but the code still works with .Net 2.0. Hint: configure three buttons on the form first, add a notifyicon1 from the toolbox, then paste the code. I can provide an executable in a zip file (for use on COM1) to those who request it.
Imports System.IO.Ports
Public Class Form1
Public s As New SerialPort
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Me.Load
s.Close()
s.PortName = "COM1"
s.BaudRate = 9600
s.DataBits = 8
s.Parity = Parity.None
s.StopBits = StopBits.One
s.Handshake = Handshake.None
s.Open()
s.RtsEnable = True
NotifyIcon1.ShowBalloonTip(2710)
Minimizetotray()
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If s.IsOpen Then GoTo 1
s.Open()
1: s.RtsEnable = True
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
s.RtsEnable = False
s.Close()
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
If s.IsOpen Then GoTo 2
s.Open()
2: s.RtsEnable = False
s.Close()
s.Dispose()
NotifyIcon1.Dispose()
End
End Sub
Private Sub Minimizetotray()
Me.Visible = False
Me.ShowInTaskbar = False
NotifyIcon1.Visible = True
End Sub
Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
If e.CloseReason = CloseReason.UserClosing Then
e.Cancel = True
Me.Visible = False
Me.NotifyIcon1.Visible = True
End If
End Sub
Private Sub NotifyIcon1_MouseClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles NotifyIcon1.MouseClick
Me.Visible = True
Me.ShowInTaskbar = True
End Sub
End Class
Fizz:
hi,thanks for the code,but i'm not a great porgrammer...i read but not understand..can u send me the zipwith the executable please? to dannicantero@yahoo.com.ar ,thanks in advance!!
Belfry:
The above code has some creature comforts--namely starting hidden, a task tray icon, a balloontip and closing to the task tray. For the most minimal functionality just leave off the last three subs, delete these lines in the first sub:
NotifyIcon1.ShowBalloonTip(2710)
Minimizetotray()
And delete this line: NotifyIcon1.Dispose()in the last remaining sub.
But the creature comforts are what really makes this hum for me!
Milton:
Hello. Thanks for the code. I am trying to teach myself VB at the moment. Could you please email me the VB file and executable file? My email address is miltonsbells@att.net.
Thanks
Milton
www.miltonsbells.com
chad:
thanks for posting your code. I'll have to give it a try.
Ron Kessler:
I just finished a VB 2005 app to control this circuit. I did some testing and I can control a 30Amp accessory relay that is used in automotive applications. This app turns the Com1 ports DTR.Enable = True or False to control the relay.
If anyone wants this app I can zip it up and send it your way. This stuff is really cool.
I can also convert it to a C# app if someone needs it.
My app does everything in code using no Serial Control or MSComm control. Very simple and straight forward.
Cheers
betsson:
Thanks for information.
Anonymous:
if i get that sample code ,that will be very use full for me
thanks
Dinu
John Joseph:
Hi Mr.Ron,
I too need that vb.net 2005 application.
can u send it to me (Email: jcb871@gmail.com)
I hav a doubt too...
did u use the same circuit which is given above?
pls reply to my mail. Its a bit urgent
Mutahira:
Hello Ron Kessler
can you please mail me the code you have written to control that circuitry on mutahirakhan@hotmail.com
I have a circuitry very similar to the one described in this forum. I am also controlling the relays through the serial port.
But I am not a good programmer I am unable to send data from .net to the microcontroller.
can you please mail me the code ASAP!
Thank you so much in advance
Rich Nguyen:
Dear Ron,
I go thru this site by chance, I am really interested with your VB 2005 app and hope I can use it for my project which is control things using serial port
Can you send it to my email address please?
Thanks
Rich
dani:
Hi Ron...
im in need for VB2005 code to send signal to my parallel port... can u help me out
my mail id is dan_inurheart@yahoo.co.in
A3:
I would like to control the rs232 port, also the individual pins if possible, and for in and output. May I benefit from your experiences and results? I am new in VB 2005 / VB.Net.
Mohamed sha:
I am doing a project based on serial port RS-232 to run a motor for a demo. So please send the vb.net code to complete the project successfully!!!
with Regards
M.Mohamed sha
Anonymous:
Can you send this code to me please. I need it as soon as possible. thanks
pitpat1969:
Hi
I am not so good programmer and I would like to make this kit working like this: each time I run an .exe file i would like to switch on the relay just once (lets say for half a second)and then release it. Can you please help me? If it is possible can you make for me this .exe file because I am not getting well with programming and compilers... :-(
Thank you again
Pit
azam:
Dear Cheers,
I am software developer and i need to control the relay through comport in vb.net 2005,can you help me please.
azam khan
software Developer
Pakistan
Anonymous:
can any one write code for VB6 since I have no idea c laguage.
Thanks
chad:
You may not be able to do this in VB. It requires low level access to the serial port. Languages like VB abstract a lot of that stuff and don't always allow you low level access.
If you need to use VB, I would suggest you get an arduino microcontroller. You can easily communicate with it and VB over serial. Tons of info for that on the web. Then have the arduino control the relay.
anonymous:
how do i control the voltage output of serial port to turn on or off the dc motor?
and i want to use visual basic 6.0 as my programming language.can you send me a source code to control the dc motor?tnx...please reply me...
Bruno:
Hello,
I've built the circuit, but somehow the relay just switches ON. Then, it ignores the DTR I think. Even if I remove the db9 from the serial port, it stays on. Only when I remove the power from the circuit, it switches off.
Any ideas of what I could have done wrong ?
Thanks =)
Panagiotis:
i have the same problem did you find the solution???
sanjay:
if u run the program previously and it swicthing on then help me how u done it. from where u runing it and tell me command to run itand if possible send me code of ur program.
i am runing short of time.please.............
chad:
First thing would be to narrow stuff down. Unplug it from the serial port and remove the relay. Now take a meter and see if you have current from the transistor (where it connects to the relay). You shouldn't. Then apply current to the diode (where DTR connects). The circuit should open then. If then works fine, then connect up DTR and try the same thing.
One thing I have noticed is that reed relays like I use, can fuse open very easily. If you draw too much current through them (like when a motor starts up) it can fuse the contacts open. So you might check that.
Ionut:
I saw that after DTR pin 4 there is a diode and then the resistor.. I started to learn electronics by my own and I chose this project as my first project (well.. not exactly this one, but this one:
http://www.instructables.com/id/EHVZ11WRT7EPD7QSQ9/
However, the author of the project above was saying he was inspired by your project so I took a look here. Indeed, the schematics look very much alike. I was wondering why you use that diode before the resistor..
Thank you,
Ionut
chad:
The diode makes sure that no current flows back into the serial port pin. It is more of safety feature. You can read on on diodes at http://en.wikipedia.org/wiki/Diode
Post new comment