using an arduino as an ADC for a raspberry pi

reading a battery discharge with an arduino
discharging a 6V lead acid cell with two 50 ohm 10 watt resistors in parallel, reading the voltage with an arduino nano clone, and logging the data with a pi zero

Most everyone knows that unlike an arduino, the pi lacks an analog to digital converter. I normally use an MCP3004. These work ok for me most of the time, but I get a lot of readings that are outliers, and obviously not correct. I haven’t noticed this as much on the analog inputs on an arduino.

In this experiment (for an upcoming project) I essentially let an arduino nano clone read the voltage of a 6V lead acid battery that I was discharging with some low-value, high wattage resistors. The arduino read the voltage and printed it on it’s serial line at 9600 baud. The serial lines on the arduino were connected to the pi’s serial lines, and the pi recorded the readings by logging a screen session. First, you have to disable getty on the serial line, and then set up screen to log the session.
sudo systemctl stop serial-getty@ttyAMA0.service
sudo screen -L /dev/ttyAMA0 9600

I forgot to mention, that I used a 10K potentiometer as a voltage divider to step down the 6V so the arduino could read it.

6V battery discharging over 9 hour period under moderate load

I got a pretty good dataset with very little jitter and hardly any outliers. I think a pi and an ardino make a powerful combination. You can effectively extend all the arduino’s gpio’s, i2c, spi, etc. to the pi. You also have all the muscle and storage of the pi at your disposal for your projects.

rssi vs. relative humidity

receive signal strength indication vs. relative humidity data logger
receive signal strength indication vs. relative humidity data logger

I finally got around to deploying my rssi / temperature / relative humidity data logger and recorded nearly 6,000 samples of each metric over a five day period. This radio system operates in the ISM band, and is susceptible to propagation issues due to atmospheric conditions, especially humidity. The logic board provides a terminal that outputs a DC voltage that represents the rssi in dBm. My data logger uses a raspberry pi zero w, and reads the rssi voltage with an MCP3004 analog to digital converter using a bit-banged driver I wrote in perl. Each reading is timestamped using time derived from a DS3231 i2c real-time clock chip.

I use a HTU21 temperature / relative humidity sensor break out board from adafruit. This chip is MUCH MORE STABLE & RELIABLE than the DHT22 I was originally using.

temperature vs. relative humidity data capture
temperature vs. relative humidity data capture

For starters, here is the temperature in fahrenheit (red) vs. the relative humidity (green). Predictably, when the temperature rises, the relative humidity goes down and vice versa.

rssi level vs. relative humidity at 900MHz
rssi level vs. relative humidity at 900MHz

Now here is the relative humidity vs. the rssi. This radio is about 20 miles away from the transmitter over flat terrain with an output of 37dBm (5 watts), and has an average rssi of about -90dBm. You can see from the above graph that at about sample 4,000 when the relative humidity takes a noticeable dip that the rssi has a corresponding, albeit small, increase: precisely what we would expect to see.

Nothing surprising here. It just proves the effectiveness of my data logger device. It would be useful in situations where an RF path is going up and down due to atmospheric conditions or other obstructions, and real-time data would be useful in diagnosing problems and coming up with solutions like a higher gain antenna or increasing height.

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";
    47		
    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;
    56		
    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);
    75			
    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++;
   116		
   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";
   129		
   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//;
   156			
   157			#list context, returns duration
   158			my ($ret, $dur, $ip) = $p->ping($host, $ping_delay);
       
   159			print "scanning host: $host\n";
   160			
   161			#format time
   162			$dur = sprintf("%.6f", $dur); 	
   163			
   164			my $host_desc = $host."\t\t".$description;
   165			
   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
   201		
   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";
   214		
   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.

interface raspberry pi zero with arduino via UART

interfacing arduino with a raspberry pi zero using uart
interfacing arduino with a raspberry pi zero using uart

I wanted to use a pi zero to log data captured by a arduino nano every.  The idea was to capture a screen session and parse the data with perl regular expressions.  Initially, I simply connected the UART pins directly.  You have to criss-cross the TX / RX lines.  I got a few readings from the arduino, then screen would terminate.  I was aware the logic levels were different (5V for the arduino, 3.3V for the pi) and thought that was the issue.  I connected a logic level converter and has the same result.  The issue was that I needed to disable getty.

disable getty on raspberry pi to connect to arduino via uart
disable getty on raspberry pi to connect to arduino via uart

After disabling getty,  I was able to maintain an open session between the pi and an arduino.

pi zero connected to an arduino via uart at 9600 baud
pi zero connected to an arduino via uart at 9600 baud

This output is messy, but not at all difficult to parse with perl.  The main thing is that it does not terminate suddenly because getty is disabled.  I forgot to mention that I was ssh’d into the pi zero via USB.  See my previous posts on how to enable ethernet via usb on a pi zero.

internet access to a raspberry pi zero through usb

In my previous post, I set up the ability to ssh into a pi zero (not pi zero w) via usb.  Since I got that far,  I was pretty sure it was possible to give internet access to my pi zero through my laptop.  Turns out it is not that difficult.  First I set up a default route on the pi to send all packets not on the local subnet to my laptop:

sudo route add default gw 169.254.75.231

Now here is my routing table on the pi:

NAT router to give pi zero internet access
NAT router to give pi zero internet access

Next, I set up my laptop to essentially be a NAT router for the pi zero.  This is how to share my laptop’s internet connection with my pi:

modprobe iptable_nat
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -o enp1s0 -j MASQUERADE
iptables -A FORWARD -i enps0s18f2u1 -j ACCEPT

Now my pi zero has internet access.  I tested by pinging my favorite dns server 8.8.8.8.  However, when I try to ping my beloved employer’s domain, I get nowhere.  After I added a dns server to /etc/resolv.conf,  I am good to go:

pi zero internet access via usb
pi zero internet access via usb

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

‘Oh Love That Will Not Let Me Go’

This is me playing an instrumental of ‘Oh love that will not let me go‘ arranged by Larry Shackley from his collection, Sacred Rhapsody.  He describes it as being after the style of Chopin.  It reminds me of Chopin’s first two nocturnes from Op. 27 because of it’s unshackled right hand over the left hand’s flowing arpeggios.  A very pianistic piece, I love its use of musical effects such as the polyrhythm in the last section where the right hand is playing the melody in eights against the left hand’s triplets.

O love that will not let me go, I rest my weary soul in thee.
I give thee back the life I owe, that in thine oceans depths its flow.
May richer fuller be.

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