-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathsend.py
More file actions
219 lines (190 loc) · 6.57 KB
/
send.py
File metadata and controls
219 lines (190 loc) · 6.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
## Title: send.py
## Author: Jeroen Venema
## Created: 25/10/2022
## Last update: 30/10/2025
##
## Edited by Steve Lovejoy for linux DTR issue.
##
## syntax
## send.py FILENAME <PORT> <BAUDRATE>
##
## Modinfo:
## 25/10/2022 initial version
## 10/09/2023 Script converts binary file to Intel Hex during transmission.
## Using defaults as constants.
## 11/09/2023 Wait time variable introduced for handling PC serial drivers with low buffer memory
## 29/01/2024 OS-specific serial settings to prevent board reset upon opening of serial port
## 22/10/2025 Removed termios, several issues with MacOS. Tested correct DTR on Win/Linux/MacOS
## 28/10/2025 Improved autodetection of usb/serial interfaces
## 30/10/2025 Script accepts 'auto' as PORT name
DEFAULT_START_ADDRESS = 0x40000
DEFAULT_BAUDRATE = 115200
DEFAULT_LINE_WAITTIME = 0.000 ## A value of +/- 0.003 Helps PC serial drivers with low buffer memory
IHEXBYTELENGTH = 255
WAITCRC = True # request extended format, if available from the VDP
def hexchecksum(hexstring):
bytestring = b"" # Just to shut up the lexical analyzer
result = hexstring[1:]
checksum = 0
msn = True
for digit in result:
if msn:
bytestring = digit
msn = False
else:
bytestring += digit
checksum += int(bytestring, 16)
msn = True
checksum = checksum & 0xff
checksum = (~checksum & 0xff) + 1
return checksum
def create_startrecord(crc32):
startrecord = ':060000FF0000' + hex(crc32)[2:].zfill(8).upper()
startrecord += hex(hexchecksum(startrecord))[2:].zfill(2).upper()
return startrecord
def create_stoprecord():
stoprecord = ':020000FF0001'
stoprecord += hex(hexchecksum(stoprecord))[2:].zfill(2).upper()
return stoprecord
import sys
import time
import os
import os.path
import tempfile
try:
import serial
import serial.tools.list_ports
except ModuleNotFoundError:
sys.exit('Error: missing \'pyserial\' module')
try:
from intelhex import IntelHex
except ModuleNotFoundError:
sys.exit('Error: missing \'intelhex\' module')
try:
import crcmod
except ModuleNotFoundError:
sys.exit('Error: missing \'crcmod\' module')
crc16 = crcmod.mkCrcFun(0x18005, 0x0, False, 0x0)
crc32 = crcmod.crcmod.predefined.Crc('crc-32')
if len(sys.argv) == 1 or len(sys.argv) >4:
print('Usage: send.py FILENAME <PORT> <BAUDRATE>\n')
print('PORT autodetection: leave PORT empty or specify auto')
sys.exit()
if not os.path.isfile(sys.argv[1]):
sys.exit(f'Error: file \'{sys.argv[1]}\' not found')
if len(sys.argv) == 2 or (len(sys.argv) > 2 and sys.argv[2] == "auto"):
serialports = serial.tools.list_ports.comports()
# Filter out common non-usb serial devices from the autodetect list
ports = []
for port in serialports:
ports.append(str(port))
serialports = list(filter(lambda x: "usb" in x.lower(), ports))
# Error out if autodetection still results in multiple ports
if len(serialports) > 1:
print("Multiple serial ports present - cannot automatically select:")
for serialport in serialports:
print(serialport)
sys.exit()
if len(serialports) == 0:
sys.exit("No usb serial port found")
serialport = str(serialports[0]).split(" ")[0]
#if len(sys.argv) >= 3:
else:
serialport = sys.argv[2]
if len(sys.argv) == 4:
baudrate = int(sys.argv[3])
else:
baudrate = DEFAULT_BAUDRATE
nativehexfile = ((sys.argv[1])[-3:] == 'hex') or ((sys.argv[1])[-4:] == 'ihex')
# report parameters used
print(f'Sending \'{sys.argv[1]}\' ', end="")
if nativehexfile: print('as native hex file')
else:
print('as binary data, in Intel Hex format')
print(f'Using start address 0x{DEFAULT_START_ADDRESS:x}')
print(f'Using serial port {serialport}')
print(f'Using Baudrate {baudrate}')
if nativehexfile:
# calculate crc32 of actual hex content data
ihex = IntelHex()
ihex.loadhex(sys.argv[1])
for start,end in ihex.segments():
while start != end:
byte = ihex[start].to_bytes(1,"little")
crc32.update(byte)
start += 1
# open native hex file for sending
file = open(sys.argv[1], "r")
content = file.readlines()
else:
# Instantiate ihex object and load binary file to it, write out as ihex format to temp file
ihex = IntelHex()
file = tempfile.TemporaryFile("w+t")
ihex.loadbin(sys.argv[1], offset=DEFAULT_START_ADDRESS)
ihex.write_hex_file(file, byte_count = IHEXBYTELENGTH)
file.seek(0)
# Calculate crc from binary file
with open(sys.argv[1], "rb") as f:
byte = f.read(1)
while byte:
crc32.update(byte)
byte = f.read(1)
ser = serial.Serial()
ser.baudrate = baudrate
ser.port = serialport
ser.timeout = 2
# OS-specific serial dtr/rts settings
if(os.name == 'nt'):
ser.setDTR(False)
ser.setRTS(False)
if(os.name == 'posix'):
ser.rtscts = False # not setting to false prevents communication
ser.dsrdtr = False # determines if Agon resets or not
try:
ser.open()
print('Opening serial port...')
if not nativehexfile:
content = file
if WAITCRC:
startrecord = create_startrecord(crc32.crcValue)
ser.write(startrecord.encode())
sent = crc16(startrecord.strip().encode('ascii'))
ser.write(hex(sent)[2:].zfill(4).upper().encode())
time.sleep(0.5)
if ser.in_waiting:
ret = int.from_bytes(ser.read(2), "little")
sent = crc16(startrecord.encode('ascii'))
if ret != sent:
sys.exit("Extended header sending error, aborting")
else:
WAITCRC = False # Target VDP has no extended code built in
print('VDP doesn\'t support extended CRC16/32; sending regular format')
print('Sending data...')
for line in content:
linesent_ok = False
while not linesent_ok:
ser.write(str(line).strip().encode('ascii'))
linesent_ok = True
if WAITCRC:
sent = crc16(line.strip().encode('ascii'))
ser.write(hex(sent)[2:].zfill(4).upper().encode())
while ser.in_waiting < 2:
pass
ret = int.from_bytes(ser.read(2), "little")
if ret != sent:
print("CRC error, retransmitting")
linesent_ok = False
time.sleep(DEFAULT_LINE_WAITTIME)
if WAITCRC:
while ser.in_waiting == 0:
pass
crc32result = int.from_bytes(ser.read(4), "little")
if crc32result == crc32.crcValue:
print('CRC - OK')
else:
print('CRC ERROR')
print('Done')
ser.close()
except serial.SerialException:
sys.exit('Error: serial port unavailable')
file.close()