wchill/SwitchInputEmulator

License file for project.

Closed this issue · 7 comments

Hey, great project! Thank you for all the hard work! I'm looking forward to getting this running on my Arduino Mega!

I see that there's an MIT license file provided with the Arduino code, however, there are none associated with the other C# code directories. Assuming you intend to; if the license file is moved up to the root directory, then github will apply that license to the project as a whole. If not would you mind adding an appropriate licenses to the rest of the project directories? I don't want to infringe on any of your code!

Thanks again!
Cal

Hi,

I've deliberately left the code unlicensed as I don't like it and I have plans to perform significant rewrites of everything. That work is happening in some private repos currently and I plan on making the result of that available, but there's no ETA.

Is there any code you were looking to use/fork specifically?

Okay! I'm looking specifically at the COM interfacing code, SwitchInputSink under the InputServer project. My main project is already a .net application, so this section would be quite handy!

Ah, I can give you some documentation on the protocol which will be much better than reading the file. I'll get back to you in a bit.

Here's how to communicate with the Arduino (if you use some dev hardware that doesn't use an ATMega16U2, then you'll most likely need to make some changes for things like registers and the UART RX interrupt service routine).

First thing you want to do is to synchronize your client side code with the hardware. To do this, you must first send a series of synchronization bytes.

The easiest way to do this is the following:

  1. Send nine 0xFF bytes.
  2. Wait 1ms, then read back all the bytes that have been received. You can receive anywhere from 1 to 9 bytes, and the response will always end in 0xFF. Note that you may receive multiple 0xFF bytes. Alternatively, you can clear the receive buffer, then send another 0xFF byte and wait for a response back - when the Arduino is not in "synchronized" state, it will always send a 0xFF byte to signal that it is ready to begin synchronization.
  3. Send a 0x33 byte and wait for the response (0xCC).
  4. Send a 0xCC byte and wait for the response (0x33).

Now the Arduino is in synchronized state. In this state, it will send a 0x90 byte at 128Hz to signal the end of a USB HID poll frame.

You can now send input packets to the Arduino, which will buffer the packet until the next poll frame and then send it to the Switch. If the Arduino is not in synchronized state, it will send a default packet instead (nothing pressed). Otherwise, if you do not send a packet in a given poll frame, the previously buffered packet will be used, and if more than one packet is sent in a poll frame, the last one sent will be used. Interrupts are disabled when reading from the buffer, so you should not need to worry about race conditions.

The 9 byte packet structure is as follows:

00 00 - bitfield for all the buttons, stored little endian. See Joystick.h for the correct values
08    - which direction is pressed on the D-Pad. See Joystick.h for the correct values. Any value 08 or higher is interpreted as "not pressed".
80 80 - left stick X/Y, where 00 is left/up and FF is right/down. 80 is neutral. 
80 80 - right stick X/Y, where 00 is left/up and FF is right/down. 80 is neutral.
00    - vendor spec byte. Leave this as 00.
54    - CRC8 CCITT checksum of the previous 8 bytes.

If, after receiving a packet, CRC validation fails, the Arduino will emit a 0x92 byte, and the previously buffered packet will continue to be used for subsequent poll frames. If the packet verifies successfully, nothing happens (but you can make it send a 0x91 byte by uncommenting the relevant line in Joystick.c).

If you continue to get 0x92 bytes even though your CRC validation is correct, your client side program may be out of sync by one or more bytes, so you may want to repeat the synchronization process to ensure that byte alignment is correct.

To make debugging easier, the Makefile builds an emulator that uses the same core logic as the Arduino code but stubs various Atmel-only functions so that you should be able to run it in any environment that you can compile C++ code for. Theoretically it should work the same except for timing, as the emulator will run at full speed (faster than 128Hz) and no interrupts are used, but it should be helpful since you can use more sophisticated tools to debug protocol level errors. Communication with the emulator is just via stdin/stdout, so you can use socat or other serial port emulator to talk to it as if it was a serial port.

Thanks for all this info; it's been a major help so far! At this time I'm able to flash the Mega2560 Rev 3 board using the Atmel Flip program and am attempting to interface with it using the serial outputs on a raspberry pi.

I'm having some trouble reading the outgoing serial messages, which I'm investigating. Currently seeing numbers getting mangled during calls to send_byte. 0x01 ends up as 0xFD, 0x10 ends up as 0xDF etc.

If I add the following code in the ISR method:

    send_byte(0xff);
    send_byte(0x33);
    send_byte(0xCC);

My reader script will read 0xff 0xbb 0xef

ser = serial.Serial(
    port='/dev/ttyAMA0',
    baudrate = 19200,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS,
    timeout=5)

while True:
    x = ser.read(size=1)
    if len(x) > 0:
        print(hex(ord(x)))

Going to keep digging, I think its either a register mismatch or potentially something to be changed in the init code.

My bad! I didn't have a voltage divider in between the raspberry pi and the arduino! Everything works now!! This is pretty awesome! Thanks a lot

Closing this issue! I can now interface with the Arduino using python. Thanks again.