查看: 2063|回复: 4

ESP32入门示例 - SD卡Web服务器

[复制链接]
  • TA的每日心情
    开心
    2018-8-27 15:15
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2018-8-27 15:14 | 显示全部楼层 |阅读模式
    这个是来自ESP32官方示例的改版,官方的示例由于存在一些问题所以我进行了修改
    原本的示例有点逻辑上的问题,所以进行了一些修改

    主要修改有:
    1.新增SD卡测试部分 复制自官方SD卡示例
    2.新增一个根目录页,访问根目录就可以看到
    3.修改了目录展示页,可以通过"ip地址/list"看到,原本这个页面只会返回"BAD ARGS"
    4.新增Wifi设置页,可以通过Wifi设置页更改链接的Wifi(但是建议不要随便更改,除非连着串口,不然你看不到新的IP地址)
    5.新增文件上传页,可以通过网页进行上传(调用原本的"/edit"的链接)

    下面是页面展示:
    主页
    Index.PNG
    Wifi设置页
    WifiSetting.PNG
    WIFI信息页
    WIFIINFO.PNG
    文件目录页
    File List.PNG
    文件上传页
    UPLOAD.PNG

    主要代码:

    kittenblock中小学创客名师推荐的图形化编程软件

    /*
      SDWebServer - Example WebServer with SD Card backend for esp8266
    
      Copyright (c) 2015 Hristo Gochkov. All rights reserved.
      This file is part of the WebServer library for Arduino environment.
    
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public
      License as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
    
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
    
      You should have received a copy of the GNU Lesser General Public
      License along with this library; if not, write to the Free Software
      Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    
      Have a FAT Formatted SD Card connected to the SPI port of the ESP8266
      The web root is the SD Card root folder
      File extensions with more than 3 charecters are not supported by the SD Library
      File Names longer than 8 charecters will be truncated by the SD library, so keep filenames shorter
      index.htm is the default index (works on subfolders as well)
    
      upload the contents of SdRoot to the root of the SDcard and access the editor by going to http://esp8266sd.local/edit
    
    Modified by Maoweicao 2018.8.27
    */
    #include <WiFi.h>
    #include <WiFiClient.h>
    #include <WebServer.h>
    #include <ESPmDNS.h>
    #include <SPI.h>
    #include <SD.h>
    #include "FS.h"
    
    
    #define DBG_OUTPUT_PORT Serial
    
    const char* ssid = "................."; //Replace youself wifi ssid
    const char* password = "......................"; //Replace youself wifi password
    const char* host = "esp32sd";
    String wifissid = "";
    String wifipwd = "";
    WebServer server(80);
    
    static bool hasSD = false;
    
    File uploadFile;
    //format bytes
    String formatBytes(size_t bytes) {
      if (bytes < 1024) {
        return String(bytes) + "B";
      } else if (bytes < (1024 * 1024)) {
        return String(bytes / 1024.0) + "KB";
      } else if (bytes < (1024 * 1024 * 1024)) {
        return String(bytes / 1024.0 / 1024.0) + "MB";
      } else {
        return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB";
      }
    }
    
    void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
        Serial.printf("Listing directory: %s\n", dirname);
    
        File root = fs.open(dirname);
        if(!root){
            Serial.println("Failed to open directory");
            return;
        }
        if(!root.isDirectory()){
            Serial.println("Not a directory");
            return;
        }
    
        File file = root.openNextFile();
        while(file){
            if(file.isDirectory()){
                Serial.print("  DIR : ");
                Serial.println(file.name());
                if(levels){
                    listDir(fs, file.name(), levels -1);
                }
            } else {
                Serial.print("  FILE: ");
                Serial.print(file.name());
                Serial.print("  SIZE: ");
                Serial.println(file.size());
            }
            file = root.openNextFile();
        }
    }
    
    void createDir(fs::FS &fs, const char * path){
        Serial.printf("Creating Dir: %s\n", path);
        if(fs.mkdir(path)){
            Serial.println("Dir created");
        } else {
            Serial.println("mkdir failed");
        }
    }
    
    void removeDir(fs::FS &fs, const char * path){
        Serial.printf("Removing Dir: %s\n", path);
        if(fs.rmdir(path)){
            Serial.println("Dir removed");
        } else {
            Serial.println("rmdir failed");
        }
    }
    
    void readFile(fs::FS &fs, const char * path){
        Serial.printf("Reading file: %s\n", path);
    
        File file = fs.open(path);
        if(!file){
            Serial.println("Failed to open file for reading");
            return;
        }
    
        Serial.print("Read from file: ");
        while(file.available()){
            Serial.write(file.read());
        }
        file.close();
    }
    
    void writeFile(fs::FS &fs, const char * path, const char * message){
        Serial.printf("Writing file: %s\n", path);
    
        File file = fs.open(path, FILE_WRITE);
        if(!file){
            Serial.println("Failed to open file for writing");
            return;
        }
        if(file.print(message)){
            Serial.println("File written");
        } else {
            Serial.println("Write failed");
        }
        file.close();
    }
    
    void appendFile(fs::FS &fs, const char * path, const char * message){
        Serial.printf("Appending to file: %s\n", path);
    
        File file = fs.open(path, FILE_APPEND);
        if(!file){
            Serial.println("Failed to open file for appending");
            return;
        }
        if(file.print(message)){
            Serial.println("Message appended");
        } else {
            Serial.println("Append failed");
        }
        file.close();
    }
    
    void renameFile(fs::FS &fs, const char * path1, const char * path2){
        Serial.printf("Renaming file %s to %s\n", path1, path2);
        if (fs.rename(path1, path2)) {
            Serial.println("File renamed");
        } else {
            Serial.println("Rename failed");
        }
    }
    
    void deleteFile(fs::FS &fs, const char * path){
        Serial.printf("Deleting file: %s\n", path);
        if(fs.remove(path)){
            Serial.println("File deleted");
        } else {
            Serial.println("Delete failed");
        }
    }
    
    void testFileIO(fs::FS &fs, const char * path){
        File file = fs.open(path);
        static uint8_t buf[512];
        size_t len = 0;
        uint32_t start = millis();
        uint32_t end = start;
        if(file){
            len = file.size();
            size_t flen = len;
            start = millis();
            while(len){
                size_t toRead = len;
                if(toRead > 512){
                    toRead = 512;
                }
                file.read(buf, toRead);
                len -= toRead;
            }
            end = millis() - start;
            Serial.printf("%u bytes read for %u ms\n", flen, end);
            file.close();
        } else {
            Serial.println("Failed to open file for reading");
        }
    
    
        file = fs.open(path, FILE_WRITE);
        if(!file){
            Serial.println("Failed to open file for writing");
            return;
        }
    
        size_t i;
        start = millis();
        for(i=0; i<2048; i++){
            file.write(buf, 512);
        }
        end = millis() - start;
        Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
        file.close();
    }
    
    
    void returnOK() {
      server.send(200, "text/plain", "");
    }
    
    void returnOK(String msg) {
      server.send(200, "text/plain", msg+"\r\n");
    }
    
    void returnFail(String msg) {
      server.send(500, "text/plain", msg + "\r\n");
    }
    
    bool loadFromSdCard(String path) {
      String dataType = "text/plain";
      if (path.endsWith("/")) {
        path += "index.htm";
      }
    
      if (path.endsWith(".src")) {
        path = path.substring(0, path.lastIndexOf("."));
      } else if (path.endsWith(".htm")) {
        dataType = "text/html";
      } else if (path.endsWith(".css")) {
        dataType = "text/css";
      } else if (path.endsWith(".js")) {
        dataType = "application/javascript";
      } else if (path.endsWith(".png")) {
        dataType = "image/png";
      } else if (path.endsWith(".gif")) {
        dataType = "image/gif";
      } else if (path.endsWith(".jpg")) {
        dataType = "image/jpeg";
      } else if (path.endsWith(".ico")) {
        dataType = "image/x-icon";
      } else if (path.endsWith(".xml")) {
        dataType = "text/xml";
      } else if (path.endsWith(".pdf")) {
        dataType = "application/pdf";
      } else if (path.endsWith(".zip")) {
        dataType = "application/zip";
      }
    
      File dataFile = SD.open(path.c_str());
      if (dataFile.isDirectory()) {
        path += "/index.htm";
        dataType = "text/html";
        dataFile = SD.open(path.c_str());
      }
    
      if (!dataFile) {
        return false;
      }
    
      if (server.hasArg("download")) {
        dataType = "application/octet-stream";
      }
    
      if (server.streamFile(dataFile, dataType) != dataFile.size()) {
        DBG_OUTPUT_PORT.println("Sent less data than expected!");
      }
    
      dataFile.close();
      return true;
    }
    void handleWifiPage()
    {
      String content = "<html><body>Congratulations! You Now Connect to The Wifi!<br>";
        content += "You Connect Wifi Name is:"+String(ssid)+"<br>";
        content += "Wifi Password is:"+String(password)+"<br>";
        content += "You input ssid is:" + wifissid + "<br>";
        content += "You input password is:"+wifipwd+"<br>";
        content += "</body></html>";
        server.send(200, "text/html", content);
    }
    void handleSetWifi()
    {
      String msg;
      if (server.hasArg("DISCONNECT")){
        Serial.println("Disconnection");
        String header = "HTTP/1.1 301 OK\r\nSet-Cookie: ESPSESSIONID=0\r\nLocation: /wifiinfo\r\nCache-Control: no-cache\r\n\r\n";
        server.sendContent(header);
        return;
      }
      if (server.hasArg("WIFINAME") && server.hasArg("WIFIPWD")){
          wifissid=server.arg("WIFINAME");
          wifipwd=server.arg("WIFIPWD");
          String header = "HTTP/1.1 301 OK\r\nSet-Cookie: ESPSESSIONID=1\r\nLocation: /wifiinfo\r\nCache-Control: no-cache\r\n\r\n";
          server.sendContent(header);
          Serial.println("WIFI INFO:");
          Serial.println("Wifi Name:"+wifissid);
          Serial.println("Wifi Password:"+wifipwd);
          if(WiFi.isConnected())
          {
            WiFi.disconnect();
          }
          WiFi.begin(wifissid.c_str(),wifipwd.c_str());
          int i=0;
          while (WiFi.status() != WL_CONNECTED && i++ < 50) {//wait 25 seconds
             Serial.print(".");
            delay(500);
          }
        if (i == 51) {
            DBG_OUTPUT_PORT.print("Could not connect to");
            DBG_OUTPUT_PORT.println(wifissid);
            while (1) {
            delay(500);
          }
        }
          DBG_OUTPUT_PORT.print("Connected! IP address: ");
           DBG_OUTPUT_PORT.println(WiFi.localIP());
          return;
        }
        String content = "<html><body><form method='POST'>This is a Wifi Setting Page<br>";
        content += "Wifi SSID:<input type='text' name='WIFINAME' placeholder='wifi ssid'><br>";
        content += "Wifi Password:<input type='password' name='WIFIPWD' placeholder='wifi password''><br>";
        content += "<input type='submit' name='SUBMIT' value='Submit'></form>" + msg + "<br>";
        content += "We Can Scan The Wifi List is:<br>";
        int n = WiFi.scanNetworks();
        if(n==0)
          content += "There is no wifi can use!<br>";
        else
        {
          content += "<ol>";
          for(int i=0; i<n;i++){
            content += (String("<li>")+String(" Wifi SSID:")+String(WiFi.SSID(i))+String(" Wifi RSSI:")+String(WiFi.RSSI(i))+String((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?String(" "):String("*"))+String("</li>"));
          }
          content += "</ol>";
        }
        content += "</body></html>";
        server.send(200, "text/html", content);
    }
    
    void handleRoot(){
      Serial.println("Enter handleRoot");
      String header;
      String content = "<html><body><H2>Hello, you successfully connected to ESP32-F!</H2><br>";
      if (server.hasHeader("User-Agent")){
        content += "the user agent used is : " + server.header("User-Agent") + "<br><br>";
      }
      content += "You can choose this page to visit:<br>";
      content +="<a href='/wifiset' class='button'>Wifi Setup</a><br />";
      content +="<a href='/wifiinfo' class='button'>Wifi Infomation</a><br />";
      content +="<a href='/list' class='button'>File List</a><br />";
      content +="<a href='/upload' class='button'>Upload File</a><br />";
      content +="Good Luck Everyone!";
      content += "</body></html>";
      server.send(200, "text/html", content);
    }
    
    void handleUploadWeb()
    {
      File html=SD.open("/upload/upload.html");
      String dataType = "text/html";
      server.streamFile(html,dataType);
      Serial.println("Open Upload File");
    }
    void handleFileUpload() {
      bool updone=false;
      if (server.uri() != "/edit") {
        return;
      }
      String fileName = "";
      HTTPUpload& upload = server.upload();
      if (upload.status == UPLOAD_FILE_START) {
        if (SD.exists((char *)upload.filename.c_str())) {
          SD.remove((char *)upload.filename.c_str());
        }
        fileName = "/upload/"+upload.filename.substring(upload.filename.lastIndexOf('\\')+1);
        Serial.println(fileName);
        uploadFile = SD.open(fileName, FILE_WRITE);
        if(uploadFile!=NULL)
          Serial.println("File exsist!");
        DBG_OUTPUT_PORT.print("Upload: START, filename: "); DBG_OUTPUT_PORT.println(upload.filename);
      } else if (upload.status == UPLOAD_FILE_WRITE) {
        if (uploadFile) {
          if(uploadFile.write(upload.buf, upload.currentSize))
            Serial.println("write Done");
        }
        DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: "); DBG_OUTPUT_PORT.println(upload.currentSize);
      } else if (upload.status == UPLOAD_FILE_END) {
          uploadFile.close();
        DBG_OUTPUT_PORT.print("Upload: END, Size: "); DBG_OUTPUT_PORT.println(upload.totalSize);
          String message = "Upload Done!\n\n";
        message += "URI: ";
        message += server.uri();
        message += "\nMethod: ";
        message += (server.method() == HTTP_GET) ? "GET" : "POST";
        message += "\nArguments: ";
        message += server.args();
        message += "\n";
        for (uint8_t i = 0; i < server.args(); i++) {
         message += " NAME:" + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n";
        }
        server.send(200, "text/plain", message);
        updone=true;
      }
      if(updone)
        if(fileName!="")
          readFile(SD,fileName.c_str());
        }
    
    void deleteRecursive(String path) {
      File file = SD.open((char *)path.c_str());
      if (!file.isDirectory()) {
        file.close();
        SD.remove((char *)path.c_str());
        return;
      }
    
      file.rewindDirectory();
      while (true) {
        File entry = file.openNextFile();
        if (!entry) {
          break;
        }
        String entryPath = path + "/" + entry.name();
        if (entry.isDirectory()) {
          entry.close();
          deleteRecursive(entryPath);
        } else {
          entry.close();
          SD.remove((char *)entryPath.c_str());
        }
        yield();
      }
    
      SD.rmdir((char *)path.c_str());
      file.close();
    }
    
    void handleDelete() {
      if (server.args() == 0) {
        return returnFail("BAD ARGS");
      }
      String path = server.arg(0);
      if (path == "/" || !SD.exists((char *)path.c_str())) {
        returnFail("BAD PATH");
        return;
      }
      deleteRecursive(path);
      returnOK();
    }
    
    void handleCreate() {
      if (server.args() == 0) {
        return returnFail("BAD ARGS");
      }
      String path = server.arg(0);
      if (path == "/" || SD.exists((char *)path.c_str())) {
        returnFail("BAD PATH");
        return;
      }
    
      if (path.indexOf('.') > 0) {
        File file = SD.open((char *)path.c_str(), FILE_WRITE);
        if (file) {
          file.write(0);
          file.close();
        }
      } else {
        SD.mkdir((char *)path.c_str());
      }
      returnOK();
    }
    
    String listDir2Web(fs::FS &fs, const char * dirname, uint8_t levels)
    {
      String message="";
      File root = fs.open(dirname);
        if(!root){
            message += "Failed to open directory <br />";
            return message;
        }
        if(!root.isDirectory()){
            message += "Not a directory <br />";
            return message;
        }
        File file = root.openNextFile();
        while(file){
            if(file.isDirectory()){
               message +="  DIR : ";
               message += String(file.name())+String("<br />");
                if(levels){
                    message += listDir2Web(fs, file.name(), levels -1);
                }
            } else {
                message += String("  FILE: ");
                message += String(file.name())+String("<br />");
                message += String("  SIZE: ");
                message += formatBytes(file.size())+String("<br />");
            }
            file = root.openNextFile();
        }
        return message;
    }
    void printDirectory() {
        String header = "<html><body>";
        String message= header + "<h2>List the file in the SD Card:</h2>";
        message += listDir2Web(SD,"/",5);
        server.send(200,"text/html",message);
    }
    
    void handleNotFound() {
      if (hasSD && loadFromSdCard(server.uri())) {
        return;
      }
      String message = "SDCARD Not Detected\n\n";
      message += "URI: ";
      message += server.uri();
      message += "\nMethod: ";
      message += (server.method() == HTTP_GET) ? "GET" : "POST";
      message += "\nArguments: ";
      message += server.args();
      message += "\n";
      for (uint8_t i = 0; i < server.args(); i++) {
        message += " NAME:" + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n";
      }
      server.send(404, "text/plain", message);
      DBG_OUTPUT_PORT.print(message);
    }
    
    void setup(void) {
      DBG_OUTPUT_PORT.begin(115200);
      DBG_OUTPUT_PORT.setDebugOutput(true);
      DBG_OUTPUT_PORT.print("\n");
      WiFi.mode(WIFI_STA);
      //Soft AP Setting
      /*IPAddress softLocal(192,168,25,1);
      IPAddress softGateway(192,168,25,1);
      IPAddress softSubnet(255,255,255,0);
      WiFi.softAPConfig(softLocal,softGateway,softSubnet);
      WiFi.softAP("ESP32_5672C","adminadmin");
       Serial.println("SoftAP IPAdress:"+WiFi.softAPIP());*/
      //Station Setting
      WiFi.begin(ssid, password);
      DBG_OUTPUT_PORT.print("Connecting to ");
      DBG_OUTPUT_PORT.println(ssid);
    
      // Wait for connection
      uint8_t i = 0;
      while (WiFi.status() != WL_CONNECTED && i++ < 50) {//wait 25 seconds
        Serial.print(".");
        delay(500);
      }
      if (i == 51) {
        DBG_OUTPUT_PORT.print("Could not connect to");
        DBG_OUTPUT_PORT.println(ssid);
        while (1) {
          delay(500);
        }
      }
      DBG_OUTPUT_PORT.print("Connected! IP address: ");
      DBG_OUTPUT_PORT.println(WiFi.localIP());
      //Setting sub network
    
      if (MDNS.begin(host)) {
        MDNS.addService("http", "tcp", 80);
        DBG_OUTPUT_PORT.println("MDNS responder started");
        DBG_OUTPUT_PORT.print("You can now connect to http://");
        DBG_OUTPUT_PORT.print(host);
        DBG_OUTPUT_PORT.println(".local");
      }
    
    
      server.on("/",handleRoot);
      server.on("/list", HTTP_GET, printDirectory);
      server.on("/edit", HTTP_DELETE, handleDelete);
      server.on("/edit", HTTP_PUT, handleCreate);
      server.on("/upload",HTTP_GET,handleUploadWeb);
      server.on("/wifiinfo",HTTP_GET,handleWifiPage);
      server.on("/wifiset",HTTP_GET,handleSetWifi);
      server.on("/wifiset",HTTP_POST,handleSetWifi);
      server.on("/edit", HTTP_POST, []() {
        returnOK();
      }, handleFileUpload);
      server.onNotFound(handleNotFound);
    
      server.begin();
      DBG_OUTPUT_PORT.println("HTTP server started");
    
      /*if (SD.begin(SS)) {
        DBG_OUTPUT_PORT.println("SD Card initialized.");
        hasSD = true;
      }**/
     if(!SD.begin()){
            Serial.println("Card Mount Failed");
            return;
        }
        else
        {
          Serial.println("SD Card Ready!");
          hasSD=true;
        }
        uint8_t cardType = SD.cardType();
    
        if(cardType == CARD_NONE){
            Serial.println("No SD card attached");
            return;
        }
    
        Serial.print("SD Card Type: ");
        if(cardType == CARD_MMC){
            Serial.println("MMC");
        } else if(cardType == CARD_SD){
            Serial.println("SDSC");
        } else if(cardType == CARD_SDHC){
            Serial.println("SDHC");
        } else {
            Serial.println("UNKNOWN");
        }
    
        uint64_t cardSize = SD.cardSize() / (1024 * 1024);
        Serial.printf("SD Card Size: %lluMB\n", cardSize);
    
        listDir(SD, "/", 0);
        createDir(SD, "/mydir");
        createDir(SD, "/upload");
        listDir(SD, "/", 0);
        removeDir(SD, "/mydir");
        listDir(SD, "/", 2);
        writeFile(SD, "/hello.txt", "Hello ");
        appendFile(SD, "/hello.txt", "World!\n");
        readFile(SD, "/hello.txt");
        deleteFile(SD, "/foo.txt");
        renameFile(SD, "/hello.txt", "/foo.txt");
        readFile(SD, "/foo.txt");
        testFileIO(SD, "/test.txt");
        Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
        Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
    }
    
    void loop(void) {
      server.handleClient();
    }


    当然文件上传页是在SD卡目录下面的,所以你需要在SD卡新建一个upload目录并且上传一个upload网页
    这样就可以上传了。
    下面是那个比较简单的上传页面
    记得要替换掉那个IP Address变成你自己的目录或者直接去掉

    kittenblock中小学创客名师推荐的图形化编程软件

    <form enctype="multipart/form-data" action="http://IP Address/edit" method=post>
    <!-- Replace The IP Address -->
      Choose You Will Upload File:<br>
      <input name="userfile1" type="file"><br>
      <input type=submit><input type=reset>
    </form>

    另外一个版本:

    kittenblock中小学创客名师推荐的图形化编程软件

    <form enctype="multipart/form-data" action="/edit" method=post>
      Choose You Will Upload File:<br>
      <input name="userfile1" type="file"><br>
      <input type=submit><input type=reset>
    </form>

  • TA的每日心情
    开心
    2019-3-15 00:28
  • 签到天数: 630 天

    [LV.9]以坛为家II

    发表于 2018-8-27 16:32 | 显示全部楼层
    能介绍一下esp32和esp8266的区别吗  顺便看看应用场景
    打赏作者鼓励一下!
  • TA的每日心情
    开心
    2018-8-27 15:15
  • 签到天数: 1 天

    [LV.1]初来乍到

     楼主| 发表于 2018-8-27 18:05 | 显示全部楼层
    单片机菜鸟 发表于 2018-8-27 16:32
    能介绍一下esp32和esp8266的区别吗  顺便看看应用场景

    esp32带有蓝牙 esp8266没有蓝牙
    esp8266大多数情况用于廉价的wifi设备上面
    esp32嘛……貌似现在还不怎么流行

    该用户从未签到

    发表于 2018-9-9 04:08 来自手机 | 显示全部楼层
    32应该是双核,接口数量大于8266,性能和部署比8266更快捷方便,成本是硬伤
  • TA的每日心情
    开心
    2018-12-18 11:05
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2018-12-18 11:14 | 显示全部楼层
    能不能文件发一份  需要这里的库
    您需要登录后才可以回帖 登录 | 立即注册  

    本版积分规则

    热门推荐

    今晚闲来无事搞了下STM32 Ardunio 的SPI2 SD卡的通讯
    今晚闲来无事搞了下STM32
    看到Ardunio很火,就想用STM32F103RBT6的板子试一下。之前移植网上带系统的,竟然说超
    arduino之串口通讯
    arduino之串口通讯
    总操流程: 1、写入程序 2、测试[hr][/backcolor] 写程序[/backcolor] [*]void s
    四位数码管动态显示
    四位数码管动态显示
    代码如下;从9990到9999,然后再从9898开始 代码撸起; const unsigned char dofly_Du
    求助!GM65二维码扫描模块怎么用?
    求助!GM65二维码扫描模块
    求助!GM65二维码扫描模块怎么用? 卖家给的资料真的看不懂该怎么弄。。( ̄▽ ̄)~*
    Arduino Pro Micro 打造USB音频转 3.5mm音频转接板
    Arduino Pro Micro 打造US
    3.5mm耳机接口是PC上最常见的音频接口,因为它工艺简单、价格低廉在涉及到声音输入输
    Copyright   ©2015-2016  Arduino中文社区  Powered by©Discuz!   ( 蜀ICP备14017632号-3 )
    快速回复 返回顶部 返回列表