# -*- coding: utf-8 -*- """FilmForge PID anti-windup verification: target 50C, track steady-state.""" import serial import time import threading def sum8(seq): return sum(seq) & 0xFF def frame(cmd): p = cmd.encode() body = bytes([0xD5, 0x01, len(p)]) + p return body + bytes([sum8(body[1:])]) rx = bytearray() stop = threading.Event() def reader(buf, handle): while not stop.is_set(): data = handle.read(256) if data: buf.extend(data) handle.close() def frames(buf): out, i, b = [], 0, bytes(buf) while i < len(b): if b[i] == 0xB5 and i + 3 < len(b): total = 3 + b[i + 2] + 1 if i + total <= len(b): out.append(b[i:i + total]); i += total; continue i += 1 return out s5 = serial.Serial('COM5', 115200, timeout=0.5) t = threading.Thread(target=reader, args=(rx, s5)) t.start() time.sleep(0.5) def cmd(c, wait=0.8): rx.clear() s5.write(frame(c)) time.sleep(wait) fs = frames(rx) payload = [f[3:3+f[2]].decode('ascii','replace') for f in fs if len(f) >= 4 and f[1] == 0xF0] print('[%s] resp: %s' % (c, payload if payload else '')) cmd('M4S50') cmd('M3S1') cmd('M2S500') print('--- heating to 50C, 150s sampling ---') t0 = time.time() last_print = -10 peak = 0 while time.time() - t0 < 150: time.sleep(2.0) fs = frames(rx) vals = [] for f in fs: if len(f) >= 4 and f[1] == 0xF0 and f[2] >= 2: try: vals.append(float(f[3:3+f[2]])) except ValueError: pass if vals: peak = max(peak, max(vals)) dt = time.time() - t0 if dt >= last_print + 10 and vals: print(' t+%03ds %.1f C (peak %.1f)' % (dt, vals[-1], peak)) last_print = dt - (dt % 10) cmd('M3S0', wait=1.0) cmd('M2S0') stop.set(); t.join() print('PEAK=%0.1f (target 50, overshoot %+.1f)' % (peak, peak - 50)) print('DONE')