A tiny and open source CW keyer

att45While working on a new project, I had a choice of either plugging in the output of a free-standing CW keyer or embedding one into the project. I decided to go with the embedded keyer, but then had to either find or write one. K3NG has written a top-notch keyer based on the arduino platform. Its strengths are its modular design and extensive feature list; it can be compiled to run on a number of chips, with features only limited by the flash memory capacity of the targeted chip. However, it does have a certain minimal size, even when the number of features is stripped down to bare essentials. It would have been a reasonable choice if I could have used the same microcontroller for other functions, with a sizeable portion given over to the K3NG keyer, but in this case, I just want a dead-simple CW keyer.

I’ve used K1EL‘s K12 keyer in the past, as well as the N0XAS picokeyer, and this was more what I had in mind — a low power, small chip. After a quick search, I turned up the YACK (yet another CW keyer) project by Jan Lategahn, DK3LJ. He developed it for the ATtiny45, which has only 4k of flash and even less RAM and EEPROM storage. The project was not developed on the arduino platform, so the code has a much, much smaller footprint. Jan deposited the source code in a svn repository at Source Forge: http://sourceforge.net/projects/yack/. And, this isn’t a matter of trading off features, the YACK supports a number of keying modes, has many configurable features, a beacon mode and even does code practice. Pretty feature-rich, actually.

On that site, it is possible to download not only the source, but the compiled intel hex files for flash and eeprom. Naturally, I went for instant gratification, downloaded these, and flashed them onto the ATtiny using my arduino duemilanove as an in system programmer as previously (but this time, not having to mess with the fuse settings).

I stuck the programmed chip on a protoboard and tried it out. At first I wasn’t sure if it was working at all because it make no sound when powered up, but this was the way it was designed. Next, I tried closing the dit and dah paddles, and heard the expected tone on the piezo buzzer. Holding down either paddle resulted in tones of the correct duration, and holding both resulted in iambic mode B keying. So far, so good.

However, I could not get the keyer to work in command mode. Holding the command key just resulted in rapid clicking on the piezo, perhaps meaning that it was going into and coming out of command mode immediately since it sends an “e” when it exits command mode. Holding the button and closing one or the other paddles resulted in correct behavior: increasing or decreasing the sending speed.

I also noticed that a quick (less than a dit or dah length) tap on the paddles resulted in a shortened dit or dah being sent. This is not the expected behavior for keyers like this. Even if the paddle is released early, the generated tone should be of the correct length, one or three elements long.

jackyackprotoBoth these issues made the keyer unusable, but it seemed like at least the duration issue had been addressed in an earlier changelog entry, so I thought that perhaps the compiled file might not reflect the most recent and presumably most polished version of the project.

I recompiled the project from source (version 0.7) and noted that the resulting hex file was a bit different in size that the one I had previously used. I loaded both the flash and eep files onto the ATtiny and stuck it back in my prototyped keyer circuit. This time, it worked exactly as described in its documentation. Paddle clicks yielded dits and dahs of the right duration and the command button was fully functional. I ran through all the available commands, and each performed as advertised. I ran the keyer output to my FT817, which I had set to make a tone but not transmit when keyed. I had no problems using the keyer to send at various speeds. All good.

I made some minor tweaks to the code and revised the version to 0.75.  This shouldn’t be taken as any sort of official version number, but I needed some way of setting this revision apart from DK3LJ’s original code. I didn’t fix any bugs or make any real improvements, I just optimized the configuration for my purposes. Mostly, this is a matter of taste, and of being used to certain conventions from other keyers. I left most of the user interface alone, though, since I thought Jan had made some very reasonable choices in how he set it up.

Here is a list of what I changed:

  • Power up message: Now, when powered on, the keyer sends a “73” to let you know that it is alive. This is tone-only, no keying. This provides a quick check that the battery is okay and that everything is hooked up right, which could be helpful to anyone building this circuit, or someone who is on their way out the door for field operation and wants to make sure their keyer is healthy.
  • Positive Transmit keying default: In most cases, people will want to positively key their rigs, but the default formerly was that the transmit line would go low to key and remain pulled up to VCC otherwise. I flipped this around, which let me use a cheap NPN circuit with open collector to key.
  • Side tone: For the project that I’m using, a 700Hz side tone is optimal — the keyer is followed by a filter with a 700Hz center frequency, so I changed it in the default settings. I actually prefer 800Hz, but the tone can be changed on the fly, so this isn’t really an issue.
  • Keying mode: This is entirely personal preference – I made the default keying mode iambic A rather than B, because I think it is easier for someone accustomed to Iambic B to send A rather that the reverse. People who are used to iambic A are thrown by the “extra” character that B generates.
  • Speed: I bumped the default speed to 15 wpm. Maybe 12 wpm is more inclusive, but 15 wpm works better. Also, anyone can increase or decrease the speed by holding the command button and pressing one paddle or the other. It is not necessary to send a character correctly to change the speed, so I think I haven’t really excluded slower operators.
  • Exiting command mode sound: Previously, the keyer sent an “e” to indicate that someone intentionally exited command mode by tapping the command button a second time or that the command mode had timed out due to inactivity. I found the “e” a bit short and thought it could be missed. Also, at times I forgot I was in command mode, and whatever else I was doing, it just sounded like an extra dit got in there, without making much sense. I changed the exit sound to “sk”, similar to the picokeyer, because it has a more characteristic sound and would not be expected in the audio stream except at the end of a QSO, so it is more noticeable.
  • Acknowledgment sound : After a valid command is given, the keyer used to acknowledge it with an “OK”. I changed this to “R” because it is shorter and “R” connotes “Roger, received.” This borrows a bit from the K1EL keyers. I only steal from the best.

After testing out my minor revision, I uploaded the project to a repository at google code.  The project files include a schematic in Eagle format as well as a picture of the schematic as a *.png file.  For those who would rather not compile from source, I’ve uploaded the compiled intel hex files on this server: main.hex and main.eep.  I loaded the files into a stock ATtiny45, right out of the box. No need to mess with any of the fuses — the chip is factory configured to run on its internal 8Mhz clock, scaled to 1Mhz.

The last time I checked on mouser, these chips were a bit over a dollar each and the even more spacious ATtiny85s were a bit less — and that’s unit pricing — buy a bunch and the price falls off considerably. The rest of the components in the keyer are dirt cheap and easily substituted. Making a first-rate keyer these days is not an expensive proposition.

[Update 17 May 2016]

Unfortunately, the Google Code repository no longer exists, but before it went down, I migrated everything over to github, so project files are now over there: https://github.com/dhakajack/jackyack.

Also see my later blog post about design of a PC-board for this project.

[Update 5 June 2016]

KC9ON has put together some nice kits based on the same code base, but with added features, a code practice oscillator and a small keyer board.

[Update 03 April 2024]

The Tortugas-CW group has put together a nice kit that includes a touch-sensitive iambic paddle based on a design by VK3IL and incorporates an improved version of the YACK-based keyer. See Part1 and Part2 on Youtube.

31 thoughts on “A tiny and open source CW keyer”

  1. What tool(s) did you use for development of this (atmel studio?) If so, do you have any project files? Or did you must do it with the gcc cross compiler?

  2. Hi Kevin,

    I’m working on a Mac, so I downloaded the crosspack development environment for OSX from the developers at Objective Development. Here are couple tutorials (1), (2) that go into more detail about installation.

    I started with the c code from DK3LJ, and my modified version is on the repository. In the download section of the repository, you’ll only see the final products, i.e., the files to burn to the chip. But in the “source” section of the repository, you can see the header and main file, plus the make file.

    In working on the project, I tended to work from the command line with a text editor and then ran “make” to compile the project, yielding the hex and eep files.

    To upload those files to the ATTINY, I don’t have a hardware programmer, so I used an Arduino duemilanove, which had been programmed to work as an in system programmer (this sketch ships with the Arduino). I then stuck the ATtiny on another board, and jumpered across to it, as I had done for a previous project.

    Then, to push the code from the computer, through the Arduino (playing the part of an ISP), and into the ATtiny, I used avrdude from the command line. Here are the commands that I used, although your set up will likely be different:

    avrdude -b 19200 -p attiny45 -P /dev/cu.usbserial-A9007MkN -c avrisp -U flash:w:main.hex

    avrdude -b 19200 -p attiny45 -P /dev/cu.usbserial-A9007MkN -c avrisp -U eeprom:w:main.eep

    I’m not familiar with the tools that would be used on other platforms, although it looks like WinAVR would be the way to go on Windows. I believe there are some AVR tutorials on hackaday and adafruit that go into more detail for Windows or Linux tool chains.

    Good luck!

    – Jack

  3. Back again, after 2 years – I have a TS520 and I think I have to key it differently. I was using the NPN circuit you mentioned for my TS430 but no love for the 520.

    I have 60v across the key jack on the TS520

  4. Right, you’ll need a bit more to key the TS520. Looks like various grid block keyer adapters I’ve seen on the internet use a PNP transitor to key such rigs. You’ll need a PNP rated for that voltage, plus a bit of overhead to be safe. You could adapt the keyer to drive an optoisolator rather than the base of the NPN transitor in the original design. That would then drive the base of the PNP to actually key the rig. Good luck!

  5. Just stumbled on this nice little page.

    For Development of YACK – I use Atmel Studio on Windows (Free) but I think you can use the Atmel version of GCC stand alone on Linux/Apple as well. Check out AVRFreaks, they have good info on Atmel programming and using it on other systems. Will be happy to send anyone a zip file all set up in Atmel Studio. A cheap programmer from Sparkfun (AVR pocket programmer $15) or the NT7S EtherProg (Etherkit $9) works fine.

    Yes the TS-520 uses negative grid keying (and other old tube rigs like the FT-101, HW-101……). If you look at my 3CPO keyer schematic you can grab the circuit I use that handles both positive and negative keying. If you are only using negative keying you only need the circuit starting from R507 out to the key jack (the PNP/NPN transistor pair), the diode D502 is not needed either.
    73 John kc9on

  6. Nice! I used your mods, but adapted the code for the ATTINY85 by adding four-100 character memories rather than two. I also restored the defaults to IambicB mode and 800Hz sidetone. The “R” reset command correctly restores these defaults.

    Memory 1 is now activated with “E”, Memory 2 with “I”, Memory 3 with “T” and Memory 4 with “M”. I needed to remap the keying invert function from “I” to the unused “J” to use this scheme.

    The Beacon function now works with Memory 4 instead of Memory 2.



  7. One thing I noticed is that certain CW prosigns and punctuation did not record in the memories correctly. I added those into the yack.c file.

    Added prosigns/characters are:
    KN -.–.
    BK -…-.-
    ! —.
    , –..–
    : —…
    ; -.-.-.

    They now record and play back correctly.


  8. One other bug in yack.c….The code speed changes do not save to EEPROM!

    The fix is to move the line (in the yackctrlkey function):

    yacksave(); // In case we had a speed change

    …immediately after the function key debounce. I think the dirtyflag was getting reset before yacksave was called.

    _delay_ms(50); // Trailing edge debounce
    yacksave(); // In case we had a speed change


  9. FB, Don. There is always room for improvement. Is your revised version of the code available somewhere online? I could put a pointer to it in case someone’s web search ends up here. If it’s not online and you’d like me to make it available here, let me know and I can arrange to stick it on this server. TNX ES 73, Jack 5R8SV/AI4SV

  10. If you could post my changes on your server, that would be great.

    You can grab the zip file with my changes at:


    Note that this version will only work on the attiny85 due to the 2 additional memories.

    I gave my version the number 0.85.

    I left the compiled main.hex and main.eep in the zip file. I also updated the changelog file.

    Released V. 0.85 – 22.12.2016 – Don Froula, WD9DMP
    + Default mode Iambic B
    + Default side tone 800 Hz
    + Fixed changes in speed not saving to EEPROM after command key release
    + Fixed changes in sidetone not saving to EEPROM on timeout (worked only when command key pressed to exit)
    + Added 2 additional 100 character memories, 3 and 4. Now works on ATTINY85 only
    + Changed directives for compilation on ATTINY85
    + Changed commands for memory playback: “E” for memory 1, “I” for memory 2, “T” for memory 3, “M” for memory 4
    + Added commands “3” and “4” for recording new memories
    + Changed command to change keying polarity from “I” to “F” (“Flip”) to accomodate use of “I” for memory playback
    + Unit enters low power mode after 60 seconds, not 30 as specified in yack.h. Not sure why, as other timings are ok so left it
    + Measured current draw at 4.5 volts is about 1 ma in active idle. In power down mode, draw is only .3 microamps.
    + Added additional punctuation recognition for memory recording. Now: ? . / !(American and Continental) , : ; ” $ ‘ ( ) – @ _ paragraphbreak = +
    + Added additional prosign recognition for memory recording, Now: BT SK AR KN BK AS KA VE AA



  11. Jack, would you mind picking up v0.86 from http://projectmf.homelinux.com/keyer/ ?

    0.85 works ok, but I made the following two changes for cleaner operation:

    + Added a call to save changes to EEPROM while in command mode loop if DIRTYFLAG set for better EEPROM parm save reliability.
    + Changed “SK” response when leaving command mode to “#” which now decodes to proper SK without intercharacter space.

    I also updated the version command to return the proper version string, as well as the changelog and operating manual files.



  12. Hi Don. Great video — I am impressed that you recorded it with just two continuous takes. This is a great demo of all the functionality packed into that chip. Thanks also for mentioning the PC board from OSH Park. I wasn’t sure that anyone else had ever ordered one; glad it worked.

    I see that you are up to v0.87, so I have mirrored that latest version here as well:


  13. Jack & Don –
    I just finished building and testing your excellent JackYack keyer project – it is absolutely fantastic! Thanks to both of you for your great work on design and coding (and for sharing it all with the world)! It was a *very* fun and rewarding first ATTINY85 project for me and it really opened my eyes to some of what that little chip can do. I built the keyer into a small, clear, plastic toothpick box fed with an external 9v battery & added a small on-board voltage regulator circuit to my board to bring the supply down to 5v.
    Thanks again!
    Ernie, KG9NI

  14. Check back in a few weeks for an upgrade to the keyer at kc9on.com. Upgrades include a better trainer. The old trainer only performed random 2×2 calls. The new version will generate anything from a 1×1 to a 2×3 call and a couple percent of the time throw in a DX type prefix/suffx call such as WA1ABC/VK7 or VE2/VP9ZZ. Also will be a new mod keep the transmit keyed down during memory/beacon playback, mainly to connect to an FM radio for fox hunting. Will take a bit to get the new chips flashed, manuals and sourced updated, etc. 73

  15. I tried the original version of YACK and have no problems with compiling it on linux, simply downloaded avr-gcc avr-libc and avr-binutils packages from epel repository (I am using Centos7), using make gave me some errors near extcoff, which is something I really do not need, so I’ve removed it from :all. And also had to modify chip burning settings, because I have usbasp programmer, but the Makefile is prepared for stk500 (I have stk500 too, but…)

    But had another issue. Adding small ceramic capacitor to SET button leads to that everytime I turn the keyer on, it skips into set mode. Adding simple while cycle into init section, which waits for 1 on all inputs solved it perfectly, but in my opinion, this should be included in the source code.

  16. I’m trying to push the code into 2 different ATTiny85 s but I’m stuck: I get an error from avrdude “not in sync” Help?

  17. Hi Roy,

    I’m afraid I’m not all that versed in avr-dude. I use it rarely enough that I have to relearn it each time. Sorry I can’t help on this one.

    – Jack

  18. That sounds reasonable — I don’t anticipate doing an update on this project, but if any one else does, it’s a good suggestion. – Jack

  19. Hi Jack & Don,
    Just wanted to thank both of you for the hard work you have put into this project. I Help run FISTS UK CW Club and I WAS writing a keyer prog myself when I came across yours for the Attiny85, needless to say I am not writing one now!
    Your is great and with Dons extra mods to the code just wat I wanted.
    I intend to offer pre programmed chips for cub members and currently doing a little guide to building one too.
    Thanks again great little project.
    73 Paul M0BMN

  20. I am curious if you are aware of anyone having luck porting this to a Arduino Nano.


    James WD0JB

  21. Hi James,

    No, this was written pretty specifically to fit into an ATTINY. When I was thinking about writing a keyer I found some already written code that did 99% of what I wanted and just modified that last 1% or so. For the nano, I think a good starting place would be the keyer written by K3NG.



  22. Have you run across a keyer that can be operated with no additional buttons, eg command mode from the paddles? I’d like to install something in an older rig without having to drill holes in the case. /John

  23. Hi John,

    I’ve been tied up most of the summer, so this reply probably comes so late that it won’t be helpful, but… No, I haven’t seen a keyer like that. To make that work, you’d either need a long sequence to put it into command mode or perhaps rely on a long press on one or both paddles. In either case, the keyer would send the corresponding code elements before going into command mode. For the option with a long special sequence, there’s also the challenge of keying it properly, which could get tricky.

    If you really wanted to set something like this up, one method that occurs to me would be to tie one paddle to a capacitor that either charges or discharges and incorporate a 555 timer in one-shot mode such that when triggered by a long paddle closure, it would effectively close the “command button”. To avoid transmitting garbage while that paddle is held down, you would have to disable rig transmit for a moment.

  24. Hi
    Can I get last version of working hex end eep file ? I tried to uploud ATtiny85 using avr-gcc and avrdude ver. 0.7 …. The sound on buzzer was pretty tiny not understandable… after I tried uploud main.c, yack.c and yack.h using arduino IDE with usbasp programmer…. so finnaly get it’s on device with strong, little high sound on the buzzer…. but there is a speed to fast…. the 6s time for return back from command mode was a pure a half second….. enough to try only “S” command… HI….
    I don’t know how to make a hex file from c file…. besides there is a three files not only one…..
    I will satisfied with an ino file instead too…

    Davor, 9a4kj

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.