Broadband Utilisation Display

We’ve always relied heavily on our home broadband but even more so during the current COVID19 lockdown. The whole family at home vying for the resources available and sometimes this can impact one another. I thought it would be interesting to have a glanceable display which would show the current broadband utilisation rates, along with being an interesting project to try another Arduino like board in the form of the Wemos D1 Mini.

Broadband Utilisation Display front panel

Note: This project requires an SNMP enabled router.

Hardware & Software

The following hardware was used, prices quoted are what I paid in May 2020.

  • Display: MottramLabs.com 4 Digit Display with bar graph (Wemos version) – eBay – £5.54
  • Micro Controller: Lolin Wemos D1 Mini v3.1- eBay – £5.99
  • Plus: Any old 5V USB Power supply and a micro-usb power cable or you could even use a USB battery power bank
Broadband Utilisation Display read panel showing Wemos D1 Mini

The software can be flashed using the standard Arduino IDE.

Before flashing the sketch to the Wemos D1 Mini, you will need to update a handful of variables:

  • Set your wireless network details in ssid and password
  • Set your SNMP enabled routers IP address in IPAddress router(192, 168, 200, 1);
  • Set your SNMP community string in community
  • Update the OID depending on which interface number you need to monitor, my router VDSL connection is on interface 4.

On start-up, the decimal points on the display will scroll until WiFi is connected.

Maybe in the future I’ll investigate adding a WiFiManager so that the settings can be set without needing to change the sketch.

Showing Utilisation

The MottramLabs Power Bar display was something I stumbled across on eBay whilst browsing for Wemos related component which provided the inspiration for this project.

Broadband Utilisation Display front panel showing tables on LED display elements

The 7-Segment displays and the outer Power Bar show the download utilisation rate as a percentage of ADSL line speed, whilst the 4 horizontal LEDs show the upload utilisation rate.

How it Works

When I first embarked on this project I assumed that an SNMP library would already exist for Arduino (or similar) boards. A lot of internet searches later it transpired that whilst there were a couple of SNMP Agent libraries (Arduino_SNMP and agentuino ), that enabled devices to respond with data to SNMP requests, finding an SNMP Manager that makes the requests was proving elusive.

I’d almost reconciled myself to having to not use SNMP and find another method of extracting the data from my router, such as via SSH or Telnet, when I found a fork of the Arduino_SNMP agent library where Niich had added SNMP Manager functionality. I grabbed the library and set to testing it out.

I was disappointed to find that the library didn’t work for me. I could see it was sending SNMP GetRequests and receiving GetResponses back but it was failing to parse them correctly. Several hours of debugging later I determined that the code was looking for a specific byte to be set to indicate the packet was indeed an GetResponse [0xA2]: if (_packetBuffer[13] == GetResponsePDU) in my Wireshark trace I could that byte 15 was 0xA2, not byte 13. Following a brief discussion with an SNMP expert and being pointed at a discussion on Stackoverflow, that to represent numbers > 255, the top most bit of each byte is used to indicate if the next byte needs to be added to the first byte. After being put onto the right track I could see though the change history in the parent Arduino_SNMP project that there had been several fixes to address decoding of the SNMP packets and specifically where the top bit is set. Merging the latest Master into Niich’s fork wasn’t going to be straight forward, neither did Niich’s manager support the value types I’d need to work with my router. So instead I decided to create my own library forked from the original Ardunio_SNMP agent project

Arduino SNMP Manager Library

After taking a fork of Ardunio_SNMP I set about re-implementing the manager functionality, once I had the basic GetRequest and GetResponse decoding working, I extended it with SNMP v2 support, additional data types. Finally, I removed all the agent code so that it was just an SNMP Manager. I’ve tested the library with a handful of devices and it works, even with 64bit counters.

The library is available in my GitHub repository, with documentation and examples.

Broadband Utilisation Data Collection

Now that we can make SNMP requests from the Wemos D1 Mini we’re ready to start collecting data and displaying it on the MottramLabs display.

Data Sources (where interface is required, interface 4 is used for my VDSL connection on the router):

  • Maximum download speed – ADSL-LINE-MIB (adslAtucChanCurrTxRate), OID:.1.3.6.1.2.1.10.94.1.1.4.1.2.4
    • If your router doesn’t support this, you can hardcode the value based on a speedtest. Alternatively, the ifSpeed may represent the current sync speed.
  • Maximum load speed – ADSL-LINE-MIB (adslAturChanCurrTxRate ), OID:.1.3.6.1.2.1.10.94.1.1.5.1.2.4
    • If your router doesn’t support this, you can hardcode the value.
  • Data (Bytes) received counter (ifInOctets), OID:.1.3.6.1.2.1.2.2.1.10.4
  • Data (Bytes) transmitted counter (ifOutOctets), OID:.1.3.6.1.2.1.2.2.1.16.4
  • Router uptime counter (sysUptime), OID:.1.3.6.1.2.1.1.3.0

To calculate bandwidth utilisation we need to measure the amount of data between two time period. The amount of data is provided by the ifInOctets and ifOutOctets counters, time is provided using uptime counter. As two measurements are required before we can calculate a rate, the program initially starts off fast polling, making an SNMP request every second until it has a full set of data for the current and previous period. This gets the current rate displayed quickly. After getting some initial rate, the polling interval drops back to 15 seconds as we’re interested in monitoring for a sustained high utilisation rate.

After making the SNMP GetRequests we need to wait until the callbacks have updated the variables with the new values, instead of adding a delay of several milliseconds before using the values, I’ve instead opted to wait until the uptime has changed before performing the calculations.

The calculations have to handle a number of conditions:

  • The router has rebooted, this will reset the counters, so we need to update the stored values but we can’t use the data to calculate utilisation.
  • The difference between uptime measurements is less than the poll interval, something has gone wrong, the counter has wrapped, the router has reset, again we can store the new values, but skip calculating utilisation.
  • With 32bit counters they can wrap between sample period, ideally you’d use 64bit (High Capacity) counters to avoid this, but not all devices support these. So when this occurs we can compensate by assuming that if the counter has wrapped, it will probably only have wrapped once between samples so can still determine the utilisation rates.

Broadband Utilisation Data Display

With the data collected displaying it on the MottramLabs.com display is fairly simple. The 16 LED (4 x green, 6 x yellow, 6 x red) is set via 2 byte arrays. As we want to have a linear display of these LEDs we can define two lists:

int Bar_1[17]{0, 128, 192, 224, 240, 248, 252, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255};
int Bar_2[17]{0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 192, 224, 240, 248, 252, 254, 255};

For an index of 0 to 8 only bits from the Bar_1 list are updated, after which Bar_1 keeps all bits set and Bar_2 gradually sets the bits for the remaining LEDs in the power bar.

Selecting the right index to represent the current % utilisation is done by mapping the current utilisation from 0-100% to an index in the array 0-16 for the download utilisation.

void updateDisplay()
{
  // Download Utilisation
  Display.Display_Value(1, bandwidthInUtilPct, 1, 0x00);        // Display % util on 7 segment displays
  int barPercent = map(int(bandwidthInUtilPct), 0, 100, 0, 16); // Map % util on to the Green->Amber->Red LEDs
  Display.MAX7219_Write(5, Bar_1[barPercent]);
  Display.MAX7219_Write(6, Bar_2[barPercent]);
  // Upload Utilisation
  int indicatorPercent = map(int(bandwidthOutUtilPct), 0, 100, 0, 4); // Map % util on to the indicator strip
  Display.MAX7219_Write(7, indicators[indicatorPercent]);
}

For upload utilisation, a similar exercise is performed for the 4 LEDs that make up that display.
The library for the 7-Segment display has Display_Value function that allows the utilisation % to be passed in and displayed on the display, with only the position of the decimal point needing to be specified.

It should be possible to adjust the position of the decimal place for values < 100, and show the utilisation to 2 decimal places, but I’ll leave that as an exercise for the reader.

Summary

When I embarked on this project, I assumed it would be quick and easy, by being able to use the extensive libraries available for Arduinos. The need to create an SNMP manager library was unexpected and frustrating at times. However, I learned a lot more about SNMP than I expected and I’m happy to have contributed a new library to the wider community.

The broadband utilisation display does what I set out to do, having a simple glanceable display that can let me know of sustained high bandwidth usage that might be the cause for other problems, like poor audio/video quality on conference calls.

What is more impressive is that the Wemos D1 Mini and the MottramLabs display together cost less than Raspberry Pi Zero W.

6 thoughts on “Broadband Utilisation Display

  • 12th June 2020 at 2:54 pm
    Permalink

    Hi there,

    I love this project and wanted to reproduce it.

    When I attempt to compile your code I get an error message:

    Arduino: 1.8.12 (Linux), Board: “LOLIN(WEMOS) D1 R2 & mini, 80 MHz, Flash, Legacy (new can return nullptr), All SSL ciphers (most compatible), 4MB (FS:2MB OTA:~1019KB), v2 Lower Memory, Disabled, None, Only Sketch, 921600”

    In file included from /home/deagle/Arduino/broadbandspeed.ino/broadbandspeed.ino.ino:4:0:
    /home/deagle/Arduino/libraries/Arduino_SNMP_Manager-master/Arduino_SNMP_Manager.h:14:17: fatal error: UDP.h: No such file or directory
    #include
    ^
    compilation terminated.
    exit status 1
    Error compiling for board LOLIN(WEMOS) D1 R2 & mini.

    I also get errors compiling the example code installed along with your Arduino_SNMP_Manager library, namely that ‘Arduino_SNMP.h’ doesn’t exist. When I change this to ‘Arduino_SNMP_Manager.h’ in the source I get the UDP.h error above.

    Can you help at all?

    Dave

    Reply
    • 12th June 2020 at 3:59 pm
      Permalink

      Glad you’re interested in the project. Sorry, it isn’t yet working for you. You’re right the examples hadn’t been updated with the new file name after I renamed the library. I’ve posted updated examples to github.

      As to the error finding UDP.h, this is also fixed, I’d not tested on case sensitive OS (until now). Should have been Udp.h

      Reply
      • 15th June 2020 at 1:18 pm
        Permalink

        That’s brilliant. Thanks for the prompt response.
        Great project.

        Reply
  • 2nd November 2020 at 10:11 pm
    Permalink

    What and how would I change the value to hardcode the downspeed? Changing

    “.1.3.6.1.2.1.10.94.1.1.4.1.2.17”

    to

    “40000”

    just throws an exception. I’m not sure in what format it’s looking for the downspeed?

    Thanks

    Reply
    • 3rd November 2020 at 10:20 am
      Permalink

      Hi Andrew, you can’t just change the SNMP OID to a fixed number, as it will attempt to use that number when querying data.

      To start with you can set the variables here (or convert them to const):
      //************************************
      //* Initialise *
      //************************************
      // Variables
      unsigned int downSpeed = 0;
      unsigned int upSpeed = 0;

      Then remove the handler that normally sets the value:
      // Create a handler for each of the OID
      snmp.addGuageHandler(oidAdslDownSpeed, &downSpeed);
      snmp.addGuageHandler(oidAdslUpSpeed, &upSpeed);

      You could then clean up the usage of the code that reference `oidAdslDownSpeed`

      If you need more help I can create a branch with hardcoded bandwidths set.

      Reply

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.