산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

아두이노 나노로 만드는 방수 샤워 라디오: RDA5807, 노키아 LCD, PAM8403

부품 및 소모품

부품 수량
아두이노 나노 R3 1
RDA5807 FM 라디오 모듈 1
노키아 5110 LCD (3V-5V 버전) 1
Tiny RTC 모듈 (DS1307 + 24C32 EEPROM) 1
PAM8403 2×3W 클래스 D 앰프 1
LM2596 DC-DC 강압 컨버터 모듈 1
LIR2032 3.6V 충전식 배터리 1
100옴 저항 1
1k옴 저항 1
10k옴 저항 6
100k옴 저항 1
IRLZ44 파워 MOSFET 1
PEIYING PY1010C 60W 4옴 스피커 1
12V 3A 파워 서플라이 1
2200µF 25V 커패시터 2
100µF 커패시터 1
3W LED 전구 (G4 10W 할로겐 대체용) 1

프로젝트 소개

몇 년 전 중국산 샤워 부스를 구입했는데, TR028 제어 시스템에 라디오가 내장되어 있었습니다. 어느 날 시스템이 완전히 고장 났고, 아무도 수리할 수 없어 저렴한 샤워 라디오를 하나 더 샀지만 1년 만에 또 고장 났습니다.

가족들은 샤워 부스에 라디오가 있는 걸 좋아했기에, 이 라디오들이 어떻게 만들어졌는지 연구하기 시작했습니다. 고장 난 TR028 시스템 안에서 TEA5767 모듈을 발견했는데, 검색해 보니 저렴한 FM 라디오 모듈이었습니다. 더 찾아보니 RDA5807이라는 더 나은 모듈이 있었는데, TEA5767과 비슷하지만 RDS, 볼륨 제어, 베이스 부스트 기능이 추가되어 있었습니다. 그래서 새 프로젝트에 RDA5807을 사용하기로 결정했습니다.

내가 원했던 기능

알리익스프레스에서 RDA5807, Tiny RTC, PAM8403, 노키아 5110 LCD, LM2596 모듈 등을 10유로 미만에 구입해 실험을 시작했습니다.

최종 구현된 기능

작동 원리

개선하고 싶은 점

결론

한 달간 문제없이 작동 중이며(시계 정확도 제외), 매우 만족스럽습니다. 프로그래머가 아니라 코드 최적화 여지가 많습니다. C/C++ 독학 1년 차로, 아두이노 플랫폼 첫 실용 프로젝트이자 허브 첫 공유작입니다. 미흡한 점 양해 바랍니다.

업데이트 1: 하드웨어 - 12V 라인에 2A 퓨즈 추가(안전). 소프트웨어 - 586번째 줄에 menu = 1; 추가로 전원 키 누르 후 볼륨 모드로 복구.

업데이트 2: 불행히도 LCD가 수명을 다해가고 있습니다. 5110 LCD는 작고 읽기 힘들어 재설치하지 않고 1.8" TFT LCD로 교체 실험 중입니다. 장점: 더 크고 밝고 해상도 좋음. 단점: 리소스 더 많이 소모. 조언 환영합니다.

코드

프로젝트 스케치 (아두이노)

/////////////////////////////////////////////////////////////////
// Arduino based shower FM Radio Project
// Arduino NANO, RDA5807M, RTC, EEPROM, LCD5110, Thermistor
/////////////////////////////////////////////////////////////////
#include <LCD5110_Graph.h>
#include <AT24CX.h>
#include <Wire.h>
#include <RDSParser.h>
#include <radio.h>
#include <RTClib.h>
#include <RDA5807M.h>
#include <Keypad.h>

#define MAXmenu 4
#define ledPin 13
#define blPin 7

char keys[7][2] = {
  {'L', 'P'}, // LED, POWER
  {'I', 'D'}, // INFO, DISPLAY
  {'1', '2'}, // 프리셋 1-2
  {'3', '4'}, // 프리셋 3-4
  {'5', '6'}, // 프리셋 5-6
  {'M', 'm'}, // MODE, MEM
  {'<', '>'} // down, up
};

byte rowPins[7] = {11, 12, 10, 17, 9, 16, 8};
byte colPins[2] = {15, 14};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, 7, 2);

boolean bass = 0, dspl = 0, memdisplay = 0, mempress = 0, adj = 0;
boolean ledPin_state, power_state;
int menu, volume, volumeOld = 5;
int frequency, frequencyOld;
int txtl = 0, temparray = 0;
int samples[5];
unsigned int status[6];
unsigned long timeprevious = 0, timeprev = 0;

AT24CX mem;
RTC_DS1307 rtc;
LCD5110 lcd(6, 5, 4, 2, 3);
RDA5807M radio;
RDSParser rds;

extern unsigned char SmallFont[];
extern uint8_t signal5[];
extern uint8_t signal4[];
extern uint8_t signal3[];
extern uint8_t signal2[];
extern uint8_t signal1[];

void setup() {
  analogReference(EXTERNAL);
  Serial.begin(9600);
  Wire.begin();
  
  radio.init();
  radio.debugEnable();
  
  lcd.InitLCD();
  lcd.clrScr();
  lcd.setFont(SmallFont);
  lcd.enableSleep();
  
  power_state = 0;
  
  frequency = mem.readInt(201);
  volume = 2;
  menu = 1;
  
  if (volume < 0) volume = 0;
  if (volume > 15) volume = 15;
  if (frequency < 0) frequency = 0;
  if (frequency > 210) frequency = 210;
  
  WriteReg(0x02, 0xC00d);
  radio.attachReceiveRDS(RDS_process);
  rds.attachServicenNameCallback(DisplayServiceName);
  rds.attachTextCallback(DisplayText);
}

void loop() {
  if (frequency != frequencyOld) {
    frequencyOld = frequency;
    mem.writeInt(201, frequency);
    canal(frequency);
  }
  if (volume != volumeOld) {
    volumeOld = volume;
    WriteReg(5, 0x84D0 | volume);
  }
  
  char key = keypad.getKey();
  radio.checkRDS();
  
  // 온도 측정 (0.6초마다 5회 평균)
  float average;
  unsigned long timenow = millis();
  if ((unsigned long)(timenow - timeprevious) > 600) {
    timeprevious = timenow;
    samples[temparray] = analogRead(A7);
    temparray++;
  }
  if (temparray == 5) {
    average = 0;
    for (int i = 0; i < 5; i++) average += samples[i];
    printTemp(average);
    temparray = 0;
  }
  
  // MEM 표시 4초 타임아웃
  unsigned long dabar = millis();
  if (mempress == 1) { timeprev = dabar; memdisplay = 1; mempress = 0; }
  if (memdisplay == 1) {
    if ((unsigned long)(dabar - timeprev) < 4000) memdisplay = 1;
    else memdisplay = 0;
  }
  
  // 시리얼을 통한 시간 조정 기능
  DateTime now = rtc.now();
  if (Serial.available() > 0) {
    char t = Serial.read();
    switch (t) {
      case 'h': {
        unsigned int hours = Serial.parseInt();
        rtc.adjust(DateTime(now.year(), now.month(), now.day(), hours, now.minute(), now.second()));
        Serial.println(F("Hours")); Serial.println(hours); break;
      }
      case 'm': {
        unsigned int mins = Serial.parseInt();
        rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), mins, now.second()));
        Serial.println(F("Minutes")); Serial.println(mins); break;
      }
      case 's': {
        unsigned int sec = Serial.parseInt();
        rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute(), sec));
        Serial.println(F("Seconds")); Serial.println(sec); break;
      }
    }
  }
  
  printSignalStrength();
  printLines();
  printTime();
  printFreq();
  printStereo();
  printMode();
  printMenu();
  printDate();
  lcd.update();
}

// 신호 강도 표시 (0-63을 5단계로)
void printSignalStrength() {
  unsigned int sig;
  Readstatus();
  sig = status[1] / 1000;
  if (sig <= 12) lcd.drawBitmap(1, 1, signal1, 17, 6);
  else if (sig <= 24) lcd.drawBitmap(1, 1, signal2, 17, 6);
  else if (sig <= 36) lcd.drawBitmap(1, 1, signal3, 17, 6);
  else if (sig <= 48) lcd.drawBitmap(1, 1, signal4, 17, 6);
  else lcd.drawBitmap(1, 1, signal5, 17, 6);
}

void printLines() {
  lcd.drawLine(0, 9, 84, 9);
  lcd.drawLine(0, 39, 84, 39);
}

void printTemp(float average) {
  average /= 5;
  average = 1023 / average - 1;
  average = 51700 / average;
  float steinhart = average / 50000;
  steinhart = log(steinhart);
  steinhart /= 3950;
  steinhart += 1.0 / (25 + 273.15);
  steinhart = 1.0 / steinhart;
  steinhart -= 273.15;
  int tmp = round(steinhart);
  lcd.printNumI(tmp, 60, 1, 2);
  lcd.print(F("~C"), 72, 1);
}

void DisplayServiceName(char *name) { lcd.print(name, 18, 22); }

void DisplayText(char *text) {
  lcd.print(text, txtl, 30);
  txtl = txtl - 66;
  if (txtl == -396) txtl = 0;
}

void printTime() {
  DateTime now = rtc.now();
  lcd.printNumI(now.hour(), 24, 1, 2, '0');
  lcd.print(":", 36, 1);
  lcd.printNumI(now.minute(), 42, 1, 2, '0');
}

void printDate() {
  if (dspl == 1) {
    ClearRDS();
    DateTime now = rtc.now();
    lcd.printNumI(now.year(), 12, 22, 4);
    lcd.print(".", 36, 22);
    lcd.printNumI(now.month(), 42, 22, 2, '0');
    lcd.print(".", 54, 22);
    lcd.printNumI(now.day(), 60, 22, 2, '0');
    int dw = now.dayOfTheWeek();
    const char* days[] = {"Sekmadienis","Pirmadienis","Antradienis","Treciadienis","Ketvirtadienis","Penktadienis","Sestadienis"};
    lcd.print(F(days[dw]), CENTER, 30);
    lcd.update();
    delay(4000);
    ClearRDS();
    dspl = 0;
  }
}

void printMode() { lcd.print(F("MODE "), 0, 41); }

void printMenu() {
  if (menu == 1) {
    lcd.print(F("VOLUME "), 30, 41);
    lcd.printNumI(volume + 1, 72, 41, 2, '0');
  }
  if (menu == 2) lcd.print(F("AUTO-TUNE"), 30, 41);
  if (menu == 3) lcd.print(F("MAN.-TUNE"), 30, 41);
  if (menu == 4) {
    lcd.print(F(" BASS "), 30, 41);
    lcd.print(bass ? F(" ON") : F("OFF"), 66, 41);
  }
}

void printFreq() {
  int fr = 870 + frequency;
  lcd.printNumI(fr / 10, 30, 12, 3);
  lcd.print(F("."), 48, 12);
  lcd.printNumI(fr % 10, 54, 12, 1);
  lcd.print(F("MHz"), 66, 12);
}

void printStereo() {
  if (memdisplay) lcd.print(F("MEM>"), 0, 12);
  else if ((status[0] & 0x0400) == 0) lcd.print(F("( )"), 0, 12);
  else lcd.print(F("(ST)"), 0, 12);
}

void search(byte direc) {
  byte i;
  if (!direc) WriteReg(0x02, 0xC30d);
  else WriteReg(0x02, 0xC10d);
  for (i = 0; i < 10; i++) {
    delay(200);
    Readstatus();
    if (status[0] & 0x4000) { frequency = status[0] & 0x03ff; break; }
  }
}

void canal(int canal) {
  byte numberH = canal >> 2;
  byte numberL = ((canal & 3) << 6) | 0x10;
  Wire.beginTransmission(0x11);
  Wire.write(0x03);
  Wire.write(numberH);
  Wire.write(numberL);
  Wire.endTransmission();
}

int Readstatus() {
  Wire.requestFrom(0x10, 12);
  for (int i = 0; i < 6; i++) status[i] = 256 * Wire.read() + Wire.read();
  Wire.endTransmission();
}

void WriteReg(byte reg, unsigned int valor) {
  Wire.beginTransmission(0x11);
  Wire.write(reg);
  Wire.write(valor >> 8);
  Wire.write(valor & 0xFF);
  Wire.endTransmission();
}

void RDS_process(uint16_t b1, uint16_t b2, uint16_t b3, uint16_t b4) {
  rds.processData(b1, b2, b3, b4);
}

void ClearRDS() {
  lcd.print(" ", 0, 22);
  lcd.print(" ", 0, 30);
}

void keypadEvent(KeypadEvent key, KeyState kpadState) {
  switch (kpadState) {
    case HOLD:
      if (key == 'D' && power_state == 1) adj = 1;
      break;
    case RELEASED:
      if (key == 'D' && adj == 1) {
        DateTime now = rtc.now();
        if (now.minute() >= 45) rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour() + 1, 0, 0));
        else if (now.minute() >= 1 && now.minute() <= 15) rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), 0, 0));
        adj = 0;
      }
      break;
    case PRESSED:
      if (key == 'M' && power_state) { memdisplay = 0; if (++menu > MAXmenu) menu = 1; }
      if (key == '>' && power_state) {
        memdisplay = 0;
        switch (menu) {
          case 1: if (volume < 0) { if (bass) WriteReg(0x02, 0xD00D); else WriteReg(0x02, 0xC00D); volume = 0; } else volume++; if (volume > 15) volume = 15; break;
          case 2: search(0); ClearRDS(); break;
          case 3: if (++frequency > 210) frequency = 210; ClearRDS(); break;
          case 4: if (!bass) { bass = 1; WriteReg(0x02, 0xD00D); } break;
        }
      }
      if (key == '<' && power_state) {
        memdisplay = 0;
        switch (menu) {
          case 1: if (--volume < 0) WriteReg(0x02, 0x800D); break;
          case 2: search(1); ClearRDS(); break;
          case 3: if (--frequency < 0) frequency = 0; ClearRDS(); break;
          case 4: if (bass) { bass = 0; WriteReg(0x02, 0xC00D); } break;
        }
      }
      if (key == 'L' && power_state) { digitalWrite(ledPin, !digitalRead(ledPin)); ledPin_state = digitalRead(ledPin); }
      if (key == 'P') {
        digitalWrite(blPin, !digitalRead(blPin));
        power_state = digitalRead(blPin);
        if (!power_state) { lcd.enableSleep(); digitalWrite(ledPin, LOW); }
        else lcd.disableSleep();
        volume = 2; menu = 1;
      }
      // 프리셋 1-6 처리
      for (int i = 1; i <= 6; i++) {
        if (key == char('0' + i) && power_state) {
          int addr = 100 + i * 10;
          if (!memdisplay) { frequency = mem.readInt(addr); ClearRDS(); }
          else { mem.writeInt(addr, frequency); memdisplay = 0; }
        }
      }
      if (key == 'm' && power_state) mempress = 1; else mempress = 0;
      if (key == 'I' && power_state) dspl = 1;
      break;
  }
}

신호 강도 심볼 (C/C++)

#if defined(__AVR__)
#include <avr/pgmspace.h>
#define imagedatatype const uint8_t
#elif defined(__PIC32MX__) || defined(__arm__)
#define PROGMEM
#define imagedatatype const unsigned char
#endif

imagedatatype signal5[] PROGMEM = {0xC1,0xC2,0xC4,0xFF,0xC4,0xC2,0xC1,0xC0,0xE0,0xC0,0xF0,0xC0,0xF8,0xC0,0xFC,0xC0,0xFE};
imagedatatype signal4[] PROGMEM = {0xC1,0xC2,0xC4,0xFF,0xC4,0xC2,0xC1,0xC0,0xE0,0xC0,0xF0,0xC0,0xF8,0xC0,0xFC,0xC0,0xC0};
imagedatatype signal3[] PROGMEM = {0xC1,0xC2,0xC4,0xFF,0xC4,0xC2,0xC1,0xC0,0xE0,0xC0,0xF0,0xC0,0xF8,0xC0,0xC0,0xC0,0xC0};
imagedatatype signal2[] PROGMEM = {0xC1,0xC2,0xC4,0xFF,0xC4,0xC2,0xC1,0xC0,0xE0,0xC0,0xF0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0};
imagedatatype signal1[] PROGMEM = {0xC1,0xC2,0xC4,0xFF,0xC4,0xC2,0xC1,0xC0,0xE0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0};

제조공정

  1. 라디오 기술의 진화와 공학
  2. 집적 회로(IC): 현대 전자제품을 구동하는 작고 강력한 칩
  3. JX 웨이브 제너레이터 – OLED 디스플레이를 탑재한 아두이노 호환 DDS 오실레이터
  4. MotionSense: 아두이노와 ESP8266으로 만드는 스마트 침입 감지 시스템
  5. 아두이노 MKR 번들로 만드는 보안 IoT 퍼즐 박스: 단계별 가이드
  6. 스마트 샤워 레귤레이터 – 정밀 온도 제어용 신뢰성 높은 솔레노이드 밸브
  7. 무선 적외선 온도 스캐너: 아두이노 나노 + 블루투스 + MLX90614
  8. 자율 주행 맥주 배달 탱크: Walabot + 아두이노 + 라즈베리파이 + 알렉사 통합
  9. 고급 마이크로컨트롤러 랩: ESP32, 아두이노, PIC 및 센서 모듈 실습
  10. MOSMusic 프로젝트 키트: MOSFET, 아두이노로 음악 동기화 조명 제어