# Troubleshooting

Symptoms, in approximate order of frequency.

---

## The editor cannot find the device

**Search returns nothing.**

Discovery is a UDP broadcast on port 33333, and broadcasts do not cross subnets.
Check, in order:

1. **Are you on the same subnet?** Compare your workstation's address with the
   device's. Different subnets, a VPN, or a segmented VLAN will all defeat
   discovery while leaving the device perfectly reachable.
2. **Has it finished booting?** Give it a minute after power-up.
3. **Is your host firewall dropping the reply?** Some endpoint-protection
   suites block the inbound UDP response. Allow OpenPLC Editor through.
4. **Wireless client isolation?** Many guest and corporate Wi-Fi networks block
   station-to-station traffic. Use a wired connection.

**The workaround is always the same:** find the address another way and type it
in manually.

```bash
# From the device, over an HDMI display or SSH
hostname -I

# Or sweep the subnet from your workstation
nmap -p 8443 --open 192.168.1.0/24
```

---

## The device is found but will not connect

**Connection refused or times out.** The runtime is not running. Try restarting the device.

**Login fails.** The credentials are stored on the device, not in any cloud
account. If they are lost, see {doc}`reference/factory-reset`.

---

## The SLM-RP4 is not in the Device dropdown

The support package is not installed, or the editor is too old.

1. Check the editor version. 4.3.1 or newer is required.
2. Open the Package Manager and check the **Installed** tab.
3. If it is not listed, install it. See
   {doc}`getting-started/first-project`.

---

## Backplane Configuration and HAL Settings are missing

They only appear once **SLM-RP4** is selected in **Device → Configuration**.
Select the board first.

If the board is selected and they are still missing, the package did not
register its screens. Reinstall it and reopen the project.

---

## No modules found

```text
No modules found in backplane after 5 retries
```

The base controller is alive but sees an empty bus. The SPI interface is
working, so this is a wiring or seating problem rather than a configuration
one. If the interface itself has been lost, the runtime reports a different
error instead, covered under **The backplane has stopped working entirely**
below.

- Is the first module fully seated against the base? This is the usual cause.
- Is backplane power present?

```text
No base controller activity - check power supply
```

The base controller is not responding at all. Almost always a power problem
rather than a module fault. Check 24 V at the terminal.

---

## The backplane has stopped working entirely

If no I/O works at all, rather than one module or one channel misbehaving, the
SPI interface the backplane runs on has been lost.

The symptoms are:

- The PLC does not run, and **PLC Logs** reports:

  ```text
  [SYNERGY] SPI: failed to open /dev/spidev6.0 at init (errno=2)
  [SYNERGY] SPI init failed
  Native plugin synergy failed to start (returned -1)
  ```

- A Python program fails at `synergy_logic.Base()` with:

  ```text
  ValueError: No device found for /dev/spidev6.0
  ```

- `ls /dev/spidev6.0` reports that the file does not exist.

The backplane uses `/dev/spidev6.0`, which is created by a single overlay line
in `/boot/firmware/config.txt`. Two things remove it.

**Enabling SPI in `raspi-config`.** The **Interface Options → SPI** toggle sets
`dtparam=spi=on`, which enables SPI0 on GPIO 7–11 (CE1, CE0, MISO, MOSI,
SCLK). The backplane's SPI6 overlay uses GPIO 9 and 10 as its chip selects
(`cs0_pin=9,cs1_pin=10`). The two cannot coexist, and the backplane's overlay is
the one that fails to load.

**A system update or a reimage** that rewrites `config.txt` and drops the
overlay line.

### Identify which one it is

```bash
ls /dev/spidev*
pinctrl get 9,10,19,20,21
grep -nE "dtparam=spi|dtoverlay=spi" /boot/firmware/config.txt
```

| Result | Cause |
|---|---|
| `/dev/spidev0.0` and `0.1` present, GPIO 9 and 10 shown as `SPI0_*` | SPI0 was enabled and is taking the pins |
| No `spidev` device at all, GPIO 9, 10, 19, 20, 21 all plain inputs | The overlay line is missing |
| `/dev/spidev6.0` and `6.1` present, GPIO 19, 20, 21 shown as `SPI6_*` | The interface is correct; the fault is elsewhere |

### Restore it

Edit the file:

```bash
sudo nano /boot/firmware/config.txt
```

1. If a line reads `dtparam=spi=on`, comment it out with a leading `#`, or
   delete it.
2. If the overlay line is missing, add it exactly as written:

   ```text
   dtoverlay=spi6-2cs,cs0_pin=9,cs1_pin=10
   ```

3. Save the file and reboot:

   ```bash
   sudo reboot
   ```

If SPI was enabled through `raspi-config`, turning the same toggle back off has
the same effect as commenting the line out by hand. Check afterwards that the
`dtoverlay` line is still present.

To add the overlay line without editing the file, this appends it only when it
is absent, and is safe to run more than once:

```bash
grep -q '^dtoverlay=spi6-2cs' /boot/firmware/config.txt \
  || echo 'dtoverlay=spi6-2cs,cs0_pin=9,cs1_pin=10' | sudo tee -a /boot/firmware/config.txt
```

It does not remove `dtparam=spi=on`, so check for that separately.

```{admonition} Keep the line outside a conditional section
:class: note

`config.txt` can contain conditional filters such as `[cm4]` or `[cm5]`, which
apply only to the hardware they name. The overlay line must sit under `[all]`,
or before any filter, so that it always applies.

On a factory image `[all]` is the last section, so appending to the end of the
file is correct.
```

### Verify after the reboot

```bash
ls /dev/spidev*
pinctrl get 9,10,19,20,21
```

A working device reports both devices and these pin assignments:

```text
/dev/spidev6.0  /dev/spidev6.1

 9: op -- pd | hi // GPIO9 = output
10: op -- pd | hi // GPIO10 = output
19: a3    pd | lo // GPIO19 = SPI6_MISO
20: a3    pd | lo // GPIO20 = SPI6_MOSI
21: a3    pd | hi // GPIO21 = SPI6_SCLK
```

GPIO 9 and 10 are the chip selects and appear as ordinary outputs. If they
instead show `SPI0_MISO` and `SPI0_MOSI`, SPI0 is still enabled.

Then start the PLC and confirm the backplane comes up in **PLC Logs**:

```text
[SYNERGY] SPI: opened /dev/spidev6.0 (mode=2, speed=1000000 Hz)
[SYNERGY] Slot 1: SLM-ACDCI-8NP-RLY8 (0x24A500E1)
[SYNERGY] Base controller initialized (1 module(s); DI=1 DO=1 AI=0 AO=0 bytes)
```

```{admonition} Never enable SPI through raspi-config on this device
:class: warning

The SPI toggle in `raspi-config` configures a different bus on different pins,
and enabling it disables backplane communication completely until the change is
undone and the device is rebooted.

The `dtoverlay` line is the whole SPI configuration, and it is applied at the
factory. Leave the `raspi-config` toggle off.
```

---

(sign-on-wrong-modules)=
## Sign-on lists the wrong modules

The **PLC Logs** sign-on lines come from the modules themselves and are
authoritative:

```text
Slot 1: SLM-DCI-16NP (0x052000E2)
Slot 2: SLM-DCO-15P (0x140800E6)
```

If they disagree with your Backplane Configuration, **the configuration is
wrong**. Correct it and re-upload.

Running with a mismatched configuration means your program addresses the wrong
data.

---

## An output does nothing

Work down the chain rather than guessing.

1. **Is the PLC running?** The RGB **LED** should be green.
2. **Is the variable being set?** Open the debugger and watch it. If it is not
   changing, the problem is in your logic.
3. **Did the module sign on?** Check **PLC Logs**.
4. **Does the module have field power?** Discrete output modules need their own
   supply to switch a load. The backplane powers the module's logic, not your
   load. **This is the most common cause.**
5. **Sinking or sourcing?** A load wired for a PNP module will not work on an
   NPN one, and vice versa.
6. **Right address?** Check the I/O Mapping table. Output numbering starts at
   `%QX0.0` for the *first module with outputs*, which is not necessarily
   slot 1.

---

## An input never changes

1. **Right address?** Check the I/O Mapping table.
2. **Is the field wiring correct for the module type?** Sinking inputs need the
   circuit completed to common.
3. **Is the signal actually present?** Meter it at the terminal.
4. **Did the module sign on?**

---

## An analog channel reads zero, always

At sign-on, the OpenPLC Runtime sends each analog and temperature module the
configuration from the project's Backplane Configuration, or the module's
factory defaults when the project has none. The Python and C++ libraries send
the factory defaults.

**Channel not enabled.** **Channels Enabled** restricts which channels are
sampled. Disabled channels read zero while still occupying their addresses.

**Configuration not applied.** Check the **PLC Logs** for
`Slot N (MODULE): no config available` or a configuration length mismatch
warning. Set the module's Configuration form in Backplane Configuration and
re-upload.

---

## Analog readings are wrong by a factor of eight

You are scaling against the wrong maximum.

The range of a raw analog count depends on the module's **Counts Scaling**
setting in Backplane Configuration:

- **Module resolution** (the default): counts follow the converter, 0-8191 for
  a 13-bit input and 0-4095 for a 12-bit output.
- **Rescaled to 0 - 65535**: every channel spans the full `UINT` range.

Code written for one setting and run with the other is out by a factor of
roughly eight (13-bit) or sixteen (12-bit). Check the setting, then scale
against the matching maximum. The Python and C++ libraries always report
counts at module resolution. See {doc}`getting-started/configuring-the-backplane`.

---

## Temperatures are in the wrong unit

The SLM-THM-4 defaults to **degrees Fahrenheit**.

Change it in **Burnout Detection / Units** on the module's Configuration form
and re-upload. See {doc}`getting-started/configuring-the-backplane`.

## Temperature readings are wrong but plausible

Check the **sensor type**. The default is **Type J**; a Type K thermocouple read
as Type J is linearised with the wrong curve. The error is small near ambient
and grows with temperature, so it passes a bench test and fails in the process.

## A temperature falls as the process heats

The thermocouple is wired backwards. Swap the leads. Note that under ANSI colour
coding the **red** lead is negative, which is a common source of error.

---

## The PLC keeps stopping

**Bus fault.** The HAL has detected repeated failures on a slot and applied the
configured fault action. Check **PLC Logs** for which slot.

- A module working loose is the usual cause.
- Electrical noise. Check grounding and cable routing.

**Cycle overrun.** The driver cannot complete a scan within its configured cycle
time. Raise the **Bus Scan Cycle** rather than the fault threshold. Masking an
overrun does not make the I/O any fresher.

See {doc}`getting-started/hal-settings`.

---

## Changes have no effect

Backplane configuration, HAL settings and module configuration all live in the
**project** and are pushed to the device on upload.

**Build & Upload after changing any of them.** Editing a form does not affect a
running device.

If a build behaves inexplicably, try **Clean Upload** to discard the device's
build cache.

---

## I/O stops working after a package update

A package update can change the HAL driver. The device keeps running the driver
it already has until you upload again.

Rebuild and re-upload. Then confirm that the modules in your backplane
configuration are still present in the updated module list. See
{doc}`getting-started/configuring-the-backplane`.

---

## Python or C++: erratic readings

This is almost always a backplane collision. The OpenPLC Runtime holds the
backplane while the PLC is running, and the direct libraries drive the same pins.
Running both at once produces interleaved transactions and invalid data, with no
error raised.

Stop the PLC with **Stop** in OpenPLC Editor, or stop the runtime from a terminal:

```bash
sudo docker stop openplc-bootloader openplc-runtime
```

Then power-cycle the base to clear the base controller's state, and start one
side only. See {doc}`advanced/shared-hardware-rules`.

---

## Python: `ModuleNotFoundError: No module named 'synergy_logic'`

The library is supplied under `~/Desktop/synergy_python` and is not installed
system-wide, so it is importable only when the interpreter can find it. Run your
program from that directory, or set `PYTHONPATH`:

```bash
cd ~/Desktop/synergy_python
python3 my_program.py

# or, from anywhere
export PYTHONPATH=/home/slm-rp4/Desktop/synergy_python
```

See {doc}`advanced/python/setup`.

## Python: permission denied on `/dev/spidev6.0`

```bash
groups
ls -l /dev/spidev6.0 /dev/gpiochip0
```

Add the user to the group owning the device node, then log out and back in.

## C++: `undefined reference to 'Synergy::init()'`

You compiled against the header but did not compile the implementation. Both
`Synergy.cpp` and `RPi.cpp` must be in the source list:

```bash
LIB=~/Desktop/synergy_cpp/lib/synergy
g++ -std=c++17 -O2 -I $LIB $LIB/Synergy.cpp $LIB/RPi.cpp myprogram.cpp -o myprogram
```

## C++: `Synergy.h: No such file or directory`

The `-I` path does not point at the directory containing the headers.

See {doc}`advanced/cpp/building`.

---

## A service does not start at boot

```bash
systemctl status my-controller
journalctl -u my-controller -n 100
```

Common causes:

- Forgot `sudo systemctl enable my-controller`.
- Forgot `sudo systemctl daemon-reload` after editing the unit file.
- Relative paths in the program with no `WorkingDirectory=` set.
- The program starts before the hardware is ready. Make it retry.
- Python output buffering hiding the real error. Add
  `Environment=PYTHONUNBUFFERED=1`.

See {doc}`advanced/running-on-boot`.

---

## Reading the runtime logs

The **PLC Logs** tab in OpenPLC Editor is the normal way to read the runtime's
output, and it needs no terminal access.

If the editor cannot connect, or you need more history than the tab shows, read
the logs from a terminal instead. The runtime runs as a container, so its
output comes from `docker logs`:

```bash
# The last 200 lines
sudo docker logs --tail 200 openplc-runtime

# Follow live
sudo docker logs -f openplc-runtime

# Only the last ten minutes
sudo docker logs --since 10m openplc-runtime
```

The runtime writes its own timestamps, so there is no need to add `-t`.

If the runtime is not starting at all, the component that supervises it keeps a
separate log, which will say why:

```bash
sudo docker logs --tail 50 openplc-bootloader
```

---

(troubleshooting-get-help)=
## If the problem persists

Gather this before asking for help:

- What the LEDs are doing.
- The **PLC Logs** output, especially the sign-on lines.
- `sudo docker logs --tail 200 openplc-runtime` from the device.
- `sudo docker logs --tail 50 openplc-bootloader`, if the runtime is not
  starting.
- Your backplane arrangement, slot by slot.
- What changed immediately before the problem started.

The last item resolves more cases than any other.

Contact [Synergy Logic](https://www.synergy-logic.com) for hardware, and the
[OpenPLC documentation](https://edge.autonomylogic.com/docs) for editor and
runtime questions.
