buttplugio / stpihkal

Repo deprecated, STPIHKAL moved to docs.buttplug.io repo
https://docs.buttplug.io/
93 stars 21 forks source link

Document Shockspot Protocol #85

Open blackspherefollower opened 4 years ago

blackspherefollower commented 4 years ago

Right now, this is copy/paste from Discord:

KirbyCat12/31/2018 Alright, after fiddling around with it a bit I got the recompiled shockspot software to connect to the device. It looks like it really just sends its commands to the device in plain text (Stuff like "01050407FF00F0"). Also lots of sleeps and bad error handling, I'm wonder how much the device is crippled by its software.

This stuff is just awful: https://pastebin.com/kGwQ0qw8

KirbyCat12/31/2018 Yep, it sure does. Here is how it sets up the Serial Port in code.

Baud Rate: 19200 Parity: None Databits: 8 Handshake: None Stop Bits: 1 Recieved Bytes Threadhold: 1

KirbyCat12/31/2018 So it looks like the Shockspot makes use of this to authenticate the device: https://www.ftdichip.com/Support/SoftwareExamples/FTDIChip-ID.htm FTDIChip-ID Projects USB RS232 - FTDI designs and supplies USB semiconductor devices with Legacy support including royalty-free drivers. Application areas include USB RS232, ( USB Serial ), USB Parallel, USB Docking Stations, and upgrades of Legacy designs to USB. But it checks it just by having a giant block of every single chip ID https://pastebin.com/JpyCKf1Y

I guess that could prevent the laziest of clones (If all devices had the same ID, remove the ID from the whitelist in later software releases), but still seems pretty flimsy.

KirbyCat12/31/2018 It looks like it is sending commands in a protocol called Modbus, in ASCII mode. Do you know anything about that @qdot? The software is building the commands up in a stupid way though (Its the equivalent of software that builds up XML with string concatenation)

qdot12/31/2018 First I've heard of it. "Modbus is a serial communications protocol originally published by Modicon (now Schneider Electric) in 1979 for use with its programmable logic controllers (PLCs)"

KirbyCat12/31/2018 Ok, lets see if this protocol comes with anything that can just tell me the device name so we can get a manual.

KirbyCat12/31/2018 Ok, I think I figured out how to control the device, I can now send commands directly to it though a serial port tool QModMaster. But I really think this thing can do a lot more than the guy set it up for. Hell I can already make it do stuff like change speed mid thrust that his software can't. thatdude12/31/2018 Modbus? I guess a Modbus linear actuator driver wouldn't be too uncommon.

KirbyCat12/31/2018 So, here is basically a write up of everything I have found.

There are two "types" of Shockspot. Mine seems to be Type 2, but the code seems to also support an older version as Type 1.

The Shockspot Type 2 appears to use the Modbus ASCII protocall to control the device, and makes use of slave ID 1. The serial port settings can be seen above.

The device has 128 single bit "Coils", Starting at 0x0400. The only ones that appear to get used are 0x0403, 0x0407, 0x040B, 0x0427, and 0x042C. These only get set by the software, and never read from.

The device seems to have 128KB of Addressable Memory, in 16 bit registers. The only parts ever read directly by the control program are the 10 registers starting at 0x9000. The only parts ever written to are the 2 registers starting at 0x9900 (Really only the one at 0x9901 due to the value range), and 3 registers starting at 0x9904

Registers set by program: 0x9900 - 0x9901: Current distance arm is to be extended to. The unit is in thousandths of a centimeter. For the "12 inch" device, the max thrust depth is 30cm, so a value 30000 (or 0x7530) could be written to those registers. The device seems to actually allow for 0.001 cm steps. 0x9904 - 0x9905: Velocity. Unknown unit, value can range from 0 to 60000 in code (When setting the slider to 100, 60000 is the value sent). 1800 is considered "low velocity". 0x9906: "Smoothness". Value can range from 0 to 60, 60 seems to be no smoothness.

So I'll have to look more into it, but the software seems to be really just setting 3 values. The device has two ways configure speed, and then just sets a new position for the arm when it gets to the current destination.

thatdude12/31/2018 AFAIK there is a way to query device information over Modbus if you wanted to get details on the controller. Based on what I hear actually work, I've not used Modbus myself yet.

KirbyCat12/31/2018 I can't say for sure, but I think this manual goes to the motor's controller, on the page 40 of the PDF (The one labeled 31) the register numbers seem to line up with what I observed: https://www.iai-robot.co.jp/data_dl/cad_manual/manual/PDF/CONTROLLER/OPTION_OTHERS/ENGLISH/MODBUS(ME0162-9A).pdf

KirbyCat12/31/2018 Which would mean that it most likely uses the RCP6-RA6C, though that also has a ton of minor variations: https://www.intelligentactuator.com/rcp6-robo-cylinder-series/ https://www.intelligentactuator.com/partsearch2/RCP6(S)-RA6C_p47-48.pdf IAI America RCP6 Next-generation ROBO Cylinder® - IAI America RCP6 IAI America's next-generation ROBO Cylinder® shows advantages of product equipped with Battery-less Absolute Encoders.

RexOrient01/01/2019 https://www.intelligentactuator.com/pdf/RCP6_CE0238-1A_A4s_080916.pdf RCP6S-RRA4C-WA-56P-6-300-... Looks like a likely candidate Page 11 (12 in PDF) Looking at the Shockspot website again, I'd say make that 60P. About 2 cm for the vaculock adapter, 4cm for the cylinder, 6cm for the motor

KirbyCat01/02/2019 Yep. And it looks like they used one of IAI's controllers instead of their own, so the device should be able to do more stuff then the provided software is allowing. "Each position in the point table can have it's own velocity, acceleration, and deceleration"

rezreal commented 3 years ago

So here is a first condensed "api" in c#: These are more or less all commands used by the shockspot software (for device revision 2).

public static string Calculate_LRC(string command)
        {
            short num = 0;
            checked
            {
                int num2 = (int)Math.Round((double)Strings.Len(command) / 2.0);
                for (int i = 1; i <= num2; i++)
                {
                    string value = Strings.Mid(command, i * 2 - 1, 2);
                    num = (short)(Convert.ToInt32(value, 16) + num);
                }
                num = (short)(-num);
                return ((short)(num & 0xFF)).ToString("X2");
            }
        }

        /**
         * Positioning Data Direct Writing (Queries Using Code 10))
         * VCMD Speed specification register (2 byte in 0.01 mm/sec) Writing 3 registers, 2 bytes
         * ACMD Acceleration/deceleration specification register (1 byte in 0.01 G)
         */
        public static string velocityAndAccelerationCommand(int velocity, int acceleration)
        {
            string cmd = "01109904000306" + velocity.ToString("X8") + acceleration.ToString("X4");
            return ":" + cmd + Calculate_LRC(cmd);
        }

        /**
         * DSS1 Controller Status Signal Reading 1 (reading two registers DSS1 and DSS2 ?)
         */
        public static string queryDeviceStatusCommand()
        {
            string cmd = "010390050002"; 
            return ":" + cmd + Calculate_LRC(cmd);
        }

        /**
         * Register reading DIPM Input port query  Input port monitor register (9003), 2 registers,
         * Used to determine if the hand switch is pressed.
         */
        public static string queryInputSignalStatus()
        {
            string cmd = "010390030002";
            return ":" + cmd + Calculate_LRC(cmd);
        }

        /**
         * Direct writing of positioning Data Target position coordinate specification register PCMD 9900 Register size 2 Byte size 4 chars (Unit 0.01 mm)
         */
        public static string positionCommand(int targetPosition)
        {
            string cmd = "011099000002040000" + targetPosition.ToString("X4");
            return ":" + cmd + Calculate_LRC(cmd);
        }

        /**
         * Expansion device control register (DRGE)  Deceleration stop STOP
         * The actuator will start decelerating to a stop when the deceleration stop command edge (write FF00H) is turned on
         */
        public const string decelerationStopCommand = ":0105042CFF00CB";

There are some more calls, somewhat a opening ceremony when the device connects:

            Serial.send(":01050407FF00F0"); // ALRS Alarm reset command 
            Sleep(10); // silent interval (>3ms for 9600 baud)
            Serial.send(":010504070000EF"); // ALRS Alarm reset command  (2)
            Sleep(10); // silent interval (>3ms for 9600 baud)
            Serial.send(":01050427FF00D0"); // PMSL PIO/Modbus Switching Setting (Enable Modus commands)
            Sleep(10); // silent interval (>3ms for 9600 baud)
            Serial.send(":01050403FF00F4"); // SON Servo ON/OFF  Servo ON (FF00)
            Sleep(10);
            Serial.send(":0105040B0000EB"); // HOME Home Return Start (0000)
            Sleep(10);
            Serial.send(":0105040BFF00EC"); // HOME Home Return End (FF00)
            Sleep(2000); // Give homing some time
            Serial.send(queryDeviceStatusCommand());
            // now poll for the device status until the homing is completed, then the device is ready

I'll investigate further when my device arrives :)

rezreal commented 3 years ago

Done. Demo: https://rezreal.github.io/knock-lot/ (web-serial, only works with crom'ish browsers) Protocol: https://github.com/rezreal/knock-lot/blob/master/src/knockRodProtocol.ts

ModbusASCII works, working on ModbusRTU now. Upcoming experiments by priority (for me):

@qdot Happy to help building buttplug support, but to be honest I really got lost in the rust repo.

rezreal commented 3 years ago

I've changed the linked protocol implementation to ModbusRTU already (less overhead). The Modbus Manual link in the previous comments pretty much describes everything you can to with the shockspot .

Important: modbus seems to use a propriatery crc16 code, at least it is called "crc16modbus" in the chosen crc library.

The destilled control loop would be:

Bonus: The status registers can be polled for current speed/position, travelled distances and much more. I have yet to check how the push command works and if can be used to seek/measure the distance between device and "working piece".

The jsdoc statements are partially copied from the modbus manual and might be suspect to copyright.