Stimulator Tool

© 2022-2023, Kevan Hashemi Open Source Instruments Inc.


User Programs


NOTE: This manual applies to the Stimulator Tool Version 3.4+, available in LWDAQ 10.6.0+.

[31-AUG-23] The Stimulator Tool controls our Implantable Stimulator-Transponders (ISTs). It is a component of our LWDAQ Software. The Stimulator operates in conjunction with the Neuroplayer Tool to monitor IST activity, view IST synchronizing signals, and view biometric signals provided by Subcutaneous Transmitters (SCTs). When ISTs transmit messages and signals, they use the same telemetry system as our SCTs. When we transmit commands to ISTs, we use a different protocol, a language of radio frequency pulses thousands of times more powerful than the signals transmitted by our telemetry devices.

Figure: Stimulator Tool on MacOS. Multiple devices are presented in a device list. Each device has an index, but also an identifier and parameters that control its stimulus pattern and its synchronizing signal.

Each IST is equipped with a crystal radio for command reception. We transmit commands to the IST with a command transmitter, which consists of one or more components. An example three-component command transmitter is a Loop Antenna (A3015), Command Transmitter (A3029C) and LWDAQ Driver. A simpler example is the Telemetry Control Box (TCB-B16, but this new device is still in development. All command transmitters will provide a LWDAQ server for which the Stimulator Tool will act as a client. The Stimulator needs to know the IP address of the LWDAQ server. If the LWDAQ server provides several LWDAQ Driver Sockets, the Stimulator needs to know which socket we are using. There are entries for both these values in the Stimulator Window. We select a particular IST with its identity number, or ID. The ID is written on the body of the IST as a four-digit hexadecimal number. Upon command, the IST will carry out a stimulus. We get to specify the number of pulses, the length of the pulses, the pulse interval, the strength of the pulses, and whether or not the pulses should be regular or irregular.

The IST uses SCT auxiliary messages to transmit single messages that acknowledge commands, provide battery voltage measurements, and broadcast its ID. We enable and disable the transmission of acknowledgements with the Acknowledge check button. With Verbose checked, the Stimulator will report the details of every command we transmit.

Upon command, an IST will use an SCT channel to transmit a synchronizing signal. The synchronizing signal embeds in our telemetry recording the state of the stimulus so that there is no doubt about the exact time at which each pulse occurred. Positive pulses in the synchronizing signal show when the stimulus current is turned on. The height of the pulses tells us the stimulus strength. The synchronizing signal is similar to an SCT signal in that is has a sample rate and an SCT channel number. We set the sample rate and the channel number in the Stimulator Tool.

When the Stimulator Tool transmits an identify command, all ISTs that receive the command will transmit their IDs, and they will do so in such a way as to avoid colliding with one another. To acquire these signals and auxiliary messages, the Stimulator Tool works with a Neuroplayer Tool. The Neuroplayer reads live data from disk. This data is being downloaded from the telemetry hardware and written to disk by a Neurorecorder Tool running in an independent process. If we are running an SCT system, we will have a Neurorecorder downloading telemetry signals from the system and writing them to disk continuously. So we set the Neuroplayer to play back the data as it is recorded. The Neuroplayer will display all SCT signals as well as IST synchronizing signals. The Neuroplayer provides all auxiliary messages to the Stimulator so that the Stimulator can present them in its own text window.

The primary use of the Stimulator is to initiate stimuli. We can also use it to review our stimulation history. We play a recording in the Neuroplayer with the Stimulator open, and we will see command acknowledgements appearing in the Stimulator window, and synchronizing signals displayed in the Neuroplayer. You can try this for yourself by downloading and decompressing Play this archive and you will see responses from a single IST using channel number five (5). In the Neuroplayer we see not only the synchronizing signal provided by the IST, but also stimulation artifact in an SCT signal, which results from the IST and the SCT sharing the same beaker of water.

Figure: Synchronizing Signal and Stimulation Artifact. Pink: synchronizing signal transmitted by IST No6EEA. Blue: stimulation artifact in SCT in same beaker of water.

We can initiate stimuli by pressing buttons in the Stimulator Tool, but we can also initiate them by executing the stimulus command in response to an event we detect in one of our SCT signals, so as to implement automatic, closed-loop control. The Neuroplayer's Event Classifier will detect events and call an event handler script to initiate a stimulus. If we want to respond to events that span several intervals, we enhance the event handler so that it becomes a state machine. Three ictal intervals in a row, for example, might cause us to initiate a stimulus.

In addition to its built-in stimulus and synchronizing functions our implantable stimulators will run programs we write ourselves. We describe how we can write, upload, and run our own algorithms in the User Programs section below.


[09-SEP-22] The implantable stimulator system is compatible with the SCT system, in that we can implant ISTs and SCTs in animals that live in the same enclosure, and receive signals from both. Only the ISTs will be able to respond to commands. The figure below shows the connections required by implantable stimulator system that also makes use of subcutaneous transmitters (SCT) for monitoring biopotentials. We take an SCT system and add a command transmitter with its own transmit antenna. Follow the SCT set-up instructions to set up the recording system for SCT messages, then add the command transmitter as shown below.

Figure: Implantable Stimulator and Subcutaneous Transmitter Connections for Stimulation Experiments.

Referring to the diagram, we have the following components. (1) The Stimulator, Neurorecorder, and Neuroplayer run on the data acquisition computer. (2) A local or global internet provides communication with the computer. (3) A LWDAQ Driver provides power and communication for the data receiver and command transmitter. (4) The driver receives power from a 24-V adaptor with local AC wall cable. (5) The command transmitter receives boost power from a 24-V adaptor with local AC wall cable. (6) Shielded CAT-5 cables provide LWDAQ power and communication connections. (7) The Octal Data Receiver picks up signals transmitted from the implanted stimulators. (8) The Command Transmitter transmits radio-frequency commands to the implanted stimulators. (9) The animals are housed in a faraday enclosure. (10) The command transmit antenna is a loop antenna just like the receive antennas. (11) The receive antennas are connected to coaxial cables. (12) Dozens of animals may live together in the same faraday enclosure and be part of the same implantable stimulator system, each with their own implanted device, or with an implanted SCT that performs only EEG transmission. (13) Feedthrough connectors allow use to bring cables into the faraday enclosure without allowing ambient noise and interference to enter. (14) Coaxial cable carries radio frequency signals. (15) BNC plugs and sockets. (16) RJ-45 plugs and sockets.

The Command Transmitter (A3029) plugs into a Long-Wire Data Acquisition (LWDAQ) and receives its own 24-V power to boost its transmit power. When you connect the boost power supply, its blue boost light will turn on. Without the boost power supply, it will operate, but with lower output power and therefore shorter effective range. The command transmitter works well with a Loop Antenna (A3015), the same type of antenna used to pick up data transmissions from implanted SCTs.

Figure: Stimulator Data Acquisition Architecture. Only the Neurorecorder writes to the disk file. Any number of Neuroplayers may read the files as they are written. Neuroplayer Tool No1 runs in the same process as the Stimulator Tool. The other Neuroplayers and the Neurorecorder run in separate, independent processes, either on the same computer or on different computers that share the same hard drive.

Once we have our system hardware connected together, we download and install the latest version of LWDAQ. In the Tool menu, we select the Neurorecorder. A new window opens, providing an independent Neurorecorder. We use this program to record telemetry signals from our SCT system. The Neurorecorder writes these signals to files on disk in our NDF format. Only the Neurorecorder writes to the file on disk, and it runs in a process of its own on the data acquisition computer. We go back to our original LWDAQ process and select Stimulator from the Tool menu. The Stimulator Tool opens. We use its Neuroplayer button to open a Neuroplayer. We set the Neuroplayer up to play the data being written to disk by the Neurorecorder. We play the life data file. The Neuroplayer will display SCT signals as well as IST synchronizing signals. The Stimulator will find auxiliary messages transmitted by the ISTs in the live recording and report them to its own text window.


[09-SEP-22] Set up the system as shown above. We assume the Neurorecorder is writing live data to disk and a Neuroplayer is reading it from disk. This Neuroplayer is the one that we launched from the Stimulator Tool, or it is one that launched the Stimulator Tool. The Neurorecorder is running in its own, independent process on our data acquisition computer. The Neuroplayer and Stimulator are running in their own shared, independent process.

To communicate with an IST we must know its identifying number (ID). The ID is a number between 0 and 65535. Instead of writing the number as a decimal value, we write it as a four-digit hexadecimal value. So the ID could be "A123" or "7431". The latter may be confused with a decimal number, so we prefer to write "0xA123" and "0x7431", where "0x" means "hexadecimal". The ID number of each IST is written on a label on its body. We can discover the IDs of all IST within range of our command antenna by transmitting an identify command and waiting for their responses. We press the Stimulator's Identify button and wait for identifier messages to appear in the text window, each message reporting an ID number. We can then compose a device list consisting of these ISTs and control them each with their own Start, Stop, Xon, Xoff, and Battery buttons. We prepare a list of ISTs with the Add_Device button. We enter our IST identifiers. We can save the list to disk and load lists from disk. If we save a list, then save the Stimulator configuration, the Stimulator will read the list automatically the next time it starts up. We remove devices with the X at the end of the device entry.

When an IST transmits a synchronizing signal, it must use an SCT channel. We can configure the IST to use channel number 1-254, excepting that we cannot use any number for which the remainder is either zero or fifteen when divided by sixteen. Thus we can us 1-14, 17-30, etc. Our SCTs always ship with channel numbers no greater than 222, so we can choose channel numbers 225 and higher for our ISTs and be sure they will not collide with an SCT. Do not use channel number 254, however, because that channel we reserve for identity broadcasts. We specify this channel number in the primary channel number, or pcn, field of the IST's line in the device list. The IST uses its pcn not only for its synchronizing signal, but also to construct SCT auxiliary messages. The IST uses auxiliary messages to acknowledge receipt of commands, to transmit battery measurements, and for its identification broadcasts. The Neuroplayer will pick out these auxiliary messages for the Stimulator, and the Stimulator will report them to its text window. When the Stimulator reports an acknowledgement, it writes out the type of command that is being acknowledged, and it tells us the primary channel number of the IST that provided the acknowledgement. So long as we assign unique channel numbers to each of our ISTs, we will be able to deduce which IST issued the acknowledgement.

Not only do we get to choose the channel number of an IST's synchronizing signal, we also get to specify its sample rate. The sample rate can be anywhere from 128 SPS to 2048 SPS. We recommend you use sample rates that are an even power of two, but the IST will allow any sample rate, and will do its best to transmit at the rate you request. Press Xon to start the transmission and Xoff to stop. When the IST transmits its synchronizing signal, it uses ten times more current than it does when it is asleep and waiting for commands. So the IST turns its synchronizing signal off automatically in case we forget to turn it off ourselves. When a stimulus ends, the IST starts counting samples, and when it gets to 32768 without any new stimulus starting, it turns off its synchronizing signal. At 512 SPS, that's 64 seconds idle before turn-off.

We specify an IST stimulus with a number of pulses, a pulse length, a pulse interval, a current code, and a selection of regular or irregular pulses. The pulse interval is the average time between pulses. If the pulses are regular, the time between pulses will be equal to the interval every time. If the pulses are irregular, which we select with the Random button, the pulse will be delayed by a random amount of time within its interval, subject to the end of the pulse never overlapping the start of the next interval. Regular and irregular pulses have dramatically different frequency spectra, as shown below.

Figure: Fourier Transform of Regular (Left) and Random (Right) Stimulus. Stimulus is 10 Hz pulses, each pulse 10 ms. We take the tranform of the synchronizing signal, whish is HI when the stimulus current is on and LO when the current is off.

We specify the pulse interval and pulse length in milliseconds. A pulse length of 10 ms combined with an interval of 100 ms gives 10-ms pulses at 10 Hz. The length for the A3041 IST is 1 ms and the maximum is 2 s. The A3041's pulse interval may be anything from 5 ms to 512 s. The number of pulses can be anything from 0 to 65535. The current code determines the stimulus current. The A3041A has the following relationship between current code and current.

Figure: Stimulus Current versus Current Code for Several A3041A. Codes 0-2 generate no current.

The stimulus current is subject to the constraint that the IST has a maximum voltage it can apply to its stimulus leads, and these leads have some internal resistance also. The A3041A with 45-mm leads has total lead resistance 50 Ω and a stimulus voltage of 3.3 V. If we connect the leads to a blue LED with forward voltage 2.9 V, we will see at most 8 mA flowing.

The Battery button asks the IST to respond with a battery measurement. The A3041A has an eight-bit analog-to-digital converter (ADC) dedicated to measuring its battery voltage, so we obtain an accurate measurement any time we like, during a stimulus or after. The Stimulator records the most recent battery voltage measurement in the box next to the Battery button. When the Stimulator reports a battery measurement, it writes a similar line to its text window, but it also attempts to guess which IST in our device list is the one that provided the measurement. So long as the IST is in our list, and our ISTs have distinct primary channel numbers, the Stimulator will guess correctly.

User Programs

[31-AUG-23] Our implantable stimulators permit us to upload a user program that will it will execute with a fixed execution period. These programs are written in the OSR8 assembly language and run on the stimulator's embedded OSR8 microprocessor. The microprocessor clock speed is 5 MHz, which means we can execute one hundred lines of assembler code in approximately 50 μs. By default, the A3041 sets the execution period of the user program to to 1.0 ms.

Figure: Transmit Panel. The device number refers to a device in the main window device list.

We upload user programs to the IST using the Stimulator Tool's Transmit Panel. The Transmit Panel operates upon one of the devices listed in the main Stimulator window. We refer to this device by its index in the device list, not by its four-byte device identifier. The Transmit button in the Transmit Panel transmits a sequence of commands to the selected device. We specify these commands with decimal values 0-255 or with hex values 0x00-0xFF. This transmit function is what we use when we are debugging IST firmware.

The Browse button allows us to select a program file on disk. The Run button reads a user program from disk, calls upon the OSR8 Assembler Tool to assemble the program into OSR8 machine code, uploads the program to the selected IST, and starts the IST's synchronizing signal so as to begin executing the user program. The results of assembly are printed in the Transmit Panel's text window. The Halt button stops the program execution and the synchronizing signal. The Edit button opens the program file in a LWDAQ text editor.

A user program takes the form of an assembly routine ending with a return instruction. Here is the simplest possible user program.

ret              ; Return to calling process.

The simplest program does nothing. it returns to the calling process. It is the user program that the IST writes to the user program memory when it wakes up. That way, if we tell it to run its user program without first giving it a user program, nothing bad will happen.

Any user program that does more than just return must interact with the IST's control registers and variable memory. All constants and variable locations used by the main program will be declared at the top of IST's main program. The A3041's main program is ROM.asm. The address space of the embedded microprocessor is divided into five sections: variable space, stack space, user variable space, control space, and user program space. We present the A3041 memory map below.

0x0000-0x00FFVariable SpaceVariable locations for the main program.
0x0100-0x01FFStack SpaceMain program stack starts at bottom and goes up.
0x0200-0x02FFUser Variable SpaceVariable locations for the user program.
0x0400-0x07FFControl SpaceControl and status registers.
0x0800-0x0FFFUser Program SpaceUser program code.
Table: Address Space of the A3041 IST's Microprocessor.

The control registers do things like set the stimulus current, initiate radio-frequency transmission, tell us when a command is ready, and allow us to read command bytes from the ITS's command receiver. Some of these registers can be written to and read back, but most are either write-only or read-only. The mmu_it3p location, for example, is write-only. It contains the interrupt timer period that governs how often the user program will be executed. The interrupt timer runs at 32.768 kHz and the A3041 sets mmu_it3p to 33, so the user program runs every millisecond. The main program provides two locations in its own variable space for the user program: the UPrun and UPinit flags. Both flags are set when the user program runs for the first time. The UPrun flag tells the IST to stay awake for the user program. The UPinit flag is for the user program to set to zero after it has performed its initialization, so that initialization will not be performed again.

Here is a program that transmits the IST extinguish counter as an SCT message. The extinguish counter decrements to zero, and when it reaches zero, the IST turns off. Any time we send a command to the IST, the extinguish counter resets.

; Example IST User Program. Transmits extinguish counter.
ld A,(0x001A)    ; Read low byte of extinguish counter
ld (0x0409),A    ; and load into transmit low byte.
ld A,(0x0019)    ; Read high byte of extinguish counter
ld (0x0408),A    ; and load into transmit high byte.
ld A,65          ; Write a channel number
ld (0x040A),A    ; to transmit register.
ld A,0x01        ; Write transmit code to
ld (0x040B),A    ; transmit control register.
ld A,50          ; Delay for ten microseconds
dly A            ; while transmit completes.
ret              ; Return to calling process.

Here we have all addresses hard-coded. But we could define the addresses with constants, as they are in the main program. When a user program needs variables, it has its own user program variable space. In the A3041, the user program has 512 bytes it can use for flags, numbers, pointers, and arrays. Our template user program, UProg.asm includes all the constants and variables defined in the P3041A, v1.9 main program, and executes some simple functions. The following program uses some of constants declared in the main program. The program turns on the stimulus current when the program first executes, waits for a fraction of a second, turns off the stimulus current, and clears the user program run flag so the device can go to sleep.

const mmu_stc  0x0406 ; Stimulus Current
const UPrun    0x0022 ; Running
const UPinit   0x0023 ; Initialize
const cntr     0x0200 ; Counter variable

ld A,(UPinit)     ; Check the initialization
add A,0           ; flag to see if we are 
jp z,initialized  ; starting or running.

ld A,0            ; Clear the initialization
ld (UPinit),A     ; flag.
ld A,8            ; Turn on the 
ld (mmu_stc),A    ; stimulus current.
ld A,255          ; load our counter
ld (cntr),A       ; with starting value.
ret               ; Return to main program.

ld A,(cntr)       ; Decrement our 
dec A             ; counter
ld (cntr),A       ; and if zero,
jp z,done         ; jump to done.
ret               ; Otherwise return.

ld A,0            ; Turn off stimulus
ld (mmu_stc),A    ; current and clear
ld (UPrun),A      ; run flag.
ret               ; Return to main program.

In the program, above, we use address labels "initialized" and "done" to implement our conditional statements. The assembler must resolve these labels into absolute addresses in the IST's program memory. The IST itself loads user programs into the correct location in memory, but in order for the assembler to resolve the labels, we must tell it the base address of the user program. The Base Address specifies the base address the assembler should use. In the P3041A v1.9 firmware, the program base address is 0x0800, and programs can be up to 2 KByte long.