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:

Here is an example photo reference

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.5­3 milliseconds, which allowed the RTOS timer to be used.

Here is an example photo reference Here is an example photo reference

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.

Here is an example photo reference Here is an example photo reference

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

<h1> // set up the hardware 12 #include "BasysMX3setup.h" // configuration 13 14 #include <xc.h> 15 #include <stdio.h> 16 #include <math.h> 17 #include <dsplib_dsp.h> 18 #include <fftc.h> 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 <sys/attribs.h> 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 }</h1>

Levon Markossian - 2021