1313CMD_GET_ID = const (0x02 ) # Get chip ID
1414CMD_ERASE = const (0x44 ) # Erase memory
1515CMD_GO = const (0x21 ) # Jumps to user application code located in the internal flash memory
16- CMD_WRITE = const (0x32 ) # Write memory
16+ CMD_WRITE_NO_STRETCH = const (0x32 ) # Writes up to 256 bytes to the memory, starting from an address specified
17+ CHUNK_SIZE = const (128 ) # Size of the memory chunk to write
1718
1819# Define I2C pins and initialize I2C
1920i2c = I2C (0 , freq = 100000 )
@@ -29,9 +30,9 @@ def send_reset(address):
2930 buffer += b'\x00 ' * (8 - len (buffer )) # Pad buffer to 8 bytes
3031
3132 try :
32- print (f"Sending reset command to address { hex (address )} " )
33+ print (f"🔄 Resetting device at address { hex (address )} " )
3334 i2c .writeto (address , buffer , True )
34- print ("Reset command sent successfully" )
35+ print ("✅ Reset command sent successfully" )
3536 time .sleep (0.25 ) # Wait for the device to reset
3637 return True
3738 except OSError as e :
@@ -55,13 +56,12 @@ def wait_for_ack():
5556 while res == BUSY :
5657 time .sleep (0.1 )
5758 res = i2c .readfrom (BOOTLOADER_I2C_ADDRESS , 1 )[0 ]
58- print ("Waiting for device to be finish processing" )
5959 if res != ACK :
60- print (f"Error processing command: { hex (res )} " )
60+ print (f"❌ Error processing command. Result code : { hex (res )} " )
6161 return False
6262 return True
6363
64- def execute_command (opcode , command_data , response_length = 0 , verbose = True ):
64+ def execute_command (opcode , command_data , response_length = 0 , verbose = False ):
6565 """
6666 Execute an I2C command on the device.
6767
@@ -72,34 +72,36 @@ def execute_command(opcode, command_data, response_length = 0, verbose=True):
7272 :return: The number of response bytes read, or -1 if an error occurred.
7373 """
7474 if verbose :
75- print (f"Executing command { hex (opcode )} " )
75+ print (f"🕵️ Executing command { hex (opcode )} " )
7676
7777 cmd = bytes ([opcode , 0xFF ^ opcode ]) # Send command code and complement (XOR = 0x00)
7878 i2c .writeto (BOOTLOADER_I2C_ADDRESS , cmd , True )
7979 if not wait_for_ack ():
80- print (f"Command not acknowledged: { hex (opcode )} " )
80+ print (f"❌ Command not acknowledged: { hex (opcode )} " )
8181 return None
8282
8383 if command_data is not None :
8484 i2c .writeto (BOOTLOADER_I2C_ADDRESS , command_data , True )
8585 if not wait_for_ack ():
86- print ("Command failed" )
86+ print ("❌ Command failed" )
8787 return None
8888
8989 if response_length == 0 :
9090 return None
9191
9292 data = i2c .readfrom (BOOTLOADER_I2C_ADDRESS , response_length )
9393 amount_of_bytes = data [0 ] + 1
94- print (f"Retrieved { amount_of_bytes } bytes" ) # TODO: Remove this line
94+
95+ if verbose :
96+ print (f"📦 Received { amount_of_bytes } bytes" )
9597
9698 if not wait_for_ack ():
97- print ("Failed completing command" )
99+ print ("❌ Failed completing command" )
98100 return None
99101
100102 return data [1 : amount_of_bytes + 1 ]
101103
102- def flash_firmware (firmware_path , verbose = True ):
104+ def flash_firmware (firmware_path , verbose = False ):
103105 """
104106 Flash the firmware to the I2C device.
105107
@@ -110,87 +112,85 @@ def flash_firmware(firmware_path, verbose=True):
110112 """
111113 data = execute_command (CMD_GET , None , 20 , verbose )
112114 if data is None :
113- print ("Failed :( " )
115+ print ("❌ Failed to get command list " )
114116 return False
115117
116- print (f"Bootloader version: { hex (data [0 ])} " )
117- print ("Supported commands:" )
118- for byte in data [1 :]:
119- print (hex (byte ))
118+ print (f"ℹ️ Bootloader version: { hex (data [0 ])} " )
119+ print ("👀 Supported commands:" )
120+ print (", " .join ([hex (byte ) for byte in data [1 :]]))
120121
121122 data = execute_command (CMD_GET_ID , None , 3 , verbose )
122123 if data is None :
123- print ("Failed to get device ID" )
124+ print ("❌ Failed to get device ID" )
124125 return False
125126
126127 chip_id = (data [0 ] << 8 ) | data [1 ] # Chip ID: Byte 1 = MSB, Byte 2 = LSB
127- print (f"Chip ID: { chip_id } " )
128+ print (f"ℹ️ Chip ID: { chip_id } " )
128129
129- print ("Erasing memory..." )
130+ print ("🗑️ Erasing memory..." )
130131 erase_params = bytearray ([0xFF , 0xFF , 0x0 ]) # Mass erase flash
131132 #execute_command(CMD_ERASE, erase_params, 0, verbose)
132133
133134 with open (firmware_path , 'rb' ) as file :
134135 firmware_data = file .read ()
135136 total_bytes = len (firmware_data )
136137
137- print (f"Flashing { total_bytes } bytes of firmware" )
138- for i in range (0 , total_bytes , 128 ):
138+ print (f"🔥 Flashing { total_bytes } bytes of firmware" )
139+ for i in range (0 , total_bytes , CHUNK_SIZE ):
139140 progress_bar (i , total_bytes )
140- write_buffer = bytearray ([8 , 0 , i // 256 , i % 256 ])
141- # if write_firmware_page(write_buffer, 5, firmware_data[i:i + 128], 128, verbose) < 0:
142- # print(f"Failed to write page {hex(i)}")
143- # return False
144- time .sleep (0.01 )
141+ start_address = bytearray ([8 , 0 , i // 256 , i % 256 ]) # 4-byte address: byte 1 = MSB, byte 4 = LSB
142+ checksum = 0
143+ for b in start_address :
144+ checksum ^= b
145+ start_address .append (checksum )
146+ data_slice = firmware_data [i :i + CHUNK_SIZE ]
147+ if not write_firmware_page (start_address , data_slice ):
148+ print (f"❌ Failed to write page { hex (i )} " )
149+ return False
150+ time .sleep (0.01 ) # Give the device some time to process the data
145151
146152 progress_bar (total_bytes , total_bytes ) # Complete the progress bar
147153
148- print ("Starting firmware" )
154+ print ("🏃 Starting firmware" )
149155 go_params = bytearray ([0x8 , 0x00 , 0x00 , 0x00 , 0x8 ])
150156 #execute_command(CMD_GO, go_params, 0, verbose) # Jump to the application
151157
152158 return True
153159
154- def write_firmware_page (command_buffer , command_length , firmware_buffer , firmware_length , verbose = True ):
160+ def write_firmware_page (command_params , firmware_data ):
155161 """
156162 Write a page of the firmware to the I2C device.
157163
158- :param opcode: The command opcode.
159- :param command_buffer: The buffer containing the command data.
160- :param command_length: The length of the command data.
161- :param firmware_buffer: The buffer containing the firmware data.
162- :param firmware_length: The length of the firmware data.
164+ :param command_params: The buffer containing the command data.
165+ :param firmware_data: The buffer containing the firmware data.
163166 :param verbose: Whether to print debug information.
164167 :return: The number of bytes written, or -1 if an error occurred.
165168 """
166- cmd = bytes ([CMD_WRITE , 0xFF ^ CMD_WRITE ])
167- i2c .writeto (100 , cmd )
168-
169- if command_length > 0 :
170- command_buffer [command_length - 1 ] = 0
171- for i in range (command_length - 1 ):
172- command_buffer [command_length - 1 ] ^= command_buffer [i ]
173- i2c .writeto (100 , command_buffer )
169+ cmd = bytes ([CMD_WRITE_NO_STRETCH , 0xFF ^ CMD_WRITE_NO_STRETCH ])
170+ i2c .writeto (BOOTLOADER_I2C_ADDRESS , cmd )
171+ if not wait_for_ack ():
172+ print ("❌ Write command not acknowledged" )
173+ return False
174174
175- ack = i2c .readfrom ( 100 , 1 )[ 0 ]
176- if ack != 0x79 :
177- print (f"Error first ack: { hex ( ack ) } " )
178- return - 1
175+ i2c .writeto ( BOOTLOADER_I2C_ADDRESS , command_params )
176+ if not wait_for_ack () :
177+ print ("❌ Failed to write command parameters " )
178+ return False
179179
180- tmp_buffer = bytearray (firmware_length + 2 )
181- tmp_buffer [0 ] = firmware_length - 1
182- tmp_buffer [1 :firmware_length + 1 ] = firmware_buffer
183- tmp_buffer [firmware_length + 1 ] = 0
184- for i in range (firmware_length + 1 ):
185- tmp_buffer [firmware_length + 1 ] ^= tmp_buffer [i ]
180+ data_size = len (firmware_data )
181+ tmp_buffer = bytearray (data_size + 2 ) # Data plus size and checksum
182+ tmp_buffer [0 ] = data_size - 1 # Size of the data # TODO Arduino code uses data_size - 1
183+ tmp_buffer [1 :data_size + 1 ] = firmware_data
184+ tmp_buffer [- 1 ] = 0 # Checksum placeholder
185+ for i in range (data_size + 1 ): # Calculate checksum over size byte + data bytes
186+ tmp_buffer [- 1 ] ^= tmp_buffer [i ]
186187
187- i2c .writeto (100 , tmp_buffer )
188- ack = i2c .readfrom (100 , 1 )[0 ]
189- if ack != 0x79 :
190- print (f"Error: { hex (ack )} " )
191- return - 1
188+ i2c .writeto (BOOTLOADER_I2C_ADDRESS , tmp_buffer )
189+ if not wait_for_ack ():
190+ print ("❌ Failed to write firmware" )
191+ return False
192192
193- return firmware_length
193+ return True
194194
195195def progress_bar (current , total , bar_length = 40 ):
196196 """
@@ -223,11 +223,11 @@ def select_file(bin_files):
223223 :return: The selected .bin file name.
224224 """
225225 if len (bin_files ) == 1 :
226- confirm = input (f"Found one bin file: { bin_files [0 ]} . Do you want to flash it? (yes/no) " )
226+ confirm = input (f"👀 Found one bin file: { bin_files [0 ]} . Do you want to flash it? (yes/no) " )
227227 if confirm .lower () == 'yes' :
228228 return bin_files [0 ]
229229 else :
230- print ("Found bin files:" )
230+ print ("👀 Found bin files:" )
231231 for index , file in enumerate (bin_files ):
232232 print (f"{ index + 1 } . { file } " )
233233 choice = int (input ("Select the file to flash (number): " ))
@@ -240,7 +240,7 @@ def select_i2c_device():
240240 :return: The selected I2C device address.
241241 """
242242 devices = i2c .scan ()
243- print ("I2C devices found:" )
243+ print ("👀 I2C devices found:" )
244244 for index , device in enumerate (devices ):
245245 print (f"{ index + 1 } . Address: { hex (device )} " )
246246 choice = int (input ("Select the I2C device to flash (number): " ))
@@ -251,29 +251,27 @@ def setup():
251251 Setup function to initialize the flashing process.
252252 Finds .bin files, scans for I2C devices, and flashes the selected firmware.
253253 """
254- print ("Starting setup" )
255254
256255 bin_files = find_bin_files ()
257256 if not bin_files :
258- print ("No .bin files found in the root directory." )
257+ print ("❌ No .bin files found in the root directory." )
259258 return
260259
261260 bin_file = "node_base.bin" # select_file(bin_files)
262261
263- device_address = 30 # select_i2c_device()
264- print (f"Resetting device at address { hex (device_address )} " )
262+ device_address = 30 # select_i2c_device()
265263 if send_reset (device_address ):
266- print ("Device reset successfully" )
264+ print ("✅ Device reset successfully" )
267265 else :
268- print ("Failed to reset device" )
266+ print ("❌ Failed to reset device" )
269267 return
270268
271- print (f"Flashing { bin_file } to device at address { hex (BOOTLOADER_I2C_ADDRESS )} " )
269+ print (f"🕵️ Flashing { bin_file } to device at address { hex (BOOTLOADER_I2C_ADDRESS )} " )
272270
273271 if flash_firmware (bin_file ):
274- print ("Firmware flashed successfully" )
272+ print ("✅ Firmware flashed successfully" )
275273 else :
276- print ("Failed to flash firmware" )
274+ print ("❌ Failed to flash firmware" )
277275
278276# Start the setup
279277setup ()
0 commit comments