Raspberry Pi Internet Speed Monitor

In this Raspberry Pi internet speed monitor tutorial, we will be showing you how you can set up your Raspberry Pi to monitor your internet connection and save the data to view in Grafana or Google Drive.

 Raspberry Pi Internet Speed Monitor

This tutorial is great if you’re interested in monitoring how your download speed, upload speed, and ping are affected over time.

Additionally, this can help you work out what times your network may be at its peak capacity or if you’re suffering from a degraded internet connection.

To achieve this internet speed monitor, we will be showing you how to write a small Python script that interacts with a program called Speedtest CLI from Ookla.

Speedtest CLI is what our internet speed monitor will use to monitor the internet connection.

This program works by polling the popular speedtest.net service to get your ping, download speed, and upload speed.

If you would prefer to have a more full featured speed test tracker, we recommend following our speedtest Docker guide instead. This will give you a fancy web interface you can use to view your results and still allow integration with InfluxDB.

Equipment List

Below are all the pieces of equipment that we made use of to set up our Raspberry Pi internet speed monitor.

Recommended

Optional

Video Tutorial

You can watch the video below to learn how to set up an internet speed monitor on the Raspberry Pi.

Alternatively check out the written version of the guide in the next section.

Adblock removing the video? Subscribe to premium for no-ads.

Installing the Speedtest CLI

1. Before we get started with setting up the Raspberry Pi to monitor the internet’s speed, we must first make sure our Raspberry Pi is up to date.

We can update the Raspberry Pi by running the following two commands within the terminal.

sudo apt-get update
sudo apt-get upgrade

2. We now need to install some packages so that we can add the package repository for the Speedtest CLI software.

Run the following command to install gnupg1, apt-transport-https, dirmngr and lsb-release to your Raspberry Pi.

sudo apt install apt-transport-https gnupg1 dirmngr lsb-release

The apt-transport-https package is used to add support for the https protocol to the apt package manager. Without it apt will throw errors when connecting to Ookla’s package repository.

Additionally we also install gnupg1, this package is used for secure communication between your Raspberry Pi and the Speedtest.net servers.

We then install the dirmngr package. This package is utilized for handling the addition of the package repository to your Raspberry Pi’s sources list.

Finally, the last package we install is called “lsb-release“. We utilize this package to grab the operating systems release name.

3. With the packages we need installed we can now add the GPG key for Ookla’s Speedtest repository to the keychain.

We need this keychain to be able to download the speedtest command line interface to our Raspberry Pi.

curl -L https://packagecloud.io/ookla/speedtest-cli/gpgkey | gpg --dearmor | sudo tee /usr/share/keyrings/speedtestcli-archive-keyring.gpg >/dev/null

4. Next we need to add the Ookla repository to our sources list.

Without adding the repository we won’t be able to install the Speedtest CLI to our Raspberry Pi.

You can add this repository by running the following command.

echo "deb [signed-by=/usr/share/keyrings/speedtestcli-archive-keyring.gpg] https://packagecloud.io/ookla/speedtest-cli/debian/ $(lsb_release -cs) main" | sudo tee  /etc/apt/sources.list.d/speedtest.list

Within this command, you will notice we use “$(lsb_release -cs)“. This bit of text allows us to insert the release name for our installation of Raspberry Pi OS directly into the command.

5. As we added a new package repository we need to update our package list.

Updating the package list is as simple as running the following command.

sudo apt update

6. Finally, we can install the official Speedtest CLI to our Raspberry Pi from Ookla.

Use the following command to install the package to your device.

sudo apt install speedtest

6. We can now test that we have installed the speedtest software to your Raspberry Pi.

Let us run the following command to start up the speedtest.

speedtest

When you first run the speedtest software on your Raspberry Pi you will be asked to agree to some terms and conditions.

To proceed passed this warning all you need to do is type in “YES” followed by the ENTER key.

Writing our Speed Test Python Script

1. Now that we have Speedtest CLI installed on the Raspberry Pi, we can now proceed to write our Python script that will continually monitor our download and upload speeds.

We can begin creating our Python script for the Raspberry Pi internet speed monitor by running the following command.

cd ~
nano speedtest.py

2. Within this file write the following lines of code. We will explain each important section of the code, so you get an idea of how everything works.

3. These four lines define all of the libraries that we will be relying on in our script. Below we will explain how each of these libraries will be is used.

import os: The os library is used by the script to interact with the operating system itself.

For this script, we will be using this library to check if a file exists.

import re: The re library allows us to easily do regular expressions by providing a library for handling pattern searches.

We use this to find our wanted values out of the data given to us from the Speedtest CLI.

import subprocess: The subprocess library is essential to this script, as we require it to be able to call another python script.

In our case, we will be using the subprocess library so we can launch up the Speedtest CLI software and retrieve the values returned by it.

import time: We utilize the time library so that we can record both the date and time for each call to the Speedtest CLI software.

This library is what will allow us to track our speed over a length of time.

import os
import re
import subprocess
import time

4. In this line of code, we utilize the subprocess library to launch a call to the Speedtest CLI and tell it to pipe everything from speedtest to stdout.

By using stdout.read().decode('utf-8') at the end of the call we store the response from the Speedtest CLI to our response variable and decode it to being a useable Python string.

response = subprocess.Popen('/usr/bin/speedtest --accept-license --accept-gdpr', shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')

5. These three lines of code are fairly simple, and all do the same thing.

They use the re library to run a regular expression for a certain piece of text and find the number located next to each piece of text.

For instance, the search for ping finds “Latency: 47.943 ms” but only grabs the number that is between the text.

ping = re.search('Latency:\s+(.*?)\s', response, re.MULTILINE)
download = re.search('Download:\s+(.*?)\s', response, re.MULTILINE)
upload = re.search('Upload:\s+(.*?)\s', response, re.MULTILINE)
jitter = re.search('Latency:.*?jitter:\s+(.*?)ms', response, re.MULTILINE)

6. These lines are important as we use the “.group()” function to grab the correct numbers from our regular expressions.

Using this we will get the values we are after from the output of the Speedtest CLI software that we can write to our CSV file.

ping = ping.group(1)
download = download.group(1)
upload = upload.group(1)
jitter = jitter.group(1)

7. This bit of code is straightforward. The code is kept within a try statement so that if any errors occur, it will not stop the script from operating.

Within the try statement, we first open up a call to our speedtest.csv file. Please note you may need to adjust the directory. Our code assumes that your user is called “pi“.

By using, “a+” in the arguments, we tell it that we want to create the file if it doesn’t exist, and that any new data should be appended to whatever is already in there.

Afterward, we utilize the os library to check our speedtest.csv files actual size in bytes.

If the file’s bytes is equal to 0, we go ahead.

If the file does exist, we proceed on as normal.

Ensure that you replace the placeholder “<USERNAME>” with your username.

try:
    f = open('/home/<USERNAME>/speedtest/speedtest.csv', 'a+')
    if os.stat('/home/<USERNAME>/speedtest/speedtest.csv').st_size == 0:
            f.write('Date,Time,Ping (ms),Jitter (ms),Download (Mbps),Upload (Mbps)\r\n')
except:
    pass

8. Finally, we print out all our data separated by commas.

We use the time library’s strftime() function to insert both the current date and the current time into our formatted string.

After that, we insert our ping, download, and upload.

f.write('{},{},{},{},{},{}\r\n'.format(time.strftime('%m/%d/%y'), time.strftime('%H:%M'), ping, jitter, download, upload))

9. Below we have an included an example of what the output data will look like on the first run of our code.

Date,Time,Ping (ms),Jitter (ms),Download (Mbps),Upload (Mbps)
04/29/21,06:28,18.32,1.21,23.30,7.78

10. Once you have finished writing your code, it should end up looking like what we have displayed below.

Remember that you must replace “<USERNAME>” with your username. For example, if your username was “pimylifeup” you would use “/home/pimylifeup/speedtest/speedtest.csv” within this script.

import os
import re
import subprocess
import time

response = subprocess.Popen('/usr/bin/speedtest --accept-license --accept-gdpr', shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')

ping = re.search('Latency:\s+(.*?)\s', response, re.MULTILINE)
download = re.search('Download:\s+(.*?)\s', response, re.MULTILINE)
upload = re.search('Upload:\s+(.*?)\s', response, re.MULTILINE)
jitter = re.search('Latency:.*?jitter:\s+(.*?)ms', response, re.MULTILINE)

ping = ping.group(1)
download = download.group(1)
upload = upload.group(1)
jitter = jitter.group(1)

try:
    f = open('/home/<USERNAME>/speedtest/speedtest.csv', 'a+')
    if os.stat('/home/<USERNAME>/speedtest/speedtest.csv').st_size == 0:
            f.write('Date,Time,Ping (ms),Jitter (ms),Download (Mbps),Upload (Mbps)\r\n')
except:
    pass

f.write('{},{},{},{},{},{}\r\n'.format(time.strftime('%m/%d/%y'), time.strftime('%H:%M'), ping, jitter, download, upload))

11. You can now save the file by pressing CTRL + X then Y and finally press ENTER.

12. With our script now written we need to make a folder where our speedtest.csv file will be stored. Run the following command to create this folder.

mkdir ~/speedtest

13. Now that we have made the required folder, we can go ahead and test the script.

We can test our script by running the following command.

python3 ~/speedtest.py

14. Once the script has finished executing you can then check out the results by opening up the newly created speedtest.csv.

Let’s open up this file by running the following command on the Raspberry Pi.

nano ~/speedtest/speedtest.csv

15. Within this file, you should see something like what we have below. The column headers and some rows of data.

Date,Time,Ping (ms),Jitter (ms),Download (Mbps),Upload (Mbps)
04/29/21,06:28,18.32,1.21,23.30,7.78

There are two different ways you can take this guide.

You can either use InfluxDB and Grafana to store and graph your data, or make use of gDrive to sync your data to Google Drive.

Using Grafana to Display your Speedtest Data

In this section, we will be showing you how you can use Grafana to graph your speed test data quickly.

Setting up InfluxDB for our Internet Speed Monitor

Before you start this section of this internet speed monitor tutorial, you will need to make sure you have installed InfluxDB V1 to your Raspberry Pi or on a separate server.

We will be using this as a place to store the data that our internet speed monitor receives.

1. To start, we need to go ahead and run the following command to start up the InfluxDB command-line tool.

We will be using this tool to create a database where we will store our data.

influx -username admin -password <password>

If you haven’t set up authentication, then you don’t need to worry about specifying the username and password to connect to InfluxDB.

2. Let’s now create a database called “internetspeed“.

Creating a database within InfluxDB is easy as using CREATE DATABASE followed by the database name.

CREATE DATABASE internetspeed

3. Our next step is to create a user called “speedmonitor” that we will be using to interact with our database.

Make sure that you swap out the password “pimylifeup” with something a bit more secure.

Don’t worry about privileges as we will be handling that in the next step.

CREATE USER "speedmonitor" WITH PASSWORD 'pimylifeup'

4. Now assign the new “speedmonitor” user all privileges to our “internetspeed” database.

GRANT ALL ON "internetspeed" to "speedmonitor"

5. With the database created, quit out of the tool by using the following command.

quit

6. The last thing we need to do is install the Python library we need to interact with our Influx database.

sudo apt install python3-influxdb

Saving our Speed Monitor Data to our InfluxDB

1. Now that we have our InfluxDB database created, let’s begin adding data into it by creating a new Python script.

This script will be similar to the one we created earlier, so we will only explain the new things that we are doing.

rm ~/speedtest.py
nano ~/speedtest.py

2. To start off this file, we need to import all the Python libraries we need.

import re
import subprocess
from influxdb import InfluxDBClient

As you can see, we have removed both the “os” and “time” libraries.

Both of these libraries are no longer needed as we don’t need to interact with files, and Influx automatically timestamps data.

We now import the “InfluxDBClient” client, which we will be using to interact with our InfluxDB server.

3. Our next step after importing packages is to call the Speedtest CLI and process the data.

By the end of this code block, we will have just the data that we are after.

response = subprocess.Popen('/usr/bin/speedtest --accept-license --accept-gdpr', shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')

ping = re.search('Latency:\s+(.*?)\s', response, re.MULTILINE)
download = re.search('Download:\s+(.*?)\s', response, re.MULTILINE)
upload = re.search('Upload:\s+(.*?)\s', response, re.MULTILINE)
jitter = re.search('Latency:.*?jitter:\s+(.*?)ms', response, re.MULTILINE)

ping = ping.group(1)
download = download.group(1)
upload = upload.group(1)
jitter = jitter.group(1)

4. Now here is where things get a little bit different. We need to format that data into a Python dictionary.

The reason for this is that the library expects the data to be in a JSON like format.

speed_data = [
    {
        "measurement" : "internet_speed",
        "tags" : {
            "host": "RaspberryPiMyLifeUp"
        },
        "fields" : {
            "download": float(download),
            "upload": float(upload),
            "ping": float(ping),
            "jitter": float(jitter)
        }
    }
]

Here we set up our dictionary with how InfluxDB expects the data. We gave the measurement a name, “internet_speed“.

We also set a tag called “host” to allow us to separate this out if we decide to handle multiple devices in the same database.

Next, we pass in the fields, which is the download speed, upload speed and ping that we retrieved in the previous bit of code.

We wrap our download, upload and ping variables in the float() function to convert them from a string to a number.

If we don’t use the float() function, Grafana will see these as strings and not interpret them correctly.

5. With all the data we need now on hand, we can now proceed to start using InfluxDB.

The first thing we need to do is to instantiate the InfluxDBClient library and pass in our connection details.

We only pass in the first five parameters for this function, the host, port, username, password, and database name.

If you want to see what data can be set, then you can view the official InfluxDB Python documentation.

client = InfluxDBClient('localhost', 8086, 'speedmonitor', 'pimylifeup', 'internetspeed')

If you are using an InfluxDB server that is not located on your Raspberry Pi, make sure you switch “localhost” with its IP address.

Also, you will need to change “pimylifeup” to the password that you set up earlier in the guide.

6. With a connection now made to our InfluxDB server, we can now write our data point to the server by adding the following line of code.

client.write_points(speed_data)

7. That is all we need to do to pass data to InfluxDB.

When you have finished typing in all the code the file, it should end up looking like what we have below.

import re
import subprocess
from influxdb import InfluxDBClient

response = subprocess.Popen('/usr/bin/speedtest --accept-license --accept-gdpr', shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')

ping = re.search('Latency:\s+(.*?)\s', response, re.MULTILINE)
download = re.search('Download:\s+(.*?)\s', response, re.MULTILINE)
upload = re.search('Upload:\s+(.*?)\s', response, re.MULTILINE)
jitter = re.search('Latency:.*?jitter:\s+(.*?)ms', response, re.MULTILINE)

ping = ping.group(1)
download = download.group(1)
upload = upload.group(1)
jitter = jitter.group(1)

speed_data = [
    {
        "measurement" : "internet_speed",
        "tags" : {
            "host": "RaspberryPiMyLifeUp"
        },
        "fields" : {
            "download": float(download),
            "upload": float(upload),
            "ping": float(ping),
            "jitter": float(jitter)
        }
    }
]
client = InfluxDBClient('localhost', 8086, 'speedmonitor', 'pimylifeup', 'internetspeed')

client.write_points(speed_data)

8. Save the file by pressing CTRL + X, followed by Y, then ENTER.

Viewing our Internet Speed Data in Grafana

Our next step is to get this database to show up in Grafana.

Before beginning this section, you must make sure that you have Grafana set up on your Raspberry Pi or a separate device.

Grafana is the tool that we will be using to graph and visualize all our data.

1. Once you have Grafana set up, you can proceed on with this tutorial by opening up its web interface.

If you are unsure where this sits, then go to your Raspberry Pi’s or server’s IP address, followed by the port :3000.

IPADDRESS:3000

2. Within the Grafana web interface, we need to add a new data source.

You can do this by hovering over the cog in the sidebar (1.), followed by the “Data Sources” menu option (2.).

Adding a new data source to Grafana

3. In the next menu, you will need to click the “Add data source” button to proceed.

Add Data Source to Grafana Configuration Page

4. On the “Add data source” page, you need to find InfluxDB, hover over it and click the “Select” button.

Add InfluxDB Time series database to Grafana

5. Now we are required to enter the details of your InfluxDB installation.

First, enter the URL for InfluxDB (1.). In our case, this is on the same Raspberry Pi that we are running the software on. If you are doing the same, then use the following URL.

http://localhost:8086

Next, we will need to enter all the details for our database (2.).

If you have followed our guide exactly, the Database should be set to “internetspeed“.

The User should be “speedmonitor”, and finally, the password should be the one you specified, if you used our example one this password would be “pimylifeup“.

Once you have set all the required information, click the “Save & Test” button (3.) located at the bottom of the screen.

Add InfluxDB Details for Grafana

6. The next step is to create a dashboard to display this data.

Hover over the addition icon (+) in the sidebar and click the “Dashboard” option.

Creating a Dashboard in Grafana

6. Over the “New Panel“, click the “Add Query” button.

Add Query to Dashboard

7. We can now set up the graph to read from our database.

Clickselect measurement” (1.) then select our measurement name, which in this case is “internet_speed” (2.)

Select Measurement to query

8. With the measurement set, we now need to set the field data we want to obverse.

Click the text “value” next to the field (1.). From here, select either “download“, “ping” or “upload“.

For this guide, we will be starting with “download” (2.).

Select Field to Display

9. Next, we want to make Grafana treat each result as a distinct individual result.

We can do this by clicking the addition (+) button in the select row (1.).

In the pop-up menu, hover over “Aggregations” and clickdistinct” (2.).

Change data aggregation

10. Finally, let’s give this data a better name so that we can understand it easier.

Set the alias of each of the fields to represent what they contain (1.). For example, with the download speed, we set the name “Download Speed“.

Now click the “Add Query” button (2.) and repeat steps 79 until you have all three fields (download, upload, and ping) added to the graph.

Set Data Alias for Measurement

11. Once you have added each field to the graph, click the spanner and cog symbol in the sidebar.

Configure Grafana Panel

12. In the “Title” option (1.) type in what you want to be displayed above the graph. In this example, we used the title “Network Speed“.

Once that is done, save the dashboard by clicking the save button (2.) in the top right-hand corner of the screen.

Set Panel Name for Grafana

13. Give a name to your dashboard (1.). This name can be anything you want. We called our “Network Speed Monitor“.

Now you save this new dashboard by clicking the “Save” button (2.).

Save Grafana Dashboard

14. With everything done, you should now be able to see your data displayed in a nice graph.

Internet Speed Monitor Dashboard Ready

Automating your Speed Monitor script with Grafana

Automating your script so that it runs periodically is a reasonably straightforward process.

1. The easiest way to automate your script to run every so often is to make use of the crontab.

You can modify the crontab by running the following command on your Raspberry Pi.

crontab -e

If you are asked what editor you want to use, we recommend you select nano as its the easiest to use.

2. Within this file, add the following cronjob to the bottom.

This cronjob will run every 30 minutes. If you want to work out a different timing, we recommend that you make use of our Crontab generator.

*/30 * * * * python3 /home/<USERNAME>/speedtest.py

As you can see, this is an effortless way of getting your internet speed tested regularly and not require you to run the script manually.

3. Once done, you can save the file by pressing CTRL + X, then Y, followed by ENTER.

The crontab will automatically be updated and will begin to run your script immediately.

Uploading your Internet Speed Data to Google Drive

In this section, we will be showing you how you can automatically sync your data with your Google Drive account.

Uploading Speed Test Data to Google Drive

1. Before you begin this section, you must have completed our compiling and running gDrive guide.

We will be using that software to complete the following steps.

2. With gDrive setup on your Raspberry Pi, we can now prepare it for our speed test data.

Let’s start by using gDrive to create a new folder on your Google Drive to store our speedtest.csv file.

We can do that by using the following command within the terminal.

gdrive mkdir speedtest

This command will show you a message saying the directory was created. This message will also show you an ID.

We will need this ID for our next few steps, so write it down somewhere safe.

3. With the folder now created, we can use its ID to upload a file to it.

For this test, we will be uploading our speedtest.csv file.

Run the following command and make sure you replace YOUR_FOLDER_ID with the id you retrieved in the previous step.

gdrive sync upload ~/speedtest YOUR_FOLDER_ID

On the initial sync, you should see a message like below appear on the command line.

This message tells you that the file has been successfully synced to your Google Drive account.

Starting sync...
Collecting local and remote file information...
Found 1 local files and 0 remote files

1 remote files are missing
[0001/0001] Uploading speedtest.csv -> speedtest/speedtest.csv
Sync finished in 3.119979351s

4. With Google Drive now successfully syncing with the Raspberry Pi, we can move onto automating the whole process so that we upload the speedtest.csv every time we edit it.

Automating the Raspberry Pi Internet Speed Monitor for Google Drive

1. The next big thing to do with your Raspberry Pi Internet Speed Monitor is to automate it.

To do the automation, we will be writing a simple bash script. This script will be called by crontab so that it will be run routinely.

Begin writing the bash script by running the following Linux command on your Raspberry Pi.

nano ~/speedtest.sh

2. Within this file, we want to enter the following lines.

Make sure that you replace “YOUR_FOLDER_ID” with the ID that you received when you created the folder on Google drive.

#!/bin/bash
python3 /home/<USERNAME>/speedtest.py
/usr/local/bin/gdrive sync upload  /home/<USERNAME>/speedtest YOUR_FOLDER_ID

3. Now save the file by pressing CTRL + X then Y and then finally ENTER.

4. Before we set up the crontab for our bash script, we must first give the script execution privileges.

We can achieve this by running the following command within the terminal.

sudo chmod +x ~/speedtest.sh

5. Now with everything done, we can finally move onto setting up the crontab.

Begin editing the crontab by running the following command on your Raspberry Pi. If you are asked what editor you want to use, we recommend you select Nano (Typically number 2).

crontab -e

6. Add the following line at the bottom of the file. This line will tell crontab that it should run our bash script once every hour.

If you want to change the behavior of the crontab, you can look at our Crontab generator to work out your desired values.

0 * * * * /home/<USERNAME>/speedtest.sh

7. You should now have your speed test monitor up and running with the script updating your speedtest.csv file on an hourly basis.

We hope by the end of this Raspberry Pi internet speed monitor tutorial you should now have your Raspberry Pi automatically running internet speed tests on your connection and uploading that data to Google on an hourly basis.

If you have any thoughts, feedback or anything else, then be sure to leave a comment below.

Leave a Reply

Your email address will not be published. Required fields are marked *

210 Comments

  1. Avatar for Tabby
    Tabby on

    Hi, great tutorial and thanks for putting this together.
    However, nothing is being added to the influxdb and when I run the python script using (python3 ~/speedtest.py) I get the following errors. Please can you help?

    Traceback (most recent call last):
      File "/home/pi/speedtest.py", line 33, in 
        client.write_points(speed_data)
      File "/usr/lib/python3/dist-packages/influxdb/client.py", line 525, in write_points
        return self._write_points(points=points,
      File "/usr/lib/python3/dist-packages/influxdb/client.py", line 594, in _write_points
        self.write(
      File "/usr/lib/python3/dist-packages/influxdb/client.py", line 337, in write
        self.request(
      File "/usr/lib/python3/dist-packages/influxdb/client.py", line 274, in request
        response = self._session.request(
      File "/usr/lib/python3/dist-packages/requests/sessions.py", line 528, in request
        prep = self.prepare_request(req)
      File "/usr/lib/python3/dist-packages/requests/sessions.py", line 456, in prepare_request
        p.prepare(
      File "/usr/lib/python3/dist-packages/requests/models.py", line 320, in prepare
        self.prepare_auth(auth, url)
      File "/usr/lib/python3/dist-packages/requests/models.py", line 551, in prepare_auth
        r = auth(self)
      File "/usr/lib/python3/dist-packages/requests/auth.py", line 96, in __call__
        r.headers['Authorization'] = _basic_auth_str(self.username, self.password)
      File "/usr/lib/python3/dist-packages/requests/auth.py", line 63, in _basic_auth_str
        password = password.encode('latin1')
    UnicodeEncodeError: 'latin-1' codec can't encode character '\u201d' in position 10: ordinal not in range(256)

    Thanks.

  2. Avatar for Daniel
    Daniel on

    Hi Emmet,

    just wanna say thank you very much for this tutorial! I set it up on a Raspi 2 B and it works like a charm! Helped me a lot to identify the internet bottleneck in my house. Thumbs up!!!

    Best,
    Daniel

    1. Avatar for Emmet
      Emmet on
      Editor

      Hi Roger,

      Thank you for the heads up. I have updated the tutorial to utilize their new repository.

      Cheers,
      Emmet

  3. Avatar for Ian
    Ian on

    Hi Gus,

    I have had the script running on a 4b for a couple of years now, very stable and reliable, results being uploaded to my GDrive. now Google tell me that they are depreciating this method of oauth being used to access google drive and my script will be blocked after October 3rd 2022. Are they any work arounds that I could use to keep it going. Google are very non-descript about what I need to do.
    Thanks for any help and I enjoy your tutorials and projects.
    Ian.

    1. Avatar for Emmet
      Emmet on
      Editor

      Hi Ian,

      Thank you for alerting me to this issue. It looks like that there might be a fork of the gDrive app that works around the upcoming issue and already has fixes implemented.

      I’ll need to investigate this and have added it on to my “to check” list. I will comment here again when I have updated that tutorial.

      Cheers,
      Emmet

  4. Avatar for Gus Janeson
    Gus Janeson on

    Hi Emmet,
    Thanks for the brilliant tutorial.
    I am somewhat stuck – when I run speedtest, i get:

    Retrieving speedtest.net configuration...
    Cannot retrieve speedtest configuration
    ERROR: HTTP Error 403: Forbidden

    when I type:

    /usr/bin/speedtest --accept-license --accept-gdpr

    I get the full internet speedtest being run. Any ideas what I might be doing wrong?

    many thanks
    Gus

    when i run

    1. Avatar for Emmet
      Emmet on
      Editor

      Hi Gus,

      This is an incredibly strange issue that I haven’t seen happen here previously.

      If it is working fine when using the command within the terminal I can’t see why it would be getting broken when ran using Python.

      You could try using the “-s SERVERID” option to select a specific server and see whether that solves your issues.

      Cheers,
      Emmet

    2. Avatar for David
      David on

      I got the same error, just uninstalled speedtest using “sudo apt remove speedtest” and then reinstalled it and it worked fine.

  5. Avatar for Roger L
    Roger L on

    Hi, I just set this up a couple days ago on a 3B+, using hardwired 1G networking, my feed from WOW is 200mbs and my laptop, desktop, etc all register speeds in the 200s but the speed test Pi does not, it will do 200 every now and again but mostly is in the 75-100mbs range. How do i identify what server it is connecting to? it could just be tht as I have no idea if when I run it or run speedtest manually if they are connecting to the same feed for equal comparison.

    thanks much for any help

    1. Avatar for Emmet
      Emmet on
      Editor

      Hi Roger,

      This is likely due to a limitation of the Raspberry Pi 3B+ ethernet handling. While it is technically capable of gigabit connections it can’t actually fully utilize it. The chip that handles this on the Pi 3B+ is also what handles the USB 2.0 hub, so the bandwidth is split, the more load on the USB 2.0 hub will reduce it further.

      If you run “speedtest” outside of the script, you should see it listed within the results. It is possible however to adjust the script to also save this data if its something you want to track.

      To create a more consistent result you could just adjust the command call to the speedtest CLI so that it always tries the same server.

      Within the Python script find the following line of code.

      response = subprocess.Popen('/usr/bin/speedtest --accept-license --accept-gdpr', shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')

      Now adjust “/usr/bin/speedtest --accept-license --accept-gdpr” by adding “-s SERVERID” to the end of it, where SERVERID is the ID of a server.

      For example, if we wanted to test our internet connection against the “Vocus Communications – Sydney” server we would be using the ID 17618 as shown below.
      We got this ID by running the speedtest CLI. You can however also get this by loading up speedtest.net, hovering over the ISP/Server name and making a note of the ID at the end of the URL that shows up upon hovering.

      response = subprocess.Popen('/usr/bin/speedtest --accept-license --accept-gdpr -s 17618', shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')

      Please let me know if you need a better explanation or need any other assistance with this tutorial.

      Cheers,
      Emmet

    2. Avatar for Roger L
      Roger L on

      Followup here as I can’t reply to Emmet.. Yes, I would like it to test with the same server I am testing with from the web….so thank you very much for that.

      I am aware of the USB limitation on the NIC, but the limit is 330MBS if I’m not mistaken and I only have a 200MBS, so i should see my speeds…right?
      Nothing else is attached to the PI USB.

      I mean it does show some of the tests hitting 200+, but the majority of them are not, and showing drastically different than from the web.

      I’ll update after further testing. Thanks again.

    3. Avatar for Emmet
      Emmet on
      Editor

      Hi Roger,

      Awesome, hopefully that allows you to get more consistent results from Speedtest.net.

      Please let me know if it was just the inconsistent server selection from Speedtest-cli or whether the issue still presents itself.

      Cheers,
      Emmet

    4. Avatar for Roger L
      Roger L on

      well, the reading still were coming in low, and then my PC started giving me weird readings. I was doing both at the server 24678 and tried 6436.
      So just for SAGS I installed Speedtest on an Odroid C1 I had, as it has a gigibit NIC on it.
      Well, I only got as far installing Speedtest, no Grafana yet, but the first 4 readings I got from it were 221,228, 226, and 221, in just over a 30 minute span.
      Meanwhile the original RasPi was only giving around 100s and lower during the same timeframe, just minutes apart from the Odroid…even though the Raspi did have a 200 an hour or so ago.

      So, I am going to finish the grafana install tomorrow on the Odroid and will let you know. thanks again.

    5. Avatar for Roger L
      Roger L on

      good news, I finished the grafana install on the odroid and I’ve been getting pretty consistent 220-225 MBs from the speed test. So i guess the 3B+ couldn’t take it even with nothing else on the USB. The Odroid C1 is a bit more powerful in comparison to the 3B+. Guess I’ll plan to print a case for it and leave it as the speed test unit. I’ll find something else for the 3B+. oh yeah, I’m running it on DietPi 8.8.1 which is debian based so all your instructions worked, I just had to change the directory structure. now, if only I knew how to delete old data …. like stuff after a week. any ideas? Thanks for your help.

  6. Avatar for Joshua
    Joshua on

    Set this up and it ran for a few months, but stopped on the 15th of August. Now my mail is FILLED with error messages from the system, and I cannot make heads or tails of them.

    Oldest message indicates the following:

    Traceback (most recent call last):
      File "/home/pi/speedtest.py", line 33, in 
        client.write_points(speed_data)
      File "/usr/lib/python3/dist-packages/influxdb/client.py", line 490, in write_points
        tags=tags, protocol=protocol)
      File "/usr/lib/python3/dist-packages/influxdb/client.py", line 551, in _write_points
        protocol=protocol
      File "/usr/lib/python3/dist-packages/influxdb/client.py", line 327, in write
        headers=headers
      File "/usr/lib/python3/dist-packages/influxdb/client.py", line 282, in request
        raise InfluxDBServerError(response.content)
    influxdb.exceptions.InfluxDBServerError: b'{"error":"timeout"}\n'

    Later messages say other things. When I tried running the script from the terminal manually, I got a different error code:

    [error] Error: [0] Cannot read from socket (0 bytes read)
    Traceback (most recent call last):
      File "./speedtest.py", line 15, in 
        jitter = jitter.group(1)
    AttributeError: 'NoneType' object has no attribute 'group'

    Any ideas what went wrong?

    1. Avatar for Emmet
      Emmet on
      Editor

      Hi Joshua,

      At some point the Ookla team made changes to the way it presents the jitter data. (Now provides it for all of their metrics).

      We have updated the script to accommodate these changes so please try updating to the latest version of the script in this tutorial.

      Please let me know if this fails to resolve your issues.

      Cheers,
      Emmet

  7. Avatar for Leo
    Leo on

    Hello everyone, I want to complete this project so that I can monitor the quality of my home network. I’ve been pasting every line this tutorial mentioned. Still, when i type “python3 ~/speedtest.py” this error shows up:

    Traceback (most recent call last):
      File "/home/grafana/speedtest.py", line 25, in 
        f.write('{},{},{},{},{},{}\r\n'.format(time.strftime('%m/%d/%y'), time.strftime('%H:%M'), ping, jitter, download, upload))
    NameError: name 'f' is not defined

    I tried to follow the tutorial back from start but nothing changed. I then tried to update python (and python3), yet nothing changed.

    I tried to fix this problem in many different ways but since I lack python skills I have yet to arrive to a conclusion. Hope someone can help, sorry if I misspelled something, english is not my main language.

    1. Avatar for Emmet
      Emmet on
      Editor

      Hi Leo,

      This code is written to expect a very specific setup. It looks like you are running this using a different user to the one we wrote this for. (When this guide was originally written the “pi” user was the default user).

      Within “speedtest.py” find the following two lines of code.

          f = open('/home/pi/speedtest/speedtest.csv', 'a+')
          if os.stat('/home/pi/speedtest/speedtest.csv').st_size == 0:

      Now there are two paths that you will want to change here. If you are running this under the “grafana” user as your error suggests you would change these lines to the following.

      Simply put we are replacing “pi” with your users name.

          f = open('/home/grafana/speedtest/speedtest.csv', 'a+')
          if os.stat('/home/grafana/speedtest/speedtest.csv').st_size == 0:

      Hopefully this fixes the issues that you are running into.

      Please let me know if you need additional assistance.

      Cheers,
      Emmet

  8. Avatar for Burlottimah
    Burlottimah on

    My old pi is too slow. So i wanna install this on proxmox. Which LXC should i use? ubuntu or debian? tried both (newest) version and it does not work. I does not find the repository. Does thing only work on Pi OS?

    1. Avatar for Emmet
      Emmet on
      Editor

      Hi Burlottimah,

      This tutorial should work for systems that identify as Debian (Raspberry Pi OS is basically Debian with a few Pi specific changes) so I would personally recommend using Debian. Ubuntu will work as well just needs slight changes to the repository that is being added.

      Can you try running the command “lsb_release -cs” and seeing what that returns. We use this within step 4 to retrieve the release name of the current operating system.

      Cheers,
      Emmet

  9. Avatar for UTCreeper
    UTCreeper on

    Original “just use the official client” guy back again!
    As of 8/22/22 (and verified 8/16/22), trying to install Speedtest via the official CLI instructions will error out with:

    pi@raspberrypi:~ $ sudo apt install speedtest
    Reading package lists... Done
    Building dependency tree... Done
    Reading state information... Done
    E: Unable to locate package speedtest

    I emailed Ookla support, and to their credit, they got back to me within an hour and stated the instructions are broken, and to just download the full Linux client and that would work from the cli as well. As of 8/16/2022 that file for RPi 4 is located at https://install.speedtest.net/app/cli/ookla-speedtest-1.2.0-linux-armhf.tgz
    (List of others here: https://www.speedtest.net/apps/cli under ‘download for linux’ )
    So:

    wget https://install.speedtest.net/app/cli/ookla-speedtest-1.2.0-linux-armhf.tgz
    tar -xf *.tgz
    sudo chmod +x speedtest
    sudo mv speedtest /usr/bin/

    Then it’s off to the (speed) races. Note: I assume there is a better ‘extract and install’ option within ‘tar’ – but I just kinda google my way through stuff and that method works.

    Once again though, thank you for an amazing tutorial!

  10. Avatar for richie66
    richie66 on

    Thank You this works perfect!
    I did have to add:

                "server": str(server),
                "isp": str(isp),

    to the fields

    Side project
    love to see a NUT server with a grafana dashboard setup
    I have NUT up and running on six Pis with one main server show all the units but can’t get the data imported to influx, telegraf or prometheus with any luck

  11. Avatar for Michiel
    Michiel on

    Hi!

    Seem to running into some problems.

    When using the code for Speed Test Python Script I get this message.

    Traceback (most recent call last):
    File “/home/xxxmypi/speedtest.py”, line 16, in
    jitter = jitter.group(1)
    AttributeError: ‘NoneType’ object has no attribute ‘group’

    Got anny tips ? I started over 3 times already :s

    Thanks

    1. Avatar for Emmet
      Emmet on
      Editor

      Hi Michiel,

      Do you mind running the following command within the terminal and telling me the results it returns. it should be working fine now.

      /usr/bin/speedtest --accept-license --accept-gdpr

      For example, the following is what I got after running it on my network.

         Speedtest by Ookla
      
            Server: Optus - Sydney (id: 1267)
               ISP: Starlink
      Idle Latency:    48.03 ms   (jitter: 12.23ms, low: 31.20ms, high: 55.69ms)
          Download:   189.55 Mbps (data used: 246.6 MB)
                      160.32 ms   (jitter: 51.60ms, low: 48.16ms, high: 466.95ms)
            Upload:    26.85 Mbps (data used: 43.8 MB)
                       40.51 ms   (jitter: 17.02ms, low: 25.30ms, high: 432.97ms)
       Packet Loss:     0.0%
        Result URL: https://www.speedtest.net/result/c/

      Cheers,
      Emmet

  12. Avatar for Pete
    Pete on

    Thanks for the fix for the most recent jitter pull problem.

    Does the code that is used with the database also need tweaking (so that jitter is handed in the second tutorial code as in the first)

    Also some issue with floating jitter in the second part as the variable seems to include text of ms?

    Thanks

    1. Avatar for Emmet
      Emmet on
      Editor

      Hi Pete,

      Yes it did, thank you for pointing that out, I have corrected that part of the tutorial now as well.

      In my rush to fix the broken tutorial yesterday I also slightly miswrote the regex. I have corrected it so it should now only grab the number and not “ms” as well.

      Thank you for pointing out these mistakes, it is very much appreciated.

      Cheers,
      Emmet

  13. Avatar for richie66
    richie66 on

    Thank you for the monitor:
    I have been using this for over a year with no issues until yesterday:
    Im now getting:
    Traceback (most recent call last):
    File “/home/pi/speedtest.py”, line 16, in
    jitter = jitter.group(1)
    AttributeError: ‘NoneType’ object has no attribute ‘group’
    speedtest runs fin by itself but the script gives the error.

    I have run a new setup step by step and get the same error.
    Need Ideas please

    1. Avatar for Emmet
      Emmet on
      Editor

      Hi Richie66,

      It looks like Ookla changed how the speedtest-cli shows jitter. It now gives jitter for each different test.

      I have adjusted the tutorial so it will now only grab the jitter for your latency which should solve this issue.

      Cheers,
      Emmet

    2. Avatar for richie66
      richie66 on

      Morning and thank you:
      the fix worked for the first run but import into influx gave the same error, so I move the new jitter line in to the influx import and get the new error:
      Traceback (most recent call last):
      File “/home/pi/speedtest.py”, line 28, in
      “jitter”: float(jitter)
      ValueError: could not convert string to float: ‘0.45ms’

      So the influx needed to be adjusted to reflect the change but cant convert to float
      Side request how can I capture the Testing server and ISP ?

      Thank you!

    3. Avatar for Emmet
      Emmet on
      Editor

      Hi Richie66,

      Sorry about that I had made a mistake when rewriting the regex so it was grabbing “ms” as well. I have re-adjusted it so it should now hopefully be fine.

      Grabbing the testing server and ISP are fairly straightforward and just need to break out some more regex. I guess the big question is whether you just want the full string for the server or just the ID.

      The following code for example will grab the whole strings after both the “Server:” and “ISP:” tags.

      The results will be stored within the “server” and “isp” variables respectively.

      
      server = re.search('Server:\s+(.*?)\n', response, re.MULTILINE)
      isp = re.search('ISP:\s+(.*?)\n', response, re.MULTILINE)
      
      server = server.group(1)
      isp = isp.group(1)

      You will then need to re-adjust the Grafana or CSV code to utilize these new values.

      If people believe this is a usual additional to what currently exists I can modify the tutorial.

      Cheers,
      Emmet

  14. Avatar for Luis
    Luis on

    Hi, how do I solve this problem?

    Traceback (most recent call last):
    File “/home/unknown/speedtest.py”, line 16, in
    jitter = jitter.group(1)
    AttributeError: ‘NoneType’ object has no attribute ‘group’

    Everything was fine but after the last update this error appear.

    1. Avatar for Emmet
      Emmet on
      Editor

      Hi Luis,

      Please replace the script with the latest version included within this tutorial.

      Changes were made to Ookla’s speedtest cli tool that outputted jitter differently to how we were grabbing it.

      CHeers,
      Emmet

  15. Avatar for Desert
    Desert on

    Hey, i’m using your script since a few months to measure my internet speed and everything was working well until today. there was a update for the speedtest-cli for my rapsberry pi and since then i’m getting this error message:

    Traceback (most recent call last):
    File “/home/pi/speedtest.py”, line 15, in
    jitter = jitter.group(1)
    AttributeError: ‘NoneType’ object has no attribute ‘group’

    it works after i uncommented the jitter parts of the script but my python knowledge is to low to troubleshoot this error. any help would be gretly appreciated here. thank you in advance!

    1. Avatar for Emmet
      Emmet on
      Editor

      Hi Desert,

      Thank you for alerting us to this new issue.

      I have updated the tutorial so the regex that we are using will now grab the jitter from the latency field.

      It was broken since the regex we used could suddenly no longer grab anything.

      Cheers,
      Emmet

    2. Avatar for Desert
      Desert on

      thank you emmet,

      i found the changes in the .csv part of the tutorial, did you forget to put it in the grafana part? when i try the script with the changes i get this as an output:

      Traceback (most recent call last):
      File “/home/pi/speedtest.py”, line 27, in
      “jitter”: float(jitter)
      ValueError: could not convert string to float: ‘2.00ms’

      i’m using the script with grafana, i forgot to put that in my first post.

    3. Avatar for Emmet
      Emmet on
      Editor

      Hi Desert,

      In my rush to fix the issue I made a mistake and accidentally had the regex grabbing the “ms” part of the text.

      I have now corrected this issue within the tutorial.

      Cheers,
      Emmet

  16. Avatar for Ken Fair
    Ken Fair on

    I just wanted to comment that I’ve used this tutorial multiple times and have found it invaluable to my Raspberry Pi experience and for troubleshooting my internet connection.

    Thank You,
    Ken

  17. Avatar for Ric Souza
    Ric Souza on

    Hi Emmet, hope all is well.

    I left a comment a couple of weeks ago asking about expanding this project to measure the speed in two broadband connections at the same time, saving the values to the same database and viewing the results on a single dashboard in Grafana.

    I don’t know if you even saw it and found it strange that I didn’t get a comment from you so I decided to check back in and I can’t find my comment anywhere (which would explain why you haven’t commented on it).

    So, here I am again with it. Have you thought about this at all? Has anyone else tried anything of the sort that you know about?

    In my case, since I work from home, I have my router setup for safe fail-over, so that if my main connection drops, the secondary takes over in just a few seconds. I’d like to be able to monitor the secondary connection in the same dashboard that I monitor the main one. I’m pretty sure I’d need another Pi4 but haven’t worked out the additional details, such as saving the data to the same database so I can pull it all on the same Grafana dashboard, etc.

    Looking forward to your comments.

    1. Avatar for Emmet
      Emmet on
      Editor

      Hi Ric,

      Monitoring two internet connections from the same Raspberry Pi will definitely be tricky because of the hardware limitations that you pointed out in your previous comment.

      Using two Raspberry Pi’s is probably the best way to monitor two connection concurrently. Pushing to the same influx database should be as straightforward as modifying the following line.

      Within this line you will want to change the first value (“localhost“) to be the local IP address belong to which ever Pi you decide to hold the database.

      client = InfluxDBClient('localhost', 8086, 'speedmonitor', 'pimylifeup', 'internetspeed')

      Additionally you will want to change the tag given to this data so that you can differentiate them within grafana.

              "tags" : {
                  "host": "RaspberryPiMyLifeUp"
              },

      Hopefully that helps steer you in the right direction, let me know if you run into any issues or require more assistance.

      Cheers,
      Emmet

  18. Avatar for petrus
    petrus on

    useful guide thanks. ran into some issues as i was using a RPi 1 and had to use armv6 grafana but apart from that very useful. thanks for posting it

  19. Avatar for Neon
    Neon on

    speed test from the command line is giving less download speed than from my laptop.
    From laptop, I am getting 50mbps
    whereas the command line is returning around 17mbps only.

    Tried different servers but it is still the same. Any idea how to fix this.

    /usr/bin/speedtest –accept-license –accept-gdpr -s 5744
    /usr/bin/speedtest –accept-license –accept-gdpr -s 1267

    1. Avatar for Emmet
      Emmet on
      Editor

      Hi Neon,

      Are you using the ethernet or Wi-Fi connection on your Raspberry Pi?

      If you are using Wi-Fi I highly recommend that you try using an ethernet connection. Wi-Fi can have bandwidth issues on the Pi.

      Cheers,
      Emmet