Figure out the esp8266 ADC Maximum voltage

In my quest to replace my current setup of Arduinos to esp8266 to gather sensor data I’ve been struggling with getting a similar output when using an LDR (light dependant resistor). I’ve been struggling with it for quite some time and I’ve finally come to a solution.

The issue being that the output graph from the LDR with an Arduino differs quite a bit from an esp8266 with an LDR.

After browsing for more details regarding the analog pin (ADC) on the esp8266 I realized that it does not measure between 0-3.3v but from 0-1v. When you do an analogRead on the input it outputs a value between 0-1024. But after I had introduced a voltage divider to get to 1v input I was still not getting the expected results.

In order to troubleshoot I measured the output from the ADC (i.e. digitalWrite(A0,1) ) and I received 0.6v. I thought that maybe it will output the expected max input value (but actually it was half the value).

const byte LDRpin=A0;

void setup() {
 pinMode(LDRpin, OUTPUT);
 delay(250);
 digitalWrite(LDRpin,1); //or analogWrite(LDRpin,255);
}

void loop() {
}

As a second step I wrote a small code and instead of a voltage divider I used a potentiometer to trim the input voltage to the ADC pin and the code did output 1024 when I reached 1.2v.

const byte LDRpin=A0;
volatile unsigned int ldr;

void setup() {
 Serial.begin(115200);
 pinMode(LDRpin, INPUT);
}

void loop() {
 ldr = analogRead(LDRpin);
 Serial.println(ldr);
 delay(5000);
}

As a summary the maximum voltage for my esp8266 is 0-1.2v. I don’t know if this is a common value for the esp8266 or if it was unique only for my esp8266-12e but at least there is a fairly simple way to find out.

So instead of using a 100Ω (R2) and 220Ω (R1) resistors to get to 1.0v I used 100Ω (R2) and 180Ω (R1) in the voltage divider to bring down 3.3v to 1.2v as input to the LDR. R1 was calculated using this online tool http://www.raltron.com/cust/tools/voltage_divider.asp

voltage-divider

Simple esp8266 433MHz MQTT bridge

I’ve played around with ESP8266 on a couple of occasions before but I’ve found it too unstable due to me simply not knowing enough. But as I have struggled to find a good solution for capturing signals from the sensors of my burglar alarm I decided to have another go at it.

There are tons of guides “out there” but I have been missing a good overview so I will try to explain my steps a bit more and the challenges with the esp8266.

Background

I have a cheap home burglar system with a set of PIRs and magnet sensors throughout my house. These are all sending messages to the central unit using PT2262 encryption.

As I am steering the lights etc. using norelite on Node-RED I want to capture these messages and simply put them onto an MQTT bus from where my norelite flows can subscribe to the topics and use the information.

I have tested the rc-switch library using an Arduino so I know that the interception and decoding works as required. I’ve previously used a 433 receiver with an Arduino Mini Pro and an nrf24l01 to send the data to a server that later puts the message on the MQTT bus – but there are too many components involved and it simply is not a good solution.

ESP8266-12E

Basics: Booting ESP8266 for flash

I’ve found the following setup to be stable in setting up the esp8266 for flashing.

 VCC  3.3V (Power supply)
 GND GND Power supply and
GND Serial adapter
 TX RX Serial adapter
 RX TX Serial adapter
 CH_PD  Pull high using 10kΩ resistor. Note that if you are using the module adapter it already includes the resistor
 RST  Pull high using 10kΩ resistor
 GPIO15  Pull low using 10kΩ resistor. Note that if you are using the module adapter it already includes the resistor
 GPIO0  Pull low using 10kΩ resistor

Basics: Booting ESP8266 for run

The only difference from the boot-for-flash setup is that you move GPIO0 to pull to high as shown below and removing the serial adapter (keep it if you want to get the output in the console)

 VCC  3.3V
 GND GND
 CH_PD  Pull high using 10kΩ resistor. Note that if you are using the module adapter it already includes the resistor
 RST  Pull high using 10kΩ resistor. (Move to GND in order to reset the device)
 GPIO15  Pull low using 10kΩ resistor. Note that if you are using the module adapter it already includes the resistor
 GPIO0  Pull high using 10kΩ resistor

Lessons learned when programming for the esp8266

  • Don’t forget to use delay() or yield() in your loops to give the opportunity for the device to manage background processes
  • If you are using timers (e.g. using Ticker library), make them short and very simple  – set variables and then do the major processing in the loop(). This is useful if you want to only run a certain task with a timing delay: Below is a simple example where just a variable is set in the called function and then the processing is made in the main loop function.
    #include Ticker timer;
    volatile bool timerRun;
    
    void timerTick(){
     timerRun = true; //Just setting the variable
    }
    
    void setup() {
     pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output
    
     //Setup timer
     timer.attach_ms(60000, timerTick);
     timerRun = false;
    }
    
    // the loop function runs over and over again forever
    void loop() {
     if (timerRun){
     digitalWrite(LED_BUILTIN, LOW); // Turn the LED on (Note that LOW is the voltage level
     // but actually the LED is on; this is because
     // it is acive low on the ESP-01)
     delay(1000); // Wait for a second
     digitalWrite(LED_BUILTIN, HIGH); // Turn the LED off by making the voltage HIGH
     timerRun = false;
     }
     /*
     * Other processing
     *
     */
    }
  • Don’t run the esp8266 on the power supplied from the serial cable. Use a dedicated power supply and make sure to have a common ground. I.e. connect the serial GND to the power supply GND. Don’t forget limit the supplied voltage to 3.3V…
  • Verify that the baud rate in the console is supported by the OS and is the same as specified in the code
  • I’ve also experienced some issues with the serial adapter where the communication is lost sometimes but that is usually resolved by ejecting and re-inserting the usb cable and sometimes I have to update the port setting in the Arduino IDE.
  • I’ve also heard that it can be good to reset the flash memory when troubleshooting. I’ve never done it myself but one tool that can be used is esptool

The project

Components and schema

The below schema is for run mode. When flashing move GPIO0 to GND, connect TX/RX and GND from the serial adapter.

  • (U2)  LM1117 voltage regulator. Needed as I have a 5V input and I the ESP8266 runs on 3.3V and the 433MHz receiver is powered by 3.3-5V
  • (U1) 433MHz receiver (Superheterodyne 3400 RF).
  • (R1-R4) As explained in the basics above. These are for setting up the ESP.
  • I’m also using an esp module adapter board as these ones
  • Libraries: rc-switch, PubSubClient and ESP8266WiFi

screen-shot-2016-12-06-at-09-36-00

The code

#include <SPI.h>
#include <RCSwitch.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

/*
 * 433MHz to MQTT bridge for burglar alarm
 */

#define RADIOPIN 4
#define wifi_ssid "virus"
#define wifi_password "abc123abc123"
#define mqtt_server "192.168.2.195"
#define mqtt_port 1883

#include "os_type.h"

//WIFI and MQTT
WiFiClient espClient;
PubSubClient client(espClient);

//MQTT topics
String burglar_topic_all;
const char* mqttclientid;

RCSwitch mySwitch = RCSwitch();

/*
 * Common function to get a topic based on the chipid. Useful if flashing
 * more than one device
 */
String getMqttTopic(String type){
  return "/raw/esp8266/"+String(ESP.getChipId())+"/"+type;
}

/*
 * Setup WIFI connection and connect the MQTT client to the
 * MQTT server
 */
void connection_start() {
  setup_wifi();

  //Setup MQTT preferences
  client.setServer(mqtt_server, mqtt_port);
  delay(100);
  while(!client.connected()){
    if (client.connect(mqttclientid)) {
      Serial.println("MQTT connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");

      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
  Serial.println("Connections are setup");
}
void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(mqttclientid)) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      ESP.wdtFeed();
      delay(5000);
    }
  }
}

/*
 * Setup WIFI communication. I.e. connect to the access point
 * and get an IP address
 */
void setup_wifi() {
  //delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(wifi_ssid);

  WiFi.begin(wifi_ssid, wifi_password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

/*
 * Common function to turn on WIFI radio, connect and get an IP address,
 * connect to MQTT server and publish a message on the bus.
 * Finally, close down the connection and radio
 */
void sendmsg(String topic, String payload){
    //Send status to MQTT bus if connected
    if (client.connected()){
      client.publish(topic.c_str(), payload.c_str());
      Serial.println("Published MQTT message");
    }
}

void setup() {
  Serial.begin(9600);
  delay(100);
  Serial.print("Starting\n");

  /* Setup MQTT Client ID
   *  As the id needs to be unique per user the chipid is used to
   *  generate an MQTT client id.
   */
  mqttclientid = ("ESPClient-"+String(ESP.getChipId())).c_str();
  Serial.print("MQTT Client ID: ");
  Serial.println(mqttclientid);

  //Setup connections
  connection_start();

  //Radio
  burglar_topic_all = getMqttTopic("burglar");
  Serial.print("Burglar topic: ");
  Serial.println(burglar_topic_all);
  mySwitch.enableReceive(RADIOPIN);
}

void loop() {
  //Reconnect network if needed
  if (WiFi.status() != WL_CONNECTED){
    connection_start();
  }

  //Reconnect MQTT if needed
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  /*
   * Listen for the input from the receiver
   */
  if (mySwitch.available()){

    int value = mySwitch.getReceivedValue();

    if (value == 0) {
      Serial.print("Unknown encoding");
    } else {
      //Send message
      sendmsg(burglar_topic_all, String(mySwitch.getReceivedValue()));
    }

    mySwitch.resetAvailable();
  }

  yield();
}

Script to run at startup (node example)

I’ve browsed to find an easy way to make a node.js program to run at startup and stop and start easily and this is what I came up with. As I might be running multiple node.js programs I need to search on the parameters when killing the right node program.

What I’m doing is the following:

  1. Discarding stdout and stderr from the program at start
  2. Using a parameter sent to node.js to identify the right program to stop when stopping the service

Below is my /etc/init.d/rf24snserv program and don’t forget to make it executable and add the symbolic link using update.rc. I.e.

  1. sudo nano /etc/init.d/rf24snserv
  2. Add the code as below
  3. Save and exit nano
  4. sudo chmod 755 /etc/init.d/rf24snserv
  5. sudo update.rc rf24snserv defaults

Find more details on how to create a startup script here: https://debian-administration.org/article/28/Making_scripts_run_at_boot_time_with_Debian

#! /bin/sh
# /etc/init.d/rf24snserv.sh

### BEGIN INIT INFO
# Provides: rf24sn
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Starts rf24sn
# Description: A script to start the service
### END INIT INFO

case "$1" in
        start)
                echo "Starting rf24sn"
                nohup /usr/bin/rf24sn -b mqtt://192.168.2.195:1883 --spi /dev/spidev0.0 --ce 25 --irq 24 -vvv --rate 250kbps > /dev/null 2>&1 &
                ;;
        stop)
                echo "Stopping rf24sn"
                ps -ef | grep rf24sn | grep -v grep | awk '{print $2}' | sudo xargs kill
                ;;
        *)
                echo "Usage: /etc/init.d/rf24snserv (start|stop)"
                exit 1
                ;;
esac
exit 0

I had some issue to get “stop” to work but with the above statment it finds the PID and finally passes it on to the kill command.

This is an example output from ps -ef | grep rf24sn

root      3524     1  0 19:38 ?        00:00:02 node /usr/bin/rf24sn -b mqtt://192.168.2.195:1883 --spi /dev/spidev0.0 --ce 25 --irq 24 -vvv --rate 250kbps
pi        3554  3285  0 19:54 pts/0    00:00:00 grep --color=auto rf24sn

node-red-contrib-rfxcom install issue

I’ve just released a new version of norelite that includes some fixes to the code and I ran into some issues when doing “npm update” of my installed Node-RED modules and it might be of interest to others to know how I fixed it.

The issue was not related to norelite but to node-red-contrib-rfxcom that simply couldn’t compile serialport@2.1.2 which is a dependency. After a lot of attempts and actually a re-install of nodejs, Node-RED and npm I finally found an instruction in the Node-RED docs related to Wheezy (I’m running Jessie though) that fixed the problem.

This was the error message that I received:

Failed to execute '/usr/bin/nodejs /usr/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js build --fallback-to-build --module=/home/pi/.node-red/node_modules/serialport/build/Release/serialport.node --module_name=serialport --module_path=/home/pi/.node-red/node_modules/serialport/build/Release' (1)
npm ERR! Linux 4.1.13-v7+
npm ERR! argv "/usr/bin/nodejs" "/usr/local/bin/npm" "install" "serialport@2.1.2"
npm ERR! node v4.4.7
npm ERR! npm  v2.14.15
npm ERR! code ELIFECYCLE
npm ERR! serialport@2.1.2 install: `node-pre-gyp install --fallback-to-build`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the serialport@2.1.2 install script 'node-pre-gyp install --fallback-to-build'.
npm ERR! This is most likely a problem with the serialport package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     node-pre-gyp install --fallback-to-build
npm ERR! You can get their info via:
npm ERR!     npm owner ls serialport
npm ERR! There is likely additional logging output above.
npm ERR! Please include the following file with any support request:
npm ERR!     /home/pi/.node-red/npm-debug.log

And I solved it as follows: I first installed serialport@2.1.2 with unsafe-perm variable set and then node-red-contrib-rfxcom as follows (you could probably just use the variable with node-red-contrib-rfxcom)

cd ~/.node-red
sudo npm install --unsafe-perm serialport@2.1.2
sudo npm install node-red-contrib-rfxcom

6 min video with norelite

To get a quick start with norelite I’ve made a short video that explains the core components and how to quickly design the flows required to implement 3 rules that will turn on/off a switch if:

  1. It is dark outside and it is before 23:00
  2. It is dark outside and I’m watching the TV – It might be that I’m watching the TV after 23:00 and I also want the lamp to be on 15 mins after I’ve turned off the TV
  3. It is dark outside and there is movement in the house – I.e. getting the input from a PIR detector and whenever it triggers and it is dark outside the lamp will turn on for 5 mins. E.g. if I wake up during the night and want to go and get a glass of water…

If any of the above rules are true, the lamp will be on and if not it will turn it off.

OWFS with hub support for Node-Red

I’ve built my home automation system based on Node-Red (as mentioned in Flexible home automation system) and I am using 1-wire sensors for humidity, temperature and to measure the daylight level.

Initially I used the Node-Red “node-red-contrib-owfs” package to communicate with my 1-wire server but I have multiple 1-wire hubs and the package simply does not support having an hierarchy of sensors so I created a my own node to resolve this – noreowfs.

Use the UI of the 1-wire server to find the attribute that you want to read and simply add to the configuration of the node and the value will be available in the output of the node.

17cdec26-a68e-11e5-88ac-f7002d8c994b