So, I wrote a bit of code to control brushed DC motors from a Logitech USB Gamepad -> Custom VC++ Software -> Custom Arduino Software -> Motor Driver -> Brushed DC motor. This is mostly just temporary code to test things. Ultimately there will be three modes of operation: remote via RC transmitter, remote via PC, and autonomous. Depending on how I decide to control this thing, the RC transmitter/receiver will be pretty much plug-n-play. Autonomous control will come much later. This is something I’ve done before and know how much work goes into it. Remote via PC is fairly straight-forward, as I mentioned.
Here’s my code, including the Arduino sketch and the code for VC++:
DCMotor
The DCMotor class talks with the Arduino via the USB using RS232 communications. I had to make my own protocol for passing commands to the Arduino to control the motors. Right now each message is composed of the following:
<STX> <Forward> <Speed[7-0]> <Speed[15-8]><Speed[23-16]><Speed[31-24]> <ETX>
Each are bytes, where:
STX = start of text = 0x02
Forward = 1-forward, 0-back
Speed[#-#] = individual bytes of the unsigned int speed
ETX = end of text = 0x03
Inside the DCMotor zip are the DCMotorTestDlg .cpp/.h files I’ve used. They can’t be directly built because there are several other files needed. Really all you need to know are how to use the DCMotor class and how to talk to gamepads. In the OnInitDialog() method of DCMotorTestDlg I make the following three calls:
m_DCMotor.Create(“DC Motor”, WS_CHILD|SS_NOTIFY, CRect(0,0,0,0), this, 6666);
m_DCMotor.Open(“COM13”);
SetTimer(m_pTimer, 20, NULL);
Create() is used for making a window. The window is used for sending messages internally and externally. Internal messages go to/from the internal thread which does the communications to the Arduino. External messages aren’t really used for much right now, but I’ll eventually rewrite all of this to be a Sawbot controller which will give status updates. Open() does exactly what you think. For now, I’m running at 9600, 8, n, 1. SetTimer() is used for polling the gamepad for button presses. I won’t get into the details of why I do polling instead of interrupts, except to say that polling allows for more use of the gamepad. Feel free to let me know I’m wrong.
As you will see in OnTimer() method of DCMotorTestDlg, I issue move commands to the DCMotor class based on the analog X position of the gamepad. Sorry about the massive lack of commenting. I wrote it fast, but what isn’t obvious can be Google’d. Just in case, here’s a quick explanation of what I’m doing.
if (bJoyPresent){
if ((joyInfoEx.dwXpos < 28672) || (joyInfoEx.dwXpos > 36864)){
if (joyInfoEx.dwXpos > 32767)
m_DCMotor.IssueCommand(true, (joyInfoEx.dwXpos – 32768)/128);
else
m_DCMotor.IssueCommand(false, (32767 – joyInfoEx.dwXpos)/128);
}
else {
m_nLastXpos = 32768;
m_DCMotor.IssueCommand(false, 0);
}
}
First, I’ve retrieved the gamepad/joystick info (refer to lines 104-109 of DCMotorTestDlg.cpp, not shown here). If a gamepad is connected, then I check to see if I should command the motor to move. I’ve put a dead-zone of 8192 (between 28672 and 36864). This is to prevent the motor from always wanting to turn unless the value is exactly 32767. Then I noticed a mistake.
The Arduino is an 8-bit microcontroller, and the pseudo-analog (read: PWM) output I use to control the DC motor is 8-bit. That’s a range of 0-255. Much less than 0-32767. So I divide by 128 to get in the appropriate range. Not a big deal though as I doubt I will ever need to do anything more refined.
If you don’t quite follow my mental defection logic, feel free to leave a comment and I’ll explain. Also, this code is very very basic and unfinished. The Arduino code is easy to follow and uses the same protocol I stated above. I’ll post up a demo when I get a chance.