1
|
#!/usr/bin/env python
|
2
|
|
3
|
# Script to test the comms and get a dump of the
|
4
|
# BTECH UV-5001 mobile/base radio memory.
|
5
|
#
|
6
|
# It works OK on windows with python 2.7.3 and pyserial 2.7
|
7
|
# It will try all comports from COM1 to COM10
|
8
|
#
|
9
|
# Author Pavel, CO7WT, co7wt@frcuba.co.cu, pavelmc@gmail.com
|
10
|
|
11
|
import serial
|
12
|
import struct
|
13
|
import os
|
14
|
import time
|
15
|
|
16
|
fpath = "\home\jkidd"
|
17
|
filename = "uv2501"
|
18
|
|
19
|
# accesory function, jump it.
|
20
|
def hexprint(data, addrfmt=None):
|
21
|
"""Return a hexdump-like encoding of @data"""
|
22
|
if addrfmt is None:
|
23
|
addrfmt = '%(addr)03i'
|
24
|
|
25
|
block_size = 8
|
26
|
|
27
|
lines = len(data) / block_size
|
28
|
|
29
|
if (len(data) % block_size) != 0:
|
30
|
lines += 1
|
31
|
data += "\x00" * ((lines * block_size) - len(data))
|
32
|
|
33
|
out = ""
|
34
|
|
35
|
for block in range(0, (len(data)/block_size)):
|
36
|
addr = block * block_size
|
37
|
try:
|
38
|
out += addrfmt % locals()
|
39
|
except (OverflowError, ValueError, TypeError, KeyError):
|
40
|
out += "%03i" % addr
|
41
|
out += ': '
|
42
|
|
43
|
left = len(data) - (block * block_size)
|
44
|
if left < block_size:
|
45
|
limit = left
|
46
|
else:
|
47
|
limit = block_size
|
48
|
|
49
|
for j in range(0, limit):
|
50
|
out += "%02x " % ord(data[(block * block_size) + j])
|
51
|
|
52
|
out += " "
|
53
|
|
54
|
for j in range(0, limit):
|
55
|
char = data[(block * block_size) + j]
|
56
|
|
57
|
if ord(char) > 0x20 and ord(char) < 0x7E:
|
58
|
out += "%s" % char
|
59
|
else:
|
60
|
out += "."
|
61
|
|
62
|
out += "\n"
|
63
|
|
64
|
return out
|
65
|
|
66
|
def make_frame(cmd, addr, length, data=""):
|
67
|
"""Pack the info in the headder format"""
|
68
|
if data == "":
|
69
|
return "\x06" + struct.pack(">BHB", ord(cmd), addr, length)
|
70
|
else:
|
71
|
return "\x06" + struct.pack(">BHB", ord(cmd), addr, length) + data
|
72
|
|
73
|
def tx(radio, frame):
|
74
|
"""Send data to radio"""
|
75
|
radio.write(frame)
|
76
|
|
77
|
def rx(radio, addr, length):
|
78
|
"""Get data from the radio """
|
79
|
# 1 byte ACK +
|
80
|
# 4 bytes header +
|
81
|
# data of length of data (as I see 0x40 = 64 bytes)
|
82
|
# it's send a byte at a time, but we can read it all together
|
83
|
|
84
|
# catching ack
|
85
|
ack = radio.read(1)
|
86
|
if (ack == "\x05" and addr == 0) or ack == "\x06":
|
87
|
# received a correct ACK
|
88
|
print("Correct ACK (0x%02x) for block: 0x%04x" % (ord(ack), addr))
|
89
|
else:
|
90
|
print("BAD ACK (0x%02x) for block: 0x%04x" % (ord(ack), addr))
|
91
|
raise Exception, "Error"
|
92
|
|
93
|
# Get the header
|
94
|
hdr = radio.read(4)
|
95
|
if len(hdr) != 4:
|
96
|
print("BAD header length (%s) for block: 0x%04x" % (len(hdr), addr))
|
97
|
print("Try to make the time.sleep() bigger in dump_mem")
|
98
|
raise Exception, "Error"
|
99
|
else:
|
100
|
c, a, l = struct.unpack(">BHB", hdr)
|
101
|
print("Header received:")
|
102
|
print hexprint(hdr)
|
103
|
|
104
|
if a != addr or l != length or c != ord("X"):
|
105
|
print("BAD header data")
|
106
|
print("cmd %s, addr %04x, length %02x" % (c, a, l))
|
107
|
raise Exception, "Error"
|
108
|
else:
|
109
|
print("GOOD header data...")
|
110
|
|
111
|
# Get the data
|
112
|
data = radio.read(l)
|
113
|
if len(data) != l:
|
114
|
print("BAD data length of %s, vs. %s" % (l, length))
|
115
|
raise Exception, "Error"
|
116
|
else:
|
117
|
print("GOOD data...")
|
118
|
|
119
|
return data
|
120
|
|
121
|
def do_magic():
|
122
|
"""Try to get the radio y program mode and get the ident string"""
|
123
|
# every byte of this magic chain must be send separatedly util
|
124
|
# you get a correct answer, this will try 10 times
|
125
|
magic = "\x55\x20\x15\x09\x20\x45\x4d\x02"
|
126
|
tries = 10
|
127
|
|
128
|
# do the magic
|
129
|
for a in range(0, tries):
|
130
|
radio.flushInput()
|
131
|
for byte in magic:
|
132
|
ack = radio.read(1)
|
133
|
radio.write(byte)
|
134
|
|
135
|
# wait a prudent time....
|
136
|
time.sleep(0.2)
|
137
|
# Now you get a x06 of ACK if all goes well
|
138
|
ack = radio.read(1)
|
139
|
if ack == "\x06":
|
140
|
return True
|
141
|
else:
|
142
|
print "Attempt %s didn't work, trying again..." % a
|
143
|
|
144
|
return False
|
145
|
|
146
|
def do_ident(radio):
|
147
|
"""Put the radio on PROGRAM mode & identify it"""
|
148
|
|
149
|
# this is the block of good ident (less the fisrt ACK = \x06)
|
150
|
#goodident = "\x01\x03\x00\x01\x07\x09\x04\x00\x00\x05\x02\x00\x57\x48\x4B"
|
151
|
#goodident += "\x4A\x55\x56\x2D\x31\x36\x38\x56\x31\x39\x32\x30\x34\x55\x38"
|
152
|
#goodident += "\x38\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
|
153
|
#goodident += "\x30\x30\x30\x55"
|
154
|
# lenght = 49
|
155
|
goodident = "\x01\x03\x00\x01\x07\x09\x04\x00"
|
156
|
goodident += "\x00\x05\x02\x00\x57\x48\x4B\x4a"
|
157
|
goodident += "\x31\x36\x38\x4d\x49\x4e\x4d\x32"
|
158
|
goodident += "\x39\x32\x30\x34\x55\x38\x38\x30"
|
159
|
goodident += "\x30\x30\x30\x30\x30\x30\x30\x30"
|
160
|
goodident += "\x30\x30\x30\x30\x30\x30\x30\x30"
|
161
|
goodident += "\x55"
|
162
|
|
163
|
|
164
|
# try to get the radio in program mode
|
165
|
ack = do_magic()
|
166
|
if ack is False:
|
167
|
raise Exception,"Radio did not accept program mode"
|
168
|
else:
|
169
|
print("Radio answered PC query, continue...")
|
170
|
|
171
|
# Ok, get the ident string
|
172
|
ident = radio.read(49)
|
173
|
|
174
|
# check if ident is OK
|
175
|
if len(ident) == 49 and ident == goodident:
|
176
|
# all ok
|
177
|
print("Good Ident it's a BTECH UV-2501, ident follows")
|
178
|
print(hexprint(ident))
|
179
|
else:
|
180
|
print("Bad/short/long radio ident: (%02i) bytes:" % len(ident))
|
181
|
print("Try to makee the time.sleep() bigger in dump_mem")
|
182
|
print(hexprint(ident))
|
183
|
raise Exception, "Error"
|
184
|
|
185
|
def dump_mem(radio):
|
186
|
"""Get the memory map"""
|
187
|
data = ""
|
188
|
for addr in range(0x0000, 0x3200, 0x40):
|
189
|
# sending the read request
|
190
|
tx(radio, make_frame("S", addr, 0x40))
|
191
|
|
192
|
# receiving the data and catching any errors
|
193
|
time.sleep(0.5)
|
194
|
try:
|
195
|
d = rx(radio, addr, 0x40)
|
196
|
except:
|
197
|
raise Exception, "Problem receiving block %s" % addr
|
198
|
|
199
|
print("Received block %04x" % addr)
|
200
|
data += d
|
201
|
|
202
|
return data
|
203
|
|
204
|
def upload(radio, data):
|
205
|
"""Test the upload procedure"""
|
206
|
|
207
|
# the fun start here
|
208
|
for addr in range(0x0000, 0x3200, 0x10):
|
209
|
# sending the data
|
210
|
tx(radio, make_frame("\x58", addr, 0x10, data[addr:addr + 0x10]))
|
211
|
|
212
|
# receiving the response
|
213
|
time.sleep(0.1)
|
214
|
ack = radio.read(1)
|
215
|
|
216
|
if (addr == 0 and ack == "\x05"):
|
217
|
ack = radio.read(1)
|
218
|
|
219
|
if ack == "\x06":
|
220
|
# received a correct ACK
|
221
|
print("Ok writed block block: 0x%04x" % addr)
|
222
|
else:
|
223
|
print("BAD ACK (0x%02x) for block: 0x%04x" % (ord(ack), addr))
|
224
|
raise Exception, "Error"
|
225
|
|
226
|
|
227
|
wsp = ""
|
228
|
# main program
|
229
|
for sp in range(1, 11):
|
230
|
# try to identify valid COM ports and use them to download img of the radio
|
231
|
# the IMG will be on c:\[filename].img
|
232
|
|
233
|
try:
|
234
|
radio = serial.Serial('/dev/ttyUSB0') # for windows
|
235
|
except Exception, e:
|
236
|
continue
|
237
|
|
238
|
# this port works
|
239
|
print " "
|
240
|
print("Serial por COM%s available, traying to reach radio" % sp)
|
241
|
|
242
|
try:
|
243
|
radio.setBaudrate(9600)
|
244
|
radio.setParity("N")
|
245
|
radio.setTimeout(0.5)
|
246
|
radio.flushInput()
|
247
|
radio.flushOutput()
|
248
|
except Exception, e:
|
249
|
print " "
|
250
|
print("Problem setting discipline on COM%s" % sp)
|
251
|
radio.close()
|
252
|
continue
|
253
|
|
254
|
# ok, will try to reach radio
|
255
|
do_ident(radio)
|
256
|
print " "
|
257
|
print("Radio Found!!!, downloading image to %s.img" % filename)
|
258
|
d = dump_mem(radio)
|
259
|
|
260
|
# save to file % dump to screen
|
261
|
print " "
|
262
|
print("Done, img file ready")
|
263
|
fil = open(os.path.join("", filename + ".img" ), 'wb')
|
264
|
fil.write(d)
|
265
|
fil.close()
|
266
|
# set the correct serial port
|
267
|
wsp = sp
|
268
|
radio.close()
|
269
|
|
270
|
print("If download was a succsess I will wait for 2 minutes and then I will")
|
271
|
print("try a upload of the same data, so, you have 2 minutes to hit CTRL+C")
|
272
|
print("to abort starting from now")
|
273
|
|
274
|
time.sleep(120)
|
275
|
|
276
|
print("I will try an upload now, cross yur fingers")
|
277
|
|
278
|
## test upload
|
279
|
radio = serial.Serial('/dev/ttyUSB0')
|
280
|
try:
|
281
|
radio.setBaudrate(9600)
|
282
|
radio.setParity("N")
|
283
|
radio.setTimeout(0.5)
|
284
|
radio.flushInput()
|
285
|
radio.flushOutput()
|
286
|
except Exception, e:
|
287
|
raise Exception("Problem setting discipline on COM%s" % wsp)
|
288
|
|
289
|
# open radio
|
290
|
do_ident(radio)
|
291
|
# check the dat is correct
|
292
|
fil = open(os.path.join("", filename + ".img" ), 'rb')
|
293
|
d = fil.read()
|
294
|
fil.close()
|
295
|
|
296
|
if len(d) != 0x3200:
|
297
|
raise Exception("This is not the correct file, size don't match")
|
298
|
|
299
|
upload(radio, d)
|
300
|
radio.close()
|
301
|
|