Final Project | WingCam
Table of contents
Summary
WingCam = (Flying) Wings + (Remote) Camera!
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.
FlappingWing by Yusuke |
Mini camera module |
There are also a bunch of other cool projects that aligns well with this direction.
- Yusuke’s final project – A flapping tail fin flyer
- Dian Song’s project – A flapping butterfly
- Alex’s final project – Flapping wings
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]
Drone kit for motor, gear, battery, and USB charging. ~20 dollars |
Foam throw airplane as the support. ~15 dollars |
Drone kit for DC motors and propellers. |
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
WingCam with cap. |
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.
Schematic design of the driving board for WingCam |
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.
I have to manually fix these two issues and got an ugly but kind of functional board.
Driving PCB with small traces before fixing issues |
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.
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.
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.
Changing to a PCB with larger traces. |
Changing to a pair of stronger motors. |
Changing to a higher capacity LiPo battery. |
Using wired USB power. |
Revised Schematic design of the driving board for WingCam |
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.
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 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.