Bottle Cap Detector

Submitted by Bert on Thu, 07/16/2020 - 16:38

Tools

  • Soldering iron

    A soldering iron is used to attach metal parts to each other by melting a metal alloy. It is mostly used to attach components to a printed circuit board.

Components

  • 1
    Buck converter

    A buck converter drops a voltage from a higher value to a lower value. They are based on a switching mode voltage regulator and very efficient.

  • 1
    Arduino UNO R3

    A Atmel Atmega328 based micro controller board that formed the basis for the Arduino development platform

  • 1
    Arduino prototype shield

    An Arduino prototype shiled allows you to create your own shield for your projects

  • 1
    Inductive sensor

    An inductive sensor can detect metallic objects without touching them.

  • 1
    Retro-Reflective Photoelectric Sensor

    A retro-reflective light sensor can detect objects passing throught a light beam.

  • 2
    The Resistor

    A resistor is a component that can limit current or voltage

    10
    kOhm
  • 1
    The Resistor

    A resistor is a component that can limit current or voltage

    1
    kOhm
  • 1
    The Resistor

    A resistor is a component that can limit current or voltage

    47
    kOhm
  • 1
    LED

    The Light Emitting Diode is the modern version of a light bulb. It exists in many colours and is very efficient in converting electrical to light energy.

    5
    mm
    Red
  • 1
    LED

    The Light Emitting Diode is the modern version of a light bulb. It exists in many colours and is very efficient in converting electrical to light energy.

    5
    mm
    Green
  • 1
    Push button

    A push button is a kind of switch that only makes contact when pressed.

  • 1
    Relay

    A relay is an electromechanical switch that alows a high power to be controlled using a low power.

Instructions

This detector actually detects the absence of a bottle cap when a bottle passes a beam of light after leaving an automatic bottle filling machine. When one or more bottles without a cap are detected the filling machine is stopped by activating the emergency stop.

Introduction

When I got the question about detecting bottle caps it seemed quite simple at first: get a sensor to detect a metal object and you're done, right? Well, not really. So I started a conversation with the owner of Brewery Boelens to get the complete picture. Apparently the bottle caps that have to be pressed on the bottles are presented from a filler tube. Once in a while a cap does not exit that tube properly resulting in a bottle without a bottle cap. The next step in the filler station is printing the expiry date in the cap and with the cap missing that date is printed on the beer foam making the bottle unsalable. You wouldn't want a beer with some added ink solvents, would you?

Detecting a bottle cap can be done with an inductive sensor, so add a sensor and you are done, tright? No, not really. You have to detect the bottle too. So add a light gate and detect the bottle the moment the bottle passes the gate. Yeah, we are getting somewhere but it's not enough really.

I called the owner of the brewery and asked how his filling station worked. Apparently he used a PLC which has an emergency stop circuit. Opening that emergency stop circuit halts the filling station allowing the operator the fix the bottle cap filling tube.

Let's add a few requirements to make the detection workable:

  • There must be a clear opening between two bottles so the light gate is closed between two bottles
  • The inductive sensor triggers while the light gate is opened.
  • The sensors are fast enough to trigger before the bottle has passed the gate.
  • The code is never halted and continuously keeps listening for sensor inputs.
  • A reset button is used to reset the alarm. To avoid accidental resets the button needs to be pressed for at least one second. The trigger is given when the button is released.

Flow description

Flow chart showing the loop logic

The flow chart on the left explains what the code needs to do. Creating such a flow chart makes coding a lot easier and avoids spaghetti code as it makes it necessary for you to think prior to start coding.

If you think you can simplify the process even further, please don't hesitate to let me know.

Hardware

Input

As stated in the introduction we will need three basic inputs

The light gate will detect the passing bottle and the inductive sensor will detect the bottle cap. I have created pages explaining the use of these sensors. Make sure to check these pages out before moving on. If you are not very experienced try out the different sensors individually before putting the whole bottle cap detector together

Output

Of course we need some form of output or our detector would be kind of useless. I like to use indicator LEDs if I don't have a display of some sort. So I added one for the detection of the bottle, one for the detection of the cap and one for when the alarm goes off. That last one is of course the most important one an d should not be omitted in your setup!

So here's the list of the outputs

  • Bottle detected indicator LED
  • Bottle cap detected indicator LED
  • Alarm indicator LED
  • Relay

Pinouts

As we now have a list of all the different connections we have to make we can decide what pins we are going to use on the Arduino. All inputs and outputs are digital without any need for Pulse Width Modulation.

Pin numberDirectionDescription
2OutputBottle Detection indicator LED
3OutputCap detector indicator LED
4OutputAlarm indicator LED
5InputReset button
6InputInductive sensor
7InputRetro-reflective light sensor
8OutputAlarm relay

Voltages and currents

The Inductive sensor and the light sensor require 24 volt while the Arduino, relay and the rest of the components run on 5 volt. As this project is to be installed in an industrial environment where 24 volt is a standard the higher voltage is easily covered. By using a buck converter we can convert this 24V to 5V efficiently. The question that remains is "How much current do we need at 5V?". So let's sum up the components that use 5V and the maximum current they need.

NameMaximum current
Arduino200mA
Relay100mA
LED20mA per LED

Adding all this up we get a maximum current of about 360mA on the 5V line. This means that even a very small buck converter using a MP1584 chip can provide plenty of power. The buck converter linked in the component list above provides a maximum current of 3A.

So by getting the 24V from the industrial power and putting that through the buck converter we get the 5V. Of course it is very important to connect the 0V, also referred to as ground, of both voltages together. This is necessary to have a common voltage reference for both voltage potentials.

Schematic

The schema is available in KiCAD format on the GitHub project page.

Building the prototype

Software

This project requires some experience with the Arduino framework. The code below contains plenty of comments so it is easy to follow.

DEBUG parameter

I like to be able to switch debugging voer the serial port on or off because sending data over the serial port if very time consuming. When running the code in production mode there generally is no serial port connected so there's no use in sending debug messages to the serial port. By defining a DEBUG parameter, setting it to true and encapsulating every Serial command in an if(DEBUG) {} command it is very easy to switch debugging on and off simply by setting the DEBUG parameter to true or false.

The code

  1. #include "Arduino.h"
  2. #include "OneButton.h"
  3.  
  4. //Define the used pins
  5. #define BOTTLE_DETECTION_LED_PIN 2
  6. #define CAP_DETECTION_LED_PIN 3
  7. #define ALARM_LED_PIN 4
  8. #define RESET_BUTTON_PIN 5
  9. #define INDUCTION_SENSOR_PIN 6
  10. #define LIGHT_SENSOR_PIN 7
  11. #define ALARM_RELAY_PIN 8
  12.  
  13. //The number of bottlkes without a cap within the error window that trigger a alamr
  14. #define MAX_ERRORS 3
  15. //The number of bottles in the error window. The error window is opened when the first faulty bottle is detected. If the maximum number of faulty bottles is detected within this window the aram is triggered.
  16. #define ERROR_WINDOW 20
  17. //Set the parameter below to true to get debug messages on the serial port
  18. //Set the parameter below to false to disable the serial port and not get any debug messages
  19. #define DEBUG false
  20.  
  21. //Instaniate the varables
  22. int NrOfErrors;
  23. int NrOfBottlesSinceFirstError;
  24. bool bottleDetected;
  25. bool capDetected;
  26. bool alarm;
  27. bool buttonPressed;
  28.  
  29. //Instantiate the button object and hook it up to the button pin/
  30. OneButton button(RESET_BUTTON_PIN, true);
  31.  
  32. //Create an array of LED statusses. This makes the code easier to read.
  33. enum ledStates
  34. {
  35. OFF,
  36. ON
  37. };
  38.  
  39. //Checks if a bottle is detected.
  40. //Returns false when no bottle is detected.
  41. //Returns true when a bottle is detected
  42. bool detectBottle()
  43. {
  44. if (digitalRead(LIGHT_SENSOR_PIN) == 1)
  45. {
  46. return true;
  47. }
  48. else
  49. {
  50. return false;
  51. }
  52. }
  53.  
  54. //checks if a bottle cap is detected
  55. //Returns true of a bottle cap is detected
  56. //Returns false if no bottle cap is detected
  57. bool detectCap()
  58. {
  59. if (digitalRead(INDUCTION_SENSOR_PIN) == 0)
  60. {
  61. return true;
  62. }
  63. else
  64. {
  65. return false;
  66. }
  67. }
  68.  
  69. //Switches the alarm relay on if the alarm state is ON
  70. void setAlarmRelay(ledStates state)
  71. {
  72. if (state == OFF)
  73. {
  74. digitalWrite(ALARM_RELAY_PIN, LOW);
  75. }
  76. else
  77. {
  78. digitalWrite(ALARM_RELAY_PIN, HIGH);
  79. }
  80. }
  81.  
  82. //Switches on the LED that indicates if a bottle is detected when the bottle detected state is ON
  83. void setBottleLED(ledStates state)
  84. {
  85. if (state == OFF)
  86. {
  87. digitalWrite(BOTTLE_DETECTION_LED_PIN, LOW);
  88. }
  89. else
  90. {
  91. digitalWrite(BOTTLE_DETECTION_LED_PIN, HIGH);
  92. }
  93. }
  94.  
  95. //Switches on the LED that indicates if a bottle cap is detected when the bottle cap detected state is ON
  96. void setCapLED(ledStates state)
  97. {
  98. if (state == OFF)
  99. {
  100. digitalWrite(CAP_DETECTION_LED_PIN, LOW);
  101. }
  102. else
  103. {
  104. digitalWrite(CAP_DETECTION_LED_PIN, HIGH);
  105. }
  106. }
  107.  
  108. //Switches the alamr LED on indicating that an alarm is triggered
  109. void setAlarmLED(ledStates state)
  110. {
  111. if (state == OFF)
  112. {
  113. digitalWrite(ALARM_LED_PIN, LOW);
  114. }
  115. else
  116. {
  117. digitalWrite(ALARM_LED_PIN, HIGH);
  118. }
  119. }
  120.  
  121. //This method is called when the button is pressed for at least 1 second
  122. void buttonLongPress()
  123. {
  124. //Set the flag indicating that the button has been pressed
  125. buttonPressed = true;
  126. }
  127.  
  128. //Initialise the parameters used in the application
  129. void initParameters()
  130. {
  131. NrOfErrors = 0;
  132. NrOfBottlesSinceFirstError = 0;
  133. bottleDetected = false;
  134. capDetected = false;
  135. alarm = false;
  136. buttonPressed = false;
  137.  
  138. //Initialise the indicator LEDs
  139. setBottleLED(OFF);
  140. setCapLED(OFF);
  141. setAlarmLED(OFF);
  142. setAlarmRelay(OFF);
  143. }
  144.  
  145. //Set the hardware pin modes
  146. void setPinModes()
  147. {
  148. //Set the input pins
  149. pinMode(INDUCTION_SENSOR_PIN, INPUT);
  150. pinMode(LIGHT_SENSOR_PIN, INPUT);
  151. pinMode(RESET_BUTTON_PIN, INPUT_PULLUP);
  152.  
  153. //Set the output pins
  154. pinMode(CAP_DETECTION_LED_PIN, OUTPUT);
  155. pinMode(BOTTLE_DETECTION_LED_PIN, OUTPUT);
  156. pinMode(ALARM_RELAY_PIN, OUTPUT);
  157. pinMode(ALARM_LED_PIN, OUTPUT);
  158. }
  159.  
  160. //The set up routine is only called once when starting up the code. It's used to set everything up.
  161. void setup()
  162. {
  163. //Only set up the serial port when debug messages are used.
  164. if (DEBUG)
  165. Serial.begin(9600);
  166.  
  167. //Connect the "long press" event to the buttnoLongPress method
  168. button.attachLongPressStart(buttonLongPress);
  169.  
  170. //Set the hardware pin modes
  171. setPinModes();
  172.  
  173. //Initialise all parameters
  174. initParameters();
  175. }
  176.  
  177. //The loop method is called over and over again, thus providing the mail application functionality
  178. void loop()
  179. {
  180. //monitor de reset button
  181. button.tick();
  182. //We can only start when there is no alarm
  183. if (!alarm)
  184. {
  185. //We wait for a bottle to be detected
  186. if (detectBottle())
  187. {
  188. if (DEBUG)
  189. Serial.println("Bottle detected");
  190. //Keep track of the fact a bottle is detected
  191. bottleDetected = true;
  192. //Suppose no bottle cap has been detected at this point
  193. capDetected = false;
  194. //Switch the "bottle detected" indicator LED on
  195. setBottleLED(ON);
  196. //We start the loop that runs as long as the bottle is detected and the reset button is not pressed
  197. while (detectBottle() && !buttonPressed)
  198. {
  199. //monitor the reset knop. We need this command here too, otherwise we can't get out of this loop
  200. button.tick();
  201. //Check if a bottle cap is detected. Only take action the first time the cap is detecxted
  202. if (detectCap() && !capDetected)
  203. {
  204. if (DEBUG)
  205. Serial.println("Cap detected");
  206. //Keep track that the cap is detected
  207. capDetected = true;
  208. //Swith the "bottle cap" indicator LED on
  209. setCapLED(ON);
  210. }
  211. }
  212. //The bottle has passed, switch the indicator LEDS off
  213. setCapLED(OFF);
  214. setBottleLED(OFF);
  215. //Take some action if no cap has been detected
  216. if (!capDetected)
  217. {
  218. //No cap detected, keep track of the count
  219. NrOfErrors++;
  220. if (DEBUG)
  221. {
  222. Serial.print("Number of faulty bottles = ");
  223. Serial.println(NrOfErrors);
  224. }
  225. }
  226. //If this is not the first error, increase the number of faulty bottles in the error window
  227. if (NrOfErrors > 0)
  228. {
  229. NrOfBottlesSinceFirstError++;
  230. if (DEBUG)
  231. {
  232. Serial.print("Number of bottles detected in the error window = ");
  233. Serial.println(NrOfBottlesSinceFirstError);
  234. }
  235. }
  236. //We check if the number of bottles is greater or equal to the maximum number of errors in the error window
  237. //If so, reset the error counts
  238. if (NrOfBottlesSinceFirstError >= ERROR_WINDOW)
  239. {
  240. NrOfErrors = 0;
  241. NrOfBottlesSinceFirstError = 0;
  242. }
  243. //If the maximum number of errors is reached at this point, this can only be within the fault window, so raise the alarm
  244. if (NrOfErrors >= MAX_ERRORS)
  245. {
  246. if (DEBUG)
  247. Serial.println("Maximum number of faulty bottles reached. ALARM!!");
  248. //Keep track of the alarm
  249. alarm = true;
  250. //Switch the alarm indicator LED on
  251. setAlarmLED(ON);
  252. //Switch the alarm relay on
  253. setAlarmRelay(ON);
  254. }
  255. }
  256. }
  257. else
  258. {
  259. //Whe we get here, the alarm state is raised, so we wait for the reset button to be pressed.
  260. if (buttonPressed)
  261. {
  262. if (DEBUG)
  263. Serial.println("Reset button is pressed. Initialise parameters and start again.");
  264. //Reset is pressed, so we reset all parameters and start all over again.
  265. initParameters();
  266. }
  267. }
  268. }

Result

Here's a video of the detector working at Brouwerij Boelens.