Making my own IoT backend-less vending machine
March 12, 2021 -
I am finishing my specialty in Multi-Platform Application Development before starting my university studies. Honestly, I’ve been invited to create a project to showcase my new skills. After thinking about it and weighing a few ideas, I decided to develop a project that would let me dive into the world of IoT and serverless programming.
In this case, I decided to create a ticket vending machine for events that doesn’t need a central server to operate. The idea is that the machine can issue and validate event tickets autonomously, using IoT principles and making Android the core of the user interface. Firebase became my go-to choice for managing the data layer. With it, I was able to build the database structure for tickets and advertisements without maintaining a traditional backend, and use Firestore to handle tickets, advertisements, and the entire ticket lifecycle.
Understanding the Concept
The main challenge was to design a vending setup that could issue and validate event tickets autonomously. That meant building a system that could authenticate, store, and process everything on the edge, using IoT principles and making Android the center for the UI. Firebase became my go-to for managing the data layer. With it, I could build out the database structure for tickets and ads without maintaining a traditional backend, and use Firestore for handling tickets, announcements, and the entire ticket lifecycle.
There’s an architectural angle here that makes this project interesting. With hexagonal architecture, the business logic can stay independent from the implementation details of specific hardware or even database choice, making it relatively easy to adapt the design for future hardware or requirements. Hexagonal architecture also allowed me to modularize the ticket validation and user interfaces without one depending directly on the other.
Building Out the Core System
With the architecture in place, the next step was diving into the setup for Firestore collections and Firebase Authentication, which would help keep things secure. I structured the data collections to capture concerts, tickets, and advertisements separately. With this, the vending machine could dynamically serve up ads in between purchases or during idle times. For tickets, I used Apple’s Passbook format to issue each one with a downloadable QR code, which the machine could validate on-site. That saved me a lot of trouble, as it meant the tickets were compatible with existing mobile wallets and minimized the data exchange needed between devices.
For the UI and validation interfaces, I turned to JavaFX and Android SDK. The Android device displayed the main interface, while a second device handled QR validation and triggered the access permissions. The ticketing and validation systems both use a few simple interfaces to make calls to Firestore, and I kept it flexible so I could change the data source without affecting the overall flow.
Handling Real-World Constraints
One of the biggest constraints was making sure the vending machine could work autonomously, with minimal need for maintenance. I had to ensure that user sessions and data like QR codes and tickets expired after a given period to avoid bloating memory and losing performance over time. Each ticket has a status field that updates once it’s used, so validation is straightforward and efficient. The whole system feels robust enough to handle event-level traffic without the kind of congestion that would come with multiple round-trips to a central server.
Another core piece is the emulator I used to simulate coin insertion for testing payments, which allowed me to validate the sequence end-to-end without a physical coin interface. I kept it simple: a timer increments the “inserted” count, and it’s easy enough to switch this to a real-world currency handler later on if needed.
Diving into Firebase and Data Handling
Since there was no backend server, I leveraged Firebase for both user data and ticket management. Using Firestore, I created three main collections: one for tickets, another for events, and a third for advertisements. This structure allows the machine to handle ticket sales by storing a new entry in the tickets collection with all the necessary metadata, including event details, ticket status, and QR code information.
For ticket generation, I went with Apple’s Passbook format since it’s widely compatible with mobile wallets and offers easy QR code support. Each ticket purchase generates a QR code, which is stored in Firebase and pulled up when scanned for validation. I configured Firebase Authentication to manage user access, allowing administrators to log in and update event or ad details directly from the device.
One challenge here was data persistence and cleanup. Since the machine could end up storing a large number of tickets over time, I implemented an automated cleanup process that clears expired or validated tickets after a set period. This keeps memory usage low and ensures the system doesn’t slow down over time. With Firestore, it’s fairly straightforward to set a retention policy, so I can keep data management automatic and avoid any need for manual intervention.
Implementing the User Interface and Device Setup
The main interface runs on an Android device, while a second device handles the validation side. I used JavaFX to build the ticket validation UI, while the Android SDK powers the main sales interface. The idea was to create a seamless user experience where buyers could quickly interact with the machine, and validation staff could easily check tickets without needing direct access to the main UI.
For the UI, I wanted a balance between simplicity and reliability. Using MVVM (Model View ViewModel) helped separate the presentation logic from the core business logic. This pattern lets each activity have its own ViewModel, making it easy to manage state changes without causing unexpected behavior. For instance, the sales UI could show a different screen during idle times, displaying ads from the Firebase ads collection, while the ticketing functions remain ready to jump back into action when a new user approaches.
The validation device runs a JavaFX-based program that continuously listens for QR codes using a camera module. Once a QR code is detected, it triggers the Firebase API to check ticket validity. This process happens instantly, and a simple color-coded response – green for valid, red for invalid – provides feedback without delay. The idea was to minimize user wait time and make sure the entire flow felt frictionless.
Real-World Testing and Performance
In testing, one of my priorities was ensuring that everything would work without server latency impacting the user experience. I performed several End-to-End tests using different devices, like a Google Pixel XL, Xiaomi Mi A2, and Raspberry Pi 3 with Android Things. These tests were especially useful for checking the system’s stability in low-connectivity situations and evaluating how it handled high traffic.
The Raspberry Pi setup demonstrated that the machine could run autonomously with minimal intervention. The coin-insertion emulator I used for testing was another interesting feature. By simulating coin insertion, I was able to test the entire payment and ticketing flow without a physical payment module. It worked well for prototyping, and if the machine ever gets deployed with real payment functionality, this emulator could easily be swapped out.
All tests focused on maintaining performance during idle and active states, ensuring there were no memory leaks or slowdowns. Firebase’s caching proved reliable, and having offline persistence in Firestore meant that even if connectivity dropped momentarily, the machine could still function as expected.
Conclusion
Building an IoT vending machine without a backend server was a fun and challenging project that pushed the limits of what’s possible with edge computing. By leveraging Firebase for data management and Android for the UI, I was able to create a self-sustaining system that could handle ticket sales and validation without needing a central server. The hexagonal architecture made it easy to adapt the design for different hardware setups, and the modular approach to ticketing and validation interfaces kept the system flexible and scalable.
The project is hosted on Github, and you can check it out here
Credits
The header image of this post is made using Midjourney AI.