Remotesocket

Aus [ a x e d i ]
Zur Navigation springen Zur Suche springen

Steuerung einer Funksteckdose mit Arduino (Grundlagen siehe RF Link). Eingebauter WebServer mit Oberfläche für Mobilgeräte (jQuery mobil).

Hardware

Screenshots

Sourcecode

Compiled with Arduino 0022

<syntaxhighlight lang="c">

  1. include "SPI.h"
  2. include "Ethernet.h"
  3. include "WebServer.h"
  4. include <Udp.h>
  5. include <stdio.h>
  6. include <Time.h>
  7. include <MsTimer2.h>

// Powerfix Aussenfunksteckdosen

  1. define AON "000111011111011010100011"
  2. define AOFF "000100001000110101110011"
  3. define BON "000110001011110011001010"
  4. define BOFF "000110010101011110111010"
  5. define CON "000110010101011110111010"
  6. define COFF "000100011100001011100001"
  7. define DON "000110111010101100101000"
  8. define DOFF "000101100010000000001000"
  9. define MASTERON "000110101101100111011101"
  10. define MASTEROFF "000100011100001011101101"
  1. define TIMER2 60000
  2. define AlarmHMS(_hr_, _min_, _sec_) (_hr_ * SECS_PER_HOUR + _min_ * SECS_PER_MIN + _sec_)

static uint8_t mac[6] = { 0x02, 0xAA, 0xBB, 0xCC, 0x00, 0x22 }; // MAC address static uint8_t ip[] = { 192, 168, 1, 2 }; // IP address unsigned int localPort = 8888; // local port to listen for UDP packets byte timeServer[] = { 192, 168, 1, 1}; // IP address of time server (local router) const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message byte packetBuffer[ NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets short rc_pin=8; // data pin of sender short vc_pin=10; // power pin for sender (only power up sender before sending) boolean automatic = true; // automatic switch on/off int masterOnHour = 18; // standard switch on hour int masterOnMinute = 0; // standard switch on minute int masterOffHour = 22; // standard switch off hour int masterOffMinute = 0; // standard switch off minute unsigned long epoch = 0; int NTPCounter = 0; boolean NTPUpdate = false;

  1. define PREFIX ""

WebServer webserver(PREFIX, 80);

void setup() {

 Serial.begin(9600);
 
 pinMode(rc_pin, OUTPUT);
 pinMode(vc_pin, OUTPUT);
 
 Ethernet.begin(mac, ip);
 // Activate WebServer and register 3 websites
 webserver.setDefaultCommand(&remoteCmd);
 webserver.addCommand("remote.html", &remoteCmd);
 webserver.addCommand("timer.html", &timerCmd);
 webserver.addCommand("options.html", &optionsCmd);
 
 webserver.begin();
 
 Udp.begin(localPort);
 epoch = getNTP();
 setTime(getNtpHour(), getNtpMinute(), getNtpSecond(), 1, 1, 11);
 checkTimer();
 // timer calls checkTimer every 60 sec
 MsTimer2::set(TIMER2, checkTimer);
 MsTimer2::start();

}

void loop() {

 webserver.processConnection();
 if (NTPUpdate == true) {
   epoch = getNTP();
   setTime(getNtpHour(), getNtpMinute(), getNtpSecond(), 1, 1, 11);
   NTPUpdate == false;
 }

}

// called by Timer2 every 60 sec, checks whether to switch on/off the remote control switches (Master) void checkTimer() {

 NTPCounter++;
 
 // Update Time every hour
 if (NTPCounter == 60) {
   NTPUpdate = true;
   NTPCounter = 0;
 }
 
 if (automatic == true) {
   
   time_t currentTime = AlarmHMS(hour(), minute(), second());
   time_t masterOnTime = AlarmHMS(masterOnHour, masterOnMinute, 0);
   time_t masterOffTime = AlarmHMS(masterOffHour, masterOffMinute, 0);
   if ((currentTime >= masterOnTime) && (currentTime <= masterOffTime)) {
     sendCode(MASTERON);
   } 
   else {
     sendCode(MASTEROFF);
   }
 }

}

void remoteCmd(WebServer &server, WebServer::ConnectionType type, char *, bool) {

 if (type == WebServer::POST)
 {
   bool repeat;
   char name[16], value[16];
   do
   {
     repeat = server.readPOSTparam(name, 16, value, 16);
     if (strcmp(name, "remote") == 0)
     {
        int val = strtoul(value, NULL, 10);
        char code[24];
        switch(val) {
          case 11: // A on
            strcpy(code, AON);
          break;
          case 10: // A off
            strcpy(code, AOFF);
          break;
          case 21: // B on
            strcpy(code, BON);
          break;
          case 20: // B off
            strcpy(code, BOFF);
          break;
          case 31: // C on
            strcpy(code, CON);
          break;
          case 30: // C off
            strcpy(code, COFF);
          break;
          case 41: // D on
            strcpy(code, DON);
          break;
          case 40: // D off
            strcpy(code, DOFF);
          break;
          case 51: // Master on
            strcpy(code, MASTERON);
          break;
          case 50: // Master off
            strcpy(code, MASTEROFF);
          break;
        }
        sendCode(code);
     }
   } while (repeat);
   
   server.httpSeeOther(PREFIX "/remote.html");
   return;
 }
 server.httpSuccess();
 if (type == WebServer::GET)
 {

P(remote) = "<!DOCTYPE html>" "<html>" " <head>" " <meta name='viewport' content='width=device-width, initial-scale=1'>" " <title>Steuerung Powerfix Steckdosen</title>" " <link rel='stylesheet' href='http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css' />" " <script src='http://code.jquery.com/jquery-1.6.4.min.js'></script>" " <script src='http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js'></script>" " <script>" " $(document).bind('mobileinit', function(){" " $.mobile.touchOverflowEnabled = true;" " });" " </script>" "</head>" "<body>"

"
" "
" "

Remote

"

" <a href='options.html' data-icon='star' class='ui-btn-right' data-transition='slidedown'>Optionen</a> "

"
" "
"

" <form action='/' method='post' data-ajax='false'>"

"
" "
"

" <legend>A</legend>" " <button name='remote' value='11' data-icon='check'>An" " <button name='remote' value='10' data-icon='delete'>Aus"

"
" "
" "
" "
"

" <legend>B</legend>" " <button name='remote' value='21' data-icon='check'>An" " <button name='remote' value='20' data-icon='delete'>Aus"

"
" "
" "
" "
"

" <legend>C</legend>" " <button name='remote' value='31' data-icon='check'>An" " <button name='remote' value='30' data-icon='delete'>Aus"

"
" "
" "
" "
"

" <legend>D</legend>" " <button name='remote' value='41' data-icon='check'>An" " <button name='remote' value='40' data-icon='delete'>Aus"

"
" "
" "
" "
"

" <legend>Master</legend>" " <button name='remote' value='51' data-icon='check'>An" " <button name='remote' value='50' data-icon='delete'>Aus"

"
" "
"

" </form> "

"
" "
" "
" "
    " "
  • <a href='#' data-icon='grid' class='ui-btn-active'>Remote</a>
  • " "
  • <a href='timer.html' data-icon='gear' data-transition='flip'>Timer</a>
  • " "
" "
" "
" "
"

"</body>" "</html>";

    server.printP(remote);
 }

}

void timerCmd(WebServer &server, WebServer::ConnectionType type, char *, bool) {

 if (type == WebServer::POST)
 {
   bool repeat;
   char name[16], value[16];
   do
   {
     repeat = server.readPOSTparam(name, 16, value, 16);
     if (strcmp(name, "automatic") == 0)
     {
        int val = strtoul(value, NULL, 10);
        if (val == 0)
          automatic = false;
        else
          automatic = true;
     }
     if (strcmp(name, "masterAn") == 0)
     {
       String theTime(value);
       String theHour = theTime.substring(0, theTime.indexOf(':'));
       String theMinute = theTime.substring(theTime.indexOf(':')+1);
       masterOnHour = stringToInt(theHour);
       masterOnMinute = stringToInt(theMinute);
      }
     if (strcmp(name, "masterAus") == 0)
     {
       String theTime(value);
       String theHour = theTime.substring(0, theTime.indexOf(':'));
       String theMinute = theTime.substring(theTime.indexOf(':')+1);
       masterOffHour = stringToInt(theHour);
       masterOffMinute = stringToInt(theMinute);
     }
   } while (repeat);
   
   checkTimer();
   
   server.httpSeeOther(PREFIX "/timer.html");
   return;
 }
 server.httpSuccess();
 if (type == WebServer::GET)
 {

P(timer1) = "<!DOCTYPE html>" "<html>" " <head>" " <meta name='viewport' content='width=device-width, initial-scale=1'>" " <title>Steuerung Powerfix Steckdosen</title>" " <link rel='stylesheet' href='http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css' />" " <script src='http://code.jquery.com/jquery-1.6.4.min.js'></script>" " <script src='http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js'></script>" " <script>" " $(document).bind('mobileinit', function(){" " $.mobile.touchOverflowEnabled = true;" " });" "</script>" "</head>" "<body>"

"
" "
" "

Timer

"

" <a href='options.html' data-icon='star' class='ui-btn-right' data-transition='slidedown'>Optionen</a> "

"
" "
"

" <script>" " function setTheTime() {" " now=new Date();" " hour=now.getHours();" " min=now.getMinutes();" " sec=now.getSeconds();" " if (min<=9) { min='0'+min; }" " if (sec<=9) { sec='0'+sec; }" " if (hour<=9) { hour='0'+hour; }" " document.timerForm.theTime.value = hour + ':' + min + ':' + sec;" " setTimeout(setTheTime, 1000);" " }" " $(document).ready(function() { setTheTime(); });" " </script>" " <form action='timer.html' method='post' data-ajax='false' name='timerForm'>"

"
"

" <label for='theTime'>Aktuelle Zeit:</label>" " <input type='text' name='theTime' id='theTime' value= disabled/>"

"
" "
"

" <label for='masterAn'>Master an:</label>" " <input type='time' name='masterAn' id='masterAn' value='";

P(timer2) = "' />"

"
" "
"

" <label for='masterAus'>Master aus:</label>" " <input type='time' name='masterAus' id='masterAus' value='";

P(timer3) = "' />"

"
" "
"

" <label for='automatic'>Automatik:</label>" " <select name='automatic' id='automatic' data-role='slider'>" " <option value='1' ";

P(timer4) = ">An</option>" " <option value='0' ";

P(timer5) = ">Aus</option>" " </select> "

"
"

" <button type='submit' data-theme='b' name='save' value='submit-value'>Speichern</button>" " </form> "

"
" "
" "
" "
    " "
  • <a href='remote.html' data-icon='grid' data-transition='flip'>Remote</a>
  • " "
  • <a href='#' data-icon='gear' class='ui-btn-active'>Timer</a>
  • " "
" "
" "
" "
"

"</body>" "</html>";

   server.printP(timer1);
   // Master on
   if (masterOnHour < 10)
     server.print(":0");
   server.print(masterOnHour);
   server.print(":");
   if (masterOnMinute < 10)
     server.print("0");
   server.print(masterOnMinute);
 
   server.printP(timer2);
   // Master off
   if (masterOffHour < 10)
     server.print("0");
   server.print(masterOffHour);
   server.print(":");
   if (masterOffMinute < 10)
     server.print("0");
   server.print(masterOffMinute);
   server.printP(timer3);
   // Automatic on
   if (automatic == true)
     server.print("selected");
   server.printP(timer4);
   // Automatic off
   if (automatic == false)
     server.print("selected");
   server.printP(timer5);
 }

}

void optionsCmd(WebServer &server, WebServer::ConnectionType type, char *, bool) {

 if (type == WebServer::POST)
 {
   Serial.println("POST: NTP abrufen");
   epoch = getNTP();
   setTime(getNtpHour(), getNtpMinute(), getNtpSecond(), 1, 1, 11);
   server.httpSeeOther(PREFIX "/options.html");
   return;
 }
 server.httpSuccess();
 if (type == WebServer::GET)
 {
   Serial.println("GET: Seite laden");
   P(options1) = 

"<!DOCTYPE html>" "<html>" " <head>" " <meta name='viewport' content='width=device-width, initial-scale=1'>" " <title>Optionen</title>" " <link rel='stylesheet' href='http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css' />" " <script src='http://code.jquery.com/jquery-1.6.4.min.js'></script>" " <script src='http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js'></script>" "</head>" "<body>"

"
" "
" "

Optionen

" "
" "
"

" <script>" " function setTheTime2() {" " now=new Date();" " hour=now.getHours();" " min=now.getMinutes();" " sec=now.getSeconds();" " if (min<=9) { min='0'+min; }" " if (sec<=9) { sec='0'+sec; }" " if (hour<=9) { hour='0'+hour; }" " document.optionsForm.theTime.value = hour + ':' + min + ':' + sec;" " setTimeout(setTheTime2, 1000);" " }" " $(document).ready(function() { setTheTime2(); });" " </script>" " <form action='/options.html' method='post' data-ajax='false' name='optionsForm'>"

"
"

" <label for='theTime'>Aktuelle Zeit:</label>" " <input type='text' name='theTime' id='theTime' value= disabled/>"

"
" "
"

" <label for='internTime'>Interne Zeit:</label>" " <input type='text' name='internTime' id='internTime' value='";

P(options2) = "' disabled/>"

"
"

" <button type='submit' data-theme='a' name='save' value='submit-value'>Zeit abrufen</button>" " </form>"

"
" "
" "
" "
    " "
  • <a href='remote.html' data-icon='grid' data-transition='slideup'>Remote</a>
  • " "
  • <a href='timer.html' data-icon='gear' data-transition='slideup'>Timer</a>
  • " "
" "
" "
" "
"

"</body>" "</html>";

   server.printP(options1);
   if (hour() < 10)
     server.print(":0");
   server.print(hour());
   server.print(":");
   if (minute() < 10)
     server.print("0");
   server.print(minute());
   server.print(":");
   if (second() < 10)
     server.print("0");
   server.print(second());
   
   server.printP(options2);
 }

}

// Get time from NTP-Server unsigned long getNTP() {

 sendNTPpacket(timeServer); // send an NTP packet to a time server
 delay(1000);
 
 if ( Udp.available() ) {  
   Udp.readPacket(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer
   // the timestamp starts at byte 40 of the received packet and is four bytes,
   // or two words, long. First, esxtract the two words:
   unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
   unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
   // combine the four bytes (two words) into a long integer
   // this is NTP time (seconds since Jan 1 1900):
   unsigned long secsSince1900 = highWord << 16 | lowWord;  
   // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
   const unsigned long seventyYears = 2208988800UL;     
   // subtract seventy years + one hour (GMT+1)
   // unsigned long epoch = secsSince1900 - seventyYears;
   return secsSince1900 - seventyYears + 3600;
 }

}

int getNtpHour() {

 return (epoch % 86400L) / 3600;

}

int getNtpMinute() {

 return (epoch % 3600) / 60;

}

int getNtpSecond() {

 return epoch % 60;

}

// send an NTP request to the time server at the given address unsigned long sendNTPpacket(byte *address) {

 // set all bytes in the buffer to 0
 memset(packetBuffer, 0, NTP_PACKET_SIZE); 
 // Initialize values needed to form NTP request
 packetBuffer[0] = 0b11100011;   // LI, Version, Mode
 packetBuffer[1] = 0;     // Stratum, or type of clock
 packetBuffer[2] = 6;     // Polling Interval
 packetBuffer[3] = 0xEC;  // Peer Clock Precision
 // 8 bytes of zero for Root Delay & Root Dispersion
 packetBuffer[12]  = 49; 
 packetBuffer[13]  = 0x4E;
 packetBuffer[14]  = 49;
 packetBuffer[15]  = 52;
 // all NTP fields have been given values, now
 // you can send a packet requesting a timestamp: 		   
 Udp.sendPacket( packetBuffer,NTP_PACKET_SIZE,  address, 123); //NTP requests are to port 123

}

// Send code to remote socket switch boolean sendCode(char code[]){

 digitalWrite(vc_pin, HIGH);
 for(short z = 0; z<6; z++){
   for(short i = 0; i<24; i++){
     sendByte(code[i]);
   }
   sendByte('x');
 }
 digitalWrite(vc_pin, LOW);
 return true;

}

// Send byte to remote socket switch void sendByte(char i) {

 switch(i){
 case '0':
   {
     digitalWrite(rc_pin,HIGH);
     delayMicroseconds(1000);
     digitalWrite(rc_pin,LOW);
     delayMicroseconds(500);
     return;
   }
 case '1':
   {
     digitalWrite(rc_pin,HIGH);
     delayMicroseconds(500);
     digitalWrite(rc_pin,LOW);
     delayMicroseconds(1000);
     return;
   }
 case 'x':
   {
     digitalWrite(rc_pin,HIGH);
     delayMicroseconds(3000);
     digitalWrite(rc_pin,LOW);
     delayMicroseconds(7000);
   }
 }

}

// Convert String to int int stringToInt(String str) {

 char this_char[str.length()+1];
 str.toCharArray(this_char, sizeof(this_char));
 return atoi(this_char);

} </syntaxhighlight>

Links

Video