Project

General

Profile

New Model #3015 » uv2501.py

Modified uv5001.py to support uv2501 - Foo Bar, 01/19/2016 06:03 PM

 
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

    
(6-6/10)