Telegram Based Alarm - Sensor cable protection

Menticol

Active Member
Hello ETO guys:

I'm working on a little alarm system for my workshop, using a Raspberry Pi Pico W, a PIR sensor, and a reed switch sensor. The distance between the Pi and these sensors would be 15 meters at most, connected with twisted pair cable.

With the help of AI (denying its involvement would be a gross lie) I I was able to generate a working code that connects to the Wi Fi network, can be armed / disarmed via inbound telegram message, and also reports the sensor triggering via an outgoing telegram message.

First question: This design only reads Open: Normal / Shorted to ground: Alarm. The proper way must be: x resistance value (provided by an 5.6k EOL resistor): Normal / Open or Shorted to Ground, Alarm. My first thought was switching to analog inputs, installing the cable in the workshop, measuring its resistance on ohms, and program that into the Micro. But that's very inconvenient! How is a commercial system able to work with different installations and lengths of wire, without reprogramming anything?

Second question: A long cable from the sensors to the Pico is vulnerable to electromagnetic interference, and why not sabotage (a simple taser would fry the Microcontroller). How is this problem addressed on a commercial system? I was thinking an optoisolator, but that would collide with the previous question.

As always, thank you very much for your time

Below is my code, in case the reader wants to implement a similar system at his/her home:

Python:
WIFI_SSID = "PEQLL"
WIFI_PASSWORD = "password"

TELEGRAM_BOT_TOKEN = "dummy"
ALLOWED_USERS = ["-dummy", "dummy"]
TELEGRAM_MAIN_CHAT_ID = ALLOWED_USERS[0]

armed_status = False

import network
import urequests
import time
import machine
import json


IMMEDIATE_ALARM_PINS = [15, 14, 13]
trigger_pins = []
for pin_num in IMMEDIATE_ALARM_PINS:
    trigger_pins.append(machine.Pin(pin_num, machine.Pin.IN, machine.Pin.PULL_UP))

last_update_id = None

def connect_wifi(ssid, password):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    try:
        if not wlan.isconnected():
            print('Connecting to Wi-Fi...')
            wlan.connect(ssid, password)
            max_wait = 10
            while max_wait > 0:
                status = wlan.status()
                if status < 0 or status >= 3:
                    break
                max_wait -= 1
                print('.')
                time.sleep(1)

        if wlan.isconnected():
            print('Wi-Fi Connected!')
            print('IP Address:', wlan.ifconfig()[0])
            return True
        else:
            status = wlan.status()
            error_dict = {
                network.STAT_WRONG_PASSWORD: "Wrong password",
                network.STAT_NO_AP_FOUND: "No AP found",
                network.STAT_CONNECT_FAIL: "Connection failed"
            }
            print(f"Wi-Fi Connection Failed! Status code: {status} - {error_dict.get(status, 'Unknown error')}")
            return False
    except Exception as e:
        print(f"Wi-Fi connection error: {e}")
        return False


def send_telegram_message(token, chat_id, message):
    url = f"https://api.telegram.org/bot{token}/sendMessage?chat_id={chat_id}&text={message}"
    try:
        print("Sending Telegram message...")
        response = urequests.get(url, timeout=10)
        if response.status_code == 200:
            print(f"Message '{message}' sent successfully to chat ID {chat_id}!")
        else:
            print(f"Failed to send message. Status code: {response.status_code}")
            print(f"Response: {response.text}")
        response.close()
    except Exception as e:
        print(f"Error sending Telegram message: {e}")


def get_latest_telegram_message(token):
    global last_update_id
    url = f"https://api.telegram.org/bot{token}/getUpdates"
    if last_update_id is not None:
        url += f"?offset={last_update_id + 1}&limit=1&timeout=1"
    else:
        url += f"?limit=1&timeout=1"


    try:
        response = urequests.get(url, timeout=5)
        if response.status_code == 200:
            updates = response.json()
            if "result" in updates and updates["result"]:
                update = updates["result"][0]
                last_update_id = update["update_id"]
                message = update.get("message", {})
                text = message.get("text", "").strip().lower()
                chat_id = message.get("chat", {}).get("id", "")
                return text, str(chat_id)
        else:
            print(f"Failed to fetch messages. Status code: {response.status_code}")
            pass
        response.close()
    except Exception as e:
        print(f"Error reading Telegram message: {e}")
    return None, None

def handle_arm(chat_id_to_reply):
    global armed_status
    print("Executing ARM command.")
    if armed_status:
        send_telegram_message(TELEGRAM_BOT_TOKEN, chat_id_to_reply, "No need: System was already armed")
    else:
        armed_status = True
        send_telegram_message(TELEGRAM_BOT_TOKEN, chat_id_to_reply, "System is now armed")
 

def handle_disarm(chat_id_to_reply):
    global armed_status
    print("Executing DISARM command.")
    if not armed_status:
        send_telegram_message(TELEGRAM_BOT_TOKEN, chat_id_to_reply, "No need: System was already disarmed")
    else:
        armed_status = False
        send_telegram_message(TELEGRAM_BOT_TOKEN, chat_id_to_reply, "System is now disarmed")


# --- Main loop ---
wifi_connected = connect_wifi(WIFI_SSID, WIFI_PASSWORD)
if not wifi_connected:
    print("Exiting: No Wi-Fi. Please check credentials or network.")
    machine.reset()
else:
    print("WiFi Init OK.")
    send_telegram_message(TELEGRAM_BOT_TOKEN, TELEGRAM_MAIN_CHAT_ID, "WiFi Init OK.")
    last_checked_time = time.time()
    last_telegram_check_time = time.time()

    while True:
        triggered_pin_number = None

        # Check all configured alarm pins
        for i, pin_obj in enumerate(trigger_pins):
            if pin_obj.value() == 0:  # Pin is active low (triggered)
                triggered_pin_number = IMMEDIATE_ALARM_PINS[i]
                break # Exit loop once a trigger is found

        if triggered_pin_number is not None:
            if armed_status == False:
                print(f"Sensor on PIN {triggered_pin_number} triggered, but system is not armed.")    
            else:
                print(f"¡ALERT! Sensor on PIN {triggered_pin_number} triggered.")
                send_telegram_message(TELEGRAM_BOT_TOKEN, TELEGRAM_MAIN_CHAT_ID, f"¡ALERT! Sensor on PIN {triggered_pin_number} triggered.")  
         
            current_pin_object = None
            for i, pin_num_iter in enumerate(IMMEDIATE_ALARM_PINS):
                if pin_num_iter == triggered_pin_number:
                    current_pin_object = trigger_pins[i]
                    break
           
            if current_pin_object:
                while current_pin_object.value() == 0:
                    time.sleep(0.1)      
            time.sleep(0.5) #

        # Check Telegram messages periodically
        current_time = time.time()
        if current_time - last_telegram_check_time > 3:
            if not wifi_connected:
                print("Wi-Fi lost, attempting to reconnect...")
                wifi_connected = connect_wifi(WIFI_SSID, WIFI_PASSWORD)
                if wifi_connected:
                    print("Wi-Fi reconnected.")
                    send_telegram_message(TELEGRAM_BOT_TOKEN, TELEGRAM_MAIN_CHAT_ID, "Wi-Fi reconnected.")
                else:
                    print("Failed to reconnect to Wi-Fi.")                
                    last_telegram_check_time = current_time
                    time.sleep(10) # Wait before next loop iteration if wifi is down
                    continue

            if wifi_connected:
                msg, chat_id_from = get_latest_telegram_message(TELEGRAM_BOT_TOKEN)
                if msg is not None and chat_id_from in ALLOWED_USERS:
                    print(f"Mensaje recibido de {chat_id_from}: {msg}")
                    if msg == "arm":
                        handle_arm(chat_id_from)
                    elif msg == "disarm":
                        handle_disarm(chat_id_from)                
                    else:
                        send_telegram_message(TELEGRAM_BOT_TOKEN, chat_id_from, f"Unrecognized command: {msg}")
            last_telegram_check_time = current_time

        time.sleep(0.1)
 
The zone input circuits on the high end commercial systems I use have this:

2K7 so ground from zone -, 2K7 pullup to 12V from zone +

Then, 100K from zone + to a 10K pulldown paralleled with a 0.1uF cap, with the divider junction connected to an analog multiplexer (4051 or 4052, depending on the board).
The multiplexer output goes to an ADC input with 0 - 3.3V range.

The zone terminals also have small spark gaps to ground built in to the PCB.
It uses 1K zone terminating resistors, in series for normally closed sensors or parallel for normally open.

The photo with only the 2K7 resistors visible is a system controller, which has the rest surface mount on the back.
The one with three resistors and the cap all visible is an input expander board for the same series system.

The input circuits are pretty much identical on each, other than the first four on the main board having jumpers to set an alternate mode for some USA two-wire smoke detectors, that I've never used in the UK. (I use Texecom 4 wire ones connected to standard zones).








 
Cookies are required to use this site. You must accept them to continue using the site. Learn more…