-----
The Pimoroni Galactic Unicorn is a cool piece of kit that we have had our eye on for a while. The item is popular and sells out quickly, but we finally got our hands on one. Summary: It's awesome! After the initial "WOW!" factor diminished we went in search of a project for it.
-----
The answer was obvious. How many times are you sitting comfortably in your home theater room with the family and you miss a Morse Code CQ from one of your ham radio friends? That's what we thought, so we set out to solve that inconvenient and irritating problem!
-----
The project needs a WiFi connection for the Pimoroni Galactic Unicorn. No ham radio or RF access to the ham bands are needed.
Steps:
- Buy a Pimoroni Galactic Unicorn and set it up to run MicroPython.
- Create a HamAlert account and set up some triggers for the ham operators of interest. You will need to select the "telnet" reporting option in the trigger menu.
- Copy/Paste our MicroPython source code below into the Pimoroni Galactic Unicorn. We used Thonny as our development environment. Name the program "main.py" so it will autostart at bootup.
- Place the rig under that big screen TV in the home theater room. Simply wait for your movie or favorite show to be interrupted letting you know it's time to drop everything and fire up your ham radio for a QSO!
-----
Here is a live demo of the setup. What happens is:
- K5JM, who is in my HamAlert triggers is spotted calling CQ.
- The Pimoroni Galactic Unicorn is logged into and monitoring the HamAlert telenet server.
- We parse the response of the HamAlert telnet server and display the information we want on the LED matrix.
- Of course, we answer K5JM. Success!!! Next we return to the home theater room and await the next interruption.
-----
Source code below:
'''
Morse Alert
MAY2023
WhiskeyTangoHotel.Com
This program displays your individual hamalert.org telnet CW triggers
onto the display of the Raspberry PI PicoW based Pimoroni Galactic Unicorn.
Thanks HamAlert.Org by Manuel Kasper (HB9DQM) for their telnet service!
CW CQ info shown is "Callsign", "Frequency", and "WPM", but other options are available.
The text is color coded by band.
Scroll speed and total display variables are adjustable.
Alert 'chirp' is adjustable.
A CW beacon, such as WR5U which transmits about every 30 minutes,
may be a suggested hamalert telnet trigger to avoid timeouts from the
hamalert.org server.
NOTE: Does not work with hamalert.org simulated triggers
'''
#Variable set up:
wifi_ssid = "wifi_ssid"
wifi_password = "wifi_password"
# hamalert.org login info
username = "telnet_username"
password = "telnet_passord"
telnetaddr = "hamalert.org" # "telnetaddress.com"
port = 7300 # port as a number, not a string
from galactic import GalacticUnicorn
gu = GalacticUnicorn()
# Set up speaker for a sweeping 'chirp' alert
volume = .5 # range is 0 to 1. 0 = For no sound
start_tone = 500 #500 is a good start Adjust to suit.
end_tone = 1000 #1000 is a good start. Adjust to suit.
channels = [gu.synth_channel(i) for i in range(1)]
waitmsg = "HamAlert..."
howbright = 0.1 # value range 0.0 to 1.0
dwelltime = 5 # how many seconds to display Callsign, Freq, WPM
tot_time = 60 # how long (seconds) to cycle this info
utc_offset = -5 # we print to screen (not to LEDs the zulu and local time)
import network
import usocket as socket
import time
import utime
from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN
graphics = PicoGraphics(display=DISPLAY_GALACTIC_UNICORN)
#Define some colours
BLACK = graphics.create_pen(0, 0, 0)
RED = graphics.create_pen(255, 0, 0)
YELLOW = graphics.create_pen(255, 255, 0)
GREEN = graphics.create_pen(0, 255, 0)
CYAN = graphics.create_pen(0, 255, 255)
BLUE = graphics.create_pen(0, 0, 255)
MAGENTA = graphics.create_pen(255, 0, 255)
WHITE = graphics.create_pen(200, 200, 200)
GREY = graphics.create_pen(100, 100, 100)
DRKGRY = graphics.create_pen(50, 50, 50)
FREQCOLOR = WHITE # this is the text color that will change per band
waitmsgcolor = GREY # number, not a string
# create a PicoGraphics framebuffer to draw into
graphics = PicoGraphics(display=DISPLAY_GALACTIC_UNICORN)
gu.set_brightness(howbright)
#Create a single wlan object and use as a global for all net calls
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(wifi_ssid, wifi_password)
# Wait for connect success or failure
max_wait = 100
while max_wait > 0:
if wlan.status() < 0 or wlan.status() >= 3:
break
max_wait -= 1
wifistat = 'WiFi...' + str(100-max_wait)
if max_wait == 0:
wifistat = "WiFi fail"
print(wifistat)
width = graphics.measure_text(wifistat, 1)
startplace = int(float(GalacticUnicorn.WIDTH) - width) / 2
# clear the graphics object
graphics.set_pen(BLACK)
graphics.clear()
# draw the text
graphics.set_pen(waitmsgcolor)
graphics.text(wifistat, round(startplace), 2, -1, 0.55);
# update the display
gu.update(graphics)
time.sleep(.5)
if max_wait > 0:
print("WIFI OK!")
width = graphics.measure_text('WIFI OK!', 1)
startplace = int(float(GalacticUnicorn.WIDTH) - width) / 2
# clear the graphics object
graphics.set_pen(BLACK)
graphics.clear()
# draw the text
graphics.set_pen(waitmsgcolor)
graphics.text('WIFI OK!', round(startplace), 2, -1, 0.55);
# update the display
gu.update(graphics)
time.sleep(5)
# Connect to the telnet server
tn = socket.socket()
addr = socket.getaddrinfo(telnetaddr, port)[0][-1]
tn.connect(addr)
# Log in with the username and password to telnet server
tn.send(username + "\r\n")
tn.send(password + "\r\n")
last_spot = utime.time() # we track/print time between spots
# Read and process the telnet server response
while True:
data = tn.recv(1024)
data = data.decode("utf-8")
print(data)
# Center "waitmsg"
width = graphics.measure_text(waitmsg, 1)
startplace = int(float(GalacticUnicorn.WIDTH) - width) / 2
# clear the graphics object
graphics.set_pen(BLACK)
graphics.clear()
# draw the text
graphics.set_pen(waitmsgcolor)
graphics.text(waitmsg, round(startplace), 2, -1, 0.55);
# update the display
gu.update(graphics)
# Play 'connected' Chirp alert
for tone in range(start_tone, end_tone):
channels[0].play_tone(tone, volume)
gu.play_synth()
time.sleep(.0009)
gu.stop_playing()
if "DX de" in data:
#print(data)
# Split the string into a list of values
data_list = data.split()
# Assign each value to separate variables
dx = data_list[0]
de = data_list[1]
spotter = data_list[2]
freq = data_list[3]
spotted = data_list[4]
db = data_list[5]
wpm = data_list[6]
zulu = data_list[7]
# Print the values of the variables
print("HamAlert returns:")
#print('dx:', dx )
#print('de:', de)
#print('spotter:', spotter)
print('Freq:', freq)
print('Spotted:', spotted)
#print('db:', db)
print('WPM:', wpm)
#print('zulu:', zulu)
#Convert zulu to local
hours = int(zulu[:2])
minutes = int(zulu[2:4])
local_hours = hours + utc_offset
local_minutes = minutes
# If negative hour fix wraparound
if local_hours < 0:
local_hours += 24
# Format to local timeg
local_time = "{:02d}:{:02d}".format(local_hours, local_minutes)
print('Local time:', local_time)
#print('Minutes last spot:', int( (utime.time() - last_spot)/ 60) )
last_spot = utime.time() # we track/print time between spots
print('-------------------')
print
FREQCOLOR = WHITE
band = float(freq)
if 24890 <= band <= 24990:
FREQCOLOR = CYAN # text color for 12 meter spots
if 18068 <= band <= 18168:
FREQCOLOR = BLUE # text color for 17 meter spots
if 14000 <= band <= 14350:
FREQCOLOR = MAGENTA # text color for 20 meter spots
if 10100 <= band <= 10150:
FREQCOLOR = RED # text color for 30 meter spots
if 7000 <= band <= 7300:
FREQCOLOR = YELLOW # text color for 40 meter spots
if 3500 <= band <= 4000:
FREQCOLOR = GREEN # text color for 80 meter spots
for x in range(0, int((tot_time/(dwelltime*3)))):
# Center position of the text
width = graphics.measure_text(spotted, 1)
startplace = int(float(GalacticUnicorn.WIDTH) - width) / 2
# clear the graphics object
graphics.set_pen(BLACK)
graphics.clear()
# draw the text
graphics.set_pen(FREQCOLOR)
graphics.text(spotted, round(startplace), 2, -1, 0.55);
# update the display
gu.update(graphics)
time.sleep(dwelltime)
# Center position of the text
width = graphics.measure_text(freq, 1)
startplace = int(float(GalacticUnicorn.WIDTH) - width) / 2
# clear the graphics object
graphics.set_pen(BLACK)
graphics.clear()
# draw the text
graphics.set_pen(FREQCOLOR)
graphics.text(freq, round(startplace), 2, -1, 0.55);
# update the display
gu.update(graphics)
time.sleep(dwelltime)
# Center position of the text
width = graphics.measure_text(wpm, 1)
startplace = int(float(GalacticUnicorn.WIDTH) - width) / 2
# clear the graphics object
graphics.set_pen(BLACK)
graphics.clear()
# draw the text
graphics.set_pen(FREQCOLOR)
graphics.text(wpm, round(startplace), 2, -1, 0.55);
# update the display
gu.update(graphics)
time.sleep(dwelltime)
#print(waitmsg)
# Below if you want time of last spot shown. This can help ID a telnet timeout.
# See our reason for having a CW beacon comment at the very top.
waitmsg = 'Last ' + local_time
# This if you want the your pre-defined waitmsg after a spot
#width = graphics.measure_text(waitmsg, 1)
width = graphics.measure_text(waitmsg, 1)
startplace = int(float(GalacticUnicorn.WIDTH) - width) / 2
# clear the graphics object
graphics.set_pen(BLACK)
graphics.clear()
# draw the text
graphics.set_pen(waitmsgcolor)
graphics.text(waitmsg, round(startplace), 2, -1, 0.55);
# update the display
gu.update(graphics)
-----