User Tools

Site Tools


yaesufc-40control


Differences

This shows you the differences between two versions of the page.

Link to this comparison view

yaesufc-40control [2026/07/15 03:35] – created adminyaesufc-40control [2026/07/15 04:01] (current) admin
Line 4: Line 4:
 <file C++ fc40diagnostics.ino> <file C++ fc40diagnostics.ino>
 /* /*
- * FC-40 / Yaesu-protocol tuner probe & listener + * FC-40 / Yaesu-protocol antenna tuner probe & listener 
- * Target: Arduino Uno (ATmega328P), 5V logic (direct-connect to tuner 5V TTL UART)+ * ------------------------------------------------------ 
 + * Target: ATmega32U4 board (Leonardo / Micro / Pro Micro), 5V logic
 +         Direct-connect to the tuner'5V TTL UART (verify levels on a scope!).
  *  *
- Hardware UART (pins 0/1) -> USB console @ 115200 (Arduino IDE Serial Monitor) + Serial   (USB CDC) -> IDE Serial Monitor console @ 115200 
- SoftwareSerial            -> tuner UART @ 4800 8N1+ Serial1  (HW UART, pins 0=RX / 1=TX) -> tuner UART @ 4800 8N1
  *  *
  * WIRING (uses FC-40 SERVICE MANUAL pin numbers):  * WIRING (uses FC-40 SERVICE MANUAL pin numbers):
-   Tuner pin 5  DATA OUT (tuner TX) -> Arduino D10 (SoftSerial RX) +   Tuner pin 5  DATA OUT (tuner TX) -> board RX (D0
-   Tuner pin 4  DATA IN  (tuner RX) <- Arduino D11 (SoftSerial TX) +   Tuner pin 4  DATA IN  (tuner RX) <- board TX (D1
-   Tuner pin 3  GND                 -- Arduino GND  (MUST be common) +   Tuner pin 3  GND                 -- board GND  (MUST be common) 
-   Tuner pin 1  +13.5V -> Arduino Vin (feeds onboard 7805)  [optional]+   Tuner pin 1  +13.5V -> board Vin/RAW (onboard regulator)  [optional]
  *  *
-   ***DOUBLE-CHECK PIN NUMBERING PHYSICALLY.*** The MFJ results doc numbers +   *** VERIFY PIN NUMBERING AND LOGIC LEVELS ON A SCOPE FIRST. *** 
-   this same connector in REVERSE of the FC-40 service manual. Signals agree, +   The mini-DIN can be numbered in either direction depending on whether 
-   counting does not. Verify DATA OUT with a scope before trusting anything.+   you read the plug or the jack; signals are what matter, not the count.
  *  *
- INDICATORS / CONTROL (optional, matches your front-panel idea)+ OPTIONAL: onboard user LED (D13blips on each received A1. This is 
-   D3  Green LED  - lights briefly on each A1 (tuning activity / see notes) +   convenience only -- see note below about what an A1 does and does not mean.
-   D4  Red LED    - reserved for FAIL byte you have not yet discovered +
-   D2  Button     forces a command-mode probe sequence (INPUT_PULLUP)+
  *  *
  * CONSOLE COMMANDS (type in Serial Monitor, newline to send):  * CONSOLE COMMANDS (type in Serial Monitor, newline to send):
Line 31: Line 31:
    f0 <MHz>       send enable+freq       (0xFF 0xF0 <bcd hi> <bcd lo>)    f0 <MHz>       send enable+freq       (0xFF 0xF0 <bcd hi> <bcd lo>)
    f2 <MHz>       send start-tune        (0xFF 0xF2 <bcd hi> <bcd lo>)    f2 <MHz>       send start-tune        (0xFF 0xF2 <bcd hi> <bcd lo>)
-   probe <MHz>    f1 ... (gap) ... f2 <MHz>   (mimics radio commanded tune)+   probe <MHz>    f1 ... (gap) ... f2 <MHz>   (mimics radio commanded tune)
    raw <hh..>     send arbitrary hex bytes, e.g.  raw FF E0 01 00    raw <hh..>     send arbitrary hex bytes, e.g.  raw FF E0 01 00
-                reset the timestamp clock (t0)+                reset the timestamp clock
  *  *
- All received (tuner) bytes print in [brackets] with a ms timestamp, mirroring + Received (tuner) bytes print in [brackets] with a ms timestamp. 
- WA2FZW's log format. Bytes we transmit print without brackets.+ * Bytes we transmit print without brackets. 
 + * 
 + * ------------------------------------------------------ 
 + * Acknowledgement: 
 +   This work builds directly on the prior reverse-engineering and 
 +   documentation of the Yaesu tuner control protocol by John Price (WA2FZW), 
 +   whose published test box, captures, and analysis of the FT-891 / tuner 
 +   interface made this possible. Thank you for sharing your research openly. 
 + * ------------------------------------------------------
  */  */
  
-#include <SoftwareSerial.h> +// ---- optional indicator ---- 
- +#define USE_LED_A1  1              // set 0 to disable the D13 blip entirely 
-// ---- pins ---- +const uint8_t PIN_LED 13;
-const uint8_t PIN_TUNER_RX = 10;   // <- tuner DATA OUT +
-const uint8_t PIN_TUNER_TX 11;   // -> tuner DATA IN +
-const uint8_t PIN_GREEN    = 3; +
-const uint8_t PIN_RED      = 4; +
-const uint8_t PIN_BUTTON   = 2; +
- +
-SoftwareSerial tuner(PIN_TUNER_RX, PIN_TUNER_TX);+
  
 // ---- protocol bytes ---- // ---- protocol bytes ----
Line 61: Line 62:
 const uint16_t WAKE_GAP_MS = 50;   // observed ~50ms between 0xFF and command body const uint16_t WAKE_GAP_MS = 50;   // observed ~50ms between 0xFF and command body
  
-// ---- timestamp clock (mirrors the WA2FZW logger) ----+// ---- timestamp clock ----
 uint32_t t0 = 0; uint32_t t0 = 0;
 uint32_t lastActivity = 0; uint32_t lastActivity = 0;
 const uint32_t IDLE_RESET_MS = 14000; const uint32_t IDLE_RESET_MS = 14000;
  
-// ---- A1 tracking to help distinguish auto vs command mode ---- +// ---- A1 tracking (helps distinguish auto vs command mode---- 
-uint8_t   a1Count = 0; +uint8_t  a1Count = 0;
-uint32_t  lastA1Time = 0;+
  
-// ---- green LED one-shot ---- +// ---- LED one-shot ---- 
-uint32_t greenOffAt = 0;+uint32_t ledOffAt = 0;
  
 char lineBuf[48]; char lineBuf[48];
Line 78: Line 78:
 // ------------------------------------------------------------------- // -------------------------------------------------------------------
 void setup() { void setup() {
-  Serial.begin(115200); +  Serial.begin(115200);              // USB CDC console 
-  tuner.begin(4800);              // 8N1 is SoftwareSerial's only framing anyway+  while (!Serial && millis() < 3000) { }   // wait briefly for host (32U4) 
 +  Serial1.begin(4800, SERIAL_8N1);   // tuner UART
  
-  pinMode(PIN_GREEN, OUTPUT);  digitalWrite(PIN_GREEN, LOW); +#if USE_LED_A1 
-  pinMode(PIN_RED  OUTPUT);  digitalWrite(PIN_RED  LOW); +  pinMode(PIN_LED, OUTPUT); 
-  pinMode(PIN_BUTTON, INPUT_PULLUP);+  digitalWrite(PIN_LED, LOW); 
 +#endif
  
-  Serial.println(F("\n=== FC-40 tuner probe / listener ===")); +  Serial.println(F("\n=== FC-40 tuner probe / listener (ATmega32U4) ===")); 
-  Serial.println(F("Listening on tuner UART @ 4800 8N1."));+  Serial.println(F("Listening on Serial1 @ 4800 8N1."));
   Serial.println(F("Type 'h' for commands. Power-cycle the tuner now to catch boot chatter."));   Serial.println(F("Type 'h' for commands. Power-cycle the tuner now to catch boot chatter."));
   resetClock();   resetClock();
Line 93: Line 95:
 // ------------------------------------------------------------------- // -------------------------------------------------------------------
 void loop() { void loop() {
-  drainTuner();      // capture + report anything from the tuner +  drainTuner(); 
-  handleConsole();   // parse commands from the IDE console +  handleConsole(); 
-  handleButton();    // physical button -> command-mode probe +  serviceLed(); 
-  serviceLeds();     // green one-shot timeout +  idleReset();
-  idleReset();       // reset t0 after long silence+
 } }
  
 // ------------------------------------------------------------------- // -------------------------------------------------------------------
-// Stamp helper: prints "%05lu\t" ms since t0, starting the clock on first event. 
 uint32_t stamp() { uint32_t stamp() {
   uint32_t now = millis();   uint32_t now = millis();
Line 124: Line 124:
  
 // ------------------------------------------------------------------- // -------------------------------------------------------------------
-// Read everything the tuner sends. All such bytes are printed in [brackets].+// Everything from the tuner prints in [brackets].
 void drainTuner() { void drainTuner() {
-  while (tuner.available()) { +  while (Serial1.available()) { 
-    uint8_t b = tuner.read();+    uint8_t b = Serial1.read();
     uint32_t ts = stamp();     uint32_t ts = stamp();
  
Line 140: Line 140:
       case RESP_A1:       case RESP_A1:
         a1Count++;         a1Count++;
-        lastA1Time = millis(); +        // AUTO modea single A1 means the cycle STARTED, not that it 
-        // Green blip on every A1. NOTE: in AUTO mode a single A1 means +        // succeeded. COMMAND mode: A1 #1 = key now, A1 #2 = done/unkey
-        // "cycle STARTED", not "succeeded" - do not read green as "done"+        ledBlip(1000);
-        greenOn(1000);+
         if (a1Count == 1)         if (a1Count == 1)
-          Serial.print(F("  A1  #1 (auto: cycle running / cmd: KEY radio now)"));+          Serial.print(F("  A1  #1 (auto: cycle running / cmd: key radio now)"));
         else         else
           Serial.print(F("  A1  #2 (cmd mode: tune complete, unkey)"));           Serial.print(F("  A1  #2 (cmd mode: tune complete, unkey)"));
         break;         break;
       // case RESP_FAIL:       // case RESP_FAIL:
-      //   digitalWrite(PIN_RED, HIGH); 
       //   Serial.print(F("  FAIL"));       //   Serial.print(F("  FAIL"));
       //   break;       //   break;
Line 161: Line 159:
  
 // ------------------------------------------------------------------- // -------------------------------------------------------------------
-void greenOn(uint32_t ms) { +void ledBlip(uint32_t ms) { 
-  digitalWrite(PIN_GREEN, HIGH); +#if USE_LED_A1 
-  greenOffAt = millis() + ms;+  digitalWrite(PIN_LED, HIGH); 
 +  ledOffAt = millis() + ms; 
 +#else 
 +  (void)ms; 
 +#endif
 } }
-void serviceLeds() { +void serviceLed() { 
-  if (greenOffAt && millis() >= greenOffAt) { +#if USE_LED_A1 
-    digitalWrite(PIN_GREEN, LOW); +  if (ledOffAt && millis() >= ledOffAt) { 
-    greenOffAt = 0;+    digitalWrite(PIN_LED, LOW); 
 +    ledOffAt = 0;
   }   }
 +#endif
 } }
  
 // ------------------------------------------------------------------- // -------------------------------------------------------------------
-// Transmit helpers. Bytes we send are echoed to console WITHOUT brackets.+// Bytes we send echo to the console WITHOUT brackets.
 void txByte(uint8_t b) { void txByte(uint8_t b) {
-  tuner.write(b);+  Serial1.write(b);
   uint32_t ts = stamp();   uint32_t ts = stamp();
   char buf[24];   char buf[24];
Line 182: Line 186:
 } }
  
-// Send 0xFF, wait the observed gap, then a 3-byte command body.+// 0xFF, wait the observed gap, then a 3-byte command body.
 void sendCommand(uint8_t op, uint8_t hi, uint8_t lo) { void sendCommand(uint8_t op, uint8_t hi, uint8_t lo) {
   txByte(WAKEUP);   txByte(WAKEUP);
-  tuner.flush();+  Serial1.flush();
   delay(WAKE_GAP_MS);   delay(WAKE_GAP_MS);
   txByte(op);   txByte(op);
   txByte(hi);   txByte(hi);
   txByte(lo);   txByte(lo);
-  tuner.flush();+  Serial1.flush();
 } }
  
-// MHz (float) -> two BCD bytes at 10 kHz resolution. +// MHz -> two BCD bytes at 10 kHz resolution. 
-//   3.57  -> 357  -> 0x03 0x57 +//   3.57  -> 0x03 0x57 
-//   7.22  -> 722  -> 0x07 0x22 +//   7.22  -> 0x07 0x22 
-//   14.25 -> 1425 -> 0x14 0x25+//   14.25 -> 0x14 0x25
 bool mhzToBcd(float mhz, uint8_t &hi, uint8_t &lo) { bool mhzToBcd(float mhz, uint8_t &hi, uint8_t &lo) {
   long units = lround(mhz * 100.0);        // 10 kHz units   long units = lround(mhz * 100.0);        // 10 kHz units
Line 209: Line 213:
 } }
  
-// ------------------------------------------------------------------- +// Mimic radio'commanded-tune sequence (minus actually keying RF). 
-void handleButton() { +// Watch for: [A1] (key here) ... [A0] ... [A1] (done).
-  static uint32_t lastPress = 0; +
-  static bool prev = HIGH; +
-  bool now = digitalRead(PIN_BUTTON); +
-  if (prev == HIGH && now == LOW && (millis() - lastPress) > 250) { +
-    lastPress = millis(); +
-    Serial.println(F("\n[button] commanded-tune probe @ 14.25 MHz")); +
-    commandedProbe(14.25); +
-  } +
-  prev = now; +
-+
- +
-// Mimic the radio'Tests-1/5 commanded sequence (minus actually keying RF). +
-// You watch for: [A1] (key here) ... [A0] ... [A1] (done).+
 void commandedProbe(float mhz) { void commandedProbe(float mhz) {
   uint8_t hi, lo;   uint8_t hi, lo;
Line 229: Line 220:
   a1Count = 0;   a1Count = 0;
   sendCommand(OP_DISABLE, 0x00, 0x00);   // FF F1 00 00   sendCommand(OP_DISABLE, 0x00, 0x00);   // FF F1 00 00
-  delay(400);                            // radio waited ~450ms before F2+  delay(400);
   sendCommand(OP_TUNE, hi, lo);          // FF F2 <bcd>   sendCommand(OP_TUNE, hi, lo);          // FF F2 <bcd>
 } }
Line 252: Line 243:
   if (*s == 0) return;   if (*s == 0) return;
  
-  if (!strcmp(s, "h"))      { printHelp(); return; } +  if (!strcmp(s, "h"))  { printHelp(); return; } 
-  if (!strcmp(s, "x"))      { resetClock(); return; } +  if (!strcmp(s, "x"))  { resetClock(); return; } 
-  if (!strcmp(s, "w"))      { txByte(WAKEUP); return; } +  if (!strcmp(s, "w"))  { txByte(WAKEUP); return; } 
-  if (!strcmp(s, "f1"))     { sendCommand(OP_DISABLE, 0x00, 0x00); return; }+  if (!strcmp(s, "f1")) { sendCommand(OP_DISABLE, 0x00, 0x00); return; }
  
   if (!strncmp(s, "f0", 2) || !strncmp(s, "f2", 2)) {   if (!strncmp(s, "f0", 2) || !strncmp(s, "f2", 2)) {
yaesufc-40control.txt · Last modified: by admin

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki