Fabio henrique gomes de moura silva
Published © MIT

EDGE AI SENTINEL Powered by Arduino UNO Q

EDGE AI SENTINEL IS A offline‑first smart infrastructure guardian designed to deliver real‑time safety. Integration with SCADA/BMS platforms

AdvancedWork in progress293
EDGE AI SENTINEL Powered by Arduino UNO Q

Things used in this project

Hardware components

Arduino UNO Q
×1
DragonBoard 820c
96Boards DragonBoard 820c
×1
ENV III Unit with Temperature Humidity Air Pressure Sensor (SHT30+QMP6988)
M5Stack ENV III Unit with Temperature Humidity Air Pressure Sensor (SHT30+QMP6988)
×1

Software apps and online services

Arduino IDE
Arduino IDE
Visual Studio Code Extension for Arduino
Microsoft Visual Studio Code Extension for Arduino

Hand tools and fabrication machines

Enclosure made in 3d printer

Story

Read more

Custom parts and enclosures

The Project: "Skeeter-Bunker V1" This case was designed to be compact, tactical, and to house: ESP

The Project: "Skeeter-Bunker V1"

This case was designed to be compact, tactical, and to house:

ESP32-CAM (Internally mounted)

HC-SR04 (Eyes on the outside)

6mm Laser (Below the sonar)

CAD – Enclosures and custom parts

Schematics

Schematics and Circuit DiagramsEDGE AI SENTINEL

Schematics and Circuit DiagramsEDGE AI SENTINEL

Code

cpp

C/C++
// EDGE AI SENTINEL - MCU Layer
// Deterministic sensor acquisition and safety watchdogs

#include <Arduino.h>

struct SensorFrame {
  float temperature;
  float humidity;
  float noise;
  bool presence;
};

SensorFrame frame;

void setup() {
  Serial.begin(115200);
}

void loop() {
  // Simulated sensor inputs (replace with real sensors)
  frame.temperature = random(200, 350) / 10.0; // °C
  frame.humidity    = random(300, 800) / 10.0; // %
  frame.noise       = random(30, 90);           // dB
  frame.presence    = random(0, 2);             // Occupancy

  // Structured data output to Linux layer
  Serial.print("{\"temp\":");
  Serial.print(frame.temperature);
  Serial.print(",\"hum\":");
  Serial.print(frame.humidity);
  Serial.print(",\"noise\":");
  Serial.print(frame.noise);
  Serial.print(",\"presence\":");
  Serial.print(frame.presence);
  Serial.println("}");

  delay(1000);
}

New CPP

C/C++
The original design does not include a physical servo motor. However, it is possible to simulate the scanning logic via software or prepare the code so that one of the ESP32 cores processes the data as if it were in an expanded field of view.
To actually move the sensor without a motor, some makers use multiple sensor or vibration techniques, but the most efficient way to implement "scanning" via pure code on the ESP32-CAM (which is dual-core) is to create a Virtual Sampling Scan.
/*
 * SKEETER-SCAN 16 - Virtual Sweep Upgrade
 * Simula a lgica de varredura de 0 a 180 para integrao com o HUD
 */

#include <Arduino.h>

const int PIN_TRIG = 13;
const int PIN_ECHO = 12;
const int PIN_LASER = 4;

// Lgica de Varredura Virtual
int anguloAtual = 0;
int direcao = 1; // 1 para aumentar, -1 para diminuir
const int PASSO = 10; // Incremento de 10 graus por leitura

void setup() {
  Serial.begin(115200);
  pinMode(PIN_TRIG, OUTPUT);
  pinMode(PIN_ECHO, INPUT);
  pinMode(PIN_LASER, OUTPUT);
  
  Serial.println("SKEETER-SCAN 16: Modo Varredura Virtual Ativo");
}

void loop() {
  // 1. Lgica de Movimentao do ngulo (Simulada)
  // Se voc adicionar um servo no futuro, o comando 'servo.write(anguloAtual)' viria aqui.
  anguloAtual += (direcao * PASSO);

  if (anguloAtual >= 180 || anguloAtual <= 0) {
    direcao *= -1; // Inverte a direo ao chegar nos limites
  }

  // 2. Medio Ultrassnica
  digitalWrite(PIN_TRIG, LOW);
  delayMicroseconds(2);
  digitalWrite(PIN_TRIG, HIGH);
  delayMicroseconds(10);
  digitalWrite(PIN_TRIG, LOW);

  long duracao = pulseIn(PIN_ECHO, HIGH);
  int distancia = duracao * 0.034 / 2;

  // 3. Output Formatado para o Framework 16 / HUD
  // Enviamos o ngulo e a distncia para que o processamento 3D no PC mapeie o arco
  Serial.print("ANG:"); 
  Serial.print(anguloAtual);
  Serial.print("|DIST:");
  Serial.println(distancia);

  // 4. Lgica de Disparo (Laser)
  // O laser s deve disparar se o alvo estiver no "centro" ou em um ngulo de interesse
  if (distancia > 0 && distancia <= 50) {
    digitalWrite(PIN_LASER, HIGH);
    // No HUD, isso apareceria como um "Lock-on" no ngulo X
    Serial.printf("!!! ALVO DETECTADO NO ANGULO %d !!!\n", anguloAtual);
  } else {
    digitalWrite(PIN_LASER, LOW);
  }

  delay(50); // Delay menor para uma varredura mais fluida
}

Skeeter Scan in Python

Python
Skeeter Scan in Python This code transforms your laptop into a defense control station (Skeeter-Scan V3). It integrates computer vision (motion detection) with telemetry
import cv2
import numpy as np
import serial
import time
import threading
import sys

# ==========================================
# CONFIGURAES DO USURIO (AJUSTE AQUI)
# ==========================================
# IP da ESP32-CAM (veja no Monitor Serial ao iniciar o ESP32)
ESP32_IP = "192.168.1.100" 
URL_STREAM = f"http://{ESP32_IP}:81/stream"

# Porta Serial (Windows: 'COM3', 'COM4' | Linux/Mac: '/dev/ttyUSB0')
SERIAL_PORT = 'COM3' 
BAUD_RATE = 115200

# Parmetros de Deteco (Calibragem)
AREA_MINIMA = 50   # Tamanho mnimo do objeto (filtra rudo)
AREA_MAXIMA = 800  # Tamanho mximo (filtra pessoas/mos)
DISTANCIA_TIRO = 50 # Distncia mxima em cm para disparo

# ==========================================
# VARIVEIS GLOBAIS DE ESTADO
# ==========================================
telemetria = {
    "distancia": 0,
    "sistema_ativo": True,
    "laser_ativo": False,
    "lock_visual": False
}

# ==========================================
# THREAD DE COMUNICAO SERIAL
# ==========================================
def serial_comms():
    """Gerencia envio e recebimento de dados com o ESP32 em paralelo."""
    try:
        ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=0.1)
        time.sleep(2) # Tempo para estabilizar conexo
        print(f"[SERIAL] Conectado na porta {SERIAL_PORT}")
        
        while telemetria["sistema_ativo"]:
            # 1. LEITURA (Recebe dados do Sonar)
            if ser.in_waiting:
                try:
                    linha = ser.readline().decode('utf-8', errors='ignore').strip()
                    if "DIST:" in linha:
                        # Formato esperado: "ANG:0|DIST:45" ou apenas "DIST:45"
                        partes = linha.split("DIST:")
                        if len(partes) > 1:
                            telemetria["distancia"] = int(partes[1])
                except ValueError:
                    pass
            
            # 2. ESCRITA (Envia comando do Laser)
            # Protocolo: 'L' = Laser ON, 'O' = Laser OFF
            comando = b'L' if telemetria["laser_ativo"] else b'O'
            ser.write(comando)
            
            time.sleep(0.05) # Evita sobrecarga da CPU
            
        ser.close()
    except Exception as e:
        print(f"[ERRO SERIAL] Falha na conexo: {e}")
        telemetria["distancia"] = -1

# ==========================================
# VISO COMPUTACIONAL E HUD
# ==========================================
def desenhar_hud(frame, largura, altura):
    """Desenha a interface ttica sobre o vdeo."""
    centro_x, centro_y = largura // 2, altura // 2
    cor_hud = (0, 255, 0) # Verde Hacker
    cor_alerta = (0, 0, 255) # Vermelho
    
    # 1. Retcula Central
    if telemetria["laser_ativo"]:
        cv2.circle(frame, (centro_x, centro_y), 40, cor_alerta, 2)
        cv2.putText(frame, "ENGAGED", (centro_x-35, centro_y-50), cv2.FONT_HERSHEY_SIMPLEX, 0.6, cor_alerta, 2)
    else:
        cv2.circle(frame, (centro_x, centro_y), 20, cor_hud, 1)
        cv2.line(frame, (centro_x-30, centro_y), (centro_x+30, centro_y), cor_hud, 1)
        cv2.line(frame, (centro_x, centro_y-30), (centro_x, centro_y+30), cor_hud, 1)

    # 2. Painel de Dados
    cv2.rectangle(frame, (10, 10), (250, 110), (0, 0, 0), -1) # Fundo preto semi-transparente
    cv2.rectangle(frame, (10, 10), (250, 110), cor_hud, 1)
    
    cv2.putText(frame, "SKEETER-SCAN V3", (20, 35), cv2.FONT_HERSHEY_SIMPLEX, 0.6, cor_hud, 2)
    
    # Status do Sonar
    dist = telemetria["distancia"]
    txt_dist = f"SONAR: {dist} cm" if dist != -1 else "SONAR: ERRO"
    cv2.putText(frame, txt_dist, (20, 65), cv2.FONT_HERSHEY_SIMPLEX, 0.6, cor_hud, 1)
    
    # Status do Sistema
    status = "ARMED" if telemetria["lock_visual"] else "SCANNING"
    cor_sts = cor_alerta if telemetria["lock_visual"] else cor_hud
    cv2.putText(frame, f"STATUS: {status}", (20, 95), cv2.FONT_HERSHEY_SIMPLEX, 0.6, cor_sts, 2)

def main():
    # Inicia a thread serial
    t_serial = threading.Thread(target=serial_comms)
    t_serial.daemon = True
    t_serial.start()

    print(f"[VIDEO] Conectando ao stream: {URL_STREAM}")
    cap = cv2.VideoCapture(URL_STREAM)
    
    # Subtrator de fundo para detectar movimento
    fgbg = cv2.createBackgroundSubtractorMOG2(history=500, varThreshold=50, detectShadows=False)

    while True:
        ret, frame = cap.read()
        if not ret:
            print("[VIDEO] Falha ao capturar frame. Verifique o IP.")
            time.sleep(2)
            continue

        altura, largura, _ = frame.shape
        centro_x, centro_y = largura // 2, altura // 2
        
        # --- PROCESSAMENTO DE IMAGEM ---
        # Aplica mscara de movimento
        mask = fgbg.apply(frame)
        _, mask = cv2.threshold(mask, 200, 255, cv2.THRESH_BINARY)
        
        # Limpeza de rudo (eroso e dilatao)
        mask = cv2.erode(mask, None, iterations=2)
        mask = cv2.dilate(mask, None, iterations=2)
        
        contornos, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        
        telemetria["lock_visual"] = False
        alvo_no_centro = False

        for cnt in contornos:
            area = cv2.contourArea(cnt)
            
            # Filtra objetos pelo tamanho (mosquito vs ventilador vs pessoa)
            if AREA_MINIMA < area < AREA_MAXIMA:
                x, y, w, h = cv2.boundingRect(cnt)
                
                # Desenha caixa ao redor do alvo
                cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 2)
                
                # Verifica se o alvo est na "zona de tiro" (centro da tela)
                zona_tiro_x = (centro_x - 50, centro_x + 50)
                zona_tiro_y = (centro_y - 50, centro_y + 50)
                
                centro_obj_x = x + w//2
                centro_obj_y = y + h//2
                
                if (zona_tiro_x[0] < centro_obj_x < zona_tiro_x[1]) and \
                   (zona_tiro_y[0] < centro_obj_y < zona_tiro_y[1]):
                    alvo_no_centro = True
                    telemetria["lock_visual"] = True

        # --- LGICA DE DISPARO (FUSO DE SENSORES) ---
        # Dispara SE: (Alvo Visual no Centro) E (Distncia Sonar < Limite)
        dist = telemetria["distancia"]
        if alvo_no_centro and (0 < dist <= DISTANCIA_TIRO):
            telemetria["laser_ativo"] = True
        else:
            telemetria["laser_ativo"] = False

        # --- RENDERIZAO ---
        desenhar_hud(frame, largura, altura)
        
        cv2.imshow("SKEETER-SCAN 16 - CONTROL STATION", frame)

        # 'q' para sair
        if cv2.waitKey(1) & 0xFF == ord('q'):
            telemetria["sistema_ativo"] = False
            break

    cap.release()
    cv2.destroyAllWindows()

if __name__ == "__main__":
    main()

edge_ai_sentinel_edge_ai.py

Python
# EDGE AI SENTINEL - Edge AI Runtime
# Offline AI and rule-based intelligence

import json
import sys
from datetime import datetime

TEMP_LIMIT = 30.0
NOISE_LIMIT = 70

def analyze(data):
    alerts = []

    if data["temp"] > TEMP_LIMIT:
        alerts.append("THERMAL_RISK")

    if data["noise"] > NOISE_LIMIT:
        alerts.append("ACOUSTIC_ANOMALY")

    if data["presence"]:
        alerts.append("UNAUTHORIZED_PRESENCE")

    return alerts

for line in sys.stdin:
    sensor_data = json.loads(line)
    alerts = analyze(sensor_data)

    output = {
        "timestamp": datetime.utcnow().isoformat(),
        "sensors": sensor_data,
        "alerts": alerts,
        "status": "OK" if not alerts else "ALERT"
    }

    print(json.dumps(output))
    sys.stdout.flush()

Credits

Fabio henrique gomes de moura silva
2 projects • 3 followers

Comments