diff --git a/examples/40-fm-tuner-rda5807/5651_tuner84_RDA5807M_datasheet_v1.pdf b/examples/40-fm-tuner-rda5807/5651_tuner84_RDA5807M_datasheet_v1.pdf new file mode 100644 index 0000000..175b0d0 Binary files /dev/null and b/examples/40-fm-tuner-rda5807/5651_tuner84_RDA5807M_datasheet_v1.pdf differ diff --git a/examples/40-fm-tuner-rda5807/RDA5807MP_datasheet.pdf b/examples/40-fm-tuner-rda5807/RDA5807MP_datasheet.pdf new file mode 100644 index 0000000..d7700de Binary files /dev/null and b/examples/40-fm-tuner-rda5807/RDA5807MP_datasheet.pdf differ diff --git a/examples/40-fm-tuner-rda5807/RDA5807_01_RDS_TEST_01_SERIAL_MONITOR/RDA5807_01_RDS_TEST_01_SERIAL_MONITOR.ino b/examples/40-fm-tuner-rda5807/RDA5807_01_RDS_TEST_01_SERIAL_MONITOR/RDA5807_01_RDS_TEST_01_SERIAL_MONITOR.ino new file mode 100644 index 0000000..16947bf --- /dev/null +++ b/examples/40-fm-tuner-rda5807/RDA5807_01_RDS_TEST_01_SERIAL_MONITOR/RDA5807_01_RDS_TEST_01_SERIAL_MONITOR.ino @@ -0,0 +1,175 @@ +/* + + Test RDS functions using Serial Monitor. + This sketch is useful to check how the RDS work and how the local stations provide this service. + For example: I have noticed in my location that some stations send wrong time information. So, for these stations, + the RDS time functions implemented by this library seem do not work fine + + To use this aplication, please select the Arduino IDE Serial Monitor. + Type ? to see the instructions. + + + Arduino Pro Mini and RDA5807 wire up + + | Device RDA5807 | Arduino Pin | + | --------------- | ------------ | + | SDIO | A4 | + | SCLK | A5 | + + + ATTENTION: + Please, avoid using the computer connected to the mains during testing. Used just the battery of your computer. + This sketch was tested on ATmega328 based board. If you are not using a ATmega328, please check the pins of your board. + + By Ricardo Lima Caratti, 2023. +*/ + +#include + +#define MAX_DELAY_RDS 80 // polling method +#define MAX_DELAY_STATUS 5000 +#define MAX_DELAY_SHOW_RDS 250 + +#define STATION_WITH_RDS_SERVICE 103 // Local station with good RDS service (Example: 89,90Mhz) + +long rds_elapsed = millis(); +long status_elapsed = millis(); + +uint8_t showrRdsInfo = 3; // Default: show RDS time. + +RDA5807 rx; + +void setup() +{ + + Serial.begin(9600); + while (!Serial) + ; + Serial.println(F("\nPU2CLR RDA5807 Arduino Library.")); + + rx.setup(); + + rx.setRDS(true); // Turns RDS on + + rx.setVolume(6); + + delay(500); + + // Select a station with RDS service in your place + Serial.print(F("\nTuning at the FM local station with good RDS service (see: STATION_WITH_RDS_SERVICE")); + rx.setFrequency(STATION_WITH_RDS_SERVICE); + + // RDS setup + rx.setRDS(true); + rx.setRdsFifo(true); + rx.setLnaPortSel(3); // Trying improve sensitivity. + rx.setAFC(true); // Sets Automatic Frequency Control + + showHelp(); +} + +void showHelp() +{ + Serial.println(F("Type U to increase and D to decrease the frequency")); + Serial.println(F(" S or s to seek station Up or Down")); + Serial.println(F(" + or - to volume Up or Down")); + Serial.println(F(" 0 to show current status")); + Serial.println(F(" 1 to show RDS Station")); + Serial.println(F(" 2 to show RDS Message")); + Serial.println(F(" 3 to show RDS Time")); + Serial.println(F(" ? to this help.")); + Serial.println(F("==================================================")); + delay(5000); +} + +// Show current frequency +void showStatus() +{ + char aux[80]; + sprintf(aux, "\nYou are tuned on %u MHz | RSSI: %3.3u dbUv | Vol: %2.2u | Stereo: %s\n", rx.getFrequency(), rx.getRssi(), rx.getVolume(), (rx.isStereo()) ? "Yes" : "No"); + Serial.print(aux); + status_elapsed = millis(); +} + +/********************************************************* + RDS + *********************************************************/ +char *rdsMsg; +char *stationName; +char *rdsTime; + +void showRDS() +{ + if ( showrRdsInfo == 1) { + rdsMsg = rx.getRdsText2A(); + if ( rdsMsg != NULL ) Serial.println(rdsMsg); + } + else if ( showrRdsInfo == 2) { + stationName = rx.getRdsText0A(); + if (stationName != NULL) Serial.println(stationName); + } + else if ( showrRdsInfo == 3 ) { + rdsTime = rx.getRdsTime(); + if (rdsTime != NULL ) Serial.println(rdsTime); + } + delay(MAX_DELAY_SHOW_RDS); +} + +void loop() +{ + if ((millis() - rds_elapsed) > MAX_DELAY_RDS) + { + if (rx.getRdsReady() && rx.hasRdsInfo()) + showRDS(); + rds_elapsed = millis(); + } + + if ((millis() - status_elapsed) > MAX_DELAY_STATUS) + { + showStatus(); + status_elapsed = millis(); + } + + if (Serial.available() > 0) + { + char key = Serial.read(); + switch (key) + { + case '+': + rx.setVolumeUp(); + break; + case '-': + rx.setVolumeDown(); + break; + case 'U': + case 'u': + rx.setFrequencyUp(); + break; + case 'D': + case 'd': + rx.setFrequencyDown(); + break; + case 'S': + rx.seek(RDA_SEEK_WRAP, RDA_SEEK_UP, showStatus); + break; + case 's': + rx.seek(RDA_SEEK_WRAP, RDA_SEEK_DOWN, showStatus); + break; + case '1': + showrRdsInfo = 1; + break; + case '2': + showrRdsInfo = 2; + break; + case '3': + showrRdsInfo = 3; + break; + case '?': + showHelp(); + break; + default: + break; + } + showStatus(); + } +} diff --git a/examples/40-fm-tuner-rda5807/RDA5807_01_RDS_TEST_02_SERIAL_MONITOR/RDA5807_01_RDS_TEST_02_SERIAL_MONITOR.ino b/examples/40-fm-tuner-rda5807/RDA5807_01_RDS_TEST_02_SERIAL_MONITOR/RDA5807_01_RDS_TEST_02_SERIAL_MONITOR.ino new file mode 100644 index 0000000..8f09962 --- /dev/null +++ b/examples/40-fm-tuner-rda5807/RDA5807_01_RDS_TEST_02_SERIAL_MONITOR/RDA5807_01_RDS_TEST_02_SERIAL_MONITOR.ino @@ -0,0 +1,166 @@ +/* + + Test RDS functions using Serial Monitor. + This sketch is useful to check how the RDS work and how the local stations provide this service. + For example: I have noticed in my location that some stations send wrong time information. So, for these stations, + the RDS time functions implemented by this library seem do not work fine + + To use this aplication, please select the Arduino IDE Serial Monitor. + Type ? to see the instructions. + + + Arduino Pro Mini and RDA5807 wire up + + | Device RDA5807 | Arduino Pin | + | --------------- | ------------ | + | SDIO | A4 | + | SCLK | A5 | + + + ATTENTION: + Please, avoid using the computer connected to the mains during testing. Used just the battery of your computer. + This sketch was tested on ATmega328 based board. If you are not using a ATmega328, please check the pins of your board. + + By Ricardo Lima Caratti, 2023. +*/ + +#include + +#define MAX_DELAY_RDS 80 // polling method +#define MAX_DELAY_STATUS 50000 +#define MAX_DELAY_SHOW_RDS 80 + +#define STATION_WITH_RDS_SERVICE 8830 // Local station with good RDS service (Example: 89,90Mhz) + +long rds_elapsed = millis(); +long status_elapsed = millis(); + +uint8_t showrRdsInfo = 3; // Default: show RDS time. + +RDA5807 rx; + +void setup() { + + Serial.begin(9600); + while (!Serial) + ; + Serial.println(F("\nPU2CLR RDA5807 Arduino Library.")); + + rx.setup(); + + rx.setRDS(true); // Turns RDS on + + rx.setVolume(6); + + delay(500); + + // Select a station with RDS service in your place + Serial.print(F("\nTuning at the FM local station with good RDS service (see: STATION_WITH_RDS_SERVICE")); + rx.setFrequency(STATION_WITH_RDS_SERVICE); + + // RDS setup + rx.setRDS(true); + rx.setRdsFifo(true); + rx.setLnaPortSel(3); // Trying improve sensitivity. + rx.setAFC(true); // Sets Automatic Frequency Control + + showHelp(); +} + +void showHelp() { + Serial.println(F("Type U to increase and D to decrease the frequency")); + Serial.println(F(" S or s to seek station Up or Down")); + Serial.println(F(" + or - to volume Up or Down")); + Serial.println(F(" ? to this help.")); + Serial.println(F("==================================================")); + delay(5000); +} + +// Show current frequency +void showStatus() { + char aux[80]; + sprintf(aux, "\nYou are tuned on %u MHz | RSSI: %3.3u dbUv | Vol: %2.2u | Stereo: %s\n", rx.getFrequency(), rx.getRssi(), rx.getVolume(), (rx.isStereo()) ? "Yes" : "No"); + Serial.print(aux); + status_elapsed = millis(); +} + +/********************************************************* + RDS + *********************************************************/ +char *programInfo; +char *stationName; +char *stationInfo; +char *utcTime; +char *localTime; + +void showRdsInfo(char *infoType, char *infoText) { + char aux[120]; + //if (infoText != NULL) { + sprintf(aux, "%s => %s", infoType, infoText); + Serial.println(aux); + //} +} + +void showRDS() { + // Checks RDS information. Gets the points of char array containing RDS information. + if (rx.getRdsReady()) { + if (rx.hasRdsInfo()) { + programInfo = rx.getRdsProgramInformation(); + showRdsInfo((char *)"Progrgam Information: ", programInfo); + stationInfo = rx.getRdsStationInformation(); + showRdsInfo((char *)"Station Information: ", stationInfo); + stationName = rx.getRdsStationName(); + showRdsInfo((char *)"Station Name........: ", stationName); + utcTime = rx.getRdsTime(); + showRdsInfo((char *)"UTC Time............: ", utcTime); + delay(MAX_DELAY_SHOW_RDS); + localTime = rx.getRdsLocalTime(); + showRdsInfo((char *)"Local Time............: ", localTime); + Serial.println(); + } + } +} + +void loop() { + if ((millis() - rds_elapsed) > MAX_DELAY_RDS) { + showRDS(); + rds_elapsed = millis(); + } + + if ((millis() - status_elapsed) > MAX_DELAY_STATUS) { + showStatus(); + status_elapsed = millis(); + } + + if (Serial.available() > 0) { + char key = Serial.read(); + switch (key) { + case '+': + rx.setVolumeUp(); + break; + case '-': + rx.setVolumeDown(); + break; + case 'U': + case 'u': + rx.setFrequencyUp(); + break; + case 'D': + case 'd': + rx.setFrequencyDown(); + break; + case 'S': + rx.seek(RDA_SEEK_WRAP, RDA_SEEK_UP, showStatus); + break; + case 's': + rx.seek(RDA_SEEK_WRAP, RDA_SEEK_DOWN, showStatus); + break; + case '?': + showHelp(); + break; + default: + break; + } + showStatus(); + } +} diff --git a/examples/40-fm-tuner-rda5807/RDA5807_01_RDS_TEST_03_INTERRUPT_SERIAL_MONITOR/RDA5807_01_RDS_TEST_03_INTERRUPT_SERIAL_MONITOR.ino b/examples/40-fm-tuner-rda5807/RDA5807_01_RDS_TEST_03_INTERRUPT_SERIAL_MONITOR/RDA5807_01_RDS_TEST_03_INTERRUPT_SERIAL_MONITOR.ino new file mode 100644 index 0000000..7ae0e62 --- /dev/null +++ b/examples/40-fm-tuner-rda5807/RDA5807_01_RDS_TEST_03_INTERRUPT_SERIAL_MONITOR/RDA5807_01_RDS_TEST_03_INTERRUPT_SERIAL_MONITOR.ino @@ -0,0 +1,174 @@ +/* + + Test RDS functions using Serial Monitor. + This sketch is useful to check how the RDS and interrupt setup. + + To use this aplication, please select the Arduino IDE Serial Monitor. + Type ? to see the instructions. + + + Arduino Pro Mini and RDA5807 wire up + + | Device RDA5807 | Arduino Pin | Description | + | --------------- | ------------ | ----------------- | + | GPIO2 | 2 | Will tell to the system when a RDS action occurs | + | SDIO | A4 | I2C Data communication | + | SCLK | A5 | I2C Clock communication | + + + ATTENTION: + Please, avoid using the computer connected to the mains during testing. Used just the battery of your computer. + This sketch was tested on ATmega328 based board. If you are not using a ATmega328, please check the pins of your board. + + By Ricardo Lima Caratti, 2023. +*/ + +#include + +#define MAX_DELAY_STATUS 30000 // Defined time to update the receiver status information +#define RDS_ACTION_INTERRUPT 2 +#define STATION_WITH_RDS_SERVICE 8830 // Local station with good RDS service (89,90Mhz) +#define RDS_DELAY 1000 + + +long rds_elapsed = millis(); +long status_elapsed = millis(); +uint8_t showrRdsInfo = 3; // Default: show RDS time. + +volatile int rdsCount = 0; // + + +RDA5807 rx; + +void setup() { + Serial.begin(9600); + while (!Serial) + ; + Serial.println(F("\nPU2CLR RDA5807 Arduino Library.")); + Serial.println(F("\nRDA5807FP Device and RDS with Interrupt control via GPIO2")); + + attachInterrupt(digitalPinToInterrupt(RDS_ACTION_INTERRUPT), rdsAction, RISING); + + rx.setup(); + + rx.setLedStereoIndicator(true); // Out of the topic: Same: rx.setGpio(3, 1); // Just checking the GPIO3 - LED Stereo indicator setup + rx.setInterruptMode(1); // Sets interrupt on GPIO2 to deal with RDS. + + rx.setRDS(true); // Turns RDS on + rx.setRdsFifo(true); + + rx.setVolume(6); + delay(500); + // Select a station with RDS service in your place + Serial.print(F("\nTuning at the FM local station with good RDS service (see: STATION_WITH_RDS_SERVICE")); + rx.setFrequency(STATION_WITH_RDS_SERVICE); + // RDS setup + rx.setRDS(true); + rx.setRdsFifo(true); + rx.setLnaPortSel(3); // Trying improve sensitivity. + rx.setAFC(true); // Sets Automatic Frequency Control + showHelp(); +} + + +// Tells to the system that RDA5807FP has RDS information +void rdsAction() { + rdsCount++; +} + + +void showHelp() { + Serial.println(F("Type U to increase and D to decrease the frequency")); + Serial.println(F(" S or s to seek station Up or Down")); + Serial.println(F(" + or - to volume Up or Down")); + Serial.println(F(" ? to this help.")); + Serial.println(F("==================================================")); + delay(5000); +} + +// Show current frequency +void showStatus() { + char aux[80]; + sprintf(aux, "\nYou are tuned on %u MHz | RSSI: %3.3u dbUv | Vol: %2.2u | Stereo: %s\n", rx.getFrequency(), rx.getRssi(), rx.getVolume(), (rx.isStereo()) ? "Yes" : "No"); + Serial.print(aux); + status_elapsed = millis(); +} + +/********************************************************* + RDS + *********************************************************/ +char *programInfo; +char *stationName; +char *stationInfo; +char *utcTime; + +void showRdsInfo(char *infoType, char *infoText) { + char aux[120]; + if ( infoText != NULL ) { + sprintf(aux,"%s => %s", infoType, infoText); + Serial.println(aux); + } +} + +void showRDS() { + if (rx.getRdsAllData(&stationName, &stationInfo, &programInfo, &utcTime)) { + programInfo = rx.getRdsProgramInformation(); + showRdsInfo((char *) "Program Information: ", programInfo); + stationInfo = rx.getRdsStationInformation(); + showRdsInfo((char *) "Station Information: ", stationInfo); + stationName = rx.getRdsStationName(); + showRdsInfo((char *) "Station Name........: ", stationName); + utcTime = rx.getRdsTime(); + showRdsInfo((char *) "UTC Time............: ", utcTime); + } +} + +void loop() { + + // checks for any RDS action + + if ( rdsCount > 1 && (millis() - rds_elapsed) > RDS_DELAY ) { + showRDS(); // Consumes all RDS information + rds_elapsed = millis(); + rdsCount = 0; + } + + if ((millis() - status_elapsed) > MAX_DELAY_STATUS) { + showStatus(); + status_elapsed = millis(); + } + + if (Serial.available() > 0) { + char key = Serial.read(); + rx.clearRdsBuffer(); + switch (key) { + case '+': + rx.setVolumeUp(); + break; + case '-': + rx.setVolumeDown(); + break; + case 'U': + case 'u': + rx.setFrequencyUp(); + break; + case 'D': + case 'd': + rx.setFrequencyDown(); + break; + case 'S': + rx.seek(RDA_SEEK_WRAP, RDA_SEEK_UP, showStatus); + break; + case 's': + rx.seek(RDA_SEEK_WRAP, RDA_SEEK_DOWN, showStatus); + break; + case '?': + showHelp(); + break; + default: + break; + } + showStatus(); + } + delay(5); +} diff --git a/examples/40-fm-tuner-rda5807/RDS.pdf b/examples/40-fm-tuner-rda5807/RDS.pdf new file mode 100644 index 0000000..c57b3e1 Binary files /dev/null and b/examples/40-fm-tuner-rda5807/RDS.pdf differ diff --git a/examples/40-fm-tuner-rda5807/_steam_klub_test.py b/examples/40-fm-tuner-rda5807/_steam_klub_test.py new file mode 100644 index 0000000..0268676 --- /dev/null +++ b/examples/40-fm-tuner-rda5807/_steam_klub_test.py @@ -0,0 +1,123 @@ + +# https://101-things.readthedocs.io/en/latest/fm_radio.html +# https://github.com/wagiminator/ATtiny85-TinyFMRadio/blob/master/software/TinyFMRadio.ino +# https://github.com/pu2clr/RDA5807/tree/master/examples +# https://maker.pro/arduino/projects/simple-fm-radio-receiver-with-arduino-uno-and-rda5807m + +# https://github.com/franckinux/python-rd5807m/tree/master + + + + + + +# Micropython builtin modules +import time +from machine import Pin, SoftI2C, RTC, PWM +from math import floor +from dht import DHT11 + +# External modules +import ssd1306 # OLED +from bh1750 import BH1750 # Lux meter +from bmp180 import BMP180 # Pressure meter +from MPU6050 import MPU6050 # Accelerometer + gyroscope +import rda5807 # FM radio module + + +# DHT11: Data - 4 +# Temperature + Humidity +d = DHT11(Pin(4, Pin.IN, Pin.PULL_UP)) # The input mode and pull-up need to be set manually! + +# Piezo buzzer - 13 +buzzer = PWM(Pin(13, Pin.OUT), duty=0) # duty=0 prevents default waveform from starting immediately + +# PIR sensor: GPIO 14 +pir = Pin(14, Pin.IN) + +# Capacitive touch sensor: GPIO 12 +touch = Pin(12, Pin.IN) + +# I2C devices: SDA - 21, SCK - 22 +i2c = SoftI2C(sda=Pin(21), scl=Pin(22)) + + +# OLED +display = ssd1306.SSD1306_I2C(128, 32, i2c) # using default address 0x3C + +# Lux meter +bh1750 = BH1750(0x23, i2c) + +# Pressure meter +bmp180 = BMP180(i2c) +bmp180.oversample_sett = 2 +bmp180.baseline = 101325 + +# Accelero + gyroscope +mpu = MPU6050(i2c) + +# FM Radio module +radio = rda5807.Radio(i2c) +time.sleep(0.1) # Let the radio initialize!!! Without the sleep the module does not work! +# init with default settings +radio.set_volume(1) # 0-15 +radio.set_frequency_MHz(103.0) # Radio Krokodyl (Brno) +radio.mute(False) + +# Piezo buzzer 100ms beep +buzzer.freq(4095) +buzzer.duty(50) +time.sleep(0.1) +buzzer.duty(0) +buzzer.deinit() + + + +# Custom function definitions +def floatToStr(f: float): + return str(round(f, 2)) # Max 2 decimal digits + + + +# Main loop +while True: + display.fill(0) + + # Lux + pressure meter + PIR + Capacitive touch + display.text(floatToStr(bh1750.measurement)+' lx', 0, 0, 1) + display.text(floatToStr(bmp180.pressure/1000)+' kPa', 0, 8, 1) + display.text(floatToStr(bmp180.temperature)+' \'C', 0, 16, 1) + display.text('PIR: '+str(pir.value()), 0, 24, 1) + display.text('Touch: '+str(touch.value()), 56, 24, 1) + + # Accelerometer + #accel = mpu.read_accel_data() + #gyro = mpu.read_gyro_data() + #temp = mpu.read_temperature() + #Xstr = 'X % 3d.%02d % 3d.%02d' % (floor(accel["x"]), round(100*(accel["x"]%1)), floor(gyro["x"]), round(100*(gyro["x"]%1))) + #Ystr = 'Y % 3d.%02d % 3d.%02d' % (floor(accel["y"]), round(100*(accel["y"]%1)), floor(gyro["y"]), round(100*(gyro["y"]%1))) + #Zstr = 'Z % 3d.%02d % 3d.%02d' % (floor(accel["z"]), round(100*(accel["z"]%1)), floor(gyro["z"]), round(100*(gyro["z"]%1))) + #display.text(Xstr, 0, 0, 1) + #display.text(Ystr, 0, 8, 1) + #display.text(Zstr, 0, 16, 1) + #display.text('Temp '+floatToStr(temp)+' \'C', 0, 24, 1) + + # DHT11: Temperature + Humidity + #try: + # d.measure() + # display.text('DHT11', 0, 0, 1) + # display.text('Temp: '+str(d.temperature())+' \'C', 0, 10, 1) + # display.text('Humi: '+str(d.humidity())+' %', 0, 20, 1) + #except: + # display.text('DHT11 meas. fail', 0, 0, 1) + + # FM Radio + #display.text(str(radio.get_frequency_MHz())+' MHz', 0, 0, 1) + #radio.update_rds() + #print(radio.get_rds_block_group()) # How to decode RDS? + #time.sleep(0.5) + + + display.show() + time.sleep(0.01) + diff --git a/examples/40-fm-tuner-rda5807/rda5807.py b/examples/40-fm-tuner-rda5807/rda5807.py new file mode 100644 index 0000000..3362086 --- /dev/null +++ b/examples/40-fm-tuner-rda5807/rda5807.py @@ -0,0 +1,382 @@ +# https://github.com/dawsonjon/101Things/blob/master/19_fm_radio/main.py + +# _ ___ _ _____ _ _ +# / |/ _ \/ | |_ _| |__ (_)_ __ __ _ ___ +# | | | | | | | | | '_ \| | '_ \ / _` / __| +# | | |_| | | | | | | | | | | | | (_| \__ \ +# |_|\___/|_| |_| |_| |_|_|_| |_|\__, |___/ +# |___/ +# +# Copyright (c) Jonathan P Dawson 2024 +# filename: rda5807.py +# description: +# +# Python module to interface with RDA5807 FM radio device using I2C bus +# +# License: MIT +# + +from machine import RTC + +random_access_address = 17 + +#Register addresses +RDA5807M_REG_CHIPID = 0x00 +RDA5807M_REG_CONFIG = 0x02 +RDA5807M_REG_TUNING = 0x03 +RDA5807M_REG_GPIO = 0x04 +RDA5807M_REG_VOLUME = 0x05 +RDA5807M_REG_I2S = 0x06 +RDA5807M_REG_BLEND = 0x07 +RDA5807M_REG_FREQ = 0x08 +RDA5807M_REG_STATUS = 0x0A +RDA5807M_REG_RSSI = 0x0B +RDA5807M_REG_RDSA = 0x0C +RDA5807M_REG_RDSB = 0x0D +RDA5807M_REG_RDSC = 0x0E +RDA5807M_REG_RDSD = 0x0F +RDA5800_REG_LNA = 0x10 +RDA5807M_REG_SEEK = 0x20 + +#Flag bits (to the chip) +RDA5807M_FLG_DHIZ = 0x8000 +RDA5807M_FLG_DMUTE = 0x4000 +RDA5807M_FLG_MONO = 0x2000 +RDA5807M_FLG_BASS = 0x1000 +RDA5807M_FLG_RCLKNOCAL = 0x0800 +RDA5807M_FLG_RCLKDIRECT = 0x0400 +RDA5807M_FLG_SEEKUP = 0x0200 +RDA5807M_FLG_SEEK = 0x0100 +RDA5807M_FLG_SKMODE = 0x0080 +RDA5807M_FLG_RDS = 0x0008 +RDA5807M_FLG_NEW = 0x0004 +RDA5807M_FLG_RESET = 0x0002 +RDA5807M_FLG_ENABLE = 0x0001 +RDA5807M_FLG_DIRECT = 0x0020 +RDA5807M_FLG_TUNE = 0x0010 +RDA5807M_FLG_DE = 0x0800 +RDA5807M_FLG_SOFTMUTE = 0x0200 +RDA5807M_FLG_AFCD = 0x0100 +RDA5807P_FLG_INTMODE = 0x8000 +RDA5807M_FLG_EASTBAND65M = 0x0200 +RDA5807M_FLG_SOFTBLEND = 0x0002 +RDA5807M_FLG_FREQMODE = 0x0001 +RDA5807M_FLG_FMTRUE = 0x0100 +RDA5807M_FLG_FMREADY = 0x0080 +RDA5807M_FLG_BLOCKE = 0x0010 +RDA5807P_FLG_STCIEN = 0x4000 +RDA5807P_FLG_I2S = 0x0040 +RDA5807P_FLG_I2SSLAVE = 0x1000 +RDA5807P_FLG_SWLR = 0x0800 +RDA5807P_FLG_SCLKINVERT_I = 0x0400 +RDA5807P_FLG_SIGNED = 0x0200 +RDA5807P_FLG_WSINVERT_I = 0x0100 +RDA5807P_FLG_WSINVERT_O = 0x0008 +RDA5807P_FLG_SCLKINVERT_O = 0x0004 +RDA5807P_FLG_DELAY_L = 0x0002 +RDA5807P_FLG_DELAY_R = 0x0001 +RDA5800_FLG_SPACE_200K = 0x0001 +RDA5800_FLG_SPACE_50K = 0x0004 +RDA5800_FLG_BAND_JAPAN = 0x0002 + +rds_program_types_europe = [ +"No programme type defined", "News", "Current affairs", "Information", +"Sport", "Education", "Drama", "Culture", "Science", "Varied", +"Popular Music (Pop)", "Rock Music", "Easy Listening", "Light Classical", +"Serious Classical", "Other Music", "Weather", "Finance", "Children's Programmes", +"Social Affairs", "Religion", "Phone-in", "Travel", "Leisure", "Jazz Music", +"Country Music", "National Music", "Oldies Music", "Folk Music", "Documentary", +"Alarm Test","Alarm"] + +class Radio: + + """ Access RDA5807M Device """ + + def __init__(self, i2c, region="US/Europe", frequency_spacing_kHz=100): + + """ Configure RDA5807M Device + + arguments: + + i2c - an I2C bus object + region - "US/Europe" | "Japan" | "World Wide" | "East Europe" + frequency_spacing_kHz - 25 | 50 | 100 | 200 + + """ + + self.i2c = i2c + self.mute_flag = False + self.bass_boost_flag = True + self.mono = False + self.rtc = RTC() + + #read chip ID and check + data = self.read_reg(RDA5807M_REG_CHIPID) + if(data>>8 == 0x58): + print("Radio Found!") + + #configure radio + flags = RDA5807M_FLG_DHIZ | RDA5807M_FLG_BASS | RDA5807M_FLG_ENABLE | RDA5807M_FLG_NEW | RDA5807M_FLG_SEEKUP | RDA5807M_FLG_RDS + self.write_reg(RDA5807M_REG_CONFIG, 0b11000001 | RDA5807M_FLG_RESET) + self.write_reg(RDA5807M_REG_TUNING, 0x0) + self.write_reg(RDA5807M_REG_GPIO, 0x1a00) + self.write_reg(RDA5807M_REG_VOLUME, 0x880f) + self.write_reg(RDA5807M_REG_I2S, 0x0000) + self.write_reg(RDA5807M_REG_BLEND, 0x4202) + + self.write_reg(RDA5807M_REG_CONFIG, flags) + + if region == "US/Europe": + self.start_frequency_MHz = 87.0 + self.band = 0 + elif region == "Japan": + self.start_frequency_MHz = 76.0 + self.band = 1 + elif region == "World Wide": + self.start_frequency_MHz = 76.0 + self.band = 2 + elif region == "East Europe": + self.start_frequency_MHz = 65.0 + self.band = 3 + + self.frequency_spacing_MHz = frequency_spacing_kHz/1000.0 + if frequency_spacing_kHz == 100: + self.spacing = 0 + elif frequency_spacing_kHz == 200: + self.spacing = 1 + elif frequency_spacing_kHz == 50: + self.spacing = 2 + elif frequency_spacing_kHz == 25: + self.spacing = 3 + + self.write_reg(RDA5807M_REG_TUNING, 0x10 | (self.band << 2) | self.spacing) + self.clear_rds_data() + + def clear_rds_data(self): + + """ Clear RDS data, e.g. after retuning """ + + self.last_ab = 0 + self.station_name = [" " for i in range(8)] + self.station_name_buffer = [" " for i in range(8)] + self.radio_text = [" " for i in range(64)] + self.radio_text_buffer = [" " for i in range(64)] + self.last_offset = 0 + self.last_st_offset = 0 + self.hours = 0 + self.minutes = 0 + + def read_reg(self, reg): + + """ Read data from i2c register """ + + self.i2c.writeto(random_access_address, bytes([reg])) + data = self.i2c.readfrom(random_access_address, 2) + return (data[0] << 8) | data[1] + + def write_reg(self, reg, data): + + """ Write data to i2c register """ + + data = bytes([reg, data >> 8, data&0xff]) + self.i2c.writeto(random_access_address, data) + + def update_reg(self, reg, mask, value): + + """ Update specific bits in I2C register """ + + data = self.read_reg(reg) + data = (data & ~mask) | value + self.write_reg(reg, data) + + def set_volume(self, volume): + + """ Set volume 0 to 15 """ + + volume &= 0xf + self.update_reg(RDA5807M_REG_VOLUME, 0xf, volume) #bits 3:0 + + def get_volume(self): + + """ Get Volume 0 to 15 """ + + return self.read_reg(RDA5807M_REG_VOLUME) & 0xf #bits 3:0 + + def mute(self, mute): + + """ Mute True = mute False = Normal """ + + if not mute: + self.update_reg(RDA5807M_REG_CONFIG, RDA5807M_FLG_DMUTE, RDA5807M_FLG_DMUTE) + else: + self.update_reg(RDA5807M_REG_CONFIG, RDA5807M_FLG_DMUTE, 0) + self.mute_flag = mute + + def bass_boost(self, bass_boost): + + """ Bass Boost True = enable bass boost False = disable bass boost """ + + if bass_boost: + self.update_reg(RDA5807M_REG_CONFIG, RDA5807M_FLG_BASS, RDA5807M_FLG_BASS) + else: + self.update_reg(RDA5807M_REG_CONFIG, RDA5807M_FLG_BASS, 0) + self.bass_boost_flag = bass_boost + + def mono(self, mono): + + """ Force Mono True = force mono False = stereo """ + + if mono: + self.update_reg(RDA5807M_REG_CONFIG, RDA5807M_FLG_MONO, RDA5807M_FLG_MONO) + else: + self.update_reg(RDA5807M_REG_CONFIG, RDA5807M_FLG_MONO, 0) + self.mono_flag = mono + + def seek_up(self): + + """ Find next station (blocks until tuning completes) """ + + self.clear_rds_data() + self.update_reg(RDA5807M_REG_CONFIG, + (RDA5807M_FLG_SEEKUP | RDA5807M_FLG_SEEK), + (RDA5807M_FLG_SEEKUP | RDA5807M_FLG_SEEK)) + while 1: + data = self.read_reg(RDA5807M_REG_CONFIG) + if not data & RDA5807M_FLG_SEEK: + break + + def seek_down(self): + + """ Find previous station (blocks until tuning completes) """ + + self.clear_rds_data() + self.update_reg(RDA5807M_REG_CONFIG, + (RDA5807M_FLG_SEEKUP | RDA5807M_FLG_SEEK), + RDA5807M_FLG_SEEK) + while 1: + data = self.read_reg(RDA5807M_REG_CONFIG) + if not data & RDA5807M_FLG_SEEK: + break + + def get_frequency_MHz(self): + + """ Get tuned frequency in MHz """ + + frequency = self.start_frequency_MHz + ((self.read_reg(RDA5807M_REG_STATUS) & 0x3ff) * self.frequency_spacing_MHz) + return frequency + + def set_frequency_MHz(self, frequency_MHz): + + """ Set tuned frequency in MHz """ + + self.clear_rds_data() + frequency_steps = int((frequency_MHz - self.start_frequency_MHz)/self.frequency_spacing_MHz) + data = (frequency_steps << 6) | 0x10 | (self.band << 2) | self.spacing + self.write_reg(RDA5807M_REG_TUNING, data) + + def get_signal_strength(self): + + """ Recieved Signal Strength Indicator 0 = low, 7 = high (logarithmic)""" + + rssi = round(7*(self.read_reg(RDA5807M_REG_RSSI) >> 9)/127) + return rssi + + def get_rds_block_group(self): + + """ Read all 4 RDS blocks from device """ + + return self.read_reg(RDA5807M_REG_RDSA), self.read_reg(RDA5807M_REG_RDSB), self.read_reg(RDA5807M_REG_RDSC), self.read_reg(RDA5807M_REG_RDSD) + + def update_rds(self): + + """ Check for new RDS messages and decode if present + + Should be polled regularly so that we don't miss any. + Returns true if new data received. + + .station_name, .radio_text contain decoded data. + + machine RTC is updated upon reception of time/date messages""" + + if (self.read_reg(RDA5807M_REG_STATUS) & 0x8000): + + #check for uncorrectable errors + if (self.read_reg(RDA5807M_REG_RSSI) & 0x3) == 0x3: + return False + if (self.read_reg(RDA5807M_REG_RSSI) & 0xc) == 0xc: + return False + + a, b, c, d = self.get_rds_block_group() + + program_information = a + group_type = b >> 12 + group_version = (b >> 11) & 1 + traffic_program = (b >> 10) & 1 + program_type = (b >> 5) & 0x1f + + #station name + if group_type == 0: + offset = b & 0x3 + character_a = chr(d >> 8) + character_b = chr(d & 0xff) + + #Check multiple messages for consistency + self.station_name_buffer[offset*2] = character_a + self.station_name_buffer[(offset*2)+1] = character_b + if offset < self.last_st_offset: + self.station_name = self.station_name_buffer + self.last_st_offset = offset + + #radio text + elif group_type == 2 and group_version == 0: + offset = b & 0xf + + ab = (b >> 4) & 1 + character_a = c >> 8 + character_b = c & 0xff + character_c = d >> 8 + character_d = d & 0xff + if ab != self.last_ab: + self.clear_buffer = True + self.radio_text_buffer = [" " for i in range(64)] + self.last_ab = ab + self.radio_text_buffer[offset*4] = chr(character_a) + self.radio_text_buffer[(offset*4)+1] = chr(character_b) + self.radio_text_buffer[(offset*4)+2] = chr(character_c) + self.radio_text_buffer[(offset*4)+3] = chr(character_d) + + if offset < self.last_offset: + self.radio_text = self.radio_text_buffer + self.last_offset = offset + + #radio text type 2 + elif group_type == 2 and group_version == 1: + offset = b & 0xf + ab = (b >> 4) & 1 + character_c = d >> 8 + character_d = d & 0xff + if ab != self.last_ab: + self.radio_text_buffer = [" " for i in range(64)] + self.radio_text_buffer[(offset*2)+0] = chr(character_c) + self.radio_text_buffer[(offset*2)+1] = chr(character_d) + + if offset < self.last_offset: + self.radio_text = self.radio_text_buffer + self.last_offset = offset + + elif group_type == 4 and group_version == 0: + hours_utc = ((c & 1) << 4) | ((d & 0xf000) >> 12) + minutes = ((d & 0xfc0) >> 6) + utc_offset = (d & 0x1f) * 0.25 + utc_sign = d & 0x20 + + if utc_sign: + hours = hours_utc + utc_offset + else: + hours = hours_utc - utc_offset + try: + self.rtc.datetime((2000, 01, 01, 01, int(hours), int(minutes), 0, 0)) + except OSError: + pass + + return True diff --git a/examples/40-fm-tuner-rda5807/serial_example.py b/examples/40-fm-tuner-rda5807/serial_example.py new file mode 100644 index 0000000..0cb7dbc --- /dev/null +++ b/examples/40-fm-tuner-rda5807/serial_example.py @@ -0,0 +1,92 @@ +# _ ___ _ _____ _ _ +# / |/ _ \/ | |_ _| |__ (_)_ __ __ _ ___ +# | | | | | | | | | '_ \| | '_ \ / _` / __| +# | | |_| | | | | | | | | | | | | (_| \__ \ +# |_|\___/|_| |_| |_| |_|_|_| |_|\__, |___/ +# |___/ +# +# Copyright (c) Jonathan P Dawson 2024 +# filename: main.py +# description: +# +# RDA5807 example application +# +# Minimal example using serial interface +# +# License: MIT +# +import sys +import time +import rda5807 +from machine import Pin, I2C + +#disable power save mode to reduce regulator noise +psu_mode = Pin(23, Pin.OUT) +psu_mode.value(1) + +# configure radio module +radio_i2c=I2C(0, sda=Pin(21), scl=Pin(22), freq=400_000) +radio = rda5807.Radio(radio_i2c) +time.sleep_ms(1000) + +volume = 1 +mute = False +frequency = 88.3 + +radio.set_volume(volume) +radio.set_frequency_MHz(frequency) +radio.mute(mute) + +print("Pi Pico RDA5807 FM Radio Example") +print("press ? for help") +while(1): + # command = sys.stdin.read(1) + command = "?" + if command == "?": + print("Commands") + print("========") + print("") + print("? - help (this message)") + print(", - seek down") + print(". - seek up") + print("- - volume down") + print("= - volume up") + print("") + + rssi = radio.get_signal_strength() + print(rssi, "dBm") + time.sleep_ms(100) + frequency = radio.get_frequency_MHz() + print(frequency, "MHz") + time.sleep_ms(100) + radio.update_rds() + time.sleep_ms(100) + print(radio.station_name) + print(radio.radio_text) + + if command == ".": + print("seeking...") + radio.seek_up() + frequency = radio.get_frequency_MHz() + print(frequency, "MHz") + elif command == ",": + print("seeking...") + radio.seek_down() + frequency = radio.get_frequency_MHz() + print(frequency, "MHz") + elif command == "=": + if mute == True: + mute = False + radio.mute(mute) + elif volume < 15: + volume += 1 + radio.set_volume(volume) + elif command == "-": + if volume > 0: + volume -= 1 + radio.set_volume(volume) + elif mute == False: + mute = True + radio.mute(mute) + + time.sleep_ms(5000)