Rievo Lie detector

Around 1997 I imported the software which was published by Rolf Hilchner from (somewhere near) Neuss in Germany. They sold the PTS brand and a lot of gimmicks. Party gear, to get things going. Now, germans are not known for their sense of humor or their easy going mind. Yet, this is all bullshit. If there is one people in the world that knows how to party, it's the germans. Many have a dedicated Party Keller (Party cellar) and not for offering guests a place to sleep. There may be a bed involved, but not for closed eye activities. :0)

One of the party gimmicks was the Rievo lie detector. You see the box in which it was shipped, in the picture above. The lines on the box translate to something like:

Below is the backside of the box. You run it through Gogle translate.

The general idea was: hook up someone, ask him/her some naughty questions and then let the situation get our of control. I envy the germans for their parties. I'd love to loose all my sense of humor, only to have their party culture...

Convert to an oscilloscope

On the right you see the circuit drawing for the original "Rievo Luegendetektor" as it was sold by Rievo. The design was ingeniously simple. A cheap yet more than enough accurate serial low-power 10 bit Analog to Digital Convertor (ADC) was always enabled and was read out continuously by the software.

It was connected to the COM (= serial) port. It also took its power from the COM port. The subject to be tested was hooked up across the C2 capacitor (far right).

By just clipping out the 100 k resistor and the C2 capacitor, you get a simple oscilloscope or data logger. Whatever you choose. The sample is taken from pin J2c. For more information, consult fruttenboel/asm/lda.html


Create a data logger or DSO

With some clever software I created a slow sampling DSO or a fast enough sampling data logger. Below is the source code:

MODULE Sample;

FROM    barith      IMPORT  nand, or, test, xor, MulDiv;
FROM    Display     IMPORT  ClrEOS, ClrEOL, SetCursorPosition;
FROM    InOut       IMPORT  Write, WriteLine, WriteLn, WriteCard, WriteHex, WriteString,
                            RedirectOutput, CloseOutput;
FROM    Keyboard    IMPORT  GetKey, KeyPressed;
FROM    LowLevel    IMPORT  IOsize, InPort, OutPort;
FROM    SYSTEM      IMPORT  ASSEMBLER;
FROM    System      IMPORT  GetArg, Terminate;
FROM    Strings     IMPORT  Assign, CompareStr;
FROM    Storage     IMPORT  Available, ALLOCATE, DEALLOCATE;
FROM    Timer       IMPORT  OpenTimer, CloseTimer, ReadTimer, Wait;

FROM    NumberConversion    IMPORT  StringToCard;

CONST   TxDbit      = 40H;      (* bit 6    *)
        ClkBit      =  2H;      (* bit 1    *)
        DataBit     = 20H;      (* bit 5    *)
        Tab         = 11C;
        Esc         = 33C;
        AdcAccuracy =  10;      (* for TLC 1549 *)

TYPE    PowerState  = (On, Off);                (* ADC power states                 *)
        ClockState  = (Low, High, Strobe);      (* Three options for ADC clocking   *)
        SampleMode  = (DMM, Timed, FreeRunning);

VAR     ComBase, Value, seconds,
        BufferSize, MaxRate, Speed,
        SamplePeriod                    : CARDINAL;

        Filename            : ARRAY [0..49] OF CHAR;
        DataPtr             : POINTER TO ARRAY [0..32000] OF CARDINAL;
        EscapePressed       : BOOLEAN;
        Sampling            : SampleMode;


PROCEDURE UserMessage (Number : CARDINAL);

BEGIN
    WriteLn;
    CASE Number OF
      1 :   WriteLine ("CopyLeft Jan Verhoeven 1999. This is GNU GPL style FREE software.");
            WriteLine ("Please find yourself a copy of the GPL. It might be useful...");
            |
      2 :   WriteLine ("The commandline for using SAMPLE is as follows:");          WriteLn;
            Write (Tab); WriteLine ("SAMPLE COMx mode rate secs filename.ext");     WriteLn;
            WriteString ("Where:");
            Write (Tab); WriteLine (" - COMx is the COM port to which the ADC is connected,");
            Write (Tab); WriteLine (" - mode is the sampling mode (DMM, Free, Test, Timed),");
            Write (Tab); WriteLine (" - rate is the number of samples per second (0..100),");
            Write (Tab); WriteLine (" - secs is the nr of seconds to record,");
            Write (Tab); WriteLine (" - 'filename.ext' is the device to send the data to.");
            WriteLn;
            WriteLine ("The commandline is CASE SENSITIVE! Perform a Test-run before a Free run.");
            WriteLn;
            WriteLine ("mode = DMM or Test  => no parameters are needed.");
            WriteLine ("mode = Free         => only the sample-time is needed.");
            UserMessage (1);
            |
      3 :   WriteLine ("Error 3: In number-entry, please use only decimal digits (0..9).");
            WriteLine ("Thank you for your cooperation.");
            UserMessage (1);
            |
      4 :   WriteLine ("Error 4: The samplerate may not exceed 100 samples per second.");
            UserMessage (1);
            |
      5 :   WriteLine ("Error 1: Please specify the COM port the LD-ADC is connected to.");
            |
      6 :   WriteLine ("Error 2: Not enough memory available to run this application.");
            UserMessage (1);
            |
      7 :   WriteLine ("Error 5: No sample time specified!");
            WriteLine ("         Please input how many seconds you want to record.");
            UserMessage (1);
            |
      8 :   WriteLine ("Error 6: Too many datapoints. Make sure rate * seconds does not exceed 32000.");
            UserMessage (1);
    END;
    WriteLn;
END UserMessage;


PROCEDURE AdcPower (State : PowerState);

VAR     al, port        : CARDINAL;

BEGIN
    port := ComBase + 3;
    al := InPort (port, Byte);
    CASE State OF
        On  : al := or (al, TxDbit);        |
        Off : al := nand (al, TxDbit);
    END;
    OutPort (port, al, Byte);
    Wait (5);                   (*  Wait 5 ms to stabilise ADC  *)
END AdcPower;


PROCEDURE Clock (State  : ClockState);

VAR     al, port        : CARDINAL;

BEGIN
    port := ComBase + 4;
    al := InPort (port, Byte);
    CASE State OF
        Low     : al := nand (al, ClkBit);  |
        High    : al := or (al, ClkBit);    |
        Strobe  : al := xor (al, ClkBit);
                  OutPort (port, al, Byte);
                  al := xor (al, ClkBit);
    END;
    OutPort (port, al, Byte);
END Clock;


PROCEDURE ReadWord (VAR value : CARDINAL);

VAR     al, index, port     : CARDINAL;

BEGIN
    port := ComBase + 6;
    value := 0;
    FOR index := 1 TO AdcAccuracy DO
        value := value * 2;
        al := InPort (port, Byte);
        IF test (al, DataBit) THEN
            INC (value)
        END;
        Clock (Strobe);
    END;
END ReadWord;


PROCEDURE CalibrateTime (Iterations : CARDINAL) : CARDINAL;

VAR     i, j, first, last,
        zero, time              : CARDINAL;

BEGIN
    WriteLn;
    OpenTimer;
    first := ReadTimer ();
    FOR i := 1 TO Iterations DO
        (*      Nothing     *)
    END;
    last := ReadTimer ();
    zero := first - last;               (* determine looping overhead   *)

    first := ReadTimer ();
    FOR i := 1 TO Iterations DO
        ReadWord (j);
    END;
    last := ReadTimer ();           (* determine total looping time     *)
    time := first - last - zero;

    RETURN (time);
    CloseTimer;
END CalibrateTime;


PROCEDURE FindAddress (Port : CARDINAL) : CARDINAL;

VAR     Result      : CARDINAL;

BEGIN
    ASM
        MOV  AX, 0
        MOV  ES, AX
        MOV  BX, 0400H      (* Start address of COM port addresses  *)
        MOV  AX, Port
        SHL  AX, 1
        ADD  BX, AX
        MOV  AX, ES:[BX]
        MOV  Result, AX
    END;
    RETURN Result;
END FindAddress;


PROCEDURE CheckArguments;

VAR     Option              : ARRAY [0..49] OF CHAR;
        count, ComPort,
        rate, index         : CARDINAL;
        char                : CHAR;
        done                : BOOLEAN;
        MaxSex              : REAL;

BEGIN
    GetArg (Option, count);     (* Get first argument from command line *)
    IF count # 4 THEN                       (* IF invalid tail, get out *)
        UserMessage (5);
        UserMessage (2);
        Terminate (5);                      (* Inform the moron of it   *)
    END;
    char := Option [3];
    ComPort := ORD (char) - ORD('0');
    ComBase := FindAddress (ComPort - 1);
    WriteString ("Specified COM-port: ");   WriteCard (ComPort, 1);
    Write (Tab);
    WriteString (" base address: ");        WriteHex (ComBase, 4);
    WriteLine ("h.");

    GetArg (Option, count);                 (* Retrieve sample-rate     *)
    IF count = 0 THEN
        UserMessage (2);            (* if no samplerate, tell the man   *)
        Terminate (2);
    END;
    IF CompareStr (Option, "DMM") = 0 THEN
        Sampling := DMM;
        RETURN;
    ELSIF CompareStr (Option, "Test") = 0 THEN
        WriteString ("This system is capable of taking ");
        WriteCard (MaxRate, 6);
        WriteLine (" samples per second.");
        WriteLine ("Press ENTER to continue.");
        WaitTrigger (15C);
        Terminate (255);
    ELSIF CompareStr (Option, "Free") = 0 THEN
        Sampling := FreeRunning;
        rate := MaxRate;
    ELSIF CompareStr (Option, "Timed") = 0 THEN
        Sampling := Timed;
        GetArg (Option, count);
        StringToCard (Option, rate, done);
        IF NOT done THEN
            UserMessage (3);            (* error converting ASCII to number *)
            Terminate (3);                      (* report it to the user    *)
        END;
        SamplePeriod := 1000 DIV rate;
        WriteString ("The sample rate is ");        WriteCard (rate, 1);
        WriteString (" samples per second, or ");   WriteCard (SamplePeriod, 1);
        WriteLine   (" ms between samples.");
    END;

    GetArg (Option, count);
    IF count = 0 THEN
        UserMessage (7);            (* if no sampletime, tell the man   *)
        Terminate (7);
    END;
    StringToCard (Option, seconds, done);
    IF NOT done THEN
        UserMessage (3);            (* error converting ASCII to number *)
        Terminate (3);                      (* report it to the user    *)
    END;
    MaxSex := FLOAT (seconds) * FLOAT (rate);
    IF MaxSex > 32000.0 THEN
        UserMessage (8);
        Terminate (8);
    END;
    BufferSize := seconds * rate;

    GetArg (Option, count);             (*  Get device where to store data  *)
    IF count = 0 THEN
        Option := "Sample.Adc"          (*  Generate default file   *)
    END;
    Assign (Option, Filename);
    WriteString ("The data will be stored on device "); WriteString (Filename);
    WriteLine (".");
    WriteLn
END CheckArguments;


PROCEDURE WaitTrigger (trigger : CHAR);

VAR     k   : CHAR;

BEGIN
    LOOP
        REPEAT
            (*      Do nothing      *)
        UNTIL KeyPressed ();
        GetKey (k);
        IF k = trigger THEN  EXIT
        ELSIF k = Esc THEN
            EscapePressed := TRUE;
            EXIT;
        END
    END
END WaitTrigger;


PROCEDURE LongWait (time : CARDINAL);       (*  Wait increments of 1 ms *)

VAR     i       : CARDINAL;

BEGIN
    FOR i := 1 TO time DO
        Wait (1);
    END
END LongWait;


PROCEDURE ShowVolt;

VAR     value, x, y     : CARDINAL;
        k               : CHAR;

BEGIN
    WriteLine ("Press SPACE to start/stop measuring.");
    WaitTrigger (" ");
    IF EscapePressed THEN  RETURN  END;
    SetCursorPosition (0, 0);
    ClrEOS;
    LOOP
        ReadWord (value);
        SetCursorPosition (2, 4);
        WriteCard (value, 5);
        Wait (500);
        IF KeyPressed () THEN
            GetKey (k);
            IF k = " " THEN  EXIT  END
        END
    END
END ShowVolt;


PROCEDURE TakeSamples (mode : SampleMode);

VAR     i, val, max     : CARDINAL;
        k               : CHAR;

BEGIN
    WriteLine ("Press SPACE to start measuring.");
    WriteLine ("SAMPLE will take samples, store them in memory and afterwards save them.");
    WaitTrigger (" ");
    max := BufferSize - 1;
    CASE mode OF
    FreeRunning : FOR i := 0 TO max DO
                    ReadWord (val);
                    DataPtr^[i] := val;                 (*  store the value in memory   *)
                  END;                              |
    Timed       : FOR i := 0 TO max DO
                    ReadWord (val);
                    DataPtr^[i] := val;                 (*  store the value in memory   *)
                    LongWait (SamplePeriod);
                  END
    END;
    WriteLine ("Sampling done. Writing data to specified device.");

    RedirectOutput (Filename);              (*  send screen-output to file  *)
    FOR i := 0 TO max DO
        WriteCard (i, 5);
        Write (",");
        Write (Tab);
        WriteCard (DataPtr^[i], 5);
        WriteLn;
    END;
    CloseOutput;                            (*  restore output to screen    *)

    WriteLine ("Data written to file. File closed.");
END TakeSamples;


PROCEDURE Initialize;       (* Various initialization steps     *)

BEGIN
    EscapePressed := FALSE;
    Speed := CalibrateTime (10) DIV 10;
    MaxRate := MulDiv (1000, 1193, Speed);
    CheckArguments;
    IF Available (BufferSize * 2) THEN
        ALLOCATE (DataPtr, BufferSize * 2);
    ELSE
        UserMessage (6);
        Terminate (6);
    END;
END Initialize;


BEGIN
    Initialize;
    AdcPower (On);
    ReadWord (Value);       (*  Get rid of first value after PowerUp    *)
    IF Sampling = DMM THEN
        ShowVolt;
    ELSE
        TakeSamples (Sampling);
    END;
    WriteLn;
    WriteLine ("Shutting down ADC and clearing buffers.");
    AdcPower (Off);
    DEALLOCATE (DataPtr, BufferSize * 2);
    UserMessage (1);
END Sample.
   

Page created September 13, 2012 and