Tag: Output

Fun with ESP, WS2812 and Homewizard

Fun with ESP, WS2812 and Homewizard

Well, I managed to get the ESP8266 working with the WS2812 in the Arduino IDE and it is time for some fun in combination with my Homewizard.

What I wanted to accomplish is this: I have solar panels at home and I have connected these to my Homewizard (a domotica appliance) along with the power meters which means my Homewizard knows about my power usage and my solar power production. What I wanted to do is have a small WS2812 ring show my usage and my production simultaneously. I defined usage as being the ‘red’ color and solar production as ‘yellow’. I want to show the production at the same time as the usage so I thought I would define each led to be the equivalent of 250 W power. I would ‘plot’ both the production and the usage on the circle starting at the same point. The overlap would be orange and excess solar power would be yellow and excess usage would be red, so if the circle showed 3 orange leds and 3 yellow, it meant I was producing 1500W (3+3 times 250W) while at the same time using 750W (3 times 250W). If the circle would show 3 orange and 3 red, it would mean 750W production and 1500W usage. After attempt 1 I found out that it was possible to use more than the LEDs would allow (ok, has not happened yet, but theoratically it is possible so I decided to add overflow. If production or usage would be over 3000W (12 times 250W), any excess would result in blue leds at the beginning, symbolizing double ‘value’ LEDs.

Still with me so far? After implementing, I found out that during Autumn, which is now when I created this project, production is not that high and my usage is usually also not that high, so often only 2 or 3 LEDs would light up. What I needed was increased resolution in such cases. I know, way too complex but he, it was meant to be a fun project. So I introduced green LEDs at the beginning. One green LED meant the resolution of the remaining 11 LEDs was 125W and two green LEDs mean the resolution of the remaining 10 LEDs was 50W.

The code will probably not be something you want to use without modifying but it might help you get some ideas on how to play with the WS2812. The code for getting the solar production and usage might be useful to others with a Homewizard.

github-mark-32px Available on GitHub with more comment in the code. Direct download.

Let’s look at the code:

#include <Adafruit_NeoPixel.h>
#include <ESP8266WiFi.h>
#include <PAM_WiFiConnect.h>
#include <PAM_WiFiClient.h>

#define CIRCLEPIN 2
#define PIXELCOUNT 12
#define POWERPERLED1 250
#define POWERPERLED2 125
#define POWERPERLED3 50

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXELCOUNT,CIRCLEPIN,
  NEO_GRB+NEO_KHZ800);

int solarPower = 0;
int powerConsumption = 0;
float solarPowerDay = 0;
float powerConsumptionDay = 0;

void setup() {
  Serial.begin(115200);
  wifiConnect("YourSSID","YourPassword");
  pixels.begin();
  for(byte i=0;i<PIXELCOUNT;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
  }
  pixels.show();
}

void readSolar () {
  String t = getURL("YourHomewizardUrl","/YourHWPassword/get-status",80);
  t = t.substring(t.indexOf("energylinks"));
  t = t.substring(t.indexOf("s1"));
  t = t.substring(t.indexOf("po")+4);
  int xsolarPower = t.substring(0,t.indexOf(",")).toInt();
  t = t.substring(t.indexOf("dayTotal")+10);
  int xsolarPowerDay = t.substring(0,t.indexOf(",")).toFloat()*1000;
  t = t.substring(t.indexOf("used"));
  t = t.substring(t.indexOf("po")+4);
  int xpowerConsumption = t.substring(0,t.indexOf(",")).toInt();
  t = t.substring(t.indexOf("dayTotal")+10);
  int xpowerConsumptionDay = t.substring(0,t.indexOf(",")).toFloat()*1000;
  t = t.substring(0,t.indexOf("heatlinks"));
  Serial.print("Solar       now :");
  Serial.println(solarPower);
  Serial.print("Consumption now :");
  Serial.println(powerConsumption);
  Serial.print("Solar       day :");
  Serial.println(solarPowerDay);
  Serial.print("Consumption day :");
  Serial.println(powerConsumptionDay);
  Serial.println();
  if (xsolarPower!=0 || xpowerConsumption!=0) {
    solarPower = xsolarPower;
    solarPowerDay = xsolarPowerDay;
    powerConsumption = xpowerConsumption;
    powerConsumptionDay = xpowerConsumptionDay;
  }
}

void loop() {
  readSolar();

  byte solarLed;
  byte powerLed;
  byte base;
  if (_max(solarPower,powerConsumption)<=POWERPERLED3*(PIXELCOUNT-2)) {
    solarLed = round((solarPower+POWERPERLED3/2)/POWERPERLED3)+4;
    powerLed = round((powerConsumption+POWERPERLED3/2)/POWERPERLED3)+4;
    base = 2;
    for (byte i=0;i<base;i++) {
      pixels.setPixelColor(i,pixels.Color(0,42,0));
    }
  } else if (_max(solarPower,powerConsumption)<=POWERPERLED2*(PIXELCOUNT-1)) {
    solarLed = round((solarPower+POWERPERLED2/2)/POWERPERLED2)+2;
    powerLed = round((powerConsumption+POWERPERLED2/2)/POWERPERLED2)+2;
    base = 1;
    pixels.setPixelColor(0,pixels.Color(0,42,0));
  } else {
    solarLed = round((solarPower+POWERPERLED1/2)/POWERPERLED1);
    powerLed = round((powerConsumption+POWERPERLED1/2)/POWERPERLED1);
    base = 0;
    if (solarLed>PIXELCOUNT || powerLed>PIXELCOUNT) {
      base = _max(solarLed,powerLed)-PIXELCOUNT;
      for (byte i=0;i<base;i++) {
        pixels.setPixelColor(i,pixels.Color(0,0,42));
      }
    }
  }
  for (byte i=base;i<_min(solarLed,powerLed)-base;i++) {
    pixels.setPixelColor(i,pixels.Color(110,27,0));
  }
  if (powerLed>solarLed) {
    for (byte i=_max(base,_min(solarLed,powerLed)-base);i<powerLed-base;i++) {
      pixels.setPixelColor(i,pixels.Color(55,0,0));
    }
    for (byte i=powerLed-base;i<PIXELCOUNT;i++) {
      pixels.setPixelColor(i,pixels.Color(0,0,0));
    }
  } else {
    for (byte i=_max(base,_min(solarLed,powerLed)-base);i<solarLed-base;i++) {
      pixels.setPixelColor(i,pixels.Color(55,42,0));
    }
    for (byte i=solarLed-base;i<PIXELCOUNT;i++) {
      pixels.setPixelColor(i,pixels.Color(0,0,0));
    }
  }
  pixels.show();
  delay(500);
}

Ok, maybe way to much code to just dump here but it’s not that difficult to read.

Let’s start with the includes; NeoPixel library from AdaFruit, the standard ESP8266WiFi library and my own two libraries, PAM_WiFiConnect and PAM_WiFiClient. Next we define our constants and initalize the NeoPixel library.

The setup function is straight forward, set up Serial, connect to wifi and set all pixels to black.

The readSolar function connects to the Homewizard and gets the status information. This json file is than parsed a couple of times to get the current solar power production in solarPower, the days solar power production solarPowerDay, the current power consumption, powerConsumption and finally the power consumption of the day, powerConsumptionDay. Both day values are not used in this sketch. I intend to make some kind of switches where you can choose to see the current production/consumption or the totals of the day.

The loop function is where the number of leds and color is determined. The first section, the extensive if statement, determines which value to assign to each led, 250W, 125W or 5oW. This is done by checking the maximum of the values of solarPower and powerConsumption. If the maximum is small enough to fit within the 50W scale that is used, if not it is checked whether it will fit in the 125W scale and else it will be put in the 250W scale. If it is 50W, the first 2 leds are made green (signifying a change from the default scale) and 125W shows 1 green led.

Next the color of the remaining leds is determined by checking the maximum (to see if blue leds are needed) and than counting how many should be red (power usage), yellow (power production) or orange (production and usage).

As mentioned before, you probably cannot use the supplied code straight ‘out of the box’ but you can use it for ideas.

The end result of my solar meter in a small plastic box:

ws2812funa

At the bottom left, a USB to serial adapter. In the bottom middle a ESP-12E on a small breakout board. At the middle left a reed contact which allows for an update to the code by holding a magnet near the back of the box and finally the 12 bit neopixel circle.

When the box is closed, there is a normal piece of paper at the top abive the lights so that they are not too bright and slightly diffused which gives this end result:

ws2812funb

ESP8266 & WS2812

ESP8266 & WS2812

I managed to get the ESP and the WS2812 individually configurable LEDs to play nice to each other and coded the ESP through the Arduino IDE. The final setup which I used, I thought I had tried before but somehow I didn’t apparently. So what worked for me is the following technical setup:

A 5V to 3.3V converter with a USB feed to my laptop, +5V to +5V and GND to GND. Next connect the 3.3V of the converter to 5V of the WS2812 and GND of the 3.3V end to the GND of the WS2812 led strip (or circle in my case) and connect the same GND to the GND of your ESP. I have it now working with a NodeMCU v1 and a Wemos D1 Mini. The DI pin (data in) of the WS2812 goes to pin D4 which is GPIO2.

For the library you need the Adafruit Neopixel library which you can download here. The example code should work (check to make sure you select the right pin).

github-mark-32px Available on GitHub with more comment in the code. Direct download.

You can also try my example which is a running yellow strip of light:

//
// The library from Adafruit which enables the use of the WS2812
// or NeoPixel
//
#include <Adafruit_NeoPixel.h>

//
// The pin the circle of WS2812 leds is connected to
//
#define CIRCLEPIN 2
//
// The number of leds in our circle
//
#define PIXELCOUNT 12

//
// Initialize the Adafruit library
//
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXELCOUNT,CIRCLEPIN,
  NEO_GRB+NEO_KHZ800);

//
// The number of leds lit
//
byte maxLed = 1;
//
// Is the number of leds increasing or decreasing
//
int direction = 1;

void setup() {
  //
  // Start the led string and turn them all off
  //
  pixels.begin();
  for(byte i=0;i<PIXELCOUNT;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
  }
  pixels.show();
}

void loop() {
  //
  // Turn the first 'maxLed' leds yellow
  //
  for(byte i=0;i<maxLed;i++){
    pixels.setPixelColor(i, pixels.Color(55,42,0));
  }
  //
  // Set the remaining leds black
  //
  for(byte i=maxLed;i<PIXELCOUNT;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
  }
  //
  // Don't forget to call the pixels.show();
  //
  pixels.show();
  delay(100);
  //
  // Determine the next step in number of leds to show
  //
  maxLed = maxLed+direction;
  if (maxLed<1 || maxLed>PIXELCOUNT) {
    direction = -direction;
    maxLed = maxLed+direction;
  }
}

A very basic example but I was very happy that it worked.

Theoratically speaking, you should be able to run this from other pins and with a small number of leds, maybe even without a seperate power source. I also still want to try to run it at 5v and use a logical converter for the DI pin. If I manage to do this, I will share this.

Fun with HC-SR04 and WS2812

Fun with HC-SR04 and WS2812

After I managed to get the distance sensor HC-SR04 working and got the LED circle based on WS2812 working, I felt it was time to combine them in a fun project.

I connected two LED circles, 16 LEDs and 12 LEDs where the DO of the 16 went into the DI of the 12. What I wanted to do is in the outer circle have a red circle run in circles where the speed was dependant on the distance of an object in front of the sensor. The closer the object, the faster it moves. The inner circle shows the distance to the object. The farther the object, the more LEDs would light up.

github-mark-32px Available on GitHub with more comment in the code. Direct download.

The code for the Arduino IDE:

//
// The library from Adafruit which enables the use of the WS2812
// or NeoPixel
//
#include <Adafruit_NeoPixel.h>

//
// The pin the circle of WS2812 leds is connected to
//
#define CIRCLEPIN 6
//
// The number of leds in our two circles (12+16)
//
#define PIXELCOUNT 28

//
// Initialize the Adafruit library
//
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXELCOUNT,CIRCLEPIN,
  NEO_GRB+NEO_KHZ800);

//
// The current position
//
int degree = 0;

//
// The trigger and echo pin for the HC-SR04
//
#define TRIGGERPIN 13
#define ECHOPIN 12

//
// How long we delay before the next read and update of the leds
//
int delayMove = 50;

void setup() {
  //
  // Start the led string and turn them all off
  //
  pixels.begin();
  for(int i=0;i<PIXELCOUNT;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
  }
  pixels.show();
  //
  // Prepare the HC-SR04 pins
  //
  pinMode(TRIGGERPIN, OUTPUT);
  pinMode(ECHOPIN, INPUT);
}

void loop() {
  //
  // Determine the distance between object and HC-SR04
  // Read about it in more detail on
  // https://piandmore.wordpress.com/2016/10/04/arduino-hc-sr04-ultrasonic-distance/
  //
  long duration, distanceCM;
  digitalWrite(TRIGGERPIN, LOW);
  delayMicroseconds(5);
  digitalWrite(TRIGGERPIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIGGERPIN, LOW);
  duration = pulseIn(ECHOPIN, HIGH);
  //
  // The distance in cms
  //
  distanceCM = (duration/2) / 29.1;
  //
  // Turn all pixels off
  //
  for(int i=0;i<PIXELCOUNT;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
  }
  //
  // Turn on the inner circle leds
  // 1 led for every 2 cm of distance
  //
  for (int i=16;i<16+distanceCM/2;i++) {
    pixels.setPixelColor(i, pixels.Color(0,50,0));
  }
  //
  // Determine the current position led of the outer circle
  // and turn on the required leds
  //
  int thisPixel = (degree*16)/360;
  pixels.setPixelColor((thisPixel+3)%16,pixels.Color(255,0,0));
  pixels.setPixelColor((thisPixel+2)%16,pixels.Color(100,0,0));
  pixels.setPixelColor((thisPixel+1)%16,pixels.Color(40,0,0));
  pixels.setPixelColor((thisPixel)%16,pixels.Color(15,0,0));
  pixels.show();
  //
  // Depending on the distance of an object, make the circle move
  // faster (if it is closer) or slower (if it is farther away)
  // and take the maximum time of it is very far away or
  // if there is no object
  //
  if (distanceCM>=200) {
    delayMove = 200;
  } else {
    delayMove = distanceCM;
  }
  delay(delayMove);
  degree += 10;
  if (degree>=360) {
    degree -= 360;
  }
}

The code looks a lot like the two earlier mentioned codes combined. What is added is the 12 LEDs which, as you can see, are addressed as being the next 12 on the same string as the 16.

Arduino & WS2812

Arduino & WS2812

The WS2812 is an RGB led with a controller built in. More importantly, the built in controller allows for concatenation of the leds and you supply the colors on a bus and each led ‘takes’ one color and passes the rest on. You can buy these as a long string or in various shapes. In this example, I have a 16 led circle.

Connecting the circle is done with four wires, GND to GND, VCC to 5V (please note it is 5 Volts and not 3.3 Volts). There is also a DI and a DO, Data In and Data Out. You connect the DI pin to a pin on your Arduino. In my example, it is pin 6.

The library you need can be found here. Like most Arduino IDE libraries, you install it by going to the menus Sketch, Include Library, Add .ZIP Library. Don’t forget to close all instances of Arduino IDE and restart.

github-mark-32px Available on GitHub with more comment in the code. Direct download.

Let’s first look at the code:

//
// The library from Adafruit which enables the use of the WS2812
// or NeoPixel
//
#include <Adafruit_NeoPixel.h>

//
// The pin the circle of WS2812 leds is connected to
//
#define CIRCLEPIN 6
//
// The number of leds in our circle
//
#define PIXELCOUNT 16

//
// Initialize the Adafruit library
//
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXELCOUNT,CIRCLEPIN,
  NEO_GRB+NEO_KHZ800);

//
// The current position in degrees
//
int degree = 0;

void setup() {
  Serial.begin(115200);
  //
  // Start the led string and turn them all off
  //
  pixels.begin();
  for(int i=0;i<PIXELCOUNT;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
  }
  pixels.show();
}

void loop() {
  Serial.println(degree);
  //
  // Turn all leds to black
  //
  for(int i=0;i<PIXELCOUNT;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
  }
  //
  // Convert degrees to the pixel position
  //
  int thisPixel = (degree*PIXELCOUNT)/360;
  //
  // Set the pixels with a trailing tail
  //
  pixels.setPixelColor((thisPixel+3)%16,pixels.Color(255,0,0));
  pixels.setPixelColor((thisPixel+2)%16,pixels.Color(100,0,0));
  pixels.setPixelColor((thisPixel+1)%16,pixels.Color(40,0,0));
  pixels.setPixelColor((thisPixel)%16,pixels.Color(15,0,0));
  //
  // Don't forget to call the pixels.show();
  //
  pixels.show();
  delay(50);
  degree += 10;
  if (degree>=360) {
    degree -= 360;
  }
}

In the setup function, we call pixels.begin() to start initialize. Every time we call pixels.show the data is send to the LEDs. With setPixelColor you set the color for each pixel.

So what this code does is make a red light go around in circles followed by a small fading trail.

With regards to the code, I know it would have made more sense to just use the pixel counter instead of degrees, especially since 360 degrees into 16 LEDs means an increase of 22.5.

Also, you do not need to set every color back to black because it remembers the last color. Taking all this into account, the smaller version of the code will be:

//
// The library from Adafruit which enables the use of the WS2812
// or NeoPixel
//
#include <Adafruit_NeoPixel.h>

//
// The pin the circle of WS2812 leds is connected to
//
#define CIRCLEPIN 6
//
// The number of leds in our circle
//
#define PIXELCOUNT 16

//
// Initialize the Adafruit library
//
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXELCOUNT,CIRCLEPIN,
  NEO_GRB+NEO_KHZ800);

//
// The current pixel
//
byte thisPixel = 0;

void setup() {
  //
  // Start the led string and turn them all off
  //
  pixels.begin();
  for(int i=0;i<PIXELCOUNT;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
  }
  pixels.show();
}

void loop() {
  //
  // Set the pixels with a trailing tail
  // Since we paint the last pixel black, we do not need to initialize
  // all pixels to black
  //
  pixels.setPixelColor((thisPixel+4)%16,pixels.Color(255,0,0));
  pixels.setPixelColor((thisPixel+3)%16,pixels.Color(100,0,0));
  pixels.setPixelColor((thisPixel+2)%16,pixels.Color(40,0,0));
  pixels.setPixelColor((thisPixel+1)%16,pixels.Color(15,0,0));
  pixels.setPixelColor((thisPixel)%16,pixels.Color(0,0,0));
  //
  // Don't forget to call the pixels.show();
  //
  pixels.show();
  delay(30);
  thisPixel++;
  if (thisPixel>16) {
    thisPixel = 0;
  }
}
Arduino & OLED display

Arduino & OLED display

A while back I bought an OLED display on AliExpress. It’s yellow and blue and 128×64. The nice thing was that it was based on I2C protocol so I thought I would get it working on my ESP but I couldn’t.

Now with the Arduino‘s, I thought I would give it another try and it turns out te be quite easy. Connecting the OLED to the Arduino requires four wires. GND goes to GND, VCC goes to 3.3V, SCL goes to A5 and finally SDA goes to A4.

Next you need a library of course. In this case it is called u8glib and you can download it on github (the downloadlink for the Arduino library is in the text if you scroll down). Install it in the Arduino IDE using the menus Sketch, Include Library, Add .ZIP Library. That’s it. You are ready to experiment.

The library comes with a lot of examples, but let’s start out with Hello World!

#include "U8glib.h"

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);

int offset;

void draw() {
  u8g.setFont(u8g_font_unifont);
  u8g.drawStr(0,10+offset,"Hello World!");
}

void setup() {
  offset = 0;
}

void loop() {
  u8g.firstPage();  
  do {
     draw();
  } while(u8g.nextPage());
  delay(50);
  offset++;
  if (offset>50) {
    offset = 0;
  }
}

In the loop function, you have to have the u8g.firstPage() and the loop with draw() until u8g.nextPage() is false. You put your ‘art’ in the draw() function. The loop is needed because the display is written to in sections. You don’t have to worry how. This is what the loop does for you.

The u8g library is very extensive and very well documented. All functions can be found in the reference. I suggest trying some things yourself.

Supported devices

Arduino, OLED LCD & 4 sensors

Arduino, OLED LCD & 4 sensors

After describing four sensors, the SI7021, TLS2561, BH1750 and the BMP180, I wanted to see if I could combine all of these on one bus and display information on an OLED screen, also I2C.

In short, it is very possible and it is combining the different code to read the sensors and than creating some lines to display on the OLED screen.

combiningi2c-1

github-mark-32px Available on GitHub with more comment in the code. Direct download.

Since the code is not that difficult, I will share it here and in a later stage, describe it in more detail:

//
// LCD
//
#include "U8glib.h"

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);

//
// SI7021
//
#include <Wire.h>
#include <SI7021.h>

#if defined(ESP8266)
//
// For the ESP8266 we need these to be defined for I2C
//
#define SDA 4
#define SCL 5
#endif

SI7021 sensor;

float temperature;
int humidity;

//
// TSL2561
//
#include "TSL2561.h"

TSL2561 tsl(TSL2561_ADDR_FLOAT); 

boolean hasTSL = false;
uint16_t lux1;

//
// BH1750
//
#include <BH1750.h>

BH1750 lightMeter;

uint16_t lux2;

//
// BMP180
//
#include <SFE_BMP180.h>
#define ALTITUDE 0

SFE_BMP180 pressure;

boolean hasBMP = false;
double temperature2,pressure2,altitude2;

//
// LCD, the function that is called to draw to the OLED
//
void draw() {
  //
  // Set the correct values for drawing with the u8g library
  //
  u8g.setFont(u8g_font_6x10);
  u8g.setFontRefHeightExtendedText();
  u8g.setDefaultForegroundColor();
  u8g.setFontPosTop();
  char printThis[14];
  String t;
  //
  // Temperature from SI7021 and if we have it
  // the BMP180 (which is than averaged)
  //
  float tmp2 = temperature;
  if (hasBMP) {
    tmp2 = (tmp2+temperature2)/2;
  }
  t = "Temp:";
  t.toCharArray(printThis,13);
  u8g.drawStr(0,0,printThis);
  //
  // The temperature with degrees and C added
  //
  t = String(tmp2,1)+" "+(char)176+"C";
  t.toCharArray(printThis,13);
  u8g.drawStr(50,0,printThis);
  //
  // Humidity from the SI7021
  //
  t = "Humi:";
  t.toCharArray(printThis,14);
  u8g.drawStr(0,11,printThis);
  //
  // The humidity and % added
  //
  t = String(humidity)+" %";
  t.toCharArray(printThis,14);
  u8g.drawStr(50,11,printThis);
  //
  // The lux from the BH1750 and from the
  // TSL2561 if we have it (it is averaged if we have it)
  //
  t = "Lux:";
  t.toCharArray(printThis,14);
  u8g.drawStr(0,22,printThis);
  uint16_t tmp = lux2;
  if (hasTSL) {
    tmp = (tmp+lux1)/2;
  }
  //
  // The lux with lx added
  //
  t = String(tmp)+" lx";
  t.toCharArray(printThis,8);
  u8g.drawStr(50,22,printThis);
  //
  // The pressure from the BMP180
  //
  t = "Press:";
  t.toCharArray(printThis,14);
  u8g.drawStr(0,33,printThis);
  //
  // The pressure with mb added
  //
  t = String(pressure2,0)+" mb";
  t.toCharArray(printThis,14);
  u8g.drawStr(50,33,printThis);
  //
  // The altitude from BMP180
  //
  t = "Alti:";
  t.toCharArray(printThis,14);
  u8g.drawStr(0,44,printThis);
  //
  // The altitude with m added
  //
  t = String(altitude2,0)+" m";
  t.toCharArray(printThis,14);
  u8g.drawStr(50,44,printThis);
}

void setup() {
  //
  // I2C startup
  //
#if defined(ESP8266)
  //
  // For the ESP8266 we need to start the sensor with SDA and SCL pin numbers
  //
  sensor.begin(SDA,SCL);
#else
  //
  // For Arduino we can just call begin.
  //
  sensor.begin();
#endif
  //
  // SI7021
  //
  temperature = 0;
  humidity = 0;
  
  //
  // TSL2561
  //
  if (tsl.begin()) {
    hasTSL = true;
    tsl.setGain(TSL2561_GAIN_16X);
    tsl.setTiming(TSL2561_INTEGRATIONTIME_13MS);
  }

  //
  // BH1750
  //
  lightMeter.begin();
  //
  // BMP180
  //
  if (pressure.begin()) {
    hasBMP = true;
  }
}

void loop() {
  //
  // LCD, the draw routine is handled by the u8g library
  //
  u8g.firstPage();  
  do {
     draw();
  } while(u8g.nextPage());

  //
  // SI7021
  //
  temperature = sensor.getCelsiusHundredths();
  temperature = temperature / 100;
  humidity = sensor.getHumidityPercent();

  //
  // TSL2561
  //
  if (hasTSL) {
    uint32_t lum = tsl.getFullLuminosity();
    uint16_t ir, full;
    ir = lum >> 16;
    full = lum & 0xFFFF;
    lux1 = tsl.calculateLux(full, ir);
  }

  //
  // BH1750
  //
  lux2 = lightMeter.readLightLevel();

  //
  // BMP180
  //
  if (hasBMP) {
    char status;
    status = pressure.startTemperature();
    if (status != 0) {
      delay(status);
      status = pressure.getTemperature(temperature2);
      if (status != 0) {
        status = pressure.startPressure(3);
        if (status != 0) {
          delay(status);
          status = pressure.getPressure(pressure2,temperature2);
          if (status != 0) {
            double p0 = pressure.sealevel(pressure2,ALTITUDE);
            altitude2 = pressure.altitude(pressure2,p0);
          }
        }
      }
    }
  }
  
  delay(50);
}

If you have any questions/remarks, feel free to post them below.

SG90 and the NodeMCU

SG90 and the NodeMCU

On my Raspberry Pi I have code the control the Tower Pro SG90, a servo motor that can rotate 180 degrees. It would of course be great if I can get this cheap servo motor working on the cheap NodeMCU. I found some code on the internet which I modified and made into code which is usable on the NodeMCU. This video was actually quite useful in understanding the SG90.

Let’s hook up the SG90 to the NodeMCU. The brown wire is attached to ground, the red to 3.3v and the yellow/orange is your control signal. This you should connect to a digital out. In my code I used D3.

The SG90 is not like a motor that runs if you apply current but instead it moves itself to a position based on the width of the pulse in a 20 ms cycle. A 0.5 ms pulse will turn it to 0 degrees, a 1.5 ms pulse will turn it to 90 degrees and a 2.5 ms pulse will turn it to 180 degrees. The SG90 moves a bit towards the desired position with every pulse so you cannot just send one pulse and expect it to reach it’s end position.

The base of the end functions is this setup:

tmr.alarm(tmrnum,20,1,function()
  if servovalue and servovalue>0 then
    gpio.write(pin, gpio.HIGH)
    tmr.delay(servovalue)
    gpio.write(pin, gpio.LOW)
  end
end

It moves the servo motor on pin pin in steps towards position servovalue where servovalue is a number between 0 and 2000. This works, but it would be nice if we could set an angle (0 to 180 degrees) instead of a value between 0 and 1023. Especially since the 0 does not match 0 degrees and nor does 2000 match 180 degrees. This is mainly because each SG90 needs to be calibrated. Also the timer does not stop and that is also something that we want. So let’s first make a function that moves the SG90 to a specific value and stops afterwards. For now, ignore the servotimer variable. I will get to that later on.

--
-- move the servo on the specified pin to value servovalue
-- this is done in 25 steps
--
function setservovalue (pin, svalue)
  local cnt = 25
  local tmrnum
  if not servotimer[pin] then
    tmrnum = 2
  else
    tmrnum = servotimer[pin]
  end
  servovalue = min(2000,max(svalue,0))
  tmr.alarm(tmrnum,20,1,function()
    if servovalue and servovalue>0 then
      gpio.write(pin, gpio.HIGH)
      tmr.delay(servovalue)
      gpio.write(pin, gpio.LOW)
    end
    cnt = cnt-1
    if cnt<=0 then
      tmr.stop(tmrnum)
    end
  end)
end

In short, you call the function with the pin number that your SG90 is attached to and the value to which you want to move the SG90. The value is automatically trimmed to be between 0 and 2000. Moving the servo is done in 25 steps (found that out by trying, moving from 0 to 180 degrees). After the 25 steps, the timer is stopped.

You can use this function to find at which value your SG90 is at 0 degrees and at 180 degrees. Next, you want a function which you can call with degrees instead of a servo value. So let’s take a look at the whole code:

 --
-- define a servo entry where left is the servo value
-- at which it is at 0 degrees and right is the value
-- at which it is at 180 degrees.
--
-- example:
--   defineservo(3,200,1850)
--
-- the parameters are saved in servoleft and servoright
-- and a timernumber is generated and remembered so you
-- can change multiple servo's at the same time
--
function defineservo (pin, left, right)
  if not servoleft then
    servoleft = {}
  end
  if not servoright then
    servoright = {}
  end
  if not servotimer then
    servotimer = {}
  end
  servoleft[pin] = left
  servoright[pin] = right
  servotimer[pin] = 2+#servotimer
  gpio.mode(pin,gpio.OUTPUT)
end

--
-- move the servo on the specified pin to value servovalue
-- this is done in 25 steps
--
function setservovalue (pin, svalue)
  local cnt = 25
  local tmrnum
  if not servotimer or not servotimer[pin] then
    tmrnum = 2
  else
    tmrnum = servotimer[pin] 
  end
  servovalue = math.min(2000,math.max(svalue,0))
  tmr.alarm(tmrnum,20,1,function()
    if servovalue and servovalue>0 then
      gpio.write(pin, gpio.HIGH)
      tmr.delay(servovalue)
      gpio.write(pin, gpio.LOW)
    end
    cnt = cnt-1
    if cnt<=0 then
      tmr.stop(tmrnum)
    end
  end)
end

--
-- after you've defined the servo, you can set the angle
-- with this function
--
function setservo (pin, angle)
  local servovalue
  if servoleft[pin] and servoright[pin] then
    servovalue = (servoright[pin]-servoleft[pin])/180*angle+servoleft[pin] 
  else
    servovalue = 2000/180*angle
  end
  setservovalue(pin,servovalue)
end

You can use the function setservovalue to find out at what value you get a nice 0 degrees (in my test SG90 this was at 200) and after that use setservovalue to test at which value we get a nice 180 degrees (my test SG90, 1850). Now you are ready to define the servo with the function defineservo(3,200,1850). The values are kept and a timer number specifically for this servo is generated. I usually start at timer 2 for generic purposes so that’s why I used 2+#servotimer. Now it’s setup up for you to call setservo(3,0) to set it at 0 degrees, or for example setservo(3,45) to set it at 45 degrees.

NodeMCU & KY-019 5v relay module

NodeMCU & KY-019 5v relay module

A very nice addition to the 37 sensors set is the KY-019 5v relay module. It will allow you to switch 220v equipment with your NodeMCU.

Connecting it is straight forward, connect – to ground, s to a digital output (I use D3 in my example below) and the middle pin to 5v or 3.3v. According to the specs it is a 5v relay but I found that it seems to work with 3.3v as well.

On the other side of the relay there are 3 contacts. You might be able to read NC (normally close) and NO (normally open) on the print. The center pin is where you put the feed. On my print, the left pin was normally close, which means it is connected to the middle pin when no signal is put on port s. If you apply a signal on port s, the relay will switch (you can hear the click) and the middle pin and the right pin will be connected (the middle pin and the left will no longer be connected).

(please remember to take good care when you are switching 220v with your NodeMCU. Both you and the NodeMCU won’t like a 220v charge)

The code to switch the relay on and off:

gpio.mode(3,gpio.OUTPUT)

-- turn the relay on
gpio.write(3,gpio.HIGH)

-- turn the relay off
gpio.write(3,gpio.LOW)

 

NodeMCU & KY-012 Active buzzer

NodeMCU & KY-012 Active buzzer

The KY-012 active buzzer module, part of the 37 sensors set, is an active buzzer. It means operating it is much simpler than the passive buzzer, just apply power, but it also means it is less flexible; it either produces a sound or it doesn’t.

To connect it to the NodeMCU, connect the – to ground, the s (signal) to a digital input (D3 in my example) and the middle (+) to 3.3v. To use the buzzer:

gpio.mode(3,gpio.OUTPUT)

-- turn the buzzer on
gpio.write(3,gpio.HIGH)

-- turn the buzzer off
gpio.write(3,gpio.LOW)
NodeMCU & KY-006 passive buzzer

NodeMCU & KY-006 passive buzzer

There are 2 buzzers in the 37 sensors set and one of them is the KY-006 small passive buzzer. With a passive buzzer you create sound by applying current and removing it in rapid succession.

The NodeMCU is not the fastest machine out there, so don’t expect Für Elise but a few different notes you can get out of it.

To connect, simply attach – to ground, S to a digital input and the middle pin to 3.3v.

Now let’s look at the code:

function buzznow ()
  gpio.write(3,gpio.HIGH)
  gpio.write(3,gpio.LOW)
  cnt = cnt-1
  if cnt

To make a sound, you call for example buzz(3) which produces a note. I have found that it works in the range of 1 to 10. Larger numbers work but the tone does not change much.