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