433Mhz trip wire sensor system: Difference between revisions
From Pessin randon wiki
No edit summary |
|||
| Line 270: | Line 270: | ||
if (dipId <= 0) { | if (dipId <= 0) { | ||
return; | return; | ||
} | } | ||
dipSeen[dipId - 1] = millis(); // Päivitä aikaleima | dipSeen[dipId - 1] = millis(); // Päivitä aikaleima | ||
Revision as of 08:57, 21 September 2025
Vastaanotinyksikkö
Vastaanottimen käyttö
Tämä laite vastaanottaa 433 MHz -radioviestejä, joissa on kiinteitä DIP-koodeja. Jokaisella DIP-koodilla on pysyvä ID-numero (1–16). Näytöllä näkyy aina yksi koodi kerrallaan.

Mitä näyttö kertoo?
Näyttö on jaettu kahteen osaan:
- Vasemmassa reunassa (suurena):
- ID-numero (esim.
5) - Huutomerkki
!näkyy, jos koodi on aktiivinen (viesti saatu viimeisen 5 minuutin aikana)
- ID-numero (esim.
- Oikeassa reunassa (pienellä tekstillä):
- Ylärivi: Esim.
2/(3)/[5]2= aktiivisten lähetysten määrä(3)= passiivisten lähetysten määrä (viesti yli 5 min vanha)[5]= kaikkien vastaanotettujen lähetyksien määrä
- Toinen rivi (vain jos koodi on passiivinen):
- Aika viimeisestä viestistä minuutteina (esim.
17m)
- Aika viimeisestä viestistä minuutteina (esim.
- Kolmas rivi (vain jos passiivinen):
- Sana
PASSIVE
- Sana
- Alarivi:
- DIP-koodi (esim.
0100)
- DIP-koodi (esim.
Vastaanottimen kytkentäkaavio ja tarvittavat osat
Tarvittavat osat
| Komponentti | Määrä | Hinta | Linkki |
|---|---|---|---|
| Arduino Nano v3 | 1 | 4,2 € | Binghe Development Board with Chip CH340 Type-C Connector 5V 16M Microcontroller Compatible with Arduino IDE Pack of 5 |
| 433 MHz ASK-radiovastaanotin | 1 | 2,8 € | ALAMSCN Set of 5 433 MHz RF Receiver and Radio Transmission Module + RF 433 MHz Spring Antenna Kit Compatible with Arduino |
| OLED-näyttö (128×32, I2C, SSD1306) | 1 | 4,5 € | MakerHawk I2C OLED Display Module I2C SSD1306 Tiny Screen Module 0.91 Inch, in White 128X32 I2C OLED Driver |
| 9V patteriliitin | 1 | 0,8 € | HeyNana 5 x Battery Clip 9 Volt Block Battery I Type Clip Snap with 15 cm Connection Cable for 9 V Blocks Plug Connection Cable |
| 9V Paristo | 1 | 1,2 € | GutAlkaLi Batteries 9 V Block Battery, Pack of 6, Longlife Power for Smoke Detectors, Fire & Fire Detectors, Microphone 6lr61 6F22 PP3 MN1604 |
| Momenttikytkin (painonappi) (EI PAKOLLINEN) | 1 | DAOKAI Micro Switch NC+NO Hinge Straight Lever Micro Limit Switch 125V 1A PDT Limit Micro Switch Moment Switch for Arduino (Pack of 20) | |
| Yhteensä |
Näytön kytkentä
| OLED-pinni | Arduino Nano -pinni | Selitys |
|---|---|---|
| VCC | 5V | Virransyöttö |
| GND | GND | Maa |
| SDA | A4 | I2C-data |
| SCL | A5 | I2C-kello |
Vastaanottimen kytkentä
| Vastaanottimen pinni | Arduino Nano -pinni | Selitys |
|---|---|---|
| VCC | 5V | Virransyöttö |
| GND | GND | Maa |
| DATA (OUT) | D12 | Vastaanottodata |
Näytön pikarullausnappi
| Nappi | Arduino Nano | Selitys |
|---|---|---|
| 1 | D2 | Luku (INPUT_PULLUP) |
| 2 | GND | Painettaessa yhdistää maahan |
Lisää tarvittavat kirjastot
- RadioHead (RH_ASK)
- Adafruit SSD1306
- Adafruit GFX
- Wire
Vastaanottimen lähdekoodi
#include <RH_ASK.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Radiovastaanotin
RH_ASK driver(1000, 12, 11, 10); // RX = D12
// Ajastukset
const unsigned long ENTRY_TIMEOUT = 30 * 60 * 1000UL; // 30 min
const unsigned long DISPLAY_DURATION = 2500; // 2.5 s
// Seuraava ruutu nappi
#define BUTTON_PIN 2
// Kiinteät DIP-koodit (16 kpl)
const char* dipIdMap[16] = {
"0000", "0001", "0010", "0011",
"0100", "0101", "0110", "0111",
"1000", "1001", "1010", "1011",
"1100", "1101", "1110", "1111"
};
//Vilkku
bool blinkState = false;
unsigned long lastBlink = 0;
const unsigned long BLINK_INTERVAL = 500;
// Tallennetaan viimeisin aikaleima DIP-koodeille
unsigned long dipSeen[16] = {0};
// Painallusmuuttujat
bool buttonPressed = false;
unsigned long lastDisplaySwitch = 0;
// CRC8 Dallas/Maxim
byte crc8_dallas(const char* data, uint8_t len) {
byte crc = 0x00;
for (uint8_t i = 0; i < len; i++) {
crc ^= data[i];
for (uint8_t j = 0; j < 8; j++) {
crc = (crc & 0x80) ? (crc << 1) ^ 0x31 : (crc << 1);
}
}
return crc;
}
// Palauttaa ID:n 1–16 tai 0 jos tuntematon
int getDipId(const char* dip) {
for (int i = 0; i < 16; i++) {
if (strncmp(dipIdMap[i], dip, 4) == 0) {
return i + 1;
}
}
return 0;
}
void setup() {
// Serial.begin(9600);
// delay(1000);
pinMode(BUTTON_PIN, INPUT_PULLUP);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.println(F("OLED ei vastaa"));
display.display();
while (1); // Pysäyttää ohjelman
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(F("Odotetaan..."));
display.display();
if (!driver.init()) {
display.clearDisplay();
display.setCursor(0, 0);
display.println(F("Radio ei vastaa"));
display.display();
while (1); //
}
pinMode(12, INPUT);
}
void loop() {
receiveRadio();
cleanExpiredEntries();
checkButton();
updateDisplay();
delay(10);
}
void checkButton() {
static bool prevState = HIGH;
bool state = digitalRead(BUTTON_PIN);
if (prevState == HIGH && state == LOW) {
buttonPressed = true;
}
prevState = state;
}
void receiveRadio() {
uint8_t buf[32];
uint8_t buflen = sizeof(buf);
if (driver.recv(buf, &buflen)) {
if (buflen < 8 || buf[0] != 'S' || buf[5] != 'E') return;
byte expectedCrc = crc8_dallas((char*)buf, 6);
char crcStr[3] = { (char)buf[6], (char)buf[7], '\0' };
byte receivedCrc = strtoul(crcStr, NULL, 16);
if (expectedCrc != receivedCrc) return;
char dip[5];
memcpy(dip, &buf[1], 4);
dip[4] = '\0';
int dipId = getDipId(dip);
if (dipId <= 0) {
return;
}
dipSeen[dipId - 1] = millis(); // Päivitä aikaleima
}
}
void cleanExpiredEntries() {
unsigned long now = millis();
for (int i = 0; i < 16; i++) {
if (dipSeen[i] != 0 && now - dipSeen[i] > ENTRY_TIMEOUT) {
dipSeen[i] = 0; // Nollaa aikaleima = ei enää aktiivinen
}
}
}
void updateDisplay() {
static int currentActiveIndex = -1;
unsigned long now = millis();
int allSeenIndices[16];
int seenCount = 0;
int activeCount = 0;
int passiveCount = 0;
// Kerätään vastaanotetut DIP:t ja lasketaan aktiiviset/passiiviset
for (int i = 0; i < 16; i++) {
if (dipSeen[i] != 0) {
allSeenIndices[seenCount++] = i;
unsigned long ageMin = (now - dipSeen[i]) / 60000UL;
if (ageMin <= 5) {
activeCount++;
} else {
passiveCount++;
}
}
}
display.clearDisplay();
if (seenCount <= 0) {
// Jos ei ole aktiivisia koodeja
currentActiveIndex = -1;
display.setTextSize(1);
display.setCursor(0, 0);
display.println(F("Kuunnellaan radiota..."));
// Vilkku aina näkyviin
if (now - lastBlink >= BLINK_INTERVAL) {
blinkState = !blinkState;
lastBlink = now;
}
if (blinkState) {
display.fillRect(SCREEN_WIDTH - 3, SCREEN_HEIGHT - 3, 2, 2, SSD1306_WHITE);
}
display.display();
return;
}
// Päivitä ruutu vain tarvittaessa
if (now - lastDisplaySwitch >= DISPLAY_DURATION || buttonPressed) {
currentActiveIndex = (currentActiveIndex + 1) % seenCount;
lastDisplaySwitch = now;
buttonPressed = false;
}
// Haetaan näytettävä DIP
int dipIndex = allSeenIndices[currentActiveIndex];
int id = dipIndex + 1;
unsigned long ageMin = (now - dipSeen[dipIndex]) / 60000UL;
bool isPassive = (ageMin > 5);
// Vasemmalle iso ID-numero
display.setTextSize(4);
display.setCursor(0, 0);
display.print(id);
if (!isPassive) {
display.print("!");
}
// Oikealle tilastorivi
display.setTextSize(1);
int xTextStart = SCREEN_WIDTH - 6 * 10;
char indexStr[20];
snprintf(indexStr, sizeof(indexStr), "%d/(%d)/[%d]", activeCount, passiveCount, seenCount);
display.setCursor(xTextStart, 0);
display.print(indexStr);
// PASSIVE-merkinnät jos yli 5 min vanha
if (isPassive) {
char timeStr[10];
snprintf(timeStr, sizeof(timeStr), "%lum", ageMin);
display.setCursor(xTextStart, 8);
display.print(timeStr);
display.setCursor(xTextStart, 16);
display.print(F("PASSIVE"));
}
// DIP-koodi alariville
display.setCursor(xTextStart, SCREEN_HEIGHT - 8);
display.print(dipIdMap[dipIndex]);
// Vilkku (aina näkyvissä)
if (now - lastBlink >= BLINK_INTERVAL) {
blinkState = !blinkState;
lastBlink = now;
}
if (blinkState) {
display.fillRect(SCREEN_WIDTH - 3, SCREEN_HEIGHT - 3, 2, 2, SSD1306_WHITE);
}
display.display();
}
Lähetinyksikkö
Lähettimen käyttö
Lähetinyksikön pohjassa on DIP kytkinkaavio, josta valitaan lähetettävä ID. ID tulisi olla jokaisessa lähettimessä oma, jotta hälytykset on identifioitavissa.
Lähettimen pohjayksikkö on irtirepäistävä 360 astetta pyörivä pohja joka irrottuaan aktivoi lähetyksen välittömästi.
Lähetinyksikön voi myös laittaa omalla painollaan pohja edellä jollekkin tasaiselle alustalle ja mikäli se kaatuu alkaa lähetys.
Järjestelmä toimii 9v paristolla.
Lähettimen kytkentäkaavio ja tarvittavat osat
Tarvittavat osat
| Komponentti | Määrä | Hinta | Linkki |
|---|---|---|---|
| Arduino Nano v3 | 1 | 4,2 € | Binghe Development Board with Chip CH340 Type-C Connector 5V 16M Microcontroller Compatible with Arduino IDE Pack of 5 |
| 433 MHz ASK-radiolähetin | 1 | 2,8 € | ALAMSCN Set of 5 433 MHz RF Receiver and Radio Transmission Module + RF 433 MHz Spring Antenna Kit Compatible with Arduino |
| 9V patteriliitin | 1 | 0,8 € | HeyNana 5 x Battery Clip 9 Volt Block Battery I Type Clip Snap with 15 cm Connection Cable for 9 V Blocks Plug Connection Cable |
| 9V Paristo | 1 | 1,2 € | GutAlkaLi Batteries 9 V Block Battery, Pack of 6, Longlife Power for Smoke Detectors, Fire & Fire Detectors, Microphone 6lr61 6F22 PP3 MN1604 |
| Momenttikytkin (painonappi) | 1 | 0,25 € | DAOKAI Micro Switch NC+NO Hinge Straight Lever Micro Limit Switch 125V 1A PDT Limit Micro Switch Moment Switch for Arduino (Pack of 20) |
| Yhteensä |
Kytkentäkaavio
Lähettimen kytkentä
| Vastaanottimen pinni | Arduino Nano -pinni | Selitys |
|---|---|---|
| VCC | 5V | Virransyöttö |
| GND | GND | Maa |
| DATA (OUT) | D12 | Lähetysdata |
| Antenni | - | Antennin optimaalinen pituus ~17cm |
Virtalähteen kytkentä pikarullausnappi
| Nappi | Arduino Nano | Selitys |
|---|---|---|
| 1 | D2 | Luku (INPUT_PULLUP) |
| 2 | GND | Painettaessa yhdistää maahan |
Vastaanottimen lähdekoodi
#include <RH_ASK.h>
#include <SPI.h>
RH_ASK driver(1000);
#define TX_PIN 12
#define DIP1 2
#define DIP2 3
#define DIP3 4
#define DIP4 5
byte crc8_dallas(const String &data) {
byte crc = 0x00;
for (int i = 0; i < data.length(); i++) {
crc ^= data[i];
for (int j = 0; j < 8; j++) {
crc = (crc & 0x80) ? (crc << 1) ^ 0x31 : (crc << 1);
}
}
return crc;
}
void setup() {
driver.init();
pinMode(DIP1, INPUT_PULLUP);
pinMode(DIP2, INPUT_PULLUP);
pinMode(DIP3, INPUT_PULLUP);
pinMode(DIP4, INPUT_PULLUP);
pinMode(TX_PIN, OUTPUT);
randomSeed(analogRead(0));
}
String readDipCode() {
String code = "";
code += (digitalRead(DIP1) == LOW) ? '1' : '0';
code += (digitalRead(DIP2) == LOW) ? '1' : '0';
code += (digitalRead(DIP3) == LOW) ? '1' : '0';
code += (digitalRead(DIP4) == LOW) ? '1' : '0';
return code;
}
void loop() {
String code = readDipCode();
String core = "S" + code + "E";
byte crc = crc8_dallas(core);
String message = core + String(crc, HEX);
driver.send((uint8_t*)message.c_str(), message.length());
driver.waitPacketSent();
delay(random(800, 1800)); // satunnainen lähetysväli
}
ID Dip taulukko
| ID | DIP-koodi |
|---|---|
| 1 | 0000 |
| 2 | 0001 |
| 3 | 0010 |
| 4 | 0011 |
| 5 | 0100 |
| 6 | 0101 |
| 7 | 0110 |
| 8 | 0111 |
| 9 | 1000 |
| 10 | 1001 |
| 11 | 1010 |
| 12 | 1011 |
| 13 | 1100 |
| 14 | 1101 |
| 15 | 1110 |
| 16 | 1111 |