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):
Vastaanottimen oled näyttöID-numero (esim. 5)
Huutomerkki ! näkyy, jos koodi on aktiivinen (eli viesti saatu viimeisen 5 minuutin aikana)
🔹 Oikeassa reunassa (pienellä tekstillä):
Ylärivi: Esim. 2 / (3) / [5]
2 = aktiivisten määrä
(3) = passiivisten määrä (viesti yli 5 min vanha)
[5] = kaikkien vastaanotettujen määrä
Toinen rivi (vain jos koodi on passiivinen):
Aika viimeisestä viestistä minuutteina (esim. 17m)
Kolmas rivi (vain jos passiivinen):
Sana PASSIVE
Alarivi:
DIP-koodi (esim. 0101)
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ä
Arduino Nano v3
1
433 MHz ASK-radiovastaanotin
1
OLED-näyttö (128×32, I2C, SSD1306)
1
Momenttikytkin (painonappi)
1
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
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 -1Adafruit_SSD1306display(SCREEN_WIDTH,SCREEN_HEIGHT,&Wire,OLED_RESET);// RadiovastaanotinRH_ASKdriver(1000,12,11,10);// RX = D12// AjastuksetconstunsignedlongENTRY_TIMEOUT=30*60*1000UL;// 30 minconstunsignedlongDISPLAY_DURATION=2500;// 2.5 s// Seuraava ruutu nappi#define BUTTON_PIN 2// Kiinteät DIP-koodit (16 kpl)constchar*dipIdMap[16]={"0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111"};//VilkkuboolblinkState=false;unsignedlonglastBlink=0;constunsignedlongBLINK_INTERVAL=500;// Tallennetaan viimeisin aikaleima DIP-koodeilleunsignedlongdipSeen[16]={0};// PainallusmuuttujatboolbuttonPressed=false;unsignedlonglastDisplaySwitch=0;// CRC8 Dallas/Maximbytecrc8_dallas(constchar*data,uint8_tlen){bytecrc=0x00;for(uint8_ti=0;i<len;i++){crc^=data[i];for(uint8_tj=0;j<8;j++){crc=(crc&0x80)?(crc<<1)^0x31:(crc<<1);}}returncrc;}// Palauttaa ID:n 1–16 tai 0 jos tuntematonintgetDipId(constchar*dip){for(inti=0;i<16;i++){if(strncmp(dipIdMap[i],dip,4)==0){returni+1;}}return0;}voidsetup(){// 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);}voidloop(){receiveRadio();cleanExpiredEntries();checkButton();updateDisplay();delay(10);}voidcheckButton(){staticboolprevState=HIGH;boolstate=digitalRead(BUTTON_PIN);if(prevState==HIGH&&state==LOW){buttonPressed=true;}prevState=state;}voidreceiveRadio(){uint8_tbuf[32];uint8_tbuflen=sizeof(buf);if(driver.recv(buf,&buflen)){if(buflen<8||buf[0]!='S'||buf[5]!='E')return;byteexpectedCrc=crc8_dallas((char*)buf,6);charcrcStr[3]={(char)buf[6],(char)buf[7],'\0'};bytereceivedCrc=strtoul(crcStr,NULL,16);if(expectedCrc!=receivedCrc)return;chardip[5];memcpy(dip,&buf[1],4);dip[4]='\0';intdipId=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}}voidcleanExpiredEntries(){unsignedlongnow=millis();for(inti=0;i<16;i++){if(dipSeen[i]!=0&&now-dipSeen[i]>ENTRY_TIMEOUT){dipSeen[i]=0;// Nollaa aikaleima = ei enää aktiivinen}}}voidupdateDisplay(){staticintcurrentActiveIndex=-1;unsignedlongnow=millis();intallSeenIndices[16];intseenCount=0;intactiveCount=0;intpassiveCount=0;// Kerätään vastaanotetut DIP:t ja lasketaan aktiiviset/passiivisetfor(inti=0;i<16;i++){if(dipSeen[i]!=0){allSeenIndices[seenCount++]=i;unsignedlongageMin=(now-dipSeen[i])/60000UL;if(ageMin<=5){activeCount++;}else{passiveCount++;}}}display.clearDisplay();if(seenCount<=0){// Jos ei ole aktiivisia koodejacurrentActiveIndex=-1;display.setTextSize(1);display.setCursor(0,0);display.println(F("Kuunnellaan radiota..."));// Vilkku aina näkyviinif(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 tarvittaessaif(now-lastDisplaySwitch>=DISPLAY_DURATION||buttonPressed){currentActiveIndex=(currentActiveIndex+1)%seenCount;lastDisplaySwitch=now;buttonPressed=false;}// Haetaan näytettävä DIPintdipIndex=allSeenIndices[currentActiveIndex];intid=dipIndex+1;unsignedlongageMin=(now-dipSeen[dipIndex])/60000UL;boolisPassive=(ageMin>5);// Vasemmalle iso ID-numerodisplay.setTextSize(4);display.setCursor(0,0);display.print(id);if(!isPassive){display.print("!");}// Oikealle tilastorividisplay.setTextSize(1);intxTextStart=SCREEN_WIDTH-6*10;charindexStr[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 vanhaif(isPassive){chartimeStr[10];snprintf(timeStr,sizeof(timeStr),"%lum",ageMin);display.setCursor(xTextStart,8);display.print(timeStr);display.setCursor(xTextStart,16);display.print(F("PASSIVE"));}// DIP-koodi alarivilledisplay.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();}