Without USB you can send information to an external server using Syslog
i tried to run on SXTsq Lite5 AC [6.48.6 (long-term) ]. i tried but i failed to setup this. I don’t where i am getting stuck. No report generated. Can you please provide a video tutorial how to set it up?
1st i do turn on kid control through you give the code [ /ip kid-control add name=Monitor mon=0s-1d tue=0s-1d wed=0s-1d thu=0s-1d fri=0s-1d sat=0s-1d sun=0s-1d ]
and then
2nd i go to script and then paste the whole code and run interval of 00:59:59 .
nothing getting output. i change the directory according to me. i don’t have external storage so i create a folder on Mikrotik and then i choose the path.
Troubleshoot by me:
I tried to run in scheduler not in script nothing happen
i tried to paste the whole code in script and then run it through schulder.
i tried to change the interval runtime to 10 sec to 1 min and 5min nothing results.
Please make a video tutorial and the installation. Please. I need this very badly to track my own internet ussage without any external thing.
You can follow the steps to setup monitoring here:
Kid-control doesn’t seem to give accurate bandwidth usage information. I’m using 7.10 on an hap ac2 as a test. When I run a speedtest on my computer, I get about 100Mbps down and about 45Mbps up. Kid control is showing about 30Mbps down and kbps up.
Is anyone else having the same issue? Does it seem to work with applications other than speedtest? It might just be showing one of the speedtest streams which would be a little unlucky.
I am currently using torch which gives accurate information but it would be nice to use kidcontrol instead so I don’t need to add up all the connections for the same IPs.
Thank you for your work, it works perfect.
When I try to save the report to a shared drive (runs on SMB raspberrypi) using
:local reportpath (“smb://user:password@192.168.3.19/home/pi/MyNASA/BWbyIP/report-” . $yearmonth . “.html”)
either with or without the user/password
failure: cannot open output file
Is it my smb syntax or elsewhere ? Thanks.
The script was developed and tested only for RouterOS 7.
I don’t think kid control is a good tool for watching live traffic utilization, it’s probably averaging out the numbers. However, the count is accurate to the best of my knowledge.
If you want to watch live traffic, I suggest using interface graphs or just interfaces window.
RouterOS cannot use SMB shares as a client, unfortunately. There is the fetch tool that you can use to upload via SFTP, but to work with files you still need to keep them locally either in RAM or on USB drive.
ROSE package ??
You can mount a SMB-share from your NAS and write it on there.

Recently I started to get close to reaching my ISP data cap, so I wanted to track monthly bandwidth usage per each device behind my home router hAP ac2. I searched the forum and saw several ideas, but they weren’t quite what I was looking for. So I wrote my own script that I would like to share with the community.
I just wanted to thank you for the script! I tried using Splunk and Grafana to get a simple total bandwidth usage per interface, but neither could calculate it out of the box. One suggestion: it would be great if the script could calculate the total bandwidth usage per interface listed as WAN. This would be helpful in scenarios where a WAN link is configured for failover.
hello thank your for script but my report show noting !!
report-2025-01.html.txt:
<html><head>
<script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script>
<script type='text/javascript'>
google.charts.load('current', {packages: ['corechart', 'bar']});
google.charts.setOnLoadCallback(drawMultSeries);
function drawMultSeries() {
var data = google.visualization.arrayToDataTable([
['Device', 'Download', 'Upload', 'Total'],
export of my kid-control :
/ip kid-control
add fri=0s-1d mon=0s-1d name=Monitor sat=0s-1d sun=0s-1d thu=0s-1d tue=0s-1d wed=0s-1d
/ip kid-control device
add mac-address=00:0C:29:E6:CE:C1 name=device1 user=Monitor
add mac-address=00:0C:29:88:6E:85 name=device3 user=Monitor
i have dynamic and static of kid-control device
2025-01-ECBD1DEE6001.txt (76 Bytes)
Do you see any data collected in Winbox under Kid Control - Devices? My script just takes the data generated by Kid Control.
Looking at the file you provided, I think there is something wrong with how you run the script. It should look like this (dummy data):
00:00:00:DB:00:00,XBOXONE,10.1.2.63,4967533,3788218,8755751,
Do you see any data collected in Winbox under Kid Control - Devices? My script just takes the data generated by Kid Control.
Looking at the file you provided, I think there is something wrong with how you run the script. It should look like this (dummy data):
00:00:00:DB:00:00,XBOXONE,10.1.2.63,4967533,3788218,8755751,
yes i have dynamic and static device in kid-control
Have I made a mistake?
I suggest you black out MAC addresses as that is usually considered a privacy concern.
As a troubleshooting step, open a Terminal window in Winbox and paste this code:
It’s just an excerpt from the script without saving anything to disk.
{
/ip/kid-control/device
:foreach device in=[find] do={
:local mac [get $device mac-address]
# Remove colons from MAC.
:local mac2 ([:pick $mac 0 2] . [:pick $mac 3 5] . [:pick $mac 6 8] . [:pick $mac 9 11] . [:pick $mac 12 14] . [:pick $mac 15 18])
:local name [get $device name]
# Empty names or IPs will cause issues when reading into array, so replace them.
:if ([:len $name] = 0) do={ :set $name "unknown" }
:local ip [:tostr [get $device ip-address]]
:if ([:len $ip] = 0) do={ :set $ip "no-ip" }
:local down [get $device bytes-down]
:local up [get $device bytes-up]
:local total ($down + $up)
# Save data as array: 0 1 2 3 4 5
:put "$mac,$name,$ip,$down,$up,$total,"
# End of for loop.
}
}
It should produce a list of devices in the format I posted earlier.
I suspect the issue is with IPv6 addresses having colons. I remember I had to remove colons from MAC addresses but never used IPv6. I will see what can be done about it.
I suggest you black out MAC addresses as that is usually considered a privacy concern.
As a troubleshooting step, open a Terminal window in Winbox and paste this code:
It’s just an excerpt from the script without saving anything to disk.{ /ip/kid-control/device :foreach device in=[find] do={ :local mac [get $device mac-address] # Remove colons from MAC. :local mac2 ([:pick $mac 0 2] . [:pick $mac 3 5] . [:pick $mac 6 8] . [:pick $mac 9 11] . [:pick $mac 12 14] . [:pick $mac 15 18]) :local name [get $device name] # Empty names or IPs will cause issues when reading into array, so replace them. :if ([:len $name] = 0) do={ :set $name "unknown" } :local ip [:tostr [get $device ip-address]] :if ([:len $ip] = 0) do={ :set $ip "no-ip" } :local down [get $device bytes-down] :local up [get $device bytes-up] :local total ($down + $up) # Save data as array: 0 1 2 3 4 5 :put "$mac,$name,$ip,$down,$up,$total," # End of for loop. } }
It should produce a list of devices in the format I posted earlier.
I suspect the issue is with IPv6 addresses having colons. I remember I had to remove colons from MAC addresses but never used IPv6. I will see what can be done about it.
thanks but no need to black out the mac address they are virtual made by vmware and any time i cant delete the mac address
output:
00:0C:29:E6:CE:C1,fgde1,212.xx.xx.193,13567097295,13314776453,26881873748,
00:0C:29:6A:0F:48,fgus1,212.xx.xx.194,6787106593,6758910538,13546017131,
00:0C:29:37:0D:A4,ubuntu,fe80::20c:29ff:fe37:da4;212.xx.xx.206,26387020,11721983,38109003,
00:0C:29:F0:8E:A8,windows-2025,fe80::e720:e828:8548:a53;212.xx.xx.207,146270,176998,323268,
00:0C:29:1F:96:A1,DetaBase,212.xx.xx.192,98202582,50713849,148916431,
00:0C:29:88:6E:85,fgtr1,212.xx.xx.195,693958,2672979,3366937,
00:0C:29:19:0F:40,fgfi,212.xx.xx.196,16335611419,16145100134,32480711553,
00:0C:29:5E:9F:0D,fgfr,212.xx.xx.197,488893,2168415,2657308,
00:0C:29:B1:1A:0B,fguk,212.xx.xx.198,1139875,2759023,3898898,
EC:BD:1D:EE:60:01,unknown,85.x.xx.129,0,0,0,
00:0C:29:E6:DB:46,unknown,fe80::20c:29ff:fee6:db46,0,0,0,
00:0C:29:92:F0:93,unknown,fe80::20c:29ff:fe92:f093,0,0,0,
00:0C:29:66:28:3A,unknown,fe80::20c:29ff:fe66:283a,0,0,0,
00:0C:29:A0:83:CB,unknown,fe80::20c:29ff:fea0:83cb,0,0,0,
00:0C:29:01:93:F0,unknown,fe80::20c:29ff:fe01:93f0,0,0,0,
00:0C:29:F9:ED:AE,unknown,fe80::20c:29ff:fef9:edae,0,0,0,
00:0C:29:1D:3B:01,unknown,fe80::20c:29ff:fe1d:3b01,0,0,0,
00:0C:29:30:91:D8,unknown,fe80::20c:29ff:fe30:91d8,0,0,0,
00:0C:29:AE:F9:F5,unknown,fe80::20c:29ff:feae:f9f5,0,0,0,
00:0C:29:BD:1C:35,unknown,fe80::20c:29ff:febd:1c35,0,0,0,
00:0C:29:92:DE:29,unknown,fe80::20c:29ff:fe92:de29,0,0,0,
00:50:56:BC:AE:57,unknown,fe80::250:56ff:febc:ae57,0,0,0,
00:0C:29:D2:D2:3C,unknown,fe80::20c:29ff:fed2:d23c,0,0,0,
00:50:56:BC:57:97,unknown,fe80::250:56ff:febc:5797,0,0,0,
00:50:56:84:4A:2B,unknown,fe80::250:56ff:fe84:4a2b,0,0,0,
00:50:56:BC:A6:03,unknown,fe80::250:56ff:febc:a603,0,0,0,
00:50:56:BC:0A:CF,unknown,fe80::250:56ff:febc:acf,0,0,0,
00:0C:29:30:93:EA,unknown,fe80::20c:29ff:fe30:93ea,0,0,0,
00:0C:29:DA:67:16,unknown,fe80::20c:29ff:feda:6716,0,0,0,
I also deleted all devices that had IPv6 and ran the script, but the html file was still the same as before.
output after remove ipv6 devices :
00:0C:29:E6:CE:C1,fgde1,212.xx.xx.193,840899749,830698808,1671598557,
00:0C:29:6A:0F:48,fgus1,212.xx.xx.194,7922273387,7887784538,15810057925,
00:0C:29:1F:96:A1,DetaBase,212.xx.xx.192,114360091,61259459,175619550,
00:0C:29:88:6E:85,fgtr1,212.xx.xx.195,780762,3004863,3785625,
00:0C:29:19:0F:40,fgfi,212.xx.xx.196,17739021198,17542112225,35281133423,
00:0C:29:5E:9F:0D,fgfr,212.xx.xx.197,544288,2437490,2981778,
00:0C:29:B1:1A:0B,fguk,212.xx.xx.198,1280800,3105035,4385835,
OK, please do me a favor and paste the following into the Terminal:
{
:local sysdate [/system/clock/get date]
:global yearmonth [:pick $sysdate 0 7]
:local reportpath ("usb1-part1/report-" . $yearmonth . ".html")
:local savedir "usb1-part1/bandwidth"
/ip/kid-control/device
:foreach device in=[find] do={
:local mac [get $device mac-address]
:local mac2 ([:pick $mac 0 2] . [:pick $mac 3 5] . [:pick $mac 6 8] . [:pick $mac 9 11] . [:pick $mac 12 14] . [:pick $mac 15 18])
:local filename ($savedir . "/" . $yearmonth . "-" . $mac2 . ".txt")
# If the file does not exist, create it using a workaround.
:if ([:len [/file/find name=$filename]] = 0) do={
/system/identity/print file=$filename
### Wait for the write to finish, otherwise "set" will fail later.
:while ([:len [/file/find name=$filename]] = 0) do={ :delay 2 }
}
/file/set $filename contents="$mac,testname,testip,5,5,10,"
}
}
After this, all 2025-01-mac.txt files should have: “MAC,testname,testip,5,5,10,”. This is just to prove that the file writes are taking place.
OK, please do me a favor and paste the following into the Terminal:
{ :local sysdate [/system/clock/get date] :global yearmonth [:pick $sysdate 0 7] :local reportpath ("usb1-part1/report-" . $yearmonth . ".html") :local savedir "usb1-part1/bandwidth" /ip/kid-control/device :foreach device in=[find] do={ :local mac [get $device mac-address] :local mac2 ([:pick $mac 0 2] . [:pick $mac 3 5] . [:pick $mac 6 8] . [:pick $mac 9 11] . [:pick $mac 12 14] . [:pick $mac 15 18]) :local filename ($savedir . "/" . $yearmonth . "-" . $mac2 . ".txt") # If the file does not exist, create it using a workaround. :if ([:len [/file/find name=$filename]] = 0) do={ /system/identity/print file=$filename ### Wait for the write to finish, otherwise "set" will fail later. :while ([:len [/file/find name=$filename]] = 0) do={ :delay 2 } } /file/set $filename contents="$mac,testname,testip,5,5,10," } }
After this, all 2025-01-mac.txt files should have: “MAC,testname,testip,5,5,10,”. This is just to prove that the file writes are taking place.
I thank you for your support regarding this script.
Sorry but it seems to be a mistake on my part.
I don’t have much knowledge in Mikrotik scripts and since I couldn’t connect usb in the virtual machine I decided to delete “usb1-part1” but I didn’t know that I even had to delete “/”.
I had made these changes in order:
your script:
:local sysdate [/system/clock/get date]
:global yearmonth [:pick $sysdate 0 7]
:local reportpath ("usb1-part1/report-" . $yearmonth . ".html")
:execute {
:local savedir "usb1-part1/bandwidth"
:local downmonthly 0
:local upmonthly 0
:local gig 1073741824
my first change (delete “usb1-part1”):
:local sysdate [/system/clock/get date]
:global yearmonth [:pick $sysdate 0 7]
:local reportpath ("/report-" . $yearmonth . ".html")
:execute {
:local savedir "/bandwidth"
:local downmonthly 0
:local upmonthly 0
:local gig 1073741824
my second change (delete “usb1-part1/”):
:local sysdate [/system/clock/get date]
:global yearmonth [:pick $sysdate 0 7]
:local reportpath ("report-" . $yearmonth . ".html")
:execute {
:local savedir "bandwidth"
:local downmonthly 0
:local upmonthly 0
:local gig 1073741824
Is everything okay now
Great to hear! Yes, the reportpath is the variable to the report file location. The savedir is where text files with bandwidth data are stored between script executions.
Keep in mind that on some (most?) devices a path location that is just in the root directory “/” means RAM. So, if the device is rebooted, all data stored there is lost. If you want to save to internal flash storage (I would be careful with that), you need to use /flash. See more here: https://help.mikrotik.com/docs/spaces/ROS/pages/2555971/Files
RouterOS now also has the ability to mount a remote share like SMB that you could use to store this data. https://help.mikrotik.com/docs/spaces/ROS/pages/259031065/ROSE-storage#ROSEstorage-SMB
my html is all zeros, but the bandwith folder files has values, what could be wrong? i have attached some files and a generated html (removed fisical mac addresses)
<html><head>
<script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script>
<script type='text/javascript'>
google.charts.load('current', {packages: ['corechart', 'bar']});
google.charts.setOnLoadCallback(drawMultSeries);
function drawMultSeries() {
var data = google.visualization.arrayToDataTable([
['Device', 'Download', 'Upload', 'Total'],
['pihole-lan 192.168.60.16\nBC:24:11:40:73:8E', 0, 0, 0],
['The Dude 192.168.0.4\nBC:24:11:F9:C2:01', 0, 0, 0],
['unRAID 169.254.72.79;192.168.0.6\nBC:24:11:98:6A:F9', 0, 0, 0],
['ubuntu-server 192.168.0.7\nBC:24:11:48:9B:83', 0, 0, 0],
['WS2022 192.168.50.18\nBC:24:11:82:E0:6F', 0, 0, 0],
['unknown 192.168.0.22\nBC:33:29:26:4C:0F', 0, 0, 0],
['unknown 192.168.0.197\nBC:24:11:6B:2F:71', 0, 0, 0],
['unknown 192.168.60.15\nBC:24:11:69:96:70', 0, 0, 0],
['unknown 192.168.0.180\nBC:24:11:CA:98:59', 0, 0, 0],
['unknown 192.168.0.177\nBC:24:11:FA:E3:2E', 0, 0, 0],
['unknown 192.168.0.176\nBC:24:11:18:7B:A6', 0, 0, 0],
['unknown 192.168.0.173;192.168.0.13\nBC:24:11:16:6E:AE', 0, 0, 0],
['unknown 192.168.0.175\nBC:24:11:0F:8B:8F', 0, 0, 0],
]);
data.sort([{column: 3, desc: true}, {column: 1, desc: true}])
var view = new google.visualization.DataView(data);
view.setColumns([0, 1,
{ calc: 'stringify', sourceColumn: 1, type: 'string', role: 'annotation' },
2,
{ calc: 'stringify', sourceColumn: 2, type: 'string', role: 'annotation' },
3,
{ calc: 'stringify', sourceColumn: 3, type: 'string', role: 'annotation' }]);
// set inner height to fixed pixels per row
var chartAreaHeight = data.getNumberOfRows() * 100;
// add padding to outer height to accomodate title, axis labels, etc
var chartHeight = chartAreaHeight + 150;
var options = {
title: 'Bandwidth consumption for 2025-04',
hAxis: { title: 'Data, GB' },
vAxis: { title: 'Device' },
legend: { position: 'top' },
height: chartHeight,
chartArea: { height: chartAreaHeight },
fontSize: '14'
};
var chart = new google.visualization.BarChart(document.getElementById('chart_div'));
chart.draw(view, options);
}
</script></head>
<div style='margin-left: 20%'>Report created: 2025-04-10 23:08:15</br>
Download total: 3GB</br>
Upload total: 0GB</br>
Total: 4GB</div>
<body><div id='chart_div'></div></body></html>
2025-04-BC2411699670.txt (47 Bytes)
2025-04-BC2411986AF9.txt (73 Bytes)
2025-04-BC3329264C0F.txt (55 Bytes)
2025-04-BC241140738E.txt (66 Bytes)
2025-04-BC241182E06F.txt (61 Bytes)
2025-04-BC2411166EAE.txt (60 Bytes)
2025-04-BC2411187BA6.txt (47 Bytes)
2025-04-BC2411489B83.txt (67 Bytes)
2025-04-BC2411FAE32E.txt (47 Bytes)
2025-04-BC24110F8B8F.txt (47 Bytes)
2025-04-BC24116B2F71.txt (47 Bytes)
2025-04-BC2411F9C201.txt (64 Bytes)
2025-04-BC2411CA9859.txt (47 Bytes)
The files you attached all have 5-10 bytes of data. The report can show a minimum of 1 gigabyte. You simply don’t have yet enough data to be shown on the report. Just wait for a few hours, depending on how much there is activity and how often you have the script scheduled.