Serial Wombat
a general-purpose digital interface device for hobbyists, engineers and students

 


Home
Overview
Protocol
Documentation
Getting Started
Connectivity
Pin Mode SDK Beta
Sample Projects
WombatPanel
iTunes Control
Serial To TCP/IP
QBasic
'C'
Solar Water Display
Downloads
Contact Us
Purchase
Forum

Did you know...

 

Interfacing the Serial Wombat from QBasic

Ah, QBasic. That wonderful little gem that came with MS-DOS 5.0. It checked your code as you wrote it. It introduced me to the concept of a breakpoint. It provided support for any serial port you wanted to use (as long as you wanted to use COM1 or COM2). It automagically added returns when you declared a subroutine. In my experiences, no programming environment since has so thoroughly changed the landscape from what preceeded it.

And people are still using it, most often for its easy access to COM ports on pre-Windows NT OSes. If you look deep on your windows 98 program CD, you'll find it's still there.

Below is some sample code which will provide a framework for accessing a Serial Wombat through QBasic. After looking around on the web, I was not actually able to find a program which both read and wrote the COM port in binary. So it took me a while to figure out how to do this. Among the things that complicate this code:

  • QBasic apparently doesn't have a "byte" type. Everything is 16 bits or longer. The closest you can get to a byte is a 1 character string.
  • The ON COM handler doesn't really tell you alot about what caused you to get there, just that a COM port event happened. The best you can do is see what the last position written in the buffer is using LOC , and try to pull that many characters off using INPUT$. You then have to convert each character back to an integer. Sometimes this appears to result in I/O errors, I'm assuming if the buffers get out of sync with what you're expecting. This seems to coincide with when you do other stuff under windows while running your Basic program. The ON COM response appears pretty slow, as some times 3 characters can build up at 9600 baud.
  • You have to reference all your arrays from 1. Yech.

To use this framework, you basically just fill in the area between MainLoop and GOTO MainLoop. I've provided a subroutine TRANSACTION which will stream out whatever 8 bytes are in TXARRAY, and put the received response in RXARRAY. There's a variable RXRESULT which can be checked after TRANSACTION to see what happened. 1 means OK, 2 means a timeout happened, and 3 means the Wombat returned an Error Message.

I've written up a couple of simple subroutines which set a pin and read a pin's public data as examples. The example framework simply continously reads the Public data from Wombat Pin 2, which by default starts up as an analog to digital converter measurement pin.

I tested this on my Libretto 100CT, running Win98 SE. I love this little guy. Running a 166MHz Pentium, it's no screamer, but its charm is in its body, not its brains. You fold it up and it's about the size of a VHS cassette tape. You can get them on Ebay for under $100. I get way more comments about this tiny portable than my Core2 17" Dell inspiron. Shown here with a Wombat chip and my desktop's keyboard for scale.

 

 

 

 

 

 

The Code (download it here):

DECLARE SUB SETPin (Pin AS INTEGER, state AS INTEGER)
DECLARE SUB GetPublicData (Pin AS INTEGER, Result AS LONG)
DECLARE SUB TRANSACTION ()
DECLARE SUB RESYNC ()

'Declare an array for storing/passing RX Data
DIM SHARED RXARRAY(8) AS INTEGER

'RXCount is used by the interrupt to keep track of
'The number of bytes received so far.  Reset to 0 at the
'Beginning of each transaction
DIM SHARED RXCount AS INTEGER
'RX Result.  0 = In progress, 1=Success, 2 = Timeout
DIM SHARED RXRESULT AS INTEGER

'Array to pass transmit data in
DIM SHARED TXARRAY(8) AS INTEGER

        CLS

' Open serial port and surpress all hardware handshaking, set interrupt
        OPEN "COM1:9600,N,8,1,BIN,CD0,CS0,DS0,OP0,RS" FOR RANDOM AS #1
        ON COM(1) GOSUB RXHANDLER
        RXCount = 0
        RXRESULT = 0
        COM(1) ON

        CALL RESYNC

DIM Result AS LONG
MainLoop:

CALL GetPublicData(2, Result)
IF (RXRESULT = 1) THEN
        PRINT "Result was "; Result

END IF


GOTO MainLoop


  END


RXHANDLER:
  DIM rxin AS STRING
  n = LOC(1)
  rxin = INPUT$(n, 1)
  FOR i = 1 TO LEN(rxin)
  IF (RXCount < 8) THEN
        RXCount = RXCount + 1
        RXARRAY(RXCount) = ASC(MID$(rxin, i, 1))
  END IF
  NEXT
  IF (RXCount = 8) THEN
        RXRESULT = 1
  END IF
RETURN

SUB GetPublicData (Pin AS INTEGER, Result AS LONG)
        TXARRAY(1) = ASC("A")

        'Convert Pin to ascii
        TXARRAY(2) = Pin \ 10 + ASC("0")
        TXARRAY(3) = (Pin MOD 10) + ASC("0")
        'D for decimal:
        TXARRAY(4) = ASC("D")
        'Last 4 unused:
        TXARRAY(5) = ASC("U")
        TXARRAY(6) = ASC("U")
        TXARRAY(7) = ASC("U")
        TXARRAY(8) = ASC("U")

        CALL TRANSACTION

        IF (RXRESULT = 1) THEN
             Result = 0
             Result = Result + RXARRAY(8) - ASC("0")
             Result = Result + (RXARRAY(7) - ASC("0")) * 10
             Result = Result + (RXARRAY(6) - ASC("0")) * 100
             Result = Result + (RXARRAY(5) - ASC("0")) * 1000
             DIM TEMP AS LONG
             TEMP = RXARRAY(4) - ASC("0")
             TEMP = TEMP * 10000
             Result = Result + TEMP

        ELSE
            'Error.  Return -1
            Result = -1
        END IF


END SUB

SUB RESYNC
        TXARRAY(1) = 85
        TXARRAY(2) = 85
        TXARRAY(3) = 85
        TXARRAY(4) = 85
        TXARRAY(5) = 85
        TXARRAY(6) = 85
        TXARRAY(7) = 85
        TXARRAY(8) = 85
        CALL TRANSACTION
END SUB

SUB SETPin (Pin AS INTEGER, state AS INTEGER)
        TXARRAY(1) = ASC("P")

        'Convert Pin to ascii
        TXARRAY(2) = Pin \ 10 + ASC("0")
        TXARRAY(3) = (Pin MOD 10) + ASC("0")
        IF (state = 0) THEN
                TXARRAY(4) = ASC("0")
        ELSEIF (state = 1) THEN
                TXARRAY(4) = ASC("1")
        ELSEIF (state = 2) THEN
                TXARRAY(4) = ASC("I")
        ELSE
                TXARRAY(4) = ASC("U")
        END IF
        TXARRAY(5) = ASC("U")
        TXARRAY(6) = ASC("U")
        TXARRAY(7) = ASC("U")
        TXARRAY(8) = ASC("U")

        CALL TRANSACTION
END SUB

SUB TRANSACTION
'This Subroutine is used to send 8 bytes to The Wombat, and wait for 8
'bytes back.  It assumes that this can occur within 1 second (shouldn't
' be a problem).  It creates an output string from TXARRAY, and dumps
' it to the serial port.  This is necessary because QBasic doesn't have
' a byte type; It's integer type is 16 bits, so to get 8 bytes, you need
' a string of 8 characters.
' After the string is sent to the port ( I assume this is just queuing it
' in the FIFO, but i don't know), the Wombat uses the timer function to
' get the number of seconds since Midnight.  It waits for one of two things
' to happen:
' RXResult gets set to 1 by the Receive interrupt
' OR
' Timer changes values twice.  This means that between 1 and 2
' Seconds have passed since we first transmitted, So we time out
' The message.
' We could have used the QBasic ON TIMER functionality to do this,
' but since this timeout task is so simple, it seems smart not to
' waste ON TIMER on such a simple task.

   DIM TryNumber AS INTEGER
   TryNumber = 1
   output$ = "UUUUUUUU"
   FOR i = 1 TO 8
        MID$(output$, i, 1) = CHR$(TXARRAY(i))
   NEXT i
TRYAGAIN:
   RXRESULT = 0
   RXCount = 0
   RXARRAY(1) = ASC("X")
   RXARRAY(2) = ASC("X")
   RXARRAY(3) = ASC("X")
   RXARRAY(4) = ASC("X")
   RXARRAY(5) = ASC("X")
   RXARRAY(6) = ASC("X")
   RXARRAY(7) = ASC("X")
   RXARRAY(8) = ASC("X")
   PRINT #1, output$;
   DIM TimechangeCounter AS INTEGER
   TimechangeCounter = 0
   StartTime = TIMER
   WHILE (RXRESULT = 0)
        NewTime = TIMER
        IF (StartTime <> NewTime) THEN
           TimechangeCounter = TimechangeCounter + 1
           StartTime = NewTime
           IF (TimechangeCounter > 1) THEN
                RXRESULT = 2
'Comment out the following code if desired.
                PRINT "Timeout!"
           END IF
        END IF
   WEND
   IF (RXRESULT = 1 AND RXARRAY(1) = ASC("E")) THEN
        ' ERROR was received. 
        RXRESULT = 3
   END IF


   IF ((RXRESULT <> 1)) THEN
        'ERROR State.  Send RESYNC, and Try again
        IF (TryNumber = 1) THEN
            PRINT #1, "UUUUUUUUUUUUUU"
            TryNumber = 2
            GOTO TRYAGAIN
        END IF
   END IF


'Print out the TX and RX packets.  Comment out if desired.
   IF (RXRESULT = 1) THEN
        PRINT USING "### "; TXARRAY(1);
        PRINT USING "### "; TXARRAY(2);
        PRINT USING "### "; TXARRAY(3);
        PRINT USING "### "; TXARRAY(4);
        PRINT USING "### "; TXARRAY(5);
        PRINT USING "### "; TXARRAY(6);
        PRINT USING "### "; TXARRAY(7);
        PRINT USING "###       "; TXARRAY(8);
        PRINT USING "### "; RXARRAY(1);
        PRINT USING "### "; RXARRAY(2);
        PRINT USING "### "; RXARRAY(3);
        PRINT USING "### "; RXARRAY(4);
        PRINT USING "### "; RXARRAY(5);
        PRINT USING "### "; RXARRAY(6);
        PRINT USING "### "; RXARRAY(7);
        PRINT USING "### "; RXARRAY(8)
   END IF



END SUB

 

 

Copyright Wombat Interface Products, 2005-2008. All Rights Reserved.