CyberKicker: A remote-controlled board resetter

Tonights project is a parallel port (!) based eight channel reset controller. The purpose is to enable development boards that have frozen to be rebooted remotely. The interface itself is dead simple, it just connects the data pins from the parallel port to some optical relays, with a (value tbd) current limiting resistor in series. The whole project, including a workable Linux software driver to work the parallel port (C) along with a CGI script (Perl) to allow the board to be controlled remotely. As usual, code and schematics after the break, this time public domain. Note that I made a prototype circuit up, but have not actually made the PCB yet.

Project file: cyberkicker.tar

Here is the schematic:

The board layout:

Here is the C ‘driver’ program:

/* Cyber Kicker, a utility to remotely reset a development board
 *
 * Written by Matt Mets in 2007
 * This code is released into the public domain
 *
 * Adapted from an example by Tomi Engdahl <then@delta.hut.fi>
 * http://www.epanorama.net/circuits/parallel_output.html
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/io.h>
 
#define base 0x378      /* base address of parallel port */
 
#define PULSE_PIN_OK            0
#define PULSE_PIN_BAD_PIN       1
#define PULSE_PIN_BAD_TIME      2
#define PULSE_PIN_PORT_ERROR    3
 
/* Pulse the selected pin high for a given amount of time
 *
 * address: address of the parallel port
 * pin_number: pin 0-7 (corresponds to pin 2-9 on the parallel port)
 * time: time to pulse the pin for, in seconds
 */
int pulsePin( int address, int pin_number , int time ) {
    unsigned char pin_mask;
 
    if( (pin_number < 1) || (pin_number > 8) )
        return PULSE_PIN_BAD_PIN;
 
    if( (time < 1) || (time > 10) )
        return PULSE_PIN_BAD_TIME;
 
    if( ioperm( address,1,1 ) )
        return PULSE_PIN_PORT_ERROR;
 
    printf( "Pulsing Pin #%i for %i seconds:\n", pin_number, time );
 
    pin_mask = 1 << (pin_number - 1);
 
    outb( pin_mask, address );
    sleep( time );
    outb( 0, address );
 
    return PULSE_PIN_OK;
}
 
 
int main( int argc, char **argv ) {
    int ret;
 
    if( argc != 3 ) {
        printf( "%s: incorrect number of arguments specified\n", argv[0] );
        printf( "Usage: %s pin# time\n", argv[0] );
        printf( "Options:\n" );
        printf( "  pin#: Pin to be toggled, in the range 1 to 8\n" );
        printf( "  time: Length of time to toggle pin for, in seconds.\n" );
        printf( "        Valid times are 1 to 10 seconds\n" );
        return PULSE_PIN_BAD_PIN;
    }
 
    ret = pulsePin( base, atoi(argv[1]), atoi(argv[2]) );
    switch( ret ) {
        case PULSE_PIN_OK:
            printf( "Success\n" );
            break;
        case PULSE_PIN_BAD_PIN:
            printf( "%s: Pin value out of range (must be 1-8)\n", argv[0] );
            break;
        case PULSE_PIN_BAD_TIME:
            printf( "%s: Time value out of range (must be 1-10)\n", argv[0]);
            break;
        case PULSE_PIN_PORT_ERROR:
            printf( "%s: Unable to open port for I/O (suid?)\n", argv[0] );
            break;
    }
    return ret;
}

And the CGI interface in Perl:

#!/usr/bin/perl
# CGI frontend for the kicker program
#
# Written By Matt Mets in 2007
# This program is released into the public domain
#
# Some Perl help from:
# http://homepages.paradise.net.nz/milhous/cgi.htm
 
# Board definitions (todo: offload these to a file...)
@board_names = (
    "Board connected to output #1",
    "Board connected to output #2",
    "Board connected to output #3",
    "Board connected to output #4",
    "Board connected to output #5",
    "Board connected to output #6",
    "Board connected to output #7",
    "Board connected to output #8");
 
# Location of the kicker executable
$kicker = "/home/matt/projects/cyberkicker/kicker";
 
# Split the inputs passed by GET into a Hash
@values = split(/&/, $ENV{'QUERY_STRING'});
foreach $i (@values) {
    ($varname, $mydata) = split(/=/, $i);
    $get_vars{$varname} = $mydata;
}
 
$board = $get_vars{'board'};
$time = $get_vars{'time'};
$action = $get_vars{'action'};
 
 
# Header
print <<EndOfHeader;
Content-type: text/html\n\n
<html>
<head><title>CyberKicker: Reset a test board!</title></head>\n
<body>\n
EndOfHeader
 
# Execute page
if( $action eq "yes" ) {
    print( "Executing kicker program:<br>" );
    $result = system( $kicker, $board, $time );
    print <<EndOfExecute
<form action="test.cgi" method="get">
<input type="submit" name="action" value="return">
</form>
EndOfExecute
}
 
# Conformation page
elsif( $action eq "confirm" ) {
    if( ($board < 1) || ($board > 8) ) {
        print( "Invalid board specified; Redirececting..." );
        print( "<meta http-equiv=refresh content=\"2; url=test.cgi\">" );
    }
    print <<EndOfConfirm;
Lack of confidence: Really reset `@board_names[$board - 1]`?\n
<form action="test.cgi" method="get">
<input type="hidden" name="board" value="$board">
<input type="hidden" name="time" value="$time">
<input type="submit" name="action" value="yes">
<input type=\"submit" name="action" value="no">
</form>
EndOfConfirm
}
 
 
# Start page 
else {
    print "Please choose the board to reset:\n";
    print "<form action=\"test.cgi\" method=\"get\">";
    for( $i=1; $i<9; $i++ ) {
        print "<input type=\"radio\" name=\"board\" value=\"$i\">";
        print "@board_names[$i-1]\n";
        print "</input><br>";
    }
    print <<EndOfStart
Pulse Time(s):<input type="text" name="time" value="2" size="5">
<input type="hidden" name="action" value="confirm">
<input type="submit" value="Reset the board!">
</form>
EndOfStart
}
 
# Footer
print "</body></html>";
This entry was posted in tech. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>