Capacitive Volume Sensing
Open Smart Switch
I haven't forgotten about this site! Today I cleared out some outdated/irrelevant photos from my Projects page and added some new ones, accompanied by details and videos. Here are the newly-added projects:
Capacitive Volume Sensing
Open Smart Switch
Over the weekend, my friend Mark Omo (markomo.me) and I participated in the Hack Arizona 2016 (hackarizona.org) hackathon, creating a drone control platform we called Drone Flight Area Control (GitHub). Much to our excitement, we won not only first place overall, but best in software and third place in the Raytheon drone category. So, here's the writeup we did for the competition (also on Devpost), plus some additional notes.
Personally, I wrote the frontend, as has often been the case in projects I've worked on. It built on techniques I had used in creating an early demo for a search and rescue application, using the Google Maps API to bring an interactive and detailed map into the interface with our own data on top of it. This was actually the first functional non-standalone webapp I've made, so seeing it function as expected was very exciting for me. It was also my first time using jQuery, which I had been reluctant to learn for various reasons. However, seeing as how it is one of the leading web frameworks, learning what it is and how it works was definitely worth the time, and it definitely saved me time. The big challenge we hit was efficiently exchanging data between the frontend and backend. Originally, the plan was to use WebSockets, and that plan still makes the most sense in my mind. However, implementation was more nuanced than just "using WebSockets". Ultimately, I didn't contribute to the backend, but even if it had used WebSockets the client-side implementation was a bit over my head that late at night. The backend actually does use Python's WebSockets library for the server, but in reality all the defining aspects of WebSockets were ignored. Rather, it just responded to HTTP GET requests with JSON as would a normal HTTP server. The difficulty that arose from this, however, was getting the headers right. Generating the HTTP headers is achieved through the use of a single function that generates the appropriate headers for a desired HTTP response code (in our case, 200). However, since we were sending out JSON, we needed to add in Cross-Origin Resource Sharing data for the client browser to allow itself to interpret it. The solution ended up not being that difficult (just add the proper headers to the _gen_headers() library), but since we were both busy with other things at the time we didn't get a chance to look into how it all worked in our situation and get it implemented. Ultimately, we presented the interface using a plugin for Chrome that just disabled CORS security as a quick-fix. Despite those challenges, we ended up producing a highly functional web-based drone control platform that is essentially ready to run with real drones (we tested using SITL simulated drones), save for a few minor details on the drone end to deal with the realities of reality. We're very happy with how our software came out, and very pleased that we won the competition. So, here's the rest of the writeup:
Unmanned Aerial Vehicles are the future! They are one of the world's fastest growing industries, with enterprises around the world exploring their widespread deployment. The difficulty, however, lies in efficiently managing them without unnecessary loitering and wasted time.
What It Does
Drone Flight Area Control, or DFAC for short, maintains a list of drones and tasks, and sends drones out to those tasks in the most efficient order. The drones and tasks can be monitored and managed through the DFAC web interface, which allows the user to visualize the layout of the tasks, keep track of the drones and their current tasks, and add, modify, and remove tasks. The interactions with the DFAC backend, however, are limitless. Data is exchanged via HTTP GET and JSON, so there are many innovative possibilities for managing the drones and tasks. This also means DFAC can be used as a scheduling and control backend to existing applications.
Who Did What
The DFAC backend and interface was developed by Mark Omo, and the DFAC web interface was developed by James Rowley.
How I Built It
Mark: The DFAC backend is a multi-tiered, multi-threaded, socket driven system, which allows it to be infinitely extensible and scalable to hundreds of task regions and hundreds of thousands of drones. The backend is entirely python based, leveraging existing resources, and enabling quick additions and modifications. Adding new types or classes of UAVs is very easy, and seamless integration into the current tasking system allows them to execute tasks tailored to their strengths.
Challenges I Ran Into
Mark: Ensuring the application was thread safe and did not require cross thread variables was a challenge, along with ensuring the whole system meshed together and could meet the load demands of many vehicles at the same time.
James: I have never written a real-time webapp before, so getting the data to update and synchronize with the server in a timely manner was a challenge. Additionally, getting the CSS to cooperate and not send entire sections off the page while still being scalable and extensible was tough at times.
Accomplishments That I'm Proud Of
Mark: Getting the system working in the short time we were allotted was the biggest achievement in my eyes, along with keeping the server thread safe, and eliminating the possibility of the threads tripping on one another.
James: I'm definitely proud of getting the whole thing to run smoothly and interact with the DFAC backend, while being able to handle hundreds of tasks and drones scalably. Also I'm pretty happy with the overall look and feel, as well as the drone and marker graphics.
What I Learned
Mark: I learned the importance of writing agreeable system interface documents. James and I went back and forth several times on the structure and terminology used in the application interface, using only text chat to boot, so getting the interface right was a learning experience.
James: This was my first time working with jQuery, which I was a bit reluctant to pick up, but I've seen the light and learned quite a bit about what seems to be one of the most prominent web frameworks. I also picked up a bit of python from modifying the dummy server, so that's nice.
What's Next for Drone Flight Area Control
In the future, we want to build in specific applications to assist with task creation, including automatic response to traffic congestion, traffic accidents, and emergencies, automatic industrial and commercial inspection (i.e. inspecting a skyscraper or pipeline), and features targeted for search and rescue applications. In terms of specific features, we want to add result collection and presentation for tasks, a log of previous tasks and drone actions, alerts for completed tasks and drone problems, and task prioritization. For the frontend, we want to add paths/trails for the drone as well as heading of the drone.
Note: Since we submitted it that last line has changed, in my mind: heading of the drone is way too tough to pull off with the Google Maps API and an SVG icon, and I definitely want to add tags underneath the markers and drones with their numbers, plus the number of the drone that is handling a task, or the number of the task a drone is handling.
First, I'll give some context - I recently finished working on a project in which some students motorized an old R2-D2 themed Pepsi cooler thing, and I set up all the electronics for, including a repurposed flight controller that mixed the RC signals from the cheapo transmitter/receiver set.
Initially, the robot had no onboard computer or brain of any sort. The motors were directly controlled by the radio receiver, with each side controlled by the vertical axis of each stick. This made it difficult to drive, for a variety of reasons. From the beginning, I planned to have it driven arcade style - only using one stick, with vertical being forward/backward and horizontal being steering. The task, therefore, was to take two RC signals in arcade configuration as input, do some math on them to make them into tank drive, or a value for each side of the drivetrain, and output them as two RC signals. I had hoped to simply use an old ArduPilot flight controller loaded with the APM:Rover firmware, but unfortunately all of our ArduPilots were nowhere to be found. The easy solution from that point would have been to use an Arduino, but I wanted something a bit more compact, and with PWM headers. A programmable flight controller fits that bill fairly well, and even though I couldn’t find an ArduPilot, I had a different kind of flight controller sitting on my desk.
The “HobbyKing Multi-Rotor Control Board V3.0”, based on something from KK MultiCopter. The documentation was about as informative as the name, and the firmware was closed source, but what I could tell was that it had an AVR microcontroller and an ISP header. That was good enough for some simple RC input and output, with the added benefit that I have been working with AVRs for a while now, using Arduino. While Arduino was an option for this project, I have written board definitions in the past and just didn’t feel the urge to do it again for a one-off project. Instead, I took this as an opportunity to learn the Atmel Studio workflow, something I had been meaning to do for a while.
I was hit with some major differences from Arduino off the bat - I had done one thing without Arduino before, so I knew that many of the usual functions would be gone, but I was surprised to find that super simple stuff such as a delay function and a system time were missing. Delay was as easy as including a header and specifying the system speed, but a system clock, which was necessary for RC input, was not so quick. To have that sort of clock, one of the timer/counters had to be used as just that - a timer, counting up forever. The timer ticks up by 1 every time it receives a pulse from the clock source (hence, the name “counter”), to a maximum value of 256, as it is an 8-bit timer. The system clock on this board was 8 MHz, or 8 ticks per microsecond. Since microseconds were my desired unit, I used the prescaler functionality of the timer to make it tick up once every 8 clock cycles. However, the 8-bit nature of the timer meant that it could only keep track of 256 microsecond intervals. The RC signals it needed to decode were between about 1000 and 2000 microseconds long, so this was an issue. The solution is simple, however: The timer can be set up to run an interrupt every time it overflows, or goes back to zero. In this interrupt, a variable is set to increment by 1. So, the number of microseconds since the timer was turned on is the current value of the timer plus the number of overflows times 256. Seems simple enough, but I couldn’t get it working. After a few hours of trying to implement my own system timer, I ended up just using an excerpt from the Wiring library, which is part of Arduino.
Having the groundwork out of the way, the next challenge was to actually input and output those RC signals. This was a bit daunting, so I referenced the code of MultiWii as a starting point, having previously read through most of it. I started with input, as it’s a good thing to have when generating output. The general concept of reading the RC signals is that the time between pulses is recorded, and if it’s a sane value (between the expected minimum and maximum), it’s accepted as the input value. This is accomplished by setting up the AVR to fire an interrupt every time the input pins change, and then running a short burst of code for that interrupt (the “Interrupt Service Routine”, or ISR) which tracks the change in time for each pin. In order to prevent the interrupt running for pins I don’t care about and clogging up the CPU, only the Pin Change Interrupt (PCINT) for the bank of pins the RC input is on is enabled, and a mask (PCMSK) is applied to that interrupt so only the RC input pins trigger it. AVR pins are divided into ports, which are sets of bytes, so all the pins are in groups of 8, and have to be accessed in those groups. Fortunately, all the input pins were on one port and one PCINT bank, so things were a bit easier than they could have been.
When the ISR for the pin change runs, interrupts are disabled (to prevent interrupting the time-sensitive parts), the current time is recorded, the change in the RC pins is recorded (by applying a logical XOR to the current port value and the last recorded port value), and interrupts are re-enabled. Then, a for loop iterates through an index of the RC input pins, which contains their position in the port (a bitmask with the pin in question as a 1). For each pin, the previously found change is compared to the pin position with a logical AND, and if the result is boolean true, then the change in time is found for that pin by subtracting the last recorded time from the current time. If that value is within the expected limits, that is, between 900 and 2100 microseconds, it is recorded as the RC input value for that pin/channel. Otherwise, the value is discarded. Then, the current time is recorded as the new last time for that channel, and the for loop continues. After that’s done, the current port value is recorded as the new last port value, interrupt concludes, and we have our RC inputs.
The major disadvantage of these boards versus Arduino is that they have no USB interface, and the serial pins are hooked up to some trimpots. While programming is still easy enough, there isn’t any easy way to extract real-time data from the boards. So, whether or not my code was working was basically a guess, and I didn’t want to tackle output until I knew input was working. The only method of communication I had readily available on the board was the status LED. So, I set up the main loop to turn on the LED if the channel 0 input was above 1500 microseconds (the expected midpoint for RC PWM). Unsurprisingly, it didn’t work the first time. Since the program was and still is very small, I simply changed the conditions for turning on the LED and recompiled until it came on, then changed them a few more times until I knew what values my code was returning and how they corresponded to the actual output of the receiver. As it turned out, my code was actually fine the first time, I had just mistakenly set the maximum input value to 1100 instead of 2100, causing the code to throw out everything (my controller strangely didn’t have the expected full range, so it never got to 1100). Having solved input, it was time to tackle output.
The RC output is based around a single 16-bit hardware timer, which drives both the output pins. This was a bit more foreign and challenging to me than the input code, as the timers are controlled by registers and code that has no clear meaning without thoroughly studying the datasheet. The concept is fairly simple: have a timer that resets every 20 milliseconds, and set the output pin high if the timer is currently below the desired output value (between 1 and 2 milliseconds), otherwise set it low. Actually getting the timer to cooperate is the challenging part. I read the datasheet’s chapter on Timer1 (the 16-bit one) many times over, and studied MultiWii’s output code before attempting anything. A quick note: how I implemented RC output is not necessarily the best way to do it. I don’t know what the maximum refresh rate on my motor controllers or most motor controllers is, so I settled for 50Hz and aimed to replicate the output of the radio receiver, at the cost of some precision.
The timer is configured using 3 registers TCCR1A, B, and C (Timer/Counter Control Register 1 A, B, and C). These define how fast the timer runs, what its max value is, and how the output pins are controlled. The registers are set using atomic logical ORs and ANDs, although they could just be set to a value since no other part of the program is or should be using them. I set the prescaler value to 64, meaning the timer ticked up once every 8 microseconds, and meaning that my output had a precision of 8 microseconds, which is about equivalent to 7 bits of precision for the range I was outputting in. Probably not good enough for flight control, but fine for a simple ground vehicle. I then set the Waveform Generation Mode to mode 3, 10-bit Phase Correct PWM. “Phase Correct” is important here - in short terms, is means the output waveforms are back to back, which effectively halves the frequency and doubles precision. This means I actually get about 8 bits of precision, which is what I wanted and expected. Lastly, I set both the output compare pins to mode 2 (COM1n1:0=2) so that they output the desired waveform. With the timers properly set up, the only thing left to do is set the Output Compare registers, which tell the timer when to turn the output pins on or off. The difficulty there is that my output value is in microseconds, but the timer is running at 8 microsecond intervals, and back to back. The solution seemed clear: divide my output value by four. My logic might be flawed, but what matters is that it didn’t work. The actual output was way off from what I expected, and in an odd way. I didn’t write down what the initial results were, and I’m writing this from a different computer so I don’t know numerically what exactly I did. Basically, I divided my output value by a constant, and subtracted another constant from that. Similar to testing with the LED earlier on, I just changed constants, recompiled, and repeated until I got the output I wanted. Not necessarily elegant, but effective and reliable.
I ran into a major roadblock when writing the output code: for a while, I was compiling and running code (out of desperation, at one point I just compiled the MultiWii code), and I was getting basically no output. The first few times, my waveform was sort of visible at the microvolt level and with completely wrong timing, but the correct response to the input. I spent an hour or so getting no results, then left it for a while. Then when I came back for round two, I just got electrical noise. No changes to the code could get any sort of output on those pins. I realized then that I had forgotten to set the output pins as outputs, by setting them high in the Data Direction Register. So I set them as outputs, and still got electrical noise. I put a simple blink program on it and confirmed that they were outputs, could be set high, and that my oscilloscope was working. I kept playing with it for another hour with no changes, which was very confusing, so I called it a day at that point. When I came in the next morning and turned everything on, the waveform was there on the oscilloscope as expected. I’m really not sure where it all went wrong, but it was probably tunnel vision of some sort. At that point I started changing my coefficient and offset to get the waveform I wanted, but that’s just a great example of how sometimes just taking a break and restarting can solve problems.
With RC input and output written and reliable, the hard part was over. From there I basically just had 2 inputs, 2 outputs, and some simple math in between. I made channel 1 my acceleration and channel 2 my steering, and subtracted 1500 from the steering to give it a range of plus or minus 500. Then I just added steering to acceleration on one channel, and subtracted steering from acceleration on the other. Compiled, tested, had the steering reversed, reversed the steering, compiled again, and had it working the way I wanted. At that point, I saved everything, cleaned out broken code and old bits from my code, figured out what I needed to do to comply with the LGPL that the Wiring library is under, and called it done. I will be posting the code in question somewhere soon.
I went into this project knowing that it would be a challenge and knowing that there were easier ways, and it was every bit as fun as I expected. Staring at code for hours wondering why it’s not working and what could possibly be wrong is never fun, but getting to see the results when something finally works keeps me going, and the final functional product is always exciting to see, which definitely makes the whole process worth it. From a board that did nothing, to a Blink program, to an exploration of interrupts and timers, to a fully functional board that converts arcade drive to tank drive, the process was a great learning experience and goes to show that something seemingly simple like inputting and outputting a few RC signals can actually be a complex problem. Finding documentation and tutorials for my particular application was tough, so hopefully this article and the accompanying code can be of assistance to anyone trying to do something similar. Thanks for reading!
Update Jan. 14, 2016:
Here is the source code that this article discusses: