安裝與設定Raspberry Pi的RealVNC伺服器

VNC伺服器可讓使用者從遠端以圖像介面操控Raspberry Pi,我在樹莓派一代安裝的是免費的TightVNC,它雖然可以被設定支援在遠端和本機電腦端進行文字剪貼,但是它僅支援Latin-1編碼,中文字在剪貼之後會變成??問號亂碼。

所以我改用RealVNC,它有分成免費、個人和商業版,最主要的差異在於免費版不支援加密(encryption)、也不具備檔案傳輸功能,但是剪貼中文沒問題。詳細的功能比較,請參閱realvnc.com的VNC產品頁面

取得RealVNC的授權碼

啟動RealVNC伺服器需要輸入授權碼,安裝之前,請先連到Activate VNC頁面,點選“Free license only,…(僅免費授權)”選項、輸入你的大名、e-mail、確認e-mail、選擇國家,最後按下“Continue(繼續)”,即可收到授權碼。

安裝RealVNC

以下操作步驟節錄自RealVNC的Raspberry Pi產品頁面

1. 在樹莓派的終端機執行底下兩行敘述,下載最新版的RealVNC軟體並解壓縮

$ curl -L -o VNC.tar.gz https://www.realvnc.com/download/binary/latest/debian/arm/ 
tar xvf VNC.tar.gz 

解壓縮之後可取得Server(伺服器)和Viewer(用戶端)兩個套件。目前的版本是5.2.3。

2. 只需要安裝VNC伺服器

$ sudo dpkg -i VNC-Server-5.2.3-Linux-ARM.deb 

3. 加入授權碼

$ sudo vnclicense -add  授權碼

初次啟動RealVNC伺服器軟體時,它的操作視窗可能會要求我們輸入連線密碼,請在Users & Permissions(使用者和權限)畫面輸入密碼和確認密碼(按下右上角的Add鈕新增密碼):

Users & Permissions(使用者和權限)畫面

啟動VNC伺服器:Service Mode

VNC伺服器有兩種啟動模式,我們可選用任一種:

  • Service Mode(服務模式):VNC連線用戶的操作以及所見畫面,都和主機同步,相當於直接操作Pi。
  • Virtual Mode(虛擬模式):VNC伺服器將為連線用戶建立「虛擬桌面」,因此VNC用戶的操作不影響同樣在操作Pi的用戶。

使用免費版初次啟動「服務模式」時,需要設定連線密碼。請執行底下的指令,然後跟著指示輸入兩次密碼:

$ sudo vncpasswd /root/.vnc/config.d/vncserver-x11 

接著輸入底下的指令啟動VNC伺服器:

$ sudo /etc/init.d/vncserver-x11-serviced start 

如果需要在開機時啟動VNC伺服器,請輸入:

$ sudo update-rc.d vncserver-x11-serviced defaults 

日後若想要取消開機啟動VNC伺服器,請輸入:

$ sudo update-rc.d vncserver-x11-serviced remove 

使用VNC Viewer(用戶端)連線Pi

預設的RealVNC伺服器埠號是5900,假設樹莓派的IP位址是192.168.1.11,從個人電腦或手機、平板的VNC Viewer軟體(如:Chrome線上應用商店的VNC Viewer)連線時,要輸入5900埠號,Picture quality(影像品質)選擇"Automatic"(自動)即可:

輸入連線IP位址和埠號

連線時,VNC會顯示底下的訊息,提示這是「未加密連線」(點選左下角的核取方塊,就不會再出現這個訊息了):

「未加密連線」提示

輸入密碼即可連線:

樹莓派本機的IP位址可透過ifconfig指令,或者按一下系統右上角的VNC選單圖示,即可從底下的畫面看到IP位址(註:在VNC選單按滑鼠右鍵,選擇Options指令,可設定VNC伺服器的參數)。

顯示伺服器的IP位址

設定樹莓派的初始螢幕解析度(方法一):透過例行性工作排程(crontab)

如果沒有接上螢幕開機,樹莓派預設的螢幕解析度將是640×480,VNC用戶端的連線畫面也是不堪使用的640×480。在終端機執行xrandr(原意為X Rotate and Reflect Extension,改變大小與旋轉擴充),將能顯示當前的螢幕解析度:

$ xrandr
xrandr: Failed to get size of gamma for output default
Screen 0: minimum 640 x 480, current 640 x 480, maximum 640 x 480
default connected 640x480+0+0 0mm x 0mm

根據Raspberry Pi官方論壇的“X11VNC headless resolution”這一篇討論,透過底下的指令建立一個例行性工作排程(crontab)設定螢幕解析度:

$ crontab -e 

然後在此排程最後加入這一行敘述:

@reboot /bin/fbset -g 1366 768 1366 768 16 

按下Ctrl+O鍵寫入、Ctrl+X退出之後,重新啟動,就能獲得1366×768像素、16位元色彩深度的VNC畫面了。

註:fbset用於設定顯示卡的frame buffer(圖框緩衝區)參數-g代表geometry(尺寸),後面的數字分別代表“水平可見解析度(像素)”、“垂直可見解析度”、“虛擬水平解析度”、“虛擬垂直解析度”以及“色彩深度(亦即,每個像素的色彩位元數,16位元是數千色,24位元是全彩)”。

設定樹莓派的初始螢幕解析度(方法二):使用HDMI轉VGA接頭

有些電腦必須連接顯示器,顯示卡的GPU才會啟動,像Apple的Mac Mini就是一例。若需要在不連接螢幕的情況下,開啟GPU運算功能,可以購買一個像Headless Ghost的HDMI模擬器,將它插在電腦的HDMI座。

或者,依照macminicolo的方法(Build a Dummy Dongle for a headless Mac mini),準備一條HDMI轉VGA線,在VGA端子那一端的第16腳,插入一個電阻(原作者用的是85Ω,我用的是120Ω),也能騙過電腦。將此HDMI轉換線插在樹莓派上:

用HDMI轉VGA線改成HDMI模擬器

Pi的系統不需要任何設定,也不需要加上例行性工作排程,VNC連線的解析度就是1920×1080的Full HD解析度了。 當然,測試成功之後,我實際並沒有採用這種方式,用上一節的方法就行了。

啟動VNC伺服器:Virtual Mode

多數人都是使用Virtual Mode(虛擬模式)連接VNC。在終端機視窗輸入底下的指令,便能以「虛擬模式」啟動VNC伺服器:

$ vncserver 

我們可以在指令後面加上解析度的參數,以1366×768解析度為例:

$ vncserver -geometry 1366x768 

免費版的RealVNC伺服器最多可接受5個用戶端同時連線(亦即,虛擬5個螢幕)。虛擬模式的VNC埠號從5901~5905。假設樹莓派的IP位址是192.168.1.11,請在VNC用戶端使用5901埠號連線:

若要關閉此虛擬模式的VNC伺服器,請輸入(1代表虛擬的螢幕編號):

$ vncserver -kill :1 

開機啟動虛擬模式的VNC伺服器

開機啟動虛擬模式的VNC伺服器的設定步驟,和其他VNC伺服器軟體大同小異,除了在 /etc/init.d/ 路徑下建立控制腳本(script)之外,比較簡單的方法是在家目錄底下的“.config(設置)”隱藏目錄裡面,存入開機設置程序,底下的步驟修改自Adafriut的TightVNC開機啟動設定

切換到家目錄.config目錄

$ cd ~/.config 

在此目錄底下新增一個autostart(自動啟動)資料夾:

$ mkdir autostart 

使用nano文字編輯器,在autostart路徑底下新增一個“realvnc.desktop”檔:

$ nano autostart/realvnc.desktop 

在此文字檔中輸入底下的內容:

[Desktop Entry]
Type=Application
Name=RealVNC
Exec=vncserver -geometry 1366x768
StartupNotify=false

按下Ctrl+O寫入、Ctrl+X關閉檔案。

日後每次開機時,Pi都會啟動虛擬模式的VNC伺服器。

Posts created 467

20 thoughts on “安裝與設定Raspberry Pi的RealVNC伺服器

  1. 老師請問Arduino怎麼寫1個按鈕開關控制8燈跑馬燈?
    按一下循環位移1燈,還要能保持狀態?
    搞了2天了~先用8051那麼簡單的東西,發現難度超高,
    再來用Arduino,翻了你的書沒範例,翻了另外一本揚明豐出的,有4燈的範例,但是是教錯誤的,上傳程式到晶片後,按鈕都還沒按就自己輪流在跑了T_T,搜遍網路居然找不到現成的範例,因為急用沒時間自己編譯.

    1. hi dull-boy:

      《超圖解Arduino互動設計入門》第四章有4個跑馬燈範例,你的需求可用「動手做4-4」單元的陣列範例,加上開關電路改寫。

      「按一下循環位移1燈」指的是「假設現在亮LED1,按一下按鈕,亮LED2」嗎?這樣的話,你只需用一個變數紀錄LED陣列的索引值即可。每按一下,索引值加1;若索引值超出LED陣列範圍,就索引設成0。

      如果是「按一下,先輪流點亮所有的LED一次,第一次停在LED1,第二次停在LED2…」,那就相當於「每次輪流點亮8個LED,只是起始點不同:第一次從LED1開始,第二次從LED2開始…」。這同樣也是設定一個「起始」索引值,每次循環之後,起始索引值加1…點亮LED的程式可以寫成一個函式,依照需求從loop()循環呼叫…這樣就行啦!

      thanks,
      jeffrey

  2. LED亮燈順序就
    00000000,
    10000000,
    01000000,
    00100000,
    00010000,
    00001000,
    00000100,
    00000010,
    00000001,
    謝老師!

  3. 因為急用,後來讓我找到8051現成的範例,我用89S2051搞定了,
    看似簡單的動作,如果不是老師這種等級,是寫不出來的.

    1. 沒有啦~如果所有我們想達成的功能,都有現成範例可以套用,那就不需要工程師了 :-p 寫程式初期,真正厲害的是筆和紙。底下是依照我上個留言,修改自書本4-4範例的程式碼:

      const byte LEDs[] = {8,9,10,11,12};
      const byte total = sizeof(LEDs);
      byte index = 0;
      byte lastLightLED = 0; // 紀錄上一次點亮的LED
      
      const byte SW = 2;     // 開關的腳位
      boolean buttonState;             // 當前讀取到的開關值
      boolean lastButtonState = LOW;   // 上一次紀錄的開關值
      long lastDebounceTime = 0;  // 紀錄上一次開關發生變化的時間
      long debounceDelay = 50;    // 消除彈跳的時間間隔(ms)
      
      void setup() {
        for (byte i=0; i debounceDelay) {
          // 延遲一段時間(50ms)後,若確認開關狀態改變了...
          if (reading != buttonState) {
            // 讀取開關值
            buttonState = reading;
            // 若開關狀態為「高電位」...
            if (buttonState == HIGH) {
              // 熄滅上一次點亮的LED
              digitalWrite(LEDs[lastLightLED], LOW);
              lastLightLED = index;
              // 點亮索引指定的LED;
              digitalWrite(LEDs[index], HIGH);
              // 設定索引值
              if (index < total) {
                index ++;
              } 
              else {
                index = 0;
              }
            }
          }
        }
        
        // 紀錄開關狀態
        lastButtonState = reading;
      }
      

      thanks,
      jeffrey

  4. 謝老師,不過我把他改成亮8燈後怪怪的,循環第一輪正常,第二輪就只跳固定2燈ˇˇ

    1. 拍謝,剛剛發現這一行程式碼錯了:

      // 熄滅上一次點亮的LED
      digitalWrite(lastLightLED, LOW);
      

      正確的寫法:

      // 熄滅上一次點亮的LED
      digitalWrite(LEDs[lastLightLED], LOW);
      

      thanks,
      jeffrey

  5. 老師我把接腳指定為const byte LEDs[] = {5,6,7,9,10,11,12,13};
    可正常點燈;但是陣列只要放入接腳8,當燈跑到全熄,跑第2輪他就會和第5接腳的LED一起點亮,好奇怪喔

  6. 指定這樣const byte LEDs[] = {5,6,7,8,9,10,11,12,13};
    第8腳卻可以照順序亮燈ˇˇ這是為何阿,老師這太深澳了吧
    急用這程式的當天,我有不小心把4針的排針,放在板子USB下方4個接點+ D+ D- -的下方,會是板子短路嗎?
    可若短路故障,為何還能寫入程式,就上述第8腳怪怪的.
    本來嚇一跳以為電腦端USB燒了,我曾經不小心燒掉一個電腦端的USB孔T_T

  7. 試了一下,只要頭尾出現雙數腳位,第8腳輸出led燈就會不正常,
    這需要老師開導了@_@

    1. 我是在紙上構想程式,沒有實際測試,會在「第二輪」時點亮第8腳,我也很納悶,請再試試看,把loop()當中的判斷條件式:

      // 設定索引值
      if (index < total) {
        index ++;
      } else {
        index = 0;
      }
      

      改成底下的敘述:

      // 設定索引值
      index = (++index) % total;
      

      thanks,
      jeffrey

  8. 改成老師的說明,怎麼試都正常了
    老師真是太強大了
    說真的`網上是很多範例,可是都是還沒按鈕就自己跑燈了,很難想像錯誤的資訊怎還PO上網ˇˇ

  9. 您好,請問一下:

    WAMP,就是架站綜合包,可以「安裝」,也可以「portable可攜式」,請問,這兩個差在哪?網路上都找不到答案耶!請多講一點,越詳細越好,感恩不盡!

    1. 啊這不就像統一麵,看你要帶著直接乾吃,還是泡在湯碗裡面吃,反正都能解饞……

      thanks,
      jeffrey

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top