const sys = @import("sys"); const errors = @import("error"); // ───────────────────────────────────────────────────────────────────────────── // Re-export ESP-IDF types so callers only need to @import("uart") // ───────────────────────────────────────────────────────────────────────────── pub const Port = sys.uart_port_t; pub const WordLength = sys.uart_word_length_t; pub const StopBits = sys.uart_stop_bits_t; pub const Parity = sys.uart_parity_t; pub const HWFlowCtrl = sys.uart_hw_flowcontrol_t; pub const Mode = sys.uart_mode_t; pub const Sclk = sys.uart_sclk_t; pub const Config = sys.uart_config_t; pub const IntrConfig = sys.uart_intr_config_t; pub const QueueHandle = sys.QueueHandle_t; pub const TickType = sys.TickType_t; // ───────────────────────────────────────────────────────────────────────────── // Pin assignment sentinel — mirrors UART_PIN_NO_CHANGE from esp-idf // ───────────────────────────────────────────────────────────────────────────── pub const pin_no_change: c_int = -1; // ───────────────────────────────────────────────────────────────────────────── // Driver lifecycle // ───────────────────────────────────────────────────────────────────────────── pub const DriverConfig = struct { rx_buffer_size: usize, tx_buffer_size: usize = 0, /// Pass null to disable event queue. queue_size: c_int = 0, uart_queue: ?*QueueHandle = null, intr_alloc_flags: c_int = 0, }; pub fn driverInstall(port: Port, cfg: DriverConfig) !void { return errors.espCheckError(sys.uart_driver_install( port, @intCast(cfg.rx_buffer_size), @intCast(cfg.tx_buffer_size), cfg.queue_size, cfg.uart_queue orelse null, cfg.intr_alloc_flags, )); } pub fn driverDelete(port: Port) !void { return errors.espCheckError(sys.uart_driver_delete(port)); } pub fn isDriverInstalled(port: Port) bool { return sys.uart_is_driver_installed(port); } // ───────────────────────────────────────────────────────────────────────────── // Parameter configuration (word length, stop bits, parity, baud rate …) // ───────────────────────────────────────────────────────────────────────────── pub fn paramConfig(port: Port, cfg: *const Config) !void { return errors.espCheckError(sys.uart_param_config(port, cfg)); } pub fn setWordLength(port: Port, data_bit: WordLength) !void { return errors.espCheckError(sys.uart_set_word_length(port, data_bit)); } pub fn getWordLength(port: Port) !WordLength { var v: WordLength = undefined; try errors.espCheckError(sys.uart_get_word_length(port, &v)); return v; } pub fn setStopBits(port: Port, stop_bits: StopBits) !void { return errors.espCheckError(sys.uart_set_stop_bits(port, stop_bits)); } pub fn getStopBits(port: Port) !StopBits { var v: StopBits = undefined; try errors.espCheckError(sys.uart_get_stop_bits(port, &v)); return v; } pub fn setParity(port: Port, parity: Parity) !void { return errors.espCheckError(sys.uart_set_parity(port, parity)); } pub fn getParity(port: Port) !Parity { var v: Parity = undefined; try errors.espCheckError(sys.uart_get_parity(port, &v)); return v; } pub fn setBaudrate(port: Port, baudrate: u32) !void { return errors.espCheckError(sys.uart_set_baudrate(port, baudrate)); } pub fn getBaudrate(port: Port) !u32 { var v: u32 = 0; try errors.espCheckError(sys.uart_get_baudrate(port, &v)); return v; } pub fn getSclkFreq(sclk: Sclk) !u32 { var v: u32 = 0; try errors.espCheckError(sys.uart_get_sclk_freq(sclk, &v)); return v; } pub fn setLineInverse(port: Port, inverse_mask: u32) !void { return errors.espCheckError(sys.uart_set_line_inverse(port, inverse_mask)); } pub fn setMode(port: Port, mode: Mode) !void { return errors.espCheckError(sys.uart_set_mode(port, mode)); } pub fn setLoopBack(port: Port, enable: bool) !void { return errors.espCheckError(sys.uart_set_loop_back(port, enable)); } pub fn setAlwaysRxTimeout(port: Port, enable: bool) void { sys.uart_set_always_rx_timeout(port, enable); } // ───────────────────────────────────────────────────────────────────────────── // Pin mapping // ───────────────────────────────────────────────────────────────────────────── pub const PinConfig = struct { tx: c_int = pin_no_change, rx: c_int = pin_no_change, rts: c_int = pin_no_change, cts: c_int = pin_no_change, }; pub fn setPin(port: Port, pins: PinConfig) !void { return errors.espCheckError(sys.uart_set_pin(port, pins.tx, pins.rx, pins.rts, pins.cts)); } /// Drive RTS line manually (level: true = high, false = low). pub fn setRTS(port: Port, level: bool) !void { return errors.espCheckError(sys.uart_set_rts(port, @intFromBool(level))); } /// Drive DTR line manually. pub fn setDTR(port: Port, level: bool) !void { return errors.espCheckError(sys.uart_set_dtr(port, @intFromBool(level))); } pub fn setTXIdleNum(port: Port, idle_num: u16) !void { return errors.espCheckError(sys.uart_set_tx_idle_num(port, idle_num)); } // ───────────────────────────────────────────────────────────────────────────── // Flow control // ───────────────────────────────────────────────────────────────────────────── pub fn setHWFlowCtrl(port: Port, flow_ctrl: HWFlowCtrl, rx_thresh: u8) !void { return errors.espCheckError(sys.uart_set_hw_flow_ctrl(port, flow_ctrl, rx_thresh)); } pub fn getHWFlowCtrl(port: Port) !HWFlowCtrl { var v: HWFlowCtrl = undefined; try errors.espCheckError(sys.uart_get_hw_flow_ctrl(port, &v)); return v; } pub fn setSWFlowCtrl(port: Port, enable: bool, rx_thresh_xon: u8, rx_thresh_xoff: u8) !void { return errors.espCheckError(sys.uart_set_sw_flow_ctrl(port, enable, rx_thresh_xon, rx_thresh_xoff)); } // ───────────────────────────────────────────────────────────────────────────── // Interrupt configuration // ───────────────────────────────────────────────────────────────────────────── pub fn intrConfig(port: Port, cfg: *const IntrConfig) !void { return errors.espCheckError(sys.uart_intr_config(port, cfg)); } pub fn clearIntrStatus(port: Port, clr_mask: u32) !void { return errors.espCheckError(sys.uart_clear_intr_status(port, clr_mask)); } pub fn enableIntrMask(port: Port, mask: u32) !void { return errors.espCheckError(sys.uart_enable_intr_mask(port, mask)); } pub fn disableIntrMask(port: Port, mask: u32) !void { return errors.espCheckError(sys.uart_disable_intr_mask(port, mask)); } pub fn enableRXIntr(port: Port) !void { return errors.espCheckError(sys.uart_enable_rx_intr(port)); } pub fn disableRXIntr(port: Port) !void { return errors.espCheckError(sys.uart_disable_rx_intr(port)); } pub fn enableTXIntr(port: Port, enable: bool, thresh: c_int) !void { return errors.espCheckError(sys.uart_enable_tx_intr(port, @intFromBool(enable), thresh)); } pub fn disableTXIntr(port: Port) !void { return errors.espCheckError(sys.uart_disable_tx_intr(port)); } // ───────────────────────────────────────────────────────────────────────────── // RX / TX thresholds and timeouts // ───────────────────────────────────────────────────────────────────────────── pub fn setRXFullThreshold(port: Port, threshold: c_int) !void { return errors.espCheckError(sys.uart_set_rx_full_threshold(port, threshold)); } pub fn setTXEmptyThreshold(port: Port, threshold: c_int) !void { return errors.espCheckError(sys.uart_set_tx_empty_threshold(port, threshold)); } pub fn setRXTimeout(port: Port, tout_thresh: u8) !void { return errors.espCheckError(sys.uart_set_rx_timeout(port, tout_thresh)); } // ───────────────────────────────────────────────────────────────────────────── // Write // ───────────────────────────────────────────────────────────────────────────── /// Transmit raw bytes directly (no ring buffer). /// Returns the number of bytes pushed into the TX FIFO. pub fn txChars(port: Port, data: []const u8) usize { const rc = sys.uart_tx_chars(port, data.ptr, @intCast(data.len)); return if (rc < 0) 0 else @intCast(rc); } /// Write bytes through the TX ring buffer (if installed) or directly. /// Returns number of bytes written, or error on failure. pub fn writeBytes(port: Port, data: []const u8) !usize { const rc = sys.uart_write_bytes(port, data.ptr, data.len); if (rc < 0) return error.UartWriteFailed; return @intCast(rc); } /// Like `writeBytes` but appends a serial break signal of `brk_len` bit-periods. pub fn writeBytesWithBreak(port: Port, data: []const u8, brk_len: c_int) !usize { const rc = sys.uart_write_bytes_with_break(port, data.ptr, data.len, brk_len); if (rc < 0) return error.UartWriteFailed; return @intCast(rc); } pub fn waitTXDone(port: Port, ticks_to_wait: TickType) !void { return errors.espCheckError(sys.uart_wait_tx_done(port, ticks_to_wait)); } pub fn waitTXIdlePolling(port: Port) !void { return errors.espCheckError(sys.uart_wait_tx_idle_polling(port)); } // ───────────────────────────────────────────────────────────────────────────── // Read // ───────────────────────────────────────────────────────────────────────────── /// Read up to `buf.len` bytes from the RX ring buffer. /// Returns the number of bytes actually read, or error on failure. pub fn readBytes(port: Port, buf: []u8, ticks_to_wait: TickType) !usize { const rc = sys.uart_read_bytes(port, buf.ptr, @intCast(buf.len), ticks_to_wait); if (rc < 0) return error.UartReadFailed; return @intCast(rc); } /// Returns the number of bytes waiting in the RX ring buffer. pub fn getBufferedDataLen(port: Port) !usize { var v: usize = 0; try errors.espCheckError(sys.uart_get_buffered_data_len(port, &v)); return v; } /// Returns free space in the TX ring buffer (bytes). pub fn getTXBufferFreeSize(port: Port) !usize { var v: usize = 0; try errors.espCheckError(sys.uart_get_tx_buffer_free_size(port, &v)); return v; } // ───────────────────────────────────────────────────────────────────────────── // Flush // ───────────────────────────────────────────────────────────────────────────── /// Flush TX FIFO — waits until all bytes are sent. pub fn flush(port: Port) !void { return errors.espCheckError(sys.uart_flush(port)); } /// Flush (discard) RX ring buffer contents. pub fn flushInput(port: Port) !void { return errors.espCheckError(sys.uart_flush_input(port)); } // ───────────────────────────────────────────────────────────────────────────── // Pattern detection // ───────────────────────────────────────────────────────────────────────────── pub const PatternConfig = struct { /// Character to detect (e.g. '+' for AT commands). chr: u8, /// Number of consecutive pattern characters required. chr_num: u8, /// Maximum gap between characters (in baud ticks). chr_tout: c_int, /// Idle time after pattern (baud ticks). post_idle: c_int, /// Idle time before pattern (baud ticks). pre_idle: c_int, }; pub fn enablePatternDetBaudIntr(port: Port, cfg: PatternConfig) !void { return errors.espCheckError(sys.uart_enable_pattern_det_baud_intr( port, cfg.chr, cfg.chr_num, cfg.chr_tout, cfg.post_idle, cfg.pre_idle, )); } pub fn disablePatternDetIntr(port: Port) !void { return errors.espCheckError(sys.uart_disable_pattern_det_intr(port)); } /// Pop the oldest pattern position from the queue. /// Returns `null` if the queue is empty or an error occurred. pub fn patternPopPos(port: Port) ?usize { const rc = sys.uart_pattern_pop_pos(port); return if (rc < 0) null else @intCast(rc); } /// Peek at the oldest pattern position without removing it. pub fn patternGetPos(port: Port) ?usize { const rc = sys.uart_pattern_get_pos(port); return if (rc < 0) null else @intCast(rc); } pub fn patternQueueReset(port: Port, queue_length: c_int) !void { return errors.espCheckError(sys.uart_pattern_queue_reset(port, queue_length)); } // ───────────────────────────────────────────────────────────────────────────── // RS-485 collision detection // ───────────────────────────────────────────────────────────────────────────── pub fn getCollisionFlag(port: Port) !bool { var v: bool = false; try errors.espCheckError(sys.uart_get_collision_flag(port, &v)); return v; } // ───────────────────────────────────────────────────────────────────────────── // Light-sleep wakeup // ───────────────────────────────────────────────────────────────────────────── pub fn setWakeupThreshold(port: Port, threshold: c_int) !void { return errors.espCheckError(sys.uart_set_wakeup_threshold(port, threshold)); } pub fn getWakeupThreshold(port: Port) !c_int { var v: c_int = 0; try errors.espCheckError(sys.uart_get_wakeup_threshold(port, &v)); return v; }