Jump to: navigation, search
DFF4.1 1.0.png
Developer Alexander Christian
Status Version 1.0 finished
Microcontroller/Board M0dularisM+
KNX connectivity Siemens BCU, Eugen's µBCU


The purpose of this actuator is, to control Roto Rototronic skylight with shutters and/or sun blinds. It has 4 "channels". Each channel internally uses two relays: One for the open-action, and one for the close-action.

Of course you can also control your Roto Rototronic skylight with help of a standard switch-actuator. But then you don't have the possibility to "open the window 75%" or "close the shutter 80%". The firmware for DFF4.1 provides all this neat feature that you know from typical shutter/blinds actuators.


This actuator consists of the following REG parts:

I2C address on application board:

A0: 1
A1: 0 
A2: 0

I2C address on frontend board:

A0: 0
A1: 0
A2: 0


User Documentation

Check here for the DFF4.1 User Manual

Developer Documentation

Firmware sourcecode is on github: https://github.com/KONNEKTING/DFF4.1 Follow this guide for updating the firmare from source-code: KONNEKTING USB Firmware Update

Get a DIY kit

You are interested in this actuator? Great! This device is available as a DIY kit.

The kit includes all required parts with pre-soldered SMD parts and pre-flashed controller board.

You only need:

  • soldering iron and solder
  • a screw driver
  • a sharp knife with a fine tip
  • a working KNX installation where you can connect the device to
  • ~2-3hrs to build it according to the build instructions linked on this page
  • a computer: Windows, Linux or MAC that is able to run KONNEKTING Suite plus a KNX IP Interface or an KNX IP Router.
  • for updating the firmware: A Mini USB cable (Not Micro-USB like on your smartphone. Mini USB is a big thicker as Micro USB)

Please write a mail to info@konnekting.de and refer to this page.

Build It

This actuator uses three PCBs, a bunch of SMD components as well as a lot of mechanical parts. As most users are not able to solder SMD, the PCBs are pre-soldered with all SMD components. Due to the big amount of parts, this actuator comes as a DIY kit: You have to solder non-SMD parts yourself and build up the device and finally flash the firmware with help of a USB connection.

Check here for the DFF4.1 Building Instructions


If relays do not work as expected, you can run this test-sketch to be able to switch relais by serial console. Commands follow this syntax (\n = LF/LineFeed/NewLine):


f.i. "a0" disables relay A, "f1" enables relay F On startup all relays are disabled. It is recommended to use this test without attached window/shutter, but with am multimeter/continuity-tester to be able to test relay by relay.

#include <Wire.h>
#include <Adafruit_MCP23017.h>

Adafruit_MCP23017 mcp;
#define LED A5
#define EN A0

// MCP23017 Output register<->pin map
#define GPA0 0 // E 1
#define GPA1 1 // E 0 
#define GPA2 2
#define GPA3 3
#define GPA4 4
#define GPA5 5
#define GPA6 6
#define GPA7 7
#define GPB0 8
#define GPB1 9
#define GPB2 10 // B 0
#define GPB3 11 // B 1
#define GPB4 12 // C 0
#define GPB5 13 // C 1
#define GPB6 14 // D 0
#define GPB7 15 // D 1

#define A_SET   GPB1
#define A_RESET GPB0

#define B_SET   GPB3
#define B_RESET GPB2

#define C_SET   GPB5
#define C_RESET GPB4

#define D_SET   GPB7
#define D_RESET GPB6

#define E_SET   GPA0
#define E_RESET GPA1

#define F_SET   GPA2
#define F_RESET GPA3

#define G_SET   GPA4
#define G_RESET GPA5

#define H_SET   GPA6
#define H_RESET GPA7

boolean state[16];

void setup() {

  while (!SerialUSB) {

  SerialUSB.println("SetupMCP Relais Test ...");

  pinMode(LED, OUTPUT);
  pinMode(EN, OUTPUT);
  digitalWrite(LED, HIGH);
  digitalWrite(EN, HIGH);


  for (int i = 0; i < 16; i++) {
    mcp.pinMode(i, OUTPUT);
    setMCP(i, false);

//#define D 100

  SerialUSB.println("... done");
  digitalWrite(LED, LOW);

  // initial delay
  SerialUSB.println("Initial delay done.");
  SerialUSB.println("Waiting for command...[a-h0-1]");

#define WAIT_TIME 84

#define SET_TIME 16

String cmd;

char c[4];

void loop() {

  cmd = SerialUSB.readStringUntil('\n');

  if (cmd.length() == 2) {

    SerialUSB.print("Command: ");

    if (cmd == "a1") {
    } else if (cmd == "a0") {

    else if (cmd == "b1") {
    } else if (cmd == "b0") {

    else if (cmd == "c1") {
    } else if (cmd == "c0") {

    else if (cmd == "d1") {
    } else if (cmd == "d0") {

    else if (cmd == "e1") {
    } else if (cmd == "e0") {

    else if (cmd == "f1") {
    } else if (cmd == "f0") {

    else if (cmd == "g1") {
    } else if (cmd == "g0") {

    else if (cmd == "h1") {
    } else if (cmd == "h0") {

    SerialUSB.println("\n\nWaiting for command...[a-h0-1]");

  } else if (cmd.length() == 4) {
    SerialUSB.print("Command: ");

    if (cmd == "gpa0") {
      state[GPA0] = !state[GPA0];
      setMCP(GPA0, state[GPA0gpb5]);
    } else 
    if (cmd == "gpa1") {
      state[GPA1] = !state[GPA1];
      setMCP(GPA1, state[GPA1]);
    } else 
    if (cmd == "gpa2") {
      state[GPA2] = !state[GPA2];
      setMCP(GPA2, state[GPA2]);
    } else 
    if (cmd == "gpa3") {
      state[GPA3] = !state[GPA3];
      setMCP(GPA3, state[GPA3]);
    } else 
    if (cmd == "gpa4") {
      state[GPA4] = !state[GPA4];
      setMCP(GPA4, state[GPA4]);
    } else 
    if (cmd == "gpa5") {
      state[GPA5] = !state[GPA5];
      setMCP(GPA5, state[GPA5]);
    } else 
    if (cmd == "gpa6") {
      state[GPA6] = !state[GPA6];
      setMCP(GPA6, state[GPA6]);
    } else 
    if (cmd == "gpa7") {
      state[GPA7] = !state[GPA7];
      setMCP(GPA7, state[GPA7]);
    } else
    if (cmd == "gpb0") {
      state[GPB0] = !state[GPB0];
      setMCP(GPB0, state[GPB0]);
    } else 
    if (cmd == "gpb1") {
      state[GPB1] = !state[GPB1];
      setMCP(GPB1, state[GPB1]);
    } else 
    if (cmd == "gpb2") {
      state[GPB2] = !state[GPB2];
      setMCP(GPB2, state[GPB2]);
    } else 
    if (cmd == "gpb3") {
      state[GPB3] = !state[GPB3];
      setMCP(GPB3, state[GPB3]);
    } else 
    if (cmd == "gpb4") {
      state[GPB4] = !state[GPB4];
      setMCP(GPB4, state[GPB4]);
    } else 
    if (cmd == "gpb5") {
      state[GPB5] = !state[GPB5];
      setMCP(GPB5, state[GPB5]);
    } else 
    if (cmd == "gpb6") {
      state[GPB6] = !state[GPB6];
      setMCP(GPB6, state[GPB6]);
    } else 
    if (cmd == "gpb7") {
      state[GPB7] = !state[GPB7];
      setMCP(GPB7, state[GPB7]);

void setMCP(int i, boolean s){
  SerialUSB.print("Set addr #");
  SerialUSB.print(" to ");
  mcp.digitalWrite(i, s);

void pulseMCP(int i) {
  SerialUSB.print("Pulse at addr #");

  mcp.digitalWrite(i, HIGH);
  mcp.digitalWrite(i, LOW);
  SerialUSB.println(" *DONE*");

If there are relais that do not switch, follow these steps:

  1. Set MCP IO pin one by one and check if voltage (0V vs. 3.3V) can be measured between MCP IC and ULN2803 IC.
  2. Measure also the voltage on ULN2803 output pin.

If MCP fires 0V (low), ULN should almost show 5V (typically 4.8V) on output. If MCP fires 3.3V (high) the ULN output voltage will go down to almost 0V (low), but typically 0.7V. If itÄs 0.9V, it will work as well. But if voltage just drops to 1..1.5V, the ULN driver IC is bad and needs to be replaced. Make sure you use a genuine one and not a china-copy.