STM32_09.GardeningBlackPillSTM32F411CEU6

Garden Automation Controller

Automatyzacja ogrodowa na STM32F411CEU6. Firmware obsługuje czujniki wilgotności gleby, czujnik poziomu wody, pompę, oświetlenie roślin i konfigurację przez UART.

STM32F4CHALAutomationBlack Pill
Schemat sterownika automatyki ogrodowej na STM32F411CEU6 Black Pill z wejściami ADC i wyjściami pompy oraz LED.

Informacje

  • Repozytorium: STM32_09.GardeningBlackPillSTM32F411CEU6
  • Płytka / obszar: STM32F411CEU6 Black Pill
  • Kategoria: Automation
  • Status: Public repository

Opis techniczny

  • Aplikacja łączy pomiary z wejść analogowych i cyfrowych z decyzjami wykonawczymi: uruchomieniem pompy, blokadą przy niskim poziomie wody i sterowaniem oświetleniem.
  • Kod obsługuje dwa czujniki wilgotności gleby, czujnik światła LDR, czujnik poziomu wody, wyjście pompy oraz wyjście oświetlenia.
  • Struktura STM32CubeIDE zawiera konfigurację pinów i peryferiów w pliku .ioc.
  • Schemat zawiera zasilanie 12 V/3,3 V, Black Pill F411, tory ADC dla czujników, buzzer oraz stopnie tranzystorowe/MOSFET dla pompy i LED.
  • Firmware łączy pomiary ADC z DMA, konfigurację zapisywaną w flash i decyzje wykonawcze zależne od wilgotności, światła oraz poziomu wody.

Co dzieje się w kodzie

Wybrane najważniejsze mechanizmy z projektu.

  • main.c realizuje pełny sterownik ogrodniczy: odczyt wilgotności gleby, czujnik światła, czujnik poziomu wody, pompę i oświetlenie.
  • ADC działa przez DMA: trzy kanały trafiają do adc_buffer, a kod przepisuje próbki do osobnych buforów dla dwóch czujników wilgotności oraz LDR.
  • Konfiguracja kalibracyjna jest zapisywana w pamięci flash. Na starcie firmware sprawdza magic value i przy pierwszym uruchomieniu zapisuje wartości domyślne.
  • UART pełni rolę prostego interfejsu serwisowego. Komendy typu S1DValue, S1WValue, lightTime, runPump i lightOn pozwalają kalibrować i testować układ bez rekompilacji.
  • Logika pompy ma histerezę: start przy wilgotności poniżej progu, stop przy wilgotności powyżej progu oraz blokadę działania przy niskim poziomie wody.
  • Logika światła rozróżnia DARK, NIGHT_OR_MORNING i BRIGHT, a doświetlanie działa w oknie czasowym zamiast prostego włącz/wyłącz.
  • Zapis do Flash: dane konfiguracyjne są zapisywane do pamięci flash przy zmianie ustawień, co zapewnia trwałość konfiguracji po restarcie.
  • Sterowanie pompą: funkcja MixWater realizuje cykliczne mieszanie wody co 3 godziny z podziałem na fazy impulsu i pauzy.

Kluczowe fragmenty kodu

Kod jest skrócony do części istotnych dla zrozumienia projektu.

Konfiguracja zapisywana w flash

Core/Src/main.c
typedef struct {
  uint32_t magic;
  uint32_t Sensor1DryValue;
  uint32_t Sensor1WetValue;
  uint32_t Sensor2DryValue;
  uint32_t Sensor2WetValue;
  uint32_t GetMoistureAvg_TimeLenghtDefault;
  uint32_t GetMoistureAvg_TimeLenghtFast;
  uint32_t StartPumpWhenMoistureLower;
  uint32_t StopPumpWhenMoistureHigher;
  uint32_t lightTime;
} DataFlash;

if (dane.magic != FLASH_MAGIC) {
  dane.Sensor1DryValue = 2700;
  dane.Sensor1WetValue = 2020;
  dane.StartPumpWhenMoistureLower = 40;
  dane.StopPumpWhenMoistureHigher = 75;
  dane.lightTime = 3600 * 1000 * 10;
  memoryPageErase(0);
  Flash_WriteStruct(flash_address, &dane, sizeof(DataFlash));
}

Ten kod robi z projektu realne urządzenie, a nie jednorazowy test. Kalibracja czujników, progi pompy i czas świecenia są pamiętane po restarcie. Magic value rozpoznaje, czy flash zawiera poprawne dane.

Interfejs UART do kalibracji i testów

Core/Src/main.c
while (USART_TakeLine(line, sizeof(line))) {
  matched |= parse_kv_int(line, "S1DValue", &dane.Sensor1DryValue);
  matched |= parse_kv_int(line, "S1WValue", &dane.Sensor1WetValue);
  matched |= parse_kv_int(line, "StartPumpWhenMoistureLower",
                          &dane.StartPumpWhenMoistureLower);
  matched |= parse_kv_int(line, "StopPumpWhenMoistureHigher",
                          &dane.StopPumpWhenMoistureHigher);

  if (matched) {
    memoryPageErase(0);
    Flash_WriteStruct(flash_address, &dane, sizeof(DataFlash));
  }
}

Firmware przyjmuje komendy tekstowe w formacie klucz:wartość. Dzięki temu można ustawić kalibrację suchej/mokrej gleby i progi pracy pompy z terminala UART, bez zmiany kodu i ponownego flashowania.

Sterowanie pompą z histerezą i zabezpieczeniem poziomu wody

Core/Src/main.c
MoistureGeneral =
  (MoistureHumidityPercent_Dev_1 + MoistureHumidityPercent_Dev_2) / 2;

if (WaterSensor1State != GPIO_PIN_SET) {
  if (MoistureGeneral <= dane.StartPumpWhenMoistureLower &&
      PumpIsRunnign == false) {
    HAL_GPIO_WritePin(PIN_PB10_Pump_GPIO_Port, PIN_PB10_Pump_Pin, GPIO_PIN_SET);
    PumpIsRunnign = true;
  }
}

if (MoistureGeneral >= dane.StopPumpWhenMoistureHigher &&
    PumpIsRunnign == true) {
  HAL_GPIO_WritePin(PIN_PB10_Pump_GPIO_Port, PIN_PB10_Pump_Pin, GPIO_PIN_RESET);
  PumpIsRunnign = false;
}

Kod liczy średnią z dwóch czujników wilgotności i steruje pompą dopiero na podstawie wartości ogólnej. Dwa różne progi tworzą histerezę: pompa nie klika co chwilę przy granicznej wilgotności. Czujnik poziomu wody blokuje start pompy.

Logika doświetlania roślin

Core/Src/main.c
if (LightSensorPercent <= DARK)
  LightSensor = DARK;
else if (LightSensorPercent > DARK && LightSensorPercent < BRIGHT)
  LightSensor = NIGHT_OR_MORNING;
else if (LightSensorPercent > BRIGHT)
  LightSensor = BRIGHT;

switch (LightSensor) {
  case NIGHT_OR_MORNING:
    if (LightTimerEnabled == false && waitFordark == false) {
      GetLightTime_FromTime = TimeNOW;
      LightTimerEnabled = true;
    }
    HAL_GPIO_WritePin(PIN_PB11_Light_GPIO_Port, PIN_PB11_Light_Pin, GPIO_PIN_SET);
    break;

  case BRIGHT:
    HAL_GPIO_WritePin(PIN_PB11_Light_GPIO_Port, PIN_PB11_Light_Pin, GPIO_PIN_RESET);
    break;
}

Oświetlenie nie jest sterowane tylko prostym progiem jasności. Kod rozpoznaje zakres przejściowy, uruchamia okno czasowe doświetlania i wyłącza światło przy zbyt dużej jasności.

Funkcja mieszania wody

Core/Src/main.c
MixWater_State MixWaterState = MIX_WATER_IDLE;
const uint32_t MixWaterPulseTime = 5000;
const uint32_t MixWaterPauseTime = 5000;

switch (MixWaterState)
{
  case MIX_WATER_PULSE_1_ON:
    // Turn pump on for MixWaterPulseTime
  case MIX_WATER_PULSE_1_OFF:
    // Turn pump off for MixWaterPauseTime  
  case MIX_WATER_PULSE_2_ON:
    // Turn pump on for MixWaterPulseTime again
}

Funkcja mieszania wody realizuje cykliczne mieszanie wody co 3 godziny z podziałem na fazy impulsu i pauzy. Umożliwia równomierne rozprowadzenie nawozów w systemie.