STM3 ESC dual brushless motor controller. 10-60v, motor power rating tiny to kW. Ganged or independent motor control As used in 'The Brute' locomotive - www.jons-workshop.com
Dependencies: mbed BufferedSerial Servo FastPWM
Diff: cli_BLS_nortos.cpp
- Revision:
- 12:d1d21a2941ef
- Parent:
- 11:bfb73f083009
--- a/cli_BLS_nortos.cpp Sat Jan 19 11:45:01 2019 +0000 +++ b/cli_BLS_nortos.cpp Mon Mar 04 17:51:08 2019 +0000 @@ -1,300 +1,447 @@ -// DualBLS2018_06 +/** +* 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 "FastPWM.h" #include "DualBLS.h" #include "brushless_motor.h" #include <cctype> -#include "DualBLS.h" using namespace std; -extern int I_Am () ; // Returns boards id number as ASCII char '0', '1' etc. Code for Broadcast = '\r' +extern eeprom_settings mode ; +extern error_handling_Jan_2019 ESC_Error ; // Provides array usable to store error codes. extern int WatchDog; extern bool WatchDogEnable; -extern char mode_bytes[]; - -extern brushless_motor MotorA, MotorB; +extern double rpm2mph ; -const int BROADCAST = '\r'; -const int MAX_PARAMS = 20; -struct parameters { - struct kb_command const * command_list; - BufferedSerial * com; // pc or com2 - char cmd_line[120]; - char * cmd_line_ptr; - int32_t position_in_list, numof_dbls, target_unit, numof_menu_items, cl_index, gp_i; - double dbl[MAX_PARAMS]; - bool respond, resp_always; -} ; +extern brushless_motor MotorA, MotorB; // Controlling two motors together or individually +extern char const_version_string[] ; -struct parameters pccom, lococom; -// WithOUT RTOS -extern BufferedSerial com2, pc; -extern void send_test () ; -extern void setVI (double v, double i) ; -extern void setV (double v) ; -extern void setI (double i) ; -//extern void last_VI (double * val) ; // only for test from cli - -//BufferedSerial * com; +extern BufferedSerial com2, pc; // The two com ports used. There is also an unused com port, com3 setup @ 1200 baud +extern void setVI (double v, double i) ; // Set motor voltage limit and current limit extern double Read_DriverPot (); extern double Read_BatteryVolts (); -void pot_cmd (struct parameters & a) +extern void mode_set_both_motors (int mode, double val) ; // called from cli to set fw, re, rb, hb +extern void rcin_report () ; + +// 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) { - pc.printf ("Driver's pot %.3f\r\n", Read_DriverPot ()); + if (a.source == SOURCE_PC) + pc.printf ("Version %s\r\n", const_version_string); + else { + if (a.source == SOURCE_TS) + if (a.respond) // Only respond if this board addressed + a.com->printf ("%s\r", const_version_string); + 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 ("At null_cmd, board ID %c, parameters : First %.3f, second %.3f\r\n", I_Am(), a.dbl[0], a.dbl[1]); -} - -// {"wden", "enable watchdog if modes allow", wden_lococmd}, -// {"wddi", "disable watchdog always", wddi_lococmd}, - -void wden_lococmd (struct parameters & a) -{ - if (mode_bytes[COMM_SRC] != 3) // When not hand pot control, allow watchdog enable - WatchDogEnable = true; -} -void wden_pccmd (struct parameters & a) -{ - wden_lococmd (a); - a.com->printf ("Watchdog %sabled\r\n", WatchDogEnable ? "En":"Dis"); + a.com->printf ("At null_cmd, board ID %c\r\n", mode.rd(BOARD_ID)); } -void wddi_lococmd (struct parameters & a) -{ - WatchDogEnable = false; -} -void wddi_pccmd (struct parameters & a) -{ - wddi_lococmd (a); - a.com->printf ("Watchdog %sabled\r\n", WatchDogEnable ? "En":"Dis"); -} - -extern void report_motor_types () ; +#ifdef USING_DC_MOTORS +/** +void mt_cmd (struct parameters & a) + PC Only + Responds YES, causes action NO + report_motor_types () // Reports 'Brushless' if Hall inputs read 1 to 6, 'DC' if no Hall sensors connected, therefore DC motor assumed +*/ void mt_cmd (struct parameters & a) { - report_motor_types (); -// if (a.respond) -// a.com->printf ("At null_cmd, board ID %c, parameters : First %.3f, second %.3f\r\n", I_Am(), a.dbl[0], a.dbl[1]); + if (a.source == SOURCE_PC) + pc.printf ("Mot A is %s, Mot B is %s\r\n", MotorA.dc_motor ? "DC":"Brushless", MotorB.dc_motor ? "DC":"Brushless"); + else + pc.printf ("Wrong use of mt_cmd\r\n"); +} +#endif + +/** +* 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 } -extern void mode_set_both_motors (int mode, double val) ; // called from cli to set fw, re, rb, hb - -void rdi_cmd (struct parameters & a) // read motor currents -{ - if (a.respond) - a.com->printf ("rdi%.0f %.0f %.1f\r", MotorA.I.ave, MotorB.I.ave, Read_BatteryVolts ()); // 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", MotorA.last_V, MotorA.last_I, MotorB.last_V, MotorB.last_I); -} - -void fw_cmd (struct parameters & a) // Forward command -{ - mode_set_both_motors (FORWARD, 0.0); + 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 re_cmd (struct parameters & a) // Reverse command +/** +* 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 { - mode_set_both_motors (REVERSE, 0.0); -} - -void rb_cmd (struct parameters & a) // Regen brake command -{ - double b = a.dbl[0] / 100.0; -// a.com->printf ("Applying brake %.3f\r\n", b); - mode_set_both_motors (REGENBRAKE, b); -// apply_brake (b); + mode_set_both_motors (MOTOR_FORWARD, 0.0); + if (a.source == SOURCE_PC) + pc.printf ("fw\r\n"); // Show response to action if command from pc terminal } -extern bool wr_24LC64 (int mem_start_addr, char * source, int length) ; -extern bool rd_24LC64 (int mem_start_addr, char * dest, int length) ; - -void erase_cmd (struct parameters & a) // Sets eeprom contents to all 0xff. 256 pages of 32 bytes to do +/** +* 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 { - char t[36]; - for (int i = 0; i < 32; i++) - t[i] = 0xff; - for (int i = 0; i < 8191; i += 32) { - a.com->printf ("."); - if (!wr_24LC64 (i, t, 32)) - a.com->printf ("eeprom write prob\r\n"); - } -} -/*struct motorpairoptions { // This to be user settable in eeprom, 32 bytes - uint8_t MotA_dir, // 0 or 1 - MotB_dir, // 0 or 1 - gang, // 0 for separate control (robot mode), 1 for ganged loco bogie mode - serv1, // 0, 1, 2 = Not used, Input, Output - serv2, // 0, 1, 2 = Not used, Input, Output - cmd_source, // 0 Invalid, 1 COM1, 2 COM2, 3 Pot, 4 Servo1, 5 Servo2 - {'1', '9', '0', "Alternative ID ascii '1' to '9'"}, // defaults to '0' before eerom setup for first time - {50, 250, 98, "Wheel diameter mm"}, // New 01/06/2018 - {10, 250, 27, "Motor pinion"}, // New 01/06/2018 - {50, 250, 85, "Wheel gear"}, // New 01/06/2018 -// last; -} ; -*/ - -// New 22 June 2018 -// get bogie bytes - report back to touch controller -void gbb_cmd (struct parameters & a) // -{ - if (a.target_unit == BROADCAST || !a.resp_always) { -// a.com->printf ("At mode_cmd, can not use BROADCAST with mode_cmd\r\n"); - } else { - pc.printf ("At gbb\r\n"); - char eeprom_contents[36]; // might need to be unsigned? - memset (eeprom_contents, 0, 36); - a.com->printf ("gbb"); - rd_24LC64 (0, eeprom_contents, 32); - for (int i = 0; i < numof_eeprom_options; i++) - a.com->printf (" %d", eeprom_contents[i]); - a.com->putc ('\r'); - a.com->putc ('\n'); - } + mode_set_both_motors (MOTOR_REVERSE, 0.0); + if (a.source == SOURCE_PC) + pc.printf ("re\r\n"); } -void mode_cmd (struct parameters & a) // With no params, reads eeprom contents. With params sets eeprom contents +/** +* 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 { - if (a.target_unit == BROADCAST || !a.resp_always) { -// a.com->printf ("At mode_cmd, can not use BROADCAST with mode_cmd\r\n"); - } else { - char t[36]; - a.com->printf ("At mode_cmd with node %d\r\n", a.target_unit); - rd_24LC64 (0, t, 32); - a.com->printf ("Numof params=%d\r\n", a.numof_dbls); - for (int i = 0; i < numof_eeprom_options; i++) - a.com->printf ("%2x\t%s\r\n", t[i], option_list[i].t); - if (a.numof_dbls == 0) { // Read present contents, do not write - a.com->printf ("That's it\r\n"); - } else { // Write new shit to eeprom - a.com->printf ("\r\n"); - if (a.numof_dbls != numof_eeprom_options) { - a.com->printf ("params required = %d, you offered %d\r\n", numof_eeprom_options, a.numof_dbls); - } else { // Have been passed correct number of parameters - int b; - a.com->printf("Ready to write params to eeprom\r\n"); - for (int i = 0; i < numof_eeprom_options; i++) { - b = (int)a.dbl[i]; // parameter value to check against limits - if (i == 6) // Alternative ID must be turned to ascii - b |= '0'; - if ((b < option_list[i].min) || (b > option_list[i].max)) { // if parameter out of range - a.com->printf("Warning - Parameter = %d, out of range, setting to default %d\r\n", b, option_list[i].def); - b = option_list[i].def; - } - a.com->printf ("0x%2x\t%s\r\n", (t[i] = b), option_list[i].t); - } - wr_24LC64 (0, t, numof_eeprom_options); - memcpy (mode_bytes,t,32); - a.com->printf("Parameters set in eeprom\r\n"); - } - } - } + mode_set_both_motors (MOTOR_REGENBRAKE, a.dbl[0] / 100.0); + if (a.source == SOURCE_PC) + pc.printf ("rb %.2f\r\n", a.dbl[0] / 100.0); } -/*void coast_cmd (struct parameters & a) { // Coast -} +/** +* 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 +* */ -void hb_cmd (struct parameters & a) +void hb_cmd (struct parameters & a) // Hand brake command { if (a.respond) { a.com->printf ("numof params = %d\r\n", a.numof_dbls); a.com->printf ("Hand Brake : First %.3f, second %.3f\r\n", a.dbl[0], a.dbl[1]); } - mode_set_both_motors (HANDBRAKE, 0.0); + mode_set_both_motors (MOTOR_HANDBRAKE, 0.0); } -extern uint32_t last_temp_count; + +extern int numof_eeprom_options2 ; +extern struct optpar const option_list2[] ; +/**void mode_cmd (struct parameters & a) // With no params, reads eeprom contents. With params sets eeprom contents +* mode_cmd called only from pc comms. No sense calling from Touch Screen Controller +* +* Called without parameters - Lists to pc terminal current settings +* +*/ +void mode19_cmd (struct parameters & a) // With no params, reads eeprom contents. With params sets eeprom contents +{ + char temps[36]; + int i; + double topspeed; // New Jan 2019 - set max loco speed + pc.printf ("\r\nmode - Set system data in EEPROM - Jan 2019\r\nSyntax 'mode' with no parameters lists current state.\r\n"); + if (a.numof_dbls) { // If more than 0 parameters supplied + for (i = 0; i < a.numof_dbls; i++) + temps[i] = (char)a.dbl[i]; // recast doubles to char + while (i < 33) + temps[i++] = 0; + switch ((int)a.dbl[0]) { + case 0: // MotorA_dir [0 or 1], MotorB_dir [0 or 1] + if (temps[1] == 0 || temps[1] == 1) + mode.wr(temps[1], MOTADIR); + if (temps[2] == 0 || temps[2] == 1) + mode.wr(temps[2], MOTBDIR); + break; + case 1: // MotorA_poles [4,6,8], MotorB_poles [4,6,8] + if (temps[1] == 4 || temps[1] == 6 || temps[1] == 8) + mode.wr(temps[1], MOTAPOLES); + if (temps[2] == 4 || temps[2] == 6 || temps[2] == 8) + mode.wr(temps[2], MOTBPOLES); + break; + case 2: // MotorA_ current sense resistors [1 to 4], MotorB_ current sense resistors [1 to 4] + if (temps[1] > 0 && temps[1] < 5) + mode.wr(temps[1], MOTADIR); + if (temps[2] > 0 && temps[2] < 5) + mode.wr(temps[2], MOTBDIR); + break; + case 3: // 2 Servo1 [0 or 1], Servo2 [0 or 1] + if (temps[1] == 0 || temps[1] == 1) + mode.wr(temps[1], SVO1); + if (temps[2] == 0 || temps[2] == 1) + mode.wr(temps[2], SVO2); + break; + case 4: // 3 RCIn1 [0 or 1], RCIn2 [0 or 1] + if (temps[1] == 0 || temps[1] == 1) + mode.wr(temps[1], RCIN1); + if (temps[2] == 0 || temps[2] == 1) + mode.wr(temps[2], RCIN2); + break; + case 5: // 4 Board ID '0' to '9' + if (temps[1] <= 9) // pointless to compare unsigned integer with zero + mode.wr('0' | temps[1], BOARD_ID); + break; + case 6: // TOP_SPEED + topspeed = a.dbl[1]; + if (topspeed > 25.0) topspeed = 25.0; + if (topspeed < 1.0) topspeed = 1.0; + mode.wr((char)(topspeed * 10.0), TOP_SPEED); + break; + case 7: // 5 Wheel dia mm, Motor pinion teeth, Wheel gear teeth + mode.wr(temps[1], WHEELDIA); + mode.wr(temps[2], MOTPIN); + mode.wr(temps[3], WHEELGEAR); + break; + case 8: // {2, 5, 2, "Command source 2= COM2 (Touch Screen), 3= Pot, 4= RC Input1, 5= RC Input2"}, + if (temps[1] > 1 && temps[1] < 6) + mode.wr(temps[1], COMM_SRC); + break; + case 83: // set to defaults + mode.set_defaults (); + break; + case 9: // 9 Save settings + mode.save (); + pc.printf ("Saving settings to EEPROM\r\n"); + break; + default: + break; + } // endof switch + } // endof // If more than 0 parameters supplied + else { + pc.printf ("No Changes\r\n"); + } + pc.printf ("mode 0\tMotorA_dir [0 or 1]=%d, MotorB_dir [0 or 1]=%d\r\n", mode.rd(MOTADIR), mode.rd(MOTBDIR)); + pc.printf ("mode 1\tMotorA_poles [4,6,8]=%d, MotorB_poles [4,6,8]=%d\r\n", mode.rd(MOTAPOLES), mode.rd(MOTBPOLES)); + pc.printf ("mode 2\tMotorA I 0R05 sense Rs [1to4]=%d, MotorB I 0R05 sense Rs [1to4]=%d\r\n", mode.rd(ISHUNTA), mode.rd(ISHUNTB)); + pc.printf ("mode 3\tServo1 [0 or 1]=%d, Servo2 [0 or 1]=%d\r\n", mode.rd(SVO1), mode.rd(SVO2)); + pc.printf ("mode 4\tRCIn1 [0 or 1]=%d, RCIn2 [0 or 1]=%d\r\n", mode.rd(RCIN1), mode.rd(RCIN2)); + pc.printf ("mode 5\tBoard ID ['0' to '9']='%c'\r\n", mode.rd(BOARD_ID)); + pc.printf ("mode 6\tTop Speed MPH [1.0 to 25.0]=%.1f\r\n", double(mode.rd(TOP_SPEED)) / 10.0); + pc.printf ("mode 7\tWheel dia mm=%d, Motor pinion teeth=%d, Wheel gear teeth=%d\r\n", mode.rd(WHEELDIA), mode.rd(MOTPIN), mode.rd(WHEELGEAR)); + pc.printf ("mode 8\tCommand Src [%d] - 2=COM2 (Touch Screen), 3=Pot, 4=RC In1, 5=RC In2\r\n", mode.rd(COMM_SRC)); + pc.printf ("mode 83\tSet to defaults\r\n"); + pc.printf ("mode 9\tSave settings\r\r\n"); + +} + +extern uint32_t last_temperature_count; +/** +* void temperature_cmd (struct parameters & a) { +* Few boards have temperature sensor fitted. Non-preferred feature +*/ void temperature_cmd (struct parameters & a) { if (a.respond) { - a.com->printf ("tem%c %d\r\n", mode_bytes[ID], (last_temp_count / 16) - 50); + a.com->printf ("tem%c %d\r\n", mode.rd(BOARD_ID), (last_temperature_count / 16) - 50); } } -void bogie_constants_report_cmd (struct parameters & a) { - if (a.respond) { - a.com->printf ("bc%c %d %d %d\r\n", mode_bytes[ID], mode_bytes[WHEELDIA], mode_bytes[MOTPIN], mode_bytes[WHEELGEAR]); - } -} - +/** +*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%d %d\r", MotorA.RPM, MotorB.RPM); +// a.com->printf ("rpm%c %d %d\r%s", mode.rd(BOARD_ID), MotorA.RPM, MotorB.RPM, a.source == SOURCE_PC ? "\n" : ""); + a.com->printf ("rpm%c %.0f %.0f\r%s", mode.rd(BOARD_ID), MotorA.dRPM, MotorB.dRPM, a.source == SOURCE_PC ? "\n" : ""); } -extern double rpm2mph ; -void mph_cmd (struct parameters & a) // to report miles per hour +/** +*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 { if (a.respond) - a.com->printf ("mph%c %.3f\r", mode_bytes[ID], (double)(MotorA.RPM + MotorB.RPM) * rpm2mph / 2.0); +// a.com->printf ("mph%c %.1f\r%s", mode.rd(BOARD_ID), (double)(MotorA.RPM + MotorB.RPM) * rpm2mph / 2.0, a.source == SOURCE_PC ? "\n" : ""); + a.com->printf ("mph%c %.1f\r%s", mode.rd(BOARD_ID), (double)(MotorA.dMPH + MotorB.dMPH) / 2.0, 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", mode.rd(BOARD_ID), Read_BatteryVolts(), a.source == SOURCE_PC ? "\n" : ""); } -void menucmd (struct parameters & a); +/** +*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) // Calibration, refinement of 6000.0 (not miles out) first guess possible. +// a.com->printf ("?i%c %.2f %.2f\r%s", mode_bytes[BOARD_ID], (double)MotorA.I.ave / 6000.0, (double)MotorB.I.ave / 6000.0, a.source == SOURCE_PC ? "\n" : ""); +// a.com->printf ("?i%c %.2f %.2f\r%s", mode.rd(BOARD_ID), (double)MotorA.I.ave / 6000.0, MotorA.I.dblave2 / 6000.0, a.source == SOURCE_PC ? "\n" : ""); + a.com->printf ("?i%c %.2f %.2f\r%s", mode.rd(BOARD_ID), MotorA.Idbl, MotorB.Idbl, a.source == SOURCE_PC ? "\n" : ""); +} + +/**void vi_cmd (struct parameters & a) +* +* For setting motor voltage and current limits from pc terminal for test +*/ void vi_cmd (struct parameters & a) { -// if (a.respond) -// com->printf ("In setVI, setting V to %.2f, I %.2f\r\n", a.dbl[0], a.dbl[1]); setVI (a.dbl[0] / 100.0, a.dbl[1] / 100.0); + pc.printf ("setVI 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) { -// if (a.respond) -// com->printf ("In setV, setting V to %.2f\r\n", a.dbl[0]); - setV (a.dbl[0] / 100.0); + 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) { -// if (a.respond) -// a.com->printf ("In setI, setting I to %.2f\r\n", a.dbl[0]); - setI (a.dbl[0] / 100.0); + 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 the watchdog +/** +*void kd_cmd (struct parameters & a) // kick and enable the watch dog +* +*/ +void kd_cmd (struct parameters & a) // kick the watchdog. Reached from TS or pc. { - WatchDog = WATCHDOG_RELOAD + (I_Am() & 0x0f); -// a.com->printf ("Poked %d up Dog\r\n", WatchDog); + WatchDog = WATCHDOG_RELOAD + (mode.rd(BOARD_ID) & 0x0f); // Reload watchdog timeout. Counted down @ 8Hz + WatchDogEnable = true; // Receipt of this command sufficient to enable watchdog } -void who_cmd (struct parameters & a) +/** +*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 { - int i = I_Am (); - if (I_Am() == a.target_unit) - a.com->printf ("who%c\r\n", a.target_unit); + if (a.source == SOURCE_PC || mode.rd(BOARD_ID) == a.target_unit) + a.com->printf ("who%c\r%s", mode.rd(BOARD_ID), a.source == SOURCE_PC ? "\n" : ""); } -extern void rcin_report () ; +/** +*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 (); } -struct kb_command { - const char * cmd_word; // points to text e.g. "menu" - const char * explan; - void (*f)(struct parameters &); // points to function -} ; +void scmd (struct parameters & a) // filter coefficient fiddler +{ + switch ((int)a.dbl[0]) { + case 1: + MotorA.s[1] = MotorB.s[1] = a.dbl[1]; + break; + case 2: + MotorA.s[2] = MotorB.s[2] = a.dbl[1]; + break; + case 3: + MotorA.s[3] = MotorB.s[3] = a.dbl[1]; + break; + case 4: + MotorA.s[4] = MotorB.s[4] = a.dbl[1]; + break; + case 5: + MotorA.set_speed (a.dbl[1]); + MotorB.set_speed (a.dbl[1]); + break; + default: + pc.printf ("Wrong use of scmd %f\r\n", a.dbl[0]); + } + pc.printf ("Filter Coeffs 1 to 4\r\n"); + pc.printf ("1 %.3f\tPscale 0.01-0.5\r\n", MotorA.s[1]); + pc.printf ("2 %.3f\tP_gain 1.0-1000.0\r\n", MotorA.s[2]); + pc.printf ("3 %.3f\tDscale 0.01-0.5\r\n", MotorA.s[3]); + pc.printf ("4 %.3f\tD_gain 1.0-1000.0\r\n", MotorA.s[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\r\nAt menucmd function - listing commands, source %s:-\r\n", 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 9600, 8,n,1 +* 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[] = { - {"ls", "Lists available commands", menucmd}, - {"?", "Lists available commands, same as ls", menucmd}, +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}, @@ -302,23 +449,19 @@ {"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}, + {"kd", "kick the dog, reloads WatchDog", kd_cmd}, + // ***** 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}, - {"mode", "read or set params in eeprom", mode_cmd}, - {"erase", "set eeprom contents to all 0xff", erase_cmd}, {"tem", "report temperature", temperature_cmd}, - {"kd", "kick the dog, reloads WatchDog", kd_cmd}, - {"wden", "enable watchdog if modes allow", wden_lococmd}, - {"wddi", "disable watchdog always", wddi_lococmd}, - {"rpm", "read motor pair speeds", rpm_cmd}, {"mph", "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}, // OBSOLETE, replaced by 'gbb' - {"gbb", "get bogie bytes from eeprom and report", gbb_cmd}, - {"nu", "do nothing", null_cmd}, -}; - -//const int numof_loco_menu_items = sizeof(loco_command_list) / sizeof(kb_command); +// {"rvi", "read most recent values sent to pwms", rvi_cmd}, +// {"rdi", "read motor currents and power voltage", rdi_cmd}, + // ***** Endof +} ; /** @@ -328,7 +471,10 @@ struct kb_command const pc_command_list[] = { {"ls", "Lists available commands", menucmd}, {"?", "Lists available commands, same as ls", menucmd}, +#ifdef USING_DC_MOTORS {"mtypes", "report types of motors found", mt_cmd}, +#endif + {"s","1-4, filter param", scmd}, {"pot", "read drivers control pot", pot_cmd}, {"fw", "forward", fw_cmd}, {"re", "reverse", re_cmd}, @@ -337,195 +483,120 @@ {"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}, {"who", "search for connected units, e.g. 3who returs 'who3' if found", who_cmd}, - {"mode", "read or set params in eeprom", mode_cmd}, - {"erase", "set eeprom contents to all 0xff", erase_cmd}, - {"tem", "report temperature", temperature_cmd}, + {"mode", "read or set params in eeprom", mode19_cmd}, // Big change Jan 2019 +// {"erase", "set eeprom contents to all 0xff", erase_cmd}, + {"tem", "report temperature", temperature_cmd}, // Reports -50 when sensor not fitted {"kd", "kick the dog, reloads WatchDog", kd_cmd}, - {"wden", "enable watchdog if modes allow", wden_pccmd}, - {"wddi", "disable watchdog always", wddi_pccmd}, + {"ver", "Version", ver_cmd}, {"rcin", "Report Radio Control Input stuff", rcin_pccmd}, {"rpm", "read motor pair speeds", rpm_cmd}, {"mph", "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' +// {"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}, -}; +} ; -void setup_comms () { - pccom.com = & pc; - pccom.command_list = pc_command_list; - pccom.numof_menu_items = sizeof(pc_command_list) / sizeof(kb_command); - pccom.cl_index = 0; - pccom.gp_i = 0; // general puropse integer, not used to 30/4/2018 - pccom.resp_always = true; - lococom.com = & com2; - lococom.command_list = loco_command_list; - lococom.numof_menu_items = sizeof(loco_command_list) / sizeof(kb_command); - lococom.cl_index = 0; - lococom.gp_i = 0; // general puropse integer, toggles 0 / 1 to best guess source of rpm - lococom.resp_always = false; -} - - -void menucmd (struct parameters & a) -{ - if (a.respond) { - a.com->printf("\r\n\nDual BLDC ESC type STM3 2018\r\nAt menucmd function - listing commands:-\r\n"); - 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"); - } -} +// 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 boards running this code. +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 command_line_interpreter (void const *argument) -void cli_core (struct parameters & a) { - const int MAX_CMD_LEN = 180; - int ch, IAm = I_Am(); + +/** +* void cli_2019::test () { +* +* Daft check that class instantiation worked +*/ +void cli_2019::test () { + 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 = mode.rd(BOARD_ID); char * pEnd;//, * cmd_line_ptr; while (a.com->readable()) { ch = a.com->getc(); - if (a.cl_index > MAX_CMD_LEN) { // trap out stupidly long command lines + 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"); - a.cl_index = 0; + clindex = 0; } if(ch != '\r') { // was this the 'Enter' key? if (ch != '\n') // Ignore line feeds - a.cmd_line[a.cl_index++] = ch; // added char to command being assembled + 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' - a.cmd_line_ptr = a.cmd_line; - a.cmd_line[a.cl_index] = 0; // null terminate command string - if(a.cl_index) { // If have got some chars to lookup + cmdline_ptr = cmdline; + cmdline[clindex] = 0; // null terminate command string + if(clindex) { // If have got some chars to lookup int i, wrdlen; - if (isdigit(a.cmd_line[0])) { // Look for command with prefix digit - a.cmd_line_ptr++; // point past identified digit prefix - a.target_unit = a.cmd_line[0]; // '0' to '9' + 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(a.command_list[i].cmd_word); - if(strncmp(a.command_list[i].cmd_word, a.cmd_line_ptr, wrdlen) == 0 && !isalpha(a.cmd_line_ptr[wrdlen])) { // If match found + 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_PARAMS; k++) { a.dbl[k] = 0.0; } a.position_in_list = i; a.numof_dbls = 0; - pEnd = a.cmd_line_ptr + wrdlen; + pEnd = cmdline_ptr + wrdlen; while (*pEnd) { // Assemble all numerics as doubles a.dbl[a.numof_dbls++] = strtod (pEnd, &pEnd); while (*pEnd && !isdigit(*pEnd) && '-' != *pEnd && '+' != *pEnd) { pEnd++; } } - //com->printf ("\r\n"); // Not allowed as many may output this. - //for (int k = 0; k < param_block.numof_dbls; k++) - // com->printf ("Read %.3f\r\n", param_block.dbl[k]); -// param_block.times[i] = clock(); -// a.respond = false; 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)) - a.command_list[i].f(a); // execute command if addressed to this 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", a.cmd_line); + 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 - //com->printf("\r\n>"); - a.cl_index = 0; - } // End of else key was CR, may or may not be command to lookup - } // End of while (com->readable()) -} - -void command_line_interpreter_pc () { - cli_core (pccom); -} -void command_line_interpreter_loco () { - cli_core (lococom); -} - -void command_line_interpreter () -{ -/* const int MAX_CMD_LEN = 120; - static char cmd_line[MAX_CMD_LEN + 4]; - static int cl_index = 0; - int ch, IAm = I_Am(); - char * pEnd, * cmd_line_ptr; - static struct parameters param_block ; - com = &com2; - while (com->readable()) { -// ch = tolower(com->getc()); - ch = com->getc(); - if (cl_index > MAX_CMD_LEN) { // trap out stupidly long command lines - com->printf ("Error!! Stupidly long cmd line\r\n"); - cl_index = 0; - } - if(ch != '\r') // was this the 'Enter' key? - cmd_line[cl_index++] = ch; // added char to command being assembled - else { // key was CR, may or may not be command to lookup - param_block.target_unit = BROADCAST; // Set to BROADCAST default once found command line '\r' - cmd_line_ptr = cmd_line; - cmd_line[cl_index] = 0; // null terminate command string - if(cl_index) { // If have got some chars to lookup - int i, wrdlen; - if (isdigit(cmd_line[0])) { // Look for command with prefix digit - cmd_line_ptr++; // point past identified digit prefix - param_block.target_unit = cmd_line[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(a.command_list[i].cmd_word); - if(strncmp(a.command_list[i].cmd_word, a.cmd_line_ptr, wrdlen) == 0 && !isalpha(a.cmd_line_ptr[wrdlen])) { // If match found - for (int k = 0; k < MAX_PARAMS; k++) { - param_block.dbl[k] = 0.0; - } - param_block.position_in_list = i; -// param_block.last_time = clock (); - param_block.numof_dbls = 0; - pEnd = cmd_line_ptr + wrdlen; - while (*pEnd) { // Assemble all numerics as doubles - param_block.dbl[param_block.numof_dbls++] = strtod (pEnd, &pEnd); - while (*pEnd && !isdigit(*pEnd) && '-' != *pEnd && '+' != *pEnd) { - pEnd++; - } - } - //com->printf ("\r\n"); // Not allowed as many may output this. - //for (int k = 0; k < param_block.numof_dbls; k++) - // com->printf ("Read %.3f\r\n", param_block.dbl[k]); -// param_block.times[i] = clock(); - param_block.respond = false; - if (((param_block.target_unit == BROADCAST) && (IAm == '0')) || (IAm == param_block.target_unit)) - param_block.respond = true; // sorted 26/4/18 - // All boards to obey BROADCAST command, only specific board to obey number prefixed command - if ((param_block.target_unit == BROADCAST) || (IAm == param_block.target_unit)) - command_list[i].f(param_block); // execute command if addressed to this unit - i = numof_menu_items + 1; // to exit for loop - } // end of match found - } // End of for numof_menu_items - if(i == numof_menu_items) - com->printf("No Match Found for CMD [%s]\r\n", cmd_line); - } // End of If have got some chars to lookup - //com->printf("\r\n>"); - cl_index = 0; - } // End of else key was CR, may or may not be command to lookup - } // End of while (com->readable()) -// Thread::wait(20); // Using RTOS on this project -// }*/ -} + 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