totalreverse / ttyT1941

Simple demonstrator to show how to control a Tacx T1941 motor brake via a serial connection
GNU General Public License v3.0
16 stars 3 forks source link

T1932 Brake type = Magnetic or Motor #20

Closed WouterJD closed 3 years ago

WouterJD commented 3 years ago

I would appreciate your view on the decision whether a T1932 is connected to a magnetic or motorbrake.

Current options:

BUT: there appear to be more types (e.g. 49) AND: not always a correct message is returned, even after retry causing FortiusAnt to behave like a magnetic brake, where a motor brake is connected

Issue: https://github.com/WouterJD/FortiusANT/issues/200

Code: https://github.com/WouterJD/FortiusANT/blob/5.1%2B4.2-Quality-upgrade/pythoncode/usbTrainer.py#L2516 https://github.com/WouterJD/FortiusANT/blob/5.1%2B4.2-Quality-upgrade/pythoncode/usbTrainer.py#L2141

Would 0.1 seconds wait-time be too short? According to good old code comment, 100ms should be safe ?? '# TRAINER- SHOULD WRITE THEN READ 70MS LATER REALLY

totalreverse commented 3 years ago

Looks like a problem with junk data or some kind of out-of-sync of commands and answers ...

For my brake FortiusAnt sometimes prints nothing and sometimes wrong values like

Motor Brake Unit Firmware=6918 Serial=37184 year=2078 type=T19164 Version2=0 MotorBrake=True

TTY1941 says:

firmwareVersion= 00.00.09.65,   serial= 410502330 (Tacx T1941 Year 2005 #02330),   Date= 0c.08 Unknown= 00.00

100ms should be safe to not overrun the serial communication. But it is difficult to sync commands and answers of the brake until you wait for an expected answer after doing some dummy reads to remove old data or junk communication pipe of the headunit-to-host communication.

Short test ... after repeating the version request in usbTrainer.py Line 2486 like this

            self.SendToTrainer(True, modeMotorBrake)
            time.sleep(0.1)                        # Allow head unit time to process
            self._ReceiveFromTrainer_MotorBrake()

            self.SendToTrainer(True, modeMotorBrake)
            time.sleep(0.1)                        # Allow head unit time to process
            self._ReceiveFromTrainer_MotorBrake()

            self.SendToTrainer(True, modeMotorBrake)
            time.sleep(0.1)                        # Allow head unit time to process
            self._ReceiveFromTrainer_MotorBrake()

            self.SendToTrainer(True, modeMotorBrake)
            time.sleep(0.1)                        # Allow head unit time to process
            self._ReceiveFromTrainer_MotorBrake()

            self.SendToTrainer(True, modeMotorBrake)
            time.sleep(0.1)                        # Allow head unit time to process
            self._ReceiveFromTrainer_MotorBrake()

I got

13:19:04,860: Motor Brake Unit Firmware=0 Serial=85760 year=2088 type=T19164 Version2=0 MotorBrake=True
13:19:04,962: Motor Brake Unit Firmware=2405 Serial= 2330 year=2005 type=T1941 Version2=3080 MotorBrake=True
13:19:05,166: Motor Brake Unit Firmware=2405 Serial= 2330 year=2005 type=T1941 Version2=3080 MotorBrake=True
13:19:05,269: Motor Brake Unit Firmware=2405 Serial= 2330 year=2005 type=T1941 Version2=3080 MotorBrake=True

Result: After sending 5 request, I only got 4 answer: 1 wrong and 3 valid.

totalreverse commented 3 years ago

To conclude the whole thing:

There are a lot of buffers on the way

  1. from the host to the head unit,
  2. from the head unit to the brake and then
  3. back to the head unit and
  4. back to the host.

If you send a command from the host to the head unit you cannot ensure, that the next USB read is the direct answer on your request. If you startup the brake it even sends some junk.

Better you do some dummy reads on the USB before sending the first command. And maybe you should even send the version request more than one time, until you got a valid and expected version answer from the brake.

The "random" version messages I got in my test above are very probably old answers on a control command from the last run. So, a simple improvement may be to check the type of answer before decoding it to a specific type. Every answer on a version request starts (in bytes 24..27) with "0x03 0x0c 0x00 0x00" and every answer on a control command starts with "0x03 0x13 0x02 0x00".

WouterJD commented 3 years ago

Thanks for quick reply, I will add a sort-of initialization loop.

Now you mention it -we also found this- the MotorBrakeUnitSerial (see https://github.com/totalreverse/ttyT1941/wiki#t1941-motor-brake-version-message) is not always tt-YY-##### the actual serial number can also be ####; something for the wiki if you like.

I was put on the wrong leg because the MotorBrakeUnitSerial is received as int, but it better be processed as str(MotorBrakeUnitSerial).

WouterJD commented 3 years ago

In the meantime I heard from @bikebeppe64 that a magnetic brake returns MotorBrakeUnitSerial = 0. How easy can it be.

WouterJD commented 3 years ago

So, a simple improvement may be to check the type of answer before decoding it to a specific type.

Will do

totalreverse commented 3 years ago

In the meantime I heard from @BikeBeppe64 that a magnetic brake returns MotorBrakeUnitSerial = 0. How easy can it be.

Ok. In this case the head unit must emulate a serial number, because the old magnetic breaks are just too dump to report a serial.

WouterJD commented 3 years ago

Thanks

WouterJD commented 3 years ago

In the meantime I heard from @BikeBeppe64 that a magnetic brake returns MotorBrakeUnitSerial = 0. How easy can it be.

Pffff... this appears to be a misunderstanding

What would you think that T1932 returns for a magnetic brake?

totalreverse commented 3 years ago

Just went downstairs and connected my old eddy current brake to a T1932 head unit. You need to switch on the magnetic brake. Then a version command (hex) 02 00 00 00 triggers a 64 byte answer, which I splitted in three parts here Bytes 0..23 is the standard head unit frame Bytes 24..48 is the (emulated) brake frame and the last part 49..63 are just padding bytes

07 db 07 00 00 02 00 00 09 00 00 00 00 00 00 00 00 00 0d 0a 00 00 00 00 
03 0c 00 00 02 19 00 00 00 00 00 00 00 00 00 00 35 69 00 00 00 00 02 55 64 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

The important parts are bytes 24..27 (the standard version command answer header) and bytes 28..31, which converts to 0x00001902 if you read them as little endian encoded uint32. I think ,it is a good idea to check these 8 bytes.

Edit: FortiusAnt converts Hex 0x1902 to a Decimal and says:

Motor Brake Unit Firmware=6402 Serial= 0 year=2000 type=T190 Version2=0 MotorBrake=False

WouterJD commented 3 years ago

@totalreverse you're a great help; thank you very much. I'd like to have a beer together; if you do - you know how to find me. I will implement; and you can add to the wiki :-)

totalreverse commented 3 years ago

I'd like to have a beer together; if you do - you know how to find me.

Summer and better times will come. Would be a long ride for a beer, though; at least 330km. I live in southern germany and work in Lower Saxony.

WouterJD commented 3 years ago

That's a deal🍺🍻

WouterJD commented 3 years ago

bytes 28..31, which converts to 0x00001902

Which is quite funny, since that suggests the T1932 headunit operates in a sort of T1902 head unit mode, although the interface is different. Good reason to display the firmware in hex-format.

WouterJD commented 3 years ago

The code now is

        self.MotorBrake = True                                # This sets the default
        if len(data) < 40:
            pass
        else:

            if self.MotorBrakeUnitType in (41, 46, 49):
                self.MotorBrake = True                        # Explicitly Motor brake

            if self.MotorBrakeUnitSerial == 0:
                self.MotorBrake = False                       # Explicitly Magnetic brake

        if self.clv.Tacx_MotorBrake:    self.MotorBrake = True    # Overrule from -t command line option
        if self.clv.Tacx_MagneticBrake: self.MotorBrake = False

So it's always a motor-brake, unless a message is received with MotorBrakeUnitSerial OR -t MagneticBrake As always; suggestions welcome....

WouterJD commented 3 years ago

I do not only retry on the length but also the expected header. 6402 --> 0x1902 in version V

Thanks, some other steps forward

13:42:51,600: clsTacxNewUsbTrainer._ReceiveFromTrainer_MotorBrake()...
13:42:51,600: Trainer recv hdr=0x21303 data="array('B', [47, 80, 47, 0, 0, 2, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 10, 0, 0, 0, 0, 3, 19, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 4, 15, 4, 0, 0, 0, 0, 0, 0, 2, 85, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])" (len=64)
13:42:51,600: Retry because short buffer (len=64) or incorrect header received (expected: 0xc03 received: 0x21303)
13:42:51,702: Trainer recv hdr=0xc03 data="array('B', [47, 80, 47, 0, 0, 2, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 10, 0, 0, 0, 0, 3, 12, 0, 0, 2, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 105, 0, 0, 0, 0, 2, 85, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])" (len=64)
13:42:51,702: Motor Brake Unit Firmware=6402 Serial=    0 year=2000 type=T190 Version2=0 MotorBrake=False
13:42:51,702: ... returns True
13:42:51,808: FortiusAnt applies the MagneticBrake power curve