Sunday, December 25, 2016

Mini Portable Temp/Humidity Sensor (ESP01 + DHT22 + LiPo Battery)

I like making sensors, I have a few projects at any given time dealing with some sort of sensors. I finished one today, enough to call it done for now that is. It uses a basic DHT22 sensor with an ESP01 wifi module. You can also use a DHT11 but the 22's cost a little more and offer better accuracy.

There are plenty of tutroials out there on how to do this. The sort of unique thing for mine is I wanted the data put in to an ElasticSearch instance I run. I'm all about sensors and storing info, and ElasticSearch has been great for that. So since I already have an ELK setup running for things like my Geiger Counter, Nest Thermostat, and Weather Underground data, I figured having my own little temp/humidity modules adding data would be a great spot.

The problem with most tutorials out there is that they focused on creating a webpage you go to, or posting data to some website that offers IOT services. So my version 1 of this included just making the ESP have a basic webpage with the available temperature and humidity information on it. It would just constantly loop checking the sensor and providing the connection. That wasn't ideal as that involves me to visit the webpage either manually or with a script. So I used a script for a while and it worked fine, but I still wanted them to be standalone objects where I didn't need to have static IP's for them or modify the script everytime I added or removed one.

So version 2 I sat down and finally figured out how to have the ESP module post data to a Logstash instance and get it imported in to Elasticsearch. Turns out it was pretty easy to do.


First the Hardware. This is the working version and it's compact. Not the prettiest but it works fine. Here's what's inside it and some rough prices.


  • ESP01 module - $2
  • DHT22 sensor module - $3
  • TP4056 LiPo Charger - $1.20
  • 100 mAh LiPo battery - $4
  • Micro USB prt on the TP4056 and a Male USB port added for power as well $0.20
  • On/Off Switch $0.02
  • A Diode to drop the voltage just a little to the ESP $0.01
So about $11 per module. Cheaper if you just use a 3.3 volt regulator on a USB port to power it. But I wanted a battery to make it portable (put it where you want to measure something like a car or garage).







You can plug it in to Micro USB....

Or plug it in to one of the readily available USB plugs and let it be.



Here's a basic chart of the data the module POSTs to ELK. The temp is high because it's sitting on my computer tower running in battery only mode right now. I'm trying to see the run time on it, and I'll hopefully update the next sentance once I get a time.





And the data that's being collected each POST and stored.






And here's the code. The few parts you'd want to change are probably the Node name of the module, and your Logstash instance IP/Port.


#include
#include

const char* ssid     = "SSID Home";
const char* password = "P@ssword";

#define DHTPIN 2
#define DHTTYPE DHT22

DHT dht(DHTPIN, DHTTYPE);



void setup() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  pinMode(LED_BUILTIN, OUTPUT);
  while (WiFi.status() != WL_CONNECTED) {
    digitalWrite(LED_BUILTIN, LOW);
    delay(100);
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
  }
  dht.begin();
}




void loop() {
  

  // Read humidity
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  float f = dht.readTemperature(true);

  WiFiClient client;

  String PostData = "{\"node\": \"LivingRoom\",\"humidity\": ";
  PostData += (float)h, 2;
  PostData += ",";
  PostData += "\"temp_c\": ";
  PostData += (float)t, 2;
  PostData += ",";
  PostData += "\"temp_f\": ";
  PostData += (float)f, 2;
  PostData += "}";


  const uint16_t port = 8080;
  const char* host = "192.168.1.18";

  if (client.connect(host, port)) {
    digitalWrite(LED_BUILTIN, LOW);
    client.println("POST /posts HTTP/1.1");
    client.println("Host: 192.168.1.18");
    client.println("Cache-Control: no-cache");
    client.println("Content-Type: application/json");
    client.print("Content-Length: ");
    client.println(PostData.length());
    client.println();
    client.println(PostData);
    client.stop();
    digitalWrite(LED_BUILTIN, HIGH);
  }

  delay(60000);

  
}






Friday, December 23, 2016

ESP8266 - Create tons of SSID's...with style

First off, the inspiration and most of the credit comes from this blog post. It gave me an idea to create a module that has some extended capabilities. So I set to work. And spoiler, I made it and it works fine.

Random SSID AP's are neat but the esp can do a lot more (more than I know how to do I'm sure). So I decided to add some features. Since there are 2 GPIO's and two states, I could come up with 4 variations in the actions of that script. So before building or coding anything, I decided what I wanted it to do (without knowing if it was even possible).


  • Mimic Mode - This would scan local access points and spoof them.
  • Static Mode - This would be a single static SSID that would be blasted out.
  • List Mode - This would be a similar to static, but from a preset list it loops through.
  • Random Mode - essentially the same thing as the blog post that inspired this.
First things first. I'm not that good with arduino and this is my first esp8266 project. But there are a lot of examples and truthfully, I'm pretty good a copy/pasting other peoples code and getting it to work how I want. So the code probably isn't the best and definitely isn't pretty. The only coding language I know is bash, so if anything it's probably written in more of a scripting way.

Here's the kicker from the original blog post. That's cool that one esp can do all those fake esp's, but what about more. What about.....8 :). What about.....16 :). Well that's what I decided to do. The module has 8-16 available sockets when plugged in. Here's a picture real quick so you know what it looks like as I continue to talk about it.


A few notes:
  • The LED display at the top is a pre-bought voltage meter. These are great and cheap (get them on eBay). Since the ESP modules are sensitive to voltage, I added one. 
  • Three slide switches.
    • Upper right - Power
    • Lower ones connect to the two GPIO pins and set the Mode of operation listed above
  • USB pcb plug bolted on
  • Right above the USB plug is a buck converter set to drop the 5 volts USB down to about 3.8 volts for the ESP modules
  • All of the sockets for the ESP's (2x4's) are all connected in parallel, which sucked to solder.


I said up to 16, why are there only 8 modules? Well, power. I used this with a USB power bank that is rated to provide up to 2.4 amps. That amperage is able to power 8 of them steadily, but not 16. That dips the voltage too much and most of the modules end up not starting up. So here's what I did. The back PCB plate with the other 8 sockets is removable. I did this by using 4 brass standoffs to separate the PCB's. With 4 posts, I could provide positive power, negative power, and the two GPIO pin settings. Just pinch the wire between the posts and it makes it easy to add the second board with sockets or remove it and have a blank board as a back. Bam, easy.



About the Code. 
The hardest thing to program was the Mimic Mode. This one scans for local access points to mimic. Scan, store, reproduce. But just mimicking isn't good enough. The SSID's need to be slightly different. This is because the exact same SSID's will normally jsut be grouped together. So I came up with a simple way to avoid this from happening for the host machines. Just add spaces. So here's what I did. When broadcasting any of the SSID's, every loop adds a space to teh SSID until it hits the max SSID length. So guest wifi will be guest wifi , guest wifi  , guest wifi   , guest wifi    , etc. Easy for a computer to tell the difference but near impossible to tell for a human. 

The GPIO settings. So the ESP (to start up properly) needs the gpio pins set to HIGH to start properly. Opposite from most things Arduino, OFF is HIGH and ON is LOW essentially. If the ESP starts with the GPIO pins LOW, it won't start at all and will end up in flash mode or something else. So in the VOID SETUP section, the modules wait 5 seconds after power on for the GPIO pins to be set, then reads them. One they are read, it sets the parameters for whatever mode is selected. 

The relevant code about the Mode selection (the delays were for some watchdog issues that may have been unrelated...):


  if (switchGPIO0 == HIGH && switchGPIO2 == HIGH)
  {
    mimicMode = true;
    scan = true;
    delay(1);
  }
  else if (switchGPIO0 == LOW && switchGPIO2 == HIGH)
  {
    randomMode = true;
    delay(1);
  }
  else if (switchGPIO0 == HIGH && switchGPIO2 == LOW)
  {
    listMode = true;
    delay(1);
  }
  else //(switchGPIO0 == LOW && switchGPIO2 == LOW)
  {
    staticMode = true;
    delay(1);
  }


So by having each pin in a given on/off position, you have 4 modes it can be configured to do. Since mimic mode is the coolest mode, and the pin defaults are HIGH, that's the defaulkt mode if no switches are toggled.

Really that's pretty much it. It can broadcast a crap ton of stuff. It's hard to measure since it overloads most wifi modules I've checked. Like a built in wifi card in a netbook and an external Alfa adapter I checked. Seems that when there' sover 255 AP's available, stuff starts having problems.

Here's a quick video of it in Mimic Mode. There are 5 flashes to set the mode, and then 3 more in Mimic Mode to let all the ESP's finish their scan. The once any broadcasting in any more starts, it flashes the built in Blue LED on and off for each loop. It sends a lot out since it looks steady once started.



And the current code (it ain't pretty) in Arduino IDE. I'm sure it can be better but for a silly project, I'm calling it done:


#include

extern "C" {
#include "user_interface.h"
}


byte channel;
int wifiCount = 0;
int fullPacket;
int switchGPIO0;
int switchGPIO2;
String wifissid;

bool promiscuousMode = false;

bool mimicMode = false;
bool scan = false;

bool randomMode = false;
int randomStart;

bool listMode = false;
char* networkList[] = {"attwifi", "HomeNetwork", "Starbucks", "guest", "ATT_8765", "Beaches", "Seagulls" };


bool staticMode = false;
String staticNet = "¯\\_(ツ)_/¯";





void setup() {
  delay(1);
  WiFi.mode(WIFI_STA);
  // Initialize the LED_BUILTIN pin as an output
  pinMode(LED_BUILTIN, OUTPUT);

  // Initialize the GPIO pins as inputs
  pinMode(0, INPUT);
  pinMode(2, INPUT);
  // Wait 5 seconds to set switches
  for (int i = 0; i < 5; ++i)
  {
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
  }

  // Check the GPIO config set up and determine the operation mode being used
  switchGPIO0 = digitalRead(0);
  switchGPIO2 = digitalRead(2);
  delay(1);

  if (switchGPIO0 == HIGH && switchGPIO2 == HIGH)
  {
    mimicMode = true;
    scan = true;
    delay(1);
  }
  else if (switchGPIO0 == LOW && switchGPIO2 == HIGH)
  {
    randomMode = true;
    delay(1);
  }
  else if (switchGPIO0 == HIGH && switchGPIO2 == LOW)
  {
    listMode = true;
    delay(1);
  }
  else //(switchGPIO0 == LOW && switchGPIO2 == LOW)
  {
    staticMode = true;
    delay(1);
  }


}







void loop() {

  // Write blue LED HIGH (off)
  digitalWrite(LED_BUILTIN, HIGH);

  // Do an initial scan to find available network names
  if (scan == true)
  {
    wifiCount = WiFi.scanNetworks();
    // Make sure this doesn't run again now that the scan is done
    scan = false;
    // Wait 3 seconds for the other modules before starting
    for (int i = 0; i < 3; ++i)
    {
      digitalWrite(LED_BUILTIN, LOW);
      delay(500);
      digitalWrite(LED_BUILTIN, HIGH);
      delay(500);
    }
  }
  else if (randomMode == true)
  {
    wifiCount = 50;
    // So after 50 loops below, pick a new random number
    randomStart = random(23560);
  }
  else if (listMode == true)
  {
    wifiCount = 6;

  }
  else if (staticMode == true)
  {
    wifiCount = 1;
  }




  if (promiscuousMode == false)
  {
    promiscuousMode = true;
    wifi_set_opmode(STATION_MODE);
    wifi_promiscuous_enable(1);
  }


  delay(1);




  // Start looping through SSID's
  for (int i = 0; i < wifiCount; ++i)
  {
    // Get the SSID to use this round (increasing array value changes SSID)
    if (mimicMode == true)
    {
      // Pick a random SSID
      //int wifiCountArray = wifiCount - 1;
      wifissid = WiFi.SSID(random(wifiCount));
    }
    else if (randomMode == true)
    {
      // Just start at the picked random number and add 1 each loop
      int randVal = randomStart + 1;
      wifissid = String(randVal);
    }
    else if (listMode == true)
    {
      wifissid = networkList[i];
    }
    else
    {
      wifissid = staticNet;
    }



    // Beacon Packet buffer ( this resets the variable every loop. weird things happened when it was global... )
    uint8_t packet[128] = { 0x80, 0x00, 0x00, 0x00,
                            /*4*/   0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                            /*10*/  0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
                            /*16*/  0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
                            /*22*/  0xc0, 0x6c,
                            /*24*/  0x83, 0x51, 0xf7, 0x8f, 0x0f, 0x00, 0x00, 0x00,
                            /*32*/  0x64, 0x00,
                            /*34*/  0x01, 0x04,
                            /* SSID */
                            /*36*/  0x00, 0x01, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
                            /*71*/  0x01, 0x08, 0x82, 0x84,
                            /*75*/  0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, 0x03, 0x01,
                            /*83*/  0x05, 0x20
                          };


    // Get the SSID length to modify that length value in the beacon
    int wifiLength = wifissid.length();
    // Array spot to start printing SSID
    int ssidSpot = 38;
    // If the SSID is too long, cut it
    if (wifiLength > 30)
    {
      wifiLength = 30;
    }


    // Start printing the SSID in to the packet array at the start spot stated above
    for (int i = 0; i < wifiLength; ++i)
    {
      packet[ssidSpot] = wifissid[i];
      ++ssidSpot;
    }

    // BROADCAST LOOP. Start with 1 space added to SSID and continue until 30 characters long
    for (int j = wifiLength; j < 30; ++j)
    {
      // Kill the "add a space each iteration" looping here if you want



      // Add a space right after the SSID that was printed above
      packet[ssidSpot] = packet[84];
      // Print the now complete SSID length right before the SSID start spot in packet
      packet[37] = j + 1;



      // Move to the next array spot after the last space was added
      ++ssidSpot;

      // Pick a channel to use
      channel = random(1, 12);
      wifi_set_channel(channel);

      // Change the last array value in the "ending" part of the SSID beacon to the channel being used
      packet[83] = channel;
      // Just copying the left off ssid spot for the second part of the packet to start
      int packetPartTwo = ssidSpot;

      // Print the complete second packet portion in to the complete packet array now that the SSID is printed in
      for (int m = 71; m < 84; ++m)
      {
        packet[packetPartTwo] = packet[m];
        ++packetPartTwo;
      }

      // Random MAC address
      packet[10] = packet[16] = random(256);
      packet[11] = packet[17] = random(256);
      packet[12] = packet[18] = random(256);
      packet[13] = packet[19] = random(256);
      packet[14] = packet[20] = random(256);
      packet[15] = packet[21] = random(256);

      // Broadcast the Beacons!
      fullPacket = packetPartTwo; // + 1;
      // Write blue LED LOW (on)
      digitalWrite(LED_BUILTIN, LOW);
      wifi_send_pkt_freedom(packet, fullPacket, 0);
      wifi_send_pkt_freedom(packet, fullPacket, 0);
      wifi_send_pkt_freedom(packet, fullPacket, 0);


      // Write blue LED HIGH (off)
      digitalWrite(LED_BUILTIN, HIGH);

      delay(10);
    }
    delay(10);
  }
  delay(10);
}