madleech / ArduinoCMRI

Arduino library for connecting your computer to your model railroad.
27 stars 18 forks source link

Errors in the JMRI console? #6

Closed k4kfh closed 8 years ago

k4kfh commented 8 years ago

Whenever I close a JMRI turnout that's assigned to a CMRI bit, the console gives an error like these:

2016-07-07 16:21:27,084 jmrix.AbstractMRTrafficController     ERROR - reply complete in unexpected state: 10 was 41 52 00 00 00 [Receive]
2016-07-07 16:21:27,089 jmrix.AbstractMRTrafficController     ERROR - reply complete in unexpected state: 10 was 41 52 00 00 00 [Receive]
2016-07-07 16:21:33,494 jmrix.AbstractMRTrafficController     ERROR - reply complete in unexpected state: 15 was 41 52 00 00 00 [Receive]
2016-07-07 16:21:33,498 jmrix.AbstractMRTrafficController     ERROR - reply complete in unexpected state: 15 was 41 52 00 00 00 [Receive]
2016-07-07 16:21:33,554 jmrix.AbstractMRTrafficController     ERROR - reply complete in unexpected state: 15 was 41 52 00 00 00 [Receive]

I've also run into some issues where the Arduino seems to be "imagining" bits. It will act on some random bit being set even if I don't have that bit set up to do anything in JMRI. I may be posting this in the wrong place; it very well could be a bug with JMRI, but I wanted to check here first.

Thanks for your patience. I know I've posted a ton of issues, so I appreciate your help.

madleech commented 8 years ago

Thanks for the report. I have attempted to debug the issue but can't recreate it. Can you give me some details, such as:

As for imaginary bits, could you post the sketch that is exhibiting this behaviour? I haven't come across this issue before, so I'd be keen to get to the bottom of it.

k4kfh commented 8 years ago

Update: According to the JMRI devs, those console errors mean that JMRI is expecting a reply, but didn't receive it in time. The use case for this is a homemade indexing system for an Atlas turntable (you know, the one that's been around forever and is a total pain to DCC) so I suspect the problem is that the Arduino is waiting on the turntable to finish rotating when it should be handling CMRI stuff. I'll post the full sketch/JMRI information shortly, but I wouldn't be surprised to find out that's where the "imaginary bits" are coming from and that it's not an inherent fault in your library.

k4kfh commented 8 years ago

Arduino Sketch

The Arduino sketch can be found on my turningTables Repository. I am using Arduino IDE 1.6.7 with an official Arduino Uno (on a USB 2.0 port, if that matters) on Ubuntu GNOME 16.04 amd64.

Most of the sketch has to do with hardware/motor stuff, so here is a "condensed" version:

void setup() {
  //set all the pins to the correct mode
  pinMode(motor_cw_pin, OUTPUT); //motor clockwise
  pinMode(motor_ccw_pin, OUTPUT); //motor counterclockwise
  pinMode(hall_pin, INPUT); //hall effect sensor input (used as a tachometer)
  pinMode(service_btn_cw_pin, INPUT_PULLUP); //these buttons are for calibrating the turntable
  pinMode(service_btn_ccw_pin, INPUT_PULLUP);

  //read EEPROM for last position
  EEPROM.get(0, currentPosition);

  //making hall effect sensor interrupt
  attachInterrupt(digitalPinToInterrupt(hall_pin), hallTrigger, FALLING);

  //start Serial connection for CMRI
  Serial.begin(9600, SERIAL_8N2);

  //set last bit states initially - this is part of my hacked together system for detecting if a bit changes
  for (int i=0;i < 47;i++) {
    lastBitStates[i] = cmri.get_bit(i);
  }

  //play startup noise - this is just so I know when the turntable is ready, it plays on a little piezo speaker. it does use delay()
  startup_beep();
}

void loop() {
  //This code handles reading the alignment buttons. The buttons move the turntable not one track position, but one rotation of the measured gear, which is useful if the mechanism gets a few turns out of alignment.
  buttonState_cw = digitalRead(service_btn_cw_pin);
  if (buttonState_cw != lastButtonState_cw) {
    if (buttonState_cw == LOW) {
      click_cw();
    }
  }
  lastButtonState_cw = buttonState_cw;

  buttonState_ccw = digitalRead(service_btn_ccw_pin);
  if (buttonState_ccw != lastButtonState_ccw) {
    if (buttonState_ccw == LOW) {
      click_ccw();
    }
  }
  lastButtonState_ccw = buttonState_ccw;
  //end button handling code

  //CMRI interface code
  cmri.process();

  //all the bits for the stalls - there are 24 stall tracks so we read all those bits here
  for (int i=0; i < num_tracks; i++) {
    int currentBit = cmri.get_bit(i);
    if (currentBit == 1) {
      if (currentBit != lastBitStates[i]) {
        rotate(i,0); //this is the tricky part. this function does not complete until the turntable is done rotating, so you can imagine this can take a little bit.
      }
    }
  }

  //bit 25 is supposed to rotate the turntable 180 degrees. This is also a problem area as this function often gets called several times (I think because of the time it takes to complete screwing up the timing of the CMRI data tx/rx) which is useless because then  the turntable just does a full 360 (or more) instead of a 180.
  if (cmri.get_bit(25) == 1) {
    if (cmri.get_bit(25) != lastBitStates[25]) {
      turn_ccw(12);
    }
  }

  //set last bit states at the end of the "cycle".
  //this goes along with my makeshift "is this bit changed?" system.
  for (int i=0;i < 47;i++) {
    lastBitStates[i] = cmri.get_bit(i);
  }
}

If you need more information about any of that I can answer questions. I'm new to C in general so my commenting/organizational skills are probably a little lacking.


JMRI/PC Info

PC Specs:

I do not have a panel set up for this project yet, so I just included screenshots of the turnouts table and the JMRI version info.

The turnouts are set up as steady-state output, 1-bit turnouts. There are no Logix set up for them.

If I throw the "Turn 180 degrees" turnout, sometimes it will be called 1 time, sometimes several times. This doesn't make a difference with the other turnouts because once it's at that position, the rotate() function in the Arduino code simply finds that 0 moves are required to get to the desired position, and it does nothing. But with the "turn 180 degrees" turnout, it can just keep running over and over again. I haven't narrowed down the "imagining bits" issue to specific steps (yet) but it frequently will turn to a stall track that corresponds to a bit that isn't even configured with JMRI as a turnout, and this happens randomly, but especially frequently after throwing the "turn 180 degrees" turnout.

madleech commented 8 years ago

I'll do some tests tonight, but I believe the issue might be due to the turn_cw and turn_ccw functions being blocking. So while the turntable is turning, it is so busy checking its position that no CMRI messages are processed. Once it finishes turning, the queued up messages (sitting in the Arduino's serial buffer) get processed. These messages are almost certainly POLL requests, resulting in a bunch of GET replies being sent out (41 52 ...) when JMRI isn't expecting any; it only expects a single GET message in response to each POLL message that it sends out.

While these unexpected messages won't hurt JMRI, it will cause it to complain. Do you have any sensors set up? Can you show the output of C/MRI > List Assignments?

madleech commented 8 years ago

Just seen your earlier message; seems the JMRI devs came to the same conclusion as me. You'll need to convert your code to set a target value for the motor, and then check that each time through the main loop. It's not too complicated to do, just requires a different way of looking at the logic.

The other option is to not have any CMRI sensors set up; then JMRI won't send out POLL requests for it.

k4kfh commented 8 years ago

Okay, sounds good, I'll try that and let you know if that fixes it. Out of curiosity, how long does it typically take to run cmri.process()?

madleech commented 8 years ago

It varies depending on whether there is a POLL message that needs responding to or not. If there is no data to process, it is near enough to instant. If it needs to send out a poll response, then it'll take fractionally longer, but it shouldn't be significant.

k4kfh commented 8 years ago

I just finished rewriting the rotation system to be non-blocking. After some tinkering (which had less to do with your library and more to do with my limited C experience) I was able to get it working with JMRI. The only blocking thing in the program now is the initial startup_beep() function that runs at the very end of setup(), and plays a quick series of notes on a piezo speaker to let me know the controller just booted up. Everything seems to work! A couple times I have gotten one of those errors in JMRI immediately after the board starts, so I think that's due to the startup_beep() function being blocking. In an hour or so of testing I was not able to reproduce any of those errors by throwing/closing turnout bits, which is what caused them before.

So in short, this was my fault for not writing everything to be non-blocking. Thank you so much for your patience and for your help with this program. I really appreciate it!

madleech commented 8 years ago

Awesome glad to help. So the random toggling of lines has gone away too?

k4kfh commented 8 years ago

Seems to. The only thing that I have not gotten totally working is the "turn 180 degrees" function, which sometimes gets called twice, but if I do it in a strange way it works fine. I'm pretty sure that's issues in my programming.