Packets are how the user's computer communicates with the NullSpace hardware. They consist of a series of bytes, that dictate what the hardware should be doing and request information in return. Packets are sorted by their Instruction Byte, which determines what the packet actually does, and have a variable number of additional bytes for alignment, data integrity, and command parameters.
Sent packets can have variable length. Every packet will look something like this (bytes are separated by the | character):
| 0x24 | 0x02 | INST | LENGTH | PARAM1 | …. PARAMX | 0xFF | 0xFF | {0x0A} |
The structure is as follows:
Header
| 0x24 | 0x02 |
These two bytes indicate the packet is starting. Their values are arbitrary, and are based off the packet structure used by Invensense's teapot demo.
Signifiers
| INST | LENGTH |
The instruction byte determines what the packet is supposed to do, whether that's to write a command to a motor, request a status byte from the arduino, or request a stream of tracking data. You can find a complete summary of the different possible instructions in this file. Not every instruction has the same number of bytes, so the length byte exists to tell the arduino how many byte's it's supposted to read. Both of these signifier bytes will exist in every packet.
Parameters
| PARAM1 | PARAM2 | …. PARAMX |
Parameter bytes contain the information of an instruction. This could be pad numbers, effect numbers, register values, etc. The number of parameter bytes varies by instruction. See each individual instruction to find out what the parameter values are.
Footer
| 0xFF | 0xFF | 0x0A |
The footer concludes the packet. These three bytes conclude every possible packet and should always be included in this exact format.
Unlike sent packets, return packets are always 16 bytes in length. Typically most of those bytes are 0. Every packet will look something like this (bytes are separated by the | character):
| 0x24 | 0x02 | RETURN_TYPE | PARAM1 | ...PARAM_11 | 0x00 | 0x00 | 0x0d (\r) | 0x0a (\n) |
or
{ '$', 0x02, 0x33, 0,0, 0,0, 0,0, 0,0, 0, 0x00, 0x00, '\r', '\n' }
"Set" Instructions do not generate a return packet, they are "fire and forget", but may keep the suit busy for a period of time. "Get" Instructions always generate a return packet. Whenever possible, they should return a memory variable, generated by a "set" command. (i.e. "set init" instruction, then a "get status" instruction). Currently the implementation is a single thread of execution, and the suit will block until a command is complete. (Characters will buffer in the background interrupt context, but eventually, too many characters will overflow the buffers. Input buffer sizes may need to be increased for higher baud rates.
Reserved for use by Suit Control for all non-module activity.
| Value: | Name | Purpose: | Parameters: | | 0x00 | | | | | 0x01 | DEV_GET_VERSION | Ask for the version of the suit | void | | 0x02 | DEV_STATUS_PING | Ping the arduino for a status packet | void | | 0x05 | DEV_LED_CONTROL | Control the RGB inputs of the LED | R1,R2,G1,G2,B1,B2 |
Reserved for use by the haptic driver network
| Value: | Name | Purpose: | Parameters | | 0x10 | | | | | 0x11 | DRV_RESET_DRIVERS | Resets every driver on the body | void |
| 0x12 | DRV_INIT_MOTOR_DRIVERS | Initialize every driver on the body | void
| 0x13 | DRV_PLAY_EFFECT | Write an effect to a single driver and go | pad, effect
| 0x14 | DRV_WRITE_DATA | Write to a register on a single driver | pad, register, data
| 0x15 | DRV_READ_DATA | Read from a register on a single driver | pad, register
| 0x16 | DRV_LOAD_CONTINUOUS | Load an effect without playing it | pad, effect
| 0x17 | DRV_HALT_SINGLE | Stop a single motor by deselecting Go register | pad
| 0x18 | DRV_STREAM_CONTINUOUS | Streams continuous play data. Does not change go registers by itself, depends on the contplay routine to activate. | [4->10]contplay state
| 0x19 | DRV_PLAY_CONTINUOUS | Play a motor continuously | pad, effect
| 0x1A | DRV_AUDIOMODE_ENABLE | Turns on audio mode for one motor | [3]pad, [4]audiomax d0x22, [5]audiomin d0x04, [6]peaktime d0x01, [7]filter d0x01
| 0x1B | DRV_INTRIGMODE_ENABLE | Switches to intrig mode (default) | pad
| 0x1C | DRV_RTPMODE_ENABLE | Sets mode to Realtime | pad
| 0x1D | DRV_PLAY_RTP | Load a volume from 0 to 255 using RTP | pad, volume/td>
| 0x1E | DRV_SET_WAVEFORM | Add a waveform to the current queue | pad, waveform
| 0x1F | DRV_GO | Play the current queue | pad
| 0x20 | DRV_CHECK_INIT | Check the initialization status of a single DRV | pad
| 0x21 | DRV_CHECK_INIT_ALL | Check the initialization status of every DRV | void
Reserved for use by the inertial tracking network
| 0x32 | INIT_TRACKING | Re-initialize the tracking network | void
| 0x33 | MPU_GET_DATA | gets data from one MPU | sensor_id
| 0x34 | ENABLE_TRACKING | Turn on tracking | void
| 0x35 | DISABLE_TRACKING | turn off tracking | void
| 0x36 | MPU_GET_GRAVITY | Get the gravity vector, order xyz | void
| 0x37 | MPU_GET_MAG | get the magnetometer vector, order xyz | void
| Return Type | Name | Purpose: | Parameters: | | 0x01 | Version | Return the suit version, directly programmed | [3]mark#, [4]revision# | | 0x02 | Ping | Return an empty byte to test connection | void | | 0x03 | InitMessage | | void | | 0x15 | DRV Data | Return a byte from the DRV (usuallys status) | [3]byte,[4]drv#,[5]reg# | | 0x33 | teapotPacket | Return 8 bytes of tracking data | [3,4]w [5,6]x, [7,8]y, [9,10]z, [11]imu#, [12]count# |
| 0x24 | 0x02 | 0x33 | IMU_NUMBER | Q1_1 | Q1_2 | Q2_1 | Q2_2 | Q3_1 | Q3_2 | Q4_1 | Q4_2 | Q2_2 | 0x00 | 0x00 |
Quaternion are two byte undivided integers for efficiency. To get their real values, perform the following operation: QuaternionN = ((QN_1 << 8) | QN_2) / 16384.0f
Note Quaternion data is ordered WXYZ
- bytes 0 and 1 are the header
- bytes 2 is the instruction
- bytes 3-10 contain the data
- byte 11 contains the IMU number
- byte 12 is a counter that increments with every packet sent
- byte 13 contains the system calibration # from 0-3 (3 being most calibrated)
DRV Read data will attemp to read a single byte of data from anywhere in the DRV's register map, as specified in the instruction. Here are some common locations to read from:
- 0x00 Status Register: used for detecting issues. We only really care about bits 0 and 1, which are flags for temperature and current overloads (which shut down the chip and require a reset), as shown below.
| Bit 7-5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0
| Device ID | Reserved | Diagnostic result | Reserved | OVER TEMPERATURE | OVER CURRENT
- 0x01 Control Register: used for checking standby and reading the MODE. Chips start in Standby, so if you see that bit asserted, the chip hasn't been initialized. Read MODE to see the different modes.
| Bit 7 | Bit 6 | Bit 5-3 | Bit 2-0
| Dev Reset | STANDBY | Reserved | MODE
- 0x02 RTP INPUT: check this to see what volume the RTP mode is set to play at.
- 0x04 WAV_FRM_SEQ1: bits 0-6 of this register should tell you what effect you're currently set to play when using INTRIG mode. Ignore the 7th bit.