STM32_11.KMU_FishSTM32G030F6P6

Water Level Protection Controller

Water level protection controller with persistent alarm memory. Implementation on STM32G030F6P6 with float sensor, AC relay and sleep mode support.

STM32G0CHALGPIOEEPROM emulationLow power
Water level controller with an STM32G030 microcontroller and actuator electronics.

Project facts

  • Repository: STM32_11.KMU_FishSTM32G030F6P6
  • Board / area: STM32G030F6P6
  • Category: Embedded controller
  • Status: Public repository

Technical description

  • The repository follows a typical STM32CubeIDE structure: Core, Drivers, .ioc file, linker script and Debug/Release configuration.
  • The firmware targets a concrete water level protection device: float sensor input, manual reset, signalling and power cut-off through the relay.
  • The schematic and code target the small STM32G030 variant: GPIO inputs handle the float sensor and user switch, while outputs drive the LED, buzzer and AC relays.
  • The STM32G030 version keeps the hardware resources limited to what is required for level sensing, signalling and actuator control.
  • The project uses a typical STM32CubeIDE configuration for STM32G0, with application code based on HAL GPIO, flash/EEPROM emulation and low-power operation.

What happens in the code

Selected key mechanisms from the project code. This is not a full repository dump, but an analysis of the parts responsible for the main firmware logic.

  • The project is a protection/actuator controller for the small STM32G030F6P6. Main elements are UserSwitch, FloatSensor, ACRelay, LED and sleep mode.
  • The code stores LowLevelWasReached and the button-press counter in emulated EEPROM through EE_WriteVariable/EE_ReadVariable.
  • On startup the firmware reads flash. If low level was detected earlier, the AC relay remains disabled after reset, so the alarm state is persistent.
  • The main loop enters Sleep Mode through WFE and wakes on GPIO events, so the controller behaves as low-power event-driven firmware.
  • UserSwitch acts as a manual alarm reset, but only when FloatSensor does not report an active low-level condition.
  • The LED signals alarm by blinking, while ACRelay is disabled when low level is detected.

Key code excerpts

The code is shortened to the parts that matter for understanding the project.

Persistent alarm state in emulated EEPROM

Core/Src/app_fish_tank.c
#define FLASH_MAGIC_16          ((uint16_t)0x7A4D)
#define VA_MAGIC                ((uint16_t)0x1001)
#define VIRTADDR_KEYCOUNT       ((uint16_t)0x1002)
#define VIRTADDR_LOWLEVEL       ((uint16_t)0x1003)

typedef struct {
    uint16_t magic;
    uint16_t keyPressedCount;
    bool lowLevelWasReached;
} DataFlash;

uint16_t VirtAddVarTab[NB_OF_VAR] = {
    VA_MAGIC,
    VIRTADDR_KEYCOUNT,
    VIRTADDR_LOWLEVEL
};

This snippet defines the persisted values: magic value, button counter and low-level flag. After power loss, the controller should not forget that an alarm state was reached.

Startup reaction to remembered low-level state

Core/Src/app_fish_tank.c
static void App_ApplyStoredRelayState(void)
{
    if (app.flashData.lowLevelWasReached) {
        App_SetRelayEnabled(false);
        Send("AC Relay: Disable 
");
        app.disableACRelay = true;
        app.goToSleepMode = false;
        app.lowLevelWasReachedPrev = true;
        app.blinkLedFromTick = HAL_GetTick();
    } else {
        App_SetRelayEnabled(true);
        Send("AC Relay: Enable 
");
        app.disableACRelay = false;
    }
}

After reset the device restores the last important state. If low level was previously reached, the AC relay is disabled, the LED is on and the device does not immediately go to sleep.

Sleep through WFE

Core/Src/app_fish_tank.c
static void App_EnterSleepIfAllowed(void)
{
    if (!app.goToSleepMode) {
        return;
    }

    Send("Going to sleep mode!
");

    __SEV();
    __WFE();
    __WFE();

    HAL_SuspendTick();
    HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFE);
    HAL_ResumeTick();
}

This is the power-saving part. The code clears the event mechanism with SEV/WFE/WFE, suspends SysTick and enters Sleep Mode until an event arrives.

Alarm reset and relay-disable conditions

Core/Src/app_fish_tank.c
static void App_HandleKeyPress(void)
{
    if (app.flashData.keyPressedCount < UINT16_MAX) {
        app.flashData.keyPressedCount++;
    }

    app.flashData.lowLevelWasReached = false;
    app.lowLevelWasReachedPrev = false;
    app.disableACRelay = false;

    Send("Key pressed
");
    Send("FloatSensor Reset !
");

    App_SetRelayEnabled(true);

    if (EE_WriteVariable(VIRTADDR_KEYCOUNT, app.flashData.keyPressedCount) != EE_OK) {
        Send("Blad zapisu KeyPressedCount!
");
    }

    if (EE_WriteVariable(VIRTADDR_LOWLEVEL, 0U) != EE_OK) {
        Send("Blad zapisu LowLevelWasReached!
");
    }

    app.goToSleepMode = true;
    HAL_Delay(100U);
}

static void App_HandleLowWaterDetected(void)
{
    if (app.lowLevelWasReachedPrev) {
        return;
    }

    Send("FloatSensor Low Level !
");

    app.lowLevelWasReachedPrev = true;
    app.flashData.lowLevelWasReached = true;

    if (!app.disableACRelay) {
        App_SetRelayEnabled(false);
        Send("Stan wody: NISKI 
");
        Send("AC Relay: Disable 
");
        app.disableACRelay = true;

        if (EE_WriteVariable(VIRTADDR_LOWLEVEL, 1U) != EE_OK) {
            Send("Blad zapisu LowLevelWasReached!
");
        }
    }

    app.blinkLedFromTick = HAL_GetTick();
    app.goToSleepMode = false;
}

The logic has two scenarios: the button resets the alarm only when the level sensor is inactive, while an active FloatSensor stores the alarm and disables the relay. This is the core protection logic.