// Sigfox s ATtiny85 //======================== // // verze: 31.10.2021 // // Mereni dvou teplot a jejich odesilani kazdych 14 minut na server. // V pripade vzbuzeni pomoci INT0 se odesle extra zprava. // Reset zpusobi take odeslani specialni servisni zpravy. // Samotny Sigfox odesila 1x za 2 hodiny info o stavu baterie a teplote cipu (nepresne). // DownLink je nepouzity (zpravy smerem ze site do ATtiny). // Nekontroluje se ani potvrzeni 'OK' po UpLinkove zprave, protoze to trva dlouho a procesor by musel byt po dobu vyckavani vzbuzeny = zvyseny odber. // // Seznam odesilanych zprav do Sigfoxu: // "AT$SF=FFFFFFFFFF" .... prave doslo k resetu (nemeri se teplota ani se netestuje kontakt) // "AT$SF=12AB1FBC00" .... bezne 14-minutove odeslani dvou teplot (teplota1 "12AB" = -2,21'C, teplota2 "1FBC" = 31,24'C, "00" = kontakt rozepnuty) // "AT$SF=1C000FAD03" .... bezne 14-minutove odeslani dvou teplot (teplota1 "1C00" = 21,68'C, teplota2 "0FAD" = -9,87'C, "03" = kontakt sepnuty) // "AT$SF=FFFFFFFF01" .... kontakt se prave sepnul (predcasne probuzeni) - teplota se v tomto pripade nemeri // // Prepocet HEXA teploty na 'C: HEXA cislo se prevede na desitkove, odecte se 5000 a vysledek se vydeli 100. // priklady: 0x1C00 = 7168; 7168 - 5000 = 2168; 2168 / 100 = 21,68'C // 0x12AB = 4779; 4779 - 5000 = -221; -221 / 100 = -2,21'C // //============================================================================================================= // Zapojeni: //============= // ATtiny85 // +--\__/--+ // nepouzito (RESET) - PB5 1| |8 Vcc // kom_Rx (Sigfox Tx) - PB3 2| |7 PB2 - WakeUp vstup od kontaktu (INT0) // kom_Tx (Sigfox Rx) - PB4 3| |6 PB1 - spolecny vstup od dvou teplomeru DS18B20 (externi Pull-Up 4k7) // GND 4| |5 PB0 - vystup na signalizacni LED // +--------+ //============================================================================================================= #include // knihovna pro seriovou komunikaci #include // knihovna pro OneWire komunikaci s cidlem DS18B2 #include // knihovna pro DS18B12 #include // knihovna pro uspavani procesoru #include // knihovna pro Watchdog #define PAUZAx8 105 // pocet probuzeni watchdogu, ktere se budou ignorovat // (WD je napevno nastaven na 8s. Pro cislo 105 se program bude spoustet zhruba 1x za 14 minut) #define pin_kom_RX 3 // prijimaci pin zvenku do ATtiny #define pin_kom_TX 4 // vysilaci pin z ATtiny ven #define pin_LED 0 // pin se signalizacni LED #define pin_teplomer 1 // pin pro vstup teplotniho cidla #define pin_INT0 2 // pin pro predcasne probuzeni ze spanku SoftwareSerial SSer(pin_kom_RX,pin_kom_TX); OneWire OW(pin_teplomer); DallasTemperature teplomer(&OW); unsigned int odpocet; // pocitadlo probuzeni volatile boolean INT0_flag; // znacka, ze doslo k probuzeni pomoci pinu PB2 (INT0) //============================================================================================================= void setup(void) { SSer.begin(9600); pinMode(pin_LED,OUTPUT); pinMode(pin_INT0, INPUT_PULLUP); teplomer.begin(); teplomer.setResolution(12); for (byte i = 0; i<= 20 ; i ++) // zablikani pri startu { digitalWrite(pin_LED,HIGH); delay(50); digitalWrite(pin_LED,LOW); delay(50); } pinMode(pin_LED,INPUT); // par mikroamper se uspori nastavenim vystupu na INPUT odpocet = PAUZAx8; // pocitadlo ignorovanych probuzeni se nastavi podle konstanty PAUZAx8 SSer.println("AT$SF=FFFFFFFFFF"); // zprava: "Prave doslo k resetu" zakladni_nastaveni_registru(); // vypnuti A/D prevodniku, zruseni "PinChange Interruptu" } //============================================================================================================= //============================================================================================================= // hlavni smycka void loop(void) { unsigned int tep16b1; // prvni teplota zaokrouhlena a prevedena na 16-bitove kladne cislo unsigned int tep16b2; // druha teplota zaokrouhlena a prevedena na 16-bitove kladne cislo odpocet --; // pri kazdem vzbuzeni se pocitadlo snizi o 1 if (odpocet == 0) // kdyz pocitadlo dopocita do 0, vykona se merici a odesilaci podprogram { teplomer.requestTemperatures(); byte timeout = 100; while (!teplomer.isConversionComplete() and timeout > 0) // cekat na dokonceni mereni nebo vypreseni timeoutu (100 x 10ms = 1 sekunda) { timeout --; delay(10); } float teplota1 = teplomer.getTempCByIndex(0); // prvni teplotni cidlo - prumerovani provadi samotne cidlo pomoci bitoveho rozliseni (opakovane mereni) float teplota2 = teplomer.getTempCByIndex(1); // druhe teplotni cidlo - prumerovani provadi samotne cidlo pomoci bitoveho rozliseni (opakovane mereni) if (teplota1 >= 0) tep16b1 = ((teplota1 + 50.005) * 100.0); // prevod teploty na cele kladne cislo vcetne zaokrouhleni (rozsah 0 az 10000 pro -50.00'C az +50,00'C) else tep16b1 = ((teplota1 + 50.004) * 100.0); // (zaokrouhleni pro zapornou teplotu) if (teplota2 >= 0) tep16b2 = ((teplota2 + 50.005) * 100.0); // prevod teploty na cele kladne cislo vcetne zaokrouhleni (rozsah 0 az 10000 pro -50.00'C az +50,00'C) else tep16b2 = ((teplota2 + 50.004) * 100.0); // (zaokrouhleni pro zapornou teplotu) SSer.print("AT$SF="); // prikaz pro odeslani 16-bitoveho HEXA cisla if (tep16b1 < 4096) SSer.print('0'); // doplneni uvodnich nul if (tep16b1 < 256) SSer.print('0'); if (tep16b1 < 16) SSer.print('0'); SSer.print(tep16b1, HEX); // teplota z prvniho cidla if (tep16b2 < 4096) SSer.print('0'); // doplneni uvodnich nul if (tep16b2 < 256) SSer.print('0'); if (tep16b2 < 16) SSer.print('0'); SSer.print(tep16b2, HEX); // teplota z druheho cidla if (digitalRead(pin_INT0) == HIGH) { SSer.println("00"); // doplneni zpravy " ... kontakt rozepnuty" } else { SSer.println("03"); // doplneni zpravy " ... kontakt sepnuty" } pinMode(pin_LED,OUTPUT); // kratke bliknuti LEDkou po kazdem odeslani dat digitalWrite(pin_LED,HIGH); delay(50); // kratka pauza aby mel Sigfox dostatek casu na nacteni celeho retezce pred uspanim ATtiny digitalWrite(pin_LED,LOW); pinMode(pin_LED,INPUT); // na zaver kvuli uspore energie prepnout pin na vstup delay(50); // priprava na hluboky spanek odpocet = PAUZAx8; // dalsi nastaveni pocitadla ignorovanych probuzeni } uspat(); // uspani procesoru do doby, nez se aktivuje WD nebo INT0 if (INT0_flag == true) { INT0_flag = false; SSer.println("AT$SF=FFFFFFFF01"); // zprava: "Prave doslo k predcasne aktivaci pres INT0" (bez teplot) pinMode(pin_LED,OUTPUT); // dlouhe bliknuti LEDkou digitalWrite(pin_LED,HIGH); delay(1000); digitalWrite(pin_LED,LOW); pinMode(pin_LED,INPUT); // na zaver kvuli uspore energie prepnout pin na vstup } } //============================================================================================================= //============================================================================================================= // uspi procesor do minimalni spotreby a ceka na vyprseni watchdogu void uspat(void) { if (digitalRead(pin_INT0) == HIGH) // dalsi probouzeni pomoci pinu INT0 je mozne pouze v pripade, ze se usina s tim pinem ve stavu HIGH { GIMSK |= 0b01000000; // zapne probouzeni pomoci pinu INT0 (bit 6 = '1') } USICR &= 0b00001111; // vypne USI, kvuli kteremu se budil procesor vzdycky po prijmu nejake nahodne komunikace na Rx pinu WDTCR = 0b01100001; // 8 sekund MCUSR = 0b00000111; // resetovaci bity (bity 2, 1, 0) do HIGH krome WDRF (bit 3) set_sleep_mode(SLEEP_MODE_PWR_DOWN); // nejhlubsi spanek MCUCR &= 0b11111100; // LOW level na INT0 generuje interrupt (ICS01 (bit 1) = '0' ; ICS00 (bit 0) = '0') sleep_enable(); sleep_mode(); // tady usne // *** *** *** // spanek s minimalnim odberem // tady se vzbudi nejakym interruptem (bud od WD, nebo od INT0) sleep_disable(); USICR |= 0b10000000; // znova zapne USI, ktere bylo vypnute kvuli nechtenemu probuzeni po prijmu 'OK' od Sigfoxu } //============================================================================================================= //============================================================================================================= // nastaveni vnitrnich registru, ktere staci provest jen 1x pri zapnuti napajeni void zakladni_nastaveni_registru(void) { ADCSRA &= 0b01111111; // vypne AD prevodnik kvuli snizeni spotreby (bit7 = '0') GIMSK &= 0b11011111; // vypne probouzeni pomoci pin change interrupt (bit 5 = '0') } //============================================================================================================= //============================================================================================================= // pri aktivaci WD program odskoci nejdriv sem ISR(WDT_vect) { wdt_disable(); // WD se deaktivuje a pokracuje se v probouzeni } //============================================================================================================= //============================================================================================================= // pri aktivaci INT0 program odskoci nejdriv sem ISR(INT0_vect) { GIMSK &= 0b10111111; // docasne vypne probouzeni pomoci INT0 (zapina se az pred dalsim uspanim) INT0_flag = true; } //=============================================================================================================