1 /*
2 * Copyright (c) 2024, Fabian Blatz <fabianblatz@gmail.com>
3 * Copyright (c) 2024, Jilay Sandeep Pandya
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/shell/shell.h>
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/stepper.h>
11
12 #include <zephyr/logging/log.h>
13 LOG_MODULE_REGISTER(stepper_shell, CONFIG_STEPPER_LOG_LEVEL);
14
15 enum {
16 ARG_IDX_DEV = 1,
17 ARG_IDX_PARAM = 2,
18 ARG_IDX_VALUE = 3,
19 };
20
21 struct stepper_microstep_map {
22 const char *name;
23 enum stepper_micro_step_resolution microstep;
24 };
25
26 struct stepper_direction_map {
27 const char *name;
28 enum stepper_direction direction;
29 };
30
31 #define STEPPER_DIRECTION_MAP_ENTRY(_name, _dir) \
32 { \
33 .name = _name, \
34 .direction = _dir, \
35 }
36
37 #define STEPPER_MICROSTEP_MAP(_name, _microstep) \
38 { \
39 .name = _name, \
40 .microstep = _microstep, \
41 }
42
print_callback(const struct device * dev,const enum stepper_event event,void * user_data)43 static void print_callback(const struct device *dev, const enum stepper_event event,
44 void *user_data)
45 {
46 const struct shell *sh = user_data;
47 if (!sh) {
48 return;
49 }
50
51 switch (event) {
52 case STEPPER_EVENT_STEPS_COMPLETED:
53 shell_info(sh, "%s: Steps completed.", dev->name);
54 break;
55 case STEPPER_EVENT_STALL_DETECTED:
56 shell_info(sh, "%s: Stall detected.", dev->name);
57 break;
58 case STEPPER_EVENT_LEFT_END_STOP_DETECTED:
59 shell_info(sh, "%s: Left limit switch pressed.", dev->name);
60 break;
61 case STEPPER_EVENT_RIGHT_END_STOP_DETECTED:
62 shell_info(sh, "%s: Right limit switch pressed.", dev->name);
63 break;
64 case STEPPER_EVENT_STOPPED:
65 shell_info(sh, "%s: Stepper stopped.", dev->name);
66 break;
67 default:
68 shell_info(sh, "%s: Unknown signal received.", dev->name);
69 break;
70 }
71 }
72
device_is_stepper(const struct device * dev)73 static bool device_is_stepper(const struct device *dev)
74 {
75 return DEVICE_API_IS(stepper, dev);
76 }
77
78 static const struct stepper_direction_map stepper_direction_map[] = {
79 STEPPER_DIRECTION_MAP_ENTRY("positive", STEPPER_DIRECTION_POSITIVE),
80 STEPPER_DIRECTION_MAP_ENTRY("negative", STEPPER_DIRECTION_NEGATIVE),
81 };
82
83 static const struct stepper_microstep_map stepper_microstep_map[] = {
84 STEPPER_MICROSTEP_MAP("1", STEPPER_MICRO_STEP_1),
85 STEPPER_MICROSTEP_MAP("2", STEPPER_MICRO_STEP_2),
86 STEPPER_MICROSTEP_MAP("4", STEPPER_MICRO_STEP_4),
87 STEPPER_MICROSTEP_MAP("8", STEPPER_MICRO_STEP_8),
88 STEPPER_MICROSTEP_MAP("16", STEPPER_MICRO_STEP_16),
89 STEPPER_MICROSTEP_MAP("32", STEPPER_MICRO_STEP_32),
90 STEPPER_MICROSTEP_MAP("64", STEPPER_MICRO_STEP_64),
91 STEPPER_MICROSTEP_MAP("128", STEPPER_MICRO_STEP_128),
92 STEPPER_MICROSTEP_MAP("256", STEPPER_MICRO_STEP_256),
93 };
94
cmd_stepper_direction(size_t idx,struct shell_static_entry * entry)95 static void cmd_stepper_direction(size_t idx, struct shell_static_entry *entry)
96 {
97 if (idx < ARRAY_SIZE(stepper_direction_map)) {
98 entry->syntax = stepper_direction_map[idx].name;
99 } else {
100 entry->syntax = NULL;
101 }
102 entry->handler = NULL;
103 entry->help = "Stepper direction";
104 entry->subcmd = NULL;
105 }
106
107 SHELL_DYNAMIC_CMD_CREATE(dsub_stepper_direction, cmd_stepper_direction);
108
cmd_stepper_microstep(size_t idx,struct shell_static_entry * entry)109 static void cmd_stepper_microstep(size_t idx, struct shell_static_entry *entry)
110 {
111 if (idx < ARRAY_SIZE(stepper_microstep_map)) {
112 entry->syntax = stepper_microstep_map[idx].name;
113 } else {
114 entry->syntax = NULL;
115 }
116 entry->handler = NULL;
117 entry->help = "Stepper microstep resolution";
118 entry->subcmd = NULL;
119 }
120
121 SHELL_DYNAMIC_CMD_CREATE(dsub_stepper_microstep, cmd_stepper_microstep);
122
cmd_pos_stepper_motor_name(size_t idx,struct shell_static_entry * entry)123 static void cmd_pos_stepper_motor_name(size_t idx, struct shell_static_entry *entry)
124 {
125 const struct device *dev = shell_device_filter(idx, device_is_stepper);
126
127 entry->syntax = (dev != NULL) ? dev->name : NULL;
128 entry->handler = NULL;
129 entry->help = "List Devices";
130 entry->subcmd = NULL;
131 }
132
133 SHELL_DYNAMIC_CMD_CREATE(dsub_pos_stepper_motor_name, cmd_pos_stepper_motor_name);
134
cmd_pos_stepper_motor_name_dir(size_t idx,struct shell_static_entry * entry)135 static void cmd_pos_stepper_motor_name_dir(size_t idx, struct shell_static_entry *entry)
136 {
137 const struct device *dev = shell_device_filter(idx, device_is_stepper);
138
139 if (dev != NULL) {
140 entry->syntax = dev->name;
141 } else {
142 entry->syntax = NULL;
143 }
144 entry->handler = NULL;
145 entry->help = "List Devices";
146 entry->subcmd = &dsub_stepper_direction;
147 }
148
149 SHELL_DYNAMIC_CMD_CREATE(dsub_pos_stepper_motor_name_dir, cmd_pos_stepper_motor_name_dir);
150
cmd_pos_stepper_motor_name_microstep(size_t idx,struct shell_static_entry * entry)151 static void cmd_pos_stepper_motor_name_microstep(size_t idx, struct shell_static_entry *entry)
152 {
153 const struct device *dev = shell_device_filter(idx, device_is_stepper);
154
155 if (dev != NULL) {
156 entry->syntax = dev->name;
157 } else {
158 entry->syntax = NULL;
159 }
160 entry->handler = NULL;
161 entry->help = "List Devices";
162 entry->subcmd = &dsub_stepper_microstep;
163 }
164
165 SHELL_DYNAMIC_CMD_CREATE(dsub_pos_stepper_motor_name_microstep,
166 cmd_pos_stepper_motor_name_microstep);
167
parse_device_arg(const struct shell * sh,char ** argv,const struct device ** dev)168 static int parse_device_arg(const struct shell *sh, char **argv, const struct device **dev)
169 {
170 *dev = shell_device_get_binding(argv[ARG_IDX_DEV]);
171 if (!*dev) {
172 shell_error(sh, "Stepper device %s not found", argv[ARG_IDX_DEV]);
173 return -ENODEV;
174 }
175 return 0;
176 }
177
cmd_stepper_enable(const struct shell * sh,size_t argc,char ** argv)178 static int cmd_stepper_enable(const struct shell *sh, size_t argc, char **argv)
179 {
180 const struct device *dev;
181 int err = 0;
182 bool enable = shell_strtobool(argv[ARG_IDX_PARAM], 10, &err);
183
184 if (err < 0) {
185 return err;
186 }
187
188 err = parse_device_arg(sh, argv, &dev);
189 if (err < 0) {
190 return err;
191 }
192
193 err = stepper_enable(dev, enable);
194 if (err) {
195 shell_error(sh, "Error: %d", err);
196 }
197
198 return err;
199 }
200
cmd_stepper_stop(const struct shell * sh,size_t argc,char ** argv)201 static int cmd_stepper_stop(const struct shell *sh, size_t argc, char **argv)
202 {
203 const struct device *dev;
204 int err = 0;
205
206 err = parse_device_arg(sh, argv, &dev);
207 if (err < 0) {
208 return err;
209 }
210
211 err = stepper_stop(dev);
212 if (err) {
213 shell_error(sh, "Error: %d", err);
214 return err;
215 }
216
217 err = stepper_set_event_callback(dev, print_callback, (void *)sh);
218 if (err != 0) {
219 shell_error(sh, "Failed to set callback: %d", err);
220 }
221
222 return err;
223 }
224
cmd_stepper_move_by(const struct shell * sh,size_t argc,char ** argv)225 static int cmd_stepper_move_by(const struct shell *sh, size_t argc, char **argv)
226 {
227 const struct device *dev;
228 int err = 0;
229
230 int32_t micro_steps = shell_strtol(argv[ARG_IDX_PARAM], 10, &err);
231
232 if (err < 0) {
233 return err;
234 }
235
236 err = parse_device_arg(sh, argv, &dev);
237 if (err < 0) {
238 return err;
239 }
240
241 err = stepper_set_event_callback(dev, print_callback, (void *)sh);
242 if (err != 0) {
243 shell_error(sh, "Failed to set callback: %d", err);
244 }
245
246 err = stepper_move_by(dev, micro_steps);
247 if (err) {
248 shell_error(sh, "Error: %d", err);
249 }
250
251 return err;
252 }
253
cmd_stepper_set_microstep_interval(const struct shell * sh,size_t argc,char ** argv)254 static int cmd_stepper_set_microstep_interval(const struct shell *sh, size_t argc, char **argv)
255 {
256 const struct device *dev;
257 int err = 0;
258 uint64_t step_interval = shell_strtoull(argv[ARG_IDX_PARAM], 10, &err);
259
260 if (err < 0) {
261 return err;
262 }
263
264 err = parse_device_arg(sh, argv, &dev);
265 if (err < 0) {
266 return err;
267 }
268
269 err = stepper_set_microstep_interval(dev, step_interval);
270 if (err) {
271 shell_error(sh, "Error: %d", err);
272 }
273
274 return err;
275 }
276
cmd_stepper_set_micro_step_res(const struct shell * sh,size_t argc,char ** argv)277 static int cmd_stepper_set_micro_step_res(const struct shell *sh, size_t argc, char **argv)
278 {
279 const struct device *dev;
280 enum stepper_micro_step_resolution resolution;
281 int err = -EINVAL;
282
283 for (int i = 0; i < ARRAY_SIZE(stepper_microstep_map); i++) {
284 if (strcmp(argv[ARG_IDX_PARAM], stepper_microstep_map[i].name) == 0) {
285 resolution = stepper_microstep_map[i].microstep;
286 err = 0;
287 break;
288 }
289 }
290 if (err != 0) {
291 shell_error(sh, "Invalid microstep value %s", argv[ARG_IDX_PARAM]);
292 return err;
293 }
294
295 err = parse_device_arg(sh, argv, &dev);
296 if (err < 0) {
297 return err;
298 }
299
300 err = stepper_set_micro_step_res(dev, resolution);
301 if (err) {
302 shell_error(sh, "Error: %d", err);
303 }
304
305 return err;
306 }
307
cmd_stepper_get_micro_step_res(const struct shell * sh,size_t argc,char ** argv)308 static int cmd_stepper_get_micro_step_res(const struct shell *sh, size_t argc, char **argv)
309 {
310 const struct device *dev;
311 int err;
312 enum stepper_micro_step_resolution micro_step_res;
313
314 err = parse_device_arg(sh, argv, &dev);
315 if (err < 0) {
316 return err;
317 }
318
319 err = stepper_get_micro_step_res(dev, µ_step_res);
320 if (err < 0) {
321 shell_warn(sh, "Failed to get micro-step resolution: %d", err);
322 } else {
323 shell_print(sh, "Micro-step Resolution: %d", micro_step_res);
324 }
325
326 return err;
327 }
328
cmd_stepper_set_reference_position(const struct shell * sh,size_t argc,char ** argv)329 static int cmd_stepper_set_reference_position(const struct shell *sh, size_t argc, char **argv)
330 {
331 const struct device *dev;
332 int err = 0;
333 int32_t position = shell_strtol(argv[ARG_IDX_PARAM], 10, &err);
334
335 if (err < 0) {
336 return err;
337 }
338
339 err = parse_device_arg(sh, argv, &dev);
340 if (err < 0) {
341 return err;
342 }
343
344 err = stepper_set_reference_position(dev, position);
345 if (err) {
346 shell_error(sh, "Error: %d", err);
347 }
348
349 return err;
350 }
351
cmd_stepper_get_actual_position(const struct shell * sh,size_t argc,char ** argv)352 static int cmd_stepper_get_actual_position(const struct shell *sh, size_t argc, char **argv)
353 {
354 const struct device *dev;
355 int err;
356 int32_t actual_position;
357
358 err = parse_device_arg(sh, argv, &dev);
359 if (err < 0) {
360 return err;
361 }
362
363 err = stepper_get_actual_position(dev, &actual_position);
364 if (err < 0) {
365 shell_warn(sh, "Failed to get actual position: %d", err);
366 } else {
367 shell_print(sh, "Actual Position: %d", actual_position);
368 }
369
370 return err;
371 }
372
cmd_stepper_move_to(const struct shell * sh,size_t argc,char ** argv)373 static int cmd_stepper_move_to(const struct shell *sh, size_t argc, char **argv)
374 {
375 const struct device *dev;
376 int err = 0;
377 const int32_t position = shell_strtol(argv[ARG_IDX_PARAM], 10, &err);
378
379 if (err < 0) {
380 return err;
381 }
382
383 err = parse_device_arg(sh, argv, &dev);
384 if (err < 0) {
385 return err;
386 }
387
388 err = stepper_set_event_callback(dev, print_callback, (void *)sh);
389 if (err != 0) {
390 shell_error(sh, "Failed to set callback: %d", err);
391 }
392
393 err = stepper_move_to(dev, position);
394 if (err) {
395 shell_error(sh, "Error: %d", err);
396 }
397
398 return err;
399 }
400
cmd_stepper_run(const struct shell * sh,size_t argc,char ** argv)401 static int cmd_stepper_run(const struct shell *sh, size_t argc, char **argv)
402 {
403 const struct device *dev;
404 int err = -EINVAL;
405 enum stepper_direction direction = STEPPER_DIRECTION_POSITIVE;
406
407 for (int i = 0; i < ARRAY_SIZE(stepper_direction_map); i++) {
408 if (strcmp(argv[ARG_IDX_PARAM], stepper_direction_map[i].name) == 0) {
409 direction = stepper_direction_map[i].direction;
410 err = 0;
411 break;
412 }
413 }
414 if (err != 0) {
415 shell_error(sh, "Invalid direction %s", argv[ARG_IDX_PARAM]);
416 return err;
417 }
418
419 err = parse_device_arg(sh, argv, &dev);
420 if (err < 0) {
421 return err;
422 }
423
424 err = stepper_set_event_callback(dev, print_callback, (void *)sh);
425 if (err != 0) {
426 shell_error(sh, "Failed to set callback: %d", err);
427 }
428
429 err = stepper_run(dev, direction);
430 if (err) {
431 shell_error(sh, "Error: %d", err);
432 return err;
433 }
434
435 return 0;
436 }
437
cmd_stepper_info(const struct shell * sh,size_t argc,char ** argv)438 static int cmd_stepper_info(const struct shell *sh, size_t argc, char **argv)
439 {
440 const struct device *dev;
441 int err;
442 bool is_moving;
443 int32_t actual_position;
444 enum stepper_micro_step_resolution micro_step_res;
445
446 err = parse_device_arg(sh, argv, &dev);
447 if (err < 0) {
448 return err;
449 }
450
451 shell_print(sh, "Stepper Info:");
452 shell_print(sh, "Device: %s", dev->name);
453
454 err = stepper_get_actual_position(dev, &actual_position);
455 if (err < 0) {
456 shell_warn(sh, "Failed to get actual position: %d", err);
457 } else {
458 shell_print(sh, "Actual Position: %d", actual_position);
459 }
460
461 err = stepper_get_micro_step_res(dev, µ_step_res);
462 if (err < 0) {
463 shell_warn(sh, "Failed to get micro-step resolution: %d", err);
464 } else {
465 shell_print(sh, "Micro-step Resolution: %d", micro_step_res);
466 }
467
468 err = stepper_is_moving(dev, &is_moving);
469 if (err < 0) {
470 shell_warn(sh, "Failed to check if the motor is moving: %d", err);
471 } else {
472 shell_print(sh, "Is Moving: %s", is_moving ? "Yes" : "No");
473 }
474
475 return 0;
476 }
477
478 SHELL_STATIC_SUBCMD_SET_CREATE(
479 stepper_cmds,
480 SHELL_CMD_ARG(enable, &dsub_pos_stepper_motor_name, "<device> <on/off>", cmd_stepper_enable,
481 3, 0),
482 SHELL_CMD_ARG(set_micro_step_res, &dsub_pos_stepper_motor_name_microstep,
483 "<device> <resolution>", cmd_stepper_set_micro_step_res, 3, 0),
484 SHELL_CMD_ARG(get_micro_step_res, &dsub_pos_stepper_motor_name, "<device>",
485 cmd_stepper_get_micro_step_res, 2, 0),
486 SHELL_CMD_ARG(set_reference_position, &dsub_pos_stepper_motor_name, "<device> <position>",
487 cmd_stepper_set_reference_position, 3, 0),
488 SHELL_CMD_ARG(get_actual_position, &dsub_pos_stepper_motor_name, "<device>",
489 cmd_stepper_get_actual_position, 2, 0),
490 SHELL_CMD_ARG(set_microstep_interval, &dsub_pos_stepper_motor_name,
491 "<device> <microstep_interval_ns>", cmd_stepper_set_microstep_interval, 3, 0),
492 SHELL_CMD_ARG(move_by, &dsub_pos_stepper_motor_name, "<device> <microsteps>",
493 cmd_stepper_move_by, 3, 0),
494 SHELL_CMD_ARG(move_to, &dsub_pos_stepper_motor_name, "<device> <microsteps>",
495 cmd_stepper_move_to, 3, 0),
496 SHELL_CMD_ARG(run, &dsub_pos_stepper_motor_name_dir, "<device> <direction>",
497 cmd_stepper_run, 3, 0),
498 SHELL_CMD_ARG(stop, &dsub_pos_stepper_motor_name, "<device>", cmd_stepper_stop, 2, 0),
499 SHELL_CMD_ARG(info, &dsub_pos_stepper_motor_name, "<device>", cmd_stepper_info, 2, 0),
500 SHELL_SUBCMD_SET_END);
501
502 SHELL_CMD_REGISTER(stepper, &stepper_cmds, "Stepper motor commands", NULL);
503