All pages
Powered by GitBook
1 of 16

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

1. GPIO例程

操作BiBoard的GPIO

BiBoard没有单独的GPIO,但是复用的串口2(16,17脚)或没有使用的PWM舵机接口的PWM脚可以用作GPIO。BiBoard的GPIO使用也相对简单,配置输入输出模式后和UNO的用法完全相同。您可以使用任何Arduino UNO的IO控制控制程序,更改IO号即可。

/* In this demo, we use TX2, RX2 as general purpose IO
*   TX2 : IO17
*   RX2 : IO16
*/

void setup() {
  // initialize digital pin 16 & 17 as an output.
  pinMode(16, OUTPUT);
  pinMode(17, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
    
  digitalWrite(16, HIGH);            // GPIO 16 & 17 HIGH
  digitalWrite(17, HIGH);
  delay(1000);                       // wait for a second

  digitalWrite(16, LOW);             // GPIO 16 & 17 LOW
  digitalWrite(17, LOW);
  delay(1000);                       // wait for a second
}

2. 串口

BiBoard有2个串口,分别位于2个2个扩展插座(P16,P17)上。

P16上的串口1同时连接着USB下载器,请勿同时使用下载器和外接串口设备,会因为串口电压分压而导致通讯错误。

在Arduino例程中,串口0为Serial,串口1为Serial1。将Serial和Serial1的数据互相转发。

/* In this demo, we use Serial and Serial1 
*  Serial and Serial1 send to each other 
*/

void setup() {
  // initialize both serial ports:
  Serial.begin(115200);
  Serial1.begin(115200);
}

void loop() {
  // read from port 1, send to port 0:
  if (Serial1.available()) {
    int inByte = Serial1.read();
    Serial.write(inByte);
  }

  // read from port 0, send to port 1:
  if (Serial.available()) {
    int inByte = Serial.read();
    Serial1.write(inByte);
  }
}

例程介绍

4. 数字-模拟转换器

DAC的用法

DAC的用途和ADC正好相反,DAC是将数字信号转换成模拟信号输出。

还记得NyBoard开机的那一段音乐?那是使用PWM发出的,PWM采用高速开关调节占空比来输出电压。

相比PWM,DAC会直接输出电压而无需计算占空比。ESP32集成了1个2路8位DAC,数值为 0 - 255。电压范围是 0 - 3.3V。所以DAC的输出电压计算公式如下:

DAC=(int)TargetV/3.3V∗255DAC = (int)TargetV / 3.3V * 255DAC=(int)TargetV/3.3V∗255

例程如下:

#define DAC1 25 

void setup() {  
}

void loop() {
  
  // 8bit DAC, 255 = 3.3V, 0 = 0.0V 
  for(int i = 0; i < 255; i++){
    dacWrite(DAC1, i);
    delay(10);
  }
}

10. 经典蓝牙串口SPP

示例代码主要演示了蓝牙串口和串口之间相互转发信息,来源于ESP32的官方例程,简单易懂。所以说明中主要解释代码中出现的概念。

1. 蓝牙协议

目前主要的蓝牙协议分为2类,基于RFCOMM的传统蓝牙(HS/BR/EDR)和基于GATT的蓝牙低功耗(BLE)。

传统蓝牙速度较快,具体应用协议多,如面向音频的A2DP,蓝牙串口SPP等。但功耗较高,且接入苹果设备需要MFi(Made For iOS)的芯片及认证。

蓝牙低功耗(BLE,Bluetooth Low Energy)可以自行定义各种GATT的Profile,自身也配置了常用的Profile(如设备信息、电池等),功耗低用途广,可以用在苹果设备上,缺点是比起传统蓝牙速度比较慢。蓝牙低功耗多用于手环/智能手表/信标等数据量低但是对功耗敏感的设备上。

2. 经典蓝牙串口 ( SPP)

本示例使用的是基于传统蓝牙的SPP协议,自带全部的串口协议。当计算机或安卓手机连接配对后会在系统自动生成一个串口号进行通讯,使用体验和普通有线串口没有太大区别。

//This example code is in the Public Domain (or CC0 licensed, at your option.)
//By Evandro Copercini - 2018
//
//This example creates a bridge between Serial and Classical Bluetooth (SPP)
//and also demonstrate that SerialBT have the same functionalities of a normal Serial

#include "BluetoothSerial.h"

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

BluetoothSerial SerialBT;

void setup() {
  Serial.begin(115200);
  SerialBT.begin("BTSPP_Test");          //Bluetooth device name
  Serial.println("The device started,");
  Serial.println("Now you can pair it with bluetooth!");
}

void loop() {
  if (Serial.available()) {
    SerialBT.write(Serial.read());
  }
  if (SerialBT.available()) {
    Serial.write(SerialBT.read());
  }
  delay(20);
}

蓝牙低功耗串口将在下一章演示,本质上是配置了一个串口的Profile, 需要主机软件支持。

8. PWM

1. BiBoard(ESP32)PWM功能简介

BiBoard使用的ESP32和UNO用的328P有所不同。ESP32的PWM因为使用了矩阵总线的缘故,可以使用在非特定的引脚上。

ESP32的PWM被称作LED控制器(LEDC),LED PWM 控制器主要用于控制 LED,也可产生 PWM 信号用于其他设备的控制。该控制器有 8个定时器,对应8个高速通道和8个低速通道,合计16通道。

Key Settings of LED PWM Controller's API

相比UNO直接使用“analogWrite()” 输入0-255之间任意的占空比。BiBoard上ESP32的PWM控制要略麻烦一些。需要控制的参数如下:

  1. 人工选择PWM通道(0-15),也提高了引脚的使用灵活性

  2. PWM波形的位数,决定了PWM波形占空比的分辨率,位数越高精度越高。

  3. PWM波形的频率,决定了PWM波形的速度,频率越高速度越快。

PWM波形的频率和位数是相对的,位数越高频率越低。以下例子引用自ESP32编程手册:

比如,PWM 频率为 5 kHz 时,占空比分辨率最大可为 13 位。这意味着占空比可为 0 至 100% 之间的任意值,分辨率为 ~0.012%(2 ** 13 = 8192 LED 亮度的离散电平)。

LED PWM 控制器可用于生成频率较高的信号,足以为数码相机模组等其他设备计时。此时,最大频率可为 40 MHz,占空比分辨率为 1 位。也就是说,占空比固定为 50%,无法调整。

LED PWM 控制器 API 可在设定的频率和占空比分辨率超过 LED PWM 控制器硬件范围时报错。例如,试图将频率设置为 20 MHz、占空比分辨率设置为 3 位时,串行端口监视器上会报错。

2. Arduino配置BiBoard频率

如上文所示,我们需要配置通道、频率和位数,同时选择输出引脚。

第一步:配置PWM控制器

const int freq = 5000; // PWM frequency
const int ledcChannel = 0; // ledc channel, 0-15
const int resolution = 8; // resolution of PWM,8bit(0~255)
ledcSetup(ledcChannel, freq, resolution);

第二步:配置PWM输出引脚

ledcAttachPin(ledPin, ledcChannel);

第三步:输出PWM波形

ledcWrite(ledcChannel, dutyCycle);

例程中我们选择IO2作为输出引脚,连接IO2至一个LED,可以观察到LED呼吸灯的效果。

3. 完整的代码:

/* In this demo, we show how to use PWM in BiBoard(ESP32)
* It's different from the Arduino UNO based on the ATMega328P
*/

// define the PWM pin
const int ledPin = 2;  // 16 corresponds to GPIO16

// setting PWM properties
const int freq = 5000;          // PWM frequency
const int ledcChannel = 0;      // ledc channel, in ESP32 there're 16 ledc(PWM) channels
const int resolution = 8;       // resolution of PWM
 
void setup(){
  // configure ledc functionalitites
  // channels 0-15, resolution 1-16 bits, freq limits depend on resolution
  // ledcSetup(uint8_t channel, uint32_t freq, uint8_t resolution_bits);
  ledcSetup(ledcChannel, freq, resolution);     
  
  // attach the channel to the GPIO to be controlled
  ledcAttachPin(ledPin, ledcChannel);
}
 
void loop(){
  // increase the LED brightness
  for(int dutyCycle = 0; dutyCycle <= 255; dutyCycle++){   
    // changing the LED brightness with PWM
    ledcWrite(ledcChannel, dutyCycle);
    delay(15);
  }

  // decrease the LED brightness
  for(int dutyCycle = 255; dutyCycle >= 0; dutyCycle--){
    // changing the LED brightness with PWM
    ledcWrite(ledcChannel, dutyCycle);   
    delay(15);
  }
}

7. 红外遥控

BiBoard配置了红外传感器,连接在第23引脚。红外的使用和基于AVR的UNO完全一样。

首先下载2.6.1版本的IRremote库,需要手工选择2.6.1版本。因为之后的版本红外编码有变化,如果使用3.X版本,指令会无法被翻译。为了同我们之前的产品兼容,测试后决定使用2.6.1版本。

在使用NyBoard的时候,为了保证代码可以顺利编译,我们需要给IRremote库进行“瘦身”,即去掉我们用不到的编码/解码器,只保留NEC_DECODER即NEC格式的38KHz信号解码器。

在Flash巨大的BiBoard上,我们无需进行“瘦身”。

最后附上例程,接受红外信号并串口打印,也可以使用官方的例程进行测试。

9. 舵机(施工)

#include <Arduino.h>
#include <IRremote.h>

int RECV_PIN = 23;

IRrecv irrecv(RECV_PIN);

decode_results results;

void setup() {
  Serial.begin(115200);
  irrecv.enableIRIn();
  Serial.println("IR Receiver ready");
}

void loop() {
  if (irrecv.decode(&results)) {
    Serial.println(results.value, HEX);
    Serial.print(" - ");
    irrecv.resume(); // Receive the next value
  }
  delay(300);
}
UNO使用了23%的Flash
BiBoard只使用的4%

11. 蓝牙低功耗(BLE)串口透传

蓝牙低功耗(BLE, Bluetooth Low Energy)串口透传使用较为广泛。在苹果的iOS平台,经典蓝牙需要有MFi认证才可以和苹果的iOS设备进行连接。而蓝牙低功耗设备并没有这个限制。

蓝牙低功耗的协议栈和原理这里就不再赘述了,相关的文章和视频较多。简单的来说就是蓝牙服务以Profile的方式提供,每个服务的Profile下有N个具有独立ID(UUID)的character。每个character有不同的权限(read,write,notify,indicate)。用户定义character并且和权限组合后,就可以提供完整的服务了。

BLE透传的实际上是建立了一个BLE的Service,这个Profile下有2个character。

#define SERVICE_UUID           "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"

一个用于TX(发送数据),一个用于RX(接收数据)。为此他们有不同的权限。下面的代码是新建service和character的:

  // Create the BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);
  
  // Create a BLE Characteristic
  pTxCharacteristic = pService->createCharacteristic(
										CHARACTERISTIC_UUID_TX,
										BLECharacteristic::PROPERTY_NOTIFY
									);
                      
  pTxCharacteristic->addDescriptor(new BLE2902());

  BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
											 CHARACTERISTIC_UUID_RX,
											BLECharacteristic::PROPERTY_WRITE
										);

接下来是2个回调函数,分别在有连接时,以及有write RX character时进行的操作:

class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
    };

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};

class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string rxValue = pCharacteristic->getValue();

      if (rxValue.length() > 0) {
        Serial.println("*********");
        Serial.print("Received Value: ");
        for (int i = 0; i < rxValue.length(); i++)
          Serial.print(rxValue[i]);

        Serial.println();
        Serial.println("*********");
      }
    }
};

最后主循环是连接的控制,判断是否又连接,是否断开连接。

    if (deviceConnected) {
        pTxCharacteristic->setValue(&txValue, 1);
        pTxCharacteristic->notify();
        txValue++;
		    delay(10); // bluetooth stack will go into congestion, if too many packets are sent
	  }
    
    // disconnecting
    if (!deviceConnected && oldDeviceConnected) {
        delay(500); // give the bluetooth stack the chance to get things ready
        pServer->startAdvertising(); // restart advertising
        Serial.println("start advertising");
        oldDeviceConnected = deviceConnected;
    }
    // connecting
    if (deviceConnected && !oldDeviceConnected) {
		// do stuff here on connecting
        oldDeviceConnected = deviceConnected;
    }

完整的代码见官方库的示例:ble_uart,调试工具可以使用LightBlue。

14. 播放MP3

在OpenCatEsp32/ModuleTests文件目录中有一个名为testMP3的示例程序,该程序主要功能是播放存储在SPIFFS文件系统中的.mp3文件。用户可以通过在串口监视器中输入0~6数字(.mp3文件序号)来选择播放不同的.mp3文件。

在编译程序之前,请先下载安装并配置软件环境 (Arduino IDE 1.8.*, 开发板 esp32 2.0*, 库 ESP8266Audio 1.9.7, 分区配置 BiBoard V0 )。

编译完成后,使用最新的SPIFFS文件上传插件,上传.mp3文件到BiBoard的SPIFFS文件系统分区中,具体方法请参考文件系统SPIFFS章节

注意:

  • Arduino IDE 2.0 版本目前无法增加 large_spiffs_16MB (4.5MB APP with OTA/6.93MB SPIFFS)配置选项。

  • Arduino IDE 1.8.* 中SPIFFS文件上传插件是用 Java 编写的。 而 Arduino IDE 2.0 是用不同的语言编写的(TypeScript + Golang),因此之前的上传插件不能在Arduino IDE 2.0 中使用。目前还没有支持Arduino IDE 2.0 SPIFFS文件上传插件。

因此建议您暂时安装使用 Arduino IDE 1.8.* 版本软件上传BiBoard程序及.mp3文件。

当然,你也可以使用VS Code + PlatformIO 上传BiBoard程序及.mp3文件,详情请参考以下文档:

Getting Started with VS Code and PlatformIO IDE for ESP32 and ESP8266 (Windows, Mac OS X, Linux Ubuntu)

ESP32 with VS Code and PlatformIO: Upload Files to Filesystem (SPIFFS)

在上传程序和.mp3文件之前,请将data文件夹移动到项目根目录下,代码文件存放在src目录下,并在项目根目录下platformio.ini文件中配置分区选项 board_build.partitions,如下图所示:

6. 陀螺仪IMU(MPU6050)

MPU6050是使用最广泛的6轴陀螺仪,不仅可以较为精确的测量3轴角速度及3轴加速度,也可以使用内置的数字运动处理器单元(DMP,Digital Motion Processor)来进行基于硬件的姿态融合计算。这样新手也可以很方便的使用。为此我们也采用的MPU6050陀螺仪。

MPU6050在Arduino UNO上的例程很多,最著名的就是jrowberg的I2Cdev和MPU6050DMP库:

但是很不幸,这个库无法直接在基于ESP32的BiBoard上运行。我们找到了Github上移植好的库,方便使用。这个库增加了了ARM和ESP系列的PGMSpace的定义,增加了校准函数,去掉了FIFO溢出的处理函数(有兴趣的朋友可以使用Beyond Compare进行代码对比)。库包含了I2Cdev和MPU6050,地址和压缩包如下:

下载完成后,在文档/Arduino/library下建立一个MPU6050的文件夹,把压缩包里的库文件拷贝进去即可。这个修改过的MPU6050的库也是可以兼容ARM和AVR的,所以如果你的计算机中有原版的I2Cdev和MPU6050库,可以删除。

我们使用官方的MPU6050_DMP6示例即可。

5. EEPROM(电擦除存储器)

EEPROM的用法同Arduino UNO一样,分为读和写两种操作。

读取操作:

  • EEPROM存储器的I2C地址

  • EEPROM内部地址(存储数据的地址)

  • 读取数值

写入操作:

  • EEPROM存储器的I2C地址

  • EEPROM内部地址(存储数据的地址)

  • 写入数值

在BiBoard的例程中,我们的EEPROM在I2C总线上的地址是0x54,容量为8192Bytes(64Kbit)。我们往EEPROM里从第一个地址开始顺序写入0到15一共16个数值,然后再读出来进行对比。理论上EEPROM写入的数值和对应的地址应该相同。

在NyBoard出厂测试中,我们也是使用这种方法,只不过更复杂一些。我们会使用固定列表填满EEPROM再读取出来进行对比。

#include <Wire.h>

#define EEPROM_ADDRESS 0x54
#define EEPROM_CAPACITY 8192       // 64Kbit
#define EEPROM_TESTBYTES 16

// write 1 byte EEPROM by address
void writeEEPROM(int deviceaddress, unsigned int eeaddress, byte data ) 
{
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));   // MSB
  Wire.write((int)(eeaddress & 0xFF)); // LSB
  Wire.write(data);
  Wire.endTransmission();
 
  delay(5);
}

// read 1 byte EEPROM by address
byte readEEPROM(int deviceaddress, unsigned int eeaddress ) 
{
  byte rdata = 0xFF;
 
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));   // MSB
  Wire.write((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();
 
  Wire.requestFrom(deviceaddress,1);
 
  if (Wire.available()) 
    rdata = Wire.read();
  return rdata;
}

void testI2CEEPROM(){

    byte tmpData = 0;

    Serial.println("EEPROM Testing...");
    
    // write EEPROM from 0 to EEPROM_TESTBYTES
    for(int i = 0; i < EEPROM_TESTBYTES; i++){
        writeEEPROM(EEPROM_ADDRESS, i, i % 256);
        delay(1);
    }

    Serial.println();
    
    // read from 0 to EEPROM_TESTBYTES
    for(int i = 0; i < EEPROM_TESTBYTES; i++){
        tmpData =  (int)readEEPROM(EEPROM_ADDRESS, i);
        Serial.print(tmpData);
        Serial.print("\t");
    }
}


void setup(){

    Serial.begin(115200);
    Wire.begin();
    
    testI2CEEPROM();
}

void loop(){
  
}

最后一点需要注意,EEPROM的操作,尤其是写入操作一般不会放入loop()循环当中。虽然EEPROM很耐擦写(10万次),但如果在loop循环中频繁写入某一个块,会导致EEPROM出故障的。

78KB
mpu6050.zip
archive
Open
mpu6050.zip

13. Arduino IDE增加硬件分区配置选项

ESP32板的Flash有16M,用16进制数表示存储地址的范围是:0x0 - 0x01000000 。

这是系统已经配置好的分区表, 如下图:

此分区表文件在电脑中的存储位置:

C:\Users\{YourUserName}\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.*\tools\partitions\large_spiffs_16MB.csv

从上述分区表中可以看出:APP0区和APP1区各4.5M;数据区为SPIFFS,大小为6.9M。

但是在Arduino IDE中,ESP32 Dev Module的硬件分区配置选项中未包含这项配置:

我们要为ESP32 Dev Module加入这个配置。

打开开发板配置文件:C:\Users\{YourUserName}\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.*\boards.txt

定位开发板的名称:esp32.name=ESP32 Dev Module,如下图所示:

在配置文件中ESP32 Dev Module分区配置的此行文字

esp32.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728

下面加入以下3行文字:

esp32.menu.PartitionScheme.large_spiffs=BiBoard V0(4.5 MB APP with OTA /6.9 MB SPIFFS)
esp32.menu.PartitionScheme.large_spiffs.build.partitions=large_spiffs_16MB
esp32.menu.PartitionScheme.large_spiffs.upload.maximum_size=4685824

下面分别解释一下这3行文字的含义:

esp32.menu.PartitionScheme.large_spiffs=BiBoard V0(4.5 MB APP with OTA /6.9 MB SPIFFS)

ESP32 分区配置的名称,命名为BiBoard V0(4.5M APP with OTA /6.9 MB SPIFFS),也可以换成你熟悉的其它名称。

esp32.menu.PartitionScheme.large_spiffs.build.partitions=large_spiffs_16MB

分区配置文件信息,就是large_spiffs_16MB.csv文件。你也可以自己写一个分区文件调整APP和数据区的文件大小。

esp32.menu.PartitionScheme.large_spiffs.upload.maximum_size=4685824

此行文字指定上传最大的程序大小 4685824 bytes。

下面我们试着编译一个简单的程序,测试以上配置是否设置成功。

重新打开Arduino IDE,我们可以看到刚才配置的BiBoard:

编译程序后,结果如下图所示:

编译完成,使用了213KB的Flash(4%)最大可使用4685824 bytes。

其中4685824 bytes就是刚才增加到配置文件的第三行文字中指定的最大程序上传字节数。

至此您已在Arduino IDE中完成了对拥有最大Flash空间的开发板的配置。

3. 模拟-数字转换器

BiBoard(ESP32)可变增益ADC的应用

BiBoard ADC使用说明

ESP32模块34,35,36和39脚仅支持输入,在BiBoard上我们将其配置成模拟输入端口,方便开发者们连接4个足底传感器。

BiBoard模拟输入模-数转换器(ADC)的用法和基础的Arduino UNO相同,但是精度更高(12位,UNO为10位),同时增加了可编程增益放大器,可以让ADC工作在最佳的范围内。

当输入1V电压信号时,如果按照普通配置为12bit访问,参考电压等于电源电压(3.3V):对应的输出为0~1241,ADC很大一部分量程会被浪费掉,造成数据不精确。当我们配置成可编程增益的时候,就可以让1V的输入信号填满几乎整个ADC量程,精度和分辨率大大提高。

我们的例程使用4个输入,分别配置成:0/2.5/6/11分贝的放大增益,需要注意的是ESP32 Arduino默认配置的是11分贝的放大增益。

我们使用“analogSetPinAttenuation(PIN_NAME, attenuation)”来配置单个输入引脚的增益,也可以使用“analogSetAttenuation(attenuation)”来配置全部模拟输入引脚的增益。

实际测试,当输入1V标准电压时,ADC的数值分别为:3850/2890/2025/1050。在今后的制作中,可以通过改变ADC的增益改变ADC的量程,而不需要麻烦的更换基准电压源了。

// Ain 34 - 0dB Gain - ADC_0db
analogSetPinAttenuation(34, ADC_0db);

// Ain 35 - 2.5dB Gain - ADC_2_5db
analogSetPinAttenuation(35, ADC_2_5db);

// Ain 36 - 6dB Gain - ADC_6db
analogSetPinAttenuation(36, ADC_6db);

// Ain 39 - 11dB Gain - ADC_11db    (default)
analogSetPinAttenuation(39, ADC_11db);

12. 文件系统SPIFFS

ESP32 文件系统SPIFFS配置指南

1. 为什么要使用文件系统

在BiBoard(ESP32)中,除了常规的程序区和引导区,我们在Flash分区中使用了文件系统。

独立分区的文件系统作用如下:

  • 在指定的地址保存数据而不会因为重新更新而删除(如校准数据,步态数据)

  • 不用外置的SD卡,节约了硬件资源

  • 保存HTML和CSS文件以建立Web服务器;

  • 保存图像,音频等文件;

常见的文件系统有Windows的NTFS,exFAT,Linux的日志文件系统Ext和XFS。但是在嵌入式领域,这些大型的文件系统太过庞大。我们使用了轻量级的SPIFFS(SPI Flash File System SPI闪存文件系统),用于SPI NOR flash设备的嵌入式文件系统,支持磨损均衡、文件系统一致性检查等功能。

因为轻量,SPIFFS最大的特点在于不支持树形目录,即全部文件扁平存储于同一层。ESP32提供的SPIFFS有以下特点:

  • 目前,SPIFFS 尚不支持目录,但可以生成扁平结构。如果 SPIFFS 挂载在 /spiffs 下,在 /spiffs/tmp/myfile.txt 路径下创建一个文件则会在 SPIFFS 中生成一个名为 /tmp/myfile.txt 的文件,而不是在 /spiffs/tmp 下生成名为 myfile.txt 的文件;

  • SPIFFS 并非实时栈,每次写操作耗时不等;

  • 目前,SPIFFS 尚不支持检测或处理已损坏的块。

2. 安装Arduino ESP32文件系统上传器

您可以通过自己的Arduino代码创建/保存以及删除文件,但操作比较繁琐,您需要将数据甚至二进制文件放入Arduino的Sketch并通过运行程序创建文件。

然而有个很好用的工具,可以直接把文件从计算机上传至文件系统,虽然比起“可移动存储”的“拖-放”式拷贝略微麻烦一些,但是无论是MP3音频还是HTML网页文件,都可以很轻松的上传至Flash。下面我们来学习如何使用这个插件。

3. 安装ESP32 文件上传插件

3.1 环境准备

请安装Arduino IDE(版本号:1.8.*),并且具有Arduino IDE的ESP32支持包(参考BiBoard V0快速上手指南)。

3.2 下载ESP32FS插件

下载ESP32FS插件的压缩包,地址:

下载适用于Arduino IDE的ESP32 SPIFFS文件系统fs

转到“Arduino”目录,然后打开“tools”文件夹

C:\Users\{YourUserName}\Documents\Arduino\tools

将下载的.zip文件夹解压缩到 “tools” 文件夹。您应该具有以下类似的文件目录结构:

C:\Users\{YourUserName}\Documents\Arduino\tools\ESP32FS\tool\esp32fs.jar

最后,重新启动Arduino IDE。

要检查插件是否已成功安装,请打开Arduino IDE。选择开发板(ESP32 Dev Module),转到“工具”,然后检查是否具有“ ESP32 Sketch Data Upload ”选项。

4. 使用文件系统上传器上传文件

要将文件上传到ESP32文件系统,请按照以下步骤:

  • 创建一个Arduino项目工程(比如:名称 Test.ino)并保存

  • 打开该工程的目录,可以使用“项目-显示项目文件夹”选项

  • 在该文件夹内,创建一个名为“data”的新文件夹。

  • 在“data” 文件夹中,放入您要保存到SPIFFS文件系统中的文件。例如,创建一个test_example.txt文件,内容如下图所示:

  • 在Arduino IDE中,点击 “工具” - “ESP32 Sketch Data Upload”

当您看到“ SPIFFS Image Uploaded ”提示信息时,文件已成功上传到SPIFFS文件系统中。

5. 文件系统使用例程

文件系统的例程SPIFFS_Test.ino(C:\Users\{YourUserName}\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.*\libraries\SPIFFS\examples\SPIFFS_Test)来源于ESP32官方未做修改,代码实现了“增删改查”的基本操作,并提供了一个SPI flash的IO测试程序。

如果有需要,建议直接使用例程的代码进行操作。

15. WiFi和OTA的使用

ESP32的Arduino例程提供了OTA(On The Air upgrade,在线升级)的功能。

1. 什么是OTA?

我们BiBoard的配置是16MB的Flash,具体分区如下:

OTA主要操作OTAdata区域和APP1,APP2区域。原理是:

  • BiBoard运行含有OTA功能的固件,此时启动指向APP1区域。

  • 通过WiFi向ESP32发送OTA指令,升级程序的二进制文件被传送到APP2区域。

  • 如果APP2传输完毕并校验成功,OTAdata指向APP2区域,下次则从更新后的固件区域(APP2)启动,APP1数据保留。下次OTA将写入APP1区覆盖掉旧的固件。

  • 如果因为网络传输错误导致APP2未传输完成,由于APP2没有通过校验,OTAdata没有指向APP2区域。复位启动后依然执行APP1区域的程序,损坏的APP2区域将在下次OTA的时候被全部擦写覆盖。

Arduino下的OTA操作

例程中首先配置WiFi,配置WiFi模式为STA(Station,基站模式)。启用WiFi函数并传入账户密码“WiFi.begin(ssid, password); ”

当WiFi顺利连接上时,会在串口打印IP地址;如果连接错误,ESP32会重启。

例程中可以配置端口号,OTA密钥或密钥的哈希,以及OTA的区域及类型(默认注释掉的)。

下面是几个类似回调函数的代码段,用于OTA各个阶段状态的判断。

按照例程配置完之后,在loop中调用“ArduinoOTA.handle();”即可。后面的analogWrite函数是为了区别不同固件更新的效果(通过改变数值)。

第一次使用串口下载,调用的是python工具“esptool”。下载完成后即可使用OTA。在端口选项中会发现多出了一个基于IP地址的端口,这便是OTA的地址。

选择该地址,右下角变成ESP32 Dev Module on 你的BiBoard的IP地址(192.168.1.178)。

同时也会弹出:“Serial monitor is not supported on network ports such as 192.168.1.178 for the ESP32 Dev Module in this release(该版本无法使用基于网络的串口监视器)”的警告。

Arduino的ESP32 OTA仅适用于更新程序,无法完成串口调试工作。如果需要调试BiBoard请连接USB-C接口。

下载程序,如图所示。

  Serial.println("Booting");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  // Port defaults to 3232
  // ArduinoOTA.setPort(3232);

  // Hostname defaults to esp3232-[MAC]
  // ArduinoOTA.setHostname("myesp32");

  // No authentication by default
  // ArduinoOTA.setPassword("admin");

  // Password can be set with it's md5 value as well
  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
  // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
  ArduinoOTA
    .onStart([]() {
      String type;
      if (ArduinoOTA.getCommand() == U_FLASH)
        type = "sketch";
      else // U_SPIFFS
        type = "filesystem";

      // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
      Serial.println("Start updating " + type);
    })
    .onEnd([]() {
      Serial.println("\nEnd");
    })
    .onProgress([](unsigned int progress, unsigned int total) {
      Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
    })
    .onError([](ota_error_t error) {
      Serial.printf("Error[%u]: ", error);
      if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
      else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
      else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
      else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
      else if (error == OTA_END_ERROR) Serial.println("End Failed");
    });

  ArduinoOTA.begin();
void loop() {
  ArduinoOTA.handle();
  analogWrite(2, 127);    // test OTA firmware
}
i2cdevlib/Arduino/MPU6050 at master · jrowberg/i2cdevlibGitHub
mpu6050/src at master · ElectronicCats/mpu6050GitHub
Logo
Releases · me-no-dev/arduino-esp32fs-pluginGitHub
Logo
Logo