A Speech Synthesized Goniometer

This page can be found at: http://www.pacifier.com/~mcginty

A goniometer is a device to measure the how far a patient can bend joints.

This speech synthesized gonimeter is built around the Information Storage Devices model 1000A Voice Record and Playback Integrated Circuit. The IC is controlled by a Parallax Inc. BASIC Stamp II microcontroller. The Goniometer consists of four parts.

1. The transducer, which converts the degrees measured into electrical impulses.

2. The microcontroller, the self contained microcomputer that translates the transducer impulses into addresses and control signals for the voice IC.

3. The voice IC, which has twenty seconds of stored audio.

4. The headphones to listen to the audio.

1. Transducer: Commercially available transducers are called 'digital protractors', 'electronic levels', or 'digital inclinomters'. Among the cheapest are the Spitronic Pro 3600 digital level, at $329. It features .01 degree resolution, far in excess of what is needed for a goniometer, and requires a laptop computer to read its output. Also available is the Fowler Universal Digital Protractor at $1095, again far in excess of what is required. Computerized Goniometers and dual inclinometers are also available, in the thousands of dollar range and requiring IBM computers for data storage. Also available are 'digital setting circles' for amateur astronomers who control telescopes by computer, again in the several hundred dollar range.

1. The transducer circuit:

The transducer was constructed around a variable resistor. As the goniometers arm is rotated, the resistance increases from zero to 50,000 ohms in an approximately linear fashion. The resistor is connected in a configuration known as a 'voltage divider' with a 10,000 ohm resistor and a .1 microfarad capacitor.

The transducer circuit:

The microcontroller can cannot measure resistance directly. The procedure for how this is converted into degrees is covered in more detail later in the text.

2. Parallax Inc. makes a line of microcomputer boards based on Microchip Co.'s series of microcontroller. A microcontroller is similar to the microprocessor in your PC, optimized to stand alone (containing on board, RAM, ROM, clock etc.) where speed is less critical. Typically microcontrollers are found in microwave ovens, cars, toys etc. It is interesting to note that in 1976 there were 50,000 computers in the world, now we make 50,000 computers a day. The vast majority of these computers are micro controllers.

The Parallax BASIC Stamp II consists of a Microchip PIC 16C57 controller and an EEPROM (Electrically Erasable Programmable Read Only Memory) and a clock circuit on a 24 pin DIP (Dual In-line Package, the familiar 'bug' chip shape). It is aproximately the size of a postage stamp, hence the name. A carrier and proto typing board make the package slightly smaller than a CD case. The STAMP communicates with the outside world via a serial cable to an IBM computer for programming, and 16 input/output pins. Once loaded the stamp can stand alone and run its program. 12 pins are used in the Goniometer application, 8 for selecting an address and 3 for control of the voice chip, and 1 connected to the transducer.

3. The Voice Chip is a 28 pin package. The eight address pins allow you to address the voice memory in .125 second increments. The eight address pins are tied to the first eight output pins of the stamp.

Setting Stamp pins to these states:
Pin 0 = 0 volts = low
Pin 1 = 0 volts = low
Pin 2 = 0 volts = low
Pin 3 = 0 volts = low
Pin 4 = 0 volts = low
Pin 5 = 0 volts = low
Pin 6 = 0 volts = low
Pin 7 = 0 volts = low
Pin 8 = 0 volts = low

Puts the address of '0000 0000' to the voice chip.


ADDRESS SECONDS
0000 0000 0 seconds
0000 0001 .125 second
0000 1000 beginning of first second
0001 0000 beginning of second second
on up through
1011 0000 beginning of nineteenth second.

The voice chip is controlled by four signals
From the stamp to the chip:

Pin 27 Play/Record Tied high for play, low for record
Pin 24 Power Down High to disable and reset the chip, low to activate
Pin 23 Chip Enable Starts Record or Play
From the chip to the stamp
Pin 25 End of Message Goes high when end of message reached.

Helpful for debugging, monitoring of this pin is not required.

Audio is put into the chip via a microphone input or an analog audio level input. I never did get the microphone input to work satisfactorily, despite much vexation.

I found a satisfactory method of inputting the audio with my computer. Using the installed sound card and a microphone, and windows 95 Sound Recorder accessory, I recorded the words:
zero
one
two
three
four
five
six
seven
eight
nine
twenty
thirty
forty
fifty
sixty
seventy
eighty
ninety
one hundred
degrees.
Since these words were all one third to one half second long, I though I would have plenty of room to spare. This was not to be the case.

I found a shareware sound effects editor, Goldwave 3.03, that let me polish and edit the sounds. I cut off blank parts from the begiining and end, and adjusted the volume on some.

I discovered that while I could store the words in one half second of space, there was no reliable way to insert the End of Message bit. Without this marker bit the chip will play the one after the other without stopping. I eventually hit upon recording the word at the start of one second (address 0000) then recording a short blip at .875 second (address 0111). This worked satisfactorily, was inaudible, and gave me twenty one second slices of voice memory.

Here is how the voice chip memory is partitioned

Memory Seconds Audio inserted
Address In
in binary Decimal
00000000 0 0 "zero"
00000001 1 .125
00000010 2 .25
00000011 3 .375
00000100 4 .5
00000101 5 .625
00000110 6 .75
00000111 7 .875 momentary blip
00001000 8 1 "one"
00001001 9 1.125
00001010 10 1.25
00001011 11 1.375
00001100 12 1.5
00001101 13 1.625
00001110 14 1.75
00001111 15 1.875 momentary blip
00010000 16 2 "two"
00010001 17 2.125
00010010 18 2.25
00010011 19 2.375
00010100 20 2.5
00010101 21 2.625
00010110 22 2.75
00010111 23 2.875 momentary blip
00011000 24 3 "three"
00011001 25 3.125
00011010 26 3.25
00011011 27 3.375
00011100 28 3.5
00011101 29 3.625
00011110 30 3.75
00011111 31 3.875 momentary blip
00100000 32 4 "four"
00100000 32 4
00100001 33 4.125
00100010 34 4.25
00100011 35 4.375
00100100 36 4.5
00100101 37 4.625
00100110 38 4.75
00100111 39 4.875 momentary blip
00101000 40 5 "five"
00101001 41 5.125
00101010 42 5.25
00101011 43 5.375
00101100 44 5.5
00101101 45 5.625
00101110 46 5.75
00101111 47 5.875 momentary blip
00110000 48 6 "six"
00110001 49 6.125
00110010 50 6.25
00110011 51 6.375
00110100 52 6.5
00110101 53 6.625
00110110 54 6.75
00110111 55 6.875 momentary blip
00111000 56 7 "seven"
00111001 57 7.125
00111010 58 7.25
00111011 59 7.375
00111100 60 7.5
00111101 61 7.625
00111110 62 7.75
00111111 63 7.875 momentary blip
01000000 64 8 "eight"
01000001 65 8.125
01000010 66 8.25
01000011 67 8.375
01000100 68 8.5
01000101 69 8.625
01000110 70 8.75
01000111 71 8.875 momentary blip
01001000 72 9 "nine"
01001001 73 9.125
01001010 74 9.25
01001011 75 9.375
01001100 76 9.5
01001101 77 9.625
01001110 78 9.75
01001111 79 9.875 momentary blip
01010000 80 10 "twenty"
01010001 81 10.125
01010010 82 10.25
01010011 83 10.375
01010100 84 10.5
01010101 85 10.625
01010110 86 10.75
01010111 87 10.875 momentary blip
01011000 88 11 "thirty"
01011001 89 11.125
01011010 90 11.25
01011011 91 11.375
01011100 92 11.5
01011101 93 11.625
01011110 94 11.75
01011111 95 11.875 momentary blip
01100000 96 12 "forty"
01100001 97 12.125
01100010 98 12.25
01100011 99 12.375
01100100 100 12.5
01100101 101 12.625
01100110 102 12.75
01100111 103 12.875 momentary blip
01101000 104 13 "fifty"
01101001 105 13.125
01101010 106 13.25
01101011 107 13.375
01101100 108 13.5
01101101 109 13.625
01101110 110 13.75
01101111 111 13.875 momentary blip
01110000 112 14 "sixty"
01110001 113 14.125
01110010 114 14.25
01110011 115 14.375
01110100 116 14.5
01110101 117 14.625
01110110 118 14.75
01110111 119 14.875 momentary blip
01111000 120 15 "seventy"
01111001 121 15.125
01111010 122 15.25
01111011 123 15.375
01111100 124 15.5
01111101 125 15.625
01111110 126 15.75
01111111 127 15.875 momentary blip
10000000 128 16 "eighty"
10000001 129 16 .125
10000010 130 16.25
10000011 131 16.375
10000100 132 16.5
10000101 133 16.625
10000110 134 16.75
10000111 135 16.875 momentary blip
10001000 136 17 "ninety"
10001001 137 17.125
10001010 138 17.25
10001011 139 17.375
10001100 140 17.5
10001101 141 17.625
10001110 142 17.75
10001111 143 17.875 momentary blip
10010000 144 18 "one hundred"
10010001 145 18.125
10010010 146 18.25
10010011 147 18.375
10010100 148 18.5
10010101 149 18.625
10010110 150 18.75
10010111 151 18.875 momentary blip
10011000 152 19 "degrees"
10011001 153 19.125
10011010 154 19.25
10011011 115 19.375
10011100 156 19.5
10011101 157 19.625
10011110 158 19.75
10011111 159 19.875
11100000 160 unused
though
10111111 191 unused

The Play Circuit:

The record circuit is a slight modification of the Play circuit shown above. A single throw switch is added to the Play / Record pin, and the connection to pin 8 of the stamp removed:

and a momentary action push-button is added to Chip Enable, and the connection to pin 9 of the stamp removed.


Sequence of events, inputting the audio:
1. Connect the audio out from the sound card to the audio input. Call up the sound program and voice file.

2. Set the Play / Record switch to Record.

2. The program for recording is loaded into the stamps memory from an IBM type computer and the attached serial cable:

' Name:play_reco.bs2 RECORD/PLAY
' lines preceded by a single quote are comments, others are instructions
'1. set up address to start at 0-7
'set up pins 0 through 7 as output pins
dirl=255
'set the pins for address 0000 0000
low 0
low 1
low 2
low 3
low 4
low 5
low 6
low 7
'8 = EOM, I'm going to monitor this pin with the debug tool
input 8
'9 = CE active low
'10 = p/r
input 10
'set POWER DOWN pin 11, cycle the voice chip
'chip disabled
high 11
'pause 250 milliseconds, 1/4 second
pause 250
'chip enabled
low 11
' this info will now be echoed to the PC screen
debug home,"ADR 0 pin0= ",bin out0,cr
debug "ADR 1 pin1= ",bin out1,cr
debug "ADR 2 pin2= ",bin out2,cr
debug "ADR 3 pin3= ",bin out3,cr
debug "ADR 4 pin4= ",bin out4,cr
debug "ADR 5 pin4= ",bin out5,cr
debug "ADR 6 pin4= ",bin out6,cr
debug "ADR 7 pin4= ",bin out7,cr
debug "EOM pin8= ",bin in8,cr
debug "CE pin9= ",bin in9,cr
debug "1P0R pin10= ",bin in10,cr
debug "PD pin11= ",bin in11,cr
'OK, cycle the voice chip again
pause 50
high 11
pause 250
low 11

The program has set up the address and readied the chip.

3. Start the voice file playing on the PC. It is easy to see when to start the recording from the graphical display.

4. Push the Chip Enable button to record.

5. Edit the program and change 'low 0, low 1, low 2' to 'high 0, high 1, high 3' and load it over again. This sets up address 0000 0111, which is .875 milliseconds.

6. Momentarily push the Chip Enable. This inserts the End of Message bit.

7. Ideally, you can flip P/R to play, push chip enable and hear your audio. In practice it takes several tries to get the level correct, not cut off the beginning or end, and not run the End of Message bit over into second number one.

8. The program can be manually edited for each relevant address.

The circuit can be restored once all the messages are recorded. It is important to tie the Play / Record high, as it is easy to accidentally erase parts of the memory.

The Overall Circuit:


How the Stamp converts resistance to degrees.

Being a digital device in an analog world, the microcontroller needs a way to convert an analog quantity (resistance) into a digital quantity (a number in memory). The stamps instruction set includes a command, called "rctime" which times how long it takes a pin change state from a zero to a one.

The capacitor is a device that stores static electricity for short periods of time. The device wants to discharge to ground. It's discharge path is through the variable resistor, which will let it dischard quickly (when resistance is low) or more slowly (when the resistance is high)

The sequence of events is as follows:

high 12
Pin 12 goes to +5 Volts and the capacitor begins discharging. (This may seem counter-intuitive, but a capacitor holding electricity sees a voltage difference between its two plates. Bringing pin 12 up to 5 volts brings both sides up to equal voltages and discharges the capacitor.)
pause 1
Wait 10 milliseconds until the cap is fully discharged.
rctime 12,1,result
Bring pin 12 low, change pin 12 from an output (putting out 5 volts) to an input (look for a change of state. It is 0 volts now. When the voltage rises to aproxmately 1.5 volts, it will trgger into a 1 state.

Start an internal clock running, with one 'tic' every two microseconds. Start accumulating the number of tics in a variable called "result".

The capacitor begins charging, at a rate determined by the rotation of the variable resistor.

Since the audio has been programmed, the final STAMP instructions can be loaded.

'McGinty Products mcginty@pacifier.com 4/11/98
	'-------------set up some variables
	'define a and b for a temporary 16bit variables
a var word
b var word
start:
	'The green LED is on pin 14. Turn it off, then on
low14
high 14
	'The red LED is on pin 13. Turn it off
low 13
	'pin 15 is the calibrate button, next to the red LED
input 15
	' kkk is our main counting number, store holds the number
	' used for setting "zero"
	'variable called result to hold the result from the RCTIME routine
	'"define RESULT as a VARiable and it is a WORD long (16bits)
result var word
store var word
kkk var word

	'byte to hold the 100's digit
hun var byte
	'set it to zero
hun = 0
	'byte to hold the "ty" digits (20,30,40,50,60,70,80,90)
ty var byte
	'set it to zero
ty = 0
	'byte to hold the tens digit
tens var byte
	'set it to zero
tens = 0
	'byte to hold the ones digit
ones var byte
	'set it to zero
ones = 0
	'@@@@@@@@@@@@@@@begin the read arm transducer routine
	' set pin12 high
high 12
	' give circuit 10 microseconds to get fully charged
pause 1
	'let capacitor discharge, start an internal clock counting every 2
	'microseconds.  Stop counting when pin 12 reaches a '1' state and
	'store the numcer of tics in result
rctime 12,1,result
	'is the calibrate button being pressed? 0=NO
	'"hop over" the next steps if button is NOT being pressed
	'@@@@@@@@@@@@@@@end the read arm transducer routine
	' ==============begin the service the calibrate button routine
if in15 =0 then hopover
	' set the new zero to be where the arm is right now
	'by setting our main counter to zero for this value of result
	'light the red LED
high 13
	'put the RCTIME counter in a temporary variable called store
  store=result
	'and subtracting store from itself means kkk=0
  kkk=result-store
hopover:
	' ==============end the service the calibrate button routine
	' ##############begin the counter routine
	' set the counter equal to the measured position minus the "zero"
kkk=result-store
	' if the arm goes backwards past zero, kkk will be in the region of
	' 65,000. So if it is more than 2000 (about 1 1/2 turns, 450 degrees,
	' go back to start
if kkk > 2000 then start
	' converting the raw number to degrees.  Technique used is
	'"squaring the circle" or "median average"
	'first multiply by 8 so we don't lose the lower digits with
	'our integer math
kkk=kkk*8
	'2/3 gives a little lower than true value, kkk=86 at 90 degrees
a=2*(kkk/3)
	'11/15 gives a little higher than true value, kkk=93 at 90 degrees
b=11*(kkk/15)
	'divide a,b and kkk bye 8 to get them back to right range
kkk=kkk/8
a=a/8
b=b/8
	'average the values of a and b, (86+93)/2=89 at 90 degrees
	'                       and (172+188)/2=180 at 180 degrees
kkk=(a+b)/2

'debug ? a
'debug ? b
debug ? kkk
'debug ? in15
pause 500
	'----------------
	'The serial output routine
	'will send the degrees to the computer
      if kkk>99 then dit100s
      if kkk>9 then dit10s
      if kkk<9 then dit1s
	'dit1s = ones digit
dit1s:
serout 10,16468,["00",dec kkk]
goto alldone
	'dit10s = tens digit
dit10s:
serout 10,16468,["0",dec kkk]
goto alldone
	'dit100s = hundreds digit
dit100s:
serout 10,16468,[dec kkk]
	'the degrees have been sent to the computer
alldone:
'debug ? kkk
'-------------set up the stamp microcontroller
'set pins 0 to 7 as outputs..very important, won't work otherwise
dirl=255
	'set lower three bits to zero, we won't be counting smaller than 
	'1 second increments
low 0
low 1
low 2
	'set up Power Down to be high (power is not down)
high 11
	'set up chip enable to be high (chip is not enabled, is waiting)
high 9
'-------------------------------------------------------
'++++++++++++++++++++++++++++here is the calculation part
	'command puts the values of kkk's hundreds digit into variable hun
hun = kkk dig 2
	'command puts the values of kkk's tens digit into variable tens
tens = kkk dig 1
	'convert tens to correct ISD 1000 address (used for 10 to 19 only)
tens = tens * 8
	'this command puts the values of kkk's ones digit into ones
ones = kkk dig 0
	'convert ones to correct ISD 1000 address
ones = ones * 8
	'load and convert the 20 thru 90 address
ty = kkk dig 1 
ty = ty + 8
ty = ty * 8

'goto start
'is hun = one? then must be over one hundred.  fall through.
if hun = 1 then speakhun
'is tens zero?  then must be between 0 and 9.  Jump to speak single digit
if tens = 0 then speak1
'is tens one?  then must be between 10 and 19.  Jump to speak ten
if tens = 8 then speakten
'is tens 2 or more?  then must be between 20 and 99.  Jump to speak tenty
if tens > 8 then speaktenty

'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>here is the one hundred routine
speakhun:
debug "hun",cr 
'if in15 =0 then hopover
	'put address on address pins
  outl=144
  outl=144 & outl
	'The play cycle.  Power down, chip enable for 750 milliseconds
	't needs a power down cycle, don't know why  
	high 11
	pause 250
	low 11  
	'take chip enable low and let it play for 1 second.  Timing is tricky on
	'this part.
	'it needs this pause, dont know why.  will play the next addres otherwise
  	pause 50
  	LOW 9
  	PAUSE 750
  	high 9
	'clear the pins.  needs this step
  outl = 0
 
'need to skip over the tens digit now. ones digit stays same
if tens = 0 then speak1
if tens = 8 then speakten
if tens > 8 then speaktenty
if ones = 0 then speakdegrees

'
'if it is an even one hundred, skip over the 'ones' part or
'it will say 'one hundred-zero'
if hun=1 then speakdegrees
'then just fall through to the next routine
'^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^here is twenty through ninety
speaktenty:
debug "ty",cr
'if in15 =0 then hopover
	'put address on address pins
  outl=ty
  outl=ty & outl
	'The play cycle.  Power down, chip enable for 750 milliseconds
	't needs a power down cycle, don't know why  
	high 11
	pause 250
	low 11  
	'take chip enable low and let it play for 1 second.  Timing is tricky on
	'this part.
	'it needs this pause, dont know why.  will play the next addres otherwise
  	pause 50
  	LOW 9
  	PAUSE 750
  	high 9
	'clear the pins.  needs this step
  outl = 0
'if it is an even twenty, thirty, etc., skip over the 'ones' part or
'it will say 'twenty-zero'
debug "ty2",cr
if ones = 0 then speakdegrees
'remember if you used the ty part, you don't use the tens routine
debug "ty3".cr
goto speak1

'=======================================here is the tens digit routine
speakten:
'debug "ten",cr
'if in15 =0 then hopover
  outl=tens
  outl=tens & outl
	'The play cycle.  Power down, chip enable for 750 milliseconds
	'it needs a power down cycle, don't know why  
	high 11
	pause 25
	low 11  
	'take chip enable low and let it play for 1 second.  Timing is tricky on
	'this part.
	'it needs this pause, dont know why.  will play the next addres otherwise
	  pause 50
	  LOW 9
	  PAUSE 750
	  high 9
	'clear the pins  
	outl = 0
'----------------------------------------here is the single digit routine
speak1:
debug "ones===",kkk,cr
'if in15 =0 then hopover
if kkk = 100 then speakdegrees
  outl=ones
  outl=ones & outl
	'it needs a power down cycle, don't know why  
	high 11
	pause 250
	low 11  
	'take chip enable low and let it play for 1 second.  Timing is tricky on
	'this part.
	'it needs this pause, dont know why.  will play the next addres otherwise
  	pause 50
  	LOW 9
  	PAUSE 750
 	 high 9
'
  outl = outl & 0
'remember to take this loop out if you want to hear 'degrees'
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`here is the "degrees" routine
speakdegrees:
debug "sdeg",cr
'if in15 =0 then hopover
  outl=152
  outl=152 & outl
'it needs a power down cycle, don't know why  
high 11
pause 250
low 11  
'take chip enable low and let it play for 1 second.  Timing is tricky on
'this part.
'it needs this pause, dont know why.  will play the next addres otherwise
  pause 50
  LOW 9
  PAUSE 750
  high 9
  outl = 0
'and don't forget to start over
goto start
The data out to the computer, while involving only a few instructions on the goniometer end:

"serout 10,16468,[dec kkk] 'the degrees have been sent to the computer"

Present some interesting problems on the other end and are dealt with on a separate web page.