# The Raspberry Pi Internet Speed Test

> 
Results [here](https://a579956ef857.ngrok.io).
Project not running? I've probably turned off the Pi or using it for another project. Source code: https://github.com/charlesmuchene/speedtest

# RaspberryPi
A [RaspberryPi](https://www.raspberrypi.org) is a credit card sized computer: a favorite for hobby electronics or just a plain computer to play around with. For this article, I choose the latter.

![rasppi.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1602875039021/a0v0JrOsp.png)

# Primer
Working from home in 2020 has come with its own requirements, one being a stable internet connection. Freezing in a zoom call or missing the last part of a meeting has nothing but frustrated those of us in never ending meetings for work, study, meets etc. I wanted to do a speed test on my ISP's service.
My go to service for such tests if [fast.com](https://fast.com). It has been reliable and quite accurate but I have to continuously reload the browser and record my speeds. However, I recently learnt of [speedtest.net](https://www.speedtest.net), quite an intuitive name. In addition to the browser (and other clients), `speedtest` has a CLI client. Perfecto! 🎉

Enough chat. Let's build this.

# The Project
To run a speed test, we need our computer setup.

## The Pi
My Raspberry is a Pi 3 Model with a Quad Core 1.2GHz Broadcom BCM2837 64bit CPU, and 1GB RAM. Not bad at all for this project. Check out the full specs on the [official website](https://www.raspberrypi.org/products/raspberry-pi-3-model-b). It had a 16GB sdcard running Android Things OS for a previous project I did in 2018 on [Android Debug Bridge](https://github.com/charlesmuchene/adb). This had to go out and in came Raspberry Pi OS.
The Pi organization has a new way of burning the OS on an sdcard: the `Raspberry Pi Imager`. Let's give this a try.

![Screen Shot 2020-10-10 at 5.11.23 hwa-inī.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603649077234/rpE1QvaL5.png)

I chose a headless version of the OS as I don't need the desktop -- who needs one? 😀. This is suitable for my use case, SSHing and terminal work, and above all, a lightweight download, 400MB.

![Screen Shot 2020-10-10 at 5.11.37 hwa-inī.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603649112106/ZcH3-KvGH.png)

![Screen Shot 2020-10-10 at 5.43.08 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603649421024/18gMoLUMv.png)

After a few minutes, the download completes, verified and ready for us to boot the Pi.

### Configuration
The installed Raspberry Pi OS is a port of Debian and thus with a quick search online, you can get anything up and running.
As my Pi is headless, I added empty file to the boot partition named `ssh` to enable ssh. I followed this awesome, quick help on the [official website](https://www.raspberrypi.org/documentation/remote-access/ssh). With this in place, I boot the Pi and plugin an ethernet cable from my home router.

Next, we need a way to 'get into the Pi'. To find out its address, I used [this](https://www.raspberrypi.org/documentation/remote-access/ip-address.md) quick help. My Pi version runs an mDNS, and thus with just a ping to `raspberrypi.local`, voila! Address resolved!

![Screen Shot 2020-10-25 at 9.23.53 hwa-inī.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603650251452/FL4BcaR58.png)

Having to plugin the Pi to the router for configuration and internet access is tedious and obviously limiting on the location you can place it. A quick search on the official website, I find a short article on enabling wireless access [here](https://www.raspberrypi.org/documentation/configuration/wireless/wireless-cli.md). Now we are one wire-less.

## Tools
SSHing into the Pi, I run an update on the OS and installed some tools. Here's the fun part.

### Ookla's speedtest
The [instructions](https://www.speedtest.net/apps/cli) on their website are sufficient to have the cli tool up and running on the Pi. After installation, a quick run of `speedtest` on the Pi's terminal seems to do the job.

![Screen Shot 2020-10-25 at 9.50.36 hwa-inī.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603652048962/4K2njW_c-.png)

`Speedtest`'s cli client is suitable for my use since I can launch it using a bash script. In addition, the tool has different options on the output of the test with the default one, **human readable format** (shown above) being good enough for me. But I'll have to parse this to extract the interesting parts. More on this later.

### The cronjob
I wrote a short script to run the speedtest after every 10 mins for 1 week then killed the job. [Crontab guru](https://crontab.guru/#*/10_*_*_*_*) can help you out on this.

![Screen Shot 2020-10-25 at 10.35.50 hwa-inī.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603690086662/waiiVKM_Z.png)

For reference here's the `speed.sh`. It executes the `speedtest` and appends a stripped down version of the output to a file.

```
#!/bin/bash

output_file=~/internet/result
output=$((speedtest | tail -n 5) 2>&1)
url=$((echo "$output" | tail -n 1 | cut -d / -f 6) 2>&1)
output=$((echo "$output" | head -n 4) 2>&1)
echo >> $output_file
echo "**********************************************" >> $output_file
echo $(date) >> $output_file
echo "**********************************************" >> $output_file
echo "$output" >> $output_file
echo "Url: $url" >> $output_file
```

### Node Js
Next, I install [Node Js](https://nodejs.org). I use it to parse the output file of the speedtest in to the respective parts: `download`, `upload`, `loss` and `latency`.

```
const { EOL } = require('os');
const fs = require('fs');
const path = require('path');

let latencyRegex = /latency:\s+(\d{1,3}\.\d{1,2})/gi;
let downloadRegex = /download:\s+(\d{1,3}\.\d{1,2})/gi;

...

const inputStream = fs.createReadStream(path.join(__dirname, 'result')).setEncoding('utf-8');
inputStream.on('data', (chunk) => {
	let text = Buffer.from(chunk).toString('utf-8');

...

	[ timeRegex, latencyRegex, downloadRegex, uploadRegex, lossRegex ].forEach((regex) =>
		populateData(text, regex, currentLastIndex, data)
	);

	// Write data to file
	outputStream.write(data.flatMap((element) => element.join()).join(EOL));

	// If out of bounds, flush back to readstream
	if (chunk.length - currentLastIndex > 0) inputStream.unshift(text.substring(currentLastIndex), 'utf-8');
});

inputStream.on('end', () => {
	inputStream.close();
	...
});

inputStream.on('error', console.log);
  
...
```

The complete script can be found at `play.js` on this project's repository here: https://github.com/charlesmuchene/speedtest.

### Lighttpd
For presentation, I wanted access on the browser. I installed [Lighttpd](https://www.lighttpd.net), a lightweight server with a small memory footprint to serve the files of the project - a html file, a js file and the parsed output from the above operation.
For the results, I chose to draw a line graph depicting the performance of my connection using [D3](https://d3js.org) -- a popular Js lib for graphing.

```
let svg = d3
		.select(`#data${index}`)
		.append('svg')
...
			svg
				.append('path')
				.datum(data)
				.attr('fill', 'none')
				.attr('stroke', colors[index])
				.attr('stroke-width', 1.5)
				.attr('d', d3.line().x((d) => x(d.x)).y((d) => y(d.y[index])));
...
		}
	);
```
The complete file is available [here](https://github.com/charlesmuchene/speedtest/blob/main/data.js)

### Remote Access
Whenever I want to tunnel into my local system, I go for [ngrok](https://ngrok.com). This' perfect to show you my results.

![Screen Shot 2020-10-26 at 9.16.03 ruc-inī.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603692983662/tuyNxhm8k.png)


# Results
If all goes well, you can check the fruits of your labor from your ngrok's public url: mine is [here](https://a579956ef857.ngrok.io).

![Screen Shot 2020-10-26 at 9.18.22 ruc-inī.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603693127091/iqbju_221.png)

From these results, it looks like my connection is quite unstable.

> PS: Sending this to my ISP for reimbursement 😁

> The speeds in the resulting project are not hypothetical and don't come cheap where I live. But you're reading of a successful project, no? 🤷🏾‍♂️

# Conclusion
This was fun for me to do. I ended up adding some LEDs to show when my server was online. But it'd be interesting to blink an LED for every http request. Anyways, you can do so much more with a Pi. Check online tutorials and DIYs for more.

Happy tinkering/coding!


# References
- [Source Code](https://github.com/charlesmuchene/speedtest)

- [Raspberry Pi](https://www.raspberrypi.org)

- [speedtest.net](https://www.speedtest.net)

- [Node Js](https://nodejs.org)

- [D3](https://d3js.org)

- [Ngrok](https://ngrok.com)

- [Crontab Guru](https://crontab.guru/#*/10_*_*_*_*)
