AnasSarkiz/magnetic-shutter-remote-r8
A USB‑C rechargeable battery-powered ESP32‑C3 wireless controller with Li‑ion charging, protected 3.3‑V buck‑boost regulation, thermal/safety monitoring, status LEDs, buttons, programming UART, crystal clock, and 2.4‑GHz antenna matching network.
- Version
- 0.4.0-prototype
- License
- unset
- Stars
- 0
scripts/flash-r8-standard-jst.py
"""Manual ESP32-C3 ROM flashing through standard-jst-programmer CDC0 UART.
Default performs image validation only. --write and an explicit CDC0 --port
are required to touch hardware. Uses public esptool4.8.1 existing-port API.
"""
import argparse
import hashlib
import io
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
DEFAULT_IMAGE = ROOT/'firmware/artifacts/R8-DOIT-C3-hosted-2026-10-05/zephyr.bin'
DEFAULT_SHA256 = 'bef862d532945b309b30fa0f7bf2313db459de81f5261c3f8e4816d43de7e132'
def validate_image(path, expected_sha256, image_class, chip_id):
raw=path.read_bytes()
if len(raw)>4*1024*1024 or hashlib.sha256(raw).hexdigest()!=expected_sha256:
raise ValueError('Image size/hash does not match the reviewed ESP32-C3 artifact')
image=image_class(io.BytesIO(raw))
image.verify()
if image.chip_id!=chip_id:
raise ValueError('Only ESP32-C3 images are accepted')
if image.checksum!=image.calculate_checksum():
raise ValueError('ESP image segment checksum differs')
if image.append_digest and image.stored_digest!=image.calc_digest:
raise ValueError('ESP image appended SHA256 differs')
return {'bytes':len(raw),'sha256':hashlib.sha256(raw).hexdigest(),'chip_id':image.chip_id}
def flash(port,image,serial_module,esptool_module,rom_class):
# Construct while closed so Windows receives DTR=true at port opening.
with serial_module.Serial(port=None,baudrate=115200,timeout=3) as connection:
connection.port=port
connection.dtr=True
connection.rts=False
connection.open()
esp=rom_class(connection,baud=115200)
esp.connect('no_reset')
if esp.secure_download_mode or esp.read_reg(esp.CHIP_DETECT_MAGIC_REG_ADDR) not in esp.CHIP_DETECT_MAGIC_VALUE:
raise ValueError('A positively identified ESP32-C3 ROM in normal download mode is required')
common=['--chip','esp32c3','--port',port,'--baud','115200',
'--before','no_reset','--after','no_reset','--no-stub']
esptool_module.main(common+['write_flash','--flash_mode','dio',
'--flash_freq','80m','--flash_size','4MB','0x0',str(image)],esp=esp)
esptool_module.main(common+['verify_flash','0x0',str(image)],esp=esp)
print('Flash and readback verification completed. Release BOOT and press RESET to run.')
def main():
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('--image',type=Path,default=DEFAULT_IMAGE)
parser.add_argument('--sha256',default=DEFAULT_SHA256,help='Reviewed image SHA256 for later authorized firmware revisions')
parser.add_argument('--port',help='Explicit CDC0 UART device, never the CDC1 telemetry port')
parser.add_argument('--write',action='store_true',help='Write flash after manually entering BOOT+RESET download mode')
args=parser.parse_args()
import esptool
import serial
from serial.tools import list_ports
if esptool.__version__!='4.8.1' or serial.__version__!='3.5':
raise ValueError('Install the reviewed host tools: esptool==4.8.1 pyserial==3.5')
from esptool.bin_image import ESP32C3FirmwareImage
from esptool.targets.esp32c3 import ESP32C3ROM
result=validate_image(args.image,args.sha256,ESP32C3FirmwareImage,ESP32C3ROM.IMAGE_CHIP_ID)
print('Image validation PASS:',result)
if not args.write:
print('No serial device opened. Use --write --port after following PROGRAMMING.md.')
return
if not args.port:
parser.error('--write requires the explicit CDC0 --port')
for port in list_ports.comports():
if port.device==args.port and 'telemetry' in (' '.join(str(x or '') for x in [port.description,port.interface])).lower():
raise ValueError('The selected port is telemetry CDC1; choose UART CDC0')
print('Using manual reset with DTR enabled; board battery on, board USB-C unplugged.')
flash(args.port,args.image,serial,esptool,ESP32C3ROM)
if __name__=='__main__':
main()