Jakieś 1,5 roku temu kupiłem sobie uszkodzoną stację pogodową. Pełno w sieci można znaleźć takich ofert, gdzie zazwyczaj nie działa wyświetlacz albo komunikacja. O ile w przypadku wyświetlacza sprawa jest dość prosta czyli wiemy, że jest spora szansa, że jednostka zewnętrzna działa to w przypadku komunikacji może być różnie.
Za jakieś 50 zł + wysyłka kupiłem na OLX, termometr oraz stację pomiarową z pomiarem temperatury, wilgotności, prędkości wiatru, kierunku. Jest też pomiar opadu deszczu i jakieś mniej istotne parametry dla mnie jak np. nasłonecznienie (w LUX) i UV.
Skoro komunikacja nie działała postanowiłem zainwestować w RTL-SDR, aby sobie te dane samem czytywać. Na samym początku podpiąłem urządzenie do komputera i odpaliłem program rtl_433, który przeskanowała dla mnie różne częstotliwości abym mógł zobaczyć co się dzieje w powietrzu. Takie urządzenie ma całkiem spore możliwości. Można odbierać telewizję, można poosłuchiwać komunikację lotniczą, a nawet satelity pogodowe.
Wszystko zależy tak na prawdę od tego jaką mamy antenę.
Home Assistant
Gdy już upewniłem się, że jestem wstanie odczytać dane ze stacji pogodowej zainstalowałem sobie rtl_433 na rpi, który otrzymane wiadomości potrafi od razu przesłać do serwera mqtt. Wiadomości zaś odczytuje HA i przesyła je do influxDB. Gdy już dane są w bazie to jesteśmy w domu.
Grafana
Ze względu na to, że lubię różne statystyki. Postanowiłem napisać sobie zapytanie, które pobierze mi dane z influxDB do grafany.
import "math"
import "join"
data = from(bucket: "homeassistant")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "%" or r["_measurement"] == "km/h")
|> filter(fn: (r) => r["_field"] == "value")
|> filter(fn: (r) => r["domain"] == "sensor")
|> filter(fn: (r) => r["entity_id"] == "sensorroofwinddir" or r["entity_id"] == "sensorroofwindavg")
|> filter(fn: (r) => r["friendly_name"] == "SensorRoofWindAvg" or r["friendly_name"] == "SensorRoofWindDir")
|> aggregateWindow(every: 1m, fn: mean, createEmpty: true)
|> fill(usePrevious: true)
|> filter(fn : (r) => exists r["_value"])
|> keep(columns: ["_time", "_value", "friendly_name"])
|> pivot(columnKey:["friendly_name"], valueColumn: "_value", rowKey: ["_time"])
|> map(fn: (r) => ({ r with id: 1, _time: 1,SensorRoofWindDir: (math.round(x: (r["SensorRoofWindDir"] + 15.) / 30.) * 30.) % 360., SensorRoofWindAvg: math.round(x: r["SensorRoofWindAvg"] / 20.) * 20. }) )
|> group(columns: ["id", "SensorRoofWindDir", "SensorRoofWindAvg"])
|> sum(column: "_time")
|> group()
total = data
|> keep(columns: ["_time"])
|> sum(column: "_time")
|> group()
|> map(fn: (r) => ({id: 1, total: r._time}))
join.tables(
method: "inner",
left: total,
right: data,
on: (l, r) => l.id == r.id,
as: (l, r) => ({l with time: r._time, SensorRoofWindDir: r.SensorRoofWindDir, SensorRoofWindAvg: r.SensorRoofWindAvg}),
)
|> map(fn: (r) => ({r with timep: float(v: r.time) / float(v: r.total)}))
|> sort(columns: ["SensorRoofWindAvg"])
Zapytanie robi kolejno:
- Pobiera wszystkie dane z odpowiednich czujników w tym przypadku, kierunek i siła wiatru
- Zaokrągla prędkość wiatru i kierunek aby łatwiej dało się to połączyć
- Przerabia dane na postać ile minut trwał rekord np. 3 minuty wiało z kierunku 180 stopni o sile 20km/h
- Grupuje wszystko po kierunku i prędkości, a liczbę minut sumuje
- Na koniec liczy jaki to procent całego czasu
Następnie użyłem biblioteki Apache Echart to wyświetlenia ich w odpowiedniej formie.
const dane = data.series[0];
const speeds = [0, 20, 40, 60, 80, 100, 120];
function getDirections(selected) {
let dis = [];
if (selected != undefined) {
dis = Object.entries(selected).reduce((accumulator, value) => {
if (value[1] === false) {
console.log(value[1])
accumulator.push(parseInt(value[0]))
}
return accumulator
}, [])
console.log(dis);
}
// find max
let max = 0;
console.log(dane)
for (let i = 0; i < dane.fields[5].values.length; i++) {
if (dane.fields[5].values[i] > max && !dis.includes(dane.fields[0].values[i])) {
max = dane.fields[5].values[i];
}
}
var directions = [];
for (var i = 0; i < 12; i++) {
directions.push({ name: 360 - i * 30, max: max * 100 });
}
return directions;
}
function genOptions() {
var directions = getDirections();
function find(dir, speed) {
// DIR
for (let i = 0; i < dane.fields[1].values.length; i++) {
if (dane.fields[1].values[i] == dir && dane.fields[0].values[i] == speed) {
return dane.fields[5].values[i];
}
}
return 0.0;
}
const speeds = [0, 20, 40, 60, 80, 100, 120];
var data = speeds.map((value) => {
return {
value: directions.map((dir) => Math.round(find(dir.name, value) * 10000) / 100),
name: "" + value + "km/h"
}
});
return option = {
color: [
"#FD0100", "#F76915", "#EEDE04", "#A0D636", "#2FA236", "#333ED4", "#FFF"
].reverse(),
tooltip: {
trigger: 'item'
},
title: {
text: 'Kierunek vs prędkość'
},
legend: {
data: data.map((value) => value.name)
},
radar: {
shape: 'circle',
indicator: directions
},
series: [
{
name: 'name',
type: 'radar',
data
},
]
};
}
echartsInstance.on('legendselectchanged', function (params) {
console.log(params);
echartsInstance.setOption({
radar: {
indicator: getDirections(params.selected)
}
});
});
return genOptions();
W ten sposób otrzymałem różę wiatrów, która jest w stanie pokazać mi z jakiego kierunku wieje najwięcej wiatru i o jakiej sile. Zrobiłem też podobny wykres zamieniając kilka rzeczy w zapytaniu i skrypcie tak abym mógł zobaczyć w jakich godzinach wieje mocniejszy wiatr.
Efekt można zobaczyć tutaj:
W sumie świetnie to wygląda. Zastanawiam się czy są jakieś otwarte inicjatywy udostępniania danych pogodowych, czy coś wiesz na ten temat
Ogólnie jest bardzo dużo serwisów gdzie możesz dodać swoją stację pogodową i wysyłać dane po odpowiednim API. Trudno mi powiedzieć, które są na tyle otwarte aby np. każdy mógł sobie te dane wziąć do analizy i np. karmienia sztucznej inteligencji danymi w celu przewidywania pogody. Nawet openweathermap.org ma jakieś płatne subskrypcje więc chyba daleko im do pełnej otwartości. Domyślam się, że płatne subskrypcje mają pokrywać koszt infrastruktury. Hmmm... gdyby tak pchać takie dane do hive? XD