Complete Mikrotik Router Monitoring via SNMP and VizIoT

Professional network equipment, such as Mikrotik routers, provides a vast amount of data about their status: from CPU temperature and fan speeds to traffic on each of dozens of ports. But how do you collect all this information in one place and turn it into clear graphs for analysis?

In this article, we'll create a comprehensive monitoring system for a Mikrotik router using the SNMP protocol, and send all collected data to VizIoT to build an informative dashboard. You'll be able to track equipment status, network load in real-time, and quickly identify problems.

Final Result: Your Own Network Management Dashboard

Before we begin, take a look at the powerful and intuitive monitoring dashboard we'll create. It will allow you to track everything: from overall uptime and power supply status to detailed load on each SFP port.

  • Overall Status: Uptime, temperature, fan speeds, power supply status.
  • Total Traffic: Aggregated receive and transmit speeds across all ports.
  • Detailed Port Load: Individual graphs for each SFP and SFP+ port, allowing you to see which channels are most loaded.

Interested? Let's set up such a system. It's easier than it seems.


What You'll Need

Hardware and Software

  • Mikrotik Router: With SNMP server enabled.
  • Computer for Data Collection: Any device with Linux or Windows that will be permanently on (for example, Raspberry Pi or a small server).
  • NodeJS: Environment for running our collector script.
  • PM2: Process manager for reliable script autostart.

Step 1: Enabling SNMP on Mikrotik Router

SNMP (Simple Network Management Protocol) is a standard way for network devices to provide information about their status.

  1. Log into your Mikrotik router's control panel (for example, via WinBox or web interface).
  2. Go to IP -> SNMP section.
  3. Check the Enabled box.
  4. The Community field uses public by default. This is the community name that works like a password. You can leave it or change it, but make sure it matches the settings in our script.
  5. Click Apply.

Now your router is ready to provide data via SNMP.


Step 2: Setting Up Device in VizIoT

  1. Create a new device, for example, Mikrotik Router.
  2. In the Basic Settings section, copy the Access Key and Access Password. Our script will need them.

Step 3: Environment Setup and Script Creation

On the computer that will collect data, we'll install everything necessary.

Installing NodeJS via NVM

Detailed documentation: https://nodejs.org

  1. Install NVM:
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
  2. Activate NVM (without re-login):
    \. "$HOME/.nvm/nvm.sh"
  3. Install the latest version of Node.js:
    nvm install 24
  4. Verify installation:
    node -v
    npm -v

Creating the Project

  1. Create a directory for the project:
    mkdir mikrotik-monitor
    cd mikrotik-monitor
  2. Initialize NodeJS project:
    npm init -y
  3. Install required libraries:
    npm install net-snmp big-integer viziot-mqtt-client-nodejs

Creating the Collector Script

Create an index.js file:

nano index.js

Insert the following code. Don't forget to specify your data in the CONFIG section.

const snmp = require("net-snmp");
const bigInt = require("big-integer");
const viziotMQTT = require("viziot-mqtt-client-nodejs");

// Configuration
const CONFIG = {
  keyDevice: "YOUR_DEVICE_KEY", // VizIoT device key
  passDevice: "YOUR_DEVICE_PASSWORD", // VizIoT device password
  intervalSendData: 30000, // Polling interval in milliseconds (30 seconds)
  ipRouter: "192.168.0.25", // IP address of your Mikrotik router
  snmpCommunity: "public", // SNMP community name (must match router settings)
};

// Value converters
const hexToBigInt = (value) => +bigInt(`0x${value.toString('hex')}`).toString(10)
const divideBy10 = (value) => value / 10
const divideBy100ToDay = (value) => value / 100 / 60 / 60 / 24
const identity = (value) => value

// OID generator for rx and tx ports
const generatePortOIDs = () => {
    const oids = {}
    const rxBase = '1.3.6.1.4.1.14988.1.1.14.1.1.31'
    const txBase = '1.3.6.1.4.1.14988.1.1.14.1.1.61'

    // SFPP1 port
    oids[`${rxBase}.1`] = { key: 'rx_sfpp1', convert: hexToBigInt }
    oids[`${txBase}.1`] = { key: 'tx_sfpp1', convert: hexToBigInt }

    // SFP ports 1-12
    for (let i = 1; i <= 12; i++) {
        oids[`${rxBase}.${i + 1}`] = { key: `rx_sfp${i}`, convert: hexToBigInt }
        oids[`${txBase}.${i + 1}`] = { key: `tx_sfp${i}`, convert: hexToBigInt }
    }

    return oids
}

// OID generator for CPU load
const generateProcessorLoadOIDs = () => {
    const oids = {}

    const base = '1.3.6.1.2.1.25.3.3.1.2.'

    // 16-core processor
    for (let i = 1; i <= 16; i++) {
        oids[`${base}.${i}`] = { key: `cpu${i}`, convert: identity }
    }

    return oids
}

// SNMP OID configuration
const SNMP_OIDS = {
    '1.3.6.1.2.1.1.3.0': { key: 'uptime', convert: divideBy100ToDay },
    ...generatePortOIDs(),
    '1.3.6.1.4.1.14988.1.1.3.11.0': { key: 'processorTemp', convert: divideBy10 },
    '1.3.6.1.4.1.14988.1.1.3.15.0': { key: 'power1State', convert: identity },
    '1.3.6.1.4.1.14988.1.1.3.16.0': { key: 'power2State', convert: identity },
    '1.3.6.1.4.1.14988.1.1.3.17.0': { key: 'fan1RPM', convert: identity },
    '1.3.6.1.4.1.14988.1.1.3.18.0': { key: 'fan2RPM', convert: identity },
    ...generateProcessorLoadOIDs(),
}

// Class for working with SNMP data
class SNMPMonitor {
    constructor() {
        this.prevResult = null
        this.mqttClient = new viziotMQTT(CONFIG.keyDevice, CONFIG.passDevice)
        this.intervalId = null
    }

    start() {
        this.mqttClient.connect(() => {
            console.log(new Date(), 'MQTT connected')
            this.startPolling()
        })
    }

    startPolling() {
        if (this.intervalId) {
            clearInterval(this.intervalId)
        }
        this.intervalId = setInterval(() => this.poll(), CONFIG.intervalSendData)
    }

    async poll() {
        try {
            const result = await this.getSNMPData()

            if (!result) {
                console.log(new Date(), 'SNMP result is empty')
                return
            }

            if (!this.prevResult) {
                this.prevResult = result
                console.log(new Date(), 'First SNMP result received')
                return
            }

            const packet = this.buildPacket(result)
            this.prevResult = result

            //   console.log(packet)

            this.mqttClient.sendDataToVizIoT(packet, (err) => {
                if (!err) console.log(new Date(), 'Packet sent')
            })
        } catch (error) {
            console.error(new Date(), 'Polling error:', error)
        }
    }

    getSNMPData() {
        return new Promise((resolve) => {
            const session = snmp.createSession(CONFIG.ipRouter, CONFIG.snmpCommunity)
            const oids = Object.keys(SNMP_OIDS)

            session.get(oids, (error, varbinds) => {
                session.close()

                if (error) {
                    console.error(new Date(), 'SNMP varbinds:', varbinds)
                    console.error(new Date(), 'SNMP error:', error)
                    resolve(null)
                    return
                }

                const result = { timeGet: Date.now() }

                varbinds.forEach((varbind) => {
                    if (snmp.isVarbindError(varbind)) {
                        console.error(new Date(), snmp.varbindError(varbind))
                        return
                    }

                    const config = SNMP_OIDS[varbind.oid]
                    if (config) {
                        result[config.key] = config.convert(varbind.value)
                    }
                })

                resolve(result)
            })
        })
    }

    // Calculate CPU load statistics
    calculateCPUStats(current) {
        const cpuLoads = []

        // Collect load values for all cores
        for (let i = 1; i <= 16; i++) {
            const cpuKey = `cpu${i}`
            if (current[cpuKey] !== undefined) {
                cpuLoads.push(current[cpuKey])
            }
        }

        if (cpuLoads.length === 0) {
            return { avg: 0, min: 0, max: 0 }
        }

        // Calculate average, minimum, and maximum
        const sum = cpuLoads.reduce((acc, val) => acc + val, 0)
        const avg = Math.round((sum / cpuLoads.length) * 100) / 100
        const min = Math.min(...cpuLoads)
        const max = Math.max(...cpuLoads)

        return { avg, min, max }
    }

    buildPacket(current) {
        const elapsedSec = Math.round((current.timeGet - this.prevResult.timeGet) / 1000)

        // Calculate CPU statistics
        const cpuStats = this.calculateCPUStats(current)

        const packet = {
            date: Math.floor(Date.now() / 1000),
            uptime: current.uptime,
            processorTemp: current.processorTemp,
            cpuAvg: cpuStats.avg, // Average CPU load
            cpuMin: cpuStats.min, // Minimum load across cores
            cpuMax: cpuStats.max, // Maximum load across cores
            power1State: current.power1State,
            power2State: current.power2State,
            fan1RPM: current.fan1RPM,
            fan2RPM: current.fan2RPM,
            aggRxMb: 0,
            aggTxMb: 0,
        }

        // Calculate speeds for all ports
        const ports = ['sfpp1', ...Array.from({ length: 12 }, (_, i) => `sfp${i + 1}`)]

        ports.forEach((port) => {
            const rxKey = `rx_${port}`
            const txKey = `tx_${port}`

            if (current[rxKey] !== undefined && this.prevResult[rxKey] !== undefined) {
                const rxMb = this.calcBandwidth(current[rxKey], this.prevResult[rxKey], elapsedSec)
                const txMb = this.calcBandwidth(current[txKey], this.prevResult[txKey], elapsedSec)

                const portName = port.charAt(0).toUpperCase() + port.slice(1)
                packet[`rx${portName}Mb`] = rxMb
                packet[`tx${portName}Mb`] = txMb

                packet.aggRxMb += rxMb
                packet.aggTxMb += txMb
            }
        })

        return packet
    }

    calcBandwidth(current, previous, seconds) {
        return ((current - previous) / seconds / 1024 / 1024) * 8
    }

    stop() {
        if (this.intervalId) {
            clearInterval(this.intervalId)
            this.intervalId = null
        }
    }
}

// Start monitoring
const monitor = new SNMPMonitor()
monitor.start()

// Process termination handling
process.on('SIGINT', () => {
    console.log('\nStopping monitoring...')
    monitor.stop()
    process.exit(0)
})

Step 4: Automatic Startup via PM2

To keep the script running 24/7, we'll use the PM2 process manager.

  1. Install PM2 globally:

    npm install pm2 -g
  2. Start the script via PM2:

    pm2 start index.js --name "mikrotik-monitor"
  3. Configure PM2 autostart on system boot:

    pm2 startup

    Copy and execute the command that PM2 provides.

  4. Save the current process list:

    pm2 save

Now your collector script will reliably run in the background.


Step 5: Creating a Monitoring Dashboard in VizIoT

The most interesting part! Data is coming in, and now we can visualize it.

  1. Create a new dashboard named "Mikrotik Router".

  2. Start adding widgets. VizIoT will automatically create parameters when receiving the first data. Here are some widget ideas:

    • "Latest Values" Widget: Display uptime, power1State, power2State.
    • "Gauge" Widget: Perfect for processorTemp or fan1RPM.
    • "Chart" Widget:
      • Total Traffic: Add aggRxMb (incoming) and aggTxMb (outgoing) parameters to one chart.
      • Temperature and Fans: Combine processorTemp, fan1RPM, fan2RPM on another chart.
      • Port Traffic: Create separate charts for each important port or port group, adding corresponding rx...Mb and tx...Mb parameters to them.

Experiment with widget types and layouts to create a dashboard that perfectly suits your needs.

Done!

You've created a powerful and flexible monitoring system for your Mikrotik router. Now you'll always be aware of your network equipment's status and can quickly respond to any anomalies in traffic or device operation.