Arduino MEGA + Ethernet Shield + NRF24l01 + RF433MHZ


Som nævnt i et par af mine andre artikler har jeg i mit sommerhus nogle temperatur-/luftfugtighedsfølere samt fjernbetjente stikkontakter styret vha. en Arduino MEGA webserver.

I denne artikel vil jeg gennemgå, hvordan jeg har lavet det.
Det bygger på RF24Network lavet af maniacbug. Se mere om maniacbugs projekter her: https://maniacbug.wordpress.com/

Jeg har brugt:

1 stk. Arduino MEGA
1 stk. Arduino Ethernet Shield (med 11x11x5.5m Aluminum Heatsink på)
1 stk. Arduino Mini Pro 5v
1 stk. 78L33 3.3V Voltage Regulator (Bruges på Mini Pro 5V for at få 3,3V til NRF24l01 modulet)
2 stk. Arduino Mini Pro 3,3V
4 stk. NRF24l01+  (VIGTIGT: må kun få 3,3V på VCC)
1 stk. RF433MHZ
3 stk. 4K7 modstande
2 stk. DS18B20
1 stk. DHT22
1 stk. FT232RL USB To Serial (til programmering af mini'erne)

Arduino IDE samt følgende library's:

RF24Network https://github.com/maniacbug/RF24Network (til mini pro'erne)
SocketSwitch https://github.com/mortensalomon/Socket_Switch
iBoardRF24/iBoardRF24Network https://github.com/andykarpov (RF24Network på andre pins end standard SPI)
digitalWriteFast http://code.google.com/p/digitalwritefast/downloads/list
Ethernet
SPI
OneWire
DallasTemperature
DHT22


Jeg vil kun løst beskrive hvordan det sættes sammen:

MEGA'en:

Sæt ethernet shield på (har nogle gange været nødt til at tage det af ved upload af programmet)

Da ethernet shield'et bruger SPI er det ikke muligt også at sætte en NRF24l01+ på, idet den også skal bruge SPI.
Man burde kunne sætte flere devices på SPI, men efter hvad jeg har kunnet læse mig til er der en fejl i ethernet library'et der gør det umuligt.
For at omgå dette kan/skal man bruge iBoardRF24Network der bruger alternativ SPI.

Sæt NRF24l01+ modulet på følgende pins:

cepin 3, cspin 4, mosi_pin 6, miso_pin 7, sck_pin 5, irq_pin 8

RF433MHZ sender DATA pin sættes på pin 30

Pro'erne:

God side, der kan bruges, når man skal sætte en NRF24l01+ på en arduino: https://arduino-info.wikispaces.com/Nrf24L01-2.4GHz-HowTo
NRF24l01 sættes på som vist herunder på en Arduino Nano (husk 3,3V regulator på mini pro 5V'en, da der ikke er 3,3V VCC):


4K7 pullup modstand på DHT22 og DS18B20


Og så til MEGA webserver koden:

Kode:
// 1: RF24 ds18b20
// 2: RF24 dht22


#include <digitalWriteFast.h>
#include <iBoardRF24Network.h>
#include <iBoardRF24.h>
#include <SPI.h>
#include <Ethernet.h>
#include <Socketswitch.h>

#define NODES 3

Socketswitch mySwitch(30);


// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(10, 10, 10, 178);

// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);

// (cepin 3, cspin 4, mosi_pin 6, miso_pin 7, sck_pin 5, irq_pin 8)
iBoardRF24 radio(3, 8, 5, 6, 7, 2);
iBoardRF24Network network(radio);

// Address of our node
const uint16_t this_node = 0;

struct payload_t  
{
  unsigned long ms;
  unsigned long counter;
  byte device_type;
  float humidity;
  float temperature;
  long voltage;
};

struct sensors_t
{
  unsigned long ms;
  unsigned long counter;
  byte device_type;
  float humidity;
  float temperature;
  long voltage;
  unsigned long local_ms;
};

struct sensors_t sensors[NODES];

String readString;

void setup() {
  // Initialize array which holds the data from nodes
  for (int i = 0; i < NODES; i++) {
    sensors[i].device_type = 0;
    sensors[i].humidity = 0;
    sensors[i].temperature = 0;
    sensors[i].voltage = 0;
    sensors[i].counter = 0;
    sensors[i].ms = 0;
    sensors[i].local_ms = 0;
  }

  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();

  mySwitch.setDevice(0x34FCFF);

  SPI.begin();
  radio.begin();
  network.begin(/*channel*/ 93, /*node address*/ this_node);
}


void socketSwicth(int id, int stat) {
  switch (id) {
    case 0:
      if (stat == 1) {
        mySwitch.channelOn(0);
      } else {
        mySwitch.channelOff(0);
      }
      break;
    case 1:
      if (stat == 1) {
        mySwitch.channelOn(1);
      } else {
        mySwitch.channelOff(1);
      }
      break;
    case 2:
      if (stat == 1) {
        mySwitch.channelOn(2);
      } else {
        mySwitch.channelOff(2);
      }
      break;
    case 9:
      if (stat == 1) {
        mySwitch.groupOn();
      } else {
        mySwitch.groupOff();
      }
      break;
  }
}

void loop() {

  const unsigned long tenMinutes = 10 * 60 * 1000UL;
  static unsigned long lastSampleTime = 0 - tenMinutes;  // initialize such that a reading is due the first time through loop()

  unsigned long now = millis();
  if (now - lastSampleTime >= tenMinutes)
  {
    lastSampleTime += tenMinutes;
  }

  // Pump the network regularly
  network.update();

  // Is there anything ready for us?
  while ( network.available() )
  {
    // If so, grab it and print it out
    RF24NetworkHeader header;
    payload_t payload;
    network.read(header, &payload, sizeof(payload));
    sensors[header.from_node - 2].device_type = payload.device_type;
    sensors[header.from_node - 2].humidity = payload.humidity;
    sensors[header.from_node - 2].temperature = payload.temperature;
    sensors[header.from_node - 2].voltage = payload.voltage;
    sensors[header.from_node - 2].counter = payload.counter;
    sensors[header.from_node - 2].ms = payload.ms;
    sensors[header.from_node - 2].local_ms = millis();
  }

  // listen for incoming clients
  readString = "";
  EthernetClient client = server.available();
  if (client) {
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (readString.length() < 100) {
          readString += c;
        }
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          //	  client.println("Refresh: 5");  // refresh the page automatically every 5 sec
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html><head><style>button { width: 400px;  height: 100px; font-size:48px;  }</style></head><title>Salomon Switch server (Remote: ??)</title>");
          if (readString.indexOf("status=yes") > 0) {

            client.println("<center><h1>Status:</h1><br><table>");
            for (int i = 0; i < NODES; i++) {
              client.print("<tr><td>Node:</td><td>");
              client.print(i + 2);
              client.print("</td><td>Device type:</td><td>");
              client.print(sensors[i].device_type);
              client.print("</td><td>Humidity:</td><td>");
              client.print(sensors[i].humidity);
              client.print("</td><td>Temperature:</td><td>");
              client.print(sensors[i].temperature);
              client.print("</td><td>Voltage:</td><td>");
              client.print(sensors[i].voltage);
              client.print("</td><td>Packet #");
              client.print(sensors[i].counter);
              client.print(" at ");
              client.print(sensors[i].ms);
              client.print("</td><td>Received:");
              client.print(sensors[i].local_ms);
              client.print("</td></tr>");
            }
            client.println("</table><br><br><a href=/?switch=yes>Back to control</a></center>");
          } else {
            if (readString.indexOf("switch=yes") > 0) {
              client.println("<center><h1>Control:</h1><br><form method=get><input type=hidden name=switch value=yes><button name=sw type=submit value=01>SW1 ON</button> <button name=sw type=submit value=00>SW1 OFF</button><br><button name=sw type=submit value=11>SW2 ON</button> <button name=sw type=submit value=10>SW2 OFF</button><br><button name=sw type=submit value=21>SW3 ON</button> <button name=sw type=submit value=20>SW3 OFF</button><br><button name=sw type=submit value=91>SWG ON</button> <button name=sw type=submit value=90>SWG OFF</button></form><br><a href=/?status=yes>Status</a></center>");
              int pos = readString.indexOf("sw=");

              if (pos > 0) {
                socketSwicth(readString.substring(pos + 3, pos + 4).toInt(), readString.substring(pos + 4, pos + 5).toInt());
              }
              client.println("<br />");
            }
          }
          client.println("<p><center><h1>Dansk Elektronik Forum's artikel konkurrence  </h1><br><a href=http://elektronik-forum.dk/index.php?topic=987&asarticle target=_new>Dansk Elektronik Forum artikel</a></center></html>");
          break;
        }

        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        }
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
  }
}

Sæt MEGA'en til strøm og netværk. Du kan fange status siden via. http://din ip/status=yes



Og stikkontakt styring via. http://din ip/switch=yes




Og koden til DS18B20'erne (low power software version - husk at skifte this_node):

Kode:
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "LowPower.h"

#define ONE_WIRE_BUS 7
#define TEMPERATURE_PRECISION 12


// nRF24L01(+) radio attached using Getting Started board
RF24 radio(9, 10);

// Network uses that radio
RF24Network network(radio);

// Address of our node MAX 6 Nodes
const uint16_t this_node = 3;


// Address of the other node
const uint16_t other_node = 0;


// 1: ds18b20
// 2: dht22

const byte this_type = 1;


// How often to send 'hello world to the other unit
const unsigned long interval = 2000; // 2000; //ms

// When did we last send?
unsigned long last_sent;

// How many have we sent already
unsigned long packets_sent;

// Temp
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

DeviceAddress termo;

float tempC;

// Structure of our payload
struct payload_t
{
  unsigned long ms;
  unsigned long counter;
  byte device_type;
  float humidity;
  float temperature;
  long voltage;
};

boolean serial = false;
boolean device_found;

long readVcc() {
  long result;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA, ADSC));
  result = ADCL;
  result |= ADCH << 8;
  result = 1125300L / result; // Back-calculate AVcc in mV

  return result;
}

void sleepTenMinutes()
{
  for (int i = 0; i < 75; i++) {
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
  }
}


void setup(void)
{
  if (serial) {
    Serial.begin(57600);
    Serial.println("RF24Network/NODE5_tx/");

  }
  pinMode(8, OUTPUT);
  SPI.begin();
  radio.begin();
  network.begin(/*channel*/ 93, /*node address*/ this_node);
  sensors.begin();
  device_found = sensors.getAddress(termo, 0);
  if (device_found) sensors.setResolution(termo, TEMPERATURE_PRECISION);
  if (serial) {
    Serial.print("Device found: ");
    Serial.println(device_found);
  }

}

void loop(void)
{

  // Pump the network regularly
  network.update();

  // If it's time to send a message, send it!
  //  unsigned long now = millis();
  //  if ( now - last_sent >= interval  ) {
  if (device_found) {
    sensors.requestTemperatures();
    tempC = sensors.getTempC(termo);
  } else {
    device_found = sensors.getAddress(termo, 0);
    if (device_found) sensors.setResolution(termo, TEMPERATURE_PRECISION);
    tempC = -123;
  }
  byte hum = 0;
  //   last_sent = now;


  if (serial)   Serial.print("Sending...");
  payload_t payload = { millis(), packets_sent++, this_type, hum, tempC, readVcc()};
  RF24NetworkHeader header(/*to node*/ other_node);
  bool ok = network.write(header, &payload, sizeof(payload));

  if (ok) {
    if (serial) Serial.println("ok.");
  } else {
    if (serial) Serial.println("failed.");
    digitalWrite(8, HIGH);   // sets the LED on
    delay(1000);                  // waits for a second
    digitalWrite(8, LOW);    // sets the LED off

  }

  radio.powerDown();
  sleepTenMinutes();
}

Der skal sættes en diode via. en modstand på pin 8. Den lyser 1 sek., når afsendelse fejler.


Og koden til DHT22'eren:

Kode:
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#include "LowPower.h"
#include <dht.h>

dht DHT;

// nRF24L01(+) radio attached using Getting Started board
RF24 radio(9, 10);

// Network uses that radio
RF24Network network(radio);

// Address of our node MAX 6 Nodes
const uint16_t this_node = 2;


// Address of the other node
const uint16_t other_node = 0;


// 1: ds18b20
// 2: dht22

const byte this_type = 2;

// How many have we sent already
unsigned long packets_sent;

// Structure of our payload
struct payload_t
{
  unsigned long ms;
  unsigned long counter;
  byte device_type;
  float humidity;
  float temperature;
  long voltage;
};

boolean serial = true;

float humidity;
float temperature;


long readVcc() {
  long result;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA, ADSC));
  result = ADCL;
  result |= ADCH << 8;
  result = 1125300L / result; // Back-calculate AVcc in mV

  return result;
}

void sleepFiveMinutes()
{
  for (int i = 0; i < 38; i++) {
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
  }
}


void setup(void)
{
  if (serial) {
    Serial.begin(57600);
    Serial.println("RF24Network/NODE2_tx/");
  }
  pinMode(8, OUTPUT);
  SPI.begin();
  radio.begin();
  network.begin(/*channel*/ 93, /*node address*/ this_node);
}

void loop(void)
{

  // Pump the network regularly
  network.update();

  uint32_t start = micros();
  int chk = DHT.read22(3);
  uint32_t stop = micros();

  switch (chk)
  {
    case DHTLIB_OK:
      if (serial) Serial.print("OK,\t");
      if (serial) {
        Serial.print(DHT.humidity, 2);
        Serial.print(",\t");
        Serial.println(DHT.temperature, 2);
      }
      humidity = DHT.humidity;
      temperature = DHT.temperature;
      break;
    case DHTLIB_ERROR_CHECKSUM:
      if (serial) Serial.print("Checksum error,\t");
      humidity = -123;
      temperature = -123;
      break;
    case DHTLIB_ERROR_TIMEOUT:
      if (serial) Serial.print("Time out error,\t");
      humidity = -123;
      temperature = -123;

      break;
    default:
      if (serial) Serial.print("Unknown error,\t");
      humidity = -123;
      temperature = -123;

      break;
  }

  if (serial)   Serial.print("Sending...");
  payload_t payload = { millis(), packets_sent++, this_type, humidity, temperature, readVcc()};
  RF24NetworkHeader header(/*to node*/ other_node);
  bool ok = network.write(header, &payload, sizeof(payload));

  if (ok) {
    if (serial) Serial.println("ok.");
  } else {
    if (serial) Serial.println("failed.");
    digitalWrite(8, HIGH);
    delay(1000);
    digitalWrite(8, LOW);

  }
  sleepFiveMinutes();
}

Der skal sættes en diode via. en modstand på pin 8. Den lyser 1 sek., når afsendelse fejler.


NRF24l01 modulerne kan kun "snakke" 6 stk. indbyrdes, så man bliver nødt til at lave en router, hvis man skal have flere en 5 sensorer på.

Jeg håber, at den kan bruges og inspirere til leg med Arduino.

P.S.
Jeg har snydt lidt, men det burde alt sammen virke (Dvs. at jeg ikke har testet pro'erne).
Jeg har nemlig kun 3 stk. hjemmelavet low power atmega328p (8 mhz internt) som kører på 2 stk. AA batterier.