DEV Community

Cover image for Vending Machine Controller
Jorge Martin
Jorge Martin

Posted on • Edited on

Vending Machine Controller

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.
Arduino Schematic

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.
Enter fullscreen mode Exit fullscreen mode

include


- This [library](https://github.com/adafruit/DHT-sensor-library) is necessary to interact with the DHT sensor.
Enter fullscreen mode Exit fullscreen mode

include

include

include


- This [library](https://www.arduino.cc/reference/en/libraries/timerone/) is used to set and control timers.
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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");
}


Enter fullscreen mode Exit fullscreen mode
  • 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);
}


Enter fullscreen mode Exit fullscreen mode
  • 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;
}


Enter fullscreen mode Exit fullscreen mode

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("%");
}


Enter fullscreen mode Exit fullscreen mode
  • 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);
}


Enter fullscreen mode Exit fullscreen mode
  • Software interruption.


void setup() {
  Serial.begin(9600);
  Timer1.initialize(1000000);  
  Timer1.attachInterrupt(callback_count);
}


Enter fullscreen mode Exit fullscreen mode
  • 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;
Enter fullscreen mode Exit fullscreen mode

}
else {
// Enable interruptions
interrupts();

// Enable watchdog(8s)
wdt_enable(WDTO_8S);

// Start the timer
Timer1.start();
Enter fullscreen mode Exit fullscreen mode

}
}
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>
Enter fullscreen mode Exit fullscreen mode
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
stockdale_roger_999e292a7 profile image
stockdale roger

I want to know this good article which haven't person to look