Apparcane

Duplicate Stratasys chips

Nov 11th, 2025 (edited)
100
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.60 KB | Source Code | 0 0
  1. #!/usr/bin/env python3
  2. """
  3. clone_eeprom_gpio_skiprom.py
  4. Read via sysfs and write DS2433 (1-Wire) via GPIO bit-bang using SKIP ROM (single device only).
  5. Requirements: RPi.GPIO, run with sudo.
  6. Hardware note: COPY SCRATCHPAD requires strong pull-up (or external Vcc) during t_PROG (~<=5 ms).
  7. """
  8.  
  9. import RPi.GPIO as GPIO
  10. import time
  11. import os
  12. import glob
  13. import hashlib
  14. import subprocess
  15. import sys
  16.  
  17. # ---------- CONFIG ----------
  18. W1_PATH = "/sys/bus/w1/devices"
  19. DUMP_DIR = "dumps"
  20. GPIO_PIN = 4              # wire on GPIO4 (BCM numbering)
  21. PAGE_SIZE = 32            # bytes per scratchpad/page
  22. READ_TIMEOUT = 12
  23. HOLD_SECONDS = 6
  24. # ----------------------------
  25.  
  26. # 1-Wire command opcodes for DS2433 family
  27. CMD_READ_SCRATCHPAD = 0xAA
  28. CMD_WRITE_SCRATCHPAD = 0x0F
  29. CMD_COPY_SCRATCHPAD = 0x55
  30. CMD_SKIP_ROM = 0xCC
  31.  
  32. # Timing helpers (microseconds)
  33. def udelay(us):
  34.     time.sleep(us / 1_000_000.0)
  35.  
  36. # ---------- 1-Wire bit-bang ----------
  37. GPIO.setmode(GPIO.BCM)
  38. GPIO.setup(GPIO_PIN, GPIO.OUT, initial=GPIO.HIGH)
  39.  
  40. def drive_low():
  41.     GPIO.setup(GPIO_PIN, GPIO.OUT)
  42.     GPIO.output(GPIO_PIN, GPIO.LOW)
  43.  
  44. def release_line():
  45.     GPIO.setup(GPIO_PIN, GPIO.IN, pull_up_down=GPIO.PUD_OFF)
  46.  
  47. def read_line():
  48.     GPIO.setup(GPIO_PIN, GPIO.IN)
  49.     return GPIO.input(GPIO_PIN)
  50.  
  51. def reset_pulse():
  52.     """Reset and detect presence pulse."""
  53.     drive_low()
  54.     udelay(500)         # reset low
  55.     release_line()
  56.     udelay(100)         # wait
  57.     present = not read_line()
  58.     udelay(400)         # finish timeslot
  59.     return present
  60.  
  61. def write_bit(bit):
  62.     if bit:
  63.         drive_low()
  64.         udelay(6)
  65.         release_line()
  66.         udelay(64)
  67.     else:
  68.         drive_low()
  69.         udelay(60)
  70.         release_line()
  71.         udelay(10)
  72.  
  73. def read_bit():
  74.     drive_low()
  75.     udelay(6)
  76.     release_line()
  77.     udelay(9)
  78.     val = read_line()
  79.     udelay(55)
  80.     return 1 if val else 0
  81.  
  82. def write_byte(b):
  83.     for i in range(8):
  84.         write_bit((b >> i) & 1)
  85.     udelay(1)
  86.  
  87. def read_byte():
  88.     val = 0
  89.     for i in range(8):
  90.         val |= (read_bit() << i)
  91.     return val
  92.  
  93. def write_bytes(data):
  94.     for b in data:
  95.         write_byte(b)
  96.  
  97. def read_bytes(n):
  98.     return bytes(read_byte() for _ in range(n))
  99.  
  100. # ---------- DS2433 write functions ----------
  101. def write_scratchpad_skiprom(ta1, ta2, page_bytes):
  102.     """Write up to PAGE_SIZE bytes to scratchpad at address TA."""
  103.     if not reset_pulse():
  104.         raise RuntimeError("No device present before write_scratchpad")
  105.     write_byte(CMD_SKIP_ROM)  # single device on line
  106.     write_byte(CMD_WRITE_SCRATCHPAD)
  107.     write_byte(ta1)
  108.     write_byte(ta2)
  109.     write_bytes(page_bytes)
  110.     udelay(1)
  111.  
  112. def read_scratchpad_skiprom(expected_len=PAGE_SIZE):
  113.     """Read scratchpad after write (SKIP ROM)."""
  114.     if not reset_pulse():
  115.         raise RuntimeError("No device present before read_scratchpad")
  116.     write_byte(CMD_SKIP_ROM)
  117.     write_byte(CMD_READ_SCRATCHPAD)
  118.     ta1 = read_byte()
  119.     ta2 = read_byte()
  120.     es = read_byte()
  121.     data = bytes(read_byte() for _ in range(expected_len))
  122.     crc_l = read_byte()
  123.     crc_h = read_byte()
  124.     crc = (crc_h << 8) | crc_l
  125.     return ta1, ta2, es, data, crc
  126.  
  127. def copy_scratchpad_skiprom(ta1, ta2, es, strong_pullup_sec=0.010):
  128.     """Issue COPY SCRATCHPAD (SKIP ROM)."""
  129.     if not reset_pulse():
  130.         raise RuntimeError("No device present before copy_scratchpad")
  131.     write_byte(CMD_SKIP_ROM)
  132.     write_byte(CMD_COPY_SCRATCHPAD)
  133.     write_byte(ta1)
  134.     write_byte(ta2)
  135.     write_byte(es)
  136.     # strong pull-up for t_PROG
  137.     GPIO.setup(GPIO_PIN, GPIO.OUT)
  138.     GPIO.output(GPIO_PIN, GPIO.HIGH)
  139.     time.sleep(strong_pullup_sec)
  140.     release_line()
  141.     time.sleep(0.01)
  142.  
  143. # ---------- Utilities ----------
  144. def sha1_file(path):
  145.     h = hashlib.sha1()
  146.     with open(path,"rb") as f:
  147.         for chunk in iter(lambda: f.read(1<<20), b""):
  148.             h.update(chunk)
  149.     return h.hexdigest()
  150.  
  151. def ensure_dir(d):
  152.     os.makedirs(d, exist_ok=True)
  153.  
  154. def list_devices():
  155.     return sorted([os.path.basename(p) for p in glob.glob(os.path.join(W1_PATH,"23-*"))])
  156.  
  157. def wait_for_device(prompt, ignore_uid=None, timeout=40):
  158.     print(prompt)
  159.     input("Press Enter to start waiting...")
  160.     start = time.time()
  161.     while time.time() - start < timeout:
  162.         ids = list_devices()
  163.         if ignore_uid:
  164.             ids = [x for x in ids if x != ignore_uid]
  165.         if ids:
  166.             print("Found device:", ids[0])
  167.             return ids[0]
  168.         time.sleep(0.5)
  169.     return None
  170.  
  171. def read_eeprom_sysfs(uid, outpath):
  172.     eeprom_path = os.path.join(W1_PATH, uid, "eeprom")
  173.     alt = glob.glob(os.path.join(W1_PATH,"w1_bus_master1",uid,"eeprom"))
  174.     if not os.path.exists(eeprom_path) and alt:
  175.         eeprom_path = alt[0]
  176.     if not os.path.exists(eeprom_path):
  177.         raise FileNotFoundError("EEPROM path not found for "+uid)
  178.     print("Reading sysfs eeprom from", eeprom_path)
  179.     rc = subprocess.call(f"timeout {READ_TIMEOUT} dd if={eeprom_path} bs=1 count=512 of={outpath}", shell=True)
  180.     size = os.path.getsize(outpath) if os.path.exists(outpath) else 0
  181.     if rc != 0 or size != 512:
  182.         raise RuntimeError(f"Failed to read EEPROM via sysfs (bytes={size}, rc={rc})")
  183.     return outpath
  184.  
  185. # ---------- Main workflow ----------
  186. def clone_workflow():
  187.     if os.geteuid() != 0:
  188.         print("Run as root (sudo). Exiting.")
  189.         sys.exit(1)
  190.     ensure_dir(DUMP_DIR)
  191.  
  192.     # --- Read SOURCE chip ---
  193.     print("=== Read SOURCE chip via sysfs ===")
  194.     uid1 = wait_for_device("Connect the SOURCE chip now.")
  195.     if not uid1:
  196.         print("Source not found.")
  197.         return
  198.     out1 = os.path.join(DUMP_DIR, f"{uid1}.bin")
  199.     print(f"Waiting {HOLD_SECONDS}s before read...")
  200.     time.sleep(HOLD_SECONDS)
  201.     read_eeprom_sysfs(uid1, out1)
  202.     print("SHA1 of source:", sha1_file(out1))
  203.  
  204.     # --- Write TARGET chip ---
  205.     print("\nPrepare to write to TARGET chip.")
  206.     print("Unplug source and plug TARGET. Ensure strong pull-up / Vcc.")
  207.     uid2 = wait_for_device("Connect the TARGET chip now.", ignore_uid=uid1, timeout=60)
  208.     if not uid2:
  209.         print("Target not found.")
  210.         return
  211.     print("Found target UID:", uid2)
  212.     input("Press Enter to start writing...")
  213.  
  214.     # delay to settle
  215.     print("Waiting 1s for device to settle...")
  216.     time.sleep(1)
  217.  
  218.     # read dump to write
  219.     with open(out1,"rb") as f:
  220.         full = f.read()
  221.     if len(full)!=512:
  222.         print("Source dump size not 512 bytes, abort.")
  223.         return
  224.  
  225.     # write page by page
  226.     for page in range(0,512,PAGE_SIZE):
  227.         page_data = full[page:page+PAGE_SIZE]
  228.         ta1 = page & 0xFF
  229.         ta2 = (page >> 8) & 0xFF
  230.         print(f"Writing page at offset {page} (TA1={ta1:02X}, TA2={ta2:02X}) ...")
  231.         write_scratchpad_skiprom(ta1, ta2, page_data)
  232.         ta1_r, ta2_r, es, data_read, crc = read_scratchpad_skiprom(PAGE_SIZE)
  233.         if data_read[:len(page_data)] != page_data:
  234.             raise RuntimeError(f"Scratchpad verify failed at page {page}")
  235.         copy_scratchpad_skiprom(ta1_r, ta2_r, es, strong_pullup_sec=0.010)
  236.         print(f"Page {page} written.")
  237.  
  238.     # verify via sysfs
  239.     tmp_verify = os.path.join(DUMP_DIR,f"{uid2}.after.bin")
  240.     try:
  241.         read_eeprom_sysfs(uid2, tmp_verify)
  242.         print("SHA1 source:", sha1_file(out1))
  243.         print("SHA1 target:", sha1_file(tmp_verify))
  244.     except Exception as e:
  245.         print("Could not read back via sysfs:", e)
  246.         print("Verify with external programmer if needed.")
  247.  
  248.     print("Done.")
  249.  
  250. if __name__=="__main__":
  251.     try:
  252.         clone_workflow()
  253.     finally:
  254.         GPIO.cleanup()
  255.  
Advertisement
Add Comment
Please, Sign In to add comment