This is the blog of the P3-SETR, where I have created a Vending Machine Controller using an Arduino Kit.
Schematic
Here I show you the schematic of the circuit.
Components
Component Name | Amount |
---|---|
Arduino Uno board | x1 |
Protoboard | x1 |
LEDs | x2 |
Button | x1 |
DHT11 sensor | x1 |
Ultrasonic sensor | x1 |
Joystick | x1 |
LCD | x1 |
330Ω resistance | x3 |
2kΩ resistance | x1 |
Requirements
- One of the LEDs must be connected to a digital input (pin 1 to pin 13) in order to be able to modify the LED intensity using pulse-width modulation (PWM).
- The button must be connected in the pin 2 or 3, those pins are associated to the interruptions.
- The joystick have 2 potentiometer (X and Y) that generate analogic signals so the pins related to the X and Y axis of the joystick must being connected to an analogic input.
- The button of the joystick must be connected to a digital input in order to be able to declare the button as a pull-up input.
Coding
Libraries
During this proyect we need to use multiple libraries.
- This library is related to the implementation and management of threads. ```
include
include
include
- This [library](https://www.arduino.cc/en/Reference/LiquidCrystal) is used to control the LCD.
include
- This [library](https://github.com/adafruit/DHT-sensor-library) is necessary to interact with the DHT sensor.
include
include
include
- This [library](https://www.arduino.cc/reference/en/libraries/timerone/) is used to set and control timers.
include "TimerOne.h"
- This [library](https://github.com/vancegroup-mirrors/avr-libc/blob/master/avr-libc/include/avr/wdt.h) is used to set and control the watchdog.
include
### Threads
To make this project i used 5 differents thread associated to periodic tasks.
- Show distance in real time.
```c
// Creation of threads
Thread show_distanceThread = Thread();
void setup(){
Serial.begin(9600);
// show_distance Thread config
show_distanceThread.enabled = true;
show_distanceThread.setInterval(200);
show_distanceThread.onRun(callback_measure_distance);
}
void callback_measure_distance(){
// Send a 10us pulse
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Calculate the distance in cm
t = pulseIn(echoPin, HIGH);
d = t/59;
// Print on the LCD the actual disntance
lcd.clear();
lcd.print(d);
lcd.print(" cm");
}
- Show how much time has passed since the program started.
// Creation of threads
Thread clockThread = Thread();
void setup(){
Serial.begin(9600);
// clock Thread config
clockThread.enabled = true;
clockThread.setInterval(200);
clockThread.onRun(callback_clock);
}
void callback_clock(){
initial_time = millis();
lcd.clear();
lcd.print(initial_time);
}
- Check if someone is near the machine (< 1m).
// Creation of threads
Thread check_distanceThread = Thread();
void setup(){
Serial.begin(9600);
// check_distance Thread config
check_distanceThread.enabled = true;
check_distanceThread.setInterval(2500);
check_distanceThread.onRun(callback_check_distance);
}
void callback_check_distance() {
// Send a 10us pulse
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Calculate the distance in cm
t = pulseIn(echoPin, HIGH);
d = t/59;
lcd.clear();
// If someone is < 1m from the sensor, distance = true
if (d < 100){
distance = true;
}
else{
distance = false;
show_temp = true;
}
return distance, show_temp;
}
Controller
For the next 2 threads we will need a controller, because one of the threads will show the temperature and the other will show the humidity, they must update themselves, but they must be executed interleaved.
- Show temperature thread. ```c
// Creation of threads
Thread measure_tempThread = Thread();
void setup(){
Serial.begin(9600);
// measure_temp Thread config
measure_tempThread.enabled = true;
measure_tempThread.setInterval(1000);
measure_tempThread.onRun(callback_measure_temp);
}
void callback_measure_temp() {
// Lógica para medir la temperatura aquí
temperature = dht.readTemperature();
lcd.clear();
lcd.print("T: ");
lcd.print(temperature);
lcd.print("C");
delay(1000);
}
- Show humidity thread.
```c
// Creation of threads
Thread measure_humThread = Thread();
void setup(){
Serial.begin(9600);
// measure_hum Thread config
measure_humThread.enabled = true;
measure_humThread.setInterval(1000);
measure_humThread.onRun(callback_measure_hum);
}
void callback_measure_hum() {
// Lógica para medir la humedad aquí
humidity = dht.readHumidity();
lcd.clear();
lcd.print("H: ");
lcd.print(humidity);
lcd.print("%");
}
- Controller for the two threads. ```c
// Creation of the controller
ThreadController controller = ThreadController();
void setup(){
Serial.begin(9600);
// adding measure_temp Thread and measure_hum Thread to the controller
controller.add(&measure_tempThread);
controller.add(&measure_humThread);
}
void admin_temp(){
while (analogRead(pinX) > 400){
controller.run();
}
}
### Interruption
I used interruptions to be able to enter the admin mode and reset while serving a coffee.
I used 2 types of interruptions:
- Hardware interruption.
```c
void setup() {
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(BUTTON), button_pressed, CHANGE);
}
- Software interruption.
void setup() {
Serial.begin(9600);
Timer1.initialize(1000000);
Timer1.attachInterrupt(callback_count);
}
- Both interruptions: ```c
void button_pressed() {
// Enable interruptions
interrupts();
// Enable the watchdog (8sec)
wdt_enable(WDTO_8S);
// If the button is not pressed
if (digitalRead(BUTTON) == HIGH) {
wdt_disable();
// Stops the timer
Timer1.stop();
// Depending on the time and where were you before entering the interruption does a thing or an other
if (pressed_time >= 2 && pressed_time <= 3) {
// Reset pressed_time
pressed_time = 0;
Serial.println("Botón pulsado entre 2 y 3 segundos");
if (check_serve == true){
check_serve = false;
menu();
}
}
else if (pressed_time >= 5) {
// Reset pressed_time
pressed_time = 0;
Serial.println("Botón pulsado más de 5 segundos");
if (check_admin == false){
admin();
}
else if (check_admin == true){
analogWrite(LED2, 0);
digitalWrite(LED1, LOW);
menu();
}
}
// Reset pressed_time
pressed_time = 0;
}
else {
// Enable interruptions
interrupts();
// Enable watchdog(8s)
wdt_enable(WDTO_8S);
// Start the timer
Timer1.start();
}
}
void callback_count() {
// Disable interruptions
noInterrupts();
pressed_time++;
Serial.println(pressed_time);
}
#### Variables
To modify variables that are inside interruptions, we need to declare them as volatile.
volatile bool check_admin = false;
volatile int pressed_time;
volatile bool check_serve = false;
</code></pre></div><h4>
<a name="timerone" href="#timerone">
</a>
TimerOne
</h4>
<p>We need to use TimerOne library because the Timer0 of the Arduino board doesn´t work while it´s inside an interruption, so, to count how many seconds have you been pressing the button we need it.</p>
<h3>
<a name="watchdog" href="#watchdog">
</a>
Watchdog
</h3>
<p>This program has a watchdog in the interruptions, in case something has blocked or is not responding to the behavior wanted, the watchdog resets the system after 8 seconds.</p>
<div class="highlight"><pre class="highlight c"><code>
<span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
<span class="n">Serial</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="mi">9600</span><span class="p">);</span>
<span class="c1">// Disable the watchdog</span>
<span class="n">wdt_disable</span><span class="p">();</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">button_pressed</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Enable the watchdog (8sec)</span>
<span class="n">wdt_enable</span><span class="p">(</span><span class="n">WDTO_8S</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div><h3>
<a name="functionality" href="#functionality">
</a>
Functionality
</h3>
<ul>
<li>Client mode.
<iframe
width="710"
height="399"
src="https://www.youtube.com/embed/roYreZuSSTw"
allowfullscreen
loading="lazy">
</iframe>
(In case the video doesn´t play try this link: <a href="https://youtu.be/roYreZuSSTw">https://youtu.be/roYreZuSSTw</a>)</li>
<li>Admin mode.
<iframe
width="710"
height="399"
src="https://www.youtube.com/embed/-UY6RScsZmc"
allowfullscreen
loading="lazy">
</iframe>
(In case the video doesn´t play try this link: <a href="https://youtu.be/-UY6RScsZmc">https://youtu.be/-UY6RScsZmc</a>)</li>
</ul>
<p><strong>Complete code:</strong> <a href="https://github.com/JMMinguez/Vending_Machine_Controller">https://github.com/JMMinguez/Vending_Machine_Controller</a></p>
Top comments (1)
I want to know this good article which haven't person to look