circuitpython-serial-neopixel 1.0.0
A CircuitPython library for managing segments of NeoPixel strips as independent addressable units (works on CircuitPython and Raspberry Pi)
License: MIT
Requires Python: >=3.7
Keywords: circuitpython, neopixel, led, rgb, adafruit, raspberry-pi, ws2812

# CircuitPython Serial NeoPixel

A CircuitPython library that provides a `SerialNeoPixel` class for managing segments of NeoPixel LED strips as independent addressable units.

## Overview

`SerialNeoPixel` acts as a wrapper around the standard Adafruit CircuitPython NeoPixel library, allowing you to treat a contiguous subset of pixels in a strip as a separate, independently addressable unit. This is particularly useful for projects where you want to logically divide a single physical strip into multiple sections without manually managing global pixel indices.

### Key Features

- **Segment-based addressing**: Create multiple segments on a single strip, each with its own 0-based indexing
- **Full sequence protocol support**: Supports indexing, slicing, negative indices, and `len()`
- **Pythonic interface**: Feels like working with a native NeoPixel object
- **No copying overhead**: Direct pass-through to parent strip (no pixel data duplication)
- **Brightness control**: Access parent strip's brightness property (affects entire strip)

## Installation

### From PyPI (when published)

```bash
pip install circuitpython-serial-neopixel
```

### Manual Installation

Copy `serial_neo.py` to your CircuitPython device's `lib` folder or your project directory.

## Requirements

### For CircuitPython (microcontrollers)
- CircuitPython 7.0 or higher
- `adafruit-circuitpython-neopixel` library

### For Raspberry Pi (standard Python)
- Python 3.7 or higher
- `adafruit-circuitpython-neopixel` library
- SPI enabled (via `raspi-config` or `/boot/config.txt`)
- NeoPixel DIN connected to GPIO10 (physical pin 19)

## Quick Start

### CircuitPython Example

```python
import board
import neopixel
from serial_neo import SerialNeoPixel

# Create a NeoPixel strip with 30 LEDs
strip = neopixel.NeoPixel(board.D18, 30, brightness=0.3, auto_write=False)

# Create three segments on the same strip
segment1 = SerialNeoPixel(strip, 0, 10)   # Pixels 0-9
segment2 = SerialNeoPixel(strip, 10, 10)  # Pixels 10-19
segment3 = SerialNeoPixel(strip, 20, 10)  # Pixels 20-29

# Each segment has independent 0-based indexing
segment1[0] = (255, 0, 0)    # Red - affects strip pixel 0
segment2[0] = (0, 255, 0)    # Green - affects strip pixel 10
segment3[0] = (0, 0, 255)    # Blue - affects strip pixel 20

# Update the physical strip
segment1.show()  # Any segment can call show() (affects entire strip)
```

### Raspberry Pi Example (Standard Python)

```python
import board
import neopixel
from serial_neo import SerialNeoPixel

# Create a NeoPixel strip with 30 LEDs on GPIO10 (physical pin 19)
# Note: Requires SPI enabled and sudo-less access configured
strip = neopixel.NeoPixel(board.D10, 30, brightness=0.3, auto_write=False)

# Create segments exactly the same way as CircuitPython
segment1 = SerialNeoPixel(strip, 0, 10)
segment2 = SerialNeoPixel(strip, 10, 10)
segment3 = SerialNeoPixel(strip, 20, 10)

# Same API works on both platforms
segment1[0] = (255, 0, 0)
segment2[0] = (0, 255, 0)
segment3[0] = (0, 0, 255)
segment1.show()
```

## Usage Examples

### Basic Indexing

```python
# Set individual pixels
segment[0] = (255, 0, 0)      # First pixel
segment[5] = (0, 255, 0)      # Sixth pixel
segment[-1] = (0, 0, 255)     # Last pixel (negative indexing)

# Get pixel colors
color = segment[0]            # Returns (R, G, B) or (R, G, B, W) tuple
last_color = segment[-1]      # Get last pixel
```

### Slice Operations

```python
# Set multiple pixels at once
segment[0:3] = [(255, 0, 0), (0, 255, 0), (0, 0, 255)]  # First three pixels

# Set a range to the same color
segment[5:10] = [(128, 128, 128)] * 5  # Pixels 5-9 to gray

# Use slice with step
segment[::2] = [(255, 0, 0)] * 5  # Every other pixel to red

# Get multiple pixels
colors = segment[0:5]  # Returns list of color tuples
```

### Fill Operations

```python
# Fill entire segment with one color
segment.fill((255, 0, 0))  # Fill with red

# Clear a segment
segment.fill((0, 0, 0))  # Turn off all pixels
```

### Brightness Control

**Important**: The `brightness` property affects the **entire parent strip**, not just the individual segment.

```python
# Get current brightness
current = segment.brightness  # Returns float 0.0-1.0

# Set brightness (affects ALL segments on the same strip)
segment.brightness = 0.5  # 50% brightness for entire strip

# This also affects all other segments sharing the same parent strip
segment1.brightness = 0.3  # segment2 and segment3 are also affected
```

### Working with Multiple Segments

```python
import board
import neopixel
from serial_neo import SerialNeoPixel

# 60-pixel strip divided into 6 segments of 10 pixels each
strip = neopixel.NeoPixel(board.D18, 60, auto_write=False)
segments = [SerialNeoPixel(strip, i*10, 10) for i in range(6)]

# Animate each segment independently
for i, seg in enumerate(segments):
    seg.fill(colors[i])

strip.show()
```

### Advanced: Overlapping Segments

You can create overlapping segments if needed (though use carefully):

```python
full_strip = SerialNeoPixel(strip, 0, 30)   # All 30 pixels
left_half = SerialNeoPixel(strip, 0, 15)    # First 15 pixels
right_half = SerialNeoPixel(strip, 15, 15)  # Last 15 pixels
center = SerialNeoPixel(strip, 10, 10)      # Middle 10 pixels (overlaps both halves)
```

## API Reference

### `SerialNeoPixel(strip, pixel_start, pixel_count)`

Create a new segment wrapper.

**Parameters:**
- `strip`: Parent NeoPixel strip object
- `pixel_start`: Starting index in parent strip (0-based)
- `pixel_count`: Number of pixels in this segment

### Methods

#### `fill(color)`
Fill all pixels in the segment with the specified color.

**Parameters:**
- `color`: RGB tuple `(r, g, b)` or RGBW tuple `(r, g, b, w)` with values 0-255

#### `show()`
Update the physical LED strip to display current pixel colors. Affects the entire parent strip.

### Properties

#### `brightness`
Get or set the brightness of the parent strip (0.0 to 1.0).

**Warning**: This affects the **entire parent strip**, not just this segment. All segments sharing the same parent will be affected.

### Sequence Protocol

The class implements Python's sequence protocol:

- `len(segment)`: Returns number of pixels in segment
- `segment[i]`: Get/set pixel at index `i` (supports negative indices)
- `segment[start:stop:step]`: Get/set slice of pixels
- `repr(segment)`: String representation showing start position and count

## Platform Compatibility

This library works on both:

1. **CircuitPython microcontrollers**: Circuit Playground Express, Raspberry Pi Pico, Metro M4, etc.
2. **Raspberry Pi with standard Python**: Pi 3, Pi 4, Pi 5, Pi Zero W, etc.

The same code works on both platforms with only minor hardware pin differences (e.g., `board.D18` vs `board.D10`).

### Raspberry Pi Setup

To use NeoPixels on Raspberry Pi, you need to:

1. **Enable SPI** via `raspi-config`:
   ```bash
   sudo raspi-config
   # Navigate to: Interfacing Options -> SPI -> Enable
   ```

2. **Install the library**:
   ```bash
   pip3 install circuitpython-serial-neopixel
   ```

3. **Connect your NeoPixel strip**:
   - DIN (Data In) → GPIO10 (physical pin 19)
   - 5V → 5V power
   - GND → Ground

4. **Configure sudo-less access** (optional, for running without sudo):
   See [Adafruit's documentation](https://docs.circuitpython.org/projects/neopixel/en/latest/) for detailed setup.

## Limitations and Caveats

1. **Brightness is strip-wide**: The `brightness` property affects the entire parent strip, not individual segments. If multiple segments share a parent strip, changing brightness on one affects all.

2. **No bounds checking between segments**: You can create overlapping segments. The library doesn't prevent this, as it might be intentional for some use cases.

3. **`show()` updates entire strip**: Calling `show()` on any segment updates the entire physical strip, not just that segment's pixels.

4. **Requires parent strip compatibility**: The parent strip object must support the standard NeoPixel interface (`__getitem__`, `__setitem__`, `show()`, `brightness`).

5. **Auto-write behavior inherited**: If the parent strip has `auto_write=True`, changes may appear immediately. For better performance with segments, use `auto_write=False` on the parent strip and call `show()` manually.

## Performance Tips

1. **Use `auto_write=False`**: Create the parent strip with `auto_write=False` and call `show()` manually after batch updates:

```python
strip = neopixel.NeoPixel(board.D18, 30, auto_write=False)
segment = SerialNeoPixel(strip, 0, 10)

# Make multiple changes
segment[0] = (255, 0, 0)
segment[1] = (0, 255, 0)
segment[2] = (0, 0, 255)

# Update once
segment.show()
```

2. **Use `fill()` for uniform colors**: It's more efficient than setting pixels individually.

3. **Batch slice operations**: Use slice assignment instead of loops when possible:

```python
# Good
segment[0:5] = [(255, 0, 0)] * 5

# Less efficient
for i in range(5):
    segment[i] = (255, 0, 0)
```

## Examples

See the examples directory for more detailed usage examples (coming soon).

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## License

MIT License - see LICENSE file for details.

## Credits

Built on top of the [Adafruit CircuitPython NeoPixel](https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel) library.

## Support

For bugs and feature requests, please open an issue on GitHub.