Touch screen drivers control dashboard for miniature locomotive. Features meters for speed, volts, power. Switches for lights, horns. Drives multiple STM3_ESC brushless motor controllers for complete brushless loco system as used in "The Brute" - www.jons-workshop.com

Dependencies:   TS_DISCO_F746NG mbed Servo LCD_DISCO_F746NG BSP_DISCO_F746NG QSPI_DISCO_F746NG AsyncSerial FastPWM

cli_TS_nortos.cpp

Committer:
JonFreeman
Date:
2019-03-04
Revision:
14:6bcec5ac21ca
Parent:
12:a25bdf135348

File content as of revision 14:6bcec5ac21ca:

//Loco_TS_2018
#include "mbed.h"
#include "AsyncSerial.hpp"
#include <cctype>
#include "Electric_Loco.h"
using namespace std;

const int   BROADCAST   = '\r';

//  WithOUT RTOS
extern  Serial pc;
extern  AsyncSerial com2escs;
extern  error_handling_Jan_2019   Controller_Error    ;
extern  const   char   const_version_string[] ;  //  Version string, readable from serial ports

extern  STM3_ESC_Interface    My_STM3_ESC_boards   ;

/*genio::genio    (int dtype, int dlen)  {    //  constructor
    d_type = dtype;
    d_len = dlen;
    for (int i = 0; i < MAX_PARAMS; i++)    {
        si[i] = 0;
        dbl[i] = 0.0;
    }
    count = 0;  //  running total of times called
    available = false;  //  bool
    //available = 0; // if using integer rather than bool
}

void    genio::store   (struct parameters & a)  {   //  copies up to MAX_PARAMS into int and double arrays
    count++;            //  number of times this response rec'd this session
    available = true;
    for (int i = 0; i < d_len; i++) {   //  dlen //  loaded on setup indicating expected num of parameters to handle. Why not just use MAX_PARAMS ?
        si[i] = (int32_t)a.dbl[i];
        dbl[i] = a.dbl[i];
    }
}

bool    genio::read    (int32_t ** addr)  {
    if  (!available)
        return  false;
    available = false;
    *addr = si;         //      int32_t    ui[MAX_PARAMS];
    return  true;
}
*/

command_line_interpreter_core::command_line_interpreter_core    (int p, int menulen, struct kb_command const * clistp)  {
    clist = clistp;
    a.numof_menu_items = menulen;
    a.numof_cl_values_read = 0;
    portio = p;
    cl_index = 0;
}

int     command_line_interpreter_core::clreadable    ()  {
    if  (portio == 0)
        return  pc.readable();
    return  com2escs.readable();
}  

void    command_line_interpreter_core::clputc    (int c)  {
    if  (portio == 0)
        pc.putc (c);
    else
        com2escs.putc   (c);
}  

int     command_line_interpreter_core::clgetc    ()  {
    if  (portio == 0)
        return  pc.getc();
    return  com2escs.getc();
}  


void    null_cmd (struct parameters & a)
{
    pc.printf   ("At null_cmd, parameters : First %.3f, second %.3f\r\n", a.dbl[0], a.dbl[1]);
}

void    menucmd (struct parameters & a);

void    kd_cmd (struct parameters & a)  //  kick the watchdog
{
}

void    upmph_cmd (struct parameters & a)  //  controller rec'd mph from driver boards
{
    My_STM3_ESC_boards.mph_update  (a.dbl[1]);
}


void    TSver_cmd (struct parameters & a)  //   Report current TS software version to pc
{
    pc.printf   ("ver %s\r\n", const_version_string);
}

void    boards_cmd  (struct parameters & a) {
    int boards[MAX_ESCS], count = 0;
    My_STM3_ESC_boards.get_boards_list   (boards);
    while  (boards[count] && count < MAX_ESCS)
        count++;
    pc.printf   ("Board Count = %d\r\n", count);
    if  (count) {
        pc.printf   ("Boards found [");
        for (int i = 0; i < count; i++)
            pc.printf   ("%c ", boards[i]);
        pc.printf   ("]\r\n");
    }
    else
        pc.printf   ("No STM3_ESC boards found\r\n");
}

void    who_cmd (struct parameters & a)  //   Have read "whon" back from STM3_ESC where 'n' is STM3_Esc board ID number (ascii digit)
{
    int v = (int)a.dbl[0] | '0';
    if  (a.numof_cl_values_read == 1 && isdigit(v)) {
//        pc.printf   ("who%c, vals read %d\r\n", (int)a.dbl[0] | '0', a.numof_cl_values_read);
        My_STM3_ESC_boards.set_board_ID  (v);//        set_board_id    (v);  //  just to test and prove can not add same board twice
    }
    else
        pc.printf   ("Garbage response to 'who', numof vals %d, value %c\r\n", a.numof_cl_values_read, v);
}

void    ver_req_cmd (struct parameters & a)  //   Touch Screen controller requesting version string from one specific ESC board
{
    pc.printf   ("ver_req_cmd not yet implemented\r\n");
}

void    queryv  (struct parameters & a) {   //  Handle response from ESC resulting from prior TSC "n?v\r" sent request - ESC'n' system voltage
    pc.printf   ("Responding ESC id=%d, volts=%.2f\r\n", (int)a.dbl[0], a.dbl[1]);
}
void    queryi  (struct parameters & a) {   //  Handle response from ESC resulting from prior TSC "n?i\r" sent request - ESC'n' motor pair currents
    pc.printf   ("Responding ESC id=%d, I MotorA=%.2f, I MotorB=%.2f\r\n", (int)a.dbl[0], a.dbl[1], a.dbl[2]);
}

struct kb_command  {
    const char * cmd_word;         //  points to text e.g. "menu"
    const char * explan;
    void (*f)(struct parameters &);   //  points to function
}  ;

/**
*   struct  kb_command const command_list[] = {
*
*   struct  kb_command const xx_list[]
*
*   Each line in list has 3 elements
*       e.g. "ver\r" is message we may expect to be recieved here as a command or command response sent from somewhere else
*       "waffle" is purely to inform you something about this message
*       cmd is pointer to function, the what to do in response to the incoming message
*           function type is void (*foo) (struct parameters &)
*       This gives function access to parameters recieved with the message, e.g. "?i 7.45 8.23\r"
*   User is at liberty to send using e.g. 'STM3_ESC_Interface::message' anything to any remote connected entity at any time.
*   It is up to the user to ensure whatever is out there can make sense of it !
*   The list of commands and responses the 'Brute_TS_Controller' is expected to recognise and act upon
*   One such list for commands from pc terminal, another for comms with some number of STM3_Esc boards
*/
struct  kb_command const command_list[] = {
    {"ls", "Lists available commands", menucmd},
    {"?", "Lists available commands, same as ls", menucmd},     //  Send this list, a menu
    {"ver", "report const_version_string", TSver_cmd},          //  Report TS controller version
    {"boards", "List STM3_ESC boards connected", boards_cmd},
    {"kd", "kick the dog", kd_cmd},                             //  Broadcast Command to STM3_ESCs, resets and enables WatchDog timeout
    {"?v", "Ask ESC for system voltage reading", queryv},           //  Makes no sense for pc to ask this of TS controller, just to test function
    {"?i", "Ask ESC for pair of motor current readings", queryi},   //  Makes no sense for pc to ask this of TS controller, just to test function
    {"nu", "do nothing", null_cmd},
}   ;

struct  kb_command const loco_command_list[] = {
    {"who", "Reads 'whon' response from STM3_ESC where 'n' is board num ascii from eeprom", who_cmd},
    {"ver", "report const_version_string", ver_req_cmd},    //  Request software version of one specific ESC board
    {"mph", "bogie mph reading", upmph_cmd},    //  22/06/2018
    {"?v", "Ask ESC for system voltage reading", queryv},
    {"?i", "Ask ESC for pair of motor current readings", queryi},
}   ;


void    menucmd (struct parameters & a)
{
    pc.printf("\r\n\nTS_2018 Locomotive Touch Screen Controller\r\nAt menucmd function - listing commands:-\r\n");
    for(int i = 0; i < a.numof_menu_items; i++)
        pc.printf("[%s]\t\t%s\r\n", command_list[i].cmd_word, command_list[i].explan);
    pc.printf("End of List of Commands\r\n");
}


/**
New - March 2018
Using opto isolated serial port, paralleled up using same pair to multiple 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, 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
*/
command_line_interpreter_core   pcli    (0, sizeof(command_list) / sizeof(kb_command), command_list);
command_line_interpreter_core   ploco   (1, sizeof(loco_command_list) / sizeof(kb_command), loco_command_list);

void    command_line_interpreter_core::sniff    ()  {   //  look for recieved commands and act upon them
    int ch;
    char * pEnd;
    while   (clreadable())    {
        ch = clgetc   ();
        if(ch != '\r')  {//  was this the 'Enter' key?
            if  (ch != '\n')    {   //  Ignore line feeds
                cmd_line[cl_index++] = ch;  //  added char to command being assembled
                if  (cl_index >= MAX_CMD_LINE_LEN-2)  {
                    cl_index = 0;     //  Line is garbage anyway, so might as well junk the lot ?
                    pc.printf   ("cl_index=%d TOO LONG in clicore\r\n", cl_index);
                    Controller_Error.set    (FAULT_COM_LINE_LEN, 1);
                }
            }       //  endof if(ch != '\n')    ignore line feeds
        }           //  endof if(ch != '\r')    
        else    {   //  key was CR, may or may not be command to lookup
            target_unit = BROADCAST;    //  Broadcast
            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
                    target_unit = cmd_line[0];  //  '0' to '9'
                    //pc.printf ("Got prefix %c\r\n", a.cmd_line[0]);
                }
                for (i = 0; i < a.numof_menu_items; i++)   {   //  Look for input match in command list
                    wrdlen = strlen(clist[i].cmd_word);
                    if(strncmp(clist[i].cmd_word, cmd_line_ptr, wrdlen) == 0 && !isalpha(cmd_line_ptr[wrdlen]))  {   //  If match found
                        for (int k = 0; k < MAX_PARAMS; k++)    {
                            a.dbl[k] = 0.0;
                        }
                        //position_in_list = i;
                        a.numof_cl_values_read = 0;
                        pEnd = cmd_line_ptr + wrdlen;
                        while   (*pEnd)  {          //  Assemble all numerics as doubles
                            a.dbl[a.numof_cl_values_read++] = strtod    (pEnd, &pEnd);
                            while   (*pEnd && !isdigit(*pEnd) && '-' != *pEnd && '+' != *pEnd)  {
                                pEnd++;
                            }
                        }
                        //pc.printf   ("\r\n");   //  Not allowed as many may output this.
                        //for (int k = 0; k < param_block.a.numof_cl_values_read; k++)
                        //    pc.printf   ("Read %.3f\r\n", param_block.dbl[k]);
//                            param_block.times[i] = clock();
//                            if  ((param_block.target_unit == BROADCAST) && (I_Am() == '0'))
//                                param_block.respond = true;
                        clist[i].f(a);   //  execute command
                        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) {
                    pc.printf("No Match Found for CMD [%s]\r\n", cmd_line);
                    Controller_Error.set    (FAULT_COM_NO_MATCH, 1);
                }
            }           //  End of If have got some chars to lookup
            cl_index = 0;
        }               // End of else key was CR, may or may not be command to lookup
    }                   //  End of while (pc.readable())
}