433Mhz trip wire sensor system: Difference between revisions
From Pessin randon wiki
No edit summary |
|||
| Line 1: | Line 1: | ||
== Vastaanottimen käyttö == | == 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. | 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. | ||
[[File:Vastaanotin-oled.jpg|thumb|Vastaanottimen oled näyttö]] | [[File:Vastaanotin-oled.jpg|thumb|Vastaanottimen oled näyttö]] | ||
=== Mitä näyttö kertoo? === | |||
==== Mitä näyttö kertoo? ==== | |||
Näyttö on jaettu kahteen osaan: | Näyttö on jaettu kahteen osaan: | ||
| Line 75: | Line 78: | ||
|} | |} | ||
== Vastaanottimen kytkentäkaavio ja tarvittavat osat == | === Vastaanottimen kytkentäkaavio ja tarvittavat osat === | ||
== Tarvittavat osat | ==== Tarvittavat osat ==== | ||
{| class="wikitable" | {| class="wikitable" | ||
!Komponentti | !Komponentti | ||
| Line 174: | Line 177: | ||
* Wire | * Wire | ||
== Lähettimen lähdekoodi == | === Lähettimen lähdekoodi === | ||
<syntaxhighlight lang="c++" line="1">#include <RH_ASK.h> | <syntaxhighlight lang="c++" line="1">#include <RH_ASK.h> | ||
#include <Wire.h> | #include <Wire.h> | ||
Revision as of 15:55, 20 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.
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 |
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 |
| 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ä | 4 | 11,5 € |
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
Lähettimen 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) {
//Serial.print(F("⚠️ Tuntematon DIP: "));
//Serial.println(dip);
return;
}
/*
Serial.print(F("✅ DIP "));
Serial.print(dip);
Serial.print(F(" → ID "));
Serial.println(dipId);
*/
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();
}