Skip to content

Commit 379aa42

Browse files
committed
Merge branch 'release/0.1.0'
2 parents 742b3c7 + 99e18fe commit 379aa42

File tree

8 files changed

+959
-91
lines changed

8 files changed

+959
-91
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
*.hex
12
.pio
23
.vscode/.browse.c_cpp.db*
34
.vscode/c_cpp_properties.json

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ The Dshot value sent by default is **0**. Via serial console other values (up to
2323
## How does it work?
2424
A timer triggers sending of the DShot frame via interrupt then the following things happen:
2525

26-
1. B8 is set as output
26+
1. D8 is set as output
2727
2. Dshot frame is sent
2828
3. D8 is set as input
2929
4. Input capture on timer 1 is used to capture the time between falling and rising endges
@@ -34,11 +34,31 @@ A timer triggers sending of the DShot frame via interrupt then the following thi
3434
## Usage
3535
I recommend you use Visual Studio Code with the platform IO plugin. A **platformio.ini** is included in the repository with a working serial monitor configuration.
3636

37+
## C2 Interface
38+
This sketch also includes a C2 interface for your convenience, allowing to read, write and erase an EFM8 MCU. This will require you to install the EFM8 python client. Also by default the Arduino will start in DShot mode, to enable the C2 interface, pull pin 13 low and reset the arduino.
39+
40+
To switch back to DShot mode, remove the jumper to low form pin 13 and reset the arduino.
41+
42+
### Installing python EFM8 client
43+
use pipenv to create an virtual env, install dependencies and enter the env:
44+
```
45+
pipenv install
46+
pipenv shell
47+
```
48+
49+
then run the client like so:
50+
51+
```
52+
python efm8.py read /dev/ttyUSB0 output.hex
53+
python efm8.py write /dev/ttyUSB0 input.hex
54+
python efm8.py erase
55+
```
56+
3757
## Compatibility
3858
This should work on any Arduino Uno or clone running at 16MHz. If you are using a different Arduino board, make sure you are using (and setting) the pin which has the "Input Capture" functionality.
3959

4060
## Resources
4161
* [DShot - the missing handbook](https://brushlesswhoop.com/dshot-and-bidirectional-dshot/)
4262

4363
## Contributions
44-
Contributions are very welcome. Feel free to submit a PR against the develop branch. If you have any questions or feature requests, feel free to [open an issue](https://github.com/bird-sanctuary/arduino-bi-directional-dshot/issues).
64+
Contributions are very welcome. Feel free to submit a PR against the develop branch. If you have any questions or feature requests, feel free to [open an issue](https://github.com/bird-sanctuary/arduino-bi-directional-dshot/issues).

client/Pipfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[[source]]
2+
url = "https://pypi.org/simple"
3+
verify_ssl = true
4+
name = "pypi"
5+
6+
[packages]
7+
pyserial = "*"
8+
9+
[dev-packages]
10+
11+
[requires]
12+
python_version = "3.7"

client/Pipfile.lock

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/efm8.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
from hashlib import new
2+
import serial
3+
import time
4+
import sys
5+
import argparse
6+
from tokenize import String
7+
8+
class ProgrammingInterface:
9+
def __init__(self, port, baudrate = 1000000):
10+
self.serial = serial.Serial(port, baudrate, timeout = 1)
11+
12+
# Give Arduino some time
13+
time.sleep(2)
14+
15+
def initialize(self):
16+
done = False
17+
while not done:
18+
try:
19+
self.serial.write(b"\x01\x00")
20+
result = self.serial.read(1)
21+
assert result == b"\x81"
22+
done = True
23+
except:
24+
print("Error: Could not establish connection - try resetting your Arduino")
25+
sys.exit(1)
26+
27+
print("Connected to interface")
28+
29+
def read(self, file, start=0x00, size=0x3FFF, chunksize=0x10):
30+
for address in range(start, start + size, chunksize):
31+
request = [
32+
0x05, 0x05,
33+
chunksize,
34+
(address >> 16) & 0xFF,
35+
(address >> 8) & 0xFF,
36+
address & 0xFF,
37+
0x00,
38+
]
39+
40+
self.serial.write(request)
41+
response = self.serial.read(chunksize + 1)
42+
body = response[1:]
43+
44+
if len(response) > 0 and len(body) > 0:
45+
print("===============================================")
46+
print("address: %s" % hex(address))
47+
print("request: %s" % bytes(request).hex())
48+
print("response code: %s" % hex(response[0]))
49+
print("response body: %s" % body.hex())
50+
51+
line = bytearray([chunksize, (address >> 8) & 0xFF, address & 0xFF, 0x00]) + body
52+
crc = 0
53+
for nextbyte in line:
54+
crc = crc + nextbyte
55+
56+
crc = (~crc + 1) & 0xFF
57+
line.append(crc)
58+
file.write(":%s\n" % line.hex())
59+
60+
else:
61+
break
62+
63+
def erase(self):
64+
self.serial.write(b"\x04\x00")
65+
assert self.serial.read(1) == b"\x84"
66+
print("Device erased")
67+
68+
def reset(self):
69+
self.serial.write(b"\x02\x00")
70+
assert self.serial.read(1) == b"\x82"
71+
72+
def write(self, file):
73+
lines = file.readlines()
74+
for line in lines:
75+
assert line[0] == ":"
76+
if line[7:9] != "00":
77+
continue
78+
79+
length = int(line[1:3], 16)
80+
assert length + 4 < 256
81+
82+
addressHi = int(line[3:5], 16)
83+
addressLo = int(line[5:7], 16)
84+
data = bytearray.fromhex(line[9 : 9 + length * 2])
85+
assert len(data) == length
86+
crc = addressHi + addressLo
87+
for i in range(len(data)):
88+
crc += data[i]
89+
crc = crc & 0xFF
90+
print(
91+
"0x{:04X}, Bytes: {:02X}, Data: {}".format(
92+
(addressLo + (addressHi << 8)), len(data), data.hex()
93+
)
94+
)
95+
self.serial.write([0x3, len(data) + 5, len(data), 0, addressHi, addressLo, crc])
96+
self.serial.write(data)
97+
response = self.serial.read(1)
98+
if response != b"\x83":
99+
print("Error: Failed writing data")
100+
return None
101+
102+
self.reset()
103+
104+
parser = argparse.ArgumentParser(description='Interact with the Arduino based EFM8 C2 interface')
105+
parser.add_argument('action', metavar='ACTION', type=str,
106+
help='Action to perform: read, write or erase',
107+
choices=['read', 'write', 'erase'],)
108+
parser.add_argument('port', metavar='PORT', type=str,
109+
help='Port to use')
110+
parser.add_argument('destination', metavar='DESTINATION', type=str, nargs='?', default=None,
111+
help='Destination to write to or read from')
112+
113+
args = parser.parse_args()
114+
interface = ProgrammingInterface(args.port)
115+
interface.initialize()
116+
117+
if args.action == 'read':
118+
if not args.destination:
119+
parser.print_usage()
120+
parser.exit()
121+
122+
file = open(args.destination, "w")
123+
124+
# Fetch the flash segment
125+
interface.read(file, 0, 0x3FFF)
126+
127+
# Fetch the bootloader
128+
# TODO: this could be passed via a parameter
129+
#interface.read(file, 0xF000, 0x07FF)
130+
131+
file.write(":00000001FF\n")
132+
133+
if args.action == 'erase':
134+
interface.erase()
135+
136+
if args.action == 'write':
137+
if not args.destination:
138+
parser.print_usage()
139+
parser.exit()
140+
141+
file = open(args.destination, "r")
142+
143+
interface.erase()
144+
interface.write(file)

0 commit comments

Comments
 (0)