Project Criteria - SmartMilk/SmartMilk GitHub Wiki
Responsiveness of the Application
In it's final state, we feel SmartMilk has achieved the appropriate level of responsiveness in the context of its application; our main priority was making sure the timers to sending Prowl messages actually count down 1 second at a time, which it does even after longer periods of time (we tested its accuracy up to an hour and a half). Unfortunately, the system does offset its timer countdown by 5 seconds each time it sends the Prowl message, but within the context of the application we feel this isn't a fatal flaw; since the condition and environment of each milk sample can't be assessed, the timer countdowns are more of a suggestion based on general health guidelines rather than a clear-cut "Your milk is OK/ inconsumable" situation.
Sampling Rate
The temperature of the milk is sampled at 2Hz. Keeping in mind if the system is portable, it would need to be powered using a battery, so minimizing the processing load (ergo saving on battery life) is a priority. We decided not to keep the sample rate any lower otherwise it would lose it's "real-time" feel.
Program Latency
As stated, the sampling rate of the temperature is 2Hz, so in general there is a 0.5 second delay between an a change in temperature and that change being plotted on the QT graph. There is around a 0.5 second delay between pressing the 'celsius' and 'farenheit' buttons and the plots on the QT graph changing. By far the largest latency occurs when executing the Prowl scripts, which lasts around 5 seconds. This latency is a combined result of the pi having to execute a perl language script via the main system, and then transmitting the Prowl message via Wi-Fi. During this time, SmartMilk.exe will be completely overloaded, which results in no temperature plots and the countdown to sending other messages will be frozen. Since the timing of the system does not need to be exact, we feel this latency although unfortunate, does not significantly degrade the overall functionality of the program. Once we have more programming experience, we would attempt to reduce the latency by converting the prowl script to c++ (thus avoiding the need for system commands) and having this process of sending the message dealt with in a thread so that the timers and plot can still run in realtime.
Bus Protocol
Bus Protocol used in this program is Dallas 1-wire rather than the conventional I2C or SPI protocols. The Dallas 1-wire sends temperature readings directly to the raspberry Pi, and unlike I2C and SPI it does not have a clock data line. The Dallas 1-wire device (i.e the DS18B20 temperature sensor) acts as a permanent master and the RPi the slave. Since we do not need precise resolution (in micro/nano or even millisecond) or control in the timing of the temperature readings, we felt that despite being one of the slowest forms of bus protocol, Dallas was an acceptable choice for the project. Another benefit of using Dallas and the DS18B20 sensor is that the sensor probe has a very long wire (about 50cm long) with no level-shifting required, which allows it to be easily physically configured to take direct readings from the milk carton.
No. of channels
Just 1, for the temperature sensor.
Low level implementation: kernel or userspace?
Userspace is used over Kernel. Although Kernel is much faster, it is far more complex and it can result in accidentally overwriting important parts of the operating system. As this is our first c++ project, usage of userspace is a no brainer.
Data flow from hardware to GUI to output with data formats, latencies, processing and conversions
The SmartMilk data flow is shown in the schematic below.
Milk temperature (Tm) readings are initiailly read from the the DS18B20 probe and stored in a character array (char
) from the tempread.cpp
script. It is then converted into a double and stored in the inVal
variable when the signalData()
function (from tempread.cpp
) is called from plotUpdate()
in the window.cpp
script.
This update function runs every 0.5s, and stores Tm in truput
to account for button functionality (through setCelsius()
and setFarenheit()
). The application then plots truput
alongside the fridge and room temperature thresholds (Tf and Tr resp.) on the GUI every 0.5s as well: this latency can be changed from the timerP->setInterval()
function.
Finally, prowl messages are sent to the users phone, which introduces a 5s latency to the GUI plotting. Button clicking also adds a latency of 0.5s.
Buffering of data
Temperature is retrieved in a buffer sample with 8-bit resolution (approximately +-0.5*C). 8-bit resolution is the typical choice for applications which do not need precise resolution, which we feel is appropriate for the project. Additionally, increasing the resolution will also increase the DS18B20 conversion time: at 12-bit resolution, the conversion time increases from ~90ms to a whopping 750ms, which would significantly decrease the responsiveness of our application.
Postprocessing of Data
We did not have to implement a realtime filter, so strictly speaking no postprocessing was required. However, we did find that in our realtime GUI plot, the value of the temperature could sometimes be inexplicably multiplied by a factor of 10 or 100, so we had to implement a filter to correct these values. This produced no noticeable latency or increase in processor load.
Threads
SmartMilk uses a singular thread (QThread because its a QApplication) which is responsible for reading in temperature from the DS182B0 sensor. This was discovered to be absolutely necessary as prior to its successful implementation, the program was very unresponsive; latency between input reading and GUI plot was 1.5 seconds, and the timers to sending messages were also inaccurate (The countdown decremented at nearly 2 seconds instead of 1). As discussed earlier, we could have made the program more responsive by providing another thread for sending the Prowl messages.
GUI implementation
The GUI is implemented by QT5, which can be viewed on a computer monitor. However, unless you have an interest in monitoring the temperature of your milk in real time, realistically, no GUI is really needed. The GUI on the iPhone is produced directly via the Prowl app. The application does not use the QTimerEvent class, instead the GUI refresh is controlled by a QTimer, which executes every 500ms, thus refresh rate is 2Hz. As stated earlier, any increase in refresh rate would result in unnecessary processor load.
Structure of Classes and Unit Tests
SmartMilk uses 2 classes, Window and Tempread. Window is the "main" class, where the resulting QT plots, timer countdowns and Prowl perl script executions are carried out. Tempread is a QThread class which carries out the temperature readings and leaves pointers for the plotUpdate() and timerCountdown() functions of the Window class to simultaneously access.
In terms of testing, we first built the Tempread class as its own executable c++ program, once we were happy with the results we then constructed the Window class. For several iterations of SmartMilk, the tempread existed as its own independent function inside Window's .cpp file, not inheriting from any classes. While this was happening, we constructed individual elements of the GUI and tested each feature one at a time (first starting with a single plot, then adding in the temperature thresholds then adding temperature scale buttons). We felt it would be unnecessary to construct and test these features in separate .cpp files since most changes were fairly minor. We then implemented timers which were first tested by sending system messages instead of executing Prowl scripts, then adding in the Prowl script execution once that was successful. Upon successfully implementing the countdown timers, although they achieved their functionality, they did not countdown in real time correctly, and the overall responsiveness of the application had became sub-par. This lead us to move the Tempread function to its own QThread class, which drastically improved the responsiveness of the application.
Since it would be impractical otherwise, testing with the temperature sensor was always carried out at room temperature, with our thresholds to initiate countdown temperatures usually being 2 or 3 degrees higher than the room temperature, which we would control by gripping the sensor, making it warmer. The countdown to sending messages were also significantly shorter than they are in the real program (10 - 15 seconds instead of 15 minutes - 2 hours).
An instance of when Unit Testing came in extremely handy was when we had significant long-running bug in our program which could not be identified as a compiler error. When running the SmartMilk program, after 15 minutes the program would stop running due to a "too many open files" issue, and we could not determine whether this was an issue relating to our QT plot or the temperature sensor readings. By enabling only the most essential parts of the program, and then 1 process at a time, we were eventually able to locate the source of our error (our close() function in the TempRead class was not located in the right loop) and fix it.
Team Structure
Our team is comprised of 2 members: Alexander Jamieson and Calum MacLellan. Neither have any particular expertise in these sort of projects, so we split working on the code 50-50, swapping using the pi every few days. Alexander handles coding involving data acquisition from the temperature sensor, implementing threads, and constructing the hardware for the system, while Calum is more focused on Prowl communication with the iPhone. We both have equal involvement in the GUI development.
Time division between hardware, software and debugging
The hardware aspect (materials, circuit, PCB) of the project was fairly basic and so that was our main focus at the beginning of the project. Once that was dealt with, the remainder of the project has been a continuous cycle of software-debugging-software etc. etc. In general, we would both be responsible for debugging our own code and not each others'. However, in our bi-weekly meetings we often came together to co-write and debug code for our GUI.
Version Control Software
Unsurprisingly, we used Git as our source of version control. Once we successfully managed to implement a working GUI, we named our first .exe file SmartMilkv1, and each successive version that implemented a new working feature was named SmartMilkv2, SmartMilkv3 etc, with the final version to be named simply SmartMilkApp. In hindsight, this was a poor choice of version numbering since each iteration was not actually anywhere near completion of the final product. In future projects we would name our updates as [Project]v0.1.1. Major updates would be confined to the 0.[] and minor confined to 0.0.[], and the final version just being [Project]v1.
Publication of our project
Aside from Github, we have our own twitter: click here! On our twitter we will provide a link to our github and the youtube video once it comes out, and tweet once the project is complete. Our measurement of success won't be something superficial like "number of retweets" or "youtube hits", but rather someone telling us they used the project and found it beneficial. Of course, being featured on a programming blog like hackaday would also be an indicator that we did well.
Self-Evaluation of the application
As our first embedded programming project, we were very proud of being able to create a system that achieved its key functionality, which was to alert the user via their Smartphone that their milk had been left out of the fridge. We realize that while using this project may not actually save users a whole lot of money (cost of buying materials for the system including a RPi would be about £30, vs. a new carton of milk for £2), we believe in an important non-financial value in the frustration of having lost a perfectly good carton of milk and the inconvenience of having to buy another. While we do regret not being able to enlarge the scope of our project (for instance, we had thoughts about implementing a nitrogen gas delivery system which would be able to keep the milk fresh, which was not possible due to logistical and financial reasons) , we feel that because our project can be so easily replicated, this is a huge benefit; anyone who is looking to get into embedded programming for practical purposes can use our project as a good starting point.
Between our initial vision and final product, most of our features were preserved. The only unfortunate significant loss was the fact we had to abandon creating the custom iOS app, instead relying on Prowl for mobile communication. This was necessary because neither of the project members had any experience with the Swift language, and we decided to prioritize on creating a functional program in time. In the future we would like to create our own free iOS (even android) app custom tailored to SmartMilk.