I'm blown away at the cost of DAQ cards - we use one at work that costs £225 and provides 32-channels of isolated digital I/O and a timer; that's all! Have they never heard of Maplin? :)
Quite often I need a couple of digital inputs or a couple of digital outputs to interface with a bit of kit: some LED's, a sensor, a switch, a relay, etc. At work we do this all the time through high-cost industrial PCI cards. At home I do it through whatever comes to hand...
Keyboard controllers
Serial Port
Parallel Port - Even modern laptops often have a parallel port - cheap and cheerful - the types and modes SPP, Bi-directional, EPP, ECP, Multi-mode
Joystick/MIDI Port - analog inputs, digital inputs
USB devices - a £10 USB to Playstation2 contoller adapter allows a number of digital and analog inputs
pros and cons
cost
effort
inputs and outputs
drawing power
electrical isolation
danger!
speed, latency, real-time
Serial - USB adapters under Linux
"Extra Value Wasp Serial To USB Converter" £3.85 (£4.53 inc VAT)
http://www.ebuyer.com/UK/product/045196
And in the comments: -
LINUX DRIVER comment by: Kevin Smart Tue 26th June 2007 03:53:pm
"This device is detected by Linux kernel 2.6.20 as a Prolific PL2303 USB to serial adaptor driver; at least, until the manufacturer changes the chipset :o)"
Software
interfacing to serial/parallel/USB/Joystick under Linux
comedi
Java rxtx
Here's a Java application, "CarrierDetect.java" that demonstrates a single digital input on a serial line e.g. from a switch. It uses the carrier detect input on pin 1 of a D9 connector which can be shorted to pin 4 which the app detects. So make up a little cable for pins 1 and 4 that leads to a switch somewhere, e.g. on a door or window or fridge! Then modify the SerialEvent method to do whatever you want: animate an icon, play some music, phone the police, whatever. This hastily crafted version has a gui and sets the colour of a large label when the carrier detect state changes - yours can be useful!
CarrierDetect.java
NB: the gui was created in just a few minutes with NetBeans 5 so there's a lot of machine generated code (i.e. not my fault!). The rest of it was created with Eclipse 3.2.
It requires jars for rxtx, log4j and swing-layout-1.0.jar (from the NetBeans install).
/*
* CarrierDetect.java
*
* Created on 15 August 2007, 15:49
*/
package com.tecspy.serial;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.awt.Color;
import java.util.TooManyListenersException;
import org.apache.log4j.Logger;
/**
*
* @author michael
*/
public class CarrierDetect extends javax.swing.JDialog implements SerialPortEventListener {
private static Logger log = Logger.getLogger(CarrierDetect.class);
/** Creates new form CarrierDetect */
public CarrierDetect(java.awt.Frame parent, boolean modal) {
super(parent, modal);
initComponents();
setCdIndicator(false);
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
// //GEN-BEGIN:initComponents
private void initComponents() {
portTextField = new javax.swing.JTextField();
jLabel1 = new javax.swing.JLabel();
useButton = new javax.swing.JButton();
indicatorLabel = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setTitle("Carrier Detect");
portTextField.setText("/dev/ttyS0");
jLabel1.setLabelFor(portTextField);
jLabel1.setText("Port");
useButton.setText("Use");
useButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
useButtonActionPerformed(evt);
}
});
indicatorLabel.setBackground(new java.awt.Color(255, 153, 51));
indicatorLabel.setForeground(new java.awt.Color(153, 0, 0));
indicatorLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
indicatorLabel.setText("unknown");
indicatorLabel.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
indicatorLabel.setOpaque(true);
org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(layout.createSequentialGroup()
.addContainerGap()
.add(jLabel1)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(portTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 103, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(useButton)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(indicatorLabel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 135, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.addContainerGap(39, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(layout.createSequentialGroup()
.addContainerGap()
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
.add(jLabel1)
.add(portTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.add(useButton)
.add(indicatorLabel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 90, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
pack();
}// //GEN-END:initComponents
private void useButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useButtonActionPerformed
// use this port
setPort(portTextField.getText());
}//GEN-LAST:event_useButtonActionPerformed
private void setPort(String portName){
// open the given port create the event listener
// setCdIndicator(!cdIndicator);
try {
goPort(portName);
} catch (NoSuchPortException e) {
// TODO Auto-generated catch block
log.fatal("ugh", e);
} catch (PortInUseException e) {
// TODO Auto-generated catch block
log.fatal("ugh", e);
} catch (TooManyListenersException e) {
// TODO Auto-generated catch block
log.fatal("ugh", e);
}
}
private void goPort(String portName) throws NoSuchPortException, PortInUseException, TooManyListenersException{
SerialPort p = null;
CommPortIdentifier cpi = CommPortIdentifier.getPortIdentifier(portName);
p = (SerialPort) cpi.open("me", 2000);
p.notifyOnCarrierDetect(true);
p.addEventListener(this);
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new CarrierDetect(new javax.swing.JFrame(), true).setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel indicatorLabel;
private javax.swing.JLabel jLabel1;
private javax.swing.JTextField portTextField;
private javax.swing.JButton useButton;
// End of variables declaration//GEN-END:variables
/**
* Holds value of property cdIndicator.
*/
private boolean cdIndicator;
/**
* Getter for property cdIndicator.
* @return Value of property cdIndicator.
*/
public boolean isCdIndicator() {
return this.cdIndicator;
}
/**
* Setter for property cdIndicator.
* @param cdIndicator New value of property cdIndicator.
*/
public void setCdIndicator(boolean cdIndicator) {
this.cdIndicator = cdIndicator;
Color newColor = cdIndicator ? Color.green : Color.yellow;
String newText = cdIndicator ? "ON":"OFF";
indicatorLabel.setBackground(newColor);
indicatorLabel.setText(newText);
}
public void serialEvent(SerialPortEvent ev) {
// TODO Auto-generated method stub
int et = ev.getEventType();
switch (et) {
case SerialPortEvent.CD:
// boolean oldValue = ev.getOldValue();
boolean newValue = ev.getNewValue();
setCdIndicator(newValue);
}
}
}