Category: "Nano"

Motion Sensors, a switch and a relay

January 3rd, 2016

In this part we're going to expand the node to have some additional functionality.
The node is going to get treated to a couple of motion sensors, a relay output and a switch.

I've added two three way screw down connectors, one for each motion sensor, also added a three way connector for the relay and a two pin connector for the switch.
The switch could be a manual push button or a door contact etc.

First, because I've got space on the board I'm going to add a strip of 4 ground connections. I don't need them at this point but they are useful to have when testing

 



Next I'm going to add the three way connector for the relay. I'm going to drive a 5V relay as pictured below



The underside of the connector



And the topside of the relay connector



Ok, it's confession time. I was originally going to use a 12V relay, but couldn't get it to work as it would have required a driver transistor to reach its turn on voltage so some of the later pictures here may show the power of the relay connected to 12V, not the 5V as shown in the image below.



Next I'm going to add two three way screw down connectors, one for each of the motion sensors. I need to slightly enlarge the holes on the perf board to accomodate the pin diameter of the connectors. I have used two different colours for clarity



The movement sensors I'm using are inexpensive passive IR sensors, I run them at 5V and when tripped they give a 3.3V output. To ensure there are no spurious signals I want to fit a pull-down resistor to each of the movement sensor data pins. 10K will suffice. I prefer to have physical pull-up and pull-down resistors, but am aware that the Arduino can do this internally ( at least for the pull-ups ), so the choice is up to you.



Now I'm going to add a two way connector for a switch. This will use a pull-up resistor to keep the input high when the switch is not pressed. It is a momentary switch (doesn't stay latched ) so when the button is pushed the switch pin ( D2 ) will go low and when it is released D2 will go high again.



I've also added another connector for the PWM outputs, so the board now has male and female connectors for the PWM, just for convenience.



Now we're good to go with the hardware after double checking for short circuits. I use a current limiting bench power supply to run the node before connecting it to the computer, this allows me to limit the current to none damaging levels and is the result of not finding some shorts.

Let's move over to PiDome and set up the new hardware.

/* PiDome_Man Cave 1 Tutorial Code
 
  Spiralux 2015
 
  man cave is going to have 4 x pwm - code done 04 Dec 2015
  will also have control over a 4 way power strip
  has a DHT11 temp/humidity Sensor and LDR
  The relay for the power strip has been added and the code done 11th Dec 2015
  Added connections for a switch on digital pin 2 and connections for two IR movement sensors
  connected to A0 and A1. A0 and A1 have pull down 10K resistors. D2 has a 10K pull up resistor
  The analogue pins (A0 and A1 ) will be used as digital input pins. The switch input will go low on switch press
  Code for switch and both motion sensors has been added, with debouncing on the switch
*/
#define SN "Man Cave Tutorial Code"
#define SV "1.4"

#include <MySensor.h>
#include <SPI.h>
#include <DHT.h>  /* include the DHT library */
#include <Bounce2.h> /* for debouncing switch presses */

#define CHILD_ID_HUM 0 /* child ID of the humidity sensor as presented to PiDome */
#define CHILD_ID_TEMP 1 /* child ID of the temperature sensor as presented to PiDome */
#define HUMIDITY_SENSOR_DIGITAL_PIN 8 /* the digital pin the DHT11 is connected to */
#define CHILD_ID_LIGHT 2 /* child ID of the LDR */
#define LIGHT_SENSOR_ANALOG_PIN 21 /* Analogue pin the LDR is connected to - A7 in this case */
#define PWM1_PIN 3 /* PWM 1 Digital Pin */
#define PWM2_PIN 6
#define PWM3_PIN 9
#define PWM4_PIN 10
#define CHILD_ID_PWM1 3 /* Child ID for PWM1. These are the Group ID numbers in PiDome */
#define CHILD_ID_PWM2 6
#define CHILD_ID_PWM3 9
#define CHILD_ID_PWM4 10
#define RF24_CE_PIN 5 /*CE pin */
#define RF24_CS_PIN 7 /* CS pin, using 7 as pin 6 is a PWM pin on the Nano */
#define MAXIMUM_LED_BRIGHTNESS 225 /* I usually run the LEDs below max power to lower heat generation and current consumption */
                                   /*   If you want to run up to full brightness change this to 255 */
#define CHILD_ID_RELAY 4 /* the child ID of the relay for the power strip */
#define RELAY_PIN 4 /* the digital pin to drive the relay */
#define RELAY_ON 0  /* GPIO value to write to turn on attached relay  */
#define RELAY_OFF 1 /* GPIO value to write to turn off attached relay */

#define SWITCH_PIN 2 /* the digital pin the switch is connected to, other side of switch to ground */
#define SWITCH_CHILD_ID 5 /* the child id of the switch to present to the gateway */

#define MOVEMENT_SENSOR_1_PIN 14 /* Pin A0 for first movement sensor */
#define MOVEMENT_SENSOR_2_PIN 15 /* Pin A1 for second movement sensor */
#define MOVEMENT_SENSOR_1_CHILD_ID 7 /* Child id for the first movement sensor */
#define MOVEMENT_SENSOR_2_CHILD_ID 8 /* Child id for the second movement sensor */


 /* NRFRF24L01 radio driver (set high transmit power by default) */
MyTransportNRF24 radio(RF24_CE_PIN, RF24_CS_PIN, RF24_PA_LEVEL);

/* Select AtMega328 hardware profile */
MyHwATMega328 hw;
/* Construct MySensors library */
MySensor gw(radio, hw);

int loopCounter = 0;
DHT dht;
float lastTemp; /* holds the last temperature reading */
float lastHum; /* holds the last humidty reading */
boolean metric = true; /* if you're in the USA you might want false here to have temperatures reported in Farenheit */
MyMessage msgHum(CHILD_ID_HUM, V_HUM); /* Humidity message */
MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP); /* Temperature message */
MyMessage msgLight(CHILD_ID_LIGHT, V_LIGHT_LEVEL); /* Light level message */
MyMessage msgMotion1(MOVEMENT_SENSOR_1_CHILD_ID, V_TRIPPED);
MyMessage msgMotion2(MOVEMENT_SENSOR_2_CHILD_ID, V_TRIPPED);
MyMessage msgSwitch1(SWITCH_CHILD_ID, V_TRIPPED );
int lastLightLevel; /* holds the last light level */
int node_id = 30; /* set node ID here for manual */
int switch_1_OldValue = 1; /* for tracking changes in the switch state */
/* switch debounce */
Bounce debouncer = Bounce();

/* motion sensor states */
bool sensor1CurrentState = 0; /* set the current state of the sensor to a known value */
bool sensor1PreviousState = 0; /* and the previous state to the same as the current state */
bool sensor2CurrentState = 0;
bool sensor2PreviousState = 0;

void setup(){
    gw.begin(processIncomingMessage, node_id, true); /* start the node, acting as a repeater with a fixed node ID ( 30 in this case) */
    dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN); /* setup the DHT11 on digital pin 8 */
    
    gw.sendSketchInfo(SN, SV); /* Send the Sketch Version Information to the Gateway */
    /* Register all sensors to gw (they will be created as child devices) */
  /* humidity / temp */
  gw.present(CHILD_ID_HUM, S_HUM); /* present humidity sensor */
  gw.present(CHILD_ID_TEMP, S_TEMP); /* present temperature sensor */
  metric = gw.getConfig().isMetric;
  /* light level */
  gw.present(CHILD_ID_LIGHT, S_LIGHT_LEVEL); /* present light level sensor */
  /* PWM (Dimmer) setup */
  gw.present(CHILD_ID_PWM1, S_DIMMER); /* Present the dimmers */
  gw.present(CHILD_ID_PWM2, S_DIMMER);
  gw.present(CHILD_ID_PWM3, S_DIMMER);
  gw.present(CHILD_ID_PWM4, S_DIMMER);
  pinMode(PWM1_PIN, OUTPUT); /* Set the PWM pins to output */
  pinMode(PWM2_PIN, OUTPUT);
  pinMode(PWM3_PIN, OUTPUT);
  pinMode(PWM4_PIN, OUTPUT);
  analogWrite(PWM1_PIN, MAXIMUM_LED_BRIGHTNESS * gw.loadState(CHILD_ID_PWM1) / 100); /* Get value from RAM and write to output */
  analogWrite(PWM2_PIN, MAXIMUM_LED_BRIGHTNESS * gw.loadState(CHILD_ID_PWM2) / 100);
  analogWrite(PWM2_PIN, MAXIMUM_LED_BRIGHTNESS * gw.loadState(CHILD_ID_PWM3) / 100);
  analogWrite(PWM2_PIN, MAXIMUM_LED_BRIGHTNESS * gw.loadState(CHILD_ID_PWM4) / 100);

  /* Request the dimmer values from the gateway */
  gw.request(CHILD_ID_PWM1, V_DIMMER);
  gw.process();
  gw.request(CHILD_ID_PWM2, V_DIMMER);
  gw.process();
  gw.request(CHILD_ID_PWM3, V_DIMMER);
  gw.process();
  gw.request(CHILD_ID_PWM4, V_DIMMER);
  gw.process();

  /* present the relay for the man cave 1 power board */
  gw.present(CHILD_ID_RELAY, S_LIGHT);
  /* Then set relay pins in output mode */
  pinMode(RELAY_PIN, OUTPUT);   
  /* Set relay to last known state (using eeprom storage) */
  digitalWrite(RELAY_PIN, gw.loadState(CHILD_ID_RELAY)?RELAY_ON:RELAY_OFF);

  /* present the switch */
  pinMode(SWITCH_PIN, INPUT);
  /* digitalWrite(SWITCH_PIN, HIGH); */ /* activate the pull up resistor - don't need this as I've physically fitted a pullup */
  debouncer.attach(SWITCH_PIN); /* attach the switch to the debouncer */
  debouncer.interval(5); /* set the debounce interval */
  gw.present(SWITCH_CHILD_ID, S_DOOR); /* Present the switch */
}
void loop(){

    gw.process(); /* receive/send messages to/from the gateway */
  /* read the switch */
  debouncer.update();
  /* Get the update value */
  int value = debouncer.read();
 
  if (value != switch_1_OldValue) {
    /* Send the switch state to the gateway */
    gw.send(msgSwitch1.set(value==HIGH?0:1));
    switch_1_OldValue = value; /* store the current value as the previous value */
  }
    loopCounter ++;
    if (loopCounter % 23000 == 0){ /* check the temperature and humidity every 23000 iterations of the loop */
        getTempHumidity();
    }
    if (loopCounter % 25000 == 0){ /* check the light level */
        int lightLevel = 100-((1023-analogRead(LIGHT_SENSOR_ANALOG_PIN))/10.23); /* gives a range of 0-100 (100 being full brightness) */
      if (lightLevel != lastLightLevel) { /* if the light level is not the same as the previous reading */
          gw.send(msgLight.set(lightLevel)); /* send the new light level */
          lastLightLevel = lightLevel; /* store the current light level for comparison next time */
          Serial.print("Light level = ");
          Serial.println(lightLevel);
      }
    }
  /* read the state of the first motion sensor */
  sensor1PreviousState = sensor1CurrentState;
  sensor1CurrentState = digitalRead(MOVEMENT_SENSOR_1_PIN);
  if (sensor1CurrentState != sensor1PreviousState){ /* then sensor 1 has changed state */
    gw.send(msgMotion1.set(sensor1CurrentState?"1":"0"));  /* Send tripped value to gw */
  }

  /* and the state of the second motion sensor */
  sensor2PreviousState = sensor2CurrentState;
  sensor2CurrentState = digitalRead(MOVEMENT_SENSOR_2_PIN);

  if (sensor2CurrentState != sensor2PreviousState){ /* then sensor 2 has changed state */
    gw.send(msgMotion2.set(sensor2CurrentState?"1":"0"));  /* Send tripped value to gw */
  }

}

void processIncomingMessage(const MyMessage &message) /* this will process the incoming messages */
{
    if (message.type == V_PERCENTAGE){ /* then we have a V_PERCENTAGE message - note V_DIMMER is deprecated as of MySensors V1.5 */
        uint8_t incomingDimmerStatus = atoi(message.data);    /*get the message data as an int */
        analogWrite(message.sensor, MAXIMUM_LED_BRIGHTNESS * incomingDimmerStatus / 100); /* Change Dimmer level */
        gw.saveState(message.sensor, incomingDimmerStatus); /*Save value to RAM */
  } else if (message.type == V_STATUS) { /* this is 1.5 code, if using 1.4 V_LIGHT is what you need */
      if (message.sensor == CHILD_ID_RELAY){ /* then we are turning the power strip/relay on */
        digitalWrite(RELAY_PIN, message.getBool()?RELAY_ON:RELAY_OFF);
        gw.saveState(message.sensor, message.getBool()?RELAY_ON:RELAY_OFF);
      }
     /* save the state in eeprom */
  } else { /* the message type was something else */
        Serial.print("Message type = ");
        Serial.println(message.type);
        Serial.print("Message data = ");
        Serial.println(message.data);
    }
}

void getTempHumidity(){
  float temperature = dht.getTemperature(); /* get the current temperature */
  if (isnan(temperature)) { /* if the temperature is NaN ( Not a Number) */
      Serial.println("Failed reading temperature from DHT"); /* error message */
  } else if (temperature != lastTemp) { /* if the temperature has changed */
    lastTemp = temperature; /* store the current temperature for later comparison */
    if (!metric) { /* if we're not metric then convert the temperature */
      temperature = dht.toFahrenheit(temperature);
    }
    gw.send(msgTemp.set(temperature, 1)); /* send the temperature message */
    Serial.print("T: ");
    Serial.println(temperature);
  }
 
  float humidity = dht.getHumidity();
  if (isnan(humidity)) {
      Serial.println("Failed reading humidity from DHT");
  } else if (humidity != lastHum) { /* if the humidity has changed since the previous reading */
      lastHum = humidity; /* store it */
      gw.send(msgHum.set(humidity, 1)); /* send it */
      Serial.print("H: ");
      Serial.println(humidity);
  }
}



Let's add the relay to our current node, so create a new group with the appropriate child id



Now add a toggle control to the group with the control id of V_STATUS



Another group, this time for the switch



and configure the switch

 

I mentioned before about the two motion sensors, let's set them up now

Add the group



Configure the sensor ( as a switch )


Add a group for the second motion sensor



and again configure the sensor



Right, now we have our four new sensors/devices added as shown below


As I'm not using the RGB custom device driver that was created ( by cloning ) in the previous tutorial I'll delete it.

 


Let's go edit the dashboard now and add the relay


then the switch



and finally the motion sensors









 





 

 

 

Driving an RGB LED strip

January 3rd, 2016

In this part of the tutorial we're going to use the existing PWM devices (well three of them anyway) from the previous tutorial. We'll use pins 3,6 & 9 to drive an LED RGB strip.
The strip has been cut to about 1 metre and is mounted inside a 16mm x 16mm aluminium channel. The opening on the channel is 13mm wide and I got some opal white acrylic sheet cut fit, locally I can get two strips (2440mm long x 13mm wide) for about AU$5.

We're going to connect the 12V supply to the +12V of the RGB strip. On my board the orange screw down tag strip nearest the radio provides easy access to the 12V. The Red connection from the RGB strip goes to FET1 drain which is driven by pin 3, the Green to FET2 (pin 6 ) and the Blue to FET3 ( pin 9).

We don't need to add any extra hardware to this board for this tutorial and I'm just going to clone the device skeleton we used earlier.

Here's the code. The RGB code is borrowed from redviper2100 and modified to store the last value locally as well as requesting the status of the panel from the gateway on boot.

/* PiDome_Man Cave 1 Tutorial Code
 
  Spiralux 2015
 
  man cave is going to have 4 x pwm
  will also have control over a 4 way power strip
  has a DHT11 temp/humidity Sensor
*/
#define SN "Man Cave Tutorial Code"
#define SV "1.0"

#include <MySensor.h>
#include <SPI.h>
#include <DHT.h>  /* include the DHT library */

#define CHILD_ID_HUM 0 /* child ID of the humidity sensor as presented to PiDome */
#define CHILD_ID_TEMP 1 /* child ID of the temperature sensor as presented to PiDome */
#define HUMIDITY_SENSOR_DIGITAL_PIN 8 /* the digital pin the DHT11 is connected to */
#define CHILD_ID_LIGHT 2 /* child ID of the LDR */
#define LIGHT_SENSOR_ANALOG_PIN 21 /* Analogue pin the LDR is connected to - A7 in this case */
#define RF24_CE_PIN 5 /*CE pin */
#define RF24_CS_PIN 7 /* CS pin, using 7 as pin 6 is a PWM pin on the Nano */

/* RGB code based on redviper2100s code here forum.pidome.org/viewtopic.php?id=58 */
#define RED  3  /* Arduino PWM pin for Red */
#define GREEN 6 /* Arduino PWM pin for Green */
#define BLUE 9  /* Arduino PWM pin for Blue */
#define strip 1  /* sensor number needed in the custom devices set up */

int RGB_pins[3] = {RED,GREEN,BLUE};
long RGB_values[3] = {0,0,0};

/* NRFRF24L01 radio driver (set high transmit power by default) */
MyTransportNRF24 radio(RF24_CE_PIN, RF24_CS_PIN, RF24_PA_LEVEL);

/* Select AtMega328 hardware profile */
MyHwATMega328 hw;
/* Construct MySensors library */
MySensor gw(radio, hw);

int loopCounter = 0;
DHT dht;
float lastTemp; /* holds the last temperature reading */
float lastHum; /* holds the last humidty reading */
boolean metric = true; /* if you're in the USA you might want false here to have temperatures reported in Farenheit */
MyMessage msgHum(CHILD_ID_HUM, V_HUM); /* Humidity message */
MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP); /* Temperature message */
MyMessage msgLight(CHILD_ID_LIGHT, V_LIGHT_LEVEL); /* Light level message */
int lastLightLevel; /* holds the last light level */
int node_id = 30; /* set node ID here for manual */

void setup(){
    gw.begin(processIncomingMessage, node_id, true); /* start the node, acting as a repeater with a fixed node ID ( 30 in this case) */
    dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN); /* setup the DHT11 on digital pin 8 */
    
    gw.sendSketchInfo(SN, SV); /* Send the Sketch Version Information to the Gateway */
    /* Register all sensors to gw (they will be created as child devices) */
      /* humidity / temp */
      gw.present(CHILD_ID_HUM, S_HUM); /* present humidity sensor */
      gw.present(CHILD_ID_TEMP, S_TEMP); /* present temperature sensor */
      metric = gw.getConfig().isMetric;
      /* light level */
      gw.present(CHILD_ID_LIGHT, S_LIGHT_LEVEL); /* present light level sensor */
}

void loop(){

    gw.process(); /* receive/send messages to/from the gateway */
    loopCounter ++;
    if (loopCounter % 23000 == 0){ /* check the temperature and humidity every 23000 iterations of the loop */
        getTempHumidity();
    }
    if (loopCounter % 25000 == 0){ /* check the light level */
        int lightLevel = 100-((1023-analogRead(LIGHT_SENSOR_ANALOG_PIN))/10.23); /* gives a range of 0-100 (100 being full brightness) */
          if (lightLevel != lastLightLevel) { /* if the light level is not the same as the previous reading */
              gw.send(msgLight.set(lightLevel)); /* send the new light level */
              lastLightLevel = lightLevel; /* store the current light level for comparison next time */
              Serial.print("Light level = ");
              Serial.println(lightLevel);
          }
    }

}

void processIncomingMessage(const MyMessage &message) /* this will process the incoming messages */
{
    if (message.type == V_PERCENTAGE){ /* then we have a V_PERCENTAGE message */
        uint8_t incomingDimmerStatus = atoi(message.data);    /*get the message data as an int */
        analogWrite(message.sensor, 255 * incomingDimmerStatus / 100); /* Change Dimmer level */
        gw.saveState(message.sensor, incomingDimmerStatus); /*Save value to RAM */
    } else { /* the message type was something else */
        Serial.print("Message type = ");
        Serial.println(message.type);
        Serial.print("Message data = ");
        Serial.println(message.data);
    }
}

void getTempHumidity(){
  float temperature = dht.getTemperature(); /* get the current temperature */
  if (isnan(temperature)) { /* if the temperature is NaN ( Not a Number) */
      Serial.println("Failed reading temperature from DHT"); /* error message */
  } else if (temperature != lastTemp) { /* if the temperature has changed */
    lastTemp = temperature; /* store the current temperature for later comparison */
    if (!metric) { /* if we're not metric then convert the temperature */
      temperature = dht.toFahrenheit(temperature);
    }
    gw.send(msgTemp.set(temperature, 1)); /* send the temperature message */
    Serial.print("T: ");
    Serial.println(temperature);
  }
 
  float humidity = dht.getHumidity();
  if (isnan(humidity)) {
      Serial.println("Failed reading humidity from DHT");
  } else if (humidity != lastHum) { /* if the humidity has changed since the previous reading */
      lastHum = humidity; /* store it */
      gw.send(msgHum.set(humidity, 1)); /* send it */
      Serial.print("H: ");
      Serial.println(humidity);
  }
}



We'll need to add the RGB strip to the device skeleton. I've cloned the skeleton from one of the previous tutorials, if the PWM's are already in the skeleton they will need to be deleted.

After the device skeleton has been cloned we'll need to edit it and add the RGB Child ID. I'm going to use a high number out of the range of the node ID's I'm currently using.

 

 
I've used a Child id of 101, this needs to be the same as the 'strip' in the Arduino code.
 
 Add the colour picker
Save the cloned device
 
Cloned device visible in list
 
 
 
Add the colour picker to the dashboard
 
Now we should be cooking with gas. In the version of PiDome that I'm using there is an error generated everytime the colour is changed through the dashboard, I guess it will be fixed in the future. It doesn't seem to affect macros that change the colour, but it is visible in the video.
 

 
 
Below are some static images of some of the colours the strip produces
 
 
 
 
 
 
 
 

 

 

 

 

Adding the PWM devices

December 9th, 2015

Ok, we'll get the node to do something useful in this part. The Nano I'm using has 6 PWM pins which can be used to drive such things as LED lights and fans. These pins are 3,5,6,9,10 & 11 but we can't use 11 as it is used by the radio immutably. The default MySensors setup also uses pin 6 for the radio but in this setup it is overridden to use pin 7 instead.

PWM works by varying the 'duty cycle' of the digital output and is an power efficient dimming DC lights such  GU5.3 LED downlights. If you only want to drive a small number of LEDs then you can drive them directly from the PWM pins, but if you want to drive a more useful DC ( Direct Current ) load, such as the LED downlights, then you'll need something that can handle the power.

I'm using a FET to handle the load. The advantages and disadvantages of various types and applications for FET's are discussed at length elsewhere on the Internet. The intended load for the PWM's in this setup is less than 1 Amp at 12V which is well within the load capability without the need for heatsinking. Please check that you're operating your FETs within their specifications.

I'm using IRFZ44N's to drive the load, most sites I've seen say these are NOT suitable for directly driving from the Arduino pins but I have a load of them and they work fine for me, your mileage may vary.

The FET has three pins.

  • Gate - connected via 220Ω resistor to PWM pin
  • Source - connected to ground
  • Drain - connected to the load ( the LED strip)

The 220Ω resistor between the gate and the PWM pin is to limit the current drawn by the FET.  There is also another resistor between the gate and ground to ensure the FET is switched off when not being driven by the PWM pin. I've used 82KΩ as I couldn't find a higher value in my box of resistors.

As I'm using 4 PWM pins I need 4 sets of the above components.

WARNING: I am not responsible for you hurting yourself or others with your implementation of these tutorials. Check your wiring at least twice. If possible use a current limiting ( Bench ) power supply so your newly soldered creation doesn't emit the magic blue smoke that renders electronics useless.

I'm also going to add a small power supply ( LM2596S )to the board to supply 5V to the Arduino. The PSU will be run from 12V so we can tap the input to drive the LED. It should be noted that the PWM's in this circuit switch the ground side of the circuit and as such even when all the PWM's are switched to off there will still be 12V available for the LEDs so don't short it out when you are soldering.

OK, first step with my existing board. I've mounted all four PWMs and they'll share a common source rail. Apologies for the poor focus, but it's too late to redo now :). I've left space between the FETs to mount the resistors.

Fets added to board
Fets added - Alternative angle

 The next image shows the 220Ω resistors that are in series with the Gate and the PWM pins.

Gate resistors added

The next images show the bleed resistors ( between gate and source/ground 82KΩ ) and the wiring to the PWM pins ( 3,6,9 & 10)

Bleed resistors added
Bleed resistors added - Alternative angle

Ok, let's have a look at the underside of the board. FET's 4,3 & 2 are fully visible, but FET 1 is being a little camera shy. Note the common ground rail running down the right hand side of the FETs and the breaks in the tracks.

FETs Underside

The next image shows the 4 pin header added. One for each FET drain, the left hand one is connected to FET 1 and the right hand side one is connected to FET 4.

PWM output header pins

Let's have a look at the underside.

PWM header pins - underside

Now on to the power supply. I've adjusted the output to 5V using the little gold screw on the turnpot. I'm going to feed it with 12V which also be supplied to the PWM devices ( remember PWM is switching the ground side of the circuit not the positve side). This also take the 5V conversion away from the Arduino so it runs cooler. The power supply can be seen below.

LM2596S Power supply

Below you can see the mounting holes ( and track cuts around them ) for the power supply.

Power supply mounting holes - underside

In the next shot you can see the power supply mounted with M3 bolts. I couldn't get the nut on the lower left hand one so I secured it with a blob of solder. The output is connected to the 5V pin and the nearest ground pin on the Arduino

Power supply mounted

Now I'll mount a three way screw down terminal block for the 12V output to the PWM devices. The underside can be seen along with the stripped wire that will be soldered along them to raise the current carrying capacity above that of the copper track. The other end of this is connected to the input of the power supply.

12V common rail - underside
Once the 12V connection is soldered on all that remains is to hook up the ground to the FETs as can be seen below with the black cable running from the Arduino ground to the FETs common ground rail. Observe the build up of solder on the track to ensure greater current handling capacity.
PWM common ground connected

Ok, now the hard work is done. Next I'll upload a simple sketch that will drive the PWM outputs up and down so we can test the outputs before proceeding to the PiDome integration.

 
/* for driving pwm on pins 3,6,9 & 10 
   so pwm outputs can be tested
   It drives PWM1 and PWM3 to the same values
   And drives PWM2 and PWM4 to the inverse of PWM1/3
   eg. if PWM1 is 255 ( full ) PWM2 will be 0 ( off )
*/

int timer = 40;          
int PWM1_PIN = 3;
int PWM2_PIN = 6;
int PWM3_PIN = 9;
int PWM4_PIN = 10;
void setup() {
    Serial.begin(115200);
  /* set the PWM pins as output */
  pinMode(PWM1_PIN, OUTPUT);
  pinMode(PWM2_PIN, OUTPUT);
  pinMode(PWM3_PIN, OUTPUT);
  pinMode(PWM4_PIN, OUTPUT);
}

void loop() {
  for (int i=0;i<255;i++){ /* loop through all 256 possible values */
    analogWrite(PWM1_PIN, i);
    analogWrite(PWM2_PIN, 255-i); /* inverse of PWM1 */
    analogWrite(PWM3_PIN, i);
    analogWrite(PWM4_PIN, 255-i);
    Serial.println(i);  
    delay(timer);                     
  }

}

 
If you've got a current limiting power supply now would be the time to break it out, set its output voltage to 12V and limit the current to a low level. If you haven't got a current limiting power supply you could run the whole node of a 9V ( PP3 ) battery. A cheap zinc-carbon one will limit the current to about 400mA which may help reduce any damage caused by wiring faults. 
Once we've done that we can hook up some loads and test the outputs. I'll hook up an Osram Superstar MR16 7.8W Warm White bulb as detailed Osram LED light. Note I am not endorsing the supplier, just linking to their description.
Each LED hooked up should vary in brightness.
Here's a video showing them in action. The middle of the three GU16 has a colour temperature of 6500K ( daylight ), the ones on either side are warm white ( 3000K ). The led strip is blue. You can see the current being drawn from the PSU.

 
Now we know the PWM outputs are all working we can upload the sketch to drive them from PiDome.
 
/* PiDome_Man Cave 1 Tutorial Code
 
  Spiralux 2015
 
  man cave is going to have 4 x pwm - code done 04 Dec 2015
  will also have control over a 4 way power strip
  has a DHT11 temp/humidity Sensor and LDR
*/
#define SN "Man Cave Tutorial Code"
#define SV "1.2"

#include <MySensor.h>
#include <SPI.h>
#include <DHT.h>  /* include the DHT library */

#define CHILD_ID_HUM 0 /* child ID of the humidity sensor as presented to PiDome */
#define CHILD_ID_TEMP 1 /* child ID of the temperature sensor as presented to PiDome */
#define HUMIDITY_SENSOR_DIGITAL_PIN 8 /* the digital pin the DHT11 is connected to */
#define CHILD_ID_LIGHT 2 /* child ID of the LDR */
#define LIGHT_SENSOR_ANALOG_PIN 21 /* Analogue pin the LDR is connected to - A7 in this case */
#define PWM1_PIN 3 /* PWM 1 Digital Pin */
#define PWM2_PIN 6
#define PWM3_PIN 9
#define PWM4_PIN 10
#define CHILD_ID_PWM1 3 /* Child ID for PWM1. These are the Group ID numbers in PiDome */
#define CHILD_ID_PWM2 6
#define CHILD_ID_PWM3 9
#define CHILD_ID_PWM4 10
#define RF24_CE_PIN 5 /*CE pin */
#define RF24_CS_PIN 7 /* CS pin, using 7 as pin 6 is a PWM pin on the Nano */
#define MAXIMUM_LED_BRIGHTNESS 225 /* I usually run the LEDs below max power to lower heat generation and current consumption */
                                   /*   If you want to run up to full brightness change this to 255 */

/* NRFRF24L01 radio driver (set high transmit power by default) */
MyTransportNRF24 radio(RF24_CE_PIN, RF24_CS_PIN, RF24_PA_LEVEL);

/* Select AtMega328 hardware profile */
MyHwATMega328 hw;
/* Construct MySensors library */
MySensor gw(radio, hw);

int loopCounter = 0;
DHT dht;
float lastTemp; /* holds the last temperature reading */
float lastHum; /* holds the last humidty reading */
boolean metric = true; /* if you're in the USA you might want false here to have temperatures reported in Farenheit */
MyMessage msgHum(CHILD_ID_HUM, V_HUM); /* Humidity message */
MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP); /* Temperature message */
MyMessage msgLight(CHILD_ID_LIGHT, V_LIGHT_LEVEL); /* Light level message */
int lastLightLevel; /* holds the last light level */
int node_id = 30; /* set node ID here for manual */

void setup(){
    gw.begin(processIncomingMessage, node_id, true); /* start the node, acting as a repeater with a fixed node ID ( 30 in this case) */
    dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN); /* setup the DHT11 on digital pin 8 */
    
    gw.sendSketchInfo(SN, SV); /* Send the Sketch Version Information to the Gateway */
    /* Register all sensors to gw (they will be created as child devices) */
  /* humidity / temp */
  gw.present(CHILD_ID_HUM, S_HUM); /* present humidity sensor */
  gw.present(CHILD_ID_TEMP, S_TEMP); /* present temperature sensor */
  metric = gw.getConfig().isMetric;
  /* light level */
  gw.present(CHILD_ID_LIGHT, S_LIGHT_LEVEL); /* present light level sensor */
  /* PWM (Dimmer) setup */
  gw.present(CHILD_ID_PWM1, S_DIMMER); /* Present the dimmers */
  gw.present(CHILD_ID_PWM2, S_DIMMER);
  gw.present(CHILD_ID_PWM3, S_DIMMER);
  gw.present(CHILD_ID_PWM4, S_DIMMER);
  pinMode(PWM1_PIN, OUTPUT); /* Set the PWM pins to output */
  pinMode(PWM2_PIN, OUTPUT);
  pinMode(PWM3_PIN, OUTPUT);
  pinMode(PWM4_PIN, OUTPUT);
  analogWrite(PWM1_PIN, 255 * gw.loadState(CHILD_ID_PWM1) / 100); /* Get value from RAM and write to output */
  analogWrite(PWM2_PIN, 255 * gw.loadState(CHILD_ID_PWM2) / 100);
  analogWrite(PWM2_PIN, 255 * gw.loadState(CHILD_ID_PWM3) / 100);
  analogWrite(PWM2_PIN, 255 * gw.loadState(CHILD_ID_PWM4) / 100);

  /* Request the dimmer values from the gateway */
  gw.request(CHILD_ID_PWM1, V_DIMMER);
  gw.process();
  gw.request(CHILD_ID_PWM2, V_DIMMER);
  gw.process();
  gw.request(CHILD_ID_PWM3, V_DIMMER);
  gw.process();
  gw.request(CHILD_ID_PWM4, V_DIMMER);
  gw.process();
}

void loop(){

    gw.process(); /* receive/send messages to/from the gateway */
    loopCounter ++;
    if (loopCounter % 23000 == 0){ /* check the temperature and humidity every 23000 iterations of the loop */
        getTempHumidity();
    }
    if (loopCounter % 25000 == 0){ /* check the light level */
        int lightLevel = 100-((1023-analogRead(LIGHT_SENSOR_ANALOG_PIN))/10.23); /* gives a range of 0-100 (100 being full brightness) */
      if (lightLevel != lastLightLevel) { /* if the light level is not the same as the previous reading */
          gw.send(msgLight.set(lightLevel)); /* send the new light level */
          lastLightLevel = lightLevel; /* store the current light level for comparison next time */
          Serial.print("Light level = ");
          Serial.println(lightLevel);
      }
    }

}

void processIncomingMessage(const MyMessage &message) /* this will process the incoming messages */
{
    if (message.type == V_PERCENTAGE){ /* then we have a V_PERCENTAGE message - note V_DIMMER is deprecated as of MySensors V1.5 */
        uint8_t incomingDimmerStatus = atoi(message.data);    /*get the message data as an int */
        analogWrite(message.sensor, MAXIMUM_LED_BRIGHTNESS * incomingDimmerStatus / 100); /* Change Dimmer level */
        gw.saveState(message.sensor, incomingDimmerStatus); /*Save value to RAM */
  } else { /* the message type was something else */
        Serial.print("Message type = ");
        Serial.println(message.type);
        Serial.print("Message data = ");
        Serial.println(message.data);
    }
}

void getTempHumidity(){
  float temperature = dht.getTemperature(); /* get the current temperature */
  if (isnan(temperature)) { /* if the temperature is NaN ( Not a Number) */
      Serial.println("Failed reading temperature from DHT"); /* error message */
  } else if (temperature != lastTemp) { /* if the temperature has changed */
    lastTemp = temperature; /* store the current temperature for later comparison */
    if (!metric) { /* if we're not metric then convert the temperature */
      temperature = dht.toFahrenheit(temperature);
    }
    gw.send(msgTemp.set(temperature, 1)); /* send the temperature message */
    Serial.print("T: ");
    Serial.println(temperature);
  }
 
  float humidity = dht.getHumidity();
  if (isnan(humidity)) {
      Serial.println("Failed reading humidity from DHT");
  } else if (humidity != lastHum) { /* if the humidity has changed since the previous reading */
      lastHum = humidity; /* store it */
      gw.send(msgHum.set(humidity, 1)); /* send it */
      Serial.print("H: ");
      Serial.println(humidity);
  }
}

 
Now let's move over to the PiDome control panel and modify the device skeleton we created earlier. We'll add the four PWM devices to the skeleton and then they'll be available to the dashboard after a reboot of the sensor node. You'll need to select Management | Devices | Custom devices and then click edit on the device skeleton we want to modify.
 
Select device skeleton
 
Drag the 'Add group' control below the last entry ( the light level entry ) and fill in the details as below. The 'Group id' is the same as the PWM Child ID (and PWM pin number ) set in the Arduino code.
 
Add PWM1 group
 Next we want to drag the 'Add slider' control onto the group we've just added and configure it as below. Note we're using the V_PERCENTAGE control id as the V_DIMMER has been deprecated as of MySensors V1.5. If you're using V1.4 you'll need to use V_DIMMER here and in the Arduino message processing code.
 
Add PWM1 Slider
Save the changes and then we'll repeat the process for PWM2, PWM3 and PWM4
 
Add PWM2 Group
 
Add PWM2 slider
 
Add PWM3 group
 
Add PWM3 slider
 
Add PWM4 group
 
Add PWM4 slider
 
Now you should see the four PWM controls in the device skeleton.
 
All four PWM controls added
 
Remember to click the 'Update device' or else you'll lose the settings.
 
Remember to update device
 
Now, after you've updated the device we can set about adding the PWMs to the dashboard
Click Management | Dashboards and edit the dash you want the controls to appear on. Then click the 'Add item' button and configure the four PWM devices as shown below.
 
Add PWM1 to dash
 
Add PWM2 to dash
 
Add PWM3 to dash
 
Add PWM4 to dash
 
Now click the 'Save dashboard' button and go view the fruits of your labours.
 
Dashboard with PWM controls added
 

The video below shows a demonstration of the 4 PWM controls working. The power supply shows the approximate current that is being drawn by the node and it peaks at just under 2.5A. Each of the GU16 lights draws about 0.7A at full brightness, so the LED strip is drawing around 0.4A at full brightness.
 

 
In the next part we'll modify the code to use the same hardware to drive an RGB LED strip light from PiDome.
 

Integrating with PiDome

December 7th, 2015

Now I have a working device let's walk through the PiDome setup for the sensors. The sensors currently on the node are

  • Humidity Sensor (Child ID 0)
  • Temperature Sensor (Child ID 1)
  • LDR (Child ID 2)

The child ID's will be used in PiDome along with the MySensors type for the device ( V_HUM, V_TEMP and V_LIGHT_LEVEL )

First I need to set up a skeleton for the device by selecting Custom devices.

Creating device skeleton

On the Custom devices page there is an option to 'Create a new device'. Clicking on that gives a window that allows configuration of our new device, such as which driver it's using, what sensors it has etc. The first part of the configuration is below

Create device skeleton

I've selected the MySensors driver in the first selection box, you can choose your own device name and device description.  The 'Generated id' is automatic.

The 'Group id' needs to be the same as the CHILD_ID_HUM in the Arduino code, which in my case is 0. Choose a group name that is appropriate. I'm not 100% sure of the significance of the Group name at this point.

Select humidty group

Next we'll add a data field by dragging the 'Add data field' selector onto the group that's just been created. It'll create a blank data field and we'll need to click on the spanner to edit it.

Humidity data field added

Once the spanner is clicked there is a long list of options. At this point we're not interested in most of them.

Humidity data setup

The 'Control id' is V_HUM, the same as MySensors refers to it, the data type produced by the Arduino code is a float so select that here. The type of graph is your own choice, I haven't really explored the differences yet, neither have I hidden a device. 'Remember last value' will keep showing the last received value for the humidity sensor even if the sensor is removed from the network. The only thing I have changed in the features area is the 'Representation' to Humidity.

Now we'll do the same thing with the temperature using a 'Group id' of 1 and the V_TEMP variable, so drag the 'Add Group' below the humidity group and name it as suits.

Temperature group

Temperature data setup

Notice the representation of the Temperature. Adjust to your requirements.

Now we can set up the LDR using the 'Group id' of 2 and V_LIGHT_LEVEL,  so again drag the 'Add group' over below the temperature group.

Light level group

Light level data setup

Now we have set up the skeleton for the sensor in its current configuration. We can come back later and add more sensors to the node which will be available to all the devices using this skeleton. First though the skeleton needs to be saved.

Save device skeleton

Now we can see the new skeleton added under the custom devices

Device skeleton added

Now let's add an actual device based on this device skeleton.

Add actual deviceClick the 'Add new device' button.Add device button

Then fill in the appropriate info. Before you can proceed past this point you'll have to have defined the floors and rooms in your layout, at least the floor and room in which this sensor is going to reside. This is where we'll use the node id set in the Arduino code ( 30 ), this goes in the 'Address box'. Select the MySensors driver in the drop down at the top of the form and the node location as required.

Set device infoAfter clicking the 'Add device' button you should see your new device in the list like below

New device in list

We're ready now to add the output of the temperature, humidty and light level on a dashboard. First we need to pick the dashboard to edit. I have setup a user just for this dash.

Edit dashboard

Select dashboard to edit

Here we have the test dashboard with a couple of sensors already added.

Test dashboard

Click the 'Add item' button and select as below, choosing the device we have just added and humidity as the control to match against.

Add humidity part oneClick the 'Add item' button and then fill in the next page as below

Add humidity part twoI've chosen 'Gauge' as the display type, graph is another option you may prefer. I've set the upper limit of the humidity gauge to 100.
 Click the 'Add' button and we'll proceed to adding the temperature sensor.

Add temperature part 1

Click the 'Add' button and fill in the next page

Add temperature part 2

I've used 60 as the maximum temperature for the gauge, choose as suits your needs. Click the 'Add' button when done, then click the 'Add item' button again and we'll add the light sensor.

Add light level part one

Almost done, click the 'Add item' button and fill in the details as below.

Add light level part 2

Right, now we're done we can save the dashboard and then see the results of the hard work.

Save dashboard

Just before we get to the display it's worth looking at the messages being received by the gateway. In particular we are looking for messages from node 30. Give the node a quick reset by pushing the button on the Arduino so it can present the sensors to the gateway and then click on Drivers and select the MySensors gateway and you should see some messages from your node. Blowing on the temperature sensor will cause the output to change, but as the screen does not auto update you'll have to click on the MySensors driver again to see the new messages.

Message flow

Once your happy your sensor is communicating then log on as the user assigned to the dashboard and you should be presented with something like below.

Working dash

Looking good? Feeling pleased? You should be! Go grab a coffee and in the next part we'll add four PWM (Pulse Width Modulation) drivers suitable for driving low voltage LED strips or fans etc. We'll be using some FETs (Field Effect Transistors) to handle the load. This setup will be capable of driving four single colour LED light strips or 1 RGB LED and 1 single colour LED strips. 

 

 

 

 

 

 

Man Cave 1 Code

December 3rd, 2015

It's time now to have a look at the code for the sensor. Just to recap, I have built the node with a DHT11 temperature/humidity sensor and a simple LDR for measuring light level. I'm manually assigning the node ID, partly because I'm a control freak and partly because I've been using these nodes for a couple of years now and auto assignment of addresses was not something that worked reliably with different controllers. The code I'm running on the node is below. In the next part we'll walk through adding the node to PiDome and having it display the temperature, humidity and light level on the dashboard.

 

/* PiDome_Man Cave 1 Tutorial Code
 
  Spiralux 2015
 
  man cave is going to have 4 x pwm
  will also have control over a 4 way power strip
  has a DHT11 temp/humidity Sensor
*/
#define SN "Man Cave Tutorial Code"
#define SV "1.0"

#include <MySensor.h>
#include <SPI.h>
#include <DHT.h>  /* include the DHT library */

#define CHILD_ID_HUM 0 /* child ID of the humidity sensor as presented to PiDome */
#define CHILD_ID_TEMP 1 /* child ID of the temperature sensor as presented to PiDome */
#define HUMIDITY_SENSOR_DIGITAL_PIN 8 /* the digital pin the DHT11 is connected to */
#define CHILD_ID_LIGHT 2 /* child ID of the LDR */
#define LIGHT_SENSOR_ANALOG_PIN 21 /* Analogue pin the LDR is connected to - A7 in this case */
#define RF24_CE_PIN 5 /*CE pin */
#define RF24_CS_PIN 7 /* CS pin, using 7 as pin 6 is a PWM pin on the Nano */

/* NRFRF24L01 radio driver (set high transmit power by default) */
MyTransportNRF24 radio(RF24_CE_PIN, RF24_CS_PIN, RF24_PA_LEVEL);

/* Select AtMega328 hardware profile */
MyHwATMega328 hw;
/* Construct MySensors library */
MySensor gw(radio, hw);

int loopCounter = 0;
DHT dht;
float lastTemp; /* holds the last temperature reading */
float lastHum; /* holds the last humidty reading */
boolean metric = true; /* if you're in the USA you might want false here to have temperatures reported in Farenheit */
MyMessage msgHum(CHILD_ID_HUM, V_HUM); /* Humidity message */
MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP); /* Temperature message */
MyMessage msgLight(CHILD_ID_LIGHT, V_LIGHT_LEVEL); /* Light level message */
int lastLightLevel; /* holds the last light level */
int node_id = 30; /* set node ID here for manual */

void setup(){
    gw.begin(processIncomingMessage, node_id, true); /* start the node, acting as a repeater with a fixed node ID ( 30 in this case) */
    dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN); /* setup the DHT11 on digital pin 8 */
    
    gw.sendSketchInfo(SN, SV); /* Send the Sketch Version Information to the Gateway */
    /* Register all sensors to gw (they will be created as child devices) */
      /* humidity / temp */
      gw.present(CHILD_ID_HUM, S_HUM); /* present humidity sensor */
      gw.present(CHILD_ID_TEMP, S_TEMP); /* present temperature sensor */
      metric = gw.getConfig().isMetric;
      /* light level */
      gw.present(CHILD_ID_LIGHT, S_LIGHT_LEVEL); /* present light level sensor */
}

void loop(){

    gw.process(); /* receive/send messages to/from the gateway */
    loopCounter ++;
    if (loopCounter % 23000 == 0){ /* check the temperature and humidity every 23000 iterations of the loop */
        getTempHumidity();
    }
    if (loopCounter % 25000 == 0){ /* check the light level */
        int lightLevel = 100-((1023-analogRead(LIGHT_SENSOR_ANALOG_PIN))/10.23); /* gives a range of 0-100 (100 being full brightness) */
          if (lightLevel != lastLightLevel) { /* if the light level is not the same as the previous reading */
              gw.send(msgLight.set(lightLevel)); /* send the new light level */
              lastLightLevel = lightLevel; /* store the current light level for comparison next time */
              Serial.print("Light level = ");
              Serial.println(lightLevel);
          }
    }

}

void processIncomingMessage(const MyMessage &message) /* this will process the incoming messages */
{
    if (message.type == V_DIMMER){ /* then we have a V_DIMMER message */
        uint8_t incomingDimmerStatus = atoi(message.data);    /*get the message data as an int */
        analogWrite(message.sensor, 255 * incomingDimmerStatus / 100); /* Change Dimmer level */
        gw.saveState(message.sensor, incomingDimmerStatus); /*Save value to RAM */
    } else { /* the message type was something else */
        Serial.print("Message type = ");
        Serial.println(message.type);
        Serial.print("Message data = ");
        Serial.println(message.data);
    }
}

void getTempHumidity(){
  float temperature = dht.getTemperature(); /* get the current temperature */
  if (isnan(temperature)) { /* if the temperature is NaN ( Not a Number) */
      Serial.println("Failed reading temperature from DHT"); /* error message */
  } else if (temperature != lastTemp) { /* if the temperature has changed */
    lastTemp = temperature; /* store the current temperature for later comparison */
    if (!metric) { /* if we're not metric then convert the temperature */
      temperature = dht.toFahrenheit(temperature);
    }
    gw.send(msgTemp.set(temperature, 1)); /* send the temperature message */
    Serial.print("T: ");
    Serial.println(temperature);
  }
 
  float humidity = dht.getHumidity();
  if (isnan(humidity)) {
      Serial.println("Failed reading humidity from DHT");
  } else if (humidity != lastHum) { /* if the humidity has changed since the previous reading */
      lastHum = humidity; /* store it */
      gw.send(msgHum.set(humidity, 1)); /* send it */
      Serial.print("H: ");
      Serial.println(humidity);
  }
}