A remote Ethernet switch for my relay box

Aus DL8RDS Wiki
Wechseln zu: Navigation, Suche

1 Project Scope

The relay box I built recently is great, but without any physical switch a little hard to use. So the requirements profile is this:

  • Must be usable through a web interface.
  • Must be usable haprically through a physical switchboard.
  • Must consume as little power as possible if not used.
  • Must deactivate itself if unused.
  • Must not change relay state if switched of or switched back on again.
  • Must connect to the RaspberryPi that controls the relay box.
  • Must retrieve the switch state when switched on.
  • Must retrieve the switch state every 5 seconds.

2 Solution concept

I am using two Arduinos:

  • Arduino Mega 2560 with Ethernet shield.
The ethernet shield consumes quite much power and the ethernet chip is getting warm. So it must be switched off if not actively used.
The Arduino Mega provides enough GPIO pins to evaluate the switch state all the eight switches and control the red LED switchstate indicators.
  • Arduino Pro Mini
The ProMini is udes to control the entire switch board and consider inactivity times.

Both programs use the same debouncing algorithm, and the code of the Arduino ProMini is enhanced to consider an activity indicator that comes from the Arduino Mega. Whenever a switch is toggled, it sends a 50ms signal over to the ProMini so that the timeout watchdog is reset.

3 Components

Considerations and alternatives:

  • The LAS2GQ button switch has a LED status indicator ring. The datasheet does not say anything about the LED resistor and its value, which I measured at 960 Ohm. I=U/R tells me that the current will be 52mA, which is above the max ratings of the GPIO pins, so I decided to use a ULN2803AGP Darlington transistor array to reduce the load of the GPIO pins. The ULN datasheet says that the ideal use case would be as emitter switch, so I did that. The eight pins caused a bit of cabling salad, but it works fine :-)

Components list:

  • Arduino ProMini, 5€
  • FTDI Basic USB programmer interface for the ProMini, 5€
  • Arduino Mega, 32€
  • Ethernet shield, 15€
  • Neutrik Ethernet socket, 5€
  • 9x LAS2GQ switches with red LED ring, 55€ https://www.reichelt.de/?ARTICLE=108416
Datasheet: http://www.robotshop.com/media/files/PDF/datasheet-swt101a2b.pdf
Note: The 12V version also works with 5V
  • 8x 10k PullDown Resistors for the switches
  • Aluminium panel, 10€
  • Euro PCB 5€
  • 64 pin backplane header
  • DC-DC converter, 5€
  • 2x ferrite cores, 5€
  • 2x 4mm banana sockets for external power supply
  • Resistors 2k2, 2k2, 4k7, 10k
  • Diode 1N4148
  • Relay FRS1B-S 5V, 5€
  • ULN2803AGP Darlington transistor array

The values are rounded. Total costs: 150€

4 Schema

4.1 Arduino ProMini

2017-06-04-ArduinoProMiniSchaltbild.jpg

4.2 Arduino Mega

2017-06-04-ArduinoMegaSchaltbild.jpg

5 Operational observations

  • Max power consumption: 2,68W
  • Standby power consumption: 134mW
  • Max input voltage: 30V

6 Code

6.1 Arduino ProMini


// constants won't change. They're used here to
// set pin numbers:
const int buttonPin = 3;    // the number of the pushbutton pin
const int ledPin = 5;      // the number of the LED pin
const int externSwitchInput = 0;

// Variables will change:
int ledState = LOW;         // the current state of the output pin
int buttonState;             // the current reading from the input pin
int lastButtonState = HIGH;   // the previous reading from the input pin

// the following variables are unsigned long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers
unsigned long lastSwitchTime = 0;
unsigned int inactivityTime = 20000;  // milliseconds since last activity

void setup() {
  Serial.begin(9600);
  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);

  // set initial LED state
  digitalWrite(ledPin, ledState);

}

void loop() {
  // read the state of the switch into a local variable:
  int reading = digitalRead(buttonPin);
  int remoteSwitchEvent = analogRead(externSwitchInput);

  // remote activity
  if (remoteSwitchEvent > 512u) {
    Serial.print("Tastendruck extern zum Zeitpunkt>");
    Serial.println(millis(), DEC);
    lastSwitchTime = millis();
  }

  // switch off because of inactivity

  if (ledState == HIGH && (millis() - lastSwitchTime > inactivityTime)) {
    Serial.print("Millis >");
    Serial.println(millis());
    Serial.print("LastSwitchTime >");
    Serial.println(lastSwitchTime);
    Serial.print("millis() - lastSwitchTime >");
    Serial.println(millis() - lastSwitchTime);
    Serial.print("Inactivity Time >");
    Serial.println(inactivityTime);
    Serial.println("Timeout");
    ledState = LOW;
    lastButtonState = HIGH;
    lastSwitchTime = 0;
  }

  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH),  and you've waited
  // long enough since the last press to ignore any noise:

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

      // only toggle the LED if the new button state is HIGH
      if (buttonState == HIGH) {
        ledState = !ledState;
        lastSwitchTime = millis();
        Serial.println("Switch Event");
      }
    }
  }

  // set the LED:
  digitalWrite(ledPin, ledState);

  // save the reading.  Next time through the loop,
  // it'll be the lastButtonState:
  lastButtonState = reading;
}

6.2 Arduino Mega 2560

This is not yet ready. The network functionality is still missing. But here's the code nevertheless:




#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress dnServer(44, 130, 60, 100);
IPAddress gateway(44, 225, 41, 1);
IPAddress subnet(255, 255, 255, 240);
IPAddress ip(44, 225, 41, 5);

byte server[] = { 44, 225, 41, 4 };

EthernetClient client;

// constants won't change. They're used here to
// set pin numbers:
const int buttonPin1 = 31;    // the number of the pushbutton pin
const int buttonPin2 = 33;
const int buttonPin3 = 35;
const int buttonPin4 = 37;
const int buttonPin5 = 39;
const int buttonPin6 = 41;
const int buttonPin7 = 43;
const int buttonPin8 = 45;

const int ledPin1 = 30;      // the number of the LED pin
const int ledPin2 = 32;
const int ledPin3 = 34;
const int ledPin4 = 36;
const int ledPin5 = 38;
const int ledPin6 = 40;
const int ledPin7 = 42;
const int ledPin8 = 44;

// Variables will change:
int ledState1 = LOW;         // the current state of the output pin
int ledState2 = LOW;
int ledState3 = LOW;
int ledState4 = LOW;
int ledState5 = LOW;
int ledState6 = LOW;
int ledState7 = LOW;
int ledState8 = LOW;

int buttonState1;             // the current reading from the input pin
int buttonState2;
int buttonState3;
int buttonState4;
int buttonState5;
int buttonState6;
int buttonState7;
int buttonState8;

int lastButtonState1 = HIGH;   // the previous reading from the input pin
int lastButtonState2 = HIGH;
int lastButtonState3 = HIGH;
int lastButtonState4 = HIGH;
int lastButtonState5 = HIGH;
int lastButtonState6 = HIGH;
int lastButtonState7 = HIGH;
int lastButtonState8 = HIGH;

// the following variables are unsigned long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime1 = 0;  // the last time the output pin was toggled
unsigned long lastDebounceTime2 = 0;
unsigned long lastDebounceTime3 = 0;
unsigned long lastDebounceTime4 = 0;
unsigned long lastDebounceTime5 = 0;
unsigned long lastDebounceTime6 = 0;
unsigned long lastDebounceTime7 = 0;
unsigned long lastDebounceTime8 = 0;

unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

const int activityPin = 52;

char httpResponse[1000] = {0};
int relstate[8] = {0};

void communicateWithRaspi(String rlycommand) {
  String httpCommand = "GET /cgi-bin/steuerung/rly16.py?command=" + rlycommand + " HTTP/1.0";
  if (client.connect(server, 80)) {
    Serial.println("connected");
    Serial.println(httpCommand);
    client.println(httpCommand);
    client.println();

    int index = 0;
    while (client.connected()) {
      if ((index < 1000) && (client.available())) {
        char c = client.read();
        //Serial.print(c);
      
        httpResponse[index] = c;
        index++;
      }
    }
    Serial.print("Zeichen gelesen >");
    Serial.println(index);
    Serial.println("Ende");
    client.stop();

    if (httpResponse[index-9] == 48) {
      relstate[0] = LOW;
    } else {
      relstate[0] = HIGH;
    }

    if (httpResponse[index-8] == 48) {
      relstate[1] = LOW;
    } else {
      relstate[1] = HIGH;
    }
      
    if (httpResponse[index-7] == 48) {
      relstate[2] = LOW;
    } else {
      relstate[2] = HIGH;
    }
      
    if (httpResponse[index-6] == 48) {
      relstate[3] = LOW;
    } else {
      relstate[3] = HIGH;
    }
    
    if (httpResponse[index-5] == 48) {
      relstate[4] = LOW;
    } else {
      relstate[4] = HIGH;
    }
      
    if (httpResponse[index-4] == 48) {
      relstate[5] = LOW;
    } else {
      relstate[5] = HIGH;
    }
      
    if (httpResponse[index-3] == 48) {
      relstate[6] = LOW;
    } else {
      relstate[6] = HIGH;
    }
      
    if (httpResponse[index-2] == 48) {
      relstate[7] = LOW;
    } else {
      relstate[7] = HIGH;
    }
      
    //this all works fine
    
    Serial.print(relstate[0]);
    Serial.print(relstate[1]);
    Serial.print(relstate[2]);
    Serial.print(relstate[3]);
    Serial.print(relstate[4]);
    Serial.print(relstate[5]);
    Serial.print(relstate[6]);
    Serial.println(relstate[7]);
    

    if (relstate[0] == LOW) { 
      ledState1 = LOW;  
      lastButtonState1 = HIGH;
    } else {
      ledState1 = HIGH;
      lastButtonState1 = LOW;
    }

    if (relstate[1] == LOW) { 
      ledState2 = LOW;  
      lastButtonState2 = HIGH;
    } else {
      ledState2 = HIGH;
      lastButtonState2 = LOW;
    }

    if (relstate[2] == LOW) { 
      ledState3 = LOW;  
      lastButtonState3 = HIGH;
    } else {
      ledState3 = HIGH;
      lastButtonState3 = LOW;
    }

    if (relstate[3] == LOW) { 
      ledState4 = LOW;  
      lastButtonState4 = HIGH;
    } else {
      ledState4 = HIGH;
      lastButtonState4 = LOW;
    }

    if (relstate[4] == LOW) { 
      ledState5 = LOW;  
      lastButtonState5 = HIGH;
    } else {
      ledState5 = HIGH;
      lastButtonState5 = LOW;
    }

    if (relstate[5] == LOW) { 
      ledState6 = LOW;  
      lastButtonState6 = HIGH;
    } else {
      ledState6 = HIGH;
      lastButtonState6 = LOW;
    }

    if (relstate[6] == LOW) { 
      ledState7 = LOW;  
      lastButtonState7 = HIGH;
    } else {
      ledState7 = HIGH;
      lastButtonState7 = LOW;
    }

    if (relstate[7] == LOW) { 
      ledState8 = LOW;  
      lastButtonState8 = HIGH;
    } else {
      ledState8 = HIGH;
      lastButtonState8 = LOW;
    }

  } else {
    Serial.println("connection failed");
  }
}

void setup() {
  Serial.begin(9600);
  
  pinMode(buttonPin1, INPUT);
  pinMode(buttonPin2, INPUT);
  pinMode(buttonPin3, INPUT);
  pinMode(buttonPin4, INPUT);
  pinMode(buttonPin5, INPUT);
  pinMode(buttonPin6, INPUT);
  pinMode(buttonPin7, INPUT);
  pinMode(buttonPin8, INPUT);

  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  pinMode(ledPin4, OUTPUT);
  pinMode(ledPin5, OUTPUT);
  pinMode(ledPin6, OUTPUT);
  pinMode(ledPin7, OUTPUT);
  pinMode(ledPin8, OUTPUT);

  pinMode(activityPin, OUTPUT);

  // start the Ethernet connection:
  Ethernet.begin(mac, ip, dnServer, gateway, subnet);
  Serial.print("IP = ");
  Serial.println(Ethernet.localIP());

  communicateWithRaspi("91");

  // set initial LED state
  digitalWrite(ledPin1, ledState1);
  digitalWrite(ledPin2, ledState2);
  digitalWrite(ledPin3, ledState3);
  digitalWrite(ledPin4, ledState4);
  digitalWrite(ledPin5, ledState5);
  digitalWrite(ledPin6, ledState6);
  digitalWrite(ledPin7, ledState7);
  digitalWrite(ledPin8, ledState8);
  
}

unsigned long lastConnect = 0;

void loop() {

  // read the state of the switch into a local variable:
  int reading1 = digitalRead(buttonPin1);
  int reading2 = digitalRead(buttonPin2);
  int reading3 = digitalRead(buttonPin3);
  int reading4 = digitalRead(buttonPin4);
  int reading5 = digitalRead(buttonPin5);
  int reading6 = digitalRead(buttonPin6);
  int reading7 = digitalRead(buttonPin7);
  int reading8 = digitalRead(buttonPin8);

  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH),  and you've waited
  // long enough since the last press to ignore any noise:

  // If the switch changed, due to noise or pressing:
  if (reading1 != lastButtonState1) {
    // reset the debouncing timer
    lastDebounceTime1 = millis();
  }
  if (reading2 != lastButtonState2) {
    // reset the debouncing timer
    lastDebounceTime2 = millis();
  }
  if (reading3 != lastButtonState3) {
    // reset the debouncing timer
    lastDebounceTime3 = millis();
  }
  if (reading4 != lastButtonState4) {
    // reset the debouncing timer
    lastDebounceTime4 = millis();
  }
  if (reading5 != lastButtonState5) {
    // reset the debouncing timer
    lastDebounceTime5 = millis();
  }
  if (reading6 != lastButtonState6) {
    // reset the debouncing timer
    lastDebounceTime6 = millis();
  }
  if (reading7 != lastButtonState7) {
    // reset the debouncing timer
    lastDebounceTime7 = millis();
  }
  if (reading8 != lastButtonState8) {
    // reset the debouncing timer
    lastDebounceTime8 = millis();
  }

  if ((millis() - lastDebounceTime1) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading1 != buttonState1) {
      buttonState1 = reading1;

      // only toggle the LED if the new button state is HIGH
      if (buttonState1 == HIGH) {
        ledState1 = !ledState1;
      } 
      
      // if we have a local switch event
      if (ledState1 == LOW) {
          communicateWithRaspi("101");
      } else {
          communicateWithRaspi("111");
      }     

      digitalWrite(activityPin, HIGH);
      delay(50);
      digitalWrite(activityPin, LOW);
    }
  }

  if ((millis() - lastDebounceTime2) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading2 != buttonState2) {
      buttonState2 = reading2;

      // only toggle the LED if the new button state is HIGH
      if (buttonState2 == HIGH) {
        ledState2 = !ledState2;
      }
      
      // if we have a local switch event
      if (ledState2 == LOW) {
          communicateWithRaspi("102");
      } else {
          communicateWithRaspi("112");
      }

      digitalWrite(activityPin, HIGH);
      delay(50);
      digitalWrite(activityPin, LOW);
    }
  }

  if ((millis() - lastDebounceTime3) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading3 != buttonState3) {
      buttonState3 = reading3;

      // only toggle the LED if the new button state is HIGH
      if (buttonState3 == HIGH) {
        ledState3 = !ledState3;
      }

      // if we have a local switch event
      if (ledState3 == LOW) {
          communicateWithRaspi("103");
      } else {
          communicateWithRaspi("113");
      }
      
      digitalWrite(activityPin, HIGH);
      delay(50);
      digitalWrite(activityPin, LOW);
    }
  }

  if ((millis() - lastDebounceTime4) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading4 != buttonState4) {
      buttonState4 = reading4;

      // only toggle the LED if the new button state is HIGH
      if (buttonState4 == HIGH) {
        ledState4 = !ledState4;
      }
      
      // if we have a local switch event
      if (ledState4 == LOW) {
          communicateWithRaspi("104");
      } else {
          communicateWithRaspi("114");
      }
      
      digitalWrite(activityPin, HIGH);
      delay(50);
      digitalWrite(activityPin, LOW);
    }
  }

  if ((millis() - lastDebounceTime5) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading5 != buttonState5) {
      buttonState5 = reading5;

      // only toggle the LED if the new button state is HIGH
      if (buttonState5 == HIGH) {
        ledState5 = !ledState5;
      }
      
      // if we have a local switch event
      if (ledState5 == LOW) {
          communicateWithRaspi("105");
      } else {
          communicateWithRaspi("115");
      }
     
      
      digitalWrite(activityPin, HIGH);
      delay(50);
      digitalWrite(activityPin, LOW);
    }
  }

  if ((millis() - lastDebounceTime6) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading6 != buttonState6) {
      buttonState6 = reading6;

      // only toggle the LED if the new button state is HIGH
      if (buttonState6 == HIGH) {
        ledState6 = !ledState6;
      }

      // if we have a local switch event
      if (ledState6 == LOW) {
          communicateWithRaspi("106");
      } else {
          communicateWithRaspi("116");
      }

      digitalWrite(activityPin, HIGH);
      delay(50);
      digitalWrite(activityPin, LOW);
    }
  }

  if ((millis() - lastDebounceTime7) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading7 != buttonState7) {
      buttonState7 = reading7;

      // only toggle the LED if the new button state is HIGH
      if (buttonState7 == HIGH) {
        ledState7 = !ledState7;
      }

      // if we have a local switch event
      if (ledState7 == LOW) {
          communicateWithRaspi("107");
      } else {
          communicateWithRaspi("117");
      }
      
      digitalWrite(activityPin, HIGH);
      delay(50);
      digitalWrite(activityPin, LOW);
    }
  }

  if ((millis() - lastDebounceTime8) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading8 != buttonState8) {
      buttonState8 = reading8;

      // only toggle the LED if the new button state is HIGH
      if (buttonState8 == HIGH) {
        ledState8 = !ledState8;
      }

      // if we have a local switch event
      if (ledState8 == LOW) {
          communicateWithRaspi("108");
      } else {
          communicateWithRaspi("118");
      }
      
      digitalWrite(activityPin, HIGH);
      delay(50);
      digitalWrite(activityPin, LOW);
    }
  }

  // set the LED:
  digitalWrite(ledPin1, ledState1);
  digitalWrite(ledPin2, ledState2);
  digitalWrite(ledPin3, ledState3);
  digitalWrite(ledPin4, ledState4);
  digitalWrite(ledPin5, ledState5);
  digitalWrite(ledPin6, ledState6);
  digitalWrite(ledPin7, ledState7);
  digitalWrite(ledPin8, ledState8);

  // save the reading.  Next time through the loop,
  // it'll be the lastButtonState:
  lastButtonState1 = reading1;
  lastButtonState2 = reading2;
  lastButtonState3 = reading3;
  lastButtonState4 = reading4;
  lastButtonState5 = reading5;
  lastButtonState6 = reading6;
  lastButtonState7 = reading7;
  lastButtonState8 = reading8;
  
}


7 Images

2017-06-04-RemoteEthernetSwitchboard8-1.jpg 2017-06-04-RemoteEthernetSwitchboard8-2.jpg

2017-06-04-RemoteEthernetSwitchboard8-3.jpg 2017-06-04-RemoteEthernetSwitchboard8-4.jpg

2017-06-04-RemoteEthernetSwitchboard8-5.jpg 2017-06-04-RemoteEthernetSwitchboard8-6.jpg

2017-06-04-RemoteEthernetSwitchboard8-7.jpg 2017-06-04-RemoteEthernetSwitchboard8-8.jpg

2017-06-04-RemoteEthernetSwitchboard8-9.jpg 2017-06-04-RemoteEthernetSwitchboard8-10.jpg