第一篇:最简单DIY基于ESP32CAM的物联网相机系统①(用网页实现拍照图传)
第二篇:最简单DIY基于ESP32CAM的物联网相机系统②(在JAVAWEB服务器实现图片查看器)
第三篇:最简单DIY基于ESP32CAM的物联网相机系统③(在JSP服务器图传相片给所有客户端欣赏)
第四篇:最简单DIY基于ESP32CAM的物联网相机系统④(用调试串口助手实现串口图传)
第五篇(上):最简单DIY基于ESP32CAM的物联网相机系统⑤(用1306OLED实现WIFI黑白屏照相机)


文章目录

  • 前言
  • 一、最简单DIY基于ESP32CAM的物联网相机系统⑤(用1306OLED实现WIFI黑白屏照相机)是什么?
  • 二、实现需求
    • 1.先打开官方1306显示屏的代码
    • 2.再打开官方ESP32CAM照相机程序
    • 3.修改上面1和2的官方源码变成自己的源码
  • 三、运行与调试
  • 总结

前言

    daodanjishui物联网核心原创技术之最简单DIY基于ESP32CAM的物联网相机系统⑤(用1306OLED实现WIFI黑白屏照相机)。
    该专栏的第四篇博文:最简单DIY基于ESP32CAM的物联网相机系统④(用调试串口助手实现串口图传) 实现的是用ESP32CAM拍摄照片传输到电脑串口调试助手,在串口调试助手里面拷贝图片信息出来放到图片查看器可以看到拍摄的图片,其实这样很麻烦。
    在这篇博文,我将会带大家用最便宜的0.96寸1306OLED黑白显示屏显示ESP32CAM单片机拍摄的图片,就像真正的相机一样拍完照片之后可以直接在相机的屏幕上查看拍摄照片的效果。显示屏是直接通过I2C总线连接在ESP32CAM单片机上的。
优酷视频演示地址:https://v.youku.com/v_show/id_XNTE1Mzk2MTUwOA==.html

最简单DIY基于ESP32CAM的物联网相机系统⑤(用1306OLED实现WIFI黑白屏照相机)

显示屏外形:

该设计的全家福如下所示:

另外我还加入了STA模式,相机可以连接路由器,在局域网的所有用户都可以根据OLED显示的局域网IP=192.168.252.10,访问相机的嵌入式主页,如下图所示,控制拍照的依旧是网页:


一、最简单DIY基于ESP32CAM的物联网相机系统⑤(用1306OLED实现WIFI黑白屏照相机)是什么?

    用0.96寸的OLED显示屏来显示ESP32CAM相机拍摄的黑白照片。


二、实现需求

1.先打开官方1306显示屏的代码

    在Arduino IDE有很多例子供开发者学习的。
(1)第一个相关的例子就是串口通信的例子了:
源码路径:

/**************************************************************************This is an example for our Monochrome OLEDs based on SSD1306 driversPick one up today in the adafruit shop!------> http://www.adafruit.com/category/63_98This example is for a 128x64 pixel display using I2C to communicate3 pins are required to interface (two I2C and one reset).Adafruit invests time and resources providing this opensource code, please support Adafruit and open-sourcehardware by purchasing products from Adafruit!Written by Limor Fried/Ladyada for Adafruit Industries,with contributions from the open source community.BSD license, check license.txt for more informationAll text above, and the splash screen below must beincluded in any redistribution.**************************************************************************/#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 64// OLED display height, in pixels// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     4// Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);#define NUMFLAKES     10// Number of snowflakes in the animation example#define LOGO_HEIGHT   16
#define LOGO_WIDTH    16
static const unsigned char PROGMEM logo_bmp[] =
{ B00000000, B11000000,B00000001, B11000000,B00000001, B11000000,B00000011, B11100000,B11110011, B11100000,B11111110, B11111000,B01111110, B11111111,B00110011, B10011111,B00011111, B11111100,B00001101, B01110000,B00011011, B10100000,B00111111, B11100000,B00111111, B11110000,B01111100, B11110000,B01110000, B01110000,B00000000, B00110000 };void setup() {Serial.begin(9600);// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internallyif(!display.begin(SSD1306_SWITCHCAPVCC, 0x3D)) { // Address 0x3D for 128x64Serial.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(' ');else          display.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);}}}
}

值得注意的是需要安装一个“水果库”,#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h> 否则有可能出错。


2.再打开官方ESP32CAM照相机程序

源码路径是(只要读者正确安装了ESP32开发环境,这个源码可以在Arduino里面按照下面截图的方式打开的):

#include "esp_camera.h"
#include <WiFi.h>//
// WARNING!!! PSRAM IC required for UXGA resolution and high JPEG quality
//            Ensure ESP32 Wrover Module or other board with PSRAM is selected
//            Partial images will be transmitted if image exceeds buffer size
//// Select camera model
#define CAMERA_MODEL_WROVER_KIT // Has PSRAM
//#define CAMERA_MODEL_ESP_EYE // Has PSRAM
//#define CAMERA_MODEL_M5STACK_PSRAM // Has PSRAM
//#define CAMERA_MODEL_M5STACK_WIDE // Has PSRAM
//#define CAMERA_MODEL_AI_THINKER // Has PSRAM
//#define CAMERA_MODEL_TTGO_T_JOURNAL // No PSRAM#include "camera_pins.h"const char* ssid = "*********";
const char* password = "*********";void startCameraServer();void setup() {Serial.begin(115200);Serial.setDebugOutput(true);Serial.println();camera_config_t config;config.ledc_channel = LEDC_CHANNEL_0;config.ledc_timer = LEDC_TIMER_0;config.pin_d0 = Y2_GPIO_NUM;config.pin_d1 = Y3_GPIO_NUM;config.pin_d2 = Y4_GPIO_NUM;config.pin_d3 = Y5_GPIO_NUM;config.pin_d4 = Y6_GPIO_NUM;config.pin_d5 = Y7_GPIO_NUM;config.pin_d6 = Y8_GPIO_NUM;config.pin_d7 = Y9_GPIO_NUM;config.pin_xclk = XCLK_GPIO_NUM;config.pin_pclk = PCLK_GPIO_NUM;config.pin_vsync = VSYNC_GPIO_NUM;config.pin_href = HREF_GPIO_NUM;config.pin_sscb_sda = SIOD_GPIO_NUM;config.pin_sscb_scl = SIOC_GPIO_NUM;config.pin_pwdn = PWDN_GPIO_NUM;config.pin_reset = RESET_GPIO_NUM;config.xclk_freq_hz = 20000000;config.pixel_format = PIXFORMAT_JPEG;// if PSRAM IC present, init with UXGA resolution and higher JPEG quality//                      for larger pre-allocated frame buffer.if(psramFound()){config.frame_size = FRAMESIZE_UXGA;config.jpeg_quality = 10;config.fb_count = 2;} else {config.frame_size = FRAMESIZE_SVGA;config.jpeg_quality = 12;config.fb_count = 1;}#if defined(CAMERA_MODEL_ESP_EYE)pinMode(13, INPUT_PULLUP);pinMode(14, INPUT_PULLUP);
#endif// camera initesp_err_t err = esp_camera_init(&config);if (err != ESP_OK) {Serial.printf("Camera init failed with error 0x%x", err);return;}sensor_t * s = esp_camera_sensor_get();// initial sensors are flipped vertically and colors are a bit saturatedif (s->id.PID == OV3660_PID) {s->set_vflip(s, 1); // flip it backs->set_brightness(s, 1); // up the brightness just a bits->set_saturation(s, -2); // lower the saturation}// drop down frame size for higher initial frame rates->set_framesize(s, FRAMESIZE_QVGA);#if defined(CAMERA_MODEL_M5STACK_WIDE)s->set_vflip(s, 1);s->set_hmirror(s, 1);
#endifWiFi.begin(ssid, password);while (WiFi.status() != WL_CONNECTED) {delay(500);Serial.print(".");}Serial.println("");Serial.println("WiFi connected");startCameraServer();Serial.print("Camera Ready! Use 'http://");Serial.print(WiFi.localIP());Serial.println("' to connect");
}void loop() {// put your main code here, to run repeatedly:delay(10000);
}

3.修改上面1和2的官方源码变成自己的源码

(1)编写图像处理代码(注释相当详细):

  void my_camera_show(void)
{camera_fb_t *fb = esp_camera_fb_get();int i = 0;for(y = 0; y < SCREEN_HEIGHT; y++){for(x = 0;x < SCREEN_WIDTH; x++){oldpixel = fb->buf[i]; //保持原始的灰度值newpixel = (255*(oldpixel >> 7)); //门槛值128,只留下最后高位的第八位二进制数据,右移7位变成0或者1,再与255相乘,结果就是0或者255fb->buf[i] = newpixel; //缓冲区现在是单通道,0或255// floyd-steignburg抖动算法quanterror = oldpixel - newpixel; //像素之间的误差//将此错误分发给相邻像素://右if(x < SCREEN_WIDTH-1)//边界检查...{fb->buf[(x + 1)+(y * SCREEN_WIDTH)] += ((quanterror * 7)>> 4);}// 左下if((x > 1) && (y < SCREEN_HEIGHT-1))//边界检查...{fb->buf [(x-1)+((y + 1)* SCREEN_WIDTH)] == ((quanterror * 3)>> 4); }//下if(y < 63)//边界检查...{fb->buf [(x)+((y + 1)* SCREEN_WIDTH)] == ((quanterror * 5)>> 4);}// 右下if((x < SCREEN_WIDTH-1) && (y < SCREEN_HEIGHT-1))//边界检查...{fb->buf [(x + 1) + ((y + 1)* SCREEN_WIDTH)] == (quanterror >> 4);}// 画这个像素switch(fb->buf[i]%2){case 0://display.setColor(BLACK);display.drawPixel(x,y,BLACK);break;case 1://display.setColor(WHITE);// draw a single pixeldisplay.drawPixel(x,y,WHITE);break;case 2://display.setColor(INVERSE);// draw a single pixeldisplay.drawPixel(x,y,INVERSE);break;}//display.setPixel(x, y);i++;}}display.display();esp_camera_fb_return(fb);
}

(2)结合图像采集和图像处理完成最终的OLED显示的设计
需要完整代码的读者请到最后面的链接下载工程源码。这里省去几千字的代码······


三、运行与调试

(1)按照下图接线:

(1.1)配置好路由器的账号和密码烧录代码开机,查看OLED显示屏的IP地址:

(2)打开浏览器输入IP地址进入智能照相机主页(相机和浏览器都要在一个局域网):

(3)点击send之后就会触发ESP32CAM在拍照,OLED 马上显示拍摄的黑白照片。

调试到此结束,根据结果可以满足博文提出的要求。


总结

总结:读者可以在这里学会了AJAX技术,可以在不刷新网页的前提下不断点击send按钮拍照,并且网页会显示一个变化的随机数nocathe=1620924773578 提示用户拍照成功,这个反馈不知道有多少个技术开发者想写都难写出来的,因为这个技术要熟悉JS技术才能使用得淋漓尽致了,还有就是可以学到嵌入式网页设计及嵌入式服务器的设计及嵌入式服务器如何接受get请求和用异步响应get请求,服务器根据请求验证提交表单的账号和密码,并没有使用到数据库又可以实现账号密码认证,多少硬件工程师都想不出来是这样写的,根据密码认证的结果解析指令控制照相机硬件摄像头拍照,总的来说不简单的,网上开源的老外代码没有我的那么详细和简洁的,相信daodanjishui品牌的力量,后期我可能会去编写一本关于ESP32CAM的技术书籍,玩技术又有冒险的尝试和收割成果的喜悦,大概就是所谓的技术真爱吧!这是我以前的项目中没有出现过的知识点(后期还会开源在ESP32CAM本机加载JS库的开源代码实现图像处理,敬请期待······)包括串口图传,WIFI图传,C#图传,JAVA图传,安卓图传,JSP图传,MFC图传······精彩值得期待。

代码工程下载链接:https://www.cirmall.com/circuit/24336/
点我直接跳转

最简单DIY基于ESP32CAM的物联网相机系统⑤(用1306OLED实现WIFI黑白屏照相机)相关推荐

  1. 最简单DIY基于ESP32CAM的物联网相机系统⑥(用上位机VS2013 MFC实现WIFI图传)

    第一篇:最简单DIY基于ESP32CAM的物联网相机系统①(用网页实现拍照图传) 第二篇:最简单DIY基于ESP32CAM的物联网相机系统②(在JAVAWEB服务器实现图片查看器) 第三篇:最简单DI ...

  2. 最简单DIY基于ESP32CAM的物联网相机系统⑤(用C#上位机实现串口图传)

    第一篇:最简单DIY基于ESP32CAM的物联网相机系统①(用网页实现拍照图传) 第二篇:最简单DIY基于ESP32CAM的物联网相机系统②(在JAVAWEB服务器实现图片查看器) 第三篇:最简单DI ...

  3. 最简单DIY基于ESP32CAM的物联网相机系统③(在JSP服务器图传相片给所有客户端欣赏)

    最简单DIY基于ESP32CAM的物联网相机系统系列文章目录 第一篇:最简单DIY基于ESP32CAM的物联网相机系统①(用网页实现拍照图传) 第二篇:最简单DIY基于ESP32CAM的物联网相机系统 ...

  4. 最简单DIY基于ESP32CAM的物联网相机系统②(在JAVAWEB服务器实现图片查看器)

    最简单DIY基于ESP32CAM的物联网相机系统系列文章目录 第一篇:最简单DIY基于ESP32CAM的物联网相机系统①(用网页实现拍照图传) 第二篇:最简单DIY基于ESP32CAM的物联网相机系统 ...

  5. 最简单DIY基于ESP32CAM的物联网相机系统④(用调试串口助手实现串口图传)

    第一篇:最简单DIY基于ESP32CAM的物联网相机系统①(用网页实现拍照图传) 第二篇:最简单DIY基于ESP32CAM的物联网相机系统②(在JAVAWEB服务器实现图片查看器) 第三篇:最简单DI ...

  6. 最简单DIY基于ESP8266的物联网智能小车①(webserver服务器网页简单遥控版)

    ESP8266和ESP32物联网智能小车开发系列文章目录 第一篇:最简单DIY基于ESP8266的物联网智能小车①(webserver服务器网页简单遥控版) 文章目录 ESP8266和ESP32物联网 ...

  7. 最简单DIY基于STM32的远程控制电脑系统②(无线遥杆+按键控制)

    STM32库函数开发系列文章目录 第一篇:STM32F103ZET6单片机双串口互发程序设计与实现 第二篇:最简单DIY基于STM32单片机的蓝牙智能小车设计方案 第三篇:最简单DIY基于STM32F ...

  8. 最简单DIY基于STM32的远程控制电脑系统①(电容触摸+按键控制)

    STM32库函数开发系列文章目录 第一篇:STM32F103ZET6单片机双串口互发程序设计与实现 第二篇:最简单DIY基于STM32单片机的蓝牙智能小车设计方案 第三篇:最简单DIY基于STM32F ...

  9. 最简单DIY基于ESP8266的智能彩灯⑥(Android开发通过WIFI控制彩灯实现表白神器)

    ESP8266和ESP32智能彩灯开发系列文章目录 第一篇:最简单DIY基于ESP8266的智能彩灯①(在网页用按钮点亮普通RGB灯) 第二篇:最简单DIY基于ESP8266的智能彩灯②(在网页用按键 ...

最新文章

  1. 随机数计算html,随机数的产生方法 电脑随机数产生的计算具体方法
  2. pagerank算法实现matlab,Matlab 入门及PageRank算法求解.ppt
  3. 使用Faster-Rcnn进行目标检测(实践篇)
  4. xxx in ‘Anonymous class derived from xxx clashes with ‘call(T)‘ in xxx attempting to use incompati
  5. 以太坊POA共识机制Clique源码分析
  6. Codeforces 71A Way Too Long Words
  7. AIX 系统默认ftp
  8. 谷歌翻译插件安装使用
  9. 如何解决同花顺软件打开时,出现 “无法与服务器建立连接” 的问题
  10. spring 通过yml格式配置log日志
  11. 智慧路灯控制系统解决方案
  12. python评分卡3_woe与IV分箱实现
  13. 分数阶微分_分数微分特征
  14. 实现Android手机之间在局域网下传输任意文件
  15. arduino继电器控制风扇_Arduino基础入门篇24—继电器控制
  16. 阿里云视频点播(VOD)控制台上传慢解决方案
  17. 各种颜色对应的RGB值
  18. 【渝粤题库】广东开放大学 互联网金融 形成性考核
  19. 能复活超级英雄的除了时间宝石,还有量子计算机?
  20. UI灵感分析笔记#3(22.1.2)

热门文章

  1. MXCornerRadius 只需1行代码让你的UIImageView 有任意的cornerRadius圆角!
  2. 【python爬虫专项(9)】哪吒之魔童降世影片的海报爬取
  3. 微信小程序是什么?微信小程序该怎么开发?
  4. Python3网络爬虫--爬取歌词并制作GUI(附源码)
  5. 三星i9300手机电池拆解分析
  6. 其他国产品牌会用鸿蒙系统吗,鸿蒙系统发布,为什么没有看到其他国产品牌表态支持?...
  7. java 创建数组的三种方式及区别
  8. TSP问题(换位表达,启发式交叉,启发式变异)C++实现
  9. Spine之六——纹理打包Texture packing
  10. 每日一学-- 主动学习(active learning)