My final project is done! Overall, I am very proud of what I have created. The original intent of this project was to demonstrate real-time graphing of data being sent from remote sensors to a central server. I wanted to project to be scalable so that multiple sources of information could feed the same central server, and the user could choose what information they wanted to display. The system I have in place allows users to select graphs based off individual sensors, or grouped "regions" of sensors and view data as it becomes available in real-time.
To me, this is a clear situation where a RESTful interface would be used for data collection. Since there were multiple participants, no persistent connection needed to be maintained (each data reading is a single POST), REST made the most sense. I broke the overall system up into 2 parts; the "central" instance which I developed using Python / Flask and some RESTful helper libraries, and the "sender" service which ended up being written in C. I also wrote a simulator that produces similar data to the sender service in Python using the Requests library, both to test my RESTful web service as well as to simulate multiple sensor inputs without needing to purchase additional hardware.
The central instance was broken up into 3 parts; the RESTful endpoints that accept new data and display requested data as JSON objects, the "Admin" page that lets administrative users control what senders are allowed to send readings, and the Graphing interface that lets non-privileged users select data to be graphed and see the resulting graphs. In development the admin page does not prompt for a username or password. In a production system I would configure the web server to require logins for that URL path to take the responsibility of authenticating users outside of the web application's control. If I wanted users to "own" groups of sensor nodes or to set up a multi-tenant instance (SaaS), then I would set up an authentication stack inside the code it's self. All of this design was largely complete before I selected the sensor to monitor the gas I wanted, because in essence, all sensors that measure gas as a concentration in a volume of air work on the same principal. Once I had the basic functionality working, I dug into Twitter Bootstrap a bit to make the interface look nicer.
After originally looking at the prices for CO2 sensors I decided to look for a (much cheaper) CO sensor instead. The sensor I ended up settling on was the Hanwei MQ-2, which came in a larger kit of sensors by Sunfounder Labs. I received band information when initially researching this sensor, and it turns out the MQ-2 was not a Carbon Monoxide detector as I had originally thought. Instead, it is a combustable gas detector, which makes it easier to demonstrate (all you need is a lighter) but doesn't quite measure what I was hoping to observe. The general principal is the same, however, so with the right sensor the only piece of the code that would need to be rewritten would be the sender agent on the Raspberry Pi. A wiring diagram of connecting the MQ-2 to the Raspberry Pi through the provided ADC (analog-to-digital) converter is in this post, as well as in the code on GitHub. This was difficult to figure out, given I had limited previous experience with translating wiring diagrams into completed projects.
Once I did have it wired, however, I was happy that the demo code provided by Sunfounder worked as expected. This was soon followed by frustration when translating that code into Python to use the GPIO library and Requests (my original intent) did not work the same as it did in C. I suspect that either some behavior the GPIO library tries to protect against (and the WiringPi C library does not) was preventing results, or Python was sampling the data too slowly to get numbers. After some experimenting running the C example in a subprocess and using Python to send the result, I ended up learning a bit of C (namely, libcurl and libconfig) to expand the sample code into a complete sender service.
As it stands right now, the system is working as intended. After setting up the Python / MongoDB environment for the central instance, you can start one or more simulator instances and/or wired MQ-2 sensors all pointing at the same central instance and graph any part of that. To accept new sensors into the system and assign them names and region groupings, click the button for the Admin Page. Once graphing, to change the values on the MQ-2 sensor graph, simply hold a lit lighter up to the sensor for a few seconds and observe the values on the graph increase. The values of the simulated sensor nodes will rise and fall between 80-120 (ppm/v) randomly on their own. Those values are based of the data observed when testing the actual MQ-2 sensor without zeroing it out first. In production, I would let the sensor run for a few minutes and determine a "zero" before sending data. This makes for much less interesting graphs and I decided to leave it uncalibrated during the demo for effect.
There are still a few things I would do differently or add on given more time. Waiting to select the sensor toward the end worked out, but resulted in measurement of an undesired metric. With more money and time I would have selected better components and gotten more comfortable with them before beginning the project, perhaps tried a few out before settling one one. There are also some new features that would really improve the project overall. The next step in functionality I see for this project would be assigning GPS coordinates to the sensor nodes in the central instance and plotting them on a map. I had considered doing this for the project, but backed away when I realized how much work was involved in learning mapping as well as learning graphing libraries, and graphing was my primary focus. The next version of this project could include color-coded pins on a map, the color indicating a pre-defined threshold of "air quality" based off what concentration of that particular gas is safe for humans. Plotting these nodes, mixed with the concept of regions, would let me close off geometric shapes on the map overlay and color-code those as well. This would give the user a point of reference when correlating graph data to real world locations. I had also briefly considered setting up email alerts when levels crossed certain thresholds for configured period of times; i.e. "bad air reports". I thought this would be too hard to demonstrate in the class, but would make a good future enhancement.