---
# System prepended metadata

title: ESP32-s

---

# ESP32-s
by Tseng

* [Raspberry pi pico w參考](https://hackmd.io/@9FlBS64-RXuEYo-jZ-hMBw/SyOu0sF8i)
* [ESP32線上模擬器](https://wokwi.com/)
# GPIO
* ![](https://i.imgur.com/94cxZfE.png)
* **GPIO注意事項**
    * 1. GPIO 34,35,36,39：僅能input,不能作為output 
    * 2. GPIO 0,6,7,8,9,10,11：系統使用 
    * 3. GPIO 12, 2 燒錄時不可接任何裝置，燒錄完成後再接回，否則會upload失敗。
    * 4. WiFi 模式啟動後，2,4,12,13,14,15,25,26,27僅能digital讀取，不可analog
    * 5. DHT11: 不可以用GPIO 5、26
# Arduino IDE 設定
    * 將下列網址貼上 arduino ide的Preferences
    * https://dl.espressif.com/dl/package_esp32_index.json
![](https://i.imgur.com/yNql9ws.png) 
![](https://i.imgur.com/W3fWQKJ.png)

 ![](https://i.imgur.com/nL2xIog.png)
 ![](https://i.imgur.com/Pi2F7j2.png)

## SHT31 溫溼度感測器
* ### 基本測試 ![](https://i.imgur.com/r3wFNQ6.png)
* ![](https://i.imgur.com/rPrpDrh.png)
* ## [github 程式碼](https://github.com/chiangyih/ESP32/blob/a513df02b1705df2cfa3223cbda4f09cce75951c/SHT31_%E6%BA%AB%E6%BA%BC%E5%BA%A6/sht31-basic/sht31-basic.ino) 


---
## SHT31使用OLED顯示
![](https://hackmd.io/_uploads/rkNQMKONn.png)

### [git程式碼](https://github.com/chiangyih/ESP32/blob/c4999b8d835a2e469cc4d05d0ff8f347779c9a3b/oled_sht31/oled_sht31.ino)

### 透過串列埠，在電腦端顯示溫溼度值
![](https://hackmd.io/_uploads/rk6IUtOV3.png)

labview code
![](https://hackmd.io/_uploads/H1Q_LYuEh.png)









---

## 土壤溼度感測器
![](https://i.imgur.com/VqX74HB.png)
![](https://i.imgur.com/PKFcG3b.jpg)

* ### 完全乾燥時讀取值=0，濕度達100%時=4095
* ### 上面照片範例，小盆栽加20cc水後值約1000~2000間![](https://i.imgur.com/nIaUuBb.png)
* ## [github](https://github.com/chiangyih/ESP32/tree/master/%E5%9C%9F%E5%A3%A4%E6%BF%95%E5%BA%A6sensor/test1)

---
## GP2Y1014AU(灰塵感測)
![](https://i.imgur.com/ar8YZWU.png)
![](https://i.imgur.com/VQuIdpd.png)
![](https://i.imgur.com/gR9pzl3.png)
## [程式碼](https://github.com/chiangyih/ESP32/tree/master/%E7%81%B0%E5%A1%B5%E6%84%9F%E6%B8%AC%E5%99%A8/GP2Y1014AU/test1)

#### 測得值
3000 + = 很差
1050-3000 = 差
300-1050 = 一般
150-300 = 好
75-150 = 很好
0-75 = 非常好




---

## L298N DC motor 基本控制
![image](https://hackmd.io/_uploads/rJZ0CfWCa.png)


| motor    | IN1/IN3  | IN2/IN4  |
| -------- | -------- | -------- |
| 正轉      | High     | Low      |
| 逆轉      | Low      | High     |
| 停止      | Low      | Low      |

![image](https://hackmd.io/_uploads/SJeVYQ7-0p.png)
[程式碼](https://github.com/chiangyih/ESP32/tree/master/vscode-L298N/)



## L298N DC motor PWM
pwm訊號接至L298N上的en接腳
![image](https://hackmd.io/_uploads/HkdjGSZR6.png)






## 類比轉數位(ADC)練習
* [線上模擬](https://wokwi.com/projects/349650657658012244) 含程式碼

## WS2818b模擬(使用可變電阻調整顏色)
* [線上模擬](https://wokwi.com/projects/349646102033597012) 含程式碼
## WS2818(以8顆燈燈串測試)
* 基本測試(流水燈來回一次)
    * [程式碼Git](https://github.com/chiangyih/ESP32/tree/master/ws2812/ws2812-basic-test0-esp32) 
    * ![](https://i.imgur.com/N6LQXuF.png)
* 按鈕切換顯示模式
    * [程式碼git](https://github.com/chiangyih/ESP32/blob/master/ws2812/buttoncycler-esp32/)
    * ![](https://i.imgur.com/DxH2pKo.png)
    * {%youtube HflxMedMLqU%}

## 8X8LED矩陣(使用MAX7219)

| ESP32 | MAX7219 Module | 
| -------- | -------- | 
| 5V       | Vcc     | 
| GND      | GND     | 
| GPIO18   | CLK     | 
| GPIO5    | CS     | 
| GPIO23   | Din     |
### 簡易測試
```
#include <Arduino.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
#include <MD_Parola.h>

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW // Example hardware type
#define MAX_DEVICES 1                    // Number of connected devices
#define DATA_PIN   23
#define CLK_PIN    18
#define CS_PIN     5

// 使用硬體 SPI 初始設定 (如果要使用軟體 SPI，可改建構子參數順序)
MD_Parola display = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

const char message[] = "Hello, World! "; // 訊息，尾端加空白讓捲動較順
const uint16_t SCROLL_SPEED = 60; // 捲動速度 (越大越慢)
const uint16_t SCROLL_PAUSE = 0;  // 停頓時間 (ms)

void setup() {
  display.begin();
  display.setIntensity(1); // Set brightness level (0 is min, 15 is max)
  display.displayClear(); // Clear display

  // 設定顯示為向左捲動
  // 參數: (文字, 對齊, 速度, 停頓, 進入特效, 離開特效)
  display.displayText(message, PA_LEFT, SCROLL_SPEED, SCROLL_PAUSE, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}

void loop() {
  // 驅動動畫；回傳 true 表示該訊息已完成顯示
  if (display.displayAnimate()) {
    // 當動畫完成時，重新設定顯示文字讓它持續循環
    display.displayReset();
  }
}

```


### 1.定點顯示
```
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

// Uncomment according to your hardware type
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
//#define HARDWARE_TYPE MD_MAX72XX::GENERIC_HW

#define MAX_DEVICES 4  //連接8X8LED數量
#define CS_PIN 5

MD_Parola Display = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

void setup() {
 
  Display.begin();
  Display.setIntensity(0);
  Display.displayClear();
}

void loop() {
  Display.setTextAlignment(PA_LEFT);  //靠左
  Display.print("Arduino test");
  delay(2000);
  
  Display.setTextAlignment(PA_CENTER);  //置中
  Display.print("ESP32");
  delay(2000);

  Display.setTextAlignment(PA_RIGHT);  //靠右
  Display.print("ESP32");
  delay(2000);

  Display.setTextAlignment(PA_CENTER);
  Display.setInvert(true);    //反相顯示
  Display.print("ESP32");
  delay(2000);

  Display.setInvert(false);
  delay(2000);
}
```
### 2.使用串列埠輸入顯示(可變電阻改變捲動速度)
```
#include <MD_MAX72xx.h>
#include <SPI.h>

#define IMMEDIATE_NEW   0     // if 1 will immediately display a new message
#define USE_POT_CONTROL 1
#define PRINT_CALLBACK  0

#define PRINT(s, v) { Serial.print(F(s)); Serial.print(v); }

// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted
//#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4

#define CLK_PIN   18  // or SCK
#define DATA_PIN  23  // or MOSI
#define CS_PIN    5  // or SS

// SPI hardware interface
MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// Arbitrary pins
//MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

// Scrolling parameters
#if USE_POT_CONTROL
#define SPEED_IN  A5
#else
#define SCROLL_DELAY  1000  // in milliseconds
#endif // USE_POT_CONTROL

#define CHAR_SPACING  1 // pixels between characters

// Global message buffers shared by Serial and Scrolling functions
#define BUF_SIZE  75
uint8_t curMessage[BUF_SIZE] = { "Hello!  " };
uint8_t newMessage[BUF_SIZE];
bool newMessageAvailable = false;

uint16_t  scrollDelay;  // in milliseconds

void readSerial(void)
{
  static uint8_t  putIndex = 0;

  while (Serial.available())
  {
    newMessage[putIndex] = (char)Serial.read();
    if ((newMessage[putIndex] == '\n') || (putIndex >= BUF_SIZE-3)) // end of message character or full buffer
    {
      // put in a message separator and end the string
      newMessage[putIndex++] = ' ';
      newMessage[putIndex] = '\0';
      // restart the index for next filling spree and flag we have a message waiting
      putIndex = 0;
      newMessageAvailable = true;
    }
    else if (newMessage[putIndex] != '\r')
      // Just save the next char in next location
      putIndex++;
  }
}

void scrollDataSink(uint8_t dev, MD_MAX72XX::transformType_t t, uint8_t col)
// Callback function for data that is being scrolled off the display
{
#if PRINT_CALLBACK
  Serial.print("\n cb ");
  Serial.print(dev);
  Serial.print(' ');
  Serial.print(t);
  Serial.print(' ');
  Serial.println(col);
#endif
}

uint8_t scrollDataSource(uint8_t dev, MD_MAX72XX::transformType_t t)
// Callback function for data that is required for scrolling into the display
{
  static uint8_t* p = curMessage;
  static enum { NEW_MESSAGE, LOAD_CHAR, SHOW_CHAR, BETWEEN_CHAR } state = LOAD_CHAR;
  static uint8_t  curLen, showLen;
  static uint8_t  cBuf[15];
  uint8_t colData = 0;    // blank column is the default

#if IMMEDIATE_NEW
  if (newMessageAvailable)  // there is a new message waiting
  {
    state = NEW_MESSAGE;
    mx.clear(); // clear the display
  }
#endif

  // finite state machine to control what we do on the callback
  switch(state)
  {
    case NEW_MESSAGE:   // Load the new message
      memcpy(curMessage, newMessage, BUF_SIZE);	// copy it in
      newMessageAvailable = false;    // used it!
      p = curMessage;
      state = LOAD_CHAR;
      break;

    case LOAD_CHAR: // Load the next character from the font table
      showLen = mx.getChar(*p++, sizeof(cBuf)/sizeof(cBuf[0]), cBuf);
      curLen = 0;
      state = SHOW_CHAR;

      // if we reached end of message, opportunity to load the next
      if (*p == '\0')
      {
        p = curMessage;     // reset the pointer to start of message
#if !IMMEDIATE_NEW
        if (newMessageAvailable)  // there is a new message waiting
        {
          state = NEW_MESSAGE;    // we will load it here
          break;
        }
#endif
      }
      // !! deliberately fall through to next state to start displaying

    case SHOW_CHAR: // display the next part of the character
      colData = cBuf[curLen++];
      if (curLen == showLen)
      {
        showLen = CHAR_SPACING;
        curLen = 0;
        state = BETWEEN_CHAR;
      }
      break;

    case BETWEEN_CHAR: // display inter-character spacing (blank columns)
      colData = 0;
      curLen++;
      if (curLen == showLen)
        state = LOAD_CHAR;
      break;

    default:
      state = LOAD_CHAR;
  }

  return(colData);
}

 void scrollText(void)
{
  static uint32_t	prevTime = 0;

  // Is it time to scroll the text?
  if (millis()-prevTime >= scrollDelay)
  {
    mx.transform(MD_MAX72XX::TSL);  // scroll along - the callback will load all the data
    prevTime = millis();      // starting point for next time
  }
}

uint16_t getScrollDelay(void)  //設定捲動速度
{
#if USE_POT_CONTROL
  uint16_t  t;

  //t = analogRead(SPEED_IN);  //使用可變電阻調整捲動速度(此處直接給定常數180)
  //t = map(t, 0, 1023, 25, 250);  //0~1023對應至25~250
  t=180;
 
  return(t);
#else
  return(SCROLL_DELAY);
#endif
}

void setup()
{
  mx.begin();
  mx.setShiftDataInCallback(scrollDataSource);
  mx.setShiftDataOutCallback(scrollDataSink);

#if USE_POT_CONTROL
  pinMode(SPEED_IN, INPUT);
#else
  scrollDelay = SCROLL_DELAY;
#endif

  newMessage[0] = '\0';

  Serial.begin(57600);
  Serial.print("\n[MD_MAX72XX Message Display]\nType a message for the scrolling display\nEnd message line with a newline");
}

void loop()
{
  scrollDelay = getScrollDelay();
  readSerial();
  scrollText();
}
```
### 3.使用按鈕切換顯示變化
```
// Program to exercise the MD_MAX72XX library
//
// Test the library transformation functions
//藉由按鈕切換顯示的文字變換模式
#include <MD_MAX72xx.h>
#include <SPI.h>

// Use a button to transfer between transformations or just do it on a timer basis
#define USE_SWITCH_INPUT  1

#define SWITCH_PIN  8 // switch pin if enabled - active LOW 切換按鈕

// We always wait a bit between updates of the display
#define  DELAYTIME  500  // in milliseconds

// Number of times to repeat the transformation animations
#define REPEATS_PRESET  16

// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted
//#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES   4
#define WRAPAROUND_MODE MD_MAX72XX::ON

#define CLK_PIN   18  // or SCK
#define DATA_PIN  23  // or MOSI
#define CS_PIN    5  // or SS

// SPI hardware interface
MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// Arbitrary pins
//MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);


bool changeState(void)
{
  bool b = false;

#if USE_SWITCH_INPUT

  static int8_t	lastStatus = HIGH;
  int8_t	status = digitalRead(SWITCH_PIN);

  b = (lastStatus == HIGH) && (status == LOW);
  lastStatus = status;
#else
  static uint32_t	lastTime = 0;
  static uint8_t	repeatCount = 0;

  if (repeatCount == 0)
    repeatCount = REPEATS_PRESET;

  if (millis()-lastTime >= DELAYTIME)
  {
    lastTime = millis();
    b = (--repeatCount == 0);
  }
#endif

  return(b);
}

void transformDemo(MD_MAX72XX::transformType_t tt, bool bNew)
{
  static uint32_t lastTime = 0;

  if (bNew)
  {
    mx.clear();

    for (uint8_t i=0; i<MAX_DEVICES; i++)
    mx.setChar(((i+1)*COL_SIZE)-1, 'o'+i);
    lastTime = millis();
  }

  if (millis() - lastTime >= DELAYTIME)
  {
    mx.transform(0, MAX_DEVICES-1, tt);
    lastTime = millis();
  }
}

void setup()
{
  mx.begin();
  // use wraparound mode
  mx.control(MD_MAX72XX::WRAPAROUND, WRAPAROUND_MODE);

#if USE_SWITCH_INPUT
  pinMode(SWITCH_PIN, INPUT_PULLUP);
#endif

  Serial.begin(57600);
  Serial.println("[Transform Test]");
}

void loop()
{
  static int8_t tState = -1;
  static bool bNew = true;

  if (bNew)
  {
    tState = (tState+1) % 8;
    Serial.print("State: "); Serial.println(tState);
  }

  switch (tState)   //依據GPIO8按鈕輸入切換顯示模式
  {
    case 0: transformDemo(MD_MAX72XX::TSL,  bNew);  break;
    case 1: transformDemo(MD_MAX72XX::TSR,  bNew);  break;
    case 2: transformDemo(MD_MAX72XX::TSU,  bNew);  break;
    case 3: transformDemo(MD_MAX72XX::TSD,  bNew);  break;
    case 4: transformDemo(MD_MAX72XX::TFUD, bNew);  break;
    case 5: transformDemo(MD_MAX72XX::TFLR, bNew);  break;
    case 6: transformDemo(MD_MAX72XX::TRC,  bNew);  break;
    case 7: transformDemo(MD_MAX72XX::TINV, bNew);  break;
    default:  tState = 0; // just in case
  }

  bNew = changeState();
}


```

## 藍芽初始化
* ## 程式碼:[Github](https://github.com/chiangyih/VScode_esp32-bt/blob/81160369fb815c74c17ce579c3df8219678032ac/src/main.cpp)

* ## 使用藍芽控制LED
    * ### 程式碼:[github](https://github.com/chiangyih/ESP32-Bluetooth1_vscode/blob/d728935bf2d1f2c7dc54bb5e2d0e37439b97b115/src/main.cpp)
    *  ![](https://i.imgur.com/C5kAtVn.png)

## 雙核心工作任務
   *  xTaskCreate建立的任務都是運行在核心0
   *  主程式 loop()運行在核心1    *
   *  範例:使用3顆LED，2顆綠燈分別為任務1與任務2，由核心0負責，1顆藍燈閃爍於主程式(核心1)負責
   *  ![](https://i.imgur.com/EkUWoKZ.png)
   *  {%youtube 1bU1GBATItw%}
   *  ## 程式碼:[GitHub](https://github.com/chiangyih/ESP32/blob/c3f48f632971189888bea5bbe6fdcd67ceff967e/multiTask/2Task_test_2led/2Task_test_2led.ino)
  
## 連線WiFi範例
   * ## 程式碼:[GitHub](https://github.com/chiangyih/ESP32-Wifi_connect/blob/master/WiFi_connect.ino)
   * json資料格式解析
    * [資料解析](http://jsonpath.com/)
    * [json資料線上瀏覽1](http://jsonviewer.stack.hu/)
    * [json資料線上瀏覽2](https://jsoneditoronline.org/)
---
## sht31溫溼度感測與土壤濕度感測寫入Google Sheet
[Google Sheet紀錄](https://docs.google.com/spreadsheets/d/1g23kwsH2B62oTP40rE4PSNd_TnGrqWhFCQBy6fhzACE/edit?usp=sharing)
[程式碼Github](https://github.com/chiangyih/ESP32/blob/master/%E8%87%AA%E5%8B%95%E5%8C%96%E8%BE%B2%E6%A5%AD/wifi-sht31-moisture-pump/wifi-sht31-moisture-pump.ino)
**這裡使用google form介接寫入，簡化使用google sheet的程式**(有關介接方式請參閱[App inventor 使用 Google Sheet](https://hackmd.io/qtYVwfNIQu2SBz5jd318hg?view#App-inventor-%E4%BD%BF%E7%94%A8-Google-Sheet))
![](https://i.imgur.com/Pybdo8I.png)
![](https://i.imgur.com/dX932Q0.png)
### 上述程式加上傳送至MQTT broker(使用broker.emqx.io)
(每秒偵測天氣溫溼度及土壤濕度並上傳MQTT Broker已顯示於MQTT DASH，另每30分鐘寫入Google Sheet一筆紀錄)
[Node-red Dashboard](http://210.60.50.184:1880/ui/#!/7?socketid=UIfdhIprM6GNhAxRAAAD)
[程式碼github](https://github.com/chiangyih/ESP32/blob/master/%E8%87%AA%E5%8B%95%E5%8C%96%E8%BE%B2%E6%A5%AD/wifi-sht31-moisture-pump-MultiTask-mqtt/wifi-sht31-moisture-pump-MultiTask-mqtt.ino)
Android  MQTT Dash
![](https://i.imgur.com/TtOW3UT.png)





---

## 環境溫溼度、人體紅外線監測範例
   * 溫溼度與人體紅外驗監測、上傳MQTT Broker，Node-red顯示結果，有人員進入時發送line-notify(node-red上設定)
    * [環境現況監看](http://210.60.50.184:1880/ui/#!/1?socketid=JFpB7o6MUPnfURMqAAAH)
    * 線路圖 ![](https://i.imgur.com/pwp08rn.png)
    * node-red 流程設定 ![](https://i.imgur.com/jdNJPVh.png)
程式碼[github](https://github.com/chiangyih/ESP32/blob/master/DHT22_mqtt_computer-center/DHT22_mqtt_computer-center.ino)


---
## 使用Arduino IOT Cloud
  [Arduino Cloud](https://cloud.arduino.cc/)
  ![](https://i.imgur.com/9JSN6eP.png)
  





