diff --git a/.gitignore b/.gitignore
index 39672af..595d462 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,11 @@
/.venv
-/__pycache__
+__pycache__
# User config (contains GPS coordinates, shutter names, MQTT credentials)
operateShutters.conf
# Log files (contain personal data and occupancy patterns)
*.log
-*.log.*
\ No newline at end of file
+*.log.*
+
+.vscode
\ No newline at end of file
diff --git a/Home Assistant/addon/pi_somfy/DOCS.md b/Home Assistant/addon/pi_somfy/DOCS.md
index 4bb3dd8..4c33f1c 100644
--- a/Home Assistant/addon/pi_somfy/DOCS.md
+++ b/Home Assistant/addon/pi_somfy/DOCS.md
@@ -19,9 +19,30 @@ This add-on runs [Pi-Somfy](https://github.com/Nickduino/Pi-Somfy) directly on y
## Configuration
-| Option | Default | Description |
-|------------|---------|------------------------------------------------|
-| `gpio_pin` | `4` | GPIO pin number for the 433.42 MHz transmitter |
+| Option | Default | Description |
+|---------------|---------|---------------------------------------------------------------------------|
+| `gpio_pin` | `4` | GPIO pin number for the 433.42 MHz transmitter |
+| `rx_gpio_pin` | (none) | GPIO wired to a CC1101 receiver's data output. Leave blank to disable the physical-remote receiver entirely. |
+| `spi_sck` | `21` | CC1101 bit-banged SPI clock GPIO (only used when `rx_gpio_pin` is set) |
+| `spi_mosi` | `20` | CC1101 bit-banged SPI MOSI GPIO (only used when `rx_gpio_pin` is set) |
+| `spi_miso` | `19` | CC1101 bit-banged SPI MISO GPIO (only used when `rx_gpio_pin` is set) |
+| `spi_csn` | `16` | CC1101 bit-banged SPI chip-select GPIO (only used when `rx_gpio_pin` is set) |
+
+### Physical remote receiver (optional)
+
+Setting `rx_gpio_pin` enables listening for physical Somfy RTS remote button
+presses via a CC1101 receiver module, so a physical remote and the app stay
+in sync. Pair a physical remote to a shutter from the web UI's "Physical
+Remotes" section: press a button on the remote, find it listed under
+"Recently Heard", and assign it to one or more shutters.
+
+**Before enabling `rx_gpio_pin`, SPI must be enabled on the host**, even
+though the receiver bit-bangs SPI over ordinary GPIOs rather than using
+the Pi's dedicated hardware SPI peripheral — confirmed necessary in
+practice on Home Assistant OS. Power down the Pi, read the Micro-SD card
+on another computer (it mounts as a normal FAT32 `boot`/`bootfs` drive),
+add `dtparam=spi=on` to the bottom of `config.txt`, then reinsert the card
+and power the Pi back on.
## Web UI
@@ -50,7 +71,7 @@ Shutter configuration and rolling codes are stored in `/data/operateShutters.con
- This add-on runs Pi-Somfy with the web interface and scheduler only (no MQTT, no Alexa emulation)
- For MQTT or Alexa integration, run Pi-Somfy standalone on a dedicated Raspberry Pi
-- Shutter position tracking is estimated based on timing and is reset on restart
+- Shutter position is estimated based on movement timing, but persists across restarts (saved to `/data/operateShutters.conf`'s `[ShutterPositions]` section as it changes)
## Support
diff --git a/Home Assistant/addon/pi_somfy/Dockerfile b/Home Assistant/addon/pi_somfy/Dockerfile
index ddb20d0..28e0c71 100644
--- a/Home Assistant/addon/pi_somfy/Dockerfile
+++ b/Home Assistant/addon/pi_somfy/Dockerfile
@@ -3,6 +3,7 @@ FROM ${BUILD_FROM}
# Install system dependencies (Debian base)
# Build lgpio from source (for Pi 5) and pigpiod from source (for Pi 1/2/3/4)
+COPY patch_pigpiod.py /tmp/patch_pigpiod.py
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 \
python3-pip \
@@ -24,11 +25,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
&& pip3 install --no-cache-dir --break-system-packages . \
&& cd /tmp \
&& git clone --depth 1 https://github.com/joan2937/pigpio.git \
+ && python3 /tmp/patch_pigpiod.py /tmp/pigpio/pigpiod.c \
&& cd pigpio \
&& make \
&& make install \
&& cd / \
- && rm -rf /tmp/lg /tmp/pigpio \
+ && rm -rf /tmp/lg /tmp/pigpio /tmp/patch_pigpiod.py \
&& apt-get purge -y --auto-remove make gcc g++ libc6-dev swig python3-dev \
&& rm -rf /var/lib/apt/lists/*
diff --git a/Home Assistant/addon/pi_somfy/config.yaml b/Home Assistant/addon/pi_somfy/config.yaml
index 681ec42..b08c084 100644
--- a/Home Assistant/addon/pi_somfy/config.yaml
+++ b/Home Assistant/addon/pi_somfy/config.yaml
@@ -20,6 +20,7 @@ privileged:
- SYS_RAWIO
devices:
- /dev/mem
+ - /dev/vcio
- /dev/gpiochip0
- /dev/gpiochip4
map:
@@ -32,7 +33,16 @@ ports_description:
watchdog: "http://[HOST]:[PORT:80]/cmd/getConfig"
options:
gpio_pin: 4
+ spi_sck: 21
+ spi_mosi: 20
+ spi_miso: 19
+ spi_csn: 16
schema:
gpio_pin: "int(0,27)"
+ rx_gpio_pin: "int(0,27)?"
+ spi_sck: "int(0,27)"
+ spi_mosi: "int(0,27)"
+ spi_miso: "int(0,27)"
+ spi_csn: "int(0,27)"
discovery:
- pi_somfy
diff --git a/Home Assistant/addon/pi_somfy/patch_pigpiod.py b/Home Assistant/addon/pi_somfy/patch_pigpiod.py
new file mode 100644
index 0000000..abba040
--- /dev/null
+++ b/Home Assistant/addon/pi_somfy/patch_pigpiod.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+"""Patch pigpiod's unconditional /dev/pigerr error-FIFO setup (build-time only).
+
+pigpiod.c's main() always does unlink()+mkfifo()+chmod()+freopen() on
+/dev/pigerr, with no command-line flag to disable it (verified against
+joan2937/pigpio master — -f only gates the /dev/pigpio *command* FIFO, not
+this error-output companion). In a container where /dev is read-only except
+for the specific device nodes this add-on's config.yaml requests, mkfifo()
+silently fails and the following chmod() aborts the whole daemon.
+
+This add-on only talks to pigpiod over its TCP socket interface, so the
+error FIFO is unneeded — replace its setup with a direct assignment to the
+process's own stderr, which the container already captures as add-on logs.
+"""
+import re
+import sys
+
+path = sys.argv[1]
+with open(path) as f:
+ src = f.read()
+
+pattern = re.compile(
+ r"/\*\s*create pipe for error reporting\s*\*/.*?"
+ r"errFifo\s*=\s*freopen\(PI_ERRFIFO,\s*\"w\+\",\s*stderr\);",
+ re.DOTALL)
+
+patched, count = pattern.subn(
+ "/* patched: container /dev is read-only, skip the error FIFO */\n"
+ " errFifo = stderr;",
+ src, count=1)
+
+if count != 1:
+ sys.exit("patch_pigpiod.py: expected pigpiod.c pattern not found — "
+ "pigpio source may have changed upstream, update the patch")
+
+with open(path, "w") as f:
+ f.write(patched)
+
+print("pigpiod.c patched: error FIFO disabled (using stderr directly)")
diff --git a/Home Assistant/addon/pi_somfy/run.sh b/Home Assistant/addon/pi_somfy/run.sh
index f3354c3..6f4e03b 100644
--- a/Home Assistant/addon/pi_somfy/run.sh
+++ b/Home Assistant/addon/pi_somfy/run.sh
@@ -20,6 +20,30 @@ else
sed -i "/^\[General\]/a TXGPIO = ${GPIO_PIN}" "${CONFIG_FILE}"
fi
+# RX receiver (physical Somfy remotes) is optional — only write RXGPIO/RXSpi*
+# into the config file if the user set rx_gpio_pin, so operateShutters.py's
+# `config.RXGPIO is not None` gate stays unset (receiver disabled) otherwise.
+if bashio::config.has_value 'rx_gpio_pin'; then
+ RX_GPIO_PIN=$(bashio::config 'rx_gpio_pin')
+ SPI_SCK=$(bashio::config 'spi_sck')
+ SPI_MOSI=$(bashio::config 'spi_mosi')
+ SPI_MISO=$(bashio::config 'spi_miso')
+ SPI_CSN=$(bashio::config 'spi_csn')
+ bashio::log.info "RX receiver enabled: GPIO ${RX_GPIO_PIN} (CC1101)"
+
+ for entry in "RXGPIO:${RX_GPIO_PIN}" "RXSpiSCK:${SPI_SCK}" "RXSpiMOSI:${SPI_MOSI}" "RXSpiMISO:${SPI_MISO}" "RXSpiCSN:${SPI_CSN}"; do
+ key="${entry%%:*}"
+ value="${entry#*:}"
+ if grep -q "^${key}" "${CONFIG_FILE}"; then
+ sed -i "s/^${key}.*/${key} = ${value}/" "${CONFIG_FILE}"
+ else
+ sed -i "/^\[General\]/a ${key} = ${value}" "${CONFIG_FILE}"
+ fi
+ done
+else
+ bashio::log.info "RX receiver disabled (set rx_gpio_pin in add-on options to enable)"
+fi
+
# Ensure log location exists and is writable
sed -i "s|^LogLocation.*|LogLocation = /data/|" "${CONFIG_FILE}"
@@ -50,7 +74,10 @@ if [ "${IS_PI5}" = true ]; then
bashio::log.info "Pi 5 detected — using lgpio (no pigpiod needed)"
else
bashio::log.info "Starting pigpiod..."
- pigpiod -l -m
+ # Deliberately not passing -m (disable alerts): -m silently prevents
+ # pi.callback() from ever delivering edge notifications, which the RX
+ # receiver needs when rx_gpio_pin is set.
+ pigpiod -l
sleep 1
if ! pgrep -x pigpiod > /dev/null; then
diff --git a/Home Assistant/custom_components/pi_somfy/cover.py b/Home Assistant/custom_components/pi_somfy/cover.py
index 7505dcb..77dc1a2 100644
--- a/Home Assistant/custom_components/pi_somfy/cover.py
+++ b/Home Assistant/custom_components/pi_somfy/cover.py
@@ -100,10 +100,22 @@ def is_closing(self) -> bool:
@callback
def _handle_coordinator_update(self) -> None:
- """Handle updated data from the coordinator."""
- pos = self.current_cover_position
- if pos is not None and (pos <= 0 or pos >= 100):
- self._moving = None
+ """Handle updated data from the coordinator.
+
+ Reads Pi-Somfy's own movement-state signal (populated by any trigger
+ source — the app, a physical remote, or the web UI — not just
+ commands issued through Home Assistant) rather than only clearing
+ the optimistic local flag once position settles. This overwrites
+ the optimistic set from async_open_cover/async_close_cover/
+ async_set_cover_position with the server-authoritative value as soon
+ as the next poll lands, which also happens to confirm HA-issued
+ commands almost immediately since the coordinator refreshes right
+ after sending one.
+ """
+ shutter = self.coordinator.data.get(self._shutter_id)
+ if shutter is not None:
+ state = shutter.get("movementState")
+ self._moving = state if state in ("opening", "closing") else None
self.async_write_ha_state()
async def async_open_cover(self, **kwargs: Any) -> None:
diff --git a/README.md b/README.md
index 5a99586..020f113 100644
--- a/README.md
+++ b/README.md
@@ -38,6 +38,79 @@ OK. now this all should look like this. Note that some of the pictures are a bit


+### 2.1 Receiver (optional) — tracking physical remotes
+
+Everything above (§2) covers *sending* commands, and is all you need to
+operate your shutters from Pi-Somfy. This section adds an optional extra:
+*listening* for presses on your existing physical Somfy remotes, so
+pressing one updates Pi-Somfy's tracked shutter position (and Home
+Assistant, via MQTT or the custom component) immediately — not just
+commands issued through the app itself.
+
+**This is purely additive — it doesn't replace or change anything above.**
+Your existing RF transmitter (with its oscillator swap) stays exactly as
+it is; the receiver is a second, independent piece of hardware you add
+alongside it. And unlike the transmitter, **the receiver needs no
+soldering at all**: CC1101 modules are sold as ready-made breakout boards
+with header pins, so it's just a few jumper wires straight into the Pi's
+GPIO header.
+
+#### What to buy
+
+Any CC1101 breakout module works, as long as you can match its pins to
+the table below. This is the exact module used to build and test this
+feature: [CC1101 433 MHz module (AliExpress)](https://he.aliexpress.com/item/1005005933919297.html).
+You'll also want a handful of female-to-female jumper wires to connect it
+to the Pi (the same kind used for the transmitter in §2).
+
+#### Wiring
+
+Match module pins by silkscreen **label**, not position — layouts vary
+between sellers, and MOSI/MISO are sometimes printed as `SI`/`SO` instead:
+
+| CC1101 pin | Signal | Default GPIO | Physical pin (Pi 4) |
+|---|---|---|---|
+| VCC | 3.3 V supply | — | 17 (or 1) — **never 5 V** |
+| GND | ground | — | 39 |
+| SCK | SPI clock | GPIO 21 | 40 |
+| MOSI (SI) | SPI data → radio | GPIO 20 | 38 |
+| MISO (SO) | SPI data → Pi | GPIO 19 | 35 |
+| CSN | chip select | GPIO 16 | 36 |
+| GDO0 | demodulated data out | GPIO 26 | 37 |
+| GDO2 | — | not connected | — |
+| ANT | antenna | — | 17 cm solid-core wire, **required** |
+
+
+
+*(Shown together for a complete GPIO reference — the transmitter wiring above is the same setup from §2, unchanged.)*
+
+#### Step-by-step setup
+
+1. Buy a CC1101 module (see above) and some jumper wires.
+2. **Power off the Pi**, then wire the module's 6 pins per the table above.
+3. **Enable SPI on the Pi itself**, even though it's bit-banged over
+ ordinary GPIOs rather than the Pi's dedicated hardware SPI peripheral —
+ in practice (confirmed on Home Assistant OS) the receiver still needs
+ the `spi` device-tree overlay active, or it won't work:
+ - Power down the Pi, remove the Micro-SD card, and read it on another
+ computer (it mounts as a normal FAT32 drive, `boot`/`bootfs`).
+ - Open `config.txt` in a text editor, add `dtparam=spi=on` at the
+ bottom, and save.
+ - Reinsert the SD card into the Pi and power it back on.
+4. Enable it in config: set `RXGPIO = 26` in `operateShutters.conf`'s
+ `[General]` section (and `RXSpiSCK`/`RXSpiMOSI`/`RXSpiMISO`/`RXSpiCSN`
+ too, only if you wired it to different pins than the table above).
+ Running the Home Assistant add-on instead? The same options are on its
+ configuration page as `rx_gpio_pin`/`spi_sck`/`spi_mosi`/`spi_miso`/`spi_csn`
+ — no manual config file editing needed.
+5. Restart Pi-Somfy (or the add-on).
+6. Pair a physical remote to a shutter from the web UI's "Physical
+ Remotes" section (see §5 below): press a button on the remote, find it
+ listed under "Recently Heard", and assign it to one or more shutters.
+
+Leave `RXGPIO` unset/commented out (or `rx_gpio_pin` blank in the add-on)
+to run without a receiver at all, exactly as before this feature existed.
+
## 3 Software
If you are new to using a Raspberry Pi and Linux please refer to other sources for coming up to speed with the environment. Having a base knowledge will go a long way. This [site](https://www.raspberrypi.org/help/) is a great place to start if you are new to these topics.
@@ -206,6 +279,10 @@ Click the "Add" button, select the name for your shutter (this is also the name
1. Finally, it's time to program your shutters schedule. To do so, use the "Scheduled Operations" menu.

+1. If you've wired up the optional CC1101 receiver (§2.1), pair your physical remotes using the "Physical Remotes" menu. Press a button on the remote, find it listed under "Recently Heard", click the assign icon, and pick which shutter(s) it should control.
+
+1. If you set up the CC1101 receiver, also set each shutter's "My" position — essential so Pi-Somfy's tracked position stays correct when a physical remote's STOP/MY button sends the shutter there. In "Add/Remove Shutters", click the Configure (wrench) icon, open "Setting the 'My' Position", and enter the matching percentage (a warning shows on Manual Operation until you do). To find the right number: send the shutter to 0% first (the one position Pi-Somfy tracks exactly), then use the wizard's own up/down buttons — not the remote — to reach the physical My position, and copy the percentage shown on Manual Operation into the Save field.

+
## 6 Alexa Integration
diff --git a/config.py b/config.py
index 8973050..cc90f21 100644
--- a/config.py
+++ b/config.py
@@ -132,6 +132,16 @@ def __init__(self, filename = None, section = None, log = None):
self.ShuttersByName = {}
self.Schedule = {}
self.Password = ""
+ self.PhysicalRemotes = {}
+ self.ShutterPositions = {}
+ # Optional (unlike TXGPIO, not guaranteed present in defaultConfig.conf)
+ # — must default to None so `is not None` checks don't raise
+ # AttributeError on a config file that omits them.
+ self.RXGPIO = None
+ self.RXSpiSCK = None
+ self.RXSpiMOSI = None
+ self.RXSpiMISO = None
+ self.RXSpiCSN = None
try:
self.config = RawConfigParser(strict=False)
@@ -150,7 +160,8 @@ def __init__(self, filename = None, section = None, log = None):
# -------------------- MyConfig::LoadConfig-----------------------------------
def LoadConfig(self):
- parameters = {'LogLocation': str, 'Latitude': float, 'Longitude': float, 'SendRepeat': int, 'UseHttps': bool, 'HTTPPort': int, 'HTTPSPort': int, 'TXGPIO': int, 'RTS_Address': str, "Password": str}
+ parameters = {'LogLocation': str, 'Latitude': float, 'Longitude': float, 'SendRepeat': int, 'UseHttps': bool, 'HTTPPort': int, 'HTTPSPort': int, 'TXGPIO': int, 'RTS_Address': str, "Password": str,
+ 'RXGPIO': int, 'RXSpiSCK': int, 'RXSpiMOSI': int, 'RXSpiMISO': int, 'RXSpiCSN': int}
for key, type in parameters.items():
try:
@@ -201,7 +212,23 @@ def LoadConfig(self):
except Exception as e1:
self.LogErrorLine("Missing config file or config file entries in Section Scheduler for key "+key+": " + str(e1))
return False
-
+
+ remotes = self.GetList(section="PhysicalRemotes")
+ for key, value in remotes:
+ try:
+ self.PhysicalRemotes[key] = [s.strip() for s in value.split(",")]
+ except Exception as e1:
+ self.LogErrorLine("Missing config file or config file entries in Section PhysicalRemotes for key "+key+": " + str(e1))
+ return False
+
+ positions = self.GetList(section="ShutterPositions")
+ for key, value in positions:
+ try:
+ self.ShutterPositions[key] = int(value)
+ except Exception as e1:
+ self.LogErrorLine("Missing config file or config file entries in Section ShutterPositions for key "+key+": " + str(e1))
+ return False
+
return True
#---------------------MyConfig::setLocation---------------------------------
@@ -314,7 +341,14 @@ def WriteValue(self, Entry, Value, section = None):
last_data_line = i
if not in_target_section:
- raise Exception("NOT ABLE TO FIND SECTION:" + sect)
+ # Auto-create the missing section rather than failing:
+ # fresh installs get every section from defaultConfig.conf,
+ # but a pre-M1 config file being upgraded in place may be
+ # missing newer sections (e.g. [PhysicalRemotes],
+ # [ShutterPositions]) entirely.
+ lines.append("")
+ lines.append("[" + sect + "]")
+ section_header_line = len(lines) - 1
if key_line >= 0:
# Replace existing key
@@ -346,3 +380,70 @@ def WriteValue(self, Entry, Value, section = None):
except Exception as e1:
self.LogError("Error in WriteValue: " + str(e1))
return False
+
+ #---------------------MyConfig::RemoveValue----------------------------------
+ # Deletes a single key from a section — WriteValue can only replace/insert
+ # a key, never remove one, which a real "unassign" (e.g. [PhysicalRemotes])
+ # needs, unlike [Shutters]'s soft-delete-via-flag convention which doesn't
+ # fit a plain "key = value" row shape. Mirrors WriteValue's exact
+ # scan-then-atomic-rewrite pattern. Idempotent: removing an already-absent
+ # key (or a section that doesn't exist at all) is not an error.
+ def RemoveValue(self, Entry, section = None):
+
+ sect = section if section is not None else self.Section
+
+ try:
+ with self.CriticalLock:
+ with open(self.FileName, 'r') as f:
+ lines = f.read().splitlines()
+
+ in_target_section = False
+ key_line = -1
+
+ for i, line in enumerate(lines):
+ m_sect = self._RE_SECTION.match(line)
+ if m_sect:
+ if in_target_section:
+ break # reached next section, stop
+ if m_sect.group(1).strip().lower() == sect.lower():
+ in_target_section = True
+ continue
+
+ if in_target_section:
+ m_kv = self._RE_KEY_VALUE.match(line)
+ if m_kv and m_kv.group(2).strip() == Entry:
+ key_line = i
+ break
+
+ if not in_target_section or key_line < 0:
+ return True # nothing to remove — idempotent, not an error
+
+ del lines[key_line]
+ content = "\n".join(lines) + "\n"
+
+ # Atomic write: write to temp file, then replace
+ tmp = self.FileName + ".tmp"
+ with open(tmp, 'w') as f:
+ f.write(content)
+ f.flush()
+ os.fsync(f.fileno())
+ if sys.version_info[0] >= 3:
+ os.replace(tmp, self.FileName)
+ else:
+ if os.path.exists(self.FileName):
+ os.remove(self.FileName)
+ os.rename(tmp, self.FileName)
+
+ # RawConfigParser.read() only merges in additions/updates, it
+ # never drops an option that disappeared from the file — the
+ # removed key would otherwise stay stale in the cached view.
+ try:
+ self.config.remove_option(sect, Entry)
+ except Exception:
+ pass
+ self.config.read(self.FileName)
+ return True
+
+ except Exception as e1:
+ self.LogError("Error in RemoveValue: " + str(e1))
+ return False
diff --git a/defaultConfig.conf b/defaultConfig.conf
index bb0599b..fe7c046 100644
--- a/defaultConfig.conf
+++ b/defaultConfig.conf
@@ -49,6 +49,21 @@ HTTPSPort = 443
# each instance is set to a different value to avoid possible conflicts
RTS_Address = 0x279620
+# (Optional) GPIO wired to the RTS receiver's demodulated data output
+# (CC1101 GDO0). When set, Pi-Somfy listens for physical Somfy remote
+# button presses and updates the tracked shutter position accordingly
+# Leave commented out to disable the receiver entirely.
+# Example: GPIO 26 (physical pin 37)
+# RXGPIO = 26
+
+# (Optional) Bit-banged SPI pins used to configure the CC1101 receiver
+# module. Only used when RXGPIO is set above. Defaults shown below; change
+# only if wired differently.
+#RXSpiSCK = 21
+#RXSpiMOSI = 20
+#RXSpiMISO = 19
+#RXSpiCSN = 16
+
###################################################################
###################################################################
## LIST OF ALL SHUTTERS REGISTERED
@@ -131,4 +146,29 @@ EnableDiscovery = true
#
[Scheduler]
+###################################################################
+###################################################################
+## PHYSICAL REMOTE PAIRING (used by the RTS receiver, when RXGPIO is set)
+###################################################################
+###################################################################
+#
+# Maps a physical Somfy remote's address (24bit identifier, same format as
+# [Shutters]) to the shutterId(s) it should control. The config value is a
+# comma delimited list of shutterId(s) (the addresses used as keys in
+# [Shutters]) — list more than one to fan a single remote channel out to a
+# group of shutters.
+#
+# Example: a remote at address 0x27ABCD operating two shutters:
+#0x27ABCD = 0x279620, 0x14A2C7
+#
+[PhysicalRemotes]
+
+
+# Last known position (0-100 %) of each shutter, keyed by the same
+# shutterId used in [Shutters]. Automatically maintained by Pi-Somfy as
+# shutters move — like [ShutterRollingCodes], you normally won't edit this
+# by hand. Seeds the tracked position on startup so it isn't lost across a
+# restart.
+#
+[ShutterPositions]
diff --git a/documentation/CC1101 Wiring Diagram.svg b/documentation/CC1101 Wiring Diagram.svg
new file mode 100644
index 0000000..303e59a
--- /dev/null
+++ b/documentation/CC1101 Wiring Diagram.svg
@@ -0,0 +1,247 @@
+
diff --git a/documentation/p5.png b/documentation/p5.png
new file mode 100644
index 0000000..2da7cfa
Binary files /dev/null and b/documentation/p5.png differ
diff --git a/html/index.html b/html/index.html
index 02027d7..ca88972 100755
--- a/html/index.html
+++ b/html/index.html
@@ -228,6 +228,58 @@