顯示具有 arduino 標籤的文章。 顯示所有文章
顯示具有 arduino 標籤的文章。 顯示所有文章

2026年8月5日 星期三

RC522 RFID 讀卡機除錯戰報:從 0x82 假死到滿血復活的硬體排錯全紀錄

[Arduino實戰] RC522 RFID 讀卡機除錯戰報:從 0x82 假死到滿血復活的硬體排錯全紀錄

在進行 Arduino 門禁系統或 RFID 專案時,MIFARE RC522 模組幾乎是所有創客與工程師的首選。然而,很多人按照官方線路圖接好線、燒錄程式後,會遇到一個經典的「大坑」:監控視窗印出 Firmware Version: 0x82 = (unknown),然後不管拿什麼磁扣或卡片去刷,模組就是毫無反應、死寂一片。

很多人遇到這個狀況,會瘋狂重裝函式庫,或是以為買到瑕疵品直接丟進垃圾桶。但身為工程師,我們怎麼能就這樣放棄?這篇文章將帶你回顧一段「教科書級」的硬體 Debug 歷程,從底層通訊、韌體辨識,一路追查到物理電源層,最後靠一顆超便宜的電子元件漂亮破局!

🕵️‍♂️ 深度除錯:從軟體層到物理層的排錯之旅

Step 1通訊建立與「身世之謎」

現象:剛接好 SPI 線路跑基本測試程式時,序列埠印出了 Firmware Version: 0x82 = (unknown)

分析:這是一個非常關鍵的起點。能順利讀出 0x82,代表 Arduino 與 RC522 之間的 SPI 實體接線與通訊是完全正常的(MISO/MOSI/SCK/SS 都沒接錯)。系統顯示 unknown 單純是因為這塊模組使用的晶片並非 NXP 原廠的 RC522,而是相容的副廠晶片(Clone 版)。因此,通訊沒問題,可以完全排除杜邦線接錯或 SPI 協定錯誤的可能。

Step 2無聲的抗議:PICC_IsNewCardPresent 毫無反應

現象:雖然 SPI 通訊正常,晶片也活著,但不管拿什麼卡片貼上去,迴圈裡的 mfrc522.PICC_IsNewCardPresent() 就是讀不到任何東西。

分析:晶片活著卻無法與外界溝通。RFID 讀卡機要能工作,必須主動發射 13.56MHz 的電磁波去喚醒卡片。如果完全讀不到卡,最有可能的嫌疑犯就是:高頻天線根本沒在運作

Step 3追查發射端:天線是否有開啟?

現象:開始往程式碼底層追查,確認程式是否有正確下達開啟天線的指令 PCD_AntennaOn(),並檢查相關的暫存器狀態。

分析:檢查後確認程式邏輯無誤,開啟天線的指令確實發送了,但天線依然沒有發出磁場。這時候,我們的除錯思維必須從「軟體層(Code)」正式跨入「硬體層(Hardware)」。

Step 4抓出真兇:天線啟動瞬間的「斷電休克」

現象:經過精密測試後,發現了一個致命規律——只要程式一執行「開啟天線」的指令,模組就會瞬間斷線或重置。

分析:這是 RC522 模組最惡名昭彰的硬體陷阱!RC522 在待機時非常省電,但當天線啟動、開始激發 13.56MHz 高頻電磁波的瞬間,會產生一個極大的瞬間突波電流(Transient Current,可能飆破 100~150mA)。Arduino 板載的 3.3V 穩壓腳位(或品質較差的麵包板模組)根本來不及瞬間供給這麼大的電流,導致電壓瞬間被拉垮掉到 3.3V 以下。晶片遇到電壓不足,觸發了低壓重置保護(Brown-out Reset),直接當機休克,天線當然就打不開了。

Step 5終極解法:外掛「大電容」完美救援

現象與解法:我們在 RC522 的 3.3V (VCC)GND 腳位之間,直接並聯跨接了一顆 1000µF 的電解電容(手邊現有的,非一定要1000uf)

分析:這顆大電容在這裡扮演了「本地水塔(能量儲存庫)」的角色。當天線瞬間開啟、索求巨大電流時,Arduino 電源來不及給,這顆 1000µF 的電容立刻放電,瞬間補足了晶片所需的突波電流。電壓從此穩如泰山!

🎉 最終結果:
晶片不再休克,天線順利激發強大的電磁波。卡片放上去的瞬間「嗶」聲連連,讀取率直接飆升到 100% 完美狀態!我們甚至能用它來進行後續進階的 MIFARE 全扇區密碼掃描與破解實戰。

💡 工程師總結

這段歷程完美展示了系統開發與排錯的最高心法:「軟體問題找程式,硬體問題找電源」

很多初學者遇到 0x82 或讀不到卡片,往往在軟體堆裡鬼打牆。但透過扎實的邏輯推演,我們精準定位出「瞬間壓降」的物理現象,並用一顆幾塊錢的電容漂亮地解決了硬體本身的物理限制。

下次如果你的感測器或射頻模組出現「通訊正常,但一執行動作就當機」的詭異現象,別忘了先給它的電源線加上一顆旁路電容(Bypass Capacitor),這往往就是滿血復活的關鍵!

2026年2月15日 星期日

電子實作必備:免費線上 SMD 電阻代碼與色碼換算工具

 在進行電子電路實作或是維修時,大家應該都有過這種困擾:拿起一顆電阻,卻一時想不起來「紫、綠、黑」到底是多少歐姆?或者看到電路板上密密麻麻的 SMD 貼片電阻寫著 01C3001,還要特地上網找對照表,非常浪費時間。

雖然網路上有很多計算機,但大多數不是廣告滿天飛,就是介面老舊、手機很難按。

作為一個工程師,「自己的工具自己做」是合情合理的!所以我利用 Google Cloud (Firebase) 技術,開發了一個完全免費、無廣告、且針對手機優化的「全能電阻計算器」

🛠️ 工具網址:https://vkinngworld-81451.web.app/

(建議將此連結設為書籤,或加入手機主畫面)


✨ 這個工具有什麼特色?

不同於一般的色碼查詢網頁,我特別針對實際維修與開發的需求做了優化:

1. 支援 SMD 貼片電阻代碼 (最實用!) 除了傳統色碼,現在更多時候我們是在跟 SMD 零件奮鬥。這個工具支援:

  • 3碼標示法: 例如 104 (= 100kΩ)

  • 4碼標示法: 例如 1002 (= 10kΩ)

  • R小數點標示: 支援 4R7R05 這種常見的低阻值標示法。

2. 四環 / 五環電阻切換 支援精密的五環電阻計算,不用再煩惱第三環到底是倍率還是數值。

3. 視覺化即時預覽 當你選擇顏色時,畫面上的電阻圖案會跟著變色。這對於教學或是確認顏色順序非常有幫助,所見即所得。

4. 手機版介面優化 (Mobile First) 我知道大家通常是在工作桌或實驗室,拿著手機在查資料。所以我特別設計了大按鈕分頁切換,單手就能操作,不用放大縮小找按鈕。


📷 實際操作畫面

SMD電阻計算器介面

SMD電阻計算器介面




🚀 未來開發計畫

這只是第一版,目前我正在研究結合 Google Gemini AI 技術,未來預計加入**「拍照辨識」**功能。直接用手機拍下電路板,AI 就會自動幫你辨識元件規格,敬請期待!

如果你在使用上有發現任何 Bug,或是希望加入什麼新功能 (例如電容代碼、電感色碼?),歡迎在下方留言或是到我的 YouTube 頻道告訴我。

👇 馬上試用: 👉 點此進入 電子實驗室工具箱


喜歡這類電子技術工具嗎?別忘了訂閱我的 YouTube 頻道,獲取更多自動化控制與 Arduino 實作教學!我的Youtube頻道







2023年8月24日 星期四

串口屏-小遊戲製作1-畫面切換

 

我們使用的串口屏,是大陸-淘晶馳的串口屏,人民幣140元,4.3寸IPS高清 800X480

沒有特定的廠商情結,我詢問過台灣的廠商,

台制的要1700元,所以我還是拿大陸做的來試試吧!


淘晶馳的官網在這裡:

https://www.tjc1688.com/

裡面可以下載今天使用的軟件。也可以模擬,滿意後,再買產品即可。

在淘寶有淘晶馳的官方賣場。




在資料下載的-上位機下載,就是這個模擬軟件。
http://wiki.tjc1688.com/download/usart_hmi.html

下面的影片,就是軟件的介紹及畫面切換功能說明






2023年8月22日 星期二

串口屏小遊戲-寶可夢收集

 

前幾篇文章介紹了串口屏,

後續又買了一個新的4.3寸電容觸模屏,解析度800x480像素,價格才140人民幣。

淘寶上買的。


然後摸索了一陣了,做出個簡易的小遊戲,及加上充組模組及鋰電池,

就完成一個簡易可帶出門的小遊戲機了。


可能大人覺得很無聊,但小孩玩的還蠻有趣的。


以下就是示範的影片:


後續再一步一步的發佈製做過程。




2023年8月3日 星期四

Arduino 搭 智能TFT = 簡易UI (

 在上一期,利用智能TFT,就可以達到,自動跳頁的功能了。

但實際上,我們還是需要讓主控,知道目前的頁面,

或是讓主控也能控制跳頁,雙向都能控制,才會比較靈活。


以下是示範的影片:



在這個示範裡面,用的是上一篇文章的智能TFT檔案(有小改)影片裡有提到。


1.Arduino控制換頁功能

基本上就是利用SERIAL PORT來傳送資料給智能TFT,來達到換頁的功能。

這個功能是不必另外寫功能就能達到。

只需要serial port:送出  page page__  \xff\xff\xff   即可。後面的xff是結束碼

__就是換頁的頁面名稱


2.要知道目前智能TFT的頁面

可以利用每個頁面加入sendme的指令,(影片後半段有介紹)

當換頁後,就會自動發送頁號給Arduino,

只是送的資料有含開頭碼跟結束碼,需要分離出來,才是我們要的資料。


後續會把Arduino的數值/字串,傳送到智能TFT,也是比較主要的功能之一。




智能TFT,好用快速的開發你的UI介面,單片機,Arduino適用


先看成果:製作時間1小時內。約20個畫面。無需寫程式。

元件:淘晶馳串口屏 ( https://www.tjc1688.com/index.html)

淘寶價,不到100人民幣<450台幣。

3.5寸,電阻觸控(有電容),320x480


調適的接線:



示範影片:













2023年8月1日 星期二

Arduino uno 搭0.96寸 OLED模組,讓ChatGPT幫我們寫程式

 

接線方式:(參考:https://blog.jmaker.com.tw/arduino-ssd1306-oled/)



第一個程式,寫一個計時功能。(因為Arduino UNO沒有時鐘,所以改計時)

================================

#include <Wire.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>


#define SCREEN_WIDTH 128 // OLED display width, in pixels

#define SCREEN_HEIGHT 64 // OLED display height, in pixels


// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);


void setup() {

  // initialize and clear display

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // Address 0x3C for 128x64

  display.clearDisplay();

  display.display();

}


void loop() {

  display.clearDisplay();

  display.setTextSize(2);

  display.setTextColor(WHITE);


  unsigned long currentTime = millis();

  unsigned long seconds = currentTime / 1000;

  unsigned long minutes = seconds / 60;

  unsigned long hours = minutes / 60;


  seconds = seconds % 60;

  minutes = minutes % 60;

  

  display.setCursor(0,0);

  if (hours < 10)

    display.print('0');

  display.print(hours, DEC);


  display.print(':');

  

  if (minutes < 10)

    display.print('0');

  display.print(minutes, DEC);


  display.print(':');

  

  if (seconds < 10)

    display.print('0');

  display.print(seconds, DEC);


  display.display();

  delay(1000);

}

==========================
實測可行。



第二個程式:把原本數字計時,改成指計。
===========================

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

#define CLOCK_CENTER_X 64
#define CLOCK_CENTER_Y 32
#define CLOCK_RADIUS 30

void setup() {
  // initialize and clear display
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // Address 0x3C for 128x64
  display.clearDisplay();
  display.display();
}

void loop() {
  display.clearDisplay();

  unsigned long currentTime = millis();
  unsigned long seconds = currentTime / 1000;
  unsigned long minutes = seconds / 60;
  unsigned long hours = minutes / 60;

  seconds = seconds % 60;
  minutes = minutes % 60;
  hours = hours % 12;

  // draw clock face
  display.drawCircle(CLOCK_CENTER_X, CLOCK_CENTER_Y, CLOCK_RADIUS, WHITE);
  
  // draw hour hand
  drawHand(hours / 12.0, CLOCK_RADIUS * 0.5, 4);
  
  // draw minute hand
  drawHand(minutes / 60.0, CLOCK_RADIUS * 0.7, 2);

  // draw second hand
  drawHand(seconds / 60.0, CLOCK_RADIUS * 0.9, 1);
  
  display.display();
  delay(1000);
}

void drawHand(float fraction, float handLength, uint8_t handWidth) {
  float angle = fraction * 2.0 * PI - PI / 2;
  int16_t x = (int16_t)(CLOCK_CENTER_X + cos(angle) * handLength);
  int16_t y = (int16_t)(CLOCK_CENTER_Y + sin(angle) * handLength);
  display.drawLine(CLOCK_CENTER_X, CLOCK_CENTER_Y, x, y, WHITE);
  display.drawLine(CLOCK_CENTER_X + 1, CLOCK_CENTER_Y, x + 1, y, WHITE);
  display.drawLine(CLOCK_CENTER_X, CLOCK_CENTER_Y + 1, x, y + 1, WHITE);
  display.drawLine(CLOCK_CENTER_X + 1, CLOCK_CENTER_Y + 1, x + 1, y + 1, WHITE);
}
========================
測試成功:





第三個程式:左邊指令,右邊數字時鐘

=第一次===

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

#define CLOCK_CENTER_X 32
#define CLOCK_CENTER_Y 32
#define CLOCK_RADIUS 30

void setup() {
  // initialize and clear display
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // Address 0x3C for 128x64
  display.clearDisplay();
  display.display();
}

void loop() {
  display.clearDisplay();

  unsigned long currentTime = millis();
  unsigned long seconds = currentTime / 1000;
  unsigned long minutes = seconds / 60;
  unsigned long hours = minutes / 60;

  seconds = seconds % 60;
  minutes = minutes % 60;
  hours = hours % 24;

  // draw clock face
  display.drawCircle(CLOCK_CENTER_X, CLOCK_CENTER_Y, CLOCK_RADIUS, WHITE);
 
  // draw hour hand
  drawHand(hours / 12.0, CLOCK_RADIUS * 0.5, 4);
 
  // draw minute hand
  drawHand(minutes / 60.0, CLOCK_RADIUS * 0.7, 2);

  // draw second hand
  drawHand(seconds / 60.0, CLOCK_RADIUS * 0.9, 1);

  // draw digital clock
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(70,0);
  if (hours < 10)
    display.print('0');
  display.print(hours, DEC);

  display.print(':');
 
  if (minutes < 10)
    display.print('0');
  display.print(minutes, DEC);

  display.print(':');
 
  if (seconds < 10)
    display.print('0');
  display.print(seconds, DEC);
 
  display.display();
  delay(1000);
}

void drawHand(float fraction, float handLength, uint8_t handWidth) {
  float angle = fraction * 2.0 * PI - PI / 2;
  int16_t x = (int16_t)(CLOCK_CENTER_X + cos(angle) * handLength);
  int16_t y = (int16_t)(CLOCK_CENTER_Y + sin(angle) * handLength);
  display.drawLine(CLOCK_CENTER_X, CLOCK_CENTER_Y, x, y, WHITE);
  display.drawLine(CLOCK_CENTER_X + 1, CLOCK_CENTER_Y, x + 1, y, WHITE);
  display.drawLine(CLOCK_CENTER_X, CLOCK_CENTER_Y + 1, x, y + 1, WHITE);
  display.drawLine(CLOCK_CENTER_X + 1, CLOCK_CENTER_Y + 1, x + 1, y + 1, WHITE);
}

數字超過寬度,跑到下一行。


請chatgpt修改:寬度

=====第二次============

#include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); #define CLOCK_CENTER_X 32 #define CLOCK_CENTER_Y 32 #define CLOCK_RADIUS 30 void setup() { // initialize and clear display display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Address 0x3C for 128x64 display.clearDisplay(); display.display(); } void loop() { display.clearDisplay(); unsigned long currentTime = millis(); unsigned long seconds = currentTime / 1000; unsigned long minutes = seconds / 60; unsigned long hours = minutes / 60; seconds = seconds % 60; minutes = minutes % 60; hours = hours % 24; // draw clock face display.drawCircle(CLOCK_CENTER_X, CLOCK_CENTER_Y, CLOCK_RADIUS, WHITE); // draw hour hand drawHand(hours / 12.0, CLOCK_RADIUS * 0.5, 4); // draw minute hand drawHand(minutes / 60.0, CLOCK_RADIUS * 0.7, 2); // draw second hand drawHand(seconds / 60.0, CLOCK_RADIUS * 0.9, 1); // draw digital clock display.setTextSize(1); // reduce text size display.setTextColor(WHITE); display.setCursor(72,0); // adjust cursor position if (hours < 10) display.print('0'); display.print(hours, DEC); display.print(':'); if (minutes < 10) display.print('0'); display.print(minutes, DEC); display.print(':'); if (seconds < 10) display.print('0'); display.print(seconds, DEC); display.display(); delay(1000); } void drawHand(float fraction, float handLength, uint8_t handWidth) { float angle = fraction * 2.0 * PI - PI / 2; int16_t x = (int16_t)(CLOCK_CENTER_X + cos(angle) * handLength); int16_t y = (int16_t)(CLOCK_CENTER_Y + sin(angle) * handLength); display.drawLine(CLOCK_CENTER_X, CLOCK_CENTER_Y, x, y, WHITE); display.drawLine(CLOCK_CENTER_X + 1, CLOCK_CENTER_Y, x + 1, y, WHITE); display.drawLine(CLOCK_CENTER_X, CLOCK_CENTER_Y + 1, x, y + 1, WHITE); display.drawLine(CLOCK_CENTER_X + 1, CLOCK_CENTER_Y + 1, x + 1, y + 1, WHITE); }

是ok的。

拍照有斷線,實際是沒有的