fbpx

Designing an IoT Product from Scratch (Food Dispenser)

Designing an IoT Product from Scratch (Food Dispenser)
Reading Time: 10 minutes

By Yeifer Mena, Staff Software Engineer at Growth Acceleration Partners

Greetings everyone! It has been a long time since I last wrote something on this blog, but I did not disappear because I chose to. I was busy learning a lot of things, and in this series of posts, I want to share with you a few things I learned about the world of IoT as a novice.

Let’s get started with a case: imagine you are a junior developer in a fictional company called Acme Corporation controlled by cats. Monty is one of the founders, and he wants a device that feeds him when his master is not home. As a developer, you are tasked with building that device. Monty gives you one single condition: All this work should be as minimalist as possible. He does not want to pay for a Cloud service or have a server running 24/7 just to be fed.

Building the Prototype

To be able to achieve this, you decided to first focus your attention on the mechanical design. You know it does not matter if your code is great but your device does not move or feed Monty. The device has to give him food when he needs it (basically he needs to give him food when his plate is empty or nearly empty) and his master is no longer at home. The company has a 3D printer, and you think you can somehow leverage it, but you do not have any experience using it. Luckily for you, there are a couple of options you can use to get started:

  • In case you do not want to or have the time to make your own custom design, platforms like Thingiverse are the right choice. They are like a social platform with a lot of readily available 3D designs. You just need to download the files for a specific design and slice the pieces to get the .gcode files (The only format a 3D printer understands). There are plenty of food dispenser designs. This platform can also give you an idea of what this device should be like — basically a motor and a container with food; each time the motor moves, food needs to be delivered. This is good, but what if Monty says the device is too small or too big and a modification must be made? Do I contact the design owner to get what I need? The answer must be “no” — no one should do your work in their spare time if you need a custom design.
  • In case you want your own design, Thinkercad, FreeCAD, Onshape and Solidworks are the tools you need, among many others. They allow you to build designs with your own parameters.

There is no right or wrong approach here. It all depends on the available time and how comfortable you feel with these tools. Just to give you an idea, here is a design I created using Onshape. I just work with the elements I have at my disposal, but I am also creating a design from something else I found on Thingiverse. It is a load cell bed, and you can find it here.

If you have any experience with mechanical engineering and you think this is wrong, please let me know. I am just a software guy who used CAD software to build something with a Nema 17 and a few rods.

Building the Circuit

Here you are; you have a 3D design that works for the job, but it does not move by itself. You need a circuit that moves it, but you are just a software engineer. Software is your expertise, not hardware, so you need to keep things simple. You have a stepper motor, but you are going to need a driver to use it. Doing some quick search, you find out the most common one is A4988.


You cannot use this driver manually; to be able to use it, you need a microcontroller. Since you spent a big amount of time the last couple of months learning how to use an ESP32, you are going to stick with it. That comes in handy because this device is super cheap and powerful compared with others meant for the same job.

Now, you have the device that is going to control the food dispenser, but how is this device going to know that Monty’s plate is empty or about to be empty? You need a load cell and the HX711 amplifier to get readings out of the load cell, and an OLED display to show the readings and the state of the device.

We will also add three buttons to the design. We can use two of them for the load cell calibration process, and another one to manually deliver food when necessary.

After all the extensive research, you realized you have all the pieces in place, and you are ready to write some code. In the end, your circuit looks like this:

Programming the Device

We need a couple of libraries to make this work:

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include “freertos/FreeRTOS.h”

#include “freertos/task.h”

#include “rom/ets_sys.h”

#include “driver/gpio.h”

#include “esp_log.h”

#include “driver/i2c.h”

#include “oled.h”

#include “wifi_connect.h”

#include “esp_netif.h”

#include “esp_http_server.h”

#include “esp_spiffs.h”

The first three libraries — stdio.h, string.h and stdlib.h — are common in C programs for string manipulation and input-output controls the flow. Since ESP32 uses FreeRTOS, you are going to use freertos/FreeRTOS.h and freertos/task.h. The first one is the core for ESP32 programming, and the last one is used for creating tasks leveraging the two cores ESE32 has.

Driver/gpio.h comes in handy for button manipulation. It gives you control of the GPIO pins ESP32 has, and driver/i2c.h allows you to control the two I2C modules this chip has to communicate with sensors and other devices. Another useful library is esp_log.h, which basically gives you the ability to output logs using ESP_LOGx functions.

Using OLED displays is quite difficult, but there are plenty of libraries out there that can be adapted to your needs. You just need letters and numbers and nothing more, so porting this library can get you right on track.

The display is quite good to help a human interact with the device, but what if it breaks or somehow the amount of food Monty needs changes because he is too fat? What if we need to remotely help him lose some weight without starving? A web interface is going to be needed for this purpose. We can make the ESP32 expose a web UI to adjust those parameters and check how much food is left on the plate in real time. That is why we need the rest of the libraries. Now, let’s set a couple of definitions:

static void food_dispenser_init(void)

static void food_dispenser_task(void *arg)

static void food_dispenser_close(void)

static void food_dispenser_open(void)

static void IRAM_ATTR food_dispenser_manual_isr(void *arg)

First, you need to make the ESP32 notify you when the manual button to give food to Monty gets pressed. That notification process is called an interrupt and it will allow you to fire a function to handle this event. You just created food_dispenser_manual_isr to handle this event and the food_dispenser_init function initializes this process. The rest of the functions are just for controlling the motor to open and close the food dispenser. Now the load cell initialization takes place, and for that, we have the following functions:

static void weight_sensor_init(void)

static void IRAM_ATTR weight_sensor_calibrate_isr(void *arg)

static void IRAM_ATTR weight_sensor_zero_offset_isr(void *arg)

static void weight_sensor_calibrate_task(void *arg)

static void weight_sensor_trigger_low_food_task(void *arg)

static void weight_sensor_readings_task(void *arg)

static float weight_sensor_get_value(void)

static void weight_sensor_zero_offset_task(void *arg)

The load cell bed has some weight. That’s why we need a button and its interrupt to get the device to know what the zero state will be (when the load cell bed is empty). weight_sensor_zero_offset_isr and weight_sensor_zero_offset_task are meant for that. A known weight is needed to calibrate the load cell. Since you know that the stepper motor you have weighs 130 kilograms, you will use it for the calibration process on the weight_sensor_calibrate_task and weight_sensor_calibrate_isr functions.

When the device detects the food on the plate is below a certain threshold, it must open the dispenser food gate to feed Monty. The weight_sensor_trigger_low_food_task function will take care of that along with the rest. Now we have the wifi and web server definitions:

static void wifi_init(void)

static void wifi_failed_callback(void)

static void wifi_connect_callback(void)

static void web_server_init(void)

static void web_server_start(void)

static esp_err_t handle_http_get(httpd_req_t *request)

static esp_err_t handle_calibrate_req(httpd_req_t *request)

static esp_err_t handle_zero_offset_req(httpd_req_t *request)

static esp_err_t handle_dispense_food_req(httpd_req_t *request)

static esp_err_t handle_ws_req(httpd_req_t *request)

static void send_async(void *arg)

The first three functions are wifi_init, wifi_failed_callback and wifi_connect_callback. They allow you to control the wifi at your will. For the web server creation, you are going to need something else: a web page, and this one will do the trick:

<!DOCTYPE html>

<html lang=”en”>

<head>

<meta charset=”UTF-8″>

<meta http-equiv=”X-UA-Compatible” content=”IE=edge”>

<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>

<title>Food Dispenser</title>

</head>

<body>

<div class=”container”>

<br>

<h1>Food Dispenser</h1>

<div class=”card”>

<div class=”card-header”>

<h5 class=”card-title”>Parameters</h5>

</div>

<ul class=”list-group list-group-flush”>

<li class=”list-group-item”>

<div class=”fw-bold”>Current Weight: </div>

<p id=”weight”></p>

</li>

<li class=”list-group-item”>

<div class=”fw-bold”>Min Weight: </div>

<p id=”min_weight”></p>

</li>

<li class=”list-group-item”>

<div class=”fw-bold”>Max Weight: </div>

<p id=”max_weight”></p>

</li>

<li class=”list-group-item”>

<div class=”fw-bold”>Device IP Address: </div>

<p id=”ip_address”></p>

</li>

<li class=”list-group-item”>

<div class=”fw-bold”>Calibration Weight: 130 g </div>

<p id=”calibration_weight”></p>

</li>

</ul>

<div class=”card-footer”>

<button class=”btn btn-lg btn-danger” id=”btnGiveFood”>Give Food!</button>

<button class=”btn btn-lg btn-primary” id=”btnSetZeroOffset”>Set Zero Offset</button>

<button class=”btn btn-lg btn-info” id=”btnCalibrate”>Calibrate</button>

</div>

</div>

<div class=”position-fixed bottom-0 end-0 p-3″>

<div id=”workingToast” class=”toast”>

<div class=”toast-header”>

<strong class=”me-auto”>Food Dispenser</strong>

<button type=”button” class=”btn-close” data-bs-dismiss=”toast”></button>

</div>

<div class=”toast-body”>

Done!

</div>

</div>

</div>

</div>

</div>

</body>

</html>

This is a web page with a few labels and some scripts. I removed the scripts for practical reasons, but you can get them from the code repository. This file is stored on /spiffs/index.html inside the ESP32 chip. web_server_init will pull this out of the device and initialize the file system.

Then, the endpoints needed to communicate the web app, and the ESP32 device are registered in web_server_start, where one of the endpoints must be a Web Socket endpoint. This will allow us to get and send information out of the device in real time using just a web browser.

The rest of the functions are just handlers to get the information coming in and out. If you are interested in this in a more detailed way, taking a look at the repository will work. Testing all the pieces in place we have a device like this:

 

 

Demonstration

You just finished the project and you are about to show your results in a meeting. Monty is happy with the solution and brings Snowbell to show him the device and how it will help them. But Snowbell is not happy; in fact, he is so angry. But who is Snowbell?

Snowbell is the CEO of Acme Corporation. He is the owner of the company and he no longer cares about his subordinates being fed. He wants to control the entire world, including his subordinates’ food supply (which is hundreds of cats around the world). He also wants statistics, reports and a few other things. A single device that is not even connected to the cloud is no longer suitable for the job. Monty was able to calm him down and convince him to build another device connected with any cloud, but that will be for another day. I will leave this as is.

Source Code

https://github.com/yeifermp/food-dispenser-devonly

References