phatduinoa personal home automation technology arduino





Software

Light Switches • Pachube • Writing present data to user interface (using PHP post method) • Getting changes from user interface • Resetting the ethernet module • The setup function • Flow, please!

You are smart and so is Phatduino. It is controlled by a powerful, intuitive code perfectly serving the user's needs. Phatduino has a function for every task that needs to be done - it is just like going through a to do list step by step. To get an idea on how Phatduino makes life easier it is a good idea to take an insight into the structure of the code starting with a nicely plotted overview.

Please note: This image is a link map and works as table of contents!


Arduino software and function reference get_changes() write_present() pachube_in_out() change_light_status() prepare_temp() measure_lmab() get_temp(outside) get_tempS(inside) door_check() night_check() bed_check() light_automation() reset_wiznet() setup() loop() change_manual_mode()

In this section we are going to talk about the main functions that do not have anything to do with automation or reading/writing sensors. For sensor functions please see the feel() section. Anything about automation you will find in the automate() section.

The main focus of Phatduino is you as a user. You influence the world Phatduino sees, just like Phatduino influences your world. As you can see in the diagram the device has to connect to various services, represented by the squares. The directions of the arrows show the direction of communication. A dashed communication arrow shows an optional way of communication. A dashed square frame represents a certain module or category of interfaces. Now we will go through the main interfaces step by step, following with a round-up in the loop() function.

Light switches

As you know from the hardware section Phatduino connects with the light switches via a seperate board holding a 4051 chip. In the software the function change_light_status() is responsible for calling those switches.

The following variables have to be declared in oder to make this function work:

  • int SLC[] = {0,0,0,0,0} Whenever the switch state of a light has to be changed it is stored in the SLC array as "1" with its number as an index. For example if light 2 has to be changed the array should be SLC[] = {0,0,2,0,0}. It was easier to create five indexes than to name the lights as numbers zero to three. Now the lights have the names one to four.
  • int SL[] = {0,0,0,0,0} This is the state of all lights. If one is switched on, its specific state is changed to "1". I know that it is a real overkill to save those states as integer values but as there are as far as I know no boolean arrays in Arduino this method works best in case you want to increase the number of switches some time.
  • int onPins[4], offPins[4] These arrays keep the pins of the 4051 that are connected to the on/off light switches on the remote.
  • int bin [] = {000, 1, 10, 11, 100, 101, 110, 111} Binary code to call the pins (see above) on 4051.
  • int PinS0, PinS1, PinS2, PinVoltage These variables keep the pins of Arduino the 4051 is connected to. (PinVoltage is connected to Vcc on 4051 and is only switched on in switching process to prevent the 4051 from switching when PinS0, PinS1 and PinS2 are set to LOW.)
void change_light_status(){
    // Change routine
int i = 1;
while(i <= 4){
  if(SLC[i] == 1){
      if(SL[i] == 0){
          changepin = OnPins[i];   
          SL[i] = 1;
          Serial << F("Licht nr. ") << i << F(" geht an!\n");
      }else{
        changepin = OffPins[i];
        SL[i] = 0;
        Serial << F("Licht nr. ") << i << F(" geht aus!\n");
      }
        SLC[i] = 0;

        row = bin[changepin];      
        r0 = row & 0x01;
        r1 = (row>>1) & 0x01;
        r2 = (row>>2) & 0x01;
        for(int k = 1;k<=2;k++){
          digitalWrite(PinVoltage, HIGH);
          digitalWrite(PinS0, r0);
          digitalWrite(PinS1, r1);
          digitalWrite(PinS2, r2);
          delay(500);
          digitalWrite(PinVoltage, LOW);
          digitalWrite(PinS0, LOW);
          digitalWrite(PinS1, LOW);
          digitalWrite(PinS2, LOW);
        }
    
    }  
   i++; 
}
}

As you see the function checks one by one whether lights shall be switched. If there is a light to switch the "state array" SL is set to that state and the switch is performed. In any case the change array SLC is resetted to 0. You notice that the switch routine runs twice. This is because I noticed that my old remote sometimes needs two (or even more, but thats easy to set now) "tries" to switch. I found out that 500 ms is a good time to simulate a deep, forceful finger press.

∧ back_to_top()

Pachube

Stop! If you do not know what Pachube is, please check their site here. They offer a wonderful service that is like a "Twitter" for sensors. Phatduino posts sensor data to Pachube to make it available for 3rd party applications and projects, to spread it into the internet of things!

So now that you know what Pachube is we start right into the Pachube communication function pachube_in_out(). It is nothing really special, I got almost all of it from this website. The only thing I added is the delay at the end. This is a workaround for a problem I was facing with my ethernet interface. These are important variables that have been declared at some previous point of the code:

  • Client pachubeClient(remoteServer, 80); remoteServer is the IP of the Pachube server, it connects via port 80.
  • int Whole, Fract, SignBit These are values belonging to the temperature sensor and important for giving decimal and negative values to Pachube API.
  • tempp,light,moisture,access_var,bed Guess what. Sensor data of course.
void pachube_in_out(){

    Serial << F("Connecting...");
    if (pachubeClient.connect()) {

        if (SignBit) // If its negative
            {
              if (Fract > 10){
                   sprintf(pachube_data,"%d,%d,%d,%d,%d,%d,%d,-%d.%d,%d,%d",
¬ SL[1],SL[2],SL[3],SL[4],int(tempp),light,moisture,Whole,Fract,access_var,bed);                   }else{                     sprintf(pachube_data,"%d,%d,%d,%d,%d,%d,%d,-0%d.%d,%d,%d",
¬ SL[1],SL[2],SL[3],SL[4],int(tempp),light,moisture,Whole,Fract,access_var,bed);                   }             }else{               if (Fract > 10){                 sprintf(pachube_data,"%d,%d,%d,%d,%d,%d,%d,%d.%d,%d,%d",
¬ SL[1],SL[2],SL[3],SL[4],int(tempp),light,moisture,Whole,Fract,access_var,bed);               }else{                 sprintf(pachube_data,"%d,%d,%d,%d,%d,%d,%d,%d.0%d,%d,%d",
¬ SL[1],SL[2],SL[3],SL[4],int(tempp),light,moisture,Whole,Fract,access_var,bed);               }             }                  int content_length = strlen(pachube_data);       pachubeClient.print("PUT /api/");       pachubeClient.print(SHARE_FEED_ID);       pachubeClient.print(".csv HTTP/1.1\nHost: pachube.com\nX-PachubeApiKey: xxx");       pachubeClient.print("\nUser-Agent: Arduino\nContent-Type: text/csv\nContent-Length: ");       pachubeClient.print(content_length);       pachubeClient.print("\nConnection: close\n\n");       pachubeClient.print(pachube_data);       pachubeClient.print("\n");       Serial << F("finished PUT: ") << millis();       pachubeClient.stop();              while(pachubeClient.status() != 0) {          delay(5);       }        } }

To cut a long story short the function creates a string based on the required formatting of the outside temperature (which is a decimal value) that is posted to pachube. After that the function waits for the connection to the Pachube client to close.

∧ back_to_top()

Writing present data to user interface

The function write_present() ends data via the variable "aaaa" in a post method to a php script that stores the data in a MySQL database, a textfile or whatever (you are able to configure this in the php file). The code is pretty simple. But just let me tell you something about the variable "lcm" - "SLC" you already know.

  • int lcm[] = {0,0,0,0,0} This array stores information whether a light switch is in "manual mode" or not. Manual mode means that the light's status must not be changed by the automation (the exception is the switch off of all lights when the door is locked, but check this out here.)
  • Client lclient(light_server, 80) light_server is the IP of the server the php script is stored, it connects via port 80.
void write_present(){
  if (lclient.connect()) { 
     lclient <<  "POST http://herecomesyourpage/write1.php HTTP/1.0\nHost: localhost
¬ \nUser-Agent: Arduino\nConnection: close\nContent-Length: 14\n"
;       lclient << "Content-Type: application/x-www-form-urlencoded\n\n" << "aaaa=" << SL[1]
¬ << SL[2] << SL[3] << SL[4] << lcm[1] << lcm[2] << lcm[3] << lcm[4];       Serial << F("Just updated present client side data!");          }   lclient.stop();   while(lclient.status() != 0) {     delay(5);   }  }

∧ back_to_top()

Getting changes from user interface

To interact with the user it is also important to read out the changes he wants in the system. This is why we have to call a further php script reading out the values from a database/textfile that was created by a third script when the user sent the form via his browser. These values have to be sent to Arduino so that it can store them to be able to take influence in Phatduino software. This is what the function get_changes() does.

Even though the job sounds easy it was tricky to create a function that would read the values regardless of which kind of error or whatsoever faliure or page the remote server reported. By now the function checks the text received from the server for the fantasy word "valuoo". Having found this word the server reads out the desired changes, first the changes of the switches and then the states of the manual mode.
It is extraordinary important that the switch arguments represent a change call while the manual change arguments represent a current desired state. This means that there can be a check on whether the manual mode has to be changed at all. At the current state of the project this check makes no sense as you could simply write the value received from the script into the manual mode array, but for future applications I wanted to keep the possibility of triggering a manual mode change. This is where the function change_manual_mode() comes in. It is documented at the end of this chapter.
After Phatduino read the data from the server it connects again and tells a php script to reset the change request for the lights.

  • char wort[6], cx These values are used to store the characters read out from the server.
void get_changes(){
  if (lclient.connect()) {
          Serial << F("Now get light changes!");
          lclient.print("GET http://herecomesyourpage/read.php HTTP/1.0\r\nHost:
¬ yourhost\r\n\r\n"
);         } else {           Serial << F("Connection to light server failed! lclient.status = ") <<
¬ lclient.status();         }                  delay(500);                  if (lclient.connected() && lclient.available()) {           if(access_last_checked == 1){            while(lclient.available()){              wort[0] = wort[1];              wort[1] = wort[2];              wort[2] = wort[3];              wort[3] = wort[4];              wort[4] = wort[5];              wort[5] = lclient.read();                            cx = ' ';                             if(strncmp(wort, "valuoo",6) == 0){                 Serial << F("Found values!");                                  cx = lclient.read();                 if(cx == '1') SLC[1] = 1;                                  cx = lclient.read();                 if(cx == '1') SLC[2] = 1;                                  cx = lclient.read();                 if(cx == '1') SLC[3] = 1;                                  cx = lclient.read();                 if(cx == '1') SLC[4] = 1;                                  cx = lclient.read();                                  if(cx == '1'){                   change_manual_mode(1, 1);                 }else{                   change_manual_mode(1, 0);                 }                                  cx = lclient.read();                 if(cx == '1'){                   change_manual_mode(2, 1);                 }else{                   change_manual_mode(2, 0);                 }                                  cx = lclient.read();                 if(cx == '1'){                   change_manual_mode(3, 1);                 }else{                   change_manual_mode(3, 0);                 }                                  cx = lclient.read();                 if(cx == '1'){                   change_manual_mode(4, 1);                 }else{                   change_manual_mode(4, 0);                 }                      }           }         }         }         lclient.stop();                 while(lclient.status() != 0) {          delay(5);         }                   delay(1000);                if (lclient.connect()) {         lclient <<  "POST http://herecomesyourpage/write.php HTTP/1.0\nHost: localhost\n
¬ User-Agent: Arduino\nConnection: close\nContent-Length: 14\n"
;         lclient << "Content-Type: application/x-www-form-urlencoded\n\n" << "aaaa=0000" <<
¬ lcm[1] << lcm[2] << lcm[3] << lcm[4];         Serial << F("Just updated present client side data!");       }       lclient.stop();   while(lclient.status() != 0) {      delay(5);   }    }

Finally let us have a look at the function change_manual_mode().

void change_manual_mode(int port, int desired_state){
  
  if(lcm[port] != desired_state){
    lcm[port] = desired_state;
  }
  
}

∧ back_to_top()

Resetting the ethernet module

During my testing period I experienced problems with the WIZnet ethernet module making the program freeze. On the internet I heard that resetting the interface from time to time might be a solution for the problem. This is why I connected the ethernet interface reset channel to a pin and perform a reset by depowering the device for a second with the function reset_wiznet().

void reset_wiznet(){
  digitalWrite(wizPin, LOW);
  delay(1000);
  digitalWrite(wizPin, HIGH);
  delay(1000);
}

∧ back_to_top()

The setup function

This function initializes the ethernet connection, the light switches, the reset pin for the ethernet interface and the door lock sensor. As you will notice the pins are stored in seperate variables.

void setup()
{
  // setup the Ehternet library to talk to the Wiznet board
  Ethernet.begin(mac, ip);

  Serial.begin(9600);
  Serial << F("Setup is being processed...\n");
  pinMode(PinS0, OUTPUT);    // s0
  pinMode(PinS1, OUTPUT);    // s1
  pinMode(PinS2, OUTPUT);    // s2
  pinMode(PinVoltage, OUTPUT); // Voltage for 4051 switch
  pinMode(wizPin, OUTPUT); // Reset Pin for WIZnet module
  
  digitalWrite(PinVoltage, LOW);
  digitalWrite(PinS0, LOW);
  digitalWrite(PinS1, LOW);
  digitalWrite(PinS2, LOW);
  digitalWrite(wizPin, HIGH);
  
  // Following sets up Lock pin
  pinMode(lockPin, INPUT);
  
  Serial << F("Setup completed!\n");
  delay(1000);

  
}

∧ back_to_top()

Flow, please!

And now the kernel of Phatduino... the loop() function! Basically it just works as if you would be rotating around the "Arduino" box in the diagram at the top of this section. But as I said - it is only "basically".
To speak the truth I did not feel that it was necessary to do everything every time Arduino runs through the loop. Originally I defined certain periods of time for the different actions, e.g. data was sent to Pachube only once in ten seconds. I experienced that it takes a lot of memory to store those period of time float variables. Keep in mind that there have to be three values in memory for each defined period of time and the check on whether a function has to be executed: The current time, the time the check was performed last and the period of time!
As I experienced problems with Phatduino's memory usage I switched over to a different procedure: The periods are no longer stored as milliseconds but as loops, giving the possibility to use a modulo operator. This means that there have to be two simple integer variables only: One for the number of the current loop and one for the interval. The number of loops can even be set to zero again (just take care of the automation functions recording loop numbers). Of course the user's orientation in setting up intervals is a matter of the time it takes to run through a certain loop, but an average loop time could be measured easily.

There are plenty of user configurable values around, so let us first get an overview about them.

  • int loop_no This integer represents the number of loops Phatduino ran through.
  • int interval This is the interval for sending data to Pachube.
  • int client_side_update_interval, measure_interval, automation_interval I hope these intervals are self-explanatory.
void loop()
{    
    Serial.println(availableMemory());
    
    if((loop_no % client_side_update_interval) == 0){
       Serial.println("write present");
       write_present();
       delay(200);
    }
    
    if((loop_no % interval) == 0){
      pachube_in_out();
      delay(500);
    }
    
    delay(500);
    if((loop_no % measure_interval) == 0){
      measure_lmab();
    
      // Calling temp measurement from loop() causes less RAM usage
      tempp = get_temp(outside);
      get_tempS(inside);
          
      Serial << F("Temperature:\n   ") << tempp << F(" C outside temperature   \n") << "   "
¬ << Whole << "." << Fract << F(" C inside temperature\n");                  // Initiate temperature measurement       prepare_temp(outside);     }     Serial << F("-loop no. ") << loop_no << "-\n";     change_light_status();        if((loop_no % automation_interval) == 0){       Serial << F("Automation started!\n");       door_check();       night_check();       if(access_last_checked == 1){           bed_check();       }     }         light_automation();      if((loop_no % client_side_update_interval) == 0){     get_changes();   delay(500);   reset_wiznet();   delay(1000);   }     loop_no++; }

∧ back_to_top()

Pen

 Comments

From: [javascript protected email address]

Seriously... I love the way you explain everything. I am just thinking about building a 'Phatduino' myself. It is also interesting to see how it all comes together in memory usage calculation. As an Arduino newbie this is a good place to start! Greetings from California, Dan
reply()