Skip to main content Link Search Menu Expand Document (external link)

Final Project | WingCam

Table of contents
  1. Final Project | WingCam
    1. Summary
    2. Main Idea and Brainstorming Designs
      1. Initial Idea
      2. Reshaped Idea and Practical Goals
      3. Fixed-Wing Solution [suggested by Junhong]
    3. Design and Implementation
      1. Top-Level Design
      2. 3D Scanning and Printing
      3. Electronics Design and Production
      4. Embedded Programming and Interfacing
      5. System Integration and Packaging
    4. Testing and Evaluation
    5. Conclusions and Future Work
      1. Detailed Plan
      2. Inventory and Schedule

Summary

WingCam = (Flying) Wings + (Remote) Camera!

image

As the name can tell, WingCam is a pair of flying wings combined with a camera that can capture images and videos remotely.

Main Idea and Brainstorming Designs

WingCam originates from my intuition to make (paper) boomerang airplane that can record a short video of the scene along the flying-out-and-in path. Ideally, it would be both light-weighted and cost-effective, such that we can carry in our already-heavy backpack while camping.

Initial Idea

Intrigued by Yusuke’s final project and all his previous super cool Flapping Wing projects, I would like to make a Flapping Wing Camera. Just look at how elegant when the light and big wings are flying in the air.

There are also a bunch of other cool projects that aligns well with this direction.

Reshaped Idea and Practical Goals

However, all these flapping wing projects are quite complex and require a lot of time and effort to build. I would like to make something that is more practical and can be done in a short time. So Junhong helped me to narrow down the scope of the project and come up with a more practical idea, that is a fixed-wing drone equipped with a camera. And Anthony reminded me that we can just use the off-the-shelf ESP32-CAM module as the camera module as well as the control module. This is why I started to use ESP32-CAM in Week 10 making a WiFi WebCam and Week 11 making web interface with ESP32-CAM, which turns out to be exactly what I needed for the final project. That comes to two sides, one is the camera module streaming video to the web interface and the other is the web interface controlling the drone.

Fixed-Wing Solution [suggested by Junhong]

image
Drone kit for motor, gear, battery, and USB charging. ~20 dollars
image
Foam throw airplane as the support. ~15 dollars
image
Drone kit for DC motors and propellers.
image
Drone kit for battery.

Design and Implementation

Top-Level Design

As shown in the summary figure, there are four main parts in the design procedure of WingCam.

  • [1] 3D Scanning and Printing: modelling and making the cap of the body to fit and hold the electronics.
  • [2] Electronics Design and Production: designing and making the PCB to giving power from the battory to the ESP32-CAM module and the DC motors, controlling the DC motors with pins from the ESP32-CAM module, and programming the ESP32-CAM module using an FTDI programmer.
  • [3] Embedded Programming and Interfacing: Programming the ESP32-CAM module to stream video to the web interface and control the DC motors.
  • [4] System Integration and Packaging: Packaging the final project WingCam as a whole product-like instance, which distinguish the final project from all previous weekly makings (emphasizing on one particular skill and do not require product-level integration and packaging).

3D Scanning and Printing

image
WingCam with cap.
image
WingCam without cap.

As first, I tried to scan the original cap (as shown in the below model) and 3D print a new one to better fit the shape of electronics. And later Anthony reminded me that the 3D printed version using PLS will not nessarily be lighter and better fit than the original one, considering the precision and weight of the material. So I decided to use the original cap and just make a similar shape as a demo of 3D modelling and printing skills.

Also, as a part of the integration part, I manually model and reshaped the space inside the head of the foam plane to fit the electronics and the battery. And I also made a small hole on the bottom of the head to let the camera module stream video out.

Electronics Design and Production

This is the most essential part of the WingCam project. The PCB design is shown in the below figure. The ESP32-CAM module is connected to the FTDI programmer through the USB port. The ESP32-CAM module is also connected to the DC motors through the pins. The battery is connected to the PCB through the JST connector. The PCB is connected to the USB port through the USB connector. The PCB is connected to the ESP32-CAM module through the 5V and GND pins. The PCB is connected to the DC motors through the 5V and GND pins. The PCB is connected to the battery through the 5V and GND pins.

image
Schematic design of the driving board for WingCam
image
PCB design of the driving board for WingCam

As noticed here, there are a couple of issues with the initial board, where the power pin of the motors are connected to the battery instead of the switched input power (from either the USB or the battery). Another key issue here is that some of the traces, especially the power traces are way too thin. And because I was using the 1/32 Conservative and 1/64 Flat End Mills, some small traces require me to manually cut traces after milling since the smaller 1/64 Flat End Mill cannot cut through the traces. One of the reason comes form the 3x2 connector that I was hoping to connect to a ToF sensor that I made in Week 4, which turns out to be unnessary.

image

I have to manually fix these two issues and got an ugly but kind of functional board.

image
Driving PCB with small traces before fixing issues
image
Driving PCB with small traces after fixing issues

Embedded Programming and Interfacing

This part comes a lot easier with the experience from Week 10 making a WiFi WebCam and Week 11 making web interface with ESP32-CAM.

Basically, I just write an asynchronous TCP web server to communicate with the web interface and control the DC motors. And it can control the DC motors with two different channels of PWN signals. The resulting voltage will determine the speed of each DC motor.

Additionally, since this is WiFi connection, I need to connect it to a WiFi network at the very beginning. At first, I just manually set the WiFi network name and password in the code. But later I found out that the ESP32-CAM module can connect to multiple WiFi networks and automatically switch to the best one. So I added the WiFiMulti library to the code and it can automatically connect to the best WiFi network.

Eventually the web interface looks like pretty neat and we can control it from any edge devices with a web browser. image

wingcam_esp32.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
// WingCam using ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

#include <WiFiMulti.h> /*Multi WIFI library included*/

WiFiMulti wifiMulti;
/*Per AP connect time. Increase when ESP32 take more time for connection*/
const uint32_t connectTimeoutMs = 10000;

// const char* ssid = "StataCenter";
// const char* ssid = "EECS_Labs";
// const char* password = "";

// Motors A and B
const int motorL = 13;  // IO13 - left wing
const int motorR =  2;  // IO2 - right wing

// Setting PWM properties
const int freq = 30000;
const int pwmL = 0;  // PWM Channel 0 - motorL
const int pwmR = 1;  // PWM Channel 1 - motorR
const int resolution = 8;
int dutyCycle = 200;
const int dutyCycleMax = 255;

String speedL = "0";  // left speed from web slider
String speedR = "0";  // right speed from web slider

const char* left_input_parameter = "lvalue";
const char* right_input_parameter = "rvalue";

AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>DC Motor Speed Control Web Server</title>
  <style>
    html {font-family: Times New Roman; display: inline-block; text-align: center;}
    h2 {font-size: 2.3rem;}
    p {font-size: 2.0rem;}
    body {max-width: 400px; margin:0px auto; padding-bottom: 25px;}
    .slider { -webkit-appearance: none; margin: 14px; width: 360px; height: 25px; background: #4000ff;
      outline: none; -webkit-transition: .2s; transition: opacity .2s;}
    .slider::-webkit-slider-thumb {-webkit-appearance: none; appearance: none; width: 35px; height: 35px; background:#01070a; cursor: pointer;}
    .slider::-moz-range-thumb { width: 35px; height: 35px; background: #01070a; cursor: pointer; } 
  </style>
</head>
<body>
  <h2>WingCam ESP32 Web Controller</h2>
  <p><span id="text_left_slider_value">Left Motor</span></p>
  <p><input type="range" onchange="updateSliderPWM(this)" id="pwmLeftSlider" min="0" max="255" value="%LMOTORVALUE%" step="1" class="slider"></p>

  <p><span id="text_right_slider_value">Right Motor</span></p>
  <p><input type="range" onchange="updateSliderPWM(this)" id="pwmRightSlider" min="0" max="255" value="%RMOTORVALUE%" step="1"
      class="slider"></p>
<script>
function updateSliderPWM(element) {
  var left_slider_value = document.getElementById("pwmLeftSlider").value;
  document.getElementById("text_left_slider_value").innerHTML = left_slider_value;
  console.log(left_slider_value);

  var right_slider_value = document.getElementById("pwmRightSlider").value;
  document.getElementById("text_right_slider_value").innerHTML = right_slider_value;
  console.log(right_slider_value);

  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/slider?lvalue="+ left_slider_value+"&rvalue="+right_slider_value, true);
  xhr.send();
}
</script>
</body>
</html>
)rawliteral";

String processor(const String& var) {
  if (var == "LMOTORVALUE") {
    return speedL;
  }
  if (var == "RMOTORVALUE") {
    return speedR;
  }
  return String();
}

void setup() {
  // sets the pins as outputs:
  // pinMode(motorL, OUTPUT);
  // pinMode(motorR, OUTPUT);
  Serial.begin(115200);
  delay(100);

  // configure LED PWM functionalitites
  Serial.println("Setting up motor control");
  ledcSetup(pwmL, freq, resolution);
  ledcSetup(pwmR, freq, resolution);

  // attach the channel to the GPIO to be controlled
  ledcAttachPin(motorL, pwmL);
  ledcAttachPin(motorR, pwmR);

  ledcWrite(pwmL, speedL.toInt());
  ledcWrite(pwmR, speedR.toInt());

  // setting up WiFi
  // [depricated] connecting to a single ssid and password 
  Serial.println("Setting up WiFi Async webserver");
  // WiFi.begin(ssid, password);
  // while (WiFi.status() != WL_CONNECTED) {
  //   delay(1000);
  //   Serial.println("Connecting...");
  // }
  // Serial.println(WiFi.localIP());

  // connected to multiple known ssids 
  delay(10);
  WiFi.mode(WIFI_STA);    /*ESP32 WIFI initialized as Station*/
  /*Type all known SSID and their passwords*/
  wifiMulti.addAP("EECS_Labs", "");  /*Network 1 we want to connect*/
  wifiMulti.addAP("StataCenter", "");  /*Network 2 we want to connect*/
  wifiMulti.addAP("MIT", "");  /*Network 2 we want to connect*/
  wifiMulti.addAP("YangAPP", "liuyang12");  /*Network 2 we want to connect*/
  // WiFi.scanNetworks will give total networks
  int n = WiFi.scanNetworks();  /*Scan for available network*/
  Serial.println("scan done");  
  if (n == 0) {
      Serial.println("No Available Networks");  /*Prints if no network found*/
  }
  else {
    Serial.print(n);
    Serial.println(" Networks found");  /*Prints if network found*/
    for (int i = 0; i < n; ++i) {
      Serial.print(i + 1);  /*Print the SSID and RSSI of available network*/
      Serial.print(": ");
      Serial.print(WiFi.SSID(i));
      Serial.print(" (");
      Serial.print(WiFi.RSSI(i));
      Serial.print(")");
      Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*");
      delay(10);
    }
  }
 /*Connects to strongest available defined network with SSID and Password available*/
   Serial.println("Connecting to Wifi...");
  if(wifiMulti.run() == WL_CONNECTED) {
    Serial.println("");
    Serial.println("Connected to WIFI Network");
    Serial.println("IP address of Connected Network: ");
    Serial.println(WiFi.localIP());    /*Prints IP address of connected network*/
  }

  server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
    request->send_P(200, "text/html", index_html, processor);
  });

  server.on("/slider", HTTP_GET, [](AsyncWebServerRequest* request) {
    String message;
    if (request->hasParam(left_input_parameter)) {
      speedL = request->getParam(left_input_parameter)->value();
      speedR = request->getParam(right_input_parameter)->value();

      message = "L: " + speedL + ", R: " + speedR;

      ledcWrite(pwmL, speedL.toInt());
      ledcWrite(pwmR, speedR.toInt());
    } else if (request->hasParam(right_input_parameter)) {
      speedL = request->getParam(left_input_parameter)->value();
      speedR = request->getParam(right_input_parameter)->value();

      message = "L: " + speedL + ", R: " + speedR;
            
      ledcWrite(pwmL, speedL.toInt());
      ledcWrite(pwmR, speedR.toInt());
    } else {
      message = "No message sent";
    }
    Serial.println(message);
    request->send(200, "text/plain", "OK");
  });

  server.begin();

  // testing
  Serial.println("Controlling WingCam Motors L / R from WiFi webserver");
}

void loop() {
  if (wifiMulti.run(connectTimeoutMs) == WL_CONNECTED) {  /*if the connection lost it will connect to next network*/
    Serial.print("WiFi connected: ");
    Serial.print(WiFi.SSID());
    Serial.print(" ");
    Serial.println(WiFi.RSSI());
  }
  else {
    Serial.println("WiFi not connected!");  /*if all conditions fail print this*/
  }
  delay(1000);
}

System Integration and Packaging

As a particular skill to be completed for this final project, I decided to make it as compact and neat as possible by making the PCB directly tapping onto the ESP32-CAM and making the connection wires as minimal and invisible as possible.

It turns out to be a really time consuming process, which requires a tons of iterations to make everything perfectly aligned. And this integration iteration could partially explain why my initial driving board is kind of messy and not as neat as I expected. This is because the compactness of the PCB is slightly more important than the neatness of the traces.

This turns out to be a bad practice. And the initial board suffered a lot from manual milling and tweaking individual traces. I should have made the board as neat as possible first and then make it compact. This is a lesson learned.

Another huge drawback is that although the urgly PCB worked, it cannot support high current for the DC motors, which largely limits the functionality of the motors since they are too weak to bring the WingCam the air.

After several iteration, I managed to make a kind of neat and functional PCB. The traces are not as neat as I expected, but it is still much better than the initial one. The board is also compact enough to fit into the WingCam.

image You can bearly see any flying wires even inside of the cap of the WingCam, which is the most proud part of this project.

Testing and Evaluation

The evaluation scope comes into two folds. One is the web interface is able to reliably control two motors and WingCam can (potentially) make turns according to the input in the web interface. The other is the WingCam can fly in the air and take pictures.

The first goal comes out nicely even with the ugly board.

However, it cannot fly in the air. The main reason is that the board does not support enough current for two DC motors. One thing happening quite frequently is that if one motor is pushed to a relative maximum speed, the other motor will stop working. This is because the board cannot provide enough current for both motors. I then made a bunch of adjustments.

image
Changing to a PCB with larger traces.
image
Changing to a pair of stronger motors.
image
Changing to a higher capacity LiPo battery.
image
Using wired USB power.
image
Revised Schematic design of the driving board for WingCam
image
Revised PCB design of the driving board for WingCam

During joint debugging and testing, the most significant issue I was facing is that the short of current from the battery and I even drained out the battery somehow and it cannot charge. It turns out that I might have misused the charging port and the USB charger provided by the drone kit its not even working. Guillaume helped me out to charge the battery properly using a power supply and limit its volatge to 4.2V and current to 0.2A. This is a very important lesson learned.
image

Finally, a neat demo of the WingCam making turns on hand and still cannot fly in the air.

Conclusions and Future Work

I would hope to make WingCam flying in the air and take pictures at the same time. However, the current board cannot support enough current for the motors. I would switch to brushless DC motors, which is more efficient and powerful and brushed counterparts used here, as suggested by Neil in the expo.

Detailed Plan

Instead of making a paper-version boomarang airplane, I would like to implement a FlappingWing version of it using plastic with carbon rod as support for the wing and adding additional electronics for the camera.
image Image from Yusuke’s final project of Flapping Tail Fin Flyer

Process Elements Skills and Weeks
subtractive fabrication processes Laser cut shape-customized wings and supporting parts Plastics using Vinyl Cutting and supports using laser cutting (W1 – Computer-Aided Cutting)
additive fabrication processes 3D print joint, assembly, and connection parts Joints, assembly and connection elements (W3 – 3D scanning)
electronics design and production Design and fabricate the motor, sensor, control boards Motor, sensor, control board (W4-W5 & W8-W9 – PCB fabrication and programming, Input & Output Devices)
embedded microcontroller design, interfacing, and programming Wireless communication and interfacing Wireless connection and programming (W10-W11)
system integration and packaging Integrated solution for final product Combined skill throughout all weeks

Inventory and Schedule

Key components are motor(s), sensor(s), control and communication boards, and the camera module (camera, battery, memory card).

Component Model Lead time Week [TODO]
Motor and gear Gear reduction unit: Precision Micro 10mm Planetary Gear Motor *1, Replacement motor (615 Brushed Coreless Motor*1) 1 month (alternatives?) W10-11 [todo]
ToF Sensor VL53L0X*1 in-stock W8 [done]
Mini camera module Mini Camera Module*1 2-3 days [amazon] W10-11 [todo]
SD card MicroSD TF Card*1 2-3 days [amazon] W11-12 [todo]
Control board micro:bit*1 1 week [sparkfun] W12-13 [todo]
Battery 3.7V 130mAh Lipo Battery*1 1.5 months (alternatives) W11-12 [todo]

ACKNOWLEDGEMENT

This final project is inspired by the philosophy in Week 2, where we cost-effectiveness plays a key role in the design process.