Skip to content

Technik Blog

IoT, Smart Home & Mikrocontroller einfach erklärt

Menu
  • Smarthome
  • Gartenautomation
  • Mikrocontroller
    • Arduino
    • ESP32 & Co.
    • Calliope Mini
    • Raspberry Pi & Pico
  • Solo Mining
  • Über mich
Menu

Mehrere Shellys mit Arduino steuern – lokal und ohne Cloud

Veröffentlicht am 22. Juli 202619. Juli 2026 von Stefan Draeger

Braucht man für jeden Shelly eigentlich einen eigenen Arduino oder ESP32?

Diese Frage kam unter meinem YouTube-Video zur lokalen Shelly-Steuerung mit Arduino UNO und ESP32-Alternative auf. Und die kurze Antwort lautet: Nein.

Ein einzelner Arduino UNO kann mehrere Shelly-Geräte im lokalen Netzwerk steuern. Dafür muss nicht vor jedem Shelly ein eigener Mikrocontroller sitzen. Entscheidend ist nur, dass jeder Shelly eine eigene IP-Adresse besitzt und der Arduino diese Geräte im Netzwerk erreichen kann.

Genau darum geht es in diesem Beitrag.

Mehrere Shellys mit 1 Arduino steuern – lokal & ohne Cloud!
Dieses Video auf YouTube ansehen.

Im ersten Projekt habe ich gezeigt, wie ein Arduino UNO mit Ethernet Shield einen Shelly Plug S über eine kleine Webseite lokal schalten kann „Shelly Plug S Gen3 mit Arduino UNO und Ethernet Shield steuern“. In diesem Beitrag erweitern wir die Idee: Statt nur einen Shelly anzusteuern, wird der Arduino-Webserver um weitere Schaltflächen ergänzt.

Hinweis: Im ersten Projekt kam ein Arduino UNO R3 mit Ethernet Shield zum Einsatz. Für die erweiterte Variante in diesem Beitrag verwende ich den Arduino UNO R4 WiFi aus dem Arduino Plug and Make Kit. Das Grundprinzip bleibt gleich: Der Arduino sendet lokale HTTP-Requests an die Shelly-Geräte.

So wird aus dem Arduino UNO eine kleine lokale Steuerzentrale für mehrere Shelly-Geräte – direkt im Heimnetz und ohne Cloud.

Inhaltsverzeichnis

  • Die Grundidee: Ein Arduino-Webserver für mehrere Shellys
  • Sicherheitshinweis zu Shelly-Geräten an 230 V
  • Was wird benötigt?
  • Beispielaufbau mit drei Shelly-Geräten
  • Erweiterung mit dem Arduino Plug and Make Kit
    • Bedienkonzept: Shelly auswählen, schalten und Status anzeigen
  • Den Sketch für mehrere Shellys erweitern
    • Webseite für mehrere Shellys
    • Rotary Encoder und Button Modulino
    • Eine zentrale Schaltfunktion
  • Häufige Fragen
  • Fazit: Shelly lokal steuern – ganz ohne Cloud
    • Ausblick: Rückmeldung vom Shelly auswerten

Die Grundidee: Ein Arduino-Webserver für mehrere Shellys

Die Erweiterung von einem auf mehrere Shellys ist einfacher, als es zunächst klingt. Der Arduino UNO muss nicht „wissen“, welches Gerät ein Shelly Plug S, ein Shelly 1 Mini oder ein anderer Shelly Aktor ist. Für den Arduino zählt im ersten Schritt nur eines: Welche IP-Adresse soll aufgerufen werden und welcher Schaltbefehl soll gesendet werden?

Im ersten Projekt gab es nur einen Shelly und damit auch nur eine Zieladresse. Klickt man auf der Webseite des Arduino-Webservers auf einen Button, sendet der Arduino einen HTTP-Request an genau diesen Shelly.

Bei mehreren Shellys wird dieses Prinzip einfach erweitert.

Jeder Shelly bekommt eine eigene IP-Adresse im lokalen Netzwerk. Die Webseite auf dem Arduino bekommt mehrere Schaltflächen. Jede Schaltfläche steht dann für ein bestimmtes Gerät oder eine bestimmte Aktion.

Ein einfaches Beispiel:

Button auf der WebseiteZielgerätAktion
Shelly 1 EINShelly 1einschalten
Shelly 1 AUSShelly 1ausschalten
Shelly 2 EINShelly 2einschalten
Shelly 2 AUSShelly 2ausschalten
Shelly 3 EINShelly 3einschalten
Shelly 3 AUSShelly 3ausschalten

Der Browser ruft dabei weiterhin nur den Arduino-Webserver auf. Der eigentliche Schaltbefehl wird wieder vom Arduino gesendet.

Das ist der entscheidende Punkt: Der Arduino wird dadurch nicht nur zu einer kleinen Webseite mit ein paar Links, sondern zu einer einfachen lokalen Steuerzentrale. Er nimmt den Klick entgegen, entscheidet anhand des aufgerufenen Pfads, welcher Shelly gemeint ist, und sendet dann den passenden HTTP-Befehl an die richtige IP-Adresse.

Der Ablauf bleibt also überschaubar:

Schaubild zeigt den Ablauf vom Browser über den Arduino-Webserver zum Shelly-Gerät
Der Browser ruft die Webseite des Arduino-Webservers auf. Der Arduino sendet anschließend den HTTP-Request an das Shelly-Gerät.

Damit lässt sich der Aufbau sehr einfach erweitern. Für einen weiteren Shelly wird keine zusätzliche Mikrocontroller-Hardware benötigt. Es werden lediglich eine weitere IP-Adresse im Sketch und weitere Buttons auf der Webseite ergänzt.

Sicherheitshinweis zu Shelly-Geräten an 230 V

Bevor wir uns den Beispielaufbau anschauen, ein wichtiger Hinweis: Nicht jedes Shelly-Gerät ist gleich zu behandeln.

Shelly Outdoor Plug S Gen3 & Shelly Plug S MTR Gen3
Shelly Outdoor Plug S Gen3 & Shelly Plug S MTR Gen3

Ein Shelly Plug S wird einfach wie ein Zwischenstecker in die Steckdose gesteckt. Für Testaufbauten ist das vergleichsweise einfach und sicher zu handhaben.

Anders sieht es bei Shelly-Modulen wie dem Shelly 1PM, Shelly Plus 1PM oder anderen Einbaumodulen aus. Diese werden direkt an 230 V Netzspannung angeschlossen. Arbeiten daran dürfen nur von Personen durchgeführt werden, die dafür fachlich qualifiziert sind.

Shelly 1PM Gen3 und Mini
Shelly 1PM Gen3 und Mini

Bei Arbeiten an elektrischen Anlagen gelten die 5 Sicherheitsregeln:

  1. Freischalten
  2. Gegen Wiedereinschalten sichern
  3. Spannungsfreiheit feststellen
  4. Erden und kurzschließen
  5. Benachbarte, unter Spannung stehende Teile abdecken oder abschranken

Besonders wichtig: Die Spannungsfreiheit muss fachgerecht geprüft werden. Dafür ist ein zweipoliger Spannungsprüfer zu verwenden, zum Beispiel ein Duspol. Ein einpoliger Phasenprüfer, oft auch „Lügenstift“ genannt, ist dafür nicht geeignet.

zwei poliger Spannungsprüfer (Duspol), Phasenprüfer und berührungsloser Spannungsprüfer
zwei poliger Spannungsprüfer (Duspol), Phasenprüfer und berührungsloser Spannungsprüfer

Was wird benötigt?

Für den Aufbau verwende ich:

  • Arduino UNO R4 WiFi*
  • Ethernet Shield W5100*
  • Arduino Plug and Make Kit*
  • Rotary Encoder Modulino
  • Button Modulino
  • RGB LED / Pixel Modulino
  • zwei Shelly Plug S*
  • einen Shelly 1PM* mit Lampe
  • lokales Netzwerk mit festen IP-Adressen

Hinweis von mir: Die mit einem Sternchen (*) markierten Links sind Affiliate-Links. Wenn du über diese Links einkaufst, erhalte ich eine kleine Provision, die dazu beiträgt, diesen Blog zu unterstützen. Der Preis für dich bleibt dabei unverändert. Vielen Dank für deine Unterstützung!

Arduino UNO R4 - Plug and Make Kit zum steuern mehrerer Shellys ohne Cloud

Hinweis: Wenn du das Arduino Plug and Make Kit verwendest, sind der Arduino UNO R4 WiFi sowie die benötigten Modulinos bereits im Set enthalten. Für diesen Aufbau musst du dann nicht jedes Modul einzeln kaufen. Zusätzlich wird nur noch ein passendes Ethernet Shield benötigt, wenn du die kabelgebundene LAN-Variante nachbauen möchtest.

Beispielaufbau mit drei Shelly-Geräten

Für meinen Testaufbau verwende ich insgesamt drei Shelly-Geräte, die alle über einen Arduino UNO im lokalen Netzwerk geschaltet werden:

  • zwei Shelly Plug S
  • einen Shelly 1PM mit angeschlossener Lampe

Die beiden Shelly Plug S eignen sich für so einen Test besonders gut, weil sie einfach in eine Steckdose gesteckt werden können. Zusätzlich besitzen sie eine LED-Beleuchtung, deren Farbe je nach Schaltzustand angepasst werden kann. In meinem Aufbau bedeutet:

Zwei Shelly Plug S in einer Mehrfachsteckdose und ein Shelly 1PM mit angeschlossener Lampe
Der Testaufbau zeigt zwei Shelly Plug S in einer Mehrfachsteckdose sowie einen Shelly 1PM mit angeschlossener Lampe.
SchaltzustandLED-Farbe am Shelly Plug S
Ausgang EINGrün
Ausgang AUSRot

Dadurch lässt sich im Video und im Testaufbau sehr schnell erkennen, ob der jeweilige Ausgang gerade eingeschaltet oder ausgeschaltet ist.

Beim Shelly 1PM ist zusätzlich eine Lampe angeschlossen. Hier erkennt man den Zustand direkt an der Lampe: Leuchtet sie, ist der Schaltausgang des Shelly 1PM aktiv. Leuchtet sie nicht, ist der Schaltausgang deaktiviert.

Wichtig: Der Shelly selbst bleibt dabei natürlich weiterhin mit Spannung versorgt und im Netzwerk erreichbar. Es wird nur der Ausgang beziehungsweise das Relais geschaltet.

Die Einrichtung der Shelly-Geräte selbst ist nicht Bestandteil dieses Beitrags. Die Shellys müssen bereits im lokalen Netzwerk eingerichtet und erreichbar sein. Dazu gehört auch, dass jedes Gerät eine eigene feste IP-Adresse oder eine feste DHCP-Zuweisung im Router bekommt.

Wie man einen Shelly grundsätzlich einrichtet, habe ich bereits ausführlich in meinem YouTube-Video gezeigt:

Shelly Plus 1 Mini - Schaltung mit einer Lampe aufbauen und Integration ins Heimnetzwerk
Dieses Video auf YouTube ansehen.

In diesem Beitrag konzentrieren wir uns daher auf die Erweiterung des Arduino-Webservers: Aus einer Steuerung für einen einzelnen Shelly wird eine kleine lokale Oberfläche für mehrere Shelly-Geräte.

Erweiterung mit dem Arduino Plug and Make Kit

Bisher wird die Steuerung der Shelly-Geräte über eine Webseite gelöst. Das funktioniert gut, ist aber noch recht browserlastig: Man ruft die Arduino-Webseite auf und klickt dort auf die jeweiligen Buttons.

Für eine erweiterte Steuerung bietet sich das Arduino Plug and Make Kit an. Dieses enthält mit dem Arduino UNO R4 WiFi und den sogenannten Modulinos bereits viele Bausteine, die man für eine einfache lokale Steuerung nutzen kann.

Der große Vorteil: Die Modulinos werden über Qwiic / I2C verbunden. Dadurch muss nicht jedes Modul einzeln mit vielen Jumper-Kabeln verdrahtet werden. Für ein Lern- oder Demo-Projekt ist das sehr angenehm, weil der Fokus stärker auf der eigentlichen Logik liegt.

Aufbau der Schaltung - Fang den Regentropfen am Arduino UNO R4 WiFi
Aufbau der Schaltung – Fang den Regentropfen am Arduino UNO R4 WiFi

Für dieses Projekt sind besonders drei Modulinos interessant:

ModulinoAufgabe im Projekt
Rotary Encoder ModulinoAuswahl des Shelly-Geräts
Button ModulinoEingabe zum Schalten der Shelly-Geräte
RGB LED ModulinoAnzeige des aktuellen Zustands oder einer Aktion
Rotary Encoder Modulino, Button Modulino und RGB LED Modulino für ein Arduino Shelly-Bedienpanel
Für die erweiterte Shelly-Steuerung werden ein Rotary Encoder Modulino, ein Button Modulino und ein RGB LED Modulino verwendet.

Zusätzlich wird für diesen Aufbau noch ein Ethernet Shield mit W5100-Chip* benötigt. Dieses sorgt dafür, dass der Arduino per LAN-Kabel mit dem lokalen Netzwerk verbunden wird. Solche W5100 Ethernet Shields sind häufig recht günstig erhältlich, zum Beispiel bei eBay bereits ab etwa 10 Euro zuzüglich Versandkosten.

Damit kombiniert der Aufbau zwei Dinge: Die einfache Bedienung über Modulinos und die stabile Netzwerkverbindung über Ethernet.

Das Plug and Make Kit ersetzt dabei nicht die Shelly-Geräte selbst. Die Shellys bleiben weiterhin im lokalen Netzwerk erreichbar und werden per HTTP-Request geschaltet. Das Kit erweitert nur die Art der Bedienung und Anzeige.

Das ist vielleicht keine klassische Smart-Home-Endlösung für den Alltag, aber als Lernprojekt ist es sehr spannend. Man sieht direkt, wie Eingabe, Netzwerkkommunikation und Ausgabe zusammenspielen. Genau dadurch eignet sich der Aufbau sehr gut, um lokale Smart-Home-Steuerung ohne Cloud verständlich zu erklären.

Bedienkonzept: Shelly auswählen, schalten und Status anzeigen

Damit die Steuerung mehrerer Shellys nicht unübersichtlich wird, soll nicht für jeden Shelly ein eigener Button auf dem Arduino-Aufbau verwendet werden. Stattdessen wird der gewünschte Shelly über den Rotary Encoder ausgewählt.

Das Bedienprinzip ist damit recht einfach:

  1. Mit dem Rotary Encoder wird der gewünschte Shelly ausgewählt.
  2. Das Button Modulino schaltet den ausgewählten Shelly.
  3. Das RGB LED Modulino zeigt eine Aktion oder den Zustand an.

Dadurch entsteht ein kleines lokales Bedienpanel. Der Arduino UNO R4 WiFi übernimmt dabei weiterhin die zentrale Logik. Er merkt sich, welcher Shelly aktuell ausgewählt ist, und sendet beim Tastendruck den passenden HTTP-Request an genau dieses Gerät.

Ein möglicher Ablauf sieht so aus:

Über den Rotary Encoder wird das gewünschte Shelly-Gerät ausgewählt. Anschließend kann es über den Arduino lokal im Netzwerk geschaltet werden.

Den Sketch für mehrere Shellys erweitern

Im ersten Projekt war im Sketch nur ein einzelner Shelly hinterlegt. Für einen einfachen Test reicht das völlig aus. Sobald aber mehrere Shelly-Geräte gesteuert werden sollen, muss der Sketch etwas flexibler aufgebaut werden.

Der wichtigste Unterschied ist: Die Shelly-IP-Adresse wird nicht mehr als einzelne feste Variable verwendet. Stattdessen wird eine kleine Liste mit mehreren Shelly-Geräten angelegt.

Im Sketch sieht das so aus:

struct ShellyGeraet {
  const char* name;
  IPAddress ip;
};

ShellyGeraet shellys[] = {
  { "Shelly 1", IPAddress(192, 168, 178, 199) },
  { "Shelly 2", IPAddress(192, 168, 178, 106) },
  { "Shelly 3", IPAddress(192, 168, 178, 163) },
};

Jeder Eintrag besteht aus einem Namen und der IP-Adresse des jeweiligen Shelly-Geräts. Genau hier müssen später die eigenen Geräte eingetragen werden.

Der Vorteil dieser Lösung: Der restliche Code muss nicht für jeden Shelly komplett neu geschrieben werden. Der Arduino kann einfach über den Index auf das gewünschte Gerät zugreifen.

Die Anzahl der Shellys wird automatisch aus der Liste berechnet:

const uint8_t ANZAHL_SHELLYS = sizeof(shellys) / sizeof(shellys[0]);

Dadurch weiß der Sketch, wie viele Shelly-Geräte konfiguriert wurden.

In diesem Aufbau ist die Anzahl zusätzlich auf maximal acht Shellys begrenzt. Der Grund dafür ist das Pixel-Modulino, denn dieses besitzt acht LEDs. Für jeden Shelly kann dadurch eine eigene LED-Position verwendet werden.

static_assert(sizeof(shellys) / sizeof(shellys[0]) <= 8,
              "Maximal 8 Shellys moeglich (8 LEDs am Pixel-Modulino)");

Damit ist der Sketch schon deutlich flexibler als die erste Version. Möchte man später einen weiteren Shelly ergänzen, muss lediglich ein weiterer Eintrag in der Liste hinzugefügt werden.

Zum Beispiel:

{ "Shelly 4", IPAddress(192, 168, 178, 164) },

Wichtig ist auch hier wieder: Die IP-Adressen müssen zum eigenen Netzwerk passen. Außerdem sollten die Shelly-Geräte feste IP-Adressen oder feste DHCP-Zuweisungen im Router bekommen, damit die Zuordnung dauerhaft funktioniert.

Webseite für mehrere Shellys

Neben der internen Liste wird auch die Webseite erweitert. Im ersten Projekt gab es nur Buttons für einen einzelnen Shelly. In dieser Version erzeugt der Arduino für jeden konfigurierten Shelly automatisch einen eigenen Block mit drei Schaltflächen:

  • EIN
  • AUS
  • 3s

Im Browser entstehen dadurch mehrere Shelly-Blöcke auf einer Seite. Jeder Block gehört zu einem bestimmten Shelly aus der Liste.

Die Aufrufe folgen dabei diesem Muster:

/shelly/<index>/an
/shelly/<index>/aus
/shelly/<index>/an3
Screenshot der Arduino-Webseite mit drei Steuerblöcken für mehrere Shelly-Geräte
Die modifizierte Arduino-Webseite zeigt für jedes Shelly-Gerät einen eigenen Steuerblock mit Schaltflächen für EIN, AUS und Timer.

Der Platzhalter <index> steht für die Position des Shellys in der Liste. Der erste Shelly hat den Index 0, der zweite Shelly den Index 1, der dritte Shelly den Index 2.

Ein Beispiel:

/shelly/0/an

schaltet den ersten Shelly ein.

/shelly/1/aus

schaltet den zweiten Shelly aus.

/shelly/2/an3

schaltet den dritten Shelly für drei Sekunden ein.

Der Arduino wertet diese Route im Sketch aus und entscheidet darüber, welches Gerät geschaltet werden soll. Gleichzeitig wird der per Webseite ausgewählte Shelly auch als aktiver Shelly übernommen. Dadurch passen Weboberfläche, Rotary Encoder und LED-Anzeige zusammen.

Rotary Encoder und Button Modulino

Die Weboberfläche ist aber nur eine Möglichkeit zur Bedienung. Zusätzlich kann der gewünschte Shelly über den Rotary Encoder Modulino ausgewählt werden.

Der Sketch merkt sich die aktuelle Auswahl in dieser Variable:

uint8_t aktiverShellyIndex = 0;

Dreht man am Rotary Encoder, wird dieser Index verändert. Dadurch wechselt die Auswahl zwischen den konfigurierten Shelly-Geräten.

Das Pixel-Modulino zeigt die aktuelle Auswahl an. Die LED an der passenden Position leuchtet gelb:

leds.set(aktiverShellyIndex, YELLOW, ledHelligkeit);

Das Button Modulino löst anschließend die eigentliche Aktion aus:

TasteIndexAktion
Taste A0ausgewählten Shelly einschalten
Taste B1ausgewählten Shelly ausschalten
Taste C2ausgewählten Shelly für 3 Sekunden einschalten

Damit kann der Shelly also nicht nur über die Webseite, sondern auch direkt über die angeschlossenen Modulinos gesteuert werden.

Das Bedienprinzip lautet damit:

  • Rotary Encoder drehen → Shelly auswählen
  • Button drücken → Aktion ausführen
  • Pixel-Modulino → aktuelle Auswahl anzeigen

Eine zentrale Schaltfunktion

Besonders wichtig ist, dass am Ende alle Wege dieselbe Schaltlogik verwenden. Es ist also egal, ob der Befehl über die Webseite oder über das Button Modulino ausgelöst wird.

Die eigentliche Schaltfunktion bekommt die Ziel-IP-Adresse als Parameter:

void schalteShelly(IPAddress ip, bool einschalten, unsigned int timerSekunden)

Dadurch kann dieselbe Funktion jeden Shelly aus der Liste schalten. Der HTTP-Aufruf bleibt dabei derselbe wie im ersten Projekt:

/relay/0?turn=on
/relay/0?turn=off
/relay/0?turn=on&timer=3

Der Unterschied ist nur, dass die Ziel-IP-Adresse nun flexibel aus der Shelly-Liste kommt.

Genau dadurch wird aus dem einfachen Sketch für einen Shelly eine erweiterbare lokale Steuerung für mehrere Shelly-Geräte.

Quellcode – mehrere Shellys via Arduino lokal steuern
/*
  arduinoR4WifiShelly.ino

  Arduino UNO R4 WiFi + Ethernet Shield (W5100) + Modulino Buttons +
  Modulino Knob (Rotary Encoder) + Modulino Pixels (8x RGB LED)
  -----------------------------------------------------------------------------------------------
  Portierung von "shellyWebserver.ino" (UNO R3) auf den UNO R4 WiFi.

  Der Arduino stellt weiterhin eine Weboberflaeche per Ethernet bereit
  (http://<arduinoIp>/). Dort gibt es fuer JEDEN konfigurierten Shelly einen
  eigenen Block mit drei Buttons (EIN / AUS / EIN fuer 3s), erreichbar unter:

    GET /shelly/<index>/an    -> Shelly <index> EIN
    GET /shelly/<index>/aus   -> Shelly <index> AUS
    GET /shelly/<index>/an3   -> Shelly <index> EIN fuer 3 Sekunden

  <index> ist die Position in der Liste "shellys" weiter unten (0, 1, 2, ...).
  Ein Klick auf einen dieser Buttons waehlt den betroffenen Shelly zugleich
  als "aktiven" Shelly aus (siehe naechster Absatz).

  Zusaetzlich koennen die drei physischen Taster des Button-Modulinos
  (I2C/Qwiic) dieselben Aktionen fuer den aktuell ausgewaehlten Shelly
  ausloesen:

    Taste 0 (A) -> ausgewaehlter Shelly EIN
    Taste 1 (B) -> ausgewaehlter Shelly AUS
    Taste 2 (C) -> ausgewaehlter Shelly EIN fuer 3 Sekunden

  Mit dem Modulino Knob (Rotary Encoder) wird ebenfalls ausgewaehlt, welcher
  von bis zu 8 Shellys gerade der "aktive" ist (Liste "shellys" weiter
  unten). Drehen wechselt die Auswahl (mit Wraparound), das Pixel-Modulino
  zeigt den ausgewaehlten Shelly als GELBE LED an der zugehoerigen Position
  an (LED-Index = Index des Eintrags in "shellys").

  Web-Buttons, physische Taster und Encoder-Auswahl rufen letztlich alle
  dieselbe zentrale Funktion schalteUndAktualisiere() auf, die den jeweiligen
  Shelly per HTTP-Client-Anfrage schaltet (klassische Gen1-Relay-API):

    http://<SHELLY_IP>/relay/0?turn=on
    http://<SHELLY_IP>/relay/0?turn=on&timer=3
    http://<SHELLY_IP>/relay/0?turn=off

  Alle Modulino-Module (Buttons, Knob, Pixels) haengen ueber Qwiic-Kabel in
  Reihe am selben I2C-Bus des Arduino und werden gemeinsam ueber
  Modulino.begin() initialisiert.

  Anpassen muss man nur den Block "Konfiguration" weiter unten - dort auch
  die Liste "shellys" um weitere Geraete ergaenzen (max. 8 Stueck, da das
  Pixel-Modulino 8 LEDs hat).
*/

#include <SPI.h>
#include <Ethernet.h>
#include <Wire.h>
#include <Arduino_Modulino.h>
#include <stdio.h>

// ---------- Konfiguration ----------

// MAC-Adresse des Ethernet Shields (frei waehlbar, muss im Netz eindeutig sein)
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

// Feste IP-Adresse, unter der der Arduino-Webserver erreichbar sein soll
IPAddress arduinoIp(192, 168, 178, 50);

// Ein Eintrag pro steuerbarem Shelly. Die Reihenfolge legt fest, an welcher
// LED-Position (0..7) des Pixel-Modulinos der Eintrag ausgewaehlt angezeigt
// wird, wenn man ihn per Rotary Encoder anwaehlt.
struct ShellyGeraet {
  const char* name;
  IPAddress ip;
};

ShellyGeraet shellys[] = {
  { "Shelly 1", IPAddress(192, 168, 178, 199) },
  { "Shelly 2", IPAddress(192, 168, 178, 106) },
  { "Shelly 3", IPAddress(192, 168, 178, 163) },
};
const uint8_t ANZAHL_SHELLYS = sizeof(shellys) / sizeof(shellys[0]);
static_assert(sizeof(shellys) / sizeof(shellys[0]) <= 8,
              "Maximal 8 Shellys moeglich (8 LEDs am Pixel-Modulino)");

const uint16_t shellyPort = 80;

// Helligkeit der Auswahl-LED (0-100)
const uint8_t ledHelligkeit = 25;

// ------------------------------------

EthernetServer server(80);

ModulinoButtons buttons;
ModulinoKnob knob;
ModulinoPixels leds;

// Puffer fuer die erste Zeile der eingehenden HTTP-Anfrage, z.B.
// "GET /heizung/an HTTP/1.1"
const size_t REQUEST_BUFFER_SIZE = 64;
char requestBuffer[REQUEST_BUFFER_SIZE];

// Index des per Rotary Encoder ausgewaehlten Shellys (Index in "shellys").
uint8_t aktiverShellyIndex = 0;

void setup() {
  Serial.begin(9600);

  Modulino.begin();
  buttons.begin();
  knob.begin();
  leds.begin();
  aktualisiereAuswahlLed();

  Ethernet.begin(mac, arduinoIp);
  delay(1000); // dem Shield etwas Zeit zur Initialisierung geben

  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println(F("Kein Ethernet Shield gefunden."));
    while (true) {
      delay(1);
    }
  }

  if (Ethernet.linkStatus() == LinkOFF) {
    Serial.println(F("Achtung: Ethernet-Kabel scheint nicht verbunden zu sein."));
  }

  server.begin();
  Serial.print(F("Webserver gestartet: http://"));
  Serial.println(Ethernet.localIP());
}

void loop() {
  behandleTaster();
  behandleEncoder();

  EthernetClient client = server.available();
  if (client) {
    liesAnfrageZeile(client, requestBuffer, REQUEST_BUFFER_SIZE);

    if (behandleShellyRoute(requestBuffer)) {
      sendeRedirect(client);
    } else {
      sendeStartseite(client);
    }

    delay(1);
    client.stop();
  }
}

// Fragt die drei Taster des Button-Modulinos ab und loest bei einem
// Tastendruck dieselbe Aktion aus wie der entsprechende Web-Button.
void behandleTaster() {
  if (!buttons.update()) {
    return;
  }

  if (buttons.isPressed(0)) {
    Serial.println(F("Taste 0 (A) gedrueckt: Shelly EIN"));
    schalteUndAktualisiere(true, 0);
  } else if (buttons.isPressed(1)) {
    Serial.println(F("Taste 1 (B) gedrueckt: Shelly AUS"));
    schalteUndAktualisiere(false, 0);
  } else if (buttons.isPressed(2)) {
    Serial.println(F("Taste 2 (C) gedrueckt: Shelly EIN fuer 3 Sekunden"));
    schalteUndAktualisiere(true, 3);
  }
}

// Liest die Drehrichtung des Rotary-Encoder-Modulinos aus und wechselt
// damit den aktuell ausgewaehlten Shelly (mit Wraparound ueber die Liste).
void behandleEncoder() {
  int8_t richtung = knob.getDirection();
  if (richtung == 0) {
    return;
  }

  aktiverShellyIndex = (aktiverShellyIndex + ANZAHL_SHELLYS + richtung) % ANZAHL_SHELLYS;
  aktualisiereAuswahlLed();

  Serial.print(F("Ausgewaehlter Shelly: "));
  Serial.print(shellys[aktiverShellyIndex].name);
  Serial.print(F(" ("));
  Serial.print(shellys[aktiverShellyIndex].ip);
  Serial.println(F(")"));
}

// Schaltet den aktuell ausgewaehlten Shelly (per Encoder oder zuletzt per
// Web-Button gewaehlt).
void schalteUndAktualisiere(bool einschalten, unsigned int timerSekunden) {
  schalteShelly(shellys[aktiverShellyIndex].ip, einschalten, timerSekunden);
}

// Erkennt Web-Anfragen der Form "GET /shelly/<index>/<aktion> HTTP/1.1"
// (z.B. "/shelly/1/an3") und schaltet damit gezielt einen der konfigurierten
// Shellys. Waehlt den betroffenen Shelly dabei zugleich als "aktiv" aus, so
// dass Auswahl-LED und physische Taster ab sofort auf ihn wirken.
// Gibt true zurueck, wenn eine gueltige Schaltanfrage erkannt wurde.
bool behandleShellyRoute(const char *zeile) {
  if (strncmp(zeile, "GET /shelly/", 12) != 0) {
    return false;
  }

  int index = -1;
  char aktion[8] = "";
  if (sscanf(zeile + 12, "%d/%7s", &index, aktion) != 2) {
    return false;
  }
  if (index < 0 || index >= ANZAHL_SHELLYS) {
    return false;
  }

  aktiverShellyIndex = (uint8_t)index;
  aktualisiereAuswahlLed();

  if (strcmp(aktion, "an3") == 0) {
    schalteUndAktualisiere(true, 3);
  } else if (strcmp(aktion, "an") == 0) {
    schalteUndAktualisiere(true, 0);
  } else if (strcmp(aktion, "aus") == 0) {
    schalteUndAktualisiere(false, 0);
  } else {
    return false;
  }

  return true;
}

// Zeigt auf dem Pixel-Modulino (8 LEDs), welcher Shelly aktuell ausgewaehlt
// ist: Die LED an der Position von aktiverShellyIndex leuchtet GELB, alle
// anderen LEDs sind aus.
void aktualisiereAuswahlLed() {
  leds.clear();
  leds.set(aktiverShellyIndex, YELLOW, ledHelligkeit);
  leds.show();
}

// Liest die erste Zeile der HTTP-Anfrage (z.B. "GET /shelly/an HTTP/1.1")
// in buffer und verwirft anschliessend den Rest der Header, damit der
// Client sauber geschlossen werden kann.
void liesAnfrageZeile(EthernetClient &client, char *buffer, size_t bufferSize) {
  size_t index = 0;
  buffer[0] = '\0';

  unsigned long start = millis();
  while (client.connected() && millis() - start < 2000) {
    if (client.available()) {
      char c = client.read();
      if (c == '\n') {
        break;
      }
      if (c != '\r' && index < bufferSize - 1) {
        buffer[index++] = c;
      }
    }
  }
  buffer[index] = '\0';

  // restliche Header-Zeilen einfach verwerfen
  start = millis();
  while (client.connected() && millis() - start < 200) {
    while (client.available()) {
      client.read();
      start = millis();
    }
  }
}

// Schickt als HTTP-Client eine Anfrage an den Shelly mit der angegebenen IP,
// um ihn ein- oder auszuschalten. timerSekunden > 0 schaltet den Shelly nach
// der angegebenen Zeit automatisch wieder aus (nur bei einschalten == true).
void schalteShelly(IPAddress ip, bool einschalten, unsigned int timerSekunden) {
  EthernetClient shellyClient;

  if (!shellyClient.connect(ip, shellyPort)) {
    Serial.println(F("Verbindung zum Shelly fehlgeschlagen."));
    return;
  }

  shellyClient.print(F("GET /relay/0?turn="));
  shellyClient.print(einschalten ? F("on") : F("off"));
  if (einschalten && timerSekunden > 0) {
    shellyClient.print(F("&timer="));
    shellyClient.print(timerSekunden);
  }
  shellyClient.println(F(" HTTP/1.1"));

  shellyClient.print(F("Host: "));
  shellyClient.println(ip);
  shellyClient.println(F("Connection: close"));
  shellyClient.println();

  // Antwort des Shelly abwarten und verwerfen, bevor die Verbindung
  // geschlossen wird
  unsigned long start = millis();
  while (shellyClient.connected() && millis() - start < 2000) {
    while (shellyClient.available()) {
      shellyClient.read();
      start = millis();
    }
  }
  shellyClient.stop();

  Serial.print(F("Shelly geschaltet: "));
  if (einschalten && timerSekunden > 0) {
    Serial.print(F("AN (Timer "));
    Serial.print(timerSekunden);
    Serial.println(F("s)"));
  } else {
    Serial.println(einschalten ? F("AN") : F("AUS"));
  }
}

// Leitet den Browser nach einer Aktion zurueck auf die Startseite,
// damit ein Neuladen der Seite die Aktion nicht wiederholt.
void sendeRedirect(EthernetClient &client) {
  client.println(F("HTTP/1.1 303 See Other"));
  client.println(F("Location: /"));
  client.println(F("Connection: close"));
  client.println();
}

// Liefert die Startseite mit den Schalt-Buttons aus.
void sendeStartseite(EthernetClient &client) {
  client.println(F("HTTP/1.1 200 OK"));
  client.println(F("Content-Type: text/html; charset=UTF-8"));
  client.println(F("Connection: close"));
  client.println();

  client.println(F("<!DOCTYPE html><html lang=\"de\"><head>"));
  client.println(F("<meta charset=\"UTF-8\">"));
  client.println(F("<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">"));
  client.println(F("<title>Shelly Control</title>"));
  client.println(F("<style>"));
  client.println(F("*{box-sizing:border-box;}"));
  client.println(F("body{margin:0;min-height:100vh;display:flex;align-items:center;"));
  client.println(F("justify-content:center;background:radial-gradient(circle at top,#1b2735,#090a0f);"));
  client.println(F("font-family:'Segoe UI',Roboto,Consolas,monospace;color:#e6edf3;}"));
  client.println(F(".card{background:#111820;border:1px solid #1f2937;border-radius:16px;"));
  client.println(F("padding:36px 40px;box-shadow:0 0 40px rgba(61,220,132,.08),0 8px 24px rgba(0,0,0,.6);"));
  client.println(F("max-width:460px;width:90%;text-align:center;}"));
  client.println(F(".badge{display:inline-flex;align-items:center;gap:6px;background:#0d2818;"));
  client.println(F("color:#3ddc84;border:1px solid #1f6b45;padding:4px 12px;border-radius:20px;"));
  client.println(F("font-size:11px;letter-spacing:1px;text-transform:uppercase;margin-bottom:18px;}"));
  client.println(F(".dot{width:8px;height:8px;border-radius:50%;background:#3ddc84;"));
  client.println(F("box-shadow:0 0 8px #3ddc84;}"));
  client.println(F("h1{font-size:22px;letter-spacing:2px;margin:0 0 4px;text-transform:uppercase;}"));
  client.println(F("p.sub{color:#6b7684;font-size:13px;margin:0 0 28px;}"));
  client.println(F(".devices{display:flex;flex-direction:column;gap:16px;}"));
  client.println(F(".device{border:1px solid #1f2937;border-radius:12px;padding:14px 16px;"));
  client.println(F("background:#0d131a;text-align:left;}"));
  client.println(F(".device.aktiv{border-color:#d4b106;box-shadow:0 0 0 1px #d4b106 inset;}"));
  client.println(F(".device-name{font-size:14px;font-weight:600;letter-spacing:.3px;margin-bottom:10px;}"));
  client.println(F(".device-ip{color:#6b7684;font-weight:400;font-size:12px;}"));
  client.println(F(".device-buttons{display:flex;gap:8px;}"));
  client.println(F(".device-buttons a.btn{flex:1;display:flex;align-items:center;justify-content:center;"));
  client.println(F("gap:6px;padding:10px 4px;border-radius:8px;text-decoration:none;font-size:13px;"));
  client.println(F("font-weight:600;letter-spacing:.3px;color:#fff;border:1px solid rgba(255,255,255,.08);}"));
  client.println(F(".an{background:linear-gradient(135deg,#1f9d55,#0c6b34);"));
  client.println(F("box-shadow:0 4px 14px rgba(31,157,85,.35);}"));
  client.println(F(".aus{background:linear-gradient(135deg,#d64545,#8f1f1f);"));
  client.println(F("box-shadow:0 4px 14px rgba(214,69,69,.35);}"));
  client.println(F(".timer{background:linear-gradient(135deg,#2f7fd6,#14417d);"));
  client.println(F("box-shadow:0 4px 14px rgba(47,127,214,.35);}"));
  client.println(F("</style></head><body>"));

  client.println(F("<div class=\"card\">"));
  client.println(F("<div class=\"badge\"><span class=\"dot\"></span>Gateway Online</div>"));
  client.println(F("<h1>Shelly Control</h1>"));
  client.println(F("<p class=\"sub\">Arduino UNO R4 WiFi &middot; Ethernet Relay Gateway</p>"));

  client.println(F("<div class=\"devices\">"));
  for (uint8_t i = 0; i < ANZAHL_SHELLYS; i++) {
    client.print(F("<div class=\"device"));
    if (i == aktiverShellyIndex) {
      client.print(F(" aktiv"));
    }
    client.println(F("\">"));

    client.print(F("<div class=\"device-name\">"));
    client.print(shellys[i].name);
    client.print(F(" <span class=\"device-ip\">("));
    client.print(shellys[i].ip);
    client.println(F(")</span></div>"));

    client.println(F("<div class=\"device-buttons\">"));

    client.print(F("<a class=\"btn an\" href=\"/shelly/"));
    client.print(i);
    client.println(F("/an\">&#9889; EIN</a>"));

    client.print(F("<a class=\"btn aus\" href=\"/shelly/"));
    client.print(i);
    client.println(F("/aus\">&#9209; AUS</a>"));

    client.print(F("<a class=\"btn timer\" href=\"/shelly/"));
    client.print(i);
    client.println(F("/an3\">&#9201; 3s</a>"));

    client.println(F("</div>")); // device-buttons
    client.println(F("</div>")); // device
  }
  client.println(F("</div>")); // devices

  client.println(F("</div>")); // card

  client.println(F("</body></html>"));
}

Häufige Fragen

Braucht jeder Shelly einen eigenen Arduino?
Nein. Ein Arduino kann mehrere Shelly-Geräte lokal steuern, solange jedes Gerät eine eigene IP-Adresse besitzt.

Funktioniert das ohne Shelly Cloud?
Ja. Die Steuerung erfolgt lokal im Netzwerk per HTTP-Request.

Warum braucht jeder Shelly eine feste IP-Adresse?
Der Arduino sendet den Schaltbefehl direkt an die eingetragene IP-Adresse. Ändert sich diese, erreicht der Befehl das Gerät nicht mehr.

Kann ich mehr als drei Shellys steuern?
Ja. Im gezeigten Aufbau ist die Anzahl durch das Pixel-Modulino sinnvoll auf acht begrenzt, weil dieses acht LEDs besitzt.

Wird der echte Zustand des Shelly abgefragt?
In diesem Sketch steht die Auswahl und das Schalten im Vordergrund. Eine echte Statusabfrage wäre eine sinnvolle Erweiterung.

Ist das eine fertige Smart-Home-Lösung?
Eher ein Lern- und Demonstrationsprojekt. Für produktive Installationen können Home Assistant, Node-RED oder feste Bedienpanels sinnvoller sein.

Fazit: Shelly lokal steuern – ganz ohne Cloud

Das Projekt zeigt sehr schön, dass Shelly-Geräte nicht zwingend über eine Cloud oder App gesteuert werden müssen. Wenn die Geräte im lokalen Netzwerk erreichbar sind, kann ein Arduino die Schaltbefehle direkt per HTTP-Request senden.

Damit bleibt die Steuerung im eigenen Netzwerk und ist unabhängiger von externen Diensten. Gerade für einfache Automationen, Testaufbauten oder Lernprojekte ist das ein großer Vorteil.

Aus dem Arduino wird so eine kleine lokale Steuerzentrale, die mehrere Shellys ansprechen kann – nachvollziehbar, erweiterbar und ohne Cloud-Zwang.

Ausblick: Rückmeldung vom Shelly auswerten

Bisher sendet der Arduino den Schaltbefehl einfach an den jeweiligen Shelly und geht davon aus, dass dieser Befehl auch angekommen ist. Für einen ersten Testaufbau reicht das aus, für eine saubere Steuerung fehlt damit aber noch eine wichtige Prüfung.

Denn aktuell wissen wir nur:

Der Arduino hat einen HTTP-Request gesendet.

Was wir noch nicht sicher wissen:

Hat der Shelly den Befehl wirklich angenommen?
Ist der Ausgang danach tatsächlich eingeschaltet oder ausgeschaltet?
War das Gerät überhaupt erreichbar?

Genau diese Rückmeldung ist der nächste sinnvolle Schritt. Im nächsten Beitrag soll der Arduino daher nicht nur Befehle senden, sondern auch die Antwort des Shelly auswerten beziehungsweise den aktuellen Zustand gezielt abfragen.

Damit wird aus dem einfachen „Befehl senden und hoffen“ eine deutlich zuverlässigere lokale Steuerung. Besonders spannend wird das in Kombination mit den Modulinos: Das RGB LED Modulino könnte dann nicht nur eine Aktion anzeigen, sondern den echten Zustand des Shelly visualisieren.

Zum Beispiel:

ZustandAnzeige
Shelly Ausgang EINGrün
Shelly Ausgang AUSRot
Shelly nicht erreichbarBlau oder Gelb
Fehler beim SchaltenBlinksignal

Damit wäre die lokale Shelly-Steuerung nicht nur bedienbar, sondern auch überprüfbar. Genau darum geht es dann im nächsten Schritt: Shelly-Status mit dem Arduino abfragen und auswerten.

Letzte Aktualisierung am: 19. Juli 2026

Foto von Stefan Draeger
Über den Autor

Stefan Draeger — Entwickler & Tech-Blogger

Ich zeige praxisnah, wie du Projekte mit Arduino, ESP32 und Smarthome-Komponenten umsetzt – Schritt für Schritt, mit Code und Schaltplänen.

Mehr Artikel von Stefan →

Schreibe einen Kommentar Antwort abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Kategorien

Kontakt & Hilfe

Fragen oder Feedback?

Du hast eine Idee, brauchst Hilfe oder möchtest Feedback loswerden? Erstelle einfach ein Support-Ticket.

Support-Ticket erstellen
Keine Beiträge verpassen

Newsletter abonnieren

Erhalte regelmäßig Updates zu neuen Projekten, Tutorials und Tipps rund um Arduino, ESP32, Smarthome und IoT direkt in dein Postfach.

Newsletter abonnieren
Freiwillige Unterstützung

Unterstütze meinen Blog

Wenn dir meine Inhalte gefallen, freue ich mich über deine Unterstützung auf Tipeee. So hilfst du dabei, den Blog am Leben zu halten und neue Beiträge zu ermöglichen.

Auf Tipeee unterstützen

Vielen Dank für deinen Support!
– Stefan Draeger

Kostenlose Online-Werkzeuge

Tools

Praktische Generatoren, Rechner und Hilfsmittel für deine Elektronik- und Entwicklungsprojekte.

Generatoren & Konverter

  • QR-Code Generator
  • Passwort Generator
  • PNG zu WebP konvertieren
  • Bilder online komprimieren
  • Code online formatieren

Rechner & Tabellen

  • Unix-Zeitstempel-Rechner
  • ASCII-Tabelle
  • Spannung, Strom, Widerstand und Leistung berechnen
  • Widerstandsrechner

LED-Matrix

  • 8×8 LED Matrix Tool
  • 8×16 LED Matrix Modul von Keyestudio
  • 16×16 LED Matrix Generator
Kontakt

Anschrift & Kontaktdaten

Stefan Draeger

Königsberger Str. 13
38364 Schöningen

Telefon 01556 5432686

E-Mail info@draeger-it.blog

Blogverzeichnis Bloggerei.de TopBlogs.de das Original - Blogverzeichnis | Blog Top Liste Blogverzeichnis trusted-blogs.com
Social Media

Folge mir auf

Weitere Projekte, Videos und Neuigkeiten findest du auch auf meinen Social-Media-Kanälen.

  • f Facebook
  • in LinkedIn
  • ▶ YouTube
  • ♪ TikTok
  • P Pinterest
  • ◎ Instagram
Rechtliche Informationen

Rechtliches

  • Impressum
  • Datenschutzerklärung
  • Disclaimer
  • Cookie-Richtlinie (EU)
©2026 Technik Blog | Built using WordPress and Responsive Blogily theme by Superb
Cookie-Zustimmung verwalten
Wir verwenden Technologien wie Cookies, um Geräteinformationen zu speichern und/oder darauf zuzugreifen. Wir tun dies, um das Surferlebnis zu verbessern und um personalisierte Werbung anzuzeigen. Wenn Sie diesen Technologien zustimmen, können wir Daten wie das Surfverhalten oder eindeutige IDs auf dieser Website verarbeiten. Wenn Sie Ihre Zustimmung nicht erteilen oder zurückziehen, können bestimmte Funktionen beeinträchtigt werden.
Funktional Immer aktiv
Die technische Speicherung oder der Zugang ist unbedingt erforderlich für den rechtmäßigen Zweck, die Nutzung eines bestimmten Dienstes zu ermöglichen, der vom Teilnehmer oder Nutzer ausdrücklich gewünscht wird, oder für den alleinigen Zweck, die Übertragung einer Nachricht über ein elektronisches Kommunikationsnetz durchzuführen.
Vorlieben
Die technische Speicherung oder der Zugriff ist für den rechtmäßigen Zweck der Speicherung von Präferenzen erforderlich, die nicht vom Abonnenten oder Benutzer angefordert wurden.
Statistiken
Die technische Speicherung oder der Zugriff, der ausschließlich zu statistischen Zwecken erfolgt. Die technische Speicherung oder der Zugriff, der ausschließlich zu anonymen statistischen Zwecken verwendet wird. Ohne eine Vorladung, die freiwillige Zustimmung deines Internetdienstanbieters oder zusätzliche Aufzeichnungen von Dritten können die zu diesem Zweck gespeicherten oder abgerufenen Informationen allein in der Regel nicht dazu verwendet werden, dich zu identifizieren.
Marketing
Die technische Speicherung oder der Zugriff ist erforderlich, um Nutzerprofile zu erstellen, um Werbung zu versenden oder um den Nutzer auf einer Website oder über mehrere Websites hinweg zu ähnlichen Marketingzwecken zu verfolgen.
  • Optionen verwalten
  • Dienste verwalten
  • Verwalten von {vendor_count}-Lieferanten
  • Lese mehr über diese Zwecke
Einstellungen anzeigen
  • {title}
  • {title}
  • {title}