10. LeArm AI + Sliding Rail Course

10.1 Sliding Rail Introduction and Installation

10.1.1 Product Introduction

The electric sliding rail is a common automation device powered by a motor. The motor is controlled via a motor driver to enable smooth and precise movement.

It is often used with robotic arm teaching platforms to significantly expand the arm’s operating range, making it ideal for educational experiments and theoretical verification.

10.1.2 Structure and Working Principle

  • Mechanical Structure

The sliding rail consists of the following mechanical components:

(1) Drag chain: protects the connecting wires.

(2) Timing pulley: connected to the stepper motor and drives the conveyor belt on the sliding rail.

(3) Slider platform: fixed to the conveyor belt and mounted on the guide rail.

  • Hardware Components

The hardware module includes the A4988 stepper motor driver that powers the stepper motor, and a stepper motor control board which connects the driver and expands the I/O ports. The stepper motor model J-4218HB4401 uses PWM control to drive the timing pulley, which moves the belt.

During operation, the A4988 driver board may experience excessive power consumption, causing its temperature to rise. If the temperature gets too high, it can damage the driver board. To prevent this situation, a heat sink is attached to the driver IC (applied at the factory) to help dissipate heat and maintain a stable operating temperature.

Note

After extended use, the driver may become hot. Avoid touching it directly to prevent burns!

  • Working Principle

The electric sliding rail transmits the motor’s power through a transmission mechanism to the guide rail, enabling linear movement. The control process involves three main stages: motor driving, travel control, and sensor feedback.

(1) Motor Driving: It is the most fundamental control method. The motor receives energy via input current and is driven by the motor driver. This ensures the robot performs stable and precise movements.

(2) Travel Control: Travel distance must be regulated according to the rail’s range. This is managed via programmed code and current control to accurately set displacement and speed.

(3) Sensor Feedback: For precise control, the sliding rail is equipped with sensors to monitor the system’s status in real time. The rail’s travel limit switch is a reset-type sensor that detects its initial state.

10.1.3 Pin Instruction

  • As shown in the figure above:

(1) Motor Drive Interface – Connects to the stepper motor on the other end.

(2) Limit Switch Interface – Connects to the travel limit switch on the other end.

(3) IIC Interface – The other end connects to the I2C interface on the control board. This product uses I2C communication to interact with the stepper motor driver.

(4) Power Interface – Connects to the power supply on the other end.

Note

The wires are designed to prevent reverse insertion. Do not force the connector if it does not fit.

The diagram below shows the stepper motor driver A4988:

Note

The above diagram is a schematic of the A4988 stepper motor driver module only. The actual module color may vary based on the final product.

  • Power Supply Pins:

(1) VDD and GND power the internal logic circuit, supporting voltages from 3V to 5.5V.

(2) VMOT and GND supply power to the motor, supporting voltages from 8V to 35V.

  • Step Resolution Pins:

The stepper motor’s precision can be controlled by setting different step resolutions.

The A4988 driver has three step resolution selection pins: MS1, MS2, and MS3, as shown in the diagram below.

These allow the step resolution to be set to one of five levels. The smaller the step size, the higher the precision. For more details about step settings, please refer to the section 10.1.4 Pulse Configuration in this document.

MS1 MS2 MS3 Step Resolution
0 0 0 Full step
1 0 0 Half step
0 1 0 Quarter step
1 1 0 Eighth step
1 1 1 Sixteenth step
  • Control Input Pins:

The driver board features two main control input pins: STEP and DIR, as shown in the diagram below.

(1) STEP controls the stepping of the motor. A high-frequency signal results in faster rotation, while a low-frequency signal yields slower rotation.

(2) DIR controls the motor’s rotation direction. A high signal sets the motor to rotate clockwise, and a low signal sets it to rotate counterclockwise.

  • Power Control Pins:

The ENABLE (EN), RESET (RST), and SLEEP (SLP) pins are used to manage the power states of the driver, as illustrated below.

(1) These three pins are active-low, meaning they are activated when set to a low logic level.

(2) The difference is that when the EN pin is set to low, the A4988 driver is enabled, meaning the board is powered and operating normally.

(3) Setting RST pin low will reset the internal translator of the driver and ignore incoming STEP signals. This action reinitializes the driver and is influenced by the step resolution settings.

(4) When SLP pin is low, the driver enters a low-power sleep mode to conserve energy when the motor is not in use.

  • Output Pins

The output pins of the motor driver are distributed along the edge of the module as follows: The 1B, 1A, 2A, and 2B pins can be supplied with a voltage ranging from 8V to 35V, and they deliver an output current of up to 2A.

10.1.4 Pulse Configuration

By adjusting the width of PWM pulses, you can control the motor’s rotation speed—wider pulses result in faster rotation, while narrower pulses slow it down. To better understand the relationship between pulse width and the motor’s actual rotation angle or number of revolutions, you need to calculate how many pulses are required for one full revolution. The process is as follows:

(1) Determining the Motor Step Angle:

As shown in the image, the highlighted section "1.8°/STEP" indicates the step angle of the motor. This means that for each electrical pulse the motor receives, the rotor rotates by 1.8°.

(2) Calculating Pulses per Revolution:

The step angle of the stepper motor is 1.8°. Since a full circle is 360°, when the microstepping setting, which divides the step angle into smaller increments per pulse, is set to the default value of 1, the motor requires 200 pulses to complete one full revolution (360 ÷ 1.8 = 200).

(3) Microstepping (Pulse Subdivision):

To improve the rotational precision of the motor, you can configure microstepping, which divides each full step into smaller increments:

① 2 microsteps (1/2 step): Step angle = 1.8° ÷ 2 = 0.9°, Pulses per revolution = 200 × 2 = 400.

② 4 microsteps (1/4 step): Step angle = 1.8° ÷ 4 = 0.45°, Pulses per revolution = 200 × 4 = 800.

③ 8 microsteps (1/8 step): Step angle = 1.8° ÷ 8 = 0.225°, Pulses per revolution = 200 × 8 = 1600.

Note

When a high microstepping setting is used, for example, microstepping set to 1/8, the smallest possible step becomes 0.225°. Since this is a very small angle and may not correspond to the motor’s actual physical position, which aligns with full steps such as 1.8°, the stepper motor must remain powered even when it is stationary. This ensures the rotor holds its current microstep position and doesn’t snap back to the nearest full-step physical detent. As a result, it is normal to hear a faint humming or current noise from the motor when it’s idle. This is a normal behavior and not a cause for concern.

Based on the timing pulley specifications used on the sliding rail, one full rotation of the motor equals one full rotation of the pulley. Since this translates to a travel distance of 75 mm per rotation, you can use this standard (75 mm per rotation) as a reference when writing code for pulse testing and movement calibration.

10.1.5 Current Adjustment

Note

  • Each motor driver board comes with a heatsink pre-installed from the factory.

  • The board can become hot after running for extended periods—do not touch it directly to avoid burns.

Before operating the stepper motor, you need to set the current limit on the driver board to ensure the current does not exceed the rated current of the driver.

Use a Phillips screwdriver to adjust the current limiter (as shown below). Turn clockwise to increase the current, while turn counterclockwise to decrease the current. Adjust the current gradually until it reaches a safe level close to the driver’s rated current.

[Current Adjustment Potentiometer]

If you’d like to understand the exact method for calculating and adjusting the current, refer to the following. Otherwise, feel free to skip this section:

Without a heatsink, keep the current below 1.2A. With a heatsink installed, do not exceed 2A.

(1) Use a multimeter to measure the reference voltage on the driver board. The measurement point is indicated in the image on the left below.

A4988 Maximum Current Calculation Formula:

V is the measured voltage. R is the sense resistor value, 0.1Ω in this section, typically 0.1Ω for R100, 0.2Ω for R200, or 0.05Ω for R050.

(2) Use the formula to determine the appropriate current limit for your setup.

10.1.6 Specifications

  • Sliding Rail Parameters

Profile Width 40mm Lead of Module 75mm Operating Temperature -30°C to 60°C
Maximum Load 10KG Timing Pulley Specification HTD3M, 25 teeth Base Weight (at 0 mm stroke) 1.1KG
Customizable Stroke ≤500mm Timing Belt Specification HTD3M, 15 mm width Weight Change per 100 mm Stroke 0.2KG
Linear Speed ≤1.5m/s Repeat Positioning Accuracy ≤±0.05mm Motor Connection Type Direct drive or with gearbox
Applicable Drive Torque ≤3N·M Straightness / Torsional bending ≤±0.05/300mm Compatible Motors Stepper and Servo Motors (200W / 400W / 750W)
  • Stepper Motor Specifications:

Model 42CM06 Body Length 47.5mm Number of Wires 4 wires
Wiring Configuration Black: A+, Green: A- Holding Torque 0.60N·M Current 1.96A
Step Angle 1.8°±5% Number of Phases 2 Rated Voltage 3.68V
Rated Current 2.3A Resistance 1.6ohm±10%(20℃) Inductance 4 mH±20%(1Vrms.1k Hz)
  • Stepper Motor Driver:

Model A4988 Dimensions 1.5mmx2mm Weight 3.7g
Motor Supply Voltage 8~35V Microstepping Modes 5 (Full, Half, 1/4, 1/8, 1/16) Maximum Drive Current 2A

10.1.7 Registers

The following table provides a description of the relevant registers:

Register Name Address Function Description
I2C_ADDR 0x35 Used for I2C communication protocol
MOTOR_STATE_ADDR 0x20 Sets the motor state
STEPS_DRIVER_MODE_ADDR 0x21 Initializes different motor microstepping modes
MOTOR_AUTO_REPOSITION_ADDR 0x22 Controls the motor to return to its initial position
STEPS_ADDR 0x24 Writes the motor movement steps
STEPS_TIME_ADDR 0x28 Sets the run time for each step

10.1.8 Frequently Asked Questions

(1) Why is the sliding rail moving very slowly during operation?

① Answer:

Please check the power supply voltage for the sliding rail. Make sure to use a 6–12V power source. If the voltage is below 6V, it may cause the rail to operate at a reduced speed. Depending on your application, you can adjust the microstepping settings in the program. A lower microstepping value results in faster movement, while a higher value provides slower but more stable and powerful motion.

(2) Why does the motor keep working and make a clicking sound when the rail reaches its limit?

① Answer:

The sliding rail is controlled by pulse signals sent from the driver to the stepper motor. Each pulse adds to or subtracts from the previous position. When the rail reaches its physical limit and pulses continue to be sent, it may cause damage to the rail mechanism. Therefore, it’s important to configure appropriate pulse limits in your program to avoid over-travel and protect the hardware.

10.1.9 Installation and Wiring Interface Indicators

  • Install the Sliding Rail Bracket

  • Install the Tank Tread

  • Install LeArm AI

  • Wiring to Connect sliding rail and LeArm AI

Connect the 3-pin cable from the sliding rail to any bus servo port on the left side of the diagram to supply power. Connect the 4-pin cable to any IIC port on the right side of the diagram for communication.

10.2 Wireless Controller Control

10.2.1 Project Introduction

In this lesson, a wireless controller is used to control both the robotic arm and the sliding rail.

10.2.2 Project Process

10.2.3 Module Instruction

The PS2 controller communicates with the controller via a receiver using a 2.4G wireless signal. The controller has two modes, which can be toggled using the “MODE” button.

10.2.4 Program Download

Source Code

(1) Connect the core board to the computer using a USB cable.

(2) Locate the corresponding Arduino project file in the same directory as this document.

(3) Select the development board model when you open the program, and the specific model is shown in the figure below.

(4) Click “Compile” first, then click “Upload”. After the upload is completed, the program download is completed if the following interface appears in the output box below the software.

10.2.5 Project Outcome

When the controller is in digital mode (only green light on), the robotic arm will not respond to control inputs.

When in analog mode (both red and green lights on), the robotic arm can be controlled. Pressing any button together with “SELECT” will trigger a predefined motion group. Moving the right joystick left or right will slide the rail in the corresponding direction.

10.2.6 Program Brief Analysis

Note

The controller input handling and protocol parsing are explained in detail in the earlier lesson titled Wireless Controller Control. This section highlights only the differences specific to this project.

  • ino File (Application Layer)

(1) Imports the following libraries: config.h, Hiwonder.hpp, Robot_arm.hpp, PS2_CTL.hpp, and stepper.hpp. Hiwonder.hpp defines the LED, buzzer, and button objects. Robot_arm.hpp defines the robotic arm object. PS2_CTL.hpp defines the PS2 controller object. stepper.hpp defines the sliding rail object.

{lineno-start=}

#include "Config.h"
#include "Hiwonder.hpp"
#include "Robot_arm.hpp"
#include "./src/PS2/PS2_CTL.hpp"
#include "stepper.hpp"

(2) Creates objects for the LED, buzzer, robotic arm, PS2 controller, IIC, and conveyor belt. Declares variables related to the sliding rail.

 7
 8
 9
10
11
12
13
14
Led_t led_obj;
Buzzer_t buzzer_obj;
LeArm_t arm;
PS2_CTL ps2;
IIC iic;
Stepper stepper_obj;

int stepper_run = 0;

(3) In the setup function, first delay for 1000 ms (1 second), then power off the Bluetooth module. Next, initialize the robotic arm, LED, buzzer, I2C, and sliding rail objects, then open the serial port and set the baud rate to 9600. Then call the sliding rail object and set the step value to 1000.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void setup() {
  delay(1000);
  pinMode(IO_BLE_CTL, OUTPUT);
  digitalWrite(IO_BLE_CTL, LOW);  // Set the Bluetooth control pin to low level to power off the Bluetooth module (设置蓝牙控制引脚为低电平时断开蓝牙模块电源)

  arm.init();
  led_obj.init(IO_LED);
  buzzer_obj.init(IO_BUZZER);
  ps2.init();
  iic.init();
  stepper_obj.init(&iic);
  stepper_obj.driver_mode(0x02);
  stepper_obj.reset();
  Serial.begin(9600);

  delay(2000);
}

(4) In the main loop, the ps2_Task function is called to process data and control the movement of the sliding rail.

34
35
36
37
void loop() {
  ps2.PS2_Task(&arm, &led_obj, &buzzer_obj, &stepper_run);
  stepper_obj.set_step(stepper_run);
}
  • PS2_CTL.cpp File (Low-Level)

(1) In the get_result command frame processing function, the logic is executed only when the current command frame object’s working mode “mode” is in analog mode (PS2_COORDINATE_MODE).

186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
int PS2_CTL::get_result(LeArm_t* robot,Led_t* led,Buzzer_t* buzzer,int* stepper_run)
{
  if(rec_flag){
    rec_flag = false;
    if(keyvalue.mode == PS2_COORDINATE_MODE)
    {
      if(keyvalue.bit_triangle == 1)
      {
        robot->knot_run(1, 1600, action_running_time);
      }
      else if(keyvalue.bit_cross == 1)
      {
        robot->knot_run(1, MIN_DUTY, action_running_time);
      }
      else if(keyvalue.bit_triangle == 0 || keyvalue.bit_cross == 0)
      {
        robot->knot_stop(1);
      }

      if(keyvalue.bit_square == 1)
      {
        robot->knot_run(2, MIN_DUTY, action_running_time);
      }
      else if(keyvalue.bit_circle == 1)
      {
        robot->knot_run(2, MAX_DUTY, action_running_time);
      }
      else if(keyvalue.bit_square == 0 || keyvalue.bit_circle == 0)
      {
        robot->knot_stop(2);
      }

      if(keyvalue.bit_r1 == 1)
      {
        robot->knot_run(3, MAX_DUTY, action_running_time);
      }
      else if(keyvalue.bit_r2 == 1)
      {
        robot->knot_run(3, MIN_DUTY, action_running_time);
      }
      else if(keyvalue.bit_r1 == 0 || keyvalue.bit_r2 == 0)
      {
        robot->knot_stop(3);
      }

      if(keyvalue.bit_l1 == 1)
      {
        robot->knot_run(4, MAX_DUTY, action_running_time);
      }
      else if(keyvalue.bit_l2 == 1)
      {
        robot->knot_run(4, MIN_DUTY, action_running_time);
      }
      else if(keyvalue.bit_l1 == 0 || keyvalue.bit_l2 == 0)
      {
        robot->knot_stop(4);
      }
      
      if(keyvalue.bit_up == 1)
      {
        robot->knot_run(5, MAX_DUTY, action_running_time);
      }
      else if(keyvalue.bit_down == 1)
      {
        robot->knot_run(5, MIN_DUTY, action_running_time);
      }
      else if(keyvalue.bit_up == 0 || keyvalue.bit_down == 0)
      {
        robot->knot_stop(5);
      }

      if(keyvalue.bit_left == 1)
      {
        robot->knot_run(6, MAX_DUTY, action_running_time);
      }
      else if(keyvalue.bit_right == 1)
      {
        robot->knot_run(6, MIN_DUTY, action_running_time);
      }
      else if(keyvalue.bit_left == 0 || keyvalue.bit_right == 0)
      {
        robot->knot_stop(6);
      }

      if(keyvalue.bit_start == 1)
      {
        robot->reset();
        delay(800);
      }

      if(keyvalue.bit_select == 1 && keyvalue.bit_up == 1)
      {
        buzzer->blink(1500 , 100, 100, 1);
        while(1)
        {
          if(robot->action_group_run(0, 1))
          {
            robot->action_group_reset();
            break;
          }
        }
      }
      
      if(keyvalue.bit_select == 1 && keyvalue.bit_down == 1)
      {
        buzzer->blink(1500 , 100, 100, 1);
        while(1)
        {
          if(robot->action_group_run(1, 1))
          {
            robot->action_group_reset();
            break;
          }
        }
      }
      
      if(keyvalue.bit_select == 1 && keyvalue.bit_left == 1)
      {
        buzzer->blink(1500 , 100, 100, 1);
        while(1)
        {
          if(robot->action_group_run(2, 1))
          {
            robot->action_group_reset();
            break;
          }
        }
      }		

      if(keyvalue.bit_select == 1 && keyvalue.bit_right == 1)
      {
        buzzer->blink(1500 , 100, 100, 1);
        while(1)
        {
          if(robot->action_group_run(3, 1))
          {
            robot->action_group_reset();
            break;
          }
        }
      }	

      if(keyvalue.bit_select == 1 && keyvalue.bit_l1 == 1)
      {
        buzzer->blink(1500 , 100, 100, 1);
        while(1)
        {
          if(robot->action_group_run(4, 1))
          {
            robot->action_group_reset();
            break;
          }
        }
      }	
      
      if(keyvalue.bit_select == 1 && keyvalue.bit_l2 == 1)
      {
        buzzer->blink(1500 , 100, 100, 1);
        while(1)
        {
          if(robot->action_group_run(5, 1))
          {
            robot->action_group_reset();
            break;
          }
        }
      }	
      
      if(keyvalue.bit_select == 1 && keyvalue.bit_triangle == 1)
      {
        buzzer->blink(1500 , 100, 100, 1);
        while(1)
        {
          if(robot->action_group_run(6, 1))
          {
            robot->action_group_reset();
            break;
          }
        }
      }
      
      if(keyvalue.bit_select == 1 && keyvalue.bit_cross == 1)
      {
        buzzer->blink(1500 , 100, 100, 1);
        while(1)
        {
          if(robot->action_group_run(7, 1))
          {
            robot->action_group_reset();
            break;
          }
        }
      }
      
      if(keyvalue.bit_select == 1 && keyvalue.bit_square == 1)
      {
        buzzer->blink(1500 , 100, 100, 1);
        while(1)
        {
          if(robot->action_group_run(8, 1))
          {
            robot->action_group_reset();
            break;
          }
        }
      }
      
      if(keyvalue.bit_select == 1 && keyvalue.bit_circle == 1)
      {
        buzzer->blink(1500 , 100, 100, 1);
        while(1)
        {
          if(robot->action_group_run(9, 1))
          {
            robot->action_group_reset();
            break;
          }
        }
      }

      if(keyvalue.bit_select == 1 && keyvalue.bit_r1 == 1)
      {
        buzzer->blink(1500 , 100, 100, 1);
        while(1)
        {
          if(robot->action_group_run(10, 1))
          {
            robot->action_group_reset();
            break;
          }
        }
      }
      
      if(keyvalue.bit_select == 1 && keyvalue.bit_r2 == 1)
      {
        buzzer->blink(1500 , 100, 100, 1);
        while(1)
        {
          if(robot->action_group_run(11, 1))
          {
            robot->action_group_reset();
            break;
          }
        }
      }
      
      if(keyvalue.bit_leftjoystick_press)
      {
        buzzer->blink(1500 , 100, 100, 1);
        led->blink(100, 100, 1);
        if(action_running_time > 400)
        {
          action_running_time  -= 200;
        }
      }
      
      if(keyvalue.bit_rightjoystick_press)
      {
        buzzer->blink(1500 , 100, 100, 1);
        led->blink(100, 100, 1);
        if(action_running_time < 10000)
        {
          action_running_time += 200;
        }
      }

      int speed = (uint32_t)keyvalue.left_joystick_x > 200 ? -20 : ((uint32_t)keyvalue.left_joystick_x < 50? 20 : 0);
      *stepper_run = speed;
    }
  }
  return 0;
}

(2) If the controller is in analog mode:

① If the “SELECT” button is not pressed but other buttons are pressed, the corresponding servos are controlled to move or stop.

② If the “SELECT” button is pressed together with other buttons, an action group is executed.

③ If the “START” button is pressed, a reset is triggered.

④ When the left or right joystick buttons are pressed down, the movement duration time is increased or decreased.

⑤ For the right joystick pushed left or right, moving the right joystick left or right controls the stepper motor inside the sliding rail, driving the sliding base carrying the robotic arm to move sideways. Detailed behaviors as described below:

⑥ When the right joystick is centered (x=127), the set_motor_move function is called to keep the stepper motor stopped.

⑦ When the right joystick is pushed left (x < 50), the step size stepper_run is set to -20, causing the stepper motor to rotate in one direction. Similarly, when the right joystick is pushed to the right, it causes the stepper motor to rotate in the opposite direction, with the step size stepper_run set to 20.

452
453
      int speed = (uint32_t)keyvalue.left_joystick_x > 200 ? -20 : ((uint32_t)keyvalue.left_joystick_x < 50? 20 : 0);
      *stepper_run = speed;

10.3 Sliding Rail + Color Sorting

10.3.1 Project Introduction

In this lesson, we use the WonderMV vision module to detect colored waste blocks. Based on the type of waste detected by the vision module, the sliding rail and robotic arm work together to sort wastes.

10.3.2 Project Process

10.3.3 Module Instruction

  • WonderMV Vision Module

The WonderMV module is equipped with a high-performance K210 chip and supports various AI vision functions such as color recognition, face detection, and identification through programming. This module features an IIC interface, making it easy to connect and exchange data with different controllers, providing an efficient solution for intelligent vision applications.

Module connection: As shown in the figure below, please connect the module to any IIC port marked in the red frame on the servo control board before starting the feature.

10.3.4 Program Download

  • WonderMV Program Download

Source Code

(1) Double-click to open the “CanMV IDE” development software, and click the “connect” button in the lower left corner.

(2) Select the corresponding port number, check “Advanced Settings”, and choose “Mode-3”.

(3) Click “OK” and wait for the connection.

(4) Once connected successfully, the icon in the lower left corner of the “CanMV IDE” will appear as shown below.

(5) Drag the program file from the same directory of this lesson into the code editor area of “CanMV IDE”. Click “Tools” in the toolbar, then select “Save the currently opened script as (main.py) to CanMV Cam”, as shown below.

(6) Next, click “Yes”.

(7) When the write process is successful, a prompt will appear, then click “OK”. Through above steps to save the MicroPython file to the K210 vision module.

  • ESP32 Program Download

Source Code

(1) Connect the core board to the computer using a USB cable.

(2) Locate the corresponding Arduino project file in the same directory as this document.

(3) Select the development board model when you open the program, and the specific model is shown in the figure below.

(4) Click “Compile” first, then click “Upload”. After the upload is completed, the program download is completed if the following interface appears in the output box below the software.

10.3.5 Project Outcome

When the vision module detects a waste block in front, the robotic arm will pick it up and sort it into different locations based on the type of waste, placing each block at a different distance from the detection point.

10.3.6 Program Brief Analysis

(1) The program imports the following libraries: config.h, Hiwonder.hpp, Robot_arm.hpp, stepper.hpp, and WonderMV.hpp. Among them, Hiwonder.hpp defines the LED, buzzer, and button objects. Robot_arm.hpp defines the robotic arm object. stepper.hpp defines the sliding rail object. WonderMV.hpp defines the vision module object.

1
2
3
4
5
#include "Config.h"
#include "Hiwonder.hpp"
#include "Robot_arm.hpp"
#include "stepper.hpp"
#include "WonderMV.hpp"

(2) LED, buzzer, button, robotic arm, IIC, vision module, and ultrasonic sensor objects are created, as well as a variable to store the vision recognition results.

 7
 8
 9
10
11
12
13
14
Led_t led_obj;
Buzzer_t buzzer_obj;
LeArm_t arm;
IIC iic;
Stepper stepper_obj;
WonderMV mv;

MV_RESULT_ST result;

(3) In the setup() function:

① first delay for 1000 ms (1 second), then power off the Bluetooth module. Next, initialize the robotic arm, LED, buzzer, I2C, sliding rail, and vision module objects, then open the serial port and set the baud rate to 9600.

② Then call the sliding rail object and set the step value to 1000. Then, the coordinate_set method of the robotic arm object is called to move the arm to the detected position.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void setup() {
  delay(1000);
  pinMode(IO_BLE_CTL, OUTPUT);
  digitalWrite(IO_BLE_CTL, LOW);  // When the Bluetooth control pin is set to low, power to the Bluetooth module is cut off (设置蓝牙控制引脚为低电平时断开蓝牙模块电源)

  arm.init();
  led_obj.init(IO_LED);
  buzzer_obj.init(IO_BUZZER);
  iic.init();
  stepper_obj.init(&iic);
  mv.init(&iic);
  stepper_obj.driver_mode(0x02);
  stepper_obj.reset();
  Serial.begin(9600);
  delay(2000);
  stepper_obj.set_step(1000);
  delay(1500);
  arm.coordinate_set(14, 0, -9, 0, -90, 90, 1000); //Identify position (识别位置)
  Serial.println("start");
}

(4) A run flag variable is also created.

38
uint16_t step_num = 0;

(5) In the main loop:

① First, the get_result method of the vision MV object is called to obtain the detected color data, which is then stored in result.

② Next, result.id is checked. If it is not 0, it indicates that a color has been detected. The program then waits for 3 seconds and retrieves the color data again. If result.id is still not 0, the blink method of the buzzer object is called to make a short beep. This double check of result.id is used to filter out false detections.

③ After that, the coordinate_set method of the robotic arm object is called to move the arm above the block, preparing for gripping. Then, the claw_set method is called to grip the block.

④ After gripping, the coordinate_set method is called again to move the arm back to its initial position. Next, based on the value of result.id (i.e., the recognized color), different step values are set for the sliding rail according to the detected color.

⑤ Then, the set_step method of the sliding rail object is called to move the sliding rail to the corresponding position. After that, the robotic arm is moved to the release position, the claw is opened to drop the block, and then the arm is lifted back to its initial position.

⑥ Finally, the sliding rail is moved back, and the robotic arm is returned to the detection position.

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
void loop() {
  mv.get_result(COLOR_REG , &result);
  if(result.id != 0)
  {
    delay(3000);
    mv.get_result(COLOR_REG , &result);
    if(result.id != 0)
    {
      buzzer_obj.blink(1500, 100, 100, 1);
      delay(500);
      arm.coordinate_set(15, 0, -13, 0, -90, 90, 1000); //Prepare to pick (准备夹取)
      delay(1500);
      arm.claw_set(30, 200); //Pick (夹取)
      delay(400);
      arm.coordinate_set(15.0f, 0, 15, 0, -90, 90, 1000); //Reset (复位)
      delay(1500);
      step_num = 1000+(result.id*1000);
      step_num = step_num > 5000 ? 5000 : step_num;
      stepper_obj.set_step(step_num); //Move to the corresponding position (运动到对应位置)
      delay(step_num);
      arm.coordinate_set(15, 0, -11, 0, -90, 90, 1000); //Prepare to place (准备放下)
      delay(1500);
      arm.claw_set(90, 200); //Place (放下)
      delay(400);
      arm.coordinate_set(15.0f, 0, 15, 0, -90, 90, 1000); //Reset (复位)
      delay(1500);
      stepper_obj.set_step(-step_num);
      delay(step_num);
      arm.coordinate_set(14, 0, -9, 0, -90, 90, 1000); //识别位置
      delay(1500);
      step_num = 0;
    }
  }
  delay(100);
}