├── sketch_schema.png ├── Donations_Funding.md ├── Author.md ├── fb-rotary.ino ├── fc_cat.ino ├── fe-eeprom.ino ├── Manual.md ├── fd-si5351.ino ├── ff-abuttons.ino ├── Si5351_issues.md ├── README.md ├── fa-smeter.ino ├── z-end.ino ├── fa-lcd.ino ├── arduino-arcs.ino └── licence.txt /sketch_schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavelmc/arduino-arcs/HEAD/sketch_schema.png -------------------------------------------------------------------------------- /Donations_Funding.md: -------------------------------------------------------------------------------- 1 | 2 | # About donations and founding # 3 | 4 | No payment of whatsoever is required to use this code: this is [Free/Libre Software](https://en.wikipedia.org/wiki/Software_Libre), nevertheless donations and funding are very welcomed. 5 | 6 | I live in the Cuba island and the Internet is very expensive here; you can help me stay connected by fund me or simply donate the cost of a beer/coffee if you like to do so, of course more than one is allowed ;-) 7 | 8 | If you like to contribute in this way please contact the author at pavelmc@gmail.com for instructions on how to do it. 9 | 10 | Thanks before hand. 11 | -------------------------------------------------------------------------------- /Author.md: -------------------------------------------------------------------------------- 1 | 2 | # Author # 3 | 4 | I'm Pavel Milanes, the main developer of this sketch and can be reached at pavelmc@gmail.com I'm an active amateur radio operator under the call sign CO7WT, and I live in Camagüey, Cuba, the Caribbean island, exactly at the FL11aj locator. I'm active from time to time around 7.125 Khz on 40m from 2200z to 0200z on weekdays. 5 | 6 | ## Acknowledge to direct & indirect contributors ## 7 | 8 | This work is based on the previous work of these people (without their work this project may not exist): 9 | 10 | * [NT7S](https://github.com/etherkit/Si5351Arduino) for his work on the Silabs Si5351 and the Arduino library. 11 | * Ben Buxton & [mathertel](https://github.com/mathertel/RotaryEncoder) for the arduino rotary encoder library 12 | * [Thomas Ouellet](https://github.com/thomasfredericks/Bounce2/) for the Bounce2 library 13 | * [SQ9NJE](http://sq9nje.pl) for the example codes and it's sketchs. 14 | * [AK2B](http://ak2b.blogspot.com) for his multifeatured VFO in which this code is based. 15 | * [WJ6C](www.frcuba.com) for the idea and support, this OM is the motor of this project. 16 | * [QRP Labs](http://qrp-labs.com) explanation and examples of libraries for the Si5351 with MCU (size) focus. 17 | * Many other hams elsewhere with critics, opinions, public code on the Internet and ideas. 18 | -------------------------------------------------------------------------------- /fb-rotary.ino: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * 3 | * This file is part of the Arduino-Arcs project, see 4 | * https://github.com/pavelmc/arduino-arcs 5 | * 6 | * Copyright (C) 2016...2017 Pavel Milanes (CO7WT) 7 | * 8 | * This program is free software under the GNU GPL v3.0 9 | * 10 | * ***************************************************************************/ 11 | 12 | 13 | #ifdef ROTARY 14 | // the encoder has moved 15 | void encoderMoved(int dir) { 16 | // check if in memory 17 | #ifdef MEMORIES 18 | if (!vfoMode) { 19 | // we are in mem mode, move it 20 | int tmem = mem + dir; 21 | 22 | // limits check 23 | if (tmem < 0) tmem = memCount; 24 | if (tmem > (int)memCount) tmem = 0; 25 | 26 | // update the mem setting 27 | mem = word(tmem); 28 | loadMEM(mem); 29 | updateAllFreq(); 30 | 31 | // update flag and return 32 | update = true; 33 | return; 34 | } 35 | #endif 36 | 37 | // check the run mode 38 | if (runMode) { 39 | // update freq 40 | updateFreq(dir); 41 | update = true; 42 | } 43 | 44 | #ifdef LCD // no meaning if no lcd 45 | #ifdef ABUT // no meaning if no Analog buttons 46 | if (!runMode) { 47 | // update the values in the setup mode 48 | updateSetupValues(dir); 49 | } 50 | #endif // abut 51 | #endif // nolcd 52 | } 53 | 54 | 55 | // change the steps 56 | void changeStep() { 57 | // calculating the next step 58 | if (step < 7) { 59 | // simply increment 60 | step += 1; 61 | } else { 62 | // default start mode is 2 (100Hz) 63 | step = 2; 64 | // in setup mode and just specific modes it's allowed to go to 1 hz 65 | boolean am = false; 66 | am = am or (config == CONFIG_USB) or (config == CONFIG_PPM); 67 | if (!runMode and am) step = 1; 68 | } 69 | 70 | // if in normal mode reset the counter to show the change in the LCD 71 | if (runMode) showStepCounter = STEP_SHOW_TIME; 72 | } 73 | 74 | 75 | // update freq procedure 76 | void updateFreq(int dir) { 77 | long freq = *ptrVFO; 78 | 79 | if (ritActive) { 80 | // we fix the steps to 10 Hz in rit mode 81 | freq += 100 * dir; 82 | // check we don't exceed the MAX_RIT 83 | if (abs(tvfo - freq) > MAX_RIT) return; 84 | } else { 85 | // otherwise we use the default step on the environment 86 | freq += getStep() * dir; 87 | // check we don't exceed the limits 88 | if(freq > F_MAX) freq = F_MIN; 89 | if(freq < F_MIN) freq = F_MAX; 90 | } 91 | 92 | // apply the change 93 | *ptrVFO = freq; 94 | 95 | // update the output freq 96 | setFreqVFO(); 97 | } 98 | 99 | #endif // rotary 100 | -------------------------------------------------------------------------------- /fc_cat.ino: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * 3 | * This file is part of the Arduino-Arcs project, see 4 | * https://github.com/pavelmc/arduino-arcs 5 | * 6 | * Copyright (C) 2016...2017 Pavel Milanes (CO7WT) 7 | * 8 | * This program is free software under the GNU GPL v3.0 9 | * 10 | * ***************************************************************************/ 11 | 12 | 13 | #ifdef CAT_CONTROL 14 | // instruct the sketch that must go in/out of TX 15 | void catGoPtt(boolean tx) { 16 | if (tx) { 17 | // going to TX 18 | going2TX(); 19 | } else { 20 | // goint to RX 21 | going2RX(); 22 | } 23 | 24 | // update the state 25 | update = true; 26 | } 27 | 28 | 29 | // set VFO toggles from CAT 30 | void catGoToggleVFOs() { 31 | activeVFO = !activeVFO; 32 | update = true; 33 | } 34 | 35 | 36 | // set freq from CAT 37 | void catSetFreq(long f) { 38 | // we use 1 hz resolution, so scale it 39 | f *= 10; 40 | 41 | // check for the freq boundaries 42 | if (f > F_MAX) return; 43 | if (f < F_MIN) return; 44 | 45 | // set the freq for the active VFO 46 | *ptrVFO = f; 47 | 48 | // apply changes 49 | updateAllFreq(); 50 | update = true; 51 | } 52 | 53 | 54 | // set mode from CAT 55 | void catSetMode(byte m) { 56 | // the mode can be any of the CAT ones, we have to restrict it to our modes 57 | if (m > 2) return; // no change 58 | 59 | // by luck we use the same mode than the CAT lib so far 60 | *ptrMode = m; 61 | 62 | // Apply the changes 63 | updateAllFreq(); 64 | update = true; 65 | } 66 | 67 | 68 | // get freq from CAT 69 | long catGetFreq() { 70 | // get the active VFO freq and pass it 71 | return *ptrVFO; 72 | } 73 | 74 | 75 | // get mode from CAT 76 | byte catGetMode() { 77 | // get the active VFO mode and pass it 78 | return *ptrMode; 79 | } 80 | 81 | 82 | // get the s meter status to CAT 83 | byte catGetSMeter() { 84 | // returns a byte in wich the s-meter is scaled to 4 bits (1023 > 15) 85 | #ifdef SMETER 86 | return sMeter >> 6 ; 87 | #else 88 | return 0; 89 | #endif 90 | } 91 | 92 | 93 | // get the TXstatus to CAT 94 | byte catGetTXStatus() { 95 | // prepare a byte like the one the CAT wants: 96 | 97 | /* 98 | * this must return a byte in wich the different bits means this: 99 | * 0b abcdefgh 100 | * a = 0 = PTT off 101 | * a = 1 = PTT on 102 | * b = 0 = HI SWR off 103 | * b = 1 = HI SWR on 104 | * c = 0 = split on 105 | * c = 1 = split off 106 | * d = dummy data 107 | * efgh = PO meter data 108 | */ 109 | 110 | // build the byte to return 111 | #ifdef SMETER 112 | return (tx<<7) + (split<<5) + sMeter; 113 | #else 114 | return (tx<<7) + (split<<5); 115 | #endif 116 | } 117 | 118 | 119 | // delay with CAT check, this is for the welcome screen 120 | // see the note in the setup; default ~2 secs 121 | void delayCat(int del = 2000) { 122 | // delay in msecs to wait, 2000 ~2 seconds by default 123 | long delay = millis() + del; 124 | long m = 0; 125 | 126 | // loop to waste time 127 | while (m < delay) { 128 | cat.check(); 129 | m = millis(); 130 | } 131 | } 132 | 133 | #endif // cat 134 | 135 | 136 | // smart Delay trick, normal when no cat and non-blocking when cat 137 | void smartDelay() { 138 | #ifdef CAT_CONTROL 139 | delayCat(); // 2 secs 140 | #else 141 | delay(2000); 142 | #endif 143 | } 144 | -------------------------------------------------------------------------------- /fe-eeprom.ino: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * 3 | * This file is part of the Arduino-Arcs project, see 4 | * https://github.com/pavelmc/arduino-arcs 5 | * 6 | * Copyright (C) 2016...2017 Pavel Milanes (CO7WT) 7 | * 8 | * This program is free software under the GNU GPL v3.0 9 | * 10 | * ***************************************************************************/ 11 | 12 | 13 | /* 14 | EEPROM amount vary from board to board, a simple list here: 15 | 16 | Larger AVR processors have larger EEPROM sizes, E.g: 17 | - Arduno Duemilanove: 512b EEPROM storage. (ATMega 168) 18 | - Arduino Uno: 1kb EEPROM storage. (ATMega 328) 19 | - Arduino Mega: 4kb EEPROM storage. (ATMega 2560) 20 | 21 | Rather than hard-coding the length, you should use the pre-provided length 22 | function. This will make your code portable to all AVR processors. 23 | 24 | EEPROM.length() 25 | */ 26 | 27 | 28 | // check if the EEPROM is initialized 29 | boolean checkInitEEPROM() { 30 | byte t; 31 | bool flag = true; // true if eeprom is initialized and match 32 | 33 | // check the firmware version 34 | t = EEPROM.read(0); 35 | if (t != FMW_VER) flag = false; 36 | 37 | // check the eeprom version 38 | t = EEPROM.read(1); 39 | if (t != EEP_VER) flag = false; 40 | 41 | // return it 42 | return flag; 43 | } 44 | 45 | 46 | // initialize the EEPROM mem, also used to store the values in the setup mode 47 | // this procedure has a protection for the EEPROM life using update semantics 48 | // it actually only write a cell if it has changed 49 | void saveEEPROM() { 50 | // write it 51 | EEPROM.put(0, u); 52 | } 53 | 54 | 55 | // load the eprom contents 56 | void loadEEPROMConfig() { 57 | // get it 58 | EEPROM.get(0, u); 59 | 60 | // force to operation 61 | CXTAL = XTAL + u.ppm; 62 | updateAllFreq(); 63 | 64 | // force a reset 65 | Si5351_resets(); 66 | } 67 | 68 | 69 | #ifdef MEMORIES 70 | // save memory location 71 | void saveMEM(word memItem, boolean configured) { 72 | // real or empty 73 | if (!configured) { 74 | // default values 75 | memo.configured = false; 76 | memo.vfo = 7110000; 77 | memo.vfoMode = MODE_LSB; 78 | } else { 79 | // ok, real ones, set the values 80 | memo.configured = true; 81 | memo.vfo = *ptrVFO; 82 | memo.vfoMode = *ptrMode; 83 | } 84 | 85 | // write it 86 | EEPROM.put(MEMSTART + (sizeof(mmem) * memItem), memo); 87 | } 88 | 89 | 90 | // load memory location 91 | boolean loadMEM(word memItem) { 92 | // get the values 93 | EEPROM.get(MEMSTART + (sizeof(mmem) * memItem), memo); 94 | 95 | // is the mem valid? 96 | if (!memo.configured) return false; 97 | 98 | // load it 99 | *ptrVFO = memo.vfo; 100 | *ptrMode = memo.vfoMode; 101 | 102 | // return true 103 | return true; 104 | } 105 | 106 | 107 | // wipe mem, this is loaded in the init process of the eeprom. 108 | void wipeMEM() { 109 | // run for the entire mem area writing the defaults to it, 110 | // with no-go flag on it 111 | for (word i = 0; i <= memCount; i++ ) saveMEM(i, 0); 112 | } 113 | 114 | 115 | #ifdef MEM_SCAN 116 | // check memscan 117 | void checkMemScan() { 118 | // check timer until the next mem jump 119 | if ((mscan == true) and (scanTime < millis())) { 120 | // move to the next mem 121 | do {// increase the mem & check it 122 | mem += 1; 123 | if (mem >= memCount) mem = 0; 124 | } while(!loadMEM(mem)); 125 | 126 | // reset timer 127 | scanTime = millis() + MEM_SCAN_INTERVAL; 128 | } 129 | } 130 | 131 | #endif // mem scan 132 | 133 | #endif // memories 134 | -------------------------------------------------------------------------------- /Manual.md: -------------------------------------------------------------------------------- 1 | Arduino ARCS Manual 2 | ========================= 3 | 4 | # OUTDATED, will be upgraded soon # 5 | 6 | This is a short manual to know how to operate the sketch and the button's functions. The sketch has two modes, the user's mode and the setup mode, let's review them 7 | 8 | User mode 9 | ========= 10 | 11 | General description 12 | ------------------- 13 | 14 | The user mode is the natural way the hardware boots if you don't touch anything, yo will see the Arduino ARCS banner and a note of the firmware version, the memory firmware version and the author, then you will get the main screen. 15 | 16 | The main screen is composed of two lines the top line and the bottom line, both lines has the same information for each of the two VFOs the sketch has, upper is VFO A and lower is VFO B. You know which VFO is selected by the ">" sign in front of it. 17 | 18 | The frequency is printed grouped by units and separated by a dot to easy viewing, first the Mhz units, then the khz part and finally the Hz part. 19 | 20 | The frequency is truncated at the 10th of hz part to be honest, the Si5351 has a resolution of 0.1 hz in our configuration and an accuracy of 0 ppm error against the reference crystal used (that's per the OEM datasheet), then all the accuracy is referenced to the accuracy of the reference crystal and it has (usually on free air) about 10 ppm of error; using a 27 Mhz xtal we can expect a extreme variation of about 270 hz with temperature, voltage and humidity fluctuations. 21 | 22 | In the best case scenario if you have a relatively stable operating temperature and humidity you can get about less 10hz of variation normally under this conditions. 23 | 24 | This is why we have not 1hz accuracy in place, we can implement it, sure, the chip will be happy moving in 1hz steps but that "accuracy" will be undermined by the real stability of the reference crystal unless you managed to get a temp oven for the crystal, and that is an overkill to out goal. 25 | 26 | If you have a crystal in an oven and you want to use 1hz steps I will be happy to mod the sketch for you. 27 | 28 | 29 | VFO button 30 | ---------- 31 | 32 | This Button toggles from the two available VFOs, as simple as that. 33 | 34 | RIT button 35 | ---------- 36 | 37 | This button toggles the RIT function for the **active** VFO and will show the RIT detail in the other position, the RIT uses a +/- 9.9 Khz, so you can go further than that; this can be modified by software. When you have the RIT active and hit this button again, it will reset the VFO to the previous value as you expect. 38 | 39 | The step on the RIT mode is fixed by software at 100hz, you can get it at the 10hz step if you change it on the source code in the sketch. 40 | 41 | A note: when you have the RIT active on one VFO and you want to switch to the other VFO the active RIT will be reseted before the switch to the other VFO. 42 | 43 | MODE button 44 | ----------- 45 | 46 | The mode button change the mode of the **active** VFO in a closed loop from one of the predefined modes, so far: LSB > USB > CW and then back to LSB. 47 | 48 | STEP button _(encoder push button)_ 49 | --------------------------------- 50 | 51 | The encoder push button has the control of the steps in the rotation of the encoder, pushing it will cycle steps in 10hz, 100hz, 1khz, 10khz, 100khz and 1 Mhz and then back to 10hz. 52 | 53 | Upon the modification of the step, the LCD will show the value in the area used to show the S-meter bargraph for about three seconds. 54 | 55 | Encoder 56 | ------- 57 | 58 | The encoder is simply the vernier to selecting the frequency of the active VFO in the steps selected by the push button described before. 59 | 60 | 61 | SETUP mode 62 | ========== 63 | 64 | The setup mode is reached by powering on the circuit and holding the encoder button pressed until you see the acknowledge in the LCD for it being on the SETUP mode. 65 | 66 | In the setup mode you will find a menu with the following options to select and change on the encoder rotation and push. 67 | 68 | * IF frequency 69 | * VFO A start frequency 70 | * VFO B start frequency 71 | * VFO frequency for the LSB mode 72 | * VFO frequency for the USB mode 73 | * VFO frequency for the CW mode 74 | * VFO A start mode 75 | * VFO B start mode 76 | * XFO frequency 77 | * Si5351 PPM error correction 78 | 79 | All this options are cycled via the encoder rotation in one or another direction and selected via the encoder push button; we will describe the buttons use in the setup mode and be aware that they change a little in the setup mode, we will name it as the usual function and will name the special function between parenthesis. 80 | 81 | Encoder push button _(Select & Confirm)_ 82 | ----------------------------------- 83 | 84 | This button is used to select the option in the main menu to be modified, and also to confirm it's change after it was modified and get it back to the main menu saving it on the EERPOM. 85 | 86 | MODE button _(Cancel)_ 87 | -------------------- 88 | 89 | This button has effect only when you have selected and option in the main menu and it's purpose is to leave this option unchanged and get back to the main menu. If you changed the value of an option selected (by rotating the encoder) but you want to cancel it's modification this option is what you need. 90 | 91 | RIT button _(Step)_ 92 | ----------------- 93 | 94 | This button has only effect when you have selected to modify a menu entry that leads to a numeric option (for example frequencies, PPM error, etc.) and it will change the step for the encoder rotation showing the value selected in the LCD until you rotate the encoder. 95 | 96 | VFO button _(Reset)_ 97 | ------------------ 98 | 99 | This button has a special meaning in the options refereed to LSB & USB BFO frequencies and the Si5351 PPM correction. Pressing this buttons will reset the values of that option to it's default values as this: 100 | 101 | * BFO LSB frequency = -15000 102 | * BFO USB frequency = +15000 103 | * BFO CW frequency = +10000 104 | * Si5351 PPM error correction = 0 105 | -------------------------------------------------------------------------------- /fd-si5351.ino: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * 3 | * This file is part of the Arduino-Arcs project, see 4 | * https://github.com/pavelmc/arduino-arcs 5 | * 6 | * Copyright (C) 2016...2017 Pavel Milanes (CO7WT) 7 | * 8 | * This program is free software under the GNU GPL v3.0 9 | * 10 | * ***************************************************************************/ 11 | 12 | 13 | // set a freq to a clk 14 | // Frequency in Hz; must be within [7,810 kHz to ~220 MHz] 15 | void si5351aSetFrequency(byte clk, unsigned long frequency) { 16 | #define c 1048575; 17 | unsigned long fvco; 18 | unsigned long outdivider; 19 | byte R = 1; 20 | byte a; 21 | unsigned long b; 22 | float f; 23 | unsigned long MSx_P1; 24 | unsigned long MSNx_P1; 25 | unsigned long MSNx_P2; 26 | unsigned long MSNx_P3; 27 | byte shifts = 0; 28 | 29 | // With 900 MHz beeing the maximum internal PLL-Frequency 30 | outdivider = 900000000 / frequency; 31 | 32 | // If output divider out of range (>900) use additional Output divider 33 | while (outdivider > 900) { 34 | R = R * 2; 35 | outdivider = outdivider / 2; 36 | } 37 | 38 | // finds the even divider which delivers the intended Frequency 39 | if (outdivider % 2) outdivider--; 40 | 41 | // Calculate the PLL-Frequency (given the even divider) 42 | fvco = outdivider * R * frequency; 43 | 44 | // Convert the Output Divider to the bit-setting required in register 44 45 | switch (R) { 46 | case 1: R = 0; break; 47 | case 2: R = 16; break; 48 | case 4: R = 32; break; 49 | case 8: R = 48; break; 50 | case 16: R = 64; break; 51 | case 32: R = 80; break; 52 | case 64: R = 96; break; 53 | case 128: R = 112; break; 54 | } 55 | 56 | a = fvco / CXTAL; 57 | f = fvco - a * CXTAL; 58 | f = f * c; 59 | f = f / CXTAL; 60 | b = f; 61 | 62 | MSx_P1 = 128 * outdivider - 512; 63 | f = 128 * b / c; 64 | MSNx_P1 = 128 * a + f - 512; 65 | MSNx_P2 = f; 66 | MSNx_P2 = 128 * b - MSNx_P2 * c; 67 | MSNx_P3 = c; 68 | 69 | // CLK# registers are exactly 8 * clk bytes shifted from a base register. 70 | shifts = clk * 8; 71 | 72 | // plls, A & B registers separated by 8 bytes 73 | si5351ai2cWrite(26 + shifts, (MSNx_P3 & 65280) >> 8); // Bits [15:8] of MSNx_P3 in register 26 74 | si5351ai2cWrite(27 + shifts, MSNx_P3 & 255); 75 | si5351ai2cWrite(28 + shifts, (MSNx_P1 & 196608) >> 16); 76 | si5351ai2cWrite(29 + shifts, (MSNx_P1 & 65280) >> 8); // Bits [15:8] of MSNx_P1 in register 29 77 | si5351ai2cWrite(30 + shifts, MSNx_P1 & 255); // Bits [7:0] of MSNx_P1 in register 30 78 | si5351ai2cWrite(31 + shifts, ((MSNx_P3 & 983040) >> 12) | ((MSNx_P2 & 983040) >> 16)); // Parts of MSNx_P3 and MSNx_P1 79 | si5351ai2cWrite(32 + shifts, (MSNx_P2 & 65280) >> 8); // Bits [15:8] of MSNx_P2 in register 32 80 | si5351ai2cWrite(33 + shifts, MSNx_P2 & 255); // Bits [7:0] of MSNx_P2 in register 33 81 | 82 | // multisynths 83 | si5351ai2cWrite(42 + shifts, 0); // Bits [15:8] of MS0_P3 (always 0) in register 42 84 | si5351ai2cWrite(43 + shifts, 1); // Bits [7:0] of MS0_P3 (always 1) in register 43 85 | // See datasheet, special trick when R=4 86 | if (outdivider == 4) { 87 | si5351ai2cWrite(44 + shifts, 12 | R); 88 | si5351ai2cWrite(45 + shifts, 0); // Bits [15:8] of MSx_P1 must be 0 89 | si5351ai2cWrite(46 + shifts, 0); // Bits [7:0] of MSx_P1 must be 0 90 | } else { 91 | si5351ai2cWrite(44 + shifts, ((MSx_P1 & 196608) >> 16) | R); // Bits [17:16] of MSx_P1 in bits [1:0] and R in [7:4] 92 | si5351ai2cWrite(45 + shifts, (MSx_P1 & 65280) >> 8); // Bits [15:8] of MSx_P1 in register 45 93 | si5351ai2cWrite(46 + shifts, MSx_P1 & 255); // Bits [7:0] of MSx_P1 in register 46 94 | } 95 | si5351ai2cWrite(47 + shifts, 0); // Bits [19:16] of MS0_P2 and MS0_P3 are always 0 96 | si5351ai2cWrite(48 + shifts, 0); // Bits [15:8] of MS0_P2 are always 0 97 | si5351ai2cWrite(49 + shifts, 0); // Bits [7:0] of MS0_P2 are always 0 98 | 99 | // no pll reset as it makes noise, click noise, just reset one time on the setup 100 | } 101 | 102 | 103 | // reset the PLL this will produce a click noise 104 | void Si5351_resets() { 105 | // PLL & synths reset 106 | 107 | // This soft-resets PLL A & and enable it's output 108 | si5351ai2cWrite(177, 32); 109 | si5351ai2cWrite(16, 79); 110 | 111 | // This soft-resets PLL B & and enable it's output 112 | si5351ai2cWrite(177, 128); 113 | si5351ai2cWrite(17, 111); 114 | } 115 | 116 | 117 | // write a byte value to a resgister on the Si5351 118 | void si5351ai2cWrite(byte regist, byte value){ 119 | Wire.beginTransmission(96); 120 | Wire.write(regist); 121 | Wire.write(value); 122 | Wire.endTransmission(); 123 | } 124 | 125 | 126 | // set the calculated freq to the VFO 127 | void setFreqVFO() { 128 | // temp var to hold the calculated value 129 | long freq = *ptrVFO; 130 | 131 | //setting about higher IF 132 | if (u.if2 != 0) { 133 | // add the High IF to VFO 134 | freq += u.if2; 135 | } else { 136 | // add just the unique IF 137 | freq += u.ifreq; 138 | } 139 | 140 | // apply the ssb/cw correction factor 141 | if (*ptrMode == MODE_USB) freq += u.usb; 142 | if (*ptrMode == MODE_CW) freq += u.cw; 143 | 144 | // set the Si5351 up with the change 145 | si5351aSetFrequency(0, freq); 146 | } 147 | 148 | 149 | // Force freq update for all the environment vars 150 | void updateAllFreq() { 151 | // VFO update 152 | setFreqVFO(); 153 | 154 | // BFO update 155 | long freq = u.ifreq; 156 | 157 | // deactivate it if zero 158 | if (freq == 0) { 159 | // deactivate it 160 | si5351aSetFrequency(1, 0); 161 | } else { 162 | // mod it by mode 163 | if (*ptrMode == MODE_USB) freq += u.usb; 164 | if (*ptrMode == MODE_CW) freq += u.cw; 165 | 166 | // output it 167 | si5351aSetFrequency(1, freq); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /ff-abuttons.ino: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * 3 | * This file is part of the Arduino-Arcs project, see 4 | * https://github.com/pavelmc/arduino-arcs 5 | * 6 | * Copyright (C) 2016...2017 Pavel Milanes (CO7WT) 7 | * 8 | * This program is free software under the GNU GPL v3.0 9 | * 10 | * ***************************************************************************/ 11 | 12 | 13 | #ifdef ABUT 14 | // VFO A/B button click >>>> (OK/SAVE click) 15 | void btnVFOABClick() { 16 | // beep 17 | beep(); 18 | 19 | // normal mode 20 | if (runMode) { 21 | #ifdef MEMORIES 22 | // is the scanning feature turned on? 23 | #ifdef MEM_SCAN 24 | if (!vfoMode) { 25 | mscan != mscan; 26 | return; 27 | } 28 | #endif 29 | #endif 30 | 31 | // we force to deactivate the RIT on VFO change, as it will confuse 32 | // the users and have a non logical use, only if activated and 33 | // BEFORE we change the active VFO 34 | if (ritActive) toggleRit(); 35 | 36 | // now we swap the VFO. 37 | swapVFO(); 38 | 39 | // update VFO/BFO and instruct to update the LCD 40 | updateAllFreq(); 41 | 42 | // force a memory save 43 | saveEEPROM(); 44 | 45 | // set the LCD update flag 46 | update = true; 47 | } else { 48 | // SETUP mode 49 | if (!inSetup) { 50 | // I'm going to setup a element 51 | inSetup = true; 52 | 53 | // change the mode to follow VFO A 54 | if (config == CONFIG_USB) u.aMode = MODE_USB; 55 | else if (config == CONFIG_CW) u.aMode = MODE_CW; 56 | else u.aMode = MODE_LSB; 57 | 58 | #ifdef LCD 59 | // config update on the LCD 60 | showModConfig(); 61 | #endif // nolcd 62 | } else { 63 | // get out of the setup change 64 | inSetup = false; 65 | 66 | // save to the eeprom 67 | saveEEPROM(); 68 | 69 | #ifdef LCD 70 | // lcd delay to show it properly (user feedback) 71 | lcd.setCursor(0, 0); 72 | lcd.print(F("## SAVED ##")); 73 | delay(1000); 74 | 75 | // show setup 76 | showConfig(); 77 | #endif // nolcd 78 | 79 | // reset the minimum step if set (1hz > 10 hz) 80 | if (step == 1) step = 2; 81 | } 82 | } 83 | } 84 | 85 | 86 | // MODE button click >>>> (CANCEL click) 87 | void btnModeClick() { 88 | // beep 89 | beep(); 90 | 91 | // normal mode 92 | if (runMode) { 93 | changeMode(); 94 | update = true; 95 | } else if (inSetup) { 96 | // setup mode, just inside a value edit, then get out of here 97 | inSetup = false; 98 | 99 | #ifdef LCD 100 | // user feedback 101 | lcd.setCursor(0, 0); 102 | lcd.print(F(" # Canceled # ")); 103 | delay(1000); 104 | 105 | // show it 106 | showConfig(); 107 | #endif // nolcd 108 | } 109 | } 110 | 111 | 112 | // RIT button click >>>> (RESET values click) 113 | void btnRITClick() { 114 | // beep 115 | beep(); 116 | 117 | // normal mode 118 | if (runMode) { 119 | #ifdef MEMORIES 120 | // if we are in mem mode RIT has no sense 121 | if (!vfoMode) return; 122 | #endif 123 | 124 | toggleRit(); 125 | update = true; 126 | } else if (inSetup) { 127 | // SETUP mode just inside a value edit. 128 | 129 | // where we are to know what to reset? 130 | if (config == CONFIG_USB) u.usb = 2400; 131 | if (config == CONFIG_CW) u.cw = 600; 132 | if (config == CONFIG_IF) u.ifreq = 0; 133 | if (config == CONFIG_PPM) u.ppm = 0; 134 | 135 | // update the freqs for 136 | updateAllFreq(); 137 | #ifdef LCD 138 | showModConfig(); 139 | #endif // nolcd 140 | } 141 | } 142 | 143 | 144 | // SPLIT button click >>>> (Nothing yet) 145 | void btnSPLITClick() { 146 | // beep 147 | beep(); 148 | 149 | // normal mode 150 | if (runMode) { 151 | #ifdef MEMORIES 152 | // if we are in mem mode split has no sense 153 | if (!vfoMode) return; 154 | #endif 155 | split = !split; 156 | update = true; 157 | } 158 | } 159 | 160 | 161 | // RIT toggle 162 | void toggleRit() { 163 | if (!ritActive) { 164 | // going to activate it: store the active VFO 165 | tvfo = *ptrVFO; 166 | ritActive = true; 167 | } else { 168 | // going to deactivate: reset the stored VFO 169 | *ptrVFO = tvfo; 170 | ritActive = false; 171 | #ifdef SMETER 172 | // flag to redraw the bar graph 173 | barReDraw = true; 174 | #endif 175 | } 176 | } 177 | 178 | 179 | #ifdef MEMORIES 180 | // VFO/MEM mode change 181 | void btnVFOMEM() { 182 | // beep 183 | beep(); 184 | 185 | // toggle the flag 186 | vfoMode = !vfoMode; 187 | 188 | // force the update of the mem 189 | if (!vfoMode) loadMEM(mem); 190 | 191 | // rise the update flag 192 | update = true; 193 | } 194 | 195 | 196 | // mem > vfo | vfo > mem, taking into account in what mode we are 197 | void btnVFOsMEM() { 198 | // beep 199 | beop(); 200 | 201 | // detect in which mode I'm, to decide what to do 202 | if (vfoMode) { 203 | // VFO > MEM 204 | saveMEM(mem, true); 205 | // swap to mem mode 206 | vfoMode = false; 207 | } else { 208 | // MEM > VFO 209 | loadMEM(mem); 210 | // swap to the VFO mode 211 | vfoMode = true; 212 | } 213 | 214 | // update flag for the LCD 215 | update = true; 216 | } 217 | 218 | 219 | // erase the actual mem position 220 | void btnEraseMEM() { 221 | // beep 222 | beop(); 223 | 224 | // erase the actual mem position 225 | saveMEM(mem, false); 226 | 227 | // now force an update of the LCD 228 | update = true; 229 | } 230 | 231 | 232 | // erase the whole mem space 233 | void btnEraseWholeMem() { 234 | // sound signal 235 | beep(); 236 | delay(50); 237 | beop(); 238 | 239 | // sure? 240 | wipeMEM(); 241 | 242 | // now force an update of the LCD 243 | update = true; 244 | } 245 | 246 | #endif // memories 247 | 248 | #endif 249 | -------------------------------------------------------------------------------- /Si5351_issues.md: -------------------------------------------------------------------------------- 1 | # Si5351 Clock generator issues # 2 | 3 | There are some founded worries about the use of the Si5351 Clock generator in transceiver business, and now with more and more builders using it, that problems are beginning to fade away. 4 | 5 | This article will grow with experience notes about the problems and our experience about them. For the record, we have so far 5 different setups testing this sketch + hardware. 6 | 7 | * BitX 20 _(converted to 40m, IF 8.2158 Mhz from a FT-747GX SSB filter, **CO7WT**)_ 8 | * Anritsu transceiver _(Unknown model, IF1 1.6 Mhz, IF2 455 Khz, **CO7LX**)_ 9 | * RFT SEG-15 _(WWII transceiver, IF1 28.2 Mhz, IF2 200 Khz, **CO7KD**)_ 10 | * Furuno FS1000 _(IF 9 Mhz, **CO7YB**)_ 11 | * Homebrew 500Khz singe conversion SSB radio _(Using a SSB filter from a USSR/CCCP "KARAT" transceiver, **CM7MTL**)_ 12 | 13 | Please note that almost all of this radios are actually kind of Frankenstein's cousins, as they preserve the main RF/IF chain from the mentioned radios, but can have another final stage (homebrewed or borrowed for a commercial unit) as well as chassis/front panels, etc. 14 | 15 | ## Crosstalk ## 16 | 17 | Some users are pointing that the outputs of the chip is not clean enough for the use in heterodyne transceivers, [here you can see of what we are talking about](http://nt7s.com/2014/12/si5351a-investigations-part-8/), as you can see, some of the signal of one output can sneak into the other output, this is a proved bad thing. 18 | 19 | Then the question now is: **how bad is it?** 20 | 21 | The graphs in the last link shows the worst scenario, you can see that the worst case is only about -35 dB down the main frequency, that will result in about 25mW of the spurious frequency at the output of a main signal with 100W for the fundamental frequency. 22 | 23 | With proper measures in design and other tricks (Bandstop/bandpass filters) you can get that crosstalk down below 50dB and the problem can be minimized at the point to be almost eliminated. 24 | 25 | ### Experience from our side ### 26 | 27 | From a technician point of view with laboratory grade equipment, you are gonna find the crosstalk, it's there. From a normal homebrewer with COTS (Cost Off The Shelf) equipment it's not a big deal on reception and it has a no-noticeable impact on your transmission even with 150W in one of our local setups. 28 | 29 | ## Square wave output ## 30 | 31 | Thanks to Mr. Fourier we know that square waves are a pure sine wave of the fundamental frequency plus all the harmonics of it until infinity... but a signal full of it's harmonics is a bad thing in a VFO or any other place where we need a pure sine signal, right? 32 | 33 | That's why we designed the sketch to always use a final VFO frequency **above** the fundamental RF frequency and all the outputs of the chips will be followed with a matched low pass or a band pass filter to get rid of the most of the harmonics to minimize this problem. 34 | 35 | Reception is affected in a different manner even when the VFO is over the RF; if you had to generate a BFO or a VFO signal below 2Mhz you will get more and more spurious spikes across the spectrum. Eventually at a BFO of 455/500 kHz you will need some filtering in place for a proper operation. 36 | 37 | A trick from one of our builders CO7LX: he has a 1rst IF of 1.6 MHz then a second IF of 455 kHz. The reception with the BFO at 455 kHz coupled directly (just a dc blocking capacitor) was really poor. After inspection wit and SDR we noted a lot of spurious spikes and an unusual high noise floor. 38 | 39 | As this signal is fixed and if needed just moved a couple of kHz we make a simple filter with two TOKO IF cans coupled back to back to form a crude and yet simple band pass filter and we managed to push down the noise floor and the numerous spurious signals to a very pleasant level. 40 | 41 | ### Experience from our side ### 42 | 43 | The simple fix is to put a low pass filter for the used frequency range, or even better, a band pass filter you will be safe and this will do the job, believe us, this is a "must" not an "if". 44 | 45 | ## Jitter & phase noise ## 46 | 47 | There are some sources talking about the jitter in the output frequencies, but this author can't find any technical info on the web about how bad is it. 48 | 49 | For SSB communications we (humans) can tolerate a jitter of about 2-5 hz without noticing it, and free running LC oscillators can have more than that and we can deal with that, so I think this will not defeat the purpose of this Chip as a frequency generator in the cheap ham market. 50 | 51 | The code for managing the Si5351 uses a integer division always, with that we get sure you get as low jitter and phase noise as possible. 52 | 53 | ## Birdies and Background Noise ## 54 | 55 | In almost all setups out there in the internet you will find a few users talking about the birdies in the receiver and even about an artificial (high) background noise, well, after experimentation with this setup with a few receiver configurations here in Cuba I can tell you this: 56 | 57 | 1 - Not all birdies comes from the Si5351, some are Arduino and/or LCD related. 58 | 59 | In one of my setups I tested to power off the Arduino and the LCD but leaving the Si5351 powered and generating the frequency for the rest of the receiver chain, I can spot at least two birdies that will go completely off once you erase the Arduino + LCD from the equation, so the Si5351 is not the sole culprit. Also the background noise levels drops. 60 | 61 | 2 - Yes, you can experiment an artificially (high) background noise, mainly from the Arduino talking with the LCD. 62 | 63 | In one of the developing stages, early in the barGraph introduction I suddenly experienced a high background noise, after firing my Yaesu FT-747GX on and check I can confirm that the noise comes from inside the hombrewed radio... 64 | 65 | Later investigations revealed that the MCU (Arduino) to LCD communications and the ADC reading generate a lot of noise, high enough to mask some times the weak signals and even get at annoying levels at certain frequencies. 66 | 67 | To deal with that this sketch minimizes the writes to the LCD to the least possible and the results are very good compared to the initials with the all time refreshing LCD. 68 | 69 | A user on a mailing list commented recently that keeping the Arduino + LCD + Si5351 module in a shielded compartment away from the sensitive RF components can eliminate this "artificially (high) background noise". Local testing made by Soris CO7YV proved that this is a great help to make the radio even quieter. 70 | 71 | ## Datasheet tells three outputs, but in practive it's a gamble of 2+? ## 72 | 73 | The internals of the Si5351 are described in depth in the datasheet and further publications, long story short: the Chip has an external crystal as a reference for two (2) independent PLL VCOs in the VHF to UHF range, then you select a chain of divisors to process that VCOs outputs to get your desired frequency. 74 | 75 | The trick is that with three outputs you will have to share two of the outputs with a common PLL VCO frequency and you will have one of them that can be expressed in the correct math to get right on the frequency under certain circumstances. 76 | 77 | ### Experience from our side ### 78 | 79 | In the RFT SEG-15 we test the use of the three frequencies from the sketch, one for the VFO with and independent VCO (29 to 60 Mhz) and then a XFO of 28.0 Mhz and a BFO of 200 Khz sharing the same VCO. 80 | 81 | Strange things happened when we start to tune the frequencies out in the radio: the BFO will not move in steps of less than 150 Hz even if you set a step of 1 Hz, further investigations revealed that the library was trading stability (aka: struggle to keep the output active) vs. accuracy of the output. 82 | 83 | In this configuration we found that with a 28.0 Mhz and a another less than 1 Mhz output, sharing the same VCO inside the Si5351 we face accuracy problems on the < 1 Mhz output. 84 | 85 | So we sacrificed the XFO and go back to use the original crystal XFO inside the radio, taking that into account in the sketch: for the RFT SEG-15 the XFO is taken into account and used on the calculations but not activated in any way. 86 | 87 | That lead to the desision of using just two of the 3 outputs of the Chip, almost all radio manufacturers use a real crystal for the fixed conversion from the 1rst to the 2nd IF; please do use it. 88 | 89 | ## Resume ## 90 | 91 | There are many hams that are building transceivers with this chip (Si5351) and posting the results on the web, none of them have reported any of this mentioned problems as an impairment for that particular radio. So far in our experiments, we are confirming this, the Si5351 is suited to the job. 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Arduino-Arcs # 3 | 4 | _**Amateur Radio Control & Clocks (RF) Solution**_ 5 | 6 | This is an amateur approach to a radio control solution with clock (RF sources) generation (VFO and BFO) for homebrewed and old (not frequency synthesized) transceivers; of course based on the Arduino platform. 7 | 8 | You can [take a peek here](http://www.qrz.com/db/wj6c) to see what's it looks like in the bench. 9 | 10 | ## Motivations ## 11 | 12 | This project arose as a solution for the Cuban hams who have built a simple QRP transceivers but lacks the principal part: a digital controller to avoid the "DRIFT" and get some of the standard radio features a ham's needs today in a radio. 13 | 14 | [Juan Carlos (WJ6C)](http://www.qrz.com/db/wj6c), Axel (CO6ATL), [Heriberto (CM2KMK)](http://www.qrz.com/db/wj6c) and many others hams were trying with some Arduino sketches found on the network until [I (CO7WT)](http://www.qrz.com/db/co7wt) decided to give a hand using my skills and expertise on MCU programming with [Jal for the PICs](http://www.justanotherlanguage.com) & [Arduino](http://www.arduino.cc). 15 | 16 | This code is the result of a group of Cubans that joint together to fulfill the expectations for a simple, affordable and yet modern radio logic controller for homebrewed radios that any ham in the world can use. 17 | 18 | This work is and always will be in constant development, this must be always considered a Beta version. 19 | 20 | ## Hardware ## 21 | 22 | We are using the ubiquitous Arduino boards for the main brain, a list of the needed hardware follows. 23 | 24 | - Arduino as the brain; developed for the Arduino Uno R3 as base, but it's adaptable to other boards, we are using mainly the Mini & Mini Pro now (Yes it's less than 16 kb at the end) 25 | - LCD 16 columns and 2 lines as display (LiquidCrystal default Arduino lib) 26 | - Si5351 as VFO and BFO, any of the breakout board out there will work, we choose to use a embedded code for the Si5351 control. Our code is a modified version of one of the examples in the [QRP Labs site](http://qrp-labs.com) (with my mods to allow two outputs instead of only one) and trick learned from [DK7IH](https://radiotransmitter.wordpress.com/category/si5351a/) to eliminate the unpleasant clicks when tunning. You can use it as a full library named [Si5351mcu here](http://github.com/pavelmc/si5351mcu). 27 | - For the human interface part we use a bunch of [analog push buttons with dual functions](https://github.com/pavelmc/BMUx/) and a [rotary encoder](https://github.com/mathertel/RotaryEncoder) + [push button](https://github.com/thomasfredericks/Bounce2/). 28 | - Since September of 2016 the sketch has CAT support, if you are using an Arduino R3 or others with a USB port you are done; if not you need any of the [cheap ebay USB to RS-232/TTL converters](http://www.google.com/q=cp-2021+USB+serial+ttl+converter) just watch out for the correct drivers if you use any Windows OS. 29 | 30 | You can see the schematic diagram (designed for an Arduino Uno R3 in mind) in the file sketch_schema.png (I can provide Fritzing files on request, but only the schematic part is ok.) 31 | 32 | ## Features implemented by now ## 33 | 34 | - **Usual features** of normal commercial transceivers. 35 | - Two VFO (A/B) 36 | - RIT with +/- 9.99 Khz 37 | - Variable VFO speed in steps of 10hz, 100hz (Default), 1khz, 10khz, 100khz, 1Mhz (just push of the encoder) 38 | - Split for contest/pileups 39 | - VFOs A & B are preserved in the EEPROM across power cycle. _(See Note 1 below)_ 40 | - **S-Meter in the LCD** _(Vref is 5.0V)_ _(See Note 2 below)_ 41 | - **Relative TX power in the LCD** _(using the same S-Meter tech, no power calcs, yet)_ 42 | - Initially mono-band in 40m, but **adaptable to any band** 43 | - **Full user customization** of the IF and BFO modes (side bands) via configuration menus. (The VFO is assumed always above the RF and side bands are adjusted, se notes on Si5351 noise) 44 | - **Hot tunning of the parameters** when in configuration mode for ease the adjust. 45 | - **Basic CAT control** (like a Yaesu FT-857D) using my GPL 3.0 [ft857d library](https://github.com/pavelmc/ft857d) _(See Note 3 below)_ 46 | - The sketch is coded with **feature segmentation in mind**, you can rule out the CAT support if you don't need it, disable the ugly S-meter or rule out all other HID and just compile with CAT to get a slim CAT radio solution that fits in a tiny ATMega8 core _(I'm no kidding, it do fit in that chip!)_ 47 | - **Memories** Using the internal EEPROM and limited to 100 (0-99), because the channel number is displayed in two LCD chars. Also you will get what your Chip has to offer: bigger chips my exceed the 100 mem channels count and get toped, but other not and you will have less than 100 mem channels. 48 | - ** Scan in memories ** if you enable the memories in the compilation you have also the possibility of enable the scanning features to loop trough the active memories and stay in each one by a defined amount of time. 49 | 50 | _**Note 1:** The firmware save the VFO info in a 10 minutes interval. Also a touch on the "VFO A/B" button will force an update_ 51 | 52 | _I was once concerned with the EERPOM wear out, but not anymore: The auto-save every 10 minutes **with a heavy use** of the transceiver can lead to about 3 years of life for the internal EEPROM _under datasheet conditions_. But you can expect at least x2 of that amount as the real world data shows (that's at least 6 years with heavy use or more than 10 years with little use)_ 53 | 54 | _Since October 2016 we are now using the new EEPROM.h library (since Arduino IDE 1.6.9 and later); that lib do a trick to preserve the internal EEPROM life against the wear out, so the real life of the EEPROM must be more than 6 years minimum, that for a **heavy daily use**._ 55 | 56 | _**Note 2:** You have two options for the SMeter graphics, the default now is a 26 steps solid bars like "|||||||||||||||" and the alternative is the old "1-3-5-7-9----" with a resolution of only 13 steps. You can change it via a #define in the code, read it._ 57 | 58 | _**Note 3:** If you use a regular Arduino board and upload the code via serial port you do have a bootloader in place. In this case when you setup your CAT program (in your PC) you will have to set the "retries" parameter to 3 or more and the "timeout" parameter to 500 msec or more to get the CAT working. (you will have to play with it a bit to get it working)_ 59 | 60 | _This is because the CAT setup process in your PC/software will reset the Arduino board (and hence the "radio")_ 61 | 62 | _Then the bootloader will introduce a short delay before our firmware kicks in and the PC may complain about "radio not answering to commands". The fix is in the previous paragraph. If you have no bootloader and upload the code via ICP you must not see this problems._ 63 | 64 | ## Features in the TODO list ## 65 | 66 | This are the wish list so far, with no particular order. 67 | 68 | - Multi-band support with a function to jump quickly from band to band. 69 | - Band selector for BPF switching via a external I2C port expander like the boards with the PCF8574, or alternatively a PIC programed to the task. 70 | - Real Power and SWR measurements. 71 | - Support for various LCDs, for example the Nokia 5110, some of the TFTs and some OLEDs. 72 | 73 | ## Ino, then Arturo, now Amake ## 74 | 75 | Old users of this sketch may notice that it's now full Arduino IDE compatible _(since January/2017 with the release of the Arduino IDE version 1.8.0)_ and you don't need to make any more changes to the name of the file. 76 | 77 | I use Ubuntu Linux for daily work here, and I don't like the Arduino IDE as a IDE. I use the [Geany IDE](http://www.geany.org) to code in Jal, Python, Arduino etc. In the past two projects helped me to link my Geany IDE to the Arduino tool-chain: [ino](https://github.com/amperka/ino) and it's folk [Arturo](https://github.com/scottdarch/Arturo/) when Ino reached the end of development. Thanks to the developers of this two projects for the great tools. 78 | 79 | Since the release of the Arduino IDE version 1.8.0 it's possible to use a new tool "arduino-builder" that make possible to compile and upload a sketch entirely from the command line, but that tool is a nightmare to configure for the job in the command line. 80 | 81 | And that was the born of [Amake](https://github.com/pavelmc/amake/), this a bash script that introduce a layer to get the compilation and uploading of arduino sketches from the command line like a breeze, and it's full Arduino IDE compatible. It's my contribution to make the life simpler ;-) 82 | 83 | ## Si5351 known issues ## 84 | 85 | The Si5351 chip is not the panacea of the frequency generator (but it's a close one), as almost all good things in life it has a dark side, know more about in [this](https://github.com/pavelmc/arduino-arcs/Si5351_issues.md) document. 86 | 87 | 73 Pavel CO7WT. 88 | -------------------------------------------------------------------------------- /fa-smeter.ino: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * 3 | * This file is part of the Arduino-Arcs project, see 4 | * https://github.com/pavelmc/arduino-arcs 5 | * 6 | * Copyright (C) 2016...2017 Pavel Milanes (CO7WT) 7 | * 8 | * This program is free software under the GNU GPL v3.0 9 | * 10 | * ***************************************************************************/ 11 | 12 | 13 | // do you have SMETER? 14 | #ifdef SMETER 15 | 16 | // declaration for the special chars in the SMETER bar 17 | /************************************** 18 | * As per the LCD datasheet: 19 | * 20 | * Each char is a matrix of 8x8 21 | * but internally they are: 22 | * > 5 bits per line (lower 5 bits) 23 | * > 7 lines 24 | * > the underscore line 25 | * 26 | **************************************/ 27 | #ifdef SMETER_ALT 28 | // Alternative SMeter, low resolution and more code size 29 | byte bar[8] = { 30 | B11111, 31 | B11111, 32 | B11111, 33 | B10001, 34 | B11111, 35 | B11111, 36 | B11111 37 | }; 38 | 39 | byte s1[8] = { 40 | B11111, 41 | B10011, 42 | B11011, 43 | B11011, 44 | B11011, 45 | B10001, 46 | B11111 47 | }; 48 | 49 | byte s3[8] = { 50 | B11111, 51 | B10001, 52 | B11101, 53 | B10001, 54 | B11101, 55 | B10001, 56 | B11111 57 | }; 58 | 59 | byte s5[8] = { 60 | B11111, 61 | B10001, 62 | B10111, 63 | B10001, 64 | B11101, 65 | B10001, 66 | B11111 67 | }; 68 | 69 | byte s7[8] = { 70 | B11111, 71 | B10001, 72 | B11101, 73 | B11011, 74 | B11011, 75 | B11011, 76 | B11111 77 | }; 78 | 79 | byte s9[8] = { 80 | B11111, 81 | B10001, 82 | B10101, 83 | B10001, 84 | B11101, 85 | B11101, 86 | B11111 87 | }; 88 | 89 | #else 90 | // Default SMEter, high resolution small code size 91 | byte half[8] = { 92 | B11000, 93 | B11000, 94 | B11000, 95 | B11000, 96 | B11000, 97 | B11000, 98 | B11000, 99 | B00000 100 | }; 101 | 102 | byte full[8] = { 103 | B11011, 104 | B11011, 105 | B11011, 106 | B11011, 107 | B11011, 108 | B11011, 109 | B11011, 110 | B00000 111 | }; 112 | #endif 113 | 114 | 115 | // bar show function two alternatives upon smeter type 116 | #ifdef SMETER_ALT 117 | // Alternative "1-3-5-7-9-+20" 118 | void showBarGraph() { 119 | // we are working on a 2x16 and we have 13 bars to show (0-12) 120 | unsigned long ave = 0, i; 121 | volatile static byte barMax = 0; 122 | 123 | // find the average 124 | for (i=0; i barMax) { 146 | // LCD position & print the bars 147 | lcd.setCursor(3 + barMax, 1); 148 | 149 | // write it 150 | for (i = barMax; i <= local; i++) { 151 | switch (i) { 152 | case 0: 153 | lcd.write(byte(1)); 154 | break; 155 | case 2: 156 | lcd.write(byte(2)); 157 | break; 158 | case 4: 159 | lcd.write(byte(3)); 160 | break; 161 | case 6: 162 | lcd.write(byte(4)); 163 | break; 164 | case 8: 165 | lcd.write(byte(5)); 166 | break; 167 | default: 168 | lcd.write(byte(0)); 169 | break; 170 | } 171 | } 172 | 173 | // second part of the erase, preparing for the blanking 174 | if (barReDraw) barMax = 12; 175 | } 176 | 177 | // shrinking bar: erase the old ones print spaces to erase just the diff 178 | if (barMax > local) { 179 | lcd.setCursor(3 + barMax, 1); 180 | spaces(barMax - local); 181 | } 182 | 183 | // put the var for the next iteration 184 | barMax = local; 185 | //reset the redraw flag 186 | barReDraw = false; 187 | } 188 | #else 189 | // default smeter "|||||||||||||||||||" 190 | void showBarGraph() { 191 | // we are working on a 2x16 and we have 13 bars to show (0-13) 192 | // as we are on a double line we have 0-24 in value 193 | unsigned long ave = 0, i; 194 | volatile static byte barMax = 26; 195 | byte fb, hb; 196 | 197 | // pack for average 198 | for (i=0; i barMax) { 221 | // reset barMax to a even number to avoid half bars loose 222 | if (barMax % 2) barMax += -1; 223 | 224 | // how many bars 225 | fb = (actual - barMax) / 2; 226 | hb = (actual - barMax) % 2; 227 | 228 | // LCD position 229 | lcd.setCursor(3 + (barMax/2), 1); 230 | 231 | // full bars 232 | if (fb > 0) 233 | for (word i = 0; i < fb; i++) 234 | lcd.write(byte(0)); // full bar 235 | 236 | // half bars 237 | // must be always just one half bar 238 | if (hb > 0) 239 | lcd.write(byte(1)); // half bar 240 | } 241 | 242 | // shrinking bar: erase the old ones 243 | // just print spaces to erase just the diff 244 | if (barMax > actual) { 245 | // base position, lower value 246 | fb = actual / 2; // base position 247 | hb = actual % 2; 248 | 249 | // fail safe we always want a single bar even if zero 250 | if (actual = 0) hb = 1; 251 | 252 | // LCD position 253 | lcd.setCursor(3 + fb, 1); 254 | 255 | // half bars 256 | if (hb > 0) { 257 | // must be always just one half bar 258 | lcd.write(byte(1)); // half bar 259 | } 260 | 261 | // erase the next resting bars 262 | spaces(((barMax + 1) - actual) / 2); 263 | } 264 | 265 | // put the var for the next iteration 266 | barMax = actual; 267 | 268 | // reset the bar redraw flag 269 | barReDraw = false; 270 | } 271 | #endif 272 | 273 | 274 | // take a sample an inject it on the array 275 | void takeSample() { 276 | // reference is 5v 277 | word val; 278 | byte adcPin = 1; 279 | 280 | // check if TX 281 | if (tx) adcPin = 0; 282 | // take sample 283 | val = analogRead(adcPin); 284 | 285 | // push it in the array 286 | for (byte i = 0; i < BARGRAPH_SAMPLES - 1; i++) pep[i] = pep[i + 1]; 287 | pep[BARGRAPH_SAMPLES - 1] = val; 288 | } 289 | 290 | 291 | // smeter reading, this take a sample of the smeter/txpower each time; an will 292 | // rise a flag when they have rotated the array of measurements 2/3 times to 293 | // have a moving average 294 | void smeter() { 295 | // static smeter array counter 296 | volatile static byte smeterCount = 0; 297 | 298 | // no matter what, I must keep taking samples 299 | takeSample(); 300 | 301 | // it has rotated already? 302 | if (smeterCount > (BARGRAPH_SAMPLES * 2 / 3)) { 303 | // rise the flag about the need to show the bar graph and reset the count 304 | smeterOk = true; 305 | smeterCount = 0; 306 | } else { 307 | // just increment it 308 | smeterCount += 1; 309 | } 310 | } 311 | 312 | #endif 313 | -------------------------------------------------------------------------------- /z-end.ino: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * 3 | * This file is part of the Arduino-Arcs project, see 4 | * https://github.com/pavelmc/arduino-arcs 5 | * 6 | * Copyright (C) 2016...2017 Pavel Milanes (CO7WT) 7 | * 8 | * This program is free software under the GNU GPL v3.0 9 | * 10 | * ***************************************************************************/ 11 | 12 | 13 | // main setup function 14 | void setup() { 15 | // set the defult values, before restoring theEEPROM ones 16 | setDefaultVals(); 17 | 18 | #ifdef CAT_CONTROL 19 | // CAT Library setup 20 | cat.addCATPtt(catGoPtt); 21 | cat.addCATAB(catGoToggleVFOs); 22 | cat.addCATFSet(catSetFreq); 23 | cat.addCATMSet(catSetMode); 24 | cat.addCATGetFreq(catGetFreq); 25 | cat.addCATGetMode(catGetMode); 26 | cat.addCATSMeter(catGetSMeter); 27 | cat.addCATTXStatus(catGetTXStatus); 28 | // now we activate the library 29 | cat.begin(38400, SERIAL_8N2); 30 | #endif 31 | 32 | #ifdef LCD 33 | #ifdef SMETER 34 | // LCD init, create the custom chars first 35 | // depending on the selected smeter type 36 | #ifdef SMETER_ALT 37 | lcd.createChar(0, bar); 38 | lcd.createChar(1, s1); 39 | lcd.createChar(2, s3); 40 | lcd.createChar(3, s5); 41 | lcd.createChar(4, s7); 42 | lcd.createChar(5, s9); 43 | #else 44 | // default 45 | lcd.createChar(0, full); 46 | lcd.createChar(1, half); 47 | #endif 48 | #endif // smeter 49 | 50 | // now load the library 51 | lcd.begin(16, 2); 52 | lcd.clear(); 53 | #endif // nolcd 54 | 55 | #ifdef ABUT 56 | // analog buttons setup 57 | abm.init(KEYS_PIN, 3, 20); 58 | abm.add(bvfoab); 59 | abm.add(bmode); 60 | abm.add(brit); 61 | abm.add(bsplit); 62 | 63 | // how many memories this chip supports 64 | #ifdef MEMORIES 65 | memCount = (EEPROM.length() - MEMSTART) / sizeof(mmem); 66 | // self limiting the mem amount to 100 (0-99) 67 | // we have only two chars on the LCD and 100 mems are a lot 68 | if (memCount > 99) memCount = 99; 69 | #endif 70 | #endif 71 | 72 | #ifdef ROTARY 73 | // buttons debounce & PTT 74 | pinMode(btnPush, INPUT_PULLUP); 75 | dbBtnPush.attach(btnPush); 76 | dbBtnPush.interval(debounceInterval); 77 | // pin mode of the PTT 78 | pinMode(inPTT, INPUT_PULLUP); 79 | dbPTT.attach(inPTT); 80 | dbPTT.interval(debounceInterval); 81 | // default awake mode is RX 82 | #endif // ROTARY 83 | 84 | // set PTT at the start to LOW aka RX 85 | pinMode(PTT, OUTPUT); 86 | digitalWrite(PTT, 0); 87 | 88 | // I2C init 89 | Wire.begin(); 90 | 91 | // check the EEPROM to know if I need to initialize it 92 | if (checkInitEEPROM()) { 93 | // just if it's already ok 94 | loadEEPROMConfig(); 95 | } else { 96 | #ifdef LCD 97 | // full init, LCD banner 98 | lcd.clear(); 99 | lcd.setCursor(0, 0); 100 | lcd.print(F("Init EEPROM")); 101 | lcd.setCursor(0, 1); 102 | lcd.print(F("Please wait")); 103 | #endif // nolcd 104 | 105 | // init eeprom. 106 | saveEEPROM(); 107 | 108 | #ifdef MEMORIES 109 | wipeMEM(); 110 | #endif 111 | 112 | // make a smart delay 113 | smartDelay(); 114 | 115 | #ifdef LCD 116 | lcd.clear(); 117 | #endif // nolcd 118 | } 119 | 120 | #ifdef LCD 121 | // Welcome screen 122 | lcd.clear(); 123 | lcd.print(F(" Aduino Arcs ")); 124 | lcd.setCursor(0, 1); 125 | lcd.print(F("Fv: ")); 126 | lcd.print(FMW_VER); 127 | lcd.print(F(" Mfv: ")); 128 | lcd.print(EEP_VER); 129 | #endif // nolcd 130 | 131 | // A software controlling the CAT via USB will reset the sketch upon 132 | // connection, so we need turn the cat.check() when running the welcome 133 | // banners (use case: Fldigi) 134 | 135 | // make a smart delay 136 | smartDelay(); 137 | 138 | #ifdef LCD 139 | lcd.setCursor(0, 0); 140 | lcd.print(F(" by Pavel CO7WT ")); 141 | #endif // nolcd 142 | 143 | // make a smart delay 144 | smartDelay(); 145 | 146 | #ifdef LCD 147 | lcd.clear(); 148 | #endif // nolcd 149 | 150 | #ifdef ROTARY // check for SETUP mode 151 | // if you have no analog buttons then 152 | // there is no meaning for the setup mode 153 | #ifdef ABUT 154 | // Check for setup mode 155 | if (digitalRead(btnPush) == LOW) { 156 | // sound signal 157 | beep(); 158 | delay(50); 159 | beop(); 160 | 161 | // rise the flag of setup mode for every body to see it. 162 | runMode = false; 163 | 164 | // beep signal to the user 165 | tone(4, 800, 250); 166 | delay(250); 167 | 168 | #ifdef CAT_CONTROL 169 | // CAT is disabled in SETUP mode 170 | cat.enabled = false; 171 | #endif 172 | 173 | #ifdef LCD 174 | // we are in the setup mode 175 | lcd.setCursor(0, 0); 176 | lcd.print(F(" You are in the ")); 177 | lcd.setCursor(0, 1); 178 | lcd.print(F(" SETUP MODE ")); 179 | delay(2000); 180 | lcd.clear(); 181 | // show setup mode 182 | showConfig(); 183 | #endif // lcd 184 | } 185 | #endif // abut 186 | #endif // rotary 187 | 188 | // setting up VFO A as principal. 189 | activeVFO = true; 190 | ptrVFO = &u.a; 191 | ptrMode = &u.aMode; 192 | 193 | // init the wire lib 194 | Wire.begin(); 195 | 196 | // start the VFOa and it's mode 197 | updateAllFreq(); 198 | 199 | // reset the Si5351 for the forst time 200 | Si5351_resets(); 201 | } 202 | 203 | 204 | // forever loop 205 | void loop() { 206 | #ifdef ROTARY 207 | // encoder check 208 | encoderState = encoder.process(); 209 | if (encoderState == DIR_CW) encoderMoved(+1); 210 | if (encoderState == DIR_CCW) encoderMoved(-1); 211 | #endif // rotary 212 | 213 | // LCD update check in normal mode 214 | if (update and runMode) { 215 | #ifdef LCD 216 | // update and reset the flag 217 | updateLcd(); 218 | #endif // nolcd 219 | update = false; 220 | } 221 | 222 | #ifdef ROTARY 223 | // check Hardware PTT and make the RX/TX changes 224 | if (dbPTT.update()) { 225 | // state changed 226 | if (dbPTT.fell()) { 227 | // line asserted (PTT Closed) going to TX 228 | going2TX(); 229 | 230 | // clear the memory scan flag if active 231 | #ifdef MEMORIES 232 | #ifdef MEM_SCAN 233 | mscan = false; 234 | #endif 235 | #endif 236 | } else { 237 | // line left open, going to RX 238 | going2RX(); 239 | } 240 | 241 | // update flag 242 | update = true; 243 | } 244 | 245 | // debouce for the push 246 | dbBtnPush.update(); 247 | tbool = dbBtnPush.fell(); 248 | 249 | if (runMode) { 250 | // we are in normal mode 251 | 252 | // step (push button) 253 | if (tbool) { 254 | // beep 255 | beep(); 256 | 257 | // VFO step change 258 | changeStep(); 259 | update = true; 260 | } 261 | 262 | #ifdef SMETER 263 | // Second line of the LCD, I must show the bargraph only if not rit nor steps 264 | if ((!ritActive and showStepCounter == 0) and smeterOk) 265 | showBarGraph(); 266 | #endif 267 | } 268 | 269 | 270 | #ifdef ABUT // no setup mode if no analog buttons to change it 271 | if (!runMode) { 272 | // setup mode 273 | 274 | // Push button is step in Config mode 275 | if (tbool) { 276 | // change the step and show it on the LCD 277 | changeStep(); 278 | #ifdef LCD 279 | showStep(); 280 | #endif // nolcd 281 | } 282 | 283 | } 284 | #endif // abut 285 | #endif // rotary 286 | 287 | // timed actions, it ticks every 1/4 second (250 msecs) 288 | if ((millis() - lastMilis) >= TICK_INTERVAL) { 289 | // Reset the last reading to keep track 290 | lastMilis = millis(); 291 | 292 | #ifdef SMETER 293 | // I must sample the input for the bar graph 294 | smeter(); 295 | #endif 296 | 297 | // time counter for VFO remember after power off 298 | if (qcounter < SAVE_INTERVAL) { 299 | qcounter += 1; 300 | } else { 301 | // the saveEEPROM has already a mechanism to change only the parts 302 | // that has changed, to protect the EEPROM of wear out 303 | saveEEPROM(); 304 | qcounter = 0; 305 | } 306 | 307 | // step show time show the first time 308 | if (showStepCounter >= STEP_SHOW_TIME) { 309 | #ifdef LCD 310 | showStep(); 311 | #endif // nolcd 312 | } 313 | 314 | // decrement timer 315 | if (showStepCounter > 0) { 316 | showStepCounter -= 1; 317 | #ifdef SMETER 318 | // flag to redraw the bar graph only if zero 319 | if (showStepCounter == 0) barReDraw = true; 320 | #endif 321 | } 322 | } 323 | 324 | #ifdef CAT_CONTROL 325 | // CAT check 326 | cat.check(); 327 | #endif 328 | 329 | #ifdef ABUT 330 | // analog buttons check 331 | abm.check(); 332 | #endif 333 | 334 | #ifdef MEMORIES 335 | #ifdef MEM_SCAN 336 | // memory scan timming check 337 | checkMemScan(); 338 | #endif 339 | #endif 340 | } 341 | -------------------------------------------------------------------------------- /fa-lcd.ino: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * 3 | * This file is part of the Arduino-Arcs project, see 4 | * https://github.com/pavelmc/arduino-arcs 5 | * 6 | * Copyright (C) 2016...2017 Pavel Milanes (CO7WT) 7 | * 8 | * This program is free software under the GNU GPL v3.0 9 | * 10 | * ***************************************************************************/ 11 | 12 | 13 | #ifdef LCD 14 | #ifdef ABUT 15 | // check some values don't go below zero, as some values are not meant 16 | // to be below zero, this check and fix that 17 | void belowZero(long *value) { if (*value < 0) *value = 0; } 18 | 19 | 20 | // update the setup values 21 | void updateSetupValues(int dir) { 22 | // we are in setup mode, showing or modifying? 23 | if (!inSetup) { 24 | // just showing, show the config on the LCD 25 | updateShowConfig(dir); 26 | } else { 27 | // I'm modifying, switch on the config item 28 | switch (config) { 29 | case CONFIG_IF: 30 | // change the IF value 31 | u.ifreq += getStep() * dir; 32 | belowZero(&u.ifreq); 33 | break; 34 | case CONFIG_IF2: 35 | // change the high IF value 36 | u.if2 += getStep() * dir; 37 | belowZero(&u.if2); 38 | break; 39 | case CONFIG_VFO_A: 40 | case CONFIG_VFO_B: 41 | // update freq 42 | updateFreq(dir); 43 | break; 44 | case CONFIG_MODE_A: 45 | case CONFIG_MODE_B: 46 | // update mode 47 | changeMode(); 48 | break; 49 | case CONFIG_USB: 50 | // change the mode to USB 51 | *ptrMode = MODE_USB; 52 | // change the USB BFO 53 | u.usb += getStep() * dir; 54 | break; 55 | case CONFIG_CW: 56 | // change the mode to CW 57 | *ptrMode = MODE_CW; 58 | // change the CW BFO 59 | u.cw += getStep() * dir; 60 | break; 61 | case CONFIG_PPM: 62 | // change the Si5351 PPM 63 | u.ppm += getStep() * dir; 64 | // instruct the lib to use the new ppm value 65 | CXTAL = XTAL + u.ppm; 66 | // reset the Si5351 67 | Si5351_resets(); 68 | break; 69 | } 70 | 71 | // for all cases update the freqs 72 | updateAllFreq(); 73 | 74 | // update el LCD 75 | showModConfig(); 76 | } 77 | } 78 | 79 | 80 | // update the configuration item before selecting it 81 | void updateShowConfig(int dir) { 82 | // move the config item, it's a byte, so we use a temp var here 83 | int tconfig = config; 84 | tconfig += dir; 85 | 86 | if (tconfig > CONFIG_MAX) tconfig = 0; 87 | if (tconfig < 0) tconfig = CONFIG_MAX; 88 | config = tconfig; 89 | 90 | // force specific VFO/MODE operations by config items 91 | 92 | // force VFO A Operation 93 | tbool = (config == CONFIG_VFO_A) or (config == CONFIG_MODE_A); 94 | if (tbool) swapVFO(1); 95 | 96 | // force VFO B Operation 97 | tbool = (config == CONFIG_VFO_B) or (config == CONFIG_MODE_B); 98 | if (tbool) swapVFO(0); 99 | 100 | // force USB 101 | if (config == CONFIG_USB) *ptrMode = MODE_USB; 102 | 103 | // force CW 104 | if (config == CONFIG_CW) *ptrMode = MODE_CW; 105 | 106 | // force LSB for all config modes with no specific mode 107 | tbool = (config == CONFIG_IF) or (config == CONFIG_IF2); 108 | tbool |= (config == CONFIG_PPM); 109 | if (tbool) *ptrMode = MODE_LSB; 110 | 111 | // set all freqs to apply any changes 112 | updateAllFreq(); 113 | 114 | // update the LCD 115 | showConfig(); 116 | } 117 | 118 | 119 | // show the labels of the config 120 | void showConfigLabels() { 121 | switch (config) { 122 | case CONFIG_IF: 123 | lcd.print(F(" Main IF freq. ")); 124 | break; 125 | case CONFIG_IF2: 126 | lcd.print(F(" High IF (opt) ")); 127 | break; 128 | case CONFIG_VFO_A: 129 | case CONFIG_VFO_B: 130 | lcd.print(F(" VFO ")); 131 | vfoLetter(); 132 | lcd.print(F(" freq ")); 133 | break; 134 | case CONFIG_MODE_A: 135 | case CONFIG_MODE_B: 136 | lcd.print(F(" VFO ")); 137 | vfoLetter(); 138 | lcd.print(F(" mode ")); 139 | break; 140 | case CONFIG_USB: 141 | lcd.print(F(" BFO freq. USB ")); 142 | break; 143 | case CONFIG_CW: 144 | lcd.print(F(" BFO freq. CW ")); 145 | break; 146 | case CONFIG_PPM: 147 | lcd.print(F("Si5351 PPM error")); 148 | break; 149 | } 150 | } 151 | 152 | 153 | // show the setup main menu 154 | void showConfig() { 155 | // we have update the whole LCD screen 156 | lcd.setCursor(0, 0); 157 | lcd.print(F("#> SETUP MENU <#")); 158 | lcd.setCursor(0, 1); 159 | // show the specific item label 160 | showConfigLabels(); 161 | } 162 | 163 | 164 | // show the mode for the passed mode in setup mode 165 | void showModeSetup(byte mode) { 166 | // now I have to print it out 167 | lcd.setCursor(0, 1); 168 | spaces(11); 169 | showModeLcd(mode); // this one has a implicit extra space after it 170 | } 171 | 172 | 173 | // show the ppm as a signed long 174 | void showConfigValue(long val) { 175 | // add spacers 176 | spaces(3); 177 | 178 | // Show the sign only on config and not VFO & IF 179 | boolean t; 180 | t = (config == CONFIG_VFO_A) or (config == CONFIG_VFO_B); 181 | t |= (config == CONFIG_IF); 182 | if (!runMode and !t) showSign(val); 183 | 184 | // print it 185 | formatFreq(abs(val)); 186 | 187 | // if on normal mode we show in 10 Hz 188 | if (runMode) lcd.print("0"); 189 | lcd.print(F("Hz")); 190 | } 191 | 192 | 193 | // update the specific setup item 194 | void showModConfig() { 195 | lcd.setCursor(0, 0); 196 | showConfigLabels(); 197 | 198 | // show the specific values 199 | lcd.setCursor(0, 1); 200 | switch (config) { 201 | case CONFIG_IF: 202 | showConfigValue(u.ifreq); 203 | break; 204 | case CONFIG_IF2: 205 | showConfigValue(u.if2); 206 | break; 207 | case CONFIG_VFO_A: 208 | case CONFIG_VFO_B: 209 | showConfigValue(*ptrVFO); 210 | break; 211 | case CONFIG_MODE_A: 212 | case CONFIG_MODE_B: 213 | showModeSetup(*ptrMode); 214 | break; 215 | case CONFIG_USB: 216 | showConfigValue(u.usb); 217 | break; 218 | case CONFIG_CW: 219 | showConfigValue(u.cw); 220 | break; 221 | case CONFIG_PPM: 222 | showConfigValue(u.ppm); 223 | break; 224 | } 225 | } 226 | #endif // abut|rotary 227 | 228 | 229 | // print the sign of a passed parameter 230 | void showSign(long val) { 231 | // just print it 232 | if (val > 0) lcd.print("+"); 233 | if (val < 0) lcd.print("-"); 234 | if (val == 0) lcd.print(" "); 235 | } 236 | 237 | 238 | // print a string of spaces, to save some flash/eeprom "space" 239 | void spaces(byte m) { 240 | // print m spaces in the LCD 241 | while (m) { lcd.print(" "); m--; } 242 | } 243 | 244 | 245 | // format the freq to easy viewing 246 | void formatFreq(long freq) { 247 | // for easy viewing we format a freq like 7.110 to 7.110.00 248 | long t; 249 | 250 | // Mhz part 251 | t = freq / 1000000; 252 | if (t < 10) lcd.print(" "); 253 | if (t == 0) { 254 | spaces(2); 255 | } else { 256 | lcd.print(t); 257 | // first dot: optional 258 | lcd.print("."); 259 | } 260 | // Khz part 261 | t = (freq % 1000000); 262 | t /= 1000; 263 | if (t < 100) lcd.print("0"); 264 | if (t < 10) lcd.print("0"); 265 | lcd.print(t); 266 | // second dot: forced 267 | lcd.print("."); 268 | // hz part 269 | t = (freq % 1000); 270 | if (t < 100) lcd.print("0"); 271 | // check if in config and show up to 1hz resolution 272 | if (!runMode) { 273 | if (t < 10) lcd.print("0"); 274 | lcd.print(t); 275 | } else { 276 | lcd.print(t/10); 277 | } 278 | } 279 | 280 | 281 | // main update Lcd procedure 282 | void updateLcd() { 283 | // need to check if memories are available 284 | #ifdef MEMORIES 285 | // VFO or memory 286 | if (vfoMode) { 287 | // VFO mode, normal LCD 288 | vfoUpdateLcd(); 289 | } else { 290 | memUpdateLcd(); 291 | } 292 | #else 293 | vfoUpdateLcd(); 294 | #endif // memories 295 | 296 | } 297 | 298 | 299 | #ifdef MEMORIES 300 | // lcd update in mem mode 301 | void memUpdateLcd() { 302 | /****************************************************** 303 | * 0123456789abcdef 304 | * ------------------ 305 | * |01 14.280.25 lsb| 306 | * 307 | ******************************************************/ 308 | 309 | // first line 310 | lcd.setCursor(0, 0); 311 | 312 | // channel number with provision for the leading space below 10 313 | if (mem < 10) lcd.print("0"); 314 | lcd.print(mem); 315 | lcd.print(" "); 316 | 317 | lcdRefresh(); 318 | } 319 | #endif // memories 320 | 321 | 322 | // lcd update in normal mode 323 | void vfoUpdateLcd() { 324 | /****************************************************** 325 | * 0123456789abcdef 326 | * ------------------ 327 | * |A* 14.280.25 lsb| normal 328 | * 329 | * |A 14.280.25 lsb| split 330 | ******************************************************/ 331 | 332 | // first line 333 | lcd.setCursor(0, 0); 334 | // active vfo 335 | vfoLetter(); 336 | 337 | // split? 338 | if (split) { 339 | // ok, show the split status as a * sign 340 | lcd.print("* "); 341 | } else { 342 | // print a separator. 343 | spaces(2); 344 | } 345 | 346 | // main lcd routine 347 | lcdRefresh(); 348 | } 349 | 350 | 351 | // refresh the lcd routine 352 | void lcdRefresh() { 353 | 354 | #ifdef MEMORIES 355 | if (!vfoMode and !memo.configured) { 356 | // memory not configured, so not showing the freq 357 | lcd.print(F("--.---.-- ---")); 358 | } else { 359 | // show VFO and mode 360 | formatFreq(*ptrVFO); 361 | // space and mode 362 | spaces(1); 363 | showModeLcd(*ptrMode); 364 | } 365 | 366 | 367 | #else 368 | // show VFO and mode 369 | formatFreq(*ptrVFO); 370 | spaces(1); 371 | showModeLcd(*ptrMode); 372 | #endif 373 | 374 | // second line 375 | lcd.setCursor(0, 1); 376 | if (tx) { 377 | lcd.print(F("TX ")); 378 | } else { 379 | lcd.print(F("RX ")); 380 | } 381 | 382 | // if we have a RIT or steps we manage it here and the bar will hold 383 | if (ritActive) showRit(); 384 | } 385 | 386 | 387 | // show rit in LCD 388 | void showRit() { 389 | /*********************************************************************** 390 | * RIT show something like this on the line of the non active VFO 391 | * 392 | * |0123456789abcdef| 393 | * |----------------| 394 | * |RX RIT -9.99 khz| 395 | * |----------------| 396 | * 397 | * WARNING !!! If the user change the VFO we need to *RESET* 398 | * & disable the RIT ASAP. 399 | * 400 | **********************************************************************/ 401 | 402 | // get the active VFO to calculate the deviation & scale it down 403 | long diff = (*ptrVFO - tvfo)/10; 404 | 405 | // we start on line 2, char 3 of the second line 406 | lcd.setCursor(3, 1); 407 | lcd.print(F("RIT ")); 408 | 409 | // show the difference in Khz on the screen with sign 410 | // diff can overflow the input of showSign, so we scale it down 411 | showSign(diff); 412 | 413 | // print the freq now, we have a max of 10 Khz (9.990 Khz) 414 | diff = abs(diff); 415 | 416 | // Khz part (999) 417 | word t = diff / 100; 418 | lcd.print(t); 419 | lcd.print("."); 420 | // hz part 421 | t = diff % 100; 422 | if (t < 10) lcd.print("0"); 423 | lcd.print(t); 424 | spaces(1); 425 | // unit 426 | lcd.print(F("kHz")); 427 | } 428 | 429 | 430 | // show the mode on the LCD 431 | void showModeLcd(byte mode) { 432 | // print it 433 | switch (mode) { 434 | case MODE_LSB: 435 | lcd.print(F("LSB ")); 436 | break; 437 | case MODE_USB: 438 | lcd.print(F("USB ")); 439 | break; 440 | case MODE_CW: 441 | lcd.print(F("CW ")); 442 | break; 443 | } 444 | } 445 | 446 | 447 | // show the vfo step 448 | void showStep() { 449 | // in nomal or setup mode? 450 | if (runMode) { 451 | // in normal mode is the second line, third char 452 | lcd.setCursor(3, 1); 453 | } else { 454 | // in setup mode is just in the begining of the second line 455 | lcd.setCursor(0, 1); 456 | } 457 | 458 | // show it 459 | switch (step) { 460 | case 1: 461 | lcd.print(F(" 1Hz")); 462 | break; 463 | case 2: 464 | lcd.print(F(" 10Hz")); 465 | break; 466 | case 3: 467 | lcd.print(F("100Hz")); 468 | break; 469 | case 4: 470 | lcd.print(F(" 1kHz")); 471 | break; 472 | case 5: 473 | lcd.print(F("10kHz")); 474 | break; 475 | case 6: 476 | lcd.print(F(" 100k")); 477 | break; 478 | case 7: 479 | lcd.print(F(" 1MHz")); 480 | break; 481 | } 482 | spaces(11); 483 | } 484 | 485 | #endif // lcd 486 | -------------------------------------------------------------------------------- /arduino-arcs.ino: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * ARCS - Amateur Radio Control & Clock Solution 4 | * ----------------------------------------------- 5 | * A full QRP/Hombrew transceiver control with RF generation, the Cuban way. 6 | * 7 | * Copyright (C) 2016...2017 Pavel Milanes (CO7WT) 8 | * 9 | * This work is based on the previous work of these great people: 10 | * * NT7S (http://nt7s.com) 11 | * * SQ9NJE (http://sq9nje.pl) 12 | * * AK2B (http://ak2b.blogspot.com) 13 | * * QRL Labs team (http://qrp-labs.com) 14 | * * WJ6C for the idea and hardware support. 15 | * * Many other hams with code, ideas, critics and opinions 16 | * 17 | * Latest version is always found on the Github repository (URL below) 18 | * https://www.github.com/pavelmc/arduino-arcs/ 19 | * 20 | * This program is free software: you can redistribute it and/or modify 21 | * it under the terms of the GNU General Public License as published by 22 | * the Free Software Foundation, either version 3 of the License, or 23 | * (at your option) any later version. 24 | * 25 | * This program is distributed in the hope that it will be useful, 26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 | * GNU General Public License for more details. 29 | * 30 | * You should have received a copy of the GNU General Public License 31 | * along with this program. If not, see . 32 | *******************************************************************************/ 33 | 34 | /******************************************************************************* 35 | * Important information you need to know about this code & Si5351 36 | * 37 | * We use a embed Si5351a control code that presume this: 38 | * * We use CLK0 & CLK1 ONLY 39 | * * CLK0 use PLLA & CLK1 use PLLB 40 | * * CLK0 is employed as VFO (to mix with the RF from/to the antenna) 41 | * * CLK1 is employed as BFO (to mix with the IF to mod/demodulate the audio) 42 | * * We use the full power in all outputs (8mA) 43 | * 44 | * * Please have in mind that this IC has a SQUARE wave output and you MAY 45 | * need to apply some kind of low-pass/band-pass filtering to smooth it 46 | * and get rid of the nasty harmonics. 47 | * * If you generate a BFO below 1Mhz you MUST filter the output of that CLK. 48 | * The output of the Si5351 is harmonic rich below 1Mhz and you will NEED 49 | * the filtering because those harmonics are close enough of the fundamental. 50 | ******************************************************************************/ 51 | 52 | /****************************** FEATURES SEGMENTATION ************************* 53 | * Here we activate/deactivate some of the sketch features, it's just a matter 54 | * of comment out the feature and it will keep out of compilation (smaller code) 55 | * 56 | * For example: one user requested a "headless" mode: no lcd, no buttons, just 57 | * cat control via serial/usb from the PC. for that we have the headless mode. 58 | * Tip: uncomment th LCD define and you will have it. 59 | *******************************************************************************/ 60 | 61 | // You like to have CAT control (PC control) of the sketch via Serial link? 62 | #define CAT_CONTROL True 63 | 64 | // Rotary and push control? 65 | #define ROTARY True 66 | 67 | // Analog button support? 68 | // please note that if no rotary then abut is disabled 69 | #define ABUT True 70 | 71 | // Memories? 72 | #define MEMORIES True // hard limited to 99 mems (~80 on the ATMEga168) 73 | // because we have only two spaces to represent that 74 | 75 | // Also you want to scan over enabled memories? 76 | #define MEM_SCAN True 77 | 78 | // Smeter on the LCD? 79 | #define SMETER True 80 | 81 | // You have two alternatives for the SMeter graphics 82 | // The default is a simple bar with 24 points (high) resolution 83 | // The alternative is a low resolution one with 12 points 84 | // but numbers in the 1-3-5-7-9-+20 85 | // 86 | // If you want the alternative just uncomment this code below 87 | // the alternative willl cost you about 10 bytes of firmware 88 | //#define SMETER_ALT True 89 | 90 | // do you have an LCD? 91 | #define LCD True 92 | 93 | // if you want a headless control unit just comment the line above to 94 | // effectively disable the LCD, when you have no LCD then there is no 95 | // meaning for buttons, memories, rotary encoder, etc. 96 | // You will get just CAT control 97 | 98 | #ifndef LCD 99 | // no smeter 100 | #ifdef SMETER 101 | #undef SMETER 102 | #endif 103 | 104 | // no Analog Buttons 105 | #ifdef ABUT 106 | #undef ABUT 107 | #endif // abut 108 | 109 | // no rotary ot push buttons 110 | #ifdef ROTARY 111 | #undef ROTARY 112 | #endif // rotary 113 | 114 | // no memories 115 | #ifdef MEMORIES 116 | #undef MEMORIES 117 | #endif // memories 118 | 119 | // YES CAT_CONTROL 120 | #ifndef CAT_CONTROL 121 | #define CAT_CONTROL True 122 | #endif // cat control 123 | #endif // headless 124 | 125 | // safety check for no rotary, if no rotary then abut has no use. 126 | #ifndef ROTARY 127 | // no need for Analog Buttons 128 | #ifdef ABUT 129 | #undef ABUT 130 | #endif // abut 131 | 132 | // no memories, beacause no abut 133 | #ifdef MEMORIES 134 | #undef MEMORIES 135 | #endif // memories 136 | #endif // rotary 137 | 138 | // safety check for memories only when analog buttons are in use 139 | #ifndef ABUT 140 | #ifdef MEMORIES 141 | #undef MEMORIES 142 | #endif // memories 143 | #endif // abut 144 | 145 | // default (non optional) libraries loading 146 | #include // Internal EEPROM 147 | #include // I2C 148 | 149 | // The eeprom & sketch version; if the eeprom version is lower than the one on 150 | // the sketch we force an update (init) to make a consistent work on upgrades 151 | const byte EEP_VER = 8; 152 | const byte FMW_VER = 16; 153 | 154 | // structured data: Main Configuration Parameters 155 | struct userData { 156 | byte fmwver = FMW_VER; 157 | byte eepver = EEP_VER; 158 | long ifreq; // first or unique IF 159 | long if2; // second IF, usually higher than the ifreq 160 | long a; // VFO a 161 | byte aMode; // VFO a mode 162 | long b; // VFO b 163 | byte bMode; // VFO b mode 164 | int usb; 165 | int cw; 166 | int ppm; 167 | }; 168 | 169 | // declaring the main configuration variable for mem storage 170 | // BEWARE: you can't use it outside a procedure or function 171 | // the compiler is not happy with that. 172 | struct userData u; 173 | 174 | // The start byte in the eeprom where we put mem[0] 175 | #define MEMSTART sizeof(u) 176 | 177 | // the limits of the VFOs: full HF; you can tweak it with the 178 | // limits of your particular hardware, again this are LCD diplay frequencies. 179 | #define F_MIN 500000 // 500 kHz 180 | #define F_MAX 30000000 // 30.0 MHz 181 | 182 | // PTT IN/OUT pin 183 | #define PTT 13 // PTT actuator, this will put the radio on TX 184 | // this match the led on pin 13 with the PTT 185 | #define inPTT 12 // PTT/CW KEY Line with pullup 186 | 187 | #ifdef ROTARY 188 | // Enable weak pullups in the rotary lib before inclusion 189 | #define ENABLE_PULLUPS 190 | 191 | // If you have a half step encoder (you need two clicks to make a step) 192 | // uncomment this to get it working 193 | //#define HALF_STEP 194 | 195 | // include the libs 196 | #include // https://github.com/mathertel/RotaryEncoder/ 197 | #include // https://github.com/thomasfredericks/Bounce2/ 198 | 199 | // define encoder pins 200 | #define ENC_A 3 // Encoder pin A 201 | #define ENC_B 2 // Encoder pin B 202 | #define btnPush 11 // Encoder Button 203 | 204 | // rotary encoder library setup 205 | Rotary encoder = Rotary(ENC_A, ENC_B); 206 | 207 | // the debounce instances 208 | #define debounceInterval 10 // in milliseconds 209 | Bounce dbBtnPush = Bounce(); 210 | Bounce dbPTT = Bounce(); 211 | #endif // rotary 212 | 213 | 214 | #ifdef ABUT 215 | // define the max count for analog buttons in the BMux library 216 | #define BUTTONS_COUNT 4 217 | 218 | // define the sampling rate used 219 | #define BMUX_SAMPLING 10 // 10 samples per second 220 | 221 | // define the analog pin to handle the buttons 222 | #define KEYS_PIN 2 223 | 224 | // include the lib 225 | #include // https://github.com/pavelmc/BMux/ 226 | 227 | // instantiate it 228 | BMux abm; 229 | 230 | // Creating the analog buttons for the BMux lib; see the BMux doc for details 231 | // you may have to tweak this values a little for your particular hardware 232 | // 233 | // Also on the examples on the lib there is a sketch that will allow to 234 | // compute the exact values for your hardware. 235 | // 236 | // Define the adc levels of for the buttons values 237 | // top resistor 4k7 2k2 10k 238 | #define b1 207 // 1k2 470 2k2 239 | #define b2 370 // 2k7 1k 4k7 240 | #define b3 512 // 4k7 2k2 10k 241 | #define b4 697 // 10k 4k7 22k 242 | 243 | #ifdef MEMORIES 244 | // Analog buttons has a second action related to memories 245 | // but only if we have memories set 246 | Button bvfoab = Button(b1, &btnVFOABClick, &btnVFOMEM); 247 | Button bmode = Button(b2, &btnModeClick, &btnVFOsMEM); 248 | Button brit = Button(b3, &btnRITClick, &btnEraseMEM); 249 | Button bsplit = Button(b4, &btnSPLITClick, &btnEraseWholeMem); 250 | 251 | // memory object definition 252 | boolean vfoMode = true; 253 | word mem = 0; // actual memory channel 254 | word memCount; // how many mems this chip support 255 | // (it's calculated in the setup process) 256 | 257 | // memory type 258 | struct mmem { 259 | boolean configured; 260 | long vfo; 261 | byte vfoMode; 262 | }; 263 | 264 | // declaring the main configuration variable for mem storage 265 | struct mmem memo; 266 | 267 | // some vars for the memory scan feature if defined 268 | #ifdef MEM_SCAN 269 | // general memscan flag 270 | bool mscan = true; 271 | #define MEM_SCAN_INTERVAL 5000; // in msecs, the time between scan jumps 272 | unsigned long scanTime; // counter against millis 273 | #endif 274 | 275 | #else 276 | // Analog buttons with single functions 277 | Button bvfoab = Button(b1, &btnVFOABClick); 278 | Button bmode = Button(b2, &btnModeClick); 279 | Button brit = Button(b3, &btnRITClick); 280 | Button bsplit = Button(b4, &btnSPLITClick); 281 | #endif 282 | #endif // abut 283 | 284 | 285 | #ifdef CAT_CONTROL 286 | // library include 287 | #include // https://github.com/pavelmc/ft857d/ 288 | 289 | // instantiate it 290 | ft857d cat = ft857d(); 291 | #endif // cat 292 | 293 | 294 | #ifdef LCD 295 | // lib include 296 | #include // default 297 | 298 | // lcd pins assuming a 1602 (16x2) at 4 bits 299 | // COLAB shield + Arduino Mini/UNO Board 300 | #define LCD_RS 5 301 | #define LCD_E 6 302 | #define LCD_D4 7 303 | #define LCD_D5 8 304 | #define LCD_D6 9 305 | #define LCD_D7 10 306 | 307 | // lcd library instantiate 308 | LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7); 309 | 310 | #ifdef SMETER 311 | // how many samples we take in the smeter, we use a 2/3 trick 312 | // to get some inertia and improve the look & feel of the bar 313 | #define BARGRAPH_SAMPLES 6 314 | word pep[BARGRAPH_SAMPLES] = {}; // s-meter readings storage 315 | boolean smeterOk = false; // it's ok to show the bar graph 316 | word sMeter = 0; // the value of the Smeter readings 317 | // in both RX and TX modes 318 | boolean barReDraw = true; // Smeter bar needs to be redrawn 319 | #endif // smeter 320 | #endif // nolcd 321 | 322 | // run mode constants 323 | #define MODE_LSB 0 324 | #define MODE_USB 1 325 | #define MODE_CW 2 326 | #define MODE_MAX 2 // the mode count to cycle (-1) 327 | #define MAX_RIT 99900 // RIT 9.99 Khz * 10 328 | 329 | // config constants 330 | #define CONFIG_IF 0 331 | #define CONFIG_IF2 1 332 | #define CONFIG_VFO_A 2 333 | #define CONFIG_MODE_A 3 334 | #define CONFIG_VFO_B 4 335 | #define CONFIG_MODE_B 5 336 | #define CONFIG_USB 6 337 | #define CONFIG_CW 7 338 | #define CONFIG_PPM 8 339 | // the amount of configure options 340 | #define CONFIG_MAX 8 341 | 342 | // Tick interval for the timed actions like the SMeter and the autosave 343 | #define TICK_INTERVAL 250 // in milli seconds, 4 ticks per second 344 | 345 | // EERPOM auto saving interval (if some parameter has changed) in TICK_INTERVAL 346 | // var is word so max is 65535 in 1/4 secs is ~ 16383 sec ~ 273 min ~ 4h 33 min 347 | #define SAVE_INTERVAL 2400 // 10 minutes = 60 sec * 4 ticks/sec * 10 min 348 | 349 | // Si5351a Xtal 350 | const long XTAL = 27000000; // default FREQ of the XTAL for the Si5351a 351 | long CXTAL = XTAL + u.ppm; // corrected xtal with the ppm 352 | 353 | /****************************************************************************** 354 | * The use of an XFO... some users are requesting the use of an XFO in the 355 | * calculations. 356 | * 357 | * Some users with double conversion radios has 1st & 2nd IF, so this radios 358 | * usually has a XTAL oscillator to mix 1st & 2nd IF back and forward, this is 359 | * called an XFO. 360 | * 361 | * WARNING!: we are not generating that XFO frequency, just taking it into 362 | * account in the calculations; so, put your soldering iron down and keep the 363 | * XTAL oscillator running please. 364 | * 365 | * If you have this scenario just set the u.if2 value to the the 2nd IF value 366 | * for example you have a high IF of 74.055 MHz and lower IF of 8.215 MHz, then 367 | * your u.if2 = 74.055 MHz & u.ifreq = 8.215 MHz 368 | * 369 | * If you use just one IF set the u.if2 value to zero. 370 | * 371 | * This will trigger a few macros ahead and will calculate the correct VFO 372 | * frequencies for you in normal and SETUP mode. 373 | *****************************************************************************/ 374 | 375 | 376 | /****** hardware pre-configured values ******/ 377 | void setDefaultVals() { 378 | // 1st (or High) IF value, if you have just one IF this is ZERO 379 | u.if2 = 0; // Zero if no second IF 380 | 381 | // 2nd of unique IF, this is the one you beat to get the audio 382 | u.ifreq = 10700000; // 10.7 MHz 383 | 384 | // USB shift 385 | u.usb = 3.200; // typical value 386 | 387 | // CW shift 388 | u.cw = 600; // typical value 389 | 390 | // VFO A default value 391 | u.a = 7110000; // 7.110 kHz 392 | 393 | // VFO A default mode 394 | u.aMode = MODE_LSB; // LSB 395 | 396 | // VFO B default value 397 | u.b = 7125000; // 7.125 kHz 398 | 399 | // VFO B default mode 400 | u.aMode = MODE_LSB; // LSB 401 | 402 | // This value is not the real PPM value is just the freq correction for 403 | // yourparticular xtal from the 27.00000 Mhz one, if you can measure it 404 | // put it here, this trick is computational simpler and yet accurate 405 | u.ppm = 2256; // in Hz, mine is 2.256 Khz up 406 | } 407 | 408 | long tvfo = 0; // temporal VFO storage for RIT usage 409 | long txSplitVfo = 0; // temporal VFO storage for SPPLIT 410 | byte step = 3; // default steps position index: 411 | // as 1*10E2 = 100 = 100hz; step position is 412 | // calculated to avoid to use a big array 413 | // see getStep() function 414 | boolean update = true; // lcd update flag in normal mode 415 | byte encoderState = 0; // encoder state (DIR_NONE) 416 | byte config = 0; // holds the configuration item selected 417 | boolean inSetup = false; // the setup mode 418 | // false is just looking, true is modifying 419 | #define STEP_SHOW_TIME 6 // the time the step must be shown in 420 | // in 1/4 secs aka: aprox 1.5 secs 421 | byte showStepCounter = 0; // the step timer counter 422 | boolean runMode = true; // true: normal, false: setup 423 | boolean activeVFO = true; // true: A, False: B 424 | boolean ritActive = false; // true: rit active, false: rit disabled 425 | boolean tx = false; // whether we are on TX mode or not 426 | unsigned long lastMilis = 0; // to track the last sampled time 427 | boolean split = false; // this holds th split state 428 | boolean catTX = false; // CAT command to go to PTT 429 | word qcounter = 0; // Timer to be incremented each 1/4 second 430 | // approximately, to trigger a EEPROM update 431 | // if needed 432 | 433 | // temp boolean var (used in the loop function) 434 | boolean tbool = false; 435 | 436 | // pointers to the actual values 437 | long *ptrVFO; // will hold the value of the selected VFO 438 | byte *ptrMode; // idem but for the mode of the *ptrVFO 439 | 440 | 441 | /******** MISCELLANEOUS FUNCTIONS RELATED TO SEVERAL PROCEDURES ***********/ 442 | 443 | 444 | // return the right step size to move 445 | long getStep() { 446 | // we get the step from the global step var 447 | long ret = 1; 448 | for (byte i=0; i < step; i++, ret *= 10); 449 | return ret/10; 450 | } 451 | 452 | 453 | // split check 454 | void splitCheck() { 455 | if (split) { 456 | // revert back the VFO 457 | activeVFO = !activeVFO; 458 | updateAllFreq(); 459 | } 460 | } 461 | 462 | 463 | // change the modes in a cycle 464 | void changeMode() { 465 | // normal increment 466 | *ptrMode += 1; 467 | 468 | // checking for overflow 469 | if (*ptrMode > MODE_MAX) *ptrMode = 0; 470 | 471 | // Apply the changes 472 | updateAllFreq(); 473 | } 474 | 475 | 476 | // hardware or software commands in to RX 477 | void going2RX() { 478 | // PTT released, going to RX 479 | tx = false; 480 | digitalWrite(PTT, 0); 481 | 482 | // make changes if tx goes active when RIT is active 483 | if (ritActive) { 484 | // get the TX vfo and store it as the reference for the RIT 485 | tvfo = *ptrVFO; 486 | // restore the rit VFO to the actual VFO 487 | *ptrVFO = txSplitVfo; 488 | } 489 | 490 | // make the split changes if needed 491 | splitCheck(); 492 | } 493 | 494 | 495 | // hardware or software commands in to TX 496 | void going2TX() { 497 | // PTT asserted, going into TX 498 | tx = true; 499 | digitalWrite(PTT, 1); 500 | 501 | // make changes if tx goes active when RIT is active 502 | if (ritActive) { 503 | // save the actual rit VFO 504 | txSplitVfo = *ptrVFO; 505 | // set the TX freq to the active VFO 506 | *ptrVFO = tvfo; 507 | } 508 | 509 | // make the split changes if needed 510 | splitCheck(); 511 | } 512 | 513 | 514 | // swaps the VFOs 515 | void swapVFO(byte force = 2) { 516 | // swap the VFOs if needed 517 | if (force == 2) { 518 | // just toggle it 519 | activeVFO = !activeVFO; 520 | } else { 521 | // set it as commanded 522 | activeVFO = bool(force); 523 | } 524 | 525 | // setting the VFO/mode pointers 526 | if (activeVFO) { 527 | ptrVFO = &u.a; 528 | ptrMode = &u.aMode; 529 | } else { 530 | ptrVFO = &u.b; 531 | ptrMode = &u.bMode; 532 | } 533 | } 534 | 535 | 536 | // print the "A"/"B" letter to match the selected VFO, a trick to save space 537 | void vfoLetter() { 538 | if (activeVFO) lcd.print("A"); 539 | else lcd.print("B"); 540 | } 541 | 542 | 543 | // beep function 544 | #ifdef ROTARY 545 | // beep function a 1.2Khz tone for 50 msecs 546 | void beep() { 547 | tone(4, 1200, 50); 548 | delay(50); 549 | } 550 | 551 | // beep-boop 552 | // a 1.2Khz tone for 50 msecs 553 | // a 0.6Khz tone for 25 msecs 554 | void beop() { 555 | tone(4, 1200, 50); 556 | delay(50); 557 | 558 | tone(4, 600, 25); 559 | delay(25); 560 | } 561 | #endif 562 | 563 | 564 | /***************************************************************************** 565 | * Where did the other functions go? 566 | * 567 | * This sketch use the "split in files" feature of the Arduino IDE, look for 568 | * tabs in you editor. 569 | * 570 | * Tabs are organized by group of functions preceded with the letters "fx-" 571 | * where "x" is a sequence to preserve order in the files upon load 572 | * 573 | * The "setup" and "loop" are placed in the file "z-end" to get placed at the 574 | * end when the compiler does it's magic. 575 | * 576 | * ***************************************************************************/ 577 | -------------------------------------------------------------------------------- /licence.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------