文章目录
0 前言1 简介2 主要器件3 实现效果4 硬件设计风速计简介Adafruit风速计传感器5 软件说明Arduino风速计的源代码/程序5 最后0 前言
🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。
为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是
🚩基于单片机的风速测量系统
🥇学长这里给一个题目综合评分(每项满分5分)
难度系数:4分工作量:4分创新点:3分
🧿选题指导, 项目分享:
/dancheng-senior/project-sharing-1/blob/master/%E6%AF%95%E8%AE%BE%E6%8C%87%E5%AF%BC/README.md
1 简介
基于Arduino单片机的风速计测量系统,使用Adafruit风速计传感器模块和Arduino开发板测量风速。风速计是用于测量风速和风向的设备,它也是一种非常常见的气象站仪器。
在所有涉及风速测量的研究中,例如气象学、风力涡轮机和农业,我们需要准确的风速信息来进行决策。市面上有几种类型的风速计,如风杯、热线和皮托管。这里使用的风速计是杯型风速计。
2 主要器件
Arduino Nano开发板Adafruit风速计传感器0.96寸I2C OLED显示屏MT3608升压转换器模块3.7V锂离子电池3 实现效果
4 硬件设计
风速计简介
本项目,我们使用的风速计传感器是Adafruit公司的风速计。Adafruit风速计能够测量高达70m/s或156mph的风速,这对于我们来说应该足够了。我们可以将Adafruit风速计传感器与Arduino和OLED显示屏连接起来。传感器将以m/s为单位测量风速,然后在OLED屏幕上显示该值。您可以将风速从m/s转换为英里/小时或公里/小时。
风速计(Anemometer)是用于测量风速和风向的设备。它广泛用于测量大气、风洞和其他气流应用中的气流速度。该术语源自希腊语 anemos,意思是风,用于描述气象学中使用的任何风速仪器。
用于风速测量的最广泛使用的风速计是转杯式电动风速计。旋转风杯驱动发电机。发电机的输出运行一个在风速中校准的电表。三杯风速计目前被用作风资源评估研究和实践的行业标准。
杯型风速计由安装在水平臂上的3或4个半球形杯组成,这些杯安装在垂直轴上。在任何水平方向通过杯子的气流以大致与风速成比例的速度转动轴。因此,在设定的时间间隔内计算轴的转数会产生一个与速度范围内的平均风速成比例的值。
Adafruit风速计传感器
Adafruit风速计传感器是一种三杯型风速计,能够测量高达 70m/s 或 156mph 的风速。它由外壳、风杯、电路模块组成。
传感器配有3芯电缆。黑色线连接电源信号地,棕色线用于电源,可以是7-24v DC,第三根蓝色线通过模拟电压提供测量值。输出模拟电压范围为0.4V(0 m/s 风)至 2.0V(32.4m/s 风)。
传感器模块的规格
所需电压:7-24v DC
输出:0.4V至2V
测试范围:0.5m/s至50m/s
启动风速:0.2m/s
分辨率:0.1m/s
精度:最差情况1米/秒
最大风速:70m/s
引脚: 1 – 电源 (棕色线), 2 – 地 (黑色线), 3 – 信号 (蓝色线)
传感器的内部电路
去除掉外壳后就可以传感器。内部电路包含集成到内部驱动器中的光伏模块、工业处理器、电流发生器。
电路PCB的材料为军工A级,保证了参数的稳定性和电性能的质量。内部电子元件均为工业级芯片,具有极其可靠的抗电磁干扰能力。内部系统可在–20℃~+50℃,湿度35%~85%下正常工作。
电缆的插头是军用插头。具有良好的防腐和防侵蚀性能,可确保仪器长期使用。
原理图或电路
dafruit 风速计在7-24VDC 之间工作。所以Arduino的电压不足以启动传感器。因此,必须使用MT3608 DC-DC升压转换器模块将锂离子电池的3.7V升压至7.5V。要调整输出电压,首先将电池连接到MT3608模块的输入端,然后旋转电位器,直到万用表上的输出电压显示为7.5V。
传感器的VCC和Arduino的Vin引脚由升压转换器模块的输出提供7.5V。作为风速计传感器模拟输出引脚的蓝线连接到Arduino开发板的A0。OLED显示屏的I2C引脚,即SDA和SCL连接到Arduino的A4和A5。 OLED显示屏由Arduino的3.3V引脚提供电压。
5 软件说明
Arduino风速计的源代码/程序
以下是使用Arduino IDE编写的简易Arduino风速计代码。编译Arduino风速计项目代码并上传到Arduino开发板。您可以将风速更改为m/s 或 mph。
#include "SPI.h"#include "Adafruit_GFX.h"#include "Adafruit_ILI9341.h"// For the Adafruit shield, these are the default.#define TFT_DC 9#define TFT_CS 10// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DCAdafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);// If using the breakout, change pins as desired//Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);void setup() {Serial.begin(9600);Serial.println("ILI9341 Test!"); tft.begin();// read diagnostics (optional but can help debug problems)uint8_t x = tft.readcommand8(ILI9341_RDMODE);Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX);x = tft.readcommand8(ILI9341_RDMADCTL);Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX);x = tft.readcommand8(ILI9341_RDPIXFMT);Serial.print("Pixel Format: 0x"); Serial.println(x, HEX);x = tft.readcommand8(ILI9341_RDIMGFMT);Serial.print("Image Format: 0x"); Serial.println(x, HEX);x = tft.readcommand8(ILI9341_RDSELFDIAG);Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX); Serial.println(F("BenchmarkTime (microseconds)"));delay(10);Serial.print(F("Screen fill "));Serial.println(testFillScreen());delay(500);Serial.print(F("Text "));Serial.println(testText());delay(3000);Serial.print(F("Lines"));Serial.println(testLines(ILI9341_CYAN));delay(500);Serial.print(F("Horiz/Vert Lines "));Serial.println(testFastLines(ILI9341_RED, ILI9341_BLUE));delay(500);Serial.print(F("Rectangles (outline)"));Serial.println(testRects(ILI9341_GREEN));delay(500);Serial.print(F("Rectangles (filled)"));Serial.println(testFilledRects(ILI9341_YELLOW, ILI9341_MAGENTA));delay(500);Serial.print(F("Circles (filled) "));Serial.println(testFilledCircles(10, ILI9341_MAGENTA));Serial.print(F("Circles (outline) "));Serial.println(testCircles(10, ILI9341_WHITE));delay(500);Serial.print(F("Triangles (outline)"));Serial.println(testTriangles());delay(500);Serial.print(F("Triangles (filled) "));Serial.println(testFilledTriangles());delay(500);Serial.print(F("Rounded rects (outline) "));Serial.println(testRoundRects());delay(500);Serial.print(F("Rounded rects (filled) "));Serial.println(testFilledRoundRects());delay(500);Serial.println(F("Done!"));}void loop(void) {for(uint8_t rotation=0; rotation<4; rotation++) {tft.setRotation(rotation);testText();delay(1000);}}unsigned long testFillScreen() {unsigned long start = micros();tft.fillScreen(ILI9341_BLACK);yield();tft.fillScreen(ILI9341_RED);yield();tft.fillScreen(ILI9341_GREEN);yield();tft.fillScreen(ILI9341_BLUE);yield();tft.fillScreen(ILI9341_BLACK);yield();return micros() - start;}unsigned long testText() {tft.fillScreen(ILI9341_BLACK);unsigned long start = micros();tft.setCursor(0, 0);tft.setTextColor(ILI9341_WHITE); tft.setTextSize(1);tft.println("Hello World!");tft.setTextColor(ILI9341_YELLOW); tft.setTextSize(2);tft.println(1234.56);tft.setTextColor(ILI9341_RED); tft.setTextSize(3);tft.println(0xDEADBEEF, HEX);tft.println();tft.setTextColor(ILI9341_GREEN);tft.setTextSize(5);tft.println("Groop");tft.setTextSize(2);tft.println("I implore thee,");tft.setTextSize(1);tft.println("my foonting turlingdromes.");tft.println("And hooptiously drangle me");tft.println("with crinkly bindlewurdles,");tft.println("Or I will rend thee");tft.println("in the gobberwarts");tft.println("with my blurglecruncheon,");tft.println("see if I don't!");return micros() - start;}unsigned long testLines(uint16_t color) {unsigned long start, t;int x1, y1, x2, y2,w = tft.width(),h = tft.height();tft.fillScreen(ILI9341_BLACK);yield();x1 = y1 = 0;y2 = h - 1;start = micros();for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);x2 = w - 1;for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);t= micros() - start; // fillScreen doesn't count against timingyield();tft.fillScreen(ILI9341_BLACK);yield();x1 = w - 1;y1 = 0;y2 = h - 1;start = micros();for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);x2 = 0;for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);t += micros() - start;yield();tft.fillScreen(ILI9341_BLACK);yield();x1 = 0;y1 = h - 1;y2 = 0;start = micros();for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);x2 = w - 1;for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);t += micros() - start;yield();tft.fillScreen(ILI9341_BLACK);yield();x1 = w - 1;y1 = h - 1;y2 = 0;start = micros();for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);x2 = 0;for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);yield();return micros() - start;}unsigned long testFastLines(uint16_t color1, uint16_t color2) {unsigned long start;int x, y, w = tft.width(), h = tft.height();tft.fillScreen(ILI9341_BLACK);start = micros();for(y=0; y<h; y+=5) tft.drawFastHLine(0, y, w, color1);for(x=0; x<w; x+=5) tft.drawFastVLine(x, 0, h, color2);return micros() - start;}unsigned long testRects(uint16_t color) {unsigned long start;int n, i, i2,cx = tft.width() / 2,cy = tft.height() / 2;tft.fillScreen(ILI9341_BLACK);n= min(tft.width(), tft.height());start = micros();for(i=2; i<n; i+=6) {i2 = i / 2;tft.drawRect(cx-i2, cy-i2, i, i, color);}return micros() - start;}unsigned long testFilledRects(uint16_t color1, uint16_t color2) {unsigned long start, t = 0;int n, i, i2,cx = tft.width() / 2 - 1,cy = tft.height() / 2 - 1;tft.fillScreen(ILI9341_BLACK);n = min(tft.width(), tft.height());for(i=n; i>0; i-=6) {i2 = i / 2;start = micros();tft.fillRect(cx-i2, cy-i2, i, i, color1);t += micros() - start;// Outlines are not included in timing resultstft.drawRect(cx-i2, cy-i2, i, i, color2);yield();}return t;}unsigned long testFilledCircles(uint8_t radius, uint16_t color) {unsigned long start;int x, y, w = tft.width(), h = tft.height(), r2 = radius * 2;tft.fillScreen(ILI9341_BLACK);start = micros();for(x=radius; x<w; x+=r2) {for(y=radius; y<h; y+=r2) {tft.fillCircle(x, y, radius, color);}}return micros() - start;}unsigned long testCircles(uint8_t radius, uint16_t color) {unsigned long start;int x, y, r2 = radius * 2,w = tft.width() + radius,h = tft.height() + radius;// Screen is not cleared for this one -- this is// intentional and does not affect the reported time.start = micros();for(x=0; x<w; x+=r2) {for(y=0; y<h; y+=r2) {tft.drawCircle(x, y, radius, color);}}return micros() - start;}unsigned long testTriangles() {unsigned long start;int n, i, cx = tft.width() / 2 - 1,cy = tft.height() / 2 - 1;tft.fillScreen(ILI9341_BLACK);n= min(cx, cy);start = micros();for(i=0; i<n; i+=5) {tft.drawTriangle(cx , cy - i, // peakcx - i, cy + i, // bottom leftcx + i, cy + i, // bottom righttft.color565(i, i, i));}return micros() - start;}unsigned long testFilledTriangles() {unsigned long start, t = 0;int i, cx = tft.width() / 2 - 1,cy = tft.height() / 2 - 1;tft.fillScreen(ILI9341_BLACK);start = micros();for(i=min(cx,cy); i>10; i-=5) {start = micros();tft.fillTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,tft.color565(0, i*10, i*10));t += micros() - start;tft.drawTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,tft.color565(i*10, i*10, 0));yield();}return t;}unsigned long testRoundRects() {unsigned long start;int w, i, i2,cx = tft.width() / 2 - 1,cy = tft.height() / 2 - 1;tft.fillScreen(ILI9341_BLACK);w= min(tft.width(), tft.height());start = micros();for(i=0; i<w; i+=6) {i2 = i / 2;tft.drawRoundRect(cx-i2, cy-i2, i, i, i/8, tft.color565(i, 0, 0));}return micros() - start;}unsigned long testFilledRoundRects() {unsigned long start;int i, i2,cx = tft.width() / 2 - 1,cy = tft.height() / 2 - 1;tft.fillScreen(ILI9341_BLACK);start = micros();for(i=min(tft.width(), tft.height()); i>20; i-=6) {i2 = i / 2;tft.fillRoundRect(cx-i2, cy-i2, i, i, i/8, tft.color565(0, i, 0));yield();}return micros() - start;}
SSD1306屏幕代码
#include <SPI.h>#include <Wire.h>#include <Adafruit_GFX.h>#include <Adafruit_SSD1306.h>#define SCREEN_WIDTH 128 // OLED display width, in pixels#define SCREEN_HEIGHT 32 // OLED display height, in pixels// Declaration for SSD1306 display connected using software SPI (default case):#define OLED_MOSI 9#define OLED_CLK 10#define OLED_DC 11#define OLED_CS 12#define OLED_RESET 13Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);/* Comment out above, uncomment this block to use hardware SPI#define OLED_DC6#define OLED_CS7#define OLED_RESET 8Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,&SPI, OLED_DC, OLED_RESET, OLED_CS);*/#define NUMFLAKES10 // Number of snowflakes in the animation example#define LOGO_HEIGHT 16#define LOGO_WIDTH 16static const unsigned char PROGMEM logo_bmp[] ={0b00000000, 0b11000000,0b00000001, 0b11000000,0b00000001, 0b11000000,0b00000011, 0b11100000,0b11110011, 0b11100000,0b11111110, 0b11111000,0b01111110, 0b11111111,0b00110011, 0b10011111,0b00011111, 0b11111100,0b00001101, 0b01110000,0b00011011, 0b10100000,0b00111111, 0b11100000,0b00111111, 0b11110000,0b01111100, 0b11110000,0b01110000, 0b01110000,0b00000000, 0b00110000 };void setup() {Serial.begin(9600);// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internallyif(!display.begin(SSD1306_SWITCHCAPVCC)) {Serial.println(F("SSD1306 allocation failed"));for(;;); // Don't proceed, loop forever}// Show initial display buffer contents on the screen --// the library initializes this with an Adafruit splash screen.display.display();delay(2000); // Pause for 2 seconds// Clear the bufferdisplay.clearDisplay();// Draw a single pixel in whitedisplay.drawPixel(10, 10, SSD1306_WHITE);// Show the display buffer on the screen. You MUST call display() after// drawing commands to make them visible on screen!display.display();delay(2000);// display.display() is NOT necessary after every single drawing command,// unless that's what you want...rather, you can batch up a bunch of// drawing operations and then update the screen all at once by calling// display.display(). These examples demonstrate both approaches...testdrawline();// Draw many linestestdrawrect();// Draw rectangles (outlines)testfillrect();// Draw rectangles (filled)testdrawcircle(); // Draw circles (outlines)testfillcircle(); // Draw circles (filled)testdrawroundrect(); // Draw rounded rectangles (outlines)testfillroundrect(); // Draw rounded rectangles (filled)testdrawtriangle(); // Draw triangles (outlines)testfilltriangle(); // Draw triangles (filled)testdrawchar();// Draw characters of the default fonttestdrawstyles(); // Draw 'stylized' characterstestscrolltext(); // Draw scrolling texttestdrawbitmap(); // Draw a small bitmap image// Invert and restore display, pausing in-betweendisplay.invertDisplay(true);delay(1000);display.invertDisplay(false);delay(1000);testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps}void loop() {}void testdrawline() {int16_t i;display.clearDisplay(); // Clear display bufferfor(i=0; i<display.width(); i+=4) {display.drawLine(0, 0, i, display.height()-1, SSD1306_WHITE);display.display(); // Update screen with each newly-drawn linedelay(1);}for(i=0; i<display.height(); i+=4) {display.drawLine(0, 0, display.width()-1, i, SSD1306_WHITE);display.display();delay(1);}delay(250);display.clearDisplay();for(i=0; i<display.width(); i+=4) {display.drawLine(0, display.height()-1, i, 0, SSD1306_WHITE);display.display();delay(1);}for(i=display.height()-1; i>=0; i-=4) {display.drawLine(0, display.height()-1, display.width()-1, i, SSD1306_WHITE);display.display();delay(1);}delay(250);display.clearDisplay();for(i=display.width()-1; i>=0; i-=4) {display.drawLine(display.width()-1, display.height()-1, i, 0, SSD1306_WHITE);display.display();delay(1);}for(i=display.height()-1; i>=0; i-=4) {display.drawLine(display.width()-1, display.height()-1, 0, i, SSD1306_WHITE);display.display();delay(1);}delay(250);display.clearDisplay();for(i=0; i<display.height(); i+=4) {display.drawLine(display.width()-1, 0, 0, i, SSD1306_WHITE);display.display();delay(1);}for(i=0; i<display.width(); i+=4) {display.drawLine(display.width()-1, 0, i, display.height()-1, SSD1306_WHITE);display.display();delay(1);}delay(2000); // Pause for 2 seconds}void testdrawrect(void) {display.clearDisplay();for(int16_t i=0; i<display.height()/2; i+=2) {display.drawRect(i, i, display.width()-2*i, display.height()-2*i, SSD1306_WHITE);display.display(); // Update screen with each newly-drawn rectangledelay(1);}delay(2000);}void testfillrect(void) {display.clearDisplay();for(int16_t i=0; i<display.height()/2; i+=3) {// The INVERSE color is used so rectangles alternate white/blackdisplay.fillRect(i, i, display.width()-i*2, display.height()-i*2, SSD1306_INVERSE);display.display(); // Update screen with each newly-drawn rectangledelay(1);}delay(2000);}void testdrawcircle(void) {display.clearDisplay();for(int16_t i=0; i<max(display.width(),display.height())/2; i+=2) {display.drawCircle(display.width()/2, display.height()/2, i, SSD1306_WHITE);display.display();delay(1);}delay(2000);}void testfillcircle(void) {display.clearDisplay();for(int16_t i=max(display.width(),display.height())/2; i>0; i-=3) {// The INVERSE color is used so circles alternate white/blackdisplay.fillCircle(display.width() / 2, display.height() / 2, i, SSD1306_INVERSE);display.display(); // Update screen with each newly-drawn circledelay(1);}delay(2000);}void testdrawroundrect(void) {display.clearDisplay();for(int16_t i=0; i<display.height()/2-2; i+=2) {display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i,display.height()/4, SSD1306_WHITE);display.display();delay(1);}delay(2000);}void testfillroundrect(void) {display.clearDisplay();for(int16_t i=0; i<display.height()/2-2; i+=2) {// The INVERSE color is used so round-rects alternate white/blackdisplay.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i,display.height()/4, SSD1306_INVERSE);display.display();delay(1);}delay(2000);}void testdrawtriangle(void) {display.clearDisplay();for(int16_t i=0; i<max(display.width(),display.height())/2; i+=5) {display.drawTriangle(display.width()/2 , display.height()/2-i,display.width()/2-i, display.height()/2+i,display.width()/2+i, display.height()/2+i, SSD1306_WHITE);display.display();delay(1);}delay(2000);}void testfilltriangle(void) {display.clearDisplay();for(int16_t i=max(display.width(),display.height())/2; i>0; i-=5) {// The INVERSE color is used so triangles alternate white/blackdisplay.fillTriangle(display.width()/2 , display.height()/2-i,display.width()/2-i, display.height()/2+i,display.width()/2+i, display.height()/2+i, SSD1306_INVERSE);display.display();delay(1);}delay(2000);}void testdrawchar(void) {display.clearDisplay();display.setTextSize(1);// Normal 1:1 pixel scaledisplay.setTextColor(SSD1306_WHITE); // Draw white textdisplay.setCursor(0, 0);// Start at top-left cornerdisplay.cp437(true); // Use full 256 char 'Code Page 437' font// Not all the characters will fit on the display. This is normal.// Library will draw what it can and the rest will be clipped.for(int16_t i=0; i<256; i++) {if(i == '\n') display.write(' ');elsedisplay.write(i);}display.display();delay(2000);}void testdrawstyles(void) {display.clearDisplay();display.setTextSize(1); // Normal 1:1 pixel scaledisplay.setTextColor(SSD1306_WHITE); // Draw white textdisplay.setCursor(0,0); // Start at top-left cornerdisplay.println(F("Hello, world!"));display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' textdisplay.println(3.141592);display.setTextSize(2); // Draw 2X-scale textdisplay.setTextColor(SSD1306_WHITE);display.print(F("0x")); display.println(0xDEADBEEF, HEX);display.display();delay(2000);}void testscrolltext(void) {display.clearDisplay();display.setTextSize(2); // Draw 2X-scale textdisplay.setTextColor(SSD1306_WHITE);display.setCursor(10, 0);display.println(F("scroll"));display.display();// Show initial textdelay(100);// Scroll in various directions, pausing in-between:display.startscrollright(0x00, 0x0F);delay(2000);display.stopscroll();delay(1000);display.startscrollleft(0x00, 0x0F);delay(2000);display.stopscroll();delay(1000);display.startscrolldiagright(0x00, 0x07);delay(2000);display.startscrolldiagleft(0x00, 0x07);delay(2000);display.stopscroll();delay(1000);}void testdrawbitmap(void) {display.clearDisplay();display.drawBitmap((display.width() - LOGO_WIDTH ) / 2,(display.height() - LOGO_HEIGHT) / 2,logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);display.display();delay(1000);}#define XPOS 0 // Indexes into the 'icons' array in function below#define YPOS 1#define DELTAY 2void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) {int8_t f, icons[NUMFLAKES][3];// Initialize 'snowflake' positionsfor(f=0; f< NUMFLAKES; f++) {icons[f][XPOS] = random(1 - LOGO_WIDTH, display.width());icons[f][YPOS] = -LOGO_HEIGHT;icons[f][DELTAY] = random(1, 6);Serial.print(F("x: "));Serial.print(icons[f][XPOS], DEC);Serial.print(F(" y: "));Serial.print(icons[f][YPOS], DEC);Serial.print(F(" dy: "));Serial.println(icons[f][DELTAY], DEC);}for(;;) {// Loop forever...display.clearDisplay(); // Clear the display buffer// Draw each snowflake:for(f=0; f< NUMFLAKES; f++) {display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SSD1306_WHITE);}display.display(); // Show the display buffer on the screendelay(200); // Pause for 1/10 second// Then update coordinates of each flake...for(f=0; f< NUMFLAKES; f++) {icons[f][YPOS] += icons[f][DELTAY];// If snowflake is off the bottom of the screen...if (icons[f][YPOS] >= display.height()) {// Reinitialize to a random position, just off the topicons[f][XPOS] = random(1 - LOGO_WIDTH, display.width());icons[f][YPOS] = -LOGO_HEIGHT;icons[f][DELTAY] = random(1, 6);}}}}