close-circle
Close
0%
0%

ESP Swiss Knife

This battery powered device is a all-in-one toolbox : WiFi AP, 3D printer remote, clock, serial console, sensors hub, and many more...

Similar projects worth following
close
When I'm working outside my basement, I often need the same tools :
- an arduino + laptop for rapid prototyping
- a wifi access point to easily connect my wireless "devices" (laptop, homebuilt robot, ESP-devices, 3d printer...)
- multimeter / logic analyzer / serial monitor for rapid debugging
- a DSLR intervalometer
...
This is a whole suitcase of tools already...

On top of that, I would really appreciate a device that could :
- tell the time
- tell the weather forecast
- tell the local temperature
- offer some simple calc tools : chronometer, timer...
- interface with my 3d printer
- interface with my robots
- interface with my emoncms server
- interact with my other wireless projects and my DIY home automation/sensors system
- replace my DSLR intervalometer / trigger
- do stuff I couldn't imagine right now

I also really want to add a special socket to it : the "utility socket".

The device is built around a ESP8266 (ESP-12E actually) and a SSD1306 I2C OLED display. It is battery powered (using a 18650 cell from a dead laptop battery). 2 buttons are used to navigate the interface and activate functions.

I also plan to use an "utility socket" to expand capabilities : https://hackaday.io/project/11869/log/38691-the-utility-socket-explained


Right now, working functions are :

  • some nice navigation menus
  • analog and digital clock
  • gets real temperature using a DS1621 I2C sensor
  • connects to known networks
  • checks internet connectivity
  • gets current time via NTP
  • gets weather forecast on timed intervals (using a php script hosted on my home server)
  • posts real temperature and voltage to my emoncms server on timed intervals
  • shows my 3d printer status : on / off / disconnected, running state, hotend temperature
  • gives details about the ESP chip : connectivity, ESSID, IP, free ram, battery voltage...
  • On demand "WiFi smart config" : starts an AP and a webserver during 30s, allowing to add new WiFi credentials using a web interface.
  • OTA firmware updates


Circuit board still needs some work : it is a prototype on a perfboard, and while it is working, I now want to move to a real PCB. Problem is : I can't find how to export a working gerber from Designspark software...

IOTMBPF-16100.jpg

Mainboard - Perfboard design - v1.00

JPEG Image - 249.53 kB - 05/30/2016 at 12:29

eye
Preview
download-circle
Download

IOTMBSL-16100.pdf

Mainboard - Single Layer - v1.00

Adobe Portable Document Format - 14.58 kB - 05/29/2016 at 01:59

eye
Preview
download-circle
Download

IOTMBPF-16100_bottom.pdf

Mainboard - Perfboard bottom - v1.00

Adobe Portable Document Format - 12.94 kB - 05/29/2016 at 01:54

eye
Preview
download-circle
Download

IOTMBPF-16100_top.pdf

Mainboard - Perfboard top - v1.00

Adobe Portable Document Format - 18.03 kB - 05/29/2016 at 01:54

eye
Preview
download-circle
Download

  • 1 × ESP8266 (ESP-12E) WiFi Microcontroler module
  • 1 × SSD1306 OLED Display I2C 128x64px OLED display
  • 1 × DS1621 temperature sensor DS1621 I2C temperature sensor
  • 4 × Pushbutton
  • 1 × LM1117 3.3v Power Management ICs / Linear Voltage Regulators and LDOs

View all 6 components

  • Pictures...

    Arcadia Labs07/05/2016 at 13:56 0 comments

    First, I'm very happy to see my little project on Hackaday frontpage today. Thanks Hackaday !

    Here are some pictures of the device :

    The inside

    Cover and buttons

    Device start-up... Hello Jolly Wrencher ! :)

    Test rig using the utility socket : a potentiometer to simulate analog input, and a IR receiver module to simulate digital input.

    Analog monitor, with min, actual and maximum values, while playing with the potentiometer

    Digital monitor while playing with the IR receiver and a TV remote (sorry for the blur, difficult to capture). Sampling speed could be changed with 10Hz increments. Next feature will be a new running mode, starting only on a pin change, sampling during a fixed time, and then showing on a scrollable / zoomable static graph.

  • Better enclosure, hardware corrections, software improvements

    Arcadia Labs06/24/2016 at 15:26 0 comments

    It's been a few weeks since the last update, but this project is still very active...

    • I wasn't happy with the display positioning, and it looked even worse in the enclosure. So I modified the circuit board, and now the display is "perfectly" centered.
    • I discovered too late I couldn't use GPIO2 as an input. Unfortunately, I routed one of the new pushbuttons to this GPIO, so the button was non-functional. I modified the circuit board and routed it to GPIO16 instead (last free GPIO remaining...).
    • The first enclosure had a few issues so I re-designed / re-printed it. It is now fitting perfectly.
    • I replaced the yellow / blue display with a blue one. I now have 16 pixels more for graphics, charts...

    About the software part :

    • The analog monitor is now fully usable (I used it on the field already). It still needs some custom settings
    • I wrote the digital monitor, and it is also working very well. Now, I'm trying to find a way to capture / show very fast data (kind of slow signal analyzer). Right now I could sample at 1000Hz reliably, but showing it in a 128x64 graph is not easy...
    • I improved the Octoprint 3D printer monitor to better handle connection issues (while roaming)
    • I'm actually trying to write the serial monitor code, but this one is giving me headaches...

    I'll show pictures and maybe a video in the next build log (paint is still drying).

  • Now, an enclosure

    Arcadia Labs06/11/2016 at 01:35 0 comments

    I have to do something about it or something bad will happen... 2 months of software and hardware revisions without magic smoke or bug invasion, ... it won't last forever.

    I could not play with this device without a proper enclosure anymore...

    Printing it's own enclosure.... so untertaining....
    • left and right buttons are not aligned with oled display : really ? ... :) I'll take care about that while knitting PCB version2.... :)
    • forget that, version2 won't be on a perfboard : it will have a real pcb with proper design and charging circuitry...

    That being said, the current v1 prototype is very usefull already : today I used it to check my 3d printer while being outside my basement (printing its own enclosure...), and I could use the analog monitor to determine the perfect LDR for a customer device. One month ago, I may have deployed a laptop and an arduino for the same experiment...

    3D design is not my thing and I'm not sure if it will work, but it is now printing ... (3D design/printing is just a tool, like PHP, solder iron, electronics design and cake cooking)... as usual, I learned a lot today, goal as been achieved :)

  • First prototype on perfboard

    Arcadia Labs05/30/2016 at 12:21 0 comments

    I finally built the first prototype circuit on a perfboard.

    For those who want the method, here's how I do it :

    • first, I draw the layout in Designspark (free PCB software) with 2.54mm spacing everywhere. I try to draw a single layer circuit, with minimal bridges on the top layer.
    • when I'm happy with the design, I print both sides on paper (bottom side mirrored). They are my templates.
    • I glue the top template on my perfbard, and punch every hole
    • I solder the bridges on the top side, and then the components / pin headers
    • when it's done, I solder the bottom layer tracks, following my mirrored template.

    Here is the result, not too bad for something built on a single rainy day (template PDFs are in the project files).

    Now, I'm looking for someone to show me how to export a working gerber from Designspark...

  • ESP12 battery experiment

    Arcadia Labs05/26/2016 at 22:28 0 comments

    One big problem in my designs is the powering circuit : there are so many dedicated chips, battery chemistries, it's very difficult (for me) to design a good one.

    For an experiment, I wanted to know how much time a 2200mAh Li-Ion battery could last with a ESP12, as I plan to use some 18650 cells from dead laptop batteries (I have a lot of these).

    So, I bought a TP4056 Li-Ion protecting circuit, and powered the ESP12 directly from the module output, without a regulator. 3.3v<->4.2v is out of specs, I know that, but I successfully powered a bunch of ESP directly from 2.9v to 5v without a problem. So the experiment was almost risk-less.

    For the test, I wanted the ESP to eat (drink ?) as much power as possible. So I disabled the screen backlight powersave, overclocked the chip to the max, and made the ESP talking with my 3D printer constantly.

    Here is the result. End of the graph is the moment the ESP12 started to show instability (rebooting, not connecting anymore to the AP, etc...)

    So, as a conclusion :

    A ESP12 module + SSD1306 OLED could run happily on a Li-Ion battery + protecting circuit during ~9h... This is 245mAh on average.

    One could notice the ESP started showing instability around 3.4v, when the datasheet says 3.3v, and I tested it successfully at 2.9v. I think the Li-Ion battery current becomes less stable when depleting. Next experiment will add a very Low Drop Out regulator, actually in the mail...

  • Talking with the 3D printer

    Arcadia Labs05/26/2016 at 21:57 0 comments

    This is the last improvement to the device : showing the state of my 3D printer.

    I already connected a Raspberry Pi, a TFT and a relay to the printer, installed Octoprint and writed a custom pygame interface (I love pygame + RPi). It allows me to control it from everywhere.

    I also wanted to check the printer status, without a computer.

    Octoprint software exposes an API, so it was almost just a matter of connecting the ESP to it, and parsing the exposed json.

    Here is the code :

    // OCTOPRINT
    #define octoprint_host "1.2.3.4"
    const int octoprint_httpPort = 5000;
    String octoprint_apikey = "YOUR_OCTOPRINT_API_KEY";
    
    String printerOperational;
    String printerPaused;
    String printerPrinting;
    String printerReady;
    String printerText;
    String printerHotend;
    String printerTarget;
    
    // Function to get and parse the octoprint data
    void getOctoPrint() {
      http.begin("http://" + String(octoprint_host) + ":" + String(octoprint_httpPort) + "/api/printer?apikey=" + String(octoprint_apikey) ); //HTTP
    
      // start connection and send HTTP header
      int httpCode = http.GET();
      // httpCode will be negative on error
      if(httpCode > 0) {
          // HTTP header has been send and Server response header has been handled
          // Serial.println("[OctoPrint] GET... code: " + String(httpCode) );
    
          if(httpCode == 409) {
            payload = http.getString();
            if (String(payload) == "Printer is not operational") {
              printerText = "Printer is not operational";
            }
          }
    
          else if(httpCode == 200) {
            payload = http.getString();
    
            StaticJsonBuffer<1024> jsonBuffer;
            JsonObject& root = jsonBuffer.parseObject(payload.c_str());
            // Test if parsing succeeds.
            if (!root.success()) {
              Serial.println("parseObject() failed");
              return;
            }
            else {
              const char* text;
              text = root["state"]["text"];
              printerText = String(text);
              text = root["state"]["flags"]["operational"];
              printerOperational = String(text);
              text = root["state"]["flags"]["paused"];
              printerPaused = String(text);
              text = root["state"]["flags"]["printing"];
              printerPrinting = String(text);
              text = root["state"]["flags"]["ready"];
              printerReady = String(text);
              text = root["temperature"]["tool0"]["actual"];
              printerHotend = String(text);
              text = root["temperature"]["tool0"]["target"];
              printerTarget = String(text);
    
              return;
            }
          }
          else {
            payload = http.getString();
            Serial.println( "[OctoPrint] HTTP_CODE_" + String(httpCode) + " : " + String(payload) );
          }
      } else {
          Serial.println("[OctoPrint] GET... failed, error: " + String(http.errorToString(httpCode).c_str()) );
      }
      http.end();
    }
    
    // Function to show octoprint state to the display
    void octoprint1(SSD1306 *display, SSD1306UiState* state, int16_t x, int16_t y) {
      page = 41;
      ui.disableIndicator();
      if (printerText == "Printer is not operational") {
        display->setTextAlignment(TEXT_ALIGN_CENTER);
        display->setFont(ArialMT_Plain_16);
        display->drawString(displayWidth / 2 + x , 14 + y, "OFF" );
      }
      else {
        display->setTextAlignment(TEXT_ALIGN_CENTER);
        display->setFont(ArialMT_Plain_16);
        display->drawString(displayWidth / 2 + x , 14 + y, printerText);
        display->setFont(ArialMT_Plain_24);
        display->drawString(displayWidth / 2 + x , 40 + y, printerHotend + "°");
      }
      
    }
    Of course, you will have to check the octoprint status on timed intervals (I set it to 5 seconds).

    It is also a very basic script, using the "state" json only. An improved script, checking the other exposed json, could tell remaining printing time, % completed, file being printed, etc... (example : http://docs.octoprint.org/en/master/api/job.html)

    In all honesty : this is very cool and I love this feature !

  • The Utility Socket explained

    Arcadia Labs05/25/2016 at 21:07 0 comments

    In the details, I mentioned the "utility socket" I plan to add to the device.

    I'm trying to use only I2C modules and components inside the device. So, there are "a lot" of unused GPIOs.

    I plan to use some of these remaining GPIOs in the utility socket :

    • 3.3v
    • GND
    • UART Tx
    • UART Rx
    • I2C SDA
    • I2C SCL
    • ADC
    • 1 digital pin
    • GPIO 0
    • Reset

    It would allow using "slave modules" with the device, while GPIO 0 and Reset will allow to reprogram it.

    For an example, I could plug a I2C Inertial Measurement Unit, or a Serial GPS.

    I plan to also use AtTinys and Atmegas coupled with sensors/actuators, as I2C slave devices. This way, I could use some IO absent on the ESP8266 : SPI, hardware PWM, analog... Each slave device will have its own unique address, so the main device could identify it, and run the appropriate function.

  • Sending data to emonCMS server

    Arcadia Labs05/25/2016 at 19:57 0 comments

    The device sends its voltage and temperature to my emoncms server.

    Emoncms (https://openenergymonitor.org/emon/) is an open-source web-based software for monitoring home power, temperature and sensors. This is a central part of my DIY home automation system.

    Emoncms exposes an API, so reading / posting data to the server is very easy.

    Here is the piece of code I'm using :

    #define emoncms_host "1.2.3.4"
    const int emoncms_httpPort = 80;
    String emoncms_apikey = "YOUR_API_KEY";
    int node = 1;
    
    void postEmoncms() {
      // run only if the ESP is connected to a network
      if ( WiFi.status() == WL_CONNECTED ) {
        WiFiClient client;
        if (!client.connect(emoncms_host, emoncms_httpPort)) {
          Serial.println("Post to emoncms failed");
          return;
        }
        // We now create a URI for the request
        String url = "GET /emoncms/input/post.json?apikey=" + emoncms_apikey + "&node=" + node + "&json={2:" + temperature + ",1:" + vcc + "";
        client.println(url);
        client.print("Host: ");
        client.println(emoncms_host);
        client.println("Connection: close");
        client.println();
        client.stop(); //Stopping client
        Serial.println("Post to emoncms : success");
      }
      else {
        Serial.println("Post to emoncms : not connected to internet");
      }
    }
    Of course, this function shouldn't run constantly in the main loop, but should run on timed intervals. I'm using RBD_Timer lib (https://github.com/alextaujenis/RBD_Timer)

    This function runs in the background, so there won't be any screenshot this time !

  • Getting time...

    Arcadia Labs05/24/2016 at 23:45 0 comments

    With a working menus system, the first thing to add is a clock.

    If possible, I don't want to add a RTC chip, so I have to find a way to get time.

    Fortunately, we have NTP / Network Time Protocol. So I only need a working internet connectivity to set time.

    Here is the piece of code to do just that :

    unsigned int localPort = 2390;      // local port to listen for UDP packets
    
    /* Don't hardwire the IP address or we won't get the benefits of the pool.
     *  Lookup the IP address for the host name instead */
    // IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server
    IPAddress timeServerIP; // time.nist.gov NTP server address
    
    const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
    
    byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
    
    // A UDP instance to let us send and receive packets over UDP
    WiFiUDP udp;
    
    unsigned long sendNTPpacket(IPAddress& address)
    {
      Serial.println("sending NTP packet...");
      // set all bytes in the buffer to 0
      memset(packetBuffer, 0, NTP_PACKET_SIZE);
      // Initialize values needed to form NTP request
      // (see URL above for details on the packets)
      packetBuffer[0] = 0b11100011;   // LI, Version, Mode
      packetBuffer[1] = 0;     // Stratum, or type of clock
      packetBuffer[2] = 6;     // Polling Interval
      packetBuffer[3] = 0xEC;  // Peer Clock Precision
      // 8 bytes of zero for Root Delay & Root Dispersion
      packetBuffer[12]  = 49;
      packetBuffer[13]  = 0x4E;
      packetBuffer[14]  = 49;
      packetBuffer[15]  = 52;
    
      // all NTP fields have been given values, now
      // you can send a packet requesting a timestamp:
      udp.beginPacket(address, 123); //NTP requests are to port 123
      udp.write(packetBuffer, NTP_PACKET_SIZE);
      udp.endPacket();
    }
    
    void get_NTP() {
      Serial.print("Starting UDP\n");
      udp.begin(localPort);
      Serial.print("Local port: ");
      Serial.println(udp.localPort());
      
      //get a random server from the pool
      WiFi.hostByName(ntpServerName, timeServerIP);
    
      sendNTPpacket(timeServerIP); // send an NTP packet to a time server
      // wait to see if a reply is available
      delay(1000);
    
      int cb = udp.parsePacket();
      if (!cb) {
        Serial.print(".");
      }
      else {
        Serial.println("");
        Serial.print("packet received, length=");
        Serial.println(cb);
        // We've received a packet, read the data from it
        udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
    
        //the timestamp starts at byte 40 of the received packet and is four bytes,
        // or two words, long. First, esxtract the two words:
    
        unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
        unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
        // combine the four bytes (two words) into a long integer
        // this is NTP time (seconds since Jan 1 1900):
        unsigned long secsSince1900 = highWord << 16 | lowWord;
        Serial.print("Seconds since 1 1 1900 = " );
        Serial.println(secsSince1900);
    
        // now convert NTP time into everyday time:
        Serial.print("Unix time = ");
        // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
        const unsigned long seventyYears = 2208988800UL;
        // subtract seventy years:
        unsigned long epoch = secsSince1900 - seventyYears + timeZone * SECS_PER_HOUR;
        // print Unix time:
        Serial.println(epoch);
        setTime(epoch); 
      }    
    }
    
    get_NTP()
    Conversion from epoch to human time is easily done with the TimeLib library. For an example :
    String timeNow = String(hour()) + ":" + String(minute()) + ":" + String(second());

    Finally, here is the piece of code I'm using to display the analog clock :

    int screenW = 128;
    int screenH = 64;
    int clockCenterX = screenW/2;
    int clockCenterY = ((screenH-16)/2)+16;   // top yellow part is 16 px height
    int clockRadius = 23;
    
    void analogClockFrame(SSD1306 *display, SSD1306UiState* state, int16_t x, int16_t y) {
      ui.disableIndicator();
    
      // Draw the clock face
      //  display->drawCircle(clockCenterX + x, clockCenterY + y, clockRadius);
      display->drawCircle(clockCenterX + x, clockCenterY + y, 2);
      //
      //hour ticks
      for( int z=0; z < 360;z= z + 30 ){
      //Begin at 0°...
    Read more »

  • Choosing a SSD1306 lib

    Arcadia Labs05/24/2016 at 23:13 0 comments

    I have to find a SSD1306 compatible with ESP8266. I need it to be simple and fast.

    Fortunately, I found https://github.com/squix78/esp8266-oled-ssd1306.

    It is easy to use, easy to hack, with very nice UI functions : it works like a scrolling carousel, and I could use this feature and 2 pushbuttons to navigate in menus.

    It also has a overlay feature, very handy to draw some kind of "status bar".

    Here is an example :

    The lib is so good I forked it and I'm now contributing to the dev branch (https://github.com/CaptainStouf/esp8266-oled-ssd1306/tree/dev-branch-3.0.0). I added the drawLine function and a quick/dirty hack for multilevel menus (I will soon improve that).


View all 10 project logs

Enjoy this project?

Share

Discussions

DrivenMadz wrote 10/20/2017 at 23:40 point

Thank You for the project inspiration! You made me braek out the boxes of "stuff" again :)

  Are you sure? yes | no

Pete wrote 07/05/2016 at 12:50 point

The first link when I search google for "designspark gerber" pulls up a page describing how to generate plots where you can choose gerber as one of the output "devices."  I'll be switching from circuits.io to kicad soon, though, so don't have any practical experience with DesignSpark..  http://lmgtfy.com/?q=designspark+gerber  :)

  Are you sure? yes | no

hubidrei wrote 07/05/2016 at 10:42 point

Tis is really cool. 
But why inventing the wheel for new?

My solution... using the ESP01 as serial over WIFI bridge,  http://hackaday.com/2015/09/18/transparent-esp8266-wifi-to-serial-bridge/

and connectig a buspirate without the FTDI chip, or the BP v1 to it. So you have the mighty buspirate over WIFI. 

  Are you sure? yes | no

Arcadia Labs wrote 07/05/2016 at 13:22 point

Hi, using your solution, you still need a computer at the other end, right ? My goal is to remove the computer from the equation for simpler tasks.

  Are you sure? yes | no

hubidrei wrote 07/05/2016 at 15:25 point

You are right... It needs a computer. But a RASPi  zero is more than enough. 

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates