Project

General

Profile

New Model #3015 » UV5001_DN.py

Jim Unroe, 01/20/2016 04:42 AM

 
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 = ".\\"
17
filename = "uv5001"
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

    
156
    # try to get the radio in program mode
157
    ack = do_magic()
158
    if ack is False:
159
        raise Exception,"Radio did not accept program mode"
160
    else:
161
        print("Radio answered PC query, continue...")
162

    
163
    # Ok, get the ident string
164
    ident = radio.read(49)
165

    
166
    # check if ident is OK
167
    if len(ident) == 49 and ident == goodident:
168
        # all ok
169
        print("Good Ident it's a BTECH UV-5001, ident follows")
170
        print(hexprint(ident))
171
    else:
172
        print("Bad/short/long radio ident: (%02i) bytes:" % len(ident))
173
        print("Try to makee the time.sleep() bigger in dump_mem")
174
        print(hexprint(ident))
175
        raise Exception, "Error"
176

    
177
def dump_mem(radio):
178
    """Get the memory map"""
179
    data = ""
180
    for addr in range(0x0000, 0x4000, 0x40):
181
        # sending the read request
182
        tx(radio, make_frame("S", addr, 0x40))
183

    
184
        # receiving the data and catching any errors
185
        time.sleep(0.1)
186
        try:
187
            d = rx(radio, addr, 0x40)
188
        except:
189
            raise Exception, "Problem receiving block %s" % addr
190

    
191
        print("Received block %04x" % addr)
192
        data += d
193

    
194
    return data
195

    
196
def upload(radio, data):
197
    """Test the upload procedure"""
198

    
199
    # the fun start here
200
    for addr in range(0x0000, 0x4000, 0x10):
201
        # sending the data
202
        tx(radio, make_frame("\x58", addr, 0x10, data[addr:addr + 0x10]))
203

    
204
        # receiving the response
205
        time.sleep(0.1)
206
        ack = radio.read(1)
207

    
208
        if (addr == 0 and ack == "\x05"):
209
            ack = radio.read(1)
210

    
211
        if ack == "\x06":
212
            # received a correct ACK
213
            print("Ok writed block block: 0x%04x" % addr)
214
        else:
215
            print("BAD ACK (0x%02x) for block: 0x%04x" % (ord(ack), addr))
216
            raise Exception, "Error"
217

    
218

    
219
wsp = ""
220
# main program
221
for sp in range(1, 11):
222
    # try to identify valid COM ports and use them to download img of the radio
223
    # the IMG will be on c:\[filename].img
224

    
225
    try:
226
        radio = serial.Serial(port="COM" + str(sp)) # for windows
227
    except Exception, e:
228
        continue
229

    
230
    # this port works
231
    print " "
232
    print("Serial por COM%s available, traying to reach radio" % sp)
233

    
234
    try:
235
        radio.setBaudrate(9600)
236
        radio.setParity("N")
237
        radio.setTimeout(0.5)
238
        radio.flushInput()
239
        radio.flushOutput()
240
    except Exception, e:
241
        print " "
242
        print("Problem setting discipline on COM%s" % sp)
243
        radio.close()
244
        continue
245

    
246
    # ok, will try to reach radio
247
    do_ident(radio)
248
    print " "
249
    print("Radio Found!!!, downloading image to %s.img" % filename)
250
    d = dump_mem(radio)
251

    
252
    # save to file % dump to screen
253
    print " "
254
    print("Done, img file ready")
255
    fil = open(os.path.join(fpath, filename + "_dn.img" ), 'wb')
256
    fil.write(d)
257
    fil.close()
258
    # set the correct serial port
259
    wsp = sp
260
    radio.close()
261

    
262
"""
263
print("If download was a succsess I will wait for 2 minutes and then I will")
264
print("try a upload of the same data, so, you have 2 minutes to hit CTRL+C")
265
print("to abort starting from now")
266

    
267
time.sleep(120)
268

    
269
print("I will try an upload now, cross yur fingers")
270

    
271
## test upload
272
radio = serial.Serial(port="COM" + str(wsp))
273
try:
274
    radio.setBaudrate(9600)
275
    radio.setParity("N")
276
    radio.setTimeout(0.5)
277
    radio.flushInput()
278
    radio.flushOutput()
279
except Exception, e:
280
    raise Exception("Problem setting discipline on COM%s" % wsp)
281

    
282
# open radio
283
do_ident(radio)
284
# check the dat is correct
285
fil = open(os.path.join(fpath, filename + "_up.img" ), 'rb')
286
d = fil.read()
287
fil.close()
288

    
289
if len(d) != 0x3200:
290
    raise Exception("This is not the correct file, size don't match")
291

    
292
upload(radio, d)
293
radio.close()
294
"""
(8-8/10)