massa
Published © GPL3+

Desktop Radio Mini

This radio is a classic radio with a built-in FM/AM tuner. The tuner is controlled using M5stack's DinMeter.

IntermediateFull instructions provided4 hours18
Desktop Radio Mini

Things used in this project

Hardware components

M5stack - DinMeter
×1
Si4730 - AM/FM DSP radio module
×1
Diodes - PAM8406DR
×1
MOLEX - PicoBlade 53047-0210
×1
XKB Connection - XKB5858-Z-C
×1
HOOYA - PJ-307A-1.3
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Schematics

Schematics

Code

Sharing the setup and loop parts for reference

Arduino
// ------------------------------- 
// setup / loop 
// ------------------------------- 
void setup() { 
  Serial.begin(115200); 
  Serial.println(F("Si4730 FM demo start"));

  // --- DIN METER 初期化 ---
  din_meter_init();
  si4730_initPins();

  // --- Si4730 を一旦リセット ---
  reset();

  Serial.println("Si4730 POWER ON");
  delay(1000);

  if (band == "FM") {
    fm_power_up();
    volume_set(nowvolume);
    fmfreq_set(prefmfreq);
  }
  else {
    am_power_up();
    volume_set(nowvolume);
    amfreq_set(preamfreq);
  }

  currentFreqFM = prefmfreq;
  targetFreqFM  = prefmfreq;

  currentFreqAM = preamfreq;
  targetFreqAM  = preamfreq;

  // --- Si4730 のバージョン読み取り ---
  get_rev();
  Serial.println("Si4730 STATUS/RESPONSE 読み取り開始");

  // --- エンコーダ初期位置 ---
  DinMeter.update();
  oldPosition = DinMeter.Encoder.read();

  // --- 初回画面描画(FM/AM 共通) ---
  redrawScreen();
}

void loop() {

  // ============================
  // ボタン更新(最優先)
  // ============================
  DinMeter.update();

  // 音量モード中は周波数処理を完全停止
  if (volumeMode) {
    return;
  }
  
  //void updateBandSwitch() {
  // ボタン押した瞬間
  if (DinMeter.BtnA.wasPressed()) {
    holdStart = millis();
    holdActive = true;
    previewShown = false;
    bandShouldSwitch = false;    
  }

  // BtnA 長押し中
  if (holdActive && DinMeter.BtnA.isHolding()) {

    uint32_t held = millis() - holdStart;

    //
    if (held > 500 && !previewShown) {
      previewShown = true;
      bandShouldSwitch = true;

      DinMeter.Display.fillRect(0, 0, 120, 40, BLACK);
      DinMeter.Display.setTextSize(0);
      DinMeter.Display.setTextColor(ORANGE);

      if (band == "FM") {
      DinMeter.Display.drawString("AM", 50, 20);
      } else {
      DinMeter.Display.drawString("FM", 50, 20);
      }
    }

    //
    if (held > 1000 && bandShouldSwitch) {
      toggleBand();   // ★ AM/FM 切替
      redrawScreen();
        
      //
      holdActive = false;
      bandShouldSwitch = false;
      previewShown = false;
      return;
    }
  }

  // ボタンを離したとき
  if (DinMeter.BtnA.wasReleased()) {

  //
    if (bandShouldSwitch) {
      toggleBand();
      redrawScreen();
    }

    //
    holdActive = false;
    bandShouldSwitch = false;
    previewShown = false;      
  }


  // 短押し → 音量調整モード
  if (DinMeter.BtnA.wasClicked()) {
    enterVolumeMode();
    return;
  }

  // ============================
  // エンコーダ処理
  // ============================
  static uint32_t lastRead = 0;
  if (millis() - lastRead < 2) return;
  lastRead = millis();

  long newPosition = DinMeter.Encoder.read();
  long diffPos = newPosition - oldPosition;

  // チャタリング完全除去
  if (abs(diffPos) == 1) return;
  if (diffPos % 2 != 0) return;

  //
  static int lastDir = 0;
  int dir = (diffPos > 0) ? 1 : -1;

  if (lastDir != 0 && dir != lastDir) {
    lastDir = dir;
    return;
  }
  lastDir = dir;

  // 回転速度 → ステップ可変
  static uint32_t lastRotateTime = 0;
  uint32_t now = millis();
  uint32_t diff = now - lastRotateTime;
  lastRotateTime = now;

  int stepFM = 1;
  int stepAM = 1;

  if (diff < 10) {
    stepFM = 10;
    stepAM = 5;
  }
  else if (diff < 20) {
    stepFM = 5;
    stepAM = 2;
  }

  // ============================
  // FM 周波数変更
  // ============================
  if (band == "FM" && (newPosition != oldPosition)) {

    int delta = diffPos / 2;
    targetFreqFM = prefmfreq - delta * (stepFM * 10);

    if (targetFreqFM < 7600)  targetFreqFM = 7600;     // 下限で止まる
    if (targetFreqFM > 10800) targetFreqFM = 10800;    // 上限で止まる

    prefmfreq = targetFreqFM;
    oldPosition = newPosition;
  }

  // ============================
  // AM 周波数変更
  // ============================
  else if (band == "AM" && (newPosition != oldPosition)) {

    int delta = diffPos / 2;
    targetFreqAM = preamfreq - delta * (stepAM * 9);

    // ★ ループではなくリミット固定に変更
    if (targetFreqAM < 531)  targetFreqAM = 531;     // 下限で止まる
    if (targetFreqAM > 1602) targetFreqAM = 1602;    // 上限で止まる

    preamfreq = targetFreqAM;
    oldPosition = newPosition;
  }
  
  // 画面表示
  if (band == "FM") {
    if (currentFreqFM != targetFreqFM) {

      if (currentFreqFM < targetFreqFM) currentFreqFM += 10;
      else if (currentFreqFM > targetFreqFM) currentFreqFM -= 10;

      //
      if (currentFreqFM > 10800) currentFreqFM = 10800;    // 上限で止まる
      if (currentFreqFM < 7600)  currentFreqFM = 7600;     // 下限で止まる

      char buf[10];
      dtostrf(float(currentFreqFM) / 100, 5, 1, buf);
      drawFreqSmooth(String(buf));
      
      if (currentFreqFM == targetFreqFM) fmfreq_set(currentFreqFM);
    }
  }

  else if (band == "AM") {
    if (currentFreqAM != targetFreqAM) {

      if (currentFreqAM < targetFreqAM) currentFreqAM += 9;
      else if (currentFreqAM > targetFreqAM) currentFreqAM -= 9;

      if (currentFreqAM < 531)  currentFreqAM = 531;     // 下限で止まる
      if (currentFreqAM > 1602) currentFreqAM = 1602;    // 上限で止まる
 
      char buf[10];
      dtostrf(float(currentFreqAM), 4, 0, buf);
      drawFreqSmooth(String(buf));

      if (currentFreqAM == targetFreqAM) amfreq_set(currentFreqAM);
    }
  }
}

Credits

massa
2 projects • 0 followers

Comments