ET312 Protocol Documentation

Table of Contents

Introduction

This document is a specification for the serial communications protocol of the ET312 Electrostimulation box by Erostek. The following specifications are for v1.5 and v1.6 of the Erostek firmware, which it is assumed all modern boxes are running.

Communication via Link Cable

Serial Link (PC to ET-312)

Communicating with the ET312 box happens via an RS-232 Connection to the Link port of the box. The link cable consists of a 3.5mm TRS (stereo audio) jack, going to some sort of computer connection, be it Female DB-9 or a RS232-to-USB converter. The pin connections are as follows:

  • 3.5mm Tip <-> RX (DB-9 Pin 2)
  • 3.5mm Ring <-> TX (DB-9 Pin 3)
  • 3.5mm Sleeve <-> Ground (DB-9 Pin 5)

Serial connections are 19200/8/N/1, or:

  • 19200 baud
  • Data Bits: 8
  • Stop Bits: 1
  • Partity: None

Handshake and Synchronization

Handshaking consists of a byte sent to the box, and a byte received back:

  • 0x00 is sent to ET312
  • 0x07 is read from ET312

This is done as a way to establish connection and synchronize the protocol.

If the box has been previously connected to and not powered off, the 0x00 sent to the ET312 will need to be encrypted with the key established during the previous session with the box, unless it was reset (see Key Resets section).

Similarly, if a connection is interrupted in the middle of a message, sending 0x0's until a 0x7 is received is a good way to re-synchronize the protocol. As the longest message possible with the ET-312 protocol is 11 bytes, up to 11 0x0s may need to be sent.

Key Exchange

After the handshake ends, you can then send "Read Byte" commands without performing a key exchange. If you wish to "Write Bytes" then XOR keys must be exchanged. This involves sending a 3 byte sequence to the box, and receiving 3 bytes back:

  • [0x2f, 0xVV, 0xWW] sent to ET312
  • [0x21, 0xXX, 0xYY] is read from ET312

Where:

  • 0xVV is a random unsigned 8-bit number, chosen by the host, used as the first key
  • 0xXX is a random unsigned 8-bit number, chosen by the ET312, used as the second key
  • 0xWW/0xYY is a checksum, the 8-bit unsigned sum of the first two bytes, wrapped if the sum is > 255.

For instance:

  • [0x2f, 0x04, 0x33] is sent to ET312
    • 0x04 is the host XOR key
    • 0x33 is the checksum (0x2f + 0x04)
  • [0x21, 0xef, 0x10] is read from ET312, meaning
    • 0xef is the box XOR key
    • 0x10 is the checksum ((0x21 + 0xef) % 0x100)

Note that the key chosen by the host need never change, it can simply be hardcoded into the protocol implementation. Most implementations simply use 0 for the host key, which simplifies calculation of encryption.

Key Usage (Protocol Encryption)

Once the keys are agreed upon, all further communication going from host to ET312 is required to be encoded using the following scheme, using ^ as an XOR operator:

Data Byte ^ (Host Key ^ Box Key ^ 0x55)

The part of the expression in parenthesis will be constant after key exchange, and can be pre-calculated and stored.

Only data sent from host to ET312 requires encryption, all data received from the ET312 will be cleartext.

Master and Slave Link (ET-312 to ET-312)

Two ET312 boxes can be linked together using a cross-over cable.

  • Box 1 3.5mm Tip <-> Box 2 3.5mm Ring
  • Box 1 3.5mm Ring <-> Box 2 3.5mm Tip
  • Box 1 3.5mm Sleeve <-> Box 2 3.5mmSleeve

A box becomes Master when you navigate in the menu options to the Link option. Once linked, a Slave unit A and B channels will follow those from the Master unit. The A and B pots still control the output levels for the Slave A and B.

Master-Slave communications are known to be a bit troublesome and can easily fail.

Handshake

Note: There is no encryption (xor bytes) used.

On selecting the menu item, the Master box will send a single byte on the serial port 0x0e. It expects to see a single byte back from the Slave 0x05.

After handshake is complete the master box will use the standard protocol as above to send memory locations to the slave.

The master will first send a 0x9d 0x40 0x04 followed by 6 bytes and a checksum.

When the slave sends an acknowledgement back (a single 0x06), the master will send a 0x9d 0x40 0x0a followed by 6 bytes.

When the master gets the next 0x06 back it will send the first 6 bytes again, forever, as fast as the slave processes them.

Locations $4004-$400f contain the processor registers r4-r15

Commands

Outside of the initial key setup, talking to the ET312 happens through 2 functions. These resemble peek and poke, except that developers can send between 1-8 bytes at a time. Only 1 byte may be read at a time. Both functions take 16 bit addresses, which map into a virtual memory space set up by the communications handler on the ET312. This memory space looks like:

Address Range Description
$0000 - $00ff Flash (256b from 0x1f00-0x1fff)
$4000 - $43ff Registers and Partial RAM (1k)
$8000 - $81ff EEPROM (512b)

Reading past the end of these ranges will just loop the last valid range.

All further documentation will use these ranges as reference, so when we mention writing/reading to, say, $4010, this means we're writing to byte 16 of the Register/RAM address space.

Also note that we do not have access to all of the RAM via this protocol. The CPU and IO registers take up the first 96 bytes of the address space we can access, and do not count as SRAM space. Since the virtual memory addressing cuts us off at $43ff, we cannot access the last 96 bytes of RAM. That said, the stack pointer never seems to move from 0x045f, which is gcc's RAM end.

Read Bytes

Reading a byte happens via a command with 3 byte length (minus checksum)

0x3c 0xGG 0xHH

  • 0xHH - High byte of address
  • 0xII - Low byte of address

Write Bytes

Writing a byte happens via a command with 4 byte length (minus checksum)

0xGd 0xHH 0xII [0xJJ 0xKK…]

  • 0xGd - High nibble is amount of data to write to address plus 0x3, low nibble is always 0x0d
  • 0xHH - High byte of address
  • 0xII - Low byte of address
  • [0xJJ 0xKK]… - Value(s) to set address to

For instance, if we wanted to write 2 bytes, 0xFE 0xFF, starting $4010, the command would look like

0x5d 0x40 0x10 0xfe 0xff
  • 0x5d is the write command with amount (0x3d + 0x20 since we're writing 2 bytes)
  • 0x40 0x10 is our 16-bit address ($4010)
  • 0xfe 0xff is the data we want to write to $4010 and $4011, respectively.

Memory Layout Tables

All entries in bold have been mapped and are useful.

Flash

Address Description
$0000 - $0098 Partial String Table
$0098 - $00fb .data Segment
$00fc Box Model
$00fd - $00ff Firmware Version

RAM

Address Description
$4000 r0 (CPU Register)
$4001 r1 (CPU Register)
$4002 r2 (CPU Register)
$4003 r3 (CPU Register)
$4004 r4 (CPU Register)
$4005 r5 (CPU Register) copied from $4090
$4006 r6 (CPU Register) copied from $409c
$4007 r7 (CPU Register) copied from $40a5
$4008 r8 (CPU Register) copied from min(9, $40ae)
$4009 r9 (CPU Register) copied from min(50,$40b7)
$400a r10 (CPU Register) copied from $4190
$400b r11 (CPU Register) copied from $419c
$400c r12 (CPU Register) copied from $41a5
$400d r13 (CPU Register) copied from min(9, $41ae)
$400e r14 (CPU Register) copied from min(50, $41b7)
$400f r15/ADC disable and other flags - COMM_SYSTEM_FLAG
$4010 r16 (CPU Register) various flags
$4011 r17 (CPU Register) various flags
$4012 r18 (CPU Register)
$4013 r19 (CPU Register) action when down key pushed
$4014 r20 (CPU Register) action when up key pushed
$4015 r21 (CPU Register) action when menu key pushed
$4016 r22 (CPU Register) action when ok key pushed
$4017 r23 (CPU Register)
$4018 r24 (CPU Register)
$4019 r25 (CPU Register)
$401a r26 (CPU Register)
$401b r27 (CPU Register)
$401c r28 (CPU Register)
$401d r29 (CPU Register)
$401e r30 (CPU Register)
$401f r31 (CPU Register)
$4020 TWBR (IO Register)
$4021 TWSR (IO Register)
$4022 TWAR (IO Register)
$4023 TWDR (IO Register)
$4024 ADCL (IO Register)
$4025 ADCH (IO Register)
$4026 ADCSRA (IO Register)
$4027 ADMUX (IO Register)
$4028 ACSR (IO Register)
$4029 UBRRL (IO Register, Baud Rate)
$402a UCSRB (IO Register)
$402b UCSRA (IO Register)
$402c UDR (IO Register)
$402d SPCR (IO Register)
$402e SPSR (IO Register)
$402f SPDR (IO Register)
$4030 PIND (IO Register)
$4031 DDRD (IO Register)
$4032 PORTD (IO Register)
$4033 PINC (IO Register)
$4034 DDRC (IO Register)
$4035 PORTC (IO Register)
$4036 PINB (IO Register)
$4037 DDRB (IO Register)
$4038 PORTB (IO Register)
$4039 PINA (IO Register)
$403a DDRA (IO Register)
$403b PORTA (IO Register)
$403c EECR (IO Register)
$403d EEDR (IO Register)
$403e EEARL (IO Register)
$403f EEARH (IO Register)
$4040 UBRRH/UCSRC (IO Register)
$4041 WDTCR (IO Register)
$4042 ASSR (IO Register)
$4043 OCR2 (IO Register)
$4044 TCNT2 (IO Register)
$4045 TCCR2 (IO Register)
$4046 ICR1L (IO Register)
$4047 ICR1H (IO Register)
$4048 OCR1BL (IO Register)
$4049 OCR1BH (IO Register)
$404a OCR1AL (IO Register)
$404b OCR1AH (IO Register)
$404c TCNT1L (IO Register)
$404d TCNT1H (IO Register)
$404e TCCR1B (IO Register)
$404f TCCR1A (IO Register)
$4050 SFIOR (IO Register)
$4051 OSCCAL/OCDR (IO Register)
$4052 TCNT0 (IO Register)
$4053 TCCR0 (IO Register)
$4054 MCUCSR (IO Register)
$4055 MCUCR (IO Register)
$4056 TWCR (IO Register)
$4057 SPMCSR (IO Register)
$4058 TIFR (IO Register)
$4059 TIMSK (IO Register)
$405a GIFR (IO Register)
$405b GICR (IO Register)
$405c OCR0 (IO Register)
$405d SPL (IO Register)
$405e SPH (IO Register)
$405f SREG (IO Register)
$4060 ADC0: Output Current Sense COMM_MAIN_CBLOCK_BASE
$4061 ADC1: Multi Adjust Offset - CBLOCK_MULTI_A_OFFSET
$4062 ADC2: Power Supply Voltage
$4063 ADC3: Battery Voltage
$4064 ADC4: Level Pot A - CBLOCK_POT_A_OFFSET
$4065 ADC5: Level Pot B - CBLOCK_POT_B_OFFSET
$4066 ADC6: Audio Input Level A (Half wave)
$4067 ADC7: Audio Input Level B (Half wave)
$4068 Current pushed buttons
$4069 Last pushed buttons
$406A Master timer (MSB) (0x4073 LSB) runs 1.91Hz
$406B Channel A calibration (DAC power offset)
$406C Channel B calibration (DAC power offset)
$406D Menu State
$406E unused
$406F unused
$4070 Execute Command (1)
$4071 Execute Command (2)
$4072 Last random number picked
$4073 Master timer (LSB) runs at 488Hz (8MHz/64(scaler)/256)
$4074 Random 1 mode, 1 (start) or current random mode number
$4075 Random 1 mode, stores counter time when to change mode
$4076 unused
$4077 unused
$4078 Current displayed Menu Item/Mode (not yet selected)
$4079 Lowest Selectable Menu Item/Mode
$407A Highest Selectable Menu Item/Mode
$407b Current Mode
$407c Oscillator Ch A (updated but unused)
$407d Oscillator Ch A (updated but unused)
$407e Oscillator Ch B (updated but unused)
$407F Oscillator Ch B (updated but unused)
$4080 unused (0x00)
$4081 unused (0x00)
$4082 retry counter when communicating with slave (0x02)
$4083 Output Control Flags - COMM_CONTROL_FLAG (0x00)
$4084 module to load if condition met
$4085 when module loading determines which channels to set (0x03)
$4086 Multi Adjust Range Min (0x0f)
$4087 Multi Adjust Range Max (0xff)
$4088 Module timer (3 bytes) low - 244Hz (409uS)
$4089 Module timer (3 bytes) mid - 0.953Hz (1.048S)
$408a Module timer (3 bytes) high - (268.43S)
$408b Module timer (slower) - 30.5Hz
$408c Module temporary byte store
$408d Random Number Min
$408e Random Number Max
$408f Module to load if audio triggered
$4090 Channel A: Current Gate Value (0x06)
$4091 module wants to change channel A gates
$4092 module wants to change channel B gates
$4093 unused
$4094 Next module timer current (0x00)
$4095 Next module timer max (0xff)
$4096 Next module flag (0x00)
$4097 Next module number (0x00)
$4098 Channel A: Current Gate OnTime (0x3e)
$4099 Channel A: Current Gate OffTime (0x3e)
$409a Channel A: Current Gate Select (0x00)
$409b Channel A: number of Gate transitions done (0x00)
$409c Mode Switch Ramp Value Counter (0x9c)
$409d Mode Switch Ramp Value Min (0x9c)
$409e Mode Switch Ramp Value Max (0xff)
$409f Mode Switch Ramp Value Rate (0x07)
$40a0 Mode Switch Ramp Value Step (0x01)
$40a1 Mode Switch Ramp Action at Min (0xfc)
$40a2 Mode Switch Ramp Action at Max (0xfc)
$40a3 Mode Switch Ramp Select (0x01)
$40a4 Mode Switch Ramp Current Timer (0x00)
$40a5 Channel A: Current Intensity Modulation Value (0xff)
$40a6 Channel A: Current Intensity Modulation Min (0xcd)
$40a7 Channel A: Current Intensity Modulation Max (0xff)
$40a8 Channel A: Current Intensity Modulation Rate (0x01)
$40a9 Channel A: Current Intensity Modulation Step (0x01)
$40aa Channel A: Current Intensity Action at Min (0xff)
$40ab Channel A: Current Intensity Action at Max (0xff)
$40ac Channel A: Current Intensity Modulation Select (0x00)
$40ad Channel A: Current Intensity Modulation Timer (0x00)
$40ae Channel A: Current Frequency Modulation Value (0x16)
$40af Channel A: Current Frequency Modulation Min (0x09)
$40b0 Channel A: Current Frequency Modulation Max (0x64)
$40b1 Channel A: Current Frequency Modulation Rate (0x01)
$40b2 Channel A: Current Frequency Modulation Step (0x01)
$40b3 Channel A: Current Frequency Modulation Action Min (0xff)
$40b4 Channel A: Current Frequency Modulation Action Max (0xff)
$40b5 Channel A: Current Frequency Modulation Select (0x08)
$40b6 Channel A: Current Frequency Modulation Timer (0x00)
$40b7 Channel A: Current Width Modulation Value (0x82)
$40b8 Channel A: Current Width Modulation Min (0x32)
$40b9 Channel A: Current Width Modulation Max (0xc8)
$40ba Channel A: Current Width Modulation Rate (0x01)
$40bb Channel A: Current Width Modulation Step (0x01)
$40bc Channel A: Current Width Modulation Action Min (0xff)
$40bd Channel A: Current Width Modulation Action Max (0xff)
$40be Channel A: Current Width Modulation Select (0x04)
$40bf Channel A: Current Width Modulation Timer (0x00)
$40c0 - $4177 Space for User Module Scratchpad A
$4180 Write LCD Parameter
$4181 Write LCD Position
$4182 Parameter r26 for box command
$4183 Parameter r27 for box command
$4184 - $418f unused
$4190 Channel B: Current Gate Value (0 when no output)
$4191 - $4193 unused
$4194 Next module timer current (0x00)
$4195 Next module timer max (0xff)
$4196 Next module flag (0x00)
$4197 Next module number (0x00)
$4198 Channel B: Current Gate OnTime (0x3e)
$4199 Channel B: Current Gate OffTime (0x3e)
$419a Channel B: Current Gate Select (0x00)
$419b Channel B: number of Gate transitions done (0x00)
$419c Mode Switch Ramp Value Counter (0x9c)
$419d Mode Switch Ramp Value Min (0x9c)
$419e Mode Switch Ramp Value Max (0xff)
$419f Mode Switch Ramp Value Rate (0x07)
$41a0 Mode Switch Ramp Value Step (0x01)
$41a1 Mode Switch Ramp Action at Min (0xfc)
$41a2 Mode Switch Ramp Action at Max (0xfc)
$41a3 Mode Switch Ramp Select (0x01)
$41a4 Mode Switch Ramp Current Timer (0x00)
$41a5 Channel B: Current Intensity Modulation Value (0xff)
$41a6 Channel B: Current Intensity Modulation Min (0xcd)
$41a7 Channel B: Current Intensity Modulation Max (0xff)
$41a8 Channel B: Current Intensity Modulation Rate (0x01)
$41a9 Channel B: Current Intensity Modulation Step (0x01)
$41aa Channel B: Current Intensity Action at Min (0xff)
$41ab Channel B: Current Intensity Action at Max (0xff)
$41ac Channel B: Current Intensity Modulation Select (0x00)
$41ad Channel B: Current Intensity Modulation Timer (0x00)
$41ae Channel B: Current Frequency Modulation Value (0x16)
$41af Channel B: Current Frequency Modulation Min (0x09)
$41b0 Channel B: Current Frequency Modulation Max (0x64)
$41b1 Channel B: Current Frequency Modulation Rate (0x01)
$41b2 Channel B: Current Frequency Modulation Step (0x01)
$41b3 Channel B: Current Frequency Modulation Action Min (0xff)
$41b4 Channel B: Current Frequency Modulation Action Max (0xff)
$41b5 Channel B: Current Frequency Modulation Select (0x08)
$41b6 Channel B: Current Frequency Modulation Timer (0x00)
$41b7 Channel B: Current Width Modulation Value (0x82)
$41b8 Channel B: Current Width Modulation Min (0x32)
$41b9 Channel B: Current Width Modulation Max (0xc8)
$41ba Channel B: Current Width Modulation Rate (0x01)
$41bb Channel B: Current Width Modulation Step (0x01)
$41bc Channel B: Current Width Modulation Action Min (0xff)
$41bd Channel B: Current Width Modulation Action Max (0xff)
$41be Channel B: Current Width Modulation Select (0x04)
$41bf Channel B: Current Width Modulation Timer (0x00)
$41c0 - $41cf last 16 MA knob readings used for averaging
$41d0 - $41ef User Module Scratchpad Pointers
$41f0 pointer (counter) for MA knob averaging (0xc0)
$41f1 pointer (counter) for serial output buffer (0x2c)
$41f2 pointer (counter) for serial input buffer (0x20)
$41f3 CurrentTopMode (written during routine write) (0x87)
$41f4 PowerLevel - COMM_POWER_LEVEL / COMM_LMODE (0x02)
$41f5 Split Mode Number A (0x77)
$41f6 Split Mode Number B (0x76)
$41f7 Favourite Mode (0x76)
$41f8 Advanced Parameter: RampLevel (0xe1)
$41f9 Advanced Parameter: RampTime (0x14)
$41fa Advanced Parameter: Depth (0xd7)
$41fb Advanced Parameter: Tempo (0x01)
$41fc Advanced Parameter: Frequency (0x19)
$41fd Advanced Parameter: Effect (0x05)
$41fe Advanced Parameter: Width (0x82)
$41ff Advanced Parameter: Pace (0x05)
$4200 value of advanced parameter being edited
$4201 min value of advanced parameter being edited
$4202 max value of advanced parameter being edited
$4203 battery level as a percentage (0-99)
$4204 calculated pwm frequency
$4205 channel a dac level
$4206 channel b dac level
$4207 debug mode: displays current module number if not 0
$4208 used for DAC SPI transfer
$4209 channel a pwm mark
$420a channel a pwm mark
$420b channel a pwm space
$420c channel a pwm space
$420d Current Multi Adjust Value / COMM_MULTI_AVG
$420e channel b pwm mark
$420f channel b pwm mark
$4210 channel b pwm space
$4211 channel b pwm space
$4212 com instruction expected instruction length
$4213 com cipher key
$4214 com buffer incrementer
$4215 power status bits
$4216 unused
$4217 unused
$4218 - $421f decoded module instruction to parse
$4220 - $422b serial comms input buffer
$422c - $4237 serial comms output buffer
$4238 - $43FF unused

EEPROM

Address Description
$8000 not used, not set
$8001 Magic (0x55 means we're provisioned)
$8002 Box Serial 1
$8003 Box Serial 2
$8004 not used, set to 0x00
$8005 not used, set to 0x00
$8006 ELinkSig1 - ELINK_SIG1_ADDR (default 0x01)
$8007 *ELinkSig2 - ELINK_SIG2_ADDR * (default 0x01)
$8008 TopMode NonVolatile (written during routine write)
$8009 Power Level
$800A Split A Mode Num
$800B Split B Mode Num
$800C Favourite Mode
$800D Advanced Parameter: RampLevel
$800E Advanced Parameter: RampTime
$800F Advanced Parameter: Depth
$8010 Advanced Parameter: Tempo
$8011 Advanced Parameter: Frequency
$8012 Advanced Parameter: Effect
$8013 Advanced Parameter: Width
$8014 Advanced Parameter: Pace
$8015 not used, set to 0x00
$8016 not used, set to 0x00
$8017 not used, set to 0x00
$8018 Start Vector User 1 - COMM_USER_BASE
$8019 Start Vector User 2
$801A Start Vector User 3
$801B Start Vector User 4
$801C Start Vector User 5
$801D Start Vector User 6
$801E Start Vector User 7 (not implemented)
$801F Start Vector User 8 (not implemented)
$8020 - $803f User routine module pointers 0x80-0x9f
$8040 - $80ff Space for User Modules
$8100 - $811f User routine module pointers 0xa0-0xbf
$8120 - $81ff Space for User Modules
   

Memory Address Descriptions

$0000:$0097 - String Table

Contains a portion of the string table used for the UI on the ET312 LCD. Each string is 8 bytes long, padded by spaces (0x20) if needed, with no null termination.

$0098:$00fb - Data Segment

$00fc - Box Version

For the ET312, this will always be 0x0c. (Checked in v1.5 and v1.6 firmware)

$00fd:$00ff - Firmware version

The Major, Minor, and Interval revision for the firmware on the ET312. Usually something like

0x01 0x06 0x00

For the v1.6 firmware

$400f - Register 15, ADC disable and other flags

Byte used for various functions

Bit Description
0 Disable ADC (pots etc) (SYSTEM_FLAG_POTS_DISABLE_MASK)
1 If set then we jump to a new module number given in $4084
2 Can this program be shared with a slave unit
3 Disable Multi Adjust (SYSTEM_FLAG_MULTIA_POT_DISABLE_MASK)
4-7 unused

If bit 0 is set the ADC data is ignored, so effectively disabling the the front panel potentiometers. You can then send commands to change the A, B, and MA levels directly. Enabling again sets the unit back to the actual potentiometer values.

To set the A level write to $4064 (CurrentLevelA 0-255), to set the B level write to $4065 (CurrentLevel B 0-255), to set the MA write to $420D (Current Multi Adjust Value, range from min at $4086 to max at $4087).

$4010 - Register 16, flags

Byte used for various functions

Bit Description
0 ??
1 ??
2 set if we are a linked slave
3 ??
4 ??
5 ??
6 in slave mode determines which registers to send (toggles)
7 ??

$4011 - Register 17, flags

Byte used for various functions

Bit Description
0 when module loading to apply module to channel A
1 when module loading to apply module to channel B
2 used to tell main code that the timer has triggered
3 set while ADC conversion is running
4 ??
5 set if received a full serial command to parse
6 set if serial comms error
7 set if we are a linked master

$4029 - UBRRL I/O Register

The low byte of the Serial I/O Register.

By default, this is set to 0x19, with the U2X bit in $402b (UCSRA) set to 0, meaning that at the 8mhz clock, the serial port will run at 19200 baud. If this byte is set to 0x0c, the serial port will run at 38400 baud with no noticeable effects on the ET312.

Other non-standard, higher baud rates may be possible, but testing has not been successful thus far. See http://wormfood.net/avrbaudcalc.php for baud rate calculations, using the 8mhz table.

$402b - UCSRA I/O Register

Contains the U2X bit for doubling serial baud rates. Testing of setting the U2X bit has usually ended in ET312 communications no longer working properly (checksum errors).

$406D - Menu State

Value Description
0x01 In startup screen or in a menu
0x02 No menu, program is running and displaying

$4070 and $4071 - Box Command

Value Description
0x00 Start "Favourite" Routine
0x01 do nothing
0x02 Display Status Screen
0x03 Select current Menu Item
0x04 Exit Menu
0x05 Same as 0x00
0x06 Set Power Level
0x07 Edit Advanced Parameter
0x08 display next menu item
0x09 display previous menu item
0x0a Show Main Menu
0x0b Jump to split mode settings menu
0x0c Activates Split Mode
0x0d Advanced Value Up
0x0e Advanced Value Down
0x0f Show Advanced Menu
0x10 Switch to Next mode
0x11 Switch to Previous mode
0x12 New Mode
0x13 Write Character to LCD
0x14 Write Number to LCD
0x15 Write String from Stringtable to LCD
0x16 Load module
0x17 Not used (error)
0x18 Clear module (Mute)
0x19 Swap Channel A and B
0x1a Copy Channel A to Channel B
0x1b Copy Channel B to Channel A
0x1c Copy defaults from EEPROM
0x1d Sets up running module registers
0x1e Handles single instruction from a module
0x1f General way to call these functions
0x20 Advanced Setting Update
0x21 Start Ramp
0x22 Does an ADC conversion
0x23 Set LCD position
0x24 (redundant)
0x25 Not used (error)
0x26 Not used (error)
0x27 Not used (error)
0xff No command

Set $4070 to the value above for the command you want to execute. This location is checked in the main loop many times a second. If you want to give more than one command you need to have a short delay after writing to $4070 (>~18mS) to ensure the first command is actioned. If you want to execute two commands you can write a second command to $4071 and this location is checked immediately after $4070 is actioned.

Note: if a command needs parameters, r26 is read from $4182 and r27 is read from $4183

Note: Parameters for load module

Module number is read from $4182

Note: Parameters for set power level

Level $4078
low 0x6b
normal 0x6c
high 0x6d

Note: Parameters for the LCD write command

Command $4180 $4181
Write Character (0x13) Character ASCII value Display Position (+64 = second row)
Write Number (0x14) Numerical Value Display Position (+64 = second row)
Write String (0x15) Stringtable Index ???

$407b - Box Modes

Value Description
0x00 MODE_NUM_POWER_ON
0x01 MODE_NUM_UNKNOWN
0x76 MODE_NUM_WAVES / MODE_NUM_LOWER
0x77 MODE_NUM_STROKE
0x78 MODE_NUM_CLIMB
0x79 MODE_NUM_COMBO
0x7a MODE_NUM_INTENSE
0x7b MODE_NUM_RHYTHM
0x7c MODE_NUM_AUDIO1
0x7d MODE_NUM_AUDIO2
0x7e MODE_NUM_AUDIO3
0x7f MODE_NUM_SPLIT
0x80 MODE_NUM_RANDOM1
0x81 MODE_NUM_RANDOM2
0x82 MODE_NUM_TOGGLE
0x83 MODE_NUM_ORGASM
0x84 MODE_NUM_TORMENT
0x85 MODE_NUM_PHASE1
0x86 MODE_NUM_PHASE2
0x87 MODE_NUM_PHASE3
0x88 MODE_NUM_USER1
0x89 MODE_NUM_USER2
0x8a MODE_NUM_USER3
0x8b MODE_NUM_USER4
0x8c MODE_NUM_USER5
0x8d MODE_NUM_USER6
0x8e MODE_NUM_USER7 / MODE_NUM_UPPER

Note: To set mode

  • Write New Mode Number to $407b
  • Write 0x04 to $4070 (execute "exit menu")
  • Write 0x12 to $4071 (execute "select new mode")
  • Wait 18ms (lets box execute previous commands before you change mode again)

(Note you can write to two adjacent memory locations in one command so you can do a write of 0x4 0x12 to $4070 with the same effect)

$4083 - Phase, Front Panel, Mute/Mono/Stereo Control

Value Description
0x01 Phase Control
0x02 Mute
0x04 Phase Control 2
0x08 Phase Control 3
0x20 Disable Frontpanel Switches
0x40 Mono Mode (off=Stereo)

Note: ErosLink uses the following masks:

  • 0x00 - CONTROLFLAG_NORMAL_MASK
  • 0x04 - CONTROLFLAG_ALLOW_OVERLAP_MASK
  • 0x05 - CONTROLFLAG_PHASE_MASK
  • 0x20 - CONTROLFLAG_DISABLE_SWITCHES_MASK

$4098 - Current Channel Gate Time On

Sets the time on for the current gate ($409A).

$4099 - Current Channel Gate Time Off

Sets the time on for the current gate ($409A).

$409A - Current Channel Gate

Sets which channel gating commands will control.

Value Description
0x01 Channel A
0x02 Channel B
0x03 Channel A+B

$409c-$40bf - Main Variables

The box output is determined by a group of four variables: Ramp, Intensity, Frequency, Width. Each variable has several parameters that determine the current value and how to change that value. Once a module has set up these parameters, the variables get updated automatically as configured. The variables may get set to a static value, they may ramp or or down or loop over time, vary according to the other channel, or the MA knob.

Parameter Name Description
0 Value The current value of the variable
1 Min Minimum value, if reached an action is performed
2 Max Maximum value, if reached an action is performed
3 Rate Rate of update, based on one of three timers selected by Select
4 Step Used to set direction and speed of update
5 At Min What to do when minimum value is reached
6 At Max What to do when maximum value is reached
7 Select Determines how to update the variable
8 Timer Timer count for rate

Defaults when a mode starts:

Variable value min max rate step at min at max select
Ramp 0x9c 0x9c 0xff 0x07 0x01 0xfc (stop) 0xfc (stop) 0x01 (use rate parameter, 244Hz timer)
Intensity 0xff 0xcd 0xff 0x01 0x01 0xff (reverse) 0xff (reverse) 0x00 (static)
Frequency 0x16 0x09 0x64 0x01 0x01 0xff (reverse) 0xff (reverse) 0x08 (use MA knob)
Width 0x82 0x32 0xc8 0x01 0x01 0xff (reverse) 0xff (reverse) 0x04 (use advanced parameter default)

Select

Bit 0 Bit 1 Description
0 0 Set the value to an absolute value determined by the other bits
0 1 Update the value based on timer at $4088 (244Hz)
1 0 Update the value based on timer at $4088 divided by 8 (30.5Hz)
1 1 Update the value based on timer at $4089 (.953Hz)

Absolute value when Bit 0 and Bit 1 are 0:

Value Description
0x00 Leave value alone, nop
0x04 Set the value to advanced_parameter default for this variable
0x08 Set the value to the current MA knob value
0x0c Copy from the other channels value
0x14 Set the value to the inverse of the advanced_parameter default
0x18 Set the value to the inverse of the current MA knob value
0x1c Inverse of the other channels value

Timer based updates when Bit 0 and Bit 1 are both not 0.

bit 7 bit 6 bit 5 Description
0 0 0 Rate is from parameter (example $40ba)
0 0 1 Rate is from advanced_parameter default
0 1 0 Rate is from MA value
0 1 1 Rate is rate from other channel
1 0 0 Rate is inverse of parameter (example $40ba)
1 0 1 Rate is inverse of advanced_parameter default
1 1 0 Rate is inverse of MA value
1 1 1 Rate is inverse of rate from other channel

Each time we reach the "rate" for the selected timer we add the step (can be negative) to the current value. We then look to see if we want to update the "min" value depending on the bits 2, 3, 4:

bit 4 bit 3 bit 2 Description
0 0 0 Don't change min
0 0 1 Set min to advanced_parameter default
0 1 0 Set min to MA value
0 1 1 Set min to min of other channel
1 0 0 Invert current min value
1 0 1 Set min to inverse of advanced_parameter default
1 1 0 Set min to inverse MA value
1 1 1 Set min to inverse of min of other channel

At Min / At Max

This byte specifies what to do when the current value reaches the end of the range (either the maximum or minimum depending on what variable is being used).

Value Description
0x00-0xfb Change to new module (value gives module number)
0xfc Stop, no longer update this variable
0xfd Loop back round (if below min, set to max; if above max, set to min)
0xfe Reverse direction, toggle gate, and continue
0xff Reverse direction and continue

Example

As an example, The "Mode" waves has a single module which uses the defaults as above, but modifies them as follows (for this example we'll just show channel A):

  • sets the MA knob range from ($4086) 0x01 - ($4087) 0x40,
  • sets the Width step ($40bb) to 0x02
  • sets the Width select ($40be) to 0x41 (244Hz timer, rate is from MA knob). So the Width will go from 0x32 to 0xc8 in 0x01 steps at 244Hz, then back down, repeating. Note though that the minimum width is actually 0x46, so the width value will "stick" at 0 for a little while before coming back up.
  • sets the Frequency max to ($40b0) to 0x80
  • sets the Frequency select ($40b5) to 0x41 (244Hz timer, rate is from MA knob). So the Frequency will go from 0x09 to 0x80 in 0x01 steps at 244Hz, then back down, repeating.
  • doesn't change Ramp variable (so the default ramp will start at 0x9c and go up to 0xff in 0x01 steps, at 244Hz/7)
  • doesn't change Intensity variable (so will be static at 0xff)

$41f4 - Power Levels

Value Description
0x00 LOW
0x01 NORMAL
0x02 HIGH
0x03 UNKNOWN

$4215 - Power status bits

Bit Description
0 Set if we have a battery
1 Set if we have a PSU connected
2-7 unused

Common Usages and Tricks

Key Resets

If you just close the connection to the box then the box won't be able to handshake again unless you remember the key you got given last time.

However if just before closing the connection to the box you clear out the current XOR key, future serial connections to the box will appear as new connections without needing the box to be power cycled.

Do this by sending command below to Write Bytes, setting location $4213 to 0x00.

Peeking before Key Exchange

Most client software performs a key handshake. However, if your application only wants to read bytes there is no need to perform a handshake and exchange keys.

Writing to the LCD

Baud Rate Changes

Created: 2016-07-14 Thu 14:33

Validate