From: Mikko Rasa Date: Tue, 2 Jul 2013 14:58:57 +0000 (+0300) Subject: Add decoder programming utility X-Git-Url: http://git.tdb.fi/?p=model-railway-devices.git;a=commitdiff_plain;h=912c9026215483f1755c2291370208f43e1a7b2c Add decoder programming utility --- diff --git a/arduprogram/Build b/arduprogram/Build new file mode 100644 index 0000000..09b1d5c --- /dev/null +++ b/arduprogram/Build @@ -0,0 +1,9 @@ +package "arduprogram" +{ + require "mspcore"; + require "sigc++-2.0"; + program "arduprogram" + { + source "main.cpp"; + }; +}; diff --git a/arduprogram/main.cpp b/arduprogram/main.cpp new file mode 100644 index 0000000..e9f62ca --- /dev/null +++ b/arduprogram/main.cpp @@ -0,0 +1,279 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace Msp; + +class ArduProgram: public RegisteredApplication +{ +private: + struct Options + { + string serial_port; + + Options(int, char **); + }; + + enum OutputState + { + INIT, + IDLE, + SEND_REG, + SEND_VALUE + }; + + enum InputState + { + WAIT, + MENU, + PROG_ADDR, + PROG_REG, + PROG_VALUE + }; + + Options options; + + IO::Serial serial; + unsigned out_addr; + bool out_active; + OutputState out_state; + Time::TimeStamp timeout; + + InputState in_state; + unsigned value; + + IO::EventDispatcher ev_disp; + +public: + ArduProgram(int, char **); + + virtual int main(); + +private: + void send_command(const string &); + void set_output(); + void set_input_state(InputState); + void user_input_available(); + void set_output_state(OutputState); + void serial_input_available(); +}; + + +ArduProgram::Options::Options(int argc, char **argv) +{ + GetOpt getopt; + getopt.add_argument("port", serial_port).set_help("Serial port to use"); + getopt(argc, argv); +} + + +ArduProgram::ArduProgram(int argc, char **argv): + options(argc, argv), + serial(options.serial_port) +{ + IO::cin.signal_data_available.connect(sigc::mem_fun(this, &ArduProgram::user_input_available)); + ev_disp.add(IO::cin); + serial.signal_data_available.connect(sigc::mem_fun(this, &ArduProgram::serial_input_available)); + ev_disp.add(serial); +} + +int ArduProgram::main() +{ + IO::print("Putting locomotive in programming mode (this will take a few seconds)\n"); + + send_command("\x02"); + Time::sleep(Time::sec); + send_command("\x01"); + + set_output_state(INIT); + set_input_state(WAIT); + + while(!done) + { + Time::TimeStamp t = Time::now(); + if(timeout) + { + if(t>=timeout) + { + timeout = Time::TimeStamp(); + + if(out_active) + { + out_active = false; + timeout = t+Time::sec; + } + else if(out_state==SEND_REG) + { + if(in_state==WAIT) + set_output_state(SEND_VALUE); + } + else + { + set_output_state(IDLE); + set_input_state(MENU); + } + } + else + ev_disp.tick(timeout-t); + } + else + ev_disp.tick(); + } + + IO::print("Resetting locomotive\n"); + + send_command("\x02"); + Time::sleep(Time::sec); + send_command("\x01"); + + return 0; +} + +void ArduProgram::send_command(const string &cmd) +{ + serial.put(cmd.size()^0xFF); + serial.write(cmd); +} + +void ArduProgram::set_output() +{ + string cmd; + cmd.reserve(4); + cmd += (out_active ? 0x12 : 0x11); + cmd += out_addr; + cmd += '\0'; + if(!out_active) + cmd += '\0'; + send_command(cmd); +} + +void ArduProgram::set_input_state(InputState s) +{ + in_state = s; + switch(in_state) + { + case MENU: + IO::print("Do what [a/r/p/q/?] "); + break; + case PROG_ADDR: + IO::print("Enter new address: "); + break; + case PROG_REG: + IO::print("Enter register: "); + break; + case PROG_VALUE: + IO::print("Enter value: "); + break; + case WAIT: + IO::print("Please wait...\n"); + break; + default:; + } +} + +void ArduProgram::user_input_available() +{ + string line; + if(!IO::cin.getline(line)) + return; + + if(in_state==MENU) + { + if(line.size()==1) + { + int c = tolower(line[0]); + if(c=='q') + exit(0); + else if(c=='a') + { + value = 1; + set_output_state(SEND_REG); + set_input_state(PROG_ADDR); + } + else if(c=='p') + set_input_state(PROG_REG); + else if(c=='r') + { + value = 8; + set_output_state(SEND_REG); + set_input_state(WAIT); + } + else if(c=='?') + { + IO::print("a: Enter new address\n" + "r: Reset to factory settings\n" + "p: Program other CV\n" + "q: Quit\n"); + set_input_state(MENU); + } + } + else + set_input_state(in_state); + } + else + { + try + { + value = lexical_cast(line); + + if(in_state==PROG_REG) + { + set_output_state(SEND_REG); + set_input_state(PROG_VALUE); + } + else if(in_state==PROG_ADDR || in_state==PROG_VALUE) + { + value = lexical_cast(line); + if(!out_active) + set_output_state(SEND_VALUE); + set_input_state(WAIT); + } + } + catch(const lexical_error &e) + { + IO::print("Bad input, try again"); + set_input_state(in_state); + } + } +} + +void ArduProgram::set_output_state(OutputState s) +{ + out_state = s; + + switch(out_state) + { + case INIT: + out_addr = 0; + out_active = true; + timeout = Time::now()+2*Time::sec; + set_output(); + break; + case IDLE: + out_addr = 0; + out_active = false; + break; + case SEND_REG: + case SEND_VALUE: + out_addr = value; + out_active = true; + timeout = Time::now()+Time::sec; + break; + default:; + } +} + +void ArduProgram::serial_input_available() +{ + int c = serial.get(); + if(c==0x80) + set_output(); + else + throw runtime_error(format("Command error: %x\n", c)); +}