summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrett Curran <brettjcurran@gmail.com>2026-05-29 17:17:48 +1000
committerBrett Curran <brettjcurran@gmail.com>2026-05-29 17:17:48 +1000
commitfd3d3911734b4de3158521a589d511128a9e3810 (patch)
tree5e87d630196b308ec1ee15b752f78c6b3a2e7ced
init
-rw-r--r--README.md87
-rw-r--r--pyproject.toml45
-rw-r--r--src/xwing_sensor/__init__.py0
-rw-r--r--src/xwing_sensor/main.py68
-rw-r--r--xwing-sensor.service15
5 files changed, 215 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e088361
--- /dev/null
+++ b/README.md
@@ -0,0 +1,87 @@
+# xwing-sensor
+
+Reads temperature and humidity from an SI7021 sensor over I2C and displays the readings on a Waveshare 2.13 inch e-Paper HAT, running on a Raspberry Pi Zero 2 W.
+
+## Hardware
+
+- Raspberry Pi Zero 2 W
+- SI7021 temperature/humidity sensor (I2C, address `0x40`, bus 1)
+- Waveshare 2.13 inch e-Paper HAT (250×122 px, SPI)
+
+Waveshare HAT documentation and wiring guide:
+https://www.waveshare.com/wiki/2.13inch_e-Paper_HAT_Manual#Overview
+
+## System dependencies
+
+Before running `uv sync`, install the required system libraries.
+
+**Pillow build dependencies:**
+
+```bash
+sudo apt-get update && sudo apt-get install -y \
+ build-essential \
+ python3-dev \
+ libjpeg62-turbo-dev \
+ zlib1g-dev \
+ libfreetype-dev \
+ libopenjp2-7-dev
+```
+
+**lg GPIO library:**
+
+The `lgpio` Python package requires the `lg` C library. Follow the install instructions at https://github.com/joan2937/lg, which requires `swig`:
+
+```bash
+sudo apt-get install -y swig
+```
+
+Then build and install `lg` per the instructions in that repository.
+
+## Waveshare EPD library
+
+The `waveshare_epd` library is **not bundled** in this repo. Download it from Waveshare's GitHub:
+
+```bash
+git clone https://github.com/waveshare/e-Paper
+```
+
+The `lib/` directory (`e-Paper/RaspberryPi_JetsonNano/python/lib`) must be on `PYTHONPATH` at runtime. When running as a systemd service this is handled via the `Environment=` line in the unit file (see below). For manual runs:
+
+```bash
+PYTHONPATH=/home/pi/e-Paper/RaspberryPi_JetsonNano/python/lib uv run xwing-sensor
+```
+
+The import in `main.py` uses `epd2in13_V4` — adjust to match your HAT version if needed.
+
+## Setup
+
+```bash
+uv sync
+```
+
+## Running as a systemd service
+
+Copy the unit file to systemd and enable it:
+
+```bash
+sudo cp xwing-sensor.service /etc/systemd/system/
+sudo systemctl daemon-reload
+sudo systemctl enable xwing-sensor
+sudo systemctl start xwing-sensor
+```
+
+Check status or logs:
+
+```bash
+sudo systemctl status xwing-sensor
+journalctl -u xwing-sensor -f
+```
+
+Before enabling, verify the paths in `xwing-sensor.service` match your environment:
+
+| Field | Default value | What to check |
+|---|---|---|
+| `User` | `pi` | The user that owns the project directory |
+| `WorkingDirectory` | `/home/pi/xwing_sensor` | Absolute path to this repo on the Pi |
+| `PYTHONPATH` | `/home/pi/e-Paper/…/lib` | Where you cloned the Waveshare repo |
+| `ExecStart` | `/home/pi/xwing_sensor/.venv/bin/xwing-sensor` | Created by `uv sync` |
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..4255c1d
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,45 @@
+[project]
+name = "xwing-sensor"
+version = "0.3.0"
+description = "SI7021 temperature/humidity sensor display for Waveshare 2.13in e-Paper HAT"
+requires-python = ">=3.13"
+dependencies = [
+ "smbus2",
+ "Pillow",
+ "RPi.GPIO",
+ "spidev",
+ "gpiozero",
+ "lgpio",
+]
+
+[project.scripts]
+xwing-sensor = "xwing_sensor.main:main"
+
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[tool.hatch.build.targets.wheel]
+packages = ["src/xwing_sensor"]
+
+[tool.bumpversion]
+current_version = "0.3.0"
+parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
+serialize = ["{major}.{minor}.{patch}"]
+search = "{current_version}"
+replace = "{new_version}"
+regex = false
+ignore_missing_version = false
+ignore_missing_files = false
+tag = true
+sign_tags = false
+tag_name = "v{new_version}"
+tag_message = "Bump version: {current_version} → {new_version}"
+allow_dirty = false
+commit = true
+message = "Bump version: {current_version} → {new_version}"
+moveable_tags = []
+commit_args = ""
+setup_hooks = []
+pre_commit_hooks = []
+post_commit_hooks = []
diff --git a/src/xwing_sensor/__init__.py b/src/xwing_sensor/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/xwing_sensor/__init__.py
diff --git a/src/xwing_sensor/main.py b/src/xwing_sensor/main.py
new file mode 100644
index 0000000..41402e6
--- /dev/null
+++ b/src/xwing_sensor/main.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+"""
+Reads temperature and humidity from an SI7021 sensor (I2C) and renders
+the values to a Waveshare 2.13 inch e-Paper HAT.
+
+The Waveshare EPD library (waveshare_epd) is not bundled in this repo.
+See README.md for setup instructions.
+"""
+import time
+
+from smbus2 import SMBus
+from PIL import Image, ImageDraw, ImageFont
+from waveshare_epd import epd2in13_V4 # adjust module name for your HAT version
+
+I2C_BUS = 1
+I2C_ADDR = 0x40
+POLL_INTERVAL = 120 # seconds between display refreshes
+
+
+def read_si7021(bus: SMBus) -> tuple[float, float]:
+ """Return (temp_c, humidity_pct) from the SI7021 sensor."""
+ rh_raw = bus.read_i2c_block_data(I2C_ADDR, 0xE5, 2) # measure RH, hold master
+ time.sleep(0.1)
+ humidity = ((rh_raw[0] * 256 + rh_raw[1]) * 125 / 65536.0) - 6
+
+ temp_raw = bus.read_i2c_block_data(I2C_ADDR, 0xE3, 2) # measure temp, hold master
+ time.sleep(0.1)
+ temp_c = ((temp_raw[0] * 256 + temp_raw[1]) * 175.72 / 65536.0) - 46.85
+
+ return temp_c, humidity
+
+
+def render(epd: epd2in13_V4.EPD, temp_c: float, humidity: float) -> None:
+ """Draw sensor readings on the e-paper display.
+
+ The 2.13in HAT is 122×250 px (portrait). We create a landscape image
+ (250×122) by swapping width/height, which matches the typical Waveshare
+ example convention of passing (epd.height, epd.width) to Image.new.
+ """
+ image = Image.new("1", (epd.height, epd.width), 255) # 255 = white background
+ draw = ImageDraw.Draw(image)
+
+ font_large = ImageFont.load_default(size=40)
+ font_small = ImageFont.load_default(size=22)
+
+ draw.text((10, 8), f"{temp_c:.1f} \u00b0C", font=font_large, fill=0)
+ draw.text((10, 62), f"{humidity:.1f} % RH", font=font_small, fill=0)
+
+ epd.display(epd.getbuffer(image))
+
+
+def main() -> None:
+ epd = epd2in13_V4.EPD()
+ epd.init()
+ epd.Clear()
+
+ bus = SMBus(I2C_BUS)
+ try:
+ while True:
+ temp_c, humidity = read_si7021(bus)
+ render(epd, temp_c, humidity)
+ time.sleep(POLL_INTERVAL)
+ except KeyboardInterrupt:
+ epd.sleep()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/xwing-sensor.service b/xwing-sensor.service
new file mode 100644
index 0000000..f8e3095
--- /dev/null
+++ b/xwing-sensor.service
@@ -0,0 +1,15 @@
+[Unit]
+Description=xwing-sensor: SI7021 temperature/humidity display on e-Paper HAT
+After=multi-user.target
+
+[Service]
+Type=simple
+User=pi
+WorkingDirectory=/home/pi/xwing_sensor
+Environment=PYTHONPATH=/home/pi/e-Paper/RaspberryPi_JetsonNano/python/lib
+ExecStart=/home/pi/xwing_sensor/.venv/bin/xwing-sensor
+Restart=on-failure
+RestartSec=10
+
+[Install]
+WantedBy=multi-user.target