PINE64::MCP23008 Module on CPAN

pinout ot the MCP23008 GPIO extender chip
pinout ot the MCP23008 GPIO extender chip

My latest module to upload to CPAN is a perl-based driver for the MCP23008 GPIO extender. This chip is very handy when you have ran out of GPIO pins on your single board computer and gives you 8 more digital I/O’s. This has come in handy for me on several occasions when I used an LCD for a project that took too many of my digital pins.

The driver works as you would expect: you can make any combination of inputs or outputs, and read in the state of the inputs. This is an i2c device that defaults to an address of 0x20. You can adjust the address by putting high / low values on the address lines A0 – A2 for a possible 8 different chips on a single i2c bus.

Below is a simple implementation of the module configured as an input and then as an output from the synopsis on CPAN ->

The methods basically use Device::I2C to manipulate the chip’s internal registers. Register 0x00 is the I/O direction register. This is an 8-bit register with each bit position representing a digital pin. Valid values are 0 – 255 where all pins default to outputs. So, to make pin 6 an input, you would call the set_direction() method like so:


The MCP23008 has internal 100K Ohm pull up resistors when pins are configured as inputs. This can save you room on your board by not having to build external pull up resistors yourself. This is configured in the GPPU (GPIO Pull-up resistor register) 0x06. The code below enables all 8 of the internal pull up resistors.


I also implemented the I/O polarity feature. With this enabled, the chip will give you the opposite polarity of the current state of the pin. The function call below reverses the polarity of pin 5.


The main methods of the module are read_pin() and write_pin(). They work as you would expect. It reads / writes values to the GPIO port register 0x09.

$gpext->write_pin(7, 1);

Here is a description of the GPIO port register straight out of the datasheet:

I didn’t implement all of the features of the MCP23008, but enough to make them useful for most projects. Here is the complete source code for the module:

     1	#!/usr/bin/perl 
     2	use strict;
     3	use Device::I2C;
     4	use IO::Handle;
     5	use Fcntl;
     6	package PINE64::MCP23008;
     7	our $VERSION = '0.9';
     8	#global vars
     9	my ($i2cbus, $addr, $gpext);
    10	my $gpregval = 0; 	#init gpio register value to 0
    11	my @pin_nums = (1,2,4,8,16,32,64,128);
    12	sub new{
    13		my $class = shift;
    14		my $self = bless {}, $class;
    15		#first arg is device address
    16		$addr = $_[0];
    17		#second arg i2c bus; optional
    18		$i2cbus = $_[1];
    20		if($i2cbus eq ''){
    21			$i2cbus = '/dev/i2c-0';
    22		}#end if
    24		$gpext = Device::I2C->new($i2cbus, "r+"); 
    25		#init i2c device
    26		$gpext->checkDevice($addr);
    27		$gpext->selectDevice($addr);
    28		#init gp register val to all off
    29		$gpregval = 0; 
    30		return $self;
    31	}#end new
    32	sub set_direction{
    33		#sets value of the IO direction register
    34		#ie 255 makes all input; 0 makes all output
    35		my $direction = $_[1];
    36		$gpext->writeByteData(0x00, $direction);	
    37	}#end set_direction
    38	sub enable_pullup{
    39		#when a pin is configured as an input
    40		#you can enable internal 100K pull-up
    41		#resistors by writing to the GPPU register
    42		#0-255 are valid values: 0 - all disabled
    43		#255 - all enabled
    45		my $en_gppu = $_[1];
    46		$gpext->writeByteData(0x06, $en_gppu);
    47	}#end enable_pullup
    48	sub set_polarity{
    49		#sets the polarity of the gpio pins; 
    50		#0 is normal polarity
    51		#255 is all pins reversed
    53		my $io_pol = $_[1];
    54		$gpext->writeByteData(0x01, $io_pol);
    55	}#end set_polarity
    56	sub write_pin{
    57		my $ind = $_[1];
    58		my $iox = $pin_nums[$ind];
    59		#1 or 0
    60		my $val = $_[2];
    61		if($val == 1){
    62			$gpregval+=$iox; 
    63		}#end if
    64		if($val == 0){
    65			$gpregval-=$iox;
    66		}#end if
    67		$gpext->writeByteData(0x09, $gpregval);
    68	}#end write_pin
    69	sub read_pin{
    70		my $ind = $_[1];
    71		my $iox = $pin_nums[$ind];
    72		my $pinval = 0; 
    73		#read GPIO register
    74		my $regval = $gpext->readByteData(0x09); 
    75		#ensure 8 binary places are displayed
    76		my $binout = sprintf("%08b", $regval);
    77		#parse eight binary digits into an array
    78		my @pinvals = split(//, $binout);
    80		#reverse array to match pin #'s
    81		@pinvals = reverse(@pinvals);
    82		#value of pin is index of $pinvals
    83		$pinval = $pinvals[$ind]; 
    84		return $pinval; 
    85	}#end read_pin
    86	1;
    87	__END__

Recognized on!

My latest cpan modules were mentioned in a recent article on! It has been very rewarding to be able to contribute to cpan after so many years as a consumer. It is even more awesome to have some of my analog to digital converter modules (PINE64::MCP300x & PINE64::MCP3208) mentioned in the hardware section of this recent blog post.

PINE64::MCP300x CPAN Module

My MCP300x module on CPAN for the PINE64A+ boards

One of the drawbacks, in my opinion, of the raspberry pi and the pine64 A+ is the lack of any analog inputs. To read an analog signal requires an external analog to digital converter chip, and will tie up several of your GPIO lines. My new module has routines to read values on an MCP3004 or MCP3008 10-bit analog to digital converter.

Here is some sample code ->

use PINE64::MCP300x; 
my $adc = PINE64::MCP300x->new(10,12,11,13); 
#5 bits because the first is the start bit
my @ch0 = (1,1,0,0,0); 
for(my $s=0;$s<200;$s++){
my ($reading, $binval, $voltage ) = $adc->read300x(\@ch0, 50, 5.01);
$voltage = sprintf("%.3f", $voltage);
print "binval: $binval\tvoltage: $voltage vdc\n";
usleep(500000);}#end for

Here is the output of the script ->


I have had another cpan module published! This one is a driver for a MAX7219 8-digit LED display on the PINE64 single board computer. The video above is a demo of some of the module’s capabilities. It is implemented as bit-banged SPI using the perl programming language.

A very easy to use display, it only uses 3 GPIO pins, and could be used to output the value of sensors, or simple menus for projects.

PINE64::GPIO module published on CPAN

my first perl module published on CPAN

One of my personal goals this year was to publish some of my work that uses perl to experiment with single board computers on CPAN (the Comprehensive Perl Archive Network). I have used perl to make windows applications, database applications, web applications, text processing utilities, inventions….. you name it. However, it is not widely used for single board computers like the raspberry pi. There are a lot of reasons for this. Perl is no longer the cool programming language say, like Python is. Programming in perl is almost an anachronism and dates you. It is relatively slow. Even so, perl is incredibly versatile and powerful and has a devoted following of some of the most brilliant people. Perl isn’t going anywhere.

I have been a consumer of CPAN for nearly 10 years. CPAN is a code repository where programmers share their code so you don’t have to re-invent the wheel in your programs if someone else has already figured out how to implement what you are trying to do.

Steve Bertrand and others have published a ton of useful modules for the raspberry pi family of SBC’s on CPAN. Dude even published a book on indiegogo on pi projects using perl. I am not near as smart as Mr. Bertrand, so I wanted to stay away from publishing my pi modules on CPAN. The idea is to do something that hasn’t been done before. No one like in the whole world seems to be using perl on the Pine64 board (if you google, I am virtually the only person doing it), so I decided to post my modules for the PineA64+.

To publish on CPAN, you have to have a PAUSE account (Perl Authors Upload Server). You have to be ‘in the club’. You basically have to apply for membership stating what you intend to offer. I also gave them the address of this site so they could see some of my work. I didn’t hear anything back for nearly six weeks. I assumed they looked at my projects and declined to grant me access. I finally did get an account and proceeded to package my modules to their requirements and uploaded my first module: a script that controls the GPIO pins on the PineA64 board’s Pi-2 bus. I consider it a tremendous honor to have been accepted.

I have a technology degree with some graduate work in computer science. I work in telecom, however, not as a programmer. Programming, though, is my secret weapon. Paired with the knowledge of electronics hardware and fabrication, I can create just about anything I can think up. It is a thrill to finally get published on CPAN. I will have many more modules for the PineA64 to follow for various sensors, analog to digital converters, displays, etc.

network scanner with perl + Tk

perl Tk ip scanner application
perl Tk ip scanner application

I enhanced my command line ip scanner by adding a Tk gui and adding a few more features.  Above, I am scanning a range of contiguous addresses on my home network.

It can also accept a list of hosts from a csv file.

list of hosts in csv format
list of hosts in csv format

Here are the results from the scan of the above list.

perl Tk ip scanner results using csv file input
perl Tk ip scanner results using csv file input

I am forced against my will to use Windows 10 on my PC at work.  I also lack admin privileges, so I cannot install a free ip scanner application.  I do, however, have ActivePerl installed on my box, and it has tons of modules so I can write my own utilities.

I have several classes of ip devices (routers, switches, battery chargers, etc.) that I want to monitor and have created individual csv files for each.  This simple script is highly effective at discovering devices on a subnet, or to give a quick glance at what devices on a list are online.

     1	#!/usr/bin/perl -w
     2	use strict;
     3	use Tkx;
     4	use Tkx::Scrolled;
     5	use Net::Ping;
     6	use Time::HiRes qw(gettimeofday);
     7	#main window
     8	my $mw = Tkx::widget->new(".");
     9	$mw->g_wm_title("nnmap network scanner");
    10	$mw->g_wm_geometry("800x600");
    11	#global vars
    12	my $ip_range = '';
    13	my $tdelay = 250; 
    14	#create ping object
    15	my $p = Net::Ping->new('icmp');
    16	#hi res time
    17	$p->hires();
    18	#a single ip address
    19	my $host;
    20	#up / down devices
    21	my @online_devices; 
    22	my @offline_devices; 
    23	#main content frame
    24	my $mfrm = $mw->new_ttk__frame(-padding => "5 10");
    25	$mfrm->g_grid(-column => 1, -row => 0, -sticky => "news");
    26	$mfrm->new_ttk__label(-text => "ip range")->g_grid(-column => 1, -row => 1, -sticky => "nw", -padx => 3, -pady => 1);
    27	$mfrm->new_ttk__entry(-textvariable => \$ip_range, -width => 24)->g_grid(-column => 1, -row => 2, -sticky => "nw", -padx => 3, -pady => 1);
    28	$mfrm->new_ttk__label(-text => "delay msec")->g_grid(-column => 1, -row => 3, -sticky => "nw", -padx => 3, -pady => 1);
    29	$mfrm->new_ttk__spinbox(-from => 1, -to => 900, -width => 5, -textvariable => \$tdelay)->g_grid(-column => 1, -row => 4, -sticky => "nw", -padx => 1, -pady => 2);
    30	$mfrm->new_ttk__button(-text => "scan", -command => sub {scan_net($ip_range);})->g_grid(-column => 1, -row => 5, -sticky => "nw", -padx => 1, -pady => 2);
    31	my $ta = $mfrm->new_tkx_Scrolled(
    32	    'text',
    33	    -scrollbars => "e",
    34	    -width => 70,
    35	    -height => 33,
    36	    -state => "normal"
    37	);
    38	$ta->g_grid(-column => 2, -row => 1, -sticky => "e", -padx => 5, -pady => 5, -rowspan => 50);
    39	#colors
    40	$ta->tag_configure("success", -foreground=>"white", -background=>"green");
    41	$ta->tag_configure("failure", -foreground=>"white", -background=>"red");
    42	#fonts
    43	$ta->tag_configure("lgtxt", -font =>"r18", -relief=>"raised", -underline=>1);
    44	sub scan_net{
    45		my $func_name = (caller(0))[3];
    46		print "Called $func_name on line ". __LINE__."\n";
    48		print "ip range: $ip_range\n";
    49		#parse ip range
    50		#only for class c or smaller subnets
    51		#?'s in regex account for a single ip address to scan
    52		$ip_range =~ /(\d+\.\d+\.\d+)\.(\d+)-?(\d+)?/;
    53		my $network = $1;
    54		my $start_ip = $2;
    55		my $end_ip = $3;
    57		print "end_ip: $end_ip\tstart ip: $start_ip\n";
    58		#account for just one ip to scan
    59		if($end_ip eq ""){
    60			$end_ip = $start_ip; 
    61		}#end if
    62		#convert time delay
    63		my $ping_delay = $tdelay * 0.001;
    64		#empty text area
    65		$ta->delete("1.0", "end");
    66		#get t0 for benchmark
    67		my $t0 = gettimeofday();
    68		#line counter
    69		my $line_n = 1; 
    70		for(my $i=$start_ip;$i<=$end_ip;$i++) { 71 $host = "$network.$i"; 72 print "scanning host: $host\n"; 73 #list context, returns duration 74 my ($ret, $dur, $ip) = $p->ping($host, $ping_delay);
    76			#format time
    77			$dur = sprintf("%.6f", $dur); 	
    78			#results
    79			if($ret){
    80				#print WHITE ON_GREEN "$host is up  latency: $dur seconds", RESET;
    81				#form response string
    82				my $response = "$host is up  $dur seconds";
    83				my $res_len = length($response); 
    84				if($res_len < 70){#fill up ta rows with color
    85					my $n_sp = 70 - $res_len; 
    86					for(my $x=0;$x<$n_sp;$x++){ 87 $response = $response." "; 88 }#end for add sp 89 }#end if 90 $response = $response."\n"; 91 #print "res_len: $res_len\n"; 92 #print "\n"; 93 $ta->insert("$line_n.0", $response);
    94				$ta->tag_add("success", "$line_n.0", "$line_n.0 lineend");
    95				push @online_devices, $host;
    96			}#end if
    97			else{
    98				my $response = "$host down";
    99				my $res_len = length($response); 
   100				if($res_len < 70){
   101					my $n_sp = 70 - $res_len; 
   102					for(my $x=0;$x<$n_sp;$x++){ 103 $response = $response." "; 104 }#end for add sp 105 }#end if 106 $response = $response."\n"; 107 #print "res_len: $res_len\n"; 108 #print WHITE ON_RED "$host down", RESET; 109 #print "\n"; 110 $ta->insert("$line_n.0", $response);
   111				$ta->tag_add("failure", "$line_n.0", "$line_n.0 lineend");
   112				push @offline_devices, $host;
   113			}#end else		
   114			#increment line number
   115			$line_n++;
   117		}#end for
   118		#results
   119		#benchmarking results
   120		my $t1 = gettimeofday();
   121		my $elapsed = $t1 - $t0; 
   122		$elapsed = sprintf("%6f", $elapsed);
   123		print "\ntime elapsed: $elapsed....\n";
   124		my $up_sz = @online_devices;
   125		my $down_sz = @offline_devices;
   126		print "$up_sz devices online\n$down_sz devices offline\n";
   127		#form results
   128		my $results = "Results\ntime elapsed: $elapsed\n\n$up_sz devices online\n$down_sz devices offline\n";
   130		#insert results
   131		$ta->insert("$line_n.0", $results);
   132		$ta->tag_add("lgtxt", "$line_n.0", "$line_n.0 lineend");
   133	}#end scan_net
   134	sub openCSV{
   135		my $func_name = (caller(0))[3];
   136		print "Called $func_name on line ". __LINE__."\n";
   137		my $fn = Tkx::tk___getOpenFile();
   138		print "FILE: $fn\n";
   139		#open csv file
   140		open HF, $fn, or die $!;
   141		#empty text area
   142		$ta->delete("1.0", "end");
   143		#ping delay
   144		my $ping_delay = $tdelay*0.001;
   145		#get t0 for benchmark
   146		my $t0 = gettimeofday();
   147		#line number counter
   148		my $line_n = 1;
   149		while(){
   150			my $ipdevice = $_;
   151			my @ipdev = split(',', $ipdevice); 
   152			my $description = $ipdev[1]; 
   153			$host = $ipdev[2]; 		
   154			$host =~ s/\s+//;
   155			$description =~ s/\n//;
   157			#list context, returns duration
   158			my ($ret, $dur, $ip) = $p->ping($host, $ping_delay);
   159			print "scanning host: $host\n";
   161			#format time
   162			$dur = sprintf("%.6f", $dur); 	
   164			my $host_desc = $host."\t\t".$description;
   166			#results
   167			if($ret){
   168				#print WHITE ON_GREEN "$host $description is up  latency: $dur seconds", RESET;
   169				#print "\n";
   170				my $response = "$host $description is up  $dur sec";
   171				my $res_len = length($response); 
   172				if($res_len < 70){#fill up ta rows with color
   173					my $n_sp = 70 - $res_len; 
   174					for(my $x=0;$x<$n_sp;$x++){ 175 $response = $response." "; 176 }#end for add sp 177 }#end if 178 $response = $response."\n"; 179 $ta->insert("$line_n.0", $response);
   180				$ta->tag_add("success", "$line_n.0", "$line_n.0 lineend");
   181				push @online_devices, $host_desc;
   182			}#end if
   183			else{
   184				#print WHITE ON_RED "$host $description down", RESET;
   185				#print "\n";
   186				push @offline_devices, $host_desc;
   187				my $response = "$host $description down";
   188				my $res_len = length($response); 
   189				if($res_len < 70){#fill up ta rows with color
   190					my $n_sp = 70 - $res_len; 
   191					for(my $x=0;$x<$n_sp;$x++){ 192 $response = $response." "; 193 }#end for add sp 194 }#end if 195 $response = $response."\n"; 196 $ta->insert("$line_n.0", $response);
   197				$ta->tag_add("failure", "$line_n.0", "$line_n.0 lineend");
   198			}#end else
   199			$line_n++;
   200		}#end while
   202		close HF; 
   203		#results
   204		#benchmarking results
   205		my $t1 = gettimeofday();
   206		my $elapsed = $t1 - $t0; 
   207		$elapsed = sprintf("%6f", $elapsed);
   208		print "\ntime elapsed: $elapsed....\n";
   209		my $up_sz = @online_devices;
   210		my $down_sz = @offline_devices;
   211		print "$up_sz devices online\n$down_sz devices offline\n";
   212		#form results
   213		my $results = "Results\ntime elapsed: $elapsed\n\n$up_sz devices online\n$down_sz devices offline\n";
   215		#insert results
   216		$ta->insert("$line_n.0", $results);
   217		$ta->tag_add("lgtxt", "$line_n.0", "$line_n.0 lineend");
   218	}#end openCSV
   219	#menu
   220	Tkx::option_add("*tearOff", 0);
   221	$mw->configure(-menu => mk_menu($mw));
   222	sub mk_menu{
   223		my $mw = shift;
   224		my $menu = $mw->new_menu();
   225		my $file = $menu->new_menu( -tearoff => 0);
   226		$menu->add_cascade(
   227			-label => "File", 
   228			-underline => 0, 
   229			-menu => $file
   230		);
   231		$file->add_command(
   232			-label => "Open CSV File", 
   233			-underline => 0, 
   234			-command => sub {openCSV();}
   235		);
   236	          $file->add_command(
   237	              -label   => "Exit",
   238	              -underline => 1,
   239	              -command => [\&Tkx::destroy, $mw],
   240	          );
   241		return $menu;
   242	}#end mk_menu
   243	Tkx::MainLoop();

This is one of those utilities of mine that will probably continue to grow in complexity.

Fret Not! guitar fretboard tutor program

I got a nice acoustic guitar for Christmas.  I haven’t played in years, really since my eldest son was born 17 years ago.  At the time I was really into Mississippi River Delta Blues, and finger-style playing in alternate tunings.  I mostly read tablature.  I knew some pentatonic shapes to play up and down the fretboard, but not the actual note names I was playing.  This time around,  I want to learn how to read notes on a staff, and that means knowing the fretboard unconsciously.  To help,  I wrote a very simple perl script that I call ‘Fret Not!’

Fret Not! guitar fretboard tutoring perl script
Fret Not! guitar fretboard tutoring perl script teaches notes on the fretboard

It uses the ascii art program figlet for some simple command line graphics.  It basically is an endless loop asking you to name the note on a particular string at a particular fret.  It really is helping me to memorize the note positions on a fretboard.

     1	#!/usr/bin/perl -w
     2	#use Data::Dumper;
     3	use strict;
     4	$| = 1;	#flush output
     5	#display heading
     6	print "#####################################################################################\n";
     7	system('figlet "FretNot!" -c');
     8	print "#####################################################################################\n";
     9	#guitar string arrays
    10	my @e1 = ('E','F','F#','G','G#','A','A#','B','C','C#','D','D#','E');
    11	my @a = ('A','A#','B','C','C#','D','D#','E','F','F#','G','G#','A');
    12	my @d = ('D','D#','E','F','F#','G','G#','A','A#','B','C','C#','D');
    13	my @g = ('G','G#','A','A#','B','C','C#','D','D#','E','F','F#','G');
    14	my @b = ('B','C','C#','D','D#','E','F','F#','G','G#','A','A#','B');
    15	my @e2 = ('E','F','F#','G','G#','A','A#','B','C','C#','D','D#','E');
    16	#hashref of all strings
    17	my $stringref = [\@e1, \@a, \@d, \@g, \@b, \@e2];
    18	#testing; worked
    19	#print Dumper($stringref);
    20	for(;;){
    21		my $str_num = 1 + int rand(6);
    22		my $fret_num = 1 + int rand(12);
    23		print "What is the note on string $str_num at fret $fret_num ?:";
    24		chomp(my $answer = );
    25		$str_num-=1;
    26		#$fret_num-=1;
    27		my $note = ${$stringref}[$str_num][$fret_num]; 
    28		print "note: $note\tans: $answer\n";
    29		if($note eq $answer){
    30			system('figlet "correct!"');
    31		}#end if
    32		else{
    33			system('figlet "LOSER!"');
    34		}#end else
    35	}#end for

As you can see, I created a data structure in the form of a reference of array references.  It randomly chooses a string at a particular fret.  A mere 35 lines of perl, but very effective for it’s purpose.  To use this,  you will have to install figlet on your linux box.

ssh to a raspberry pi zero over usb

connecting to a rapsberry pi zero via usb
connecting to a raspsberry pi zero via usb

I am working on a data logging project based on the raspberry pi zero.  NOT a pi zero w,  just the pi zero.  Adding software packages to the pi zero is a little more difficult, because it has no wifi or ethernet connection.  I have been taking the sd card out ant putting it in a pi 3b+ to get packages that I need then putting it back in the pi zero.  I have tried and failed to use minicom and zmodem to transfer deb files over UART.

I found this great tutorial that explains how to ssh into a pi via the usb port on the pi.  I did everything it said and was still not able to connect to the pi.  I did an ifconfig on the pi, and saw that it had a APIPA on it’s usb network interface like so:

usb network interface on pi zero
usb network interface on pi zero

I had a usb network interface appear on my laptop, but no ipv4 address:

usb network interface on ubuntu machine
usb network interface on ubuntu machine

I finally got it to work by issuing an ifconfig command to my laptop’s usb interface and assigning an IP address in the same class B subnet :

ifconfig on usb network interface APIPA
ifconfig on usb network interface APIPA

Another benefit to a standard pi zero having ip connectivity via usb is that you can use piscope for troubleshooting.  Here is piscope examining i2c frames polling an HTU21D relative humidity sensor:

examining an HTU21D with piscope on a pi zero with usb network interface
examining an HTU21D with piscope on a pi zero with usb network interface

dht22 indoor use only

In my empirical observations experimenting with a dht22 temperature / humidity sensor,  I have come to the conclusion that they are most definitely for INDOOR USE ONLY!  Any time I placed one outside, they quickly max out at 99.9% humidity,  and don’t recover until I dry them out, and place them indoors.  I have ordered a HTU21D-F sensor to replace the dht22 in my rf propagation / humidity experiment.

The dht22 does work pretty well indoors.  I took over 14K samples over a 30 hr period and got clean results.

dht22 sensor temperature and humidity graph
dht22 sensor temperature and humidity graph

The temperature is in red and the humidity in green.  For most of my data logging projects using a pi, I sore the results in CSV format.  I used perl’s CSV database functions to query results and create graphs.  This is the sql query I use on the to csv tables to get the above graph:

select data.index, data.temp, temp_humidity.humidity from data outer join temp_humidity on data.index = temp_humidity.index

piscope is totally AWESOME with perl!

this is a screenshot of piscope running on my laptop analyzing the gpio lines of a raspberry pi over IP that is running my 24-port battery load test analyzer
this is a screenshot of piscope running on my laptop analyzing the gpio lines of a raspberry pi over IP that is running my 24-port battery load test analyzer program

I would assume many makers are familiar with using a logic analyzer in conjunction with sigrok + pulseview.  I love these resources.  They allow you to analyze precisely what is happening on your digital IO pins on whatever microcontroller you are using whether it be arduino, raspberry pi, etc.  They can also analyze signals at the protocol level such as i2c and are so inexpensive every maker should be equipped with these tools.

Pulseview SPI
Pulseview SPI scan

I do not like to reinvent the wheel in most cases.  I wanted to use some dht22 temp / humidity sensors on an RF signal strength project I am still working on.  As I have mentioned in earlier posts, I chose to use the RPi::PIGPIO::Device::DHT22 module on cpan to read my sensor.  This required the pigpiod daemon to be running on the raspberry pi.  I am very impressed with PIGPIO.  It allows you to very easily read / write to a raspberry pi ‘s GPIO lines over TCP/IP.  Just think of the possibilities.

The author of PIGPIO also offers an incredible logic analyzer for the raspberry pi called piscope.  I may never use a standard logic analyzer on a pi ever again.  You can invoke piscope on any linux computer once you have installed it to analyze the gpio on a remote pi.

invoking piscope to monitor the gpio lines on a raspberry pi
invoking piscope to monitor the gpio lines on a raspberry pi

After this, launch piscope:

run piscope logic analyzer
run piscope logic analyzer

I am just beginning to experiment with piscope, but so far it is very user friendly.  This is a trace of the SDA and SCL lines on the pi reading an MCP9808 temperature sensor.

piscope logic analyzer zoomed in on the i2c lines of an MCP9808 temperature sensor
piscope logic analyzer zoomed in on the i2c lines of an MCP9808 temperature sensor

This trace was taken over the net.  I didn’t have to get out my logic analyzer and connect any test leads.  Here is a trace of a poll and response from a dht22 sensor connected to gpio 24.

piscope logic analyzer reading a dht22 via perl
piscope logic analyzer reading a dht22 via perl

You can see it go low, then the sensor sends its reading, and goes high again.  pigpiod is definitely a resource hog, but that is hardly a consideration for my uses of the pi in my projects.  I will definitely be incorporating piscope into my future projects.

piscope by default uses port 8888 on the pi you are monitoring.  Out of curiosity,  I scanned the incoming frames with tcpdump.

analyzing piscope frames on port 8888 using tcpdump
analyzing piscope frames on port 8888 using tcpdump

It sends a lot of traffic over the network.

piscope network traffic on linux mint's system monitor
piscope network traffic on linux mint’s system monitor

Here is a video of me launching piscope, and live traffic from a pi 3B+.