figuring out maimai
disclaimer: this plan did not work out in the end because the ITO sheets were not good enough
plan
-
use CircuitPython to control Adafruit MPR121 breakout capacitive touch boards
-
CircuitPython chosen for the programming because it can be used on raspis (i already have a raspi) but it can easily be done on an atmega arduino board with its own arduino code ide. although the code i will write is based on python in CircuitPython
-
cut the ito plastic sheet into sensor shapes
implementation: circuitpython with digitalio, usb serial, and then also adafruit mpr121 controller
adafruit
adafruit ships their own wrapper in front of their bus device driver for the MPR121, but instead a new one should be written since its shit and doesnt have all the register writing stuff. it does have hidden methods for write_register_byte
and read_register_byte
but its better to reference the i2c_device
module for better editing
from adafruit_bus_device
library theres the i2c_device
module where you can read and write to registers on an i2c device. each register can be defined under a function const()
from the micropython library which converts a hex to whatever type the i2c_device
methods can read
maybe the data packets can be sent with a python bytearray()
copy the default settings for the registers from the chinese mai2touch they use the exact same names for the registers
parts list
- Adafruit MPR121 breakout cap sense boards x3
- ITO plastic sheet - cut up into sensor shapes (get from aliexpress or adafruit sells them but more expensive and smaller)
- miscellaneous cables (qt stemma / piicodev cables and adaptor used so i dont have to bother soldering)
- Raspi - (already have)
- Arduino (alternative to raspi)
- big enough acrylic / plexiglass sheet to cover the screen and place the ito sheets on
reverse engineering packets from the game
data packet format
the following data packets are represented by ASCII, and the values are in hexadecimal.
the data packet sent by the host starts with {
and ends with }
the data packet replied by the device starts with (
and ends with )
>>>
is the data packet sent by the host, <<<
is the data packet returned by the controller
it seems the data sent can be either length 6 for commands or length 9 for touch data
example:
{from host machine} 7b 4c 42 72 32 7d
ASCII value: {LBr2}
(towards host machine) 28 4c 42 72 32 29
ASCII Value: (LBr2)
commands and controller initialisation
;
Reset command (RSET):
>>> 7b 52 53 45 54 7d
// ASCII: {RSET}
sent from host machine / game, no reply needed. will trigger a reset command to the MPR121:
void
void
assumedly this command is to initialize the touchscreen on startup
Conditioning mode command (HALT)
>>> 7b 48 41 4c 54 7d
// ASCII:{HALT}
sent from host machine / game, no reply needed. will trigger stop command to the MPR121, and then trigger configuration for the touch sensors (will write sensitivity settings etc)
controller will stay in this mode until STAT command is triggered, and in this mode changing sensor settings is possible (explained more below)
void
void
void
Touch panel ratio setting
>>>
>>> 7b 4c 41 72 32 7d
// ASCII:{LAr2}
sent from host machine / game.
L
: stands for P1 or P2, P1 = L and P2 = R
sensor
: Sensor being currently set from A1 to E8, refer to the sensor table for ascii representation
r
: value fixed to hex ascii r, not sure what this means from the original chinese doc
ratio
: ratio value of each sensor (refer to the sensor sensitivity value table), not exactly sure what this value means with regards to a real controller
with regards to the actual MPR121 itself, the ratio sets the touch sensitivity:
void
void
MprSetTouch()
sends the new touch sens values to be set to the MPR121, which it gets from the packet that it receives from the controller (referring to the arguments packet[2]
and packet[4]
) which is presumably inputted from the sensor menu in the service menu from the game (?)
will reply the same as has just been set in a smooth bracket data packet
Touch panel sensitivity setting
>>>
>>> 7b 4c 41 6b 1e 7d
/// ASCII: {LAk.}
Sent from host machine / game. Similar to ratio setting from above
L
: stands for P1 or P2, P1 = L and P2 = R
sensor
: Sensor being currently set from A1 to E8, refer to the sensor table for ascii representation
k
: value fixed to hex ascii k, not sure what this means from the original chinese doc
sens
: sensitivity value of each sensor (refer to the sensor sensitivity value table), not exactly sure what this value means with regards to a real controller
void
void
exactly the same as touch panel ratio setting
end conditioning mode
>>> 7b 53 54 41 54 7d
// ASCII: {STAT}
exit configuration mode. no reply required, can start sending touch data
touch data
touch data sent by the controller to the host, will only start sending once the controller receives a STAT command. will stop sending if the controller receives HALT
>>> 28 1f 1f 1f 1f 1f 1f 1f 29
// ASCII: (.......)
the touch data lives in the middle 7 bytes of this data packet where each bit in the byte (each byte value uses binary low 5 bit storage) represents the sensor you touched, and the bit value will flip to 1 example for touching sensor A1:
<<< 28 00 00 00 00 00 00 01 29
// Binary data: 00000000 00000000 00000000 00000000 00000000 00000000 00000001
sensor table s
(letters do not refer to keys, only translated to ascii for readability between host and device) A1=0x41 ~ A8=0x48 (a b c d e f g h) B1=0x49 ~ B8=0x50 (i j k l m n o p) C1=0x51 C2=0x52 (q r) D1=0x53 ~ D8=0x5A (s t u v w x y z) E1=0x5B ~ E8=0x62 (lwin rwin menu reserved(?) sleep num0 num1 num2)
sensor sensitivity value table
sens of A1-A8 touch points value, sens value of B1-E8 -5 = 32,5a,46 -4 = 32,50,3c -3 = 32,46,32 -2 = 32,3c,28 -1 = 32,32,1e 0 = 32,28,14 +1 = 32,1e,0f +2 = 32,1a,0a +3 = 32,17,05 +4 = 32,14,01 +5 = 32,0a,01 original chinese doc says that both ratio and sens values (under MPR121 terms ratio = touch threshold and sens = release threshold) use this same table
DTR setting for C# programs
Unfortunately Maimai does not set the DTR to true for serial communication, so it does not work out of the box with some certain microcontroller serial device implementations such as circuitpython. I have shipped a MelonLoader mod for the game to enable the DTR flag and this makes sure that it will work with any serial device implementation on any microcontroller. Make sure to install MelonLoader on the game executable and then drop the mod .dll into the Mods folder.