Funpack第八期 — Arduino Nano 33 BLE Sense 环境监测站
利用 Arduino Nano 33 BLE Sense 的板载传感器,搭建一个小型环境监测站用于监测户外环境,待监测的参数包括温湿度、大气压强、日照强度、周边平均噪声等信息。
标签
Funpack
Arduino Nano 33 BLE Sense
Funpack第八期
环境监测站
枫雪天
更新2021-05-19
873

任务介绍

   本项目实现了Funpack第八期活动的任务二——环境监测站。

   具体的待监测的参数包括:

  • 周边环境温度(精度:±0.1°C, ±0.1°F)

  • 周边环境湿度(精度:±1%)

  • 大气压强(精度:±0.1kPa, ±0.1psi)

  • 日照强度(用于判断白天/夜晚)

  • 周边平均噪声(精度:±1dB)

   项目采用一块0.96’的OLED显示屏进行数据显示。

硬件搭建

   首先介绍本期活动的主角 Arduino Nano 33 BLE Sense开发板,这块开发板是Arduino系列中非常亮眼的一款开发板。主要原因是它在仅有Arduino Nano相当的体积下,同时配备了ARM Cortex-M4内核的微处理器和温湿度、光强、麦克风等众多实用的传感器,是一块非常优秀的开发板。

本期活动所需要的传感器外设都已在板卡上配备完全,除去主控板外,所需要的硬件就是一块OLED显示屏,我使用了另一块物联网底板上的屏幕作为数据显示器。

最后用杜邦线连接 Arduino Nano 33 BLE Sense 开发板与OLED模块的IIC通信接口和3.3V电源接口,项目的硬件部分就搭建完成了。

程序分析

   接下来我们详细分析本次的代码。由于Arduino的生态非常完整,所以开发板上的各种外设基本都能够找到对应的库,以及简单的例程。

   我使用的开发环境是VSCode+PlatformIO,相比旧版的Arduino IDE,VSCode的代码编辑能力非常强,并且最重要的是,扩展PlatformIO可以实现增量编译。再也不会面临旧版Arduino IDE的一次编译就要几分钟的情况,可以极大地提高开发效率。

   首先详解主程序:

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Arduino_HTS221.h>
#include <Arduino_LPS22HB.h>
#include <Arduino_APDS9960.h>
#include <arm_math.h>
#include "Nano33BLEMicrophoneRMS.h"
#include <Scheduler.h>
​
#define MICROPHONE_BUFFER_SIZE_IN_WORDS (256U)
​
/*****************************************************************************/
/*GLOBAL Data                                                               */
/*****************************************************************************/
Nano33BLEMicrophoneRMSData microphoneData;
int16_t refBuf[32] = {0};
float temperature, humidity;
float barometricPressure;
int colourR, colourG, colourB, colourC;
​
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
​
void loop2(void);
void loop3(void);
​
void setup()
{
 pinMode(LED_BUILTIN, OUTPUT);
​
 display.clearDisplay();
 display.setTextSize(1);      //设置字体大小
 display.setTextColor(WHITE); //设置字体颜色白色
 // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
 display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3D (for the 128x64)
​
 if (!HTS.begin())
{
   display.println("Failed to initialize humidity temperature sensor!");
   goto error_code;
}
 if (!BARO.begin())
{
   display.println("Failed to initialize preasure sensor!");
   goto error_code;
}
​
 APDS.setGestureSensitivity(50);
 if (!APDS.begin())
{
   display.println("Failed to initialize APDS sensor!");
   goto error_code;
}
 APDS.setLEDBoost(0);
​
 MicrophoneRMS.begin();
​
 Serial.begin(115200);
​
 Scheduler.startLoop(loop2);
 return;
​
error_code:
 display.display();
 while (1)
  ;
}
​
void loop()
{
 static int num = 0;
 display.clearDisplay();
 display.setCursor(0, 0); //设置字体的起始位置
​
 temperature = HTS.readTemperature();
 humidity = HTS.readHumidity();
 barometricPressure = BARO.readPressure();
​
 display.print("temp: ");
 display.print(temperature);
 display.println("'C");
 display.print("humi: ");
 display.print(humidity);
 display.println("%");
 display.print("pres: ");
 display.print(barometricPressure);
 display.println("kPa");
​
 if (APDS.colorAvailable())
{
   APDS.readColor(colourR, colourG, colourB, colourC);
}
 display.print("R: ");
 display.print(colourR);
 display.print(" G: ");
 display.println(colourG);
 display.print("B: ");
 display.print(colourB);
 display.print(" C: ");
 display.println(colourC);
​
 if (colourC < 600)
{
   display.println("state: night");
}
 else
{
   display.println("state: day");
}
​
 uint16_t db = 0;
 if (MicrophoneRMS.pop(microphoneData) && microphoneData.RMSValue != 0)
{
   db = (uint16_t)20 * log10f(microphoneData.RMSValue + 0.0001);
}
 display.print("sound: ");
 display.print(db);
 display.println("dB");
​
 display.print("loops: ");
 display.println(num++);
 display.display(); //把缓存的都显示
​
 delay(50);
}
​
void loop2()
{
 static int blink = 0;
 digitalWrite(LED_BUILTIN, blink = 1 - blink);
 delay(1000);
}

   首先通过包含头文件引入相关的库,随后在初始化函数中进行OLED显示器和各类传感器的初始化。随后进入主循环。利用各传感器库函数提供的API读取各传感器的数值,并在OLED屏幕上显示出来。

#include "Nano33BLEMicrophoneRMS.h"
#include <PDM.h>
#include <arm_math.h>
​
/*****************************************************************************/
/*MACROS                                                                     */
/*****************************************************************************/
/* This value was also used in the PDM example, seems like a good enough reason to
* continue using it. With this value and 16kHz sampling frequency, the RMS sampling
* period will be 16mS */
#define MICROPHONE_BUFFER_SIZE_IN_WORDS (256U)
​
/*****************************************************************************/
/*GLOBAL Data                                                               */
/*****************************************************************************/
const uint32_t MICROPHONE_BUFFER_SIZE_IN_BYTES_C = (MICROPHONE_BUFFER_SIZE_IN_WORDS * sizeof(int16_t));
​
/* MP34DT05 Microphone data buffer with a bit depth of 16. Also a variable for the RMS value */
static int16_t microphoneBuffer[MICROPHONE_BUFFER_SIZE_IN_WORDS];
static rtos::Semaphore bufferReadySemaphore;
​
/*****************************************************************************/
/*CLASS MEMBER FUNCTION IMPLEMENTATION                                       */
/*****************************************************************************/
/**
* @brief
* This member function implementation should do everything requred to
* initialise the sensor this class is designed for. Immediately after
* this function is executed, the RTOS will begin periodically reading
* values from the sensor.
*
* @param none
* @return none
*/
void Nano33BLEMicrophoneRMS::init()
{
 /* PDM setup for MP34DT05 microphone */
 /* configure the data receive callback to transfer data to local buffer */
 PDM.onReceive(Nano33BLEMicrophoneRMS::PDM_callback);
 /* Initialise single PDM channel with a 16KHz sample rate (only 16kHz or 44.1kHz available */
 if (!PDM.begin(1, 16000))
{
   /* Something went wrong... Put this thread to sleep indefinetely. */
   osSignalWait(0x0001, osWaitForever);
}
 else
{
   /* Gain values can be from 0 to 80 (around 38db). Check out nrf_pdm.h
    * from the nRF528x-mbedos core to confirm this.
    */
   /*
    * This has to be done after PDM.begin() is called as begin() always
    * sets the gain as the default PDM.h value (20).
    */
   PDM.setGain(80);
}
}
​
/**
* @brief
* This member function implementation should do everything requred to
* read one reading from the sensor this class is designed for. This
* function is put inside an endless while loop so will be called
* endlessly, therefore a sleep should be called at the end of the
* function. The sleep period should be defined by the READ_PERIOD_MS
* defined at the start of this file.
*
* @param none
* @return none
*/
void Nano33BLEMicrophoneRMS::read(void)
{
 /*
  * Place the implementation required to read the sensor
  * once here.
  */
 bufferReadySemaphore.acquire();
 Nano33BLEMicrophoneRMSData data;
 arm_rms_q15((q15_t*)microphoneBuffer, MICROPHONE_BUFFER_SIZE_IN_WORDS, (q15_t*)&data.RMSValue);
 data.timeStampMs = millis();
 push(data);
}
​
void Nano33BLEMicrophoneRMS::PDM_callback(void)
{
 // query the number of bytes available
 int bytesAvailable = PDM.available();
​
 if(bytesAvailable == MICROPHONE_BUFFER_SIZE_IN_BYTES_C)
{
   PDM.read(microphoneBuffer, MICROPHONE_BUFFER_SIZE_IN_BYTES_C);
   bufferReadySemaphore.release();
}
}
​
Nano33BLEMicrophoneRMS MicrophoneRMS;

   这里需要特殊说明的是周边平均噪声的测量,这里需要一定的公式计算与转换,才能获得相应的数值。特别是均方根的计算,程序使用一段长度为256的数组缓冲区。来存储麦克风测量的原始数值。并通过ARM Cortex-M4提供的DSP库函数计算均方根数值,将数值代入噪声的分贝计算公式,就可以得到平均噪声。

   Tips:程序中使用了CMSIS官方的DSP库,故可以利用ARM Cortex-M4内置的DSP与FPU单元进行加速计算,但在本文档编写时,最新版的framework-arduino-mbed库缺失相应的库文件,会导致编译失败。可以从Github官方CMSIS库中下载libarm_cortexM4l_math.a库文件,放到本地的framework-arduino-mbed\variants\ARDUINO_NANO33BLE\libs目录下,并在编译参数中加入build_flags = -l arm_cortexM4l_math,就可以正常使用硬件加速了。

功能展示

Fn4Dqf-j0yA_NxUucxyzf8kvTxy5

心得体会

   很荣幸参加本期的Funpack活动,这也是我第一参加Funpack系列的活动。Arduino Nano 33 BLE Sense 是我接触到的第一块高性能Arduino开发板,在学习和编程的过程中,我感受到国内外创客群体对Arduino系列开发板的热爱以及对Arduino生态的贡献。虽然使用Arduino可以享受到快速调库、打造原型的酣畅淋漓的感觉,但真正热爱编程的程序员不应止步于此,Arduino库的源代码中蕴含着非常优秀的面向对象的编程思想和软件架构,这也是我们应该深入学习的。同时,应如苏老师所说,放宽视野,充分学习外文的优秀技术资源,取长补短,快速提升技术水平。

   最后,感谢硬禾学堂和得捷电子的大力支持,以及交流群中小伙伴们的奇思妙想,祝愿Funpack系列活动越办越好!

 

附件下载
main.cpp
源代码-主程序
Nano33BLEMicrophoneRMS.cpp
源代码-RMS子程序
团队介绍
个人
团队成员
枫雪天
一个热爱嵌入式的算法工程师
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号