6- Completion

·

5 min read

Title: Combining Modules

Content: Now that we have implemented all the modules for the functionalities, it's time to combine them.

First, we need to make the machine controllable.

Create three states for the egg incubator:

  • Preparing

  • Incubating

  • Completed

In the Preparing state, the system waits until the start button is pressed, then switches to the Incubating state. After 21 days, it transitions from Incubating to Completed.

We will use a switch to cycle through the states: Preparing, Incubating, and Completed. In the main while(1) loop, we will use a switch statement to perform different actions based on the current state.

Let's start by creating the switch circuit.

Switch Circuit

We will use an interrupt and a pull-up resistor. When the switch is pressed, the voltage will go to ground, making it low active. We chose a 10kΩ resistor because values between 10kΩ and 100kΩ are commonly used. A smaller value could cause excessive current flow, burdening the MCU, while a larger value could make the circuit susceptible to noise and slow switch response. Hence, 10kΩ is a balanced choice.

GPIO Interrupt

Let's create the switch using a GPIO interrupt and name it GPIO_Control_Switch.

NVIC Configuration

Enable the interrupt in the NVIC and set its priority to 1, as the timer-related priority is currently 0.

We can write the interrupt service routine in the stm32f1xx_it.c file.

Declare a global variable that can be accessed in both main.c and stm32f1xx_it.c.

To create a global variable:

/*
* global_vars.h
* Created on: Sep 15, 2024
* Author: sam
*/

#ifndef INC_GLOBAL_VARS_H_
#define INC_GLOBAL_VARS_H_

extern int global_var_control;

#endif /* INC_GLOBAL_VARS_H_ */

Include global_variable.h in main.c and initialize the variable:

int global_var_control = 0;

Also, include global_variable.h in stm32f1xx_it.c.

In the interrupt handler, increment the variable by 1 each time the interrupt occurs, and reset it to 0 when it reaches 3:

void EXTI2_IRQHandler(void)
{
    /* USER CODE BEGIN EXTI2_IRQn 0 */
    /* USER CODE END EXTI2_IRQn 0 */
    HAL_GPIO_EXTI_IRQHandler(GPIO_Control_Switch_Pin);
    /* USER CODE BEGIN EXTI2_IRQn 1 */
    global_var_control = (global_var_control + 1) % 3;
    /* USER CODE END EXTI2_IRQn 1 */
}

Button Press

The button works correctly when pressed.

Now, we can switch between the Preparing, Incubating, and Completed modes using the switch.

However, an error related to time calculation appeared. (I forgot to take a screenshot.)

The issue is that the time continues to be calculated even in the Preparing state. The goal is for the device to be inactive during Preparing, with elapsed time always at 0 and remaining time at 21 days.

Another issue is that in the Completed state, the time information displayed incorrect values, such as progress 2267%, elapsed time 4182 days 802 hours, and time to hatch 21 days 0 hours.

I identified the problem in the calculate_time() function:

uint32_t elapsed_seconds = current_time - (*startTime);

The issue arises here. I decided to manually set elapsed_seconds based on the mode:

if (*startTime == 1814401) {
    elapsed_seconds = 0;
}

// if the status is completed
if (*startTime == 1814402) {
    elapsed_seconds = 1814400;
}

Now it works perfectly.

However, when the switch button is pressed, the state change is not immediately visible because the ssd1306_display function is already running. We need to wait for the next ssd1306_display function call to see the state change.

To address this, I created transition display functions and called them during the interrupt:

void ssd1306_Preparing_transition() {
    ssd1306_Fill(Black);
    ssd1306_SetCursor(2, 0);
    ssd1306_WriteString("Preparing", Font_11x18, White);
    ssd1306_SetCursor(2, 30);
    ssd1306_WriteString("Incubating.", Font_11x18, White);
    ssd1306_UpdateScreen();
    HAL_Delay(2000);
}

void ssd1306_Incubating_transition() {
    ssd1306_Fill(Black);
    ssd1306_SetCursor(2, 0);
    ssd1306_WriteString("Start", Font_11x18, White);
    ssd1306_SetCursor(2, 30);
    ssd1306_WriteString("Incubating.", Font_11x18, White);
    ssd1306_UpdateScreen();
    HAL_Delay(2000);
}

void ssd1306_Completing_transition() {
    ssd1306_Fill(Black);
    ssd1306_SetCursor(2, 0);
    ssd1306_WriteString("Completing", Font_11x18, White);
    ssd1306_SetCursor(2, 30);
    ssd1306_WriteString("Incubating!", Font_11x18, White);
    ssd1306_UpdateScreen();
    HAL_Delay(2000);
}

I implemented all the functionalities and discovered that the switch interrupt was triggered even without pressing the switch. I modified the code to check the switch state first and execute the interrupt action only if the switch is pressed for more than 0.2 seconds:

if (!(HAL_GPIO_ReadPin(GPIO_RELAY_GPIO_Port, GPIO_RELAY_Pin))) {
    if ((HAL_GetTick() - m_button_before_time) > 200) {
        global_var_control = (global_var_control + 1) % 3;
        switch (global_var_control) {
            case 0:
                ssd1306_Preparing_transition();
                break;
            case 1:
                ssd1306_Incubating_transition();
                break;
            case 2:
                ssd1306_Completing_transition();
                break;
            default:
                break;
        }
        m_button_before_time = HAL_GetTick();
    }
}

All functionalities are now implemented. This marks the completion of the first version of the program.

Check the program execution in the video.

Youtube Video : https://youtu.be/h34YOa3zrrA?si=fVQKkibtToqwHyWE

You can see that the AC relay does not turn on properly. This is due to the relay's structure, which I will explain again.

Relay Structure

The relay has this structure inside, and to explain it, I need to cover some basic electrical concepts.

Electricity has an interesting relationship with magnets. A changing electric field creates a magnetic field, and a changing magnetic field creates an electric field. This is called electromagnetic induction. The core in the relay is a magnet, and the coil is a wire. When current flows through the coil, it creates a changing electric field, which affects the magnetic field.

Electromagnetic Induction

As shown in the picture, there is a specific relationship between the direction of the current and the movement of the magnet. In other words, current flow can exert a force to move the magnet. In the relay, when current flows through the coil, it creates an electromagnetic field that attracts the armature. The armature, connected to a lever, moves the COM contact from NC to NO.

When current flows through the coil, it moves the armature from NC to NO, closing the circuit connected to NO.

In this case, the armature needs to be attracted, but there seems to be an issue with the coil or some foreign material between the armature and the core, preventing them from sticking together. That's why I keep tapping it to physically shake the armature and make it stick to the core.

YouTube video: https://youtu.be/h34YOa3zrrA?si=fVQKkibtToqwHyWE

GitHub repository: https://github.com/SammyLee2/EggIncubator_v.1/tree/main

Document: https://docs.google.com/spreadsheets/d/1c3TMiFB1RRYusIVPbdw1TT1HL3B3BGssj0b-G2ev1Hs/edit?usp=sharing