HOME PAGE
Autonomous Popcorn Maker
Background
The desired outcome for this design is to minimize human interaction with a popcorn
making process, whether that be over a hot plate or in a microwave. It was observed that when popcorn is popped on a stove, the popping starts slow, becomes more frequent, and then tapers down to slow pops again. Therefore, the code utilized counters in a real time operating system to match the slower and more frequent popping patterns.
In this case a hotplate is used
as the heating element, the microphone and microprocessor account for the popcorn pops,
and the buzzer sounds when the popcorn is done popping. Furthermore, the design can be
transported to different pots or pans with different heating elements.
Some possible problems with this design include safety, and durability issues. This
project involves a relay that switches on and off AC wall power, and misuse can lead to
death by electrocution.
Technical Description
The basics of the system include the pot (or pan) which rests on the hotplate, with a microphone installed on the lid. The microphone, which connects to the 3.3V pin in the microcontroller, listens for the pops. The popping noise corresponds to some output voltage, which correlates to an analog input value that the microcontroller reads in. Notice the setup in Fig. 1:
Figure 1: System setup.
The popcorn pops were measured with the same microphone using an oscilloscope
to see the voltage peaks. Notice, in Fig. 2, the voltage peaked at approximately
2.7 volts. The threshold of the pops was set around 2.7 voltage (analog input value of
approximately 800). Each pop lasted between 1.53 milliseconds, which allowed the RTOS
timer to be used.
Figure 2: Popcorn voltage spike.
With the hotplate plugged in, power source on, and the microcontroller on, a switch
on the microcontroller will turn on the hotplate and initialize the microphone counter. Once
the popcorn starts popping, the microphone will listen for pops that have spaces less than
0.5 seconds in between each pop. Once this happens, a count is added to a counter, thus
once this counter reaches 3, another counter is introduced. This new counter uses the microphone to listen for pops that have a space of about 3 seconds in between each pop,
however, if there is for any reason a pop in between a space of 1 second occurs, this new
counter will reset back to zero automatically. This is to ensure that the new counter does not
reach its threshold of 5 counts that turns off the hotplate. The idea behind the two counters is
to accurately control when the hotplate turns off exactly when the popcorn stops popping. It
was observed that when popcorn is popped on a stove, the popping starts slow, becomes
more frequent, and then tapers down to slow pops again. Therefore, the counters are put in
place to match the slower and more frequent popping patterns
Prototype & Design
It is also worthy to note that a better system for switching on and off the hot plate is
recommended due to the high amperage requirement when the hot plate is running at full
power. A better solution would involve controlling the switch inside of the hot plate, rather
than splicing into the hot plate power cord and adding a relay. Additionally, a vibration
system or pot shaking mechanism must be implemented because the popcorn at the bottom
of the pan burns if not shaken or stirred.
Figure 3: Nomenclature of design and parts used.
A 10 amp relay was used in conjunction with one optoisolator for this task.
Figure 3 shows 12 V from the power supply, which is regulated to 5V and run
through the optoisolator, which then sinked in the microcontroller. On the other side of the
opto isolator 12V is ran through going to the relay. A buzzer is also on the opto
isolator which goes off once the popcorn is done. On the other side of this circuit, a
microphone listens to the pops in the pot and sends data as voltage spikes, which is
converted to an analog input that the microcontroller reads.
Code
// set up the hardware
12 #include "BasysMX3setup.h" // configuration
13
14 #include
15 #include
16 #include
17 #include
18 #include
19
20 // configure RTOS
21 #include "RTOS/FreeRTOS.h"
22 #include "RTOS/task.h"
23 #include "RTOS/queue.h"
24 #include "RTOS/timers.h"
25 #include "RTOS/semphr.h"
26 #include "RTOS/ConfigPerformance.h"
27
28 // Basys libraries used
29 #include
30 #include "BasysLib/config.h"
31 #include "BasysLib/adc.h"
32 #include "BasysLib/lcd.h"
33 #include "BasysLib/led.h"
34 #include "BasysLib/btn.h"
35 #include "BasysLib/swt.h"
36
37 #define UP 0
38 #define DN 1
39
40 static void updateLCD(void *pvParameters);
41 static void readmic(void *pvParameters);
42 static void onoroff(void *pvParameters);
43 static void SetupHardware(void);
44
45 xQueueHandle micqueue;
46 //xQueueHandle pause;
47 xSemaphoreHandle startup;
48
49 int main(void) {
50
51 SetupHardware();
52
53 xTaskCreate(updateLCD,
54 (signed char *) "LCD",
55 configMINIMAL_STACK_SIZE,
56 (void *) (0),
57 1,
58 NULL);
59
60 xTaskCreate(readmic,
61 (signed char *) "mic",
62 configMINIMAL_STACK_SIZE,
63 (void *) (0),
64 1,
65 NULL);
66
67 xTaskCreate(onoroff,
68 (signed char *) "mic",
69 configMINIMAL_STACK_SIZE,
70 (void *) (0),
71 1,
72 NULL);
73
74 micqueue = xQueueCreate(20, 12); //holds counters 1,2,3
75
76 startup = xSemaphoreCreateBinary(); //activates the readmic task
77
78 vTaskStartScheduler();
79
80 for (;;);
81
82 }
83
84 static void onoroff(void *pvParameters) {
85 TRISCbits.TRISC2 = 0; //for relay ac power switching on and off
86 ODCCbits.ODCC2 = 1;
87
88 int state = 0; //for switch up and down
89 int prevstate = 0; //for switch up and down
90
91 LATCbits.LATC4=1; //turns off buzzer right away.
92
93
94 while (1) {
95
96
97 if (SWT_GetValue(0) == 0) {
98 state = DN;
99 } else state = UP;
100
101 if (state == DN && state != prevstate) { //hotplate off
102 LATCbits.LATC2 = 1;
103 LED_SetValue(1, 0);
104 prevstate = state;
105 LATCbits.LATC4=1; //TURN BUZZER OFF once popping is complete
106 }
107
108 if (state != prevstate && state == UP) { //hotplate on
109 LATCbits.LATC2 = 0;
110 xSemaphoreGive(startup);
111 LED_SetValue(1, 1);
112 prevstate = state;
113 }
114
115
116
117 vTaskDelay(300);
118 }
119 }
120
121 static void updateLCD(void *pvParameters) {
122
123 char text[20];
124 int lcdcount[3];
125
126 LCD_DisplayClear();
127 vTaskDelay(500);
128
129 while (1) {
130 xQueueReceive(micqueue, lcdcount, portMAX_DELAY);
131 sprintf(text, "Total Pops=%3d", lcdcount[0]); //showing counter 1; total pops
132 LCD_WriteStringAtPos(text, 0, 0);
133
134 sprintf(text, "Slow=%3d", lcdcount[1]); //showing counter 2; slow pops occured
135 LCD_WriteStringAtPos(text, 1, 8);
136
137 sprintf(text, "Qck=%3d", lcdcount[2]); //showing counter 3, quick pops occured
138 LCD_WriteStringAtPos(text, 1, 0);
139
140 }
141
142 }
143
144 static void readmic(void *pvParameters) {
145
146 int micread; //reads voltage value from mic
147 int count = 0; //total pops via this counter
148 int count2 = 0; //slow pops are counted via this counter
149 int count3 = 0; //fast pops are counted via this counter
150 int lcdcount[3] = {0, 0, 0}; // total counts, pause counts
151 unsigned long previoustime = xTaskGetTickCount();
152 unsigned long currenttime;
153 unsigned long previoustime3 = xTaskGetTickCount();
154 unsigned long currenttime3;
155
156 TRISCbits.TRISC4 = 0; //for buzzer setup as a sink
157 ODCCbits.ODCC4 = 1;
158
159
160 xQueueSendToBack(micqueue, lcdcount, 0);
161
162
163 while (1) {
164 xSemaphoreTake(startup, portMAX_DELAY);
165 count = 0;
166 count2 = 0;
167 count3 = 0;
168
169 while (count3 < 6 && SWT_GetValue(0) == 1) {
170
171 micread = ADC_AnalogRead(17);
172 currenttime3 = xTaskGetTickCount();
173
174 if (micread > 800) { //checking to see if the pops in between 6 seconds and if there are four
175 count++; // instances of this, let counter 2 take control
176 lcdcount[0] = count;
177
178 xQueueSendToBack(micqueue, lcdcount, 0);
179
180 currenttime3 = xTaskGetTickCount();
181 if ((currenttime3 - previoustime3) < 650) {
182 count3++;
183 lcdcount[2] = count3;
184 xQueueSendToBack(micqueue, lcdcount, 0);
185 }
186 previoustime3 = currenttime3;
187 vTaskDelay(300); //task delay in there because the pops were measured to be 3 miliseonds.
188 }
189 }
190
191 //counter2 is counting the slow pops, if there is a fast pop, the counter is reset. This counter will determine when the hotplate turns off!
192 while (count2 < 5 && SWT_GetValue(0) == 1) {
193
194 micread = ADC_AnalogRead(17);
195 currenttime = xTaskGetTickCount();
196
197 if (micread > 800) {
198 count++;
199 lcdcount[0] = count;
200 xQueueSendToBack(micqueue, lcdcount, 0);
201 if ((currenttime - previoustime) > 3000) {
202 count2++;
203 lcdcount[1] = count2;
204 xQueueSendToBack(micqueue, lcdcount, 0);
205 previoustime = currenttime;
206
207 } else if ((currenttime - previoustime) < 800) {
208 count2 = 0;
209 lcdcount[1] = count2;
210 xQueueSendToBack(micqueue, lcdcount, 0);
211 previoustime = currenttime;
212 }
213 previoustime=currenttime;
214 vTaskDelay(300);
215 }
216 if (count2 > 4) {
217 LED_SetValue(0, 1);
218 LATCbits.LATC2 = 1;
219 LATCbits.LATC4=0;
220 }
221 }
222 }
223 }
224
225 static void SetupHardware(void) {
226
227 vHardwareConfigurePerformance();
228 vHardwareUseMultiVectoredInterrupts();
229 portDISABLE_INTERRUPTS();
230
231 TRISGbits.TRISG7 = 1; // configure port G7 as an input...AN17
232 ANSELGbits.ANSG7 = 1; // configure port G7 as an analog input...AN17
233
234 DDPCONbits.JTAGEN = 0;
235
236 ADC_Init();
237 LCD_Init();
238 LED_Init();
239 SWT_Init();
240 BTN_Init();
241
242 PMCONbits.ON = 0;
243
244 }
245
246 void vApplicationMallocFailedHook(void) {
247 taskDISABLE_INTERRUPTS();
248 for (;;);
249 }
250
251 /*-----------------------------------------------------------*/
252
253 void vApplicationIdleHook(void) {
254 }
255
256 /*-----------------------------------------------------------*/
257
258 void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName) {
259 (void) pcTaskName;
260 (void) pxTask;
261 taskDISABLE_INTERRUPTS();
262 for (;;);
263 }
264
265 /*-----------------------------------------------------------*/
266
267 void vApplicationTickHook(void) {
268
269 }
270
271 /*-----------------------------------------------------------*/
272
273 void _general_exception_handler(unsigned long ulCause, unsigned long ulStatus) {
274 for (;;);
275 }
276
277 /*-----------------------------------------------------------*/
278
279 void vAssertCalled(const char * pcFile, unsigned long ulLine) {
280 volatile unsigned long ul = 0;
281
282 (void) pcFile;
283 (void) ulLine;
284
285 __asm volatile( "di");
286 {
287 while (ul == 0) {
288 portNOP();
289 }
290 }
291 __asm volatile( "ei");
292 }
Levon Markossian - 2021