mohan-bee/compact-pir
Compact 3.3 V ESP8684 wireless motion/GPIO breakout board with LiPo regulation, 40 MHz crystal, U.FL antenna matching, digital PIR sensor, switched LED, decoupling, and dual 10-pin headers.
- Version
- 1.0.2
- License
- unset
- Stars
- 0
firmware/main/main.c
#include <stdio.h>
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_sleep.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "motion_logic.h"
enum { PIR_PIN = GPIO_NUM_2, LED_PIN = GPIO_NUM_7 };
static const char *TAG = "motion";
static uint64_t millis(void)
{
return (uint64_t)esp_timer_get_time() / 1000;
}
void app_main(void)
{
// Set the output latch LOW before enabling the transistor's GPIO output.
ESP_ERROR_CHECK(gpio_set_level(LED_PIN, 0));
const gpio_config_t led = {
.pin_bit_mask = 1ULL << LED_PIN,
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
ESP_ERROR_CHECK(gpio_config(&led));
const gpio_config_t pir = {
.pin_bit_mask = 1ULL << PIR_PIN,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
ESP_ERROR_CHECK(gpio_config(&pir));
ESP_ERROR_CHECK(gpio_wakeup_enable(PIR_PIN, GPIO_INTR_HIGH_LEVEL));
ESP_ERROR_CHECK(esp_sleep_enable_gpio_wakeup());
ESP_ERROR_CHECK(esp_sleep_set_console_uart_handling_mode(ESP_SLEEP_ALWAYS_FLUSH_UART));
motion_state_t state = {.boot_ms = millis()};
bool last_pir = gpio_get_level(PIR_PIN) != 0;
bool last_led = false;
ESP_LOGI(TAG, "ESP8684H4X / GPIO2 PIR / GPIO7 LED; radios not initialized");
ESP_LOGI(TAG, "LED OFF; startup settle %d s; motion hold %d s",
CONFIG_MOTION_SETTLE_SECONDS, CONFIG_MOTION_HOLD_SECONDS);
ESP_LOGI(TAG, "Initial PIR %s", last_pir ? "HIGH" : "LOW");
for (;;) {
const uint64_t now_ms = millis();
const bool pir_high = gpio_get_level(PIR_PIN) != 0;
const bool was_settled = state.settled;
const bool led_on = motion_update(&state, now_ms, pir_high,
CONFIG_MOTION_SETTLE_SECONDS * 1000U,
CONFIG_MOTION_HOLD_SECONDS * 1000U);
if (!was_settled && state.settled) {
ESP_LOGI(TAG, "Startup settle complete; motion input enabled");
}
if (pir_high != last_pir) {
ESP_LOGI(TAG, "PIR %s%s", pir_high ? "HIGH" : "LOW",
state.settled ? "" : " (ignored during settle)");
last_pir = pir_high;
}
if (led_on != last_led) {
ESP_ERROR_CHECK(gpio_set_level(LED_PIN, led_on));
ESP_LOGI(TAG, "LED %s", led_on ? "ON" : "OFF");
last_led = led_on;
}
if (state.settled && !led_on && !pir_high) {
ESP_LOGI(TAG, "Idle; light sleep until PIR HIGH");
fflush(stdout);
// Recheck after logging; HIGH-level wake also covers the entry race.
if (gpio_get_level(PIR_PIN) == 0) {
const esp_err_t result = esp_light_sleep_start();
if (result == ESP_OK) {
ESP_LOGI(TAG, "Awake; reading PIR again");
} else if (result != ESP_ERR_SLEEP_REJECT) {
ESP_ERROR_CHECK(result);
}
}
}
vTaskDelay(pdMS_TO_TICKS(20));
}
}