Meteo stanica 0310 i kako izgleda ispod kućišta [foto] [18+]

Nedavno sam zamijenio svoju skoro 10 godina staru uradi-sam žičnu Ethernet meteo stanicu baziranu na Arduinu (DHT22 + BMP085 + Geiger brojač sa SMB-20 cijevima montiran u drvenu kućicu u dvorištu) s novom integriranom stanicom s Aliexpressa, NicetyMeter 0310 Professional WiFi Weather Station koja, unatoč glupavom nazivu brenda, nije loša za cijenu po kojoj se može nabaviti.

Hardver

Meteo stanica sastoji se od displeja s mogućnošću spajanja na wifi mrežu (napaja se s tri AAA baterije i opcionalnim strujnim adapterom koji je opcionalan samo ako ne mislite koristiti Wifi, ako mislite, bez njega ne radi) i integriranim senzorom koji izgledom dosta podsjeća na Davis Vantage Vue (donje fotke su s web stranice prodavača), ali ima i UV senzor i usput mjeri sunčevo zračenje. Dolazi s kompletom za montažu, a osnovna razlika u odnosu na puno skuplje Davis “nadahnuće” je činjenica da vanjska jedinica ne puni svoje baterije (dakle solarni panel se koristi za napajanje senzora tijekom dana, a tri minjonke ga napajaju preko noći), što znači da se baterije moraju povremeno mijenjati. Proizvođač tvrdi da bi trebale potrajati barem godinu dana, što još trebam provjeriti. Nadopuna – upozorenje za nisku razinu baterije na unutarnjoj konzoli upalilo se nakon 19 mjeseci rada.

Kratkim guglanjem postaje očito da se ova kombinacija displeja i senzora prodaje pod različitim nazivima (Sainlogic WS 0310 i Ragova, između ostalog), dok se vanjski senzor koristi u još više kombinacija prijemnika i trgovačkih marki. Proizvođač je, čini se, StarMeter Instruments.

Cijela stvarčica nudi dobar odnos cijene i kvalitete, a nije šaomi. Kućište je od čvrste ABS plastike, ne odaje dojam da će vam se raspasti u ruci i ne djeluje jeftino. Jedini problem koji sam imao s vanjskim senzorom je priloženi plastični stup za montažu se pomalo klima, pa jači udari vjetra znaju aktivirati klackalicu koja broji količinu oborina. Ovo je možda i moja krivica jer je senzor montiran na antenski stup visine jednog metra koji je, pak, na ljestvama od 7 metara koja su montirana na zadnji vanjski zid kuće.

Skidanje podataka sa stanice

Osim što prikazuje podatke u stvarnom vremenu, displej konzola te podatke šalje na servise  Weather UndergroundWeathercloud. Priloženi priručnik za uporabu čak je i solidno preveden na engleski i jednostavno je konfigurirati stanicu da logira podatke online, ali ja ne bih bio ja da mi je to dosta. Oba servisa imaju svoja ograničenja – Weathercloud prihvaća podatke samo svakih 10 minuta i daje vam pristup samo zadnjoj godini dana vaših podataka bez pretplate, dok Wunderground prihvaća ažuriranja svaku minutu, ali API key koji dobijete za čitanje podataka dozvoljava 1500 poziva dnevno, dakle smijete povući podatke samo jednom u minuti.

Kako mi je proritet bio izraditi sigurnosnu kopiju podataka s moje meteo stanice (i objaviti ih na druge servise) dok ne nađem bolje rješenje, postavio sam genijalno jednostavnu Google Apps skriptu leoherzoga WundergroundStationForwarder koja preuzima podatke s Wundergrounda i prosljeđuje ih drugim servisima. Stvar trenutno radi poprilično pouzdano. Kad sam to riješio, trebalo je zaviriti ispod haube. Senzor šalje podatke svakih 16 sekundi, ali ja imam podatke svaku minutu. Sramota!

Pogled u kućište

Kako je bila velika vjerojatnost da displej ima neku vrstu ESP8266 procesora (Soft AP konfiguracija i njen naziv, WeatherHome-CFxxxxx, bili su glavni razlozi za sumnju), nadao sam se da će taj procesor imati i nekakav serijski debug za čitanje podataka.

Bio sam skoro u pravu. Displej ima ESP-WROOM-02D-V1.4,, ali njegova jedina svrha je logiranje podataka na web servise i sinkronizacija sata preko NTP protokola. Četiri rupice za nožice (označene s P5) pokraj tog procesora su, odozgo prema dolje, VCC, GND, TXD, RXD.

Port UART0 ne daje ništa korisnih podataka za debugging osim uobičajenog boot debuga tipičnog za ESP8266, pa mu je vjerojatna svrha fleširanje procesora. Boot log izgleda ovako:

ets Jan  8 2013,rst cause:1, boot mode:(7,7)

waiting for host
ets Jan  8 2013,rst cause:1, boot mode:(7,7)

waiting for host

ets Jan  8 2013,rst cause:2, boot mode:(3,7)

load 0x40100000, len 2592, room 16 
tail 0
chksum 0xf3
load 0x3ffe8000, len 764, room 8 
tail 4
chksum 0x92
load 0x3ffe82fc, len 676, room 4 
tail 0
chksum 0x22
csum 0x22

2nd boot version : 1.7(5d6f877)
SPI Speed : 40MHz
SPI Mode : QIO
SPI Flash Size & Map: 16Mbit(1024KB+1024KB)
jump to run user1 @ 1000

OS SDK ver: 2.0.0(e271380) compiled @ Mar 30 2018 18:54:06
phy ver: 1055_1, pp ver: 10.7

rf cal sector: 507
tcpip_task_hdl : 40107b28, prio:10,stack:512
idle_task_hdl : 40107bd8,prio:0, stack:384
tim_task_hdl : 40107d20, prio:2,stack:512
Load param success
Timer Server: time.windows.com
20000128128128000001281280
 ets Jan  8 2013,rst cause:2, boot mode:(3,6)

load 0x40100000, len 2592, room 16 
tail 0
chksum 0xf3
load 0x3ffe8000, len 764, room 8 
tail 4
chksum 0x92
load 0x3ffe82fc, len 676, room 4 
tail 0
chksum 0x22
csum 0x22

2nd boot version : 1.7(5d6f877)
SPI Speed : 40MHz
SPI Mode : QIO
SPI Flash Size & Map: 16Mbit(1024KB+1024KB)
jump to run user1 @ 1000

OS SDK ver: 2.0.0(e271380) compiled @ Mar 30 2018 18:54:06
phy ver: 1055_1, pp ver: 10.7

rf cal sector: 507
tcpip_task_hdl : 40107b28, prio:10,stack:512
idle_task_hdl : 40107bd8,prio:0, stack:384
tim_task_hdl : 40107d20, prio:2,stack:512
Load param success
Timer Server: time.windows.com
20000128128128000001281280
Mainboard, bottom view

Matična ploča ima i dva mikroprocesora (chip-on-board, zaliveni u epoksi smolu) koji pogone LCD displej i sve ostalo.

To sve ostalo uključuje:

  • Dvožični I2C EEPROM i senzor tlaka zraka. EEPROM ima izvučene rupe za nožice.
  • Prijemnik 433 MHz s izvučenim nožicama
  • Senzor vlažnosti zraka

Kako je ESP8266 priključen na glavni procesor/procesore preko nožica IO 12, IO 13, IO 14 i IO 15, očito je da komuniciraju preko SPI protokola, ali zaključio sam da bi istraživanje kako komuniciraju uzelo previše vremena, a procesor se javlja ESP modulu samo jednom u minuti kada treba logirati podatke. Podatke s vanjskog senzora morat ću pribaviti na neki drugi način.

Prisluškivanje komunikacije vanjskog senzora

Sljedeći je korak bio priključiti SDR (software-defined radio) prijemnik baziran na čipu RTL2832U (verzija 25 MHz do 1760 MHz, jeftikaner s Alija za 10 dolara) i vidjeti što on čuje na 433 MHz pomoću dekodera RTL_433. Nije razočarao. Najnovija verzija dekodera RTL_433 prepoznala ga je kao Cotech-367959 (i druge stanice koje koriste isti senzor, kao što su SwitchDoc Labs Weather FT020T,  Sainlogic Weather Station WS019T i vjerojatno mnoge druge).

RTL_433 identifies the station as Cotech-36759

Imajte na umu da trenutno samo najnovija verzija RTL-433 podržava ovaj senzor u potpunosti i pravilno dekodira UV indeks i intenzitet sunčevog zračenja, dok Debian paket (ako ga instalirate s apt install rtl-433) nema to dvoje, pa sam ga morao buildati iz sourcea za moj Orange Pi Zero na Armbianu.

Korištenje podataka

Ovo je pak značilo da podatke stanice jednostavno mogu uvesti u WeeWX pomoću weewx-sdr drajvera. Međutim, taj drajver trenutno ne podržava senzor Cotech-367959, pa je trebal dodati tu klasu u datoteku sdr.py. Ona se obično nalazi na weewx/user/sdr.py nakon instalacije ekstenzije u weewx.

class Cotech367959Packet(Packet):
    # ['{"time" : "2022-04-28 14:25:59", "model" : "Cotech-367959", "id" : 109, "battery_ok" : 1, "temperature_F" : 63.000, "humidity" : 55, "rain_mm" : 16.200, "wind_dir_deg" : 296, "wind_avg_m_s" : 0.900, "wind_max_m_s" : 1.300, "light_lux" : 23093, "uv" : 14, "mic" : "CRC"}']
    IDENTIFIER = "Cotech-367959"

    @staticmethod
    def parse_json(obj):
        pkt = dict()
        pkt['dateTime'] = Packet.parse_time(obj.get('time'))
        pkt['usUnits'] = weewx.METRIC
        sensor_id = obj.get('id') # changes every time you reset the outdoor sensor
        pkt['battery'] = 0 if obj.get('battery') == 'OK' else 1
        pkt['temperature'] = to_C(Packet.get_float(obj, 'temperature_F'))
        pkt['humidity'] = Packet.get_float(obj, 'humidity')
        pkt['wind_gust'] = Packet.get_float(obj, 'wind_max_m_s')
        pkt['wind_speed'] = Packet.get_float(obj, 'wind_avg_m_s')
        pkt['wind_dir'] = Packet.get_float(obj, 'wind_dir_deg')
        pkt['total_rain'] = Packet.get_float(obj, 'rain_mm')
        pkt['light_lux'] = Packet.get_int(obj, 'light_lux')
        pkt['uv'] = Packet.get_int(obj, 'uv') / 10
        pkt = Packet.add_identifiers(pkt, sensor_id, Cotech367959Packet.__name__)
        return pkt

Imajte na umu i da se vrijednost id mijenja kad god pritisnete tipku za resetiranje na vanjskom senzoru. Ako ste identificirali svoj senzor i namjeravate ga trajno koristiti na ovaj način, preporučam da tu vrijednost zamijenite fiksnom.

Kao što smo uočili u komentarima članka, izvorni sdr.py nema funkciju koja radi konverziju stupnjeva Fahrenheita u Celsiusove, pa je trebate dodati (pomoćne funkcije počinju od 169. retka sdr.py).

def to_C(v):
    if v is not None:
        v  = 5 / 9 * (v - 32)
    return v

Da biste vidjeli te podatke u WeeWXu, morate mapirati mjerenja u datoteci /etc/weewx/weewx.conf:

[SDR]
    # This section is for the software-defined radio driver.
    # The driver to use
    driver = user.sdr

    [[sensor_map]]
        windDir = wind_dir.109.Cotech367959Packet
        windSpeed = wind_speed.109.Cotech367959Packet
        windGust = wind_gust.109.Cotech367959Packet
        outTemp = temperature.109.Cotech367959Packet
        outHumidity = humidity.109.Cotech367959Packet
        rain_total = total_rain.109.Cotech367959Packet
        UV = uv.109.Cotech367959Packet
        luminosity = light_lux.109.Cotech367959Packet

U gornjem primjeru 109 je ID moje stanice, vaš će se razlikovati (ako ga ne zamijenite fiksnim, što preporučam, vidi gore).

Sada sam imao gotovo sve podatke koje sam trebao, osim tlaka zraka – vanjska jedinica nema taj senzor. U tu svrhu na svoj Orange Pi / Raspberry Pi sam spojio BME180 breakout pločicu preko I2C porta. Podatke sam povukao u WeeWX ovim Bme280wx drajverom.

Trebalo je kombinirati podatke – vrijednosti senzora BME180 mapirao sam kao inHumidity, inTemp i pressure u datoteci /etc/weewx/weewx.conf kao u donjem primjeru.

[Bme280wx]
i2c_port = 0 # OrangePi and older Raspberry Pi's use 0, usually it's 1
i2c_address = 0x76
usUnits = US
temperatureKeys = inTemp
temperature_must_have = ""
pressureKeys = pressure
pressure_must_have = outTemp
humidityKeys = inHumidity
humidity_must_have = ""

Uz to, treba reći WeeWXu da učita taj dodatni drajver u odjeljku Services datoteke  weewx.conf.

[Engine]

    # The following section specifies which services should be run and in what order.
    [[Services]]
        data_services = "", user.bme280wx.Bme280wx

Vrijednost station_type u odjeljku [Station] mora biti navedena kao SDR, jer je to glavni drajver, a BME280 je dodatni.

[Station]
    [...]

    # Set to type of station hardware. There must be a corresponding stanza
    # in this file, which includes a value for the 'driver' option.
    station_type = SDR

Weewx s tim podatcima dostupan je na
https://meteo-valpovo.žnj.hr/

Budućnost

Kako ne polažem previše nade da će jeftini SDR prijemnik savršeno raditi godinama, možda ću pokušati složiti dedicirani prijemnik 433 MHz baziran na ESP32 ili ESP8266 procesoru koji će prosljeđivati podatke dalje. Za ovo mi nedostaje port RTL-433 dekodera u obliku biblioteke za Arduino, pa mi preostaje čekati da netko pametniji to odradi.

Za sada mi je dovoljno imati lokalnu kopiju podataka sa svoje meteo stanice i drugi način objavljivanja podataka na vanjskim servisima koji ne ovisi o samom prijemniku koji sam dobio sa stanicom jer sumnjam da će izaći ažuriranje firmvera u slučaju da Weather Underground ili WeatherCloud iznenada promijene svoje API-je za slanje podataka.

Digitalna oglasna ploča

Ovoga puta donosim jedan jednostavan i jeftin projekt koji sam napravio za posao – digitalna oglasna ploča za školu u kojoj radim. Plan je bio prenamijeniti jedan veći LED TV, montirati ga u glavni hol kroz koji svi učenici prolaze i koristiti ga kao podsjetnik s korisnim, servisnim informacijama. U istom holu i roditelji najčešće čekaju termine za informacije, pa je cijela stvar i njima korisna.

Od “offline” rješenja i korištenja preglednika fotografija/videa s USB sticka smo, naravno, u startu odustali jer sam htio nešto što određen krug suradnika može lako ažurirati. Kako nam je dostupni TV opremljen HDMI-jem, odlučio sam se iskoristiti Raspberry Pi. Poduže guglanje i pregled dostupnih komercijalnih, polukomercijalnih i besplatnih rješenja za digital signage nije ponudilo jasnog favorita – rješenja su bila komplicirana za uređivanje i bila su fokusirana na video, nešto što sam smatrao nepotrebnim za ispisivanje kratkog teksta, nekoliko fotografija ili eventualne jednostavne animacije.

Na taj način odlučio sam se za kombinaciju web stranice i fullscreen browsera. Istestirao sam više kombinacija, uključujući i digital signage temu za WordPress, ali kod nje su mi nedostajale određene mogućnosti uređivanja. Kao logično rješenje nametnulo se prikazivanje HTML inačice nekog od online servisa za prezentacije, Microsoftov PowerPoint online i Presentations u Google Docsu. Pritom sam kod Microsoftovih kombinacija imao određene probleme s prijavom odnosno s javnim pristupom HTML verziji prezentacija, pa je konačni odabir pao na Google Docs Presentations.

Pripremio sam prezentaciju i opcijom objavljivanja na web dobio kod koji sam mogao otvoriti u Chromiumu na Raspberry Piju. Prikazao sam stranicu na cijelom ekranu i dobio nešto ovako:

Kao što vidite, ekran nije cijeli ispunjen jer Google inzistira na prikazivanju alatne trake na dnu ekrana.  Za uređivanje prikaza postoji poludokumentirana/nedokumentirana opcija “rm=minimal” koja uklanja i tu traku, pa URL za ugrađivanje izgleda ovako:

https://docs.google.com/presentation/d/[ID PREZENTACIJE]/embed?start=true&loop=true&delayms=15000&rm=minimal

Kad je već bilo izvjesno da će se raditi o HTML-u/CSS-u koji prikazuje iframe s prezentacijom, u cijelu kombinaciju sam dodao sat i prikaz vanjske temeprature u javascriptu.

HTML/JavaScript ovoga svega slobodno je dostupan na samoj stranici koja se koristi za prikaz na adresi https://kiosk.oskatancic.hr/, ali za svaki slučaj arhivirat ćemo ga i ovdje na blogu.

<!DOCTYPE html>
<html>
  <head>
    <link href="https://fonts.googleapis.com/css?family=Droid+Sans+Mono|Days+One|Source+Sans+Pro" rel="stylesheet" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta charset="utf-8" />
    <title>Kiosk</title>
    <style>
html, body {
	background: white;
	padding: 0;
	margin: 0;
	width: 100%;
	height: 100%;
}

.container {
	width: 100%;
	height: 100%;
	overflow: hidden;
    z-index: 1;
}
.container iframe {
	height: 100%;
	width: 100%;
}

.boks {
    font-family: 'Droid Sans Mono', monospace;
	color: red;
	position: absolute;
	right: 2%;
	top: 5%;
    z-index: 2;
	width: 24%;
	height: 9%;
    font-weight: bold;
	display: flex;
    align-items: center;
    justify-content: center;
    font-size: 5vw;
	background: linear-gradient(#a6e3f8fa, #b9e9fafa);  
}

.kalendar {
    font-family: 'Helvetica', sans-serif;
    font-weight: bold;
	color: #1155cc;
	position: absolute;
	bottom: 1%;
	left: 6%;
	z-index: 2;
	width: 90%;
	height: 3.4vw;
	font-size: 2.9vw;
	overflow: hidden;
	background: #e5e5e5;
	overflow: hidden;
    margin: 0;
    padding: 0;
    list-style: none;
}
.kalendar li {
	height: 3.4vw;
	padding: 0px;
	margin: 0px 5px;
}

#boks2 {
	top: calc(5% + 9%);
	background: linear-gradient(#b9e9fa90, #c7edfa90);  
    font-family: 'Days One', sans-serif;
    font-weight: normal;
}

.clock {
}
ul {
	list-style: none;
	padding-left: 0px;
}

ul li {
	display: inline;
}

#point {
	position: relative;
    padding-left: 0px;
    padding-right: 0px;
}
#weather-temperature {
	z-index: 2;
}

/* Animations */
@-webkit-keyframes blink {
	0% { opacity: 1.0;}
	50% { opacity: 0;}
	100% { opacity: 1.0;};      
}

@-moz-keyframes blink {
	0% {opacity: 1.0;}        
	50% {opacity: 0;}
	100% {opacity: 1.0;};
}
</style>
  </head>
  <body>
  <div class="container">
    <iframe id="presentFrame"
    src="https://docs.google.com/presentation/d/[ID_PREZENTACIJE]/embed?start=true&amp;loop=true&amp;delayms=15000&amp;rm=minimal"
    frameborder="0" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>
  </div>
  <div class="boks">
    <div class="clock">
      <ul>
        <li id="hours"></li>
        <li id="point">:</li>
        <li id="min"></li>
        <li id="point">:</li>
        <li id="sec"></li>
      </ul>
    </div>
    <br />
  </div>
  <div class="boks" id="boks2">
    <div id="weather-temperature"></div>
  </div>
  <div id="kalendar-data" class="kalendar-boks"></div>
  <script src="js/jquery-1.12.4.min.js"></script> 
  <script type="text/javascript">
// Javascript za iscrtavanje sata
$(document).ready(function() {
        setInterval( function() {
                $(".clock").css("visibility", "visible");
                // Create a new Date() object and extract the seconds, minutes and hours...
                var time = new Date();
                var seconds = time.getSeconds();
                var minutes = time.getMinutes();
                var hours = time.getHours();
                $("#sec").html(( seconds < 10 ? "0" : "" ) + seconds);
                $("#min").html(( minutes < 10 ? "0" : "" ) + minutes);
                $("#hours").html(( hours < 10 ? "0" : "" ) + hours);
        },1000);
        
});
</script> 
  <script type="text/javascript">
// Osvježavanje temperature
$(function worker(){
        $.ajaxSetup ({
cache: false,
complete: function() {
	setTimeout(worker, 10*60*1000);
                }
        });
        // load() functions
        var loadUrl = "temp";
        $("#weather-temperature").html("...");
        $("#weather-temperature").load(loadUrl);
        // end  
});
</script> 
  <script type="text/javascript">
// osvježavanje prezentacije
var refreshMinutes = 30;
var timer=setInterval(function(){refreshFrame()}, refreshMinutes*60*1000);
function refreshFrame()
{
        // var iframe = document.getElementById('presentFrame');
        // var iframeURL = iframe.src;
        // iframe.src = iframeURL;
        window.location.reload(true);
}
</script> 
  <script type="text/javascript">
// Ispis kalendara
$(function worker(){
        $.ajaxSetup ({
cache: false,
complete: function() {
	setTimeout(worker, 10*60*1000);
	}
	});
	// load() functions
	var loadUrl = "kalendar.cache";
	$("#kalendar-data").html("...");
	$("#kalendar-data").load(loadUrl);
	// end  
});
</script> 
<script type="text/javascript">
  // ticker kalendara
function tick(){
    $('#kalendar li:first').fadeToggle( function () { $(this).appendTo($('#kalendar')).fadeToggle(); });
}
setInterval(function(){ tick () }, 6000);
</script></body>
</html>

Podatak o temperaturi dolazi iz PHP skripte koja je uzima dobiva iz baze i vraća čisti neformatirani tekst koji JavaScript periodično iščitava i prikazuje iznad prezentacije.

Događaje iz Google kalendara priprema druga PHP skripta, koja preko Google Docs API-ja povlači zadani broj događaja i formatira ih u obliku liste.

<?php
setlocale(LC_ALL, 'hr_HR.UTF-8');
$apikey = 'API_KEY'; // YOUR API key here!
$sada = date(DATE_ATOM);
$limit = new DateTime();
$limit = $limit->add(new DateInterval("P1M"))->format(DATE_ATOM);
$sada = urlencode($sada);
$limit = urlencode($limit);
$brojrezultata = 100;
$adresa = "https://www.googleapis.com/calendar/v3/calendars/oskatancic.hr_[ID_KALENDARA]%40group.calendar.google.com/events?maxResults=$brojrezultata&orderBy=startTime&singleEvents=true&timeMin=$sada&timeMax=$limit&key=$apikey";
$response = file_get_contents($adresa);
$eventi = json_decode($response, true)['items'];
	
echo "<ul id=\"kalendar\" class=\"kalendar\">";
if (!empty($eventi))
{
foreach ($eventi as $dogadjaj)
{
	if (array_key_exists('dateTime', $dogadjaj['start']))
	{
	$dan =  strftime("%A, %e.%-m", strtotime($dogadjaj['start']['dateTime']));
	$pocetak =  date("G:i", strtotime($dogadjaj['start']['dateTime']));
	$recenica_vrijeme = "$dan u $pocetak sati"; 
	}
	else if (array_key_exists('date', $dogadjaj['start']))
	{
	$dan =  strftime("%A, %e.%-m", strtotime($dogadjaj['start']['date']));
	$recenica_vrijeme = "$dan"; 
	}
	$opis = $dogadjaj['summary'];
	echo "<li style=\"display: list-item\">$recenica_vrijeme: $opis</li>";
}
}
else
{
	echo "Nema najavljenih događaja";
}
echo "</ul>"; 
?>

Što se tiče same pripreme Raspberry PI-ja, koristio sam zadnju inačicu Raspbiana (desktop varijanta) uz određene izmjene.

  1. Pomoću raspi-conf  treba uključiti automatsku prijavu u desktop korisnika “pi”
  2. Postaviti mrežu pomoću GUI ili shell alata (ako koristite bežičnu mrežu)
  3. Instalirati paket unclutter koji skriva strelicu miša
    sudo apt install unclutter 
  4. Urediti autostart korisnika koji se nalazi u ~/.config/lxsession/LXDE-pi/autostart
    @lxpanel --profile LXDE-pi
    @pcmanfm --desktop --profile LXDE-pi
    @xscreensaver -no-splash
    @point-rpi
    @unclutter -idle 0.1
    @xset s off
    @xset -dpms
    @xset s noblank
    @chromium-browser --app=https://kiosk.oskatancic.hr/ --temp-profile --no-touch-pinch --kiosk
    

Još jedna stvar koja se pokazala korisnom je uključivanje flaga u Chromiumu koji automatski ponovno učitava stranicu kada se uspostavi mrežna povezanost (ukoliko, recimo, osvježavanje prezentacije započne u trenutku kada RPI izgubi mrežnu povezivost). Jedini način za uključivanje ove opcije je iz Desktop verzije Chromea/Chromiuma preko chromium://flags.

Sve ovo je, naravno, vječito u izradi i stalno se mijenja, ali trebalo bi vam moći poslužiti kao osnova.