Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- """
- clone_eeprom_gpio_skiprom.py
- Read via sysfs and write DS2433 (1-Wire) via GPIO bit-bang using SKIP ROM (single device only).
- Requirements: RPi.GPIO, run with sudo.
- Hardware note: COPY SCRATCHPAD requires strong pull-up (or external Vcc) during t_PROG (~<=5 ms).
- """
- import RPi.GPIO as GPIO
- import time
- import os
- import glob
- import hashlib
- import subprocess
- import sys
- # ---------- CONFIG ----------
- W1_PATH = "/sys/bus/w1/devices"
- DUMP_DIR = "dumps"
- GPIO_PIN = 4 # wire on GPIO4 (BCM numbering)
- PAGE_SIZE = 32 # bytes per scratchpad/page
- READ_TIMEOUT = 12
- HOLD_SECONDS = 6
- # ----------------------------
- # 1-Wire command opcodes for DS2433 family
- CMD_READ_SCRATCHPAD = 0xAA
- CMD_WRITE_SCRATCHPAD = 0x0F
- CMD_COPY_SCRATCHPAD = 0x55
- CMD_SKIP_ROM = 0xCC
- # Timing helpers (microseconds)
- def udelay(us):
- time.sleep(us / 1_000_000.0)
- # ---------- 1-Wire bit-bang ----------
- GPIO.setmode(GPIO.BCM)
- GPIO.setup(GPIO_PIN, GPIO.OUT, initial=GPIO.HIGH)
- def drive_low():
- GPIO.setup(GPIO_PIN, GPIO.OUT)
- GPIO.output(GPIO_PIN, GPIO.LOW)
- def release_line():
- GPIO.setup(GPIO_PIN, GPIO.IN, pull_up_down=GPIO.PUD_OFF)
- def read_line():
- GPIO.setup(GPIO_PIN, GPIO.IN)
- return GPIO.input(GPIO_PIN)
- def reset_pulse():
- """Reset and detect presence pulse."""
- drive_low()
- udelay(500) # reset low
- release_line()
- udelay(100) # wait
- present = not read_line()
- udelay(400) # finish timeslot
- return present
- def write_bit(bit):
- if bit:
- drive_low()
- udelay(6)
- release_line()
- udelay(64)
- else:
- drive_low()
- udelay(60)
- release_line()
- udelay(10)
- def read_bit():
- drive_low()
- udelay(6)
- release_line()
- udelay(9)
- val = read_line()
- udelay(55)
- return 1 if val else 0
- def write_byte(b):
- for i in range(8):
- write_bit((b >> i) & 1)
- udelay(1)
- def read_byte():
- val = 0
- for i in range(8):
- val |= (read_bit() << i)
- return val
- def write_bytes(data):
- for b in data:
- write_byte(b)
- def read_bytes(n):
- return bytes(read_byte() for _ in range(n))
- # ---------- DS2433 write functions ----------
- def write_scratchpad_skiprom(ta1, ta2, page_bytes):
- """Write up to PAGE_SIZE bytes to scratchpad at address TA."""
- if not reset_pulse():
- raise RuntimeError("No device present before write_scratchpad")
- write_byte(CMD_SKIP_ROM) # single device on line
- write_byte(CMD_WRITE_SCRATCHPAD)
- write_byte(ta1)
- write_byte(ta2)
- write_bytes(page_bytes)
- udelay(1)
- def read_scratchpad_skiprom(expected_len=PAGE_SIZE):
- """Read scratchpad after write (SKIP ROM)."""
- if not reset_pulse():
- raise RuntimeError("No device present before read_scratchpad")
- write_byte(CMD_SKIP_ROM)
- write_byte(CMD_READ_SCRATCHPAD)
- ta1 = read_byte()
- ta2 = read_byte()
- es = read_byte()
- data = bytes(read_byte() for _ in range(expected_len))
- crc_l = read_byte()
- crc_h = read_byte()
- crc = (crc_h << 8) | crc_l
- return ta1, ta2, es, data, crc
- def copy_scratchpad_skiprom(ta1, ta2, es, strong_pullup_sec=0.010):
- """Issue COPY SCRATCHPAD (SKIP ROM)."""
- if not reset_pulse():
- raise RuntimeError("No device present before copy_scratchpad")
- write_byte(CMD_SKIP_ROM)
- write_byte(CMD_COPY_SCRATCHPAD)
- write_byte(ta1)
- write_byte(ta2)
- write_byte(es)
- # strong pull-up for t_PROG
- GPIO.setup(GPIO_PIN, GPIO.OUT)
- GPIO.output(GPIO_PIN, GPIO.HIGH)
- time.sleep(strong_pullup_sec)
- release_line()
- time.sleep(0.01)
- # ---------- Utilities ----------
- def sha1_file(path):
- h = hashlib.sha1()
- with open(path,"rb") as f:
- for chunk in iter(lambda: f.read(1<<20), b""):
- h.update(chunk)
- return h.hexdigest()
- def ensure_dir(d):
- os.makedirs(d, exist_ok=True)
- def list_devices():
- return sorted([os.path.basename(p) for p in glob.glob(os.path.join(W1_PATH,"23-*"))])
- def wait_for_device(prompt, ignore_uid=None, timeout=40):
- print(prompt)
- input("Press Enter to start waiting...")
- start = time.time()
- while time.time() - start < timeout:
- ids = list_devices()
- if ignore_uid:
- ids = [x for x in ids if x != ignore_uid]
- if ids:
- print("Found device:", ids[0])
- return ids[0]
- time.sleep(0.5)
- return None
- def read_eeprom_sysfs(uid, outpath):
- eeprom_path = os.path.join(W1_PATH, uid, "eeprom")
- alt = glob.glob(os.path.join(W1_PATH,"w1_bus_master1",uid,"eeprom"))
- if not os.path.exists(eeprom_path) and alt:
- eeprom_path = alt[0]
- if not os.path.exists(eeprom_path):
- raise FileNotFoundError("EEPROM path not found for "+uid)
- print("Reading sysfs eeprom from", eeprom_path)
- rc = subprocess.call(f"timeout {READ_TIMEOUT} dd if={eeprom_path} bs=1 count=512 of={outpath}", shell=True)
- size = os.path.getsize(outpath) if os.path.exists(outpath) else 0
- if rc != 0 or size != 512:
- raise RuntimeError(f"Failed to read EEPROM via sysfs (bytes={size}, rc={rc})")
- return outpath
- # ---------- Main workflow ----------
- def clone_workflow():
- if os.geteuid() != 0:
- print("Run as root (sudo). Exiting.")
- sys.exit(1)
- ensure_dir(DUMP_DIR)
- # --- Read SOURCE chip ---
- print("=== Read SOURCE chip via sysfs ===")
- uid1 = wait_for_device("Connect the SOURCE chip now.")
- if not uid1:
- print("Source not found.")
- return
- out1 = os.path.join(DUMP_DIR, f"{uid1}.bin")
- print(f"Waiting {HOLD_SECONDS}s before read...")
- time.sleep(HOLD_SECONDS)
- read_eeprom_sysfs(uid1, out1)
- print("SHA1 of source:", sha1_file(out1))
- # --- Write TARGET chip ---
- print("\nPrepare to write to TARGET chip.")
- print("Unplug source and plug TARGET. Ensure strong pull-up / Vcc.")
- uid2 = wait_for_device("Connect the TARGET chip now.", ignore_uid=uid1, timeout=60)
- if not uid2:
- print("Target not found.")
- return
- print("Found target UID:", uid2)
- input("Press Enter to start writing...")
- # delay to settle
- print("Waiting 1s for device to settle...")
- time.sleep(1)
- # read dump to write
- with open(out1,"rb") as f:
- full = f.read()
- if len(full)!=512:
- print("Source dump size not 512 bytes, abort.")
- return
- # write page by page
- for page in range(0,512,PAGE_SIZE):
- page_data = full[page:page+PAGE_SIZE]
- ta1 = page & 0xFF
- ta2 = (page >> 8) & 0xFF
- print(f"Writing page at offset {page} (TA1={ta1:02X}, TA2={ta2:02X}) ...")
- write_scratchpad_skiprom(ta1, ta2, page_data)
- ta1_r, ta2_r, es, data_read, crc = read_scratchpad_skiprom(PAGE_SIZE)
- if data_read[:len(page_data)] != page_data:
- raise RuntimeError(f"Scratchpad verify failed at page {page}")
- copy_scratchpad_skiprom(ta1_r, ta2_r, es, strong_pullup_sec=0.010)
- print(f"Page {page} written.")
- # verify via sysfs
- tmp_verify = os.path.join(DUMP_DIR,f"{uid2}.after.bin")
- try:
- read_eeprom_sysfs(uid2, tmp_verify)
- print("SHA1 source:", sha1_file(out1))
- print("SHA1 target:", sha1_file(tmp_verify))
- except Exception as e:
- print("Could not read back via sysfs:", e)
- print("Verify with external programmer if needed.")
- print("Done.")
- if __name__=="__main__":
- try:
- clone_workflow()
- finally:
- GPIO.cleanup()
Advertisement
Add Comment
Please, Sign In to add comment