Skip to content

Final Project

My Fab Academy project aims to raise awareness of an issue faced by residence living near Lake Houston. I will build a robotic art project that visualize data from a water level sensor.


“Some flood forecasting professionals suspected that the previous water meter was not accurately predicting flood height because of several factors.” source

Sources: Harris County Flood Control District, City of Houston

Lake Houston Water Level Sensor Network

Hurricane Harvey flooded over 200,000 homes in Houston and almost three quarters of them are above the 100 year flood plain. Although Harvey was a rain event unlike any other, dropping 47 inches of rain in four days, many community members were experiencing their home being flooded for the 3rd or 4th time.

Being new to Houston, I surprised to see where the flooding occurred. During the storm parts of the lake were blocked by forming sandbars. My house did not flood since it is only a few hundred yard from the dam. My in-laws’ house flooded several feet deep because the water pooled deeper behind the sand deposits.

Source: Harris County Flood Warning

The three sensors on the lake were separated by sand deposits and provided wildly different information. Without a dense network of sensors community members living in between sensors couldn’t tell which side of the sand bars they were on.

Project Planning

The Sensor

One of the things that made me think of this project is a sensor my wife and I built to monitor the lake level at her parents house that was flooded due to the issue mentioned above.

The Sensor is made with a small solar panel to charge a battery. A ESP8266 wifi board runs off the battery. The board will go into a deep sleep and wake itself up every 10 minutes to take a reading, using an ultrasonic sensor. The reading is then uploaded to a SQL database via php over wifi.

The Art Project

As an interesting way to display the information I am also going to make a electronic installation that displays the sensor value via spinning LED propellers. Each of the robots will be powered by the metal cables that suspend the grid of robots.


Qty Description Price/Bot Link
1 1/4” Acrylic $ 1.00
1 PLA Filament $ 0.10—1kg-spool-(22-lbs)
1 ESP8266 $ 3.34
2 V-groove Bearing $0.40
1 Metal Cable & Turn Buckles $1.80
1 NeoPixels - WS2812B $5.94
1 FR1 Copper Clad $0.75
1 3/4” Sanded Plywood $4.00
1 DC Motor $3.75


ESP8266 Board w/ PWM Neopixel & Motor Connection: Eagle: Schematic | Board

NeoPixel Propeller: Eagle: Schematic | Board

Acrylic Chassis: CorelDRAW | DXF

Acrylic Pulley: CorelDRAW | DXF

3D Printed Bearing Offset w/ Locking Screw: CorelDRAW | STL

3D Printed Bearing Offset: CorelDRAW | STL

Large Sip Ring Pulley: CorelDRAW | STL


Slip Ring Neopixel Propeller

I started prototyping by testing the slip rings with a strip of neopixels I had laying around. I only need to use three of the wires +, -, and PWM signal. The slip ring works great and the lights spin freely without flickering.

I want the propeller to be between 6 and 8 inches. That means the store bought LED strips would only have 5 lights. I have designed my own board that squeezes twelve 5050 Neopixels on the propeller.

It is hard to capture the persistance of vision on video but with the LEDs closer together the circle of light looks great and all the lights merge together as they move.

Powering Bots

I did an initial test with just a bearing, connecting the output wire to the center screw while the power cable touches the v-groove of the bearing. Unfortunately, the bearing grease does not have enough conductivity to allow the electricity to pass through the bearing.

My first attempt at fixing this is to add a slot in the 3D printed bearing spacer for a metal plate to solder a wire to. This worked to conduct the electricty but froze the bearing.

My second attempt is to cut a little piece of copper clad that anchors to the screw holding the bearing in but also touches the outside of the bearing. This solution is perfect because it allows the bearing to spin while conducting electricity.

In this video you can see the robot can actually move up and down the cable without losing power.

For a short time, I thought maybe all the robots could move up and down. I spent about 1 week trying to get the motor mechanism working but I ran into many issues that caused me to put that feature aside.

Power Tower

In Computer Controlled Machining Week I designed what I am calling the Power Tower. It is a 8 foot tall plywood tower cut on the shopbot out of one sheet of 3/4” plywood. Metal cables stretch the height of the tower and are hooked to a 5 volt power supply. One cable is connected to the negative and one cable is hooked o the positive.

Custom ESP8266 Board & Chassis

Possibly my favorite part of this project is the custom ESP8266 circuit board and chassis for the robot. I was able to use the ESP8266 board designed in week 14 to run the robot.

I started by measuring the size and distance between all the components. The components were drawn in CorelDRAW in RED. This I drew a perimeter in black that will be cut from acrylic. I used color mapping to cut the black lines and engrave the red lines to check alignment.

That file was used to design the inner shape. This shape, in black, represents the ESP8266 PCB. It is drawn to avoid all the components.

To use the shape in Eagle I exported the vector shape as a DXF file. It took me about 2 hours to figure out how to import the DXF into Eagle.

Every time I imported the DXF into Eagle nothing would appead. Finally, after trying every combination of options I figured out the DXF has to be exported as an old DXF file type, AutoCAD R2.5

In Eagle, I opened the ESP8266 board from Week 14 and deleted the outline from the Dimension layer. Then, I imported the DXF saved from CorelDRAW by going to File/Import/DXF

I was careless in the step and did not triple check the first time around that I selected the table units of measure in Eagle as the format in CorelDRAW when I exported the DXF.

Once the outline was imported I moved all the components onto the board and routed the traces, like in Week 7

The final step in Eagle once everything was arranged was to export the CAM files. The CAM files were then opened as a new PCB in CopperCAM like explained in Week 5

PCB Problems

So, after cutting/stuffing the board I realized the board was too big to mount to the robot chassis. Turns out I drew the board in MM and imported the outline into Eagle as inches, causing it to be larger than designed. I cut it down on the band saw and it worked. The fix was easy. I made the pcb border the correct size and remade it to correct the problem.



//Creates new record as per request
    //Connect to database
    $servername = "localhost";
    $username = "root";
    $password = "root";
    $dbname = "espdemo";

    // Create connection
    $conn = new mysqli($servername, $username, $password, $dbname);
    // Check connection
    if ($conn->connect_error) {
        die("Database Connection failed: " . $conn->connect_error);

    //Get current date and time
    $d = date("Y-m-d");
    //echo " Date:".$d."<BR>";
    $t = date("H:i:s");

    if(!empty($_GET['status']) && !empty($_GET['station']))
        $status = $_GET['status'];
        $station = $_GET['station'];

        $sql = "INSERT INTO logs (station, status, Date, Time)

        VALUES ('".$station."', '".$status."', '".$d."', '".$t."')";

        if ($conn->query($sql) === TRUE) {
            echo "OK";
        } else {
            echo "Error: " . $sql . "<br>" . $conn->error;


Robot Code

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>

#define PIN            15
#define NUMPIXELS      12

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

int delayval = 500; // delay for half a second

const char* ssid = "Verizon-MiFi8800L-4302";

const char* password = "2d0fff6c";

void setup () {
pinMode(13, OUTPUT);
  Serial.begin(115200); // Start the serial monitor.
  delay(10); // Give it a moment.
#if defined (__AVR_ATtiny85__)
  if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
  // End of trinket special code
  pixels.begin(); // This initializes the NeoPixel library.
 // pinMode(15, OUTPUT); // Set GPIO2 as an OUTPUT.
  //digitalWrite(15, 0); // Start off.
  // Connect to WiFi network:
  Serial.println("Hello Digital Craft");
  Serial.println("Connecting ");
  WiFi.begin(ssid, password);
  // Show ... until connected:
  while (WiFi.status() != WL_CONNECTED) {
  Serial.println("WiFi connected");
  // Print the IP address of the device:

void loop() {
  // Verfiy WiFi is connected:

  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;  // Object of the class HTTPClient.

    http.begin("");  // Request destination.

    int httpCode = http.GET(); // Send the request.

    if (httpCode > 0) { //Check the returning code

      Serial.println("We got a repsonse!");
      String payload = http.getString();   // Get the text from the destination (1 or 0).
      Serial.println(payload); // Print the text.

if (payload.toInt()==1){
   digitalWrite(13, LOW);   // turn the LED on (HIGH is the voltage level)
     // digitalWrite(15, payload.toInt()); // Send the payload value to the pin.
for(int i=0;i<12;i++){
digitalWrite(13, HIGH);
    // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
    pixels.setPixelColor(i, pixels.Color(200,100,50)); // Moderately bright green color.; // This sends the updated pixel color to the hardware.
    delay(delayval); // Delay for a period of time (in milliseconds).
if (payload.toInt()==2){
     // digitalWrite(15, payload.toInt()); // Send the payload value to the pin.
 digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)

for(int i=0;i<12;i++){
    // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
    pixels.setPixelColor(i, pixels.Color(0,150,100)); // Moderately bright green color.; // This sends the updated pixel color to the hardware.
    delay(delayval); // Delay for a period of time (in milliseconds).
      Serial.println("Something baaaaaaad happened!");
    http.end();   //Close connection
  delay(200);    //Send a request every 30 seconds

Water Sensor Code

 * HTTP Client POST Request
 * Copyright (c) 2018,
 * All rights reserved.
 * Connects to WiFi HotSpot. */
long duration;
int distance;

#include <ESP8266WiFi.h>
#include <WiFiClient.h> 
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>

const int trigPin = 2;  //D4
const int echoPin = 0;  //D3

 String content;

/* Set these to your desired credentials. */
const char *ssid = "Verizon-MiFi8800L-4302";  //ENTER YOUR WIFI SETTINGS
const char *password = "2d0fff6c";

//Web/Server address to read/write from 
const char *host = "";   //URL where the PHP is website or IP address of server

//                    Power on setup

void setup() {
  WiFi.mode(WIFI_OFF);        //Prevents reconnection issue (taking too long to connect)
  WiFi.mode(WIFI_STA);        //This line hides the viewing of ESP as wifi hotspot

  WiFi.begin(ssid, password);     //Connect to your WiFi router

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {

pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
pinMode(echoPin, INPUT); // Sets the echoPin as an Input


  //If connection successful show IP address in serial monitor
  Serial.print("Connected to ");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());  //IP address assigned to your ESP

//                    Main Program Loop
void loop() {

  HTTPClient http;    //Declare object of class HTTPClient

  String ADCData, ADCData2, postData;

 // Clears the trigPin
digitalWrite(trigPin, LOW);
// Sets the trigPin on HIGH state for 10 micro seconds
digitalWrite(trigPin, HIGH);
digitalWrite(trigPin, LOW);
// Reads the echoPin, returns the sound wave travel time in microseconds
duration = pulseIn(echoPin, HIGH);
// Calculating the distance
distance= duration*0.034/2;
// Prints the distance on the Serial Monitor
Serial.print("Distance: ");

  int adcvalue=distance;

  //analogRead(A0);  //Read Analog value of LDR
  ADCData = adcvalue;   //String to interger conversion
  ADCData2 = adcvalue-3;

  //Post Data
  postData = "station1=" + ADCData + "&station2=" + ADCData2;

  http.begin("");              //Specify request destination
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");    //Specify content-type header

  int httpCode = http.POST(postData);   //Send the request
  String payload = http.getString();    //Get the response payload

  Serial.println(httpCode);   //Print HTTP return code
  Serial.println(payload);    //Print request response payload

  http.end();  //Close connection

  delay(5000);  //Post Data at every 5 seconds

Work Done by Others

This ESP8266 HTTP Get Tutorial was instrumental in making this project. It shows how to upload data from a weather station but sensor data is sensor data and I was able to use this for the water level sensor.

This Remote Control w/ NodeMCU and Web UI tutorial was how I realized that I could ready a .txt file with the data much easier than trying to parse data from a database.


I chose to use the FAB license for my project. I like how simple it is. I’m not worried about getting credit, but just want any arms of this project to connect back to build a larger nextwork.

(c) Brent Richardson June 1, 2019

This work may be reproduced, modified, distributed, performed, and displayed for any purpose, but must acknowledge “Lake Houston Water Level Visualization”. Copyright is retained and must be preserved. The work is provided as is; no warranty is provided, and users accept all liability.