Хочешь знать, где твоя машина, даже если её угнали? Не веришь штатной сигнализации? Собери свою IoT-систему охраны — как у топовых брендов, но за 2000 рублей из китайских модулей. Работает даже после отключения аккумулятора, ловит присутствие хозяина по телефону и шлёт всё в твою самописную систему на PHP.
Все события делятся на уровни подозрительности. Тревога срабатывает только если отсутствует хозяин (BLE-метка не обнаружена). Ниже — полный перечень сценариев и условий их срабатывания.
glass_break
owner_present
(все тревоги отключены)suspicious_loitering
loud_noise
door_opened
vehicle_moving
engine_started
ignition_on
power_disconnected
(угон в процессе!)tampering
Система не принимает решений — она фиксирует факты. Ты сам видишь: «в 03:14 напряжение пропало, через 10 сек — движение, координаты такие-то». Решать — угон или нет — будешь ты.
Классика умных сигналок:
Мы реализуем это на ESP32 (а не на Arduino — он мощнее, есть Bluetooth, Wi-Fi, два ядра и встроенный BLE).
Установи в Arduino IDE через менеджер библиотек:
Adafruit GPS Library
— для NEO-6MAdafruit BME280 Library
Adafruit MPU6050
ESP32 BLE Arduino
— для сканирования BLESoftwareSerial
— для SIM800L (или используй аппаратный UART2)Пример функции отправки события:
#include <HTTPClient.h>
void sendEventToServer(String eventType, float lat, float lon, float voltage) {
if (WiFi.status() == WL_CONNECTED) { // или через GPRS, если нет Wi-Fi
HTTPClient http;
http.begin("http://yourdomain.com/tracker.php");
http.addHeader("Content-Type", "application/json");
String json = "{";
json += "\"event\":\"" + eventType + "\",";
json += "\"lat\":" + String(lat, 6) + ",";
json += "\"lon\":" + String(lon, 6) + ",";
json += "\"voltage\":" + String(voltage) + ",";
json += "\"time\":\"" + getTimeString() + "\"";
json += "}";
int httpResponseCode = http.POST(json);
http.end();
}
}
Файл tracker.php
:
<?php
$data = json_decode(file_get_contents('php://input'), true);
// Сохраняем в БД
file_put_contents('logs.txt', date('Y-m-d H:i:s') . " " . json_encode($data) . "\n", FILE_APPEND);
// Отправляем в Telegram
$botToken = '123456:ABC';
$chatId = '7890';
$msg = "🚨 Тревога: {$data['event']}\n📍 {$data['lat']}, {$data['lon']}\n🔋 {$data['voltage']} В";
file_get_contents("https://api.telegram.org/bot{$botToken}/sendMessage?chat_id={$chatId}&text=" . urlencode($msg));
?>
Твой PHP-сервер (или отдельная IoT-система по Webhook/WeGuard) может:
Ты получил:
И всё это — за цену дешёвой китайской сигналки, но с полным контролем и расширяемостью.
#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
// === BLE ===
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
// === GPS ===
#include <Adafruit_GPS.h>
#define GPSSerial Serial2
Adafruit_GPS GPS(&GPSSerial);
// === BME280 ===
#include <Adafruit_BME280.h>
Adafruit_BME280 bme;
// === MPU6050 ===
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
Adafruit_MPU6050 mpu;
// === GSM ===
#include <TinyGsmClient.h>
#define TINY_GSM_MODEM_SIM800
#define SerialAT Serial1
TinyGsm modem(SerialAT);
TinyGsmClient client(modem);
const char* apn = "internet"; // уточни у оператора
// === Пины ===
#define VOLTAGE_PIN 34 // аналоговый вход (делитель 12В)
#define VIBRATION_PIN 4 // SW-420
#define RADAR_PIN 15 // RCWL-0516
// === BLE ===
bool phoneDetected = false;
String targetDeviceName = "YourPhoneName"; // ← ЗАМЕНИ НА ИМЯ ТВОЕГО ТЕЛЕФОНА!
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
if (advertisedDevice.haveName()) {
if (advertisedDevice.getName().c_str() == targetDeviceName) {
phoneDetected = true;
}
}
}
};
// === Вспомогательные функции ===
float readCarVoltage() {
int val = analogRead(VOLTAGE_PIN);
// Делитель: 20к + 10к → коэффициент 3.0
return (val * 3.3 / 4095.0) * 3.0;
}
void sendEvent(const char* eventType) {
// Включаем SIM800L (если управляешь через пин — добавь digitalWrite)
delay(2000);
SerialAT.begin(9600, SERIAL_8N1, 16, 17);
modem.restart();
if (!modem.gprsConnect(apn)) {
Serial.println("GPRS failed");
return;
}
// Ждём GPS фикс
GPS.begin(9600);
bool gpsFix = false;
for (int i = 0; i < 20; i++) {
GPS.read();
if (GPS.newNMEAreceived()) {
if (GPS.parse(GPS.lastNMEA())) {
if (GPS.fix) {
gpsFix = true;
break;
}
}
}
delay(500);
}
// Собираем данные
String json = "{";
json += "\"event\":\"" + String(eventType) + "\",";
json += "\"voltage\":" + String(readCarVoltage()) + ",";
json += "\"vibration\":" + String(digitalRead(VIBRATION_PIN)) + ",";
json += "\"radar\":" + String(digitalRead(RADAR_PIN)) + ",";
if (gpsFix) {
json += "\"lat\":" + String(GPS.latitude, 6) + ",";
json += "\"lon\":" + String(GPS.longitude, 6) + ",";
} else {
json += "\"lat\":0,\"lon\":0,";
}
if (bme.performReading()) {
json += "\"pressure\":" + String(bme.pressure / 100.0) + ",";
json += "\"temp\":" + String(bme.temperature) + ",";
} else {
json += "\"pressure\":0,\"temp\":0,";
}
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
json += "\"accel_x\":" + String(a.acceleration.x) + "";
json += "}";
// Отправка на сервер
if (client.connect("yourdomain.com", 80)) {
client.print("POST /tracker.php HTTP/1.1\r\n");
client.print("Host: yourdomain.com\r\n");
client.print("Content-Type: application/json\r\n");
client.print("Content-Length: " + String(json.length()) + "\r\n");
client.print("\r\n");
client.print(json);
client.stop();
Serial.println("Sent: " + json);
}
modem.gprsDisconnect();
GPS.end();
}
// === SETUP ===
void setup() {
Serial.begin(115200);
Wire.begin();
// Датчики
pinMode(VIBRATION_PIN, INPUT);
pinMode(RADAR_PIN, INPUT);
analogReadResolution(12); // ESP32
// GPS
GPSSerial.begin(9600, SERIAL_8N1, 5, -1); // RX=5, TX не нужен
// BME280
if (!bme.begin(0x76)) {
Serial.println("BME280 not found");
}
// MPU6050
if (!mpu.begin()) {
Serial.println("MPU6050 not found");
}
mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
// GSM
SerialAT.begin(9600, SERIAL_8N1, 16, 17);
modem.restart();
// BLE
BLEDevice::init("");
}
// === LOOP ===
void loop() {
// Сканируем BLE 5 сек
phoneDetected = false;
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true);
pBLEScan->start(5, false);
pBLEScan->clearResults();
delay(1000);
// Если телефона нет — читаем датчики
if (!phoneDetected) {
Serial.println("Phone NOT detected. Reading sensors...");
bool eventTriggered = false;
String eventType = "";
// Проверка вибрации
if (digitalRead(VIBRATION_PIN) == HIGH) {
eventType = "vibration";
eventTriggered = true;
}
// Проверка радара
if (digitalRead(RADAR_PIN) == HIGH) {
eventType = "radar_motion";
eventTriggered = true;
}
// Проверка напряжения (зажигание)
float v = readCarVoltage();
if (v > 13.2) {
eventType = "ignition_on";
eventTriggered = true;
}
if (eventTriggered) {
sendEvent(eventType.c_str());
delay(10000); // анти-спам
}
} else {
Serial.println("Phone detected. Sleeping...");
}
// Спим 30 сек
delay(30000);
}
Немного передумал с безопасностью ...
Многие пытаются использовать Bluetooth-маячки (beacon), чтобы автоматически включать/выключать сигнализацию: «подошёл — отключилась, ушёл — включилась». Но есть проблема: любой может просканировать MAC-адрес или UUID и подделать сигнал. Это делает такие системы уязвимыми к простой replay-атаке.
Я решил пойти другим путём — использовать одноразовые криптографические токены, по принципу автомобильных иммобилайзеров. Теперь моя сигнализация реагирует только на аутентичное подтверждение присутствия, а не на «крик в эфир».
Вместо того чтобы маячок постоянно излучал один и тот же идентификатор, я сделал так:
Даже если злоумышленник перехватит код — он не сработает повторно. Это называется HOTP (HMAC-Based One-Time Password).
// Упрощённая логика на стороне автомобиля (ESP32)
#include <WiFi.h>
#include <HTTPClient.h>
#include <Crypto.h> // Библиотека для HMAC, например: https://github.com/intrbiz/arduino-crypto
const uint8_t SECRET_KEY[32] = { /* твой 256-битный секрет */ };
uint32_t last_counter = 1000; // последний принятый счётчик
bool verify_hotp(const char* received_code, uint32_t counter) {
byte hmac[32];
hmac_sha256(SECRET_KEY, 32, (byte*)&counter, 4, hmac);
// Преобразуем HMAC в 6-значный код (как в Google Authenticator)
uint32_t code = ((hmac[19] & 0x7F) << 24 |
(hmac[20] & 0xFF) << 16 |
(hmac[21] & 0xFF) << 8 |
(hmac[22] & 0xFF)) % 1000000;
return (atoi(received_code) == code) && (counter > last_counter);
}
void handle_unlock_request(const char* hotp_code, uint32_t counter) {
if (verify_hotp(hotp_code, counter)) {
last_counter = counter;
digitalWrite(RELAY_PIN, LOW); // отключаем сигнализацию
Serial.println("✅ Сигнализация ОТКЛЮЧЕНА");
// Запускаем таймер на включение при уходе
startAutoArmTimer(30); // 30 секунд
} else {
Serial.println("❌ Неверный или повторный код!");
}
}
// При нажатии кнопки
uint32_t counter = readCounterFromEEPROM(); // читаем текущий счётчик
counter++;
// Генерируем HOTP
byte hmac[32];
hmac_sha256(SECRET_KEY, 32, (byte*)&counter, 4, hmac);
uint32_t code = /* ... как выше ... */ % 1000000;
// Отправляем по BLE / LoRa / GSM / MQTT
sendToCar(String(code), counter);
// Сохраняем новый счётчик
saveCounterToEEPROM(counter);
💡 Совет: для максимальной безопасности используй микросхему с Secure Element (например, ATECC608B или NTAG 424 DNA). Там ключ физически недоступен даже при вскрытии устройства.
Теперь моя машина знает: если код принят — это точно я, а не кто-то с клоном маячка. А когда я ухожу — она сама встаёт на охрану. Без облаков, без уязвимостей, без «водить телефоном у двери».
Блог только запустил, все статьи генерирую через нейросеть т.к. лень, возможны ошибки. Просто чтобы вы знали и не запускали ядерный реактор по моим статьям ))
Если у вас есть вопросы, или Нашли неточность? пишите в коментах — вместе поправим и сделаем статью более качественной. Я лично объясню нюансы из практики.
Комментарии
Пока нет комментариев. Будьте первым!