Dual Brushless Motor ESC, 10-62V, up to 50A per motor. Motors ganged or independent, multiple control input methods, cycle-by-cycle current limit, speed mode and torque mode control. Motors tiny to kW. Speed limit and other parameters easily set in firmware. As used in 'The Brushless Brutalist' locomotive - www.jons-workshop.com. See also Model Engineer magazine June-October 2019.
Dependencies: mbed BufferedSerial Servo PCT2075 FastPWM
Update 17th August 2020 Radio control inputs completed
cli_BLS_nortos.cpp
- Committer:
- JonFreeman
- Date:
- 2020-06-09
- Revision:
- 16:d1e4b9ad3b8b
- Parent:
- 14:acaa1add097b
- Child:
- 17:cc9b854295d6
File content as of revision 16:d1e4b9ad3b8b:
/** * Code in this file : - * * STM3_ESC board uses two serial ports. * One (use pc.printf etc) provides 9600 baud comms to a pc or other terminal. Essential for test and setup, not used in everyday use. * * com2 provides 19200 baud comms via opto-isolators to Touch Screen Controller (see "Brute_TS_Controller_2018_11"). Opto isolation allows * for several boards to parallel up on this port, each STM3_ESC board having a unique single byte ID char in range '0' to '9'. * This enables Touch Screen controller to address ESC boards individually e.g. for requesting speed, RPM etc * while also allowing broadcast of commands not requiring responses * * Code implements a Command Line Interpreter (see class cli_2019) * Two instantiations of class cli_2019 are used, 'pcc' for pc comms and 'tsc' for touch screen comms * These read incoming commands and execute code functions accordingly. These functions are all of type * void func (struct parameters &) ; * This allows any command line parameters to pass to 'func' */ // Brushless_STM3_Ctrl_2018_11 #include "mbed.h" #include "BufferedSerial.h" #include "STM3_ESC.h" #include "brushless_motor.h" #include <cctype> using namespace std; extern eeprom_settings user_settings ; extern error_handling_Jan_2019 ESC_Error ; // Provides array usable to store error codes. extern int WatchDog; // from main extern bool WatchDogEnable; // from main extern bool read_temperature (float & t) ; // from main March 2020 extern brushless_motor MotorA, MotorB; // Controlling two motors together or individually extern const char * get_version () ; // Need this as extern const char can not be made to work. This returns & const_version_string extern BufferedSerial com2, pc; // The two com ports used. There is also an unused com port, com3 setup @ 1200 baud extern void setVI_both (double v, double i) ; // Set motor voltage limit and current limit extern double Read_DriverPot (); extern double Read_BatteryVolts (); extern void mode_set_motors_both (int mode) ; // called from cli to set fw, re, rb, hb // All void func (struct parameters &) ; functions addressed by command line interpreter are together below here /** void ver_cmd (struct parameters & a) Responds YES, causes action NO PC or TS able to read software / firmware / hardware version string */ void ver_cmd (struct parameters & a) { if (a.source == SOURCE_PC) { pc.printf ("Version [%s]\r\n", get_version()); } else { if (a.source == SOURCE_TS) if (a.respond) { // Only respond if this board addressed a.com->printf ("%s\r", get_version()); } else pc.printf ("Crap source %d in ver_cmd\r\n", a.source); } } /** void pot_cmd (struct parameters & a) Responds YES, causes action NO pc reads DriverPot. No sense in TS reading as STM3_ESC uses either/or TS, DriverPot */ void pot_cmd (struct parameters & a) { if (a.source == SOURCE_PC) pc.printf ("Driver's pot %.3f\r\n", Read_DriverPot ()); else pc.printf ("Wrong use of pot_cmd\r\n"); } /** * Do nothing command, but does report board ID code '0' to '9' */ void null_cmd (struct parameters & a) { if (a.respond) a.com->printf ("Board ID %c\r\n", user_settings.rd(BOARD_ID)); } /** * void rdi_cmd (struct parameters & a) // read motor currents (uint32_t) and BatteryVolts (double) void rdi_cmd (struct parameters & a) // read motor currents (uint32_t) and BatteryVolts (double) { // Voltage reading true volts, currents only useful as relative values if (a.respond) // a.com->printf ("rdi%d %d %.1f\r%s", MotorA.I.ave, MotorB.I.ave, Read_BatteryVolts (), a.source == SOURCE_PC ? "\n" : ""); a.com->printf ("rdi%.1f %.1f %.1f\r%s", MotorA.Idbl, MotorB.Idbl, Read_BatteryVolts (), a.source == SOURCE_PC ? "\n" : ""); // Format good to be unpicked by cli in touch screen controller } */ /** * void rvi_cmd (struct parameters & a) // read last normalised motor voltage and current values sent to pwms * */ //void rvi_cmd (struct parameters & a) // read last normalised values sent to pwms //{ // if (a.respond) // a.com->printf ("rvi%.2f %.2f %.2f %.2f\r%s", MotorA.last_V, MotorA.last_I, MotorB.last_V, MotorB.last_I, a.source == SOURCE_PC ? "\n" : ""); //} /** * void fw_cmd (struct parameters & a) // Forward command * Broadcast to all STM3_ESC boards, required to ACT but NOT respond */ void fw_cmd (struct parameters & a) // Forward command { // MotorA.set_mode (MOTOR_FORWARD); // MotorB.set_mode (MOTOR_FORWARD); mode_set_motors_both (MOTOR_FORWARD); if (a.source == SOURCE_PC) pc.printf ("fw\r\n"); // Show response to action if command from pc terminal } /** * void re_cmd (struct parameters & a) // Reverse command * Broadcast to all STM3_ESC boards, required to ACT but NOT respond */ void re_cmd (struct parameters & a) // Reverse command { mode_set_motors_both (MOTOR_REVERSE); if (a.source == SOURCE_PC) pc.printf ("re\r\n"); } /** * void rb_cmd (struct parameters & a) // Regen brake command * Broadcast to all STM3_ESC boards, required to ACT but NOT respond */ void rb_cmd (struct parameters & a) // Regen brake command { double tmp = a.dbl[0] / 100.0; MotorA.brake (tmp); MotorB.brake (tmp); // Corrected May 2020 - previously MotorA twice if (a.source == SOURCE_PC) pc.printf ("rb %.2f\r\n", tmp); } /** * void hb_cmd (struct parameters & a) // Hand brake command * Broadcast to all STM3_ESC boards, required to ACT but NOT respond * * NOTE Jan 2019 Hand brake not implemented * May 2020 - Implemented, but not very good */ void hb_cmd (struct parameters & a) // Hand brake command { double tmp; if (a.numof_dbls != 0) tmp = a.dbl[0] / 100.0; // a.numof_dbls is int32_t else tmp = 0.33; if (a.respond) { // a.com->printf ("numof params = %d\r\n", a.numof_dbls); a.com->printf ("Hand Brake : Force 0 to 99 %.0f\r\n", tmp * 100.0); } mode_set_motors_both (MOTOR_HANDBRAKE); if (tmp < 0.0) tmp = 0.0; if (tmp > 1.0) tmp = 1.0; setVI_both (tmp / 5.0, 0.99); } /**void user_settings_cmd (struct parameters & a) // With no params, reads eeprom contents. With params sets eeprom contents * user_settings_cmd called only from pc comms. No sense calling from Touch Screen Controller * * Called without parameters - Lists to pc terminal current settings * */ void user_settings_cmd (struct parameters & a) // With no params, reads eeprom contents. With params sets eeprom contents { user_settings.edit (a.dbl, a.numof_dbls); } extern struct optpar option_list[] ; //= { void brake_eff_set_cmd (struct parameters & a) { // set brake effectiveness from TS May 2020 char be = (char) a.dbl[0]; pc.printf ("BRAKE_EFFECTIVENESS min = %d, max = %d\r\n", option_list[BRAKE_EFFECTIVENESS].min, option_list[BRAKE_EFFECTIVENESS].max); if (be > option_list[BRAKE_EFFECTIVENESS].max) be = option_list[BRAKE_EFFECTIVENESS].max; if (be < option_list[BRAKE_EFFECTIVENESS].min) be = option_list[BRAKE_EFFECTIVENESS].min; user_settings.wr(be, BRAKE_EFFECTIVENESS); user_settings.save (); pc.printf ("Set brake effectiveness to %d pct\r\n", be); } void ssl_cmd (struct parameters & a) { // set speed limit NEW and untested July 2019. Stored as speed * 10 to get 1dec place char sp = (char) (a.dbl[0] * 10.0); if (sp > option_list[TOP_SPEED].max) sp = option_list[TOP_SPEED].max; if (sp < option_list[TOP_SPEED].min) sp = option_list[TOP_SPEED].min; user_settings.wr(sp, TOP_SPEED); user_settings.save (); pc.printf ("Set speed limit to %.1f mph\r\n", a.dbl[0]); } /** * void temperature_cmd (struct parameters & a) { * Few boards have temperature sensor fitted. Now only supports LM75B i2c sensor */ void read_temperature_cmd (struct parameters & a) { float t = -99.25; if (a.respond) { a.com->printf ("tem%c ", user_settings.rd(BOARD_ID)); if (read_temperature(t)) a.com->printf ("Temperature = %7.3f\r\n", t); else a.com->printf ("Temp sensor not fitted\r\n"); } } /** *void rpm_cmd (struct parameters & a) // to report e.g. RPM 1000 1000 ; speed for both motors */ void rpm_cmd (struct parameters & a) // to report e.g. RPM 1000 1000 ; speed for both motors { if (a.respond) // a.com->printf ("rpm%c %d %d\r%s", user_settings.rd(BOARD_ID), MotorA.RPM, MotorB.RPM, a.source == SOURCE_PC ? "\n" : ""); a.com->printf ("rpm%c %.0f %.0f\r%s", user_settings.rd(BOARD_ID), MotorA.dRPM, MotorB.dRPM, a.source == SOURCE_PC ? "\n" : ""); } /** *void mph_cmd (struct parameters & a) // to report miles per hour */ void mph_cmd (struct parameters & a) // to report miles per hour to 1 decimal place - now 2dp May 2020 { int j = 0; double speedmph = 0.0; if (MotorA.exists ()) { j |= 1; speedmph = MotorA.dMPH; } if (MotorB.exists ()) { j |= 2; speedmph += MotorB.dMPH; } if (j == 3) speedmph /= 2.0; if (a.respond) // May 17th 2020 modified line below - removed superfluous cast, upped to 2 decimal places // a.com->printf ("mph%c %.2f\r%s", user_settings.rd(BOARD_ID), speedmph, a.source == SOURCE_PC ? "\n" : ""); a.com->printf ("?s%c %.2f\r%s", user_settings.rd(BOARD_ID), speedmph, a.source == SOURCE_PC ? "\n" : ""); } /** *void sysV_report (struct parameters & a) // to report system voltage * Reports system link voltage to one decimal place */ void sysV_report (struct parameters & a) // { if (a.respond) a.com->printf ("?v%c %.1f\r%s", user_settings.rd(BOARD_ID), Read_BatteryVolts(), a.source == SOURCE_PC ? "\n" : ""); } /** *void sysI_report (struct parameters & a) // to report motor currents * Reports doubles for each motor current amps to 2 decimal places */ void sysI_report (struct parameters & a) // { if (a.respond) a.com->printf ("?i%c %.2f %.2f\r%s", user_settings.rd(BOARD_ID), MotorA.Idbl, MotorB.Idbl, a.source == SOURCE_PC ? "\n" : ""); } /* New December 2019 - possible for implementation Having implemented radio control inputs and driver pot, there is some sense in moving slider handler from touch screen controller into STM3_ESC code. New instructions to be accepted from touch screen : - {"w", "touch screen new slider touch", slider_touch_cmd}, {"x", "updated slider RANGE 0 to 99", slider_touch_position_cmd}, // max two digits {"y", "touch_screen slider finger lifted clear", slider_untouch_cmd}, */ bool finger_on_slider = false; /**void slider_touch_cmd (struct parameters & a) * * Message from touch screen controller, slider touched */ void slider_touch_cmd (struct parameters & a) { finger_on_slider = true; } /**void slider_untouch_cmd (struct parameters & a) * * Message from touch screen controller, finger taken off slider */ void slider_untouch_cmd (struct parameters & a) { finger_on_slider = false; } /**void slider_touch_position_cmd (struct parameters & a) * * Message from touch screen controller, latest slider touch position */ void slider_touch_position_cmd (struct parameters & a) { } // End of New December 2019 /**void vi_cmd (struct parameters & a) * * For setting motor voltage and current limits from pc terminal for test */ void vi_cmd (struct parameters & a) { setVI_both (a.dbl[0] / 100.0, a.dbl[1] / 100.0); // pc.printf ("setVI_both from %s\r\n", a.source == SOURCE_PC ? "PC" : "Touch Screen"); } /** *void v_cmd (struct parameters & a) * Set motor voltage limit from either source without checking for addressed board */ void v_cmd (struct parameters & a) { MotorA.set_V_limit (a.dbl[0] / 100.0); MotorB.set_V_limit (a.dbl[0] / 100.0); } /** *void i_cmd (struct parameters & a) * Set motor current limit from either source without checking for addressed board */ void i_cmd (struct parameters & a) { MotorA.set_I_limit (a.dbl[0] / 100.0); MotorB.set_I_limit (a.dbl[0] / 100.0); } /** *void kd_cmd (struct parameters & a) // kick and enable the watch dog * * Brute_TS_Controller or other external controller to issue regular 'kd\r' to come here. * WatchDog disabled by default, enabled on first call to here * This is where WatchDog timer is reset and reloaded. * Timeout may be detected and handled in 8Hz loop in main programme loop */ void kd_cmd (struct parameters & a) // kick the watchdog. Reached from TS or pc. { WatchDog = WATCHDOG_RELOAD + (user_settings.rd(BOARD_ID) & 0x0f); // Reload watchdog timeout. Counted down @ 8Hz WatchDogEnable = true; // Receipt of this command sufficient to enable watchdog } void wd_report (struct parameters & a) // Reachable always from pc. Only addressed board responds to TS { pc.printf ("WatchDog %d\r\n", WatchDog); } /** *void who_cmd (struct parameters & a) // Reachable always from pc. Only addressed board responds to TS * * When using STM3_ESC boards together with 'Brute Touch Screen Controller', controller needs to identify number and identity * of all connected STM3_ESC boards. To do this the Touch Screen issues '0who', '1who' ... '9who' allowing time between each * for an identified STM3_ESC to respond with 'who7' (if it was number 7) without causing contention of paralleled serial opto isolated bus */ void who_cmd (struct parameters & a) // Reachable always from pc. Only addressed board responds to TS { if (a.source == SOURCE_PC || user_settings.rd(BOARD_ID) == a.target_unit) a.com->printf ("who%c\r%s", user_settings.rd(BOARD_ID), a.source == SOURCE_PC ? "\n" : ""); } /** *void rcin_pccmd (struct parameters & a) * * For test, reports to pc terminal info about radio control input channels */ //void rcin_pccmd (struct parameters & a) //{ // rcin_report (); //} /* void scmd (struct parameters & a) // filter coefficient fiddler { switch ((int)a.dbl[0]) { case 1: MotorA.sdbl[1] = MotorB.sdbl[1] = a.dbl[1]; break; case 2: MotorA.sdbl[2] = MotorB.sdbl[2] = a.dbl[1]; break; case 3: MotorA.sdbl[3] = MotorB.sdbl[3] = a.dbl[1]; break; case 4: MotorA.sdbl[4] = MotorB.sdbl[4] = a.dbl[1]; break; default: pc.printf ("Wrong use of scmd %f\r\n", a.dbl[0]); } pc.printf ("Filter Coefficient Fiddler - used in brushless_motor::speed_monitor_and_control ()"); pc.printf ("Filter Coeffs 1 to 4\r\n"); pc.printf ("1 %.3f\tPscale 0.01-0.5\r\n", MotorA.sdbl[1]); pc.printf ("2 %.3f\tP_gain 1.0-1000.0\r\n", MotorA.sdbl[2]); pc.printf ("3 %.3f\tDscale 0.01-0.5\r\n", MotorA.sdbl[3]); pc.printf ("4 %.3f\tD_gain 1.0-1000.0\r\n", MotorA.sdbl[4]); // pc.printf ("5 Set target_speed\r\n"); } */ struct kb_command { // Commands tabulated as list of these structures as seen below const char * cmd_word; // points to text e.g. "menu" const char * explan; // very brief explanation or clue as to purpose of function void (*f)(struct parameters &); // points to function } ; // Positioned in code here as knowledge needed by following menucmd /** * void menucmd (struct parameters & a) * * List available terminal commands to pc terminal. No sense in touch screen using this */ void menucmd (struct parameters & a) { if (a.respond) { a.com->printf("\r\n\nDual BLDC ESC type STM3 2018-20, Ver %s\r\nAt menucmd function - listing commands, source %s:-\r\n", get_version(), a.source == SOURCE_PC ? "PC" : "TS"); for(int i = 0; i < a.numof_menu_items; i++) a.com->printf("[%s]\t\t%s\r\n", a.command_list[i].cmd_word, a.command_list[i].explan); a.com->printf("End of List of Commands\r\n"); } } /********************** END OF COMMAND LINE INTERPRETER COMMANDS *************************************/ /** * struct kb_command const loco_command_list[] = { * List of commands accepted from external controller through opto isolated com port 19200, 8,n,1 */ struct kb_command const loco_command_list[] = { // For comms between STM3_ESC and 'Brute Touch Screen Controller' // ***** Broadcast commands for all STM3_ESC boards to execute. Boards NOT to send serial response ***** {"fw", "forward", fw_cmd}, {"re", "reverse", re_cmd}, {"rb", "regen brake 0 to 99 %", rb_cmd}, // max two digits {"hb", "hand brake", hb_cmd}, {"v", "set motors V percent RANGE 0 to 99", v_cmd}, {"i", "set motors I percent RANGE 0 to 99", i_cmd}, {"vi", "set motors V and I percent RANGE 0 to 99", vi_cmd}, {"w", "touch screen new slider touch", slider_touch_cmd}, {"x", "updated slider RANGE 0 to 99", slider_touch_position_cmd}, {"y", "touch_screen slider finger lifted clear", slider_untouch_cmd}, {"kd", "kick the dog, reloads WatchDog", kd_cmd}, {"ssl", "set speed limit e.g. 10.7", ssl_cmd}, // NEW July 2019 {"sbe", "set brake effectiveness 5 to 90 percent", brake_eff_set_cmd}, // NEW May 2020 // ***** Endof Broadcast commands for all STM3_ESC boards to execute. Boards NOT to send serial response ***** // ***** Following are rx'd requests for serial response from addressed STM3_ESC only {"?v", "Report system bus voltage", sysV_report}, {"?i", "Report motor both currents", sysI_report}, {"who", "search for connected units, e.g. 3who returs 'who3' if found", who_cmd}, {"tem", "report temperature", read_temperature_cmd}, {"mph", "read loco speed miles per hour", mph_cmd}, {"?s", "read loco speed miles per hour", mph_cmd}, // Shorter-hand added 17th May 2020 // {"ssl", "set speed limit e.g. 10.7", ssl_cmd}, // NEW July 2019 // {"sbe", "set brake effectiveness 5 to 90 percent", brake_eff_set_cmd}, // NEW May 2020 // {"rvi", "read most recent values sent to pwms", rvi_cmd}, // {"rdi", "read motor currents and power voltage", rdi_cmd}, // ***** Endof } ; /** struct kb_command const loco_command_list[] = { List of commands accepted from external pc through non-opto isolated com port 9600, 8,n,1 */ struct kb_command const pc_command_list[] = { {"ls", "Lists available commands", menucmd}, {"?", "Lists available commands, same as ls", menucmd}, {"pot", "read drivers control pot", pot_cmd}, {"fw", "forward", fw_cmd}, {"re", "reverse", re_cmd}, {"rb", "regen brake 0 to 99 %", rb_cmd}, {"hb", "hand brake", hb_cmd}, {"v", "set motors V percent RANGE 0 to 100", v_cmd}, {"i", "set motors I percent RANGE 0 to 100", i_cmd}, {"vi", "set motors V and I percent RANGE 0 to 100", vi_cmd}, {"?v", "Report system bus voltage", sysV_report}, {"?i", "Report motor both currents", sysI_report}, {"?w", "show WatchDog timer contents", wd_report}, {"who", "search for connected units, e.g. 3who returs 'who3' if found", who_cmd}, {"us", "read or set user settings in eeprom", user_settings_cmd}, // Big change Jan 2019 {"ssl", "set speed limit e.g. 10.7", ssl_cmd}, // NEW July 2019 ONLY HERE FOR TEST, normal use is from Touch Screen only. {"sbe", "set brake effectiveness 5 to 90 percent", brake_eff_set_cmd}, // NEW May 2020 // {"erase", "set eeprom contents to all 0xff", erase_cmd}, {"tem", "report temperature", read_temperature_cmd}, // Reports -50 when sensor not fitted {"kd", "kick the dog, reloads WatchDog", kd_cmd}, {"ver", "Version", ver_cmd}, {"rpm", "read motor pair speeds", rpm_cmd}, {"mph", "read loco speed miles per hour", mph_cmd}, {"?s", "read loco speed miles per hour", mph_cmd}, // {"rvi", "read most recent values sent to pwms", rvi_cmd}, // {"rdi", "read motor currents and power voltage", rdi_cmd}, // {"bc", "bogie constants - wheel dia, motor pinion, wheel gear", bogie_constants_report_cmd}, // {"gbb", "get bogie bytes from eeprom and report", gbb_cmd}, // OBSOLETE, replaced by 'gbb' {"nu", "do nothing", null_cmd}, } ; // cli_2019 (BufferedSerial * comport, kb_command const * list, int list_len, int source) { /** * cli_2019 pcc (&pc, pc_command_list, sizeof(pc_command_list) / sizeof(kb_command), SOURCE_PC) ; * cli_2019 tsc (&com2, loco_command_list, sizeof(loco_command_list) / sizeof(kb_command), SOURCE_TS) ; * * Instantiate two Command Line Interpreters, one for pc terminal and one for touch screen controller */ cli_2019 pcc (&pc, pc_command_list, sizeof(pc_command_list) / sizeof(kb_command), SOURCE_PC) ; cli_2019 tsc (&com2, loco_command_list, sizeof(loco_command_list) / sizeof(kb_command), SOURCE_TS) ; /* New - March 2018 Using opto isolated serial port, paralleled up using same pair to multiple STM3_ESC boards running this code. New feature - commands have optional prefix digit 0-9 indicating which unit message is addressed to. Commands without prefix digit - broadcast to all units, all to obey but none to respond. Only units recognising its address from prefix digit may respond. This avoids bus contention. But for BROADCAST commands, '0' may respond on behalf of the group */ /** * void cli_2019::test () { * * Daft check that class instantiation worked */ void cli_2019::flush () { // char ch; while (a.com->readable()) a.com->getc(); //pc.printf ("At cli2019::test, source %d len %d,\r\n", a.source, a.numof_menu_items); } /** * void cli_2019::core () { * * Command Line Interpreter. * This to be called every few milli secs from main programme loop. * Reads any rx'd chars into command line buffer, returns when serial buffer empty. * If last char rx'd war '\r' end of text delimiter, apt command_list is searched for a matched command in command line * If matched command found, apt function is executed. * Parameters available to functions from 'parameters' struct. */ void cli_2019::core () { int ch, IAm = user_settings.rd(BOARD_ID); char * pEnd;//, * cmd_line_ptr; while (a.com->readable()) { ch = a.com->getc(); if (clindex > MAX_CMD_LEN) { // trap out stupidly long command lines ESC_Error.set (FAULT_COM_LINE_LEN, 1); // Set FAULT_EEPROM bit 0 if 24LC64 problem a.com->printf ("Error!! Stupidly long cmd line\r\n"); clindex = 0; } if(ch != '\r') { // was this the 'Enter' key? if (ch != '\n') // Ignore line feeds cmdline[clindex++] = ch; // added char to command being assembled } else { // key was CR, may or may not be command to lookup a.target_unit = BROADCAST; // Set to BROADCAST default once found command line '\r' cmdline_ptr = cmdline; cmdline[clindex] = 0; // null terminate command string if(clindex) { // If have got some chars to lookup int i, wrdlen; if (isdigit(cmdline[0])) { // Look for command with prefix digit cmdline_ptr++; // point past identified digit prefix a.target_unit = cmdline[0]; // '0' to '9' //com->printf ("Got prefix %c\r\n", cmd_line[0]); } for (i = 0; i < a.numof_menu_items; i++) { // Look for input match in command list wrdlen = strlen(commandlist[i].cmd_word); if(strncmp(commandlist[i].cmd_word, cmdline_ptr, wrdlen) == 0 && !isalpha(cmdline_ptr[wrdlen])) { // If match found for (int k = 0; k < MAX_CLI_PARAMS; k++) { a.dbl[k] = 0.0; } a.position_in_list = i; a.numof_dbls = 0; // pEnd = cmdline + clindex; // does indeed point to null terminator pEnd = cmdline_ptr + wrdlen; // while (*pEnd) { // Assemble all numerics as doubles while (*pEnd && a.numof_dbls < MAX_CLI_PARAMS) { // Assemble all numerics as doubles a.dbl[a.numof_dbls++] = strtod (pEnd, &pEnd); while (*pEnd && (pEnd < cmdline + clindex) && *pEnd && !isdigit(*pEnd) && ('.' != *pEnd) && ('-' != *pEnd) && ('+' != *pEnd)) { // Can crash cli here with e.g. 'ls -l' pEnd++; } // problem occurs with input such as "- ", or "-a", seemingly anything dodgy following a '-' or a '+' if (((*pEnd == '-') || (*pEnd == '+')) &&(!isdigit(*(pEnd+1))) && ('.' !=*(pEnd+1))) pEnd = cmdline + clindex; // fixed by aborting remainder of line } a.respond = a.resp_always; if (((a.target_unit == BROADCAST) && (IAm == '0')) || (IAm == a.target_unit)) a.respond = true; // sorted 26/4/18 // All boards to obey BROADCAST command, only specific board to obey number prefixed command if ((a.target_unit == BROADCAST) || (IAm == a.target_unit)) commandlist[i].f(a); // execute command if addressed to this unit i = a.numof_menu_items + 1; // to exit for loop } // end of match found } // End of for numof_menu_items if(i == a.numof_menu_items) { // a.com->printf("No Match Found for CMD [%s]\r\n", cmdline); pc.printf("No Match Found for CMD [%s]\r\n", cmdline); ESC_Error.set (FAULT_COM_LINE_NOMATCH, 1); // Set FAULT_EEPROM bit 0 if 24LC64 problem } } // End of If have got some chars to lookup clindex = 0; } // End of else key was CR, may or may not be command to lookup } // End of while (com->readable()) } // end of command line interpreter core function