WiThrottle Direct support

TwinDad Feb 7, 2017

  1. TwinDad

    TwinDad TrainBoard Member

    1,844
    551
    34
    OK, this is kind-of-working enough to share and get some debug feedback, especially since I still don't have a working Mega (new one coming soon).

    https://github.com/msunderwd/BaseStation-Uno/tree/withrottle2

    That's branch "withrottle2" from my copy of the Base Station code. It should otherwise be up to date with "master" of the main code line.

    What should work:
    • Can connect directly to the base station from ONE phone using WiThrottle (or, presumably-but-not-tested, Engine Driver)
    • Can issue throttle commands (speed+direction, function keys) and track power control on/off
    • Should also be able to simultaneously connect from JMRI or Controller or other "normal" throttle.
    What doesn't work:
    • Cannot connect from more than one phone (yet). (actually you might be able to but the code will share 1 register for all phones, so results might be... interesting)
    • Always uses Register 1
    • Turnouts, Routes, Outputs don't work yet.
    • No Consists or Roster either (Base Station doesn't have a roster anyway)
    • WiThrottle "heartbeats" don't do anything
    • None of the WiThrottle 2.0 request/query commands are supported yet
    • I haven't even begun to optimize it for code store or memory or ... anything.
    How it works:

    Quite simple, actually... the WiThrottle code intercepts the incoming bytes over INTERFACE and checks to see if it looks like a WiThrottle command. If it doesn't, then it just lets it pass through to the normal SerialCommand processing. If it does look like a WiThrottle command, then the WiThrottle code will pull in the string, translate it into the equivalent BaseStation string, forward that translation to SerialCommand:: parse(), and send any needed response back to the throttle.

    Fortunately, there are very few two-way communications between the WiThrottle and its server. Also, fortunately, none of the WiThrottle commands start with a "<" :)

    If any of you want to try it out and see if it works on your setup, I would appreciate the feedback. I expect there are plenty of bugs. Heck for all I know it might crash entirely on a Mega with Ethernet.
     
  2. TwinDad

    TwinDad TrainBoard Member

    1,844
    551
    34
    PS: If you want to incorporate it into your own code, you just need the following:
    • WiThrottle.cpp and WiThrottle.hpp
    • Add the WITHROTTLE_SUPPORT define to Config.h
    • Add the changes to SerialCommand.cpp that are wrapped by the WITHROTTLE_SUPPORT define.
     
  3. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Hi Mark

    Interesting stuff. I'd like to do a bit of debugging for you, but I'll be testing with Engine Driver connected to an ESP8266
    I presume they use the same protocols (E.Driver & WiThrottle).

    Connection goes without any problems. I can input a loco and operate the throttle.
    Here's the output from the ESP...

    NEngine Driver 46a3
    HUEngine Driver 46a3
    TV0
    Tr
    TS23
    TV3
    TV4
    TV5
    TV7
    TV8
    TV10
    TV11
    TV13
    TV14
    TV16
    TV18
    TV19

    Is this the kind of thing I should be seeing, or more specifically what your code is looking to convert?

    If yes, then I can port the code to my little ESP TCP server and see what it spits out the serial.

    Edit: Some function commands

    TF14<;>
    TF04<;>S23
    TF14<;>
    TF04<;>S23
    TF14<;>
    TF04<;>S23
    TF19<;>
    TF09<;>S23
    TF16<;>
    TF06<;>S23
    TF110<;>
    TF010<;>S23

     
    Scott Eric Catalano and sboyer2 like this.
  4. TwinDad

    TwinDad TrainBoard Member

    1,844
    551
    34
    Yes, that is generally what I expect to see. Good that you are testing Engine Driver... it uses the same protocol as WiThrottle, but of course uses it slightly differently.

    The N and H commands are part of the initial handshaking.
    The T commands are throttle speeds. TVxx is "set throttle to speed xx". This is the older style (WiThrottle 1.x) throttle message.
    I'm a little less certain of the function commands. They may yet require some debugging.

    I forgot to upload this, but I added a line to the beginning of SerialCommand:: parse() that prints the value of 'com' to the serial port. You can use this to check that the WiThrottle code is sending properly formatted Base Station messages. They will not have the < > wrappers, because SerialCommand strips those before calling parse() anyway.

    ETA: Yeah, I'm pretty sure I'm misinterpreting the Function messages. I'll see if I can get that fixed pronto.

    ETA ETA: Actually, it appears I *am* interpreting the Function messages correctly, so they *should* work. If they don't, well, obviously that's a bug...
     
    Last edited: Feb 7, 2017
  5. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Mark,

    When we get this working, I see the solution as being a part of Base Stations ESP 'Server' so without any additional server needed in the chain,
    one could connect a normal DCC++ throttle of any variety, alongside, WiThrottle or Engine driver. Whatever the input to ESP the serial out to Base Station will be compliant.
    That means that ESP can do the work and let an already busy Base Station get on with what it does best.

    Thoughts?
     
  6. TwinDad

    TwinDad TrainBoard Member

    1,844
    551
    34
    Steve,

    Yes, I agree... but would go further to say that the addition of WiThrottle parsing "hooks" into SerialCommand means that the Base Station itself can accept a direct connection from a WiThrottle app (or Controller or JMRI or a throttle), whether the ESP 'Server' is there or not.

    My goal is to have an Arduino Mega with a standard "dumb" Ethernet or WiFiShield talk directly to my phone.

    I could actually see the addition of the ESP's processing ability to be able to go further, handle more clients, maintain (more) state, etc. Or offload enough of the Ethernet stack support that one could use an UNO as the base station driver.

    So yeah, I think we are in line here, or close enough. The main thing being that whether you have the ESP 'Server' or whether you just have the Arduino, you don't need (e.g.) JMRI or anything else between the phone and the Base Station.

    And yes, because of the way I'm handling the incoming data, the WiThrottle code should work "politely" with other DCC++ inputs (Controller, throttles, etc.) that are using the normal DCC++ protocol.
     
  7. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Mark

    Well after a real hatchet job on the code :rolleyes: (innocent little me).

    First results are in and it looks real promising. Don't know where the newline gets in on the output string, but here's what I got.....

    Ready! Use 'telnet 192.168.11.19 23' to connect
    New client: 0
    Received
    RX: NEngine Driver 46a3

    Name = NEngine Driver 46a3

    *0
    Received
    RX: HUEngine Driver 46a3

    RX Device ID: Engine Driver 46a3

    Received
    RX: TV0

    new speed = 0
    <t 1 3 0
    1>Received
    RX: Tr

    Received
    RX: TS3

    MT+L3<;>
    Received
    RX: TV4

    new speed = 4
    <t 1 3 4
    1>Received
    RX: TV5

    new speed = 5
    <t 1 3 5
    1>Received
    RX: TV6

    new speed = 6
     
    Scott Eric Catalano and sboyer2 like this.
  8. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Cool, a few tweaks later.....

    Ready! Use 'telnet 192.168.11.19 23' to connect
    New client: 0
    Received
    RX: NEngine Driver 46a3

    Name = NEngine Driver 46a3

    *0
    Received
    RX: HUEngine Driver 46a3

    RX Device ID: Engine Driver 46a3

    Received
    RX: TV0

    new speed = 0
    <t 1 3 0 1>

    Received
    RX: Tr

    Received
    RX: TS47

    MT+L47<;>
    Received
    RX: TV1

    new speed = 1
    <t 1 47 1 1>

    Received
    RX: TV2

    new speed = 2
    <t 1 47 2 1>

    Received
    RX: TV3

    new speed = 3
    <t 1 47 3 1>


    Received
    RX: TF16<;>

    <f 47 178>

    Received
    RX: TF06<;>S47

    <f 47 176>
     
    Scott Eric Catalano and sboyer2 like this.
  9. TwinDad

    TwinDad TrainBoard Member

    1,844
    551
    34
    The opening and closing <> shouldn't be there on the string passed into SerialCommand:: parse(). They are never actually placed in the commandString that is passed in.

    I've pushed four fixes...

    1) I split up the while client.connected() and while client.available() conditions in SerialCommand. This helps with sending the welcome message info only once per client session.

    2) I fixed the Track Power button so it actually works now.

    3) I fixed the direction control so it actually works now.

    4) I got the Function keys working properly.

    ETA: Also got dual throttles (still single phone) working. This might still be a little buggy, but it appears to work.
     
    Last edited: Feb 8, 2017
  10. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Mark

    I added the'<>' back because my ESP version is pushing to Serial.
    I also had a few issues with the sprintf() 'command' string build.
    'command' kept getting added to, until over flow and stack crash.
    Fix below...
    I'll do some more tests with your fixes and post the whole ESP sketch for you to tidy up and approve later.

    Edit: I guess this bit does the same as my fix, but I didn't include it as I just strapped the 'reader' into the WiFi read in loop(). So my bad for not spotting earlier:(

    static void WiThrottle::readCommand(char c) {
    char x;
    sprintf(command, "%c", c);


    Code:
     while(INTERFACE.available() > 0) {
        x = INTERFACE.read();
        Serial.print(x);
        if (x == '\n' || x == '\0') {
          sprintf(command,"%s%c",command, x);
          Serial.println("Received");
          parseToDCCpp(command);
          command[0] = '\0';////Resets the 'start from here' point for sprintf()
        } else if (strlen(command) < MAX_COMMAND_LENGTH) {
          sprintf(command, "%s%c", command, x);
        }
      }
    
     
    Last edited: Feb 8, 2017
    Scott Eric Catalano and sboyer2 like this.
  11. TwinDad

    TwinDad TrainBoard Member

    1,844
    551
    34
    Oh, yeah, that makes sense... cool.

    Not sure I quite follow the second part... are you saying you *thought* you had a problem with the command buffer but it turns out my code already does the fix, so there's not a problem? I just need to know if I need to go fix something... Buffer handling is one of the things on my "to do" list to clean up and optimize. I was doing a bit too much "over-sharing" of the command buffer anyway, in a ham-fisted attempt to minimize memory usage.
     
  12. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    I did have a problem initially, as I hadn't included the bit in blue - above post.

    So I fixed in my own way by doing this
    command[0] = '\0'; as we come back from parseToDCCpp()

    Then later I spotted you had taken care of it (with the bit in blue - above post) I presume.

    Function handling is now crashing the stack again, so currently trying to reverse this mornings changes.
    It was kinda working before, wish I'd keep copies of 'last working' doh...
     
    Scott Eric Catalano and sboyer2 like this.
  13. TwinDad

    TwinDad TrainBoard Member

    1,844
    551
    34
    Oh ok.

    Yeah, the function handling is where I am sort of handling the buffers with duct tape and bailing wire. I have to format the command to send to the base station, but also create a response to send back to the throttle. It's possible something's not quite right there. Be sure to get the latest pull from the repository, though. I made some changes quite late last night.
     
  14. TwinDad

    TwinDad TrainBoard Member

    1,844
    551
    34
    So... what's the process for getting this included in the main DCC++ code base?

    Once I have it stable enough, do I simply issue a pull request and let Gregg decide what to do with it?

    Is there some sort of vetting process?

    I think it's lightweight enough not to be an issue, and it's compiled out if it's un-configured, so it should be pretty harmless...
     
  15. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Mark

    I've back tracked the function handling to something like stable again.
    On Engine Driver, the function keys are set non-latching (default), or most are anyways.

    So a key press/release event results in the following....

    Press TF16<;>
    Release TF06<;>S47

    I'm a bit pressed for time right now, but I'm pretty sure the offending line is...

    address = strtol(key + 4, NULL, 10);

    On the press event, strtol() is sent out of range, hence my crashing.
    I can do some more testing tomorrow.

    Thoughts.
     
  16. TwinDad

    TwinDad TrainBoard Member

    1,844
    551
    34
    Yep. That would break it. WiThrottle uses a different string (the multi-throttle equivalent) and that Strtol call is hard coded for that version. Should be easy enough to fix.

    ETA: I think I've fixed it already. I'll push the changes as soon as I get a chance to sanity-check against my iPhone. Don't want to break anything. Also, I really need to charge up my Android phone.
     
    Last edited: Feb 8, 2017
  17. TwinDad

    TwinDad TrainBoard Member

    1,844
    551
    34
    Steve, what version of Engine Driver are you using? I just grabbed a session log from an Android phone running Engine Driver V2.12 and it appears to be communicating using the V2.0 protocol, including the Multi-throttle commands, the same as my iPhone with WiThrottle v2.16

    I'm not seeing the old-style commands like you posted above.
     
    Scott Eric Catalano and sboyer2 like this.
  18. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Hi Mark
    Not thought to upgrade recently.
    Will look into it.

    Edit: V2.12 apparently or is that just an upgrade add? Will update when near my wifi
     
    Last edited: Feb 9, 2017
    Scott Eric Catalano and sboyer2 like this.
  19. TwinDad

    TwinDad TrainBoard Member

    1,844
    551
    34
    I'm not sure. All I know is I'm getting multi-throttle commands, different from yours and more like (but not exactly like) the iPhone.

    I added Bonjour support. It's disabled, though, in Config.h because you have to hack up the Ethernet library and fix a bug in the EthernetBonjour library to make it work. Explanations are in the commit log.

    Works pretty nicely, though!
     
    Scott Eric Catalano and sboyer2 like this.
  20. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Mark

    My Engine Driver must be up to date, as I've no update option for V2.12 in Play store.

    Is there anything in the 'normal' handshake that might determine the protocol used at session time?

    I.e. What would JMRI usually send back to the phone at logon? There is every chance I've disabled an important response having just done a rough hack of your code to run on the ESP.

    Update

    A little digging revealed.......

    void WiThrottle::sendIntroMessage(void) {
    // Send version number of protocol supported
    doPrintln("VN2.0");

    But I cant see a call for this function.
     
    Last edited: Feb 10, 2017
    Scott Eric Catalano likes this.

Share This Page