Friday, 19 October 2012

PC Hall effect sensor parallel port interface

This is a circuit I built as a rev counter for my wind turbine experiments. I  started using a LED and MEL12 phototransistor, but sunlight played havoc with that, so I went to a Honeywell SS443R hall effect sensor plus a small magnet.

The circuit I used only required a few mods to work with the 443, and here it is (click on it to download a larger view):
I powered the circuit from a USB port,since it was easy to get to, but a takeoff from the pc power supply would work too (Black and Yellow wires from pc disk drive plug)

It might be possible to use the 443 without the LM339, but since I was already using it I kept it. You can add a second interface via pins 5&6 of the 339 outputting to pin 1, which connects in the same way as pin 2, only to pin13 of the parallel port instead of 12. This is how I compared versions of my wind turbines.

Be aware that the parallel port status byte address varies between pcs, normally using either x378 or x3bc, so you will need to test which you have.

I have included the c code for my parallel port monitor program below: it outputs lines to a file that look like this:

Theres a part in the middle that you can ignore that also does a "wget" to send to a web server
// monitor parallel port  input pins (11,12,13,15) and count time between pulses
// must run with root privileges or will segfault
// parallel port addr +1 = input status byte
// bits 4567 = pins 13,12,10,11 respectively
#include <stdio.h>
#include <sys/io.h>
#include <time.h>
#include <sys/types.h>
#include <string.h>
#include <math.h>
#include "../c_headers/myutils.h"
#define IOPORT 0x0378
// #define IOPORT 0x03bc
#define COMMAND  "wget -q -O /tmp/wget.txt "
#define CMDTAIL  "'  >> /tmp/data.txt "
#define WEBADDR  ""
#define LIMIT 50 // sense test -variance allowed between readings limited to 50%
FILE *fp;
struct timeval  IntervalStartTime,  *pst;
struct timespan {
struct timeval StartTime;
struct timeval EndTime;
double TimeElapsed;
int avgrpm;
int lastreading;};
struct timespan Port1,Port2,period, *ts; double Tested,Interval=10.0; // display interval is 10 secs
// function prototypes here
void CalcElapsed(struct timespan *); void PeriodCalc(struct timespan *); // time for period
void WriteLog(char *);
main() {
unsigned char c,p,port;
char dbuff[20], * dbuffp; // date/time array & pointer
char data[64],* datap; // data line buffer & pointer
char cmd[128],* cmdp; // cmd buffer & pointer
char  *argv[64];       // command line argument array (for runcmd func) int result,samplecount=0;
double duration;
int iobase=IOPORT; Port1.avgrpm=Port2.avgrpm=0;
/* Get access to the ports */
if (ioperm(iobase, 3, 1)) {perror("ioperm");}
gettimeofday(&period.StartTime, NULL); // get start time
Port2.StartTime=Port1.StartTime=period.StartTime;      // init timers
int last_status=0;
int p1avg[10],p1index,p2avg[10],p2index;
while (1) // must ctl-c to exit
     { //     c=inb(0x0379);
     if( !last_status ) {last_status=c;} // init last_status if first time thru
     if( c != last_status ) // keep reading port until change
  last_status=c; // save latest status as last
  if ( p = c & 0x30 ) // pin 12 or 13 set active? (low)
     if ( !(port = (p ^ 0x20))) // if its pin 12
 { // work out when this pin last set active (=pulse time)
// tscalc.StartTime=Port1StartTime;
printf("Port 1: %5.6f : %d rpm %d avg\n",ts->TimeElapsed,ts->lastreading,ts->avgrpm);
  if ( !(port = (p ^ 0x10))) // if its pin 13   {
ts=&Port2; PeriodCalc(ts);
printf("Port 2: %5.6f : %d rpm %d avg\n",ts->TimeElapsed,ts->lastreading,ts->avgrpm);
} // exit pin-active section
 } // exit port-changed section
// tscalc.StartTime=IntervalStartTime;
if (ts->TimeElapsed> Interval) // has interval elapsed? if so, ...
// if both ports non-zero, (ie have had readings sent) do output if  (Port1.avgrpm>0&&Port2.avgrpm>0)
   printf("Status: Port1: %d rpm; Port2: %d rpm\n", Port1.avgrpm, Port2.avgrpm);

dbuffp=&dbuff[0]; //store date,time
GetTime(dbuffp); strcpy(data,dbuff); // copy date,time into data line
sprintf(data+strlen(data),";Port1:;%drpm;Port2:;%drpm", Port1.avgrpm, Port2.avgrpm);
strcpy(cmd,COMMAND); // copy command prefix to buffer
strcpy(cmd+strlen(cmd),WEBADDR); // copy url into cmd line
strcpy(cmd+strlen(cmd),data); // copy data into cmd line
// DEBUG puts(cmd);
parsecmd(cmd,argv); // set up argv array for runcmd
runcmd(argv); // run external command and return
strcpy(data+strlen(data),"\n"); // append cr
WriteLog(data);  } ts->StartTime=ts->EndTime; // reset interval starting time } // end interval-elapsed processing
    } // ctl-c exit
void CalcElapsed(struct timespan *ts) // elapsed time to millisec accuracy
long seconds, nseconds;
gettimeofday(&ts->EndTime, NULL);
seconds  = ts->EndTime.tv_sec  - ts->StartTime.tv_sec;
nseconds = ts->EndTime.tv_usec - ts->StartTime.tv_usec;
ts->TimeElapsed = seconds + nseconds/1000000.0;
void PeriodCalc(struct timespan *ts) // time for period
int result = ceil(60 / ts->TimeElapsed);
// only record sensible time, else just replace prev reading
  int pctvar = 100*abs(result - ts->lastreading) / result; // test within variance tolerance
  if(pctvar < LIMIT)
     { // if avgrpm not 0, update average rpm, else just insert
     if(ts->avgrpm > 0)
 ts->lastreading=result; // save this reading
     else {ts->avgrpm=result;}      }
{printf("variation error: last: %d rpm; this: %d rpm\n", ts->lastreading, result);
 ts->avgrpm = 0;
 ts->lastreading=0; // save this reading
else { ts->lastreading=result; } ts->StartTime=ts->EndTime; // save end time as new start
} void WriteLog(  char *line)
fp = fopen("/tmp/data.txt","a");
if (!fp) { perror("error creating file"); }


No comments:

Post a Comment