]> git.tdb.fi Git - model-railway-devices.git/commitdiff
Add decoder programming utility
authorMikko Rasa <tdb@tdb.fi>
Tue, 2 Jul 2013 14:58:57 +0000 (17:58 +0300)
committerMikko Rasa <tdb@tdb.fi>
Tue, 2 Jul 2013 14:58:57 +0000 (17:58 +0300)
arduprogram/Build [new file with mode: 0644]
arduprogram/main.cpp [new file with mode: 0644]

diff --git a/arduprogram/Build b/arduprogram/Build
new file mode 100644 (file)
index 0000000..09b1d5c
--- /dev/null
@@ -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 (file)
index 0000000..e9f62ca
--- /dev/null
@@ -0,0 +1,279 @@
+#include <msp/core/application.h>
+#include <msp/core/getopt.h>
+#include <msp/io/console.h>
+#include <msp/io/eventdispatcher.h>
+#include <msp/io/print.h>
+#include <msp/io/serial.h>
+#include <msp/time/timestamp.h>
+#include <msp/time/utils.h>
+
+using namespace std;
+using namespace Msp;
+
+class ArduProgram: public RegisteredApplication<ArduProgram>
+{
+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<unsigned>(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<unsigned>(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));
+}