├── .arduino-ci.yml ├── .github ├── FUNDING.yml └── workflows │ ├── arduino-lint.yml │ ├── arduino_test_runner.yml │ └── jsoncheck.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── examples ├── KT0803K_minimal │ └── KT0803K_minimal.ino ├── KT0803K_presets │ └── KT0803K_presets.ino ├── KT0803K_setFrequency │ └── KT0803K_setFrequency.ino ├── KT0803_minimal │ └── KT0803_minimal.ino ├── KT0803_setFrequency │ └── KT0803_setFrequency.ino ├── KT0803_sweep_2_seconds │ └── KT0803_sweep_2_seconds.ino └── KT0803_up_down │ └── KT0803_up_down.ino ├── keywords.txt ├── library.json ├── library.properties └── src ├── KT0803.cpp └── KT0803.h /.arduino-ci.yml: -------------------------------------------------------------------------------- 1 | platforms: 2 | rpipico: 3 | board: rp2040:rp2040:rpipico 4 | package: rp2040:rp2040 5 | gcc: 6 | features: 7 | defines: 8 | - ARDUINO_ARCH_RP2040 9 | warnings: 10 | flags: 11 | 12 | packages: 13 | rp2040:rp2040: 14 | url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json 15 | 16 | compile: 17 | # Choosing to run compilation tests on 2 different Arduino platforms 18 | platforms: 19 | - uno 20 | # - due 21 | # - zero 22 | # - leonardo 23 | - m4 24 | - esp32 25 | - esp8266 26 | # - mega2560 27 | - rpipico 28 | 29 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: RobTillaart 4 | custom: "https://www.paypal.me/robtillaart" 5 | -------------------------------------------------------------------------------- /.github/workflows/arduino-lint.yml: -------------------------------------------------------------------------------- 1 | name: Arduino-lint 2 | 3 | on: [push, pull_request] 4 | jobs: 5 | lint: 6 | runs-on: ubuntu-latest 7 | timeout-minutes: 5 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: arduino/arduino-lint-action@v1 11 | with: 12 | library-manager: update 13 | compliance: strict -------------------------------------------------------------------------------- /.github/workflows/arduino_test_runner.yml: -------------------------------------------------------------------------------- 1 | name: Arduino CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | runTest: 7 | runs-on: ubuntu-latest 8 | timeout-minutes: 20 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: ruby/setup-ruby@v1 13 | with: 14 | ruby-version: 2.6 15 | - run: | 16 | gem install arduino_ci 17 | arduino_ci.rb 18 | -------------------------------------------------------------------------------- /.github/workflows/jsoncheck.yml: -------------------------------------------------------------------------------- 1 | name: JSON check 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.json' 7 | pull_request: 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 5 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: json-syntax-check 16 | uses: limitusus/json-syntax-check@v2 17 | with: 18 | pattern: "\\.json$" -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log KT0803 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | 9 | ## [0.3.0] - 2024-03-27 10 | - fix #5, getChannel() 11 | - fix KT0803K setChannel() 12 | - fix readData() 13 | - add several KT0803K specific functions (experimental) 14 | - bool setMono() 15 | - bool setStereo() 16 | - bool isStereo() 17 | - bool setBass(uint8_t bass) 18 | - uint8_t getBass() 19 | - bool powerOK() 20 | - bool silenceDetected() 21 | - update GitHub actions 22 | - update readme.md 23 | - update keywords.txt 24 | - minor edits 25 | 26 | ---- 27 | 28 | ## [0.2.0] - 2024-03-09 29 | - Fix #2 frequency to channel formula 30 | - add derived **class KT0803K** 31 | - add examples 32 | - add parameter checks functions. 33 | - add default frequency + mute to **begin()** 34 | - update documentation 35 | - update GitHub actions 36 | - minor edits 37 | 38 | ---- 39 | 40 | ## [0.1.0] - 2023-12-27 41 | - initial version 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-2024 Rob Tillaart 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Arduino CI](https://github.com/RobTillaart/KT0803/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci) 3 | [![Arduino-lint](https://github.com/RobTillaart/KT0803/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/KT0803/actions/workflows/arduino-lint.yml) 4 | [![JSON check](https://github.com/RobTillaart/KT0803/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/KT0803/actions/workflows/jsoncheck.yml) 5 | [![GitHub issues](https://img.shields.io/github/issues/RobTillaart/KT0803.svg)](https://github.com/RobTillaart/KT0803/issues) 6 | 7 | [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/KT0803/blob/master/LICENSE) 8 | [![GitHub release](https://img.shields.io/github/release/RobTillaart/KT0803.svg?maxAge=3600)](https://github.com/RobTillaart/KT0803/releases) 9 | [![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/KT0803.svg)](https://registry.platformio.org/libraries/robtillaart/KT0803) 10 | 11 | 12 | 13 | # KT0803 14 | 15 | Arduino Library for KT0803 and KT0803K FM transmitter. 16 | 17 | 18 | ### Legal point of attention 19 | 20 | In different countries there are different laws with respect to using transmitting devices 21 | and their range. 22 | Please inform yourself of the local rules and laws if and how you may or may not use a 23 | device like the KT0803 in your projects, either hobby, commercial or otherwise. 24 | 25 | 26 | ## Description 27 | 28 | This **experimental** library allows basic control of the KT0803 and / or the KT0803K 29 | FM transmitter device. 30 | It is primary written to understand the possibilities and the interface of the device. 31 | 32 | The library is not tested by me with hardware yet. See future below. 33 | 34 | There are newer, more capable, follow up devices like model K, L and M. 35 | From these only the KT0803K is supported as a derived class. 36 | The L and the M versions of the device will work with the KT0803K class too 37 | as their functionality is equal or exceeds the K version. 38 | As far as investigated the L and M versions are backwards compatible. 39 | 40 | Version 0.3.0 implements a few KT0803K specific functions, see section below. 41 | These will work for L and M devices too. 42 | 43 | For ATTinyX5 series there exists the TinyKT0803 class which is derived from this one. 44 | It uses a different I2C implementation. See - https://github.com/RobTillaart/TinyKT0803 45 | 46 | 47 | #### Hardware 48 | 49 | Read datasheet for details. 50 | 51 | **Warning** 52 | The KT0803 is an 3.3 Volt device and cannot be connected directly to 5V MCU's. 53 | 54 | 55 | ``` 56 | +----------+ 57 | | KT0803 | 58 | GND --| 1 16 |-- PA_OUT RF analog output 59 | Crystal XI --| 2 15 |-- GND 60 | Crystal XO --| 3 14 |-- SCL I2C Clock 61 | 3.3V IOVDD --| 4 13 |-- SCA I2C Data 62 | GND --| 5 12 |-- GND 63 | in left INL --| 6 11 |-- GND 64 | in right INR --| 7 10 |-- RSTB Reset 65 | enable SW --| 8 9 |-- GND 66 | | | 67 | +----------+ 68 | ``` 69 | 70 | 71 | #### Frequency range 72 | 73 | The frequency range stated on the front page of the datasheet ==> 70 MHz - 108 MHz. 74 | The frequency range stated in table 2 ==> 76 MHz - 108 MHz. 75 | So the datasheet is at least ambiguous on this point. 76 | 77 | Keep in mind that the frequency range allowed differs per country. 78 | The library does not provide this filtering, explicit responsibility of the user. 79 | 80 | 81 | #### Differences 82 | 83 | The KT0803K device has far more options, which are not all implemented. 84 | There is one important, the resolution or step-size of the frequency. 85 | 86 | | device | step-size | Notes | 87 | |:---------:|:-----------:|:--------| 88 | | KT0803 | 100 KHz | in code all math is done with 50 KHz 89 | | KT0803K | 50 KHz | 90 | 91 | Backwards compatible. 92 | According to the datasheet code for the KT0803 should work for the KT0803K. 93 | Code with the KT0803K class will probably not work on a KT0803. 94 | 95 | 96 | #### Transmit frequency 97 | 98 | The transmit frequency can be set with **setFrequency(MHz)** or by **setChannel(channel)**. 99 | Note that the channel and frequency math of the KT0803 and the KT0803K is aligned 100 | in this library. This allows exchange of channel data between device types. 101 | 102 | Note that the KT0803 will internally round to use 100 KHz steps. 103 | 104 | Some examples: 105 | 106 | | Frequency | Channel | Notes | 107 | |:------------:|:---------:|:-------:| 108 | | 70.00 MHz | 1400 | channel = freq (Mhz) \* 20 109 | | 70.05 MHz | 1401 | freq = channel \* 0.05 110 | | 70.10 MHz | 1402 | 111 | | 76.00 MHz | 1520 | 112 | | 80.00 MHz | 1600 | 113 | | 89.70 MHz | 1794 | default (see registers datasheet) 114 | | 100.00 MHz | 2000 | 115 | | 101.30 MHz | 2026 | 116 | | 105.70 MHz | 2114 | 117 | | 108.00 MHz | 2160 | 118 | 119 | 120 | #### Related 121 | 122 | - https://github.com/RobTillaart/KT0803 123 | - https://github.com/RobTillaart/TinyKT0803 124 | - https://www.hackster.io/hesam-moshiri/stereo-digital-fm-transmitter-circuit-arduino-code-2dbd8d 125 | - https://www.hackster.io/hesam-moshiri/full-digital-fm-receiver-with-arduino-and-tea5767-52be37 126 | - https://www.hackerstore.nl/Artikel/388 127 | - https://en.wikipedia.org/wiki/FM_broadcasting 128 | 129 | 130 | ## Interface KT0803 131 | 132 | ```cpp 133 | #include "KT0803.h" 134 | ``` 135 | 136 | #### Constructor 137 | 138 | - **KT0803(TwoWire \*wire = &Wire)** constructor, 139 | optional Wire interface. 140 | - **KT0803K(TwoWire \*wire = &Wire)** constructor, 141 | optional Wire interface. 142 | - **bool begin(float freq = 90.0, bool mute = true)** initializes the library. 143 | Furthermore it checks if the deviceAddress is available on the I2C bus. 144 | Default it sets the frequency to 90 MHz and **mutes the signal**. 145 | Returns true if deviceAddress is found on the bus, false otherwise. 146 | - **bool isConnected()** test to see if deviceAddress is found on the I2C-bus. 147 | 148 | 149 | #### Frequency 150 | 151 | - **bool setFrequency(float MHz)** converts the frequency in MHz to 152 | call **setChannel(channel)**. The value of channel is rounded off depending 153 | on the resolution of the device. 154 | Returns false if MHz is out of range or **setChannel()** fails. 155 | - **float getFrequency()** returns the current frequency in MHz, can be slightly different 156 | from the set value due to rounding math mentioned above. 157 | The return value is derived from a call to **getChannel()** 158 | - **bool setChannel(uint16_t channel)** writes the channel to broadcast on to the device. 159 | This involves two or three writes to different device registers. 160 | - **uint16_t getChannel()** reads the selected channel from the device and 161 | returns it. 162 | 163 | 164 | #### PGA 165 | 166 | Read Datasheet. 167 | 168 | The KT0803K has a **PGA_LSB** (2 bits) setting, which allows setting the gain 169 | with single (1) dB steps. This is not yet implemented in the library. 170 | 171 | - **bool setPGA(uint8_t pga)** sets gain according to table below. 172 | Returns false if pga is out of range (0..7). 173 | - **uint8_t getPGA()** returns 0..7, default 0. 174 | 175 | 176 | | PGA | gain | notes | 177 | |:-----:|:-------:|:-------:| 178 | | 111 | 12dB | 179 | | 110 | 8dB | 180 | | 101 | 4dB | 181 | | 100 | 0dB | 182 | | 000 | 0dB | default 183 | | 001 | -4dB | 184 | | 010 | -8dB | 185 | | 011 | -12dB | 186 | 187 | 188 | #### RFGain 189 | 190 | Read Datasheet. 191 | 192 | Note: the RFGain value (4 bits) is distributed over three registers. 193 | PA_BIAS (register 0x05) is only supported in the KT0803K device. 194 | It is not yet supported in the library. 195 | 196 | - **bool setRFGain(uint8_t rfgain)** sets rfgain according to table below. 197 | Returns false if rfgain is out of range (0..15). 198 | - **uint8_t getRFgain()** returns 0..15, default 15. 199 | 200 | 201 | | RFGAIN | RFout | PA_BIAS = 1 | notes | 202 | |:--------:|:------------:|:------------:|:-------:| 203 | | 0000 | 95.5 dBuV | - | 204 | | 0001 | 96.5 dBuV | - | 205 | | 0010 | 97.5 dBuV | - | 206 | | 0011 | 98.2 dBuV | - | 207 | | 0100 | 98.9 dBuV | - | 208 | | 0101 | 100.0 dBuV | - | 209 | | 0110 | 101.5 dBuV | - | 210 | | 0111 | 102.8 dBuV | - | 211 | | 1000 | 105.1 dBuV | 107.2 dBuV | 212 | | 1001 | 105.6 dBuV | 108.0 dBuV | 213 | | 1010 | 106.2 dBuV | 108.7 dBuV | 214 | | 1011 | 106.5 dBuV | 109.5 dBuV | 215 | | 1100 | 107.0 dBuV | 110.3 dBuV | 216 | | 1101 | 107.4 dBuV | 111.0 dBuV | 217 | | 1110 | 107.7 dBuV | 111.7 dBuV | 218 | | 1111 | 108.0 dBuV | 112.5 dBuV | default 219 | 220 | 221 | #### Region selection 222 | 223 | Read datasheet for details. 224 | 225 | Note that not all frequencies are allowed in all regions / countries! 226 | 227 | The first four are convenience wrappers for **setPHTCNST()** 228 | If some region is missing please let me know the details and I can add 229 | a wrapper for it. 230 | 231 | - **void setEurope()** 232 | - **void setAustralia()** 233 | - **void setUSA()** 234 | - **void setJapan()** 235 | - **bool setPHTCNST(bool on)** See table below. 236 | - **bool getPHTCNST()** returns set value. 237 | 238 | | PHTCNST | time | Region | 239 | |:---------:|:-------:|:--------:| 240 | | 0 | 75 μs | USA, Japan, (default) 241 | | 1 | 50 μs | Europe, Australia 242 | 243 | 244 | #### PilotToneAdjust 245 | 246 | Read datasheet. 247 | 248 | - **bool setPilotToneAdjust(uint8_t mode)** HIGH = 1 LOW = 0 249 | - **uint8_t getPilotToneAdjust()** 250 | 251 | 252 | #### Mute 253 | 254 | Default the device is not muted, but **begin()** will default mute it. 255 | See interface section above. 256 | 257 | - **bool setMute(bool mute)** enables or disables the transmitting 258 | by muting the signal. 259 | - **bool getMute()** returns the current state of muting. 260 | 261 | 262 | ## Preference channels 263 | 264 | The device and library do not implement the persistant store of user 265 | selectable preferences (frequencies or channels). 266 | This can be implemented by the user in EEPROM or another persistent medium. 267 | 268 | Think of a class that holds an array of channels and optional descriptions. 269 | A minimal hardcoded preset sketch is in the examples. 270 | 271 | 272 | ## Derived classes 273 | 274 | A derived class KT0803K class is created, with some extended 275 | functions. 276 | 277 | The KT0803L will work as it is backwards compatible with KT0803K. 278 | It has far more registers in use than the KT0803/K. 279 | 280 | The KT0803M is identical to the KT0803K (no new registers), so 281 | a derived class is straightforward. 282 | 283 | 284 | ## Interface KT0803K 285 | 286 | Added functions in 0.3.0 (not tested), check datasheet. 287 | 288 | #### Mono Stereo 289 | 290 | - **bool setMono()** idem 291 | - **bool setStereo()** idem 292 | - **bool isStereo()** idem 293 | 294 | #### Bass 295 | 296 | - **bool setBass(uint8_t bass); // 0..3 = 0, 5, 11, 17 dB 297 | - **uint8_t getBass()** idem 298 | 299 | #### Misc 300 | 301 | - **bool powerOK()** idem 302 | - **bool silenceDetected()** idem 303 | 304 | 305 | ## Future 306 | 307 | #### Must 308 | 309 | - improve documentation 310 | - buy hardware 311 | - test and verify. 312 | 313 | 314 | #### Should 315 | 316 | - update readme.md 317 | - KT0803K specific functions. 318 | - add examples for KT0803K specific functions. 319 | 320 | 321 | #### Could 322 | 323 | - RESET pin as optional parameter in constructor? 324 | - SW pin (ON/OFF) as optional parameter in constructor? 325 | - add functions for sw on/off, 326 | - what is impact on settings? 327 | - call begin () again? => default 328 | - explain well doc. 329 | - derived class KT0803M == KT0803K 330 | - derived class KT0803L >= KT0803K (compatible) 331 | - improve error handling 332 | - unit tests possible? 333 | - extend settings upon request **bold** are interesting, see table 334 | 335 | | device | setting | register | Notes | 336 | |:---------:|:-------------:|:---------------:|:--------| 337 | | KT0803 | PA_CTRL | 13, bit 2 | **WARNING in datasheet** 338 | | | | | Should it be added in API? 339 | | KT0803K | PGA_LSB | 04, bit 4+5 | gain fine tuning -> see PGA_MOD 340 | | KT0803K | FDEV | 04, bit 2+3 | Frequency deviation adjustment 341 | | KT0803K | PDPA | 0B, bit 5 | Power Amplifier Power Down ? 342 | | KT0803K | PA_BIAS | 0E, bit 1 | PA bias current enhancement. 343 | | KT0803K | LMTLVL | 10, bit 3+4 | **Internal audio limiter level control** 344 | | KT0803K | PGAMOD | 10, bit 0 | PGA mode selection (use PGA_LSB/ not) 345 | | KT0803K | SLNCDIS | 12, bit 7 | Silence detection disable 346 | | KT0803K | SLNCTHL | 12, bit 4+5+6 | Silence detection low threshold 347 | | KT0803K | SLNCTHH | 12, bit 1+2+3 | Silence detection high threshold 348 | | KT0803K | SW_MOD | 12, bit 0 | **Switching channel mode selection** 349 | | KT0803K | SLNCTIME | 14, bit 5+6+7 | silence detection time 350 | | KT0803K | SLNCCNTHIGH | 14, bit 2+3+4 | silence detection count high 351 | | KT0803K | SLNCCNTLOW | 15, bit 0+1+2 | silence detection count low 352 | 353 | 354 | #### Wont (for now) 355 | 356 | - investigate tea5767 FM receiver (Out of scope for this lib). 357 | - investigate efficiency of register access. 358 | - caching all (allowed) registers in **begin()** 359 | - 3 bytes for KT0803 360 | - 12 bytes for KT0803K 361 | - cache frequency. 362 | - only writing is needed. 363 | - send binary data over FM? 364 | - preset frequency array in .h file (hardcoded) 365 | - enums for parameters - readability? 366 | 367 | 368 | ## Support 369 | 370 | If you appreciate my libraries, you can support the development and maintenance. 371 | Improve the quality of the libraries by providing issues and Pull Requests, or 372 | donate through PayPal or GitHub sponsors. 373 | 374 | Thank you, 375 | 376 | -------------------------------------------------------------------------------- /examples/KT0803K_minimal/KT0803K_minimal.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: KT0803K_minimal.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: minimal demo 5 | // URL: https://github.com/RobTillaart/KT0803 6 | 7 | 8 | #include "Arduino.h" 9 | #include "Wire.h" 10 | #include "KT0803.h" 11 | 12 | 13 | KT0803K FM_SEND; 14 | 15 | 16 | void setup() 17 | { 18 | Serial.begin(115200); 19 | while(!Serial); 20 | 21 | Wire.begin(); 22 | 23 | FM_SEND.begin(); 24 | FM_SEND.setChannel(2000); // * 0.05 100.00 MHz 25 | FM_SEND.setMute(false); 26 | } 27 | 28 | void loop() 29 | { 30 | } 31 | 32 | 33 | // -- END OF FILE -- 34 | -------------------------------------------------------------------------------- /examples/KT0803K_presets/KT0803K_presets.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: KT0803K_minimal.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: minimal demo hardcoded presets 5 | // URL: https://github.com/RobTillaart/KT0803 6 | 7 | 8 | #include "Arduino.h" 9 | #include "Wire.h" 10 | #include "KT0803.h" 11 | 12 | 13 | KT0803K FM_SEND; 14 | 15 | // hardcoded presets adjust to your need. 16 | // note: presets by channel would take half the memory 17 | float preset[10] = 18 | { 19 | 88.6, 91.3, 92.2, 96.0, 97.3, 20 | 98.8, 100.1, 100.3, 105.4, 107.6 21 | }; 22 | 23 | 24 | void setup() 25 | { 26 | Serial.begin(115200); 27 | while (!Serial); 28 | 29 | Wire.begin(); 30 | 31 | FM_SEND.begin(preset[0], false); 32 | } 33 | 34 | 35 | void loop() 36 | { 37 | if (Serial.available()) 38 | { 39 | char c = Serial.read(); 40 | 41 | if (c == 'm') FM_SEND.setMute(false); 42 | if (c == 'M') FM_SEND.setMute(true); 43 | // select preset 0..9 44 | int p = c - '0'; // convert to digit 45 | if ((0 <= p) && (p <= 9)) FM_SEND.setFrequency(preset[p]); 46 | } 47 | } 48 | 49 | 50 | // -- END OF FILE -- 51 | -------------------------------------------------------------------------------- /examples/KT0803K_setFrequency/KT0803K_setFrequency.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: KT0803_setFrequency.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: minimal demo 5 | // URL: https://github.com/RobTillaart/KT0803 6 | 7 | 8 | #include "Arduino.h" 9 | #include "Wire.h" 10 | #include "KT0803.h" 11 | 12 | 13 | KT0803K FM_SEND; 14 | 15 | 16 | void setup() 17 | { 18 | Serial.begin(115200); 19 | while(!Serial); 20 | 21 | Wire.begin(); 22 | 23 | FM_SEND.begin(); 24 | FM_SEND.setFrequency(105.75); 25 | FM_SEND.setMute(false); 26 | 27 | Serial.print("Freq: "); 28 | Serial.println(FM_SEND.getFrequency()); 29 | Serial.print("Channel: "); 30 | Serial.println(FM_SEND.getChannel()); 31 | } 32 | 33 | 34 | void loop() 35 | { 36 | } 37 | 38 | 39 | // -- END OF FILE -- 40 | -------------------------------------------------------------------------------- /examples/KT0803_minimal/KT0803_minimal.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: KT0803_minimal.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: minimal demo 5 | // URL: https://github.com/RobTillaart/KT0803 6 | 7 | 8 | #include "Arduino.h" 9 | #include "Wire.h" 10 | #include "KT0803.h" 11 | 12 | 13 | KT0803 FM_SEND; 14 | 15 | 16 | void setup() 17 | { 18 | Serial.begin(115200); 19 | while(!Serial); 20 | 21 | Wire.begin(); 22 | 23 | FM_SEND.begin(); 24 | FM_SEND.setChannel(2000); // * 0.05 = 100.00 MHz 25 | FM_SEND.setMute(false); 26 | } 27 | 28 | void loop() 29 | { 30 | } 31 | 32 | 33 | // -- END OF FILE -- 34 | -------------------------------------------------------------------------------- /examples/KT0803_setFrequency/KT0803_setFrequency.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: KT0803_setFrequency.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: minimal demo 5 | // URL: https://github.com/RobTillaart/KT0803 6 | 7 | 8 | #include "Arduino.h" 9 | #include "Wire.h" 10 | #include "KT0803.h" 11 | 12 | 13 | KT0803 FM_SEND; 14 | 15 | 16 | void setup() 17 | { 18 | Serial.begin(115200); 19 | while(!Serial); 20 | 21 | Wire.begin(); 22 | 23 | FM_SEND.begin(); 24 | FM_SEND.setFrequency(105.75); 25 | FM_SEND.setMute(false); 26 | 27 | Serial.print("Freq: "); 28 | Serial.println(FM_SEND.getFrequency()); 29 | Serial.print("Channel: "); 30 | Serial.println(FM_SEND.getChannel()); 31 | } 32 | 33 | void loop() 34 | { 35 | } 36 | 37 | 38 | // -- END OF FILE -- 39 | -------------------------------------------------------------------------------- /examples/KT0803_sweep_2_seconds/KT0803_sweep_2_seconds.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: KT0803_sweep_2_seconds.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: minimal demo 5 | // URL: https://github.com/RobTillaart/KT0803 6 | 7 | 8 | #include "Arduino.h" 9 | #include "Wire.h" 10 | #include "KT0803.h" 11 | 12 | 13 | KT0803 FM_SEND; 14 | 15 | 16 | void setup() 17 | { 18 | Serial.begin(115200); 19 | while(!Serial); 20 | 21 | Wire.begin(); 22 | FM_SEND.begin(); 23 | FM_SEND.setMute(false); 24 | } 25 | 26 | void loop() 27 | { 28 | // adjust to your local allowed frequencies 29 | for (float freq = 88.0; freq <= 108.0; freq += 0.1) 30 | { 31 | FM_SEND.setFrequency(freq); 32 | delay(2000); 33 | } 34 | } 35 | 36 | 37 | // -- END OF FILE -- 38 | -------------------------------------------------------------------------------- /examples/KT0803_up_down/KT0803_up_down.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: KT0803_up_down.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: minimal demo 5 | // URL: https://github.com/RobTillaart/KT0803 6 | 7 | 8 | #include "Arduino.h" 9 | #include "Wire.h" 10 | #include "KT0803.h" 11 | 12 | 13 | KT0803 FM_SEND; 14 | 15 | // connect two buttons to GND 16 | // adjust pins to your needs, 17 | uint8_t UP_PIN = 6; 18 | uint8_t DOWN_PIN = 7; 19 | float freq = 100.0; 20 | float newFreq = 100.0; 21 | 22 | 23 | void setup() 24 | { 25 | Serial.begin(115200); 26 | while (!Serial); 27 | 28 | pinMode(UP_PIN, INPUT_PULLUP); 29 | pinMode(DOWN_PIN, INPUT_PULLUP); 30 | Wire.begin(); 31 | 32 | FM_SEND.begin(freq, false); 33 | } 34 | 35 | 36 | void loop() 37 | { 38 | // adjust frequency with up and down button. 39 | uint8_t u = digitalRead(UP_PIN); 40 | uint8_t d = digitalRead(DOWN_PIN); 41 | if ((u == 0) && (d == 0)) 42 | { 43 | newFreq = 100.0; 44 | } 45 | else if (u == 0) 46 | { 47 | newFreq += 0.1; 48 | if (newFreq > 108.0) newFreq = 108.0; 49 | } 50 | else if (d == 0) 51 | { 52 | newFreq -= 0.1; 53 | if (newFreq < 88.0) newFreq = 88.0; 54 | } 55 | if (newFreq != freq) 56 | { 57 | freq = newFreq; 58 | FM_SEND.setFrequency(freq); 59 | 60 | Serial.print("Freq: "); 61 | Serial.print(FM_SEND.getFrequency()); 62 | Serial.print("\t"); 63 | Serial.println(FM_SEND.getChannel()); 64 | 65 | delay(100); 66 | } 67 | } 68 | 69 | 70 | // -- END OF FILE -- 71 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | # Syntax Colouring Map For KT0803 2 | 3 | 4 | # Data types (KEYWORD1) 5 | KT0803 KEYWORD1 6 | KT0803K KEYWORD1 7 | 8 | 9 | # Methods and Functions (KEYWORD2) 10 | begin KEYWORD2 11 | isConnected KEYWORD2 12 | 13 | setFrequency KEYWORD2 14 | getFrequency KEYWORD2 15 | setChannel KEYWORD2 16 | getChannel KEYWORD2 17 | 18 | setPGA KEYWORD2 19 | getPGA KEYWORD2 20 | 21 | setRFGain KEYWORD2 22 | getRFgain KEYWORD2 23 | 24 | setEurope KEYWORD2 25 | setAustralia KEYWORD2 26 | setUSA KEYWORD2 27 | setJapan KEYWORD2 28 | setPHTCNST KEYWORD2 29 | getPHTCNST KEYWORD2 30 | 31 | setPilotToneAdjust KEYWORD2 32 | getPilotToneAdjust KEYWORD2 33 | 34 | setMute KEYWORD2 35 | getMute KEYWORD2 36 | 37 | 38 | # KT0803K specific 39 | setMono KEYWORD2 40 | setStereo KEYWORD2 41 | isStereo KEYWORD2 42 | 43 | setBass KEYWORD2 44 | getBass KEYWORD2 45 | 46 | powerOK KEYWORD2 47 | silenceDetected KEYWORD2 48 | 49 | 50 | # Constants (LITERAL1) 51 | KT0803_LIB_VERSION LITERAL1 52 | 53 | 54 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "KT0803", 3 | "keywords": "FM,Broadcast,KT0803K,KT0803L,KT0803M", 4 | "description": "Arduino library for the KT0803 and KT0803K FM transmitter.", 5 | "authors": 6 | [ 7 | { 8 | "name": "Rob Tillaart", 9 | "email": "Rob.Tillaart@gmail.com", 10 | "maintainer": true 11 | } 12 | ], 13 | "repository": 14 | { 15 | "type": "git", 16 | "url": "https://github.com/RobTillaart/KT0803.git" 17 | }, 18 | "version": "0.3.0", 19 | "license": "MIT", 20 | "frameworks": "*", 21 | "platforms": "*", 22 | "headers": "KT0803.h" 23 | } 24 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=KT0803 2 | version=0.3.0 3 | author=Rob Tillaart 4 | maintainer=Rob Tillaart 5 | sentence=Arduino library for the KT0803 and KT0803K FM transmitter. 6 | paragraph=KT0803K, KT0803L, KT0803M 7 | category=Communication 8 | url=https://github.com/RobTillaart/KT0803 9 | architectures=* 10 | includes=KT0803.h 11 | depends= 12 | -------------------------------------------------------------------------------- /src/KT0803.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: KT0803.cpp 3 | // AUTHOR: Rob Tillaart 4 | // VERSION: 0.3.0 5 | // PURPOSE: Arduino Library for KT0803 and KT0803K FM transmitter. 6 | // URL: https://github.com/RobTillaart/KT0803 7 | 8 | 9 | #include "KT0803.h" 10 | 11 | 12 | // REGISTERS on page 7 datasheet 13 | 14 | 15 | KT0803::KT0803(TwoWire * wire) 16 | { 17 | _address = 0x3E; 18 | _wire = wire; 19 | } 20 | 21 | 22 | bool KT0803::begin(float freq, bool mute) 23 | { 24 | if (! isConnected()) return false; 25 | if (! setMute(mute)) return false; 26 | return setFrequency(freq); 27 | } 28 | 29 | 30 | bool KT0803::isConnected() 31 | { 32 | _wire->beginTransmission(_address); 33 | return (_wire->endTransmission() == 0); 34 | } 35 | 36 | 37 | /////////////////////////////////////////////////////////// 38 | // 39 | // FREQUENCY 40 | // 41 | bool KT0803::setFrequency(float MHz) 42 | { 43 | if ((MHz < 70) || (MHz > 108)) return false; 44 | // steps 50 KHz although KT0803 will truncate to 100 KHz. 45 | return setChannel(round(MHz * 20)); 46 | } 47 | 48 | 49 | // MHz 50 | float KT0803::getFrequency() 51 | { 52 | return getChannel() * 0.05; 53 | } 54 | 55 | 56 | // steps of 50 KHz. 57 | bool KT0803::setChannel(uint16_t channel) 58 | { 59 | if ((channel < 1400) || (channel > 2160)) return false; 60 | // need to split over 3 registers 61 | // register 2 part skipped (always 0) for KT0803 62 | uint16_t ch = channel >> 1; 63 | 64 | uint8_t register0 = ch & 0xFF; // CHSEL[8:1] 65 | if (writeData(0x00, register0) == false) return false; 66 | 67 | ch >>= 8; 68 | uint8_t register1 = readData(0x01); 69 | register1 &= 0xF8; // CHSEL[11:9] 70 | register1 |= (ch & 0x07); // CHSEL[11:9] 71 | return writeData(0x01, register1); 72 | } 73 | 74 | 75 | uint16_t KT0803::getChannel() 76 | { 77 | uint16_t channel = readData(0x01) & 0x07; 78 | channel <<= 8; 79 | channel |= readData(0x00); 80 | channel <<= 1; 81 | return channel; 82 | } 83 | 84 | 85 | /////////////////////////////////////////////////////////// 86 | // 87 | // GAIN 88 | // 89 | bool KT0803::setPGA(uint8_t pga) 90 | { 91 | if (pga > 7) return false; 92 | uint8_t data = readData(0x01); 93 | data &= 0xC7; // keep other bits 94 | data |= (pga << 3); 95 | return writeData(0x01, data); 96 | } 97 | 98 | 99 | uint8_t KT0803::getPGA() 100 | { 101 | return (readData(0x01) >> 3) & 0x07; 102 | } 103 | 104 | 105 | bool KT0803::setRFGain(uint8_t rfgain) 106 | { 107 | if (rfgain > 15) return false; 108 | // bits 0 and 1 109 | uint8_t data = readData(0x01) & 0x3F; 110 | data |= (rfgain & 0x03) << 6; 111 | writeData(0x01, data); 112 | // bit 2 113 | data = readData(0x13) & 0x7F; 114 | data |= (rfgain & 0x04) << 5; 115 | writeData(0x13, data); 116 | // bit 3 117 | data = readData(0x02) & 0xBF; 118 | data |= (rfgain & 0x08) << 3; 119 | writeData(0x02, data); 120 | return true; 121 | } 122 | 123 | 124 | uint8_t KT0803::getRFgain() 125 | { 126 | uint8_t data = readData(0x01) >> 6; // bit 0, 1 127 | data |= ((readData(0x13) & 0x80) >> 5); // bit 2 128 | data |= ((readData(0x02) & 0x40) >> 3); // bit 3 129 | return data; 130 | } 131 | 132 | 133 | bool KT0803::setPHTCNST(bool on) 134 | { 135 | uint8_t data = readData(0x02); 136 | // is the bit already OK 137 | if ((on == true) && (data & 0x01) == 0x01) return true; 138 | if ((on == false) && (data & 0x01) == 0x00) return true; 139 | // otherwise flip the bit 140 | data = data ^ 0x01; 141 | return writeData(0x02, data); 142 | } 143 | 144 | 145 | /////////////////////////////////////////////////////////// 146 | // 147 | // MISC 148 | // 149 | bool KT0803::getPHTCNST() 150 | { 151 | return (readData(0x02) & 0x01) > 0; 152 | } 153 | 154 | 155 | bool KT0803::setPilotToneAdjust(uint8_t mode) 156 | { 157 | if (mode > 1) return false; 158 | uint8_t data = readData(0x02); 159 | // is the bit already OK 160 | if ((mode == 1) && (data & 0x04) == 0x04) return true; 161 | if ((mode == 0) && (data & 0x04) == 0x00) return true; 162 | // otherwise flip the bit 163 | data = data ^ 0x04; 164 | return writeData(0x02, data); 165 | } 166 | 167 | 168 | uint8_t KT0803::getPilotToneAdjust() 169 | { 170 | return (readData(0x02) & 0x04) > 0; 171 | } 172 | 173 | 174 | /////////////////////////////////////////////////////////// 175 | // 176 | // MUTE 177 | // 178 | bool KT0803::setMute(bool mute) 179 | { 180 | uint8_t data = readData(0x02); 181 | // is the bit already OK 182 | if ((mute == true) && (data & 0x08) == 0x08) return true; 183 | if ((mute == false) && (data & 0x08) == 0x00) return true; 184 | // otherwise flip the bit 185 | data = data ^ 0x08; 186 | return writeData(0x02, data); 187 | } 188 | 189 | 190 | bool KT0803::getMute() 191 | { 192 | return (readData(0x02) & 0x08) > 0; 193 | } 194 | 195 | 196 | /////////////////////////////////////////////////////////////////////// 197 | // 198 | // PROTECTED 199 | // 200 | bool KT0803::writeData(uint8_t reg, uint8_t data) 201 | { 202 | _wire->beginTransmission(_address); 203 | _wire->write(reg); 204 | _wire->write(data); 205 | return (_wire->endTransmission() == 0); 206 | } 207 | 208 | 209 | int KT0803::readData(uint8_t reg) 210 | { 211 | _wire->beginTransmission(_address); 212 | _wire->write(reg); 213 | _wire->endTransmission(false); // explicit no STOP fig 3 page 4 214 | 215 | if (_wire->requestFrom(_address, (uint8_t) 1) == 1) 216 | { 217 | uint8_t data = _wire->read(); 218 | return data; 219 | } 220 | return -1; 221 | } 222 | 223 | 224 | ///////////////////////////////////////////////////////////////////////////// 225 | // 226 | // DERIVED CLASSES 227 | // 228 | KT0803K::KT0803K(TwoWire * wire) : KT0803(wire) 229 | { 230 | } 231 | 232 | 233 | bool KT0803K::setChannel(uint16_t channel) 234 | { 235 | if ((channel < 1400) || (channel > 2160)) return false; 236 | // need to split over 3 registers 237 | uint16_t ch = channel; 238 | 239 | uint8_t register2 = readData(0x02) & 0x7F; 240 | register2 |= (channel & 0x01) << 7; // CHSEL[0] 241 | if (writeData(0x02, register2) == false) return false; 242 | ch >>= 1; 243 | 244 | uint8_t register0 = ch & 0xFF; // CHSEL[8:1] 245 | if (writeData(0x00, register0) == false) return false; 246 | 247 | ch >>= 8; 248 | uint8_t register1 = readData(0x01); 249 | register1 &= 0xF8; // CHSEL[11:9] 250 | register1 |= (ch & 0x07); // CHSEL[11:9] 251 | return writeData(0x01, register1); 252 | } 253 | 254 | 255 | uint16_t KT0803K::getChannel() 256 | { 257 | uint16_t channel = readData(0x01) & 0x07; 258 | channel <<= 8; 259 | channel |= readData(0x00); 260 | channel <<= 1; 261 | channel |= (readData(0x02) >> 7); 262 | return channel; 263 | } 264 | 265 | 266 | /////////////////////////////////////////////////////////// 267 | // 268 | // KT0803K SPECIFIC 269 | // 270 | bool KT0803K::setMono() 271 | { 272 | uint8_t register4 = readData(0x04); 273 | if (register4 & (1 << 6)) 274 | { 275 | register4 &= ~(1 << 6); 276 | return writeData(0x04, register4); 277 | } 278 | return true; 279 | } 280 | 281 | 282 | bool KT0803K::setStereo() 283 | { 284 | uint8_t register4 = readData(0x04); 285 | if ((register4 & (1 << 6)) == 0) 286 | { 287 | register4 |= (1 << 6); 288 | return writeData(0x04, register4); 289 | } 290 | return true; 291 | } 292 | 293 | bool KT0803K::isStereo() 294 | { 295 | uint8_t register4 = readData(0x04); 296 | return (register4 & (1 << 6)); 297 | } 298 | 299 | 300 | bool KT0803K::setBass(uint8_t bass) // 0..3 = 0, 5, 11, 17 dB 301 | { 302 | if (bass > 3) return false; 303 | uint8_t register4 = readData(0x04); 304 | register4 &= ~0x03; // mask off bits 305 | register4 |= bass; 306 | return writeData(0x04, register4); 307 | } 308 | 309 | 310 | uint8_t KT0803K::getBass() 311 | { 312 | uint8_t register4 = readData(0x04); 313 | return register4 & 0x03; 314 | } 315 | 316 | 317 | bool KT0803K::powerOK() 318 | { 319 | uint8_t register0F = readData(0x0F); 320 | return (register0F & (1 << 4)) > 0; 321 | } 322 | 323 | 324 | bool KT0803K::silenceDetected() 325 | { 326 | uint8_t register0F = readData(0x0F); 327 | return (register0F & (1 << 2)) > 0; 328 | } 329 | 330 | 331 | // -- END OF FILE -- 332 | 333 | -------------------------------------------------------------------------------- /src/KT0803.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // 3 | // FILE: KT0803.h 4 | // AUTHOR: Rob Tillaart 5 | // VERSION: 0.3.0 6 | // PURPOSE: Arduino Library for KT0803 and KT0803K FM transmitter 7 | // URL: https://github.com/RobTillaart/KT0803 8 | 9 | 10 | #include "Arduino.h" 11 | #include "Wire.h" 12 | 13 | 14 | #define KT0803_LIB_VERSION (F("0.3.0")) 15 | 16 | 17 | class KT0803 18 | { 19 | public: 20 | KT0803(TwoWire * wire = &Wire); 21 | 22 | bool begin(float freq = 90.0, bool mute = true); 23 | bool isConnected(); 24 | 25 | 26 | // FM FREQUENCY (70-108) 27 | // KT0803 truncates to 0.1 MHz 28 | // KT0803K (L&M) truncates to 0.05 MHz 29 | bool setFrequency(float MHz); 30 | float getFrequency(); 31 | 32 | 33 | // FM CHANNEL (1400-2160) 34 | // KT0803 only supports even channels 35 | // KT0803K (L&M) supports all channels 36 | bool setChannel(uint16_t channel); 37 | uint16_t getChannel(); 38 | 39 | 40 | // GAIN 41 | bool setPGA(uint8_t pga); // 0-3 = 0..-12dB 4-7 = 0..12dB 42 | // 111: 12dB 43 | // 110: 8dB 44 | // 101: 4dB 45 | // 100: 0dB 46 | // 000: 0dB 47 | // 001: -4dB 48 | // 010: -8dB 49 | // 011: -12dB 50 | uint8_t getPGA(); 51 | 52 | bool setRFGain(uint8_t rfgain); // 0-15 53 | uint8_t getRFgain(); 54 | 55 | 56 | // REGION SELECTION 57 | // first four are wrappers 58 | void setEurope() { setPHTCNST(true); }; 59 | void setAustralia() { setPHTCNST(true); }; 60 | void setUSA() { setPHTCNST(false); }; 61 | void setJapan() { setPHTCNST(false); }; 62 | bool setPHTCNST(bool on); 63 | bool getPHTCNST(); 64 | 65 | 66 | // PILOT TONE ADJUST (PLTADJ) 67 | bool setPilotToneAdjust(uint8_t mode); // HIGH = 1 LOW = 0 68 | uint8_t getPilotToneAdjust(); 69 | 70 | 71 | // MUTE software 72 | bool setMute(bool mute); // true == muted 73 | bool getMute(); // isMuted(). 74 | 75 | 76 | // direct access to registers - debug / develop 77 | // to access not implemented functions. 78 | bool writeData(uint8_t reg, uint8_t data); 79 | int readData(uint8_t reg); 80 | 81 | protected: 82 | 83 | uint8_t _address = 0x3E; // fixed address for KT0803. 84 | TwoWire * _wire = NULL; 85 | }; 86 | 87 | 88 | ///////////////////////////////////////////////////////////////////////////// 89 | // 90 | // DERIVED CLASSES 91 | // 92 | class KT0803K : public KT0803 93 | { 94 | public: 95 | KT0803K(TwoWire * wire = &Wire); 96 | 97 | // CHANNEL 98 | bool setChannel(uint16_t channel); 99 | uint16_t getChannel(); 100 | 101 | // KT0803K SPECIFIC 102 | bool setMono(); 103 | bool setStereo(); 104 | bool isStereo(); 105 | 106 | bool setBass(uint8_t bass); // 0..3 = 0, 5, 11, 17 dB 107 | uint8_t getBass(); 108 | 109 | bool powerOK(); 110 | bool silenceDetected(); 111 | }; 112 | 113 | 114 | // -- END OF FILE -- 115 | 116 | --------------------------------------------------------------------------------