Getting Started with C++¶
Library Description¶
The C++ libraries (C++11 standard) contain all of the required code to form and decode communication packets. They also contain tools for buffering packets until ready for transmission on your hardware and for storing received packets until parsing.
Each communication client object is capable of forming packets to send get, set, and save messages to a motor controller. This is done in the library with a sub-object for each piece of data that can be get, set, and saved. Thus, to form a get message, use
client_object.sub_object.get(CommunicationInterface &com)
To form a set message with a value of data type T, use
client_object.sub_object.set(CommunicationInterface &com, T value)
To form a set message with no value, use
client_object.sub_object.set(CommunicationInterface &com)
Finally, to form a save message, use
client_object.sub_object.save(CommunicationInterface &com)
These commands form serialized get/set/save
packets and store them into a CommunicationInterface
object. We supply a hardware agnostic CommunicationInterface called GenericInterface. Once packets are
stored in the GenericInterface object, the user must remove the bytes with the class method
interface_object.GetTxBytes(uint8_t* data_out, uint8_t& length_out)
and send the bytes in data_out over the hardware serial.
Similarly, when bytes are received over the hardware serial they must be transferred into the GenericInterface using the class method
interface_object.SetRxBytes(uint8_t* data_in, uint16_t length_in)
Once transferred, a packet can be peeked using
int8_t interface_object.PeekPacket(uint8_t **packet, uint8_t *length)
which will return 1 if there is a packet, 0 if not. If there is a packet, this packet must be passed to the client objects using
client_object.ReadMsg(CommunicationInterface& com, uint8_t* rx_data, uint8_t rx_length)
Once passed to all objects, drop the packet using interface_object.DropPacket()
.
You can check for newly received data with
client_object.sub_object.IsFresh()
To retrieve the most recent data, regardless of its freshness, use
data = client_object.sub_object.get_reply()
Full C++ Code Example (w/ LibSerial)¶
/*
* IQ Motion Control read motor coil temperature.
*
* This code shows how to use Serial over USB to read the
* motor coil temperature
*
*
* The circuit:
* Connected to FTDI usb to Serial
*
* This example uses:
* - LibSerial (https://libserial.readthedocs.io/en/latest/index.html)
*
* This demo works for POSIX supported systems and was ran using Linux Ubuntu 20.04.1 LTS
*
*
* Created 2021/03/31 by Malik B. Parker
*
* This example code is in the public domain.
*/
#include "generic_interface.hpp"
#include "temperature_estimator_client.hpp"
#include "libserial/SerialPort.h"
#include "libserial/SerialStream.h"
#include <string>
#include <iostream>
#include "unistd.h"
using namespace LibSerial;
int main(){
// Setup the serial interface
SerialPort my_serial_port("/dev/ttyUSB0");
my_serial_port.SetBaudRate(BaudRate::BAUD_115200);
// Make a communication interface object
// This is what creates and parses packets
GenericInterface com;
// Make a Temperature Estimator Client object with obj_id 0
TemperatureEstimatorClient temp(0);
while(true){
/**********************************************************************
*********************** Sending Get Command **************************
*********************************************************************/
// Forms a packet in the com interface with the following:
// type: (77) Temperature Estimator ID Number
// subtype: ( 0) temp
// obj/access ( 0) get
temp.temp_.get(com);
uint8_t packet_buf[64];
uint8_t length = 0;
// Get the packet from the com interface and place it into the packet buffer
if(com.GetTxBytes(packet_buf, length)){
// C is a strong typed language -_-
// so we need to convert to a string buffer to interface with LibSerial
std::string string_buf((char*)packet_buf, length);
// Send the get packet request to the motor
my_serial_port.Write(string_buf);
}
/**********************************************************************
************************** Receiving Temp Value **********************
*********************************************************************/
// Need to wait for the Motor Controller to Respond
usleep(5000);
// Serial Receive Buffer
std::string read_buf;
// How many bytes are in the read buffer
length = my_serial_port.GetNumberOfBytesAvailable();
// Read the packet from Serial
my_serial_port.Read(read_buf, length);
// Again C is strongly types so we have to convert back to byte buffer
uint8_t * cbuf = (uint8_t *) read_buf.c_str();
// Transfer the buffer into the com interface
com.SetRxBytes(cbuf, length);
/**************************************************************************
************************** Reading the Value ***************************
*************************************************************************/
// Temporary Pointer to the packet data location
uint8_t *packet_data;
uint8_t packet_length;
// Loads the packet data buffer with data receieved from the motor
com.PeekPacket(&packet_data, &packet_length);
// Loads data into the temperature client
temp.ReadMsg(packet_data, packet_length);
com.DropPacket();
// Reads the data from the temperature client
float temperature = temp.temp_.get_reply();
printf("Temperature: %f\n", temperature);
}
return 0;
}
Full Arduino Code Example (w/ Arduino Serial)¶
The below is a complete example of a program using the Arduino programming environment. This example is to demonstrate how to use the clients, the GenericInterface class, and the transfer of data between the classes and the Arduino Serial class. Please note that IQ’s dedicated Arduino libraries streamline the data transfer process, thus, actual Arduino programming is simpler than the below example. Please see the Arduino documentation if you intend on using the Arduino programming environment.
/*
* IQ Motion Control spin and report demo.
*
* This code will command a motor to spin at various voltages and
* simultaniously report the motor’s position and velocity over USB
*
*
* The circuit:
* Serial1 RX is directly connected to motor TX (Red)
* Serial1 TX is directly connected to motor RX (White)
*
* Created 2018/10/8 by Matthew Piccoli
*
* This example code is in the public domain.
*/
// USER SETABLE VALUES HERE------------------------------
// Voltage step size
const float kVoltageStep = 0.01f;
// Max voltage
const float kVoltageMax = 0.25f;
// END USER SETABLE VALUES-------------------------------
// Includes required for communication
// Message forming interface
#include <generic_interface.hpp>
// Clients that speaks to module’s objects
#include <brushless_drive_client.hpp>
// Make a communication interface object
GenericInterface com;
// Make a objects that talk to the module
BrushlessDriveClient mot(0);
void setup() {
// Initialize USB communicaiton
Serial.begin(115200);
Serial.print("Program starting");
Serial.println();
// Initialize the Serial peripheral for motor controller
Serial1.begin(115200);
}
void loop() {
static float voltage_to_set = 0.0f;
static float voltage_sign = 1.0f;
// Update voltage command
if(abs(voltage_to_set) >= kVoltageMax){
voltage_sign = -1*voltage_sign;
}
voltage_to_set += kVoltageStep*voltage_sign;
SendMessages(voltage_to_set);
ReceiveMessages();
DoSomethingWithMessages();
delay(100);
}
void SendMessages(float voltage_command){
// This buffer is for passing around messages.
uint8_t communication_buffer[64];
// Stores length of message to send or receive
uint8_t communication_length;
// Generate the set message
mot.drive_spin_volts_.set(com, voltage_command);
// Generate the get message
mot.obs_angle_.get(com);
mot.obs_velocity_.get(com);
// Grab outbound messages in the com queue, store into buffer
// If it transferred something to communication_buffer...
if(com.GetTxBytes(communication_buffer,communication_length)){
// Use Arduino serial hardware to send messages
Serial1.write(communication_buffer,communication_length);
}
Serial.print("Setting voltage: ");
Serial.print(voltage_command);
Serial.println();
}
void ReceiveMessages(){
// This buffer is for passing around messages.
uint8_t communication_buffer[64];
// Stores length of message to send or receive
uint8_t communication_length;
// Reads however many bytes are currently available
communication_length = Serial1.readBytes(communication_buffer, Serial1.available());
// Puts the recently read bytes into coms receive queue
com.SetRxBytes(communication_buffer,communication_length);
uint8_t *rx_data; // temporary pointer to received type+data bytes
uint8_t rx_length; // number of received type+data bytes
// while we have message packets to parse
while(com.PeekPacket(&rx_data,&rx_length)){
// Share that packet with all client objects
mot.ReadMsg(com,rx_data,rx_length);
// Once were done with the message packet, drop it
com.DropPacket();
}
}
void DoSomethingWithMessages(){
// Check if we have any fresh data
// Checking for fresh data is not required, it simply
// lets you know if you received a message that you
// have not yet read.
// Check for a new angle message
if(mot.obs_angle_.IsFresh()) {
Serial.print("Angle: ");
Serial.print(mot.obs_angle_.get_reply());
Serial.println();
}
// Check for a new velocity message
if(mot.obs_velocity_.IsFresh()) {
Serial.print("Velocity: ");
Serial.print(mot.obs_velocity_.get_reply());
Serial.println();
}
}