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.

arduino based hvac lead-lag controller

arduino lead-lag hvac control
Installed lead-lag controller
arduino lead-lag hvac control
arduino lead-lag hvac control
arduino hvac lead-lag controller
lead-lag controller using arduino nano

My office is in a telecommunications facility full of routers, radios, fiber-optic equipment etc. Reliable environmental controls are essential as this equipment will fail in extreme heat. I have two 20,000 BTU AC units in my office that run on 250VAC. I created this lead-lag controller to turn on the lead AC unit based on the temperature setting, and if the lead unit has run for six minutes without cooling to the setting, turns on the lag unit as well until the temperature setting is reached.

arduino lead-lag hvac controller
arduino lead-lag hvac controller

I have the programming down and the device built and installed. Initial tests work as designed. I am using some 10A 250VAC power relays. With the unit running on high fan speed and the compressor going the AC units draw about 5 amps; well within the rage of the relays.

the lead / lag controller turns on/off the 250VAC outlets of the air conditioner units

Here is the code ->

// include the library code:
#include <LiquidCrystal.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "RTClib.h"
RTC_DS3231 rtc;

// Sensor input pin
#define DATA_PIN 2
// How many bits to use for temperature values: 9, 10, 11 or 12
// Index of sensors connected to data pin, default: 0
#define SENSOR_INDEX 0

OneWire oneWire(DATA_PIN);
DallasTemperature sensors(&oneWire);
DeviceAddress sensorDeviceAddress;

// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 12, en = 11, d4 = 10, d5 = 9, d6 = 8, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
int sample_num = 0;

//thermostat setting; init to 72
int setting = 70; 

//heat or cool mode
char mode = 'C';

//relay pins
const int ac1Pin = 5;
const int ac2Pin = 4;
//const int heatPin = 6;

//control buttons
const int modePin = 3;
const int decrPin = 6;
const int incrPin = 0;

//AC ON/OFF flags
int ac1_f = 0;
int ac2_f = 0;
int heat_f = 0;

//on timers
int ac1_timer = 0;
int ac2_timer = 0;

//RTC stuff
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

void setup() {
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);

  //AC1 relay
  pinMode(ac1Pin, OUTPUT); 
  pinMode(ac2Pin, OUTPUT);
  //pinMode(heatPin, OUTPUT);

  //set AC1 off
  digitalWrite(ac1Pin, LOW);
  digitalWrite(ac2Pin, LOW);
  //digitalWrite(heatPin, LOW);    

  //control buttons
  pinMode(modePin, INPUT);
  pinMode(decrPin, INPUT);
  pinMode(incrPin, INPUT);

  sensors.getAddress(sensorDeviceAddress, 0);
  sensors.setResolution(sensorDeviceAddress, SENSOR_RESOLUTION);  

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);

  if (rtc.lostPower()) {
    Serial.println("RTC lost power, lets set the time!");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));

  //array to hold 12 readings; 5-per min
  float t_readings[12];

  //sum of readings
  float sum = 0.0;

  //avg of readings
  float average = 0.0;

void loop() {
  // set the cursor to (0,0):
  lcd.setCursor(0, 0);

  //reset sum and avg on each iteration
  sum = 0;
  average = 0;

  //12 sample loop; takes 1 min
  for(int x=0;x<12; x++){
    lcd.setCursor(0, 0);
    // Measurement may take up to 750ms

    float temperatureInCelsius = sensors.getTempCByIndex(SENSOR_INDEX);
    float temperatureInFahrenheit = sensors.getTempFByIndex(SENSOR_INDEX);
    Serial.print("Temperature: ");
    Serial.print(temperatureInCelsius, 1);
    Serial.print(" Celsius, ");
    Serial.print(temperatureInFahrenheit, 1);
    Serial.print(" x: ");
    Serial.print( x );
    Serial.println(" Fahrenheit"); 

    //push reading into array
    t_readings[x] = temperatureInFahrenheit;
    //lcd.setCursor(0, 1);

    if(digitalRead(modePin) == LOW){
      Serial.println("MODE BUTTON PRESSED! ENTERING SETUP....");
    }//end if mode pressed

    //print setting to lcd row 2
    lcd.setCursor(0, 1);
    lcd.print("Setting: ");

    //print mode

    delay(5000);  //sleep 5 sec
  }//end for loop 12 sample

  //average samples
  for(int i=0; i<12; i++){
    sum = sum + t_readings[i];
  }//end for avg
    Serial.print("sum: ");
    average = sum / 12;
    Serial.print("avg: ");

    //AC relay control
    if(average > setting && mode == 'C'){
      digitalWrite(ac1Pin, HIGH);

      //flag logic
      if(ac1_f == 0){ ac1_f = 1;}

      //timer logic
      ac1_timer += 60;    //60 because of the 12 samples/min cycle
      Serial.print("AC UNIT 1 ON ");
      Serial.print(" seconds ");

      if(ac1_timer > 60){//turn on ac2 after 30 minutes
        if(ac2_f == 0){ 
          ac2_f = 1; 
          digitalWrite(ac2Pin, HIGH);
          //increment ac2_timer
          ac2_timer += 60;
        Serial.print("AC UNIT 2 ON ");
        Serial.print(" seconds ");
      }//end if turn on AC2

      //poll RTC for time
      DateTime now =;
      Serial.print(now.year(), DEC);
      Serial.print(now.month(), DEC);
      Serial.print(, DEC);
      Serial.print(" "); 
      Serial.print(" (");
      Serial.print(") ");
      Serial.print(now.hour(), DEC);
      Serial.print(now.minute(), DEC);
      Serial.println(now.second(), DEC);
    }//end if
    if(average < setting && mode == 'C'){
      //turn AC1 OFF, reset timer & flags
      digitalWrite(ac1Pin, LOW);
      ac1_f = 0;
      ac1_timer = 0;
      Serial.print("AC UNIT 1 OFF ");

      //if on, turn off AC2
      if(ac2_f == 1){
          digitalWrite(ac2Pin, LOW);
          ac2_f = 0;
          ac2_timer = 0;
          Serial.print("AC UNIT 2 OFF ");
      }//end if
      //poll RTC for time
      DateTime now =;
      Serial.print(now.year(), DEC);
      Serial.print(now.month(), DEC);
      Serial.print(, DEC);
      Serial.print(" "); 
      Serial.print(" (");
      Serial.print(") ");
      Serial.print(now.hour(), DEC);
      Serial.print(now.minute(), DEC);
      Serial.println(now.second(), DEC);      
    }//end if

void setTemp(){
  int su_flag = 1;
  int mode_flag = 1;
  Serial.println("SETUP MODE ->");
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("Set temp: ");
  while(su_flag == 1){
    if(digitalRead(decrPin) == LOW){
      lcd.setCursor(10, 0);
    }//end if exit setup
    if(digitalRead(incrPin) == LOW){
      lcd.setCursor(10, 0);
    }//end increment temp
    if(digitalRead(modePin) == LOW){
      //exit setup
      su_flag = 0;
    }//end exit setup mode
  }//end while
  lcd.setCursor(0, 0);
  lcd.print("Set mode: ");
  while(mode_flag == 1){
    if(digitalRead(decrPin) == LOW){
      mode = 'C';
      lcd.setCursor(10, 0);
    }//end if
    if(digitalRead(incrPin) == LOW){
      mode = 'H';
      lcd.setCursor(10, 0);
    }//end if heat mode
    if(digitalRead(modePin) == LOW){
      mode_flag = 0;
    }//end if exit mode set
  }//end while set mode
}//end setTemp function

Beethoven Piano Sonata No. 4 E-flat Major (Grand Sonata)

First Movement

2020 is the 250th anniversary of Beethoven’s birth. Mrinda and I were supposed to attend an all Beethoven concert celebrating this anniversary at the Germantown performing arts center, with the main event being a performance of Beethoven’s first piano concerto and Valery Kuleshov as soloist. It was cancelled due to COVID-19.

However, in honor of this important date, I wanted to learn some Beethoven sonatas that I had never worked on or even heard. I went to the IMSLP and listened to all 32 of them and found a few that I was interested in.

No. 4 in E-flat major is really fun to play. Andras Schiff has a brilliant lecture on this piece here. I found it interesting that this is the longest sonata next to the Hammerklavier. I was totally unfamiliar with this sonata, so it was a great surprise.

Toccata in E minor BWV 914

my performance of Toccata in E minor BWV 914

The Baroque toccata is one of my favorite musical forms in the way it brings out what the instrument and the performer are capable of. BWV 914 is a masculine, muscular early work of Bach’s that gives me the same healthy release of anger that an old Metallica song does. It has that youthful vigor and creativity of an emerging artist that has not yet refined his style. It definitely bears resemblance to Buxtehude’s organ toccatas, in that it contains so many movements and musical ideas in one piece. Later on, Bach settled on the two movement prelude/fuge, or the toccata/fuge.

My performance here is riddled with mistakes and timing issues. I suffer from stage fright, even when I am alone playing in front of a camera. Playing Bach makes me even more nervous. Oh well, I’m just an amateur with limited time to practice.

perl interface to 18B20 1-wire temperature sensor

raspberry pi zero interfacing with 18B20 1-wire temperature sensor

I am pretty impressed with the responsiveness and accuracy of the 1-wire 18B20 temperature sensor. This is the first 1-wire sensor I have ever used. To get it going on the pi zero, I had to enable the 1-wire protocol via raspi-config, and edit the /boot/config.txt file to tell it what pin to use. Like gpio, 1-wire has a file system interface, which makes perl a great tool to interface to 1-wire devices.

Interestingly, each 1-wire device has a unique serial number, so it is possible to have literally thousands of 1-wire devices on a single bus; certainly far more than i2c allows. Here is the command line output of a manual reading from the 18B20:

file system interface on pi zero for a 1-wire 18B20 temperature sensor
file system interface on pi zero for a 1-wire 18B20 temperature sensor

The part of the output that says t=26500 is the temperature reading in celsius i.e. 26.500 or 79.7F.

Here is my perl code for interfacing with the 18B20 ->

#!/usr/bin/perl -w
use strict;

reads a 1wire 18B20
include this in your script, 
returns fahrenheit

#my $temp = read_18b20();
#print "temp: $temp\n";

sub read_18b20{
	`ls /sys/bus/w1/devices > 1w.txt`;
	open OW, "1w.txt" or die $!;
	my $temp_sensor = '';
		if($_ =~ /(28-............)/){
			#print "serial number:  $1\n";
			$temp_sensor = $1;	
		}#end if
	}#end while
	close OW;

	my ($tempC, $tempF) = (0,0);
	`cat /sys/bus/w1/devices/$temp_sensor/w1_slave > 1w_reading.txt`;	
	open RD, "1w_reading.txt" or die $!;
		if($_ =~ /t=(\d\d)(\d\d\d)/){
			$tempC = $1.".".$2;
			#print "celsius: $tempC\n";	
		}#end if
	}#end while
	close RD;
	$tempF = $tempC * (1.8) + 32;
	#print "farenheit: $tempF\n";
	return $tempF; 
}#end read_18b20


Here is sample output from a script that samples the sensor every 2 seconds. I breathed on the 18B20 to demonstrate the responsiveness.

samples from a 1-wire 18B20 temperature sensor using perl interface
samples from a 1-wire 18B20 temperature sensor using perl interface

arduino servo example analysis

futaba s3003 arduino servo example script
futaba s3003 arduino servo example script

I admit I know little about pulse width modulation, and have limited experience with robotics or servos. I’ve had a futaba s3003 servo in my toolbox for some time, and done nothing with it. On a lazy day when I had some down time, I decided to get it out and do some experimentation.

I’ve been doing some labor intensive projects at work lately, and while hard work is very rewarding, it takes a lot of physical and mental energy and crowds out innovation. Just out of boredom, I burned the most basic servo script example provided by the arduino IDE into a nano clone I recently bought, and analyzed it with pulseview to better understand pwm.

close up of PWM controlling a futaba s3003 servo
close up of PWM controlling a futaba s3003 servo

Above is a trace of the output of the default arduino ide servo script with me tuning a 10K pot. In this example script, the position of the servo corresponds to the position of the potentiometer. Just at a glance, you can see pulses bunched up at different intervals. What I empirically observed is that when the servo was at position 1, the pulse width was about 0.55ms in duration at the standard 50Hz required by the servo. This pulse was constant, and the servo was stationary. If the pulse width changes at all, it changes position proportionally.

The futaba s3003 has 180 degrees of rotation. At position 3 (180 degrees) the constant pulse width is about 2.4msec. as you can see in the graphic below:

futaba s3003 servo at 180 degrees of rotation

This simple analysis has demystified pwm somewhat for me. Next, I hope to experiment with some pwm on the raspberry pi using perl.

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.