My intention of this project was to make refuse collection more efficient by monitoring bin usage and using this data, combined with collection records, to better predict when the bin needs collecting before it actually does. This will reduce the inefficiency of partly empty bins being collected without the risk of a bin overflowing.
The idea of monitoring bin usage is not a new one. The concept is often championed as a demonstrable use of IoT technology in industry. Several companies already offer white label solutions that use a variety of sensors to detect how full a bin is. These are great, but they either monitor the height of rubbish in a bin, so are confused when it is not compacted, or weight where they are confused by different types of rubbish. They are also reactive and only know when a bin is ready to be collected and not when it will be the optimum time.
My solution is instead to monitor the number of times a bin was opened and use machine learning to predict when the bin was going to be ready to be collected.
The Concept - How The Idea EvolvedAnother appropriate title for this section would be “What went wrong”. I could skip over this but I pretend what I delivered was what was intended, but I think it is useful for others to learn from my experience so they can avoid the same issues.
The original concept is still good, but it needed lots and lots of data that I could collect to train the AI model. With hindsight I would never have been able to collect this information in the time available. My confusion came because I had years of collection data for the test location, but this was just what was collected and I had no record of the bin being opened.
Additionally the LTE-M network at the test sign was unreliable. The UK LET-M and NB-IoT networks are still being rolled out and appear not to work when the service status for a location is reported as “congested”. Most of the project testing and the demonstration video was done in a neighbouring city where I had a reliable connection. This is changing, but if you are starting an LTE-M project in 2024 you may need to cope with a highly unreliable network for now.
Even though the original intention of the project can not be completed, the hardware part of this project has been completed, and working very well in locations where the mobile network is functional. This is what I have published here. Please feel free to build on this work and do good things.
The DesignWhat you see here is the tipper tracker hardware. It mounts on the list of a bin and tracks when it is opened and collected.
It does this monitoring using two tilt switches connected to the AVR-IoT Celular mini. One switch to identify when the lid points up and is being filled. One to identify when the lid points down and is being emptied. The device is designed to be bolted in the inside of the bin and battery powered.
It is intended to function for several years on a single charge and spends most of its life in standby to achieve this.
Originally I was intending to use an accelerometer to detect the position of the bin lid. Although accelerometer modules are low power (around 14 μA) and have an interrupt output to wake the main controller, the tilt switches do all we need with far less complexity.
The wiring is very simple and a diagram of how to connect the tile switches is included on this listing.
The FirmwareThe code is included to download from this page so you don’t need to copy and paste this. This is an in depth description on what is going in on the code and explanation of some design decisions. Before trying to upload it to the device you are best following the setup instructions from https://www.hackster.io/keenan-johnson/avr-iot-cellular-mini-107a63 and checking if the device can connect to the cellular network.
First we need to include all the libraries that will be needed by the project.
#include <Arduino.h>
#include <http_client.h>
#include <led_ctrl.h>
#include <log.h>
#include <low_power.h>
#include <lte.h>
#include <mcp9808.h>
#include <veml3328.h>
Next we have some configuration to complete. First we set the endpoint where the tip tracker will send the data to. This is a URL of a web server that can receive an HTTP push request.
#define DOMAIN "www.example.com/tippertracker/APIendpoint"
Next are the pins the sensors/switches are connected to. If you have used the recommended wiring from the diagram then you will not need to change this.
// Pin for the test button - D11
#define SW0 PIN_PD2
// Pin for the tip up switch - D10
#define SW1 PIN_PB5
// Pin for the tip down switch - D9
#define SW2 PIN_PD0
Finally there is a timeout in seconds. The longer the better as the more time we spend asleep the longer the battery will last. If using an LTE-M network 44 minutes is the most power efficient setting. If using NB-IoT this will be 3 hours. This is because the network knows the device is active but asleep so will not require a complete power hungary reauthentication when it hears from it again. Sadly this is not supported in the UK where this has been tested, so I use 1 day by default to help with debugging.
// Maximum sleep time for keep alive
#define SLEEP_TIMEOUT 86400
Next we have a variable that stores the reason the controller has been woken up. We need this as the reason is deduced in the ISR but can not be used until the device has connected to the network.
// Globals=
volatile int sleepRestoreReason = 0;
Now we have some simple function that we will use later on. First the code to make the device go to sleep when we have finished everything we need to do.
// Sleep the device
int sleepDevice(uint32_t inDuration) {
// Turn off all the ancillary hardware
Veml3328.shutdown();
Mcp9808.shutdown();
// Reset the verable to identify why we woke up
sleepRestoreReason = 0;
// Put the core hardware to sleep
LowPower.powerDown(inDuration);
// If we are here we have just woken up and
// sleepRestoreReason will have been set if it was a switch
// Return the reason we woke up
return sleepRestoreReason;
}
When we start to call this we first close down all the hardware. We need to close down the devices that we are not using as they may be on by default. When we call LowPower.powerDown() the device will go to sleep and will not continue until an input is sensed, or a given duration has timed out. Calling LowPower.powerSave() would do the same thing, and leave the cellular module connected to the network, but as previously mentioned this is not available in the UK where it is being tested. When we come back from sleep the global variable sleepRestoreReason will magically be set. It is not really magic, but that is below.
Next we have the function to send the data to the server.
// Send an update to the server
int sendTipUpdate(int inMessage) {
if (!HttpClient.configure(DOMAIN, 80, false)) {
Log.info(F("Failed to configure http client\r\n"));
}
Log.info(F("Configured to HTTP"));
String postData = "{\"message\": \"" + String(inMessage) + "\"}";
HttpResponse response = HttpClient.post("/update/", postData.c_str());
Log.infof(F("POST - HTTP status code: %u, data size: %u\r\n"),
response.status_code,
response.data_size);
// Add some extra bytes for termination
String body = HttpClient.readBody(response.data_size + 16);
if (body != "") {
Log.infof(F("Body: %s\r\n"), body.c_str());
}
return true;
}
This uses the HTTP client to send the data. The reason for using this is it is low bandwidth and allows a standard web server to receive the message.
Now we have a number of "callback" functions. One for each input, and one common function they all call.
/ Callback functions
// A function to complete the interupt callbacks
void switchCallback() {
if (PORTD.INTFLAGS & PIN2_bm) {
// Reset the interrupt flag
PORTD.INTFLAGS = PIN2_bm;
}
}
// Callback for tipper switch 1
void switch1Callback(void) {
sleepRestoreReason = 1;
switchCallback();
}
// Callback for tipper switch 3
void switch2Callback(void) {
sleepRestoreReason = 2;
switchCallback();
}
// Callback for the debugging button (SW0)
void switch3Callback(void) {
sleepRestoreReason = 3;
switchCallback();
}
These function are ISR that are called automatically. This is enabled in the setup below. When they are called sleepRestoreReason is set so we know why we have been woken up, and then switchCallback is called to enable buttons to be pressed again.
Now we are on to the main body of code. First the setup code. At the top we enable debugging.
id setup() {
// Start the log for debugging
Log.begin(115200);
Log.info(F("Starting"));
...and enable the hardware...
// Initalise the hardware
LedCtrl.begin(); // LEDs
Veml3328.begin(); // Light sensor
Mcp9808.begin(); // Temprature sensor
...and set the system up to allow it to go to sleep...
// Alow the modem and the CPU to be powered down
// Note that the networks in the UK do not currenlty support the power save feature
LowPower.configurePowerDown();
Now we enable the IO pins for the sensors and tell the system what function to call (above) when the the sensor is active.
// Enable the switches and hook in the callbacks
pinConfigure(SW1, PIN_DIR_INPUT | PIN_PULLUP_ON);
attachInterrupt(SW1, switch1Callback, FALLING);
pinConfigure(SW2, PIN_DIR_INPUT | PIN_PULLUP_ON);
attachInterrupt(SW2, switch2Callback, FALLING);
pinConfigure(SW0, PIN_DIR_INPUT | PIN_PULLUP_ON);
attachInterrupt(SW0, switch3Callback, FALLING);
Finally in the setup we connect to the mobile network along with some debug tracking.
// Make the lights flash, becasue we can
// That and debugging. Yes, we are usig a visual indicator for debuggind and totllay not becuase it looks cool
LedCtrl.startupCycle();
// Start LTE modem and connect to the operator
if (!Lte.begin()) {
Log.error(F("Failed to connect to the operator"));
return;
}
Log.infof(F("Connected to operator: %s\r\n"), Lte.getOperator().c_str());
At the end of the code is the most important main loop. The first thin we do is go to sieep. Honestly. Now we are all set up we have noting more to do until there is something to send.
void loop() {
// Sleep the device until somthing happens
int wakeReason = sleepDevice(SLEEP_TIMEOUT);
If we get to continue it is because we have sensed something or a timeout has occured. Either way we have to send this information to the server.
Log.infof(F("Wake Reason: %d\r\n"), wakeReason);
// If we are here somthing must have happeed so report it to the server
sendTipUpdate(wakeReason);
}
...and that is all the code.
EnclosureThe enclosure is based around a 3D printed holder that will keep the main controller board, the battery, and the switches in place. This is held in a more rigid outer cover that will protect it from the elements. This case is closed using an acrylic cover that can be laser cut.
This is a demonstration of the tipper teacher in action. Because of the mobile network issues this is using a domestic bin for the demonstration.
When the bin is opened the controller wakes it, it re-authenticated with the network, and sends an HTTP push request with the data.
It would normally be bolted to the bin, but this is not my bin so it was taped for the demonstration.
ConclusionAlthough this project did not go in the direction I originally intended I am quite happy with my solution.
My one big takeaway is that if the device is not connecting then it is most likely the cellular network. Since I started it is becoming more reliable, and I hope in time the LTE-M network will be as reliable as the 4G network, but at the start of 2024 in the UK it is still work in progress. If it does not connect then take coverages with a pinch of salt, and check the network status web page when things are not working.
If I knew back at the start what I know now, I would use an RTC (Real Time Clock) module to store stat in NV RAM and send it when the network is available.
Other than that warning I am happy with the outcome and will come back and test this properly when the network is fully working.
Comments