Skip to main content

Erostek ET-312B

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.

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. Note that the final key will have the nibbles of this value flipped (see Key Usage section).
  • 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 with nibbles flipped \^ Box Key \^ 0x55)

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

The value sent as the host key in the key exchange will need to have the nibbles flipped when this final key is calculated. For instance, if the value sent to the ET312 during exchange is 0x12, when calculating the key, the value used should be 0x21.

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

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 RangeDescription
\$0000 - \$00ffFlash (256b from 0x1f00-0x1fff)
\$4000 - \$43ffRegisters and Partial RAM (1k)
\$8000 - \$81ffEEPROM (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 (plus checksum, the 8-bit unsigned sum of the first two bytes, wrapped if the sum is > 255.)

0x3c 0xGG 0xHH 0xCC

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

The box will then respond with two bytes (plus checksum, as above)

0x22 0xVV 0xCC

  • 0xVV - Content of requested address
  • 0xCC - Checksum

Write Bytes

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

0xGd 0xHH 0xII [0xJJ 0xKK...] 0xCC

  • 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
  • 0xCC - Checksum

The box will then respond with 0x06 (ACK).

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

0x5d 0x40 0x10 0xfe 0xff 0xaa
  • 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.
  • 0xaa is the checksum

Memory Layout Tables

All entries in bold have been mapped and are useful.

Flash

AddressDescription
\$0000 - \$0098Partial String Table
\$0098 - \$00fb.data Segment
\$00fcBox Model
\$00fd - \$00ffFirmware Version

RAM

AddressDescription
\$4000r0 (CPU Register)
\$4001r1 (CPU Register)
\$4002r2 (CPU Register)
\$4003r3 (CPU Register)
\$4004r4 (CPU Register)
\$4005r5 (CPU Register) copied from \$4090
\$4006r6 (CPU Register) copied from \$409c
\$4007r7 (CPU Register) copied from \$40a5
\$4008r8 (CPU Register) copied from min(9, \$40ae)
\$4009r9 (CPU Register) copied from min(50,\$40b7)
\$400ar10 (CPU Register) copied from \$4190
\$400br11 (CPU Register) copied from \$419c
\$400cr12 (CPU Register) copied from \$41a5
\$400dr13 (CPU Register) copied from min(9, \$41ae)
\$400er14 (CPU Register) copied from min(50, \$41b7)
\$400fr15/ADC disable and other flags - COMM~SYSTEMFLAG~
\$4010r16 (CPU Register) various flags
\$4011r17 (CPU Register) various flags
\$4012r18 (CPU Register)
\$4013r19 (CPU Register) action when down key pushed
\$4014r20 (CPU Register) action when up key pushed
\$4015r21 (CPU Register) action when menu key pushed
\$4016r22 (CPU Register) action when ok key pushed
\$4017r23 (CPU Register)
\$4018r24 (CPU Register)
\$4019r25 (CPU Register)
\$401ar26 (CPU Register)
\$401br27 (CPU Register)
\$401cr28 (CPU Register)
\$401dr29 (CPU Register)
\$401er30 (CPU Register)
\$401fr31 (CPU Register)
\$4020TWBR (IO Register)
\$4021TWSR (IO Register)
\$4022TWAR (IO Register)
\$4023TWDR (IO Register)
\$4024ADCL (IO Register)
\$4025ADCH (IO Register)
\$4026ADCSRA (IO Register)
\$4027ADMUX (IO Register)
\$4028ACSR (IO Register)
\$4029UBRRL (IO Register, Baud Rate)
\$402aUCSRB (IO Register)
\$402bUCSRA (IO Register)
\$402cUDR (IO Register)
\$402dSPCR (IO Register)
\$402eSPSR (IO Register)
\$402fSPDR (IO Register)
\$4030PIND (IO Register)
\$4031DDRD (IO Register)
\$4032PORTD (IO Register)
\$4033PINC (IO Register)
\$4034DDRC (IO Register)
\$4035PORTC (IO Register)
\$4036PINB (IO Register)
\$4037DDRB (IO Register)
\$4038PORTB (IO Register)
\$4039PINA (IO Register)
\$403aDDRA (IO Register)
\$403bPORTA (IO Register)
\$403cEECR (IO Register)
\$403dEEDR (IO Register)
\$403eEEARL (IO Register)
\$403fEEARH (IO Register)
\$4040UBRRH/UCSRC (IO Register)
\$4041WDTCR (IO Register)
\$4042ASSR (IO Register)
\$4043OCR2 (IO Register)
\$4044TCNT2 (IO Register)
\$4045TCCR2 (IO Register)
\$4046ICR1L (IO Register)
\$4047ICR1H (IO Register)
\$4048OCR1BL (IO Register)
\$4049OCR1BH (IO Register)
\$404aOCR1AL (IO Register)
\$404bOCR1AH (IO Register)
\$404cTCNT1L (IO Register)
\$404dTCNT1H (IO Register)
\$404eTCCR1B (IO Register)
\$404fTCCR1A (IO Register)
\$4050SFIOR (IO Register)
\$4051OSCCAL/OCDR (IO Register)
\$4052TCNT0 (IO Register)
\$4053TCCR0 (IO Register)
\$4054MCUCSR (IO Register)
\$4055MCUCR (IO Register)
\$4056TWCR (IO Register)
\$4057SPMCSR (IO Register)
\$4058TIFR (IO Register)
\$4059TIMSK (IO Register)
\$405aGIFR (IO Register)
\$405bGICR (IO Register)
\$405cOCR0 (IO Register)
\$405dSPL (IO Register)
\$405eSPH (IO Register)
\$405fSREG (IO Register)
\$4060ADC0: Output Current Sense COMM~MAINCBLOCKBASE~
\$4061ADC1: Multi Adjust Offset - CBLOCK~MULTIAOFFSET~
\$4062ADC2: Power Supply Voltage
\$4063ADC3: Battery Voltage
\$4064ADC4: Level Pot A - CBLOCK~POTAOFFSET~
\$4065ADC5: Level Pot B - CBLOCK~POTBOFFSET~
\$4066ADC6: Audio Input Level A (Half wave)
\$4067ADC7: Audio Input Level B (Half wave)
\$4068Current pushed buttons
\$4069Last pushed buttons
\$406AMaster timer (MSB) (0x4073 LSB) runs 1.91Hz
\$406BChannel A calibration (DAC power offset)
\$406CChannel B calibration (DAC power offset)
\$406DMenu State
\$406Eunused
\$406Funused
\$4070Execute Command (1)
\$4071Execute Command (2)
\$4072Last random number picked
\$4073Master timer (LSB) runs at 488Hz (8MHz/64(scaler)/256)
\$4074Random 1 mode, 1 (start) or current random mode number
\$4075Random 1 mode, stores counter time when to change mode
\$4076unused
\$4077unused
\$4078Current displayed Menu Item/Mode (not yet selected)
\$4079Lowest Selectable Menu Item/Mode
\$407AHighest Selectable Menu Item/Mode
\$407bCurrent Mode
\$407cOscillator Ch A (updated but unused)
\$407dOscillator Ch A (updated but unused)
\$407eOscillator Ch B (updated but unused)
\$407FOscillator Ch B (updated but unused)
\$4080unused (0x00)
\$4081unused (0x00)
\$4082retry counter when communicating with slave (0x02)
\$4083Output Control Flags - COMM~CONTROLFLAG~ (0x00)
\$4084module to load if condition met
\$4085when module loading determines which channels to set (0x03)
\$4086Multi Adjust Range Min (0x0f)
\$4087Multi Adjust Range Max (0xff)
\$4088Module timer (3 bytes) low - 244Hz (409uS)
\$4089Module timer (3 bytes) mid - 0.953Hz (1.048S)
\$408aModule timer (3 bytes) high - (268.43S)
\$408bModule timer (slower) - 30.5Hz
\$408cModule temporary byte store
\$408dRandom Number Min
\$408eRandom Number Max
\$408fModule to load if audio triggered
\$4090Channel A: Current Gate Value (0x06)
\$4091module wants to change channel A gates
\$4092module wants to change channel B gates
\$4093unused
\$4094Next module timer current (0x00)
\$4095Next module timer max (0xff)
\$4096Next module flag (0x00)
\$4097Next module number (0x00)
\$4098Channel A: Current Gate OnTime (0x3e)
\$4099Channel A: Current Gate OffTime (0x3e)
\$409aChannel A: Current Gate Select (0x00)
\$409bChannel A: number of Gate transitions done (0x00)
\$409cMode Switch Ramp Value Counter (0x9c)
\$409dMode Switch Ramp Value Min (0x9c)
\$409eMode Switch Ramp Value Max (0xff)
\$409fMode Switch Ramp Value Rate (0x07)
\$40a0Mode Switch Ramp Value Step (0x01)
\$40a1Mode Switch Ramp Action at Min (0xfc)
\$40a2Mode Switch Ramp Action at Max (0xfc)
\$40a3Mode Switch Ramp Select (0x01)
\$40a4Mode Switch Ramp Current Timer (0x00)
\$40a5Channel A: Current Intensity Modulation Value (0xff)
\$40a6Channel A: Current Intensity Modulation Min (0xcd)
\$40a7Channel A: Current Intensity Modulation Max (0xff)
\$40a8Channel A: Current Intensity Modulation Rate (0x01)
\$40a9Channel A: Current Intensity Modulation Step (0x01)
\$40aaChannel A: Current Intensity Action at Min (0xff)
\$40abChannel A: Current Intensity Action at Max (0xff)
\$40acChannel A: Current Intensity Modulation Select (0x00)
\$40adChannel A: Current Intensity Modulation Timer (0x00)
\$40aeChannel A: Current Frequency Modulation Value (0x16)
\$40afChannel A: Current Frequency Modulation Min (0x09)
\$40b0Channel A: Current Frequency Modulation Max (0x64)
\$40b1Channel A: Current Frequency Modulation Rate (0x01)
\$40b2Channel A: Current Frequency Modulation Step (0x01)
\$40b3Channel A: Current Frequency Modulation Action Min (0xff)
\$40b4Channel A: Current Frequency Modulation Action Max (0xff)
\$40b5Channel A: Current Frequency Modulation Select (0x08)
\$40b6Channel A: Current Frequency Modulation Timer (0x00)
\$40b7Channel A: Current Width Modulation Value (0x82)
\$40b8Channel A: Current Width Modulation Min (0x32)
\$40b9Channel A: Current Width Modulation Max (0xc8)
\$40baChannel A: Current Width Modulation Rate (0x01)
\$40bbChannel A: Current Width Modulation Step (0x01)
\$40bcChannel A: Current Width Modulation Action Min (0xff)
\$40bdChannel A: Current Width Modulation Action Max (0xff)
\$40beChannel A: Current Width Modulation Select (0x04)
\$40bfChannel A: Current Width Modulation Timer (0x00)
\$40c0 - \$4177Space for User Module Scratchpad A
\$4180Write LCD Parameter
\$4181Write LCD Position
\$4182Parameter r26 for box command
\$4183Parameter r27 for box command
\$4184set to random number during Random 1 Program
\$4185 - \$418funused
\$4190Channel B: Current Gate Value (0 when no output)
\$4191 - \$4193unused
\$4194Next module timer current (0x00)
\$4195Next module timer max (0xff)
\$4196Next module flag (0x00)
\$4197Next module number (0x00)
\$4198Channel B: Current Gate OnTime (0x3e)
\$4199Channel B: Current Gate OffTime (0x3e)
\$419aChannel B: Current Gate Select (0x00)
\$419bChannel B: number of Gate transitions done (0x00)
\$419cMode Switch Ramp Value Counter (0x9c)
\$419dMode Switch Ramp Value Min (0x9c)
\$419eMode Switch Ramp Value Max (0xff)
\$419fMode Switch Ramp Value Rate (0x07)
\$41a0Mode Switch Ramp Value Step (0x01)
\$41a1Mode Switch Ramp Action at Min (0xfc)
\$41a2Mode Switch Ramp Action at Max (0xfc)
\$41a3Mode Switch Ramp Select (0x01)
\$41a4Mode Switch Ramp Current Timer (0x00)
\$41a5Channel B: Current Intensity Modulation Value (0xff)
\$41a6Channel B: Current Intensity Modulation Min (0xcd)
\$41a7Channel B: Current Intensity Modulation Max (0xff)
\$41a8Channel B: Current Intensity Modulation Rate (0x01)
\$41a9Channel B: Current Intensity Modulation Step (0x01)
\$41aaChannel B: Current Intensity Action at Min (0xff)
\$41abChannel B: Current Intensity Action at Max (0xff)
\$41acChannel B: Current Intensity Modulation Select (0x00)
\$41adChannel B: Current Intensity Modulation Timer (0x00)
\$41aeChannel B: Current Frequency Modulation Value (0x16)
\$41afChannel B: Current Frequency Modulation Min (0x09)
\$41b0Channel B: Current Frequency Modulation Max (0x64)
\$41b1Channel B: Current Frequency Modulation Rate (0x01)
\$41b2Channel B: Current Frequency Modulation Step (0x01)
\$41b3Channel B: Current Frequency Modulation Action Min (0xff)
\$41b4Channel B: Current Frequency Modulation Action Max (0xff)
\$41b5Channel B: Current Frequency Modulation Select (0x08)
\$41b6Channel B: Current Frequency Modulation Timer (0x00)
\$41b7Channel B: Current Width Modulation Value (0x82)
\$41b8Channel B: Current Width Modulation Min (0x32)
\$41b9Channel B: Current Width Modulation Max (0xc8)
\$41baChannel B: Current Width Modulation Rate (0x01)
\$41bbChannel B: Current Width Modulation Step (0x01)
\$41bcChannel B: Current Width Modulation Action Min (0xff)
\$41bdChannel B: Current Width Modulation Action Max (0xff)
\$41beChannel B: Current Width Modulation Select (0x04)
\$41bfChannel B: Current Width Modulation Timer (0x00)
\$41c0 - \$41cflast 16 MA knob readings used for averaging
\$41d0 - \$41efUser Module Scratchpad Pointers
\$41f0pointer (counter) for MA knob averaging (0xc0)
\$41f1pointer (counter) for serial output buffer (0x2c)
\$41f2pointer (counter) for serial input buffer (0x20)
\$41f3CurrentTopMode (written during routine write) (0x87)
\$41f4PowerLevel - COMM~POWERLEVEL~ / COMM~LMODE~ (0x02)
\$41f5Split Mode Number A (0x77)
\$41f6Split Mode Number B (0x76)
\$41f7Favourite Mode (0x76)
\$41f8Advanced Parameter: RampLevel (0xe1)
\$41f9Advanced Parameter: RampTime (0x14)
\$41faAdvanced Parameter: Depth (0xd7)
\$41fbAdvanced Parameter: Tempo (0x01)
\$41fcAdvanced Parameter: Frequency (0x19)
\$41fdAdvanced Parameter: Effect (0x05)
\$41feAdvanced Parameter: Width (0x82)
\$41ffAdvanced Parameter: Pace (0x05)
\$4200value of advanced parameter being edited
\$4201min value of advanced parameter being edited
\$4202max value of advanced parameter being edited
\$4203battery level as a percentage (0-99)
\$4204calculated pwm frequency
\$4205channel a dac level
\$4206channel b dac level
\$4207debug mode: displays current module number if not 0
\$4208used for DAC SPI transfer
\$4209channel a pwm mark
\$420achannel a pwm mark
\$420bchannel a pwm space
\$420cchannel a pwm space
\$420dCurrent Multi Adjust Value / COMM~MULTIAVG~
\$420echannel b pwm mark
\$420fchannel b pwm mark
\$4210channel b pwm space
\$4211channel b pwm space
\$4212com instruction expected instruction length
\$4213com cipher key
\$4214com buffer incrementer
\$4215power status bits
\$4216unused
\$4217unused
\$4218 - \$421fdecoded module instruction to parse
\$4220 - \$422bserial comms input buffer
\$422c - \$4237serial comms output buffer
\$4238 - \$43FFunused

EEPROM

AddressDescription
\$8000not used, not set
\$8001Magic (0x55 means we're provisioned)
\$8002Box Serial 1
\$8003Box Serial 2
\$8004not used, set to 0x00
\$8005not used, set to 0x00
\$8006ELinkSig1 - ELINK~SIG1ADDR~ (default 0x01)
\$8007*ELinkSig2 - ELINK~SIG2ADDR~ * (default 0x01)
\$8008TopMode NonVolatile (written during routine write)
\$8009Power Level
\$800ASplit A Mode Num
\$800BSplit B Mode Num
\$800CFavourite Mode
\$800DAdvanced Parameter: RampLevel
\$800EAdvanced Parameter: RampTime
\$800FAdvanced Parameter: Depth
\$8010Advanced Parameter: Tempo
\$8011Advanced Parameter: Frequency
\$8012Advanced Parameter: Effect
\$8013Advanced Parameter: Width
\$8014Advanced Parameter: Pace
\$8015not used, set to 0x00
\$8016not used, set to 0x00
\$8017not used, set to 0x00
\$8018Start Vector User 1 - COMM~USERBASE~
\$8019Start Vector User 2
\$801AStart Vector User 3
\$801BStart Vector User 4
\$801CStart Vector User 5
\$801DStart Vector User 6
\$801EStart Vector User 7 (not implemented)
\$801FStart Vector User 8 (not implemented)
\$8020 - \$803fUser routine module pointers 0x80-0x9f
\$8040 - \$80ffSpace for User Modules
\$8100 - \$811fUser routine module pointers 0xa0-0xbf
\$8120 - \$81ffSpace 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

BitDescription
0Disable ADC (pots etc) (SYSTEM~FLAGPOTSDISABLEMASK~)
1If set then we jump to a new module number given in \$4084
2Can this program be shared with a slave unit
3Disable Multi Adjust (SYSTEM~FLAGMULTIAPOTDISABLEMASK~)
4-7unused

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

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

\$4011 - Register 17, flags

Byte used for various functions

BitDescription
0when module loading to apply module to channel A
1when module loading to apply module to channel B
2used to tell main code that the timer has triggered
3set while ADC conversion is running
4??
5set if received a full serial command to parse
6set if serial comms error
7set 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

ValueDescription
0x01In startup screen or in a menu
0x02No menu, program is running and displaying

\$4070 and \$4071 - Box Command

ValueDescription
0x00Start "Favourite" Routine
0x01do nothing
0x02Display Status Screen
0x03Select current Menu Item
0x04Exit Menu
0x05Same as 0x00
0x06Set Power Level
0x07Edit Advanced Parameter
0x08display next menu item
0x09display previous menu item
0x0aShow Main Menu
0x0bJump to split mode settings menu
0x0cActivates Split Mode
0x0dAdvanced Value Up
0x0eAdvanced Value Down
0x0fShow Advanced Menu
0x10Switch to Next mode
0x11Switch to Previous mode
0x12New Mode
0x13Write Character to LCD
0x14Write Number to LCD
0x15Write String from Stringtable to LCD
0x16Load module
0x17Not used (error)
0x18Clear module (Mute)
0x19Swap Channel A and B
0x1aCopy Channel A to Channel B
0x1bCopy Channel B to Channel A
0x1cCopy defaults from EEPROM
0x1dSets up running module registers
0x1eHandles single instruction from a module
0x1fGeneral way to call these functions
0x20Advanced Setting Update
0x21Start Ramp
0x22Does an ADC conversion
0x23Set LCD position
0x24(redundant)
0x25Not used (error)
0x26Not used (error)
0x27Not used (error)
0xffNo 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
low0x6b
normal0x6c
high0x6d

Note: Parameters for the LCD write command

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

\$407b - Box Modes

ValueDescription
0x00MODE~NUMPOWERON~
0x01MODE~NUMUNKNOWN~
0x76MODE~NUMWAVES~ / MODE~NUMLOWER~
0x77MODE~NUMSTROKE~
0x78MODE~NUMCLIMB~
0x79MODE~NUMCOMBO~
0x7aMODE~NUMINTENSE~
0x7bMODE~NUMRHYTHM~
0x7cMODE~NUMAUDIO1~
0x7dMODE~NUMAUDIO2~
0x7eMODE~NUMAUDIO3~
0x7fMODE~NUMSPLIT~
0x80MODE~NUMRANDOM1~
0x81MODE~NUMRANDOM2~
0x82MODE~NUMTOGGLE~
0x83MODE~NUMORGASM~
0x84MODE~NUMTORMENT~
0x85MODE~NUMPHASE1~
0x86MODE~NUMPHASE2~
0x87MODE~NUMPHASE3~
0x88MODE~NUMUSER1~
0x89MODE~NUMUSER2~
0x8aMODE~NUMUSER3~
0x8bMODE~NUMUSER4~
0x8cMODE~NUMUSER5~
0x8dMODE~NUMUSER6~
0x8eMODE~NUMUSER7~ / MODE~NUMUPPER~

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

ValueDescription
0x01Phase Control
0x02Mute
0x04Phase Control 2
0x08Phase Control 3
0x20Disable Frontpanel Switches
0x40Mono Mode (off=Stereo)

Note: ErosLink uses the following masks:

  • 0x00 - CONTROLFLAG~NORMALMASK~
  • 0x04 - CONTROLFLAG~ALLOWOVERLAPMASK~
  • 0x05 - CONTROLFLAG~PHASEMASK~
  • 0x20 - CONTROLFLAG~DISABLESWITCHESMASK~

\$4098-\$409b - Current Channel Gate

The output for each channel can be gated (turned on and off continously) with a variable speed determined by the value of the Gate Select.

Select \$409a:

Bit 1Bit 0Description
00No gating
01Use the \$4088 (244Hz) timer for gating
10Use the \$4088 div 8 (30.5Hz) timer for gating
11Use the \$4089 (.953Hz) timer for gating

If not other bits in Select are set then the on and off time come from \$4098 (on, default 0x3e) and \$4099 (off, default 0x3e)

BitDescription
2Off time is taken from the advanced parameter tempo default
3Off time follows the value of the MA knob
4Not used
5On time is taken from the advanced parameter effect default
6On time follows the value of the MA knob
7Not used

\$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.

ParameterNameDescription
0ValueThe current value of the variable
1MinMinimum value, if reached an action is performed
2MaxMaximum value, if reached an action is performed
3RateRate of update, based on one of three timers selected by Select
4StepUsed to set direction and speed of update
5At MinWhat to do when minimum value is reached
6At MaxWhat to do when maximum value is reached
7SelectDetermines how to update the variable
8TimerTimer count for rate

Defaults when a mode starts:

Variablevalueminmaxratestepat minat maxselect
Ramp0x9c0x9c0xff0x070x010xfc (stop)0xfc (stop)0x01 (use rate parameter, 244Hz timer)
Intensity0xff0xcd0xff0x010x010xff (reverse)0xff (reverse)0x00 (static)
Frequency0x160x090x640x010x010xff (reverse)0xff (reverse)0x08 (use MA knob)
Width0x820x320xc80x010x010xff (reverse)0xff (reverse)0x04 (use advanced parameter default)

Examples showing how these are used in Programs

Select

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

Absolute value when Bit 0 and Bit 1 are both 0:

ValueDescription
0x00Leave value alone, nop
0x04Set the value to advanced~parameter~ default for this variable
0x08Set the value to the current MA knob value
0x0cCopy from the other channels value
0x14Set the value to the inverse of the advanced~parameter~ default
0x18Set the value to the inverse of the current MA knob value
0x1cInverse of the other channels value

Timer based updates when either Bit 0 or Bit 1 are 1:

bit 7bit 6bit 5Description
000Rate is from parameter (example \$40ba)
001Rate is from advanced~parameter~ default
010Rate is from MA value
011Rate is rate from other channel
100Rate is inverse of parameter (example \$40ba)
101Rate is inverse of advanced~parameter~ default
110Rate is inverse of MA value
111Rate 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-4:

bit 4bit 3bit 2Description
000Don't change min
001Set min to advanced~parameter~ default
010Set min to MA value
011Set min to min of other channel
100Invert current min value
101Set min to inverse of advanced~parameter~ default
110Set min to inverse MA value
111Set 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).

ValueDescription
0x00-0xfbChange to new module (value gives module number)
0xfcStop, no longer update this variable
0xfdLoop back round (if below min, set to max; if above max, set to min)
0xfeReverse direction, toggle gate, and continue
0xffReverse direction and continue

\$41f4 - Power Levels

ValueDescription
0x00LOW
0x01NORMAL
0x02HIGH
0x03UNKNOWN

\$4215 - Power status bits

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

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.