Open TTN- opened 6 months ago
This is very cool, thanks for the link! I had no idea there was a secret menu in the programmer GUI.
There's a few potential pathways here. I'm hoping the driver component can be modified so that the arduino can be used directly for coms (somehow inverting RX and TX as required). I have FTDI dongles but they are all clones so won't talk to FTProg to invert TX and RX. This is probably the lowest effort route and best user experience pathway? The source for the driver adapter is provided in the post, but have never worked on anything like that.
Another avenue could be to decompile the program and learn more about the protocol. mmm.
I'm hoping the driver component can be modified so that the arduino can be used directly for coms (somehow inverting RX and TX as required).
Maybe the Arduino could do the inversion?
digitalWrite(3, !digitalRead(TX))
and digitalWrite(RX, !digitalRead(4))
in a loop.Not sure if that would work?
best user experience pathway
I had a lot of trouble getting the GUI to run on Windows 10 (see https://github.com/timmaxw/HitecDServo/blob/main/extras/DPC11Notes.md), and it doesn't work at all on Linux; so personally I prefer solutions that bypass the GUI entirely.
It's messed up when a vendor instructs Disable Windows driver signature verification.
It's there for a reason.
What hell lets try it. Installing drivers now.. breadboard wired, and arduino ready to go.
#define nRX 2 // connect this to servo PWM pin
#define nTX 3 //connect to nRX pin via 1K resistor
#define RX 4 //connect FTDI RX to this pin
#define TX 5 //connect FTDI TX to this pin
void setup() {
// put your setup code here, to run once:
pinMode(TX, INPUT);
pinMode(nTX, OUTPUT);
pinMode(nRX, INPUT);
pinMode(RX, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
digitalWrite(nTX, !digitalRead(TX));
digitalWrite(RX, !digitalRead(nRX));
}
The FTDI adapter is one like this.
It's showing up as USB Serial Port (COM9
. Guess now the question is how to get the right SiLabs driver installed, and then locate the .dll as per forum post. Any tips on that front?
Ok looks like I forgot to install the driver that came in the same zip as the DPC-11 application.
the DLL to be replaced lives in that location. path C:\Program Files (x86)\Hitecrcd\DPC-11
But we get some errors, I'm guessing new DPC-11 app requestst stuff that breaks this mod:
So lets get the old one, not from the forum. Note download link for the latest version is https://hitecrcd.com/uploads/DPC-11_Install__2020_12_09_01-2.9.9.zip
I bet that the older one is still in the uploads folder, so lets try that, picking the name from the forum post: https://hitecrcd.com/uploads/DPC-11_Setup_20180214_V2.9.1.zip
Which works. After installing that we are in! It sees the FTDI as adapter, but then gives up when we try to connect to the servo. It can't find it, so am hopeful the issue is something silly with my wiring or arduino sketch.
I'll go and double check everything but it's tantalizingly close!
Btw SHA256 checksum of DPC-11 software ZIP file on the forum post and official site match.
I realize the UI sucks, but I just want a solution with the least effort.
The wiring all looks ok. Not sure what issue is. Maybe the arduino is too slow. Got the dinosaur old scope on it, it's inverting correctly.
The arduino code was too slow. Got chat GPT to whip me up some direct port manipulation and it's connected:
// D2 to PWM pin of servo
// D3 jumpered to D2 via 1K resistor
// D4 to RX pin of FTDI adapter
// D5 to TX pin of FTDI
//don't forget to connect the grounds between arduino, servo, and FTDI adapter
void setup() {
// Set D2 and D5 as inputs
DDRD &= ~((1 << DDD2) | (1 << DDD5));
// Set D3 and D4 as outputs
DDRD |= (1 << DDD3) | (1 << DDD4);
}
void loop() {
// Read the state of D2 and invert it, then write to D4
if (PIND & (1 << PIND2)) {
// D2 is HIGH, set D4 LOW
PORTD &= ~(1 << PORTD4);
} else {
// D2 is LOW, set D4 HIGH
PORTD |= (1 << PORTD4);
}
// Read the state of D5 and invert it, then write to D3
if (PIND & (1 << PIND5)) {
// D5 is HIGH, set D3 LOW
PORTD &= ~(1 << PORTD3);
} else {
// D5 is LOW, set D3 HIGH
PORTD |= (1 << PORTD3);
}
}
Fuck yes.
I'll do a logic analyzer dump shortly. Any preference for where to connect the logic analyzer? On the FTDI side or servo side past the signal inversion?
Is there any programming actions that would be of particular interest?
Hooray, you got it working! If you do a complete write-up of exactly what you did, I'll link it from the main page.
A logic analyzer dump would be great! In particular, I'd love to see:
If you can provide those, that would be super helpful!
Motivation: With a DPC-11 programmer you can reprogram the endpoints of your servo to be whatever you want. The range of motion can be increased to 180 degrees. Fail safe positions can be set, overload protection settings, dead band, speed, and more..
Let's get started.
Flash your arduino with the following code (just about any arduino will work):
void setup() {
// Set D2 and D5 as inputs
DDRD &= ~((1 << DDD2) | (1 << DDD5));
// Set D3 and D4 as outputs
DDRD |= (1 << DDD3) | (1 << DDD4);
}
void loop() {
// Read the state of D2 and invert it, then write to D4
if (PIND & (1 << PIND2)) {
// D2 is HIGH, set D4 LOW
PORTD &= ~(1 << PORTD4);
} else {
// D2 is LOW, set D4 HIGH
PORTD |= (1 << PORTD4);
}
// Read the state of D5 and invert it, then write to D3
if (PIND & (1 << PIND5)) {
// D5 is HIGH, set D3 LOW
PORTD &= ~(1 << PORTD3);
} else {
// D5 is LOW, set D3 HIGH
PORTD |= (1 << PORTD3);
}
}
Using a breadboard, make the following connections:
Here is my setup:
Now that's the hardware all done. Let's look at software:
You'll need to follow the installation instructions for the DPC-11 software here: https://hitecrcd.com/products/servos/programmers/dpc-11/product
But! Use the older version of the software: https://hitecrcd.com/uploads/DPC-11_Setup_20180214_V2.9.1.zip The newer one makes requests that the adapter layer doesn't know to translate.
Key steps of the installation is:
HITECRCD_DPC-11 Driver Installer
folder which is in the zip file you just downloaded, and install the driver: HITECRCD_DPC-11 Driver Installer.exe
Open File Location
SiUSBXp.dll
into the folder that you just opened. For me this was here: C:\Program Files (x86)\Hitecrcd\DPC-11
Click on the series of servo you have, for me I have a D645MW servo, so I click the D-series.
Next screen you get is this:
Click Connect
. If all is well, it will connect, and you can make all changes as if you had a DPC-11 programmer!
User manual for full details on how to use the programmer can be found here: 411_DPC11_ManualV3.pdf
Source files for the DLL are here, it is what makes the FTDI adapter be recognized as the DPC-11 programmer: src.zip
Credit for this mod goes to user rtv over at the eevblog forums: https://www.eevblog.com/forum/mechanical-engineering/programming-hitec-d485-rc-servo-settings/
Pulseview capture of parameter refresh, 1MHz, connected to RX and TX lines of FTDI unit: Hited D645MW DPC-11 RX-TX line capture during parameter refresh.zip
Let me know if you need anything else over the course of the coming week. I'll leave this assembled here on my desk in case we need it again. :)
Here is some data:
Pulseview's decoder export options are a bit limited unfortunately but I hope that's helpful.
Maybe have a look at installing pulseview and applying the UART decoder, might be an easier way to view it
Thanks for the writeup! I've copied it into a Github Gist here https://gist.github.com/timmaxw/11b01f5703a1bc9f470ff810f17f3498 and I've linked it from the main page at https://github.com/timmaxw/HitecDServo.
I'll take a look at these logic analyzer captures and see if I can figure out what's going on...
I installed Pulseview and looked at the logic analyzer captures. The D645MW protocol looks basically identical to the D485HW protocol; I don't see anything that would explain why the HitecDServo library didn't work.
I'm curious to keep debugging this! If you want to keep helping, here are some other ideas to try:
Nice work posting the instructions!
Patch applied. 4.5V measured on 5V from arduino, so ran with 1K pullup. Result:
18:40:09.254 -> readRawRegister() reg=0x0 const0x69=0xFFFFFF9A mystery=0x69 reg2=0x0 low=0x2 high=0x55 checksum=0x0
18:40:09.254 -> Error: Corrupt response from servo.
Run again, 2K pullup:
18:41:58.235 -> readRawRegister() reg=0x0 const0x69=0xFFFFFF9A mystery=0x69 reg2=0x0 low=0x2 high=0x55 checksum=0x0
18:41:58.235 -> Error: Corrupt response from servo.
Pulseview analyzer log of pin D2 and 2K pullup: D2 log - patched lib.zip
This was running the Example 3 - read settings sketch.
Thanks! I think I might see the problem... what happens if you apply this change?
More than before :)
06:36:36.283 -> readRawRegister() reg=0x0 const0x69=0x69 mystery=0x0 reg2=0x0 low=0x55 high=0x87 checksum=0x0
06:36:36.283 -> readRawRegister() reg=0xB2 const0x69=0x69 mystery=0x0 reg2=0xB2 low=0x22 high=0x0 checksum=0xB2
06:36:36.330 -> readRawRegister() reg=0xB0 const0x69=0x69 mystery=0x0 reg2=0xB0 low=0x4D high=0x3F checksum=0xB0
06:36:36.330 -> readRawRegister() reg=0xC2 const0x69=0x69 mystery=0x0 reg2=0xC2 low=0x13 high=0x20 checksum=0xC2
06:36:36.376 -> readRawRegister() reg=0x32 const0x69=0x69 mystery=0x0 reg2=0x32 low=0x0 high=0x0 checksum=0x32
06:36:36.376 -> readRawRegister() reg=0x5E const0x69=0x69 mystery=0x0 reg2=0x5E low=0x0 high=0x0 checksum=0x5E
06:36:36.423 -> readRawRegister() reg=0x54 const0x69=0x69 mystery=0x0 reg2=0x54 low=0xFF high=0xF checksum=0x54
06:36:36.423 -> readRawRegister() reg=0x4E const0x69=0x69 mystery=0x0 reg2=0x4E low=0x1 high=0x0 checksum=0x4E
06:36:36.470 -> readRawRegister() reg=0x66 const0x69=0x69 mystery=0x0 reg2=0x66 low=0x0 high=0x0 checksum=0x66
06:36:36.517 -> readRawRegister() reg=0x68 const0x69=0x69 mystery=0x0 reg2=0x68 low=0x0 high=0x0 checksum=0x68
06:36:36.517 -> Error: Confusing response from servo.
We do have a compiler warning of overflow:
HitecDServo-main\examples\Example3_ReadSettings\HitecDServo.cpp: In member function 'int HitecDServo::readByte()':
HitecDServo-main\examples\Example3_ReadSettings\HitecDServo.cpp:606:38: warning: overflow in implicit constant conversion [-Woverflow]
int timeoutCounter = F_CPU * 0.050 / 10;
Changing the line to int32_t timeoutCounter = F_CPU * 0.050 / 10;
to avoid the overflow we get the same thing:
06:43:41.104 -> readRawRegister() reg=0x0 const0x69=0x69 mystery=0x0 reg2=0x0 low=0x55 high=0x87 checksum=0x0
06:43:41.104 -> readRawRegister() reg=0xB2 const0x69=0x69 mystery=0x0 reg2=0xB2 low=0x22 high=0x0 checksum=0xB2
06:43:41.151 -> readRawRegister() reg=0xB0 const0x69=0x69 mystery=0x0 reg2=0xB0 low=0x4D high=0x3F checksum=0xB0
06:43:41.197 -> readRawRegister() reg=0xC2 const0x69=0x69 mystery=0x0 reg2=0xC2 low=0x13 high=0x20 checksum=0xC2
06:43:41.197 -> readRawRegister() reg=0x32 const0x69=0x69 mystery=0x0 reg2=0x32 low=0x0 high=0x0 checksum=0x32
06:43:41.243 -> readRawRegister() reg=0x5E const0x69=0x69 mystery=0x0 reg2=0x5E low=0x0 high=0x0 checksum=0x5E
06:43:41.243 -> readRawRegister() reg=0x54 const0x69=0x69 mystery=0x0 reg2=0x54 low=0xFF high=0xF checksum=0x54
06:43:41.291 -> readRawRegister() reg=0x4E const0x69=0x69 mystery=0x0 reg2=0x4E low=0x1 high=0x0 checksum=0x4E
06:43:41.291 -> readRawRegister() reg=0x66 const0x69=0x69 mystery=0x0 reg2=0x66 low=0x0 high=0x0 checksum=0x66
06:43:41.337 -> readRawRegister() reg=0x68 const0x69=0x69 mystery=0x0 reg2=0x68 low=0x0 high=0x0 checksum=0x68
06:43:41.337 -> Error: Confusing response from servo.
Good catch on the overflow! I hadn't noticed that.
Seems like the problem is that the D645MW handles deadband settings differently than the D485HW does. The deadband is controlled by three registers, which I arbitrarily labeled as DEADBAND_1
, DEADBAND_2
, and DEADBAND_3
. On the D485HW they act as follows:
/* DEADBAND_1, DEADBAND_2, and DEADBAND_3 together control the servo deadband.
- If deadband == 1, then DEADBAND_1=1; DEADBAND_2=5; and DEADBAND_3=11. This is
the default setting.
- If deadband > 1, then:
- DEADBAND_1 = 4*deadband-4
- DEADBAND_2 = 4*deadband
- DEADBAND_3 = 4*deadband+6 */
#define HD_REG_DEADBAND_1 0x4E
#define HD_REG_DEADBAND_2 0x66
#define HD_REG_DEADBAND_3 0x68
Your D645MW is reporting DEADBAND_1=1
, DEADBAND_2=0
, DEADBAND_3=0
. The HitecDServo library is trying to translate this back into a "deadband setting" between 1 and 10, but it doesn't expect to see DEADBAND_2=0
or DEADBAND_3=0
, so it reports "Error: Confusing response from servo."
As a short-term workaround, you could comment out this piece of code so it just skips reading the deadband: https://github.com/timmaxw/HitecDServo/blob/main/src/HitecDServo.cpp#L163-L182
The long-term solution would be to use the Hitec official GUI to set the deadband to various values, and use the logic analyzer to snoop on what it's setting the three deadband registers to. The serial protocol is pretty simple to decode: https://github.com/timmaxw/HitecDServo/blob/main/src/HitecDServoInternal.h#L30-L40 Then the HitecDServo.cpp
code could be updated to also support the D645MW.
Sorry for the delay here crazy week... I'll try grab individual captures of setting every setting of the servo and post it here early next week when I can.
Would be nice to be able to adjust servos later without the hitec software, and better still, without risking giving my computer aids with that DLL file. Kinda feel like reinstalling my PC now 🤔
The long-term solution would be to use the Hitec official GUI to set the deadband to various values, and use the logic analyzer to snoop on what it's setting the three deadband registers to.
I'll take that a step further, capture setting ALL settings. You know, in case it's useful later.
I've just gone through the interface, setting / unsetting every feature split into separate captures, organized into folders in the ZIP file: D645MW FTDI uninverted captures.zip
Notes: To set Sensitivity Ratio the Smart Sense setting must be disabled (after all, Smart Sense, is basically some form of Sensitivity Ratio auto adjust).
Setting and opening ID's seems to be broken. When trying to a servo with ID 2, it just gets stuck repeatedly talking to the servo. But if ID 1 or 3 is set, nothing is sent. At one point I managed to have the program become unresponsive.
At some stage I'll have a look at updating the code to match the captures but might be a while.
Tim I really appreciate the effort you've put into this despite not even owning a D645MW! Much appreciated mate :)
Thanks, this is great!
I think I figured out what's going on with the deadband, and I've pushed an updated version of the library (version 1.0.1). When you have some free time, give it another try and let me know if it works now? :)
Thank you kindly! It would be my pleasure. Expect an update in 24hrs or so.
Fantastic, Example3_ReadSettings
is dumping info:
19:17:55.207 -> id=3
19:17:55.207 -> counterclockwise=1
19:17:55.207 -> speed=100
19:17:55.207 -> deadband=1
19:17:55.207 -> softStart=20
19:17:55.207 -> rangeLeftAPV=2322
19:17:55.207 -> rangeRightAPV=16324
19:17:55.253 -> rangeCenterAPV=8785
19:17:55.253 -> failSafe=0
19:17:55.253 -> failSafeLimp=0
19:17:55.253 -> powerLimit=100
19:17:55.253 -> overloadProtection=100
19:17:55.253 -> smartSense=1
19:17:55.253 -> sensitivityRatio=4095
I had a go with the programmer sketch but registers look outdated atm.
Awesome!
I had a go with the programmer sketch but registers look outdated atm.
What do you mean?
Does anyone have a copy of DPC-11 version 2.9.1? It seems like it has been removed from the archive...
https://www.eevblog.com/forum/mechanical-engineering/programming-hitec-d485-rc-servo-settings/
If link goes dead. Archived copy: https://web.archive.org/web/20240511095348/https://www.eevblog.com/forum/mechanical-engineering/programming-hitec-d485-rc-servo-settings/