Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

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).


IMG_5119.jpg


IMG_5120.jpg


IMG_5121.jpg


IMG_5122.jpg
 
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).


First of all, thank you so much Rjenkinsgb for investing your time writing this explanation, and even including beautiful photographs!

I was not able to get the CD74HC4051E, but found a little cheap module model Cd74hc4067

alarm-analog-2-rev1.png

Armed with my very basic simulation program I made the following abomination:

alarm-analog-1-rev2.png

Please apologize me if I grossly misunderstood your instructions, I'm not exactly the brightest bulb in the box. The circuit detects the open and closed condition of the sensor just fine, but not the removal of the EOL resistor or shorting the cables. What could be the problem?

Thank you!


EDIT: My Brain is slowly starting to spool up. Maybe the goal is to build a three-resistor voltage divider?

1747790257543.png
1747790425204.png
 
Last edited:
Not quite; like this:

AlarmZones_schematic.png


The 100K and 0.1uF form a noise or spike filter; those and the 10K act as a divider to scale the input voltage to a suitable low range for a 3.3V ADC input.

You only need the analog multiplexer if you have more zones than ADC channels; the boards I photographed have sixteen zones each, making up to 48 in the main unit and the version I use here works with remote expanders up to 192 zones total, though I only need one for the garage.

There is only ever one EOL resistor on a zone.

The loop supply should be a minimum of 12V, as low voltages cannot guarantee contact "wetting" and conduction with switch contacts.

This is a live reading of some zones, from the system setup program:

AlarmZones.jpg


Around 150 is safe, near zero or near 250 is an alarm state.
I'm not sure of the exact range considered "safe", it could be between 100 and 200, or 125 - 175.
The cable resistance will not be more than a few ohms even on long runs, so is negligible compared to the 1K.

(The garage o/s temp one is a data link zone; low speed serial data using a zone input and one of the outputs).
Zones on these systems can be configured for many things, general inputs & sensors as well as the perimeter & day/night/away/fire etc. alarm modes, that's what the garage door status ones are, status inputs; it's all networked with my home automation system.
 
ps. Using all the same values, 12V and 1K EOL resistor, it looks like the "Zone secure" input at the ADC should be around 0.5V
Try using 0.25V - 0.75V as a "safe" input range and outside that as an alarm?
 

Latest threads

Back
Top