We leggen graag uit hoe je de data van onze stadssensoren zelf ook kunt gebruiken. Deze uitleg hoort bij ons Smart City project voor de Kanaalzone in Alkmaar: https://junioriot.nl/burgernetwerken/

In vogelvlucht – hoe stroomt de data door onze systemen

Onze software op de sensoren zorgt ervoor dat de meetgegevens als een compact bericht wordt verstuurd via Lora. Volgens de regels van Lora sturen we ongeveer eens per 4 minuten een data bericht. Zodra dit wordt ontvangen door TTN, The Things Network, zorgt onze decoder software dat er per verzonden bericht een los bestand ontstaat, een file in Json formaat. Met Json kunnen we flexibel velden toevoegen, aanpassen en verwijderen.

Wij hebben ingesteld dat elk Json bericht direct doorstroomt naar onze dataopslag in onze data cloud bij Amazon AWS, waar het direct via een Amazon API wordt doorgegeven aan onze Lambda functie, waarmee meerdere files worden aangemaakt in de S3 opslag. In een configuratieveld bij deze Lambda functie stellen we nog enkele parameters in voor het spelen van het GPS spel, en het registreren van extra data op de kaart. We slaan alle files voor je op in een structuur die past bij de aanpak met partitionering voor Big Data analyse in AWS. De files zijn via static web hosting op de S3 bucket en Route53 beschikbaar onder een unieke url. We merken dat de file binnen enkele secondes na de meting al beschikbaar is; zo kunnen we elke meting echt ‘real-time’ volgen.

Elke opgeslagen datafile kan je benaderen in je webbrowser. De filenamen zijn uniek, met de datum/tijd van de meting, en zijn daardoor echter onvoorspelbaar. Om de losse datafiles te vinden, hebben we ervoor gezorgd voor index bestanden. Omdat de filenaam van deze indexen wel voorspelbaar is, kunnen we hiermee de meetdata makkelijk automatisch ophalen.

Met een webapplicatie voor de Smart City Challenge in 2018 laten we zien hoe je deze meetdata makkelijk kunt ophalen en zichtbaar maken. Alle software voor de landkaart en de bijbehorende grafieken draait gewoon in de browser. Daardoor kan je de code van deze software zelf bekijken om te onderzoeken hoe wij dit hebben gedaan. Je ontdekt dan hoe we omgaan met de indexen en de logfiles.

Als je zelf gaat bouwen: Let op dat je bewust omgaat met het lezen van onze data. Zeker voor automatische data loads naar jouw server werk je alleen met de nieuwe aanvullingen. We willen voorkomen dat er continu data wordt gelezen. Bij meer dan ‘incidenteel’ gebruik zal de rekening voor onze dataopslag merkbaar op gaan lopen, en dat is geen deel van onze propositie. Je wilt voorkomen dat jouw datafeed per ongeluk automatisch wordt afgesloten.

Uitgangspunten – basic principles

Met 20 jaar ervaring in ICT, datamanagement en Big Data kan ik een aantal handvatten formuleren waarmee we makkelijker onze data omgeving kunnen inrichten, onderhouden en verder kunnen laten groeien.

Serverless data flow

De verwerking van Lora berichten in TTN verloopt via een samenwerking van queues, buffers en transformaties. Een goede configuratie zorgt ervoor dat elk bericht onderweg de juiste stappen doorloopt, en uitkomt bij de verwerking of de opslag die bij dat bericht past.

Op geen enkel moment is het nodig om ons daarbij druk te maken over technische aspecten die te maken hebben met hardware en servers: We hoeven niets te weten, in te stellen of te reserveren rond het aantal servers, verwerkingscapaciteit, CPU’s, bandbreedtes, hosting, opschalen en loadbalancing. Dit is binnen TTN voor ons geregeld, onzichtbaar verstopt achter de services die we gebruiken.

De rest van de datapipeline hebben we op dezelfde manier opgebouwd. Daardoor maakt het voor de hele pipeline niet uit of we één bericht per minuut moeten verwerken, of drie miljoen – ons landschap is technisch reeds geoptimaliseerd voor beide scenario’s.

Kosten. We zien geen losse servers, en we huren geen losse servers. Dit maakt de kosten beter beheersbaar. We betalen voor het opslaan en opvragen per bericht een paar microcent; de kosten dansen daardoor naadloos mee omhoog als jouw gebruik omhooggaat.

Geen traditioneel gebruik van data

Traditioneel data gebruik is geen Big Bata: Traditioneel wordt data gefilterd en opgeslagen in databases en tabellen die zijn afgestemd op een zeker gebruiksdoel. Denk dan aan een webwinkel met orders, producten en inventaris. De applicatie die erbij hoort bepaalt welke datasets nodig zijn. Voor elke toepassing wordt gericht een datastructuur ontworpen, met vaste tabellen en kolommen.

Dit ontwerp is alleen geldig gedurende de lifecycle van de betreffende applicatie. Naast de applicatie heeft de data weinig reden om te bestaan. In onze toepassing gebruiken we een andere benadering om juist de data centraal te stellen.

Onze data heeft Big Data eigenschappen

Bij onze meetdata begrijpen we intuïtief al dat de waarde in de data zit, en een applicatie van ondergeschikt belang is. Een weergave applicatie is alleen maar een hulpmiddel of een instrument om iets met deze meetdata te doen.

Onze meetdata leeft. We leren nieuwe dingen, en bouwen nieuwe sensoren waarmee weer andere dingen worden gemeten. In de datastroom ontstaan vanaf een zekere datum nieuwe velden. Op andere momenten in de lifecycle kunnen velden ook weer verdwijnen.

De vastgelegde meetwaarden welke op een zeker meetmoment zijn vastgelegd zullen echter nooit veranderen. Iets wat eenmaal is vastgelegd, hoeft niet meer te veranderen.

Dit ‘meetwaardes veranderen niet’ is erg belangrijk. De originele files blijven ongewijzigd, ook als we onderweg iets nieuws leren, waardoor we de gemeten waardes anders interpreteren. De interpretatie van de data staat los van de dataopslag.

Dataflow gaat altijd vooruit

Bij Big Data tools is het een gegeven dat opgeslagen datasets niet worden gemuteerd. Het past beter bij de Big Data gedachte om data niet te interpreteren of te bewerken voordat deze wordt opgeslagen. Opgeslagen data is bovendien read-only. Zo is er altijd maar één versie van de waarheid. Data ‘in motion’ kan wel veranderen.

Deze gedachte leg ik uit aan de hand van onze decibel waardes. Technisch meten we een analoge geluidssterkte, maar in alle communicaties spreken we over decibel. De decibel waarde is dan de uitkomst van een berekening, het is aldus eigenlijk een interpretatie als onderdeel van de datastroom. Dit betekent voor ons dat we in de datastroom de decibel waarde toevoegen en dan ook de originele meetgegevens behouden in de databerichten.

Indien we later door onze ervaring een betere formule ontdekken voor de decibel waarde, dan kunnen we een verbeterde versie toevoegen in de interpretatie. Maar op dat moment zullen we deze niet opslaan als deel van de originele bestanden; immers deze blijven ongewijzigd.

Velden en veld namen

We leggen de waardes vast met duidelijke, en vooral betekenisvolle namen. Het kan daarbij door groeiend inzicht gebeuren dat bij de ontwikkeling van ons product een aantal veldnamen wordt aangepast. We proberen deze aanpassingen zoveel mogelijk te beperken.

In de veldnamen gebruiken we een soort hiërarchie of groepering binnen de naam. Bijvoorbeeld alle waarden afkomstig van de BME280 sensor beginnen met BME280_. Een handig bijeffect is dat in lijsten, toelichtingen, data weergaven deze velden doorgaans alfabetisch bij elkaar gesorteerd worden.

Als eenmaal een correcte benaming is gekozen, dan houden we hieraan vast, zolang de betekenis van de data gelijk blijft. Ook bij herstel van fouten in de meetapparatuur behouden we dezelfde veldnamen; ook als een trendbreuk ontstaat in de data. Hergebruik van een veldnaam voor een geheel andere betekenis is niet de bedoeling.

Een eenmaal gekozen naam hanteren we vervolgens door alle stadia die de data doormaakt. Dit kan, en moet, want de waarde verandert immers niet.

In de decoder zie je de namen voor de output values, zoals de velden in de Json genoemd worden. De interne naam in de decoder software is dan dezelfde variabele naam met een _ underscore als prefix. Zo blijft de naamgeving voldoende consistent. Het doel is dat dezelfde variabele namen worden gebruikt in de Arduino code waar het Lora bericht wordt aangemaakt.

Geen edits en deletes in opgeslagen data

Elk sensor meetmoment levert een databestand met meetwaarden. Deze bestanden worden in principe niet meer aangepast.

Mochten we willen breken met deze regel, dan is dat alleen als het écht niet anders kan worden opgelost. Een oplossing via verdere verwerkingsstappen, of in de visualisatie laag gaat altijd voor. En als we toch de bestanden aanpassen, dan voegen we alleen velden toe; nooit vervangen of verwijderen we bestaande data.

We vragen je ook deze gedachte aan te houden in de verdere verwerking van de data. Als je onze files overneemt in ons systeem, hou dan alle data aan. Ook als je er nog geen doelstelling voor hebt. Geloof mij, dit voorkomt je een hoop vermijdbare re-engineering en vervelende excuus vergaderingen.

Afleidingen, formules zijn herhaalbaar, brondata altijd opgeslagen

Doordat de originele meetdata altijd door de hele data pipeline beschikbaar blijft, kunnen andere varianten op de interpretaties worden ontwikkeld.

Voor wetenschapers klinkt het als onwaarschijnlijk dat een interpretatie noemenswaardig verandert. De formule voor een decibel is immers behoorlijk duidelijk vastgelegd. Een denkbaar voorbeeld is wellicht indien we ontdekken dat we de wegingsfactoren in onze formule voor dB(A) anders hebben ingesteld dan onze partner RIVM dit verwacht. Het zou zomaar kunnen.

Een veranderende interpretatie is wel meer gebruikelijk bij financiële data waarmee bedrijfsbeslissingen worden gestuurd. En een andere betekenis vraagt hierbij telkens om een andere veldnaam. Zo kunnen meerdere interpretaties zonder probleem naast elkaar blijven bestaan.

Data afnemers

Voor partijen wie de data willen gebruiken hebben we een handreiking welke files ze daarvoor kunnen lezen: https://junioriot.nl/data-koppelen/

Data afnemers lezen alleen de delta’s

Met de informatie onder de andere kopjes begrijpen we nu dat data die eenmaal is gepubliceerd niet zal veranderen. Waar bij traditionele data de data middels transacties continu verandert, is dat bij ons nadrukkelijk niet het geval.

Hierdoor is het nuttig om als data afnemer dagelijks alleen de ontbrekende files van de voorgaande dag in te laden. Deze opmerking is relevant.

In de praktijk, bijvoorbeeld bij Azure, is het veel makkelijker om een ‘full initial load’ te configureren, en deze dan dagelijks te herhalen. De systemen zijn dan iedere dag data opnieuw aan het overpompen die hoofdzakelijk niet verandert. De ontwikkelaar komt weg met de uitleg ‘maar de brondata kan veranderen’ – deze uitleg geldt in ons geval echter niet. Te veel herhaling kan bovendien worden gedetecteerd als misbruik. Het lijkt erop dat het zelfherstellend vermogen van onze serverless omgeving een dergelijke afnemer de toegang tot de data ontzegt, geheel automatisch, en zonder dat wij het merken.

Suggesties om het anders te doen, en lessons learned

Onze datapipeline is ontstaan bij ons eerste GPS tracker project wat in 2016 is begonnen. Maar ook de beslissingen in dat project steunen weer op ervaring en vuistregels uit datawarehousing en datamanagement. Het doel is onze data en de onderdelen van de pipeline een zo lang mogelijke Lifecycle of zo lang mogelijk bestaansrecht te geven.

Je zou onze pipeline kunnen aanpassen om makkelijker te passen bij de klik-en-sleep versie in Azure van vandaag. Of we kunnen overleggen over welke velden je wel en niet nodig hebt, en dat je kleinere files leuker vindt. En vanuit een ander applicatie perspectief zou je andere wensen kunnen ontdekken. Dit zijn echter vermijdbare discussies. We hebben dit neergezet in pure vorm, zonder de budgetten van de grote ICT-bedrijven. We laten met een eenvoudige proof-of-concept kaart- en grafiek applicatie zien dat de data werkbaar is.

Desondanks kunnen we onze aanpak opeens aanpassen. Of het ritme van de data kan veranderen. Het aantal velden zal zeker nog kunnen groeien. Wij passen ons aan, en elk deel van onze pipeline voegt zich dan soepel naar de aanpassing. Het beste is om ook in jouw software rekening te houden met verandering.

Data format – Json vanuit de decoder

Vanuit onze decoder software in TTN ontstaat ons Json data bericht. Dit wordt min of meer zonder verdere aanpassingen opgeslagen in AWS, en dat is ook de data die jouw applicatie zal consumeren. Alle data die we relevant vinden zit hierin. Daarmee is dit een goede start voor een uitgebreide toelichting.

De programmacode van deze decoder zal een waardevol en beknopt handvat vormen voor de developer die de data wil gebruiken. Om een idee te vormen kan je in github kijken naar de toelichting, en ook naar de code van de decoder van één van onze eerdere projecten: https://github.com/JuniorIOT/Junior-IOT-in-a-box-Project

Een ander compact begin voor de developer is een voorbeeld file te nemen van de actuele data. Project deelnemers in een andere rol zullen eerder kijken naar de actuele data grafiek bij één van hun sensoren.

Niet alle velden zullen zijn gevuld met data. Dat hangt af van de versie van de sensor, en de meetwaardes die we voor die toepassing hebben gekozen. Niet gebruikte velden vertonen iedere keer een vast getal, een dummy waarde, of zijn niet opgenomen in de Json files. Dat verschilt per veld, en per versie van onze implementatie. In onze voorbeeld grafieken zie je dan dat voor zo’n veld geen grafieklijn wordt toegevoegd.

Kijk naar de data om te herkennen hoe deze dat vandaag bij jouw sensors is geregeld, en hou er rekening mee dat hier verandering in kan komen.

Voorbeeld datafile

Als voorbeeld datafile kan je kijken naar: http://junioriotchallenge.nl/ttn_data/ttn_messages_partitioned/app=bvhva-samenmeten/y=2021/m=02/d=24/2021-02-24%2023.39.09.25331.txt

Deze meetwaarde betreft de Smart City sensor op Overstad, geplaatst naast de Junior IOT werkplaats. De sensor is geplaatst op een mast, en staat circa 5 meter boven het maaiveld. Er is rondom voldoende vrij zicht, zodat het Lora signaal onbelemmerd alle kanten op wordt verzonden,

Het betreffende bericht laat dan ook zien dat het door 7 gateways wordt ontvangen. We ontdekken de gateways en de signaal sterkte in onze regio via bijvoorbeeld TTN Mapper. Deze gateways zijn:

  • eui-c0ee40ffff29f2da, rssi= -114. De heatmap suggereert dat deze gateway in de buurt van Junior IOT is te vinden, maar het signaal is daar eigenlijk niet sterk genoeg voor.
  • eui-008000000000ba5b, rssi= -120. Deze ‘Triple LoraWanGW2 Outdoor’ staat op het bedrijfspand van Triple.
  • eui-0000024b080e0c15, rssi= -117. De gateway ‘Data Science Alkmaar/VU 1’ van gemeente Alkmaar staat op het dak van de telefooncentrale.
  • junior_iot_bus_001, rssi= -72. De gateway ‘junior_iot_bus_001’ staat in de Junior IOT werkplaats, op 5 meter afstand van de zender.
  • eui-0000024b080e0bb6, rssi= -89. Deze gateway is ‘Data Science Alkmaar/VU 2’ en staat boven op het stadskantoor.
  • eui-b827ebfffef799b1, rssi= -96. We vinden deze op de kaart midden in de huiswaard als ‘DX-TTN-V2’, vermoedelijk een particuliere gateway.
  • eui-58a0cbfffe8019b8, rssi= -43. Gezien de heatmap lijkt deze gateway zich ook in de Junior IOT werkplaats te bevinden. We moeten misschien binnen een keer gaan opruimen.

Uit dit enkele bericht kunnen we al concluderen dat data ontvangst vanaf deze opstelling absoluut geen probleem is. Ook wanneer enkele gateways onverhoopt wegvallen zal de ontvangst goed blijven.

Bekijk hier de inhoud van de data file…

{
	"resource": "/",
	"path": "/",
	"httpMethod": "PUT",
	"headers": {
		"Accept-Encoding": "gzip",
		"CloudFront-Forwarded-Proto": "https",
		"CloudFront-Is-Desktop-Viewer": "true",
		"CloudFront-Is-Mobile-Viewer": "false",
		"CloudFront-Is-SmartTV-Viewer": "false",
		"CloudFront-Is-Tablet-Viewer": "false",
		"CloudFront-Viewer-Country": "IE",
		"content-type": "application/json",
		"Host": "a8gd4bvigb.execute-api.eu-west-1.amazonaws.com",
		"User-Agent": "http-ttn/2.6.0",
		"Via": "2.0 7dc07eed8f5996bab114638bc1ffca05.cloudfront.net (CloudFront)",
		"X-Amz-Cf-Id": "R5uINo45jF4lY_3yJG9INEbdn9aTGC-KPIr4yCQzDQyNzTx8m-lC8A==",
		"X-Amzn-Trace-Id": "Root=1-6036e39d-55758357210e2c6b54846856",
		"X-Forwarded-For": "52.169.225.45, 70.132.45.144",
		"X-Forwarded-Port": "443",
		"X-Forwarded-Proto": "https"
	},
	"multiValueHeaders": {
		"Accept-Encoding": ["gzip"],
		"CloudFront-Forwarded-Proto": ["https"],
		"CloudFront-Is-Desktop-Viewer": ["true"],
		"CloudFront-Is-Mobile-Viewer": ["false"],
		"CloudFront-Is-SmartTV-Viewer": ["false"],
		"CloudFront-Is-Tablet-Viewer": ["false"],
		"CloudFront-Viewer-Country": ["IE"],
		"content-type": ["application/json"],
		"Host": ["a8gd4bvigb.execute-api.eu-west-1.amazonaws.com"],
		"User-Agent": ["http-ttn/2.6.0"],
		"Via": ["2.0 7dc07eed8f5996bab114638bc1ffca05.cloudfront.net (CloudFront)"],
		"X-Amz-Cf-Id": ["R5uINo45jF4lY_3yJG9INEbdn9aTGC-KPIr4yCQzDQyNzTx8m-lC8A=="],
		"X-Amzn-Trace-Id": ["Root=1-6036e39d-55758357210e2c6b54846856"],
		"X-Forwarded-For": ["52.169.225.45, 70.132.45.144"],
		"X-Forwarded-Port": ["443"],
		"X-Forwarded-Proto": ["https"]
	},
	"queryStringParameters": null,
	"multiValueQueryStringParameters": null,
	"pathParameters": null,
	"stageVariables": null,
	"requestContext": {
		"resourceId": "3owsj86a35",
		"resourcePath": "/",
		"httpMethod": "PUT",
		"extendedRequestId": "bRiAoFrLDoEFkjg=",
		"requestTime": "24/Feb/2021:23:39:09 +0000",
		"path": "/prod",
		"accountId": "667242083879",
		"protocol": "HTTP/1.1",
		"stage": "prod",
		"domainPrefix": "a8gd4bvigb",
		"requestTimeEpoch": 1614209949541,
		"requestId": "62872e88-de8a-4d27-8dee-70e1465d1713",
		"identity": {
			"cognitoIdentityPoolId": null,
			"accountId": null,
			"cognitoIdentityId": null,
			"caller": null,
			"sourceIp": "52.169.225.45",
			"principalOrgId": null,
			"accessKey": null,
			"cognitoAuthenticationType": null,
			"cognitoAuthenticationProvider": null,
			"userArn": null,
			"userAgent": "http-ttn/2.6.0",
			"user": null
		},
		"domainName": "a8gd4bvigb.execute-api.eu-west-1.amazonaws.com",
		"apiId": "a8gd4bvigb"
	},
	"isBase64Encoded": false,
	"body": {
		"app_id": "bvhva-samenmeten",
		"dev_id": "alkmaar2021-009",
		"hardware_serial": "00D6FD994A11DE4A",
		"port": 1,
		"counter": 49,
		"payload_raw": "yt0Tg2CgAAAAAAAAAAAAAAAAAAAAAAAAAAArrRtYKAkAPQEiAAAAAAAAAAAAlwCyAMgAnAC3AJwAsgkBAQAAAAMIAALbAAWcAAEkAAb6AA7SAAHoJxAAAAAAAiYAAA==",
		"payload_fields": {
			"BME280_Hum": 70,
			"BME280_Press": 102490,
			"BME280_Temp": 11.81,
			"BME280internal_Hum": 0,
			"BME280internal_Press": 0,
			"BME280internal_Temp": 0,
			"LAT": "52.638457",
			"LON": "4.749538",
			"Lux": 0,
			"P": 1024.9,
			"PM10": 29,
			"PM25": 6.1,
			"RH": 70,
			"T": 11.81,
			"Vbat": 3.64,
			"Vsupply": 5.1,
			"arduino_VCC": 0,
			"arduino_Vbat": 0,
			"arduino_tempCPU": -100,
			"bytes_length": 94,
			"dB_EQ7_1____63Hz": -10.4,
			"dB_EQ7_2___160Hz": -8.97,
			"dB_EQ7_3___400Hz": -7.96,
			"dB_EQ7_4__1000Hz": -10.12,
			"dB_EQ7_5__2500Hz": -8.73,
			"dB_EQ7_6__6250Hz": -10.12,
			"dB_EQ7_7_16000Hz": -8.97,
			"dB_Vmic": 0,
			"dB_Vmic_ref": 0,
			"dB_activated": 0,
			"dB_flag": 1,
			"dB_levelflag": 1,
			"dB_samplecount": 9,
			"dBa_avg": 54.93,
			"dBa_max": 60.79,
			"dBa_min": 46.96,
			"dBc_avg": 62.69,
			"dBc_max": 69.23,
			"dBc_min": 51.42,
			"dBc_val": 0,
			"gps_alt": 0,
			"gps_hdop": 0,
			"gps_lat": "52.638457",
			"gps_lng": "4.749538",
			"pm10": 29,
			"pm25": 6.1,
			"raw_dB_EQ7_1": 151,
			"raw_dB_EQ7_2": 178,
			"raw_dB_EQ7_3": 200,
			"raw_dB_EQ7_4": 156,
			"raw_dB_EQ7_5": 183,
			"raw_dB_EQ7_6": 156,
			"raw_dB_EQ7_7": 178,
			"raw_dBa_avg": 731,
			"raw_dBa_max": 1436,
			"raw_dBa_min": 292,
			"raw_dBc_avg": 1786,
			"raw_dBc_max": 3794,
			"raw_dBc_min": 488
		},
		"metadata": {
			"time": "2021-02-24T23:39:09.253315088Z",
			"frequency": 867.9,
			"modulation": "LORA",
			"data_rate": "SF7BW125",
			"coding_rate": "4/5",
			"gateways": [{
				"gtw_id": "eui-c0ee40ffff29f2da",
				"timestamp": 3587583580,
				"time": "",
				"channel": 7,
				"rssi": -114,
				"snr": -6,
				"rf_chain": 0
			}, {
				"gtw_id": "eui-008000000000ba5b",
				"timestamp": 698627836,
				"time": "2021-02-25T02:08:51.317214Z",
				"channel": 7,
				"rssi": -120,
				"snr": -4,
				"rf_chain": 0,
				"latitude": 52.62452,
				"longitude": 4.77436,
				"altitude": 14
			}, {
				"gtw_id": "eui-0000024b080e0c15",
				"timestamp": 4235280908,
				"time": "2021-02-24T23:39:09.239779Z",
				"channel": 7,
				"rssi": -117,
				"snr": -2.2,
				"rf_chain": 0,
				"latitude": 52.61756,
				"longitude": 4.76675,
				"altitude": 21
			}, {
				"gtw_id": "junior_iot_bus_001",
				"timestamp": 3703469156,
				"time": "2021-02-24T23:39:11Z",
				"channel": 0,
				"rssi": -72,
				"snr": 8.25,
				"rf_chain": 0
			}, {
				"gtw_id": "eui-0000024b080e0bb6",
				"timestamp": 3689718876,
				"time": "2021-02-24T23:39:09.239771Z",
				"channel": 7,
				"rssi": -89,
				"snr": 9,
				"rf_chain": 0,
				"latitude": 52.63545,
				"longitude": 4.74609,
				"altitude": 21
			}, {
				"gtw_id": "eui-b827ebfffef799b1",
				"timestamp": 3120564380,
				"time": "2021-02-24T23:39:08.239769Z",
				"channel": 7,
				"rssi": -96,
				"snr": 9.8,
				"rf_chain": 0,
				"latitude": 52.64538,
				"longitude": 4.76077,
				"altitude": 14
			}, {
				"gtw_id": "eui-58a0cbfffe8019b8",
				"timestamp": 3306161116,
				"time": "2021-02-24T23:39:09.449579Z",
				"channel": 0,
				"rssi": -43,
				"snr": 10,
				"rf_chain": 0
			}]
		}
	},
	"message_ymdhm": 202102242339,
	"url": "http://junioriotchallenge.nl/ttn_data/ttn_messages_partitioned/app=bvhva-samenmeten/y=2021/m=02/d=24/2021-02-24 23.39.09.25331.txt"
}

 

Amazon data objecten – deze negeren we nu

De data wordt opgeslagen zoals deze door onze API in AWS wordt ontvangen. Dit betekent dat in het ‘headers’ object en in multiValueHeaders informatie wordt weergegeven over Cloudfront, User-agent en de forwarding naar AWS. Het is interessant om de timestamp van de verwerking te zien, maar verder heb ik nog geen toepassing gevonden voor deze informatie.

Ook het requestContext object toont timestamps, en andere informatie over de reis die dit bericht heeft meegemaakt.

Het ‘body’ object bevat de data vanuit TTN

Het ‘body’ object geeft ons het eigenlijke bericht zoals het vanuit TTN aan de API in AWS is verzonden. Het geeft onze meetdata, plus de context betreffende de afhandeling in TTN. Hier zitten heel leerzame onderdelen tussen.

Het ‘counter’ veld informeert ons over de voortgangsteller; een waarde die we in de Arduino code bewust aan het bericht moeten meegeven. Wanneer de spanning wegvalt wordt in de Arduino code de teller teruggezet naar 0.

Payload_raw toont een bijzondere ascii weergave van de Lora data string. Let op, dit is encoded, en is niet zo bruikbaar als bijvoorbeeld de binaire weergave.

Het metadata object geeft informatie over de datumtijd van ontvangst in TTN, over de Lora zendmethode en de modulatie. Je ziet hier dat wij de data altijd verzenden op een nette, beschaafde methode volgens spreading factor SF7.

Binnen het metadata object is bovendien een verzameling ‘gateways’ beschikbaar, waarin gegevens zoals ontvangststerkte wordt getoond voor elke TTN gateway waar het bericht is ontvangen. De gps locatie en plaatsingshoogte van de gateway zijn zichtbaar. Een rssi hoger dan -110 geeft vertrouwen dat de berichten vanaf de betreffende locatie goed ontvangen zullen worden. Een lijst van meer dan één of twee gateways geeft ook aan dat data vanaf de betreffende locatie altijd goed ontvangen zal worden. Een overzicht van gateways in jouw regio, met een indicatie voor de ontvangst sterkte kan je zien op bijvoorbeeld https://ttnmapper.org/heatmap/

Het veld ‘message_ymdhm’ hebben wij middels onze data pipeline toegevoegd om duidelijker weer te geven welke datum en tijd we gebruiken als timestamp voor het bericht. Dit is belangrijk voor de url waar we het bericht hebben opgeslagen. Let op: De datum en tijd van de berichten worden genoteerd in GMT wereld tijd. Dit betekent dat de weergave één of twee uur scheef loopt met onze zomer- en wintertijd. Een handige vuistregel is dat het meest recente bericht ongeveer vier minuten geleden is geplaatst.

Het veld ‘url’ hebben we ook middels onze data pipeline toegevoegd, om aan te geven waar het bericht is opgeslagen. Een voorbeeld is de waarde: http://junioriotchallenge.nl/ttn_data/ttn_messages_partitioned/app=bvhva-samenmeten/y=2021/m=02/d=24/2021-02-24 23.39.09.25331.txt

Onze meetdata vind je in het object ‘payload_fields’ binnen de ‘body’. Hierbinnen vind je onze datavelden met de betreffende meetdata.

De ruim 60 datavelden en hun beschrijving

Bij de veldbeschrijving zie je soms ook het aantal bytes dat de waarde inneemt in het Lora bericht. Dit is informatief en kan je als programmeur in principe verder negeren. Wanneer meer bytes worden gebruikt dan heeft de doorgegeven waarde een grotere range of een hogere precisie.

Een deel van de code en de toelichting is na te zien in één van onze eerdere projecten op GitHub: https://github.com/JuniorIOT/Junior-IOT-in-a-box-Project

Sensors die worden gemeten

We werken met

  • SDS011 voor fijnstof
  • BME280 voor temperatuur, luchtdruk en luchtvochtigheid
  • Een Lux meter
  • BN180 als GPS
  • Interne meetwaarden uit de Arduin CPU
  • Een custom decibel sensor board, vanaf 2023 drie overlappende meetbereiken
  • Frequentiebanden voor geluid middels de EQ7
  • Controlemetingen van meerdere interne voltages middels spanningsdeler
  • Vanaf 2023: Gassensor voor NO2, CO en NH3 (gecombineerde gevoeligheid voor verschillende gassen)

De electronica wordt ingebouwd in een compacte behuizing met accupakket en lader (vanaf 2021 twee of drie cellen, vanaf 2023 4 cellen). In 2020 zijn enkele sensors ingebouwd in een lichtarmatuur.

Onderlinge beïnvloeding en externe beïnvloeding is mogelijk. In de data is het volgende te herkennen als warmte effecten:

  • In een lichtarmatuur functioneert de verlichting in feite als een verwarming van 40 watt. Hierdoor zal de binnentemperatuur aanmerkelijk oplopen wanneer het licht aangeschakeld wordt. De meting wordt gedaan in een afgescheiden sectie welke aan het armatuur is gemonteerd.
  • De meetunit in een armatuur, maar ook die in een compacte behuizing zal in de volle zon opwarmen.
  • In een compacte behuizing, verbonden met een voeding die af en toe aan gaat, wordt de batterij iedere dag enkele uren krachtig geladen. Tijdens het laden kan de temperatuur wellicht enkele graden oplopen. De lader is in feite een verwarming van 1 tot 5 watt afhankelijk van de laadsterkte.
  • Gasmeting is een gecombineerde meting, waarbij de uitslag ook kan zijn veroorzaakt door een vergelijkbaar gas. Bovendien is gasmeting een trend meting.

Waar we verder rekening mee houden:

  • De SDS011 heeft een ventilator om lucht langs de meter te voeren. De motor geeft een 30dB achtergrondgeluid, en we willen nog lagere geluidsterktes kunnen meten. De SDS wordt in elke meetcyclus 20 tot 30 seconden aangeschakeld, gedurende welke tijd geen audio meting wordt gedaan.
  • Voor lage geluidsterktes is een uiterst gevoelige meting nodig. De electronica is ontworpen met nauwkeurigere componenten dan gebruikelijk is. Met electronische afscherming wordt bovendien electromagnetische storing vermeden.
  • Hardere wind langs de machine wordt gemeten als geluid.
  • Theoretisch kan de temperatuur en de voedingsspanning een zekere invloed hebben op de geluidsmeting, verschillend voor de verschillende types electronica componenten. Voor enkele units worden controlemetingen doorgegeven om te beoordelen welke correctie wenselijk is.
  • De BME280 is geplaatst nabij de openingen in het kastje en meet daardoor vanuit de veilige omgeving toch zo goed mogelijk de buitenlucht.
  • De microfoon is buiten op de unit geplaatst, voorzien van een windkap, onder een afdakje. De microfoon wijst onder een hoek van 45 graden naar beneden. Doorgaans wordt de unit zo geplaatst dat deze naar de weg of naar de dichtbij zijnde drukke plek wijst.
  • De lichtsensor is in het schuine deel naast de microfoon geplaatst, en wijst in een hoek van 45 graden naar beneden.
  • De GPS antenne wordt door een metalen behuizing gestoord, en is daarom onder een kunststof dop geplaatst. Het metalen deksel kan in een volgende versie een kunststof plaat worden.
  • De Lora antenne wordt door een metalen behuizing gestoord, en is daarom naast de microfoon aan de buitenzijde geplaatst. Het metalen deksel kan in een volgende versie een kunststof plaat worden.
  • Vanaf 2023: Het tijdsveld van de gasmeting is vanwege batterij capaciteit beperkt tot circa 6:00 tot 18:00.

GPS data

De eerste bytes van ons bericht zijn gereserveerd voor de locatie data. We gebruiken dit om de locatie van de sensor dynamisch te bepalen. Ook bij vaste locaties, en we voorkomen hiermee dat een aparte administratie nodig is voor de locatie van de sensor. Dit is een betrouwbare manier om locatie ook echt in de data zelf mee te kunnen nemen.

De TTN Mapper applicatie leest deze data ook, om te bepalen wat de netwerk dekking is op de locaties waar onze sensoren zijn geweest. Daarvoor worden de eerste 9 bytes van de ruwe data gelezen, welke daarvoor in een afgesproken format zijn samengesteld.

Diepere techniek details, de decoder en de veldbeschrijvingen, alsook de electrische aansluitgegevens zijn in te zien op onze pagina over de 2017 GPS Tracker: https://junioriot.nl/gps-tracker-2017/. Een zeer gedetailleerde achtergrond beschrijving inclusief details over het aanmaken van een applicatie in TTN, en de volledige bouw instructie van de GPS-tracker vind je op onze leerling/docent pagina: https://junioriot.nl/bouw-je-gps-tracker/

gps_lat. Drie bytes. Van -90 tot +90 graden.

gps_lng. Drie bytes, van -180 tot +180 graden. Positie nauwkeurigheid van ruim 2 meter; wat iets nauwkeuriger is dan de reguliere gps-plaatsbepaling.

gps_alt. Twee bytes. Hoogte van 0 tot 65025 meter. Waardes onder de zeespiegel worden in de Arduino code naar boven afgerond naar 0.

gps_hdop. Een enkele byte. Horizontal degree of precision, een indicatie van meetnauwkeurigheid. Waarde van 0 tot 25.5. Bij een waarde tot circa 4 is de plaatsbepaling nauwkeurig.

Analoge poorten

Het is mogelijk om onze sensoren te gebruiken om te monitoren of een deur open en dicht is. Of een andere analoge of digitale meting te monitoren. Dit hebben we gebruikt bij het spel waarbij juniors de stad veroveren. Enkele test-sensoren in de kasten van Stadswerk072 monitoren via een schakelaar of een LDR of een kastdeur open of dicht is. Het moment dat een ‘open/dicht’ situatie verandert, wordt via gps-tijd ook vastgelegd, dit tijdstip wordt in het databericht meegezonden.

In de huidige implementatie van de stadssensoren wordt deze data niet meegegeven. De locatie in de datastring moet bovendien herzien wegens overlap met andere waardes.

A0..A5       2 bytes, 10 bit analog read from A1 = TEMT6000  0..1023 A0on_dt DateTime btn on    4 bytes, bits 6 Y-2000, 4 M, 5 D, 5 H, 6 M, 6 S A0off_dt DateTime btn off    4 bytes, bits 6 Y-2000, 4 M, 5 D, 5 H, 6 M, 6 S

Internals

Traditioneel meten we een aantal standaard waardes om onze meetunit in de gaten te houden.

arduino_VCC. Een enkele byte. Het Lora32U4 board heeft ingebouwde monitoring voor de voedingspanning. Dit is geen officieel supported meting van de betreffende chip. De waarde houden we in stand om eer te doen aan de tradities uit onze eerdere projecten. Een waarde 0 geeft doorgaans aan dat de meting niet aanwezig is.

arduino_tempCPU. Een enkele byte. We hebben bij ons GPS-ballonproject de ingebouwde thermometer van de Lora32u4 gebruikt. Echter, de afwijking kan oplopen tot 10 graden. In de Alkmaar Smart City sensoren gebruiken we in plaats van deze waarde nu de betrouwbare BME280. Een waarde -100 geeft doorgaans aan dat de meting niet aanwezig is.

arduino_Vbatt. Een enkele byte. Ons Lora32U4 board heeft een ingebouwde meter om de on-board batterij in de gaten te houden. Echter, om de sensoren en andere extra apparatuur te voeden wordt nu een groter accu pakket. Deze meetwaarde wordt daarom bij de Alkmaar Smart City sensoren niet gebruikt. Een waarde 0 geeft doorgaans aan dat de meting niet aanwezig is.

Payload. De string weergave van de originele message. Deze was in TTN zichtbaar bij ‘data in motion’ maar niet bij de data zoals opgeslagen in de TTN data store. Het kan voor debugging handig zijn deze waarde af en toe aan te zetten, al is dat de laatste jaren niet meer nodig geweest.

Bytes_length of payload_size. De lengte fan het Lora bericht. Het kan voor debugging handig zijn deze waarde af en toe aan te zetten, al is dat de laatste jaren niet meer nodig geweest.

Smart City Sensoren

Reeds in 2018 hebben we verdere Smart City sensoring toegevoegd aan onze data. Met mobiele trackers werden de juniors de binnenstad van Alkmaar in gestuurd om de meetwaardes in kaart te brengen. Dit is dan ook de reden dat onze kaart nog telkens inzoomt op de binnenstad van Alkmaar. Milieu waardes werden gemeten met een BME280 voor temperatuur, luchtvochtigheid en luchtdruk, een SDS011 voor fijnstof PM 10 en PM 2,5, en een CO2 meter.

Op de kaart werden real-time de teamkleuren weergegeven in de vlakjes. Het aantal veroverde vlakjes was als teamscore zichtbaar naast de kaart.

De SDS en de BME zijn nog steeds de meest actuele en gangbare sensors voor deze soort toepassing, en zijn ook in 2021 onderdeel van de nieuwe Smart City sensoren. De BME is bij de nieuwe opstelling geplaatst in de luchtstroom van de SDS om werkelijk de buitenlucht te meten.

BME280_Temp. Twee bytes. Temperatuurmeting met nauwkeurige weergave per 1/100ste graad.

BME280_Hum. Twee bytes. Relatieve vochtigheid RH in percentage van 0 tot 100, doorgegeven met 1/100 nauwkeurigheid. Ergens in het dataproces wordt de waarde op gehele getallen afgerond, wat een leerzaam getrapt effect heeft op de grafiek.

BME280_Press. Twee bytes. Luchtdruk in tienden. De gemiddeld gemeten waarde is ruim 100.000, de waarde wordt blijkbaar doorgegeven in Pascal. Op weerkaarten wordt een waarde van 100.000 Pa doorgaans weergegeven als 1000 hPa.

pm10. Twee bytes. Fijnstof 10 um, de vanuit de SDS gemeten waarde wordt in tienden nauwkeurig doorgegeven.

pm25. Twee bytes. Fijnstof 2,5 um, de vanuit de SDS gemeten waarde wordt in tienden nauwkeurig doorgegeven.

Bij sommige test versies van de nieuwe Smart City sensor is ook de interne atmosfeer in de behuizing gemeten. Dit kan nut hebben bij onderzoek naar de toepasbaarheid in een lichtarmatuur, waar het doorgaans droog en warm is. We zien de waardes duidelijk verlopen zodra de lichtarmaturen aangeschakeld worden. Een uitzonderlijke waarde kan een indicatie zijn van defecten in het armatuur. Details en de time-line van dit deel van het project hebben we in 2020 beschreven in onze uitgebreide toelichting: https://junioriot.nl/lora-straatverlichting/

BME280internal_Temp. Twee bytes. Temperatuurmeting met nauwkeurige weergave per 1/100ste graad. Een waarde 0 geeft doorgaans aan dat de sensor niet aanwezig is.

BME280internal_Hum. Twee bytes. Relatieve vochtigheid RH in percentage van 0 tot 100. Ergens in het dataproces wordt de waarde op gehele getallen afgerond, wat een leerzaam getrapt effect heeft op de grafiek. Een waarde 0 geeft doorgaans aan dat de sensor niet aanwezig is.

BME280internal_Press. Twee bytes. Luchtdruk in tienden. Een waarde 0 geeft doorgaans aan dat de sensor niet aanwezig is.

Data formatted voor RIVM

Met RIVM hebben we een afspraak gemaakt dat de data van onze applicatie wordt opgenomen in hun data set. Zodra we een sensor toevoegen aan zo’n applicatie wordt deze automatisch ook toegevoegd aan de RIVM kaart.

We hebben in het beginstadium enkele tekortkomingen geconstateerd, maar hebben nog niet gecontroleerd of deze zijn verholpen. De data van sensoren in elkaars nabijheid wordt samengevoegd tot één grafiek. Detectie of een sensor binnen of buiten is geplaatst werkt niet, en fijnstof data van zo’n binnen sensor beïnvloed de stofkaart van Nederland. Van slechts enkele velden is een grafiek zichtbaar, en mogelijk wordt onze extra data niet bij RIVM opgeslagen. Slechts beperkte data beschikbaar voor afnemers van RIVM-data.

Om de Smart City data te gebruiken adviseren we onze directe route te volgen.

T. Temperatuur, identiek aan BME280_Temp

RH. Relatieve luchtvochtigheid, identiek aan BME280_Hum

P. Luchtdruk, dit is BME280_Press / 100

PM10. Fijnstof 10 um, identiek aan de waarde pm10.

PM25. Fijnstof 2,5 um, identiek aan de waarde pm25.

LAT. Locatie, identiek aan de waarde gps_lat.

LON. Locatie, identiek aan de waarde gps_lng.

Decibel metingen

De metingen van geluidsterkte gebeuren met een speciaal ingeregelde microfoon. De meetwaardes uit de microfoon worden middels een tweetal analoge weeg circuits omgezet naar de meting welke geschikt is voor dB(A) en dB(C). Vervolgens wordt de meetwaarde middels een AD-converter digitaal ingelezen, en middels een logaritmische formule vertaald naar decibel als dB(A) en dB(C).

Het circuit is uiterst zorgvuldig gebouwd om ook lage decibelwaardes te kunnen registreren. De logaritmische schaal maakt het lastig om in één meting de hele kleine alsook de grotere geluidsterktes te meten, en daarom schakelt het meetcircuit bij grotere geluidsterktes terug naar een ‘mildere’ meetstand.

In andere geluidsmetingen zagen we bijvoorbeeld digitale softwareoplossingen met Fourier analyses. Om hiermee een betrouwbaar resultaat te behalen is doorgaans een krachtigere chip nodig dan de bediening van de sensors voorhanden is. Hierdoor worden nu concessies in de software gedaan waarmee de meting onzuiver is. Bovendien zal bij elke sensorset een nieuwe software implementatie worden gedaan, en afhankelijk van libraries, instellingen, CPU-kracht een ander meetresultaat opleveren. We zagen dat er in de markt nog geen betrouwbare implementatie beschikbaar was.

Om zo’n digitale meting te vermijden heeft Leo ons analoge meetcircuit ontworpen. Hiermee wordt de meting onafhankelijk van lastige software implementaties. We verwachten op elke toepassingswijze dan ook een betrouwbaar en vergelijkbaar resultaat. Na inbouw in de behuizing wordt de dB meter bovendien afgesteld, met een semiprofessionele ‘ijk-machine’. Hoe de gedachtegang tot de geluidsmeting tot stand is gekomen, lees je op de blogpost van Thijs uit 2018: https://junioriot.nl/citizen-science-hart-van-alkmaar/

Bij de decibel meetwaardes hebben we na onze testperiode de meer passende naamgeving ontdekt. Hieronder zie je telkens de combinatie: nieuwe naam / oude naam. De overgang vindt plaats op ongeveer 25 februari 2021. Data is ongewijzigd, waardoor de data onder de oude naam gewoon gebruikt kan blijven worden. Het overgangsmoment is te zien in deze grafiek pagina: http://junioriotchallenge.nl/graph/?dateStart=20210222&dateEnd=20210225&application=bvhva-samenmeten&device=alkmaar2021-009

Meetwaardes:

Lux. De waarde van de lichtmeting aan de Smart City Stadssensor.

Vsupply / Noise9_Vpsu. De interne spanning welke door de converter na het accupack wordt aangeboden aan de overige componenten van de Stadssensor.

Vbat / Noise7_Vbatt. De batterijspanning. Wanneer de stadssensor aan de geschakelde voeding van de stadsverlichting is gemonteerd, kunnen we in deze grafiek herkennen hoeveel tijd de sensor kan overbruggen zonder externe voedingsspanning. Een dalende spanning geeft aan dat de externe voeding ‘uit’ staat. Een oplopende spanning geeft aan dat het accupack wordt geladen.

Aan de ontladingscurve van de Smart City unit ‘alkmaar2021-009’ van 24 februari 2021 zien we dat de accu in 23 uur terugloopt van de laadspanning van 4,2 volt naar een waarde van 3 volt. Boven deze batterijspanning van 3 volt blijft het apparaat goed functioneren. Onder de 3 volt blijkt er nog 15 tot 30 minuten bedrijfsduur beschikbaar te zijn. We verwachten dat bij een iets verouderde batterij de bedrijfsduur 10-25% kan afnemen.

dB_activated / Noise5_pushpull. Een Status flag: 0 is goed, 1 geeft aan dat er een storing is op de 5V voeding naar de DB meter print. In dat geval worden er geen DB-meetwaardes meegegeven.

dB_Vmic / Noise1_Comp. De spanning op de microfoon. Dit is een controlemeting om te analyseren in hoeverre de aansturings FET verloopt vanwege bijvoorbeeld de temperatuur. Wordt niet in alle sensor units gemeten. Een waarde 0 geeft doorgaans aan dat de meting niet aanwezig is.

dBc_val / Noise2_dB. Gemeten dB(C) waarde. Is bij meer recente sensors vervangen door dBc_min, dBc_max, dBc_avg. Een waarde 0 geeft doorgaans aan dat de meting niet aanwezig is.

dB_Vmic_ref / Noise3_Ref. Referentie spanning van precies 3 volt voor de FET-microfoon. Als deze constant blijft dan wordt de microfoon goed uitgelezen. Data alleen bij FET-microfoons. Een waarde 0 geeft doorgaans aan dat de meting niet aanwezig is.

dB_samplecount / Noise4_Loops. Het aantal samples welke gemeten zijn om _min, _max en _avg te bepalen. Bij onze gebruikelijke configuratie zit er circa 4 minuten tussen de data berichten. Andere metingen nemen 30 seconden. Tussen de geluidssamples zit telkens zo’n 20 seconden en het aantal samples is dan doorgaans 9. De geluidssterkte betreft hiermee ongeveer de afgelopen 4 minuten.

dB_flag / Noise8_Version. Een serie flags die aangeeft of er in dit model een dB meter circuit is gemonteerd. De waarde 1 geeft aan dat de aangeleverde dB waardes volgens de huidige afspraak wordt behandeld. In situatie voor februari 2021 is bij waarde 0 andere dB waarde aangeleverd, welke naar andere positie in het Lora bericht c.q. andere byte breedte is gedecodeerd. Kan in een later stadium vervallen zodra alle sensoren met dB zijn uitgevoerd.

dB_levelflag / level. Een serie flags die aangeeft of omlaag is geschakeld in meetsterkte. De eerste bit, waarde 1, geeft aan dat de dBa en dBc meting omlaag is geschakeld. De tweede bit, waarde 2, geeft aan dat de EQ7 meetwaardes omlaag zijn geschakeld. Dit geeft sturing aan de formule waarmee de dB waarde wordt bepaald.

dBa_avg / NoiseA_avg_dbA. De gemiddelde waarde over de gemeten samples voor dB(A)

dBa_max / NoiseB_max_dbA. De hoogste waarde over de gemeten samples voor dB(A)

dBa_min / NoiseC_Min_dbA. De laagste waarde over de gemeten samples voor dB(A)

dBc_avg / NoiseD_avg_dbC. De gemiddelde waarde over de gemeten samples voor dB(C)

dBc_max / NoiseE_max_dbC. De hoogste waarde over de gemeten samples voor dB(C)

dBc_min / NoiseF_Min_dbC. De laagste waarde over de gemeten samples voor dB(C)

raw_*. De ruwe onbewerkte meting zoals gemeten door de DA-converter.

EQ7 geluidsmetingen

De laatste geluidssample wordt door een standaard EQ7 chip opgesplitst in 7 overlappende frequentiebanden. Hiermee wordt het hoorbare spectrum van circa 0 tot 20.000 Hz opgedeeld in nette, overlappende delen.

We verwachten dat met deze meting een indicatie kan worden ontwikkeld in hoeverre het gemeten geluid als meer of minder hinderlijk wordt ervaren in de stadse omgeving. Wellicht kan zelfs het verschil worden gesignaleerd tussen mensen, muziek, verkeer, airco’s, machines en meer.

De 7 frequentiebanden worden weergegeven in de datasheet op sparkfun.

(bron: https://cdn.sparkfun.com/datasheets/Dev/Arduino/Shields/MSGEQ7_5-11.pdf)

dB_EQ7_1____63Hz. Geluidsterkte in dB van de eerste frequentieband, gelijkmatig rond 63 Hz.
dB_EQ7_2___160Hz. Geluidsterkte in dB van de 2e frequentieband, gelijkmatig rond 160 Hz.
dB_EQ7_3___400Hz. Geluidsterkte in dB van de 3e frequentieband, gelijkmatig rond 400 Hz.
dB_EQ7_4__1000Hz. Geluidsterkte in dB van de 4e frequentieband, gelijkmatig rond 1000 Hz.
dB_EQ7_5__2500Hz. Geluidsterkte in dB van de 5e frequentieband, gelijkmatig rond 2500 Hz.
dB_EQ7_6__6250Hz. Geluidsterkte in dB van de 6e frequentieband, gelijkmatig rond 6250 Hz.
dB_EQ7_7_16000Hz. Geluidsterkte in dB van de 7e frequentieband, gelijkmatig rond 16000 Hz.

raw_*. De ruwe onbewerkte meting zoals gemeten door de DA-converter.

Gasmeting

Vanaf 2023 leveren we de gasmeter in het pakket. De meetwaardes beginnen met “gas_”. Om de batterij te sparen maken we per dag een meetserie van 12 uur.

Bij deze meting gebruiken we de sensor MICS6814. We sturen hiervan de ruwe meetwaardes uit, gecorrigeerd voor de referentiewaarde van de betreffende dag.

Om te kopen tot de juiste compensaties is in het kastje van de sensor een vocht- en temperatuur sensor geplaatst. Dit is de HTU21, waarvan de meetwaardes ook beginnen met “gas_”. Hiermee kan nog buiten de datastroom een compensatie formule worden gerealiseerd om te komen tot echte gas concentraties.

Iedere dag wordt een nieuwe calibratie gedaan. Dit is een referentiepunt in clean air. Dit gebeurt iedere dag na één uur opwarmen van de sensor (van 5:00 tot 6:00 Greenwich time). In deze tijd wordt niet gemeten, en je ziet de HTU21 temperatuur oplopen. Om 6:00 begint de meetserie van 12 uur lang. De eerste meting is de calibratie, het referentiepunt voor de meetreeks van deze dag. De sensor blijft gedurende deze hele reeks aanstaan cq opgewarmd, zoals in de officiële documentatie wordt aanbevolen.

We herkennen deze velden in de datastroom voor de referentie waardes:

  • raw_cal_NH3, referentiewaarde voor NH3 meting (blijft 12 uur constant)
  • raw_cal_OX, referentiewaarde voor meting Oxiderende gassen (NO2) (blijft 12 uur constant)
  • raw_cal_RED, referentiewaarde voor reducerende gassen (CO) (blijft 12 uur constant)

De actuele metingen voordat correctie plaats vindt:

  • raw_cur_NH3, actuele gemeten waarde uit sensor zonder correcties voor referentie, vocht en temperatuur
  • raw_cur_OX, actuele gemeten waarde uit sensor zonder correcties voor referentie, vocht en temperatuur
  • raw_cur_RED, actuele gemeten waarde uit sensor zonder correcties voor referentie, vocht en temperatuur

Uit deze basismetingen volgen de ‘officiële’ waardes die reeds gecorrigeerd zijn voor referentie, vocht en temperatuur. Let op: de formule voor de correcties is nog in ontwikkeling, en verdere finetuning zal in 2023 nog variaties veroorzaken in de meetreeks.

  • gas_NH3 (dit was raw_cmp_NH3), actuele waarde, gecompenseerd voor referentie, temperatuur en vocht (meetreeks formule wordt nog ontwikkeld);  ammoniak, geldig tussen 1 en 500 ppm
  • gas_NO2, actuele waarde, gecompenseerd voor referentie, temperatuur en vocht (meetreeks formule wordt nog ontwikkeld); stikstof, geldig tussen 0,05 en 10 ppm
  • gas_CO, actuele waarde, gecompenseerd voor referentie, temperatuur en vocht (meetreeks formule wordt nog ontwikkeld); koolmonoxide, geldig tussen 1 en 1000 ppm

Vanuit deze NH3, RED en OX kanalen worden combinatiemetingen afgeleid. De ‘raw_’ velden hiervoor zijn gelijkvormig aan de eerder genoemde variaties.

  • gas_C2H5OH, ethanol, de gecorrigeerde meetwaarde, geldig tussen 10 en 500 ppm
  • gas_C3H8, propaan, de gecorrigeerde meetwaarde, geldig >1000 ppm
  • gas_ C4H10, iso-butaan, de gecorrigeerde meetwaarde, geldig >1000 ppm
  • gas_CH4, methaan, de gecorrigeerde meetwaarde, geldig >1000 ppm
  • gas_H2, waterstof, de gecorrigeerde meetwaarde, geldig tussen 1 en 1000 ppm

En tevens meten we:

  • gas_HTU21D_Hum, vochtmeting in behuizing gassensor in % luchtvochtigheid
  • gas_HTU21D_Temp, temperatuurmeting in behuizing gassensor, in graden Celcius
  • gas_ext_cal, buitentemperatuur tijdens calibration in graden Celcius. De hele dag wordt dezelfde 6:00 waarde gerapporteerd.
  • gas_int_cal, temperatuur in behuizing gassensor tijdens calibration in graden Celcius. De hele dag wordt dezelfde 6:00 waarde gerapporteerd.
  • gas_stab_period, tijdens de calibratie wordt 30 seconden gemeten tot een stabiele waarde op de drie gassen, deze periode is langer als het langer duurt voordat de eerste meting stabiel is geworden. Max 4 minuten. De hele dag wordt dezelfde 6:00 waarde gerapporteerd.

In detail – hoe stroomt de data door onze systemen

We beschrijven als voorbeeld de stappen die de meetwaarde van de LUX meter doormaakt.

In onze Lora sensor:

  • LUX sensor maakt de lichtsterkte digitaal meetbaar
  • Onze Arduino software neemt eens per circa 4 minuten een lichtmeting
  • Meetwaarde wordt geschaald naar beschikbate bit-ruimte in de byte-reeks waarin ook alle andere meetswaardes een positie hebben
  • De byte-reeks wordt met netwerksleutel en appsleutel gecodeerd tot een Lora ddatapakket
  • Het Lora datapakket wordt middels de radio module uitgezonden op één van de 8 kanalen
    • Onze sensor ontvangt geen terugmelding of data ergens aankomt
    • We zenden ongeveer eens per 4 minuten en mogen per dag 30 seconden zendtijd gebruiken

TTN Gateways

  • Elke TTN gateway luistert op 8 kanalen en ontvangt data transmissies
    • Ons pakket kan op meerdere gateways worden ontvangen. Gateways doen onafhankelijk van elkaar hun werk, waardoor het pakket op meerdere plekken kan worden verwerkt.
  • Zodra een datapakket voldoende duidelijk is ontvangen wordt deze door de betreffende gateway doorgegeven aan de TTN backbone

TTN backbone

  • De TTN backbone decodeert het pakket met de TTN netwerksleutel, en bepaalt bij welke app het behoort, voegt bovendien de identieke pakketten welke door meerdere gateways worden ontvangen weer samen en voegt vn elke gateway de RSSI en gateway details toe.
  • Het pakket komt aan bij onze app in TTN. Met de appsleutel bij onze definitie in TTN wordt de payload verder ontsleuteld, en de byte-reeks komt beschikbaar.
  • Onze decoder formule vertaalt de payload byte-reeks naar herkenbare meet waardes, welke we als payload JSON beschikbaar maken. Deze wordt als Payload element opgenomen in de V2 of V3 versie van het datapakket binnen het TTN platform.
  • Het TTN platform geeft deze V2 of V3 versie van de JSON direct door aan de integraties die we bij onze app hebben ingesteld.

Onze integraties in TTN

  • HTTP integratie stuurt de data naar onze URL van onze AWS API
    • Bij de betreffende TTP app stellen we de HTTPS URL in met bijbehorende details
  •  Storage integratie
    • De storage integratie is eenvoudig te activeren bij de betreffende app. Data wordt tot 7 dagen vastgehouden en kan met een API worden uitgevraagd.
  • RIVM integratie
    • RIVM heeft van ons een access key ontvangen waarmee de betreffende app wordt gepollt. RIVM haalt dan de nieuwe data pakketten op. We hebben vaste veldnamen toegevoegd aan de V2 Payload. Vor deze koppeling moeten we ook de storage integratie aanzetten.
  • TTN Mapper integratie
    • Voor TTN Mapper heeft jpmeijer een HTTPS integratie ingesteld bij de betreffende app. De eerste 9 bytes van de datastring geven de locatie informatie.

RIVM

  • RIVM toont onze locatie, PM en druk meetwaardes op de stofkaart van Nederland. We hebben geen zicht op de bewerkingen en filtering welke RIVM uitvoert. Een diversiteit aan meetwaardes zou helpen bij een (toekomstige?) ML analyse im de interpretatie te verbeteren. Het vermoeden is dat sensoren die dicht bij elkaar zijn, op dit moment nog worden samengevoegd tot één meetpunt. We zien wel dat de LUX meetwaarde door RIVM wordt genegeerd.
  • De gefilterde RIVM data kan door partners worden opgehaald.

TTN Mapper

  • TTN Mapper toont een kaart van device- en gateway locaties, en gebruikt de Gateway RSSI info om een dekkingskaart over de gehele wereld op te stellen. Je kunt de locatie van een specifieke device volgen.

Juniot IOT AWS data

  • Onze API in AWS ontvangt de JSON van TTN.
  • Onze code in deze API heeft meerdere functies
    • We verwerken de locatie data tot onze game-dekkingskaart, en houden eventueel de score bij van verschillende teams.
    • De ontvangen JSON berichten worden verrijkt met onder meer de url waar het bericht uiteindelijk publiekelijk is te benaderen.
    • De verrijkte JSON berichten worden opgeslagen in een S3 ‘folderstructuur’.

AWS S3 datastructuur

  • Onze API slaat meerdere datafiles op
    • Het individuele databericht, met een relatief onvoorspelbare url bestaande uit ondermeer datum, uur, minuut en milliseconde van het bericht.
    • De device index: welke sensornamen zijn gezien, en per sensor de meest actuele url van betreffende bericht. Dit bestand heeft één vaste locatie en filemaam, wordt dagelijks opnieuw opgebouwd en wordt ververst zodra een nieuw databericht binnenkomt.
    • Game score. Teams in het GPS spel scoren punten als ze een vlak op de kaart raken welke nog niet was veroverd.
    • Heatmap of game map. De vlakken op de kaart worden gekleurd naarmate ze door de verschillende teams in het GS spel worden veroverd.
    • De device index per dag: idem, echter per dag. Dit bestand heeft één vaste locatie. Dagelijks wordt een nieuwe file opgebouwd met jaar, maand, dag in de filenaam. De data wordt ververst zodra een nieuw databericht binnenkomt.
    • Device logfile per dag per device: Per device wordt iedere dag een niewe log aangemaakt, met devicenaam, jaar, maand, dag in de filebaam. Ieder databericht voor betreffende file wordt aan de log toegevoegd.

Game map

  • Vaste HTTP en JS code welke in jouw browser draait. Haalt de actuele locaties op van de devices die vandaag zijn gezien. Teamscores en heatmap wordt getoond als extra gimmick.

Grafiek pagina per device

  • Vaste HTTP en JS code welke in jouw browser draait. Haalt de actuele data op van het betreffende device.
  • De grafiekpagina toont de interactieve grafieken voor de data van een geselecteerde device. Een datareeks kan meerdere dagen beslaan, de betreffende data files worden dynamisch opgehaald vanaf de vaste HTTPS locaties.
  • De form-fields helpen om deze datagrafieken in detail in te stellen.
  • Via de parameters in de url kunnen de instellingen verder getuned worden. De ingestelde grafiek kan via de urm zo makkelijk worden gedeeld.

Verdere ontwikkelingen

medio 2021: Overstap van The Things Network V2 naar The Things Stack Community Edition

(juli 2021)

In 2020 zien we dat The Things Network is geëvolueerd naar een robuust wereldwijd netwerk. Technische mogelijkheden in het netwerk groeien. De software en hardware van de sensoren wordt telkens krachtiger, waardoor meer requirements in het Lora protocol worden opgenomen. Een update is nodig en wenselijk om het verkeer meer de-centraal en meer efficient af te handelen, en om de nieuwe features te implementeren. Een technologie-update is daarmee nu wenselijk. De gratis community versie en de niet-gratis versie van TTI evolueren in hetzelfde ritme.

In 2020 is de migratie van de bestaande V2 versie naar V3 aangekondigd. Gebruikers moeten hiervoor in 2021 de devices en de gateways migreren. De data afhandeling via de V2 route zal eind 2021 eindigen.

Lees meer over onze migratie op: https://junioriot.nl/burgernetwerken-migratie-ttn-v2-v3/