Skip to content

Latest commit

 

History

History
743 lines (674 loc) · 39.9 KB

et312-protocol.org

File metadata and controls

743 lines (674 loc) · 39.9 KB

ET312 Protocol Documentation

Introduction

This document is a specification for the serial communications protocol of the ET312 Electrostimulation box by Erostek. The protocol was put together through multiple sources:

  • Serial monitoring of box communications with various controls software
  • Mapping from the original erosoutsider perl files
  • Message board/Mailing list posts from around the internet

The following specifications are for v1.6 of the Erostek firmware, which it is assumed all modern boxes are running.

Protocol and Communication Basics

Communication via Link Cable

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

Encryption

Communication with the box is encrypted, using a simple XOR stream cipher based on keys exchanged during handshake.

Handshake

HELLO (Connection establishment)

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 exchange only needs to happen once, but can be repeated.

XOR Key Synchronization

After the HELLO ends, 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:

  • 0xWW 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 first XOR key
    • 0x33 is the checksum (0x2f + 0x04)
  • [0x21, 0x8f, 0xb0] is read from ET312, meaning
    • 0x8f is the XOR key
    • 0xb0 is the checksum (0x21 + 0x8f)

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.

XOR Key Usage

Once the XOR 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 ^ (XOR Key 1 ^ XOR Key 2 ^ 0x55)

The part of the expression in parenthesis will be constant, 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.

Commands

Commands and Accessible Memory

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)
$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 (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 0x4010, 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 (0x4010)
  • 0xfe 0xff is the data we want to write to 0x4010 and 0x4011, respectively.

Memory Layout Tables

All entries in bold have been mapped and are useful.

Flash

AddressDescription
$0000 - $0098Partial String Table
$0098 - $00fb?? (Possibly a continuation of .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)
$4006r6 (CPU Register)
$4007r7 (CPU Register)
$4008r8 (CPU Register)
$4009r9 (CPU Register)
$400ar10 (CPU Register)
$400br11 (CPU Register)
$400cr12 (CPU Register)
$400dr13 (CPU Register)
$400er14 (CPU Register)
$400fr15/Front Panel Pot Lockout Flags - COMM_SYSTEM_FLAG
$4010r16 (CPU Register)
$4011r17 (CPU Register)
$4012r18 (CPU Register)
$4013r19 (CPU Register)
$4014r20 (CPU Register)
$4015r21 (CPU Register)
$4016r22 (CPU Register)
$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)
$4060COMM_MAIN_CBLOCK_BASE
$4061Multi Adjust Offset - CBLOCK_MULTI_A_OFFSET
$4062Power Supply Voltage
$4063Battery Voltage
$4064CurrentLevel A - CBLOCK_POT_A_OFFSET
$4065CurrentLevel B - CBLOCK_POT_B_OFFSET
$4066Audio Input Level A
$4067Audio Input Level B
$4068??
$4069Currently Pressed Button
$406A?? (some counter)
$406B??
$406C??
$406DMenu State
$406E??
$406F??
$4070Execute Command
$4071?? (does something when written to)
$4072?? (random number in random modes)
$4073?? (some timer)
$4074?? (writing disables MA)
$4075?? (writing does nothing, changes in random modes)
$4076?? (00)
$4077?? (00)
$4078Current Menu Selection
$4079Lowest Selectable Mode
$407AHighest Selectable Mode
$407bCurrent Mode
$407c?? (Oscillator Ch A? )
$407d?? (Oscillator Ch A? )
$407e?? (Oscillator Ch B? )
$407F?? (Oscillator Ch B? )
$4080?? (gets set to 0x00 when routine loaded)
$4081??
$4082??
$4083Output Control Flags - COMM_CONTROL_FLAG
$4084?? (gets set to 0x00 when routine loaded)
$4085?? (gets set to 0x03 when routine loaded)
$4086Multi Adjust Range High End
$4087Multi Adjust Range Low End
$4088Routine timer low
$4089Routine timer high
$408A?? (gets set to 0x00 when routine loaded)
$408B?? (some timer)
$408C?? (gets set to 0x00 when routine loaded)
$408D?? (used by torment routine)
$408E?? (used by torment routine)
$408F?? (gets set to 0x00 when routine loaded)
$4090Channel A: Current Gate Value (0 when no output)
$4091??
$4092??
$4093??
$4094??
$4095??
$4096??
$4097??
$4098Channel A: Current Gate OnTime
$4099Channel A: Current Gate OffTime
$409AChannel A: Current Gate Select
$409B??
$409CMode Switch Ramp Value Counter
$40A0??
$40A1??
$40A2??
$40A3Mode Switch Ramp Select
$40A4??
$40A5Channel A: Current Intensity Modulation Value
$40A6Channel A: Current Intensity Modulation Min
$40A7Channel A: Current Intensity Modulation Max
$40A8Channel A: Current Intensity Modulation Rate
$40A9??
$40AA??
$40AB??
$40acChannel A: Current Intensity Modulation Select
$40AD??
$40aeChannel A: Current Frequency Modulation Value
$40afChannel A: Current Frequency Modulation Min
$40b0Channel A: Current Frequency Modulation Max
$40B1Channel A: Current Frequency Modulation Rate
$40b2??
$40b3??
$40b4??
$40b5Channel A: Current Frequency Modulation Select
$40b6??
$40b7Channel A: Current Width Modulation Value
$40b8Channel A: Current Width Modulation Min
$40b9Channel A: Current Width Modulation Max
$40baChannel A: Current Width Modulation Rate
$40bb??
$40bc??
$40bd??
$40beChannel A: Current Width Modulation Select
$41bf??
$40c0 - $4177Space for User Routine Scratchpad A
$4180Write LCD Parameter
$4181Write LCD Position
$4182??
$4183??
$4184??
$4185??
$4186??
$4187??
$4188??
$4189??
$418A??
$418B??
$418C??
$418D??
$418E??
$418F??
$4190Channel A: Current Gate Value (0 when no output)
$4191??
$4192??
$4193??
$4194??
$4195??
$4196??
$4197??
$4198Channel B: Current Gate OnTime
$4199Channel B: Current Gate OffTime
$419AChannel B: Current Gate Select
$419B??
$419C?? (Ramp Value?)
$41A0??
$41A1??
$41A2??
$41A3?? (Ramp Select?)
$41A4??
$41A5Channel B: Current Intensity Modulation Value
$41A6Channel B: Current Intensity Modulation Min
$41A7Channel B: Current Intensity Modulation Max
$41A8Channel B: Current Intensity Modulation Rate
$41A9??
$41AA??
$41AB??
$41acChannel B: Current Intensity Modulation Select
$41AD??
$41aeChannel B: Current Frequency Modulation Value
$41afChannel B: Current Frequency Modulation Min
$41b0Channel B: Current Frequency Modulation Max
$41B1Channel B: Current Frequency Modulation Rate
$41b2??
$41b3??
$41b4??
$41b5Channel B: Current Frequency Modulation Select
$41b6??
$41b7Channel B: Current Width Modulation Value
$41b8Channel B: Current Width Modulation Min
$41b9Channel B: Current Width Modulation Max
$41baChannel B: Current Width Modulation Rate
$41bb??
$41bc??
$41bd??
$41beChannel B: Current Width Modulation Select
$41bf??
$41c0??
$41c1??
$41c2??
$41c3??
$41c4??
$41c5??
$41c6??
$41c7??
$41c8??
$41c9??
$41ca??
$41cb??
$41cc??
$41cd??
$41ce??
$41cf??
$41D0 - $41efSpace for User Routine Scratchpad B
$41f0?? (Counter)
$41f1?? (Crashes on write)
$41f2?? (Unknown)
$41f3CurrentTopMode (written during routine write)
$41f4PowerLevel - COMM_POWER_LEVEL / COMM_LMODE
$41f5Split Mode Number A
$41f6Split Mode Number B
$41f7Favourite Mode
$41F8Advanced Parameter: RampLevel
$41F9Advanced Parameter: RampTime
$41FAAdvanced Parameter: Depth
$41FBAdvanced Parameter: Tempo
$41FCAdvanced Parameter: Frequency
$41FDAdvanced Parameter: Effect
$41FEAdvanced Parameter: Width
$41FFAdvanced Parameter: Pace
$4200??
$4201??
$4202??
$4203??
$4204??
$4205??
$4206??
$4207??
$4208??
$4209??
$420a??
$420b??
$420c??
$420dCurrent Multi Adjust Value / COMM_MULTI_AVG
$420e - $4212??
$4213?? (Writing 0 kills serial communication)
$4214 - $422c??
$422d - $43FFPossibly Unused (can zero out, never see changes)

EEPROM

AddressDescription
$8000??
$8001??
$8002BoxSerial1
$8003BoxSerial2
$8004??
$8005??
$8006ELinkSig1 - ELINK_SIG1_ADDR
$8007*ELinkSig2 - ELINK_SIG2_ADDR *
$8008TopMode NonVolatile (written during routine write)
$8009PowerLevel
$800ASplitAModeNum
$800BSplitBModeNum
$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
$8015??
$8016??
$8017??
$8018Start Vector User 1 - COMM_USER_BASE
$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 - $803fSpace for User Routines A
$8040 - $80ffSpace for User Routines B
$8100 - $813fSpace for User Routines C
$8120 - $81ffSpace for User Routines D

Memory Specifics

$0000:$0098 - Partial 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 - ?? (Possibly a continuation of .data segment)

Unknown contents. This area may possibly be other constant setup in the .data section of the firmware.

$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, Front Panel Potentiometer Lockout Flags

Byte used to enable/disable front panel potentiometers.

ValueDescription
0x01Disable Level Pots (SYSTEM_FLAG_POTS_DISABLE_MASK)
0x08Disable Multi Adjust (SYSTEM_FLAG_MULTIA_POT_DISABLE_MASK)

Once the front panel potentiometers have been disabled 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).

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

$4070 - Box Command

ValueDescription
0x00Reset Current Routine
0x02Display Status Screen
0x03Select current Menu Item
0x04Exit Menu
0x05Start “Favourite” Routine
0x06(Failure 16)
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
0x16(mutes or glitches current routine)
0x17Cold Reboot
0x18Stop Routine (Mute)
0x19Swap Channel A and B
0x1aCopy Channel A to Channel B
0x1bCopy Channel B to Channel a
0x1cDefault EE
0x1dCopy Wave
0x1e(Failure 04)
0x1f(Failure 80)
0x20Advanced Update
0x21Start Ramp
0x22(no visible effect)
0x23(LCD Command?)
0x24(LCD Character?)
0x25(Failure 00)
0x26(Failure 00)
0x27(Failure 00)

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_NUM_POWER_ON
0x01MODE_NUM_UNKNOWN
0x76MODE_NUM_WAVES / MODE_NUM_LOWER
0x77MODE_NUM_STROKE
0x78MODE_NUM_CLIMB
0x79MODE_NUM_COMBO
0x7aMODE_NUM_INTENSE
0x7bMODE_NUM_RHYTHM
0x7cMODE_NUM_AUDIO1
0x7dMODE_NUM_AUDIO2
0x7eMODE_NUM_AUDIO3
0x7fMODE_NUM_SPLIT
0x80MODE_NUM_RANDOM1
0x81MODE_NUM_RANDOM2
0x82MODE_NUM_TOGGLE
0x83MODE_NUM_ORGASM
0x84MODE_NUM_TORMENT
0x85MODE_NUM_PHASE1
0x86MODE_NUM_PHASE2
0x87MODE_NUM_PHASE3
0x88MODE_NUM_USER1
0x89MODE_NUM_USER2
0x90MODE_NUM_USER3
0x91MODE_NUM_USER4
0x92MODE_NUM_USER5
0x93MODE_NUM_USER6
0x94MODE_NUM_USER7 / MODE_NUM_UPPER

Note: To set mode

  • Write New Mode Number to $407b
  • Write 0x04 to $4070 (execute “main menu”)
  • Wait 18ms (lets box execute previous command)
  • Write 0x10 to $4070 (execute “next mode” to refresh display)
  • Wait 18ms (lets box execute previous command)

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

ValueDescription
0x01Channel A
0x02Channel B
0x03Channel A+B

$40a5 - Current Level

1 byte, Range 128-255 (Range taken from erosoutsider perl script)

$40a6 - Minimum Level

1 byte, Range 128-255 (Range taken from erosoutsider perl script)

$40a7 - Maximum Level

1 byte, Range 128-255 (Range taken from erosoutsider perl script)

$40a8 - Level Rate

1 byte, Range 0-255, 0 is fastest (Range taken from erosoutsider perl script)

$40ac - Level Options

Bits 0-4 (upper nibble): Min Options Bits 5-8 (lower nibble): Rate Options (Range taken from erosoutsider perl script)

$40ae - Frequency

1 byte, Range 8-255 (?!), 8 is fastest (Range taken from erosoutsider perl script)

$40af - Maximum Frequency

1 byte, Range 8-255 (?!), 8 is fastest (Range taken from erosoutsider perl script)

$40b0 - Minimum Frequency

1 byte, Range 8-255 (?!), 8 is fastest (Range taken from erosoutsider perl script)

$40b1 - Frequency Rate

1 byte, Range 0-255, 0 is fastest (Range taken from erosoutsider perl script)

$40b5 - Frequency Options

Bits 0-4 (upper nibble): Val Options Bits 5-8 (lower nibble): Rate Options (Range taken from erosoutsider perl script)

$40b7 - Current Pulse Width

1 byte, Range 64-196 (?!) (Range taken from erosoutsider perl script)

$40b8 - Minimum Pulse Width

1 byte, Range 64-196 (?!) (Range taken from erosoutsider perl script)

$40b9 - Maximum Pulse Width

1 byte, Range 64-196 (?!) (Range taken from erosoutsider perl script)

$40ba - Pulse Width Rate

1 byte, Range 0-255, 0 is fastest (Range taken from erosoutsider perl script)

$40be - Width Options

Bits 0-4 (upper nibble): Val Options Bits 5-8 (lower nibble): Rate Options (Range taken from erosoutsider perl script)

$41F4 - Power Levels

ValueDescription
0x00LOW
0x01NORMAL
0x02HIGH
0x03UNKNOWN