Module: Prremote::Commands::SerialHelpers

Included in:
Deploy, Ls, Run
Defined in:
lib/prremote/commands/serial_helpers.rb

Constant Summary collapse

SERIAL_CHUNK_SIZE =

ESP32-C6's native USB Serial/JTAG drops bytes when a large payload is written in a single burst: its 256-byte driver RX ring buffer overflows faster than the firmware drains it byte-by-byte, and the controller does not apply USB backpressure. Writing in <=256-byte chunks with a short gap keeps the device from overrunning. The pacing is negligible on the baud-limited transports (Pico USB-CDC / ESP32 UART bridge), where a 256-byte chunk already takes ~22 ms to clock out at 115200 baud.

256
SERIAL_CHUNK_DELAY =
0.004

Instance Method Summary collapse

Instance Method Details

#normalize(str) ⇒ Object



33
34
35
# File 'lib/prremote/commands/serial_helpers.rb', line 33

def normalize(str)
  str.gsub("\r\n", "\n").gsub("\r", '')
end

#safe_read(serial, size) ⇒ Object

Wraps serial.read so that a device disconnect (e.g. ENXIO on macOS when the Pico resets) surfaces as a human-readable error instead of a bare errno name.



58
59
60
61
62
# File 'lib/prremote/commands/serial_helpers.rb', line 58

def safe_read(serial, size)
  serial.read(size) || ''
rescue RubySerial::Error => e
  raise "Device disconnected (#{e.message}). Run `prremote reset` if the device is unresponsive."
end

#wait_for_ready(serial) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/prremote/commands/serial_helpers.rb', line 4

def wait_for_ready(serial)
  # On macOS, USB CDC TX buffers can be dropped when the host reopens the port,
  # leaving the device stuck in getchar() without re-sending READY.
  # Sending Ctrl+C forces the device to restart its READY loop.
  # Resent every second: ESP32 boards reset when the port opens (DTR/RTS
  # auto-reset circuit) and a Ctrl+C sent while they are still booting
  # is lost. Harmless on Pico — 0x03 while idle just re-prints READY.
  sleep 0.1
  serial.write("\x03") rescue nil

  buf = +''
  deadline  = Time.now + 10
  next_ctrl = Time.now + 1
  loop do
    buf << normalize(safe_read(serial, 256))
    if buf.include?('READY ')
      warn_if_runtime_outdated(buf)
      return
    end
    raise 'Timeout waiting for device. Run `prremote reset` if a script is running.' if Time.now > deadline

    if Time.now > next_ctrl
      serial.write("\x03") rescue nil
      next_ctrl = Time.now + 1
    end
    sleep 0.05
  end
end

#write_chunked(serial, data) ⇒ Object



47
48
49
50
51
52
53
54
# File 'lib/prremote/commands/serial_helpers.rb', line 47

def write_chunked(serial, data)
  i = 0
  while i < data.bytesize
    serial.write(data.byteslice(i, SERIAL_CHUNK_SIZE))
    i += SERIAL_CHUNK_SIZE
    sleep SERIAL_CHUNK_DELAY if i < data.bytesize
  end
end