Skip to content
IRC-CodingIRC-Coding
основы IoTвстроенные системыдатчикимикроконтроллерыRaspberry PiArduinoEdge Computing

IoT и встроенные системы: основы и практика

Основы IoT и встроенных систем: датчики, микроконтроллеры, Raspberry Pi, Arduino, Edge Computing, MQTT, CoAP.

S

schutzgeist

28 min read
IoT и встроенные системы: основы и практика

IoT и встраиваемые системы: основы с примерами сенсоров, микроконтроллеров, Raspberry Pi, Arduino и Edge Computing

Этот материал представляет собой полное введение в основы IoT и встраиваемых систем — включая сенсоры, микроконтроллеры, Raspberry Pi, Arduino и Edge Computing с практическими примерами.

Вкратце

IoT и встраиваемые системы это специализированные компьютеры, которые соединяют физические устройства с интернетом и обрабатывают данные в реальном времени.

Техническое описание

IoT (Internet of Things) это сеть физических устройств, которые собирают данные через сенсоры, обрабатывают их и обмениваются информацией через интернет.

Ключевые компоненты:

Встраиваемые системы

  • Микроконтроллеры: ARM Cortex-M, AVR, PIC
  • Микропроцессоры: ARM Cortex-A, x86, RISC-V
  • Операционные системы: Embedded Linux, RTOS, FreeRTOS
  • Память: Flash, EEPROM, RAM, SD-карты

Сенсоры и исполнительные механизмы

  • Датчики окружающей среды: температура, влажность, давление
  • Датчики движения: акселерометр, гироскоп, магнитометр
  • Оптические датчики: камера, инфракрасное излучение, ультразвук
  • Исполнительные механизмы: моторы, серво, реле, светодиоды

Протоколы связи

  • Беспроводные: WiFi, Bluetooth, LoRaWAN, Zigbee
  • Проводные: I2C, SPI, UART, CAN, Ethernet
  • IoT-протоколы: MQTT, CoAP, HTTP/REST, WebSocket
  • Edge-протоколы: gRPC, Protocol Buffers

Edge Computing

  • Обработка данных: локальная обработка вместо облака
  • Оптимизация задержки: обработка в реальном времени
  • Эффективность полосы пропускания: сокращение передачи данных
  • Автономное функционирование: работа без интернета

Ключевые моменты

  • IoT: Internet of Things, сетевые физические устройства
  • Встраиваемые системы: специализированные компьютерные системы
  • Сенсоры: температурные, движения, оптические датчики
  • Микроконтроллеры: ARM Cortex-M, AVR, PIC
  • Raspberry Pi: одноплатный компьютер с Linux
  • Arduino: открытая платформа микроконтроллеров
  • Edge Computing: локальная обработка данных
  • RTOS: Real-Time Operating System
  • MQTT: Message Queuing Telemetry Transport
  • Современная разработка встраиваемых систем и IoT

Основные компоненты

  1. Оборудование: микроконтроллеры, процессоры, сенсоры
  2. Встроенное ПО: низкоуровневое ПО, драйверы, загрузчик
  3. Операционные системы: Embedded Linux, RTOS, FreeRTOS
  4. Коммуникация: протоколы, сети, безопасность
  5. Edge Computing: локальная обработка, аналитика
  6. Интеграция с облаком: передача данных, хранилище
  7. Управление питанием: энергоэффективность, управление батареей
  8. Безопасность: шифрование, аутентификация, обновления

Практические примеры

1. Узел IoT датчика Arduino с MQTT

#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <ArduinoJson.h>
#include <NTPClient.h>
#include <WiFiUdp.h>

// WiFi configuration
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

// MQTT configuration
const char* mqtt_server = "mqtt.example.com";
const int mqtt_port = 1883;
const char* mqtt_username = "your_username";
const char* mqtt_password = "your_password";
const char* client_id = "arduino_sensor_01";

// Sensor configuration
#define DHT_PIN 4
#define DHT_TYPE DHT22
#define LIGHT_PIN A0
#define MOTION_PIN 2
#define BUZZER_PIN 5
#define LED_PIN 13

// Topics
const char* temp_topic = "iot/sensors/temperature";
const char* humidity_topic = "iot/sensors/humidity";
const char* light_topic = "iot/sensors/light";
const char* motion_topic = "iot/sensors/motion";
const char* status_topic = "iot/sensors/status";
const char* control_topic = "iot/sensors/control";

// Timing
unsigned long lastSensorRead = 0;
const long sensorInterval = 5000; // 5 seconds
unsigned long lastStatusUpdate = 0;
const long statusInterval = 60000; // 1 minute

// Global objects
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
DHT dht(DHT_PIN, DHT_TYPE);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org");

// Sensor data structure
struct SensorData {
  float temperature;
  float humidity;
  int lightLevel;
  bool motionDetected;
  unsigned long timestamp;
};

// Device status
struct DeviceStatus {
  bool wifiConnected;
  bool mqttConnected;
  float batteryLevel;
  int uptime;
  String firmwareVersion;
};

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(10);
  
  Serial.println("IoT Sensor Node starting...");
  
  // Initialize pins
  pinMode(LED_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(MOTION_PIN, INPUT_PULLUP);
  
  // Initialize sensors
  dht.begin();
  
  // Initialize NTP client
  timeClient.begin();
  timeClient.setTimeOffset(3600); // UTC+1
  
  // Connect to WiFi
  connectWiFi();
  
  // Connect to MQTT
  connectMQTT();
  
  // Initialize time
  timeClient.update();
  
  Serial.println("IoT Sensor Node ready!");
  blinkLED(3, 200);
}

void loop() {
  // Handle WiFi connection
  if (WiFi.status() != WL_CONNECTED) {
    connectWiFi();
  }
  
  // Handle MQTT connection
  if (!mqttClient.connected()) {
    connectMQTT();
  }
  
  // Update NTP time
  timeClient.update();
  
  // Read sensors periodically
  if (millis() - lastSensorRead >= sensorInterval) {
    readAndPublishSensors();
    lastSensorRead = millis();
  }
  
  // Update status periodically
  if (millis() - lastStatusUpdate >= statusInterval) {
    publishStatus();
    lastStatusUpdate = millis();
  }
  
  // Handle MQTT messages
  mqttClient.loop();
  
  // Check for motion interrupt
  checkMotion();
  
  delay(100);
}

void connectWiFi() {
  Serial.println("Connecting to WiFi...");
  
  WiFi.begin(ssid, password);
  
  int attempts = 0;
  while (WiFi.status() != WL_CONNECTED && attempts < 30) {
    delay(500);
    Serial.print(".");
    attempts++;
  }
  
  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\nWiFi connected!");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    digitalWrite(LED_PIN, HIGH);
  } else {
    Serial.println("\nFailed to connect to WiFi");
    digitalWrite(LED_PIN, LOW);
  }
}

void connectMQTT() {
  Serial.println("Connecting to MQTT...");
  
  mqttClient.setServer(mqtt_server, mqtt_port);
  mqttClient.setCallback(mqttCallback);
  
  int attempts = 0;
  while (!mqttClient.connected() && attempts < 10) {
    Serial.print("Attempting MQTT connection...");
    
    if (mqttClient.connect(client_id, mqtt_username, mqtt_password)) {
      Serial.println("connected!");
      
      // Subscribe to control topic
      mqttClient.subscribe(control_topic);
      
      // Publish connection message
      publishConnectionStatus(true);
      
    } else {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
      attempts++;
    }
  }
  
  if (!mqttClient.connected()) {
    Serial.println("Failed to connect to MQTT");
  }
}

void mqttCallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  
  String message = "";
  for (int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  Serial.println(message);
  
  // Parse JSON message
  DynamicJsonDocument doc(1024);
  DeserializationError error = deserializeJson(doc, message);
  
  if (error) {
    Serial.println("Failed to parse JSON");
    return;
  }
  
  // Handle control commands
  if (String(topic) == control_topic) {
    handleControlCommand(doc);
  }
}

void handleControlCommand(JsonDocument& command) {
  String cmd = command["command"];
  
  if (cmd == "buzz") {
    int duration = command["duration"] | 1000;
    buzz(duration);
  } else if (cmd == "led") {
    bool state = command["state"];
    digitalWrite(LED_PIN, state ? HIGH : LOW);
  } else if (cmd == "restart") {
    Serial.println("Restarting device...");
    ESP.restart();
  } else if (cmd == "sleep") {
    int duration = command["duration"] | 10;
    Serial.println("Going to sleep for " + String(duration) + " seconds");
    ESP.deepSleep(duration * 1000000);
  }
}

void readAndPublishSensors() {
  SensorData data;
  
  // Read temperature and humidity
  data.temperature = dht.readTemperature();
  data.humidity = dht.readHumidity();
  
  // Read light level
  data.lightLevel = analogRead(LIGHT_PIN);
  
  // Read motion sensor
  data.motionDetected = digitalRead(MOTION_PIN) == HIGH;
  
  // Get timestamp
  data.timestamp = timeClient.getEpochTime();
  
  // Check for valid readings
  if (isnan(data.temperature) || isnan(data.humidity)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }
  
  // Publish sensor data
  publishSensorData(data);
  
  // Print sensor data
  Serial.println("Sensor Data:");
  Serial.print("Temperature: ");
  Serial.print(data.temperature);
  Serial.println("°C");
  Serial.print("Humidity: ");
  Serial.print(data.humidity);
  Serial.println("%");
  Serial.print("Light: ");
  Serial.println(data.lightLevel);
  Serial.print("Motion: ");
  Serial.println(data.motionDetected ? "Yes" : "No");
  Serial.print("Timestamp: ");
  Serial.println(data.timestamp);
}

void publishSensorData(SensorData& data) {
  // Create JSON document
  DynamicJsonDocument doc(1024);
  
  doc["device_id"] = client_id;
  doc["timestamp"] = data.timestamp;
  doc["temperature"] = data.temperature;
  doc["humidity"] = data.humidity;
  doc["light_level"] = data.lightLevel;
  doc["motion_detected"] = data.motionDetected;
  
  // Serialize JSON
  String jsonStr;
  serializeJson(doc, jsonStr);
  
  // Publish to topics
  mqttClient.publish(temp_topic, String(data.temperature).c_str());
  mqttClient.publish(humidity_topic, String(data.humidity).c_str());
  mqttClient.publish(light_topic, String(data.lightLevel).c_str());
  mqttClient.publish(motion_topic, String(data.motionDetected).c_str());
  
  // Publish combined data
  mqttClient.publish("iot/sensors/combined", jsonStr.c_str());
}

void publishStatus() {
  DeviceStatus status;
  
  status.wifiConnected = (WiFi.status() == WL_CONNECTED);
  status.mqttConnected = mqttClient.connected();
  status.batteryLevel = getBatteryLevel();
  status.uptime = millis() / 1000;
  status.firmwareVersion = "1.0.0";
  
  // Create JSON document
  DynamicJsonDocument doc(1024);
  
  doc["device_id"] = client_id;
  doc["timestamp"] = timeClient.getEpochTime();
  doc["wifi_connected"] = status.wifiConnected;
  doc["mqtt_connected"] = status.mqttConnected;
  doc["battery_level"] = status.batteryLevel;
  doc["uptime"] = status.uptime;
  doc["firmware_version"] = status.firmwareVersion;
  doc["free_heap"] = ESP.getFreeHeap();
  doc["cpu_freq"] = ESP.getCpuFreqMHz();
  
  // Serialize JSON
  String jsonStr;
  serializeJson(doc, jsonStr);
  
  // Publish status
  mqttClient.publish(status_topic, jsonStr.c_str());
}

void publishConnectionStatus(bool connected) {
  DynamicJsonDocument doc(512);
  
  doc["device_id"] = client_id;
  doc["timestamp"] = timeClient.getEpochTime();
  doc["connected"] = connected;
  doc["ip_address"] = WiFi.localIP().toString();
  doc["mac_address"] = WiFi.macAddress();
  
  String jsonStr;
  serializeJson(doc, jsonStr);
  
  mqttClient.publish("iot/sensors/connection", jsonStr.c_str());
}

void checkMotion() {
  static bool lastMotionState = false;
  bool currentMotionState = digitalRead(MOTION_PIN) == HIGH;
  
  if (currentMotionState && !lastMotionState) {
    // Motion detected
    Serial.println("Motion detected!");
    
    // Immediate motion notification
    DynamicJsonDocument doc(256);
    doc["device_id"] = client_id;
    doc["timestamp"] = timeClient.getEpochTime();
    doc["motion_detected"] = true;
    doc["alert_type"] = "motion";
    
    String jsonStr;
    serializeJson(doc, jsonStr);
    
    mqttClient.publish("iot/sensors/alert", jsonStr.c_str());
    
    // Alert buzzer
    buzz(200);
  }
  
  lastMotionState = currentMotionState;
}

float getBatteryLevel() {
  // Read battery voltage (assuming voltage divider)
  int rawValue = analogRead(A0);
  float voltage = rawValue * (3.3 / 4096.0) * 2.0; // Voltage divider factor
  
  // Convert to percentage (assuming 3.0V to 4.2V range)
  float percentage = ((voltage - 3.0) / 1.2) * 100.0;
  percentage = constrain(percentage, 0.0, 100.0);
  
  return percentage;
}

void buzz(int duration) {
  tone(BUZZER_PIN, 1000, duration);
}

void blinkLED(int times, int delayMs) {
  for (int i = 0; i < times; i++) {
    digitalWrite(LED_PIN, HIGH);
    delay(delayMs);
    digitalWrite(LED_PIN, LOW);
    delay(delayMs);
  }
}

// OTA Update functionality
void checkForOTAUpdate() {
  // This would implement OTA update checking
  // For simplicity, just print status
  Serial.println("Checking for OTA updates...");
}

// Power management
void enterDeepSleep(int seconds) {
  Serial.println("Entering deep sleep...");
  ESP.deepSleep(seconds * 1000000);
}

void wakeFromDeepSleep() {
  Serial.println("Waking from deep sleep...");
  // Reinitialize peripherals after wake
}

2. Обработка данных на краю с Raspberry Pi на Python

import time
import json
import threading
import queue
import subprocess
import os
import signal
import sys
from datetime import datetime
import logging
import sqlite3
import paho.mqtt.client as mqtt
import picamera
import RPi.GPIO as GPIO
import Adafruit_DHT
import board
import busio
import adafruit_ads1x15.ads1115 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
import numpy as np
import cv2
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing import image
import tensorflow as tf

class IoTEdgeGateway:
    def __init__(self, config_file='config.json'):
        """Initialize IoT Edge Gateway"""
        self.config = self.load_config(config_file)
        self.setup_logging()
        self.setup_gpio()
        self.setup_sensors()
        self.setup_camera()
        self.setup_mqtt()
        self.setup_database()
        self.setup_ml_models()
        
        # Data queues
        self.sensor_queue = queue.Queue()
        self.image_queue = queue.Queue()
        self.control_queue = queue.Queue()
        
        # Runtime state
        self.running = True
        self.threads = []
        
        # Register signal handlers
        signal.signal(signal.SIGINT, self.signal_handler)
        signal.signal(signal.SIGTERM, self.signal_handler)
        
        self.logger.info("IoT Edge Gateway initialized")
    
    def load_config(self, config_file):
        """Load configuration from JSON file"""
        try:
            with open(config_file, 'r') as f:
                return json.load(f)
        except FileNotFoundError:
            self.logger.error(f"Config file {config_file} not found")
            return self.get_default_config()
    
    def get_default_config(self):
        """Get default configuration"""
        return {
            "mqtt": {
                "broker": "localhost",
                "port": 1883,
                "username": "",
                "password": "",
                "client_id": "edge_gateway_01"
            },
            "sensors": {
                "dht_pin": 4,
                "light_pin": 18,
                "motion_pin": 24,
                "ads1115_address": 0x48
            },
            "camera": {
                "resolution": (640, 480),
                "framerate": 30,
                "rotation": 0
            },
            "database": {
                "path": "iot_data.db"
            },
            "ml_models": {
                "object_detection": "models/object_detection.h5",
                "anomaly_detection": "models/anomaly_detection.h5"
            },
            "processing": {
                "sensor_interval": 5,
                "image_interval": 10,
                "batch_size": 32
            }
        }
    
    def setup_logging(self):
        """Setup logging configuration"""
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
            handlers=[
                logging.FileHandler('edge_gateway.log'),
                logging.StreamHandler(sys.stdout)
            ]
        )
        self.logger = logging.getLogger(__name__)
    
    def setup_gpio(self):
        """Setup GPIO pins"""
        GPIO.setmode(GPIO.BCM)
        GPIO.setwarnings(False)
        
        # Setup pins
        self.dht_pin = self.config['sensors']['dht_pin']
        self.light_pin = self.config['sensors']['light_pin']
        self.motion_pin = self.config['sensors']['motion_pin']
        
        # Setup motion pin as input with pull-up
        GPIO.setup(self.motion_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        
        # Add interrupt for motion detection
        GPIO.add_event_detect(self.motion_pin, GPIO.RISING, 
                           callback=self.motion_detected, bouncetime=300)
        
        self.logger.info("GPIO setup completed")
    
    def setup_sensors(self):
        """Setup sensor interfaces"""
        # DHT22 sensor
        self.dht_sensor = Adafruit_DHT.DHT22
        
        # ADS1115 ADC for analog sensors
        i2c = busio.I2C(board.SCL, board.SDA)
        self.ads = ADS.ADS1115(i2c)
        self.ads.gain = 1  # +/- 4.096V
        
        # Analog channels
        self.light_channel = AnalogIn(self.ads, ADS.P0)
        self.temp_channel = AnalogIn(self.ads, ADS.P1)
        self.humidity_channel = AnalogIn(self.ads, ADS.P2)
        
        self.logger.info("Sensors setup completed")
    
    def setup_camera(self):
        """Setup camera interface"""
        try:
            self.camera = picamera.PiCamera()
            self.camera.resolution = tuple(self.config['camera']['resolution'])
            self.camera.framerate = self.config['camera']['framerate']
            self.camera.rotation = self.config['camera']['rotation']
            
            # Camera warm-up
            time.sleep(2)
            
            self.logger.info("Camera setup completed")
        except Exception as e:
            self.logger.error(f"Camera setup failed: {e}")
            self.camera = None
    
    def setup_mqtt(self):
        """Setup MQTT client"""
        self.mqtt_client = mqtt.Client(self.config['mqtt']['client_id'])
        
        # Set callbacks
        self.mqtt_client.on_connect = self.mqtt_on_connect
        self.mqtt_client.on_message = self.mqtt_on_message
        self.mqtt_client.on_disconnect = self.mqtt_on_disconnect
        
        # Set credentials if provided
        if self.config['mqtt']['username']:
            self.mqtt_client.username_pw_set(
                self.config['mqtt']['username'],
                self.config['mqtt']['password']
            )
        
        # Connect to broker
        try:
            self.mqtt_client.connect(
                self.config['mqtt']['broker'],
                self.config['mqtt']['port'],
                60
            )
            self.mqtt_client.loop_start()
            self.logger.info("MQTT client connected")
        except Exception as e:
            self.logger.error(f"MQTT connection failed: {e}")
    
    def setup_database(self):
        """Setup SQLite database"""
        self.db_path = self.config['database']['path']
        self.init_database()
        self.logger.info("Database setup completed")
    
    def init_database(self):
        """Initialize database tables"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # Sensor data table
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS sensor_data (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
                sensor_type TEXT,
                sensor_id TEXT,
                value REAL,
                unit TEXT,
                processed BOOLEAN DEFAULT FALSE
            )
        ''')
        
        # Image data table
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS image_data (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
                filename TEXT,
                objects_detected TEXT,
                confidence REAL,
                processed BOOLEAN DEFAULT FALSE
            )
        ''')
        
        # System status table
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS system_status (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
                cpu_usage REAL,
                memory_usage REAL,
                disk_usage REAL,
                temperature REAL,
                uptime INTEGER
            )
        ''')
        
        conn.commit()
        conn.close()
    
    def setup_ml_models(self):
        """Setup machine learning models"""
        self.models = {}
        
        try:
            # Load object detection model
            if os.path.exists(self.config['ml_models']['object_detection']):
                self.models['object_detection'] = load_model(
                    self.config['ml_models']['object_detection']
                )
                self.logger.info("Object detection model loaded")
            
            # Load anomaly detection model
            if os.path.exists(self.config['ml_models']['anomaly_detection']):
                self.models['anomaly_detection'] = load_model(
                    self.config['ml_models']['anomaly_detection']
                )
                self.logger.info("Anomaly detection model loaded")
                
        except Exception as e:
            self.logger.error(f"ML model loading failed: {e}")
    
    def mqtt_on_connect(self, client, userdata, flags, rc):
        """MQTT connection callback"""
        if rc == 0:
            self.logger.info("Connected to MQTT broker")
            # Subscribe to control topics
            client.subscribe("edge/gateway/control")
            client.subscribe("edge/sensors/+/control")
        else:
            self.logger.error(f"MQTT connection failed with code {rc}")
    
    def mqtt_on_message(self, client, userdata, msg):
        """MQTT message callback"""
        try:
            topic = msg.topic
            payload = json.loads(msg.payload.decode())
            
            self.logger.info(f"Received message on {topic}: {payload}")
            
            # Handle control messages
            if "control" in topic:
                self.control_queue.put((topic, payload))
                
        except Exception as e:
            self.logger.error(f"MQTT message processing failed: {e}")
    
    def mqtt_on_disconnect(self, client, userdata, rc):
        """MQTT disconnection callback"""
        self.logger.warning(f"MQTT disconnected with code {rc}")
    
    def motion_detected(self, channel):
        """Motion detection interrupt handler"""
        timestamp = datetime.now().isoformat()
        
        # Publish motion alert
        alert = {
            "device_id": self.config['mqtt']['client_id'],
            "timestamp": timestamp,
            "alert_type": "motion",
            "location": "entrance"
        }
        
        self.mqtt_client.publish("edge/alerts/motion", json.dumps(alert))
        
        # Trigger image capture
        if self.camera:
            self.image_queue.put(("motion", timestamp))
        
        self.logger.info("Motion detected and alert published")
    
    def read_dht_sensor(self):
        """Read DHT22 sensor data"""
        humidity, temperature = Adafruit_DHT.read_retry(
            self.dht_sensor, self.dht_pin
        )
        
        if humidity is not None and temperature is not None:
            return {
                "temperature": temperature,
                "humidity": humidity,
                "timestamp": datetime.now().isoformat()
            }
        else:
            self.logger.error("Failed to read DHT sensor")
            return None
    
    def read_analog_sensors(self):
        """Read analog sensor data"""
        try:
            # Read light sensor
            light_voltage = self.light_channel.voltage
            light_level = (light_voltage / 3.3) * 100  # Convert to percentage
            
            # Read temperature sensor (if using analog temp sensor)
            temp_voltage = self.temp_channel.voltage
            # Convert voltage to temperature (example conversion)
            analog_temp = (temp_voltage - 0.5) * 100  # LM35 conversion
            
            # Read humidity sensor (if using analog humidity sensor)
            humidity_voltage = self.humidity_channel.voltage
            analog_humidity = (humidity_voltage / 3.3) * 100
            
            return {
                "light_level": light_level,
                "analog_temperature": analog_temp,
                "analog_humidity": analog_humidity,
                "timestamp": datetime.now().isoformat()
            }
        except Exception as e:
            self.logger.error(f"Analog sensor reading failed: {e}")
            return None
    
    def capture_image(self, trigger="scheduled"):
        """Capture image from camera"""
        if not self.camera:
            return None
        
        try:
            timestamp = datetime.now().isoformat()
            filename = f"images/capture_{timestamp.replace(':', '-')}.jpg"
            
            # Capture image
            self.camera.capture(filename)
            
            # Process image with ML models
            processed_image = self.process_image(filename)
            
            return {
                "filename": filename,
                "timestamp": timestamp,
                "trigger": trigger,
                "processed": processed_image
            }
        except Exception as e:
            self.logger.error(f"Image capture failed: {e}")
            return None
    
    def process_image(self, image_path):
        """Process image with ML models"""
        try:
            # Load and preprocess image
            img = image.load_img(image_path, target_size=(224, 224))
            img_array = image.img_to_array(img)
            img_array = np.expand_dims(img_array, axis=0)
            img_array = img_array / 255.0
            
            result = {}
            
            # Object detection
            if 'object_detection' in self.models:
                predictions = self.models['object_detection'].predict(img_array)
                result['objects'] = self.decode_predictions(predictions)
            
            # Anomaly detection
            if 'anomaly_detection' in self.models:
                anomaly_score = self.models['anomaly_detection'].predict(img_array)[0][0]
                result['anomaly_score'] = float(anomaly_score)
                result['is_anomaly'] = anomaly_score > 0.5
            
            return result
            
        except Exception as e:
            self.logger.error(f"Image processing failed: {e}")
            return None
    
    def decode_predictions(self, predictions):
        """Decode ML model predictions"""
        # This would depend on the specific model
        # Example implementation
        class_names = ['person', 'car', 'bicycle', 'dog', 'cat']
        
        # Get top predictions
        top_indices = np.argsort(predictions[0])[-5:][::-1]
        results = []
        
        for i, index in enumerate(top_indices):
            results.append({
                "class": class_names[index],
                "confidence": float(predictions[0][index]),
                "rank": i + 1
            })
        
        return results
    
    def store_sensor_data(self, sensor_data):
        """Store sensor data in database"""
        try:
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()
            
            for sensor_type, value in sensor_data.items():
                if sensor_type == "timestamp":
                    continue
                
                cursor.execute('''
                    INSERT INTO sensor_data (sensor_type, sensor_id, value, unit)
                    VALUES (?, ?, ?, ?)
                ''', (sensor_type, "gateway_01", value, self.get_unit(sensor_type)))
            
            conn.commit()
            conn.close()
            
        except Exception as e:
            self.logger.error(f"Database storage failed: {e}")
    
    def get_unit(self, sensor_type):
        """Get unit for sensor type"""
        units = {
            "temperature": "°C",
            "humidity": "%",
            "light_level": "%",
            "analog_temperature": "°C",
            "analog_humidity": "%"
        }
        return units.get(sensor_type, "")
    
    def get_system_status(self):
        """Get system status information"""
        try:
            # CPU usage
            cpu_usage = psutil.cpu_percent()
            
            # Memory usage
            memory = psutil.virtual_memory()
            memory_usage = memory.percent
            
            # Disk usage
            disk = psutil.disk_usage('/')
            disk_usage = disk.percent
            
            # CPU temperature
            try:
                temp = subprocess.check_output(['vcgencmd', 'measure_temp'])
                cpu_temp = float(temp.decode().split('=')[1].split("'")[0])
            except:
                cpu_temp = 0.0
            
            # Uptime
            uptime = time.time() - psutil.boot_time()
            
            return {
                "cpu_usage": cpu_usage,
                "memory_usage": memory_usage,
                "disk_usage": disk_usage,
                "temperature": cpu_temp,
                "uptime": uptime,
                "timestamp": datetime.now().isoformat()
            }
            
        except Exception as e:
            self.logger.error(f"System status check failed: {e}")
            return None
    
    def sensor_worker(self):
        """Worker thread for sensor data collection"""
        self.logger.info("Sensor worker started")
        
        while self.running:
            try:
                # Read DHT sensor
                dht_data = self.read_dht_sensor()
                if dht_data:
                    self.sensor_queue.put(("dht", dht_data))
                
                # Read analog sensors
                analog_data = self.read_analog_sensors()
                if analog_data:
                    self.sensor_queue.put(("analog", analog_data))
                
                # Get system status
                system_status = self.get_system_status()
                if system_status:
                    self.sensor_queue.put(("system", system_status))
                
                # Sleep for interval
                time.sleep(self.config['processing']['sensor_interval'])
                
            except Exception as e:
                self.logger.error(f"Sensor worker error: {e}")
                time.sleep(1)
    
    def image_worker(self):
        """Worker thread for image processing"""
        self.logger.info("Image worker started")
        
        while self.running:
            try:
                # Get image capture request
                if not self.image_queue.empty():
                    trigger, timestamp = self.image_queue.get(timeout=1)
                    
                    # Capture image
                    image_data = self.capture_image(trigger)
                    if image_data:
                        # Publish image data
                        self.mqtt_client.publish(
                            "edge/gateway/images",
                            json.dumps(image_data)
                        )
                        
                        # Store in database
                        self.store_image_data(image_data)
                
                # Periodic image capture
                else:
                    image_data = self.capture_image("scheduled")
                    if image_data:
                        self.mqtt_client.publish(
                            "edge/gateway/images",
                            json.dumps(image_data)
                        )
                        self.store_image_data(image_data)
                
                # Sleep for interval
                time.sleep(self.config['processing']['image_interval'])
                
            except queue.Empty:
                continue
            except Exception as e:
                self.logger.error(f"Image worker error: {e}")
                time.sleep(1)
    
    def control_worker(self):
        """Worker thread for control commands"""
        self.logger.info("Control worker started")
        
        while self.running:
            try:
                # Get control command
                topic, command = self.control_queue.get(timeout=1)
                
                self.logger.info(f"Processing control command: {command}")
                
                # Handle commands
                if command.get("command") == "restart":
                    self.restart_system()
                elif command.get("command") == "shutdown":
                    self.shutdown_system()
                elif command.get("command") == "capture_image":
                    self.image_queue.put(("manual", datetime.now().isoformat()))
                elif command.get("command") == "update_config":
                    self.update_config(command.get("config", {}))
                
            except queue.Empty:
                continue
            except Exception as e:
                self.logger.error(f"Control worker error: {e}")
                time.sleep(1)
    
    def data_processor(self):
        """Worker thread for data processing and publishing"""
        self.logger.info("Data processor started")
        
        while self.running:
            try:
                # Process sensor data
                if not self.sensor_queue.empty():
                    sensor_type, data = self.sensor_queue.get(timeout=1)
                    
                    # Store in database
                    self.store_sensor_data(data)
                    
                    # Publish to MQTT
                    topic = f"edge/sensors/{sensor_type}"
                    self.mqtt_client.publish(topic, json.dumps(data))
                    
                    # Check for anomalies
                    if sensor_type == "system":
                        self.check_system_anomalies(data)
                
                time.sleep(0.1)
                
            except queue.Empty:
                continue
            except Exception as e:
                self.logger.error(f"Data processor error: {e}")
                time.sleep(1)
    
    def check_system_anomalies(self, system_data):
        """Check for system anomalies"""
        anomalies = []
        
        # Check CPU usage
        if system_data.get("cpu_usage", 0) > 80:
            anomalies.append("high_cpu_usage")
        
        # Check memory usage
        if system_data.get("memory_usage", 0) > 85:
            anomalies.append("high_memory_usage")
        
        # Check disk usage
        if system_data.get("disk_usage", 0) > 90:
            anomalies.append("high_disk_usage")
        
        # Check temperature
        if system_data.get("temperature", 0) > 70:
            anomalies.append("high_temperature")
        
        # Publish anomalies if any
        if anomalies:
            alert = {
                "device_id": self.config['mqtt']['client_id'],
                "timestamp": datetime.now().isoformat(),
                "alert_type": "system_anomaly",
                "anomalies": anomalies,
                "system_data": system_data
            }
            
            self.mqtt_client.publish("edge/alerts/system", json.dumps(alert))
            self.logger.warning(f"System anomalies detected: {anomalies}")
    
    def store_image_data(self, image_data):
        """Store image data in database"""
        try:
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()
            
            cursor.execute('''
                INSERT INTO image_data (filename, objects_detected, confidence, processed)
                VALUES (?, ?, ?, ?)
            ''', (
                image_data["filename"],
                json.dumps(image_data.get("processed", {})),
                0.0,  # Default confidence
                image_data.get("processed") is not None
            ))
            
            conn.commit()
            conn.close()
            
        except Exception as e:
            self.logger.error(f"Image data storage failed: {e}")
    
    def restart_system(self):
        """Restart the system"""
        self.logger.info("System restart requested")
        self.running = False
        
        # Restart after cleanup
        subprocess.run(['sudo', 'reboot'])
    
    def shutdown_system(self):
        """Shutdown the system"""
        self.logger.info("System shutdown requested")
        self.running = False
        
        # Shutdown after cleanup
        subprocess.run(['sudo', 'shutdown', '-h', 'now'])
    
    def update_config(self, new_config):
        """Update configuration"""
        self.config.update(new_config)
        
        # Save new config
        with open('config.json', 'w') as f:
            json.dump(self.config, f, indent=2)
        
        self.logger.info("Configuration updated")
    
    def start(self):
        """Start the edge gateway"""
        self.logger.info("Starting IoT Edge Gateway")
        
        # Start worker threads
        self.threads = [
            threading.Thread(target=self.sensor_worker, daemon=True),
            threading.Thread(target=self.image_worker, daemon=True),
            threading.Thread(target=self.control_worker, daemon=True),
            threading.Thread(target=self.data_processor, daemon=True)
        ]
        
        for thread in self.threads:
            thread.start()
        
        self.logger.info("All worker threads started")
    
    def stop(self):
        """Stop the edge gateway"""
        self.logger.info("Stopping IoT Edge Gateway")
        
        self.running = False
        
        # Stop MQTT client
        self.mqtt_client.loop_stop()
        self.mqtt_client.disconnect()
        
        # Cleanup GPIO
        GPIO.cleanup()
        
        # Wait for threads to finish
        for thread in self.threads:
            thread.join(timeout=5)
        
        self.logger.info("IoT Edge Gateway stopped")
    
    def signal_handler(self, signum, frame):
        """Handle system signals"""
        self.logger.info(f"Received signal {signum}")
        self.stop()
        sys.exit(0)
    
    def run(self):
        """Main run loop"""
        try:
            self.start()
            
            # Keep main thread alive
            while self.running:
                time.sleep(1)
                
        except KeyboardInterrupt:
            self.logger.info("Keyboard interrupt received")
        except Exception as e:
            self.logger.error(f"Unexpected error: {e}")
        finally:
            self.stop()

if __name__ == "__main__":
    # Import psutil for system monitoring
    import psutil
    
    # Create and run edge gateway
    gateway = IoTEdgeGateway()
    gateway.run()

Мы создали полнофункциональный шлюз обработки данных на краю сети. Этот класс интегрирует сбор данных с датчиков, обработку видео, машинное обучение и управление через MQTT в одну связную систему.

Основные компоненты архитектуры:

Инициализация задействует множество подсистем. Конфигурация загружается из JSON файла, что позволяет адаптировать систему без изменения кода. Логирование направляется одновременно в файл и консоль, обеспечивая удобный мониторинг. GPIO настраивается для работы с датчиками и обнаружения движения. Камера инициализируется с нужным разрешением и частотой кадров. MQTT клиент подключается к брокеру для обмена данными. База данных создаёт таблицы для хранения показаний датчиков, изображений и статуса системы. Модели машинного обучения загружаются для анализа видеопотока.

Сбор и обработка данных:

Рабочий поток датчиков циклически читает DHT22 для влажности и температуры, ADS1115 для аналоговых сигналов и метрики системы через psutil. Данные попадают в очередь для последующей обработки. Изображение захватывается по расписанию или при обнаружении движения. Каждый кадр анализируется моделями распознавания объектов и обнаружения аномалий. Результаты с доверительными оценками сохраняются в базу.

Система управления:

Рабочий поток контроля следит за входящими командами через MQTT. Поддерживаются операции перезагрузки и выключения системы, запуска видеозахвата вручную и обновления конфигурации. Все команды регистрируются для аудита и отладки.

Детектирование аномалий:

Система постоянно мониторит нагрузку на процессор, использование памяти, место на диске и температуру. При превышении заданных пороговых значений отправляется оповещение. Это позволяет оперативно реагировать на проблемы с оборудованием.

Многопоточная архитектура обеспечивает одновременную работу всех компонентов без блокировок. Очереди безопасно передают данные между потоками. Правильная обработка сигналов гарантирует чистый выход из приложения.

3. Embedded C++ для микроконтроллеров

#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <RTClib.h>
#include <DHT.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
#include <FreeRTOS.h>
#include <task.h>

// Configuration constants
#define WIFI_SSID "YOUR_WIFI_SSID"
#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD"
#define MQTT_SERVER "mqtt.example.com"
#define MQTT_PORT 1883
#define MQTT_CLIENT_ID "embedded_device_01"
#define MQTT_USERNAME "your_username"
#define MQTT_PASSWORD "your_password"

// Pin definitions
#define DHT_PIN 4
#define BMP_SDA_PIN 21
#define BMP_SCL_PIN 22
#define SD_CS_PIN 5
#define LED_PIN 2
#define BUZZER_PIN 12
#define RELAY_PIN 13
#define BUTTON_PIN 0

// Sensor types
#define DHT_TYPE DHT22

// Task priorities
#define SENSOR_TASK_PRIORITY 2
#define COMM_TASK_PRIORITY 3
#define CONTROL_TASK_PRIORITY 1
#define DATA_LOGGER_TASK_PRIORITY 1

// Task stack sizes
#define SENSOR_TASK_STACK_SIZE 2048
#define COMM_TASK_STACK_SIZE 4096
#define CONTROL_TASK_STACK_SIZE 2048
#define DATA_LOGGER_TASK_STACK_SIZE 2048

// Data structures
struct SensorData {
    float temperature;
    float humidity;
    float pressure;
    float altitude;
    unsigned long timestamp;
    bool valid;
};

struct SystemStatus {
    bool wifiConnected;
    bool mqttConnected;
    bool sdCardPresent;
    float batteryLevel;
    int uptime;
    int freeHeap;
    float cpuTemperature;
    unsigned long lastSensorRead;
    unsigned long lastMQTTMessage;
};

struct ControlCommand {
    String command;
    String parameters;
    unsigned long timestamp;
    bool processed;
};

// Global objects
DHT dhtSensor(DHT_PIN, DHT_TYPE);
Adafruit_BMP280 bmp280;
RTC_DS3231 rtc;
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

// Data queues
QueueHandle_t sensorDataQueue;
QueueHandle_t controlCommandQueue;
QueueHandle_t logDataQueue;

// Semaphores
SemaphoreHandle_t wifiMutex;
SemaphoreHandle_t mqttMutex;
SemaphoreHandle_t sensorMutex;
SemaphoreHandle_t sdMutex;

// Global variables
SystemStatus systemStatus;
TaskHandle_t sensorTaskHandle;
TaskHandle_t commTaskHandle;
TaskHandle_t controlTaskHandle;
TaskHandle_t dataLoggerTaskHandle;

// Function prototypes
void sensorTask(void *pvParameters);
void communicationTask(void *pvParameters);
void controlTask(void *pvParameters);
void dataLoggerTask(void *pvParameters);
void connectWiFi();
void connectMQTT();
void mqttCallback(char* topic, byte* payload, unsigned int length);
void publishSensorData(SensorData& data);
void publishSystemStatus();
void handleControlCommand(ControlCommand& command);
void logData(String message);
void setupSensors();
void setupSDCard();
void setupRTC();
void buttonISR();
void alertUser(int beeps, int duration);
void enterDeepSleep(int seconds);
void wakeFromDeepSleep();

void setup() {
    Serial.begin(115200);
    Serial.println("Embedded IoT Device starting...");
    
    // Initialize GPIO
    pinMode(LED_PIN, OUTPUT);
    pinMode(BUZZER_PIN, OUTPUT);
    pinMode(RELAY_PIN, OUTPUT);
    pinMode(BUTTON_PIN, INPUT_PULLUP);
    
    // Setup interrupts
    attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);
    
    // Initialize semaphores
    wifiMutex = xSemaphoreCreateMutex();
    mqttMutex = xSemaphoreCreateMutex();
    sensorMutex = xSemaphoreCreateMutex();
    sdMutex = xSemaphoreCreateMutex();
    
    // Initialize queues
    sensorDataQueue = xQueueCreate(10, sizeof(SensorData));
    controlCommandQueue = xQueueCreate(5, sizeof(ControlCommand));
    logDataQueue = xQueueCreate(20, sizeof(String[50]));
    
    // Setup hardware
    setupSensors();
    setupSDCard();
    setupRTC();
    
    // Initialize system status
    systemStatus = {
        .wifiConnected = false,
        .mqttConnected = false,
        .sdCardPresent = false,
        .batteryLevel = 0.0,
        .uptime = 0,
        .freeHeap = ESP.getFreeHeap(),
        .cpuTemperature = 0.0,
        .lastSensorRead = 0,
        .lastMQTTMessage = 0
    };
    
    // Create FreeRTOS tasks
    xTaskCreate(
        sensorTask,
        "SensorTask",
        SENSOR_TASK_STACK_SIZE,
        NULL,
        SENSOR_TASK_PRIORITY,
        &sensorTaskHandle
    );
    
    xTaskCreate(
        communicationTask,
        "CommTask",
        COMM_TASK_STACK_SIZE,
        NULL,
        COMM_TASK_PRIORITY,
        &commTaskHandle
    );
    
    xTaskCreate(
        controlTask,
        "ControlTask",
        CONTROL_TASK_STACK_SIZE,
        NULL,
        CONTROL_TASK_PRIORITY,
        &controlTaskHandle
    );
    
    xTaskCreate(
        dataLoggerTask,
        "DataLoggerTask",
        DATA_LOGGER_TASK_STACK_SIZE,
        NULL,
        DATA_LOGGER_TASK_PRIORITY,
        &dataLoggerTaskHandle
    );
    
    // Alert user that system is ready
    alertUser(3, 200);
    
    Serial.println("Embedded IoT Device ready!");
}

void loop() {
    // Main loop is handled by FreeRTOS tasks
    vTaskDelay(pdMS_TO_TICKS(1000));
}

void sensorTask(void *pvParameters) {
    Serial.println("Sensor task started");
    
    SensorData sensorData;
    const TickType_t xDelay = pdMS_TO_TICKS(5000); // 5 seconds
    
    while (1) {
        // Take sensor mutex
        if (xSemaphoreTake(sensorMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
            // Read DHT sensor
            float humidity = dhtSensor.readHumidity();
            float temperature = dhtSensor.readTemperature();
            
            // Read BMP280 sensor
            float pressure = bmp280.readPressure();
            float altitude = bmp280.readAltitude(1013.25); // Sea level pressure
            
            // Check if readings are valid
            if (!isnan(humidity) && !isnan(temperature) && pressure > 0) {
                sensorData.temperature = temperature;
                sensorData.humidity = humidity;
                sensorData.pressure = pressure / 100.0; // Convert to hPa
                sensorData.altitude = altitude;
                sensorData.timestamp = millis();
                sensorData.valid = true;
                
                // Update system status
                systemStatus.lastSensorRead = millis();
                systemStatus.freeHeap = ESP.getFreeHeap();
                
                // Send data to queue
                if (xQueueSend(sensorDataQueue, &sensorData, pdMS_TO_TICKS(100)) != pdTRUE) {
                    Serial.println("Failed to send sensor data to queue");
                }
                
                // Log sensor data
                String logMessage = "Sensor: T=" + String(temperature) + 
                                  "C, H=" + String(humidity) + 
                                  "%, P=" + String(sensorData.pressure) + "hPa";
                logData(logMessage);
                
            } else {
                Serial.println("Invalid sensor readings");
                sensorData.valid = false;
                
                // Log error
                logData("ERROR: Invalid sensor readings");
            }
            
            xSemaphoreGive(sensorMutex);
        } else {
            Serial.println("Failed to take sensor mutex");
        }
        
        vTaskDelay(xDelay);
    }
}

void communicationTask(void *pvParameters) {
    Serial.println("Communication task started");
    
    const TickType_t xDelay = pdMS_TO_TICKS(1000); // 1 second
    
    while (1) {
        // Handle WiFi connection
        if (xSemaphoreTake(wifiMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
            if (WiFi.status() != WL_CONNECTED) {
                connectWiFi();
            }
            
            systemStatus.wifiConnected = (WiFi.status() == WL_CONNECTED);
            xSemaphoreGive(wifiMutex);
        }
        
        // Handle MQTT connection
        if (systemStatus.wifiConnected) {
            if (xSemaphoreTake(mqttMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
                if (!mqttClient.connected()) {
                    connectMQTT();
                }
                
                systemStatus.mqttConnected = mqttClient.connected();
                
                // Process MQTT messages
                mqttClient.loop();
                
                xSemaphoreGive(mqttMutex);
            }
        }
        
        // Publish sensor data
        SensorData sensorData;
        if (xQueueReceive(sensorDataQueue, &sensorData, pdMS_TO_TICKS(100)) == pdTRUE) {
            if (sensorData.valid && systemStatus.mqttConnected) {
                publishSensorData(sensorData);
                systemStatus.lastMQTTMessage = millis();
            }
        }
        
        // Publish system status periodically
        static unsigned long lastStatusPublish = 0;
        if (millis() - lastStatusPublish > 60000) { // Every minute
            if (systemStatus.mqttConnected) {
                publishSystemStatus();
                lastStatusPublish = millis();
            }
        }
        
        vTaskDelay(xDelay);
    }
}

void controlTask(void *pvParameters) {
    Serial.println("Control task started");
    
    ControlCommand command;
    const TickType_t xDelay = pdMS_TO_TICKS(500); // 0.5 seconds
    
    while (1) {
        // Process control commands
        if (xQueueReceive(controlCommandQueue, &command, pdMS_TO_TICKS(100)) == pdTRUE) {
            handleControlCommand(command);
        }
        
        // Monitor system health
        static unsigned long lastHealthCheck = 0;
        if (millis() - lastHealthCheck > 30000) { // Every 30 seconds
            // Check free heap
            if (ESP.getFreeHeap() < 1000) {
                logData("WARNING: Low memory");
                alertUser(2, 100);
            }
            
            // Check uptime
            systemStatus.uptime = millis() / 1000;
            
            // Check battery level (if implemented)
            systemStatus.batteryLevel = getBatteryLevel();
            
            lastHealthCheck = millis();
        }
        
        vTaskDelay(xDelay);
    }
}

void dataLoggerTask(void *pvParameters) {
    Serial.println("Data logger task started");
    
    String logMessage;
    const TickType_t xDelay = pdMS_TO_TICKS(1000); // 1 second
    
    while (1) {
        // Process log messages
        if (xQueueReceive(logDataQueue, &logMessage, pdMS_TO_TICKS(100)) == pdTRUE) {
            // Print to serial
            Serial.println(logMessage);
            
            // Write to SD card if available
            if (systemStatus.sdCardPresent) {
                if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
                    writeLogToSD(logMessage);
                    xSemaphoreGive(sdMutex);
                }
            }
        }
        
        vTaskDelay(xDelay);
    }
}

void connectWiFi() {
    Serial.println("Connecting to WiFi...");
    
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    
    int attempts = 0;
    while (WiFi.status() != WL_CONNECTED && attempts < 30) {
        vTaskDelay(pdMS_TO_TICKS(500));
        Serial.print(".");
        attempts++;
    }
    
    if (WiFi.status() == WL_CONNECTED) {
        Serial.println("\nWiFi connected!");
        Serial.print("IP address: ");
        Serial.println(WiFi.localIP());
        digitalWrite(LED_PIN, HIGH);
        logData("WiFi connected: " + WiFi.localIP().toString());
    } else {
        Serial.println("\nFailed to connect to WiFi");
        digitalWrite(LED_PIN, LOW);
        logData("ERROR: WiFi connection failed");
    }
}

void connectMQTT() {
    Serial.println("Connecting to MQTT...");
    
    mqttClient.setServer(MQTT_SERVER, MQTT_PORT);
    mqttClient.setCallback(mqttCallback);
    
    int attempts = 0;
    while (!mqttClient.connected() && attempts < 10) {
        Serial.print("Attempting MQTT connection...");
        
        if (mqttClient.connect(MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD)) {
            Serial.println("connected!");
            
            // Subscribe to control topics
            mqttClient.subscribe("embedded/control");
            mqttClient.subscribe("embedded/" + String(MQTT_CLIENT_ID) + "/control");
            
            logData("MQTT connected");
            
        } else {
            Serial.print("failed, rc=");
            Serial.print(mqttClient.state());
            Serial.println(" try again in 5 seconds");
            vTaskDelay(pdMS_TO_TICKS(5000));
            attempts++;
        }
    }
    
    if (!mqttClient.connected()) {
        Serial.println("Failed to connect to MQTT");
        logData("ERROR: MQTT connection failed");
    }
}

void mqttCallback(char* topic, byte* payload, unsigned int length) {
    Serial.print("Message arrived [");
    Serial.print(topic);
    Serial.print("] ");
    
    String message = "";
    for (int i = 0; i < length; i++) {
        message += (char)payload[i];
    }
    Serial.println(message);
    
    // Parse JSON message
    DynamicJsonDocument doc(1024);
    DeserializationError error = deserializeJson(doc, message);
    
    if (error) {
        Serial.println("Failed to parse JSON");
        logData("ERROR: Failed to parse MQTT message");
        return;
    }
    
    // Create control command
    ControlCommand command;
    command.command = doc["command"] | "";
    command.parameters = doc["parameters"] | "";
    command.timestamp = millis();
    command.processed = false;
    
    // Send command to control task
    if (xQueueSend(controlCommandQueue, &command, pdMS_TO_TICKS(100)) != pdTRUE) {
        Serial.println("Failed to send control command to queue");
    }
}

void publishSensorData(SensorData& data) {
    if (xSemaphoreTake(mqttMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
        // Create JSON document
        DynamicJsonDocument doc(1024);
        
        doc["device_id"] = MQTT_CLIENT_ID;
        doc["timestamp"] = data.timestamp;
        doc["temperature"] = data.temperature;
        doc["humidity"] = data.humidity;
        doc["pressure"] = data.pressure;
        doc["altitude"] = data.altitude;
        
        // Serialize JSON
        String jsonStr;
        serializeJson(doc, jsonStr);
        
        // Publish to topics
        mqttClient.publish("embedded/sensors/temperature", String(data.temperature).c_str());
        mqttClient.publish("embedded/sensors/humidity", String(data.humidity).c_str());
        mqttClient.publish("embedded/sensors/pressure", String(data.pressure).c_str());
        mqttClient.publish("embedded/sensors/combined", jsonStr.c_str());
        
        xSemaphoreGive(mqttMutex);
    }
}

void publishSystemStatus() {
    if (xSemaphoreTake(mqttMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
        // Create JSON document
        DynamicJsonDocument doc(1024);
        
        doc["device_id"] = MQTT_CLIENT_ID;
        doc["timestamp"] = millis();
        doc["wifi_connected"] = systemStatus.wifiConnected;
        doc["mqtt_connected"] = systemStatus.mqttConnected;
        doc["sd_card_present"] = systemStatus.sdCardPresent;
        doc["battery_level"] = systemStatus.batteryLevel;
        doc["uptime"] = systemStatus.uptime;
        doc["free_heap"] = systemStatus.freeHeap;
        doc["cpu_temperature"] = systemStatus.cpuTemperature;
        doc["last_sensor_read"] = systemStatus.lastSensorRead;
        doc["last_mqtt_message"] = systemStatus.lastMQTTMessage;
        
        // Serialize JSON
        String jsonStr;
        serializeJson(doc, jsonStr);
        
        // Publish status
        mqttClient.publish("embedded/status", jsonStr.c_str());
        
        xSemaphoreGive(mqttMutex);
    }
}

void handleControlCommand(ControlCommand& command) {
    Serial.println("Processing control command: " + command.command);
    
    if (command.command == "buzz") {
        int duration = command.parameters.toInt();
        if (duration == 0) duration = 1000;
        alertUser(1, duration);
        
    } else if (command.command == "led") {
        bool state = (command.parameters == "on");
        digitalWrite(LED_PIN, state ? HIGH : LOW);
        
    } else if (command.command == "relay") {
        bool state = (command.parameters == "on");
        digitalWrite(RELAY_PIN, state ? HIGH : LOW);
        
    } else if (command.command == "restart") {
        Serial.println("Restarting device...");
        logData("System restart requested");
        vTaskDelay(pdMS_TO_TICKS(1000));
        ESP.restart();
        
    } else if (command.command == "sleep") {
        int seconds = command.parameters.toInt();
        if (seconds == 0) seconds = 10;
        Serial.println("Going to sleep for " + String(seconds) + " seconds");
        logData("Entering deep sleep for " + String(seconds) + " seconds");
        enterDeepSleep(seconds);
        
    } else if (command.command == "get_status") {
        publishSystemStatus();
        
    } else {
        Serial.println("Unknown command: " + command.command);
        logData("WARNING: Unknown control command: " + command.command);
    }
    
    command.processed = true;
}

void logData(String message) {
    String logEntry = String(millis()) + ": " + message;
    
    // Send to logger queue
    if (xQueueSend(logDataQueue, &logEntry, pdMS_TO_TICKS(100)) != pdTRUE) {
        Serial.println("Failed to send log message to queue");
    }
}

void setupSensors() {
    Serial.println("Setting up sensors...");
    
    // Initialize DHT sensor
    dhtSensor.begin();
    
    // Initialize BMP280
    if (!bmp280.begin(BMP280_ADDRESS_ALT)) {
        Serial.println("Could not find BMP280 sensor!");
        logData("ERROR: BMP280 sensor not found");
    } else {
        Serial.println("BMP280 sensor initialized");
        logData("BMP280 sensor initialized");
    }
    
    Serial.println("Sensors setup completed");
}

void setupSDCard() {
    Serial.println("Setting up SD card...");
    
    if (SD.begin(SD_CS_PIN)) {
        Serial.println("SD card initialized");
        systemStatus.sdCardPresent = true;
        logData("SD card initialized");
        
        // Create log file if it doesn't exist
        File logFile = SD.open("/log.txt", FILE_WRITE);
        if (logFile) {
            logFile.println("\n=== System Restart ===");
            logFile.close();
        }
    } else {
        Serial.println("SD card initialization failed");
        systemStatus.sdCardPresent = false;
        logData("ERROR: SD card initialization failed");
    }
}

void setupRTC() {
    Serial.println("Setting up RTC...");
    
    if (rtc.begin()) {
        if (rtc.lostPower()) {
            Serial.println("RTC lost power, setting to compile time");
            rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
        }
        Serial.println("RTC initialized");
        logData("RTC initialized");
    } else {
        Serial.println("Couldn't find RTC");
        logData("ERROR: RTC not found");
    }
}

void writeLogToSD(String message) {
    File logFile = SD.open("/log.txt", FILE_WRITE);
    if (logFile) {
        DateTime now = rtc.now();
        logFile.print(now.timestamp(DateTime::TIMESTAMP_FULL));
        logFile.print(": ");
        logFile.println(message);
        logFile.close();
    }
}

void buttonISR() {
    // Button press interrupt
    static unsigned long lastInterrupt = 0;
    unsigned long interruptTime = millis();
    
    if (interruptTime - lastInterrupt > 200) { // Debounce
        // Create manual capture command
        ControlCommand command;
        command.command = "capture";
        command.parameters = "manual";
        command.timestamp = interruptTime;
        command.processed = false;
        
        // Send to control task
        xQueueSendFromISR(controlCommandQueue, &command, NULL);
        
        // Alert user
        alertUser(1, 100);
    }
    
    lastInterrupt = interruptTime;
}

void alertUser(int beeps, int duration) {
    for (int i = 0; i < beeps; i++) {
        tone(BUZZER_PIN, 1000, duration);
        vTaskDelay(pdMS_TO_TICKS(duration * 2));
    }
}

float getBatteryLevel() {
    // Read battery voltage (assuming voltage divider)
    int rawValue = analogRead(A0);
    float voltage = rawValue * (3.3 / 4096.0) * 2.0; // Voltage divider factor
    
    // Convert to percentage (assuming 3.0V to 4.2V range)
    float percentage = ((voltage - 3.0) / 1.2) * 100.0;
    percentage = constrain(percentage, 0.0, 100.0);
    
    return percentage;
}

void enterDeepSleep(int seconds) {
    // Configure wake-up sources
    esp_sleep_enable_timer_wakeup(seconds * 1000000); // Convert to microseconds
    esp_sleep_enable_ext0_wakeup(GPIO_NUM_0, 0); // Wake on button press
    
    // Enter deep sleep
    esp_deep_sleep_start();
}

void wakeFromDeepSleep() {
    // This function is called after waking from deep sleep
    Serial.println("Waking from deep sleep...");
    
    // Reinitialize peripherals
    setupSensors();
    setupSDCard();
    
    // Check wake-up reason
    esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
    
    switch (wakeup_reason) {
        case ESP_SLEEP_WAKEUP_TIMER:
            Serial.println("Wakeup caused by timer");
            break;
        case ESP_SLEEP_WAKEUP_EXT0:
            Serial.println("Wakeup caused by external signal (RTC GPIO)");
            break;
        default:
            Serial.println("Wakeup was not caused by deep sleep");
            break;
    }
}

Этот пример демонстрирует полнофункциональное встраиваемое приложение IoT с использованием мультитасинга. Давайте разберемся в ключевых компонентах.

Архитектура многозадачности

Приложение использует FreeRTOS для организации параллельного выполнения четырёх основных задач. Каждая задача отвечает за определённый аспект функциональности устройства. Задача сенсоров читает показания с датчиков, задача коммуникации управляет WiFi и MQTT, задача управления обрабатывает команды, а задача логирования записывает данные.

Синхронизация и защита данных

Мьютексы обеспечивают безопасный доступ к общим ресурсам. Очереди используются для безопасной передачи данных между задачами без прямого доступа к переменным. Такой подход предотвращает состояния гонки и обеспечивает стабильность системы.

Управление энергией

Устройство поддерживает глубокий сон с возможностью пробуждения по таймеру или внешнему сигналу. Это существенно снижает потребление энергии батареей, что критично для автономных устройств.

Обработка периферии

Датчики DHT и BMP280 читаются регулярно. Данные упаковываются в JSON и отправляются по MQTT. Устройство также может принимать команды управления через MQTT и исполнять их с помощью реле, светодиодов и зуммера.

Устойчивость и диагностика

Система отслеживает своё состояние: наличие WiFi, подключение MQTT, доступность SD-карты, уровень батареи и свободную память. Периодически отправляет статус на MQTT-сервер. Все события логируются как в серийный порт, так и на SD-карту для последующего анализа.

Этот шаблон хорошо масштабируется и служит основой для более сложных IoT-проектов.

Архитектура IoT

Слойная модель

graph TD
    A[Physical Layer] --> B[Sensor/Actuator Layer]
    B --> C[Edge Computing Layer]
    C --> D[Network Layer]
    D --> E[Cloud Layer]
    E --> F[Application Layer]
    
    A1[Sensors, Actuators] --> A
    B1[Microcontrollers, Gateways] --> B
    C1[Edge Processing, Analytics] --> C
    D1[WiFi, LoRaWAN, 5G] --> D
    E1[Cloud Services, Storage] --> E
    F1[Web Apps, Mobile Apps] --> F

Сравнение встроенных систем

Микроконтроллеры vs. микропроцессоры

ХарактеристикаМикроконтроллерМикропроцессор
ИнтеграцияВысокая (CPU + периферия)Низкая (только CPU)
СтоимостьНизкаяСредняя до высокой
Потребление энергииОчень низкоеСреднее до высокого
ПроизводительностьОграниченнаяВысокая
ПамятьВнутренняя, ограниченнаяВнешняя, гибкая
ПрименениеВстроенные системы, IoTУниверсальное использование

Платформы IoT

ПлатформаПротоколыEdge ComputingИнтеграция с облакомЛицензия
ArduinoWiFi, EthernetОграниченоCloud ServicesOpen Source
Raspberry PiWiFi, EthernetДаПолная интеграцияOpen Source
ESP32WiFi, BluetoothДаMQTT, HTTPOpen Source
NVIDIA JetsonWiFi, EthernetДаNVIDIA CloudКоммерческая

Протоколы коммуникации

Беспроводные протоколы

ПротоколДальностьСкорость передачиПотребление энергииПрименение
WiFi50-100м150-600 Мбит/сВысокоеВнутри помещений, высокоскоростная
Bluetooth10-100м1-3 Мбит/сСреднееЛичная сеть
LoRaWAN2-15км0.3-50 кбит/сОчень низкоеДальние расстояния
Zigbee10-100м250 кбит/сНизкоеЯчеистые сети

Протоколы IoT

ПротоколТипОверхедQoSПрименение
MQTTPublish/SubscribeНизкий3 уровняРеальное время
CoAPRequest/ResponseОчень низкийОтсутствуетОграниченные ресурсы
HTTP/RESTRequest/ResponseВысокийTCPВеб-интеграция
WebSocketFull-DuplexСреднийTCPРеальное время

Концепции Edge Computing

Edge vs. Cloud обработка

АспектEdge ComputingCloud Computing
ЗадержкаМиллисекундыСекунды
Пропускная способностьСниженаВысокая
АвтономностьВозможнаНевозможна
МасштабируемостьОграниченнаяНеограниченная
СтоимостьПеременнаяПредсказуемая

Применения Edge Computing

  • Предсказательное обслуживание: локальный анализ данных датчиков
  • Аналитика видео: распознавание объектов в реальном времени
  • Промышленный IoT: управление производственным оборудованием
  • Умные города: анализ и управление трафиком

Преимущества и недостатки

Преимущества IoT и встроенных систем

  • Автоматизация: снижение ручного труда
  • Мониторинг в реальном времени: получение информации о статусе сразу
  • Эффективность: оптимизация процессов
  • Масштабируемость: гибкое приспособление к потребностям
  • Снижение затрат: экономия на операционных расходах

Недостатки

  • Риски безопасности: уязвимости для атак
  • Сложность: интеграция и поддержка
  • Зависимость: отказ при сбое сети
  • Защита данных: сбор персональных данных
  • Совместимость: различные стандарты

Часто задаваемые на экзаменах вопросы

  1. В чём различие между микроконтроллером и микропроцессором? Микроконтроллер интегрирует CPU, память и периферию на одном кристалле, тогда как микропроцессор содержит только CPU и требует внешних компонентов.

  2. Объясните концепцию Edge Computing! Edge Computing обрабатывает данные локально на устройствах вблизи источника данных вместо облака, снижая задержку и экономя пропускную способность.

  3. Когда использовать какой протокол IoT? MQTT для коммуникации в реальном времени, CoAP для устройств с ограниченными ресурсами, HTTP/REST для веб-интеграции, WebSocket для двусторонней коммуникации.

  4. Какие основные требования к встроенным системам? Надёжность, способность работы в реальном времени, низкое потребление энергии, экономичное производство, долговечность и нетребовательность к техническому обслуживанию.

Основные источники

  1. https://www.arduino.cc/
  2. https://www.raspberrypi.org/
  3. https://mqtt.org/
  4. https://www.espressif.com/en/products/socs/esp32
Назад к блогу
Share: