PU2CLR BK108X Arduino Library 1.0.2
This is an Arduino Library to control the BK108X device
Loading...
Searching...
No Matches
BK108X.cpp
Go to the documentation of this file.
1/**
2 * @mainpage BK108X Arduino Library implementation
3 * @details BK108X Arduino Library implementation. This is an Arduino library for the BK108X, BROADCAST RECEIVER.
4 * @details This is an Arduino library for the BK1086 and BK1088 DSP BROADCAST RECEIVER.<br>
5 * @details It works with I2C protocol and can provide an easier interface for controlling the BK1086/88 devices.<br>
6 * @details This library is based on the Document: BEKEN - BK1086/88 - BROADCAST AM/FM/SW/LW RADIO RECEIVER Rev 1.3.
7 *
8 * __THIS LIBRARY IS UNDER CONSTRUCTION.....___
9 *
10 * This library can be freely distributed using the MIT Free Software model.
11 * Copyright (c) 2020-2003 Ricardo Lima Caratti.
12 * Contact: pu2clr@gmail.com
13 */
14
15#include <BK108X.h>
16#include <Wire.h> // This library is not used to communicate wwith BK108X family device. It is used to check I2C bus.
17
18/**
19 * @defgroup GA02 BEKEN I2C BUS
20 * @section GA02 I2C
21 *
22 * @brief About I2C Address on BK1086/88 and Arduino platform
23 *
24 * The BK1086/88 Datasheet says that the I2C buss address is 0x80. However, the Wire (I2C) Arduino library does not find
25 * the device on 0x80. Actually the Arduino finds the device on 0x40.
26 * This must be due to the internal conversion of the address from 8 bits to 7 bits. (0x80 = 0b10000000; 0x40 = 0b01000000)
27 * After a few unsuccessful attempts at using the Arduino I2C library, I decided to write the necessary I2C routines to deal
28 * with BK1086/88 device.
29 *
30 * @see setI2C, i2cInit, i2cStart, i2cEndTransaction(), i2cAck, i2cNack, i2cReceiveAck, i2cWriteByte, i2cReadByte, writeRegister, readRegister
31 *
32 * IMPORTANT:
33 * For stable communication, the rising edge time of SCLK should be less than 200ns.
34 */
35
36/**
37 * @ingroup GA02
38 * @brief Sets I2C bus address
39 * @details Useful if some release of BEKEN device is different of 0x80.
40 *
41 * @param i2c_addr
42 */
43void BK108X::setI2C(uint8_t i2c_addr)
44{
45 this->deviceAddress = i2c_addr;
46}
47
48/**
49 * @ingroup GA02
50 * @brief Sets the MCU pins connected to the I2C bus
51 * @details Configures the I2C bus for BK108X
52 *
53 * @param pin_sdio SDA/SDIO MCU/Arduino pin
54 * @param pin_sclk CLK/SCLK MCU/Arduino pin
55 */
56void BK108X::i2cInit(int pin_sdio, int pin_sclk)
57{
58 this->pin_sdio = pin_sdio;
59 this->pin_sclk = pin_sclk;
60}
61
62/**
63 * @ingroup GA02
64 * @brief Starts the I2C bus transaction
65 */
67{
68 pinMode(this->pin_sdio, OUTPUT);
69 pinMode(this->pin_sclk, OUTPUT);
70 digitalWrite(this->pin_sdio, HIGH);
71 digitalWrite(this->pin_sclk, HIGH);
72 delayMicroseconds(1);
73
74 digitalWrite(this->pin_sdio, LOW);
75 delayMicroseconds(1);
76 digitalWrite(this->pin_sclk, LOW);
77 delayMicroseconds(1);
78 digitalWrite(this->pin_sdio, HIGH);
79}
80
81/**
82 * @ingroup GA02
83 * @brief Finish the I2C bus transaction
84 */
86{
87 pinMode(pin_sdio, OUTPUT);
88 digitalWrite(this->pin_sdio, LOW);
89 delayMicroseconds(1);
90
91 digitalWrite(this->pin_sclk, HIGH);
92 delayMicroseconds(1);
93
94 digitalWrite(this->pin_sdio, HIGH);
95 delayMicroseconds(1);
96}
97
98/**
99 * @ingroup GA02
100 * @brief Sends Acknowledge (ACK)
101 * @details Each byte of data (including the address byte) have to be followed by one ACK bit from the receiver.
102 * @details The ACK bit allows the receiver to communicate to the transmitter.
103 * @see https://www.ti.com/lit/an/slva704/slva704.pdf
104 */
106{
107 pinMode(pin_sdio, OUTPUT);
108 digitalWrite(this->pin_sclk, LOW);
109 digitalWrite(this->pin_sdio, LOW);
110 delayMicroseconds(1);
111 digitalWrite(this->pin_sclk, HIGH);
112 delayMicroseconds(1);
113 digitalWrite(this->pin_sclk, LOW);
114}
115
116/**
117 * @ingroup GA02
118 * @brief Sends Not Acknowledge (ACK)
119 * @see https://www.ti.com/lit/an/slva704/slva704.pdf
120 */
122{
123 pinMode(pin_sdio, OUTPUT);
124
125 digitalWrite(this->pin_sclk, LOW);
126 digitalWrite(this->pin_sdio, HIGH);
127 delayMicroseconds(1);
128 digitalWrite(this->pin_sclk, HIGH);
129 delayMicroseconds(1);
130 digitalWrite(this->pin_sclk, LOW);
131}
132
133/**
134 * @ingroup GA02
135 * @brief Gets Acknowledge (ACK)
136 * @see https://www.ti.com/lit/an/slva704/slva704.pdf
137 * @return ack value
138 */
140{
141 uint8_t ack;
142 pinMode(pin_sdio, INPUT);
143 delayMicroseconds(1);
144
145 digitalWrite(this->pin_sclk, HIGH);
146 delayMicroseconds(1);
147
148 ack = digitalRead(this->pin_sdio);
149
150 digitalWrite(this->pin_sclk, LOW);
151 delayMicroseconds(1);
152
153 return ack;
154}
155
156/**
157 * @ingroup GA02
158 * @brief Sends a Byte to the slave device
159 * @param data to be sent to the slave device
160 */
161void BK108X::i2cWriteByte(uint8_t data)
162{
163 pinMode(pin_sdio, OUTPUT);
164 delayMicroseconds(1);
165
166 for (int i = 0; i < 8; i++)
167 {
168
169 digitalWrite(this->pin_sdio, (bool)(data & this->deviceAddress));
170
171 delayMicroseconds(1);
172 digitalWrite(this->pin_sclk, HIGH);
173 delayMicroseconds(1);
174 digitalWrite(this->pin_sclk, LOW);
175 data = data << 1;
176 }
177}
178
179/**
180 * @ingroup GA02
181 * @brief Gets a Byte from the slave device
182 * @return value read from the device
183 */
185{
186 uint8_t value = 0;
187
188 pinMode(pin_sdio, INPUT);
189 delayMicroseconds(1);
190
191 for (int i = 0; i < 8; i++)
192 {
193 digitalWrite(this->pin_sclk, HIGH);
194 value = value << 1;
195 delayMicroseconds(1);
196 if (digitalRead(this->pin_sdio))
197 value = value | 1;
198 digitalWrite(this->pin_sclk, LOW);
199 delayMicroseconds(1);
200 }
201
202 return value;
203}
204
205/**
206 * @ingroup GA02
207 * @brief Sends an array of values to a BK108X given register
208 * @param reg register to be written
209 * @param value content to be stored into the register
210 */
211void BK108X::writeRegister(uint8_t reg, uint16_t value)
212{
213
214 word16_to_bytes data;
215 data.raw = value;
216
218 this->i2cWriteByte(this->deviceAddress);
219 this->i2cReceiveAck();
220
221 reg = reg << 1; // Converts address and sets to write operation
222
223 this->i2cWriteByte(reg);
224 this->i2cReceiveAck();
225
226 this->i2cWriteByte(data.refined.highByte);
227 this->i2cReceiveAck();
228 this->i2cWriteByte(data.refined.lowByte);
229 this->i2cReceiveAck();
230
232}
233
234/**
235 * @ingroup GA02
236 * @brief Gets an array of values from a BK108X given register
237 * @param reg register to be read
238 * @return register content
239 */
241{
242
243 word16_to_bytes data;
244
246 this->i2cWriteByte(this->deviceAddress);
247 this->i2cReceiveAck();
248
249 reg = (reg << 1) | 1; // Converts address and sets to read operation
250
251 this->i2cWriteByte(reg);
252 this->i2cReceiveAck();
253
254 data.refined.highByte = this->i2cReadByte();
255 this->i2cAck();
256 data.refined.lowByte = this->i2cReadByte();
257 this->i2cNack();
258
260
261 return data.raw;
262}
263
264/**
265 * @defgroup GA03 Basic Functions
266 * @section GA03 Basic
267 */
268
269/**
270 * @ingroup GA03
271 * @brief Gets a givens current register content of the device
272 * @see shadowRegisters;
273 * @param device register address
274 * @return the register content (the shadowRegisters array has this content. So, you do not need to use it most of the cases)
275 */
277{
278 uint16_t reg_content;
279 reg_content = this->readRegister(reg);
280 shadowRegisters[reg] = reg_content; // Syncs with the shadowRegisters
281 return reg_content; // Optional
282}
283
284/**
285 * @ingroup GA03
286 * @brief Sets a given value to the device registers
287 * @details For write operations, the device acknowledge is followed by an eight bit data word latched internally on rising edges of SCLK. The device acknowledges each byte of data written by driving SDIO low after the next falling SCLK edge, for 1 cycle.
288 * @details An internal address counter automatically increments to allow continuous data byte writes, starting with the upper byte of register 02h, followed by the lower byte of register 02h, and onward until the lower byte of the last register is reached. The internal address counter then automatically wraps around to the upper byte of register 00h and proceeds from there until continuous writes end.
289 * @details The registers from 0x2 to 0x07 are used to setup the device. This method writes the array shadowRegisters, elements 8 to 14 (corresponding the registers 0x2 to 0x7 respectively) into the device. See Device registers map in BK108X.h file.
290 * @details To implement this, a register maping was created to deal with each register structure. For each type of register, there is a reference to the array element.
291 *
292 * @see shadowRegisters;
293 *
294 * @param device register address
295 */
296void BK108X::setRegister(uint8_t reg, uint16_t value)
297{
298 this->writeRegister(reg, value);
299 shadowRegisters[reg] = value; // Syncs with the shadowRegisters
300 delayMicroseconds(250);
301}
302
303/**
304 * @brief Returns the Device Indentifiction
305 * @return device id
306 */
308{
309 return getRegister(REG00);
310}
311
312/**
313 * @brief Returns the Chip Indentifiction
314 *
315 * @return IC id
316 */
318{
319 return getRegister(REG01);
320}
321
322/**
323 * @ingroup GA03
324 * @brief Gets the current status (register 0x0A) content
325 * @details You can use this function when you need to get more than one status attribute at once.
326 * @details See example code below.
327 * @code
328 * bk_reg0a status = getStatus();
329 *
330 * Serial.println(status.refined.ST); // Stereo Signal Received Indicator
331 * Serial.println(status.refined.RSSI); // Current RSSI value
332 * Serial.println(status.refined.RDSR); // RDS Ready Indicator
333 *
334 * @endcode
335 *
336 * @see getRSSI
337 * @see BK1086/88E - BROADCAST AM/FM/SW/LW RADIO RECEIVER Rev 1.3; page 17.
338 *
339 * @return bk_reg0a data type status register (Register 0Ah. Status2)
340 */
341bk_reg0a BK108X::getStatus()
342{
343 bk_reg0a tmp;
344 tmp.raw = getRegister(REG0A); // update the reg0a shadow register
345 return tmp;
346}
347
348/**
349 * @ingroup GA03
350 * @brief Wait STC (Seek/Tune Complete) status becomes 0
351 * @details Should be used before processing Tune or Seek.
352 * @details The STC bit being cleared indicates that the TUNE or SEEK bits may be set again to start another tune or seek operation. Do not set the TUNE or SEEK bits until the BK108X clears the STC bit.
353 */
355{
356 while (reg0a->refined.STC == 0)
357 {
358 delay(10);
359 getRegister(REG0A);
360 }
361
362 reg03->refined.TUNE = 0;
363 setRegister(REG03, reg03->raw);
364 // delay(40);
365}
366
367/**
368 * @ingroup GA03
369 * @brief Resets the device
370 */
372{
373 reg02->refined.DISABLE = 1;
374 reg02->refined.ENABLE = 0;
375 setRegister(REG02, reg02->raw);
376 reg02->refined.DISABLE = 0;
377 reg02->refined.ENABLE = 1;
378 setRegister(REG02, reg02->raw);
379}
380
381/**
382 * @ingroup GA03
383 * @brief Powers the receiver on
384 * @details Starts the receiver and set default configurations suggested by the BELEN
385 * @see BEKEN - BK1086/88 - BROADCAST AM/FM/SW/LW RADIO RECEIVER Rev 1.3; pages 12-21
386 * @see setup
387 */
389{
390
391 // A bit delay before starting the system.
392 delay(100);
393 // Starts the mains register with default values suggested by BEKEN.
394
395 // Turn the receiver on
396 reg02->raw = 0x0280; // Sets to 0 all attributes of the register 0x02 (Power Configuration)
397 reg02->refined.DISABLE = 0; // Power up Disable: 0 = Normal operation
398 reg02->refined.ENABLE = 1; // Power the receiver UP (DISABLE has to be 0)
399 setRegister(REG02, reg02->raw); // Stores the register 0x02
400
401 setRegister(REG03, 0x0000); // Sets to 0 all attributes of the register 0x03 (Channel)
402
403 setRegister(REG04, 0x60D4); // 0b0110000011010100
404 setRegister(REG05, 0x37CF); // 0b0011011111001111
405
406 reg06->raw = 0x086F; // Sets to the default value - 0b0000100001101111 -> CLKSEL = 1
407 reg06->refined.CLKSEL = this->oscillatorType; // Sets to the clock type selected by the user
408 setRegister(REG06, reg06->raw);
409
410 setRegister(REG07, 0x0101); // 0b0000000100000001
411 setRegister(REG08, 0xAC90); // 0b1010110010010000
412
413 setRegister(REG10, 0x7B11); // 0b0111101100010001
414 setRegister(REG11, 0x004A); // 0b0000000001001010
415 setRegister(REG12, 0x4000); // 0b0100000000000000
416 setRegister(REG13, 0x3E00); // 0b0011111000000000
417 setRegister(REG14, 0xC29A); // 0b1100001010011010
418 setRegister(REG15, 0x79F8); // 0b0111100111111000
419 setRegister(REG16, 0x4012); // 0b0100000000010010
420
421 setRegister(REG17, 0x0040); // 0b0000000001000000
422 // setRegister(REG17, 0x0800); // 0b0000100000000000
423 setRegister(REG18, 0x341C); // 0b0011010000011100
424 setRegister(REG19, 0x0080); // 0b0000000010000000
425 setRegister(REG1A, 0x0000); // 0
426 setRegister(REG1B, 0x4CA2); // 0b0100110010100010
427 // 32768 crystal clock (default setup)
428 setRegister(REG1C, 0x8820); // 0b1000100000100000
429 setRegister(REG1D, 0x0200); // 0b0000001000000000 -> 512
430 // If the setup is not factory default -- The commands below are not working propely
431 if (this->oscillatorType != OSCILLATOR_TYPE_CRYSTAL || oscillatorFrequency != 32768)
432 {
433 uint32_t bk_number = (oscillatorFrequency / 512) + 0.5; // The result is a 18 bit number
434 uint16_t final_result;
435 // uint16_t aux;
436 // Sets the two most significant bits of result (bk_number) to the REG1C [1:0]
437 // reg1c->refined.FREQ_SEL = 0 // 12MHz test
438 reg1c->refined.FREQ_SEL = (bk_number >> 16);
439 setRegister(REG1C, reg1c->raw);
440 //The REG1D receives the inverted order of the 16 least significant bits of the bk_number.. The bit order of REG1D is opposite to the calculation result
441 final_result = bk_number & 0b001111111111111111;
442 // Inverts the bit order of aux;
443 // for (int i = 0; i < 16; i++)
444 // {
445 // if ((aux & (1 << i)) != 0)
446 // final_result |= 1 << (15 - i);
447 // }
448 setRegister(REG1D, final_result); // It has no effect on the REG1d register.
449 // setRegister(REG1D,0x71DA); // 12MHz test
450 }
451
452
453 delay(this->maxDelayAfterCrystalOn);
454}
455
456/**
457 * @ingroup GA03
458 * @brief Powers the receiver off
459 */
461{
462 reg02->refined.DISABLE = 1;
463 reg02->refined.ENABLE = 0;
464 setRegister(REG02, reg02->raw);
465 delay(100);
466}
467
468
469/**
470 * @ingroup GA03
471 * @brief Starts the device
472 * @details Initiates the divice (similar to the previous setup function)
473 * @details This method allows different clock frequencies.
474 * @details You have to inform at least two parameters: RESET pin and I2C SDA pin of your MCU
475 * @param sda_pin MCU SDA/SDIO pin (ATMEGA328 must be 4)
476 * @param sclk_pin MCU SCLK/CLK pin (ATMEGA328 must be 5)
477 * @param oscillator_type 0 = External clock input; 1= Internal oscillator input (default).
478 * @param oscillator_frequency from 32768Hz (32.768 KHz) to 34400000Hz (34.4Mhz). Default (32.768 kHz). For frequency less than 4 MHz, it must be multiplier of 32.768KHz.
479 */
480void BK108X::setup(int sda_pin, int sclk_pin, uint8_t oscillator_type, uint32_t oscillator_frequency)
481{
482 // Configures BEKEN I2C bus
483 this->i2cInit(sda_pin, sclk_pin);
484 this->oscillatorType = oscillator_type;
485 this->oscillatorFrequency = oscillator_frequency;
486 powerUp();
487}
488
489
490/**
491 * @ingroup GA03
492 * @brief Sets the receiver to FM mode
493 * @details Configures the receiver on FM mode; Also sets the band limits, defaul frequency and step.
494 *
495 * @param minimum_frequency minimum frequency for the band
496 * @param maximum_frequency maximum frequency for the band
497 * @param default_frequency default freuency
498 * @param step increment and decrement frequency step in KHz (default 10 * 10KHz)
499 */
500void BK108X::setFM(uint16_t minimum_frequency, uint16_t maximum_frequency, uint16_t default_frequency, uint16_t step)
501{
502 this->currentStep = step;
503 this->currentFrequency = default_frequency;
504 this->minimumFrequency = minimum_frequency;
505 this->maximumFrequency = maximum_frequency;
506 this->currentMode = BK_MODE_FM;
507
508 reg07->refined.MODE = BK_MODE_FM;
509 setRegister(REG07, reg07->raw);
510 delay(50);
511 // Sets BAND, SPACE and other parameters
512 this->currentFMBand = reg05->refined.BAND = 0;
513 this->currentFMSpace = reg05->refined.SPACE = 2; // Space will be always 2 and the tune is controlled by currentStep (multiple of 100)
514 setRegister(REG05, reg05->raw);
515 setFrequency(default_frequency);
516};
517
518/**
519 * @ingroup GA03
520 * @brief Sets the receiver to AM mode
521 * @details Configures the receiver on AM mode; Also sets the band limits, defaul frequency and step.
522 *
523 * @param minimum_frequency minimum frequency for the band
524 * @param maximum_frequency maximum frequency for the band
525 * @param default_frequency default freuency
526 * @param step increment and decrement frequency step
527 * @param am_space (default 0 = 1kHz). You can control the freqyuency step by using am_space = 0 and just set the step to 1, 5, 9 or 10 kHz.
528 * This way, you can keep the space always 0
529 */
530void BK108X::setAM(uint16_t minimum_frequency, uint16_t maximum_frequency, uint16_t default_frequency, uint16_t step, uint16_t am_space)
531{
532 this->currentStep = step;
533 this->currentFrequency = default_frequency;
534 this->minimumFrequency = minimum_frequency;
535 this->maximumFrequency = maximum_frequency;
536
537 this->currentMode = reg07->refined.MODE = BK_MODE_AM;
538 setRegister(REG07, reg07->raw);
539 delay(50);
540 // Sets BAND, SPACE and other parameters
541
542 if (minimum_frequency < 520)
543 this->currentAMBand = reg05->refined.BAND = 0; // LW
544 else if (minimum_frequency < 1800)
545 this->currentAMBand = reg05->refined.BAND = 1; // MW
546 else
547 this->currentAMBand = reg05->refined.BAND = 2; // SW
548
549 this->currentAMSpace = reg05->refined.SPACE = am_space; // Space default value 0 (0=1KHz; 1 = 5KHz; 2=9KHz; 3 = 10KHz)
550
551 setRegister(REG05, reg05->raw);
552 this->setFrequency(default_frequency);
553}
554
555/**
556 * @ingroup GA03
557 * @brief Sets the channel
558 * @param channel
559 */
560void BK108X::setChannel(uint16_t channel)
561{
562 reg02->refined.SEEK = 0;
563 setRegister(REG02, reg02->raw);
564
565 reg03->refined.TUNE = 1;
566 reg03->refined.CHAN = channel;
567
568 setRegister(REG03, reg03->raw);
569 // delay(50);
571
572 this->currentChannel = channel;
573}
574
575/**
576 * @ingroup GA03
577 * @brief Sets the FM frequency
578 * @details ....
579 * @param frequency
580 */
581void BK108X::setFrequency(uint16_t frequency)
582{
583 uint16_t channel;
584
585 if (this->currentMode == BK_MODE_FM)
586 {
587 channel = (frequency - this->fmStartBand[this->currentFMBand]) / this->fmSpace[this->currentFMSpace];
588 }
589 else
590 {
591 channel = (frequency - this->amStartBand[this->currentAMBand]) / this->amSpace[this->currentAMSpace];
592 }
593
594 this->setChannel(channel);
595}
596
597/**
598 * @ingroup GA03
599 * @brief Increments the current frequency
600 * @details The increment uses the band space as step.
601 */
603{
604 this->currentFrequency += this->currentStep;
605
606 if (this->currentFrequency > this->maximumFrequency)
607 this->currentFrequency = this->minimumFrequency;
608
609 setFrequency(this->currentFrequency);
610}
611
612/**
613 * @ingroup GA03
614 * @brief Decrements the current frequency
615 * @details The drecrement uses the band space as step.
616 */
618{
619 this->currentFrequency -= this->currentStep;
620
621 if (this->currentFrequency < this->minimumFrequency)
622 this->currentFrequency = this->maximumFrequency;
623
624 setFrequency(this->currentFrequency);
625}
626
627/**
628 * @ingroup GA03
629 * @brief Gets the current frequency.
630 * @return uint16_t
631 */
633{
634 return this->currentFrequency;
635}
636
637/**
638 * @ingroup GA03
639 * @brief Gets the current channel.
640 * @return uint16_t
641 */
643{
644 return this->currentChannel;
645}
646
647/**
648 * @ingroup GA03
649 * @brief Gets the current channel stored in register 0x0B
650 * @details This method is useful to query the current channel during the seek operations.
651 * @return uint16_t
652 */
654{
655 getRegister(REG0B);
656 return reg0b->refined.READCHAN;
657}
658
659/**
660 * @ingroup GA03
661 * @brief Gets the frequency based on READCHAN register (0x0B)
662 * @details Unlike getFrequency method, this method queries the device.
663 *
664 * @return uint16_t
665 */
667{
668 if (currentMode == BK_MODE_AM)
669 {
670 return getRealChannel() * this->amSpace[this->currentAMSpace] + this->amStartBand[this->currentAMBand];
671 }
672 else
673 {
674 return getRealChannel() * this->fmSpace[this->currentFMSpace] + this->fmStartBand[this->currentFMBand];
675 }
676}
677
678/**
679 * @todo it is not working properly
680 * @ingroup GA03
681 * @brief Seeks a station via Software
682 * @details Seeks a station up or down.
683 * @details Seek up or down a station and call a function defined by the user to show the frequency during the seek process.
684 * @details Seek begins at the current channel, and goes in the direction specified with the SEEKUP bit. Seek operation stops when a channel is qualified as valid according to the seek parameters, the entire band has been searched (SKMODE = 0), or the upper or lower band limit has been reached (SKMODE = 1).
685 * @details The STC bit is set high when the seek operation completes and/or the SF/BL bit is set high if the seek operation was unable to find a channel qualified as valid according to the seek parameters. The STC and SF/BL bits must be set low by setting the SEEK bit low before the next seek or tune may begin.
686 * @details Seek performance for 50 kHz channel spacing varies according to RCLK tolerance. Silicon Laboratories recommends ±50 ppm RCLK crystal tolerance for 50 kHz seek performance.
687 * @details A seek operation may be aborted by setting SEEK = 0.
688 * @details It is important to say you have to implement a show frequency function. This function have to get the frequency via getFrequency function.
689 * @details Example:
690 * @code
691 *
692 * BK108X rx;
693 *
694 * void showFrequency() {
695 * uint16_t freq = rx.getFrequency();
696 * Serial.print(freq);
697 * Serial.println("MHz ");
698 * }
699 *
700 * void loop() {
701 * .
702 * .
703 * rx.seek(BK_SEEK_WRAP, BK_SEEK_UP, showFrequency); // Seek Up
704 * .
705 * .
706 * }
707 * @endcode
708 * @param seek_mode Seek Mode; 0 = Wrap at the upper or lower band limit and continue seeking (default); 1 = Stop seeking at the upper or lower band limit.
709 * @param direction Seek Direction; 0 = Seek down (default); 1 = Seek up.
710 * @param showFunc function that you have to implement to show the frequency during the seeking process. Set NULL if you do not want to show the progress.
711 */
712void BK108X::seekSoftware(uint8_t seek_mode, uint8_t direction, void (*showFunc)())
713{
714 long max_time = millis(); // Maximum time seeking a station.
715
716 do
717 {
718 reg03->refined.TUNE = 0;
719 setRegister(REG03, reg03->raw);
720 delay(50);
721 reg02->refined.SKMODE = seek_mode;
722 reg02->refined.SEEKUP = direction;
723 reg02->refined.SEEK = 1;
724 setRegister(REG02, reg02->raw);
725 delay(50);
726 if (showFunc != NULL)
727 {
728 this->currentFrequency = getRealFrequency();
729 showFunc();
730 }
731 getRegister(REG0A);
732 } while ((!reg0a->refined.STC && reg0a->refined.SF_BL) && (millis() - max_time) < MAX_SEEK_TIME);
733
734 reg02->refined.SEEK = 0;
735 reg03->refined.TUNE = 0;
736 setRegister(REG02, reg02->raw);
737 setRegister(REG03, reg03->raw);
738
739 this->currentFrequency = getRealFrequency();
740}
741
742/**
743 * @todo make it work.
744 * @ingroup GA03
745 * @brief Seeks a station via hardware functionality
746 *
747 * @param seek_mode Seek Mode; 0 = Wrap at the upper or lower band limit and continue seeking (default); 1 = Stop seeking at the upper or lower band limit.
748 * @param direction Seek Direction; 0 = Seek down (default); 1 = Seek up.
749 */
750void BK108X::seekHardware(uint8_t seek_mode, uint8_t direction, void (*showFunc)())
751{
752
753 long max_time = millis(); // Max time seeking a station.
754
755 do
756 {
757 reg03->refined.TUNE = 0;
758 setRegister(REG03, reg03->raw);
759
760 reg02->refined.SKMODE = seek_mode;
761 reg02->refined.SEEKUP = direction;
762 reg02->refined.SKAFCRL = 1;
763 reg02->refined.SEEK = 1;
764 setRegister(REG02, reg02->raw);
765 delay(40);
766 while (reg0a->refined.STC == 0 && (millis() - max_time) < MAX_SEEK_TIME)
767 {
768 if (showFunc != NULL)
769 {
770 this->currentFrequency = getRealFrequency();
771 showFunc();
772 }
773 getRegister(REG0A);
774 delay(40);
775 }
776 reg02->refined.SEEK = 0;
777 setRegister(REG02, reg02->raw);
778 delay(50);
779
780 this->setChannel(this->getRealChannel());
781 this->currentFrequency = getRealFrequency();
782 this->setFrequency(this->currentFrequency);
783
784 } while (reg0a->refined.SF_BL != 0 && (millis() - max_time) < MAX_SEEK_TIME);
785 reg03->refined.TUNE = 0;
786 setRegister(REG03, reg03->raw);
787}
788
789/**
790 * @todo make it work.
791 * @ingroup GA03
792 * @brief Sets RSSI and SNR Seek Threshold
793 * @param rssiValue between 0 and 127
794 * @param snrValue between 0 and 127
795 */
796void BK108X::setSeekThreshold(uint8_t rssiValue, uint8_t snrValue)
797{
798 reg05->refined.SEEKTH = rssiValue;
799 setRegister(REG05, reg05->raw);
800
801 reg06->refined.SKSNR = snrValue;
802 setRegister(REG06, reg06->raw);
803}
804
805/**
806 * @ingroup GA03
807 * @brief Sets the current band for AM or FM
808 * @details Configures the band by setting the Register 05h. System Configuration2
809 *
810 * | Band value | AM / KHz | FM / MHz |
811 * | ---------- | -------------- | ------------------- |
812 * | 0 | LW - 153~279 | FULL - 64~108 |
813 * | 1 | MW - 520~1710 | East Europe 64~76 |
814 * | 2 | SW - 2.3~21.85 | Japan 76~91 |
815 * | 3 | MW - 522~1710 | Europe 87~108 |
816 *
817 * @see BK1086/88E - BROADCAST AM/FM/SW/LW RADIO RECEIVER Rev 1.3; page 15
818 * @param band the valid values are 0, 1, 2 and 3. See table above.
819 */
820
821void BK108X::setBand(uint8_t band)
822{
823 if (this->currentMode == BK_MODE_AM)
824 this->currentAMBand = band;
825 else
826 this->currentFMBand = band;
827
828 reg05->refined.BAND = band;
829 setRegister(REG05, reg05->raw);
830}
831
832/**
833 * @ingroup GA03
834 * @brief Sets the Space channel for AM or FM
835 *
836 * | Band value | AM | FM |
837 * | ---------- | ---------| --------- |
838 * | 0 | 1 KHz | 10 KHz |
839 * | 1 | 5 KKz | 50 KHz |
840 * | 2 | 9 KHz | 100 KHz |
841 * | 3 | 10 KHz | 200 KHz |
842 *
843 * @see BK1086/88E - BROADCAST AM/FM/SW/LW RADIO RECEIVER Rev 1.3; page 15
844 * @param space valid values 0,1,2 and 3. See table above.
845 */
846void BK108X::setSpace(uint8_t space)
847{
848 if (this->currentMode == BK_MODE_AM)
849 this->currentAMSpace = space;
850 else
851 this->currentFMSpace = space;
852
853 reg05->refined.SPACE = space;
854 setRegister(REG05, reg05->raw);
855}
856
857/**
858 * @ingroup GA03
859 * @brief Gets the current Rssi
860 *
861 * @return int
862 */
864{
865 getRegister(REG0A);
866 return reg0a->refined.RSSI;
867}
868
869/**
870 * @ingroup GA03
871 * @brief Gets the current SNR
872 *
873 * @return int The SNR Value.( in dB)
874 */
876{
877 getRegister(REG09);
878 return reg09->refined.SNR;
879}
880
881/**
882 * @ingroup GA03
883 * @brief Sets the Softmute true or false
884 * @details Enable or Disable Soft Mute resource.
885 * @param value TRUE or FALSE
886 */
887void BK108X::setSoftMute(bool value)
888{
889 reg02->refined.DSMUTE = !value; // Soft mute TRUE/ENABLE means DSMUTE = 0.
890 setRegister(REG02, reg02->raw);
891}
892
893/**
894 * @ingroup GA03
895 * @brief Sets Softmute Attack/Recover Rate.
896 *
897 * Soft mute Attack/Recover
898 *
899 * | Value | Description |
900 * | ----- | ----------- |
901 * | 0 | fastest |
902 * | 1 | fast |
903 * | 2 | slow |
904 * | 3 | slowest |
905 *
906 * @param value See table above.
907 */
908void BK108X::setSoftMuteAttack(uint8_t value)
909{
910 reg06->refined.SMUTER = value;
911 setRegister(REG06, reg06->raw);
912}
913
914/**
915 * @ingroup GA03
916 * @brief Sets Softmute Attenuation.
917 *
918 * Soft mute Attenuation.
919 * | Value | Description |
920 * | ----- | ----------- |
921 * | 0 | fastest |
922 * | 1 | fast |
923 * | 2 | slow |
924 * | 3 | slowest |
925 * @param value See table above
926 */
927void BK108X::setSoftMuteAttenuation(uint8_t value)
928{
929 reg06->refined.SMUTEA = value;
930 setRegister(REG06, reg06->raw);
931}
932
933/**
934 * @ingroup GA03
935 * @brief Set the Mute Threshold based on RSSI and SNR
936 *
937 * @see BK1086/88 - BROADCAST AM/FM/SW/LW RADIO RECEIVER Rev 1.3; page 19; Register 0x14 (Boot Configuration5)
938 * @param rssi The Mute Threshold Based on RSSI (default 26)
939 * @param snr The Mute Threshold Based on SNR (default 5)
940 */
941void BK108X::setMuteThreshold(uint8_t rssi, uint8_t snr)
942{
943 reg14->refined.RSSIMTH = rssi;
944 reg14->refined.SNRMTH = snr;
945 setRegister(REG14, reg14->raw);
946}
947
948/**
949 * @ingroup GA03
950 * @brief Disable or Enable soft mute when seeking
951 *
952 * @param value If true, enable mute during the seek;
953 */
954void BK108X::setSeekMute(bool value)
955{
956 reg14->refined.SKMUTE = value;
957 setRegister(REG14, reg14->raw);
958}
959
960/**
961 * @ingroup GA03
962 * @brief Disable or Enable soft mute when AFCRL is high
963 *
964 * @param value If true, enable soft mute when AFCRL is high
965 */
966void BK108X::setAfcMute(bool value)
967{
968 reg14->refined.AFCMUTE = value;
969 setRegister(REG14, reg14->raw);
970}
971
972/**
973 * @ingroup GA03
974 * @brief Sets the Mute true or false
975 *
976 * @param left left channel (TRUE = MUTE/ FALSE = UNMUTE)
977 * @param left right channel (TRUE = MUTE / FALSE = UMUTE)
978 */
979void BK108X::setAudioMute(bool left, bool right)
980{
981 reg02->refined.MUTEL = left;
982 reg02->refined.MUTER = right;
983 setRegister(REG02, reg02->raw);
984}
985
986/**
987 * @ingroup GA03
988 * @brief Sets the Mute true or false
989 *
990 * @param value left and right channels (TRUE = MUTE/ FALSE = UNMUTE)
991 */
992void BK108X::setAudioMute(bool value)
993{
994 this->setAudioMute(value, value);
995}
996
997/**
998 * @ingroup GA03
999 * @brief Sets the Mono true or false (stereo)
1000 * @details if TRUE, force mono; else force stereo
1001 * @param value TRUE or FALSE
1002 */
1003void BK108X::setMono(bool value)
1004{
1005 reg02->refined.MONO = value;
1006 reg02->refined.STEREO = !value;
1007 setRegister(REG02, reg02->raw);
1008}
1009
1010/**
1011 * @ingroup GA03
1012 * @brief Checks stereo / mono status
1013 *
1014 * @see getStatus
1015 * @param value TRUE if stereo
1016 */
1018{
1019 getRegister(REG0A);
1020 return reg0a->refined.STEN;
1021}
1022
1023/**
1024 * @ingroup GA03
1025 * @brief Sets the audio volume level
1026 *
1027 * @param value 0 to 31 (if 0, mutes the audio)
1028 */
1029void BK108X::setVolume(uint8_t value)
1030{
1031 if (value > 31)
1032 return;
1033 this->currentVolume = value;
1034 // reg05 is a shadow register and has the last value read or written from/to the internal device register
1035 reg05->refined.VOLUME = value;
1036
1037 setRegister(REG05, reg05->raw);
1038}
1039
1040/**
1041 * @ingroup GA03
1042 * @brief Gets the current audio volume level
1043 *
1044 * @return uint8_t 0 to 15
1045 */
1047{
1048 return this->currentVolume;
1049}
1050
1051/**
1052 * @ingroup GA03
1053 * @brief Increments the audio volume
1054 *
1055 */
1057{
1058 if (this->currentVolume < 31)
1059 {
1060 this->currentVolume++;
1061 setVolume(this->currentVolume);
1062 }
1063}
1064
1065/**
1066 * @ingroup GA03
1067 * @brief Decrements the audio volume
1068 *
1069 */
1071{
1072 if (this->currentVolume > 0)
1073 {
1074 this->currentVolume--;
1075 setVolume(this->currentVolume);
1076 }
1077}
1078
1079/**
1080 * @defgroup GA04 RDS Functions
1081 * @section GA04 RDS/RBDS
1082 */
1083
1084
1085/**
1086 * @ingroup GA04
1087 * @brief Sets the Rds Mode Standard or Verbose
1088 * @details There is no information about it in the BK1088 documentation.
1089 * @param rds_mode 0 = Standard (default); 1 = Verbose
1090 */
1091void BK108X::setRdsMode(uint8_t rds_mode)
1092{
1093 this->rds_mode = rds_mode;
1094}
1095
1096/**
1097 * @ingroup GA04
1098 * @brief Sets the RDS operation
1099 * @details Enable or Disable the RDS
1100 * @details You can setup interrupt via GPIO2. It is useful to avoid pulling during RDS queries.
1101 * @details When a new RDS data is available (new RDS information came) a 5ms low pulse will appear at GPIO2
1102 * @param true = turns the RDS ON; false = turns the RDS OFF
1103 * @param true = enable interruot at GPIO2 when new RDS is available (default false).
1104 */
1105void BK108X::setRds(bool value, bool interrupt_enable)
1106{
1107 reg04->refined.RDSEN = value;
1108 if ( interrupt_enable ) {
1109 reg04->refined.RDSIEN = interrupt_enable; // enable interrupt when new RDS information is available
1110 reg04->refined.GPIO2 = 1; // set to 5ms low pulse at GPIO2
1111 }
1112 setRegister(REG04, reg04->raw);
1113}
1114
1115/**
1116 * @ingroup GA04
1117 * @brief Returns true if RDS Ready
1118 * @details Read addresses 0Ah, 0Ch, and check the bit RDSR.
1119 * @details If in verbose mode, the BLERA bits indicate how many errors were corrected in block A. If BLERA indicates 6 or more errors, the data in RDSA should be discarded.
1120 * @details When using the polling method, it is best not to poll continuously. The data will appear in intervals of ~88 ms and the RDSR indicator will be available for at least 40 ms, so a polling rate of 40 ms or less should be sufficient.
1121 * @details ATTENTION: You must call this function before calling any RDS function to process data
1122 * @return true or false
1123 */
1125{
1126 getRegister(REG0A);
1127 if (!reg0a->refined.RDSR) // Checks if there is some RDS information available.
1128 return false;
1129 getRegister(REG0C); // The First Register of RDS - Block A
1130 getRegister(REG0D); // The second register of RDS - Block B
1131 getRegister(REG0E); // The third register of RDS - Block C
1132 getRegister(REG0F); // The third register of RDS - Bloco D
1133 return true;
1134};
1135
1136/**
1137 * @ingroup GA04
1138 *
1139 * @brief Returns the current Text Flag A/B
1140 * @details You must call getRdsReady before calling this function
1141 * @return uint8_t current Text Flag A/B
1142 */
1144{
1145 bk_rds_blockb blkb;
1146 blkb.blockB = reg0d->raw;
1147 return blkb.refined.textABFlag;
1148}
1149
1150/**
1151 * @ingroup GA04
1152 * @brief Return the group type - Gets the Group Type (extracted from the Block B)
1153 * @details You must call getRdsReady before calling this function
1154 * @return uint16_t
1155 */
1157{
1158 bk_rds_blockb b;
1159 b.blockB = reg0d->raw;
1160 return b.refined.groupType;
1161}
1162
1163/**
1164 * @ingroup GA04
1165 *
1166 * @brief Gets the version code (extracted from the Block B)
1167 * @details You must call getRdsReady before calling this function
1168 * @returns 0=A or 1=B
1169 */
1171{
1172 bk_rds_blockb b;
1173 b.blockB = reg0d->raw;
1174 return b.refined.versionCode;
1175}
1176
1177/**
1178 * @ingroup GA04
1179 * @brief Returns the Program Type (extracted from the Block B)
1180 * @details You must call getRdsReady before calling this function
1181 * @see https://en.wikipedia.org/wiki/Radio_Data_System
1182 * @return program type (an integer betwenn 0 and 31)
1183 */
1185{
1186 bk_rds_blockb b;
1187 b.blockB = reg0d->raw;
1188 return b.refined.programType;
1189}
1190
1191/**
1192 * @ingroup GA04
1193 * @brief Process data received from group 2B - Station Name
1194 * @details It appears that the BK1088 device is typically pre-configured with RBDS setup instead of RDS by default.
1195 * @details Consequently, the Station Name is obtained from block E instead of block B.
1196 * @details The documentation from BEKEN used in this project does not provide instructions on how to switch from RBDS to RDS mode.
1197 * @details If anyone has access to this information, kindly share it with us.
1198 * @param c char array reference to the "group 2B" text
1199 */
1200void inline BK108X::getNext2Block(char *c)
1201{
1202 c[1] = reg0f->refined.lowByte; // should be reg0d (block B) if RDS and reg0f (block E) if RBDS.
1203 c[0] = reg0f->refined.highByte;
1204}
1205
1206/**
1207 * @ingroup GA04
1208 *
1209 * @brief Processes data received from group 2A
1210 * @details decodes data received from block C and block D
1211 * @param c char array reference to the "group 2A" text
1212 */
1213void inline BK108X::getNext4Block(char *c)
1214{
1215 c[0] = reg0e->refined.highByte; // RDS Block C high byte
1216 c[1] = reg0e->refined.lowByte; // RDS Block C low byte
1217 c[2] = reg0f->refined.highByte; // RDS Block D high byte
1218 c[3] = reg0f->refined.lowByte; // RDS Block D low byte
1219}
1220
1221/**
1222 * @ingroup GA04
1223 *
1224 * @brief Gets the RDS Text when the message is of the Group Type 2 version A
1225 * @return char* The string (char array) with the content (Text) received from group 2A
1226 */
1227char *BK108X::getRdsText(void)
1228{
1229 return NULL;
1230}
1231
1232/**
1233 * @ingroup GA04
1234 * @todo Need to check Block B and Block E and RDS mode
1235 * @todo RDS Dynamic PS or Scrolling PS support
1236 * @brief Gets the Station Name and other messages.
1237 * @details Same getRdsStationName.
1238 * @return char* should return a string with the station name.
1239 * However, some stations send other kind of messages
1240 */
1242{
1243 static int rdsTextAdress0A;
1244 bk_rds_blockb blkb;
1245
1246 blkb.blockB = reg0d->raw;
1247
1248 if (blkb.group0.groupType == 0)
1249 {
1250 // Process group type 0
1251 rdsTextAdress0A = blkb.group0.address;
1252 if (rdsTextAdress0A >= 0 && rdsTextAdress0A < 4)
1253 {
1254 getNext2Block(&rds_buffer0A[rdsTextAdress0A * 2]);
1255 rds_buffer0A[8] = '\0';
1256 return rds_buffer0A;
1257 }
1258 }
1259 return NULL;
1260}
1261
1262/**
1263 * @ingroup @ingroup GA04
1264 *
1265 * @brief Gets the Text processed for the 2A group
1266 *
1267 * @return char* string with the Text of the group A2
1268 */
1270{
1271 static int rdsTextAdress2A;
1272 bk_rds_blockb blkb;
1273
1274 blkb.blockB = reg0d->raw;
1275 rdsTextAdress2A = blkb.group2.address;
1276
1277 if (blkb.group2.groupType == 2)
1278 {
1279 // Process group 2A
1280 // Decode B block information
1281 if (rdsTextAdress2A >= 0 && rdsTextAdress2A < 16)
1282 {
1283 getNext4Block(&rds_buffer2A[rdsTextAdress2A * 4]);
1284 rds_buffer2A[63] = '\0';
1285 return rds_buffer2A;
1286 }
1287 }
1288 return NULL;
1289}
1290
1291/**
1292 * @ingroup GA04
1293 * @brief Gets the Text processed for the 2B group
1294 * @return char* string with the Text of the group AB
1295 */
1297{
1298 static int rdsTextAdress2B;
1299 bk_rds_blockb blkb;
1300
1301 blkb.blockB = reg0d->raw;
1302 if (blkb.group2.groupType == 1)
1303 {
1304 // Process group 2B
1305 rdsTextAdress2B = blkb.group2.address;
1306 if (rdsTextAdress2B >= 0 && rdsTextAdress2B < 16)
1307 {
1308 getNext2Block(&rds_buffer2B[rdsTextAdress2B * 2]);
1309 return rds_buffer2B;
1310 }
1311 }
1312 return NULL;
1313}
1314
1315/**
1316 * @ingroup GA04
1317 * @brief Gets Station Name, Station Information, Program Information and utcTime
1318 * @details This function populates four char pointer variable parameters with Station Name, Station Information, Programa Information and UTC time.
1319 * @details You must call setRDS(true), setRdsFifo(true) before calling getRdsAllData(...)
1320 * @details ATTENTION: the parameters below are point to point to array of char.
1321 * @details the right way to call this function is shown below.
1322 * @code {.cpp}
1323 *
1324 * char *stationName, *stationInfo, *programInfo, *rdsTime;
1325 * // The char pointers above will be populate by the call below. So, the char pointers need to be passed by reference (pointer to pointer).
1326 * if (rx.getRdsAllData(&stationName, &stationInfo , &programInfo, &rdsTime) ) {
1327 * showProgramaInfo(programInfo);
1328 * showStationName(stationName);
1329 * showStationInfo(stationInfo);
1330 * showUtcTime(rdsTime);
1331 * }
1332 * @endcode
1333 * @param stationName (reference) - if NOT NULL, point to Name of the Station (char array - 9 bytes)
1334 * @param stationInformation (reference) - if NOT NULL, point to Station information (char array - 33 bytes)
1335 * @param programInformation (reference) - if NOT NULL, point to program information (char array - 65 nytes)
1336 * @param utcTime (reference) - if NOT NULL, point to char array containing the current UTC time (format HH:MM:SS +HH:MM)
1337 * @return True if found at least one valid data
1338 * @see setRDS, setRdsFifo, getRdsAllData
1339 */
1340bool BK108X::getRdsAllData(char **stationName, char **stationInformation, char **programInformation, char **utcTime)
1341{
1342
1343 if (!this->getRdsReady())
1344 return false;
1345 *stationName = this->getRdsText0A(); // returns NULL if no information
1346 *stationInformation = this->getRdsText2B(); // returns NULL if no information
1347 *programInformation = this->getRdsText2A(); // returns NULL if no information
1348 *utcTime = this->getRdsTime(); // returns NULL if no information
1349
1350 return (bool)stationName | (bool)stationInformation | (bool)programInformation | (bool)utcTime;
1351}
1352
1353/**
1354 * @ingroup GA04
1355 * @brief Gets the RDS time and date when the Group type is 4
1356 * @return char* a string with hh:mm +/- offset
1357 */
1359{
1360 bk_rds_date_time dt;
1361 word16_to_bytes blk_b, blk_c, blk_d;
1362 bk_rds_blockb blkb;
1363
1364 blk_b.raw = blkb.blockB = reg0d->raw; // Block B
1365 blk_c.raw = reg0e->raw; // Block C
1366 blk_d.raw = reg0f->raw; // Block D
1367
1368 uint16_t minute;
1369 uint16_t hour;
1370
1371 if (blkb.group0.groupType == 4)
1372 {
1373 char offset_sign;
1374 int offset_h;
1375 int offset_m;
1376
1377 // uint16_t y, m, d;
1378
1379 dt.raw[4] = blk_b.refined.lowByte;
1380 dt.raw[5] = blk_b.refined.highByte;
1381
1382 dt.raw[2] = blk_c.refined.lowByte;
1383 dt.raw[3] = blk_c.refined.highByte;
1384
1385 dt.raw[0] = blk_d.refined.lowByte;
1386 dt.raw[1] = blk_d.refined.highByte;
1387
1388 minute = dt.refined.minute;
1389 hour = dt.refined.hour;
1390
1391 offset_sign = (dt.refined.offset_sense == 1) ? '+' : '-';
1392 offset_h = (dt.refined.offset * 30) / 60;
1393 offset_m = (dt.refined.offset * 30) - (offset_h * 60);
1394
1395 // If wrong time, return NULL
1396 if (offset_h > 12 || offset_m > 60 || hour > 24 || minute > 60)
1397 return NULL;
1398
1399 this->convertToChar(hour, rds_time, 2, 0, ' ', false);
1400 rds_time[2] = ':';
1401 this->convertToChar(minute, &rds_time[3], 2, 0, ' ', false);
1402 rds_time[5] = ' ';
1403 rds_time[6] = offset_sign;
1404 this->convertToChar(offset_h, &rds_time[7], 2, 0, ' ', false);
1405 rds_time[9] = ':';
1406 this->convertToChar(offset_m, &rds_time[10], 2, 0, ' ', false);
1407 rds_time[12] = '\0';
1408
1409 return rds_time;
1410 }
1411
1412 return NULL;
1413}
1414
1415/**
1416 * @ingroup GA04
1417 * @todo Need to check.
1418 * @brief Gets the RDS time converted to local time.
1419 * @details ATTENTION: You must call getRdsReady before calling this function.
1420 * @details ATTENTION: Some stations broadcast wrong time.
1421 * @return char* a string with hh:mm
1422 * @see getRdsReady
1423 */
1425{
1426 bk_rds_date_time dt;
1427 word16_to_bytes blk_b, blk_c, blk_d;
1428 bk_rds_blockb blkb;
1429
1430 blk_b.raw = blkb.blockB = reg0d->raw; // Block B
1431 blk_c.raw = reg0e->raw; // Block C
1432 blk_d.raw = reg0f->raw; // Block D
1433
1434 uint16_t minute;
1435 uint16_t hour;
1436 uint16_t localTime;
1437
1438 if (blkb.group0.groupType == 4)
1439 {
1440 int offset_h;
1441 int offset_m;
1442
1443 dt.raw[4] = blk_b.refined.lowByte;
1444 dt.raw[5] = blk_b.refined.highByte;
1445
1446 dt.raw[2] = blk_c.refined.lowByte;
1447 dt.raw[3] = blk_c.refined.highByte;
1448
1449 dt.raw[0] = blk_d.refined.lowByte;
1450 dt.raw[1] = blk_d.refined.highByte;
1451
1452 minute = dt.refined.minute;
1453 hour = dt.refined.hour;
1454
1455 offset_h = (dt.refined.offset * 30) / 60;
1456 offset_m = (dt.refined.offset * 30) - (offset_h * 60);
1457
1458 localTime = (hour * 60 + minute);
1459 if (dt.refined.offset_sense == 1)
1460 localTime -= (offset_h * 60 + offset_m);
1461 else
1462 localTime += (offset_h * 60 + offset_m);
1463
1464 hour = localTime / 60;
1465 minute = localTime - (hour * 60);
1466
1467 if (hour > 24 || minute > 60)
1468 return NULL;
1469
1470 this->convertToChar(hour, rds_time, 2, 0, ' ', false);
1471 rds_time[2] = ':';
1472 this->convertToChar(minute, &rds_time[3], 2, 0, ' ', false);
1473 rds_time[5] = '\0';
1474
1475 return rds_time;
1476 }
1477
1478 return NULL;
1479}
1480
1481/**
1482 * @ingroup GA04
1483 * @brief Get the Rds Sync
1484 * @details Returns true if RDS currently synchronized.
1485 * @return true or false
1486 */
1488{
1489 return NULL;
1490}
1491
1492/**
1493 * @ingroup GA04
1494 * @brief Clear RDS Information (Station Name, Station Information, Program Information and Time)
1495 * @details Clear the buffer with latest RDS information
1496 */
1498{
1499 memset(rds_buffer0A, 0, sizeof(rds_buffer0A));
1500 memset(rds_buffer2A, 0, sizeof(rds_buffer2A));
1501 memset(rds_buffer2B, 0, sizeof(rds_buffer2B));
1502 memset(rds_time, 0, sizeof(rds_time));
1503}
1504
1505/**
1506 * @defgroup GA05 Tools
1507 * @section GA05 Tools / Helper
1508 */
1509
1510/**
1511 * @ingroup GA05 Check the I2C buss address
1512 * @brief Check the I2C bus address
1513 * @details For some reason, the BK1088 device does not work with the standard Wire.h library of Arduino.
1514 * @details The checkI2C function is only used to test the circuit.
1515 * @details In practice, no function from the Wire.h library is utilized in a real application with the BK1088 in this project.
1516 * @param uint8_t address Array - this array will be populated with the I2C bus addresses found (minimum three elements)
1517 * @return 0 if no i2c device is found; -1 if error is found or n > 0, where n is the number of I2C bus address found
1518 */
1519int BK108X::checkI2C(uint8_t *addressArray)
1520{
1521 Wire.begin();
1522 int error, address;
1523 int idx = 0;
1524 for (address = 1; address < 127; address++)
1525 {
1526 Wire.beginTransmission(address);
1527 error = Wire.endTransmission();
1528 if (error == 0)
1529 {
1530 addressArray[idx] = address;
1531 idx++;
1532 }
1533 else if (error == 4)
1534 return -1;
1535 }
1536 Wire.end();
1537 delay(200);
1538 return idx;
1539}
1540
1541/**
1542 * @ingroup GA05 Check the I2C buss address
1543 * @brief Returns the point of uint16_t array (size 32)
1544 * @details Useful to monitor the device internal registers
1545 * @return point of shadowRegisters
1546 */
1548 for (uint8_t i = 0; i < 32; i++ ) {
1549 this->getRegister(i); // Gets the current value of the internal register
1550 }
1551 return shadowRegisters;
1552}
1553
1554/**
1555 * @ingroup GA05 Covert numbers to char array
1556 * @brief Converts a number to a char array
1557 * @details It is useful to mitigate memory space used by functions like sprintf or othetr generic similar functions
1558 * @details You can use it to format frequency using decimal or tousand separator and also to convert smalm numbers.
1559 *
1560 * @param value value to be converted
1561 * @param strValue char array that will be receive the converted value
1562 * @param len final string size (in bytes)
1563 * @param dot the decimal or tousand separator position
1564 * @param separator symbol "." or ","
1565 * @param remove_leading_zeros if true removes up to two leading zeros (default is true)
1566 */
1567void BK108X::convertToChar(uint16_t value, char *strValue, uint8_t len, uint8_t dot, uint8_t separator, bool remove_leading_zeros)
1568{
1569 char d;
1570 for (int i = (len - 1); i >= 0; i--)
1571 {
1572 d = value % 10;
1573 value = value / 10;
1574 strValue[i] = d + 48;
1575 }
1576 strValue[len] = '\0';
1577 if (dot > 0)
1578 {
1579 for (int i = len; i >= dot; i--)
1580 {
1581 strValue[i + 1] = strValue[i];
1582 }
1583 strValue[dot] = separator;
1584 }
1585
1586 if (remove_leading_zeros)
1587 {
1588 if (strValue[0] == '0')
1589 {
1590 strValue[0] = ' ';
1591 if (strValue[1] == '0')
1592 strValue[1] = ' ';
1593 }
1594 }
1595}
#define REG12
Definition: BK108X.h:72
#define REG02
Definition: BK108X.h:56
#define REG0C
Definition: BK108X.h:66
#define REG0E
Definition: BK108X.h:68
#define REG07
Definition: BK108X.h:61
#define REG13
Definition: BK108X.h:73
#define REG17
Definition: BK108X.h:77
#define BK_MODE_AM
Definition: BK108X.h:48
#define REG15
Definition: BK108X.h:75
#define REG0D
Definition: BK108X.h:67
#define REG09
Definition: BK108X.h:63
#define REG10
Definition: BK108X.h:70
#define REG19
Definition: BK108X.h:79
#define REG0A
Definition: BK108X.h:64
#define REG1A
Definition: BK108X.h:80
#define REG1C
Definition: BK108X.h:82
#define REG00
Definition: BK108X.h:54
#define REG0B
Definition: BK108X.h:65
#define REG16
Definition: BK108X.h:76
#define REG05
Definition: BK108X.h:59
#define REG04
Definition: BK108X.h:58
#define REG1B
Definition: BK108X.h:81
#define REG1D
Definition: BK108X.h:83
#define REG0F
Definition: BK108X.h:69
#define REG11
Definition: BK108X.h:71
#define REG08
Definition: BK108X.h:62
#define REG03
Definition: BK108X.h:57
#define REG06
Definition: BK108X.h:60
#define REG18
Definition: BK108X.h:78
#define MAX_SEEK_TIME
Definition: BK108X.h:25
#define OSCILLATOR_TYPE_CRYSTAL
Definition: BK108X.h:27
#define REG14
Definition: BK108X.h:74
#define BK_MODE_FM
Definition: BK108X.h:47
#define REG01
Definition: BK108X.h:55
uint16_t getChipId()
Returns the Chip Indentifiction.
Definition: BK108X.cpp:317
char rds_buffer2A[65]
RDS Radio Text buffer - Program Information.
Definition: BK108X.h:788
char rds_buffer2B[33]
RDS Radio Text buffer - Station Informaation.
Definition: BK108X.h:789
uint16_t getDeviceId()
Returns the Device Indentifiction.
Definition: BK108X.cpp:307
char rds_buffer0A[9]
RDS Basic tuning and switching information (Type 0 groups)
Definition: BK108X.h:790
int deviceAddress
Definition: BK108X.h:795
char rds_time[20]
RDS date time received information.
Definition: BK108X.h:791
KT0915 Class.
Definition: BK108X.h:733
uint8_t i2cReceiveAck()
Gets Acknowledge (ACK)
Definition: BK108X.cpp:139
uint8_t i2cReadByte()
Gets a Byte from the slave device.
Definition: BK108X.cpp:184
uint16_t readRegister(uint8_t reg)
Gets an array of values from a BK108X given register.
Definition: BK108X.cpp:240
void i2cBeginTransaction()
Starts the I2C bus transaction.
Definition: BK108X.cpp:66
void i2cEndTransaction()
Finish the I2C bus transaction.
Definition: BK108X.cpp:85
void i2cWriteByte(uint8_t data)
Sends a Byte to the slave device.
Definition: BK108X.cpp:161
void writeRegister(uint8_t reg, uint16_t vakue)
Sends an array of values to a BK108X given register.
Definition: BK108X.cpp:211
void i2cNack()
Sends Not Acknowledge (ACK)
Definition: BK108X.cpp:121
void setI2C(uint8_t i2c_addr=I2C_DEVICE_ADDR)
Sets I2C bus address.
Definition: BK108X.cpp:43
void i2cAck()
Sends Acknowledge (ACK)
Definition: BK108X.cpp:105
void i2cInit(int pin_sdio, int pin_sclk)
Sets the MCU pins connected to the I2C bus.
Definition: BK108X.cpp:56
void reset()
Resets the device.
Definition: BK108X.cpp:371
uint16_t getChannel()
Gets the current channel.
Definition: BK108X.cpp:642
void seekHardware(uint8_t seek_mode, uint8_t direction, void(*showFunc)()=NULL)
Seeks a station via hardware functionality.
Definition: BK108X.cpp:750
void setSpace(uint8_t space=0)
Sets the Space channel for AM or FM.
Definition: BK108X.cpp:846
void seekSoftware(uint8_t seek_mode, uint8_t direction, void(*showFunc)()=NULL)
Seeks a station via Software.
Definition: BK108X.cpp:712
uint16_t getRealChannel()
Gets the current channel stored in register 0x0B.
Definition: BK108X.cpp:653
void waitAndFinishTune()
Wait STC (Seek/Tune Complete) status becomes 0.
Definition: BK108X.cpp:354
void setSoftMuteAttack(uint8_t value)
Sets Softmute Attack/Recover Rate.
Definition: BK108X.cpp:908
void setSeekThreshold(uint8_t rssiValue, uint8_t snrValue)
Sets RSSI and SNR Seek Threshold.
Definition: BK108X.cpp:796
void setAM(uint16_t minimum_frequency, uint16_t maximum_frequency, uint16_t default_frequency, uint16_t step, uint16_t am_space=0)
Sets the receiver to AM mode.
Definition: BK108X.cpp:530
int getSnr()
Gets the current SNR.
Definition: BK108X.cpp:875
void setAudioMute(bool left, bool right)
Sets the Mute true or false.
Definition: BK108X.cpp:979
void setFrequency(uint16_t frequency)
Sets the FM frequency.
Definition: BK108X.cpp:581
void setBand(uint8_t band=1)
Sets the current band for AM or FM.
Definition: BK108X.cpp:821
void setFrequencyDown()
Decrements the current frequency.
Definition: BK108X.cpp:617
void setAudioMute(bool value)
Sets the Mute true or false.
Definition: BK108X.cpp:992
void setSoftMute(bool value)
Sets the Softmute true or false.
Definition: BK108X.cpp:887
void setAfcMute(bool value)
Disable or Enable soft mute when AFCRL is high.
Definition: BK108X.cpp:966
bk_reg0a getStatus()
Gets the current status (register 0x0A) content.
Definition: BK108X.cpp:341
bool isStereo()
Checks stereo / mono status.
Definition: BK108X.cpp:1017
void setVolumeUp()
Increments the audio volume.
Definition: BK108X.cpp:1056
uint16_t getFrequency()
Gets the current frequency.
Definition: BK108X.cpp:632
void setSoftMuteAttenuation(uint8_t value)
Sets Softmute Attenuation.
Definition: BK108X.cpp:927
void powerDown()
Powers the receiver off.
Definition: BK108X.cpp:460
void setChannel(uint16_t channel)
Sets the channel.
Definition: BK108X.cpp:560
void setFM(uint16_t minimum_frequency, uint16_t maximum_frequency, uint16_t default_frequency, uint16_t step)
Sets the receiver to FM mode.
Definition: BK108X.cpp:500
void setVolume(uint8_t value)
Sets the audio volume level.
Definition: BK108X.cpp:1029
uint8_t getVolume()
Gets the current audio volume level.
Definition: BK108X.cpp:1046
void setMono(bool value)
Sets the Mono true or false (stereo)
Definition: BK108X.cpp:1003
void setFrequencyUp()
Increments the current frequency.
Definition: BK108X.cpp:602
uint16_t getRealFrequency()
Gets the frequency based on READCHAN register (0x0B)
Definition: BK108X.cpp:666
void setVolumeDown()
Decrements the audio volume.
Definition: BK108X.cpp:1070
void powerUp()
Powers the receiver on.
Definition: BK108X.cpp:388
void setup(int sda_pin, int sclk_pin, uint8_t oscillator_type=OSCILLATOR_TYPE_CRYSTAL, uint32_t oscillator_frequency=32768)
Starts the device.
Definition: BK108X.cpp:480
int getRssi()
Gets the current Rssi.
Definition: BK108X.cpp:863
void setRegister(uint8_t reg, uint16_t value)
Sets a given value to the device registers.
Definition: BK108X.cpp:296
void setMuteThreshold(uint8_t rssi, uint8_t snr)
Set the Mute Threshold based on RSSI and SNR.
Definition: BK108X.cpp:941
void setSeekMute(bool value)
Disable or Enable soft mute when seeking.
Definition: BK108X.cpp:954
uint16_t getRegister(uint8_t reg)
Gets a givens current register content of the device.
Definition: BK108X.cpp:276
char * getRdsText0A(void)
Gets the Station Name and other messages.
Definition: BK108X.cpp:1241
bool getRdsReady()
Returns true if RDS Ready.
Definition: BK108X.cpp:1124
char * getRdsText(void)
Gets the RDS Text when the message is of the Group Type 2 version A.
Definition: BK108X.cpp:1227
char * getRdsText2B(void)
Gets the Text processed for the 2B group.
Definition: BK108X.cpp:1296
void getNext4Block(char *c)
Processes data received from group 2A.
Definition: BK108X.cpp:1213
uint8_t getRdsVersionCode(void)
Gets the version code (extracted from the Block B)
Definition: BK108X.cpp:1170
bool getRdsAllData(char **stationName, char **stationInformation, char **programInformation, char **utcTime)
Gets Station Name, Station Information, Program Information and utcTime.
Definition: BK108X.cpp:1340
char * getRdsTime()
Gets the RDS time and date when the Group type is 4.
Definition: BK108X.cpp:1358
uint16_t getRdsGroupType()
Return the group type - Gets the Group Type (extracted from the Block B)
Definition: BK108X.cpp:1156
uint8_t getRdsFlagAB(void)
Returns the current Text Flag A/B.
Definition: BK108X.cpp:1143
uint8_t getRdsProgramType(void)
Returns the Program Type (extracted from the Block B)
Definition: BK108X.cpp:1184
void getNext2Block(char *c)
Process data received from group 2B - Station Name.
Definition: BK108X.cpp:1200
char * getRdsText2A(void)
Gets the Text processed for the 2A group.
Definition: BK108X.cpp:1269
void clearRdsBuffer()
Clear RDS Information (Station Name, Station Information, Program Information and Time)
Definition: BK108X.cpp:1497
bool getRdsSync()
Get the Rds Sync.
Definition: BK108X.cpp:1487
void setRdsMode(uint8_t rds_mode=0)
Sets the Rds Mode Standard or Verbose.
Definition: BK108X.cpp:1091
void setRds(bool value, bool interrupt_enable=false)
Sets the RDS operation.
Definition: BK108X.cpp:1105
char * getRdsLocalTime()
Gets the RDS time converted to local time.
Definition: BK108X.cpp:1424
void convertToChar(uint16_t value, char *strValue, uint8_t len, uint8_t dot, uint8_t separator, bool remove_leading_zeros=true)
Converts a number to a char array.
Definition: BK108X.cpp:1567
uint16_t * getRegisterValues()
Returns the point of uint16_t array (size 32)
Definition: BK108X.cpp:1547
int checkI2C(uint8_t *addressArray)
Check the I2C bus address.
Definition: BK108X.cpp:1519