Repeaters: Gloversville, NY 146.700 (-) PL: 123.0 | Town of Florida, Amsterdam NY 146.970 (-) PL: 123.0 | Northville, NY 146.835 (-) PL: 123.0

Arduino Based CW decoder. *UPDATED*

This Arduino based CW decoder was developed by Hjalmar, OZ1JHM.

Hjalmar published the schematics and source code for everyone to use.

The “sketch” programmed in to the Arduino is based on the Goertzel Algorithm for those who are interested on how it works.

To increase the flexibility a microphone like this can be added to the circuitry.

If you are interested on building this CW decoder, but need assistance or have any questions feel free to send an email to: [webmaster (at) k2jji (dot) org].

UPDATED 9/18/2015:

This post was made 1 year ago exactly to the date but it is the most viewed article on this site. I receive a lot of questions on this project. Most of the questions I get are about how to connect the components. I have deiced to post the schematics of my implementation and also the code I use on my version of this project. In addition I also designed a single sided PCB version of the board for those who want to give it a try. You can etch, CNC machine or send out the files for fabrication.

My version allows you to select the target frequency and also the bandwidth via jumpers.

Jumpers configuration:

BW 220 Hz 140 Hz 94 Hz 70 Hz Target
496 Hz 558 Hz 774 Hz 992 Hz

Source code – Codebender:

Schematics and Layout – Upverter:

Top View: Bottom View:

Completed PCB. If you want to download the fabrication files click here and scroll down to the Export Data Design section.

If you have any questions or comments, feel free to leave your message below. Let me know what you do with this.

34 thoughts on “Arduino Based CW decoder. *UPDATED*”

  1. Trial and Error, as usual…
    …came across this page and tried the above circuit. Wonder though whether the schematic should not be updated: Connections as shown above do not work and cannot. Did some reeingneering with a scope and succeeded as follows: Connect the display as per Arduinos example LCD sketch ‘Hello world’ and presto, there is the WPM display. Put morse input to pin A0 an you’ll get the characters displayed as intended. Would be nice to know where the jumpers for speed and bandwidth are now located.
    Thanks anyway, I put the page into my bookmarks and check occasionally.

    • Hi Guenter, sorry to hear you had issues getting this to work. I made PCB’s out of that exact same schematics and it works like it suppose to. What arduino are you using? I am using the arduino nano.

  2. Hi,
    I’m using the Arduino Nano too (ATMega 328 CPU) and a 2 by 16 display that claims to be conforming to the 14/16-pin HD44780 quasi-standard. When starting the sketch as above I noticed that the signals on the Arduino pins came out on different pins than noted in the above schematic. On my scope they looked rather familiar though to another HD44780 lying on the bench nearby. The rest was trial and error as above. Looks like someone changed the display type in the sketches code and forgot to update the schematic. Another possibility might be the library. Anyway, its already working… Bit late in the night over here, will put this to an on-the-air test with my station tomorrow.

  3. Hi, gm all,
    finally succeeded in locating a display (out of the junk box) that complied with my libs and the above circuit. Can now confirm it’s working 😉 with the above pinout. I generally buy displays at the surplus dealer’s. Furthermore, the IDE seems to take whatever library its finding first. In my case it tried to use an I2C-Display, obviously failing. So please accept my apologies, those were probably all my faults. The OS running the IDE here is Ubuntu, so file names are case sensitve. Looks like I blundered there…

  4. Hi all,

    I’m a newbie with the Ardino and would like to make this CW decoder that will work the first time out. Does anyone have a kit or an completed project already built that can be purchased.

    I do not know if I should purchase a Ardino UNO R3 or the Nano. Any help will be greatly appreciated….Thank you

    • I, too, am going to build this. A great deal of wonderful work has already gone into it but a word of advice. It’s a lofty goal to say: “Will work the first time out”. We need to manage our expectations especially if new to Arduino. I have been working with Arduino UNO, MEGA, Tiny85s, etc. and NEVER do things work the “first time out”. They require “tweaking”. Do the best you can but, for example, the last UNO I purchased (not a clone) was DOA from Amazon and required replacement. I didn’t know at first whether it was my project on the UNO and that alone took some time — not working “the first time out”. Projects like these seldom (if ever) work perfectly the first time. Stay with it = that’s the fun of it.

  5. Hi,

    Your CW decoder is very interesting and simple to build. Congratulations for the code.
    My question is about the “Frequency” and the “Bandwidth”.
    I would like to decode Morse code at 437 Hz. What is the variable or the parameter in the Arduino scketch I have to change ?

    Greetings from France,


    • Hi, I m glad that you find this decoder interesting. To select the different bandwidth and frequency you can set pins A0, A1, A2 an A3 to either GND or VCC. To decode at 437Hz, I would think setting A0 = GND, A1 = VCC, A2 = GND and A3 = GND should get it to work. Look at the table on the page. Try it out and let me know.
      Edgar / KC2UEZ

  6. Hi Edgar,
    I received my Arduino UNO and the tiny microphone. After some ajustments (frequency and bandwidth), the decoder works fine.
    Very nice job.


    • Hi Daniel,

      Glad you manage to get this going. Yes I do see some characters printed wrong from time to time, specially if the code being decode is made by hand or if the band conditions are not that great. Hopefully you can make the message with what you can copy.

      ~Edgar KC2UEZ

    • No sorry, only one was made for the prototype. Feel free to upload the gerber files from upverter and upload them to oshpark. It will take like 2 weeks to have them made.

  7. Hi,
    could you please explain the parameters “sampling_freq” and “n” a little bit more? I´m currently trying to convert this nice software to work on a STM32 board but until now it´s not working. I measured the sampling time of 128 samples which is about 595 microseconds.

    • Hello Jens,

      Are you using mbed? which STM32 are you using, I have a few evaluation boards myself.
      So as you may know, I am not the original programmer of this code, I ported it to work on an arduino NANO. The original programmer is Hjalmar, OZ1JHM, he will be way more qualify to answers these questions, but it is my understanding that the sampling freq is derived from the analogRead() function which takes 100 microseconds leading to a theoretical sampling rate of 9600 Hz. You can read more about this topic here:,6549.0.html

      In practice the real world sampling rate is 8928 hz when it is read on the for loop like it is done on line 194 of the code above. This reads the the analog value and then it is stored “n” times into the buffer. N determines how many samples are done, each takes 112us. Then the buffer is processed the morse code letter is presented. I don’t know how fast does your STM32 samples each ADC.

      I hope this helps, but if not feel free to ask.

  8. I´m using STM32F103C8T6 which costs about 2€ and it can be programmed with Arduino! Because the AVR-Arduino is only 16 MHz I wanted to use this software on the STM32 board which is a 32 Bit CPU running at 72 MHz! As I mentioned before the ADC sampling is about 4.6 microseconds! I don´t use the Arduino function analogRead, because it was too slow. I found another easy example to get the ADC data in one simple loop without the Arduino overhead. I think I have to ask Hjalmar, if you don´t know more details. My intention is to modify the code only with some preprocessor defines to compile it either for AVR or STM controller. Should I tell you if I got it working?

    • Hi, I also have a small board with a F103C8T6 I ordered from Aliexpress it was very cheap and loaded with the mapleleaf mini bootloader, but I reprogrammed the bootloader with the STM32duino bootloader, so I am kind of familiar with what you are doing.

      I would suggest to ask Hjalmar for some more detailed input. A mutual friend informed me that Hjalmar has already ported the code to an arm, but I am not sure if STM32 or some other vendor.

      Let me know if you get it to work. Are you using command line GCC or some other IDE? I have used EMbiz, Crosstudio and IAR. I think I like EMbiz the most. If you don’t mind could you publish or send me the final code? It would be interesting to try it out.

  9. My decoder is running VERY hot! The regulator is impossible to touch so I put a heat sink on it.Is it the program heating up the processor or what? Can’t find a possible short not a faulty component using a DMM.

    It does the same without my 4x20 character display (seems to decode when I whistle CQ into the mike and the LED is blinking).

    • The regulator should not be warm at all. The arduino uses about 100ma, make sure there are no shorts and that your input voltage is not very high maybe about 9vdc. Which regulator did you used?

  10. Hi,

    I’ve been working on this for a few weeks now trying to get everything working acceptably and have had to omit the microphone because it’s just not sensitive enough and it seems it would be simpler to connect the decoder to the auxiliary output on my radio to decode on-air CW. Currently, I’m just testing it out using the “Koch Morse code trainer” It’s a free windows program that can generate a CW signal and you can include static or pileup if you want to. Anyway, right now all I am concerned with is decode speed. My decoder is having a lot of trouble with anything over 20WPM. Did you experience the same when you were developing yours? I’m working with Hjalmar’s code but have modified it to use an I2C display because that’s what I had. I’m currently looking into incorporating an LM567 tone decoder IC hoping that will give me a better degree of accuracy with the added advantage that I can adjust the frequency with a pot at the LM567. Do you think this approach will help me decode faster signals? Let’s assume for now that we’re just talking about computer generated CW and that there is zero QRM. I posed these questions to the original author of the sketch we are all using but have not gotten a response. If you or ANYBODY else has any recommendations for me on this, please feel free to email me. My email address can be found on QRZ if you are a member. My callsign is KG7WPQ. Thank you.

  11. Hello. Been looking the source code and not sure about the Goertzel calculation.
    Most of the variables defined at “The basic Goertzel calculation” are no longer used over the rest of the code.
    Where does it take place ?
    Thanks and 73

  12. KC2UEZ, thank you for your help. My CW decoder is working properly. The initial unsuccessful compilation may be an IDE version that I have chosen to fail.

  13. Hi,
    Is it easy to adapt to take a key input as well as the existing audio input? Also, do you have a circuit diagram so I can hook everythign up on breadbord?

  14. Halo Associates.
    Excuse my English. I do not know English, so I’m writing with a Google translator.
    I built the decoder cw OZ1JHM. I wanted to install the program into Arduin, but all that I have available shows me they can not upload because of a bug in the program. I have the following sketch_nov22a, sketch_nov22b, sketch_nov22c. There is an error in each variant of the program and it is different each time.
    I’m not a programming expert, so I can not fix it.
    I would ask for help with this problem.

    My email:

    Thank you 73 France -- OK1DDA

  15. I know its not your code but is this not wrong:

    if (filteredstate != filteredstatebefore){
    if (filteredstate == HIGH){
    starttimehigh = millis();
    lowduration = (millis() -- startttimelow);

    if (filteredstate == LOW){
    startttimelow = millis();
    highduration = (millis() -- starttimehigh);
    if (highduration (5*hightimesavg) ){
    hightimesavg = highduration+hightimesavg; // if speed decrease fast ..

    Looks to me like a /2 is missing from the last line, should make tuning of speed better and copying more accurate.

  16. I have completed the decoder and everything seems to work ok, except for the jumpers on A0-A3. The jumper settings do not agree with your chart. Here is what I get:
    BW 93 186 139 279

    TF 992 558 744 496

    Any idea what is going on?

  17. Hi! made it work myself, thanks for posting it here. How hard will it be to turn the decoder into a trainer? Been thinking to try with a buzzer plus a switch to switch from the audio input to a manipulator.


Leave a Comment


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