# 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) ### 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)