MQTT with the ENC28J60 Ethershield

My old ENC28J60 based shield surely has gathered some dust
My old ENC28J60 based shield surely has gathered some dust

While playing with MQTT on various ESP8266’s, I started to wonder if maybe I could do something with an old ENC28j60 shield and module I still had laying around and actually hardly ever used.

For the youngsters: The ENC28J60 Ethernetshield was the first shield to connect the Arduino with the internet. The major drawback of the chip was that it was lacking a stack, that subsequently had to be constructed in software. As a result it was kinda memory hungry. The initial version was without an SD card slot, the later version had an SD card slot. There were two libraries available: The Ethercard and the Ethershield library. The UIPEthernet library came later.
As far as I could figure out, the PubSubClient library that is needed for MQTT doesnt work with the original Ethershield/EtherCard libraries. It does work with the UIPEthernet library though (extended fork here). The UIPEthernet library is a smart piece of coding that made programs written for the W5100 Ethernetshield suitable to be used with the ENC28J60 Shield, simply by changing the included library. Ofcourse there is a price to pay for this, namely more memory consumption. There is also an MQTT client for the ENC28J60/Atmega328 based Nanode.

Anyway, there still is enough memory to read a DHT11 sensor an analog port and some switches. In my case those were 3 door contacts. The PubSubClient is the original from Knolleary, though normally I am more a fan of the fork by Imroy. For this example I have used the Mosquitto public broker but ofcourse any broker can be used. I have installed the Mosquitto broker on a local raspberry and I found that a call to (“raspberrypi.local”,1883) does not work, using the  local 192.168.1.xxx ip number does work though

MQTT-Output
MQTT-Output ‘Dicht’ means ‘Closed’

The program is rather ‘spartan’, to save space. Print statements have been removed or commented out after initial testing.

#include <UIPEthernet.h>
#include "PubSubClient.h"
#include "DHT.h"

#define CLIENT_ID       "UnoMQTT"
#define INTERVAL        3000 // 3 sec delay between publishing
#define DHTPIN          3
#define DHTTYPE         DHT11
bool statusKD=HIGH;//living room door
bool statusBD=HIGH;//front door
bool statusGD=HIGH;//garage door
int lichtstatus;
uint8_t mac[6] = {0x00,0x01,0x02,0x03,0x04,0x05};

EthernetClient ethClient;
PubSubClient mqttClient;
DHT dht(DHTPIN, DHTTYPE);

long previousMillis;

void setup() {
pinMode(4,INPUT_PULLUP);
pinMode(5,INPUT_PULLUP);
pinMode(6,INPUT_PULLUP);
  // setup serial communication
  //Serial.begin(9600);
  // setup ethernet communication using DHCP
  if(Ethernet.begin(mac) == 0) {
    //Serial.println(F("Ethernet configuration using DHCP failed"));
    for(;;);
  }
  // setup mqtt client
  mqttClient.setClient(ethClient);
  mqttClient.setServer("test.mosquitto.org",1883);
  //mqttClient.setServer("192.168.1.xxx",1883); //for using local broker
  //mqttClient.setServer("broker.hivemq.com",1883);
  //Serial.println(F("MQTT client configured"));

  // setup DHT sensor
  dht.begin();
  previousMillis = millis();
}

void loop() {
  statusBD=digitalRead(4);
  statusGD=digitalRead(5);
  statusKD=digitalRead(6);
  lichtstatus = analogRead(A0);
  // check interval
  if(millis() - previousMillis > INTERVAL) {
    sendData();
    previousMillis = millis();
  }
  mqttClient.loop();
}

void sendData() {
  char msgBuffer[20];
  float h=dht.readHumidity();
  float t = dht.readTemperature();
  if(mqttClient.connect(CLIENT_ID)) {
   mqttClient.publish("hal/temp", dtostrf(t, 6, 2, msgBuffer));
   mqttClient.publish("hal/humid", dtostrf(h, 6, 2, msgBuffer));
   mqttClient.publish("hal/door", (statusBD == HIGH) ? "OPEN" : "DICHT");
   mqttClient.publish("hal/garage",(statusGD == HIGH) ? "OPEN" : "DICHT");
   mqttClient.publish("hal/kamer",(statusKD == HIGH) ? "OPEN" : "DICHT");
   mqttClient.publish("hal/licht", dtostrf(lichtstatus, 4, 0, msgBuffer));
 //hal=hallway, DICHT=Closed, kamer=room, licht=light
 }
}
The old ENC28J60 shield at work
The old ENC28J60 shield at work

Just for completeness sake, this sketch takes about 77% of memory. This same sketch for the WIZ5100 based Ethernet shield, with the  Ethernet.h library takes about 52% of memory. If you decide to adapt the sketch, be careful with altering the character strings. For instance, using the string “OPEN” 3 times is likely less memory consuming than having 3 different strings of the same or even shorter length. Obviously the ‘topic’ strings take a lot of space as well and if you were to shorten them to less meaningful names, you could add more sensors such as for instance a PIR sensor.

Should you copy the program from this  blog page, you may copy ‘stray characters’  that you have to delete. You can also download it here.

A final warning… after I updated my libraries, including the Adafruit DHT library, I received an error on compiling this sketch. That disappeared when I returned it to version 1.2.1. I thought I was not using the Adafruit library but the Tillaert library, but DHT libraries come a dime a dozen so maybe my compiler linked in another than I thought.
(The sketch is my adaptation of work i found on internet, but I think the original source is from Luca Dentella)

Using the 4 pins of the ESP8266-01

esp8266_henhouseThe limited number (4) of GPIO pins on the ESP8266-01 may seem like an obstacle, for any serious application.

Yet if one uses the pins in a smart way it is very well possible to do a lot with only those 4 pins.
In some of my recent postings, I have shown the use of a DHT11 a DS18B20, an OLED, an RTC and a BMP180 with the ESP8266-01.
In this posting I set out to use 4 sensors and a display, while also uploading the acquired data to Thingspeak. It actually is expanding on a project of monitoring the atmosphere in and around my chicken coop. Yes, you could call this a weatherstation, but it is just to illustrate the use of the 4 pins, you could easily make something else this way

I will be using 2 pins for I2C (BMP180 and OLED)
1 pin for 2 DS18B20 sensors via the OneWire protocol
1 pin for the DHT11
Although the ESP8266-01 now has all its pins used, I can still add more sensors (or actuators) through the OneWire protocol and/or via the I2C protocol.

So, what do we need:
BOM

  • ESP8266-01
  • 2x DS18B20
  • 1x DHT11
  • 1x BMP180
  • OLED (optional)

and ofcourse a breadboard, a 3.3 V PSU and some breadboard wires and a Thingspeak acount

Just some remarks regarding the BOM:

  • ESP8266-01
    Obviously the project is about utilizing the limited pins of the ESP8266-01, but if you still need to buy one, you could consider an ESP8266-12 that has more pins
  • DHT11
    A cheap all purpose humidity and temperature sensor. It is not hugely accurate but it will do. If you still need to buy one, you could opt for the DHT22 that is supposedly more accurate, but you could also opt for the AMS2321. That is a sort of DHT22 that is suitable for I2C, thus freeing another pin
  • BMP180
    measures temperature and Airpressure. It is the successor of the BMP085, but it also now has some suvessors itself. There is the (cheaper) BMP280, but you could also opt for the BME280 that measures temperature, airpresure AND humidity. That way you can save on the DHT/AMS sensor
  • OLED
    I just used that so I quickly could see whether the sensors were read, but you could just as well check it on Thingspeak. The OLED is too small anyway to print all the read values

The circuit

weerstationThe 4 pins of the ESP8266 are not indicated as such on the PCB, and most images only clearly state GPIO0 and GPIO2.
However the ESP826-01 has a a GPIO1 pin (the Tx pin) and a GPIO3 pin (the Rx pin).
i will be using those pins as follows

  • GPIO0 -> SDA pin of the I2C port
  • GPIO1 ->DHT11
  • GPIO2-> SCL pin of the I2C port
  • GPIO3-> OneWire Bus

As my I2C modules already have pull up resistors, I will not add any I2C pullup resistors there. The DS18B20 still needs a pull up resistor for which I used a 4k7, but it is really not that critical, a 10k is also good. The DHT11 supposedly also needs a pull-up resistor but I found it to work without one as well. adding a 4k7 resistor didnt change any of the readings, so I left it out. Many of the 3 pin DHT11 modules, already have a 10 k soldered onto the module.

I just realized that I didnt draw the connections for the OLED. That is because I only hooked it up for a quick check, but should you want to add it, it is just a matter of connecting SDA to SDA and SCL to SCL… and ofcourse the ground and Vcc pins to their counterparts

The program is quite straightforward. First it sets up the libraries and the sensors.
It attaches the DHT11 to pin 1 (Tx) and the OnWire bus for the DS18B20 to pin 3 (Rx). In order to use more than 1 DS18B20 sensor on the OneWire bus, you need to know their ‘unique adress’. If you do not have that then you need a program to read those addresses. Do that on an arduino for ease.

In the program you still have to provide your WiFi credentials as well as the write API for your Thingspeak Channel

 

/*
   Field 1 temp roost (DHT11)
   Field 2 humidity roost (DHT11)
   field 3 Coop temperature (DS18B20)
   field 4 soil temperature (DS18B20)
   field 5 Airpressure (bmp180)
   field 6 Outside temperature (bmp180)
 * */
#include <DHT.h>
#include  <OneWire.h>//  http://www.pjrc.com/teensy/td_libs_OneWire.html
#include <DallasTemperature.h> //   http://milesburton.com/Main_Page?title=Dallas_Tem...
#include <Adafruit_BMP085.h>
#include <ESP8266WiFi.h>
#include "SSD1306.h"
SSD1306  display(0x3c, 0, 2);

#define DHTPIN 1  //GPIO1 (Tx)
#define DHTTYPE  DHT11
#define ONE_WIRE_BUS 3 // GPIO3=Rx

const char* ssid     = "YourSSID";
const char* password = "YourPassword";
const char* host = "api.thingspeak.com";
const char* writeAPIKey = "W367812985"; //use YOUR writeApi

//DHT11 stuff
float temperature_buiten;
float temperature_buiten2;
DHT dht(DHTPIN, DHTTYPE, 15);

//DS18b20 stuff
OneWire oneWire(ONE_WIRE_BUS); //oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire);// Pass address of our oneWire instance to Dallas Temperature.
DeviceAddress Probe01 = { 0x28, 0x0F, 0x2A, 0x28, 0x00, 0x00, 0x80, 0x9F};
DeviceAddress Probe02 = { 0x28, 0x10, 0xA4, 0x57, 0x04, 0x00, 0x00, 0xA9};

// bmp180 stuff
Adafruit_BMP085 bmp;

void setup() {
  //I2C stuff
  Wire.pins(0, 2);
  Wire.begin(0, 2);
  // Initialize sensors
  //dht 11 stuff
  dht.begin();
  //ds18b20 stuff
  sensors.begin();//ds18b20
  // set the resolution to 10 bit (Can be 9 to 12 bits .. lower is faster)
  sensors.setResolution(Probe01, 10);
  sensors.setResolution(Probe02, 10);
  //bmp180 stuff
  if (!bmp.begin()) {
    //   Serial.println("No BMP180 / BMP085");
    //   while (1) {}
  }

  //OLED stuff
  display.init();
  display.flipScreenVertically();
  display.setFont(ArialMT_Plain_10);
  delay(1000);

  //  Connect to WiFi network
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

void loop() {
  //ds18b20stuff-------------------
  sensors.requestTemperatures(); // Send the command to get temperatures
  temperature_buiten = sensors.getTempC(Probe01);//
  temperature_buiten2 = sensors.getTempC(Probe02);//
  //dht11 stuff--------------------
  float humidity = dht.readHumidity();
  float temperature = dht.readTemperature();
  if (isnan(humidity) || isnan(temperature)) {
    return;
  }
  //bmp stuff-------------------------
    String t= String(bmp.readTemperature());
    String p=String(bmp.readPressure());
  //OLED stuff--------------------------
  display.clear();
  display.drawString(0,10,p);//bmp pressure
  display.drawString(0,24,String(temperature_buiten));//ds18b20
  display.drawString(0,38,String(humidity));//dht11
  display.display();

  // make TCP connections
  WiFiClient client;
  const int httpPort = 80;
  if (!client.connect(host, httpPort)) {
    return;
  }

  String url = "/update?key=";
  url += writeAPIKey;
  url += "&field1=";
  url += String(temperature);// roost (DHT1)
  url += "&field2=";
  url += String(humidity);// roost (DHT11)
  url += "&field3=";
  url += String(temperature_buiten);//coop temperature (DS18B20 nr 1)
  url += "&field4=";
  url += String(temperature_buiten2); //soil temperature (DS18B29 nr 2)
  url +="&field5=";
  url +=String(bmp.readTemperature());Outside temperature (BMP180)
  url +="&field6=";
  url +=String(bmp.readPressure());// Airpressure (BMP180)
  url += "\r\n";

  // Send request to the server
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");

  delay(1000);

}

Currently this program only monitors, but what is to stop you from adding a BH1750 I2C light sensor to measure if it is evening or morning or an RTC to know the time of day and to open and close the door of the coop automatically with aid of a PCF8574 I2C I/O expansion card, or as it is already in the garden, add a PCF8591 or ADS1115 AD converter to measure soil humidity and activate a pump when necessary. Or maybe switching on the water basin heater when the temperature falls below zero
if there is an I2 C chip for it, the ESP8266 can probably use it.

NOTE: the Adafruit DHT library contains an error that may show up in bigger progrms on an 8266. If the majority of readings result in “failed to read”, it is time to comment out two erroneous lines in the DHT.cpp file as in the picture below:
adafruiterror

Sending sensor data wireless (433MHz) with an Attiny85 or Attiny45 with Manchestercode

Sending sensor data wireless (433MHz) with an Attiny85 or Attiny45 with Manchestercode

P1060016
instructables-transmitter

In an earlier article I described how to send RF/wireless data between two Attiny85 chips with a 433 MHz transmitter/Receiver pair.
Now I will  give a more practical program that sends  3 variables from an LDR and a DHT11 sensor.
The connections are simple
The transmitter is connected to D0 (pin 5). The DHT11 sensor is connected to D4 (pin 3) and an LDR connected to A1 (pin 7), with the other end connected to Vcc and a corresponding pull down resistor.
The big photo shows a test circuit on stripboard, The smaller picture shows the final PCB. The huge amount of  cables at the top doesn’t mean much, I just use a few

/*
Pin 2 =A3 D3
pin 3 = A2 D4
pin5 =D0
Pin 6=D1
Pin7= D2 A1

LDR=A1
PIR= D1
RF433=D0
DHT11=D4
LED=D3
*/
// libraries
#include <dht11.h> //Rob Tillaert
#include <Manchester.h>
dht11 DHT11;
#define DHT11PIN 4
#define TX_PIN 0  //pin where your transmitter is connected
//variables
float h=0;
float t=0;
int transmit_data = 2761;
int transmit_t = 0; // temperature
int transmit_h = 0; // humidity
int transmit_l=0; // lightlevel
int transmit_p = 0; //PIR
int transmit_s = 0; /Station identifier
byte ledPin=3;
int light=0;

void setup() {
pinMode(1, INPUT);
pinMode(ledPin,OUTPUT);
man.setupTransmit(TX_PIN, MAN_1200);
}

void loop() {
int chk = DHT11.read(DHT11PIN);
h=DHT11.humidity;
t=DHT11.temperature;
transmit_h=2000+h;
transmit_t=2100+t;
transmit_l=2200+(analogRead(A1)/10);
transmit_p=2300+digitalRead(1);
transmit_s=2500;
digitalWrite(ledPin,HIGH);
man.transmit(transmit_h);
delay(200);
man.transmit(transmit_t);
delay(200);
man.transmit(transmit_l);
delay(200);
digitalWrite(ledPin,LOW);
delay(1000);
}

The idea of adding a number, in this case in the 2000 range is to be able to identify what (which station) is sending the data: any 2000 number I know is humidity, any 2100 number I know is temperature, etc. Variables h and t are floats that can have decimals. As I am only interested in 2 digit accuracy, I just add them to an integer and thus turn them into integers. WIth regard to the lightlevel as it is an analog read it might be as high as 1023 (theoretically), so when it is divided by 10, the range will usuallu be 99 max a two digit number. If by any chance it would be 100, the transmitter will send a number as 24xx. The receiving software then knows it is a more than 2 digit value, but that will be very rare I then send 5  values: Humidity, temperature, light PIR and a station identifier ending the data. The receiving station then can identify where it came from and process the incoming signals (starting by dividing subtracting  the first two digits).
WIth regard to the lightlevel as it is an analog read it might be as high as 1023 (theoretically), so when it is divided by 10, the range will usually be 99 max a two digit number. If by any chance it would be 100, the transmitter will send a number as 24xx. The receiving software then knows it is a more than 2 digit value, but that will be very rare

It takes 3374 bytes so I could compile it in an Attiny45.

See also: DHT11 on Attiny85
Attiny DHT 11 & 433 MHz (with Oregon code)