Maestro C Serial communication

Hello,

I recently purchased the micro maestro 6 channel usb board in order to drive two RC servo using serial communication. This was done for a school project where the goal is for a fully autonomous robot to follow a predefined path using a webcam while avoiding obstacles.

Using openCv, i was able to develop all of the algorithms needed for the vision part, I also developed a fuzzy logic controller in order to produce the output signal needed to control the servos. But for the past few weeks i have been stuck in the serial communication part of the project trying to find a way to send a signal from my window based c program to my robot.

I ve looked into the maestro SDK kit in order to find a way to communicate with maestro but found out that i couldn’t use those examples since my program was done using unmanaged C/C++. I have read the manual and especially the serial commands section, which I understand but I am stuck on the serial communication from c par. What i need is to find some functions and maybe also a tutorial or an example explaining exactly how to use these function.

I am currently studying mechanical engineering, so my programming skills are quite basic compared to the people on this form. Concepts such as managed, unmanaged, .Net framework are new for me and quite confusing. I don’t have much time left and it has been a frustrating past weeks trying to learn and understand serial communication, so i would appreciate any help. Please if anybody can provide me with the steps needed for me to overcame these difficulties it would be appreciated. Also if someone has C code that does what i need with the maestro that would be helpful

Regards

First of all, make sure your Maestro’s Serial Mode is set to USB Dual Port.

You said you have already started learning about serial communication. Have you found any tutorials on how to send bytes to a serial port in C? Have you attempted to write any C code to control the Maestro?

–David

Dave,

Here are some of the examples that i been tyring to learn from:
codeproject.com/KB/winsdk/Wi … iteBytes17
codeguru.com/cpp/in/network/ … .php/c2503

Dont know if i am on the right track with those links, do you have any better suggestion.

Also I am at work right know, but i will try to post some of the code that i written tonight.

Thanks for the help,

Bob

Those tutorials don’t look so good. The first one requires you to use some 3rd-party component that the author wrote. The second one is just a table of contents (with no links!) as far as I can tell.

I tried to find a better example online, but I couldn’t. So I made one. The program below is an example program for sending and receiving bytes from the Maestro over a serial port in C on Windows. I tested it with Microsoft Visual C++ 2010 Express and also MinGW, but it should work with any compiler that lets you use windows.h.

I recommend that you try to get this to compile without modifying it. Then after you verify that it works, you can modify it to suit your needs.

–David Grayson

/* MaestroSerialExampleCWindows:
 *  Example program for sending and receiving bytes from the Maestro over a serial port
 *  in C on Windows.
 *
 *  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).
 *  If channel 0 is configured as a digital output, the output should toggle
 *  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:
 *  https://www.pololu.com/docs/0J40
 *
 *  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: https://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)
	{
		fprintf(stderr, "Error: Expected to read %d bytes but only read %d (timeout). "
			"Make sure the Maestro's serial mode is USB Dual Port or USB Chained.", sizeof(command), bytesTransferred);
		return 0;
	}

	// Convert the bytes received in to a position.
	*position = response[0] + 256*response[1];

	return 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: https://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);
	if (!success)
	{
		fprintf(stderr, "Error: Unable to write Set Target 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;
	}

	return 1;
}

/** This is the first function to run when the program starts. */
int main(int argc, char * argv[])
{
	HANDLE port;
	char * portName;
	int baudRate;
	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);
	if (!success){ return -1; }
	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);
	if (!success){ return - 1; }

	/* Close the serial port so other programs can use it.
	 * Alternatively, you can just terminate the process (return from main). */
	CloseHandle(port);

	return 0;
}

David,

Thanks alot, this made my week, will test out this weeked and will let you know the results.

Thanks again,

Bob

Just wanted to say that this is the easiest way of programing the servo controller . The one in the easyExamplecpp is way more complicated .

Thanks for the sample code