feat: add LCD1602 16x2 I2C character display support#239
feat: add LCD1602 16x2 I2C character display support#239dbzx6r wants to merge 13 commits intoPierreGode:mainfrom
Conversation
- Add resources/waveshare_epd/lcd1602.py: full HD44780/PCF8574 I2C driver using smbus2 (already a dependency). Auto-detects address 0x27/0x3F. - Update shared.py: register lcd1602 in DISPLAY_PROFILES, SIZE_KEY_TO_DEFAULT_DRIVER, and get_default_config() - Update display.py: add _run_lcd1602() render loop showing live status and H/C/V counters; dispatch from run() - Update web/index_modern.html: add LCD1602 option to display dropdown - Update web/scripts/ragnar_modern.js: add lcd1602 to epdTypeToSizeKey(), displaySelectOptions, Display config section, alwaysShowKeys, fallbackValues, lcd1602_i2c_address input row, and syncDisplayRows() Wiring (I2C PCF8574 backpack): VCC -> Pi Pin 2 (5V), GND -> Pin 6, SDA -> Pin 3 (GPIO2), SCL -> Pin 5 (GPIO3) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Complete redesign of _run_lcd1602() for the 16x2 character display: Page 0 (4s) - Status + Network: Line 0: orchestrator status (e.g. 'SCANNING ') Line 1: WiFi SSID via iwgetid (e.g. '>Tango Down ') Page 1 (4s) - Counts + IP: Line 0: T:<targets> V:<vulns> C:<creds> Line 1: IP address (e.g. '192.168.1.100 ') Improvements: - Live WiFi SSID using iwgetid (consistent with ssd1306) - Page rotation every 4s; 0.5s poll tick - Reset _initialized=False on I2C error for auto-recovery - Force both lines rewrite on page switch (no stale content) lcd1602.py QA fixes: - write_line() sanitizes non-ASCII chars (SSID/unicode safety) - sleep() now resets _initialized so next init() re-runs HD44780 sequence Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
initialize_epd_display() was trying to call getbuffer() on the LCD1602
driver, which has no such method (character displays have no PIL buffer
interface). This caused the fallback auto-detect to run, find the
connected e-paper, and overwrite epd_type back to 'epd2in13_V4' --
so _run_lcd1602() was never dispatched.
Add an early-return path for _CHAR_DISPLAYS (currently {'lcd1602'})
that sets width/height from DISPLAY_PROFILES and skips all
EPDHelper / PIL buffer initialisation entirely.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The custom logger in this codebase only accepts 2 positional args; positional %-format strings raise TypeError. Switch to f-string. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- display.py: guard init_partial_update() with 'if epd_helper is not None' so character displays (lcd1602) don't crash the display thread - shared.py: call apply_display_profile() in the char-display early-return path so config ref_width/ref_height stay in sync (used for scale_factor) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- display.py: convert all logger.error('%s', exc) calls in _run_lcd1602
to f-strings so custom Logger doesn't crash the display thread
- lcd1602.py: increase EN pulse timing from 0.1ms to 0.5ms to match
known-working timing from direct I2C test
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Top row rotates every 15s: SSID -> IP address Bottom row rotates every 5s: Targets:# -> Vulnerabilities:# -> Credentials:# Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- 'Vulnerabilities:#' -> 'Vuln: #' (fits 16 chars cleanly) - IP now fetched via 'hostname -I' subprocess instead of non-existent shared_data.ipaddress attribute Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Driver verification with smbus2 install + I2C enable - Added as option 3 in TFT/OLED menu (short form) - Added as option 11 in full display menu Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a third slot to the LCD1602 top-row rotation cycle: Slot 0 (15s) - WiFi SSID Slot 1 (15s) - IP address Slot 2 (15s) - Ragnar current status (ragnarstatustext) Status examples: IDLE, NetworkScanner, NmapVulnScanner, LynisPentest, etc. Falls back to 'IDLE' if unset. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update: Ragnar status added to top-row rotationJust pushed an additional commit to this branch adding the current Ragnar operation status to the LCD1602 top-row display rotation. New top-row cycle (15 s each):
The status slot reads \shared_data.ragnarstatustext\ which is set by the orchestrator and action modules (e.g. \IDLE, \NetworkScanner, \NmapVulnScanner, \LynisPentest). Falls back to \IDLE\ if unset. This gives users at-a-glance visibility into what Ragnar is actively doing without needing to open the web UI. |
Generate a screen.png simulating the physical LCD1602 whenever the displayed content changes. The web GUI e-paper tab now shows a green-on-dark monospace image matching the current two lines on the hardware display instead of a blank/stale e-ink image. _render_lcd_preview() uses ImageFont (monospace, falls back to PIL default), sizes the canvas dynamically from glyph metrics, and writes to shared_data.webdir/screen.png — the same path the /api/epaper-display route already serves. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update: Web GUI display tab now shows live LCD previewPushed another commit — the e-paper display tab in the web UI now shows a real-time visual preview of what's on the LCD1602 hardware instead of a blank/stale image. How it works:
The preview updates in sync with the physical display rotation (every 5–15s depending on which slot changes). |
Split the main loop into two independent try blocks: 1. Content/preview block — computes lines, renders screen.png; never blocked by I2C errors 2. Hardware block — writes to the physical LCD; I2C failures only reset _initialized, not the preview state Previously an I2C exception aborted the entire tick before _render_lcd_preview() was ever called, leaving screen.png stale. Also adds _hw_line0/_hw_line1 tracking so hardware writes only happen on actual changes even after a reinit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@dbzx6r please resolve comficts 😊🙌 amazing work man. |
Merged upstream/main (MAX7219, display-rename, ap-archive, fix-view-full-report,
pager-optimize) into pr/lcd1602-upstream while preserving all LCD1602 additions.
Conflict resolution strategy — kept BOTH display type additions:
display.py
- Kept ImageFont import (required by _render_lcd_preview)
- Added upstream comment about non-EPD displays in __init__
- Kept _run_lcd1602() + _run_max7219() routing side by side
shared.py
- Kept lcd1602 in DISPLAY_PROFILES and SIZE_KEY_TO_DEFAULT_DRIVER
- Kept lcd1602_i2c_address/lcd1602_i2c_bus config defaults
- Unified _CHAR_DISPLAYS skip block into _BYPASS_DISPLAYS covering
lcd1602, max7219_4panel, and max7219_8panel
- Kept max7219_* DISPLAY_PROFILES entries and config defaults
install_ragnar.sh
- Kept LCD1602 as option 11 in full display menu
- Added MAX7219 8-panel (12) and 4-panel (13) from upstream
- Renumbered headless to 14, updated range prompt to 1-14
web/scripts/ragnar_modern.js
- Kept lcd1602_i2c_address config metadata and form input
- Kept lcd1602 in epdTypeToSizeKey() and displaySelectOptions
- Added max7219_* config metadata, form inputs, and syncDisplayRows
visibility toggling from upstream
- Merged Display section keys and alwaysShowKeys to include both
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Summary
Adds full support for the HD44780 LCD1602 16×2 character LCD with I2C PCF8574 backpack as a selectable display type in Ragnar.
New driver — \
esources/waveshare_epd/lcd1602.py\
Display loop — \display.py → _run_lcd1602()\
Routed from
un()\ when \�pd_type == 'lcd1602'\
Two independent rotation timers:
Handles I2C errors gracefully — forces full re-init on next tick, never crashes the service.
Install script — \install_ragnar.sh\
aspi-config\
Shared config — \shared.py\