Skip to content

Commit 55deb91

Browse files
authored
Merge pull request #5 from stack-chan/develop
Support Dynamixel XL330
2 parents b47c68a + ac0d04a commit 55deb91

File tree

8 files changed

+201
-33
lines changed

8 files changed

+201
-33
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,8 @@
3030
*.exe
3131
*.out
3232
*.app
33+
34+
lib/
35+
.vscode/
36+
.pio/
37+
build/

README.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,12 @@ platformio.iniのlib_depsに下記の記述を追加してください。(その
77

88
```
99
lib_deps =
10-
11-
arminjo/ServoEasing@^2.4.0
12-
madhephaestus/ESP32Servo @ 0.13.0
13-
bblanchon/ArduinoJson @ ^6
14-
tobozo/YAMLDuino
1510
https://github.com/mongonta0716/stackchan-arduino.git
16-
https://github.com/mongonta0716/SCServo
1711
lib_ldf_mode = deep ; これを忘れるとリンクエラーになります。
1812
```
1913

2014
# 設定ファイル
15+
設定内容は各ファイルに詳しくコメントしています。
2116
- [SC_BasicConfig.yaml](./data/yaml/SC_BasicConfig.yaml)<br>スタックチャンを起動するために必要な基本的なパラメータを集めています。
2217
- [SC_SecConfig.yaml](./data/yaml/SC_SecConfig.yaml)<br>個人情報用の設定ファイルです。(WiFiやAPIKey等)
2318
- [SC_ExConfig.yaml](./data/yaml/SC_ExConfig.yaml)<br>アプリケーション側で、設定を追加したいときに使用します。使い方はexamples/Advanceを参照してください。

data/yaml/SC_BasicConfig.yaml

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,30 @@ servo:
33
# ServoPin
44
# Core1 PortA X:22,Y:21 PortC X:16,Y:17
55
# Core2 PortA X:33,Y:32 PortC X:13,Y:14
6+
# CoreS3 PortA X:2, Y:1 PortB X:9, Y:8 PortC X:17, Y:18
67
# Stack-chanPCB Core1 X:5,Y:2 Core2 X:19,Y27
7-
# When using SCS0009, x:RX, y:TX (not used).(StackchanRT Version:Core1 x16,y17, Core2: x13,y14)
8+
# When using SCS0009 or Dynamixel XL330, x:RX, y:TX (not used).(StackchanRT Version:Core1 x16,y17, Core2: x13,y14)
89
x: 17
910
y: 18
1011
offset:
1112
# Specified by +- from 90 degree during servo initialization
1213
x: 0
1314
y: 0
15+
center:
16+
# サーボの初期位置
17+
# SG90: x:90 y:90
18+
# SCS0009: x:150, y:150
19+
# Dynamixel XL330: x:180, y:270
20+
x: 90
21+
y: 90
22+
lower_limit:
23+
# 可動範囲の下限(下限と言っても取り付け方により逆の場合あり, 値の小さい方を指定。)
24+
x: 0
25+
y: 60
26+
upper_limit:
27+
# 可動範囲の上限(上限と言っても取り付け方により逆の場合もあり, 値の大きい方を指定。)
28+
x: 90
29+
y: 100
1430
speed:
1531
normal_mode:
1632
interval_min: 3000
@@ -41,9 +57,11 @@ balloon:
4157
led_lr: 0 # 0:stereo, 1:left_only, 2:right_only
4258
led_pin: 15 # GoBottom1:15 GoBottom2:25
4359
takao_base: false # Whether to use takaobase to feed power from the rear connector.(Stack-chan_Takao_Base https://ssci.to/8905)
44-
servo_type: "PWM" # "PWM": SG90PWMServo, "SCS": Feetech SCS0009
45-
extend_config_filename: "/yaml/SC_ExConfig.yaml" # Configuration file for the application.
46-
extend_config_filesize: 2048 # Buffer size for feature extensions
47-
secret_config_filename: "/yaml/SC_SecConfig.yaml" # Configuration file for the File for personal information.
48-
secret_config_filesize: 2048 # Buffer size for personal information.
49-
secret_info_show: true # Whether personal information is output to the log or not.
60+
servo_type: "PWM" # "PWM": SG90PWMServo, "SCS": Feetech SCS0009 "DYN_XL330": Dynamixel XL330
61+
62+
# 下記は拡張用の設定ファイルを使用する場合に設定します。(初期値はありません。)
63+
extend_config_filename: "" # "/yaml/SC_ExConfig.yaml" # Configuration file for the application.
64+
extend_config_filesize: 0 # 2048 # Buffer size for feature extensions
65+
secret_config_filename: "" # "/yaml/SC_SecConfig.yaml" # Configuration file for the File for personal information.
66+
secret_config_filesize: 0 # 2048 # Buffer size for personal information.
67+
secret_info_show: true # Whether personal information is output to the log or not.

library.json

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,42 @@
1515
}
1616
],
1717
"license": "MIT",
18-
"dependencies": {
19-
},
18+
"dependencies": [
19+
{
20+
"owner": "M5Stack",
21+
"name": "M5Unified",
22+
"version": ">=0.1.16"
23+
},
24+
{
25+
"owner": "madhephaestus",
26+
"name": "ESP32Servo",
27+
"version": "0.13.0"
28+
},
29+
{
30+
"owner": "arminjo",
31+
"name": "ServoEasing",
32+
"version": "3.1.0"
33+
},
34+
{
35+
"owner": "bblanchon",
36+
"name": "ArduinoJson",
37+
"version": "^6"
38+
},
39+
{
40+
"owner": "robotis-git",
41+
"name": "Dynamixel2Arduino",
42+
"version": "^0.7.0"
43+
},
44+
{
45+
"owner": "tobozo",
46+
"name": "YAMLDuino",
47+
"version": "1.4.2"
48+
},
49+
{
50+
"name": "SCServo",
51+
"version": "https://github.com/mongonta0716/SCServo.git"
52+
}
53+
],
2054
"version": "0.0.1",
2155
"frameworks": "arduino",
2256
"platforms": "espressif32"

platformoio.ini

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
; PlatformIO Project Configuration File
2+
;
3+
; Build options: build flags, source filter
4+
; Upload options: custom upload port, speed and extra flags
5+
; Library options: dependencies, extra library storages
6+
; Advanced options: extra scripting
7+
;
8+
; Please visit documentation for the other options and examples
9+
; http://docs.platformio.org/page/projectconf.html
10+
11+
[platformio]
12+
default_envs = m5stack-core
13+
14+
[env]
15+
platform = espressif32
16+
framework = arduino
17+
monitor_speed = 115200
18+
build_flags =
19+
-Llib/
20+
21+
[env:m5stack-core]
22+
board = m5stack-core-esp32
23+
lib_deps = m5stack/M5Unified
24+
25+
[env:m5stack-core-with-aquestalk]
26+
board = m5stack-core-esp32
27+
lib_deps = m5stack/M5Unified
28+
29+
[env:m5stack-fire]
30+
board = m5stack-fire
31+
lib_deps =
32+
m5stack/M5Unified
33+
34+
[env:m5stack-core2]
35+
board = m5stack-core2
36+
lib_deps =
37+
m5stack/M5Unified

src/Stackchan_servo.cpp

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,19 @@
33

44
#include <ServoEasing.hpp>
55

6-
long convertSCS0009Pos(int16_t degree) {
6+
static long convertSCS0009Pos(int16_t degree) {
77
//Serial.printf("Degree: %d\n", degree);
88
return map(degree, 0, 300, 1023, 0);
99
}
1010

11+
static long convertDYNIXELXL330(int16_t degree) {
12+
M5_LOGI("Degree: %d\n", degree);
13+
14+
long ret = map(degree, 0, 360, 0, 4095);
15+
M5_LOGI("Position: %d\n", ret);
16+
return ret;
17+
}
18+
1119
// シリアルサーボ用のEasing関数
1220
float quadraticEaseInOut(float p) {
1321
//return p;
@@ -21,20 +29,45 @@ float quadraticEaseInOut(float p) {
2129
}
2230
}
2331

32+
2433
StackchanSERVO::StackchanSERVO() {}
2534

2635
StackchanSERVO::~StackchanSERVO() {}
2736

2837

2938
void StackchanSERVO::attachServos() {
30-
if (_servo_type == SCS) {
39+
if (_servo_type == ServoType::SCS) {
3140
// SCS0009
3241
Serial2.begin(1000000, SERIAL_8N1, _init_param.servo[AXIS_X].pin, _init_param.servo[AXIS_Y].pin);
3342
_sc.pSerial = &Serial2;
3443
_sc.WritePos(AXIS_X + 1, convertSCS0009Pos(_init_param.servo[AXIS_X].start_degree + _init_param.servo[AXIS_X].offset), 1000);
3544
_sc.WritePos(AXIS_Y + 1, convertSCS0009Pos(_init_param.servo[AXIS_Y].start_degree + _init_param.servo[AXIS_Y].offset), 1000);
3645
vTaskDelay(1000/portTICK_PERIOD_MS);
3746

47+
} else if (_servo_type == ServoType::DYN_XL330) {
48+
M5_LOGI("DYN_XL330");
49+
Serial2.begin(1000000, SERIAL_8N1, _init_param.servo[AXIS_X].pin, _init_param.servo[AXIS_Y].pin);
50+
_dxl = Dynamixel2Arduino(Serial2);
51+
_dxl.begin(1000000);
52+
_dxl.setPortProtocolVersion(DXL_PROTOCOL_VERSION);
53+
_dxl.ping(AXIS_X + 1);
54+
_dxl.ping(AXIS_Y + 1);
55+
_dxl.setOperatingMode(AXIS_X + 1, OP_POSITION);
56+
_dxl.setOperatingMode(AXIS_Y + 1, OP_POSITION);
57+
_dxl.writeControlTableItem(DRIVE_MODE, AXIS_X + 1, 4); // Velocityのパラメータを移動時間(msec)で指定するモードに変更
58+
_dxl.writeControlTableItem(DRIVE_MODE, AXIS_Y + 1, 4); // Velocityのパラメータを移動時間(msec)で指定するモードに変更
59+
_dxl.torqueOn(AXIS_X + 1);
60+
delay(10); // ここでWaitを入れないと、Y(tilt)サーボが動かない場合がある。
61+
_dxl.torqueOn(AXIS_Y + 1);
62+
delay(100);
63+
_dxl.writeControlTableItem(PROFILE_VELOCITY, AXIS_X + 1, 1000);
64+
_dxl.writeControlTableItem(PROFILE_VELOCITY, AXIS_Y + 1, 1000);
65+
delay(100);
66+
_dxl.setGoalPosition(AXIS_X + 1, 2048);
67+
_dxl.setGoalPosition(AXIS_Y + 1, 3073);
68+
//_dxl.torqueOff(AXIS_X + 1);
69+
//_dxl.torqueOff(AXIS_Y + 1);
70+
3871
} else {
3972
// SG90 PWM
4073
if (_servo_x.attach(_init_param.servo[AXIS_X].pin,
@@ -47,7 +80,7 @@ void StackchanSERVO::attachServos() {
4780
_init_param.servo[AXIS_Y].start_degree + _init_param.servo[AXIS_Y].offset,
4881
DEFAULT_MICROSECONDS_FOR_0_DEGREE,
4982
DEFAULT_MICROSECONDS_FOR_180_DEGREE)) {
50-
Serial.print("Error attaching servo x");
83+
Serial.print("Error attaching servo y");
5184
}
5285

5386
_servo_x.setEasingType(EASE_QUADRATIC_IN_OUT);
@@ -81,6 +114,14 @@ void StackchanSERVO::moveX(int x, uint32_t millis_for_move) {
81114
_isMoving = true;
82115
vTaskDelay(millis_for_move/portTICK_PERIOD_MS);
83116
_isMoving = false;
117+
} else if (_servo_type == ServoType::DYN_XL330) {
118+
_dxl.writeControlTableItem(PROFILE_VELOCITY, AXIS_X + 1, millis_for_move);
119+
vTaskDelay(10/portTICK_PERIOD_MS);
120+
_dxl.setGoalPosition(AXIS_X + 1, convertDYNIXELXL330(x + _init_param.servo[AXIS_X].offset));
121+
vTaskDelay(10/portTICK_PERIOD_MS);
122+
_isMoving = true;
123+
vTaskDelay(millis_for_move/portTICK_PERIOD_MS);
124+
_isMoving = false;
84125
} else {
85126
if (millis_for_move == 0) {
86127
_servo_x.easeTo(x + _init_param.servo[AXIS_X].offset);
@@ -100,11 +141,19 @@ void StackchanSERVO::moveX(servo_param_s servo_param_x) {
100141
}
101142

102143
void StackchanSERVO::moveY(int y, uint32_t millis_for_move) {
103-
if (_servo_type == SCS) {
144+
if (_servo_type == ServoType::SCS) {
104145
_sc.WritePos(AXIS_Y + 1, convertSCS0009Pos(y + _init_param.servo[AXIS_Y].offset), millis_for_move);
105146
_isMoving = true;
106147
vTaskDelay(millis_for_move/portTICK_PERIOD_MS);
107148
_isMoving = false;
149+
} else if (_servo_type == ServoType::DYN_XL330) {
150+
_dxl.writeControlTableItem(PROFILE_VELOCITY, AXIS_Y + 1, millis_for_move);
151+
vTaskDelay(10/portTICK_PERIOD_MS);
152+
_dxl.setGoalPosition(AXIS_Y + 1, convertDYNIXELXL330(y + _init_param.servo[AXIS_Y].offset)); // RT版に合わせて+180°しています。
153+
vTaskDelay(10/portTICK_PERIOD_MS);
154+
_isMoving = true;
155+
vTaskDelay(millis_for_move/portTICK_PERIOD_MS);
156+
_isMoving = false;
108157
} else {
109158
if (millis_for_move == 0) {
110159
_servo_y.easeTo(y + _init_param.servo[AXIS_Y].offset);
@@ -123,7 +172,7 @@ void StackchanSERVO::moveY(servo_param_s servo_param_y) {
123172
moveY(servo_param_y.degree, servo_param_y.millis_for_move);
124173
}
125174
void StackchanSERVO::moveXY(int x, int y, uint32_t millis_for_move) {
126-
if (_servo_type == SCS) {
175+
if (_servo_type == ServoType::SCS) {
127176
int increase_degree_x = x - _last_degree_x;
128177
int increase_degree_y = y - _last_degree_y;
129178
uint32_t division_time = millis_for_move / SERIAL_EASE_DIVISION;
@@ -137,6 +186,11 @@ void StackchanSERVO::moveXY(int x, int y, uint32_t millis_for_move) {
137186
//vTaskDelay(division_time);
138187
}
139188
_isMoving = false;
189+
} else if (_servo_type == ServoType::DYN_XL330) {
190+
_isMoving = true;
191+
_dxl.setGoalPosition(AXIS_X + 1, convertDYNIXELXL330(x + _init_param.servo[AXIS_X].offset));
192+
_dxl.setGoalPosition(AXIS_Y + 1, convertDYNIXELXL330(y + _init_param.servo[AXIS_Y].offset)); // RT版に合わせて+180°しています。
193+
_isMoving = false;
140194
} else {
141195
_servo_x.setEaseToD(x + _init_param.servo[AXIS_X].offset, millis_for_move);
142196
_servo_y.setEaseToD(y + _init_param.servo[AXIS_Y].offset, millis_for_move);
@@ -150,12 +204,17 @@ void StackchanSERVO::moveXY(int x, int y, uint32_t millis_for_move) {
150204
}
151205

152206
void StackchanSERVO::moveXY(servo_param_s servo_param_x, servo_param_s servo_param_y) {
153-
if (_servo_type == SCS) {
207+
if (_servo_type == ServoType::SCS) {
154208
_sc.WritePos(AXIS_X + 1, convertSCS0009Pos(servo_param_x.degree + servo_param_x.offset), servo_param_x.millis_for_move);
155209
_sc.WritePos(AXIS_Y + 1, convertSCS0009Pos(servo_param_y.degree + servo_param_y.offset), servo_param_y.millis_for_move);
156210
_isMoving = true;
157211
vTaskDelay(max(servo_param_x.millis_for_move, servo_param_y.millis_for_move)/portTICK_PERIOD_MS);
158212
_isMoving = false;
213+
} else if (_servo_type == ServoType::DYN_XL330) {
214+
_isMoving = true;
215+
_dxl.setGoalPosition(AXIS_X + 1, convertDYNIXELXL330(servo_param_x.degree + _init_param.servo[AXIS_X].offset));
216+
_dxl.setGoalPosition(AXIS_Y + 1, convertDYNIXELXL330(servo_param_y.degree + _init_param.servo[AXIS_Y].offset)); // RT版に合わせて+180°しています。
217+
_isMoving = false;
159218
} else {
160219
if (servo_param_x.degree != 0) {
161220
_servo_x.setEaseToD(servo_param_x.degree + servo_param_x.offset, servo_param_x.millis_for_move);
@@ -211,3 +270,4 @@ void StackchanSERVO::motion(Motion motion_number) {
211270
delay(1000);
212271
moveXY(_init_param.servo[AXIS_X].start_degree, _init_param.servo[AXIS_Y].degree, 1000);
213272
}
273+

src/Stackchan_servo.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
#include <ServoEasing.h>
1212
#include <SCServo.h>
1313
#include <M5Unified.h>
14+
#include <Dynamixel2Arduino.h>
15+
16+
using namespace ControlTableItem;
17+
1418
#define SERIAL_EASE_DIVISION 5 // シリアルサーボのEasing分割数
1519

1620
enum Motion {
@@ -28,8 +32,9 @@ enum ServoAxis {
2832
};
2933

3034
enum ServoType {
31-
PWM,
32-
SCS
35+
PWM, // SG90 PWM
36+
SCS, // Feetech SCS0009
37+
DYN_XL330 // Dynamixel XL330
3338
};
3439

3540
typedef struct ServoParam {
@@ -47,10 +52,14 @@ typedef struct StackchanServo{
4752
servo_param_s servo[2];
4853
} stackchan_servo_initial_param_s;
4954

55+
56+
const float DXL_PROTOCOL_VERSION = 2.0f;
57+
5058
class StackchanSERVO {
5159
protected:
5260
ServoType _servo_type;
5361
SCSCL _sc;
62+
Dynamixel2Arduino _dxl;
5463
ServoEasing _servo_x;
5564
ServoEasing _servo_y;
5665
void attachServos();
@@ -74,5 +83,4 @@ class StackchanSERVO {
7483
void motion(Motion motion_no);
7584
bool isMoving() { return _isMoving; }
7685
};
77-
7886
#endif // _STACKCHAN_SERVO_H_

0 commit comments

Comments
 (0)