/* MaestroSerialExampleCWindows:
* Example program
for sending
and receiving bytes from the Maestro over a serial port
*
* This program reads the position of channel 0.
* If the position
is less than
6000, it sets the target
to 7000.
* If the position
is 6000 or more
, it sets the target
to 5000.
*
* If channel
0 is configured
as a servo channel
, the servo should move
* when you
run this program
(except perhaps the first time you
run it
).
* when you
run this program.
*
* All the Windows functions called by the program are documented
on MSDN:
* http:
//msdn.microsoft.
com/ *
* The
error codes that this program may
output are documented
on MSDN:
* http:
//msdn.microsoft.
com/en
-us
/library/ms681381%28v
=vs.85%
29.aspx
*
* The Maestro's serial commands are documented in the "Serial Interface"
* section of the Maestro user's guide:
* http:
//www.pololu.
com/docs
/0J40
*
* For an advanced guide
to serial port communication in Windows
, see:
* http:
//msdn.microsoft.
com/en
-us
/library/ms810467
*
* REQUIREMENT: The Maestro's Serial Mode must be set to "USB Dual Port"
* or "USB Chained" for this program
to work.
*/
#include <stdio.h>
#include <windows.h>
/** Opens a handle
to a serial port in Windows
using CreateFile.
* portName: The
name of the port.
* baudRate: The baud rate in bits per second.
* Returns INVALID_HANDLE_VALUE
if it fails. Otherwise returns a handle
to the port.
* Examples: "COM4", "\\\\.\\USBSER000", "USB#VID_1FFB&PID_0089&MI_04#6&3ad40bf600004# */
HANDLE openPort(const char * portName, unsigned int baudRate)
{
HANDLE port;
DCB commState;
BOOL success;
COMMTIMEOUTS timeouts;
/* Open the serial port. */
port = CreateFileA(portName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (port == INVALID_HANDLE_VALUE)
{
switch(GetLastError())
{
case ERROR_ACCESS_DENIED:
fprintf(stderr, "Error:
Access denied. Try closing all other programs that are
using the device.\n
"); break;
case ERROR_FILE_NOT_FOUND:
fprintf(stderr, "Error: Serial port
not found.
" "Make sure that \
"%s\" is the right port
name.
" "Try closing all programs
using the device
and unplugging the
" "device
, or try rebooting.\n
", portName); break;
default:
fprintf(stderr, "Error: Unable
to open serial port.
Error code 0x%x.\n
", GetLastError()); break;
}
return INVALID_HANDLE_VALUE;
}
/* Set the timeouts. */
success = GetCommTimeouts(port, &timeouts);
if (!success)
{
fprintf(stderr, "Error: Unable
to get comm timeouts.
Error code 0x%x.\n
", GetLastError()); CloseHandle(port);
return INVALID_HANDLE_VALUE;
}
timeouts.ReadIntervalTimeout = 1000;
timeouts.ReadTotalTimeoutConstant = 1000;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 1000;
timeouts.WriteTotalTimeoutMultiplier = 0;
success = SetCommTimeouts(port, &timeouts);
if (!success)
{
fprintf(stderr, "Error: Unable
to set comm timeouts.
Error code 0x%x.\n
", GetLastError()); CloseHandle(port);
return INVALID_HANDLE_VALUE;
}
/* Set the baud rate. */
success = GetCommState(port, &commState);
if (!success)
{
fprintf(stderr, "Error: Unable
to get comm state.
Error code 0x%x.\n
", GetLastError()); CloseHandle(port);
return INVALID_HANDLE_VALUE;
}
commState.BaudRate = baudRate;
success = SetCommState(port, &commState);
if (!success)
{
fprintf(stderr, "Error: Unable
to set comm state.
Error code 0x%x.\n
", GetLastError()); CloseHandle(port);
return INVALID_HANDLE_VALUE;
}
/* Flush out any bytes received from the device earlier. */
success = FlushFileBuffers(port);
if (!success)
{
fprintf(stderr, "Error: Unable
to flush port buffers.
Error code 0x%x.\n
", GetLastError()); CloseHandle(port);
return INVALID_HANDLE_VALUE;
}
return port;
}
/** Implements the Maestro's Get Position serial command.
* channel: Channel number from 0 to 23
* position: A pointer to the returned position value (for a servo channel, the units are quarter-milliseconds)
* Returns 1 on success, 0 on failure.
* For more information on this command, see the "Serial Servo Commands"
* section of the Maestro User's Guide: http://www.pololu.com/docs/0J40 */
BOOL maestroGetPosition(HANDLE port, unsigned char channel, unsigned short * position)
{
unsigned char command[2];
unsigned char response[2];
BOOL success;
DWORD bytesTransferred;
// Compose the command.
command[0] = 0x90;
command[1] = channel;
// Send the command to the device.
success = WriteFile(port, command, sizeof(command), &bytesTransferred, NULL);
if (!success)
{
fprintf(stderr, "Error: Unable
to write Get Position command
to serial port.
Error code 0x%x.
", GetLastError()); return 0;
}
if (sizeof(command) != bytesTransferred)
{
fprintf(stderr, "Error: Expected
to write %d bytes but
only wrote %d.
", sizeof(command), bytesTransferred); return 0;
}
// Read the response from the device.
success = ReadFile(port, response, sizeof(response), &bytesTransferred, NULL);
if (!success)
{
fprintf(stderr, "Error: Unable
to read Get Position response from serial port.
Error code 0x%x.
", GetLastError()); return 0;
}
if (sizeof(response) != bytesTransferred)
{
"Make sure the Maestro's serial mode is USB Dual Port or USB Chained.", sizeof(command), bytesTransferred);
}
// Convert the bytes received in
to a position.
*position = response[0] + 256*response[1];
}
/** Implements the Maestro's Set Target serial command.
* channel: Channel number from
0 to 23 * target: The target value
(for a servo channel
, the units are quarter
-milliseconds
) * Returns
1 on success
, 0 on failure.
* Fore more information
on this command
, see the
"Serial Servo Commands" * section of the Maestro User's Guide: http://www.pololu.com/docs/0J40 */
BOOL maestroSetTarget(HANDLE port, unsigned char channel, unsigned short target)
{
unsigned char command[4];
DWORD bytesTransferred;
BOOL success;
// Compose the command.
command[0] = 0x84;
command[1] = channel;
command[2] = target & 0x7F;
command[3] = (target >> 7) & 0x7F;
// Send the command
to the device.
success = WriteFile(port, command, sizeof(command), &bytesTransferred, NULL);
{
fprintf(stderr, "Error: Unable to write Set Target command to serial port. Error code 0x%x.", GetLastError());
}
if (sizeof
(command
) !
= bytesTransferred
) {
fprintf(stderr, "Error: Expected to write %d bytes but only wrote %d.", sizeof(command), bytesTransferred);
}
}
int main
(int argc
, char
* argv
[]) {
HANDLE port;
char * portName;
BOOL success;
unsigned short target, position;
/* portName should be the
name of the Maestro
's Command Port (e.g. "COM4") * as shown in your computer
's Device Manager. * Alternatively you can use \\.\USBSER000
to specify the first virtual
COM * port that uses the usbser.sys driver. This will usually be the Maestro's
* command port. */
portName
= "\\\\.\\USBSER000";
// Each
double slash in this source code represents one slash in the actual
name.
/* Choose the baud rate (bits per second).
* If the Maestro
's serial mode is USB Dual Port, this number does not matter. */ baudRate = 9600;
/* Open the Maestro
's serial port. */ port = openPort(portName, baudRate);
if (port
== INVALID_HANDLE_VALUE
){ return -1;
}
/* Get the current position of channel
0.
*/ success = maestroGetPosition(port, 0, &position);
printf("Current position is %d.\n", position);
/* Choose a new target based
on the current position.
*/ target = (position < 6000) ? 7000 : 5000;
printf("Setting target to %d (%d us).\n", target, target/4);
/* Set the target of channel 0. */
success = maestroSetTarget(port, 0, target);
/* Close the serial port so other programs can use it.
* Alternatively
, you can just terminate the process
(return from main
).
*/ CloseHandle(port);
}