Locatie en Airport API

Ik heb 2 api’s gemaakt omdat ik zelf snel de data nodig had: een API voor een stadsnaam om te zetten in coordinaten, en een API om luchthavens op te zoeken:

Airport API

op https://airport-api.lamdev.be kan je de documentatie raadplegen

Locatie API

het gebruik van de locatie API is heel eenvoudig:

```
$ curl https://location-api.lamdev.be/brussel
{"status":200,"error":"","naam":"Brussel","lat":50.83,"lon":4.33}
```

deze geeft de lat en lon terug.

Alecto WS-5500 Custom Upload script

In deze post leg ik uit hoe je gebruik maakt van de custom upload functie van de WS-5500. Hiermee kan je de data meteen naar jouw eigen webserver sturen.

Het uploadscript werkt enkel over HTTP verbindingen, en niet over HTTPS verbindingen. Het weerstation volgt geen redirects, een HTTP naar HTTPS redirect zal dus niet gevolgd worden.

Het uploadscript zorgt ervoor dat je de data van het weerstation kan uitlezen. Deze dien je op een webserver te uploaden en nadien in de app in te stellen. Om de data op te slaan heb je ook en MySQL databank nodig.

Je kan het uploadscript met de knop hierboven downloaden. Dit .zip bestand bevat 2 files: updateweatherstation.php (uploadscript) en hwa.php, het script waarmee je de data kan delen met andere weerstations.

Eens je het uploadscript hebt gedownload moet je de databank nog instellen. Het SQL script hieronder maakt de tabel aan om de data in op te slaan:

CREATE TABLE `data` (
`Tijd` DATETIME NOT NULL,
`Temperatuur` DECIMAL(4,1) NOT NULL,
`Dauwpunt` DECIMAL(4,1) NOT NULL,
`Windchill` DECIMAL(4,1) NOT NULL,
`Vochtigheid` INT(16) NOT NULL,
`Windsnelheid` DECIMAL(4,1) NOT NULL,
`WindstootSnelheid` DECIMAL(4,1) NOT NULL,
`Windrichting` DECIMAL(4,1) NOT NULL,
`Barometer` DECIMAL(5,1) NOT NULL,
`RegenVandaag` DECIMAL(4,1) NOT NULL,
`RegenIntensiteit` DECIMAL(4,1) NOT NULL,
`Zonnestraling` DECIMAL(5,1) NOT NULL,
`UV` INT(16) NOT NULL,
UNIQUE (`Tijd`)
);

Zodra je de Databank hebt aangemaakt en de datatabel hebt aangemaakt kan je de gegevens aanpassen in het uploadscript. Bovenaan het uploadscript vind je dit stukje:

// Instellingen
$db_host = "localhost"; // Meestal "localhost"
$db_name = "weerstation"; // De naam van de databank
$db_user = "weerstation"; // De databank gebruiker
$db_pass = "wachtwoord"; // Het wachtwoord van de databank gebruiker
$db_table = "data"; // De naam van de datatabel

$id = "weerstation"; // stationID
$key = "key"; // stationKey
// Einde instellingen

Hier moet je de inloggegevens voor de databank aanpassen, alsook de $id en $key, zodat enkel jouw station data kan uploaden.

Nu moet je de WSView app downloaden, als je deze nog niet hebt geïnstalleerd. Deze app is uitgegeven door Shenzen Fine Offset Electronics. (androidios)

Eens het uploadscript op de webserver staat moet je het nog instellen in de app. Hiervoor kan je op het laatste tabje bij “customized” het volgende ingeven.

Bij `Server IP / Hostname` geef je de domeinnaam in van jouw website. Bij mij is dat bijvoorbeeld `www.vincentlammens.be`

Bij `path` geef je aan waar dat uploadscript staat op jouw website, bijvoorbeeld `/weerstation/updateweatherstation.php?` **Let op: dit dient te eindigen met een `?`**

Bij `Station ID` en `Station Key` geef je de waarden in die je in het uploadscript hebt ingesteld.

De `Port` is 80, in de meeste gevallen is dat correct.

`Upload interval` zet je op 16, of een andere waarde. 16 is bij dit weerstation de laagste waarde.

Nu klik je op “Save”. Je weerstation zal nu beginnen met de data door te sturen naar jouw webserver.

De data verwerken

De data kan je nu verwerken op elke manier die je maar wilt door hem uit te lezen uit de databank.

Ik ga me in deze post beperken tot het genereren van een bestand waarmee je de data met weersites kunt delen, maar uiteraard kan je net zoals bij de Weather Underground optie grafiekjes maken.

Een veelgebruikt formaat waarmee weersites data van weerstations uitlezen is het “Openweerdata.txt” formaat, ook wel “hwa.txt” genoemd. Dit bestand zit bij de zipfile van het uploadscript.

In dit script dien je bovenaan het script de databank gegevens aan te passen. Hierna kan je het uploaden, en de URL naar dit script delen met andere weersites.

Een .xlsx bestand maken met PHP

Ik wou onlangs de data van mijn weerstation exporteren naar een Excel file.
Ik wou niet gewoon een .csv exporteren, maar een .xlsx bestand met verschillende tabbladen.

Ik maak gebruik van de XLSXWriter van mk-j. Deze php class maakt het
aanmaken van een .xlsx file zeer eenvoudig.

<?php // xlsxwriter impoteren require_once('xlsxwriter.class.php'); // nieuwe writer maken $writer = new XLSXWriter(); // Hier zouden we data uit de databank uitlezen // $data1 en $data2 bevat data uit de databank // een nieuw werkblad maken: $writer->writeSheet($data1,'Blad 1');
	$writer->writeSheet($data2,'Blad 2');
	
	// Data opslaan
	$writer->writeToFile('output.xlsx');

	// of de bezoeker de data laten downloaden
	header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
	header('Content-Disposition: attachment;filename="export.xlsx"');
	$writer->writeToStdOut();
?>

Zoals je ziet is het zeer eenvoudig in gebruik.

Raspberrypi USB Webcam

Ik wou al een tijdje een Webcam voor mijn weerstation. Ik had nog een oude
USB-Webcam die ik hiervoor kon gebruiken.

**Noot:** De 5.10 Linux kernel heeft een bug waarmee /dev/video0 niet kan uitgelezen worden. De versie van Raspberrypi OS op moment van schrijven maakt gebruik van deze kernel. Je moet deze dus eerst downgraden voordat je foto’s kunt maken.

Een USB Webcam gebruiken op de raspberrypi is zeer eenvoudig. Met het programmatje `fswebcam` kan je zeer eenvoudig een foto nemen met de aangesloten webcam.

# FSWebcam Installeren
sudo apt install fswebcam

we kunnen dan met de volgende commando’s foto’s nemen:

# Gewone foto:
fswebcam foto.jpg
# Resolutie specifieren
fswebcam -r 640x480 foto.jpg
# Banner aanpassen
# Titel:
fswebcam -r 640x480 --title "Webcam Foto" foto.jpg
# Banner kleur (Hex, 2 waarden voor transparantie, 6 waarden voor kleur)
fswebcam -r 640x480 --banner-colour "#2F5B5B5B" --line-colour "#0F81FC0F" foto.jpg
# Geen banner
fswebcam -r 640x480 --no-banner foto.jpg

Met behulp van LFTP kan je eenvoudig de foto’s uploaden. LFTP is een command-line FTP programma dat naast FTP ook FTPS en SFTP ondersteund.

#lftp installeren
sudo apt install lftp
# uploaden
lftp -e "put foto.jpg; quit;" sftp://sftp.server.be -u gebruikersnaam,wachtwoord

Dit kan je evt in een cronjob combineren om de foto meteen te uploaden.

Printer Friendly CSS

Iets waar moderne websites het lastig mee hebben is om als je de webpagina afdrukt, deze vaak te veel nutteloze informatie afdrukt. Een pagina printer-friendly maken is echter niet zo moeilijk. In deze post leg ik uit hoe je een printer-friendly CSS bestand toevoegd aan je site.

Lees verder

Wat is er nieuw in PHP 8.0

Onlangs is PHP 8.0 uitgekomen. Hier zijn de meest interesante nieuwtjes

Union Types

PHP is een dynamicaly typed programmeertaal, daardoor kan een variabele soms van een ander type zijn. Met union types kan je aanduiden dat een variabele van 1 van de toegestane types is

// Union type example
public function foo(Foo|Bar $var): float|int;

JIT

De JIT is weer aangepast, waardoor PHP nog sneller is.

De Match Expressie

De match expressie is een beetje zoals de `switch` expressie.

// match example
$result = match($input) {
0 => "Hallo"
1,2 => "Wereld" 
}

ext-json standaard

De JSON extentie is nu altijd deel van PHP.

Tiny Perl CMS

Ik heb een klein, basis CMS systeem gemaakt in perl. Voornamelijk om perl uit te testen.

Het is eenorm eenvoudig, het heeft ook geen admin interface, dus zoals het script nu is
moet je pagina’s meteen in de databank aanmaken.

Hieronder zie je de sourcecode.

#!/usr/bin/perl
use warnings;
use strict;
use CGI;
use DBI;

my $driver = "mysql";
my $database = "database_naam";
my $dsn = "DBI:$driver:database=$database";
my $userid = "database_user";
my $password = "database_password";

my $dbh = DBI->connect($dsn, $userid, $password ) or die $DBI::errstr;

my $cgi = CGI->new;
print $cgi->header;

# header
print <<'EOF';
<!DOCTYPE="html">
<html>
<head>
  <title>TinyPerlCMS</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta charset="UTF-8" />
</head>
<style>
  body {
    max-width: 800px;
    padding: 1em;
    border: 1px solid black;
    margin: 0 auto;
  }

  img {
    max-width: 100%;
  }
</style>
<body>
EOF

my $p = $cgi->param('p') || '1'; # default id 1, de homepage

my $sth = $dbh->prepare("SELECT content, title
                        FROM pages
                        WHERE id = ? LIMIT 1"); #limit 1, voor de zekerheid
$sth->execute( $p ) or die $DBI::errstr;
if ($sth->rows == 0){
print "<h1>Error 404: Page not found</h1><p>The page you where looking for was not found. <a href='?p=1'>Go to the homepage</a></p>";
} else {
  while (my @row = $sth->fetchrow_array()) {
     my ($content, $title ) = @row;
     print "<h1>$title</h1>\n $content\n";
     if ($p != '1'){ # check als het niet de homepage is, om link naar homepage te tonen
     print '<a href="?p=1">Back</a>' 
     }
  }
}
$sth->finish();

#footer
print <<'EOF';
</body>
</html>
EOF

exit 0;

database structuur:

CREATE TABLE `pages` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(64) NOT NULL, -- grote kan aangepast worden
  `content` varchar(10000) NOT NULL, -- grote kan aangepast worden
  PRIMARY KEY (`id`)
)

RPI Assembler tutorial 7

De ARM architectuur is vooral gericht op embedded systeemen. Deze zijn vaak gebruikt in massaproductie-producten. In deze context zijn de marges klein, dus zal de ontwerper zo min mogenlijk componenten willen gebruiken. Een relatief duur component is geheugen, hoewel het elke dag een stukje goedkoper word. In zulke situaties wordt geheugen als bespaard door de ARM instructieset. Deze is ontworpen met dat doel. Ik zal een paar delen nodig hebben om alles uit te leggen, maar we zullen beginnen met ‘Shifted operand’.

Indexeermodes

We hebben gezien dat voor alles behalve load store and branches, ARM instructies als operators ofwel registers ofwel de waarde zelf gebruiken. We hebben ook gezien dat de eerste operator vaak het doelregister is. De instructie mov heeft nog een andere operator: een register of een waarde. Instructies zoals ADD en AND hebben vaak nog meer bronregisters. De eerste is altijd een register en de 2e kan een register zijn, maar ook een andere waarde.

Dit maakt het mogenlijk voor operators in instructies om collectief te zijn. Dit worden indexing modes genoemd. Dit idee kan een beetje ‘off-topic’ lijken omdat we niets zullen indexeren. De naam Indexing heeft wel nut voor geheugen operators. ARM instructies buiten load en store hebben geen geheugen operators.

De syntax van de meeste ARM instructies is als volgt:

instruction Rdest, Rsource1, source2

Er zijn een paar uitzonderingen: mov, b en bxx, ldr en str.

In een volgend deel zal ik de indexeermodes van ldr en str overlopen. Voor Branches is het echter zeer simpel. Hier is het enkel een label in ons programma.

Shift operaties

Wat raar is aan source2 in de instructies hierboven is dat source2 zowel een register als een waarde kan zijn. Dit hebben we al gebruikt in vorige delen bv:

mov r0, #1
mov r1, r0
add r2, r1, r0
add r2, r3, #4

Source2 kan veel meer zijn dan een waarde of een register. We kunnen het zelf combineren met een shift operatie. In deel 6 zagen we er al een, maar nu is het tijd om ze allemaal te laten zien.

  • LSL #n
    Logical Shift Left. Shifts bits n keer naar links. De n de linkse bits zijn weg en de n de rechtse bits zijn 0.
  • LSL Rsource3
    Zoals de voorige maar met de waarde van een register.
  • LSR #n
    Logical Shift Right. Zelfde van de eerste,  maar dan langs rechts,
  • LSR Rsource3
    Zelfde van de vorige, maar dan met een register.
  • ASR #n
    Arithmetic Shift Right. Zoals LSR Maar de meest linkse bit voor het shiften is gebruikt in plaats van 0 in de  n meest linkse.
  • ASR Rsource3
    Zoals de vorige, maar met een register.
  • ROR #n
    Rotate Right. Zoals LSR maar de n meest rechtste bits zijn niet weg maar worden de  n  meest linkse bits.
  • ROR Rsource3
    Zoals ROR, maar dan met een register.

In bovenstaande lijst is n een nummer van 1 tot 31. Arm heeft geen shift right en shift left instructie, dus moet je de mov instructie gebruiken:

mov r1, r2, LSL #1

Als je je afvraagt waarom je een register naar links of naar rechts wilt sturen, maar als je in deel 6 hebt gezien dat LSL een waarde geeft die hetzelfde doet als x2, dan geeft ASR een waarde die gedeeld door 2 is. Aangezien een shift van n hetzelfde is als n shifts van 1. Shifts zijn eigenlijk maal of gedeeld door een waarde van 2^n.

mov r1, r2, LSL #1      /* r1 ← (r2*2) */
mov r1, r2, LSL #2      /* r1 ← (r2*4) */
mov r1, r3, ASR #3      /* r1 ← (r3/8) */
mov r3, #4
mov r1, r2, LSL r3      /* r1 ← (r2*16) */

We kunnen dit combineren voor zeer handige gevallen:

add r1, r2, r2, LSL #1   /* r1 ← r2 + (r2*2) equivalent to r1 ← r2*3 */
add r1, r2, r2, LSL #2   /* r1 ← r2 + (r2*4) equivalent to r1 ← r2*5 */

Je kan ook zoiets doen met sub

sub r1, r2, r2, LSL #3  /* r1 ← r2 - (r2*8) equivalent to r1 ← r2*(-7)

Arm komt met een handige rsb functie (reverse subtract) instructie, deze doet het omgekeerde van de sub instructie

rsb r1, r2, r2, LSL #3      /* r1 ← (r2*8) - r2 equivalent to r1 ← r2*7 */

Een voorbeeld:

* Complicated way to multiply the initial value of r1 by 42 = 7*3*2 */
rsb r1, r1, r1, LSL #3  /* r1 ← (r1*8) - r1 equivalent to r1 ← 7*r1 */
add r1, r1, r1, LSL #1  /* r1 ← r1 + (2*r1) equivalent to r1 ← 3*r1 */
add r1, r1, r1          /* r1 ← r1 + r1     equivalent to r1 ← 2*r1 */

Je zult je afvragen waarom we deze functies willen gebruiken, maar omdat de standaard vermenigvuldigingsinstructie veel meer werk is voor de cpu, dus langer duurt, is het voor bepaalde gevallen handig (zeker als het kleinere bewerkingen zijn) om het zo te doen.

Rotaties zijn minder handig dan shifts in het werkelijke gebruik. Deze zijn dan ook vooral gebruikt voor het versleutelen van bits. ARM heeft geen functie om naar links te roteren, maar we kunnen wel iets anders doen:

/* Assume r1 is 0x12345678 */
mov r1, r1, ROR #1   /* r1 ← r1 ror 1. This is r1 ← 0x91a2b3c */
mov r1, r1, ROR #31  /* r1 ← r1 ror 31. This is r1 ← 0x12345678 */

RPI Assembler tutorial 6

In het vorige deel heb ik de branch instructies vergeleken met control-flow tools, dit was niet helemaal juist. Er zijn in ARM assembler ingebouwde if-else structuren te vinden.

IF, THEN, ELSE

Dit is een gemakkelijke. We hebben deze structuur al gebruikt in het vorige deel. Beeld je de volgende structuur in, waar E een expressie is, en S1 en S2 statements zijn.

if (E) then
   S1
else
   S2

Een manier om dit in assembler voor te stellen is:

if_eval: 
    /* Assembler that evaluates E and updates the cpsr accordingly */
bXX else /* Here XX is the appropiate condition */
then_part: 
   /* assembler for S1, the "then" part */
   b end_of_if
else:
   /* assembler for S2, the "else" part */
end_of_if:

Als er geen else deel is, kunnen we de bXX else met Bxx end_of_if vervangen.

Loops

Dit is ook een belangrijke. Een loop kan je voorstellen als volgt:

while (E)
  S

Zo doet S iets waardoor E uiteindelijk een waarde = false krijgt, waardoor de loop stopt. Anders zou hij altijd in de loop blijven. Dit is soms handig maar niet voor onze voorbeelden. Zo kan je deze implementeren:

while_condition : /* assembler to evaluate E and update cpsr */
bXX end_of_loop /* If E is false, then leave the loop right now */
/* assembler of S */
b while_condition /* Unconditional branch to the beginning */
end_of_loop:

Een andere veelvoorkomende loop is de FOR loop, deze laat een integer altijd stijgen (of dalen) zoals:

for (i = L; i < N; i += K)
  S

Deze is niets meer dan:

i = L;
  while (i < N)
  {
     S;
     i += K;
  }

Hierdoor kunnen we door een while-loop te gebruiken dezelfde functie krijgen als een for-loop

Een voorbeeld

Laat ons voor dit voorbeeld alle nummers van 1-22 optellen (1+2+3+… …+22)

/* -- loop01.s */
.text
.global main
main:
    mov r1, #0       /* r1 ← 0 */
    mov r2, #1       /* r2 ← 1 */
loop: 
    cmp r2, #22      /* compare r2 and 22 */
    bgt end          /* branch if r2 > 22 to end */
    add r1, r1, r2   /* r1 ← r1 + r2 */
    add r2, r2, #1   /* r2 ← r2 + 1 */
    b loop
end:
    mov r0, r1       /* r0 ← r1 */
    bx lr

Hier tellen we van 1 naar 22, we zullen r2 als counter gebruiken. Op lijn 6 zetten we r2 naar 1. De som zal opgeslagen worden in r1. Op het eind verplaatsten we de inhoud van r1 naar r0, waardoor we het resultaat van de som als een error code kunnen zien. Deze laatste stap kan je echter overslaan door direct r0 te gebruiken.

Op lijn 8 vergelijken we r2 (de counter) met 22 Dit update de cpsr, dus op lijn 9 kunnen we het resultaat gaan controleren. Als dat het geval is kunnen we branchen naar het eind (de end: tag). Anders voegen we de waarde van r2 toe aan r1.

Lijn 11 is een belangerijke. Hier  voegen we 1 toe aan de waarde van r2. Dit is omdat we van 1 naar 22 aan het tellen zijn we hebben de waarde van r2 al bij de som van r1 geteld. Op regel 12 branchen we weer naar het begin van de loop. Als regel 11 er niet was zou de loop nooit eindigen.

Als we dit uitvoeren zien we het resultaat.

$ ./loop01; echo $?
253

Nu, waarom heb ik 22 gekozen, en geen getal zoals bijvoorbeeld 100. Als we het zouden aanpassen naar 100 zouden we 5050 moeten krijgen, maar:

$ ./loop01; echo $?
186

Hier loopt iets mis. Dit gebeurt omdat de error-code van een programma in linux een getal is van 0 tot 255 (8 bits, 1 byte). Als het resultaat 5050 is, worden enkel de laagste 8 bits van het getal gebruikt. zo word 5050 in binair: 1001110111010, De laagste 8 bits zijn 10111010, wat 186 is. Laat ons GDB gebruiken om het te debuggen.

$ gdb loop
...
(gdb) start
Temporary breakpoint 1 at 0x8390
Starting program: /home/roger/asm/chapter06/loop01 
 
Temporary breakpoint 1, 0x00008390 in main ()
(gdb) disas main,+(9*4)
Dump of assembler code from 0x8390 to 0x83b4:
   0x00008390 <main+0>:    mov r1, #0
   0x00008394 <main+4>:    mov r2, #1
   0x00008398 <loop+0>:    cmp r2, #100    ; 0x64
   0x0000839c <loop+4>:    bgt 0x83ac <end>
   0x000083a0 <loop+8>:    add r1, r1, r2
   0x000083a4 <loop+12>:    add r2, r2, #1
   0x000083a8 <loop+16>:    b   0x8398 <loop>
   0x000083ac <end+0>:    mov r0, r1
   0x000083b0 <end+4>:    bx  lr
End of assembler dump.

Als we een breakpoint instellen op 0x00083ac, net voordat we r1 naar r0 verplaatsen:

(gdb) break *0x000083ac
(gdb) cont
Continuing.
 
Breakpoint 2, 0x000083ac in end ()
(gdb) disas
Dump of assembler code for function end:
=> 0x000083ac <+0>:    mov r0, r1
   0x000083b0 <+4>:    bx  lr
End of assembler dump.
(gdb) info register r1
r1             0x13ba    5050

Hier zien we wat we verwacht hadden, maar we konden het niet zien door de limieten van error codes.

Er is ook iets aan de gang met onze labels die functies worden. Dit zal ik verder uitleggen in een verder deel.

Dit was alles voor deel 6.