8.7. Example serial code for Windows in C

The example C code below uses the Windows API to communicate with the Simple Motor Controller G2 via serial. It demonstrates how to use the USB virtual serial port or the TTL serial port to get the error status from the controller, read a variable, and set the target speed.

For this example to work, the Simple Motor Controller G2’s input mode must be Serial/USB, the serial mode must be Binary, and the CRC must be disabled. These are the default settings that the controller is shipped with. If you are using TTL serial, you should set the controller to use a fixed baud rate of 9600, or change the baud rate in the code below to match whatever fixed baud rate you choose.

// Uses Windows API serial functions to send and receive data from a
// Simple Motor Controller G2.
// NOTE: The Simple Motor Controller's input mode must be set to Serial/USB.
// NOTE: You must change the 'const char * device' line below.

#include <stdio.h>
#include <stdint.h>
#include <windows.h>

void print_error(const char * context)
{
  DWORD error_code = GetLastError();
  char buffer[256];
  DWORD size = FormatMessageA(
    FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK,
    NULL, error_code, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
    buffer, sizeof(buffer), NULL);
  if (size == 0) { buffer[0] = 0; }
  fprintf(stderr, "%s: %s\n", context, buffer);
}

// Opens the specified serial port, configures its timeouts, and sets its
// baud rate.  Returns a handle on success, or INVALID_HANDLE_VALUE on failure.
HANDLE open_serial_port(const char * device, uint32_t baud_rate)
{
  HANDLE port = CreateFileA(device, GENERIC_READ | GENERIC_WRITE, 0, NULL,
    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (port == INVALID_HANDLE_VALUE)
  {
    print_error(device);
    return INVALID_HANDLE_VALUE;
  }

  // Flush away any bytes previously read or written.
  BOOL success = FlushFileBuffers(port);
  if (!success)
  {
    print_error("Failed to flush serial port");
    CloseHandle(port);
    return INVALID_HANDLE_VALUE;
  }

  // Configure read and write operations to time out after 100 ms.
  COMMTIMEOUTS timeouts = {0};
  timeouts.ReadIntervalTimeout = 0;
  timeouts.ReadTotalTimeoutConstant = 100;
  timeouts.ReadTotalTimeoutMultiplier = 0;
  timeouts.WriteTotalTimeoutConstant = 100;
  timeouts.WriteTotalTimeoutMultiplier = 0;

  success = SetCommTimeouts(port, &timeouts);
  if (!success)
  {
    print_error("Failed to set serial timeouts");
    CloseHandle(port);
    return INVALID_HANDLE_VALUE;
  }

  // Set the baud rate and other options.
  DCB state = {0};
  state.DCBlength = sizeof(DCB);
  state.BaudRate = baud_rate;
  state.ByteSize = 8;
  state.Parity = NOPARITY;
  state.StopBits = ONESTOPBIT;
  success = SetCommState(port, &state);
  if (!success)
  {
    print_error("Failed to set serial settings");
    CloseHandle(port);
    return INVALID_HANDLE_VALUE;
  }

  return port;
}

// Writes bytes to the serial port, returning 0 on success and -1 on failure.
int write_port(HANDLE port, const uint8_t * buffer, size_t size)
{
  DWORD written;
  BOOL success = WriteFile(port, buffer, size, &written, NULL);
  if (!success)
  {
    print_error("Failed to write to port");
    return -1;
  }
  if (written != size)
  {
    print_error("Failed to write all bytes to port");
    return -1;
  }
  return 0;
}

// Reads bytes from the serial port.
// Returns after all the desired bytes have been read, or if there is a
// timeout or other error.
// Returns the number of bytes successfully read into the buffer, or -1 if
// there was an error reading.
SSIZE_T read_port(HANDLE port, uint8_t * buffer, size_t size)
{
  DWORD received;
  BOOL success = ReadFile(port, buffer, size, &received, NULL);
  if (!success)
  {
    print_error("Failed to read from port");
    return -1;
  }
  return received;
}

// Reads a variable from the SMC.
// Returns 0 on success or -1 on failure.
int smc_get_variable(HANDLE port, uint8_t variable_id, uint16_t * value)
{
  uint8_t command[] = { 0xA1, variable_id };
  int result = write_port(port, command, sizeof(command));
  if (result) { return -1; }
  uint8_t response[2];
  SSIZE_T received = read_port(port, response, sizeof(response));
  if (received < 0) { return -1; }
  if (received != 2)
  {
    fprintf(stderr, "read timeout: expected 2 bytes, got %lld\n",
      (int64_t)received);
    return -1;
  }
  *value = response[0] + 256 * response[1];
  return 0;
}

// Gets the target speed (-3200 to 3200).
// Returns 0 on success, -1 on failure.
int smc_get_target_speed(HANDLE port, int16_t * value)
{
  return smc_get_variable(port, 20, (uint16_t *)value);
}

// Gets a number where each bit represents a different error, and the
// bit is 1 if the error is currently active.
// See the user's guide for definitions of the different error bits.
// Returns 0 on success, -1 on failure.
int smc_get_error_status(HANDLE port, uint16_t * value)
{
  return smc_get_variable(port, 0, value);
}

// Sends the Exit Safe Start command, which is required to drive the motor.
// Returns 0 on success, -1 on failure.
int smc_exit_safe_start(HANDLE port)
{
  const uint8_t command = 0x83;
  return write_port(port, &command, 1);
}

// Sets the SMC's target speed (-3200 to 3200).
// Returns 0 on success, -1 on failure.
int smc_set_target_speed(HANDLE port, int speed)
{
  uint8_t command[3];

  if (speed < 0)
  {
    command[0] = 0x86; // Motor Reverse
    speed = -speed;
  }
  else
  {
    command[0] = 0x85; // Motor Forward
  }
  command[1] = speed & 0x1F;
  command[2] = speed >> 5 & 0x7F;

  return write_port(port, command, sizeof(command));
}

int main()
{
  // Choose the serial port name.
  const char * device = "\\\\.\\COM17";

  // Choose the baud rate (bits per second).  This does not matter if you are
  // connecting to the SMC over USB.  If you are connecting via the TX and RX
  // lines, this should match the baud rate in the SMC G2's serial settings.
  uint32_t baud_rate = 9600;

  HANDLE port = open_serial_port(device, baud_rate);
  if (port == INVALID_HANDLE_VALUE) { return 1; }

  int result = smc_exit_safe_start(port);
  if (result) { return 1; }

  uint16_t error_status;
  result = smc_get_error_status(port, &error_status);
  if (result) { return 1; }
  printf("Error status: 0x%04x\n", error_status);

  int16_t target_speed;
  result = smc_get_target_speed(port, &target_speed);
  if (result) { return 1; }
  printf("Target speed is %d.\n", target_speed);

  int16_t new_speed = (target_speed <= 0) ? 3200 : -3200;
  printf("Setting target speed to %d.\n", new_speed);
  result = smc_set_target_speed(port, new_speed);
  if (result) { return 1; }

  CloseHandle(port);
  return 0;
}

Related Products

High-Power Simple Motor Controller G2 18v15 (Connectors Soldered)
High-Power Simple Motor Controller G2 18v15
High-Power Simple Motor Controller G2 24v12 (Connectors Soldered)
High-Power Simple Motor Controller G2 24v12
High-Power Simple Motor Controller G2 18v25
High-Power Simple Motor Controller G2 24v19
Log In
Pololu Robotics & Electronics
Shopping cart
(702) 262-6648
Same-day shipping, worldwide
Menu
Shop Blog Forum Support
My account Comments or questions? About Pololu Contact Ordering information Distributors