1. Converting Nunchuk data into RC signals
We grabbed MultiWii v2.2 from here (the latest release as of this writing), and examined it to determine what kind of RC input it expects. MultiWii expects RC serial messages to contain 8 16-bit integers containing values between 1000 and 2000. PWM signals for servos and ESCs typically vary between 1000µs and 2000µs. Presumably, the RC values are simply the pulse-widths which would be read from the PWM input if the quadcopter was being controlled by a traditional RC radio. The 8 signals are: roll, pitch, yaw, throttle, aux1, aux2, aux3, and aux4.In our case, the Arduino simply maps the 4 Nunchuk axis onto roll, pitch, yaw, and throttle. We used the normal axis layout for RC helicopters, known as Mode II.
The single complication in this part of the code is the throttle axis. In RC radios, the throttle is at rest at the down position, whereas the other three axis are at rest at the center position. Furthermore, the throttle in RC radios is not sprung, so it does not return to its rest position on its own. We would need to modify out Nunchuk to make the throttle operate in this way. We can compromise however, on the zero-point. We mapped the throttle such that only the top half of the stick is the active range, as shown:
This makes the throttle behave more like an RC radio controller.
As an optional safety precaution, the throttle is only active when both Nunchuks' Z buttons are held down. We chose to do this because it is so easy to accidentally bump the little Nunchuk joysticks.
2. Sending RC signals to MultiWii
Unfortunately, the MultiWii serial protocol (MSP) is described in this form post is vague. We decided to find some existing code which implements the MSP and examine it as a reference. The best existing code which does this is the MultiWii configuration program, which comes with MultiWii. It is written in Processing. Based on the Processing code and the MultiWii source code itself, we determined that MSP data frames are structured as follows:The configuration program assembled these messages using the following function:
//send msp with payload private List<byte> requestMSP (int msp, Character[] payload) { if(msp < 0) { return null; } List<byte> bf = new LinkedList<byte>(); for (byte c : MSP_HEADER.getBytes()) { bf.add( c ); } byte checksum=0; byte pl_size = (byte)((payload != null ? int(payload.length) : 0)&0xFF); bf.add(pl_size); checksum ^= (pl_size&0xFF); bf.add((byte)(msp & 0xFF)); checksum ^= (msp&0xFF); if (payload != null) { for (char c :payload){ bf.add((byte)(c&0xFF)); checksum ^= (c&0xFF); } } bf.add(checksum); return (bf); }
We re-implemented this function in Arduino like so:
// Send a message using the MultiWii serial protocol. void send_msp(uint8_t opcode, uint8_t * data, uint8_t n_bytes) { uint8_t checksum = 0; // Send the MSP header and message length Serial.write((byte *)"$M<", 3); Serial.write(n_bytes); checksum ^= n_bytes; // Send the op-code Serial.write(opcode); checksum ^= opcode; // Send the data bytes for(int i = 0; i < n_bytes; i++) { Serial.write(data[i]); checksum ^= data[i]; } // Send the checksum Serial.write(checksum); }
The MSP message which sends RC data is called MSP_SET_RAW_RC and has message type 200. It is composed of 16 bytes which represent 8 16-bit integers. We trivially translated our control signals from Step 1 into an array of bytes, and send it using the function above. For now, we're sending zero for aux1-aux4.
When our control board arrives we will test and debug our code, and post a complete Arduino sketch.