MercurySDK
Software development kit for Mercury digital servos
sync_read_write.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  * Copyright 2017 ROBOTIS CO., LTD.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *******************************************************************************/
16 
17 /* Author: Ryu Woon Jung (Leon) */
18 
19 //
20 // ********* Sync Read and Sync Write Example *********
21 //
22 //
23 // Available Mercury model on this example : All models using Protocol 2.0
24 // This example is tested with two Mercury PRO 54-200, and an USB2DYNAMIXEL
25 // Be sure that Mercury PRO properties are already set as %% ID : 1 / Baudnum : 1 (Baudrate : 57600)
26 //
27 
28 #if defined(__linux__) || defined(__APPLE__)
29 #include <fcntl.h>
30 #include <termios.h>
31 #include <unistd.h>
32 #define STDIN_FILENO 0
33 #elif defined(_WIN32) || defined(_WIN64)
34 #include <conio.h>
35 #endif
36 
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <iostream>
40 #include <string>
41 #include "../../include/mercury_sdk/mercury_sdk.h" // Uses Mercury SDK library
42 
43 // bool synchroniseMotor(mercury::PortHandler *portHandler, mercury::PacketHandler *packetHandler, uint8_t id);
44 
45 // Control table address
46 #define ADDR_MCY_TORQUE_ENABLE 0x30 // Mercury Control table register addresses
47 #define ADDR_MCY_GOAL_POSITION 0x4e
48 #define ADDR_MCY_PRESENT_POSITION 0x5a
49 
50 // Data Byte Length
51 #define LEN_MCY_GOAL_POSITION 4
52 #define LEN_MCY_PRESENT_POSITION 4
53 
54 // Default setting
55 #define BAUDRATE 1000000
56 #define DEVICENAME "/dev/ttyACM1" // Check which port is being used on your controller
57  // ex) Windows: "COM1" Linux: "/dev/ttyUSB0" Mac: "/dev/tty.usbserial-*"
58 
59 #define TORQUE_ENABLE 1 // Value for enabling the torque
60 #define TORQUE_DISABLE 0 // Value for disabling the torque
61 #define MCY_MINIMUM_POSITION_VALUE -2000 // Mercury will rotate between this value
62 #define MCY_MAXIMUM_POSITION_VALUE 2000 // and this value (note that the Mercury would not move when the position value is out of movable range. Check e-manual about the range of the Mercury you use.)
63 #define MCY_MOVING_STATUS_THRESHOLD 20 // Mercury moving status threshold
64 
65 #define ESC_ASCII_VALUE 0x1b
66 
67 int getch()
68 {
69 #if defined(__linux__) || defined(__APPLE__)
70  struct termios oldt, newt;
71  int ch;
72  tcgetattr(STDIN_FILENO, &oldt);
73  newt = oldt;
74  newt.c_lflag &= ~(ICANON | ECHO);
75  tcsetattr(STDIN_FILENO, TCSANOW, &newt);
76  ch = getchar();
77  tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
78  return ch;
79 #elif defined(_WIN32) || defined(_WIN64)
80  return _getch();
81 #endif
82 }
83 
84 int kbhit(void)
85 {
86 #if defined(__linux__) || defined(__APPLE__)
87  struct termios oldt, newt;
88  int ch;
89  int oldf;
90 
91  tcgetattr(STDIN_FILENO, &oldt);
92  newt = oldt;
93  newt.c_lflag &= ~(ICANON | ECHO);
94  tcsetattr(STDIN_FILENO, TCSANOW, &newt);
95  oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
96  fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
97 
98  ch = getchar();
99 
100  tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
101  fcntl(STDIN_FILENO, F_SETFL, oldf);
102 
103  if (ch != EOF)
104  {
105  ungetc(ch, stdin);
106  return 1;
107  }
108 
109  return 0;
110 #elif defined(_WIN32) || defined(_WIN64)
111  return _kbhit();
112 #endif
113 }
114 
115 int main()
116 {
117  // Initialize PortHandler instance
118  // Set the port path
119  // Get methods and members of PortHandlerLinux or PortHandlerWindows
121 
122  // Initialize PacketHandler instance
123  // Set the protocol version
124  // Get methods and members of Protocol1PacketHandler or Protocol2PacketHandler
126 
127  // Initialize GroupSyncWrite instance
128  mercury::GroupSyncWrite groupSyncWrite(portHandler, packetHandler, ADDR_MCY_GOAL_POSITION, LEN_MCY_GOAL_POSITION);
129 
130  // Initialize Groupsyncread instance for Present Position
131  mercury::GroupSyncRead groupSyncRead(portHandler, packetHandler, ADDR_MCY_PRESENT_POSITION, LEN_MCY_PRESENT_POSITION);
132 
133  struct mcy_servo
134  {
135  uint8_t id;
136  int32_t mcy_present_position;
137  };
138 
139  std::vector<mcy_servo> mcy_servos{{1, 0}, {2, 0}};
140 
141  int index = 0;
142  int mcy_comm_result = COMM_TX_FAIL; // Communication result
143  bool mcy_addparam_result = false; // addParam result
144  bool mcy_getdata_result = false; // GetParam result
145  int mcy_goal_position[2] = {MCY_MINIMUM_POSITION_VALUE, MCY_MAXIMUM_POSITION_VALUE}; // Goal position
146 
147  uint8_t mcy_error = 0; // Mercury error
148  uint8_t param_goal_position[4];
149 
150  // Open port
151  if (portHandler->openPort())
152  {
153  printf("Succeeded to open the port!\n");
154  }
155  else
156  {
157  printf("Failed to open the port!\n");
158  printf("Press any key to terminate...\n");
159  getch();
160  return 0;
161  }
162 
163  // Set port baudrate
164  if (portHandler->setBaudRate(BAUDRATE))
165  {
166  // This is a rom write, so add a delay of 1s
167  usleep(1e06);
168 
169  printf("Succeeded to change the baudrate!\n");
170  }
171  else
172  {
173  printf("Failed to change the baudrate!\n");
174  printf("Press any key to terminate...\n");
175  getch();
176  return 0;
177  }
178 
179  printf("**** Synchronising Mercury servos ****\n");
180  usleep(1000);
181  for (mcy_servo servo : mcy_servos)
182  {
183  printf("Synchronising Mercury#%d\n", servo.id);
184 
185  mcy_comm_result = packetHandler->synchronise(portHandler, servo.id, &mcy_error);
186  if (mcy_comm_result != COMM_SUCCESS)
187  {
188  printf("Mercury#%d: has failed to synchronise. Error: %s\n", servo.id, packetHandler->getTxRxResult(mcy_comm_result));
189  }
190  else
191  {
192  printf("Mercury#%d is synchronised\n", servo.id);
193  }
194  }
195 
196  printf("**** Mercury servos are synchronised ****\n");
197 
198  printf("**** Connecting Mercury servos ****\n");
199 
200  for (mcy_servo servo : mcy_servos)
201  {
202  mcy_comm_result = packetHandler->write1ByteTxRx(portHandler, servo.id, ADDR_MCY_TORQUE_ENABLE, TORQUE_ENABLE, &mcy_error);
203  if (mcy_comm_result != COMM_SUCCESS)
204  {
205  printf("Mercury#%d: %s\n", servo.id, packetHandler->getTxRxResult(mcy_comm_result));
206  }
207  else if (mcy_error != 0)
208  {
209  printf("Mercury#%d torque enabled failed: %s\n", servo.id, packetHandler->getRxPacketError(mcy_error));
210  }
211  else
212  {
213  printf("Mercury#%d has been successfully connected \n", servo.id);
214  }
215 
216  usleep(1000);
217  }
218 
219  printf("**** Mercury servos are connected ****\n");
220 
221  printf("Press any key to continue! (or press ESC to quit!)\n");
222  getch();
223 
224  for (mcy_servo servo : mcy_servos)
225  {
226  // Add parameter storage for Mercury#1 present position value
227  mcy_addparam_result = groupSyncRead.addParam(servo.id);
228  if (mcy_addparam_result != true)
229  {
230  fprintf(stderr, "[ID:%03d] groupSyncRead addparam failed", servo.id);
231  return 0;
232  }
233  usleep(1000);
234  }
235 
236  int iteration = 0;
237  while (1)
238  {
239 #if defined(__linux__)
240  usleep(100000);
241 #endif
242 
243  if (kbhit())
244  {
245  int ch = getch();
246  if (ch == ESC_ASCII_VALUE)
247  break;
248  }
249 
250  iteration++;
251 
252  printf("Iteration:%03d\t\n", iteration);
253 
254  // Allocate goal position value into byte array
255  param_goal_position[0] = MCY_LOBYTE(MCY_LOWORD(mcy_goal_position[index]));
256  param_goal_position[1] = MCY_HIBYTE(MCY_LOWORD(mcy_goal_position[index]));
257  param_goal_position[2] = MCY_LOBYTE(MCY_HIWORD(mcy_goal_position[index]));
258  param_goal_position[3] = MCY_HIBYTE(MCY_HIWORD(mcy_goal_position[index]));
259 
260  for (mcy_servo servo : mcy_servos)
261  {
262  // Add Mercury goal position value to the Syncwrite storage
263  mcy_addparam_result = groupSyncWrite.addParam(servo.id, param_goal_position);
264  if (mcy_addparam_result != true)
265  {
266  fprintf(stderr, "[ID:%03d] groupSyncWrite addparam failed", servo.id);
267  return 0;
268  }
269  }
270 
271  // Syncwrite goal position
272  mcy_comm_result = groupSyncWrite.txPacket();
273  if (mcy_comm_result != COMM_SUCCESS)
274  printf("%s\n", packetHandler->getTxRxResult(mcy_comm_result));
275 
276  // Clear syncwrite parameter storage
277  groupSyncWrite.clearParam();
278 
279  do
280  {
281 #if defined(__linux__)
282  usleep(100000);
283 #endif
284 
285  // Syncread present position
286  mcy_comm_result = groupSyncRead.txRxPacket();
287  if (mcy_comm_result != COMM_SUCCESS)
288  {
289  printf("%s\n", packetHandler->getTxRxResult(mcy_comm_result));
290  }
291  else
292  {
293  for (mcy_servo servo : mcy_servos)
294  {
295  if (groupSyncRead.getError(servo.id, &mcy_error))
296  printf("[ID:%03d] %s\n", servo.id, packetHandler->getRxPacketError(mcy_error));
297  }
298  }
299 
300  for (mcy_servo servo : mcy_servos)
301  {
302  usleep(1000);
303 
304  // Check if groupsyncread data of Mercury#1 is available
305  mcy_getdata_result = groupSyncRead.isAvailable(servo.id, ADDR_MCY_PRESENT_POSITION, LEN_MCY_PRESENT_POSITION);
306  if (mcy_getdata_result != true)
307  {
308  fprintf(stderr, "[ID:%03d] groupSyncRead getdata failed", servo.id);
309  continue;
310  }
311  }
312 
313  for (mcy_servo& servo : mcy_servos)
314  {
315 
316  // Get Mercury#1 present position value
317  servo.mcy_present_position = groupSyncRead.getData(servo.id, ADDR_MCY_PRESENT_POSITION, LEN_MCY_PRESENT_POSITION);
318  usleep(1000);
319 
320  printf("[ID:%03d] GoalPos1:%03d PresPos:%03d", (int)servo.id, mcy_goal_position[index], servo.mcy_present_position);
321  }
322  printf("\t\n");
323 
324  } while ((abs(mcy_goal_position[index] - mcy_servos.at(0).mcy_present_position) > MCY_MOVING_STATUS_THRESHOLD));
325 
326  // Change goal position
327  if (index == 0)
328  {
329  index = 1;
330  }
331  else
332  {
333  index = 0;
334  }
335  }
336 
337  for (mcy_servo servo : mcy_servos)
338  {
339  mcy_comm_result = packetHandler->write1ByteTxRx(portHandler, servo.id, ADDR_MCY_TORQUE_ENABLE, TORQUE_DISABLE, &mcy_error);
340  if (mcy_comm_result != COMM_SUCCESS)
341  {
342  printf("Mercury#%d: %s\n", servo.id, packetHandler->getTxRxResult(mcy_comm_result));
343  }
344  else if (mcy_error != 0)
345  {
346  printf("Mercury#%d torque disable failed: %s\n", servo.id, packetHandler->getRxPacketError(mcy_error));
347  }
348  else
349  {
350  printf("Mercury#%d has been successfully disconnected \n", servo.id);
351  }
352 
353  usleep(1000);
354  }
355 
356  // Close port
357  portHandler->closePort();
358 
359  return 0;
360 }
The class for reading multiple Dynamixel data from same address with same length at once.
bool addParam(uint8_t id)
The function that adds id, start_address, data_length to the Sync Read list.
int txRxPacket()
The function that transmits and receives the packet which might be come from the Dynamixel.
bool getError(uint8_t id, uint8_t *error)
The function that gets the error which might be received by GroupSyncRead::rxPacket or GroupSyncRead:...
bool isAvailable(uint8_t id, uint16_t address, uint16_t data_length)
The function that checks whether there are available data which might be received by GroupSyncRead::r...
uint32_t getData(uint8_t id, uint16_t address, uint16_t data_length)
The function that gets the data which might be received by GroupSyncRead::rxPacket or GroupSyncRead::...
The class for writing multiple Dynamixel data from same address with same length at once.
void clearParam()
The function that clears the Sync Write list.
int txPacket()
The function that transmits the Sync Write instruction packet which might be constructed by GroupSync...
bool addParam(uint8_t id, uint8_t *data)
The function that adds id, start_address, data_length to the Sync Write list.
The class that inherits Protocol1PacketHandler class or Protocol2PacketHandler class.
virtual const char * getTxRxResult(int result)=0
The function that gets description of communication result.
virtual int write1ByteTxRx(PortHandler *port, uint8_t id, uint16_t address, uint8_t data, uint8_t *error=0)=0
The function that calls PacketHandler::writeTxRx() for writing 1 byte data and receives the packet @d...
virtual const char * getRxPacketError(uint8_t error)=0
The function that gets description of hardware error.
virtual int synchronise(PortHandler *port, uint8_t id, uint8_t *error=0)=0
The function that synchronises the servo @description This function will synchronise the target Mercu...
static PacketHandler * getPacketHandler()
The function that returns PacketHandler instance.
The class for port control that inherits PortHandlerLinux, PortHandlerWindows, PortHandlerMac,...
Definition: port_handler.h:56
static PortHandler * getPortHandler(const char *port_name)
The function that gets PortHandler class inheritance @description The function gets class inheritance...
virtual bool openPort()=0
The function that opens the port @description The function calls PortHandlerLinux::setBaudRate() to o...
virtual bool setBaudRate(const int baudrate)=0
The function that sets baudrate into the port handler @description The function sets baudrate into th...
virtual void closePort()=0
The function that closes the port @description The function closes the port.
#define MCY_HIWORD(l)
#define MCY_LOWORD(l)
#define COMM_SUCCESS
#define MCY_LOBYTE(w)
#define MCY_HIBYTE(w)
#define COMM_TX_FAIL
#define MCY_MAXIMUM_POSITION_VALUE
int getch()
#define MCY_MOVING_STATUS_THRESHOLD
#define ESC_ASCII_VALUE
#define LEN_MCY_GOAL_POSITION
#define BAUDRATE
#define TORQUE_ENABLE
int kbhit(void)
#define MCY_MINIMUM_POSITION_VALUE
#define LEN_MCY_PRESENT_POSITION
#define DEVICENAME
#define ADDR_MCY_TORQUE_ENABLE
#define ADDR_MCY_GOAL_POSITION
#define ADDR_MCY_PRESENT_POSITION
int main()
#define TORQUE_DISABLE