Introduction: Gboard Input Switch for Morse Code

This year, 2018, at Google I/O, Google introduced Morse code for the Gboard. This has the potential for bringing Morse code technology to thousands of disabled people who would otherwise be unable to access an Android phone, or discover if Morse code is a viable method for computer access.

The intent of this Instructable is to provide an example of how easy it can be to create custom switches to interface with the Gboard.

Gboard's Morse code can be triggered by sending periods for dots and hyphens for dashes to the Android device.

You can try this out easily by hooking any USB keyboard to an Android device using an OTG cable.

But, many people with disabilities cannot use a standard keyboard. So, in this project, we will be creating a simple device to emulate a USB keyboard by sending dots and dashes through an Arduino's USB port.

In addition, Androids have the ability to access menu items using accessibility scanning. To accomplish this, we just need to send a different set of keyboard characters through the same USB port.

Finally, we will be demonstrating how to use open collector transistors to close switches, which could be used on any other device that uses switch closures as input. This could allow a person to use their native communication device while independently trying out the Gboard. Or, they could maybe use it to trigger a trigger a toy, or any other device.

To see some great Morse code toys in action, see: MorsePlay Demo by Adaptive Design Association. You can also get the code for the toys from GitHub

Your parts list is as follows:

  • An Arduino Leonardo. It is important to use the Leonardo, since it has native USB HID support, without having to use additional boards or shields. Other boards might work, based on the ATmega32u4, but they haven't been tested. The Arduino Uno will NOT work.
  • 2 Female 1/8" mono input plugs. These will accept the switch inputs.
  • 1 Male 1/8" stereo output plugs. This will be plugged into whatever device you want to relay the switch closures to. Of course, you can also use 2 mono plugs as well.
  • 2 2N2222 transistors.
  • A normally open momentary push button. This will be used to switch states.
  • 1 5V 2 Terminals Passive Electronic Alarm Buzzer
  • 1 10K Ohm potentiometer. This is used to control the output rate of the dots and dashes to the gBoard.
  • 2 330 Ohm resistors.
  • A breadboard
  • Various Jumper wires.

A complete cart of all required parts can be obtained from DigiKey: Digikey Parts List

Step 1: Fritzing Diagram...

If you are familiar with electronics, and just want to dive in an start assembling the parts, here is the Fritzing diagram.

You can assemble your project, and skip right to step X, where we will start programming it.

Step 2: Create Your Input Wires

Here, we are just splicing our input wires on to jumper wires, which will later be plugged into the breadboard. Both input share the same ground.

Step 3: Create Your Output Wires.

Just splice the male plug to the jumper wires.

Step 4: Create the Wires That Will Go From the Pins on the Arduino to the Transistors

I just split to jumper wires, and put a 330 Ohm resistor between two of them. Make sure that all current in the wires is flowing through the resistor, and the resistor is not shorted.

I just found this to be a more reliable method for adding the resistors than adding them to the breadboard.

Step 5: Splice Jumper Wires on to Your Push Button.

That's it. All the prep work is done, and it's now time to start adding the components to the Arduino and the Breadboard.

Step 6: Add a Transistor to the Breadboard...

Put your NPN 2n2222 transistor on the breadboard.

The emitter goes to ground.

The base is going to go to an IO pin on the Arduino through a 330 Ohm resistor. We are using the wire we created with the resistor in step 3.

The collector goes to one of the wires for output that we created in step 2.

This is a wonderfully useful little circuit that can be used in a variety of applications where you want to automate control of a manual switch closure.

Just a little bit of how it works, that isn't necessary to understand...

The I/O pin sends current to the base through a resistor. The resistor keeps the current low enough to turn the transistor on, without going into saturation.

Step 7: Add Another Transistor to the Breadboard...

Put your NPN 2n2222 transistor on the breadboard.

We are hooking it up the same way...

The emitter goes to ground.

The base is going to go to an IO pin on the Arduino through a 330 Ohm resistor, using the wire created in step 3.

The collector goes to the other wire for output that we created in step 2.

We are also putting the ground wire for the output wire to the ground track of the breadboard at this time.

Notice that, at this time, we are just leaving the ground wires dangling.

We will tie them together later on.

Step 8: Start Hooking Up the Arduino

Remember when we talked about how the transistors worked. The Arduino sends voltage through a resistor so that it activates the transistor without saturating it.

We hook one of the output wires with the resistor to PIN 2. We hook the other one up to PIN 3.

Later, when we program the Arduino, we will send HIGH to the appropriate pin when we want to close the switch.

Step 9: Add the Input Switch Pins to the Arduino...

Next plug the pins for the switch input into the Arduino.

There will be 3 wires.

One of the channels goes to PIN 5. The other channel goes to PIN 6.

The ground goes to a ground on the Arduino.

We are just using this ground because it is convenient. We have other grounds that we will be tying together later.

When we program the Arduino, we will be using an internal PULLUP resistor. When we see this goes low. We are going to do stuff.

We will send dots or dashes to the USB port. Or, we will send other characters to the USB port. Or, we will relay the switch closures to the transistors. In other applications, we could send data to an RF module.

Step 10: Add the Potentiometer...

The left wire goes to Ground.

The middle wire goes to PIN A0 on the Arduino.

The right wire goes to 5V on the Arduino.

The potentiometer in this project is to control the rate of dits and dahs (periods and hyphens) that the device is sending out.

Early in the development, the GBoard Morse code keyboard did not have the ability to send continuous dits and dahs when the key was held. Therefore, the functionality of this device was to mimic that behavior. And the potentiometer is used to set the rate in which those dits and dahs are repeated.

Current releases of the GBoard Morse code keyboard do support the functionality of producing multiple dits and dahs when the key is held, so the potentiometer to control the rate is now optional. (You can control the rate of the repeat in the GBoard settings).

But, the functionality is being retained because it remains useful to be able to control the speed just by turning the dial.

Step 11: Add the Speaker...

Positive goes to PIN 9.

Negative goes to ground.

The Speaker provides feedback for the keying of the dits and the dahs. It also provides a tone to signal changing the state.

Step 12: Wire the Push Button Which Changes States...

This project uses a momentary, normally open push button to change states.

One lead of the switch goes to PIN 12.

The other lead goes to ground.

Step 13: Finally, Tie Together All of Your Ground Wires.

Make sure that all of your ground wires are tied together, and lead to a ground on an Arduino.

Step 14: Program the Arduino

The full source code for this project is included in the next step.

There is a lot of functionality in that source code that is not being included, and mentioned in this project.

In the source code is the ability to send switch states wirelessly through RF.

It also has the ability to switch states by holding down a switch longer than 3 seconds. This did not work well with the menu scanning features, so I switched to a push button to switch states.

The unused functionality is disabled in the source; either by commenting it out, or if statements or compiler directives.

I'm leaving it in the final source in case it is of interest to anyone, but I won't be explaining it's functionality here.

Also in the source is a lot of code for debouncing. When a mechanical switch is closed, as it is making contact, it often makes very short opening and closings before it reaches it's final state. This can produce unintentional double or triple beeps. This can be horribly frustrating to a disabled person attempting to type with Morse code.

I was debugging the code with particularly noisy switches, which needed a lot of debouncing. So, the code handles it pretty aggressively, but you can set the amount of debouncing with a variable in the code.

I will not be calling out the debouncing code, because it can be distracting.

Necessary library for he keyboard functions:

I've mentioned it before, but if you are using an Arduino other than the Leonardo, your project might fail on this statement.

#include <Keyboard.h>

Next, we are going to assign our PINS to variables to be used later in the program.

const int DitIn = 5;  //This is the digital pin to connect the dit switch
const int DahIn = 6; //This is the digitial pin to connect the dah switch
const int DitOut = 2; //When in TM mode, this sends the dit switch states to the TM. const int DahOut = 3; //When in TM mode, this sends the dah switch states to the TM. const int SpeakerOut = 9; //The speaker pin. const int DitPot = A0; //Analog port for Dit Potent to adjust speed. const int DahPot = A1; //Analog port for Dah Potent to adjust speed. const int SwitchBut = 12; // Switch states with button

The following are configuration variables used in the program.

//These are the codes that the receiver is expecting to receive for DIT's and DAH's.
const int DitID = 'A'; const int DahID = 'B';
//When the device is in mode STATE_GB, these define the frequency for sending //the Dit and Dah keyboard characters
const int DitTime = 400;
const int DahTime = 450;

//These set what character to send when in GB mode.
const char DitChar = '.';
const char DahChar = '-';
//These set what characters to send when in Scanning mode. const char ScanCode1 = KEY_F11;
const char ScanCode2 = KEY_F12;

//These are the frequencies that will be played when sounding.
//Dit and Dah Tones
const int DitTone = 880;
const int DahTone = 500;
//This is the factor used to adjust the sensitivity of the Potent for speed adjustments const int SpeedSense = 4;

Variable declarations and assignments

//The available states that are achieved by holding down a switch.
//TODO: These should be put into an array. const int STATE_PSY = 1; const int STATE_GB = 2; const int STATE_SCAN = 3; const int STATE_RF1 = 4; //Tracks what state is it in int State = STATE_PSY; //Various housekeeping variables. int CurrentDitTime = DitTime; int CurrentDahTime = DahTime;
int LastSwitchBut = 1;
int ThisSwitchBut = 1; //These variables keep track of how long a switch was held.
unsigned long DitDownStart = 0;
unsigned long DahDownStart = 0;
//Used for properly handling strings char buf[4];

//When we are in a keyboard mode, we want to send the first character
//as soon as a switch is hit. But only once. This keeps track of that for us.
int DitPlayFirst = true;
int DahPlayFirst = true;

The setup Function

void setup() 
{
//Set up our pins
pinMode(DitIn, INPUT_PULLUP);
pinMode(DahIn, INPUT_PULLUP);
pinMode(DitOut,OUTPUT);
pinMode(DahOut,OUTPUT);
pinMode(SpeakerOut, OUTPUT);
pinMode(SwitchBut, INPUT_PULLUP);
//Begin the keyboard handler
Keyboard.begin();
}

The rest of the code is contained in the "Loop()" Function

Read the value of the state switching button.

ThisSwitchBut = digitalRead(SwitchBut);

If it's been closed (LOW), then increment the state (see later function), put a bit of a delay in there, and make sure that we are only executing it once per closure.

if((ThisSwitchBut == LOW) && (LastSwitchBut != ThisSwitchBut) )
{ IncrementState(); delay(500); }
LastSwitchBut = ThisSwitchBut;

Check to see if the Dit switch has been closed.

if (digitalRead(DitIn) == LOW)
{

Then, we see what State the device is in.

If it is the PHYSICAL state, then we are just going to relay the switch closures to the transistor

 if (State == STATE_PSY)     { 
//Close the physical Switch
digitalWrite(DitOut,HIGH);
}

If it is in GBoard State OR the Scan State, then we are going to be doing keyboard events.

 if (State == STATE_GB || State == STATE_SCAN)
    {

We want to get a character out there, right away. When a switch is hit, no delay routines should get in the way.

if (DitPlayFirst){
DitPlayFirst = false;

Then, send out the appropriate character, depending on what state we are in

if (State == STATE_GB){      
Keyboard.write(DitChar); } else if (State == STATE_SCAN){ Keyboard.write(ScanCode1); }

Play the dit tone, with a duration of 1/2 the dit time.

 playsnd(DitTone,CurrentDitTime / 2);
}

Now, we are going to loop, playing that tone over and over again while the switch is down.

while ((digitalRead(DitIn) == LOW)){ 

Read the value of the Potentiometer so that we know how fast to repeat the dits.

CurrentDitTime = DitTime - analogRead(DitPot) / SpeedSense;

Here, check to make sure that enough time has elapsed to beep another beep.

if (millis() - lastDebounceTime > CurrentDitTime){

If so, then depending on what state we are in, send the appropriate character.

if (State == STATE_GB){
	Keyboard.write(DitChar);
}
else if (State == STATE_SCAN){
	Keyboard.write(ScanCode1);
}

And then play a sound

playsnd(DitTone, CurrentDitTime /2); 

We wrap up by keeping track of our timer.

(Note: I have omitted the debounce code in this annotation. But, I use the same variable to keep track of the repeating beeps as I do for the debounce. That's why it's called "lastDebounceTime".

lastDebounceTime = millis();
}             

The code for the DAH switch is similar. I'm not going to annotate it here.

There is also a function to play the sound. It adds the ability to Mute the sound, and output some debug data.

Step 15: Full Source Code

The full source code for this project is attached here, and can be downloaded from GitHub