Month: October 2016

Photo resistors, in depth

Photo resistors, in depth

Some time ago I bought 50 photo resistors, 10 pieces of 5 different types each. Had no real need for that many but it’s very cheap if you buy them through AliExpress. It turned out to be quite difficult to find out which resistor does what. Tried looking for photoresistors, LDR, Light Dependent Resistor, photo cell but I could not find any explanation on the different types.

I did find the following information (the table is compiled from different sources):

Model Light resistance
(10 Lux) KOhm
Dark resistance
MOhm
GL5506 2-6 0.15
GL5516 5-10 0.5
GL5528 10-20 1
GL5537 16-50 2
GL5539 30-90 5

But how does that help in connecting these things? Well, I decided to connect these things, all at the same time and look at the readings.

So connecting, how do we do that? Well, we need to understand (somewhat) Ohm’s law. To measure the amount of light, we want to measure the resistance of the light sensor. Unfortunately the Arduino or the ESP8266 do not measure resistance. They can however measure the voltage on an analog port and this is where we need Ohm’s law. First, let’s look at a schematic:

lightsensorconnect

We connect the photo resistor to +5V (or +3.3V) on one end and the other end on a analog port (the ESP8266 has one, the Arduino has many). We also connect that same end to a resistor (in my diagram a 10K resistor) and the other end of the resistor to ground. What does this do? Well, current will flow from +5V over the photo resistor, continue over the 10K resistor and than to ground. What we are going to measure is the voltage at the point between the photo resistor and the normal resistor. Ohm’s law states V=IR, voltage is current times resistance. The current flowing from +5V to ground over both resistors will be the same (the amount of current does not change midway a circuit). That’s the key to figuring out the current at our analog port and here is how.

We can calculate the total current that flows from +5V to ground by adding the resistors and plugging it in Ohm’s law. Let’s say the photo resistor currently has a resistance of 10KΩ. The total resistance of the circuit is 10KΩ+10KΩ = 20KΩ. V = 5V which means I = 5V / 20KΩ = 0.25 mA. So we now know that there is 0.25 mA running through the circuit. Now we can calculate V at our analog input. The voltage drop over the photo resistor is V = IR and we now know I and R. V = 0.25 mA * 10KΩ = 2.5 V drop so at our analog input we will measure 5V – 2.5V = 2.5V. With 2 equal resistances, it makes sense that in the middle you will have 2.5V. Let’s take the next step.

The resistance of the photo resistor goes down when there is more light, so let’s make it a little darker, say 30KΩ. Let’s recalculate:

Total R = 30KΩ + 10KΩ = 40KΩ
I = 5V / 40KΩ = 0.125 mAVoltage drop over photo resistor = 0.125 mA * 30KΩ = 3.75 V
We measure 5V – 3.75V = 1.25V at our analog input

So less light means less voltage at our analog input. Well this math is all fine, but what does this mean? Well, if we know the voltage at our analog input we can also do the reverse and calculate the resistance of the photo resistor, just do the reverse.

Let me quickly share the setup and the code I used to test the resistors. You can my setup at the top. It is an Arduino Nano (which has 5 analog ports) and the 5 sensors, the bottom is the 5506 and moving up all the way to the 5539 at the top. It might be a bit hard to see. All black wires are +3.3V, all the white are ground and the different colors are for each of the analog inputs. You can see that I followed the schematic 5 times and importantly, used five 10KΩ resistors. You cannot combine all of them on one. Please do not forget to connect 3.3V to ARef which gives a better ready is my experience.

The code is simple, initialize five ports and continously display the results of the ports. I have added a header row every 10 rows so I can copy and paste that:

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

//
// The number of steps after which the header is repeated
//
#define STEPCOUNT 10
//
// The current step count
//
byte step = STEPCOUNT;

void setup() {
  //
  // Setup serial
  //
  Serial.begin(115200);
  Serial.println("Ready");
  //
  // Set the reference for measuring the analog signals
  // For some reason I got incorrect readings without connecting
  // the 3.3v (on my Arduino Nano) to AREF and setting this
  // reference
  analogReference(EXTERNAL);
  //
  // Setup all analog inputs. We are using 5 which is the maximum
  // for the Nano on which I tested.
  //
  pinMode(A0,INPUT);
  pinMode(A1,INPUT);
  pinMode(A2,INPUT);
  pinMode(A3,INPUT);
  pinMode(A4,INPUT);
}

void loop() {
  //
  // Increase our counter
  //
  step++;
  //
  // If we are over our STEPCOUNT then we repeat the header
  //
  if (step>STEPCOUNT) {
    //
    // The reference to the different photo resistors
    // GL5506, GL5516, GL5528, GL5537 and GL 5539
    //
    Serial.println("* 5506  5516  5528  5537  5539");
    step = 0;
  }
  //
  // Print all analog values to serial
  //
  Serial.print("  ");
  Serial.print(analogRead(A0));
  Serial.print("   ");
  Serial.print(analogRead(A1));
  Serial.print("   ");
  Serial.print(analogRead(A2));
  Serial.print("   ");
  Serial.print(analogRead(A3));
  Serial.print("   ");
  Serial.print(analogRead(A4));
  Serial.println();
  delay(250);
}

If we take the values it shows during daylight, inside the house:

* 5506  5516  5528  5537  5539
  975   969   774   619   218
  975   968   773   619   220
  975   969   773   618   216
  975   968   771   616   215
  975   968   771   617   221
  975   969   774   620   226
  975   969   776   622   225
  976   969   775   621   221
  975   969   774   619   220
  975   969   774   619   219
  975   968   772   617   216

As you can see, the values are relatively stable. So let’s take one of the values of the 5528, 775. How do we calculate the resistance from that? The analog port has a 10 bit resolution, which means it can go from 0 to 1023. 1023 will be at 3.3V (in my case. If you use a Uno for example it will be 5V). The reading is 496 which means it is reading a voltage of 775/1023*3.3V = 2.5V. So we know the voltage drop was 3.3V-2.5V = 0.8V. So we know V. Now we need to know I and from that we can calculate R. We can calculate R because we know V and R for the other part of the setup, the 10KΩ. I = V/R = 2.5V / 10KΩ = 0.25 mA. Now we take that 0.25 mA and use it for calculating the resistance of the photo resistor.
R = V/I = 0.8V / 0.25 mA = 3,200 Ω. Now you can go to the GL5528 datasheet and see

gl5528

that the amount of light is between 30 and 85 lux. Well, that’s quite a broad range and not really useful for calculating the amount of lux but the deviation of these sensors might vary piece to piece but do not vary that much per piece, so if your sample decides to be more towards the dark side, it will be there with higher or lower light settings as well. What this means is that the sensors are great for detecting deltas in light even though getting a precise lux reading is not really feasible. Ok, well than we need more readings:

light from a normal bulb:
* 5506  5516  5528  5537  5539
  974   967   739   623   279
  982   981   844   717   437
  992   988   870   751   454
  994   991   874   758   440
  995   991   873   752   325
  992   986   836   700   137
  985   979   795   654   62
  978   972   754   610   34
  971   969   792   668   379
  988   985   854   727   440
  993   990   871   749   440

light from a mobile phone
* 5506  5516  5528  5537  5539
  1006   1007   956   925   795
  1006   1007   956   925   796
  1006   1007   956   925   797
  1006   1007   956   925   797
  1006   1007   956   926   798
  1006   1007   955   925   795
  1006   1007   956   925   796
  1005   1007   955   924   795
  1006   1007   955   924   793
  1005   1007   955   925   796
  1005   1007   955   925   795

light at dusk
* 5506  5516  5528  5537  5539
  834   834   385   208   20
  840   838   394   216   22
  840   840   402   222   23
  840   840   405   225   24
  839   839   404   225   24
  839   838   404   224   23
  840   838   405   225   24
  841   839   405   226   24
  841   839   405   226   24
  844   840   406   227   24
  845   840   406   227   24

light in the evening
* 5506  5516  5528  5537  5539
  582   583   118   52   1
  581   582   118   52   1
  580   583   118   53   1
  580   582   118   52   1
  580   581   118   52   1
  578   580   117   52   1
  577   577   116   52   1
  565   561   109   49   1
  584   583   118   51   1
  586   587   120   53   1
  589   590   121   53   1

There are a few things to notice. First, I thought it was interesting to see that the values fluctuated with a normal bulb. This sample was not an incident, it was doing that the whole time. I think it might have to do with the fact that light bulbs fluctuate because of the way the alternating current alternates but I’m not sure. The second thing to notice is that the 5506 does not really vary that much with light, especially compared to the others. It’s not because it’s broken, but it’s because it’s resistance is small compared to the 10kΩ resistor which means it does not leave a lot of ‘bandwidth’ to show it’s capabilities.

Maybe I will do some more readings where I take a resistor to match each photo resistor and see what the results are than.

Advertisements
ESP and selecting wifi access point

ESP and selecting wifi access point

One of the nice things about the ESP8266 (apart from the wifi) is that it has a nice file system which you can use. It should be installed if you have installed the ESP boards. Documentation on the SPIFFS can be found here.

The documentation also mentions a tool to upload files to the ESP. Very convenient. Please note though that it uploads all the files in the data directory but it removes everything that is on there already. What I did not find in the documentation is that the upload will not work if you have a serial monitoring screen open.

I will not share examples from the documentation but instead I will share a convenient usage of the file system.

I like to take my current projects with me when I move around and this means that the wifi you have at home will not be available. I did not want to recompile something just to enter different wifi credentials so I came up with a trick to allow it to connect to different wifi access points depending on where it is. The assumption with this implementation is that you broadcast your SSID. You could implement something like, if I don’t find any of my preferred networks, connect to the one hidden one.

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

Let’s look at the code:

//
// You will of course need to include the ESP8266WiFi.h which
// will give you access to the WiFi library of the ESP.
//
#include "ESP8266WiFi.h"
//
// The FS.h will give you access to all of the SPIFFS functions.
//
#include "FS.h"

//
// SERIAL is only defined if you wish to start the serial monitor,
// usually only done for debugging purposes.
// When putting it to production, you can remove this define.
//
#define SERIAL

//
// Setup. This will run only once and will connect you to a
// available AP to which you have the credentials.
//
void setup() {
  //
  // Start serial monitoring if required.
  //
#ifdef SERIAL
  Serial.begin(115200);
  Serial.println("Starting SPIFFS.");
#endif
  //
  // Start the file system.
  //
  bool result = SPIFFS.begin();
#ifdef SERIAL
  Serial.println("SPIFFS opened.");
#endif
  //
  // Set WiFi to station mode and disconnect from an AP if it was
  // previously connected.
  //
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);
#ifdef SERIAL
  Serial.println("Wifi scan start.");
#endif
  //
  // WiFi.scanNetworks will return the number of networks found
  //
  int n = WiFi.scanNetworks();
#ifdef SERIAL
  Serial.println("Wifi scan done.");
#endif
  if (n==0) {
#ifdef SERIAL
    Serial.println("No networks found.");
#endif
  } else {
#ifdef SERIAL
    Serial.print(n);
    Serial.println(" networks found.");
#endif
    //
    // Loop through all the found SSIDs
    //
    for (int i=0;i<n;i++) {
      //
      // Check whether there is a file called /wifi/<SSID>.txt
      //
      if (SPIFFS.exists("/wifi/"+WiFi.SSID(i)+".txt")) {
#ifdef SERIAL
        Serial.print("Found ");
        Serial.println(WiFi.SSID(i));
#endif
        //
        // Open the file /wifi/<SSID>.txt to read the password.
        //
        File f = SPIFFS.open("/wifi/"+WiFi.SSID(i)+".txt","r");
        String line = f.readStringUntil('\n');
        char ssid[50];
        WiFi.SSID(i).toCharArray(ssid,50);
        char passw[50];
        line.toCharArray(passw,50);
        //
        // After converting the strings, start the wifi.
        //
        WiFi.begin(ssid,passw);
        //
        // After connecting to the wifi, stop looking.
        //
        break;
      }
    }
  }
}

void loop() {
}

The code starts by initializing serial (which I normally only use in development), followed by initializing SPIFFS. Next the ESP will scan all available networks (this is why your SSID needs to be visible) and for every found network, it checks whether there is a file called:

/wifi/.txt

If so, it opens the file and reads the content which will be the password for that SSID, it connects to that wifi and breaks the loop. If not it continues to with the next SSID.

So to have it autoconnect to a wifi, simply put SSID.txt files contain the password for the SSID in your data directory and upload with the ESP8266 Sketch Data Upload tool in the Tools menu of the Arduino IDE. For completeness, your path would look something like:

…./My Documents/Arduino//data/wifi/SSID1.txt

where SSID1 and so on are the actual names of a wifi access point that you have the credentials for.

ESP & internal LED

ESP & internal LED

I wanted to use the internal LED of the ESP8266 (and the NodeMCU‘s). I did in the past with LUA but I never documented that properly (just in the code) so I decided to figure it out again using the Arduino IDE.

There is a lot of discussion on whether or not you can use the internal LED or not. If you have not programmed it for any use, the blue LED will work as a TXD LED, showing you for example when you are uploading code. You can however override that behavior and use it on your own. Contrary to what I have seen on forums, you can use the internal LED and at the same time send traffic to serial without the LED losing the function you want it to have.

For the record, I have tested this on a bare ESP-12E, a Wemos D1 and a NodeMCU v1. All of them worked the same.

This will probably be the shortest code I’ve published, turning on the internal LED:

void setup() {
  pinMode(2,OUTPUT);
  digitalWrite(2,LOW);
}

void loop() {
}

Pin 2 is the one you want. Notice that you write a LOW to turn the LED on. Also notice that I did not use LED_BUILTIN which works on Arduino’s. For some reason this constant is not the same for every board. For the Wemos it is correct, 2. For the generic ESP8266 it is not. LED_BUILTIN is set to 1 which is not correct. For the NodeMCU it is set to 16. The interesting part of that is that 16 is red LED on the NodeMCU which means you can control two LEDs on the NodeMCU. Incidently, the LED on pin 16 should also be set LOW to turn it on.

One last part to this. Why? Why would you want to use the internal LED? Well if you have a very small setup, you can use it as an error LED. That is why I use it mostly, as an error LED. The ESP will start up and I will turn off the LED (also very convenient to not have this small blue LED show up as a reflection on the wall or something because your IoT lamp controller is there). If it connects to wifi correctly, I will leave it off. If for some reason it cannot connect to wifi or some other error occurs, I turn on the LED. That way I can see if it is running correctly or ran into an issue.

Updated WiFi Library

Updated WiFi Library

UPDATE: I have worked on my library and created two more. Click here to see the list of articles on the libraries

You might have seen that I created a convenient WiFi Library for easy Wifi Connect & HTTP GET. I created an updated version of this which I will paste below. I know, I should put it on github.com and as soon as I have team to figure that out, I will.

The updated library is the same except that you can connect to a custom port (which I needed)

PAM_WiFi.cpp

#include <PAM_WiFi.h>
#include <ESP8266WiFi.h>
void wifiConnect (String ssid, String password) {
  char xssid[50];
  ssid.toCharArray(xssid,50);
  char xpassword[50];
  password.toCharArray(xpassword,50);

  bool connected = false;
  byte cnt2 = 0;
  while (!connected && cnt2<5) {
    WiFi.begin(xssid,xpassword);
    byte cnt = 0;

    while (WiFi.status()!=WL_CONNECTED && cnt<20) {
      delay(500);
      Serial.print(".");
      cnt++;
    }
    if (WiFi.status()!=WL_CONNECTED) {
      WiFi.disconnect();
      Serial.println();
    } else {
      connected = true;
    }
    cnt2++;
  }
}

void doNothing (String webLine) {
}

String getURLParse (char* host, String url, bool fullContent, void (*g)(String l), int port) {
  String lastline;
  boolean inPage;
  inPage = false;
  WiFiClient client;
  if (!client.connect(host,port)) {
    return URLNOCONNECT;
  }
  client.print("GET " + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" + 
               "Connection: close\r\n\r\n");
  unsigned long timeout = millis();
  while (client.available() == 0) {
    if (millis() - timeout > 5000) {
      client.stop();
      return URLTIMEOUT;
    }
  }
  lastline = "";
  while(client.available()){
    String line = client.readStringUntil('\r');
    line.replace("\n","");
    if (line.length()>0) {
      lastline = line;
    }
    if (fullContent || inPage) {
      g(line);
    }
    if (line.length()==0) {
      inPage = true;
    }
  }
  return lastline;
}

String getURLParse (char* host, String url, bool fullContent, void (*g)(String l)) {
  return getURLParse(host,url,fullContent,g,80);
}

String getURLFullParse (char* host, String url, void (*g)(String l), int port) {
  return getURLParse(host,url,true,g,port);
}

String getURLFullParse (char* host, String url, void (*g)(String l)) {
  return getURLParse(host,url,true,g,80);
}

String getURLBodyParse (char* host, String url, void (*g)(String l), int port) {
  return getURLParse(host,url,false,g,port);
}

String getURLBodyParse (char* host, String url, void (*g)(String l)) {
  return getURLParse(host,url,false,g,80);
}

String getURL (char* host, String url, int port) {
  return getURLParse(host,url,false,doNothing,port);
}

String getURL (char* host, String url) {
  return getURLParse(host,url,false,doNothing,80);
}

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

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.

Advanced webserver in Arduino IDE

Advanced webserver in Arduino IDE

After getting the basic webserver working, I wanted to get a more complex setup, different pages and parameters in the url so that you can make you device do things. So basically it is an upgrade of the previous instance but with three functions added.

The function uriRequested returns the complete url you requested, including parameters. The function pageRequested returns only the page name you requested and finally giveUrlParam gives you the value of the parameter you requested.

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

Let’s look at the code:

//
// The ESP8266WiFi library is needed
//
#include <ESP8266WiFi.h>

//
// If a requested paramter is not found, this is what is returned
//
#define PARAMNOTFOUND "XxXxXxX"

//
// Please fill in your SSID and password
//
const char* ssid = "YourSSID";
const char* password = "YourPassword";

//
// Initialize the webserver
//
WiFiServer server(80);

void setup() {
  //
  // Initialize serial output
  //
  Serial.begin(115200);
  Serial.print("Connecting to ");
  Serial.println(ssid);
  //
  // Connect to the wifi and wait for a connection
  //
  WiFi.begin(ssid,password);
  while (WiFi.status()!=WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  //
  // Start the webserver
  //
  server.begin();
  delay(500);
  //
  // Show the IP of the server in serial monitor
  //
  Serial.println("Web server running. The IP is:");
  Serial.println(WiFi.localIP());
}

//
// Given the full request string, return just the URI
//
String uriRequested (String reqstr) {
  byte p = reqstr.indexOf("T /")+2;
  return reqstr.substring(p,reqstr.indexOf(" ",p));
}

//
// Given the full request string, return the requested page
//
String pageRequested (String reqstr) {
  String t = uriRequested(reqstr);
  if (t.indexOf("?")>=0) {
    t = t.substring(0,t.indexOf("?"));
  }
  return t;
}

//
// Given the full request string, return the value of the parameter requested
// or PARAMNOTFOUND
//
String giveUrlParam (String reqstr, String search) {
  String t = uriRequested(reqstr);
  if (t.indexOf("?")>=0) {
    t = "&"+t.substring(t.indexOf("?")+1);
    int p = t.indexOf("&"+search+"=");
    if (p==-1) {
      return PARAMNOTFOUND;
    } else {
      p = t.indexOf("=",p)+1;
      t = t.substring(p,t.indexOf("&",p));
      return t;
    }
  } else {
    return PARAMNOTFOUND;
  }
}

void loop() {
  WiFiClient client = server.available();
  if (client) {
    //
    // A web client is asking for a page
    //
    Serial.println("Page requested");
    boolean lastLineBlank = true;
    String requestString = String(100);
    while (client.connected()) {
      //
      // We need to read the request of the client
      //
      if (client.available()) {
        char c = client.read();
        if (requestString.length()<100) {
          requestString = requestString+c;
        }
        if (c=='\n' && lastLineBlank) {
          //
          // We have read the request of the client. Now send the correct page
          //
          Serial.println(requestString);
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");
          client.println();
          client.println("<!DOCTYPE HTML>");

          //
          // pr will contain the name of the page
          //
          String pr = pageRequested(requestString);
          if (pr=="/index.php" || pr=="/") {
            client.println("<html>");
            client.println("<head></head><body>");
            client.println("<h1>You requested the main page</h1>");
            client.println("</body></html>");
          } else if (pr=="/other.php") {
            client.println("<html>");
            client.println("<head></head><body>");
            client.println("<h1>You requested the other page</h1>");
            pr = giveUrlParam(requestString,"id");
            //
            // In this page we demo that you can see if someone has put
            // the id param in the requested url and if so, what the value is
            //
            if (pr==PARAMNOTFOUND) {
              client.println("You did not want to send your id");
            } else {
              client.println("You gave your id which is <b>"+pr+"<br>");
            }
            client.println("</body></html>");
          } else {
            client.println("<html>");
            client.println("<head></head><body>");
            client.println("<h1>You requested an unknown page</h1>");
            client.println("</body></html>");
          }
          break;
        }
        if (c=='\n') {
          lastLineBlank = true;
        } else if (c!='\r') {
          lastLineBlank = false;
        }
      }
    }
    delay(1);
    client.stop();
    Serial.println("Page send.");
  }
}

The code above should be, with the help of the comments, self explanatory. Feel free to use and modify. If you have questions, please post them below.

ESP Webserver with Arduino IDE

ESP Webserver with Arduino IDE

I previously shared my implementation of an HTTP server on the EPS8266 based on LUA. Now, with all the work that I’ve been doing with the Arduino IDE, I wanted to be able to do it in the Arduino code as well.

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

Below the code for a minimal web server:

//
// Include the ESP8266WiFI library
//
#include <ESP8266WiFi.h>

//
// Fill in your own SSID and password
//
const char* ssid = "YourSSID";
const char* password = "YourPassword";

//
// Initialize the WiFiServer
//
WiFiServer server(80);

void setup() {
  //
  // Initialize serial output
  //
  Serial.begin(115200);
  Serial.print("Connecting to ");
  Serial.println(ssid);
  //
  // Start the wifi and wait for a connection
  //
  WiFi.begin(ssid,password);
  while (WiFi.status()!=WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  //
  // Start the webserver
  //
  server.begin();
  delay(500);
  //
  // Show the IP of the server in serial monitor
  //
  Serial.println("Web server running. The IP is:");
  Serial.println(WiFi.localIP());
}

void loop() {
  WiFiClient client = server.available();
  if (client) {
    //
    // A web client is asking for a page
    //
    Serial.println("New client");
    boolean blank_line = true;
    while (client.connected()) {
      //
      // We need to read the request of the client
      //
      if (client.available()) {
        char c = client.read();
        if (c == '\n' && blank_line) {
          //
          // We have read the request of the client. Now send the page
          //
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");
          client.println("<head></head><body><h1>Test</h1></body></html>");  
          break;
        }
        if (c == '\n') {
          blank_line = true;
        } else if (c!= '\r') {
          blank_line = false;
        }
      }
    }  
    delay(1);
    client.stop();
    Serial.println("Client disconnected.");
  }
}

It is not totally minimal as you can remove the references to Serial. The code is mostly self explanatory but a short summary. In the setup you connect to the wifi ssid with the given password. You wait for it to be connected. After that, you start the webserver which was previously defined at port 80, wait a brief moment and share it’s IP on the serial monitor.

In the loop function we wait for a client to appear after which we read what it wants until we get a double blank line (marked by character \n) which means the request has ended. After that we send him the page. Remember to send the lines HTTP/1.1 etc because at least iOS requires this (see my previous post on this).

The next post I will take a look at a more complex webserver where we send different pages and look at the GET parameters.

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  & HC-SR04 ultrasonic distance

Arduino & HC-SR04 ultrasonic distance

A very fun sensor is the HC-SR04 ultrasonic distance sensor. It’s fun because it is quite a good sensor and fun to play with and of course it looks like it’s a pair of eyes looking at you.

When connecting the sensor, you need to connect 4 pins, GND to GND, VCC to 5V (if you connect it to a 3.3V power supply, it won’t work), a trigger pin and an echo pin.

In the example below in the Arduino IDE, I have used 13 as the trigger pin and 12 as the echo pin.

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

//
// The HC-SR04 requires two pins, one to trigger a reading (TRIGGERPIN)
// and one to measure the distance (ECHOPIN)
//
#define TRIGGERPIN 13
#define ECHOPIN 12

void setup() {
  //
  // Intialize serial
  //
  Serial.begin (115200);
  //
  // The TRIGGERPIN will be an output
  //
  pinMode(TRIGGERPIN,OUTPUT);
  //
  // The ECHOPIN will be an input
  //
  pinMode(ECHOPIN,INPUT);
}

void loop() {
  //
  // The duration will contain the time in microseconds that the echo takes
  // to return. The distance will contain the distance in cms
  //
  long duration, distance;
  //
  // To tell the HC-SR04 to start a reading, we should put the TRIGGERPIN
  // high for 10 microseconds, so we pull it LOW, than HIGH for 10 microseconds
  // and LOW again. Immediately after we check how long the ECHOPIN is HIGH
  //
  digitalWrite(TRIGGERPIN,LOW);
  delayMicroseconds(5);
  digitalWrite(TRIGGERPIN,HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIGGERPIN,LOW);
  duration = pulseIn(ECHOPIN,HIGH);
  //
  // The distance travelled by the sound send out is duration divided by the
  // pace of sound which is 29.1 ms/cm. We divide by 29.1 to get the amount
  // of centimeters and we divide by two because the sound travel to the object
  // and back, so the distance to the object is half the distance traveled
  // by the sound.
  //
  distance = (duration/2)/29.1;
  if (distance>=200 || distance<=0){
    Serial.println("Out of range");
  } else {
    Serial.print(distance);
    Serial.println(" cm");
  }
  delay(100);
}

To trigger a distance reading, you have to set the trigger pin high for 10 milliseconds. What I have done in the example above is take the trigger pin low for 5 ms, that bring it high for 10 ms and bring it back low again. Theoratically speaking, you should not need the first digitalWrite(TRIGGERPIN,LOW) but it’s there to make sure the pin is low.

Next we read the time the ECHOPIN stays HIGH (so it goes from LOW to HIGH, than stays at HIGH depending on the time it takes for the echo to return). We take that duration and divide it by 2 and than by 29.1. We divide by 29.1 because that is the pace of sound (which is 1 / speed of sound) in dry air. So we are saying that for every 29.1 ms sound travels 1 cm. We divide by two because the distance calculated is the distance from our sensor to the object and back to the sensor.