Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Commit messages follow the [Conventional Commits](https://www.conventionalcommit

**Scopes** (optional but enforced): if provided, the scope **must** be one of the allowed values. The scope is recommended for driver-specific changes but can be omitted for cross-cutting changes.

- Driver scopes: `apds9960`, `bme280`, `bq27441`, `daplink_bridge`, `daplink_flash`, `gc9a01`, `hts221`, `im34dt05`, `ism330dl`, `lis2mdl`, `mcp23009e`, `ssd1327`, `steami_config`, `vl53l1x`, `wsen-hids`, `wsen-pads`
- Driver scopes: `apds9960`, `bme280`, `bq27441`, `daplink_bridge`, `daplink_flash`, `gc9a01`, `hts221`, `im34dt05`, `ism330dl`, `lis2mdl`, `mcp23009e`, `ssd1327`, `steami_config`, `vl53l1x`, `wsen-hids`, `wsen-pads`, `steami_screen`
- Domain scopes: `ci`, `docs`, `style`, `tests`, `tooling`

### Examples
Expand Down
1 change: 1 addition & 0 deletions commitlint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ module.exports = {
'style',
'tests',
'tooling',
'steami_screen'
],
],
'type-enum': [
Expand Down
220 changes: 220 additions & 0 deletions lib/steami_screen/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
# STeaMi Screen

High-level UI library for STeaMi displays.

Provides a device-agnostic abstraction layer on top of display drivers (SSD1327, GC9A01) with a simple API to draw UI elements: text layouts, widgets, menus, icons.

---

## Features

* Display abstraction (works with any FrameBuffer-based backend)
* Automatic layout for round screens (cardinal positioning, safe margins)
* Text rendering with alignment and scaling
* Drawing primitives (pixel, line, rect, circle)
* 10 widgets: title, subtitle, value, bar, gauge, graph, menu, compass, watch, face

---

## Basic Usage

```python
import ssd1327
from machine import SPI, Pin
from steami_screen import Screen

spi = SPI(1)
dc = Pin("DATA_COMMAND_DISPLAY")
res = Pin("RST_DISPLAY")
cs = Pin("CS_DISPLAY")

display = ssd1327.WS_OLED_128X128_SPI(spi, dc, res, cs)
screen = Screen(display)

screen.clear()
screen.title("STeaMi")
screen.value(42, label="Temp", unit="C")
screen.show()
```

---

## API Reference

### Initialization

```python
screen = Screen(display)
```

`display` must expose `fill()`, `pixel()`, `line()`, `rect()`, `fill_rect()`, `text()`, `show()`. Width and height are auto-detected from the display backend.

---

### Drawing Primitives

```python
screen.pixel(x, y, color)
screen.line(x1, y1, x2, y2, color)
screen.rect(x, y, w, h, color, fill=False)
screen.circle(x, y, r, color, fill=False)
```

---

### Text

```python
screen.text("Hello", at="CENTER")
screen.text("Top", at="N")
screen.text("Custom", at=(10, 20))
screen.text("Big", at="CENTER", scale=2)
```

Cardinal positions: `"N"`, `"NE"`, `"E"`, `"SE"`, `"S"`, `"SW"`, `"W"`, `"NW"`, `"CENTER"`.

Note: `scale=2` produces a bold effect (text drawn with 1px offset), not a true pixel-scale zoom. Backends can provide `draw_scaled_text()` for true scaling.

---

### Widgets

#### Title

```python
screen.title("STeaMi")
```

Draws text centered at the top (N position).

---

#### Subtitle

```python
screen.subtitle("Line 1", "Line 2")
```

Draws text centered at the bottom (S position). Accepts multiple lines.

---

#### Value

```python
screen.value(23.5, label="Temp", unit="C")
```

Displays a large centered value with optional label above and unit below.

---

#### Progress Bar

```python
screen.bar(75, max_val=100)
```

---

#### Gauge

```python
screen.gauge(60, min_val=0, max_val=100, unit="C")
```

Draws a 270-degree arc gauge near the screen border.

---

#### Graph

```python
screen.graph([10, 20, 15, 30], min_val=0, max_val=100)
```

Draws a scrolling line graph with the last value displayed above.

---

#### Menu

```python
screen.menu(["Item 1", "Item 2", "Item 3"], selected=1)
```

---

#### Compass

```python
screen.compass(heading=45)
```

Draws a compass with cardinal labels and a rotating needle.

---

#### Watch

```python
screen.watch(hours=10, minutes=30, seconds=15)
```

Draws an analog clock face.

---

#### Face

```python
screen.face("happy")
```

Draws a pixel-art expression. Available: `"happy"`, `"sad"`, `"surprised"`, `"sleeping"`, `"angry"`, `"love"`.

---

### Control

```python
screen.clear()
screen.show()
```

---

### Properties

```python
screen.center # (64, 64) for 128x128
screen.radius # 64 for 128x128
screen.max_chars # 16 for 128px width
```

---

## Color Constants

```python
from steami_screen import BLACK, DARK, GRAY, LIGHT, WHITE
from steami_screen import RED, GREEN, BLUE, YELLOW
```

Colors are RGB tuples. On SSD1327 they degrade to greyscale automatically.

---

## Color Utilities

```python
from steami_screen import rgb_to_gray4, rgb_to_rgb565, rgb_to_rgb8
```

| Function | Output |
|----------|--------|
| `rgb_to_gray4(color)` | 4-bit greyscale (0-15) for SSD1327 |
| `rgb_to_rgb565(color)` | 16-bit RGB565 for GC9A01 |
| `rgb_to_rgb8(color)` | RGB tuple pass-through |

All accept int values for backward compatibility.
6 changes: 6 additions & 0 deletions lib/steami_screen/manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
metadata(
description="Library for controlling the STeaMi round display.",
version="0.0.1",
)

package("steami_screen")
29 changes: 29 additions & 0 deletions lib/steami_screen/steami_screen/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from steami_screen.colors import rgb_to_gray4, rgb_to_rgb8, rgb_to_rgb565
from steami_screen.device import (
BLACK,
BLUE,
DARK,
GRAY,
GREEN,
LIGHT,
RED,
WHITE,
YELLOW,
Screen,
)

__all__ = [
"BLACK",
"BLUE",
"DARK",
"GRAY",
"GREEN",
"LIGHT",
"RED",
"WHITE",
"YELLOW",
"Screen",
"rgb_to_gray4",
"rgb_to_rgb8",
"rgb_to_rgb565",
]
49 changes: 49 additions & 0 deletions lib/steami_screen/steami_screen/colors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
Color conversion utilities for STeaMi display backends.

Colors are represented as RGB tuples (r, g, b) with values 0-255.
Each backend converts to its native format:
- SSD1327 : grayscale 4-bit (0-15)
- GC9A01 : RGB565 (16-bit)
- Simulator: RGB tuple (pass-through)

All functions accept legacy int values for backward compatibility.
"""


def rgb_to_gray4(color):
"""Convert an RGB tuple to a 4-bit grayscale value (0-15).

Uses BT.601 luminance: Y = 0.299*R + 0.587*G + 0.114*B
Accepts int for backward compatibility (returned as-is, clamped to 0-15).
"""
if isinstance(color, int):
return max(0, min(15, color))
r, g, b = color
luminance = (r * 77 + g * 150 + b * 29) >> 8 # 0-255
return luminance >> 4 # 0-15


def rgb_to_rgb565(color):
"""Convert an RGB tuple to a 16-bit RGB565 integer.

Accepts int for backward compatibility (treated as gray4, expanded).
"""
if isinstance(color, int):
g = max(0, min(15, color)) * 17 # 0-255
r, b = g, g
else:
r, g, b = color
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)


def rgb_to_rgb8(color):
"""Convert a color to an RGB tuple (r, g, b).

If already a tuple, returns it unchanged.
Accepts int for backward compatibility (treated as gray4, expanded).
"""
if isinstance(color, int):
v = max(0, min(15, color)) * 17 # 0-255
return (v, v, v)
return color
Loading
Loading