❌

Reading view

There are new articles available, click to refresh the page.

The Mystery of the Sticky Feet

I’ve been powering up some of my old QRP gear to see if any of it still works. The gear has been stacked up on a shelf above my operating position for years. In testing some of this gear, I came across a curious problem.

Last night I was rearranging some of my equipment and re-routing some coax cables. When I went to move my Oak Hills Research power meter, it was stuck to the shelf. The rubber feet on the bottom of the meter had become slightly soft and sticky. I didn’t think too much of it at the time. I just finished connecting the cables and placed it back on the shelf.Β 

Today, I took my old MFJ-9030 transceiver down from the shelf and was greeted with a gooey mess. The rubber feet had completely dissolved and turned into a sticky, tar-like mess. Using a single-edged razor blade, I scraped off as much as I could from both the radio and the residue on the shelf. I used some mineral spirits to clean up as best I could. The mineral spirits worked great, but I still had sticky spots where the feet were attached to the radio. I cut some pieces of aluminum duct tape to cover those spots, and I applied four new feet from my junk box.Β 

This is the underside of my 31 year old MFJ-9030 transceiver showing the black goo from the dissolved feet. The clear feet were added by me.
This is the underside of my 31 year old MFJ-9030 transceiver showing the black goo from the dissolved feet. The clear feet were added by me.

My theory is that the rubber (or whatever material they are) feet reacted with the finish on the shelf. I know that vinyl guitar straps and accessories can damage the lacquer finish used on some high-end guitars (like my 1973 Martin D35 acoustic), so I suspect something like that happened to my radio equipment. I built the shelf and my operating table from some repurposed shelves that were in the house when we moved in back in the late 70s. So, I have no idea what kind of finish is on them.Β 

Besides the power meter I mentioned earlier, it looks like my old MFJ-941-E antenna tuner is also affected. So, I’ll have to replace the feet on those items and clean up the residue on the shelf with the mineral spirits. Needless to say, some of this equipment hasn’t been moved in years.

This wasn’t exactly how I wanted to spend my afternoon, but I’m glad I spotted the problem.

72, Craig WB3GCK

Carter Craigie N3AO (SK)

I recently learned of the passing of my old friend, Carter Craigie, N3AO. He was an avid QRPer, an excellent CW operator, and one of the nicest people I have ever met.Β 

I first met Carter sometime back in the 90s, when we were members of the now-defunct Eastern Pennsylvania QRP Club (EPA-QRP). Later on, Carter and I were members of Chester County (PA) ARES-RACES. Carter served a two-year stint leading the organization as the Emergency Coordinator (EC).Β 

Eventually, Carter and his wife, Kay N3KN (former ARRL President), moved to Blacksburg, Virginia. Although I rarely saw him in person after that, I had a bunch of QSOs with him over the years. He was a regular participant in QRP field contests, like the NJQRP Skeeter Hunt and Flight of the Bumblebees. He was also an active member of the Straight Key Century Club (SKCC).Β 

In 1999, Carter N3AO and Ed WA3WSJ stopped stopped by my campsite in French Creek State Park (PA) to check out my new camper. L-R: WA3WSJ, WB3GCK, and N3AO.
In 1999, Carter N3AO and Ed WA3WSJ stopped stopped by my campsite in French Creek State Park (PA) to check out my new camper. L-R: WA3WSJ, WB3GCK, and N3AO.

Looking back through my logs brought back some memories. Carter always tried to work me when I was out camping or traveling. He seemed to know my go-to frequencies and operating habits and would always seem to find me. I remember being on a camping trip to the Thousand Islands in New York back in 1999. I turned on the radio one morning, and without touching the tuning knob, the sound of Carter calling me greeted me. We had some nice rag chews on those trips.Β 

Phillies spring training in Clearwater, FL, in 2009. L-R: WB3GCK, my (far) better half, Kay N3KN, and Carter N3AO.
I had the pleasure of running into Carter and Kay during Phillies spring training in Clearwater, Florida, in 2009. L-R: WB3GCK, my (far) better half, Kay N3KN, and Carter N3AO.

During the year-long National Parks on the Air (NPOTA) event in 2016, I used to text Carter whenever I was out activating a park. Within minutes, I would hear Carter calling me for a contact. He would always make sure to spot me on the NPOTA Facebook page.Β 

While it’s sad to know that I won’t hear Carter on the air anymore, I feel truly blessed for having known him. Rest in peace, my friend.

72, Craig WB3GCK

Stand alone update of SiK radio firmware

SiK is an inexpensive data radio commonly used by UAVs.

At least two of the β€˜ground station’ applications incorporate facility for firmware upgrade, but if you aren’t using them for their main purpose, it is a huge installation just to upgrade the SiK radio.

Above is an inexpensive C2 programmer with DIY pogo pin adapter, one of may options. A C2 programmer is needed to write the bootloader.

The SiK radio project publishes a python script at https://github.com/ArduPilot/SiK/blob/master/Firmware/tools/uploader.py . This article gives a revised version that works on a HM-TRP radio under Windows 11.

The firmware was v2.2 sourced from https://firmware.ardupilot.org/SiK/stable/ .

Here is the revised Python script:

#next line fails in Windows
##!/usr/bin/env python3
#
# Serial firmware uploader for the SiK bootloader
#

import argparse
import binascii
import glob
import serial
import sys
import time

if sys.version_info.major < 3:
    print("This tool requires python3")
    sys.exit(1)


class Firmware(object):
    """Loads a firmware file"""

    # parse a single IntelHex line and obtain the byte array and address
    def __parseline(self, line):
        # ignore lines not beginning with :
        if line[0] != ":":
            return
        # parse the header off the line
        hexstr = line.rstrip()[1:-2]
        binstr = binascii.unhexlify(hexstr)
        command = binstr[3]

        # only type 0 and 4 records are interesting
        if command == 4 and binstr[0] == 0x02 and (binstr[1] << 8) + binstr[2] == 0x0000:
            self.upperaddress = (binstr[4] << 8) + binstr[5]
            self.bankingDeteted = True
        elif command == 0:
            address = (binstr[1] << 8) + binstr[2] + (self.upperaddress << 16) bytes = bytearray(binstr[4:]) if self.upperaddress in self.sanity_check: self.sanity_check[self.upperaddress][0] += len(bytes) if self.sanity_check[self.upperaddress][1] > (binstr[1] << 8) + binstr[2]:
                    self.sanity_check[self.upperaddress][1] = (binstr[1] << 8) + binstr[2]
                if self.sanity_check[self.upperaddress][2] < (binstr[1] << 8) + binstr[2] + len(bytes):
                    self.sanity_check[self.upperaddress][2] = (binstr[1] << 8) + binstr[2] + len(bytes)
            else:
                self.sanity_check[self.upperaddress] = []
                self.sanity_check[self.upperaddress].append(len(bytes))
                self.sanity_check[self.upperaddress].append((binstr[1] << 8) + binstr[2])
                self.sanity_check[self.upperaddress].append((binstr[1] << 8) + binstr[2] + len(bytes)) self.__insert(address, bytes) # insert the byte array into the ranges dictionary, merging as we go def __insert(self, address, bytes): # look for a range that immediately follows this one candidate = address + len(bytes) if candidate in self.ranges: # found one, remove from ranges and merge it nextbytes = self.ranges.pop(candidate) bytes.extend(nextbytes) # iterate the existing ranges looking for one that precedes this for candidate in list(self.ranges.keys()): prevlen = len(self.ranges[candidate]) if (candidate + prevlen) == address: self.ranges[candidate].extend(bytes) return # just insert it self.ranges[address] = bytes def __init__(self, path): self.ranges = dict() self.upperaddress = 0x0000 self.bankingDeteted = False self.sanity_check = dict() # read the file # XXX should have some file metadata here too ... f = open(path, "r") for line in f: self.__parseline(line) def code(self): return self.ranges class Uploader(object): """Uploads a firmware file to the SiK bootloader""" NOP = chr(0x00) OK = chr(0x10) FAILED = chr(0x11) INSYNC = chr(0x12) EOC = chr(0x20) GET_SYNC = chr(0x21) GET_DEVICE = chr(0x22) CHIP_ERASE = chr(0x23) LOAD_ADDRESS = chr(0x24) PROG_FLASH = chr(0x25) READ_FLASH = chr(0x26) PROG_MULTI = chr(0x27) READ_MULTI = chr(0x28) PARAM_ERASE = chr(0x29) REBOOT = chr(0x30) PROG_MULTI_MAX = 32 # 64 causes serial hangs with some USB-serial adapters READ_MULTI_MAX = 255 READ_MULTI_MAX = PROG_MULTI_MAX #fix buffer overrun on verify with FTDI FT-232RL BANK_PROGRAMING = -1 def __init__(self, portname, atbaudrate=57600, use_mavlink=False, mavport=0, debug=0): print(("Connecting to %s" % portname)) if use_mavlink: from pymavlink import mavutil self.port = mavutil.MavlinkSerialPort(portname, 115200, devnum=mavport, timeout=3, debug=debug) else: self.port = serial.Serial(portname, 115200, timeout=3) self.atbaudrate = atbaudrate self._debug = debug self.percent = -1 if use_mavlink: # we can send a bit more on mavlink Uploader.PROG_MULTI_MAX = 64 def debug(self, s, level=1): """write some debug text""" if self._debug >= level:
            print(s)

    def __send(self, c):
        if type(c) is str:
            self.port.write(bytes(c, 'latin-1'))
            self.debug(("writing", c), 7)
        else:
            self.debug(("writing ", binascii.hexlify(c)), 7)
            self.port.write(c)

    def __recv(self, raise_error=True):
        start_time = time.time()
        c = self.port.read(1)
        if len(c) < 1: self.debug("timeout waiting for data (%.2f seconds)" % (time.time() - start_time)) if raise_error: raise RuntimeError("timeout waiting for data") return None return c.decode('latin-1') def __getSync(self, raise_error=True): c = self.__recv(raise_error) if c is None: return False if c != self.INSYNC: print("c ", type(c), " ", c) print("INSYNC ", type(self.INSYNC), " ", self.INSYNC) self.debug(("unexpected ", c, " instead of INSYNC 0x", self.INSYNC)) if raise_error: raise RuntimeError(("unexpected ", c, " instead of INSYNC 0x", self.INSYNC)) return False c = self.__recv() if c != self.OK: self.debug(("unexpected 0x", c, " instead of OK 0x", self.OK)) if raise_error: raise RuntimeError(("unexpected ", c, "instead of OK 0x", self.OK)) return False self.debug("__getSync OK", 2) return True # attempt to get back into sync with the bootloader def __sync(self): # send a stream of ignored bytes longer than the longest possible conversation # that we might still have in progress self.debug("trying __sync") self.__send(Uploader.NOP * (Uploader.PROG_MULTI_MAX + 2)) self.port.flushInput() self.__send(Uploader.GET_SYNC + Uploader.EOC) self.debug("trying __getSync") return self.__getSync(raise_error=False) # send the CHIP_ERASE command and wait for the bootloader to become ready def __erase(self, erase_params=False): self.__send(Uploader.CHIP_ERASE + Uploader.EOC) self.__getSync() if erase_params: print("resetting parameters...") self.__send(Uploader.PARAM_ERASE + Uploader.EOC) self.__getSync() # send a LOAD_ADDRESS command def __set_address(self, address, banking): if banking: if self.BANK_PROGRAMING != address >> 16:
                self.BANK_PROGRAMING = address >> 16
                if self.BANK_PROGRAMING == 0:
                    print("HOME")
                else:
                    print("BANK", self.BANK_PROGRAMING)
            self.__send(Uploader.LOAD_ADDRESS
                        + chr(address & 0xff)
                        + chr((address >> 8) & 0xff)
                        + chr((address >> 16) & 0xff)
                        + Uploader.EOC)
        else:
            self.debug("set addr - no banking")
            self.__send(Uploader.LOAD_ADDRESS
                        + chr(address & 0xff)
                        + chr(address >> 8)
                        + Uploader.EOC)
            self.debug("set addr - done")
        self.__getSync()

    # send a PROG_FLASH command to program one byte
    def __program_single(self, data):
        self.debug("prog single")
        self.__send(Uploader.PROG_FLASH
                    + chr(data)
                    + Uploader.EOC)
        self.__getSync()

    # send a PROG_MULTI command to write a collection of bytes
    def __program_multi(self, data):
        self.debug("Program Multi", 10)
        sync_count = 0
        while len(data) > 0:
            n = len(data)
            if n > Uploader.PROG_MULTI_MAX:
                n = Uploader.PROG_MULTI_MAX
            block = data[:n]
            data = data[n:]
            self.__send(Uploader.PROG_MULTI + chr(n))
            self.__send(block)
            self.__send(Uploader.EOC)
            sync_count += 1
        while sync_count > 0:
            self.debug("sync_count=%u" % sync_count)
            self.__getSync()
            sync_count -= 1

    # verify multiple bytes in flash
    def __verify_multi(self, data):
        self.__send(Uploader.READ_MULTI
                    + chr(len(data))
                    + Uploader.EOC)
        for i in data:
            if self.__recv() != chr(i):
                return False
        self.__getSync()
        return True

    # send the reboot command
    def __reboot(self):
        self.__send(Uploader.REBOOT)

    # split a sequence into a list of size-constrained pieces
    def __split_len(self, seq, length):
        return [seq[i:i + length] for i in range(0, len(seq), length)]

    def total_size(self, code):
        """return total programming size in bytes"""
        total = 0
        for address in list(code.keys()):
            total += len(code[address])
        return total

    def progress(self, count, total):
        """show progress bar"""
        pct = int((100 * count) / total)
        if pct != self.percent:
            self.percent = pct
            hlen = (pct / 2)
            hashes = '#' * int(hlen)
            spaces = ' ' * (50 - int(hlen))
            sys.stdout.write("[%s] %u/%u (%u%%)\r" % (
                hashes + spaces,
                count,
                total,
                pct))
            sys.stdout.flush()
        if count == total:
            print("")

    # upload code
    def __program(self, fw):
        code = fw.code()
        count = 0
        total = self.total_size(code)
        for address in sorted(code.keys()):
            self.debug("address", address)
            self.__set_address(address, fw.bankingDeteted)
            groups = self.__split_len(code[address], Uploader.PROG_MULTI_MAX)
            for bytes in groups:
                self.__program_multi(bytes)
                count += len(bytes)
                self.progress(count, total)
        self.progress(count, total)
        self.debug("done")

    # verify code
    def __verify(self, fw):
        code = fw.code()
        count = 0
        total = self.total_size(code)
        for address in sorted(code.keys()):
            self.__set_address(address, fw.bankingDeteted)
            groups = self.__split_len(code[address], Uploader.READ_MULTI_MAX)
            for bytes in groups:
                if not self.__verify_multi(bytes):
                    raise RuntimeError("Verification failed in group at 0x%x" % address)
                count += len(bytes)
                self.progress(count, total)
        self.progress(count, total)
        self.debug("done")

    def expect(self, pattern, timeout):
        """wait for a pattern with timeout, return True if found, False if not"""
        import re
        prog = re.compile(pattern)
        start = time.time()
        s = ''
        while time.time() < start + timeout: b = self.port.read(1) if len(b) > 0:
                sys.stdout.write(b.decode('latin-1'))
                s += b.decode('latin-1')
                if prog.search(s) is not None:
                    return True
            else:
                time.sleep(0.01)
        return False

    def send(self, s):
        """write a string to port and stdout"""
        self.port.write(bytes(s, 'latin-1'))
        sys.stdout.write(s)

    def setBaudrate(self, baudrate):
        try:
            self.port.setBaudrate(baudrate)
        except Exception:
            # for pySerial 3.0, which doesn't have setBaudrate()
            self.port.baudrate = baudrate

    def autosync(self):
        """use AT&UPDATE to put modem in update mode"""
        if self.atbaudrate != 115200:
            self.setBaudrate(self.atbaudrate)
        print("Trying autosync")
        self.send('\r\n')
        time.sleep(1.0)
        self.send('+++')
        self.expect('OK', timeout=1.1)
        for i in range(5):
            self.send('\r\nATI\r\n')
            if not self.expect('SiK .* on', timeout=0.5):
                print("Failed to get SiK banner")
                continue
            self.send('\r\n')
            time.sleep(0.2)
            self.port.flushInput()
            self.send('AT&UPDATE\r\n')
            time.sleep(0.7)
            self.port.flushInput()
            if self.atbaudrate != 115200:
                self.setBaudrate(115200)
            print("Sent update command")
            return True
        if self.atbaudrate != 115200:
            self.setBaudrate(115200)
        return False

    # verify whether the bootloader is present and responding
    def check(self):
        for i in range(3):
            try:
                if self.__sync():
                    print("Got sync")
                    return True
                self.autosync()
            except RuntimeError:
                self.autosync()
        return False

    def identify(self):
        self.__send(Uploader.GET_DEVICE
                    + Uploader.EOC)
        board_id = self.__recv()[0]
        board_freq = self.__recv()[0]
        self.__getSync()
        return board_id, board_freq

    def upload(self, fw, erase_params=False):
        print("erasing...")
        self.__erase(erase_params)
        print("programing...")
        self.__program(fw)
        print("verifying...")
        self.__verify(fw)
        print("done.")
        self.__reboot()


if __name__ == '__main__':
    # Parse commandline arguments
    parser = argparse.ArgumentParser(description="Firmware uploader for the SiK radio system.")
    parser.add_argument('--resetparams', action="store_true", help="reset all parameters to defaults")
    parser.add_argument("--baudrate", type=int, default=57600, help='baud rate')
    parser.add_argument('--forceBanking', action="store_true", help="force the programmer to use 24bit addressing")
    parser.add_argument("--mavlink", action='store_true', default=False, help='update over MAVLink')
    parser.add_argument("--mavport", type=int, default=0, help='MAVLink port number')
    parser.add_argument("--debug", type=int, default=0, help='debug level')
    parser.add_argument('--port', required=True, action="store", help="port to upload to")
    parser.add_argument('firmware', action="store", help="Firmware file to be uploaded")
    args = parser.parse_args()

    # Load the firmware file
    fw = Firmware(args.firmware)
    if args.forceBanking:
        fw.bankingDeteted = True
    ''' This stuff does not work for high COM ports in Windows
    ports = glob.glob(args.port)
    if not ports:
        print(("No matching ports for %s" % args.port))
        sys.exit(1)
    # Connect to the device and identify it
    for port in glob.glob(args.port):
    '''
    for port in [args.port]:
        print(("uploading to port %s" % port))
        up = Uploader(port, atbaudrate=args.baudrate, use_mavlink=args.mavlink,
                      mavport=args.mavport, debug=args.debug)
        if not up.check():
            print("Failed to contact bootloader")
            sys.exit(1)
        id, freq = up.identify()
        print(("board %x  freq %x" % (ord(id), ord(freq))))
        # CPU's that support banking have the upper bit set in the byte (0x80)
        if (fw.bankingDeteted and (ord(id) & 0x80) != 0x80):
            print("This firmware requires a CPU with banking")
            sys.exit(1)
        if (ord(id) & 0x80) == 0x80:
            fw.bankingDeteted = True
            print("Using 24bit addresses")
        up.upload(fw, args.resetparams)

There are only a few changes, here is the diff file:

--- uploader.py.o	2024-07-21 12:45:58.103535500 +1000
+++ uploader.py	2024-07-21 16:36:56.263199800 +1000
@@ -1,4 +1,5 @@
-#!/usr/bin/env python3
+#next line fails in Windows
+##!/usr/bin/env python3
 #
 # Serial firmware uploader for the SiK bootloader
 #
@@ -103,6 +104,7 @@
 
     PROG_MULTI_MAX = 32  # 64 causes serial hangs with some USB-serial adapters
     READ_MULTI_MAX = 255
+    READ_MULTI_MAX = PROG_MULTI_MAX #fix buffer overrun on verify with FTDI FT-232RL
     BANK_PROGRAMING = -1
 
     def __init__(self, portname, atbaudrate=57600, use_mavlink=False, mavport=0, debug=0):
@@ -416,13 +418,15 @@
     fw = Firmware(args.firmware)
     if args.forceBanking:
         fw.bankingDeteted = True
-
+    ''' This stuff does not work for high COM ports in Windows
     ports = glob.glob(args.port)
     if not ports:
         print(("No matching ports for %s" % args.port))
         sys.exit(1)
     # Connect to the device and identify it
     for port in glob.glob(args.port):
+    '''
+    for port in [args.port]:
         print(("uploading to port %s" % port))
         up = Uploader(port, atbaudrate=args.baudrate, use_mavlink=args.mavlink,
                       mavport=args.mavport, debug=args.debug)

Prior to debugging the py script, I rewrote the bootloader using a U-EC2 programmer:

@echo off
rem this script 
set BOOTLOADER=bootloader~hm_trp~433.hex
set SIK=radio~hm_trp-2.2.hex
set SIK=junk.hex

set srec_cat=D:\Program Files\srecord\bin\srec_cat.exe
set HEXPATH=
set XXD=xxd

set FLASHUTILCL="d:\SiLabs\MCU\Utilities\FLASH Programming\Static Programmers\Command-Line\FlashUtilCL.exe"

echo on
rem fix the adress ordering
"%srec_cat%" %SIK% -intel -o temp.hex -intel
rem now write the flash
%FLASHUTILCL% FlashEraseUSB 0 1
%FLASHUTILCL% DownloadUSB "%BOOTLOADER%" 0 0 1
%FLASHUTILCL% VerifyUSB "%BOOTLOADER%" 0 1
%FLASHUTILCL% DownloadUSB temp.hex 0 0 1
%FLASHUTILCL% VerifyUSB temp.hex 0 1

@echo off

echo.
echo All done
pause

That worked fine, it seems that the flash utils do not like out of sequence data, the srec_cat fixes than (though you will get a warning).

Above are the C2 connections to the module. I powered it from a separate power supply, VDD_MCU was not used (do not connect 5V here).

The bootloader was sourced from https://firmware.ardupilot.org/SiK/bootloader/ .

Last update: 30th July, 2024, 7:14 AM

Holzforma / Farmertec G395XP chainsaw – first impressions

I purchased a Holzforma G395XP chainsaw, it is a Chinese clone of the now discontinued Husqvarna 395XP It is a relatively old technology carburetted engine without stratified intake and without introducing electronic auto tune, a 30 year old design.

Some of the fasteners used to hold the top cowl down were noodled and would only work with a plain slotted screwdriver. They were replaced with hex button head screws.

In the light of experience with Holzforma / Farmertec G372XT chainsaw – early evaluation, a decision was made to preemptively vacuum and pressure test the G395XP saw.

It failed the vacuum test, not really badly, bad badly enough to warrant repair.

On removing the clutch etc to visibly examine the PTO side seal, the outer lip was not properly sitting on the shaft, the seal need to be driven in 1.0 to 1.5mm further. Given that the seals in the G372XT failed early due to one of both of low quality seal material and dry installation, the seal was removed and replaced. It was actually ok, not burnt, not worn, but had not properly engaged the shaft.

The flywheel was removed and the seal inspected, same problem but worse… the seal need to be driven about 2mm to properly engage the shaft. For the reasons given above, the seal was removed and a new one fitted.

A vacuum and pressure test was performed after 10min of operation with the new seals. There is a very slow leak of both vacuum and pressure, so slow as not to be concerning. It is substantially improved over the original hardly used crankshaft seals which were probably just not driven in sufficiently.

However, reassembling the muffler resulted in the M6 bolt thread stripping at just 6Nm (Husqvarna specified torque is 10-12Nm, hinting grade 8.8 fasteners), hinting low grade bolts on the Holzforma. A bad design where the expensive part (the bolt) failsΒ  before the nut.

They were replaced with Husqvarna bolts, and new nuts.

Lessons learned

Some small problems, though the air leak (bad crankcase seal fitting) could quite quickly cause a hot seizure which would probably write the saw off.

I own two Holzforma saws, and both failed a crankcase vacuum test. I would probably not buy one again, but if I did, I would perform a crankcase vacuum and pressure test.

I would be reluctant to torque any fasteners to more than 50% of Husqvarna’s recommendation.

Evaluation post repairs

The saw starts easily, as easily as any big saw. After adjustment to service manual settings, idle is stable, WOT is stable. It is a heavy but powerful saw with it seems a moderately wide power band. Running on 50:1 Amsoil Sabre and Ethanol free ULP, it is fairly low smoke, not as clean as strato engines, but pretty good.

Last update: 12th July, 2024, 12:33 PM

Toro ride-on mower fuel system blues

I have a Toro ride on mower (riding mower in North America) which I ran out of fuel when it was quite new (<1y) and having put 5l of fuel in the tank, it would not start after quite a bit of cranking.

In this instance, the battery failed. Prolonged cranking can buckle battery plates, or open intercell connecting links, and although this was prolonged cranking, it was not ridiculously long. Perhaps the battery was the real cause of the failure?

Nevertheless, I asked what I could do to prevent this recurring.

Well, with best intentions of never allowing the fuel to get low, it has run out of fuel a few times over 10y and has not restarted quickly, so was primed by injecting fuel into the carburettor hose.

Time for diagnostics

A note, the engine has been run on Ethanol free ULP, no Ethanol, ever.

Is the pump faulty?

This should not be the first question, but after market impulse fuel pumps are inexpensive and it is an easy test.

So, the pump was replaced and the old one dismantled and inspected. It was 2y old, and I was surprised that the valves were not as flexible as I expected… so maybe this is the problem.

Time showed the new pump was susceptible, time to do some logical analysis of the problem.

Is there an air leak that compromises pump performance when it is dry?

It is not easy to access the fuel tank end of the fuel hose, so as a shortcut, the hose was crimped off about 300mm from the fuel tank, the outlet hose removed from the fuel pump and a vacuum test conducted to check hoses, fuel filter, fuel pump.

That revealed a tendency for leaks at hose ends if the hose clamps were not applied over the barb or bulge in the nipple. Otherwise everything was good.

Note that air leaks are insidious, small air leaks may result in the engine running too lean at times, and that can cause expensive damage.

Perhaps that fixed the problem?

No.

A primer bulb

A primer bulb was inserted in the fuel line near to the tank. This allows for manually priming the carburettor after a fuel run-out… minimising long cranking and the risk of battery damage.

A 6mm primer bulb was purchased on Aliexpress for about $5.

Above, the primer was cut into the fuel line and the fuel line length adjusted so the primer bulb in suspended in air to avoid abrasion. The fuel line is zip tied to the wiring harness between filter and primer (not seen here due to the cross member).

The fuel line appears to be 6mm bore or perhaps 1/4β€³. 13.3mm (3/8 PEX) One-ear hose clamps were used to secure the hose.

The primer works fine, even with just a small amount of fuel in the tank, it takes quite a few squeezes to reach pressure.

One thing it will show is leaks upstream due to ineffective clamps… though note pressure tests the hose fitting in a more demanding way than the normal vacuum mode.

Last update: 19th June, 2024, 12:25 PM

A Tool for Dual Activators

I was first bitten by the parks bug, taking part in ARRL’s National Parks on the Air (NPOTA) program in 2016. When NPOTA ended, I started activating parks under the World Wide Flora & Fauna (WWFF) program. When Parks on the Air (POTA) came along, I started submitting my logs to both programsβ€”and still do.Β 

Since POTA and WWFF are separately administered, there are some significant differences between the two programs. One notable difference involves the park designators. In some cases the park numbers are the same, but often they’re different. For example, the designator for PA 246 State Game Land is KFF-5862 in WWFF and US-8941 in POTA. Sometimes a park will be in one program but not the other.Β 

Fortunately, Al Zelna N3KAE has provided an online tool that makes it easy for β€œdual activators” to cross-reference the WWFF and POTA park designators in the U.S. and Canada. Visit the CQ Parks.net website to check it out.Β 

Dual Parks North America

If you aren’t familiar with WWFF, it’s the original Amateur Radio parks program. In its current form, WWFF dates back to 2012, but it has roots going back to about 2008. While POTA has seen tremendous growth since its inception, WWFF is still very popular in Europe and other parts of the world. When band conditions are good, a spot on the WWFF site or a DX cluster will often generate a European pile-up. It sure is fun having a bunch of DX stations chasing my little QRP station.

Have fun out in the parks, however you activate!

73 & 44, Craig WB3GCK

Drive-on Antenna Mount Article

Drive-on Antenna Mast Mount

Back in December, Becky Schoenfeld W1BXY, Editorial Director for ARRL’s On the Air magazine, asked me if I would be interested in writing a detailed set of step-by-step instructions for my Drive-on Portable Antenna Support. Naturally, I said I would.

I submitted my manuscript, along with an all-new set of pictures. The article was published in the current issue (May/June 2024) of On the Air (pages 20-22).

If you’re interested, have a look. ARRL members have access On the Air as part of their membership.

73, Craig WB3GCK

Avoiding the Naughty List

While exchanging Christmas gifts with my (far) better half, she surprised me with a couple of additions to my ham radio wardrobe.

One of the t-shirts I received for Christmas. "Eat, Sleep, Ham Radio, Repeat."
One of the t-shirts I received for Christmas. "Never underestimate an old man with a ham radio."

Apparently I was successful in staying off the β€œnaughty list” this year.Β 

I hope you also had great holiday.

73, Craig WB3GCK

❌