Difference between revisions of "LCDRadio ino"
(Created page with "=== Grove - I2C FM Receiver v1.1 | LCDRadio_ino | === === Code === <pre> </pre>") |
(→| Moniteur Sériel - Résultat |) |
||
| (16 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
| − | + | Grove - I2C FM Receiver v1.1 | LCDRadio_ino | | |
| + | === | LCDRadio_ino | === | ||
| + | 16 chars and 2 line display<br> | ||
| + | Serial.begin(57600)<br> | ||
| + | RotaryEncoder encoder(A2, A3)<br> | ||
| + | OneButton menuButton(A10, true)<br> | ||
| + | OneButton seekButton(A11, true)<br> | ||
| + | |||
| + | === | adaptations | === | ||
| + | il manquait | ||
| + | #include <LiquidCrystal_PCF8574.h> | ||
| + | #include <RotaryEncoder.h> | ||
| + | #include <OneButton.h> | ||
| + | |||
| + | https://github.com/mathertel/OneButton | ||
| + | https://github.com/mathertel/RotaryEncoder | ||
| + | |||
| + | choix du bon chip | ||
| + | uncomment | ||
| + | // RDA5807M radio; | ||
| + | comment | ||
| + | SI4703 radio; | ||
| + | |||
| + | LiquidCrystal_PCF8574 lcd(0x27); | ||
| + | |||
| + | A10 -> A2 & A11 -> A3 | ||
| + | OneButton menuButton(A2, true); | ||
| + | OneButton seekButton(A3, true); | ||
| + | |||
| + | // open the Serial port | ||
| + | Serial.begin(57600) | ||
| + | |||
| + | FREQ: 95.60 MHz radio lac | ||
| + | FREQ:102.60 MHz | ||
| + | FREQ: 96.20 MHz | ||
| + | FREQ: 97.70 MHz | ||
| + | FREQ: 98.50 MHz | ||
| + | FREQ:106.50 MHz | ||
| + | |||
| + | ? | ||
| + | f9560 | ||
=== Code === | === Code === | ||
<pre> | <pre> | ||
| + | /// | ||
| + | /// \file LCDRadio.ino | ||
| + | /// \brief Radio implementation including LCD output. | ||
| + | /// | ||
| + | /// \author Matthias Hertel, http://www.mathertel.de | ||
| + | /// \copyright Copyright (c) 2014 by Matthias Hertel.\n | ||
| + | /// This work is licensed under a BSD style license.\n | ||
| + | /// See http://www.mathertel.de/License.aspx | ||
| + | /// | ||
| + | /// \details | ||
| + | /// This is a full function radio implementation that uses a LCD display to show the current station information.\n | ||
| + | /// It can be used with various chips after adjusting the radio object definition.\n | ||
| + | /// Open the Serial console with 57600 baud to see current radio information and change various settings. | ||
| + | /// | ||
| + | /// Wiring | ||
| + | /// ------ | ||
| + | /// The necessary wiring of the various chips are described in the Testxxx example sketches. | ||
| + | /// The boards have to be connected by using the following connections: | ||
| + | /// | ||
| + | /// Arduino port | SI4703 signal | RDA5807M signal | ||
| + | /// :----------: | :-----------: | :-------------: | ||
| + | /// GND (black) | GND | GND | ||
| + | /// 3.3V (red) | VCC | VCC | ||
| + | /// 5V (red) | - | - | ||
| + | /// A5 (yellow) | SCLK | SCLK | ||
| + | /// A4 (blue) | SDIO | SDIO | ||
| + | /// D2 | RST | - | ||
| + | /// | ||
| + | /// More documentation and source code is available at http://www.mathertel.de/Arduino | ||
| + | /// | ||
| + | /// History: | ||
| + | /// -------- | ||
| + | /// * 05.08.2014 created. | ||
| + | /// * 06.10.2014 working. | ||
| + | |||
| + | |||
| + | |||
| + | #include <Wire.h> | ||
| + | |||
| + | #include <radio.h> | ||
| + | #include <RDA5807M.h> | ||
| + | #include <SI4703.h> | ||
| + | #include <TEA5767.h> | ||
| + | |||
| + | #include <RDSParser.h> | ||
| + | |||
| + | #include <LiquidCrystal_PCF8574.h> | ||
| + | |||
| + | #include <RotaryEncoder.h> | ||
| + | #include <OneButton.h> | ||
| + | |||
| + | // Define some stations available at your locations here: | ||
| + | // 89.40 MHz as 8940 | ||
| + | |||
| + | // RADIO_FREQ preset[] = {8850, 8930, 9320,9350, 9450,9570, 9680, 9880, 10030, 10260, 10400, 10500, 10600,10650,10800}; | ||
| + | |||
| + | RADIO_FREQ preset[] = { | ||
| + | 8770, | ||
| + | 8810, // hr1 | ||
| + | 8820, | ||
| + | 8850, // Bayern2 | ||
| + | 8890, // ??? | ||
| + | 8930, // * hr3 | ||
| + | 8980, | ||
| + | 9180, | ||
| + | 9220, 9350, | ||
| + | 9440, // * hr1 | ||
| + | 9510, // - Antenne Frankfurt | ||
| + | 9530, | ||
| + | 9560, // Bayern 1 | ||
| + | 9680, 9880, | ||
| + | 10020, // planet | ||
| + | 10090, // ffh | ||
| + | 10110, // SWR3 | ||
| + | 10030, 10260, 10380, 10400, | ||
| + | 10500 // * FFH | ||
| + | }; | ||
| + | // , 10650,10650,10800 | ||
| + | |||
| + | int i_sidx=5; // Start at Station with index=5 | ||
| + | int i_smax=14; // Max Index of Stations | ||
| + | |||
| + | /// Setup a RoraryEncoder for pins A2 and A3: | ||
| + | RotaryEncoder encoder(A2, A3); | ||
| + | // RotaryEncoder encoder(A9, A8); | ||
| + | |||
| + | int encoderLastPos; | ||
| + | unsigned long encoderLastTime; | ||
| + | |||
| + | |||
| + | // variables for rotator encoder | ||
| + | //unsigned long lastRotatorChange = 0; // last time a change of the rotator was detected. | ||
| + | |||
| + | /// The radio object has to be defined by using the class corresponding to the used chip. | ||
| + | /// by uncommenting the right radio object definition. | ||
| + | |||
| + | // RADIO radio; // Create an instance of a non functional radio. | ||
| + | RDA5807M radio; // Create an instance of a RDA5807 chip radio | ||
| + | // SI4703 radio; // Create an instance of a SI4703 chip radio. | ||
| + | // TEA5767 radio; // Create an instance of a TEA5767 chip radio. | ||
| + | |||
| + | /// The lcd object has to be defined by using a LCD library that supports the standard functions | ||
| + | /// When using a I2C->LCD library ??? the I2C bus can be used to control then radio chip and the lcd. | ||
| + | |||
| + | /// get a LCD instance | ||
| + | LiquidCrystal_PCF8574 lcd(0x27); // set the LCD address to 0x27 for a 16 chars and 2 line display | ||
| + | |||
| + | OneButton menuButton(A10, true); | ||
| + | OneButton seekButton(A11, true); | ||
| + | |||
| + | |||
| + | /// get a RDS parser | ||
| + | RDSParser rds; | ||
| + | |||
| + | |||
| + | /// State definition for this radio implementation. | ||
| + | enum RADIO_STATE { | ||
| + | STATE_NONE = 0, | ||
| + | STATE_PARSECOMMAND, ///< waiting for a new command character. | ||
| + | STATE_PARSEINT, ///< waiting for digits for the parameter. | ||
| + | STATE_EXEC, ///< executing the command. | ||
| + | |||
| + | STATE_FREQ, | ||
| + | STATE_VOL, | ||
| + | STATE_MONO, | ||
| + | STATE_SMUTE | ||
| + | |||
| + | }; | ||
| + | |||
| + | RADIO_STATE state; ///< The state variable is used for parsing input characters. | ||
| + | RADIO_STATE rot_state; | ||
| + | |||
| + | // - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
| + | |||
| + | |||
| + | /// Update the Frequency on the LCD display. | ||
| + | void DisplayFrequency(RADIO_FREQ f) | ||
| + | { | ||
| + | char s[12]; | ||
| + | radio.formatFrequency(s, sizeof(s)); | ||
| + | Serial.print("FREQ:"); Serial.println(s); | ||
| + | lcd.setCursor(0, 0); | ||
| + | lcd.print(s); | ||
| + | } // DisplayFrequency() | ||
| + | |||
| + | |||
| + | /// Update the ServiceName text on the LCD display when in RDS mode. | ||
| + | void DisplayServiceName(char *name) | ||
| + | { | ||
| + | Serial.print("RDS:"); Serial.println(name); | ||
| + | if (rot_state == STATE_FREQ) { | ||
| + | lcd.setCursor(0, 1); | ||
| + | lcd.print(name); | ||
| + | } | ||
| + | } // DisplayServiceName() | ||
| + | |||
| + | |||
| + | void DisplayTime(uint8_t hour, uint8_t minute) { | ||
| + | Serial.print("RDS-Time:"); | ||
| + | if (hour < 10) Serial.print('0'); | ||
| + | Serial.print(hour); | ||
| + | Serial.print(':'); | ||
| + | if (minute < 10) Serial.print('0'); | ||
| + | Serial.print(minute); | ||
| + | } // DisplayTime() | ||
| + | |||
| + | |||
| + | /// Display the current volume. | ||
| + | void DisplayVolume(uint8_t v) | ||
| + | { | ||
| + | Serial.print("VOL: "); Serial.println(v); | ||
| + | |||
| + | lcd.setCursor(0, 1); | ||
| + | lcd.print("VOL: "); lcd.print(v); | ||
| + | } // DisplayVolume() | ||
| + | |||
| + | |||
| + | /// Display the current mono switch. | ||
| + | void DisplayMono(uint8_t v) | ||
| + | { | ||
| + | Serial.print("MONO: "); Serial.println(v); | ||
| + | lcd.setCursor(0, 1); | ||
| + | lcd.print("MONO: "); lcd.print(v); | ||
| + | } // DisplayMono() | ||
| + | |||
| + | |||
| + | /// Display the current soft mute switch. | ||
| + | void DisplaySoftMute(uint8_t v) | ||
| + | { | ||
| + | Serial.print("SMUTE: "); Serial.println(v); | ||
| + | lcd.setCursor(0, 1); | ||
| + | lcd.print("SMUTE: "); lcd.print(v); | ||
| + | } // DisplaySoftMute() | ||
| + | |||
| + | |||
| + | // - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
| + | |||
| + | |||
| + | void RDS_process(uint16_t block1, uint16_t block2, uint16_t block3, uint16_t block4) { | ||
| + | rds.processData(block1, block2, block3, block4); | ||
| + | } | ||
| + | |||
| + | |||
| + | // this function will be called when the menuButton was clicked | ||
| + | void doMenuClick() { | ||
| + | unsigned long now = millis(); | ||
| + | |||
| + | if (rot_state == STATE_FREQ) { | ||
| + | // jump into volume mode | ||
| + | rot_state = STATE_VOL; | ||
| + | encoderLastPos = radio.getVolume(); | ||
| + | encoder.setPosition(encoderLastPos); | ||
| + | DisplayVolume(encoderLastPos); | ||
| + | |||
| + | } | ||
| + | else if (rot_state == STATE_VOL) { | ||
| + | // jump into mono/stereo switch | ||
| + | rot_state = STATE_MONO; | ||
| + | encoderLastPos = radio.getMono(); | ||
| + | encoder.setPosition(encoderLastPos); | ||
| + | DisplayMono(encoderLastPos); | ||
| + | |||
| + | } | ||
| + | else if (rot_state == STATE_MONO) { | ||
| + | // jump into soft mute switch | ||
| + | rot_state = STATE_SMUTE; | ||
| + | encoderLastPos = radio.getSoftMute(); | ||
| + | encoder.setPosition(encoderLastPos); | ||
| + | DisplaySoftMute(encoderLastPos); | ||
| + | |||
| + | } | ||
| + | else if (rot_state == STATE_SMUTE) { | ||
| + | rot_state = STATE_FREQ; | ||
| + | encoderLastPos = (radio.getFrequency() - radio.getMinFrequency()) / radio.getFrequencyStep(); | ||
| + | encoder.setPosition(encoderLastPos); | ||
| + | DisplayServiceName("..."); | ||
| + | |||
| + | } // if | ||
| + | encoderLastTime = now; | ||
| + | |||
| + | } // doMenuClick() | ||
| + | |||
| + | |||
| + | // this function will be called when the seekButton was clicked | ||
| + | void doSeekClick() { | ||
| + | Serial.println("SEEK..."); | ||
| + | radio.seekUp(true); | ||
| + | } // doSeekClick() | ||
| + | |||
| + | |||
| + | // The Interrupt Service Routine for Pin Change Interrupts | ||
| + | // On Arduino UNO you can use the PCINT1 interrupt vector that covers digital value changes on A2 and A3. | ||
| + | // On Arduino Mega 2560 you can use the PCINT2 interrupt vector that covers digital value changes on A8 and A9. | ||
| + | // Read http://www.atmel.com/Images/doc8468.pdf for more details on external interrupts. | ||
| + | |||
| + | ISR(PCINT2_vect) { | ||
| + | encoder.tick(); // just call tick() to check the state. | ||
| + | } // ISR for PCINT2 | ||
| + | |||
| + | |||
| + | /// Setup a FM only radio configuration with I/O for commands and debugging on the Serial port. | ||
| + | void setup() { | ||
| + | // open the Serial port | ||
| + | Serial.begin(57600); | ||
| + | |||
| + | // Initialize the lcd | ||
| + | lcd.begin(16, 2); | ||
| + | lcd.setBacklight(1); | ||
| + | lcd.print("Radio..."); | ||
| + | |||
| + | delay(800); | ||
| + | lcd.clear(); | ||
| + | |||
| + | // Initialize the Radio | ||
| + | radio.init(); | ||
| + | |||
| + | // Enable information to the Serial port | ||
| + | radio.debugEnable(); | ||
| + | |||
| + | // radio.setBandFrequency(RADIO_BAND_FM, 8930); // hr3 | ||
| + | radio.setBandFrequency(RADIO_BAND_FM, preset[i_sidx]); // 5. preset. | ||
| + | // radio.setFrequency(10140); // Radio BOB // preset[i_sidx] | ||
| + | |||
| + | // Setup rotary encoder | ||
| + | |||
| + | // You may have to modify the next 2 lines if using other pins than A2 and A3 | ||
| + | // On Arduino-Uno: rotary encoder in A2(PCINT10) and A3(PCINT11) | ||
| + | // PCICR |= (1 << PCIE1); // This enables Pin Change Interrupt 1 that covers the Analog input pins or Port C. | ||
| + | // PCMSK1 |= (1 << PCINT10) | (1 << PCINT11); // This enables the interrupt for pin 2 and 3 of Port C. | ||
| + | |||
| + | // On Arduino-MEGA2560: A8(PCINT16) and A9(PCINT17) for interrupt vector PCINT2 | ||
| + | PCICR |= (1 << PCIE2); | ||
| + | PCMSK2 |= (1 << PCINT16) | (1 << PCINT17); | ||
| + | |||
| + | encoderLastPos = (radio.getFrequency() - radio.getMinFrequency()) / radio.getFrequencyStep(); | ||
| + | Serial.println(encoderLastPos); | ||
| + | encoder.setPosition(encoderLastPos); | ||
| + | |||
| + | // Setup the buttons | ||
| + | // link the doMenuClick function to be called on a click event. | ||
| + | menuButton.attachClick(doMenuClick); | ||
| + | |||
| + | // link the doSeekClick function to be called on a click event. | ||
| + | seekButton.attachClick(doSeekClick); | ||
| + | |||
| + | delay(100); | ||
| + | |||
| + | radio.setMono(false); | ||
| + | radio.setMute(false); | ||
| + | // radio.debugRegisters(); | ||
| + | |||
| + | Serial.write('>'); | ||
| + | |||
| + | state = STATE_PARSECOMMAND; | ||
| + | rot_state = STATE_NONE; | ||
| + | |||
| + | // setup the information chain for RDS data. | ||
| + | radio.attachReceiveRDS(RDS_process); | ||
| + | |||
| + | rds.attachServicenNameCallback(DisplayServiceName); | ||
| + | |||
| + | rds.attachTimeCallback(DisplayTime); | ||
| + | |||
| + | } // Setup | ||
| + | |||
| + | |||
| + | /// Execute a command identified by a character and an optional number. | ||
| + | /// See the "?" command for available commands. | ||
| + | /// \param cmd The command character. | ||
| + | /// \param value An optional parameter for the command. | ||
| + | void runCommand(char cmd, int16_t value) | ||
| + | { | ||
| + | if (cmd == '?') { | ||
| + | Serial.println(); | ||
| + | Serial.println("? Help"); | ||
| + | Serial.println("+ increase volume"); | ||
| + | Serial.println("- decrease volume"); | ||
| + | Serial.println("> next preset"); | ||
| + | Serial.println("< previous preset"); | ||
| + | Serial.println(". scan up : scan up to next sender"); | ||
| + | Serial.println(", scan down ; scan down to next sender"); | ||
| + | Serial.println("fnnnnn: direct frequency input"); | ||
| + | Serial.println("i station status"); | ||
| + | Serial.println("s mono/stereo mode"); | ||
| + | Serial.println("b bass boost"); | ||
| + | Serial.println("m mute/unmute"); | ||
| + | Serial.println("u soft mute/unmute"); | ||
| + | } | ||
| + | |||
| + | // ----- control the volume and audio output ----- | ||
| + | |||
| + | else if (cmd == '+') { | ||
| + | // increase volume | ||
| + | int v = radio.getVolume(); | ||
| + | if (v < 15) radio.setVolume(++v); | ||
| + | } | ||
| + | else if (cmd == '-') { | ||
| + | // decrease volume | ||
| + | int v = radio.getVolume(); | ||
| + | if (v > 0) radio.setVolume(--v); | ||
| + | } | ||
| + | |||
| + | else if (cmd == 'm') { | ||
| + | // toggle mute mode | ||
| + | radio.setMute(! radio.getMute()); | ||
| + | } | ||
| + | |||
| + | else if (cmd == 'u') { | ||
| + | // toggle soft mute mode | ||
| + | radio.setSoftMute(! radio.getSoftMute()); | ||
| + | } | ||
| + | |||
| + | // toggle stereo mode | ||
| + | else if (cmd == 's') { radio.setMono(! radio.getMono()); } | ||
| + | |||
| + | // toggle bass boost | ||
| + | else if (cmd == 'b') { radio.setBassBoost(! radio.getBassBoost()); } | ||
| + | |||
| + | // ----- control the frequency ----- | ||
| + | |||
| + | else if (cmd == '>') { | ||
| + | // next preset | ||
| + | if (i_sidx < (sizeof(preset) / sizeof(RADIO_FREQ))-1) { | ||
| + | i_sidx++; radio.setFrequency(preset[i_sidx]); | ||
| + | } // if | ||
| + | } | ||
| + | else if (cmd == '<') { | ||
| + | // previous preset | ||
| + | if (i_sidx > 0) { | ||
| + | i_sidx--; | ||
| + | radio.setFrequency(preset[i_sidx]); | ||
| + | } // if | ||
| + | |||
| + | } | ||
| + | else if (cmd == 'f') { radio.setFrequency(value); } | ||
| + | |||
| + | else if (cmd == '.') { radio.seekUp(false); } | ||
| + | else if (cmd == ':') { radio.seekUp(true); } | ||
| + | else if (cmd == ',') { radio.seekDown(false); } | ||
| + | else if (cmd == ';') { radio.seekDown(true); } | ||
| + | |||
| + | |||
| + | // not in help: | ||
| + | else if (cmd == '!') { | ||
| + | if (value == 0) radio.term(); | ||
| + | if (value == 1) radio.init(); | ||
| + | |||
| + | } | ||
| + | else if (cmd == 'i') { | ||
| + | char s[12]; | ||
| + | radio.formatFrequency(s, sizeof(s)); | ||
| + | Serial.print("Station:"); Serial.println(s); | ||
| + | Serial.print("Radio:"); radio.debugRadioInfo(); | ||
| + | Serial.print("Audio:"); radio.debugAudioInfo(); | ||
| + | |||
| + | // Serial.print(" RSSI: "); | ||
| + | // Serial.print(info.rssi); | ||
| + | // | ||
| + | // for (uint8_t i = 0; i < info.rssi - 15; i+=2) { Serial.write('*'); } // Empfangspegel ab 15. Zeichen | ||
| + | // Serial.println(); | ||
| + | } // info | ||
| + | // else if (cmd == 'n') { radio.debugScan(); } | ||
| + | else if (cmd == 'x') { radio.debugStatus(); } | ||
| + | |||
| + | |||
| + | } // runCommand() | ||
| + | |||
| + | |||
| + | /// Constantly check for serial input commands and trigger command execution. | ||
| + | void loop() { | ||
| + | int newPos; | ||
| + | unsigned long now = millis(); | ||
| + | static unsigned long nextFreqTime = 0; | ||
| + | static unsigned long nextRadioInfoTime = 0; | ||
| + | |||
| + | // some internal static values for parsing the input | ||
| + | static char command; | ||
| + | static int16_t value; | ||
| + | static RADIO_FREQ lastf = 0; | ||
| + | RADIO_FREQ f = 0; | ||
| + | char c; | ||
| + | |||
| + | // check for the menuButton | ||
| + | menuButton.tick(); | ||
| + | |||
| + | if (Serial.available() > 0) { | ||
| + | // read the next char from input. | ||
| + | c = Serial.peek(); | ||
| + | |||
| + | if ((state == STATE_PARSECOMMAND) && (c < 0x20)) { | ||
| + | // ignore unprintable chars | ||
| + | Serial.read(); | ||
| + | |||
| + | } | ||
| + | else if (state == STATE_PARSECOMMAND) { | ||
| + | // read a command. | ||
| + | command = Serial.read(); | ||
| + | state = STATE_PARSEINT; | ||
| + | |||
| + | } | ||
| + | else if (state == STATE_PARSEINT) { | ||
| + | if ((c >= '0') && (c <= '9')) { | ||
| + | // build up the value. | ||
| + | c = Serial.read(); | ||
| + | value = (value * 10) + (c - '0'); | ||
| + | } | ||
| + | else { | ||
| + | // not a value -> execute | ||
| + | runCommand(command, value); | ||
| + | command = ' '; | ||
| + | state = STATE_PARSECOMMAND; | ||
| + | value = 0; | ||
| + | } // if | ||
| + | } // if | ||
| + | } // if | ||
| + | |||
| + | |||
| + | // check for the rotary encoder | ||
| + | newPos = encoder.getPosition(); | ||
| + | if (newPos != encoderLastPos) { | ||
| + | |||
| + | if (rot_state == STATE_FREQ) { | ||
| + | RADIO_FREQ f = radio.getMinFrequency() + (newPos * radio.getFrequencyStep()); | ||
| + | radio.setFrequency(f); | ||
| + | encoderLastPos = newPos; | ||
| + | nextFreqTime = now + 10; | ||
| + | |||
| + | } | ||
| + | else if (rot_state == STATE_VOL) { | ||
| + | radio.setVolume(newPos); | ||
| + | encoderLastPos = newPos; | ||
| + | DisplayVolume(newPos); | ||
| + | |||
| + | } | ||
| + | else if (rot_state == STATE_MONO) { | ||
| + | radio.setMono(newPos & 0x01); | ||
| + | encoderLastPos = newPos; | ||
| + | DisplayMono(newPos & 0x01); | ||
| + | |||
| + | } | ||
| + | else if (rot_state == STATE_SMUTE) { | ||
| + | radio.setSoftMute(newPos & 0x01); | ||
| + | encoderLastPos = newPos; | ||
| + | DisplaySoftMute(newPos & 0x01); | ||
| + | |||
| + | } // if | ||
| + | encoderLastTime = now; | ||
| + | |||
| + | } | ||
| + | else if (now > encoderLastTime + 2000) { | ||
| + | // fall into FREQ + RDS mode | ||
| + | rot_state = STATE_FREQ; | ||
| + | encoderLastPos = (radio.getFrequency() - radio.getMinFrequency()) / radio.getFrequencyStep(); | ||
| + | encoder.setPosition(encoderLastPos); | ||
| + | |||
| + | } // if | ||
| + | |||
| + | // check for RDS data | ||
| + | radio.checkRDS(); | ||
| + | |||
| + | // update the display from time to time | ||
| + | if (now > nextFreqTime) { | ||
| + | f = radio.getFrequency(); | ||
| + | if (f != lastf) { | ||
| + | // don't display a Service Name while frequency is no stable. | ||
| + | DisplayServiceName(" "); | ||
| + | DisplayFrequency(f); | ||
| + | lastf = f; | ||
| + | } // if | ||
| + | nextFreqTime = now + 400; | ||
| + | } // if | ||
| + | |||
| + | if (now > nextRadioInfoTime) { | ||
| + | RADIO_INFO info; | ||
| + | radio.getRadioInfo(&info); | ||
| + | lcd.setCursor(14, 0); | ||
| + | lcd.print(info.rssi); | ||
| + | nextRadioInfoTime = now + 1000; | ||
| + | } // update | ||
| + | |||
| + | } // loop | ||
| + | |||
| + | // End. | ||
| + | |||
| + | |||
| + | </pre> | ||
| + | === | Fréquence Radio, La bande FM | === | ||
| + | |||
| + | http://www.emetteurs.ch/wiki/index.php/La_bande_FM_et_DAB%2B_%C3%A0_Lausanne | ||
| + | |||
| + | <pre> | ||
| + | 8810, // SRF 1 | ||
| + | 10260, // RTS LA PREMIERE | ||
| + | 10330, // LFM | ||
| + | 10360, // NRJ Léman | ||
| + | 10500, // NOSTALGIE LEMAN | ||
| + | 10650, // ROUGE FM | ||
| + | 10720, // ONE FM | ||
| + | 8880, // RADIO 74 | ||
| + | 9010, // VIRGIN LEMAN | ||
| + | 9160, // RTS LA PREMIERE | ||
| + | 9270, // RADIO ORIENT | ||
| + | 9300, // PLUS | ||
| + | 9380, // RADIO ZONE | ||
| + | 9470, // RTS LA PREMIERE | ||
| + | 9560, // RADIO LAC | ||
| + | 9620, // RTS ESPACE 2 | ||
| + | 9780, // RSI RETE UNO | ||
| + | 9850, // RTS COULEUR 3 | ||
</pre> | </pre> | ||
| + | |||
| + | === | Moniteur Sériel - Résultat | === | ||
| + | |||
| + | [[File:COM6_UNO.JPG |600px]] | ||
| + | |||
| + | [[File:Moniteur_Serial_Radio_FM.jpg|500px]] | ||
Latest revision as of 21:19, 26 August 2018
Grove - I2C FM Receiver v1.1 | LCDRadio_ino |
Contents
| LCDRadio_ino |
16 chars and 2 line display
Serial.begin(57600)
RotaryEncoder encoder(A2, A3)
OneButton menuButton(A10, true)
OneButton seekButton(A11, true)
| adaptations |
il manquait
- include <LiquidCrystal_PCF8574.h>
- include <RotaryEncoder.h>
- include <OneButton.h>
https://github.com/mathertel/OneButton https://github.com/mathertel/RotaryEncoder
choix du bon chip uncomment // RDA5807M radio; comment SI4703 radio;
LiquidCrystal_PCF8574 lcd(0x27);
A10 -> A2 & A11 -> A3 OneButton menuButton(A2, true); OneButton seekButton(A3, true);
// open the Serial port Serial.begin(57600)
FREQ: 95.60 MHz radio lac FREQ:102.60 MHz FREQ: 96.20 MHz FREQ: 97.70 MHz FREQ: 98.50 MHz FREQ:106.50 MHz
? f9560
Code
///
/// \file LCDRadio.ino
/// \brief Radio implementation including LCD output.
///
/// \author Matthias Hertel, http://www.mathertel.de
/// \copyright Copyright (c) 2014 by Matthias Hertel.\n
/// This work is licensed under a BSD style license.\n
/// See http://www.mathertel.de/License.aspx
///
/// \details
/// This is a full function radio implementation that uses a LCD display to show the current station information.\n
/// It can be used with various chips after adjusting the radio object definition.\n
/// Open the Serial console with 57600 baud to see current radio information and change various settings.
///
/// Wiring
/// ------
/// The necessary wiring of the various chips are described in the Testxxx example sketches.
/// The boards have to be connected by using the following connections:
///
/// Arduino port | SI4703 signal | RDA5807M signal
/// :----------: | :-----------: | :-------------:
/// GND (black) | GND | GND
/// 3.3V (red) | VCC | VCC
/// 5V (red) | - | -
/// A5 (yellow) | SCLK | SCLK
/// A4 (blue) | SDIO | SDIO
/// D2 | RST | -
///
/// More documentation and source code is available at http://www.mathertel.de/Arduino
///
/// History:
/// --------
/// * 05.08.2014 created.
/// * 06.10.2014 working.
#include <Wire.h>
#include <radio.h>
#include <RDA5807M.h>
#include <SI4703.h>
#include <TEA5767.h>
#include <RDSParser.h>
#include <LiquidCrystal_PCF8574.h>
#include <RotaryEncoder.h>
#include <OneButton.h>
// Define some stations available at your locations here:
// 89.40 MHz as 8940
// RADIO_FREQ preset[] = {8850, 8930, 9320,9350, 9450,9570, 9680, 9880, 10030, 10260, 10400, 10500, 10600,10650,10800};
RADIO_FREQ preset[] = {
8770,
8810, // hr1
8820,
8850, // Bayern2
8890, // ???
8930, // * hr3
8980,
9180,
9220, 9350,
9440, // * hr1
9510, // - Antenne Frankfurt
9530,
9560, // Bayern 1
9680, 9880,
10020, // planet
10090, // ffh
10110, // SWR3
10030, 10260, 10380, 10400,
10500 // * FFH
};
// , 10650,10650,10800
int i_sidx=5; // Start at Station with index=5
int i_smax=14; // Max Index of Stations
/// Setup a RoraryEncoder for pins A2 and A3:
RotaryEncoder encoder(A2, A3);
// RotaryEncoder encoder(A9, A8);
int encoderLastPos;
unsigned long encoderLastTime;
// variables for rotator encoder
//unsigned long lastRotatorChange = 0; // last time a change of the rotator was detected.
/// The radio object has to be defined by using the class corresponding to the used chip.
/// by uncommenting the right radio object definition.
// RADIO radio; // Create an instance of a non functional radio.
RDA5807M radio; // Create an instance of a RDA5807 chip radio
// SI4703 radio; // Create an instance of a SI4703 chip radio.
// TEA5767 radio; // Create an instance of a TEA5767 chip radio.
/// The lcd object has to be defined by using a LCD library that supports the standard functions
/// When using a I2C->LCD library ??? the I2C bus can be used to control then radio chip and the lcd.
/// get a LCD instance
LiquidCrystal_PCF8574 lcd(0x27); // set the LCD address to 0x27 for a 16 chars and 2 line display
OneButton menuButton(A10, true);
OneButton seekButton(A11, true);
/// get a RDS parser
RDSParser rds;
/// State definition for this radio implementation.
enum RADIO_STATE {
STATE_NONE = 0,
STATE_PARSECOMMAND, ///< waiting for a new command character.
STATE_PARSEINT, ///< waiting for digits for the parameter.
STATE_EXEC, ///< executing the command.
STATE_FREQ,
STATE_VOL,
STATE_MONO,
STATE_SMUTE
};
RADIO_STATE state; ///< The state variable is used for parsing input characters.
RADIO_STATE rot_state;
// - - - - - - - - - - - - - - - - - - - - - - - - - -
/// Update the Frequency on the LCD display.
void DisplayFrequency(RADIO_FREQ f)
{
char s[12];
radio.formatFrequency(s, sizeof(s));
Serial.print("FREQ:"); Serial.println(s);
lcd.setCursor(0, 0);
lcd.print(s);
} // DisplayFrequency()
/// Update the ServiceName text on the LCD display when in RDS mode.
void DisplayServiceName(char *name)
{
Serial.print("RDS:"); Serial.println(name);
if (rot_state == STATE_FREQ) {
lcd.setCursor(0, 1);
lcd.print(name);
}
} // DisplayServiceName()
void DisplayTime(uint8_t hour, uint8_t minute) {
Serial.print("RDS-Time:");
if (hour < 10) Serial.print('0');
Serial.print(hour);
Serial.print(':');
if (minute < 10) Serial.print('0');
Serial.print(minute);
} // DisplayTime()
/// Display the current volume.
void DisplayVolume(uint8_t v)
{
Serial.print("VOL: "); Serial.println(v);
lcd.setCursor(0, 1);
lcd.print("VOL: "); lcd.print(v);
} // DisplayVolume()
/// Display the current mono switch.
void DisplayMono(uint8_t v)
{
Serial.print("MONO: "); Serial.println(v);
lcd.setCursor(0, 1);
lcd.print("MONO: "); lcd.print(v);
} // DisplayMono()
/// Display the current soft mute switch.
void DisplaySoftMute(uint8_t v)
{
Serial.print("SMUTE: "); Serial.println(v);
lcd.setCursor(0, 1);
lcd.print("SMUTE: "); lcd.print(v);
} // DisplaySoftMute()
// - - - - - - - - - - - - - - - - - - - - - - - - - -
void RDS_process(uint16_t block1, uint16_t block2, uint16_t block3, uint16_t block4) {
rds.processData(block1, block2, block3, block4);
}
// this function will be called when the menuButton was clicked
void doMenuClick() {
unsigned long now = millis();
if (rot_state == STATE_FREQ) {
// jump into volume mode
rot_state = STATE_VOL;
encoderLastPos = radio.getVolume();
encoder.setPosition(encoderLastPos);
DisplayVolume(encoderLastPos);
}
else if (rot_state == STATE_VOL) {
// jump into mono/stereo switch
rot_state = STATE_MONO;
encoderLastPos = radio.getMono();
encoder.setPosition(encoderLastPos);
DisplayMono(encoderLastPos);
}
else if (rot_state == STATE_MONO) {
// jump into soft mute switch
rot_state = STATE_SMUTE;
encoderLastPos = radio.getSoftMute();
encoder.setPosition(encoderLastPos);
DisplaySoftMute(encoderLastPos);
}
else if (rot_state == STATE_SMUTE) {
rot_state = STATE_FREQ;
encoderLastPos = (radio.getFrequency() - radio.getMinFrequency()) / radio.getFrequencyStep();
encoder.setPosition(encoderLastPos);
DisplayServiceName("...");
} // if
encoderLastTime = now;
} // doMenuClick()
// this function will be called when the seekButton was clicked
void doSeekClick() {
Serial.println("SEEK...");
radio.seekUp(true);
} // doSeekClick()
// The Interrupt Service Routine for Pin Change Interrupts
// On Arduino UNO you can use the PCINT1 interrupt vector that covers digital value changes on A2 and A3.
// On Arduino Mega 2560 you can use the PCINT2 interrupt vector that covers digital value changes on A8 and A9.
// Read http://www.atmel.com/Images/doc8468.pdf for more details on external interrupts.
ISR(PCINT2_vect) {
encoder.tick(); // just call tick() to check the state.
} // ISR for PCINT2
/// Setup a FM only radio configuration with I/O for commands and debugging on the Serial port.
void setup() {
// open the Serial port
Serial.begin(57600);
// Initialize the lcd
lcd.begin(16, 2);
lcd.setBacklight(1);
lcd.print("Radio...");
delay(800);
lcd.clear();
// Initialize the Radio
radio.init();
// Enable information to the Serial port
radio.debugEnable();
// radio.setBandFrequency(RADIO_BAND_FM, 8930); // hr3
radio.setBandFrequency(RADIO_BAND_FM, preset[i_sidx]); // 5. preset.
// radio.setFrequency(10140); // Radio BOB // preset[i_sidx]
// Setup rotary encoder
// You may have to modify the next 2 lines if using other pins than A2 and A3
// On Arduino-Uno: rotary encoder in A2(PCINT10) and A3(PCINT11)
// PCICR |= (1 << PCIE1); // This enables Pin Change Interrupt 1 that covers the Analog input pins or Port C.
// PCMSK1 |= (1 << PCINT10) | (1 << PCINT11); // This enables the interrupt for pin 2 and 3 of Port C.
// On Arduino-MEGA2560: A8(PCINT16) and A9(PCINT17) for interrupt vector PCINT2
PCICR |= (1 << PCIE2);
PCMSK2 |= (1 << PCINT16) | (1 << PCINT17);
encoderLastPos = (radio.getFrequency() - radio.getMinFrequency()) / radio.getFrequencyStep();
Serial.println(encoderLastPos);
encoder.setPosition(encoderLastPos);
// Setup the buttons
// link the doMenuClick function to be called on a click event.
menuButton.attachClick(doMenuClick);
// link the doSeekClick function to be called on a click event.
seekButton.attachClick(doSeekClick);
delay(100);
radio.setMono(false);
radio.setMute(false);
// radio.debugRegisters();
Serial.write('>');
state = STATE_PARSECOMMAND;
rot_state = STATE_NONE;
// setup the information chain for RDS data.
radio.attachReceiveRDS(RDS_process);
rds.attachServicenNameCallback(DisplayServiceName);
rds.attachTimeCallback(DisplayTime);
} // Setup
/// Execute a command identified by a character and an optional number.
/// See the "?" command for available commands.
/// \param cmd The command character.
/// \param value An optional parameter for the command.
void runCommand(char cmd, int16_t value)
{
if (cmd == '?') {
Serial.println();
Serial.println("? Help");
Serial.println("+ increase volume");
Serial.println("- decrease volume");
Serial.println("> next preset");
Serial.println("< previous preset");
Serial.println(". scan up : scan up to next sender");
Serial.println(", scan down ; scan down to next sender");
Serial.println("fnnnnn: direct frequency input");
Serial.println("i station status");
Serial.println("s mono/stereo mode");
Serial.println("b bass boost");
Serial.println("m mute/unmute");
Serial.println("u soft mute/unmute");
}
// ----- control the volume and audio output -----
else if (cmd == '+') {
// increase volume
int v = radio.getVolume();
if (v < 15) radio.setVolume(++v);
}
else if (cmd == '-') {
// decrease volume
int v = radio.getVolume();
if (v > 0) radio.setVolume(--v);
}
else if (cmd == 'm') {
// toggle mute mode
radio.setMute(! radio.getMute());
}
else if (cmd == 'u') {
// toggle soft mute mode
radio.setSoftMute(! radio.getSoftMute());
}
// toggle stereo mode
else if (cmd == 's') { radio.setMono(! radio.getMono()); }
// toggle bass boost
else if (cmd == 'b') { radio.setBassBoost(! radio.getBassBoost()); }
// ----- control the frequency -----
else if (cmd == '>') {
// next preset
if (i_sidx < (sizeof(preset) / sizeof(RADIO_FREQ))-1) {
i_sidx++; radio.setFrequency(preset[i_sidx]);
} // if
}
else if (cmd == '<') {
// previous preset
if (i_sidx > 0) {
i_sidx--;
radio.setFrequency(preset[i_sidx]);
} // if
}
else if (cmd == 'f') { radio.setFrequency(value); }
else if (cmd == '.') { radio.seekUp(false); }
else if (cmd == ':') { radio.seekUp(true); }
else if (cmd == ',') { radio.seekDown(false); }
else if (cmd == ';') { radio.seekDown(true); }
// not in help:
else if (cmd == '!') {
if (value == 0) radio.term();
if (value == 1) radio.init();
}
else if (cmd == 'i') {
char s[12];
radio.formatFrequency(s, sizeof(s));
Serial.print("Station:"); Serial.println(s);
Serial.print("Radio:"); radio.debugRadioInfo();
Serial.print("Audio:"); radio.debugAudioInfo();
// Serial.print(" RSSI: ");
// Serial.print(info.rssi);
//
// for (uint8_t i = 0; i < info.rssi - 15; i+=2) { Serial.write('*'); } // Empfangspegel ab 15. Zeichen
// Serial.println();
} // info
// else if (cmd == 'n') { radio.debugScan(); }
else if (cmd == 'x') { radio.debugStatus(); }
} // runCommand()
/// Constantly check for serial input commands and trigger command execution.
void loop() {
int newPos;
unsigned long now = millis();
static unsigned long nextFreqTime = 0;
static unsigned long nextRadioInfoTime = 0;
// some internal static values for parsing the input
static char command;
static int16_t value;
static RADIO_FREQ lastf = 0;
RADIO_FREQ f = 0;
char c;
// check for the menuButton
menuButton.tick();
if (Serial.available() > 0) {
// read the next char from input.
c = Serial.peek();
if ((state == STATE_PARSECOMMAND) && (c < 0x20)) {
// ignore unprintable chars
Serial.read();
}
else if (state == STATE_PARSECOMMAND) {
// read a command.
command = Serial.read();
state = STATE_PARSEINT;
}
else if (state == STATE_PARSEINT) {
if ((c >= '0') && (c <= '9')) {
// build up the value.
c = Serial.read();
value = (value * 10) + (c - '0');
}
else {
// not a value -> execute
runCommand(command, value);
command = ' ';
state = STATE_PARSECOMMAND;
value = 0;
} // if
} // if
} // if
// check for the rotary encoder
newPos = encoder.getPosition();
if (newPos != encoderLastPos) {
if (rot_state == STATE_FREQ) {
RADIO_FREQ f = radio.getMinFrequency() + (newPos * radio.getFrequencyStep());
radio.setFrequency(f);
encoderLastPos = newPos;
nextFreqTime = now + 10;
}
else if (rot_state == STATE_VOL) {
radio.setVolume(newPos);
encoderLastPos = newPos;
DisplayVolume(newPos);
}
else if (rot_state == STATE_MONO) {
radio.setMono(newPos & 0x01);
encoderLastPos = newPos;
DisplayMono(newPos & 0x01);
}
else if (rot_state == STATE_SMUTE) {
radio.setSoftMute(newPos & 0x01);
encoderLastPos = newPos;
DisplaySoftMute(newPos & 0x01);
} // if
encoderLastTime = now;
}
else if (now > encoderLastTime + 2000) {
// fall into FREQ + RDS mode
rot_state = STATE_FREQ;
encoderLastPos = (radio.getFrequency() - radio.getMinFrequency()) / radio.getFrequencyStep();
encoder.setPosition(encoderLastPos);
} // if
// check for RDS data
radio.checkRDS();
// update the display from time to time
if (now > nextFreqTime) {
f = radio.getFrequency();
if (f != lastf) {
// don't display a Service Name while frequency is no stable.
DisplayServiceName(" ");
DisplayFrequency(f);
lastf = f;
} // if
nextFreqTime = now + 400;
} // if
if (now > nextRadioInfoTime) {
RADIO_INFO info;
radio.getRadioInfo(&info);
lcd.setCursor(14, 0);
lcd.print(info.rssi);
nextRadioInfoTime = now + 1000;
} // update
} // loop
// End.
| Fréquence Radio, La bande FM |
http://www.emetteurs.ch/wiki/index.php/La_bande_FM_et_DAB%2B_%C3%A0_Lausanne
8810, // SRF 1 10260, // RTS LA PREMIERE 10330, // LFM 10360, // NRJ Léman 10500, // NOSTALGIE LEMAN 10650, // ROUGE FM 10720, // ONE FM 8880, // RADIO 74 9010, // VIRGIN LEMAN 9160, // RTS LA PREMIERE 9270, // RADIO ORIENT 9300, // PLUS 9380, // RADIO ZONE 9470, // RTS LA PREMIERE 9560, // RADIO LAC 9620, // RTS ESPACE 2 9780, // RSI RETE UNO 9850, // RTS COULEUR 3