Showing posts with label Android. Show all posts
Showing posts with label Android. Show all posts

Tuesday, July 14, 2020

Send Morse Code (CW) with your Voice

-----
CW or Morse Code is a way ham radio operators send messages with a simple tone of two durations.  It was a must before the days of wireless voice communications and still remains popular because it is a fun challenge. 

There are many online tools that will convert text into Morse Code.  However, a Google AIY Voice Bonnet came into our possession and we thought we could take that another step by creating a "Speech to Morse" translator that would actually send code over the air on the ham radio bands from spoken word.

To use the rig, you don't need a ham radio license unless you connect to an actually ham radio transmitter; which is kinda the whole point.  My quick ham radio advertisement:  Get a FCC Amateur Radio license; it's not hard and is an extremely interesting hobby.  BTW, learning Morse Code is no longer a requirement.
-----
When we were given the AIY Voice Bonnet we really didn't even know what it did.  We knew it needed a Raspberry PI and it could act somewhat like a Google Voice Assistant.   We had a Raspberry PI Zero handy and after some Google search engineering we found a disk image file to get us started.

Like a lot of internet projects APIs can change and documentation can be conflicting, we finally got the RasPI and Voice Bonnet to talk to the web after some effort.  A full blown Google Voice Assistant this thing is NOT; if you want one of those then BUY, don't BUILD.  After leaning into the AIY GitHub for knowledge we were finally rewarded with success.
-----
The connections are easy and just follow the AIY GitHub to wire the ACTIVATE BUTTON. We used IO Pin 16 on the RasPI for the relay control because it wasn't used by the AIY Voice Bonnet.  The AIY Voice Bonnet has four GPIO pins on board, but we could never get them to work.   Here's the summary:
-----
And here is the rig in action.  The Python code to make it run is below.

Demo at 20 WPM:
Demo at 12 WPM:
-----
Thanks and 73!   Here is our Python source code:

#!/usr/bin/env python3

#
#  July  2020
#  Voice to Morse Code Translator
#  WhiskeyTangoHotel.Com
#
# RaspberryPI Zero and AIY Voice Bonnet.
#
# Use Google Voice AIY to convert speech to text.
# Read that text file created and convert the text.
# Turn that text into to Morse Code "." and "-".
# Close a relay to transmit it over the air with a ham radio
#
#
# BTW, CW is fun to learn.
# *  --... / ...--   //  --... / ...--

# Some program set up and Define some variables

import os  # needed to delete the text file after reading
import time  # needed for delays
from datetime import datetime # need to calc run time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM) # Use BCM I/O board pin numbers

SendingLine = 0  # this is the text file line we will send
StartCW = 0    #  Char position to start sending from the parsed line
EndCW = 0       #  Char position to stop sending from the parsed line

keyer = 16  # Pin where the keyer relay is connected
GPIO.setup(keyer, GPIO.OUT)
GPIO.output(keyer, GPIO.LOW)  # make certain we are not keyed down

# Load the CW database 'dits' and 'dahs' for each Alphanumeric. Boring....
A = ".-"
B = "-..."
C = "-.-."
D = "-.."
E = "."
F = "..-."
G = "--."
H = "...."
I = ".."
J = ".---"
K = "-.-"
L = ".-.."
M = "--"
N = "-."
O = "---"
P = ".--."
Q = "--.-"
R = ".-."
S = "..."
T = "-"
U = "..-"
V = "...-"
W = ".--"
X = "-..-"
Y = "-.--"
Z = "--.."
period = ".-.-.-"
zero = "-----"
one = ".----"
two = "..---"
three = "...--"
four = "....-"
five = "....."
six = "-...."
seven = "--..."
eight = "---.."
nine = "----."

# WPM spacing calculated using the "Paris Standard"
# Per Morse code convention: 1 Dah = 3 Dits, LETTERS are spaced by one Dit, WORDS are spaced by one Dah.

WPM = 20.0    # Don't leave off the ".0" or WPM will be an integer (we want it to float)
Dit = 1.200 / WPM
Dah = Dit * 3.0
between_ditdah_spacing = Dit
between_character_spacing = Dit * 2
between_word_spacing = Dit * 3

"""Calls to Google Assistant GRPC recognizer."""

# We use AIY in the cloud to do the speech to text processing.  The RaspberryPI just can't do it as good.

# Copyright 2017 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import locale
import logging
import signal
import sys

from aiy.assistant.grpc import AssistantServiceClientWithLed
from aiy.board import Board

def volume(string):
    value = int(string)
    if value < 0 or value > 100:
        raise argparse.ArgumentTypeError('Volume must be in [0...100] range.')
    return value

def locale_language():
    language, _ = locale.getdefaultlocale()
    return language

def main():
    logging.basicConfig(level=logging.DEBUG)
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit(0))

    parser = argparse.ArgumentParser(description='Assistant service example.')
    parser.add_argument('--language', default=locale_language())
    parser.add_argument('--volume', type=volume, default=100)
    args = parser.parse_args()

    with Board() as board:
        assistant = AssistantServiceClientWithLed(board=board,
                                                  volume_percentage=args.volume,
                                                  language_code=args.language)
        while True:
            logging.info('Press button to start conversation...')
            board.button.wait_for_press()
            logging.info('Conversation started!')
            assistant.conversation()

            # Voice to CW routine starts here are the dim RED button is pressed
            # Button will turn bright RED while the rig is listening
            # Button will dim blink RED when ready for next speech input

            #  Read the text file created by the speech to text and get it ready to send as CW

            # Look for "Trigger Lines" in the text file.  Parse and send the line above it
            Trig1 = "Recording stopped."
            Trig2 = "End of audio request detected"
            Trig3 = "Updating conversation state"
            i = -1

            Get_file = "keywhat.txt"  # this is the text file created from AIY that we are going to process

            mylines = []
            with open (Get_file, 'rt') as myfile:    #'rt' means 'read text data'
                for line in myfile:
                    i = i + 1
                    mylines.append(line)
                   
                    # If we find one to the Trigger lines, we have passed the voice text by one line in the file
                    if mylines[i].find(Trig1) and mylines[i].find(Trig2) and mylines[i].find(Trig3)!= -1 :
                        SendingLine = i-1

            print (SendingLine, mylines[SendingLine])
            Send_text = mylines[SendingLine]

            #  Strip out only the text we want to send over the air
            StartCW = int((mylines[SendingLine].find('"')) + 1)
            EndCW = int(len(mylines[SendingLine]) - 3)
            # print StartCW, EndCW    #  used for debug

            SendasCW = Send_text[StartCW:EndCW]  # this is the text sent as CW

            #SendasCW = "test"  # used for debug
            SendasCW = (SendasCW.upper())  # and we always make certain it is all caps

            # We have the text.  Now parse it and send each letter as CW

            print ("Going to send: " + SendasCW + " at " + str(int(WPM)) + " WPM.")
            print ("-------------------------------------------")

            L = len(SendasCW)
            t0 = datetime.now()

            for x in range(0,L):  # Start character converstion to dit and dahs
                    Text_substring = SendasCW[x:x+1]
                    print ("Sending: " + Text_substring)   # for debug
                    if Text_substring == "0"  :  Text_substring = zero
                    if Text_substring == "1"  :  Text_substring = one
                    if Text_substring == "2"  :  Text_substring = two
                    if Text_substring == "3"  :  Text_substring = three
                    if Text_substring == "4"  :  Text_substring = four
                    if Text_substring == "5"  :  Text_substring = five
                    if Text_substring == "6"  :  Text_substring = six
                    if Text_substring == "7"  :  Text_substring = seven
                    if Text_substring == "8"  :  Text_substring = eight
                    if Text_substring == "9"  :  Text_substring = nine
                    if Text_substring == "."  :  Text_substring = period
                    if Text_substring == "A"  :  Text_substring = A
                    if Text_substring == "B"  :  Text_substring = B
                    if Text_substring == "C"  :  Text_substring = C
                    if Text_substring == "D"  :  Text_substring = D
                    if Text_substring == "E"  :  Text_substring = E
                    if Text_substring == "F"  :  Text_substring = F
                    if Text_substring == "G"  :  Text_substring = G
                    if Text_substring == "H"  :  Text_substring = H
                    if Text_substring == "I"  :  Text_substring = I
                    if Text_substring == "J"  :  Text_substring = J
                    if Text_substring == "K"  :  Text_substring = K
                    if Text_substring == "L"  :  Text_substring = L
                    if Text_substring == "M"  :  Text_substring = M
                    if Text_substring == "N"  :  Text_substring = N
                    if Text_substring == "O"  :  Text_substring = O
                    if Text_substring == "P"  :  Text_substring = P
                    if Text_substring == "Q"  :  Text_substring = Q
                    if Text_substring == "R"  :  Text_substring = R
                    if Text_substring == "S"  :  Text_substring = S
                    if Text_substring == "T"  :  Text_substring = T
                    if Text_substring == "U"  :  Text_substring = U
                    if Text_substring == "V"  :  Text_substring = V
                    if Text_substring == "W"  :  Text_substring = W
                    if Text_substring == "X"  :  Text_substring = X
                    if Text_substring == "Y"  :  Text_substring = Y
                    if Text_substring == "Z"  :  Text_substring = Z

                    #  Now send the dit/dahs for the Self Test Callsign Character
                    CW_length = int(len(Text_substring))

                    time.sleep(between_character_spacing) # a delay between characters

                    for j in range(0, CW_length):     # Send tones and blink LED in CW

                            Dit_or_Dah = Text_substring[j:j+1]

                            # HIGH closes a relay connect to the ham radio.  LOW opens the relay
                            # This relay closure is the same as pushing down on a straight key

                            if (Dit_or_Dah) == "."  :    # process a Dit
                                    print (Dit_or_Dah)
                                    GPIO.output(keyer, GPIO.HIGH)
                                    time.sleep(Dit) # a delay of "1 Dit" between dit/dahs in a character
                                    GPIO.output(keyer, GPIO.LOW)  #make certain we are not keyed down
                                    time.sleep(between_ditdah_spacing) # a delay bewteen characters
                                    # end if process a Dit

                            if (Dit_or_Dah) == "-"  :    # process a Dah
                                    print (Dit_or_Dah)
                                    GPIO.output(keyer, GPIO.HIGH)
                                    time.sleep(Dah) # a delay of "1 Dit" between dit/dahs in a character
                                    GPIO.output(keyer, GPIO.LOW)  #make certain we are not keyed down
                                    time.sleep(between_ditdah_spacing) # a delay bewteen characters
                                    # end if process a Dah

                            if (Dit_or_Dah) == " "  :    # process a SPACE (delay between words)
                                    print ("<SPACE>")
                                    GPIO.output(keyer, GPIO.LOW)  #make double certain we are not keyed down
                                    time.sleep(between_word_spacing) # a delay of "3 Dit" between words
                                    # end if process a SPACE


            GPIO.output(keyer, GPIO.LOW)  #make absolutely certain we are not keyed down

            t1 = datetime.now() - t0  # how long did it take to send?

            print ("-------------------------------------------")
            print ("Completed in " + str(t1) + " seconds at " + str(int(WPM)) + " WPM to send:")
            print (SendasCW)

            # close and delete voice to text cloud file to get ready for the next voice decode
            time.sleep(1)  # file handling delay, probably not needed
            myfile.close()
            os.remove(Get_file)
            time.sleep(1)  # file handling delay, probably not needed
            myfile = open(Get_file, "w")

if __name__ == '__main__':
    main()
-----

Thursday, November 5, 2015

NumerousApp: Easiest way to push RasPI data to your iPhone

-----
As a long time fan and user of IFTTT.com we would see the Numerous Channel from time to time and wonder what it did, how it could be used, etc.  The Numerous tagline is "Numerous follows the most important numbers in your life and keeps them all up to date, all in one place" and that certainly got our attention.
-----
Numerous runs on the iPhone and the app has a fantastic UI.  The developer must be some rare artist/SWE hybrid type or something.  The app flows well, takes advantage of new iOS features (TouchID, Apple Watch, etc.) and is easy to personalize.  The graphs are clear and it even lets you customize when you get notifications (value to big/small, value change, change percentage exceeded, comments updated) and more.  It short; it is a joy to use.

Also, there are dozens of pre-programmed "metrics" that you can follow.  Check them out.  They are interesting and useful, but could get boring and after a while you will want to roll your own metrics to track more personal data useful for your applications.
-----
That's where this blog post steps in.  Numerous is really easy to get your personal data into it.  After only a short time we had created "numerous"  Numerous App metrics to:
 - track temperature/humidity sensors scattered around the house
 - track Internet UP/DOWN status
 - track security system triggers
 - track UP/DOWN status of LAN devices (security cams, Raspbery PIs, Imps, etc.) 
 - several other miscellaneous things.... 
------
How to get data into Numerous?
One way is to use the Maker Channel on IFTTT.com as a trigger to the Numerous Channel on IFTTT.com.  This requires no coding and is an easy way to pass three variables in one Maker Channel event trigger.  If you want to include the Maker Channel trigger URL into a Python or PHP script the applications are endless.

If you don't mind a little extra Python code, the task is almost as easy and offers more power and flexibility.  To give credit where credit is due, it is made simple with the Python API App by outofmbufs. 

Here's my simple example using Python and a Raspberry PI to get data to the iPhone.  The example shows how to create a Numerous metric, write data and comments to it, and view a graph of the data.
-----
Run the Numerous app on the iPhone and create a new 'metric'.  See the "+" sign in the upper right.  Click that.
-----
Give your metric a name.  Don't worry about what you call it, you can change it later.  Remember, I told you the app was flexible....
 -----
After you create the metric you can customize the way it looks.  Change the title and description to suit your needs, add a meaningful background image, input data manually, set privacy and notification setting, etc.  There is even a way to set the units.  I put the units as "megaFonzies" in the example to denote how cool the Numerous App is.
-----
Making the appearance pretty is fine, but what we want is to feed the metric data from the Raspberry PI via a Python script.  For that you need two numbers:
                    - your personal and secret API key:  Get this by selecting "Settings/Developer Info" from the main screen of the Numerous app.  Don't share this API key.  It will start with "nmrs_" and is shown as "nmrs_xxxxxxxxxxx" in the source code example below.
                    - the METRIC ID that you are writing to:  In the Numerous app click on the tile for the metric you just created and customized.  In the upper right you will see the classic "menu bar" icon.  Click that then "Developer Info" to find your METRIC ID.

 -----
Modify the Python script below with your API Key and METRIC ID values and run it.  Your metric will start recording the values that you feed it:
-----
Want a graph of the data?  Turn the iPhone to landscape mode.  You can pinch to zoom in and out for both the X and Y axis!  How cool is that?  At least 52 megaFonzies!

-----
That's pretty much it.  The Python script example below will introduce you to all the basics.  It writes the system clock seconds value to the metric, but that could be any value your RasPI can produce or scrape from the web.  The Numerous metric in the example is 'Public' that anyone can view, update, and comment on.  Feel free to use it for testing.  Good luck!
-----
# WhiskeyTangoHotel.com [OCT2015]

# Super easy way to track/graph RasPI data to your mobile device using NumerousApp.
# This simple prog logs the "seconds" value of the localtime value to a Numerous metric.
# The Numerpus mobile app will log, track, and graph the data for you.
# Of course, this value could be a sensor reading, CPU usage, Core Temp, or any other numerical value.

# D/L the NumerousApp and install it on your mobile device.
#            iPhone: https://itunes.apple.com/us/app/numerous-lifes-most-important/id797642904?mt=8
#   Google Play: https://play.google.com/store/apps/details?id=com.numerousapp

#
# See http://numerousapp.com/ for full APIs and other really cool applications.

# https://github.com/outofmbufs/Nappy/blob/master/README.md

# Also check out the Numerous integration to IFTTT.com for more awesomeness.
# Numerous can act as a trigger and receive triggers from most IFTTT.com channels
# Teaming the Maker channel on IFTTT.com with Numerous is VERY powerful!

# 1st, do a one time install from the terminal using "sudo pip install numerous"
# Must have pip installed to do this: "sudo apt-get install python-pip"
# Needed for the following import line.
from numerous import Numerous   

import time                                              # Needed to get the system clock settings

MyKey = "nmrs_xxxxxxxxxxx"             # Your personal Numerous App API secret key.  

MyMetric = "2884060434653363716"    # Get this by clicking "Developer Info" from the numerous app
                                                                  # "2884060434653363716" is set as an open/public metric 

                                                                  # Anyone can write to, so you just enter your personal API key to test.
                                                                  # Link to load the metric to the app: https://nmrs.co/m/lwtm3kymv2tg
                                  
# Set up for calls
nr = Numerous(apiKey=MyKey)
metric = nr.metric(MyMetric)

# This will return the Numerous App Label string.
label = metric['label']

# This will return the current value of the metric
Current_value = str((metric.read()))
 

# Let's get the current seconds value of the system clock, convert that
# to a number, and write that seconds value to the Numerous metric
Timestamp_seconds = int(time.strftime("%S", time.localtime()))
metric.write(Timestamp_seconds)

# Uncomment the next line to add a like (a thumbs up) to the Numerous metric
#metric.like()

# This will add a comment to the Numerous metric
Comment_update = "System timestamp is: " + time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
metric.comment(Comment_update)

#Print info to the screen
print " "
print time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
print "Metric Name: " + label
print "----------------------"
print "Current value:" +  Current_value                # returns last current value of the Numerous metric   
print "New value: " + str(Timestamp_seconds)    # the value written to NumerousApp

-----

Tuesday, February 10, 2015

"Braille" Application for Pebble Smartwatch

-----
Braille is a Pebble smartwatch app that allows the vision impaired or anyone wanting to be discrete to check the time.  If you are interested in installing this free app on your smartwatch just go to the Pebble App store and search for "Braille" or click on the image below.

Braille is available on the Pebble appstore 
Give us a "like" heart while you're there.
-----
If you haven't heard about the Pebble smartwatch you probably have been living under a rock (or a pebble).  The Pebble has been one of the more successful KickStarter campaigns.  We got ours a few months ago and immediately put it to use.  Overall, the Pebble is a great value.
-----
One thing that is often handy is to be able to tell what time it is without looking at your watch.  This can be useful in business meetings, on dates, or when it is dark like at a movie.  Nothing says "I'd rather be doing something else" like checking the time.  Also, this is an absolute requirement for the vision impaired.

Before getting the Pebble I solved this problem with the very innovative Bradley Timepiece; a watch specifically designed for the vision impaired.  The challenge was to bring vision impaired features to the Pebble.  Fortunately, Pebble has a nice cloud based SDK that allows a developer to upload custom apps to the watch and make them available to the public.  With a bit of studying and some help from the Pebble Developer forums we were off and coding....
-----
How the "Braille" app works:

 - Press UP button: Count one watch vibration for each hour.

 - Press DOWN button: Count one watch vibration for every 10 minutes past the hour.  An additional quick two vibrations is added if the time is closer to the next larger 10 minute interval.  This gives a very quick resolution of <5 minutes (if you need to be more accurate, them it's time to look at your watch).  For example:  If it is 30-34 minutes past the hour the watch will give 3 long vibrations.  For 35-39 minutes past the hour the watch will give 3 long vibrations plus two quick vibrations.

- Press SELECT button:  Light the Pebble if the backlight is enabled.

- Bluetooth Alert:  "Braille" also does a vibration alert for 5 seconds and inverts the screen if Bluetooth is disconnected.  Handy for making sure you are not separated from your phone.  Upon re-connection "Braille" will vibration alert again and turn the screen back to normal.


For the curious, the icon on the watchface is in the Braille pattern for the letter "B".

Thanks for checking us out.  Enjoy the "Braille" app and let us know if you have any suggestions for improvement.