Alasdair Allan
Published © MIT

A Raspberry Pi Network Scanner

Make use of a Raspberry Pi to build a network scanner that will keep track of the what host connect to your local network and when.

IntermediateFull instructions provided4 hours9,039
A Raspberry Pi Network Scanner

Things used in this project

Hardware components

7-Segment display, 6.5” red
SparkFun #8530
×2
Large Digit Driver
SparkFun #13279
×2
SparkFun RedBoard or Arduino Uno
×1
USB mini-B cable
×1
Power supply, barrel connector
12V/2A
×1
Jumper wires, male to female
×6
Jumper wires, male to male
×6
Raspberry Pi 3
×1
Power supply, micro USB, SV/2A
×1
USB Wi-Fi dongle
We used Anewish Mini Wireless RT5370
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder

Story

Read more

Code

Code snippet #1

Plain text
 $ lsusb
 Bus 001 Device 004: ID 148f:5370 Ralink Technology, Corp. RT5370 Wireless Adapter
 Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
 Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
 Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Code snippet #2

Plain text
 $ df -h
 Filesystem Size Used Avail Capacity iused ifree %iused Mounted on
 /dev/disk0s2 699Gi 367Gi 332Gi 53% 96214802 86992771 53% /
 devfs 206Ki 206Ki 0Bi 100% 714 0 100% /dev
 map -hosts 0Bi 0Bi 0Bi 100% 0 0 100% /net
 map auto_home 0Bi 0Bi 0Bi 100% 0 0 100% /home
 /dev/disk1s1 59Gi 33Gi 26Gi 57% 8739054 6768902 56% /Volumes/SD Card

Code snippet #7

Plain text
 $ ping raspberrypi.local
 PING raspberrypi.local (192.168.1.159): 56 data bytes
 64 bytes from 192.168.1.159: icmp_seq=0 ttl=64 time=4.079 ms
 64 bytes from 192.168.1.159: icmp_seq=1 ttl=64 time=4.223 ms
 64 bytes from 192.168.1.159: icmp_seq=2 ttl=64 time=6.717 ms
 ^C
 --- raspberrypi.local ping statistics ---
 3 packets transmitted, 3 packets received, 0.0% packet loss
 round-trip min/avg/max/stddev = 4.079/5.006/6.717/1.211 ms

Code snippet #11

Plain text
 $ ifconfig
 eth0 Link encap:Ethernet HWaddr b8:27:eb:97:cc:e4
 inet addr:192.168.1.159 Bcast:192.168.1.255 Mask:255.255.255.0
 inet6 addr: fe80::b059:6454:1d28:763a/64 Scope:Link
 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
 RX packets:88128 errors:0 dropped:32 overruns:0 frame:0
 TX packets:45378 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:1000
 RX bytes:122233058 (116.5 MiB) TX bytes:3828475 (3.6 MiB)
 
 lo Link encap:Local Loopback
 inet addr:127.0.0.1 Mask:255.0.0.0
 inet6 addr: ::1/128 Scope:Host
 UP LOOPBACK RUNNING MTU:65536 Metric:1
 RX packets:38581 errors:0 dropped:0 overruns:0 frame:0
 TX packets:38581 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:1
 RX bytes:3395128 (3.2 MiB) TX bytes:3395128 (3.2 MiB)

 wlan0 Link encap:Ethernet HWaddr b8:27:eb:c2:99:b1
 inet6 addr: fe80::9380:71d4:4917:9b65/64 Scope:Link
 UP BROADCAST MULTICAST MTU:1500 Metric:1
 RX packets:7 errors:0 dropped:7 overruns:0 frame:0
 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:1000
 RX bytes:773 (773.0 B) TX bytes:0 (0.0 B)

 wlan1 Link encap:Ethernet HWaddr 00:0f:60:05:a8:5c
 UP BROADCAST MULTICAST MTU:1500 Metric:1
 RX packets:0 errors:0 dropped:0 overruns:0 frame:0
 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:1000
 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

Code snippet #12

Plain text
 $ iwconfig
 wlan0 IEEE 802.11bgn ESSID:off/any
 Mode:Managed Access Point: Not-Associated Tx-Power=31 dBm
 Retry short limit:7 RTS thr:off Fragment thr:off
 Power Management:on

 lo no wireless extensions.

 eth0 no wireless extensions.

 wlan1 IEEE 802.11bgn ESSID:off/any
 Mode:Managed Access Point: Not-Associated Tx-Power=20 dBm
 Retry short limit:7 RTS thr:off Fragment thr:off
 Power Management:off

 $ iw dev
 phy#1
 Interface wlan1
 ifindex 4
 wdev 0x100000001
 addr 00:0f:60:05:a8:5c
 type managed
 phy#0
 Interface wlan0
 ifindex 3
 wdev 0x1
 addr b8:27:eb:c2:99:b1
 type managed

Code snippet #13

Plain text
 $ sudo iw phy phy1 interface add mon1 type monitor
 $ iw dev
 phy#1
 Interface mon1
 ifindex 5
 wdev 0x100000002
 addr 00:0f:60:05:a8:5c
 type monitor
 Interface wlan1
 ifindex 4
 wdev 0x100000001
 addr 00:0f:60:05:a8:5c
 type managed
 phy#0
 Interface wlan0
 ifindex 3
 wdev 0x1
 addr b8:27:eb:c2:99:b1
 type managed

Code snippet #16

Plain text
 Supported interface modes:
 * IBSS
 * managed
 * AP
 * AP/VLAN
 * WDS
 * monitor
 * mesh point

Code snippet #21

Plain text
 network={
 ssid="OFFICE"
 psk="OFFICE-PASSWORD"
 id_str="office"
 }

 network={
 ssid="HOME"
 psk="HOME-PASSWORD"
 id_str="home"
 }

Code snippet #23

Plain text
 $ ifconfig wlan0
 wlan0 Link encap:Ethernet HWaddr b8:27:eb:c2:99:b1
 inet addr:192.168.1.217 Bcast:192.168.1.255 Mask:255.255.255.0
 inet6 addr: fe80::9380:71d4:4917:9b65/64 Scope:Link
 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
 RX packets:441 errors:0 dropped:416 overruns:0 frame:0
 TX packets:20 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:1000
 RX bytes:126262 (123.3 KiB) TX bytes:3634 (3.5 KiB)

Code snippet #24

Plain text
 allow-hotplug wlan1
 iface wlan1 inet manual
 pre-up iw phy phy1 interface add mon1 type monitor
 pre-up iw dev wlan1 del
 pre-up ifconfig mon1 up

Code snippet #26

Plain text
 $ ifconfig
 eth0 Link encap:Ethernet HWaddr b8:27:eb:97:cc:e4
 inet addr:192.168.1.159 Bcast:192.168.1.255 Mask:255.255.255.0
 inet6 addr: fe80::b059:6454:1d28:763a/64 Scope:Link
 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
 RX packets:110 errors:0 dropped:0 overruns:0 frame:0
 TX packets:95 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:1000
 RX bytes:17019 (16.6 KiB) TX bytes:13954 (13.6 KiB)

 lo Link encap:Local Loopback
 inet addr:127.0.0.1 Mask:255.0.0.0
 inet6 addr: ::1/128 Scope:Host
 UP LOOPBACK RUNNING MTU:65536 Metric:1
 RX packets:0 errors:0 dropped:0 overruns:0 frame:0
 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:1
 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

 mon1 Link encap:UNSPEC HWaddr 00-0F-60-05-A8-5C-30-30-00-00-00-00-00-00-00-00
 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
 RX packets:1414 errors:0 dropped:1414 overruns:0 frame:0
 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:1000
 RX bytes:335469 (327.6 KiB) TX bytes:0 (0.0 B)

 wlan0 Link encap:Ethernet HWaddr b8:27:eb:c2:99:b1
 inet addr:192.168.1.217 Bcast:192.168.1.255 Mask:255.255.255.0
 inet6 addr: fe80::9380:71d4:4917:9b65/64 Scope:Link
 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
 RX packets:134 errors:0 dropped:77 overruns:0 frame:0
 TX packets:31 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:1000
 RX bytes:30147 (29.4 KiB) TX bytes:5258 (5.1 KiB)"

Code snippet #27

Plain text
 $ iw dev
 phy#1
 Interface mon1
 ifindex 5
 wdev 0x100000002
 addr 00:0f:60:05:a8:5c
 type monitor
 channel 1 (2412 MHz), width: 20 MHz (no HT), center1: 2412 MHz
 phy#0
 Interface wlan0
 ifindex 3
 wdev 0x1
 addr b8:27:eb:c2:99:b1
 ssid OFFICE
 type managed

Code snippet #29

Plain text
 $ sudo iwconfig mon1 channel 6
 $ iw dev
 phy#1
 Interface mon1
 ifindex 5
 wdev 0x100000002
 addr 00:0f:60:05:a8:5c
 type monitor
 channel 6 (2437 MHz), width: 20 MHz (no HT), center1: 2437 MHz
 phy#0
 Interface wlan0
 ifindex 3
 wdev 0x1
 addr b8:27:eb:c2:99:b1
 ssid OFFICE
 type managed

Code snippet #32

Plain text
 $ sudo apt-get install git-core build-essential
 $ sudo apt-get install libncurses5-dev libpcap-dev libpcre3-dev libnl-dev libmicrohttpd10 libmicrohttpd-dev
 $ wget http://kismetwireless.net/code/kismet-2016-07-R1.tar.xz
 $ tar -xvf kismet-2016-07-R1.tar.xz
 $ cd kismet-2016-07-R1
 $ ./configure
 $ make
 $ sudo make suidinstall
 $ sudo usermod -a -G kismet pi
 $ sudo mkdir -p /usr/local/lib/kismet/
 $ mkdir -p /home/pi/.kismet/plugins/
 $ sudo mkdir -p /usr/lib/kismet/
 $ sudo reboot

Code snippet #39

Plain text
 [Unit]
 Description=/etc/rc.local Compatibility
 ConditionPathExists=/etc/rc.local

 [Service]
 Type=forking
 ExecStart=/etc/rc.local start
 TimeoutSec=0
 StandardOutput=tty
 RemainAfterExit=yes
 SysVStartPriority=99

 [Install]
 WantedBy=multi-user.target

Code snippet #41

Plain text
 $ sudo systemctl enable rc-local
 $ sudo systemctl status -l rc-local.service
 ● rc-local.service - /etc/rc.local Compatibility
 Loaded: loaded (/etc/systemd/system/rc-local.service; enabled)
 Drop-In: /etc/systemd/system/rc-local.service.d
 └─ttyoutput.conf
 Active: active (exited) since Thu 2017-02-02 12:08:33 UTC; 1min 56s ago

 Feb 02 12:08:33 netscan systemd[1]: Starting /etc/rc.local Compatibility...
 Feb 02 12:08:33 netscan systemd[1]: Started /etc/rc.local Compatibility.

Code snippet #44

Plain text
 $ sudo arp-scan --retry=8 --ignoredups -I wlan0 --localnet
 Interface: wlan0, datalink type: EN10MB (Ethernet)
 Starting arp-scan 1.8.1 with 256 hosts (http://www.nta-monitor.com/tools/arp-scan/)
 192.168.1.93 70:73:cb:b2:91:ee (Unknown)
 192.168.1.130 70:73:cb:b2:91:ee (Unknown)
 192.168.1.91 e0:cb:ee:41:ce:29 (Unknown)
 192.168.1.208 70:73:cb:b2:91:ee (Unknown)
 192.168.1.253 9e:97:26:94:76:e4 (Unknown)
 192.168.1.254 9c:97:26:94:76:e4 (Unknown)
 192.168.1.131 f4:5c:89:8b:79:a7 (Unknown)
 192.168.1.120 e0:ac:cb:a3:1d:04 (Unknown)
 192.168.1.121 60:03:08:aa:8c:0a (Unknown)
 192.168.1.129 14:10:9f:d2:23:57 (Unknown)
 192.168.1.126 e4:f8:9c:91:14:73 (Unknown)
 192.168.1.214 00:26:ab:61:a3:b3 SEIKO EPSON CORPORATION
 192.168.1.127 28:e1:4c:9c:89:4f (Unknown)
 192.168.1.125 f4:e3:fb:dc:19:08 (Unknown)
 192.168.1.132 60:92:17:71:f4:6d (Unknown)

 24 packets received by filter, 0 packets dropped by kernel
 Ending arp-scan 1.8.1: 256 hosts scanned in 7.327 seconds (34.94 hosts/sec). 15 responded

Code snippet #46

Plain text
 $ sudo arp-scan --retry=8 --ignoredups -I wlan0 --localnet
 Interface: wlan0, datalink type: EN10MB (Ethernet)
 Starting arp-scan 1.8.1 with 256 hosts (http://www.nta-monitor.com/tools/arp-scan/)
 192.168.1.208 70:73:cb:b2:91:ee Apple, Inc.
 192.168.1.86 00:e0:4c:c6:86:01 REALTEK SEMICONDUCTOR CORP.
 192.168.1.253 9e:97:26:94:76:e4 (Unknown)
 192.168.1.254 9c:97:26:94:76:e4 Technicolor
 192.168.1.121 60:03:08:aa:8c:0a Apple, Inc.
 192.168.1.93 28:cf:e9:57:cd:01 Apple, Inc.
 192.168.1.125 f4:e3:fb:dc:19:08 HUAWEI TECHNOLOGIES CO.,LTD
 192.168.1.133 24:4b:81:3d:e9:ad Samsung Electronics Co.,Ltd
 192.168.1.137 ac:e0:10:c7:c5:67 Liteon Technology Corporation
 192.168.1.130 7c:c3:a1:b1:42:52 Apple, Inc.
 192.168.1.129 14:10:9f:d2:23:57 Apple, Inc.
 192.168.1.131 f4:5c:89:8b:79:a7 Apple, Inc.
 192.168.1.126 e4:f8:9c:91:14:73 Intel Corporate
 192.168.1.135 08:6d:41:bf:ff:1a Apple, Inc.
 192.168.1.134 b4:8b:19:2c:5f:de Apple, Inc.
 192.168.1.214 00:26:ab:61:a3:b3 SEIKO EPSON CORPORATION
 192.168.1.120 e0:ac:cb:a3:1d:04 Apple, Inc.
 192.168.1.127 28:e1:4c:9c:89:4f Apple, Inc.
 192.168.1.132 60:92:17:71:f4:6d Apple, Inc.

 33 packets received by filter, 0 packets dropped by kernel
 Ending arp-scan 1.8.1: 256 hosts scanned in 7.927 seconds (32.29 hosts/sec). 19 responded

Code snippet #48

Plain text
 #!/usr/bin/env perl

 use strict;
 use warnings;

 use DBI;
 use Getopt::Long;
 use DateTime;

 my ( %opt );
 my $status = GetOptions("network=s" => \$opt{"network"},
 "dig" => \$opt{"dig"});
 $opt{"network"} = "network" unless defined $opt{"network"};

 print "\nSCANNING\n--------\n";

 my ($stmt, $sth, $rv);

 my $dbfile = "/home/pi/$opt{'network'}.db";
 my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile","","");

 my $scan = `arp-scan --retry=8 --ignoredups -I wlan0 --localnet`;
 my @lines = split("\n", $scan);

 my $csv = undef;
 my $count = 0;
 foreach my $line (@lines) {
 chomp($line);
 if ($line =~ /^\s*((?:\d{1,3}\.){3}\d{1,3})\s+((?:[a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2})\s+(\S.*)/) {
 my $ip = $1;
 my $mac = $2;
 my $desc = $3;
 print "IP=$ip, MAC=$mac, DESC=$desc";

 # Dig for the mDNS name associated with the IP
 my $mdns = undef;
 if (defined($opt{"dig"})) {

 $stmt = qq(CREATE TABLE IF NOT EXISTS mdns(mac TEXT NOT NULL PRIMARY KEY UNIQUE, mdns TEXT););
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 my $dig = `dig -x ${ip} \@224.0.0.251 -p 5353`;
 #print $dig;

 my @report = split("\n", $dig);
 my $answer = 0;
 my $local = undef;
 foreach my $entry (@report) {
 chomp($entry);
 if ( $answer == 1 ) {
 $local = $entry;
 last;
 }
 if( $entry eq ";; ANSWER SECTION:") {
 $answer = 1;
 }

 }
 if ( defined $local ) {
 #print "local name = $local\n";
 ( $mdns ) = ($local =~ /\s+(\S+\.local)\.$/);
 print ", LOCAL=$mdns\n";

 $stmt = qq(INSERT OR REPLACE INTO mdns (mac, mdns) VALUES ("$mac","$mdns"));
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 } else {
 print "\n";
 }
 } else {
 print "\n";
 }

 $stmt = qq(CREATE TABLE IF NOT EXISTS macs(mac TEXT NOT NULL PRIMARY KEY UNIQUE, count INTEGER, description TEXT););
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 $stmt = qq(SELECT count FROM macs WHERE mac="$mac";);
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 my @row = $sth->fetchrow_array();
 $sth->finish();

 my $previous;
 if (defined( $row[0] )) {
 $previous = $row[0]
 } else {
 $previous = 0;
 }
 print "Previously seen '$previous' times\n";

 $stmt = qq(INSERT OR REPLACE INTO macs (mac, count, description) VALUES ("$mac",$previous+1,"$desc"));
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 if (!defined $csv) {
 $csv = "$mac";
 } else {
 $csv = $csv . ",$mac";
 }
 $count++;
 }
 }

 my $time = DateTime->now->iso8601;
 print "\nRESULT\n------\n";
 print "time = $time\n";
 print "count = $count\n";
 print "csv = $csv\n";

 $stmt = qq(CREATE TABLE IF NOT EXISTS scan(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, datetime TEXT UNIQUE, count INTEGER, macs TEXT););
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 $stmt = qq(INSERT INTO scan (datetime, count, macs) VALUES ("$time",$count,"$csv"));
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 $stmt = qq(CREATE TABLE IF NOT EXISTS days(date TEXT UNIQUE NOT NULL PRIMARY KEY, average INTEGER, samples TEXT););
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 print "\nAVERAGE\n-------\n";
 my $day = DateTime->now->ymd('-');
 print "Today is $day\n";
 $stmt = qq(SELECT samples FROM days WHERE date="$day";);
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 my @row = $sth->fetchrow_array();
 $sth->finish();

 my $samples;
 if (defined( $row[0] )) {
 $samples = $row[0];
 } else {
 $samples = undef;
 }
 my @values;
 if (!defined $samples) {
 $values[0] = $count;
 $samples = "$count";
 } else {
 @values = split(',', $samples);
 push (@values, $count);
 $samples = $samples . ",$count";
 }

 print "Samples = $samples\n";
 #print "values = @values\n";

 my $average = 0;
 foreach my $value (@values) {
 $average = $average + $value;
 }
 $average = int($average/scalar(@values));

 $stmt = qq(INSERT OR REPLACE INTO days (date, average, samples) VALUES ("$day",$average,"$samples"));
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 print "Current running average for today is $average devices on the network.\n";

 $dbh->disconnect();
 exit;

Code snippet #57

Plain text
 #!/bin/sh -e
 #
 # rc.local

 # su pi -c '/usr/local/bin/kismet_server -n -c mon1 --daemonize'
 /home/pi/counter.pl --network kaleider &

 exit 0

Code snippet #58

Plain text
 /*
 Controlling large 7-segment displays
 By: Nathan Seidle
 SparkFun Electronics
 Date: February 25th, 2015
 License: This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).

 This code demonstrates how to post two numbers to a 2-digit display usings two large digit driver boards.

 Here's how to hook up the Arduino pins to the Large Digit Driver IN

 Arduino pin 6 -> CLK (Green on the 6-pin cable)
 5 -> LAT (Blue)
 7 -> SER on the IN side (Yellow)
 5V -> 5V (Orange)
 Power Arduino with 12V and connect to Vin -> 12V (Red)
 GND -> GND (Black)

Code snippet #59

Plain text
*/

 //GPIO declarations
 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 byte segmentClock = 6;
 byte segmentLatch = 5;
 byte segmentData = 7;

 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

 void setup()
 {
 Serial.begin(9600);
 Serial.println("Large Digit Driver Example");

 pinMode(segmentClock, OUTPUT);
 pinMode(segmentData, OUTPUT);
 pinMode(segmentLatch, OUTPUT);

 digitalWrite(segmentClock, LOW);
 digitalWrite(segmentData, LOW);
 digitalWrite(segmentLatch, LOW);
 }

 int number = 0;

 void loop()
 {
 showNumber(number); //Test pattern
 number++;
 number %= 100; //Reset x after 99

 Serial.println(number); //For debugging

 delay(500);
 }

 //Takes a number and displays 2 numbers. Displays absolute value (no negatives)
 void showNumber(float value)
 {
 int number = abs(value); //Remove negative signs and any decimals

 //Serial.print("number: ");
 //Serial.println(number);

 for (byte x = 0 ; x < 2 ; x++)
 {
 int remainder = number % 10;

 postNumber(remainder, false);

 number /= 10;
 }

 //Latch the current segment data
 digitalWrite(segmentLatch, LOW);
 digitalWrite(segmentLatch, HIGH); //Register moves storage register on the rising edge of RCK
 }

 //Given a number, or '-', shifts it out to the display
 void postNumber(byte number, boolean decimal)
 {
 // - A
 // / / F/B
 // - G
 // / / E/C
 // -. D/DP

 #define a 1<<0
 #define b 1<<6
 #define c 1<<5
 #define d 1<<4
 #define e 1<<3
 #define f 1<<1
 #define g 1<<2
 #define dp 1<<7

 byte segments;

 switch (number)
 {
 case 1: segments = b | c; break;
 case 2: segments = a | b | d | e | g; break;
 case 3: segments = a | b | c | d | g; break;
 case 4: segments = f | g | b | c; break;
 case 5: segments = a | f | g | c | d; break;
 case 6: segments = a | f | g | e | c | d; break;
 case 7: segments = a | b | c; break;
 case 8: segments = a | b | c | d | e | f | g; break;
 case 9: segments = a | b | c | d | f | g; break;
 case 0: segments = a | b | c | d | e | f; break;
 case ' ': segments = 0; break;
 case 'c': segments = g | e | d; break;
 case '-': segments = g; break;
 }

 if (decimal) segments |= dp;

 //Clock these bits out to the drivers
 for (byte x = 0 ; x < 8 ; x++)
 {
 digitalWrite(segmentClock, LOW);
 digitalWrite(segmentData, segments & 1 << (7 - x));
 digitalWrite(segmentClock, HIGH); //Data transfers to the register on the rising edge of SRCK
 }
 }

Code snippet #60

Plain text
 #include

 byte segmentClock = 6;
 byte segmentLatch = 5;
 byte segmentData = 7;
 String readString;

 int addr = 0;

 void setup() {
 Serial.begin(9600);
 Serial.println("Network Counter");

 pinMode(segmentClock, OUTPUT);
 pinMode(segmentData, OUTPUT);
 pinMode(segmentLatch, OUTPUT);

 digitalWrite(segmentClock, LOW);
 digitalWrite(segmentData, LOW);
 digitalWrite(segmentLatch, LOW);

 int n = EEPROM.read(addr);
 showNumber(n);

 }

 void loop() {
 while (Serial.available()) {
 char c = Serial.read();
 readString += c;
 delay(2);
 }
 if (readString.length() >0) {
 Serial.println(readString);
 int n = readString.toInt();
 showNumber(n);
 EEPROM.update(addr, n);
 readString = "";
 }
 }

 void showNumber(float value) {
 int number = abs(value);

 for (byte x = 0 ; x < 2 ; x++) {
 int remainder = number % 10;
 postNumber(remainder, false);
 number /= 10;
 }

 digitalWrite(segmentLatch, LOW);
 digitalWrite(segmentLatch, HIGH);
 }

 void postNumber(byte number, boolean decimal) {

 #define a 1<<0
 #define b 1<<6
 #define c 1<<5
 #define d 1<<4
 #define e 1<<3
 #define f 1<<1
 #define g 1<<2
 #define dp 1<<7

 byte segments;

 switch (number) {
 case 1: segments = b | c; break;
 case 2: segments = a | b | d | e | g; break;
 case 3: segments = a | b | c | d | g; break;
 case 4: segments = f | g | b | c; break;
 case 5: segments = a | f | g | c | d; break;
 case 6: segments = a | f | g | e | c | d; break;
 case 7: segments = a | b | c; break;
 case 8: segments = a | b | c | d | e | f | g; break;
 case 9: segments = a | b | c | d | f | g; break;
 case 0: segments = a | b | c | d | e | f; break;
 case ' ': segments = 0; break;
 case 'c': segments = g | e | d; break;
 case '-': segments = g; break;
 }

 if (decimal) segments |= dp;



 for (byte x = 0 ; x < 8 ; x++) {

   digitalWrite(segmentClock, LOW);

   digitalWrite(segmentData, segments & 1 << (7 - x));

   digitalWrite(segmentClock, HIGH);

  }

 }

Code snippet #61

Plain text
 $ ls /dev/tty*
 /dev/tty   /dev/tty19 /dev/tty3   /dev/tty40 /dev/tty51 /dev/tty62
 /dev/tty0   /dev/tty2   /dev/tty30 /dev/tty41 /dev/tty52 /dev/tty63
 /dev/tty1   /dev/tty20 /dev/tty31 /dev/tty42 /dev/tty53 /dev/tty7
 /dev/tty10 /dev/tty21 /dev/tty32 /dev/tty43 /dev/tty54 /dev/tty8
 /dev/tty11 /dev/tty22 /dev/tty33 /dev/tty44 /dev/tty55 /dev/tty9
 /dev/tty12 /dev/tty23 /dev/tty34 /dev/tty45 /dev/tty56 /dev/ttyAMA0
 /dev/tty13 /dev/tty24 /dev/tty35 /dev/tty46 /dev/tty57 /dev/ttyS0
 /dev/tty14 /dev/tty25 /dev/tty36 /dev/tty47 /dev/tty58 /dev/ttyUSB0
 /dev/tty15 /dev/tty26 /dev/tty37 /dev/tty48 /dev/tty59 /dev/ttyprintk
 /dev/tty16 /dev/tty27 /dev/tty38 /dev/tty49 /dev/tty6
 /dev/tty17 /dev/tty28 /dev/tty39 /dev/tty5   /dev/tty60
 /dev/tty18 /dev/tty29 /dev/tty4   /dev/tty50 /dev/tty61

Code snippet #63

Plain text
 #!/usr/bin/env perl


 #+
 
 # Name:
 #   counter.pl
 
 # Language:
 #   Perl
 
 # Purpose:
 #   Count the number of devices on a network
 
 # Description
 #   Script uses an ARP scan to counter the number of devices on the network 
 #   and saves this information to an SQLite3 database. Database will be created
 #   the first time the script is run.
 
 # External Modules:
 #   Getopt::Long
 #   DBI
 #   DataTime
 #   Device::SerialPort
 
 # Authors:
 #   ANA: Alasdair Allan (Babilim Light Industries)
 
 # History
 #   06-FEB-2017 (ANA):
 #     Checking for duplicate MAC address with multiple IP. Assuming it's a single device.  

 #   04-FEB-2017 (ANA):
 #     Added serial output to Arduino.
 #   03-FEB-2017 (ANA):
 #    Added optional lookup of mDNS forward address of scanned hosts.
 #   02-JAN-2017 (AA):
 #     Original Version
 
 # Copyright:
 #   Copyright (C) 2017. Babilim Light Industries, Ltd. Released under the MIT License.
 
 #-
 
 # L O A D   M O D U L E S -----------------------------------------------
 
 use strict;
 use warnings;
 
 use DBI;
 use Getopt::Long;
 use DateTime;
 use Device::SerialPort;
 
 # O P T I O N S   H A N D L I N G ---------------------------------------
 
 my ( %opt );
 my $status = GetOptions("network=s" => \$opt{"network"},
                        "dig"       => \$opt{"dig"});
 $opt{"network"} = "network" unless defined $opt{"network"};

 # S E R I A L P O R T --------------------------------------------------

 my $port = Device::SerialPort->new("/dev/ttyUSB0");
 $port->baudrate(9600);
 $port->databits(8);
 $port->parity("none");
 $port->stopbits(1);

 # R U N S C A N -------------------------------------------------------

 print "\nSCANNING\n--------\n";

 my ($stmt, $sth, $rv);

 my $dbfile = "/home/pi/$opt{'network'}.db";
 my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile","","");

 my $scan = `arp-scan --retry=8 --ignoredups -I wlan0 --localnet`;
 my @lines = split("\n", $scan);

 my $csv = undef;
 my $count = 0;
 foreach my $line (@lines) {
 chomp($line);
 if ($line =~ /^\s*((?:\d{1,3}\.){3}\d{1,3})\s+((?:[a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2})\s+(\S.*)/) {
 my $ip = $1;
 my $mac = $2;
 my $desc = $3;
 print "IP=$ip, MAC=$mac, DESC=$desc";

 # Dig for the mDNS name associated with the IP
 my $mdns = undef;
 if (defined($opt{"dig"})) {

 $stmt = qq(CREATE TABLE IF NOT EXISTS mdns(mac TEXT NOT NULL PRIMARY KEY UNIQUE, mdns TEXT););
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 my $dig = `dig -x ${ip} \@224.0.0.251 -p 5353`;
 #print $dig;

 my @report = split("\n", $dig);
 my $answer = 0;
 my $local = undef;
 foreach my $entry (@report) {
 chomp($entry);
 if ( $answer == 1 ) {
 $local = $entry;
 last;
 }
 if( $entry eq ";; ANSWER SECTION:") {
 $answer = 1;
 }

 }
 if ( defined $local ) {
 #print "local name = $local\n";
 ( $mdns ) = ($local =~ /\s+(\S+\.local)\.$/);
 print ", LOCAL=$mdns\n";

 $stmt = qq(INSERT OR REPLACE INTO mdns (mac, mdns) VALUES ("$mac","$mdns"));
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 } else {
 print "\n";
 }
 } else {
 print "\n";
 }

 my $duplicate = undef;
 if(defined $csv) {
 if( $csv =~ /$mac/ ) {
 $duplicate = 1;
 }
 }

 if ( !$duplicate ) {
 $stmt = qq(CREATE TABLE IF NOT EXISTS macs(mac TEXT NOT NULL PRIMARY KEY UNIQUE, count INTEGER, description TEXT););
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 $stmt = qq(SELECT count FROM macs WHERE mac="$mac";);
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 my @row = $sth->fetchrow_array();
 $sth->finish();

 my $previous;
 if (defined( $row[0] )) {
 $previous = $row[0]
 } else {
 $previous = 0;
 }
 print "Previously seen '$previous' times\n";

 $stmt = qq(INSERT OR REPLACE INTO macs (mac, count, description) VALUES ("$mac",$previous+1,"$desc"));
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 if (!defined $csv) {
 $csv = "$mac";
 } else {
 $csv = $csv . ",$mac";
 }
 $count++;
 } else {
 print "Ignoring duplicate MAC '$mac'\n";
 }

 }
 }

 my $time = DateTime->now->iso8601;
 print "\nRESULT\n------\n";
 print "time = $time\n";
 print "count = $count\n";
 print "csv = $csv\n";

 # S E R I A L P O R T --------------------------------------------------

 $port->write($count);

 # S A V E S C A N T O D A T A B A S E ----------------------------------

 $stmt = qq(CREATE TABLE IF NOT EXISTS scan(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, datetime TEXT UNIQUE, count INTEGER, macs TEXT););
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 $stmt = qq(INSERT INTO scan (datetime, count, macs) VALUES ("$time",$count,"$csv"));
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 # L A S T O R D E R S -------------------------------------------------

 $stmt = qq(CREATE TABLE IF NOT EXISTS days(date TEXT UNIQUE NOT NULL PRIMARY KEY, average INTEGER, samples TEXT););
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 print "\nAVERAGE\n-------\n";
 my $day = DateTime->now->ymd('-');
 print "Today is $day\n";
 $stmt = qq(SELECT samples FROM days WHERE date="$day";);
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 my @row = $sth->fetchrow_array();
 $sth->finish();

 my $samples;
 if (defined( $row[0] )) {
 $samples = $row[0];
 } else {
 $samples = undef;
 }
 my @values;
 if (!defined $samples) {
 $values[0] = $count;
 $samples = "$count";
 } else {
 @values = split(',', $samples);
 push (@values, $count);
 $samples = $samples . ",$count";
 }

 print "Samples = $samples\n";
 #print "values = @values\n";

 my $average = 0;
 foreach my $value (@values) {
 $average = $average + $value;
 }
 $average = int($average/scalar(@values));

 $stmt = qq(INSERT OR REPLACE INTO days (date, average, samples) VALUES ("$day",$average,"$samples"));
 $sth = $dbh->prepare( $stmt );
 $rv = $sth->execute();
 $sth->finish();

 print "Current running average for today is $average devices on the network.\n";

 $port->close();
 $dbh->disconnect();
 exit;

 # -----------------------------------------------------------------------

Gist

https://gist.github.com/aallan/b4bb86db86079509e6159810ae9bd3e4

Gist

https://gist.github.com/aallan/8f9087b32a81b74e72ad8e366b1c711a

Gist

http://gist.github.com/aallan/7ae04d27ac19b8ea90e26f8391f624c2

Gist

https://gist.github.com/aallan/ce7bd8a83544fca2d663405a08ca0a3f

Credits

Alasdair Allan

Alasdair Allan

3 projects • 428 followers
Scientist, author, hacker, maker, and journalist. Building, breaking, and writing. For hire. You can reach me at 📫 alasdair@babilim.co.uk.

Comments