https://github.com/jackhumbert/fardriver-controllers
Information on Nanjing Fardriver Controllers
https://github.com/jackhumbert/fardriver-controllers
electric-vehicles
Last synced: 4 months ago
JSON representation
Information on Nanjing Fardriver Controllers
- Host: GitHub
- URL: https://github.com/jackhumbert/fardriver-controllers
- Owner: jackhumbert
- License: mit
- Created: 2024-09-24T13:01:19.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2024-12-21T14:41:17.000Z (10 months ago)
- Last Synced: 2024-12-21T15:27:43.820Z (10 months ago)
- Topics: electric-vehicles
- Language: C++
- Homepage:
- Size: 2.85 MB
- Stars: 2
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Nanjing Fardriver Controllers
## Serial Protocol
You can interact with Fardriver controllers on a computer using the serial port (2x2 connector) with a RS232-USB adapter, provided you don't connect the 5V line (so only TX, RX & GND are connected), and your adapter uses 3.3V logic. [Termite](https://www.compuphase.com/software_termite.htm) works well to interact with the controller, as long as you have enabled the `Hex View` plugin under settings.
Little-endian, unless otherwise specified.
### Receiving messages
Status messages sent by the controller are usually 16 bytes in length. They rotate through nearly all of the internal memory addresses, with more important sections being repeated throughout, every 3-4 normal messages. If you plugin your controller to your computer after initializing it via a Bluetooth adapter, you should see these. Otherwise, you'll need to send some of the commands mentioned later on.
```cpp
struct {
// B0
uint8_t magic = 0xAA; // 170// B1
uint8_t id : 6; // less than 0x37 when receiving updates
uint8_t flags : 2; // generally set to 2 when receiving// B2-13
uint8_t data[12];// B14-15
uint8_t crc[2];
};
```The `crc` values are calculated with two tables, available in [fardriver.hpp](/fardriver.hpp), where `length` is the length of the entire message (`16` in the above scenario):
```cpp
void ComputeCRC(uint8_t * data, uint16_t length) {
uint8_t a = 0x3C; // 60
uint8_t b = 0x7F; // 127
uint8_t pos;
for (pos = 0; pos < length - 2; ++pos) {
auto i = a ^ data[pos];
a = b ^ crcTableHi[i];
b = crcTableLo[i];
}
data[length - 2] = a;
data[length - 1] = b;
}
```If `id` is less than `0x37` (`55`), it can be used to lookup the address of the data:
```cpp
const uint8_t flash_read_addr[55] = {
0xE2, 0xE8, 0xEE, 0x00, 0x06, 0x0C, 0x12,
0xE2, 0xE8, 0xEE, 0x18, 0x1E, 0x24, 0x2A,
0xE2, 0xE8, 0xEE, 0x30, 0x5D, 0x63, 0x69,
0xE2, 0xE8, 0xEE, 0x7C, 0x82, 0x88, 0x8E,
0xE2, 0xE8, 0xEE, 0x94, 0x9A, 0xA0, 0xA6,
0xE2, 0xE8, 0xEE, 0xAC, 0xB2, 0xB8, 0xBE,
0xE2, 0xE8, 0xEE, 0xC4, 0xCA, 0xD0,
0xE2, 0xE8, 0xEE, 0xD6, 0xDC, 0xF4, 0xFA
};
```Each address stores 16bits. The addresses range from `0x00` to `0xFA`, and the data structs can be referenced in [the `FardriverData` struct in fardriver.hpp](/fardriver.hpp).
When `id` is equal to `0x37` (`55`), a data gathering format is used (unknown at the moment).
You can use [010 Editor](https://www.sweetscape.com/010editor/) with [fardriver.bt](/fardriver.bt) and a dump of the received data to interactively explore the structures (there are some small differences between the .hpp & .bt structs and names).
### Writing addresses directly
This is the "new" way the apps interact with the controller, and allows for variable packet sizes, but most that are sent are 8 bytes total.
```cpp
struct {
// B0
uint8_t magic = 0xAA; // 170// B1
uint8_t compute_length : 6; // length of entire message minus 2 for crc
uint8_t flags : 2; // set to 1 for writing addresses// B2-3
uint8_t addr;
uint8_t addr_confirm;// B4...
uint8_t data[compute_length - 4]; // usually 2 bytes// Last two bytes
uint8_t crc[2];
};
```The CRC is calculated and assigned to the last two bytes using the `ComputeCRC` method mentioned earlier, passing in `8` for the length, instead of `16`.
System commands can be sent by writing `0x88 XX` to the address `0xA0`:
* `0x01` enters non-following status
* `0x02` starts self-learning/balance?
* `0x03` stops balancing
* `0x04` gets CAN parameters?
* `0x05` related to `0x0F`, resets
* `0x06` starts data gathering
* `0x07` stops something?
* `0x08` unknown, phase_active?
* `0x0F` same as `0x05` if Version0 == 73 or filename contains `ISOLATE_`### Sending commands
These are less commonly used, mainly with "old" configs. Commands are sent in 8 byte packets with the following format:
```cpp
struct {
uint8_t magic = 0xAA; // 170
uint8_t command;
uint8_t command_comp; // ~command
uint8_t sub_command;
uint8_t value_1;
uint8_t value_2;
uint8_t crc; // magic + command + command_comp + sub_command + value_1 + value_2
uint8_t crc_comp; // ~crc
};
```CRCs are computed differently than the other messages
Some valid `command` values I've seen - children to each list item are `sub_command` values:
* `0x01` ??
* `0x02` ?? sets some variable
* `0x03`
* `0x04` balance, BMS, shutdown
* `0x02` system shutdown, non-following status
* `0x6F` related to starting/stopping balance and MOS charging/discharging
* `0x05` sent after updating date & time
* `0x01 0x5F 0x5F` may get CAN params
* `0x06` gets:
* `0x41 AT+BAUD=19200`
* `0x07` starts the gathering of data and returns 300 frames of recent data logging?
* `0x08` Starts USART3 (21/22) maybe? with 19200 baud, with other stuff
* `0x09` sets the CAN number
* `0x0A` gets name? `AT+NAME=CONTROLDM` + something - 02 does same, sets some variable
* `0x41 AT+NAME=CONTROLDM `
* `0x0B`
* `0x0C` ??
* `0x0D`
* `0x0E` ??
* `0x0F`
* `0x10` gets controller TUUID? `AT+TUUID=FFEC` + something - 04, 0C, 06, 0E do the same
* `0x41 AT+TUUID=FFEC`
* `0x11` may be used to update params
* `0x12` updates params with 0x14 something
* `0x13` interacts with the login/binding system, can set password, phone number
* `0x07` seems to be related to the login/status update system, any values seem to start status updates for a little while
* `0x08` seems to be sent on serial connection close
* `0x10`-`0x13` are related to updating the password
* `0x14`-`0x24` are related to updating the phone number
* `0x14` sets the date
* `0x15` sets the time
* `0x17`### Other messages
* `xx 0xFF 0x01 0x01 `
* `xx 0xFE 0x01 0x01 `
* `xx 0xFD 0x01 0x01 `### CAN messages
* `0xCFE55B0` 6 & 7 set MaxLineCurr
* `0xCFE55B1` 6 & 7 set additional MaxLineCurr (adds)
* `0xCFF55B0` sets battery cap### Updating Firmware
When loading the firmware .bin file, a crc array is created with the bytes at 0x3E000, populated in the array from 4 to 124.
For the 32303, the first 0x2000 (4 packets) aren't important, so they're skipped.
During "Detect Controller", where something (at +0x448dc, based on file size/format?) is 0x7e2f / 32303: (GD32F303?)
* `0x55 0xaa 0x33 0x30 0x33 0x3c 0xfd 0xfe`
0x7d67 / 32103: (GD32F103?)
* `0x55 0xaa 0x11 0x12 0x13 0x00 0xfd 0xfe`
0x3778 / 14200: ??
* `0x55 0xaa 0x65 0x8b 0xcf 0x31 0x48 0xf3`
0x0115 / 277: ??
* `0x55 0xaa 0x31 0x32 0x33 0x00 0xfd 0xfe`
else: ??
* `0x55 0xaa 0x01 0x02 0x03 0x00 0xfd 0xfe`
mobile app also handles 32071
This may be sent first, or this follows after a 50 loop delay, or if "NewApp", SysCmd 0x5 or 0xF (when filename contains "ISOLATE_", or hardware version is `I`):
* `0xAA 0x04 0xFB 0x01 0x55 0x55 0x54 0xAB`
* SysCmd 0x5: `0xAA 0xC6 0xA0 0xA0 0x88 0x05 0x04 0xCC`
* SysCmd 0xF: `0xAA 0xC6 0xA0 0xA0 0x88 0x0F 0x84 0xCB`followed by `0xAA 0x04 0xFB 0x01 0x26 0x26 0xF6 0x09` (only on the mobile app apparently, after 10ms)
then packets are sent, tracking an index. If a variable is 1, it starts at index `54`, 2: `58`, and otherwise starts at `0`.
controller should send some sort of response with the crc of the packet sent:
* `0xAA 0x1F `
with error codes `0x7C`, `0x7D` or `0x7E`. index will be `0x1` for first packet (packet_crc should be mapped to crc_table[4])
if 32303:
if packet_crc matches && (not at the end or func == 0), send this confirming the packet_crc:
* `0x5a 0xbb 0x72 0x73 0x74 0x75 0x76`
if the second half of the packet_crc doesn't match, this is sent, and index isn't incremented:
* `0x5a 0xbb 0x72 0x73 0x74 0x75 0x76`
otherwise, packet info, total length 0x807, separated into 0x403 & 0x404 packages:
* `0x5a 0xa5 `
crc is calculated by:
```
uint32_t crc = 0xFFFFFFFF;
crc = crc32[crc & 0xFF ^ index] ^ crc >> 8;
for (i=0..2048) {
crc = crc32[crc & 0xFF ^ data[i]] ^ crc >> 8;
}
crc = ~crc;
```crc32 is created by:
```
for (i=0..256) {
uint num = i;
for (0..8) {
if (((int) num & 1) != 0)
num = num >> 1 ^ 0xedb88320;
else
num >>= 1;
}
this.crc32[i] = num;
}
```mobile app does 2055 (0x807) length packets:
```
for (;i<102) {
send 20;
sleep(10);
}
send 15;
sleep(10);
```last packet is all the crcs that start at 0x3E000 (without the 0xaa 0x55, which is added manually), handled differently:
* `0x5a 0xa5 0xaa 0x55 0xFF[2048-486] `
### Other messages sent from controller
* `xx xx xx xx xx xx 0x01 > 3>`
* `0x55 0xAA 0x00 0x00 0x0D 0x6F 0x76 0x31 0x33 0x64 0x65 0x77 0x77 0x2E `when something is `0xD`, full model number is received with extension code `X`:
* `xx xx xx xx xx 0x16 0x67 0x03 0x00 0x12 `
## Hardware Info:
### .BIN files
* 32303:
* uses crc_filetable
* takes .bin files larger than 131072
* crc starts at 253952
* 32071:
* uses crc_filetable
* takes .bin files less than or equal to 131072
* crc starts at 126976
* 32101:
* uses crc_total
* filename contains BMS
* crc starts at 60410
* 32103:
* uses crc_total
* filename doesn't contain BMS
* crc starts at 64506### Hardware `H`
Pins definitions:
* `1` ADC0 > 0x7FF, A2? related to A10 somewhere
* `4` A15
* `5` C13
* `6` C14
* `7` C15* `0` NC Normally Closed
* `1` PIN24 ADC
* `2` PIN15 A11 P24 CAN RX, High speed
* `3` PIN5 A12 P25 CAN TX, Low speed
* `4` PIN17 A15 P10 Used by encoder units
* `5` PIN14 C13 P17 Cruise/Boost
* `6` PIN3 C14 P27 AntiTheft?
* `7` PIN8 C15 P28 Reverse
* `8` PB4
* `9` PINInvalid1
* `10` PIN2
* `11` PIN18 D0 Not available in YJCAN
* `12` PIN9 Not available in YJCAN
* `13` PD1
* `14` PINInvalid2
* `15` PINInvalid3A14? Out_PP
B11 used by SpecialCode X
B10 uses OneLine, ReverseOneLine affects via B2?
A12 is serial RTS
B6,7,8 used by Hall?Mode_AF_PP:
* A8
* A9
* A10
* B13
* B14
* B15Another setup:
* A8, A9, A10: Out_PP
* B13, B14, B15: Out_PP
* A2, A14: Out_PP
* SpecialFrame == 14
* D1: IPU
* else
* D1: Out_PP
* If no inputs using pin 11:
* D0: Out_PP
* else
* D0: IPU
* B0, B1: AIN
* if (sensorType < 4)
* B3, B4, B5, B6, B7, B8, B12: IN_FLOATING
* else if (sensorType < 8)
* B3, B4, B6, B7, B8, B12: IN_FLOATING
* else
* B2, B4, B7, B12: IN_FLOATING
* B3, B5: AF_99
* B6: Out_PP
* SpecialCodes A-G:
* B9, B2: Out_PP
* else
* B2: Out_PP
* B2, B10: AF_PP
* B11: IPU
* A15: IN_FLOATING
* C13: IPU
* C14, C15: IN_FLOATING
* if (SpecialCode == 'Y')
* A11: Out_PP
* else
* A11: IPU
* A12: IPURXD:
* `0` B10: AF_PP
* `1` B10: Out_OD
* `2` B10: Out_PP
* `3` B10: AF_PPAlso makes A12 Out_PP or AF_PP
Possible Interrupts (AntiTheft checked on started I think):
* A15
* C13
* C14
* C15SpeedLimitPin:
* 4 A15
* 5 C13
*SwitchVolPin:
### Wiring
#### ND84530_24_ABH64
GigaDevice GD32F303CCT6A
https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/2208012200_GigaDevice-Semicon-Beijing-GD32F303CCT6A_C5119567.pdfUMW 78M05 FZA1
https://mm.digikey.com/Volume0/opasdata/d220001/medias/docus/5027/78Mxx.pdfRE7
http://www.semtech.ru/pdf/diodes/4221629408481.pdf##### 30 Pin plug:
Pin | Color | FD Name | Description
---|---|---|---|
1 | Brown/Red | U | Anti-theft phase wire
2 | Brown/Green | BW5V | Serial power (5V)
3 | Red/White | ACC+ | Throttle power (5V)
4 | Black | GND | Throttle GND
5 | White | TEMP | Motor temperature sensor
6 | Red | HALL+ | 12V for motor
7 | Blue | HC | Motor C sensor
8 | Green | HB | Motor B sensor
9 | Yellow | HA | Motor A sensor
10 | NC | - | -
11 | Orange | KEY | Ignition that accepts battery plus (72V, etc)
12 | Brown/Blue | TXD | Serial TX (3.3V)
13 | Black | GND | Reverse/Serial GND
14 | Green/White | SV | Throttle signal (5V)
15 | Black | GND | Anti-theft GND
16 | Black | GND | Motor GND
17 | Red/Blue | XH | Cruise
18 | Black | GND | Brake/Speed selection GND
19 | Grey | BH | Brake, pull-up to enable (12V)
20 | Yellow/Green | BL | Brake, pull-down to enable (3.3V)
21 | Orange | KEY | Ignition for Anti-theft
22 | Pink | 60VC | Anti-theft power (+72V)
23 | Red/Black | RXD | Serial RX (3.3V)
24 | Yellow/White | SDH | High speed, pull-down to enable (3.3V)
25 | Blue/White | SDL | Low speed, pull-down to enable (3.3V)
26 | Brown | BOOST | Boost, pull-down to enable
27 | Black/white | FW/FD | Anti-theft signal, pull-down to enable
28 | Brown/white | RE | Reverse, pull-down to enable
29 | Light Blue | SPD | Speed pulse/one wire (12V)
30 | Purple | SPA | Speedometer PWM (72V)##### Daughter board
LEFT:
Pin| Daughterboard | Mainboard
---|---|---|
1 | 20K to U | PB1 19, through 20ks, 1K PD
2 | Key, 72V in |
3 | Key, 72V in |
4 | 7.6K to GND, 10.4K to 12V, 2.7k to H* | 14V
5 | 60VC, 72V out | Batt+
6 | BM5V, 5V out | 5V
7 | RXD, 3.3V | PB10 21
8 | TXD, 3.3V | PB11 22
9 | FW/FD, 3.3V | PC14-OSC32IN 3
10 | GND | GND, 1k to 3.3VCENTER:
Pin| Daughterboard | Mainboard
---|---|---|
1 | 30P10, 4K to BW5V | PA15 38, 6.5k to 5V
2 | Motor C sensor | PB4 40
3 | GND, 8K to BW5V | GND
4 | Motor B sensor | PB5 41
5 | Speaker - | PB2 20
6 | Motor A sensor | PB8 45
7 | Cruise | PC13-TAMPER-RTC 2
8 | Boost | PC13-TAMPER-RTC 2
9 | BH, 12V in | PB3 39, kinda
10 | BL, 3.3V | PB3 39RIGHT:
Pin| Daughterboard | Mainboard
---|---|---|
1 | TEMP | PA0-WKUP 10
2 | SPA | PA2 12
3 | Speaker +, SPD, 12K to GND, 12R to SPD | OSCOUT 6 (PD1)
4 | SV, 5V in,5K to GND | PA1 11, ADC0_12
5 | NC | OSCIN 5 (PD0)
6 | SERIAL:1 | 3.3V
7 | RE | PC15-OSC32OUT 4
8 | High Speed, 7K to GND, 5k to 5V | PA11 32, CAN RX
9 | GND, 3k to 12V, 8k to 5V | GND
10 | Low Speed, 10k to 12V, 7k to GND | PA12 33, CAN TXSERIAL header in upper right (left-to-right):
Pin| Description
---|---|
1 | 3.3V, RIGHT:6
2 | GND, 8k to BM5V
3 | RXD
4 | TXDJTAG header on mainboard (left-to-right):
Pin| Description
---|---|
1 | GND
2 | PA14 37 I/O 5VT JTCK, SWCLK
3 | PA13 34 I/O 5VT JTMS, SWDIO
4 | 5V