ESP32輔助板背面I2C使用方法

資料來源:

1.https://youyouyou.pixnet.net/blog/post/121069748-esp32-x-nbiot-%e8%bc%94%e5%8a%a9%e7%89%88-%e8%83%8c%e9%9d%a2i2c-lcd?pixfrom=related
2.IoT物聯網應用:使用 ESP32 開發版與 Arduino C 程式語言,尤濬哲老師著

本文實作尤老師製作的「ESP32輔助板」背面 I2C 使用方法,背面的I2C為附加的PCF85741 I2C晶片,因此使用的腳位是 26 ,27 而非 21 , 22。

要使用ESP32輔助板背面的 I2C 介面,必須將 SDA 設為 GPIO 26,SCL 設為 GPIO 27。每個 I2C 裝置都會有一個位址,宣告錯誤的位址是不會有任何反應的,所以必須先做一次 I2C 掃描,取得正確位置。

我們可以使用下面程式碼掃描 I2C 的位址:

#include <Wire.h>

void setup()
{
  Serial.begin (115200);  
  Wire.begin (26, 27);   // sda=GPIO_26, scl=GPIO_27
}

void Scanner ()
{
  Serial.println ();
  Serial.println ("I2C scanner. Scanning ...");
  byte count = 0;

  Wire.begin();
  for (byte i = 8; i < 120; i++)
  {
    Wire.beginTransmission (i); // Begin I2C transmission Address (i)
    if (Wire.endTransmission () == 0) //0=success(ACK response) 
    {
      Serial.print ("Found address: ");
      Serial.print (i, DEC);
      Serial.print (" (0x");
      Serial.print (i, HEX);     // PCF8574 7 bit address
      Serial.println (")");
      count++;
    }
  }
  Serial.print ("Found ");      
  Serial.print (count, DEC);        // numbers of devices
  Serial.println (" device(s).");
}

void loop()
{
  Scanner ();
  delay (5000);
}

我接上一個 0.96吋的 OLED 螢幕,掃描到的 I2C 的位置有3個,分別是

I2C scanner. Scanning …
Found address: 32 (0x20)
Found address: 60 (0x3C)
Found address: 80 (0x50)
Found 3 device(s).

I2C 位址掃描結果

用 Arduino 打開 AdafruitSSD1306 範例檔,我參考網路上資源加上了一些文字顯示,程式碼如下:

/**************************************************************************
 This is an example for our Monochrome OLEDs based on SSD1306 drivers

 Pick one up today in the adafruit shop!
 ------> http://www.adafruit.com/category/63_98

 This example is for a 128x64 pixel display using I2C to communicate
 3 pins are required to interface (two I2C and one reset).

 Adafruit invests time and resources providing this open
 source code, please support Adafruit and open-source
 hardware by purchasing products from Adafruit!

 Written by Limor Fried/Ladyada for Adafruit Industries,
 with contributions from the open source community.
 BSD license, check license.txt for more information
 All text above, and the splash screen below must be
 included in any redistribution.
 **************************************************************************/

#include <SPI.h>
#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)
// The pins for I2C are defined by the Wire-library. 
// On an arduino UNO:       A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO:   2(SDA),  3(SCL), ...
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///這裡要設定Address為 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define NUMFLAKES     10 // Number of snowflakes in the animation example

#define LOGO_HEIGHT   16
#define LOGO_WIDTH    16
static const unsigned char PROGMEM logo_bmp[] =
{ 0b00000000, 0b11000000,
  0b00000001, 0b11000000,
  0b00000001, 0b11000000,
  0b00000011, 0b11100000,
  0b11110011, 0b11100000,
  0b11111110, 0b11111000,
  0b01111110, 0b11111111,
  0b00110011, 0b10011111,
  0b00011111, 0b11111100,
  0b00001101, 0b01110000,
  0b00011011, 0b10100000,
  0b00111111, 0b11100000,
  0b00111111, 0b11110000,
  0b01111100, 0b11110000,
  0b01110000, 0b01110000,
  0b00000000, 0b00110000 };

void setup() {
  Serial.begin (115200);  
  Wire.begin (26, 27);   // sda=GPIO_26, scl=GPIO_27,設I2C腳位為26,27
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
  display.display();
  delay(2000); // Pause for 2 seconds

  // Clear the buffer
  display.clearDisplay();

  // Draw a single pixel in white
  display.drawPixel(10, 10, SSD1306_WHITE);

  // Show the display buffer on the screen. You MUST call display() after
  // drawing commands to make them visible on screen!
  display.display();
  delay(2000);
  // display.display() is NOT necessary after every single drawing command,
  // unless that's what you want...rather, you can batch up a bunch of
  // drawing operations and then update the screen all at once by calling
  // display.display(). These examples demonstrate both approaches...

  testdrawline();      // Draw many lines

  testdrawrect();      // Draw rectangles (outlines)

  testfillrect();      // Draw rectangles (filled)

  testdrawcircle();    // Draw circles (outlines)

  testfillcircle();    // Draw circles (filled)

  testdrawroundrect(); // Draw rounded rectangles (outlines)

  testfillroundrect(); // Draw rounded rectangles (filled)

  testdrawtriangle();  // Draw triangles (outlines)

  testfilltriangle();  // Draw triangles (filled)

  testdrawchar();      // Draw characters of the default font

  testdrawstyles();    // Draw 'stylized' characters

  testscrolltext();    // Draw scrolling text

  testdrawbitmap();    // Draw a small bitmap image

  // Invert and restore display, pausing in-between
  display.invertDisplay(true);
  delay(1000);
  display.invertDisplay(false);
  delay(1000);

  testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps
}

void loop() {
  display.setTextColor(WHITE);                           //設定顯示文字的顏色
  display.setTextSize(1);                                        //設定第1行文字的大小為1
  display.setCursor(0,0);                                        //設定第1行文字在oled上顯示的座標
  display.println("Hello, world!");                         //設定第1行顯示的文字

  display.setTextSize(2);                                        //設定第2行文字的大小為2
  display.setCursor(0,15);                                      //設定第2行文字在oled上顯示的座標
  display.println("First");                                       //設定第2行顯示的文字

  display.setTextSize(3);                                        //設定第3行文字的大小為3
  display.setCursor(0,40);                                      //設定第3行文字在oled上顯示的座標
  display.println("Second");                                  //設定第3行顯示的文字

  display.display();                                                 //顯示資料緩衝區內的資料
  delay(2000);
  display.clearDisplay();                                        //清除資料緩衝區內的資料
}

void testdrawline() {
  int16_t i;

  display.clearDisplay(); // Clear display buffer

  for(i=0; i<display.width(); i+=4) {
    display.drawLine(0, 0, i, display.height()-1, SSD1306_WHITE);
    display.display(); // Update screen with each newly-drawn line
    delay(1);
  }
  for(i=0; i<display.height(); i+=4) {
    display.drawLine(0, 0, display.width()-1, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=0; i<display.width(); i+=4) {
    display.drawLine(0, display.height()-1, i, 0, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  for(i=display.height()-1; i>=0; i-=4) {
    display.drawLine(0, display.height()-1, display.width()-1, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=display.width()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-1, i, 0, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  for(i=display.height()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-1, 0, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=0; i<display.height(); i+=4) {
    display.drawLine(display.width()-1, 0, 0, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  for(i=0; i<display.width(); i+=4) {
    display.drawLine(display.width()-1, 0, i, display.height()-1, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000); // Pause for 2 seconds
}

void testdrawrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2; i+=2) {
    display.drawRect(i, i, display.width()-2*i, display.height()-2*i, SSD1306_WHITE);
    display.display(); // Update screen with each newly-drawn rectangle
    delay(1);
  }

  delay(2000);
}

void testfillrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2; i+=3) {
    // The INVERSE color is used so rectangles alternate white/black
    display.fillRect(i, i, display.width()-i*2, display.height()-i*2, SSD1306_INVERSE);
    display.display(); // Update screen with each newly-drawn rectangle
    delay(1);
  }

  delay(2000);
}

void testdrawcircle(void) {
  display.clearDisplay();

  for(int16_t i=0; i<max(display.width(),display.height())/2; i+=2) {
    display.drawCircle(display.width()/2, display.height()/2, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testfillcircle(void) {
  display.clearDisplay();

  for(int16_t i=max(display.width(),display.height())/2; i>0; i-=3) {
    // The INVERSE color is used so circles alternate white/black
    display.fillCircle(display.width() / 2, display.height() / 2, i, SSD1306_INVERSE);
    display.display(); // Update screen with each newly-drawn circle
    delay(1);
  }

  delay(2000);
}

void testdrawroundrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2-2; i+=2) {
    display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i,
      display.height()/4, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testfillroundrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2-2; i+=2) {
    // The INVERSE color is used so round-rects alternate white/black
    display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i,
      display.height()/4, SSD1306_INVERSE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testdrawtriangle(void) {
  display.clearDisplay();

  for(int16_t i=0; i<max(display.width(),display.height())/2; i+=5) {
    display.drawTriangle(
      display.width()/2  , display.height()/2-i,
      display.width()/2-i, display.height()/2+i,
      display.width()/2+i, display.height()/2+i, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testfilltriangle(void) {
  display.clearDisplay();

  for(int16_t i=max(display.width(),display.height())/2; i>0; i-=5) {
    // The INVERSE color is used so triangles alternate white/black
    display.fillTriangle(
      display.width()/2  , display.height()/2-i,
      display.width()/2-i, display.height()/2+i,
      display.width()/2+i, display.height()/2+i, SSD1306_INVERSE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testdrawchar(void) {
  display.clearDisplay();

  display.setTextSize(1);      // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE); // Draw white text
  display.setCursor(0, 0);     // Start at top-left corner
  display.cp437(true);         // Use full 256 char 'Code Page 437' font

  // Not all the characters will fit on the display. This is normal.
  // Library will draw what it can and the rest will be clipped.
  for(int16_t i=0; i<256; i++) {
    if(i == '\n') display.write(' ');
    else          display.write(i);
  }

  display.display();
  delay(2000);
}

void testdrawstyles(void) {
  display.clearDisplay();

  display.setTextSize(1);             // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE);        // Draw white text
  display.setCursor(0,0);             // Start at top-left corner
  display.println(F("Hello, world!"));

  display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
  display.println(3.141592);

  display.setTextSize(2);             // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.print(F("0x")); display.println(0xDEADBEEF, HEX);

  display.display();
  delay(2000);
}

void testscrolltext(void) {
  display.clearDisplay();

  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10, 0);
  display.println(F("scroll"));
  display.display();      // Show initial text
  delay(100);

  // Scroll in various directions, pausing in-between:
  display.startscrollright(0x00, 0x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);
  display.startscrollleft(0x00, 0x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);
  display.startscrolldiagright(0x00, 0x07);
  delay(2000);
  display.startscrolldiagleft(0x00, 0x07);
  delay(2000);
  display.stopscroll();
  delay(1000);
}

void testdrawbitmap(void) {
  display.clearDisplay();

  display.drawBitmap(
    (display.width()  - LOGO_WIDTH ) / 2,
    (display.height() - LOGO_HEIGHT) / 2,
    logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
  display.display();
  delay(1000);
}

#define XPOS   0 // Indexes into the 'icons' array in function below
#define YPOS   1
#define DELTAY 2

void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) {
  int8_t f, icons[NUMFLAKES][3];

  // Initialize 'snowflake' positions
  for(f=0; f< NUMFLAKES; f++) {
    icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
    icons[f][YPOS]   = -LOGO_HEIGHT;
    icons[f][DELTAY] = random(1, 6);
    Serial.print(F("x: "));
    Serial.print(icons[f][XPOS], DEC);
    Serial.print(F(" y: "));
    Serial.print(icons[f][YPOS], DEC);
    Serial.print(F(" dy: "));
    Serial.println(icons[f][DELTAY], DEC);
  }

  for(;;) { // Loop forever...
    display.clearDisplay(); // Clear the display buffer

    // Draw each snowflake:
    for(f=0; f< NUMFLAKES; f++) {
      display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SSD1306_WHITE);
    }

    display.display(); // Show the display buffer on the screen
    delay(200);        // Pause for 1/10 second

    // Then update coordinates of each flake...
    for(f=0; f< NUMFLAKES; f++) {
      icons[f][YPOS] += icons[f][DELTAY];
      // If snowflake is off the bottom of the screen...
      if (icons[f][YPOS] >= display.height()) {
        // Reinitialize to a random position, just off the top
        icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
        icons[f][YPOS]   = -LOGO_HEIGHT;
        icons[f][DELTAY] = random(1, 6);
      }
    }
  }
}

OLED顯示影片如下:https://www.youtube.com/watch?v=9GmNvnwMRaI

如果使用的OLED是 128×64,無法正常顯示的話,需要修改 Arduino下 Libraries 資料夾中Adafruit_SSD1306的 Adafruit_SSD1306.h,修改為 #define SSD1306_128_64。如下圖:

How to remove and reinstall Node-Red in macOS Big Sur 11.4

資料來源:
1. https://www.hackster.io/daquilnp/3-steps-to-installing-node-red-and-bean-nodes-on-mac-os-ebaee2
2.https://stackoverflow.com/questions/11177954/how-do-i-completely-uninstall-node-js-and-reinstall-from-beginning-mac-os-x?newreg=c6cfabe02a584b76a1812859f2575651
3.https://stackabuse.com/how-to-uninstall-node-js-from-mac-osx/
4.https://diyprojects.io/installing-using-node-red-macos-windows/

Uninstall Node-red

原本已經參考這位大神(資料來源1)他的安裝步驟,安裝完成Node-Red,因 使用過程中,Tensorfolw coco ssd 節點啟動不起來,移除也無法移除,只要重新安裝。

第一次刪除重新安裝步驟(失敗):(參考益師傅Node-red Windows版災難復原)

1.將隱藏檔案顯示出來

Shift + Command + .
這樣就可以顯示出隱藏的檔案囉,只要再按一次就可以重新隱藏。(參考MacUknow

Mac 隱藏檔案

2.備份.Node-Red 檔案夾的這三個檔案

(1)flows.json:這是原本流程頁簽內所有的流程的檔案,重安裝完成後匯入 Node-Red。
(2)package.json:這是原本已安裝節點的檔案,重安裝完成後匯入 Node-Red
(3)settings.js:這是Node_Red 的設定檔,存放著使用者帳號、密碼,重安裝完成後,將帳號、密碼相關資料複製到新的 settings.js 檔案中。

3.刪除.Node-Red 相關檔案夾
(1)刪除.node-Red 檔案夾
(2)刪除.node-gyd 檔案夾
(3)刪除.nmp 檔案夾

4.重新開機。

5.重新安裝Node-Red

使用大神安裝檔安裝,會把 Node-Red 與 Light Blue Bean節點一起安裝,但是我主要是安裝 Node-Red ,Light Blue Bean節點一起安裝,只要不用其實沒有什麼關係,或者安裝完成 Node-Red 後,再把它移除,以免與想要使用的節點衝到。

打開 安裝檔的目錄按滑鼠右鍵 > 服務 > 終端機視窗打開 ,

安裝完成後,Tensorfolw coco ssd 節點仍然啟動不起來,只好再次刪除重裝!!!

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

第二次刪除重新安裝步驟:(參考資料來源2資料來源3

1.將隱藏檔案顯示出來

Shift + Command + .
這樣就可以顯示出隱藏的檔案囉,只要再按一次就可以重新隱藏。(參考MacUknow

2.備份.Node-Red 檔案夾的這三個檔案(先前已完成)

3.刪除 Node-Red 所有相關檔案夾
(1)刪除.node-Red 檔案夾
(2)刪除.node-gyd 檔案夾
(3)刪除.nmp 檔案夾
(4)再參考資料來源2資料來源3 手動刪除資料夾或檔案。
下面這些可能就是所有的檔案,但是作者不確定,但我刪除這些就沒問題了。

Uninstall Node-red 手動刪除檔案清單

這樣刪除後就算是手動乾淨移除 Node-red了!

Install Node-red

參考 資料來源4 進行安裝:

1.先安裝 Node.js :前往官方網站下載 安裝檔,請下載14.17.5 LTS 版本,或者是參考 這裡 使用 Command Line 手動安裝。

Node.js官方下載點
Node-red Command Line 安裝

2.安裝 Node-red

(1)打開終端機輸入下面程式碼

sudo npm install -g --unsafe-perm node-red

您必須在 sudo 的 npm 命令之前執行。 -g 表示將上傳到 npm 目錄,並且可用於所有 node.js。 –unsafe-perm 允許在出現錯誤時繼續安裝。

(2)安裝完成後,打開終端機輸入下面程式碼:

killall Finder

重新啟動 Finder

(3)啟動 Node-red:再次打開終端機輸入下面程式碼:

node-red
啟動 Node-red

(4)最後打開瀏覽器,輸入:http://127.0.0.1:1880

Done !

ESP32學習筆記-01:ESP32輔助板-01

資料來源:
1.IoT 物聯網應用:使用ESP32開發版與 Arduino C 程式語言
2.ESP32 x NBIoT 模組 i2c燈號測試程式

這是正式學習ESP32及Arduino C 語言的第一步,買了尤濬哲老師的書「IoT 物聯網應用:使用ESP32開發版與 Arduino C 程式語言」,以及ESP32、ESP32Cam、ESP32輔助板,入門IOT領域。

以下是尤老師的程式說明:
本範例說明如何使用本教學輔助板(ESP32 Matrix)的LED
教學板使用GPIO 26,27連接附加的I2C晶片
一、依照LED編號開啟或關閉:
MatrixInt(LED編號,開關),燈號(0~7),開關(1/0)

燈編號列表:
1.訊號指示燈:0~4
2.紅色LED:5
3.綠色LED:6
4.藍色LED:7

Example:
MatrixInt(1,1);//開1號燈
MatrixInt(5,0);//關5號燈


二、按信號強度開啟訊號指示燈:
MatrixLEDrssi(rssi);
MatrixLEDrssi(-60);//大於-65:5 顆Leds
MatrixLEDrssi(-70);//大於-75:4 顆Leds
MatrixLEDrssi(-80);//大於-85:3 顆Leds
MatrixLEDrssi(-90);//大於-95:2 顆Leds
MatrixLEDrssi(-100);//大於-105:1 顆Leds
//其餘均為0 Led

三、程式碼:(小弟有做一些小修改)

#include <Wire.h>
#include "MatrixInt.h"

void setup() {
  Serial.begin(115200);
  Wire.begin(26, 27);
}

void loop() {
  Serial.println("Turn on ALL LEDs by ID");
  for (int i = 0; i < 8; ++i)
  {
    MatrixInt(i, 1);//亮燈
    Serial.println("LED " +  String(i) + " 亮燈");//在序列監視埠輸出顯示「LED 號碼 亮燈」
    delay(1000);
    MatrixInt(i, 0);//關燈    
  }
  Serial.println("Turn on RSSI LED by RSSI value");

  for (int i = -50; i > -120; i=i-1)
  {
    Serial.println("RSSI = " +  String(i));
    MatrixLEDrssi(i);
    delay(300);
  }
  delay(1000);
}

Raspberry Pi 安裝中文輸入法

1.透過指令安裝SCIM輸入法

(若是只想安裝類似新注音的輸入法,直接跳到下一步即可。)
$ sudo apt-get install scim scim-tables-zh

2.安裝SCIM輸入法中的新酷音(類似新注音)

$ sudo apt-get install scim-chewing

安裝第1項會產生非常多的輸入法,我用不到,所以只安裝第2項的新酷音輸入法。

3.設定中文輸入法

請先重開機($ sudo reboot),再到目錄下找到SCIM。

雖然只有安裝新酷音輸入法,但都是在SCIM下設定,到介面全域設定,故只設定新酷音輸入法,預設的輸入法啟用為ctrl+space(空白鍵),左邊shift為切換語言鍵。

4.再次重新開機,設定才算完成

$ sudo reboot

重新開機後,透過指令 $scim 就能夠開啟輸入法