Cheap Logic Analyzer Examined

While experimenting with my AS3935 lightning detector, I needed my Saleae logic analyzer to view the SPI protocol to make sure I was accessing the sensor correctly.

I ended up at Saleae’s website to download new software, and, to make a long story short, I discovered that the current logical analyzer models are really expensive.

I purchased an original Saleae Logic 16 Analyzer for $299 and at the time that was kind of a stretch for my budget as I didn’t really expect to use it much (and honestly haven’t – maybe once or twice a year).

Of the current models, Saleae’s least expensive 8 line logic analyzer is $479 and the 16 line model is $1399. Yikes!

How can a hobbyist justify that price? If I were looking again, I would not be able to purchase a Saleae.

I wondered if there were any alternatives and quickly found logic analyzers for about $15 on ebay. OK, does a $15 device actually work? I wanted to know so I ordered one.

I’ve gotten into the habit of calling cheap stuff from China FC-cubed (F.C.C.C). Cheap and Chinese are 2 of the words in the acronym. I’ll let you guess the other two. Since there is no Mfg/Model info for this logical analyzer, I’ll refer to it as FC-cubed.

I ended up buying one of these on ebay for $9.89:

a week later I had it:


My intention is not to test the heck out of this logical analyzer, but to see if it works at all and if so get an idea if it would work for the light duty logic analysis I do.

My Saleae Logic 16 has 16 lines and a sample rate from 500kS/sec to 50MS/sec (the new logic 16 will sample at 500MS/sec which I’m sure is part of why it is so expensive).

The FC-cubed logic analyzer has 8 data lines and a sample rate from 25kS/sec to 24MS/sec. OK, 1/2 the sample rate and 1/2 the data lines, but 1/30th the cost.

The FC-cubed is obviously very cheap. The connectors are just cheap crappy dupont connectors that were hard to use.

BUT, it does work!


When I first started looking at these logic analyzers, I saw mention that they would work with the Saleae software. So my plan was to test with their software first, then some open source software I heard of called sigrok.

My test is VERY simple. I connected my function generator to the logic analyzer and just examined one line on the logic analyzer.

First, here is the output using Saleae’s Logic 16 logic analyzer monitoring a 100kH signal on channel 0. Exactly what I would expect:

My function generator was reporting 100.5kHz, so this is right on.

Here is what I saw when I did the same test using the FC-cubed analyzer:

Channel 0 is what I expected, but what is channel 1? My guess is it is seeing a harmonic of Channel 0.

I moved the lead to channel 3 and now I see harmonics on channel 2 and 4:

Not impressive, but you can always hide unmonitored channels so you won’t see these bogus signals.

Next I increased the function generator from a 100kHz signal to a 1MHz signal. It saw this fine:

I increased my function generator to 2MHz signal which is as fast as it will go. The FC-cubed analyzer sees it just fine:

Now going in the reverse direction, I slowed the signal down to 1kHz:

Besides trying different signal frequencies, I also verified that the trigger worked correctly.

Using sigrok/PulseView

I assume that Saleae did not intend to just give their software away for free to be run on FC-cubed logic analyzers. This is really quite unfair to Saleae. The FC-cubed manufacturers can only sell their cheap products because someone else put the time into the software side. I wouldn’t put it past these manufacturers to have simply copied firmware as well. Not saying they did …

It turns out there is another group who has been writing software to take advantage of the cheap logic analyzers. That seems a little more fair.

I only learned of this software when researching the FC-cubed logic analyzer. It is open source software that can do logic analysis for many different logic analyzers, including these dirt cheap ones.

Their website is

I downloaded and installed PulseView. Not quite as simple to use as Saleae’s software, but still it only took a few minutes to get it figured out.

Here is PulseView running against the FC-cubed logic analyzer monitoring the 1kHz signal:

Interestingly, the ghost signals do not show up in PulseView.

Protocol Decoders

The other extremely useful feature of a logic analyzer is it’s ability to decode signals like SPI. Can PulseView do that? Yep! In fact, there appear to be way more decoders for it than for Saleae’s software.

Bottom Line

For $10 you just can’t beat the FC-cubed logic analyzer. This is exactly what  is needed by hobbyists. Something cheap to get your foot in the door.

I should add that many new Oscilloscopes can decode some protocols like SPI, I2C, and serial.

Posted in c-electronics | Tagged | Leave a comment

Calibrating the AS3935 Lightning Detector

I’ve been working on the free Pascal library to manage the AS3935 lightning detector. I’ll post an entry when it is ready to go.

While in the process of working on that software, I have questioned whether the lightning detector is working or I have a software issue.

To address that problem, I decided to understand how the calibration of the unit works so I can calibrate it properly.

To calibrate, I write 0x96 to register 0x3D.

putReg(reg3D, $96);

I then look at registers 0x3A and 0x3B to determine if the calibration worked:

reg.regX := getreg(reg3A);
writeln('reg3A: 0b', inttobin(reg.regx,8));
reg.regX := getreg(reg3B);
writeln('reg3B: 0b', inttobin(reg.regx, 8));

The result indicates the calibration works fine (the 1st 2 bits of both words should be 10):

reg3A: 0b10100001
reg3B: 0b10100011

I am not programmatically testing for calibration to be done or to determine it worked correctly. I just wait 100ms and then proceed.

Once I had the lightning detector calibrated, I saw if you set the DISP_LCO bit (register 8, bit 7), the antenna’s resonance frequency would be transmitted on the INT pin.

I enabled DISP_LCO as follows:

// monitor resonance freq
reg.regX := getReg(reg8);
reg.DISP_LCO := 1;
putReg(reg8, reg.regX)

I connected a frequency counter (my multimeter has that ability) to the INT pin and saw 32.33kHz:

Nowhere near 500kHz. I was perplexed and tried all kinds of things to get a ‘proper’ value.

Per,, it turns out I am seeing the correct value. The resonance frequency is divided by LCO_FDIV in register 3. By default register 3 is 0b00 which maps to 16. 16 X 32.33 is 517kHz which is close to what we want.

The resonance frequency needs to be 500kHz +/- 3.5% or 482.5 – 517.5.  I’m within spec but just barely.

You can lower the frequency by increasing the capacitance using register 8, TUN_CAP. The default is 0. Doing a binary search on the various values for TUN_CAP, I find the decimal value of 11 produces 31.24kHz which is  .01 kHz away from perfection:

Target 31.25 kHz

Cap Register Val	Freq seen on INT pin
0			32.36
8			31.53
12			31.14
10			31.33
11			31.24

I set the capacitance value using

reg.regX    := getreg(reg8);
reg.TUN_CAP := 11;
putreg(reg8, reg.regx);

My lightning detector should now be calibrated.

Unfortunately it looks like it will be days before another lightning event.


After writing this post I found this nice write up of Tuning the AS3935 lightning detector.

Update May 9, 2022:

After making this change, I have noticed the number of ‘disturber’ events has dropped dramatically. Still waiting for an electrical storm…

Posted in c-electronics | Tagged , | Leave a comment

Writing new Boot Loader to Arduino Nano 33 IOT

Some how I have managed to corrupt the boot loader on two different Nano 33 IOTs now. At $25 a pop, I need to fix the broken ones, not buy more!

I don’t know why the Nano 33 IOT boot loader is corrupting so easily. I have only had to flash one other Arduino in the past 10 years. My best guess is I am using the Watch Dog timer and I suspect it is some how the culprit.

The process of installing a new boot loader on a Nano 33 IOT ended up being way more difficult that I would have ever expected. I’ve probably spent 8 hours between research, assembling the parts, and getting it all to run.

At this time, there are very few examples on how to do this and they don’t fully describe the process. The best procedure I found is located at Troubleshooting Guide For Arduino. He documents various methods and targets. I suspect he didn’t test the actual process between two Nano 33 IOTs because, in the end, I had to change it up a little. Not a lot, but it took a long time to figure out the issue.

The Overall Goal

I have 2 dead Nano 33 IOTs and one working Nano 33 IOT. I want to program/re-flash the boot loaders on the dead Nanos  using the working Nano.

I believe you could program a Nano 33 IOT from another 3.3V MCU such as a Teensy or a 5V MCU like a Nano using a level converter. While I have a Teensy, it just seems that adds another layer of complexity which it turns out I definitely didn’t need.

It seems like the boot loader data could be embedded in the boot programmer. There is even a version that seems to do that but the compile fails to compile.

So, the plan is use the above instructions, with some fixing, to download the boot program to a micro SD card, connect the SD card to a Nano 33 IOT, then write the boot program to the Nano that needs to be fixed.

The Parts

As I said, I already have a spare Nano 33 IOT, but it needs to be able to read from a micro SD card and I’m  going to need pogo pins to attach the programmer to the dead Nano.

The Nano 33 IOT doesn’t have an integrated micro SD card reader, so I purchased one from SparkFun:

SparkFun microSD Transflash Breakout

I used the following Sparkfun ISP pogo adapter:

SparkFun ISP Pogo Adapter

Dupont Connector or Similar

See this website for a great explanation for this connectors:

DuPont and “DuPont” connectors, and how to crimp them properly

Getting the IDE Ready

If you have not already done so, you need to go into the board manager:

and install the SAMD boards:

Setting Up the Micro SD Breakout Card

Setting the Breakout card up was pretty straight forward. As a guide, I used Data Logging With an SD Card Reader using SPI Communication.

In summary, I soldered pins to the breakout card. I connected it to the Nano 33 IOT:

You will not need the CD (card detect) pin connect for this project.

Once connected, start the Arduino IDE and open the SD cardinfo sketch:

In my schematic, you can see that CS (card select) is connected to the Nano’s pin 10. You will need to change the cardinfo sketch to use that pin:

Before running the sketch, make sure the SD card is formatted as FAT16 or FAT32 (format it on your PC) and then insert it into the Sparkfun SD card reader.

Verify the IDE’s board is set to Arduino Nano 33 IOT and the port are also set correctly. You can now upload the sketch to the Nano. Start the serial monitor and you should see roughly:

Initializing SD card...Wiring is correct and a card is present.

Card type: SDHC
Clusters: 1961984
Blocks x Cluster: 8
Total Blocks: 15695872

Volume type is: FAT32
Volume size (Kb): 7847936
Volume size (Mb): 7664
Volume size (Gb): 7.48

Files found on the card (name, date and size in bytes): 
SYSTEM~1/ 2022-05-05 15:03:32
WPSETT~1.DAT 2022-05-05 15:03:32 12
INDEXE~1 2022-05-05 15:03:32 76
FW.BIN 2022-05-05 15:30:38 6400

At this point, the Micro SD card reader is working properly.

Setting up the ISP Pogo Adapter

I followed Sparkfun’s video to assemble this and I recommend doing so as soldering the pogo pins into place is a little tricky:

To connect the ISP pogo adapter to a breadboard you will need to connect the bare wires to dupont connectors. The above resource discusses it. Further, I created a video years ago when I first tried crimping these:

How to Crimp Your Own 0.1″ Terminals to Jumper Wires

Honestly, I’ve done this enough now I’m much more skilled at this than way back in 2015, but I’ve never updated the video.

Once I had all of the pins crimped I put them into a 6 pin housing:

Proper Labeling of the ISP Pogo Adapter

This was one of the bigger problems I had. I assumed (and that is almost always  a bad thing to do) the pogo adapter and the Nano programming pins matched up properly. They DO NOT.

The bottom of the Nano looks like this:

If you look at the pin labels on the 2×3 pogo pins you will see they do not match:

Going back to the dupont connector  and housing I made that plugs into the PCB (pictured above), the proper order of the wires in the dupont housing  are:

Color      Function
Blue       ResetN
Green      N/A
Yellow     SWDIO
Orange     Gnd
Red        SWCLK
Black      Vcc

Wiring ISP Pogo Adapter to Programmer Nano

This is where the other problem occurred. The troubleshooting guide for arduino has you using the SPI pins MISO and CLK. I assumed (again) SPI protocol was being used to access the Nano to be programmed. That is apparently NOT the case and if you try to use the share SPI pins between the two devices, either the software will not see the SD card or it will not be able to access the Nano to be programmed. Instead, I use normal digital pins to access the ISP Pogo adapter (7-9).

Here is the full schematic to connect the ISP Pogo adapter to the Nano:


Here is my completed project:

Unfortunately, the only way to test this adapter is to use it.

Programming the Nano 33 IOTs Boot Loader

Per the Trouble Shooting Guide for Arduino, you need to go to SAMD boot loaders page, then go to the nano_33_iot directory, and download the .bin file:

After downloading, rename this file to fw.bin. Then copy it to the Micro SD card you will be using.

In the Arduino IDE, you need to include the library Adafruit DAP library:

When you install this library, it will tell you you need to install many other libraries as well. Let it do so!

Now open the flash_from_SD sketch (File | Examples | Adafruit DAP library | samd21 | flash_fromSD:

With flash_from_SD open, near the top of the sketch you must change the pins to match this (which matches the schematic above):

#define SD_CS 10
#define SWDIO 8
#define SWCLK 7
#define SWRST 9

Now go ahead and upload the sketch WITHOUT trying to connect the pogo adapter to the Nano you wish to fix. You will see:

Card initialized
Adafruit Generic CMSIS-DAP Adapter 123456 v0.1 (S)

Enter Reset with Extension mode... 
Target prepare...
invalid response writing to reg 0 (count = 0, value = 7)

This is what the output looks like if the sketch is unable to write to the Nano needing to be flashed.

You are Ready to Write the Boot Loader

The general process to write the boot loader is

  1. Close the Serial Monitor
  2. Press the white reset button on the programmer Nano
  3. Connect the pogo adapter to the Nano to be programmed.
  4. Open the Serial Monitor
  5. Watch for a Good session

To start the programmer, the serial monitor must be closed, you must press reset, then open the serial monitor again. This is somewhat clumsy because you have to hold the pogo adapter to the Nano while starting the serial monitor. If you can get a helper to start the serial monitor, you may find this process easier.

Once you have closed the serial monitor and pressed the reset button, you need to connect the pogo pins to the pads on the bottom of the Nano to be programmed.

Here is how the pogo adapter is positioned on the Nano:

First, the pads on the nano are slightly recessed into the PCB, so the pogo pins will not slip off if light pressure is applied.

Second, notice the slight angle I use. After many failures trying to get this to work in a vertical position, I have found a slight angle allows me to see if the pins are where they need to be. As long as the pressure is light, the pogo pins will stay on the pads.

You can wiggle the pogo adapter just a bit to get all pins to seat themselves onto the pads.

Once you have the pogo pins aligned on the Nano to be programmed, use your free hand to start the serial monitor.

If the programming works, it will take less than 10 seconds and you will see:

Card initialized
Adafruit Generic CMSIS-DAP Adapter 123456 v0.1 (S)

Enter Reset with Extension mode... 
Target prepare...
Found Target: SAM D21G18A (Rev D) Flash size: 262144 Flash pages: 4096
Erasing... done.
Programming... 135


If you don’t have a good connection you will see the prior message,

invalid response writing to reg 0 (count = 0, value = 7)

Even after doing this process many times, it still takes me 5-10 attempts before I’ve got the pogo pins exactly where they need to go and I have to repeat the procedure.

Verification of Programming

The way I verify the programming is complete is to connect the programmed Nano to a windows PC, verify in device manager that the COM port shows up, then write the blink sketch.


This process is ridiculously complicated and error prone. I cannot believe there isn’t a better solution, especially given how easy it has been for me to corrupt the boot loader on this particular Arduino model.


Posted in c-arduino | Tagged , | Leave a comment

The Classic HP 16C Programmer’s Calculator

In the early 1980’s I purchased an HP 16C Programmer’s Calculator:

As a programmer of an HP3000 mini computer, I found myself needing to use this calculator constantly. It had all the bit operations I ever needed: AND, OR, NOT, Shifts, Rotates, complements, etc.

It was also programmable. Which turned out to be very useful when dealing with the HP3000. The HP3000 used octal for displaying bits and all of the docs were written using octal.

Octal made sense on machines that had a word size that was a multiple of 3 bits such as the Dec-10 (36 bits).  On those machines, instead of 8 bit ASCII, characters were often represented as sixbit. In the Dec-10’s COBOL,  sixbit (display-6) was super efficient, allowing 6 characters per word and was the default USAGE for displayed data:

Using octal on machines using 8 bit bytes is just a pain in the butt! On a ‘modern’ 16 bit machine, we would use Hex to represent the contents of a word. Each Hex digit is 4 bits of the word. A memory location of hex 0x4142 which represents the character string ‘AB’.  You can easily see 0x4 is the ‘A’ and ox42 is the ‘B’.

Because the HP3000 used octal, instead of seeing 0x4142 you would see 040502 (a leading zero is the C standard for octal literals). You cannot just look at 040502 and know what is in each byte.

Here is the ASCII Character Set from the HP3000’s MPE Software Pocket Guide. There is no reference of Hex at all:

If you look at the second page, for the letter ‘A’ in the first byte, its Octal value is 040400. The letter ‘B’ in the second byte is 0101. Added together that is 040502. Using this table was how you were expected to read an ASCII string out of an octal dump. Ugh!

So I had a program in the HP 16C that would take the octal value 040502 as input and then show the octal bytes of 0101 and 0102.

Towards the end of the HP3000’s lifespan, when it started supporting POSIX, HP started making it easier to use HEX, but I was in the networking world by then.

Texas Instruments had a calculator called the TI Programmer which competed against the HP 16C. It was less expensive but had nowhere near the same capabilities.

Sadly, after the short HP 16C run, HP never made another programmer’s calculator.

Once I moved off into networking my HP 16C was put in a drawer to almost never be seen again. For networking, and most of my programming  needs while networking, the Window’s calculator’s programmer mode was all I really ever needed. So my HP 16C has sat in a safe and clean location for 30 years. Except for a small ding at the top, it is still perfect.

I find nice versions of it are going for $300 on ebay! Although I rarely use this calculator I am not ready to sell it – once in a blue moon it is still useful to me. Plus I have almost nothing left from the 80’s.

While thinking about this calculator, I wondered if someone might have created a phone app emulation of it. I know I looked for a Windows version years ago but didn’t find anything I liked.

Sure enough there is a very nice android phone emulation:

I played around with this for an evening and it seems to do everything though I didn’t try writing a program for it. I don’t need to mess with octal these days!

You can find more information about this emulation at

The author even has a web version of it.

If you are interested in playing with this, the original manual for the HP 16C can be found at

If you find yourself needing to do a lot of bit manipulation, I heartily recommend having a look at this emulation. It is much more enjoyable to use then the Microsoft Calc program.

Posted in c-Misc | Tagged | Leave a comment

Using an AS3935 Lightning Detector on Raspberry Pi with Free Pascal / Lazarus

This time last year I added an Air Quality monitor to my weather station. This year I decided to add a lightning detector after seeing the Hacker Space post An Introduction To Storm Detector Modules.

I purchased this AS3935 Lightning Sensor in breakout board form from Sparkfun:

SparkFun Lightning Detector – AS3935

After receiving this and looking at their docs, I found they don’t implement I2C access, just SPI and most of the examples I found used I2C. Digging in a little deeper, SparkFun says they don’t support I2C (though there is a way to get it running) because it proved to be unreliable which was born out by several other posts I read on the interwebs. Honestly, that’s fine by me, I don’t care which protocol I use and since I’m going to do this in Pascal, examples will only be somewhat helpful at best.

I’m running this project on an old first Gen Raspberry Pi B. I’m doing that because the weather station is running that same hardware.

Personally, I’d like to upgrade the weather station hardware, but the website for WVIEW is long gone and I didn’t keep a backup of the install. So it’s just easier not to break what I have.

Getting the Raspberry Pi Ready

I pulled out my old Raspberry Pi Model B, and then  I installed the latest version of Raspian, er, I guess now it is called Raspberry Pi OS, found at

This was the first time I used the new Raspberry Pi Imager and I liked it – I was able to preconfig the system to boot up with SSH so I could use it immediately as a headless server.

Once I had the basic RPI up and running I needed to get SPI and GPIO setup so I can access it. I looked at these instructions:

The important part of the instructions for my needs are:

  • Get latest version of wiringPi installed (in was not installed at all for my fresh OS)
  • use raspi-config to enable SPI for user access.
  • Assign your user (pi?) to the groups spi and gpio so your program will work w/o sudo.

I extended the RPI’s GPIO pins to a breadboard using an old Raspberry Pi Cobbler for it’s 26 pins. New RPI’s and cobblers are 40 pins. It is my understanding that all of the GPIO #’s I use will remain compatible between the 2 types of RPIs so you should be fine if using a newer RPI.

I wired the AS3935 module to the Raspberry Pi as follows:

Note that I am calling pin 11 GPIO0, NOT GPIO17 as you will see on many pinouts. This is because that is what the wiringPi module calls that pin. Here are the pin names as far as wiringPi is concerned

Finally I connected my Saleae logic analyzer to all of the pins so I could monitor what was happening since, in the end, I’m going to be doing some whacky stuff. Fortunately I got my 16 port analyzer in the very early days of Saleae when it was a mere $300. Now it is $1400 😦 Despite the price increase this is a great product. Easy to use!

Also, many oscilloscopes now have the ability to decode SPI now.

Testing the Setup

Now that I have all of the wiring in place, can I access the AS3935?

I found a guy, the administrator at the Weather Watch Forum, was trying to do the same as I and he wrote up some instructions on how to do initial testing of the device:

He uses a utility called spidev_test to test SPI access to the AS3935. Upon further research, I find that the offical spidev_test is available here:

BUT it works differently. I got it working but it was much harder to use then the one at Weather Watcher Forum, so I used his using his instructions.

Download and compile his version of as3935_spidev_test. When you run it you will see about this:

./as3935_spidev_test -H -D /dev/spidev0.0 0x40,0,0,0,0,0,0,0,0,0
40 00 00 00 00 00 00 00 00 00
00 24 22 C2 00 00 00 00 3F 00

The first line is the bytes transmitted and the 2nd line are the bytes received. The first byte of the first line is the command to read register 0. The remaining 0’s of that line simply provide a clock signal to the AS3935 so it will transmit the results.

The 2nd line contains the registers. The first byte is a filler, but the rest match registers 0 thru 8. Registers 0-2, 24, 22, C2 should match exact. The rest may or may not.

If you’ve got a logic analyzer running you can see the results with it as well. NOTE: I had to change my logical analyzer to read data on the trailing edge of the clock (CPHA=1). That turns out being critical to know further on.

At this point, I know the RPI is wired correctly to the AS3935 and I can move on to writing software.

Writing a Simple C Test Program

I have to non-standard goals I’m trying to accomplish: access the AS3935 on a RPI AND access it with Free Pascal. It would be much easier to solve each problem separately, so first I will write a simple test program in C to make sure I can access the AS3935 directly using wiringPi on the RPI.

It is a good thing I started with C because, frankly, it did not go well right off the bat. In fact I got so frustrated I wasn’t sure the AS3935 was working right.

I ended up connecting it to a Teensy (another Arduino-like MCU) and running the arduino example code supplied with the Sparkfun library for their AS3995 breakout board. That worked fine so I realized I must be doing something stupid and I returned to the Raspberry Pi.

I ended up with this test program:

#include <stdio.h>
#include <wiringPi.h>
#include <stdlib.h>
#include <unistd.h>
#include <wiringPiSPI.h>

int                     lastState;

int main(
    ) {

unsigned char           buffer[100];
int                     fd;
int                     result;
int                     state;

if (wiringPiSetup() == -1) {
    printf("wiringPiSetup failed!\n");
    return 1;

fd = wiringPiSPISetupMode(0, 2000000, 1); // mode=1 specifies CPOL=0 CPHA=1 which is what device needs
printf("wiringPiSPISetup result: %d\n", fd);

printf("doing pinMode.\n");
pinMode(0, INPUT);

lastState = LOW;
printf("Starting loop with lastState = LOW\n");

while (TRUE) {
    state = digitalRead(0);
    if (state  != lastState) {
        if (state == LOW)
            printf("state now LOW\n");
        else {
            printf("state now HIGH\n");
            buffer[0] = 0x43;
            buffer[1] = 0x00;
            printf("sendind buf[0]: 0x%1x\n", (unsigned) buffer[0]);
            result= wiringPiSPIDataRW(0, buffer, 2);
            printf("wiringPiSPIDataRW result: %d\n", result);
            printf("rx'ed buf[0]: 0x%1x\n", (unsigned) buffer[0]);
            printf("rx'ed buf[1]: 0x%1x\n", (unsigned) buffer[1]);
            printf("digitalread: %d\n", digitalRead(0));
    } // while
} // main

Generating Lightning

Before getting into the test program, I need to backup just a bit. I needed a way to generate lightning. The closest thing I’ve been able to find so far is a propane grill piezo ignitor like this:

I paid too much for this at the local Ace hardware store (in the Grill Parts). For 1/2 the price I could have got 2 on Amazon, but I needed, er wanted,  it immediately.

The truth is this rarely generates a lightning interrupt on the AS3935. The AS3935 is smart enough to know it isn’t lightning. BUT the AS3935 will generate an interrupt with the disturber bit set and that is reasonable for my purposes.

A word of warning: I have repeatedly generated a spark and had the Raspberry Pi crash. I now try to keep the RPI as far from the AS3935 as I can and I don’t get the spark any closer to the AS3935 than necessary to get an interrupt.

Returning to the Regularly Scheduled Program

Here is some commentary on the program:

There are 2 calls to setup procedures. This one is the setup procedure in wiringPi that will be used for digitalread.

if (wiringPiSetup() == -1) {

This is the second call which is to setup wiringPiSPI.

fd = wiringPiSPISetupMode(0, 2000000, 1); // mode=1 specifies CPOL=0

In parameter one,  I indicate I’m using SPI channel 0. Channel 0 maps to the MOSI/MISO pins I have wired.

The 2000000 for parameter 2 indicates to use 2MHz. You must NOT use 500,000: that is the resonant frequency of the detector’s antenna and will cause false positives. Later in the project I expect I will lower this value because the wire from the sensor to the RPI is going to be several feet long.

The 1 for parameter 3 indicates the SPI mode. The default is 0 and this gave me a lot of trouble. Turns out, the mode contains the CPOL and CPHA bits. 1 indicates CPOL=0 and CPHA=1. Recall above I said I had to change my logic analyzer to CPHA=1 so it could read the data. Initially I had CPHA set to 0 so neither the logic analyzer nor the AS3935 could read the SPI commands.

Here, I assign GPIO0 (again, using wiringPi’s GPIO numbering) as an INPUT pin.

pinMode(0, INPUT);

I then start an infinite loop to read GPIO0 pin to see if it has changed.

while (TRUE) {
    state = digitalRead(0);
    if (state != lastState) {

If the state changes to low, then the AS3935 has dropped it’s interrupt and we do nothing.

if (state == LOW)
    printf("state now LOW\n");

But if the state changes to high I’m going to want to read register 3:

else {
    printf("state now HIGH\n");
    buffer[0] = 0x43;
    buffer[1] = 0x00;
    result= wiringPiSPIDataRW(0, buffer, 2);

This is where SPI gets a little weird. I’m going to put the register read into buffer location 0. But I’m also going to put a 0 into buffer location 1. Finally, I transmit 2 bytes of the buffer.

If you recall from using as3935_spidev_test, when I xmitted 0x43 on SPI, I received 0x00, then I transmitted ox00 and got back the first ox00 and then register 0 which contained ox24. Transmitting that extra 0x00 keeps the clock running so the device will send the contents of the register.

Tx:     0x43
Rx:     0x00
Tx:     0x00
Rx:     <contents of Register 3>

Finally, I save the new state and restart the while loop:


Creating a new wiringPi and wiringPiSPI for Free Pascal

To use Free Pascal with wiringPi you need a wrapper for the wiringPi.h C include file. The wrapper causes Pascal to call the C routines properly as well as define constants and structures in Pascal syntax.

In the past I have used the hwiringPi unit  which can be found at

The github version was last modified in 2015, I know there have been updates to the underlying wiringPi since. Plus I need wiringPiSPI as well which is not present in the old Lazarus hwiringPi at all.

This led me to learn how to use h2pas (included with Lazarus / Free Pascal). This utility will convert a C include file into a Pascal unit. Actually it is quite simple just use

h2pas -d <include file>

The conversion wasn’t perfect. It was unhappy with parts of the C include file that implemented threads and processes. But I don’t need that, so I deleted the code creating those errors and ended up with wiringPi.pp and wiringPiSPI.pp units. These units include all of the constants and most of the function calls found in wiringPi and wiringPiSPI.

I also include the constants GPIO_PIN0 thru GPIO_PIN7 which are the GPIO numbers as defined by wiringPi. I also included the GPIO numbers found on normal Raspberry Pi Model B pinouts: RPIO_PIN4, RPIO_PIN17, RPIO_PIN18, RPIO_PIN22, RPIO_PIN23, RPIO_PIN24, RPIO_PIN25, RPIO_PIN27.

I posted these wiringPi wrappers for Free Pascal / Lazarus at

Creating a Free Pascal / Lazarus Program

Finally I’m ready to do what I really intend: control the lightning detector from Free Pascal. I’m going to keep this simple yet. I just want to implement the C program above in Free Pascal.

If you haven’t done it already, you will want to use the Raspberry Pi’s Add/Remove Software feature to add Lazarus or Free Pascal. Though all code I’ll be writing will run at the command line, I want to use the IDE and debugging features of Lazarus so that is what I always install.

Here is my test program:

program lite;

{$mode objfpc}{$H+}

    {$IFDEF UNIX}{$IFDEF UseCThreads}

    buffer                      : array [0..99] of byte;
    fd                          : integer;
    lastState                   : integer;
    result                      : integer;
    state                       : integer;


If wiringPiSetup = -1 then begin // Setup Hardware (Gordons magic)
    writeln('wirintPiSetup failed');

fd := wiringPiSPISetupMode(0, 2000000, 1); // mode 1 specifies CPOL=0, CPHA=1
writeln('wiringPiSetup result: ', fd);

writeln('doing pinmode');
pinMode(GPIO_PIN0, INPUT);

lastState := LOW;

writeln(datetimetostr(now), ' starting loop with lastState = LOW');
while true do begin
    state := digitalRead(GPIO_PIN0);
    if state <> lastState then begin      // print state when it changes
        if state = LOW then begin
            writeln(datetimetostr(now), ' state now LOW');
        else begin
            writeln(datetimetostr(now), ' state now HIGH');
            buffer[0] := $43;
            buffer[1] := 0;
            result := wiringPiSPIdataRW(0, buffer, 2);
            writeln(datetimetostr(now), ' wiringPiSPIdataRW returned ', result);
            writeln(datetimetostr(now), ' rx''ed buf[0]: 0x', intToHex(buffer[0], 2));
            writeln(datetimetostr(now), ' rx''ed buf[1]: 0x', intToHex(buffer[1], 2));
        lastState := state;

    end; // while

The above program is exactly the same as the C program and produces the same results:

wiringPiSetup result: 4
doing pinmode
28-4-22 15:46:22 starting loop with lastState = LOW
28-4-22 15:46:29 state now HIGH
28-4-22 15:46:29 wiringPiSPIdataRW returned 2
28-4-22 15:46:29 rx'ed buf[0]: 0x00
28-4-22 15:46:29 rx'ed buf[1]: 0x04
28-4-22 15:46:29 state now LOW

A Lazarus Compile Cheat

Lazarus is a GUI IDE which requires either directly using the RPI’s keyboard and monitor or use VNC to access it remotely. My old RPI Model B has 1 CPU and I think 512M of memory. Starting Lazarus is slow and I don’t like to do that for simple programs like these.

Instead, I’ll use Lazarus to create the project (.LPR file). I then edit the .LPR file with vim. When I want to compile, I use

lazbuild -B <project>.lpi

This will compile the project at the command line. The -B option forces all units to be recompiled. You probably won’t want that most of the time.

Next Steps

I’ve proven to myself that I can access the AS3935 breakout card using Free Pascal on a Raspberry Pi. Now that I know it works, my next step will be to create a Pascal Unit (library) to allow simple easy access to many of the AS3935’s features.

Update May 20, 2022

I have decided to abandon this project. I had the AS3995 working on RPI using free Pascal. I got into trouble when I tried to create a 6′ cable to extend the lightning detector to the other side of my garage wall (my weather station RPI is inside the garage). Trying to get SPI to work on a 6′ cable is troublesome. I lowered the clock rate to the slowest I could (about 3kHz). That worked for the most part but I would still get bad data at least every 10 minutes.

While messing around trying to see if I could do something to make the cable work better I some how fried my AS3995. Though it is my normal policy to buy 2 of every component I buy (for just such an occasion), I didn’t this time I guess because they are kind of expensive. Buying a replacement and paying shipping and tax is rather spendy and I really don’t think I can get around the cable issue.

The proper way to do this, would be to connect the AS3995 to an MCU and mount both in the outside enclosure. Then use USB to communicate to the RPI. But that negates my initial plan to run this all on an RPI using free Pascal. Thus my decision to give it up.

I got wrote a free Pascal library for the AS3995 and converted the latest wiringpi.h to be accessible by free Pascal. I’ve posted that code here:

The code is at beta test reliability. I wasn’t noticing any issues but it had only been running a few weeks.

Posted in c-electronics, c-lazarus | Tagged , , , , | Leave a comment

Enabling 5G Service on Motorola One 5G Ace When All Else Fails

No one had a solution for this problem, and I stumbled across a completely unexpected solution so I’ll document it here.

I have had a Motorola One 5G Ace smart phone for about 6 months now, and I have yet to have ever seen it go into 5G mode. It is always in LTE mode. Even after traveling extensively in the South several months ago thru cities far larger than home.

I use Consumer Cellular as my carrier. I don’t use much data and their price is hard to beat. I had read somewhere that I needed a new SIM card to support 5G.

So I called Consumer Cellular to ask them if I needed a card and if so to send it. Consumer Cellular’s support has proven to be prompt and they are easy to communicate with. She told me that I do not need a special SIM card though they’d be glad to send a new one if I wanted.

So we tried various things and finally she gave up. The phone should work but doesn’t so her advice was to contact Motorola.

I started researching the problem again. Pretty much all advice was to make sure the APN settings are correct. Even though Consumer Cellular had checked the APN settings the suggested settings were different in about every case so I spent time trying several different settings. No luck.

As the frustration mounted, I noticed below the Access Point Names an Allow 2G Service setting.

Could allowing 2G service exclude 5G? Worth a shot. Turned it off and got a warning as I did so. As soon as I turned 2G service off, I saw the 5G indicator for the first time ever:

I turned 2G Service back on expecting 5G to revert to LTE. Nope, it now stays as 5G.


So I immediately did a speed test. It was no faster that LTE. Actually a little slower.


Posted in c-Misc | Tagged , | Leave a comment

Replacing RV Exhaust Vent Cover

I found myself needing to make another unscheduled RV repair whilst on site.

I opened the flap on the exhaust vent cover to use the stove top. Afterwards, I went to latch down the flap only to have it bust on the hinges and drop to the ground. Looking into the resulting hole I could see straight into the trailer – nothing to keep bugs or water out. This needed an immediate fix!

It turns out if you unscrew the bottom two screws, that will give you access to the area where the flap mounts. By doing this you can make an emergency repair using duct tape:

To make a permanent fix, I found an exact replacement on Amazon:

I also needed some RV putty, or butyl tape. I sourced this  locally so I only had to buy what I needed:

Before starting, I sprayed the cover with a UV protector and let it dry for 24 hours. The old cover yellowed horribly. I don’t know if the UV protector will help, but I doubt it will hurt.

I unscrewed the old vent cover. It took a little force to break the seal of the old tape, but once it let loose, the vent cover came off easy. After removing the old RV putty I’m ready to test fit the new cover:

Once confirming the new vent cover fit, I put RV putty on the cover. I had a heck of a time separating the putty from the paper, probably because it was quite hot outside. I got the putty into place. It was an ugly job but I think it will be water tight:

I then fit the vent cover into place and tightened the screws. I checked the seal all the way around and it appears the putty squashed nicely into place all the way around.

The completed install:


Posted in c-Misc | Tagged | Leave a comment

Insulating RV Windows

I find myself in a travel trailer for the Holidays. While the days are warm the nights are somewhat to very cold.

The trailer I’m in is a 3 season trailer and does reasonably well in cold temps, but when it is really cold out it is not toasty inside unless I want to run the furnace a lot. Possibly the largest heat lost is thru the single pane windows.

I found myself in this position several years ago and after the fact I heard that using bubble wrap on the windows will raise the R value of the glass from R1 to R2. OK, R2 doesn’t sound great, but that is still 1/2 the heat loss of R1.

On this trip, I will see if bubble wrap helps.

I found a roll of small bubble bubble wrap at Walmart like

Cut the bubble wrap to be a bit smaller than the glass. Clean the window for the best adhesion. Then spray water onto the glass and stick the smooth side of the bubble wrap to the glass.

Here is one of my completed windows

I don’t have a non-contact thermometer to measure the difference between bare glass and the bubble wrap, but just feeling the two there clearly is a difference in the amount of heat being lost.

Posted in c-Misc, Uncategorized | Tagged | Leave a comment

VeeCad / Stripboard Revisited

I’m ready to make a permanent version of my DTMF detector. I’m only going to build 2, maybe 3, of these so it’s not worth it to me to design and manufacture a PCB for the project.

Instead, I decided to build the project on stripboard, like this, using VeeCad to layout the components.

I’ve used stripboard on a few small projects in the past and documented my process here, which includes instructions on making a reaming tool:

How to use VeeCAD Stripboard Editor with CadSoft Eagle

As I reviewed my old process, I recalled that it was quite tedious to correctly ream the stripboard and then get the components into the correct position.

After doing the design, I printed it out to make sure all of the components fit the footprints I had specified:

This is when I had my ‘EUREKA’ moment. Yesterday I was cleaning out all of my photo paper and noticed I had 8×11.5 vinyl stickers I had never used. I could print the design on the vinyl, stick it on the board and use it for component placement:

Further, I could print a template for the back of the board to use to ream out the copper strips. Here, I’ve attached the template to the back of the stripboard and started reaming out the X’s:

The completed board came up on the first try!

Here are some additional notes:

  • Place X marks at the corners of the design.
  • Print front of board to vinyl sticker. Cut sticker to just beyond the X marks.
  • Holding sticker to board, align X marks to board, then create a hinge using tape.
  • Flip sticker up on hinges, remove backing, then flip back down into position – I start applying pressure at center and work out.
  • Print the back of the board on normal copy paper, and tape it to the back. Punch a paper clip thru several holes to verify both templates are aligned.
  • Take reaming tool and begin reaming out X’s on the back of the board. Pull off mask, check reams visually. Then check with continuity tester to be sure they were all reamed properly.
  • Use a paper clip to punch out holes where all leads will be placed AND the X’s to make sure residue is removed from hole. I find it easier to go down columns rather than across rows.
  • Any drilling required should be done now before adding components.
  • Solder wires, resisters, diodes, caps, other stuff… essentially in order from shortest to tallest.

Now to get it all to fit into a little box…

Posted in c-electronics | Tagged , | 6 Comments

DTMF Detector / Decoder Circuit

Note1: The FCC frowns upon you connecting devices to the telephone system (the old copper line network a.k.a. POTS). In my testing, this device is connected to a small PBX. When I’m done it will be connected to a VoIP ATA, not a copper phone line.

Note2: If you make a circuit such as this to connect to a POTS line, make sure you install the 2 specified fuses to protect the device from voltage spikes / lightning strikes.

I am working on a project where I want to press keys on my telephone and send commands via WIFI to a computer. To do that, I must be able to detect and decode the DTMF tones generated when pressing a key on the telephone handset.

(What is DTMF?)

As I started researching decoding DTMF, I found this little board does just that which I could connect to an Microcontroller Unit (MCU) quite easily. I ordered one up. This ended up coming via slow boat from Thailand. Literally. It took a good month to show up and when it did the RJ connectors are the wrong size. I assume these are for the Australian market as that is where they are sold from.

The mini-board uses an MT-8870 chip to do the decoding, so I started looking at just sourcing the chip myself. There is an Arduino library already in place, but as I continued research I found the only MT-8870 chips available are surface mount. I have very little experience in SMT and at this point in the game am better off avoiding it. I could find some old through-hole DIP MT-8870 chips on eBay.

As I contemplated the problem, it occurred to me I had once written code to emulate a 1200 baud modem using a Teensy MCU, it should be possible to decode DTMF directly on the MCU if it is fast enough.

The normal way to decode DTMF would be to use Fast Fourier Transform (FFT). FFT requires a pretty fast CPU, though. Not sure a cheap MCU would be up to it. As I continued to research, I learned of the Goertzel Algorithm. This is a specialized case of FFT which can detect individual frequencies with much less processing.

As I learned about the Goertzel algorithm, I found this very clean explanation of how it works. Unfortunately, the link to the source code is dead. After several days I finally found it here. There is also an Arduino library module of the algorithm.  These two examples gave me a good starting point for the software side.

If I was going to use an MCU to decode DTMF, I was going to need to to processes the DTMF signal properly to present it as 0-3.2 volts to the MCU’s Analog to Digital converter (ADC). Anything beyond that voltage range would destroy the MCU.

Further, I was going to have to be able to greatly attenuate the ring signal. This can be as high as 120 Volts.

Looking at the Circuit

The basis for my circuit comes from here(Marantz PMD Recorder) and here.

The full schematic of my circuit can be found as a PDF here.

Note, there is a Eagle schematic below, in Nov 3 update.

A bill of materials (BOM) can be found here.

There are 3 main parts to the circuit: the DTMF processor, the Tone Injector, and a square to sine wave converter. The square to sine wave converter is not implemented and will not be discussed.

DTMF Processor Circuit

The DTMF (and RING) signal  comes from the telephone(tip and ring). 2 fuses, F1 and F2, protect the circuit from lightning. The capacitor C1 is non-polarized and high voltage to block DC voltage.

Transformer TR1 is a normal 600:600 transformer used in telephony to isolate our circuit from the telephone network. An example of this transformer can be found here.

The values for F1, F2, C1 and TR1 are critical especially for a POTS line.

There are two ways to look at this circuit – what happens when DTMF tone occurs, and what happens when a RING signal occurs. To design it, I started by  getting the circuit to work for the DTMF tone, and then added further protection from the RING signal.

The DTMF tone starts on the phone line as a signal oscillating from 6.6 to 9.8V. When it passes thru C1 and TR1, it becomes -0.9 to +0.9V. The signal needs to be positive, so we add 3.3V to it and get 2.5 to 4.1V on the input side of C2.

C2 will block the DC component of the signal (the 3.3V we added). R2 and R3 form a voltage divider. This will center the frequency. That is, the signal crosses the X axis at 3.3V/2 or 1.65 V. At this point in the circuit, the DTMF signal can be properly read by the MCU.

The RING signal is very high voltage which will destroy the MCU. My PBX generates a -76 to +74V RING signal. D1 and D2 bleed most of the voltage off so the RING signal is between -0.7 and 7.4V on the input side of C2.

The voltage divider attenuates the RING signal a bit, but it still peaks at 4.8V – way too high. The zener diodes D3 & D4 bleed off the rest of the excess voltage. The tested voltage of the RING signal at the MCU pin is 0.9 to 2.2V.

The DTMF output signal is now MCU safe and can be connected to an ADC port on the MCU.

Tone Injector

While this post is primarily concerned with DTMF detection, I also want to notify the user  their key sequences were accepted. This is done by generating a tone with the MCU and injecting it onto the phone line using the Tone Injector circuit.

As the tone comes in, R8 and R9 create a voltage divider to attenuate the level of the tone to an appropriate value. I determined these values by first using a potentiometer and and just listening to the tone until I got a pleasing volume.

C3 simply blocks the DC voltage from where I connect this sub-circuit to the DTMF circuit.

The RING signal can make its way back from the DTMF circuit, so D5 and D6 are used to limit the voltage of any signal coming back from the DTMF circuit.

The Software Environment

For this demo, I decided to use a Teensy 3.2 MCU. For the most part, code written for an Arduino will work on a Teensy and vice versa. I have done a lot of work with Teensy’s. They are much faster than Arduinos, and I have several just laying around so I started with them.

I expect to design the final version of the project on an Arduino 33 IoT. It too is much faster than an Atmel Arduino like the Nano or Uno and it includes an integrated WIFI module.

The speed of the CPU is a major concern. The ADC is sampled 8,000 times a second. This is easy for a 72MHz Teensy. An 8MHz Arduino simply cannot sample as fast.

I did not test my code on an Arduino, but I reduced the clock speed of the Teensy to see the effects. At 24MHz, the Teensy was still sampling OK. At 16MHz it started becoming flaky. At 8MHz it did not function.

It may be possible to use this software on an 8MHz Arduino, but the sampling rate would need to be reduced. I did not experiment to see if it would work.

Since this is a new project, I downloaded and installed the latest version of the Arduino IDE (1.8.16). I then installed the latest Teensy Teensyduino (1.55) over that.

I also found I needed a few libraries which were not installed in the IDE by default. I like to keep these libraries in the code directory rather than installed into the IDE.

In these libraries are a FIFO queue handler, cppQueue, a TM1637 LED display driver, and a file of the frequencies for various musical notes.

In reviewing the few C versions of code I could find which implement the Goertzel algorithm, none did what I wanted. Because other things could be happening on the MCU besides sampling the signal, I want the sampler to run as a timed interrupt.

The Code

The code for the project can be found here.

As I mentioned, this is the beginning of a bigger project, so there is some stub code in here already for that project. The main project, sjdtmf.ino, contains a state machine which gets far enough to allow testing of the dtmf.cpp module.

I use the code::blocks editor/IDE. sjdtmf.cbp is it’s project information file.

globals.h and globals.cpp contain constants and code that could be called from any module.

Most of the pertinent code, however, can be found in the dtmf.cpp module.

dtm_init initializes the module. You pass the sample rate and block size.  This module will calculate the Goertzel constants for each of the frequencies (stored in dtm_coeffs) and then start an interrupt timer with the correct interval to sample at 8,000 samples per second.

If you change the sample rate passed to dtm_init() you will also need to calculate the appropriate timer interval. This could be done automatically, I just didn’t do so myself.

dtm_tick is the heart of the algorithm – haha. It is called once every 125 microseconds which results in an 8,000 samples/second sample rate.

First, it reads the ADC to see what voltage is coming from the DTMF processor circuit:

sample = analogRead(pin_dtmfIn);

It then uses that value to recompute Q1 and Q2 values of the Goertzel algorithm for each DTMF frequency:

for(i = 0;i < dtm_toneLen; i = i + 1) {
    Q0 = dtm_coeffs[i] * dtm_Q1[i] - dtm_Q2[i] + (float) (sample - dtm_adcCenter);
    dtm_Q2[i] = dtm_Q1[i];
    dtm_Q1[i] = Q0;

Note the calculation (sample – dtm_adcCenter). This adjusts the ADC input to a proper value for the Goertzel algorithm.

If there is NO DTMF signal, the voltage to the ADC should be 1.65V (3.3V/2) and the ADC should read 512 (1023/2). Unless the resistors R2/R3 are perfectly matched, the ADC won’t quite read 512 when there is no signal. This is corrected with a proper value for dtm_adcCenter.

To determine the value for dtm_adcCenter, I monitored the ADC when there was no signal and got 505. If you cannot determine the corrected value, then using 512 is a good estimate.

Subtracting dtm_adcCenter (505) from the ADC gives values from -505 to 518 – the proper values for the algorithm.

The next chunk of code toggles a digital pin on the MCU:

if (flag)
    digitalWrite(pin_freqTest, HIGH);
    digitalWrite(pin_freqTest, LOW);
flag = !flag;

One of the implementations I reviewed did this and I thought it was quite clever. If I want to know exactly what the real sample rate is, I simply connect an oscilloscope or frequency counter to the MCU’s pin. Then multiply by 2 to get the sample rate. With my 72MHz Teensy I was getting exactly 8K samples / second.


dtm_sampleCount = dtm_sampleCount + 1;                      
if (dtm_sampleCount >= dtm_blockSize) {                     

This increments the sample count. If we’ve seen enough samples to fill a block, then we try to detect a key, and finally reset the algorithm to read another block of samples.

dtm_detect – If dtm_tick is the heart, then dtm_detect must be the brain of the  DTMF module. It determines which, if any, frequencies were seen, if they combine to create a key press, what the key pressed is, and then queue it for consumption by the user.

First, the magnitude for each DTMF frequency is calculated in the array magnitudes:

for(i = 0; i < dtm_toneLen; i = i + 1) {
     magnitudes[i] = sqrt(
        dtm_Q1[i] * dtm_Q1[i] +
        dtm_Q2[i] * dtm_Q2[i] -
        dtm_coeffs[i] * dtm_Q1[i] *dtm_Q2[i]
    } // for

This is taken straight from the algorithm.

Next, we determine if any of the thresholds exceed an arbitrary threshold. If so a bit is set in a bitmap of frequencies:

found = false;                                              
for (i = 0; i < dtm_toneLen; i = i + 1) { 
    if (magnitudes[i] >= dtm_threshold) {
        found = true;					                    
        bitmap = bitmap | bitIx;
    bitIx = bitIx << 1;
    } // for

The value for dtm_threshold was determined empirically using every handset I could find and I have a box full of old handsets! However, in my final code, this value will be stored in eeprom and be changeable by the end-user.

If any frequency signal exceeded the threshold above, we then compare the bitmap of the frequencies found to bitmaps of valid combinations of frequencies. If a match is found, we then locate the key would have been pressed to generate the frequencies seen and place it in the variable curKey.

if (found) {						                        
    for (i = 0; i < dtm_toneLen*2; i = i + 1)		        
        if (bitmap == dtm_bitmaps[i])                       
    if (i < dtm_toneLen*2)				                    
        curKey = dtm_keymap[i];				                
        curKey = '\0';					                    
    curKey = '\0';					                        

Note the usage of the boolean found here. This is a vestige of some old code and could be reduced to just checking if bitmap != 0.

The last chunk of code looks at the transition of the current and previous key pressed and determines what needs to be done. curKey/lastKey may contain the ASCII value of a key (0,1,2…) or it may contain ‘\0’ indicating no key pressed. There are 4 possibilities:

lastKey and curKey both EMPTY: There has been no change, so we do nothing.

lastKey EMPTY and curKey VALID: a new key was pressed. Save it in lastKey and start the timer.

lastkey VALID and curKey EMPTY: the key was released. Calculate duration of key press. If the key was pressed long enough (> dtm_durationTimeout), then consider this a valid keypress and enqueue it into the FIFO queue. Finally, set lastKey to EMPTY.

lastKey == curKey: Same key is still being pressed, do nothing.

If code makes it to this point, there was an unexpected condition. Reset everything.

dtm_read / dtm_available / dtm_peek – When keys are pressed they are detected and placed into the FIFO queue by a timer-based interrupt. This leaves the main program free to do other things. It can then look to see if there are any keys in the queue to process. The read/available/peek functions provided by the DTMF module function like the SERIAL library, allowing the main program to access the keys.

For example, in the module sjdtmf.ino and in the idle state (case st_idle:) , I look to see if a key has been pressed, and if so change the state to st_keyPressed so the key can be processed:

    case st_idle:

        if (swDebounce(pin_switch)) {                       // was reset switch pressed?
            state = st_swPressed;

        if (dtm_available() > 0) {                          // was a key pressed
            state = st_keyPressed;

        break; //st_idle

Demo Video

I put together a short video of my breadboarded version of this DTMF project. It can be seen here:

Nov 2, 2021 Update

Since posting blog I have created a proper prototype:

While doing this I found some problems:

The capacitor for C1 in the BOM is redonkulously large. The next BOM will have an alternate that is reasonably sized.

The cleaned up circuit was getting a “ringing” on the line every time I pressed a key. Finally tracked it down to the LED display I’m using. I put a 100uF capacitor between the Vcc going into the LED and ground. This resolved that problem.

Once I have the problems 100% resolved, I’ll update the BOM and schematic.

Nov 3 Update

Created Eagle CAD project for the schematic.

A PDF of Eagle CAD schematic for the project can be found by clicking on this picture:

The Eagle Project can be found here.

Note that the board has not been laid out since this version is just a breadboard prototype for me.

Change notes:

F3 and D7 have been added to protect the board from excessive current or a reversed power supply.

Added zero ohm resisters in parallel to all fuses. These can be used as jumpers if the fuses are not installed.

C4 was added which keeps the signal clean when the LED is being updated.

I attempted to compile my code on the Nano 33 IoT I expect to use and it immediately failed because intervalTimer.h is a teensy-only module. There is a special timer module for the Nano 33 which I will implement. It has been so long since I used a normal Arduino, I’m not sure what library one would use to replace intervalTimer, but I’m sure one must exist.

Nov 9 Update: Nano 33 IoT Notes

The obvious changes to replace the Teensy with a Nano 33 IoT were to implement a timer library (SAMDTimer), and a fast analogRead function, analogReadFast(). It would have been nice if the Nano would just start working after those changes, but that would be too simple! It would not properly sense the DTMF tones.

The main issue is the difference in clock speed. The Nano is 48MHz while the Teensy is 72MHz. Though the Teensy worked properly when I set it to use 48Mhz, I couldn’t get the Nano to work. The frequency on pin 4, indicating the actual sample rate, had a lot of jitter on it.

It finally occurred to me to time the dtm_tick() function. NOTE: because this is an interrupt called function, micros and millis do not work in it (took me a couple of trys to realized that). Instead, I had to manually call dtm_tick to test its performance.

I found dtm_tick() took 116 microsecs to complete when dtm_detect() was NOT called. The sample rate is once every 125 microseconds, so clearly the MCU was not up to the task. (Running the same test on the Teensy, it too only 24 microsecs to execute dtm_tick).

I then started reducing the sample rate to see how low I could get it. I got it down to 4K samples / sec (vs the original of 8K) with dtm_tick() now being called once every 250 microseconds.

Sensing the DTMF now worked fine, but the tone generator sounded pretty cruddy because even with dtm_tick() taking only half the cycles, it is still too much to get a clean tone. So I modified the program such than when I need to generate a tone, I turn off dtm_tick – the user is expecting a response at that momement anyway.

That worked great. The tone is very clean. Even on the teensy, it was noisy.

Now that I have DTMF working properly on the Nano, I can begin learning how to get it to communicate via WiFi to another system I wish to control via DTMF!

Posted in c-arduino, c-electronics, c-tinys | Tagged | 1 Comment