├── .gitignore ├── .nojekyll ├── 30-iot.md ├── LICENSE ├── Makefile ├── README.md ├── build ├── author.html ├── head.html ├── metadata.xml ├── share.html ├── stats.html └── title.txt ├── chapters ├── 00-prelude.md ├── 01-basic.md ├── 1-0-arduino-basic.md ├── 1-1-arduino-hello-world.md ├── 1-2-arduino-sensor.md ├── 1-3-arduino-5110.md ├── 1-4-arduino-processing.md ├── 1-5-arduino-ble.md ├── 1-6-arduino-shield.md ├── 1-7-arduino-network.md ├── 2-0-esp8266-baisc.md ├── 2-1-esp8266-basic.md ├── 2-2-esp8266-arduino-ide.md ├── 2-3-esp8266-arduino-uno.md ├── 2-4-esp8266-sensor-to-server.md ├── 2-5-esp8266-mqtt.md ├── 2-6-esp8266-http-server.md ├── 2-7-beyond-esp8266.md ├── 3-0-rpi.md ├── 3-1-rpi-hello-world.md ├── 3-2-rpi-node-server-basic.md ├── 3-3-rpi-camera-server.md ├── 3-4-esp8266-server-connect.md ├── 3-5-arduino-ota.md ├── 3-6-rpi-mqtt-server.md ├── 3-7-rpi-iot-docker.md ├── 4-0-basic.md ├── 4-1-ha-setup.md ├── 4-2-ha-esp8266.md ├── 4-3-homekit-setup.md ├── 4-4-homekit-esp8266.md ├── 4-5-rpi-avs.md ├── 4-6-rpi-respeaker.md └── 4-7-rpi-ai-blockchain.md ├── css └── vendor.css ├── epub.css ├── images └── arduino │ ├── add-zip-library.png │ ├── arduino-5110-connection.png │ ├── arduino-button.jpg │ ├── arduino-ethernet.png │ ├── arduino-hs-sr04.png │ ├── arduino-install-package.png │ ├── arduino-nokia-5110.png │ ├── arduinos.jpg │ ├── download-zip.png │ ├── fritzing-button.png │ ├── fritzing-example.jpg │ ├── manage-library.png │ ├── serial-output-example.png │ └── shields.jpg ├── img ├── cover.jpg └── favicon.ico ├── index.html ├── init.sh ├── listings-setup.tex ├── style.css └── template └── template.tex /.gitignore: -------------------------------------------------------------------------------- 1 | *.rtf 2 | *.mobi 3 | *.pdf 4 | *.epub 5 | -------------------------------------------------------------------------------- /.nojekyll: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /30-iot.md: -------------------------------------------------------------------------------- 1 | 2 | 前言 3 | === 4 | 5 | 最近这些日子里,当我和女朋友搬到了新的合租屋里,有了更大的空间——可以再次折腾硬件。便开始去玩一些硬件家居相关的内容,也顺便接上了 Amazon Echo、小米传感器、Broadlink、ESP8266 等等的硬件。 6 | 7 | 再加上正在编写的『玩点什么』网站,便也想整理一下硬件相关的资料。想着想着,便有了这一系列的电子书。 8 | 9 | 这是一本写给软件工程师看的硬件指南。 10 | 11 | 关于作者 12 | --- 13 | 14 | 黄峰达(Phodal Huang)是一个创客、工程师、咨询师和作家。他毕业于西安文理学院电子信息工程专业,现作为一个咨询师就职于 ThoughtWorks 深圳。长期活跃于开源软件社区 GitHub,目前专注于物联网和前端领域。 15 | 16 | 作为一个开源软件作者,著有 Growth、Stepping、Lan、Echoesworks 等软件。其中开源学习应用 Growth,广受读者和用户好评,可在 APP Store 及各大 Android 应用商店下载。 17 | 18 | 作为一个技术作者,著有《自己动手设计物联网》(电子工业出版社)、《全栈应用开发:精益实践》(电子工业出版社,正在出版)。并在 GitHub 上开源有《Growth: 全栈增长工程师指南》、《GitHub 漫游指南》等七本电子书。 19 | 20 | 作为技术专家,他为英国 Packt 出版社审阅有物联网书籍《Learning IoT》、《Smart IoT》,前端书籍《Angular 2 Serices》、《Getting started with Angular》等技术书籍。 21 | 22 | 他热爱编程、写作、设计、旅行、hacking,你可以从他的个人网站:[https://www.phodal.com/](https://www.phodal.com/) 了解到更多的内容。 23 | 24 | 其它相关信息: 25 | 26 | - 微博:[http://weibo.com/phodal](http://weibo.com/phodal) 27 | - GitHub: [https://github.com/phodal](https://github.com/phodal) 28 | - 知乎:[https://www.zhihu.com/people/phodal](https://www.zhihu.com/people/phodal) 29 | - SegmentFault:[https://segmentfault.com/u/phodal](https://segmentfault.com/u/phodal) 30 | 31 | 当前为预览版,在使用的过程中遇到任何问题请及时与我联系。阅读过程中的问题,不妨在GitHub上提出来: [Issues](https://github.com/phodal/fe/issues) 32 | 33 | 阅读过程中遇到语法错误、拼写错误、技术错误等等,不妨来个Pull Request,这样可以帮助到其他阅读这本电子书的童鞋。 34 | 35 | 其他电子书: 36 | 37 | * 《[Phodal's Idea实战指南](https://github.com/phodal/ideabook)》 38 | * 《[一步步搭建物联网系统](https://github.com/phodal/designiot)》 39 | * 《[GitHub 漫游指南](https://github.com/phodal/github-roam)》 40 | * 《[RePractise](https://github.com/phodal/repractise)》 41 | * 《[Growth: 全栈增长工程师指南](https://github.com/phodal/growth-ebook)》 42 | * 《[Growth: 全栈增长工程师实战](https://github.com/phodal/growth-in-action)》 43 | * 《[我的职业是前端工程师](http://github.com/phodal/fe)》 44 | 45 | 基础篇 46 | === 47 | 48 | Arduino vs ESP8266 vs Raspberry Pi 49 | --- 50 | 51 | 在这一系列的文章里,我们使用三个主要的开发板: 52 | 53 | - Arduino。 54 | - ESP8266。 55 | - Raspberry Pi。 56 | 57 | 这三个硬件分别代表着,三种不同类型的硬件爱好者的开发板。 58 | 59 | ### Arduino 60 | 61 | > Arduino,是一个开放源代码的单片机微控制器,它使用了Atmel AVR单片机,采用了开放源代码的软硬件平台,建构于简易输出/输入(simple I/O)接口板,并且具有使用类似Java、C语言的Processing/Wiring开发环境。[^wiki_arduino] 62 | 63 | [^wiki_arduino]: 源自维基百科,https://zh.wikipedia.org/wiki/Arduino 64 | 65 | 其包含了以下的特性[^wiki_arduino]: 66 | 67 | - 基于知识共享开放源代码的电路图设计。 68 | - 免费下载,也可依需求自己修改,但需遵照姓名标示。您必须按照作者或授权人所指定的方式,表彰其姓名。 69 | - 依相同方式分享,若您改变或转变著作,当散布该派生著作时,您需采用与本著作相同或类似的授权条款。 70 | - Arduino 可使用 ICSP 在线烧入器,将 Bootloader 烧入新的 IC 芯片。 71 | - 可依据 Arduino 官方网站,获取硬件的设计档,加以调整电路板及组件,以匹配自己实际设计的需求 72 | - 可简单地与感测器,各式各样的电子组件连接,如红外线、超音波、热敏电阻、光敏电阻、伺服马达…等。 73 | - 支持多样的交互程序,如Adobe Flash, Max/MSP, VVVV, Pure Data, C, Processing…等。 74 | - 使用低价格的微处理控制器(Atmel AVR)(ATMEGA 8,168,328等)。 75 | - USB接口,不需外接电源。另外有提供直流(DC)电源输入。 76 | 77 | 当然,其最大的特色是,**完善的社区及生态系统**。几乎我们能想到的 Arduino 78 | 相关的创意,都可以在网上找到。如果没有的话,那么可能是你的创意不适合用于 79 | Arduino。 80 | 81 | Arduino 82 | 已然有一系列的相关硬件,就目前而言,最广泛的开发板,或者说是标准开发板是 83 | Arduino UNO。 84 | 85 | TODO: 相关 Arduino 硬件介绍 86 | 87 | Arduino 88 | 最吸引人的是开创性的引入电子积木的概念,即开发板的扩展板可以直接叠加在开发板上使用,而无需额外的硬件。 89 | 90 | TODO: 扩展板介绍。 91 | 92 | Arduion IDE 基于 Processing 开发环境而开发的。 93 | 94 | 概念 95 | --- 96 | 97 | 引脚 98 | 99 | 模电 100 | 101 | 数电 102 | 103 | 电流、电阻、电压 104 | 105 | etc.. 106 | 107 | PCB 108 | --- 109 | 110 | 为了更好的向读者展示硬件连线,我们将使用 Fritzing 汇制硬件电路图。 111 | 112 | ### Fritzing 113 | 114 | > Fritzing 是一个开源的硬件计划(initiative),它可以使电子元件变成任何人的创意素材。Fritzing 提供一个软件工具、一个社区网站,以及本着 Processing 和 Arduino 精神的服务,培养创造性的生态系统,允许用户记录他们的原型,与他人分享、用于课堂上的电子相关教学,以及布局和制造专业的pcb。 115 | 116 | Fritzing 的官网是:[http://fritzing.org/](http://fritzing.org/)。 117 | 118 | 因此,在开始之前我们需要在 Fritzing 的官网下载 119 | Fritzing。当前它可以支持主流的操作系统:Windows、Linux、macOS,当然如果你想自己从源码编译一个 120 | Fritzing,那也是可以的。除了中文和英语外,它还支持其它 17 种语言。 121 | 122 | 在其官网,除了能下载到该软件,还可以看到其它用户上传、汇制的 123 | Fritzing 电路图等,它们可以免费下载,并引用到你的项目中。 124 | 125 | LCEDA 126 | 127 | 128 | 硬件 129 | --- 130 | 131 | Nokia 5110 132 | 133 | 温度、红外、距离、土壤 134 | 135 | 136 | MCU vs CPU 137 | --- 138 | 139 | Input -> MCU -> Output 140 | 141 | 142 | 通讯 143 | --- 144 | 145 | 蓝牙、有线、 146 | 147 | 专用协议,blabla 148 | 149 | 第一部分 Arduino 篇 150 | === 151 | 152 | - Arduino + LED + 按钮控制 153 | - Arduino 读取传感器 154 | - Arduion + LCD 5100 显示传感器 155 | - Arduino 蓝牙通讯 156 | - Arduino Processing 可视化数据 157 | - Arduino 上传数据 158 | - Arduino 的可能性:+ Fritzing / Arduino + Johnny-Five 159 | 160 | Arduino Hello, World 161 | === 162 | 163 | 幸运的是,今天对硬件编程已经有了更多的方式,可以使用更高级的语言。除了在 Arduion 上使用 Processing 语言(可以视为 C)进行编程,还可以使用 JavaScript 等语言。我们会在后面的介绍:如何使用这些高级的语言来编程。 164 | 165 | 开发板选择 166 | --- 167 | 168 | ![Arduino 开发板示例](./images/arduino/arduinos.jpg) 169 | 170 | ![扩展板](./images/arduino/shields.jpg) 171 | 172 | Arduino IDE 默认会安装相应环境的包,如果你的开发板不在这些环境里,如你使用的是 Arduino M0 开发板。那么,系统可能会检测出来,并自动为你安装相应的环境,如下图所示。 173 | 174 | ![Arduino IDE 安装 Arduino M0 环境](./images/arduino/arduino-install-package.png) 175 | 176 | ``注意``:安装过程中会下载一系列的工具,在这个过程里,可能会遇到一些“网络问题”。请尝试更换网络,或直到到“墙”的那外一边。 177 | 178 | 对于非官方的开发板来说,则需要开发者自己手动通过『开发板管理器』来安装,如 ESP8266 开发板。 179 | 180 | hello, world 181 | --- 182 | 183 | 与桌面端使用 print、puts、console.log 来输出 Hello, world 不同的是,点亮一个 LED 是 电子世界的 Hello, world。 184 | 185 | 在硬件世界里,没有 stdlib.h 这样的库,对于多数嵌入式系统来说,也没有屏幕。在这个时候,想要知道设备的状态,最简单的方式就是 LED。常见的,如路由器、电视等家电电器上的灯,都是嵌入式设备的状态。 186 | 187 | 188 | 官方的 Blink 示例如下: 189 | 190 | ```c 191 | void setup() { 192 | // initialize digital pin LED_BUILTIN as an output. 193 | pinMode(LED_BUILTIN, OUTPUT); 194 | } 195 | 196 | // the loop function runs over and over again forever 197 | void loop() { 198 | digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) 199 | delay(1000); // wait for a second 200 | digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW 201 | delay(1000); // wait for a second 202 | } 203 | ``` 204 | 205 | 接下来,要做的事情就比较简单了。从工具( Tools )中选择对应的开发板和端口,如下图所示: 206 | 207 | ![Arduino 选择设备](./images/arduino/arduino-devices.png) 208 | 209 | 我这里使用设置是 ``Arduino M0 Pro``,对应的端口是:``/dev/tty.usbmodem1412``。 210 | 211 | 对于 Windows 用户来说,则是 COM 口——从设备管理器中找到对应的端口。 212 | 213 | 然后点击菜单中的 -> 号,即上传(Upload,又可以称为烧录)。 214 | 215 | ![Arduino 菜单](./images/arduino/arduino-menu.png) 216 | 217 | 再看看你的 Arduion,就发现上面的 LED 正在闪烁——一秒开一秒关。 218 | 219 | ### setup() 220 | 221 | 源码中的: 222 | 223 | ```c 224 | int main(void) 225 | { 226 | init(); 227 | setup(); 228 | for(;;) 229 | loop(); 230 | 231 | return 0; 232 | } 233 | ``` 234 | 235 | ### loop() 236 | 237 | 输入控制输出 238 | --- 239 | 240 | GOAL 241 | 242 | SMART 原则 243 | 244 | 仍然采用的是官方的示例: 245 | 246 | ![Arduino 控制 LED 示例](./images/arduino/fritzing-button.png) 247 | 248 | 官方示例([Digital Read Serial](https://www.arduino.cc/en/Tutorial/DigitalReadSerial))代码: 249 | 250 | ```c 251 | // digital pin 2 has a pushbutton attached to it. Give it a name: 252 | int pushButton = 2; 253 | 254 | // the setup routine runs once when you press reset: 255 | void setup() { 256 | // initialize serial communication at 9600 bits per second: 257 | Serial.begin(9600); 258 | // make the pushbutton's pin an input: 259 | pinMode(pushButton, INPUT); 260 | } 261 | 262 | // the loop routine runs over and over again forever: 263 | void loop() { 264 | // read the input pin: 265 | int buttonState = digitalRead(pushButton); 266 | // print out the state of the button: 267 | Serial.println(buttonState); 268 | delay(1); // delay in between reads for stability 269 | } 270 | ``` 271 | 272 | ### 电路图 273 | 274 | 使用 Fritzing 画板时 275 | 276 | ![](./images/arduino/fritzing-example.jpg) 277 | 278 | 我们所需要的连线图 279 | 280 | ![](./images/arduino/arduino-button.jpg) 281 | 282 | Arduino 传感器控制 283 | === 284 | 285 | 传感器入门 286 | --- 287 | 288 | > 传感器是一种物理装置或生物器官,能够探测、感受外界的信号、物理条件(如光、热、湿度)或化学组成(如烟雾),并将探知的信息传递给其他装置或器官。“传感器”在新韦式大词典中定义为:“从一个系统接受功率,通常以另一种形式将功率送到第二个系统中的器件”。根据这个定义,传感器的作用是将一种能量转换成另一种能量形式,所以不少学者也用“换能器-Transducer”来称谓“传感器-Sensor”。[^wiki_sensor] 289 | 290 | [^wiki_sensor] : https://zh.wikipedia.org/wiki/%E4%BC%A0%E6%84%9F%E5%99%A8 291 | 292 | ### 模拟元件与数字元件 293 | 294 | 超声波传感器 295 | --- 296 | 297 | 298 | ![Arduion 超声波传感器连线图](./images/arduino/arduino-hs-sr04.png) 299 | 300 | 使用到库:[NewPing],GitHub 地址:[https://github.com/PaulStoffregen/NewPing](https://github.com/PaulStoffregen/NewPing) 301 | 302 | ![下载 Zip 格式](./images/arduino/download-zip.png) 303 | 304 | 添加库 305 | 306 | ![添加 NewPing 库](./images/arduinoadd-zip-library.png) 307 | 308 | 309 | ```c 310 | #include 311 | 312 | #define TRIGGER_PIN 12 // Arduino pin tied to trigger pin on the ultrasonic sensor. 313 | #define ECHO_PIN 11 // Arduino pin tied to echo pin on the ultrasonic sensor. 314 | #define MAX_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm. 315 | 316 | NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance. 317 | 318 | void setup() { 319 | Serial.begin(115200); // Open serial monitor at 115200 baud to see ping results. 320 | } 321 | 322 | void loop() { 323 | delay(50); // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings. 324 | Serial.print("Ping: "); 325 | Serial.print(sonar.ping_cm()); // Send ping, get distance in cm and print result (0 = outside set distance range) 326 | Serial.println("cm"); 327 | } 328 | ``` 329 | 330 | ![输出示例](serial-output-example.png) 331 | 332 | 控制继电器 333 | --- 334 | 335 | Arduino 5110 显示 336 | === 337 | 338 | LCD 339 | --- 340 | 341 | 安装库 342 | --- 343 | 344 | 项目地址:[Adafruit PCD8544](https://github.com/adafruit/Adafruit-PCD8544-Nokia-5110-LCD-library) 345 | 346 | ![添加库](./images/arduino/manage-library.png) 347 | 348 | ![Arduino IDE 安装 5110](./images/arduino/arduino-nokia-5110.png) 349 | 350 | 一般来说,在安装完库后,都会有对应的示例。但是,这里的示例,稍微有一些复杂。 351 | 352 | ```c 353 | #include 354 | 355 | #include 356 | #include 357 | #include 358 | 359 | // Pins 360 | const int8_t RST_PIN = D2; 361 | const int8_t CE_PIN = D1; 362 | const int8_t DC_PIN = D6; 363 | //const int8_t DIN_PIN = D7; // Uncomment for Software SPI 364 | //const int8_t CLK_PIN = D5; // Uncomment for Software SPI 365 | const int8_t BL_PIN = D0; 366 | 367 | 368 | // Software SPI with explicit CE pin. 369 | // Adafruit_PCD8544 display = Adafruit_PCD8544(CLK_PIN, DIN_PIN, DC_PIN, CE_PIN, RST_PIN); 370 | 371 | // Software SPI with CE tied to ground. Saves a pin but other pins can't be shared with other hardware. 372 | // Adafruit_PCD8544(int8_t CLK_PIN, int8_t DIN_PIN, int8_t DC_PIN, int8_t RST_PIN); 373 | 374 | // Hardware SPI based on hardware controlled SCK (SCLK) and MOSI (DIN) pins. CE is still controlled by any IO pin. 375 | // NOTE: MISO and SS will be set as an input and output respectively, so be careful sharing those pins! 376 | Adafruit_PCD8544 display = Adafruit_PCD8544(DC_PIN, CE_PIN, RST_PIN); 377 | 378 | void setup() { 379 | Serial.begin(9600); 380 | Serial.println("\n\nWeMos D1 Mini + Nokia 5110 PCD8544 84x48 Monochrome LCD\nUsing Adafruit_PCD8544 and Adafruit_GFX libraries\n"); 381 | 382 | // Turn LCD backlight on 383 | pinMode(BL_PIN, OUTPUT); 384 | digitalWrite(BL_PIN, HIGH); 385 | 386 | display.begin(); 387 | display.setContrast(60); // Adjust for your display 388 | Serial.println("Show Adafruit logo bitmap"); 389 | 390 | // Show the Adafruit logo, which is preloaded into the buffer by their library 391 | // display.clearDisplay(); 392 | delay(2000); 393 | 394 | display.clearDisplay(); 395 | display.setTextSize(1); 396 | display.setTextColor(BLACK); 397 | display.setCursor(0,0); 398 | display.println("Hello, world!"); 399 | display.display(); 400 | } 401 | 402 | void loop() { 403 | } 404 | ``` 405 | 406 | 输出 hello, world 407 | --- 408 | 409 | Arduino Processing 410 | === 411 | 412 | 串口输出 413 | --- 414 | 415 | 416 | 复杂的例子:MPU 6050 417 | --- 418 | 419 | 420 | 421 | 422 | Arduino BLE 控制 423 | === 424 | 425 | 蓝牙串口 426 | --- 427 | 428 | Bluno 等自带蓝牙的开发板 429 | 430 | 431 | 与手机通讯 432 | --- 433 | 434 | React Native / Cordova ??? 435 | 436 | 437 | Arduino 扩展板使用 438 | === 439 | 440 | I don't know 441 | 442 | 继电器 443 | --- 444 | 445 | 网络 446 | --- 447 | 448 | ![Arduion 以太网扩展板](./images/arduino/arduino-ethernet.png) 449 | 450 | 451 | Arduino ESP8266 WiFi 452 | === 453 | 454 | ESP8266 设置 455 | --- 456 | 457 | 458 | Arduino WiFi 459 | --- 460 | 461 | 第二部分 ESP8266 462 | === 463 | 464 | - ESP8266 基础 + 连接 WiFi / NodeMCU 465 | - ESP8266 Arduino IDE ?? 466 | - ESP8266 上传 Arduino 数据 467 | - ? ESP8266 读取传感器(ADC?)),并上传数据 468 | - ESP8266 MQTT 示例 469 | - ESP8266 作为 HTTP 服务器示例 470 | - 更多的可能性探索:Mongoose OS、Johnny-Five、智能家居 471 | 472 | ESP8266 基础 473 | === 474 | 475 | 设置与 AT 指令 476 | --- 477 | 478 | 479 | NodeMCU 480 | --- 481 | 482 | 483 | ESP8266 Arduino IDE 展示 484 | === 485 | 486 | ESP8266 连接 Arduino UNO 487 | === 488 | 489 | 490 | ESP8266 上传传感器数据 491 | === 492 | 493 | ### ADC 数据 494 | 495 | ### 发送请求 496 | 497 | ```c 498 | /** 499 | * BasicHTTPClient.ino 500 | * 501 | * Created on: 24.05.2015 502 | * 503 | */ 504 | 505 | #include 506 | 507 | #include 508 | #include 509 | 510 | #include 511 | 512 | #define USE_SERIAL Serial 513 | 514 | ESP8266WiFiMulti WiFiMulti; 515 | 516 | void setup() { 517 | 518 | USE_SERIAL.begin(115200); 519 | // USE_SERIAL.setDebugOutput(true); 520 | 521 | USE_SERIAL.println(); 522 | USE_SERIAL.println(); 523 | USE_SERIAL.println(); 524 | 525 | for(uint8_t t = 4; t > 0; t--) { 526 | USE_SERIAL.printf("[SETUP] WAIT %d...\n", t); 527 | USE_SERIAL.flush(); 528 | delay(1000); 529 | } 530 | 531 | WiFiMulti.addAP("SSID", "PASSWORD"); 532 | 533 | } 534 | 535 | void loop() { 536 | // wait for WiFi connection 537 | if((WiFiMulti.run() == WL_CONNECTED)) { 538 | 539 | HTTPClient http; 540 | 541 | USE_SERIAL.print("[HTTP] begin...\n"); 542 | // configure traged server and url 543 | //http.begin("https://192.168.1.12/test.html", "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS 544 | http.begin("http://192.168.1.12/test.html"); //HTTP 545 | 546 | USE_SERIAL.print("[HTTP] GET...\n"); 547 | // start connection and send HTTP header 548 | int httpCode = http.GET(); 549 | 550 | // httpCode will be negative on error 551 | if(httpCode > 0) { 552 | // HTTP header has been send and Server response header has been handled 553 | USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); 554 | 555 | // file found at server 556 | if(httpCode == HTTP_CODE_OK) { 557 | String payload = http.getString(); 558 | USE_SERIAL.println(payload); 559 | } 560 | } else { 561 | USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); 562 | } 563 | 564 | http.end(); 565 | } 566 | 567 | delay(10000); 568 | } 569 | ``` 570 | 571 | ### 上传 572 | 573 | ESP8266 MQTT 控制 574 | === 575 | 576 | ### MQTT 577 | 578 | ### moquitto 579 | 580 | ### 581 | 582 | [https://github.com/tuanpmt/esp_mqtt](https://github.com/tuanpmt/esp_mqtt) 583 | 584 | 585 | ESP8266 HTTP 服务器 586 | === 587 | 588 | 589 | ### Simple HTTP Server 590 | 591 | 592 | ```c 593 | #include 594 | #include 595 | 596 | ESP8266WebServer server(80); 597 | 598 | void setup() { 599 | 600 | Serial.begin(115200); 601 | WiFi.begin("Network name", "Password"); //Connect to the WiFi network 602 | 603 | while (WiFi.status() != WL_CONNECTED) { //Wait for connection 604 | delay(500); 605 | Serial.println("Waiting to connect…"); 606 | } 607 | 608 | Serial.print("IP address: "); 609 | Serial.println(WiFi.localIP()); //Print the local IP 610 | 611 | server.on(" / other", []() { //Define the handling function for the path 612 | 613 | server.send(200, "text / plain", "Other URL"); 614 | 615 | }); 616 | 617 | server.on(" / ", handleRootPath); //Associate the handler function to the path 618 | server.begin(); //Start the server 619 | Serial.println("Server listening"); 620 | } 621 | 622 | void loop() { 623 | server.handleClient(); 624 | } 625 | 626 | void handleRootPath() { 627 | server.send(200, "text/plain", "Hello world"); 628 | } 629 | ``` 630 | 631 | 632 | 超越 ESP8266 633 | === 634 | 635 | 第三部分 Raspberry Pi 636 | === 637 | 638 | - Hello, world! 639 | - Node.js 服务 - 开机启动,blabla 640 | - 连接 Arduino、自动编程? OTA ? 641 | - Raspberry Pi as PC Camera + Sensors ??? 642 | - Raspberry Pi <-> ESP8266 / Node.js Example 643 | - MQTT Server 644 | - IoT Server + Database? 645 | 646 | Raspberry Pi Hello, world! 647 | === 648 | 649 | ### 镜像烧录 650 | 651 | ### SSH 连接 652 | 653 | ### Python 控制 GPIO 654 | 655 | Node.js 服务基础 656 | === 657 | 658 | ### 安装 Node.js 659 | 660 | ### Express Hello, world 661 | 662 | ### 开机启动 663 | 664 | 摄像头 + 传感器 = 智能家居? 665 | === 666 | 667 | 668 | ### 红外传感器 669 | 670 | ### 视频流 671 | 672 | ### 拍照,blabla 673 | 674 | 与 ESP8266 无线通讯 675 | === 676 | 677 | ### 建立服务 678 | 679 | ### 广播? 680 | 681 | Arduino OTA 更新 682 | === 683 | 684 | ### 服务端读取代码 685 | 686 | ### arduino-cli 更新硬件 687 | 688 | 689 | 使用 MQTT 服务器 690 | === 691 | 692 | ### MQTT 客户端 693 | 694 | ### 使用 mosquittto 695 | 696 | 跟上潮流:Docker、硬件微服务 697 | === 698 | 699 | ### 使用 Docker 700 | 701 | ### 硬件微服务 702 | 703 | 704 | 705 | Home Assistant 搭建 706 | === 707 | 708 | Home Assistant 整合 709 | === 710 | 711 | Homekit 搭建 712 | === 713 | 714 | Homekit 整合 715 | === 716 | 717 | Raspberry Pi 与智能音箱 718 | === 719 | 720 | Raspberry Pi 自制智能音箱 721 | === 722 | 723 | 超越 Raspberry Pi 724 | === 725 | 726 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | License 2 | 3 | THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. 4 | 5 | BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. 6 | 7 | 1. Definitions 8 | 9 | "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with one or more other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License. 10 | "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License. 11 | "Licensor" means the individual, individuals, entity or entities that offers the Work under the terms of this License. 12 | "Original Author" means the individual, individuals, entity or entities who created the Work. 13 | "Work" means the copyrightable work of authorship offered under the terms of this License. 14 | "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. 15 | 2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws. 16 | 17 | 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: 18 | 19 | to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works; and, 20 | to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works. 21 | The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Derivative Works. All rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Sections 4(d) and 4(e). 22 | 23 | 4. Restrictions.The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: 24 | 25 | You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of a recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. When You distribute, publicly display, publicly perform, or publicly digitally perform the Work, You may not impose any technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by Section 4(c), as requested. 26 | You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works. 27 | If You distribute, publicly display, publicly perform, or publicly digitally perform the Work (as defined in Section 1 above) or Collective Works (as defined in Section 1 above), You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Collective Work, at a minimum such credit will appear, if a credit for all contributing authors of the Collective Work appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this clause for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. 28 | For the avoidance of doubt, where the Work is a musical composition: 29 | 30 | Performance Royalties Under Blanket Licenses. Licensor reserves the exclusive right to collect whether individually or, in the event that Licensor is a member of a performance rights society (e.g. ASCAP, BMI, SESAC), via that society, royalties for the public performance or public digital performance (e.g. webcast) of the Work if that performance is primarily intended for or directed toward commercial advantage or private monetary compensation. 31 | Mechanical Rights and Statutory Royalties. Licensor reserves the exclusive right to collect, whether individually or via a music rights agency or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions), if Your distribution of such cover version is primarily intended for or directed toward commercial advantage or private monetary compensation. 32 | Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor reserves the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions), if Your public digital performance is primarily intended for or directed toward commercial advantage or private monetary compensation. 33 | 5. Representations, Warranties and Disclaimer 34 | 35 | UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND ONLY TO THE EXTENT OF ANY RIGHTS HELD IN THE LICENSED WORK BY THE LICENSOR. THE LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MARKETABILITY, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. 36 | 37 | 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 38 | 39 | 7. Termination 40 | 41 | This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collective Works (as defined in Section 1 above) from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. 42 | Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. 43 | 8. Miscellaneous 44 | 45 | Each time You distribute or publicly digitally perform the Work (as defined in Section 1 above) or a Collective Work (as defined in Section 1 above), the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. 46 | If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. 47 | No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. 48 | This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. 49 | Creative Commons Notice 50 | 51 | Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. 52 | 53 | Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License. 54 | 55 | Creative Commons may be contacted at https://creativecommons.org/. 56 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include_dir=build 2 | source=chapters/*.md 3 | title='硬件开发指南' 4 | filename='30-iot' 5 | 6 | 7 | all: html epub rtf pdf mobi 8 | 9 | markdown: 10 | awk 'FNR==1{print ""}{print}' $(source) > $(filename).md 11 | 12 | html: markdown 13 | pandoc -s $(filename).md -t html5 -o index.html -c style.css \ 14 | --include-in-header $(include_dir)/head.html \ 15 | --include-before-body $(include_dir)/author.html \ 16 | --include-before-body $(include_dir)/share.html \ 17 | --include-after-body $(include_dir)/stats.html \ 18 | --title-prefix $(title) \ 19 | --normalize \ 20 | --smart \ 21 | --toc 22 | 23 | epub: markdown 24 | pandoc -s $(filename).md --normalize --smart -t epub -o $(filename).epub \ 25 | --epub-metadata $(include_dir)/metadata.xml \ 26 | --epub-stylesheet epub.css \ 27 | --epub-cover-image img/cover.jpg \ 28 | --title-prefix $(title) \ 29 | --normalize \ 30 | --smart \ 31 | --toc 32 | 33 | rtf: markdown 34 | pandoc -s $(filename).md -o $(filename).rtf \ 35 | --title-prefix $(title) \ 36 | --normalize \ 37 | --smart 38 | 39 | pdf: markdown 40 | # OS X: http://www.tug.org/mactex/ 41 | # Then find its path: find /usr/ -name "pdflatex" 42 | # Then symlink it: ln -s /path/to/pdflatex /usr/local/bin 43 | pandoc -s $(filename).md -o $(filename).pdf \ 44 | --title-prefix $(title) \ 45 | --listings -H listings-setup.tex \ 46 | --template=template/template.tex \ 47 | --normalize \ 48 | --smart \ 49 | --toc \ 50 | --latex-engine=`which xelatex` 51 | 52 | mobi: epub 53 | # Symlink bin: ln -s /path/to/kindlegen /usr/local/bin 54 | kindlegen $(filename).epub 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 写给软件工程师的硬件编程指南 2 | 3 | TODO 4 | 5 | - [ ] Node.js 微服务架构 6 | - [ ] 基于 React 的 Dashboard 设计:GPS 与地图应用? 7 | - [ ] 基于 React Native 的 APP 8 | - [ ] 基于 JavaScript 的硬件 9 | 10 | Arduino 11 | --- 12 | 13 | - [ ] LED <-> Button 14 | - [ ] LCD 1602 <-> 温度 Sensor 15 | - [ ] NOKIA 5110 <-> 面包板 16 | - [ ] 电路设计 <-> Pritzing 17 | - [ ] Bluetooth 蓝牙 BLE 18 | - [ ] USB Host/键盘 19 | - [ ] Advanced: create library 20 | 21 | ESP8266 22 | --- 23 | 24 | - [ ] Lua Example 25 | - [ ] Arduino IDE 26 | - [ ] HTTP Example / Upload Sensor 27 | - [ ] HTTP Server 28 | - [ ] CoAP / MQTT 29 | - [ ] Arduion / ESP8266 30 | - [ ] ESP8266 Homekit Compile 31 | 32 | Raspberry Pi 33 | --- 34 | 35 | - [ ] RetroPie/Kodi 36 | - [ ] RPi GPIO 37 | - [ ] RPi <-> Camera / OpenWRT 38 | - [ ] RPi with Arduino 39 | - [ ] Node.js Server / ESP8266 <-> Express example 40 | - [ ] MQTT Server 41 | - [ ] IoT Server 42 | - [ ] AWS Server 43 | 44 | Advanced 45 | --- 46 | 47 | - [ ] Home Assistant <-> Boradlink Example 48 | - [ ] Home Assistant <-> ESP8266 Example 49 | - [ ] HomeKit <-> Miio Example 50 | - [ ] HomeKit <-> ESP8266 Emulator 51 | - [ ] RPi + ReSpeaker 52 | - [ ] RPi + AVS 53 | 54 | 55 | LICENSE 56 | --- 57 | 58 | [![Phodal's Book](http://brand.phodal.com/shields/book-small.svg)](https://www.phodal.com/) 59 | 60 | 61 | © 2017 [Phodal Huang](https://www.phodal.com). This code is distributed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 License. See `LICENSE` in this directory. 62 | -------------------------------------------------------------------------------- /build/author.html: -------------------------------------------------------------------------------- 1 |

写给软件工程师看的硬件编程指南

2 |

By Phodal (Follow Me: 微博知乎SegmentFault) 3 |

4 | 5 |

我的其他电子书:

6 | 15 | 16 |

微信公众号

17 |

18 |

19 | 当前为预览版,在使用的过程中遇到任何遇到请及时与我联系。阅读过程中问题,不烦在GitHub上提出来: 20 | Issues 21 |

22 |

23 | 阅读过程中遇到语法错误、拼写错误、技术错误等等,不烦来个Pull Request,这样可以帮助到其他阅读这本电子书的童鞋。 24 |

-------------------------------------------------------------------------------- /build/head.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /build/metadata.xml: -------------------------------------------------------------------------------- 1 | 30 物联网项目 2 | Phodal 3 | Creative Commons Attribution Non-Commercial Share Alike 3.0 4 | zh-CN -------------------------------------------------------------------------------- /build/share.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /build/stats.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/build/stats.html -------------------------------------------------------------------------------- /build/title.txt: -------------------------------------------------------------------------------- 1 | % 30 物联网项目 2 | % Phodal -------------------------------------------------------------------------------- /chapters/00-prelude.md: -------------------------------------------------------------------------------- 1 | 前言 2 | === 3 | 4 | 最近这些日子里,当我和女朋友搬到了新的合租屋里,有了更大的空间——可以再次折腾硬件。便开始去玩一些硬件家居相关的内容,也顺便接上了 Amazon Echo、小米传感器、Broadlink、ESP8266 等等的硬件。 5 | 6 | 再加上正在编写的『玩点什么』网站,便也想整理一下硬件相关的资料。想着想着,便有了这一系列的电子书。 7 | 8 | 这是一本写给软件工程师看的硬件指南。 9 | 10 | 关于作者 11 | --- 12 | 13 | 黄峰达(Phodal Huang)是一个创客、工程师、咨询师和作家。他毕业于西安文理学院电子信息工程专业,现作为一个咨询师就职于 ThoughtWorks 深圳。长期活跃于开源软件社区 GitHub,目前专注于物联网和前端领域。 14 | 15 | 作为一个开源软件作者,著有 Growth、Stepping、Lan、Echoesworks 等软件。其中开源学习应用 Growth,广受读者和用户好评,可在 APP Store 及各大 Android 应用商店下载。 16 | 17 | 作为一个技术作者,著有《自己动手设计物联网》(电子工业出版社)、《全栈应用开发:精益实践》(电子工业出版社,正在出版)。并在 GitHub 上开源有《Growth: 全栈增长工程师指南》、《GitHub 漫游指南》等七本电子书。 18 | 19 | 作为技术专家,他为英国 Packt 出版社审阅有物联网书籍《Learning IoT》、《Smart IoT》,前端书籍《Angular 2 Serices》、《Getting started with Angular》等技术书籍。 20 | 21 | 他热爱编程、写作、设计、旅行、hacking,你可以从他的个人网站:[https://www.phodal.com/](https://www.phodal.com/) 了解到更多的内容。 22 | 23 | 其它相关信息: 24 | 25 | - 微博:[http://weibo.com/phodal](http://weibo.com/phodal) 26 | - GitHub: [https://github.com/phodal](https://github.com/phodal) 27 | - 知乎:[https://www.zhihu.com/people/phodal](https://www.zhihu.com/people/phodal) 28 | - SegmentFault:[https://segmentfault.com/u/phodal](https://segmentfault.com/u/phodal) 29 | 30 | 当前为预览版,在使用的过程中遇到任何问题请及时与我联系。阅读过程中的问题,不妨在GitHub上提出来: [Issues](https://github.com/phodal/fe/issues) 31 | 32 | 阅读过程中遇到语法错误、拼写错误、技术错误等等,不妨来个Pull Request,这样可以帮助到其他阅读这本电子书的童鞋。 33 | 34 | 其他电子书: 35 | 36 | * 《[Phodal's Idea实战指南](https://github.com/phodal/ideabook)》 37 | * 《[一步步搭建物联网系统](https://github.com/phodal/designiot)》 38 | * 《[GitHub 漫游指南](https://github.com/phodal/github-roam)》 39 | * 《[RePractise](https://github.com/phodal/repractise)》 40 | * 《[Growth: 全栈增长工程师指南](https://github.com/phodal/growth-ebook)》 41 | * 《[Growth: 全栈增长工程师实战](https://github.com/phodal/growth-in-action)》 42 | * 《[我的职业是前端工程师](http://github.com/phodal/fe)》 43 | -------------------------------------------------------------------------------- /chapters/01-basic.md: -------------------------------------------------------------------------------- 1 | 基础篇 2 | === 3 | 4 | Arduino vs ESP8266 vs Raspberry Pi 5 | --- 6 | 7 | 在这一系列的文章里,我们使用三个主要的开发板: 8 | 9 | - Arduino。 10 | - ESP8266。 11 | - Raspberry Pi。 12 | 13 | 这三个硬件分别代表着,三种不同类型的硬件爱好者的开发板。 14 | 15 | ### Arduino 16 | 17 | > Arduino,是一个开放源代码的单片机微控制器,它使用了Atmel AVR单片机,采用了开放源代码的软硬件平台,建构于简易输出/输入(simple I/O)接口板,并且具有使用类似Java、C语言的Processing/Wiring开发环境。[^wiki_arduino] 18 | 19 | [^wiki_arduino]: 源自维基百科,https://zh.wikipedia.org/wiki/Arduino 20 | 21 | 其包含了以下的特性[^wiki_arduino]: 22 | 23 | - 基于知识共享开放源代码的电路图设计。 24 | - 免费下载,也可依需求自己修改,但需遵照姓名标示。您必须按照作者或授权人所指定的方式,表彰其姓名。 25 | - 依相同方式分享,若您改变或转变著作,当散布该派生著作时,您需采用与本著作相同或类似的授权条款。 26 | - Arduino 可使用 ICSP 在线烧入器,将 Bootloader 烧入新的 IC 芯片。 27 | - 可依据 Arduino 官方网站,获取硬件的设计档,加以调整电路板及组件,以匹配自己实际设计的需求 28 | - 可简单地与感测器,各式各样的电子组件连接,如红外线、超音波、热敏电阻、光敏电阻、伺服马达…等。 29 | - 支持多样的交互程序,如Adobe Flash, Max/MSP, VVVV, Pure Data, C, Processing…等。 30 | - 使用低价格的微处理控制器(Atmel AVR)(ATMEGA 8,168,328等)。 31 | - USB接口,不需外接电源。另外有提供直流(DC)电源输入。 32 | 33 | 当然,其最大的特色是,**完善的社区及生态系统**。几乎我们能想到的 Arduino 34 | 相关的创意,都可以在网上找到。如果没有的话,那么可能是你的创意不适合用于 35 | Arduino。 36 | 37 | Arduino 38 | 已然有一系列的相关硬件,就目前而言,最广泛的开发板,或者说是标准开发板是 39 | Arduino UNO。 40 | 41 | TODO: 相关 Arduino 硬件介绍 42 | 43 | Arduino 44 | 最吸引人的是开创性的引入电子积木的概念,即开发板的扩展板可以直接叠加在开发板上使用,而无需额外的硬件。 45 | 46 | TODO: 扩展板介绍。 47 | 48 | Arduion IDE 基于 Processing 开发环境而开发的。 49 | 50 | 概念 51 | --- 52 | 53 | 引脚 54 | 55 | 模电 56 | 57 | 数电 58 | 59 | 电流、电阻、电压 60 | 61 | etc.. 62 | 63 | PCB 64 | --- 65 | 66 | 为了更好的向读者展示硬件连线,我们将使用 Fritzing 汇制硬件电路图。 67 | 68 | ### Fritzing 69 | 70 | > Fritzing 是一个开源的硬件计划(initiative),它可以使电子元件变成任何人的创意素材。Fritzing 提供一个软件工具、一个社区网站,以及本着 Processing 和 Arduino 精神的服务,培养创造性的生态系统,允许用户记录他们的原型,与他人分享、用于课堂上的电子相关教学,以及布局和制造专业的pcb。 71 | 72 | Fritzing 的官网是:[http://fritzing.org/](http://fritzing.org/)。 73 | 74 | 因此,在开始之前我们需要在 Fritzing 的官网下载 75 | Fritzing。当前它可以支持主流的操作系统:Windows、Linux、macOS,当然如果你想自己从源码编译一个 76 | Fritzing,那也是可以的。除了中文和英语外,它还支持其它 17 种语言。 77 | 78 | 在其官网,除了能下载到该软件,还可以看到其它用户上传、汇制的 79 | Fritzing 电路图等,它们可以免费下载,并引用到你的项目中。 80 | 81 | LCEDA 82 | 83 | 84 | 硬件 85 | --- 86 | 87 | Nokia 5110 88 | 89 | 温度、红外、距离、土壤 90 | 91 | 92 | MCU vs CPU 93 | --- 94 | 95 | Input -> MCU -> Output 96 | 97 | 98 | 通讯 99 | --- 100 | 101 | 蓝牙、有线、 102 | 103 | 专用协议,blabla 104 | -------------------------------------------------------------------------------- /chapters/1-0-arduino-basic.md: -------------------------------------------------------------------------------- 1 | 第一部分 Arduino 篇 2 | === 3 | 4 | - Arduino + LED + 按钮控制 5 | - Arduino 读取传感器 6 | - Arduion + LCD 5100 显示传感器 7 | - Arduino 蓝牙通讯 8 | - Arduino Processing 可视化数据 9 | - Arduino 上传数据 10 | - Arduino 的可能性:+ Fritzing / Arduino + Johnny-Five 11 | -------------------------------------------------------------------------------- /chapters/1-1-arduino-hello-world.md: -------------------------------------------------------------------------------- 1 | Arduino Hello, World 2 | === 3 | 4 | 幸运的是,今天对硬件编程已经有了更多的方式,可以使用更高级的语言。除了在 Arduion 上使用 Processing 语言(可以视为 C)进行编程,还可以使用 JavaScript 等语言。我们会在后面的介绍:如何使用这些高级的语言来编程。 5 | 6 | 开发板选择 7 | --- 8 | 9 | ![Arduino 开发板示例](./images/arduino/arduinos.jpg) 10 | 11 | ![扩展板](./images/arduino/shields.jpg) 12 | 13 | Arduino IDE 默认会安装相应环境的包,如果你的开发板不在这些环境里,如你使用的是 Arduino M0 开发板。那么,系统可能会检测出来,并自动为你安装相应的环境,如下图所示。 14 | 15 | ![Arduino IDE 安装 Arduino M0 环境](./images/arduino/arduino-install-package.png) 16 | 17 | ``注意``:安装过程中会下载一系列的工具,在这个过程里,可能会遇到一些“网络问题”。请尝试更换网络,或直到到“墙”的那外一边。 18 | 19 | 对于非官方的开发板来说,则需要开发者自己手动通过『开发板管理器』来安装,如 ESP8266 开发板。 20 | 21 | hello, world 22 | --- 23 | 24 | 与桌面端使用 print、puts、console.log 来输出 Hello, world 不同的是,点亮一个 LED 是 电子世界的 Hello, world。 25 | 26 | 在硬件世界里,没有 stdlib.h 这样的库,对于多数嵌入式系统来说,也没有屏幕。在这个时候,想要知道设备的状态,最简单的方式就是 LED。常见的,如路由器、电视等家电电器上的灯,都是嵌入式设备的状态。 27 | 28 | 29 | 官方的 Blink 示例如下: 30 | 31 | ```c 32 | void setup() { 33 | // initialize digital pin LED_BUILTIN as an output. 34 | pinMode(LED_BUILTIN, OUTPUT); 35 | } 36 | 37 | // the loop function runs over and over again forever 38 | void loop() { 39 | digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) 40 | delay(1000); // wait for a second 41 | digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW 42 | delay(1000); // wait for a second 43 | } 44 | ``` 45 | 46 | 接下来,要做的事情就比较简单了。从工具( Tools )中选择对应的开发板和端口,如下图所示: 47 | 48 | ![Arduino 选择设备](./images/arduino/arduino-devices.png) 49 | 50 | 我这里使用设置是 ``Arduino M0 Pro``,对应的端口是:``/dev/tty.usbmodem1412``。 51 | 52 | 对于 Windows 用户来说,则是 COM 口——从设备管理器中找到对应的端口。 53 | 54 | 然后点击菜单中的 -> 号,即上传(Upload,又可以称为烧录)。 55 | 56 | ![Arduino 菜单](./images/arduino/arduino-menu.png) 57 | 58 | 再看看你的 Arduion,就发现上面的 LED 正在闪烁——一秒开一秒关。 59 | 60 | ### setup() 61 | 62 | 源码中的: 63 | 64 | ```c 65 | int main(void) 66 | { 67 | init(); 68 | setup(); 69 | for(;;) 70 | loop(); 71 | 72 | return 0; 73 | } 74 | ``` 75 | 76 | ### loop() 77 | 78 | 输入控制输出 79 | --- 80 | 81 | GOAL 82 | 83 | SMART 原则 84 | 85 | 仍然采用的是官方的示例: 86 | 87 | ![Arduino 控制 LED 示例](./images/arduino/fritzing-button.png) 88 | 89 | 官方示例([Digital Read Serial](https://www.arduino.cc/en/Tutorial/DigitalReadSerial))代码: 90 | 91 | ```c 92 | // digital pin 2 has a pushbutton attached to it. Give it a name: 93 | int pushButton = 2; 94 | 95 | // the setup routine runs once when you press reset: 96 | void setup() { 97 | // initialize serial communication at 9600 bits per second: 98 | Serial.begin(9600); 99 | // make the pushbutton's pin an input: 100 | pinMode(pushButton, INPUT); 101 | } 102 | 103 | // the loop routine runs over and over again forever: 104 | void loop() { 105 | // read the input pin: 106 | int buttonState = digitalRead(pushButton); 107 | // print out the state of the button: 108 | Serial.println(buttonState); 109 | delay(1); // delay in between reads for stability 110 | } 111 | ``` 112 | 113 | ### 电路图 114 | 115 | 使用 Fritzing 画板时 116 | 117 | ![](./images/arduino/fritzing-example.jpg) 118 | 119 | 我们所需要的连线图 120 | 121 | ![](./images/arduino/arduino-button.jpg) 122 | -------------------------------------------------------------------------------- /chapters/1-2-arduino-sensor.md: -------------------------------------------------------------------------------- 1 | Arduino 传感器控制 2 | === 3 | 4 | 传感器入门 5 | --- 6 | 7 | > 传感器是一种物理装置或生物器官,能够探测、感受外界的信号、物理条件(如光、热、湿度)或化学组成(如烟雾),并将探知的信息传递给其他装置或器官。“传感器”在新韦式大词典中定义为:“从一个系统接受功率,通常以另一种形式将功率送到第二个系统中的器件”。根据这个定义,传感器的作用是将一种能量转换成另一种能量形式,所以不少学者也用“换能器-Transducer”来称谓“传感器-Sensor”。[^wiki_sensor] 8 | 9 | [^wiki_sensor] : https://zh.wikipedia.org/wiki/%E4%BC%A0%E6%84%9F%E5%99%A8 10 | 11 | ### 模拟元件与数字元件 12 | 13 | 超声波传感器 14 | --- 15 | 16 | 17 | ![Arduion 超声波传感器连线图](./images/arduino/arduino-hs-sr04.png) 18 | 19 | 使用到库:[NewPing],GitHub 地址:[https://github.com/PaulStoffregen/NewPing](https://github.com/PaulStoffregen/NewPing) 20 | 21 | ![下载 Zip 格式](./images/arduino/download-zip.png) 22 | 23 | 添加库 24 | 25 | ![添加 NewPing 库](./images/arduinoadd-zip-library.png) 26 | 27 | 28 | ```c 29 | #include 30 | 31 | #define TRIGGER_PIN 12 // Arduino pin tied to trigger pin on the ultrasonic sensor. 32 | #define ECHO_PIN 11 // Arduino pin tied to echo pin on the ultrasonic sensor. 33 | #define MAX_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm. 34 | 35 | NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance. 36 | 37 | void setup() { 38 | Serial.begin(115200); // Open serial monitor at 115200 baud to see ping results. 39 | } 40 | 41 | void loop() { 42 | delay(50); // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings. 43 | Serial.print("Ping: "); 44 | Serial.print(sonar.ping_cm()); // Send ping, get distance in cm and print result (0 = outside set distance range) 45 | Serial.println("cm"); 46 | } 47 | ``` 48 | 49 | ![输出示例](serial-output-example.png) 50 | 51 | 控制继电器 52 | --- 53 | -------------------------------------------------------------------------------- /chapters/1-3-arduino-5110.md: -------------------------------------------------------------------------------- 1 | Arduino 5110 显示 2 | === 3 | 4 | LCD 5 | --- 6 | 7 | 安装库 8 | --- 9 | 10 | 项目地址:[Adafruit PCD8544](https://github.com/adafruit/Adafruit-PCD8544-Nokia-5110-LCD-library) 11 | 12 | ![添加库](./images/arduino/manage-library.png) 13 | 14 | ![Arduino IDE 安装 5110](./images/arduino/arduino-nokia-5110.png) 15 | 16 | 一般来说,在安装完库后,都会有对应的示例。但是,这里的示例,稍微有一些复杂。 17 | 18 | ```c 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | // Pins 26 | const int8_t RST_PIN = D2; 27 | const int8_t CE_PIN = D1; 28 | const int8_t DC_PIN = D6; 29 | //const int8_t DIN_PIN = D7; // Uncomment for Software SPI 30 | //const int8_t CLK_PIN = D5; // Uncomment for Software SPI 31 | const int8_t BL_PIN = D0; 32 | 33 | 34 | // Software SPI with explicit CE pin. 35 | // Adafruit_PCD8544 display = Adafruit_PCD8544(CLK_PIN, DIN_PIN, DC_PIN, CE_PIN, RST_PIN); 36 | 37 | // Software SPI with CE tied to ground. Saves a pin but other pins can't be shared with other hardware. 38 | // Adafruit_PCD8544(int8_t CLK_PIN, int8_t DIN_PIN, int8_t DC_PIN, int8_t RST_PIN); 39 | 40 | // Hardware SPI based on hardware controlled SCK (SCLK) and MOSI (DIN) pins. CE is still controlled by any IO pin. 41 | // NOTE: MISO and SS will be set as an input and output respectively, so be careful sharing those pins! 42 | Adafruit_PCD8544 display = Adafruit_PCD8544(DC_PIN, CE_PIN, RST_PIN); 43 | 44 | void setup() { 45 | Serial.begin(9600); 46 | Serial.println("\n\nWeMos D1 Mini + Nokia 5110 PCD8544 84x48 Monochrome LCD\nUsing Adafruit_PCD8544 and Adafruit_GFX libraries\n"); 47 | 48 | // Turn LCD backlight on 49 | pinMode(BL_PIN, OUTPUT); 50 | digitalWrite(BL_PIN, HIGH); 51 | 52 | display.begin(); 53 | display.setContrast(60); // Adjust for your display 54 | Serial.println("Show Adafruit logo bitmap"); 55 | 56 | // Show the Adafruit logo, which is preloaded into the buffer by their library 57 | // display.clearDisplay(); 58 | delay(2000); 59 | 60 | display.clearDisplay(); 61 | display.setTextSize(1); 62 | display.setTextColor(BLACK); 63 | display.setCursor(0,0); 64 | display.println("Hello, world!"); 65 | display.display(); 66 | } 67 | 68 | void loop() { 69 | } 70 | ``` 71 | 72 | 输出 hello, world 73 | --- 74 | -------------------------------------------------------------------------------- /chapters/1-4-arduino-processing.md: -------------------------------------------------------------------------------- 1 | Arduino Processing 2 | === 3 | 4 | 串口输出 5 | --- 6 | 7 | 8 | 复杂的例子:MPU 6050 9 | --- 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /chapters/1-5-arduino-ble.md: -------------------------------------------------------------------------------- 1 | Arduino BLE 控制 2 | === 3 | 4 | 蓝牙串口 5 | --- 6 | 7 | Bluno 等自带蓝牙的开发板 8 | 9 | 10 | 与手机通讯 11 | --- 12 | 13 | React Native / Cordova ??? 14 | 15 | -------------------------------------------------------------------------------- /chapters/1-6-arduino-shield.md: -------------------------------------------------------------------------------- 1 | Arduino 扩展板使用 2 | === 3 | 4 | I don't know 5 | 6 | 继电器 7 | --- 8 | 9 | 网络 10 | --- 11 | 12 | ![Arduion 以太网扩展板](./images/arduino/arduino-ethernet.png) 13 | 14 | -------------------------------------------------------------------------------- /chapters/1-7-arduino-network.md: -------------------------------------------------------------------------------- 1 | Arduino ESP8266 WiFi 2 | === 3 | 4 | ESP8266 设置 5 | --- 6 | 7 | 8 | Arduino WiFi 9 | --- 10 | -------------------------------------------------------------------------------- /chapters/2-0-esp8266-baisc.md: -------------------------------------------------------------------------------- 1 | 第二部分 ESP8266 2 | === 3 | 4 | - ESP8266 基础 + 连接 WiFi / NodeMCU 5 | - ESP8266 Arduino IDE ?? 6 | - ESP8266 上传 Arduino 数据 7 | - ? ESP8266 读取传感器(ADC?)),并上传数据 8 | - ESP8266 MQTT 示例 9 | - ESP8266 作为 HTTP 服务器示例 10 | - 更多的可能性探索:Mongoose OS、Johnny-Five、智能家居 11 | -------------------------------------------------------------------------------- /chapters/2-1-esp8266-basic.md: -------------------------------------------------------------------------------- 1 | ESP8266 基础 2 | === 3 | 4 | 设置与 AT 指令 5 | --- 6 | 7 | 8 | NodeMCU 9 | --- 10 | 11 | -------------------------------------------------------------------------------- /chapters/2-2-esp8266-arduino-ide.md: -------------------------------------------------------------------------------- 1 | ESP8266 Arduino IDE 展示 2 | === 3 | -------------------------------------------------------------------------------- /chapters/2-3-esp8266-arduino-uno.md: -------------------------------------------------------------------------------- 1 | ESP8266 连接 Arduino UNO 2 | === 3 | 4 | -------------------------------------------------------------------------------- /chapters/2-4-esp8266-sensor-to-server.md: -------------------------------------------------------------------------------- 1 | ESP8266 上传传感器数据 2 | === 3 | 4 | ### ADC 数据 5 | 6 | ### 发送请求 7 | 8 | ```c 9 | /** 10 | * BasicHTTPClient.ino 11 | * 12 | * Created on: 24.05.2015 13 | * 14 | */ 15 | 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #define USE_SERIAL Serial 24 | 25 | ESP8266WiFiMulti WiFiMulti; 26 | 27 | void setup() { 28 | 29 | USE_SERIAL.begin(115200); 30 | // USE_SERIAL.setDebugOutput(true); 31 | 32 | USE_SERIAL.println(); 33 | USE_SERIAL.println(); 34 | USE_SERIAL.println(); 35 | 36 | for(uint8_t t = 4; t > 0; t--) { 37 | USE_SERIAL.printf("[SETUP] WAIT %d...\n", t); 38 | USE_SERIAL.flush(); 39 | delay(1000); 40 | } 41 | 42 | WiFiMulti.addAP("SSID", "PASSWORD"); 43 | 44 | } 45 | 46 | void loop() { 47 | // wait for WiFi connection 48 | if((WiFiMulti.run() == WL_CONNECTED)) { 49 | 50 | HTTPClient http; 51 | 52 | USE_SERIAL.print("[HTTP] begin...\n"); 53 | // configure traged server and url 54 | //http.begin("https://192.168.1.12/test.html", "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS 55 | http.begin("http://192.168.1.12/test.html"); //HTTP 56 | 57 | USE_SERIAL.print("[HTTP] GET...\n"); 58 | // start connection and send HTTP header 59 | int httpCode = http.GET(); 60 | 61 | // httpCode will be negative on error 62 | if(httpCode > 0) { 63 | // HTTP header has been send and Server response header has been handled 64 | USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); 65 | 66 | // file found at server 67 | if(httpCode == HTTP_CODE_OK) { 68 | String payload = http.getString(); 69 | USE_SERIAL.println(payload); 70 | } 71 | } else { 72 | USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); 73 | } 74 | 75 | http.end(); 76 | } 77 | 78 | delay(10000); 79 | } 80 | ``` 81 | 82 | ### 上传 83 | -------------------------------------------------------------------------------- /chapters/2-5-esp8266-mqtt.md: -------------------------------------------------------------------------------- 1 | ESP8266 MQTT 控制 2 | === 3 | 4 | ### MQTT 5 | 6 | ### moquitto 7 | 8 | ### 9 | 10 | [https://github.com/tuanpmt/esp_mqtt](https://github.com/tuanpmt/esp_mqtt) 11 | 12 | -------------------------------------------------------------------------------- /chapters/2-6-esp8266-http-server.md: -------------------------------------------------------------------------------- 1 | ESP8266 HTTP 服务器 2 | === 3 | 4 | 5 | ### Simple HTTP Server 6 | 7 | 8 | ```c 9 | #include 10 | #include 11 | 12 | ESP8266WebServer server(80); 13 | 14 | void setup() { 15 | 16 | Serial.begin(115200); 17 | WiFi.begin("Network name", "Password"); //Connect to the WiFi network 18 | 19 | while (WiFi.status() != WL_CONNECTED) { //Wait for connection 20 | delay(500); 21 | Serial.println("Waiting to connect…"); 22 | } 23 | 24 | Serial.print("IP address: "); 25 | Serial.println(WiFi.localIP()); //Print the local IP 26 | 27 | server.on(" / other", []() { //Define the handling function for the path 28 | 29 | server.send(200, "text / plain", "Other URL"); 30 | 31 | }); 32 | 33 | server.on(" / ", handleRootPath); //Associate the handler function to the path 34 | server.begin(); //Start the server 35 | Serial.println("Server listening"); 36 | } 37 | 38 | void loop() { 39 | server.handleClient(); 40 | } 41 | 42 | void handleRootPath() { 43 | server.send(200, "text/plain", "Hello world"); 44 | } 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- /chapters/2-7-beyond-esp8266.md: -------------------------------------------------------------------------------- 1 | 超越 ESP8266 2 | === 3 | -------------------------------------------------------------------------------- /chapters/3-0-rpi.md: -------------------------------------------------------------------------------- 1 | 第三部分 Raspberry Pi 2 | === 3 | 4 | - Hello, world! 5 | - Node.js 服务 - 开机启动,blabla 6 | - 连接 Arduino、自动编程? OTA ? 7 | - Raspberry Pi as PC Camera + Sensors ??? 8 | - Raspberry Pi <-> ESP8266 / Node.js Example 9 | - MQTT Server 10 | - IoT Server + Database? 11 | -------------------------------------------------------------------------------- /chapters/3-1-rpi-hello-world.md: -------------------------------------------------------------------------------- 1 | Raspberry Pi Hello, world! 2 | === 3 | 4 | ### 镜像烧录 5 | 6 | ### SSH 连接 7 | 8 | ### Python 控制 GPIO -------------------------------------------------------------------------------- /chapters/3-2-rpi-node-server-basic.md: -------------------------------------------------------------------------------- 1 | Node.js 服务基础 2 | === 3 | 4 | ### 安装 Node.js 5 | 6 | ### Express Hello, world 7 | 8 | ### 开机启动 -------------------------------------------------------------------------------- /chapters/3-3-rpi-camera-server.md: -------------------------------------------------------------------------------- 1 | 摄像头 + 传感器 = 智能家居? 2 | === 3 | 4 | 5 | ### 红外传感器 6 | 7 | ### 视频流 8 | 9 | ### 拍照,blabla -------------------------------------------------------------------------------- /chapters/3-4-esp8266-server-connect.md: -------------------------------------------------------------------------------- 1 | 与 ESP8266 无线通讯 2 | === 3 | 4 | ### 建立服务 5 | 6 | ### 广播? -------------------------------------------------------------------------------- /chapters/3-5-arduino-ota.md: -------------------------------------------------------------------------------- 1 | Arduino OTA 更新 2 | === 3 | 4 | ### 服务端读取代码 5 | 6 | ### arduino-cli 更新硬件 7 | 8 | -------------------------------------------------------------------------------- /chapters/3-6-rpi-mqtt-server.md: -------------------------------------------------------------------------------- 1 | 使用 MQTT 服务器 2 | === 3 | 4 | ### MQTT 客户端 5 | 6 | ### 使用 mosquittto 7 | -------------------------------------------------------------------------------- /chapters/3-7-rpi-iot-docker.md: -------------------------------------------------------------------------------- 1 | 跟上潮流:Docker、硬件微服务 2 | === 3 | 4 | ### 使用 Docker 5 | 6 | ### 硬件微服务 7 | 8 | 9 | -------------------------------------------------------------------------------- /chapters/4-0-basic.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/chapters/4-0-basic.md -------------------------------------------------------------------------------- /chapters/4-1-ha-setup.md: -------------------------------------------------------------------------------- 1 | Home Assistant 搭建 2 | === 3 | -------------------------------------------------------------------------------- /chapters/4-2-ha-esp8266.md: -------------------------------------------------------------------------------- 1 | Home Assistant 整合 2 | === 3 | -------------------------------------------------------------------------------- /chapters/4-3-homekit-setup.md: -------------------------------------------------------------------------------- 1 | Homekit 搭建 2 | === 3 | -------------------------------------------------------------------------------- /chapters/4-4-homekit-esp8266.md: -------------------------------------------------------------------------------- 1 | Homekit 整合 2 | === 3 | -------------------------------------------------------------------------------- /chapters/4-5-rpi-avs.md: -------------------------------------------------------------------------------- 1 | Raspberry Pi 与智能音箱 2 | === 3 | -------------------------------------------------------------------------------- /chapters/4-6-rpi-respeaker.md: -------------------------------------------------------------------------------- 1 | Raspberry Pi 自制智能音箱 2 | === 3 | -------------------------------------------------------------------------------- /chapters/4-7-rpi-ai-blockchain.md: -------------------------------------------------------------------------------- 1 | 超越 Raspberry Pi 2 | === 3 | 4 | -------------------------------------------------------------------------------- /css/vendor.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | .acodecode { 4 | cursor: pointer 5 | } 6 | .acodecodeactive { 7 | cursor: default 8 | } 9 | .codecodecode { 10 | position: fixed; 11 | z-index: 100; 12 | left: 2%; 13 | bottom: -30px; 14 | width: 96%; 15 | height: 0; 16 | max-height: 270px; 17 | text-align: left 18 | } 19 | .codecodecode>div { 20 | box-shadow: 0 0 3px #444 21 | } 22 | .codecodecontrols { 23 | height: 30px; 24 | margin-top: -30px; 25 | background-color: #fff; 26 | background-color: rgba(255, 255, 255, .8); 27 | border-radius: 8px 8px 0 0 28 | } 29 | .codecodecontrols a { 30 | float: left; 31 | line-height: 30px; 32 | margin-left: 6px; 33 | font-family: Arial; 34 | font-size: 12px 35 | } 36 | .codecodecontrols .closeCodeCode { 37 | float: right; 38 | margin-right: 6px 39 | } 40 | .acodecode.codecode, .codecode { 41 | border-radius: 0 !important; 42 | position: relative !important; 43 | width: 100% !important; 44 | margin: 0 !important; 45 | overflow: auto !important; 46 | cursor: default !important 47 | } 48 | div.codecode [id^=highlighter] div.bar.show, div.codecode [id^=highlighter] div.toolbar { 49 | display: none !important 50 | } 51 | h1, h2, h3, h4, h5, h6, p { 52 | margin-top: 0; 53 | padding-right: 15px; 54 | padding-left: 15px; 55 | text-align: left; 56 | font-family: Georgia 57 | } 58 | h1 { 59 | text-align: left 60 | } 61 | h2.booktitle { 62 | font-size: 1.5em; 63 | color: #666 64 | } 65 | .subhead-link { 66 | font-size: .75em; 67 | margin-left: -15px; 68 | margin-top: 5px; 69 | float: left; 70 | visibility: hidden 71 | } 72 | .subhead-link:hover { 73 | text-decoration: none; 74 | visibility: visible 75 | } 76 | h1:hover .subhead-link, h2:hover .subhead-link, h3:hover .subhead-link, h4:hover .subhead-link { 77 | visibility: visible 78 | } 79 | .container { 80 | background: #FFF 81 | } 82 | header { 83 | background: #fff 84 | } 85 | .content { 86 | padding: 10px 0; 87 | text-align: left 88 | } 89 | .footer { 90 | background: #fff 91 | } 92 | .fltrt { 93 | float: right; 94 | margin-left: 8px 95 | } 96 | .fltlft { 97 | float: left; 98 | margin-right: 8px 99 | } 100 | .clearfloat { 101 | clear: both; 102 | height: 0; 103 | font-size: 1px; 104 | line-height: 0 105 | } 106 | .copyright { 107 | text-align: left 108 | } 109 | .booktitle { 110 | text-align: center; 111 | line-height: 41px; 112 | border-bottom: 1px solid #fff; 113 | padding: 0; 114 | font-size: 2.2em 115 | } 116 | .booktitle.author { 117 | font-size: 24px 118 | } 119 | #contents-list { 120 | background: none repeat scroll 0 0 #EEE; 121 | border: 3px solid #DDD; 122 | padding: 1em 1em 1em 3em 123 | } 124 | .subitem { 125 | margin-left: 25px 126 | } 127 | #references-list { 128 | word-wrap: break-word 129 | } 130 | pre { 131 | display: block; 132 | line-height: 18px; 133 | background-color: #f5f5f5; 134 | -webkit-border-radius: 4px; 135 | -moz-border-radius: 4px; 136 | border-radius: 4px; 137 | white-space: pre; 138 | white-space: pre-wrap; 139 | word-break: break-all; 140 | word-wrap: break-word 141 | } 142 | div.syntaxhighlighter { 143 | padding: 1em 0 144 | } 145 | .syntaxhighlighter a, .syntaxhighlighter code, .syntaxhighlighter div, .syntaxhighlighter table, .syntaxhighlighter table caption, .syntaxhighlighter table tbody, .syntaxhighlighter table td, .syntaxhighlighter table thead, .syntaxhighlighter table tr, .syntaxhighlighter textarea { 146 | -moz-border-radius: 0 !important; 147 | -webkit-border-radius: 0 !important; 148 | background: none !important; 149 | border: 0 !important; 150 | bottom: auto !important; 151 | float: none !important; 152 | height: auto !important; 153 | left: auto !important; 154 | line-height: 1.1em !important; 155 | margin: 0 !important; 156 | outline: 0 !important; 157 | overflow: visible !important; 158 | padding: 0 !important; 159 | position: static !important; 160 | right: auto !important; 161 | text-align: left !important; 162 | top: auto !important; 163 | vertical-align: baseline !important; 164 | width: auto !important; 165 | box-sizing: content-box !important; 166 | font-family: Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; 167 | font-weight: 400 !important; 168 | font-style: normal !important; 169 | font-size: 1em !important; 170 | min-height: inherit !important; 171 | min-height: auto !important 172 | } 173 | .syntaxhighlighter { 174 | width: 100% !important; 175 | margin: 1em 0 !important; 176 | position: relative !important; 177 | overflow: auto !important; 178 | font-size: 1em !important 179 | } 180 | .syntaxhighlighter.source { 181 | overflow: hidden !important 182 | } 183 | .syntaxhighlighter .bold { 184 | font-weight: 700 !important 185 | } 186 | .syntaxhighlighter .italic { 187 | font-style: italic !important 188 | } 189 | .syntaxhighlighter .line { 190 | white-space: pre !important 191 | } 192 | .syntaxhighlighter table { 193 | width: 100% !important 194 | } 195 | .syntaxhighlighter table caption { 196 | text-align: left !important; 197 | padding: .5em 0 .5em 1em !important 198 | } 199 | .syntaxhighlighter table td.code { 200 | width: 100% !important 201 | } 202 | .syntaxhighlighter table td.code .container { 203 | position: relative !important 204 | } 205 | .syntaxhighlighter table td.code .container textarea { 206 | box-sizing: border-box !important; 207 | position: absolute !important; 208 | left: 0 !important; 209 | top: 0 !important; 210 | width: 100% !important; 211 | height: 100% !important; 212 | border: 0 !important; 213 | background: #fff !important; 214 | padding-left: 1em !important; 215 | overflow: hidden !important; 216 | white-space: pre !important 217 | } 218 | .syntaxhighlighter table td.gutter .line { 219 | text-align: right !important; 220 | padding: 0 .5em 0 1em !important 221 | } 222 | .syntaxhighlighter table td.code .line { 223 | padding: 0 1em !important 224 | } 225 | .syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line { 226 | padding-left: 0 !important 227 | } 228 | .syntaxhighlighter.show { 229 | display: block !important 230 | } 231 | .syntaxhighlighter.collapsed table { 232 | display: none !important 233 | } 234 | .syntaxhighlighter.collapsed .toolbar { 235 | padding: .1em .8em 0 !important; 236 | font-size: 1em !important; 237 | position: static !important; 238 | width: auto !important; 239 | height: auto !important 240 | } 241 | .syntaxhighlighter.collapsed .toolbar span { 242 | display: inline !important; 243 | margin-right: 1em !important 244 | } 245 | .syntaxhighlighter.collapsed .toolbar span a { 246 | padding: 0 !important; 247 | display: none !important 248 | } 249 | .syntaxhighlighter.collapsed .toolbar span a.expandSource { 250 | display: inline !important 251 | } 252 | .syntaxhighlighter .toolbar { 253 | position: absolute !important; 254 | right: 1px !important; 255 | top: 1px !important; 256 | width: 11px !important; 257 | height: 11px !important; 258 | font-size: 10px !important; 259 | z-index: 10 !important 260 | } 261 | .syntaxhighlighter .toolbar span.title { 262 | display: inline !important 263 | } 264 | .syntaxhighlighter .toolbar a { 265 | display: block !important; 266 | text-align: center !important; 267 | text-decoration: none !important; 268 | padding-top: 1px !important 269 | } 270 | .syntaxhighlighter .toolbar a.expandSource { 271 | display: none !important 272 | } 273 | .syntaxhighlighter.ie { 274 | font-size: .9em !important; 275 | padding: 1px 0 !important 276 | } 277 | .syntaxhighlighter.ie .toolbar { 278 | line-height: 8px !important 279 | } 280 | .syntaxhighlighter.ie .toolbar a { 281 | padding-top: 0 !important 282 | } 283 | .syntaxhighlighter.printing .line.alt1 .content, .syntaxhighlighter.printing .line.alt2 .content, .syntaxhighlighter.printing .line.highlighted .number, .syntaxhighlighter.printing .line.highlighted.alt1 .content, .syntaxhighlighter.printing .line.highlighted.alt2 .content { 284 | background: none !important 285 | } 286 | .syntaxhighlighter.printing .line .number { 287 | color: #bbb !important 288 | } 289 | .syntaxhighlighter.printing .line .content { 290 | color: #000 !important 291 | } 292 | .syntaxhighlighter.printing .toolbar { 293 | display: none !important 294 | } 295 | .syntaxhighlighter.printing a { 296 | text-decoration: none !important 297 | } 298 | .syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a { 299 | color: #000 !important 300 | } 301 | .syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a { 302 | color: #008200 !important 303 | } 304 | .syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a { 305 | color: #00f !important 306 | } 307 | .syntaxhighlighter.printing .keyword { 308 | color: #069 !important; 309 | font-weight: 700 !important 310 | } 311 | .syntaxhighlighter.printing .preprocessor { 312 | color: gray !important 313 | } 314 | .syntaxhighlighter.printing .variable { 315 | color: #a70 !important 316 | } 317 | .syntaxhighlighter.printing .value { 318 | color: #090 !important 319 | } 320 | .syntaxhighlighter.printing .functions { 321 | color: #ff1493 !important 322 | } 323 | .syntaxhighlighter.printing .constants { 324 | color: #06c !important 325 | } 326 | .syntaxhighlighter.printing .script { 327 | font-weight: 700 !important 328 | } 329 | .syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a { 330 | color: gray !important 331 | } 332 | .syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a { 333 | color: #ff1493 !important 334 | } 335 | .syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a { 336 | color: red !important 337 | } 338 | .syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a { 339 | color: #000 !important 340 | } 341 | .syntaxhighlighter, .syntaxhighlighter .line.alt1, .syntaxhighlighter .line.alt2 { 342 | background-color: #1b2426 !important 343 | } 344 | .syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 { 345 | background-color: #323e41 !important 346 | } 347 | .syntaxhighlighter .line.highlighted.number, .syntaxhighlighter table caption { 348 | color: #b9bdb6 !important 349 | } 350 | .syntaxhighlighter .gutter { 351 | color: #afafaf !important 352 | } 353 | .syntaxhighlighter .gutter .line { 354 | border-right: 3px solid #435a5f !important 355 | } 356 | .syntaxhighlighter .gutter .line.highlighted { 357 | background-color: #435a5f !important; 358 | color: #1b2426 !important 359 | } 360 | .syntaxhighlighter.printing .line .content { 361 | border: 0 !important 362 | } 363 | .syntaxhighlighter.collapsed { 364 | overflow: visible !important 365 | } 366 | .syntaxhighlighter.collapsed .toolbar { 367 | color: #5ba1cf !important; 368 | background: #000 !important; 369 | border: 1px solid #435a5f !important 370 | } 371 | .syntaxhighlighter.collapsed .toolbar a { 372 | color: #5ba1cf !important 373 | } 374 | .syntaxhighlighter.collapsed .toolbar a:hover { 375 | color: #5ce638 !important 376 | } 377 | .syntaxhighlighter .toolbar { 378 | color: #fff !important; 379 | background: #435a5f !important; 380 | border: 0 !important 381 | } 382 | .syntaxhighlighter .toolbar a { 383 | color: #fff !important 384 | } 385 | .syntaxhighlighter .toolbar a:hover { 386 | color: #e0e8ff !important 387 | } 388 | .syntaxhighlighter .plain, .syntaxhighlighter .plain a { 389 | color: #b9bdb6 !important 390 | } 391 | .syntaxhighlighter .comments, .syntaxhighlighter .comments a { 392 | color: #878a85 !important 393 | } 394 | .syntaxhighlighter .string, .syntaxhighlighter .string a { 395 | color: #5ce638 !important 396 | } 397 | .syntaxhighlighter .keyword { 398 | color: #5ba1cf !important 399 | } 400 | .syntaxhighlighter .preprocessor { 401 | color: #435a5f !important 402 | } 403 | .syntaxhighlighter .variable { 404 | color: #ffaa3e !important 405 | } 406 | .syntaxhighlighter .value { 407 | color: #090 !important 408 | } 409 | .syntaxhighlighter .functions { 410 | color: #ffaa3e !important 411 | } 412 | .syntaxhighlighter .constants { 413 | color: #e0e8ff !important 414 | } 415 | .syntaxhighlighter .script { 416 | font-weight: 700 !important; 417 | color: #5ba1cf !important; 418 | background-color: none !important 419 | } 420 | .syntaxhighlighter .color1, .syntaxhighlighter .color1 a { 421 | color: #e0e8ff !important 422 | } 423 | .syntaxhighlighter .color2, .syntaxhighlighter .color2 a { 424 | color: #fff !important 425 | } 426 | .syntaxhighlighter .color3, .syntaxhighlighter .color3 a { 427 | color: #ffaa3e !important 428 | } 429 | .sausage-set { 430 | position: fixed; 431 | right: 0; 432 | top: 0; 433 | width: 15px; 434 | height: 100%; 435 | border-left: solid 2px #fff; 436 | border-right: solid 2px #fff; 437 | background-color: #fff; 438 | font-family: 'Helvetica Neue', Arial, sans-serif 439 | } 440 | .sausage { 441 | position: absolute; 442 | left: 0; 443 | width: 100%; 444 | height: 100%; 445 | background-color: #f1f1f1; 446 | text-decoration: none; 447 | -moz-border-radius: 8px; 448 | -webkit-border-bottom-left-radius: 8px; 449 | -webkit-border-top-left-radius: 8px; 450 | -webkit-border-bottom-right-radius: 8px; 451 | -webkit-border-top-right-radius: 8px; 452 | -moz-box-shadow: inset 0 1px 2px 4px rgba(0, 0, 0, .025); 453 | -webkit-box-shadow: inset 0 1px 2px 4px rgba(0, 0, 0, .025); 454 | cursor: pointer 455 | } 456 | .sausage-current, .sausage-hover { 457 | background-color: #f2e4ed; 458 | -moz-box-shadow: inset 0 1px 2px 4px rgba(51, 63, 70, .025) 459 | } 460 | .sausage-span { 461 | position: absolute; 462 | right: 24px; 463 | top: 5px; 464 | z-index: 2; 465 | display: none; 466 | width: 100px; 467 | padding: 2px 3px; 468 | color: #000; 469 | background-color: #fff; 470 | border: solid 2px #906; 471 | font-size: 10px; 472 | line-height: 12px; 473 | font-weight: 700; 474 | text-align: center; 475 | -moz-border-radius: 7px; 476 | -webkit-border-bottom-left-radius: 7px; 477 | -webkit-border-top-left-radius: 7px; 478 | -webkit-border-bottom-right-radius: 7px; 479 | -webkit-border-top-right-radius: 7px; 480 | -moz-box-shadow: 1px 1px 1px 1px rgba(0, 0, 0, .05); 481 | -webkit-box-shadow: 1px 1px 1px 1px rgba(0, 0, 0, .05) 482 | } 483 | .sausage-current .sausage-span, .sausage-hover .sausage-span { 484 | display: block 485 | } 486 | a, abbr, acronym, address, article, aside, blockquote, body, caption, code, dd, del, dfn, dialog, div, dl, dt, em, fieldset, figure, footer, form, h1, h2, h3, h4, h5, h6, header, hgroup, html, iframe, img, label, legend, li, nav, object, ol, p, pre, q, section, span, table, tbody, td, tfoot, th, thead, tr, ul { 487 | margin: 0; 488 | padding: 0; 489 | border: 0; 490 | font-weight: inherit; 491 | font-style: inherit; 492 | font-size: 100%; 493 | font-family: inherit; 494 | vertical-align: baseline 495 | } 496 | article, aside, dialog, figure, footer, header, hgroup, nav, section { 497 | display: block 498 | } 499 | body { 500 | line-height: 1.5 501 | } 502 | table { 503 | border-collapse: separate; 504 | border-spacing: 0 505 | } 506 | caption, td, th { 507 | text-align: left; 508 | font-weight: 400 509 | } 510 | table, td, th { 511 | vertical-align: middle 512 | } 513 | blockquote:after, blockquote:before, q:after, q:before { 514 | content: "" 515 | } 516 | blockquote, q { 517 | quotes: "" "" 518 | } 519 | a img { 520 | border: 0 521 | } 522 | .search, body, input[type=submit], input[type=text] { 523 | font-family: Palatino, "Palatino Linotype", Georgia, Times, "Times New Roman", serif 524 | } 525 | html { 526 | font-size: 100.01% 527 | } 528 | h1, h2, h3, h4, h5, h6 { 529 | font-weight: 400; 530 | color: #000 531 | } 532 | h1 { 533 | font-size: 3em; 534 | line-height: 1; 535 | margin-bottom: .5em 536 | } 537 | h2 { 538 | font-size: 2em; 539 | margin-bottom: .75em 540 | } 541 | h3 { 542 | font-size: 1.5em; 543 | line-height: 1 544 | } 545 | h4 { 546 | font-size: 1.2em; 547 | line-height: 1.25; 548 | margin-bottom: 1.25em 549 | } 550 | h5 { 551 | font-size: 1em; 552 | font-weight: 700; 553 | margin-bottom: 1.5em 554 | } 555 | h6 { 556 | font-size: 1em; 557 | font-weight: 700 558 | } 559 | h1 img, h2 img, h3 img, h4 img, h5 img, h6 img { 560 | margin: 0 561 | } 562 | p { 563 | margin: 0 0 1.5em 564 | } 565 | p img.left { 566 | float: left; 567 | margin: 1.5em 1.5em 1.5em 0; 568 | padding: 0 569 | } 570 | p img.right { 571 | float: right; 572 | margin: 1.5em 0 1.5em 1.5em 573 | } 574 | strong { 575 | font-weight: 700 576 | } 577 | dfn, em { 578 | font-style: italic 579 | } 580 | dfn { 581 | font-weight: 700 582 | } 583 | sub, sup { 584 | line-height: 0 585 | } 586 | address { 587 | margin: 0 0 1.5em; 588 | font-style: italic 589 | } 590 | del { 591 | color: #666 592 | } 593 | li ol, li ul { 594 | margin: 0 595 | } 596 | ol, ul { 597 | margin: 0 1.5em 1.5em 0; 598 | padding-left: 3.333em 599 | } 600 | ul { 601 | list-style-type: disc 602 | } 603 | ol { 604 | list-style-type: decimal 605 | } 606 | dl { 607 | margin: 0 0 1.5em 608 | } 609 | dl dt { 610 | font-weight: 700 611 | } 612 | dd { 613 | margin-left: 1.5em 614 | } 615 | table { 616 | margin-bottom: 1.4em 617 | } 618 | th { 619 | font-weight: 700 620 | } 621 | thead th { 622 | background: #c3d9ff 623 | } 624 | caption, td, th { 625 | padding: 4px 10px 4px 5px 626 | } 627 | tfoot { 628 | font-style: italic 629 | } 630 | body { 631 | font-size: 100%; 632 | color: #000; 633 | background: #F6f6F6 url(../images/base.png) repeat 0 0 634 | } 635 | h3 { 636 | border-bottom: 1px solid #CCC; 637 | margin-bottom: .5em; 638 | padding-bottom: .5em 639 | } 640 | .lead { 641 | font-size: 1.5em 642 | } 643 | .stage_links { 644 | color: #777 645 | } 646 | a, a:link, a:visited { 647 | color: #906; 648 | text-decoration: none 649 | } 650 | a:active, a:focus, a:hover { 651 | color: #E106B2; 652 | text-decoration: underline 653 | } 654 | hr.space { 655 | background: #fff; 656 | color: #fff; 657 | visibility: hidden 658 | } 659 | hr { 660 | background: #CCC; 661 | color: #CCC; 662 | clear: both; 663 | float: none; 664 | width: 100%; 665 | height: .1em; 666 | margin: 0 0 1.45em; 667 | border: 0 668 | } 669 | hr.bold { 670 | height: 1px; 671 | background-color: #906; 672 | color: #906 673 | } 674 | blockquote { 675 | overflow: hidden; 676 | margin: 0 0 1.5em; 677 | padding: 0 1.5em; 678 | color: #000; 679 | font-style: normal 680 | } 681 | blockquote p { 682 | margin-bottom: .5em 683 | } 684 | .attribution { 685 | font-style: italic; 686 | text-align: right; 687 | color: #777 688 | } 689 | table { 690 | width: 100% 691 | } 692 | code { 693 | padding: 2px 4px; 694 | color: #D14; 695 | background-color: #F7F7F9; 696 | border: 1px solid #E1E1E8; 697 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 698 | font-size: 12px; 699 | -webkit-border-radius: 3px; 700 | -moz-border-radius: 3px; 701 | border-radius: 3px 702 | } 703 | .caption, caption { 704 | caption-side: bottom; 705 | background: 0 0; 706 | font-style: italic 707 | } 708 | tr.even td { 709 | background: #F4F4F4 710 | } 711 | tfoot td { 712 | border-top: 1px solid #EAEAEA; 713 | font-weight: 700; 714 | font-style: normal 715 | } 716 | abbr, acronym { 717 | border: 0 718 | } 719 | abbr[title]:hover { 720 | border-bottom: 1px dotted #666; 721 | cursor: help 722 | } 723 | .red { 724 | background: red 725 | } 726 | .highlight { 727 | background: #EEC3C3 728 | } 729 | .blocks:after, .clearfix:after, .container:after, .edition_list:after, .editions:after, .inner:after { 730 | content: "\0020"; 731 | display: block; 732 | height: 0; 733 | clear: both; 734 | visibility: hidden; 735 | overflow: hidden 736 | } 737 | .blocks, .clearfix, .container, .edition_list, .editions, .inner { 738 | display: block 739 | } 740 | .clear { 741 | clear: both 742 | } 743 | .container { 744 | max-width: 940px; 745 | margin: 0 auto; 746 | padding: 0 16px; 747 | text-align: left 748 | } 749 | .gttr { 750 | margin-right: 2.9%; 751 | float: left 752 | } 753 | .last { 754 | float: left 755 | } 756 | .sidebar { 757 | width: 32%; 758 | float: right; 759 | margin-right: 0 760 | } 761 | .content_footer { 762 | clear: both 763 | } 764 | .span10, .span2, .span3, .span4, .span6, .span8 { 765 | margin-right: 2.3%; 766 | float: left 767 | } 768 | .span2 { 769 | width: 14.6341% 770 | } 771 | .span3 { 772 | width: 23.1707% 773 | } 774 | .span4 { 775 | width: 31.7073% 776 | } 777 | .span6 { 778 | width: 48.7805% 779 | } 780 | .span8 { 781 | width: 65.853658% 782 | } 783 | .span10 { 784 | width: 73.17073% 785 | } 786 | .blocks, .editions { 787 | margin: 1.5em 0; 788 | clear: both 789 | } 790 | .editions { 791 | margin-bottom: 0 792 | } 793 | .last { 794 | margin-right: 0 !important 795 | } 796 | .lyt_img { 797 | max-width: 100% 798 | } 799 | .page_header { 800 | overflow: hidden; 801 | padding: 0 0 .2em; 802 | margin: 1.5em 0 803 | } 804 | .logo { 805 | width: 49.5%; 806 | float: left; 807 | margin-bottom: 0 808 | } 809 | .logo span { 810 | display: block; 811 | font-style: italic; 812 | font-size: .5em 813 | } 814 | .logo a, .logo a:link, .logo a:visited { 815 | color: #000 816 | } 817 | .logo a:active, .logo a:focus, .logo a:hover { 818 | color: #906; 819 | text-decoration: none 820 | } 821 | .search_box { 822 | width: 50%; 823 | float: right 824 | } 825 | .search { 826 | width: auto; 827 | float: right; 828 | vertical-align: middle 829 | } 830 | .edition_list, .info_bubble, .inner, .stage { 831 | padding: 1em 1.5em; 832 | background: #FFF; 833 | -moz-border-radius: 3px; 834 | -webkit-border-radius: 3px; 835 | border-radius: 3px; 836 | -moz-box-shadow: 0 0 8px #999; 837 | -webkit-box-shadow: 0 0 8px #999; 838 | box-shadow: 0 0 8px #999 839 | } 840 | .stage { 841 | margin-bottom: 1.5em; 842 | padding: 3em 50% 1.5em 3em 843 | } 844 | .edition_list { 845 | margin: 0 0 1em; 846 | clear: both; 847 | overflow: hidden 848 | } 849 | .edition_list p { 850 | margin-bottom: 0; 851 | font-style: italic 852 | } 853 | .edition_list h2 { 854 | margin-bottom: 0; 855 | font-size: 1.8em; 856 | line-height: 1.5; 857 | font-weight: 700 858 | } 859 | .edition_list ul { 860 | margin: 0; 861 | padding: 0; 862 | list-style: none; 863 | display: inline 864 | } 865 | .edition_list ul li { 866 | display: inline; 867 | margin-right: 1em; 868 | padding-right: 1em; 869 | border-right: 1px solid #F6f6F6 870 | } 871 | .edition_list ul li:last-child { 872 | margin-right: 0; 873 | padding-right: 0; 874 | border-right: 0 875 | } 876 | .info_bubble { 877 | padding-bottom: 1em; 878 | color: #FFF; 879 | text-shadow: 1px 1px 0 #000; 880 | background: #906; 881 | position: relative; 882 | overflow: visible 883 | } 884 | .info_bubble p { 885 | margin-bottom: 0 886 | } 887 | .info_bubble.startpage { 888 | padding-bottom: 1em; 889 | min-height: 9em; 890 | height: auto !important; 891 | height: 9em; 892 | z-index: 0 893 | } 894 | .info_bubble.startpage span { 895 | position: absolute; 896 | display: block; 897 | bottom: 1em; 898 | left: -2em; 899 | width: 40px; 900 | height: 40px; 901 | z-index: 100 902 | } 903 | .blocks>div { 904 | margin-bottom: 1.5em 905 | } 906 | .footer { 907 | margin: 1.5em 0 0; 908 | padding: 1.5em 0 0; 909 | overflow: hidden 910 | } 911 | #buy { 912 | position: relative 913 | } 914 | #offers { 915 | position: absolute; 916 | bottom: 0; 917 | font-size: .625em; 918 | margin: 0; 919 | padding: 0; 920 | list-style: none; 921 | display: none 922 | } 923 | .save_a_tree h3 { 924 | display: none; 925 | line-height: 1.5; 926 | border-bottom: 0 927 | } 928 | #offers li { 929 | display: inline; 930 | margin-right: 1em; 931 | padding-right: 1em; 932 | border-right: 1px solid #F6f6F6 933 | } 934 | #offers li:last-child { 935 | margin-right: 0; 936 | padding-right: 0; 937 | border-right: 0 938 | } 939 | .shopping_cart_button_line, .shopping_cart_button_line_bottom { 940 | border-top: 1px solid #CCC; 941 | margin-top: .5em; 942 | padding: .5em 1px 0; 943 | overflow: hidden; 944 | clear: both 945 | } 946 | .shopping_cart_button_line_bottom { 947 | margin-bottom: 7em 948 | } 949 | .shopping_cart_button_line:first-child { 950 | border-top: 0 951 | } 952 | div.header { 953 | font-size: 1.5em; 954 | line-height: 1; 955 | border-bottom: 1px solid #CCC; 956 | margin-bottom: .5em; 957 | padding-bottom: .5em; 958 | color: #000; 959 | text-shadow: 1px 1px 0 #FFF 960 | } 961 | .product_title { 962 | font-style: italic; 963 | margin-bottom: 1.5em 964 | } 965 | .product_price { 966 | float: left 967 | } 968 | .price { 969 | color: #666 970 | } 971 | .product_buy_link { 972 | float: right; 973 | line-height: 1 974 | } 975 | .ebook_formats, .safari_read_now, .whatisthis { 976 | font-size: .75em 977 | } 978 | .safari_read_now { 979 | float: right; 980 | line-height: 2 981 | } 982 | input[type=submit], input[type=text] { 983 | font-size: 1em; 984 | -moz-border-radius: .5em; 985 | -webkit-border-radius: .5em; 986 | border-radius: .5em; 987 | vertical-align: middle 988 | } 989 | input[type=text] { 990 | width: 140px; 991 | margin: .5em 0; 992 | padding: .5em .5em .3em; 993 | border: 1px solid #F1F1F0; 994 | color: #666; 995 | -moz-box-shadow: inset 3px 3px 2px #999; 996 | -webkit-box-shadow: inset 3px 3px 2px #999; 997 | box-shadow: inset 3px 3px 2px #999 998 | } 999 | input[type=text]:active, input[type=text]:focus, input[type=text]:hover { 1000 | background: #FFF; 1001 | color: #000; 1002 | outline: 0 1003 | } 1004 | input[type=submit] { 1005 | width: auto; 1006 | margin: 0; 1007 | padding: .28em .5em; 1008 | background: #906; 1009 | color: #FFF; 1010 | border: 0; 1011 | text-transform: uppercase; 1012 | cursor: pointer; 1013 | text-shadow: 1px 1px 0 #000; 1014 | -moz-box-shadow: 0 0 2px #999; 1015 | -webkit-box-shadow: 0 0 2px #999; 1016 | box-shadow: 0 0 2px #999 1017 | } 1018 | input[type=submit]:active, input[type=submit]:focus, input[type=submit]:hover { 1019 | color: #FFF; 1020 | background: #E106B2; 1021 | text-shadow: 1px 1px 0 #000; 1022 | -moz-box-shadow: 0 0 3px #333; 1023 | -webkit-box-shadow: 0 0 3px #333; 1024 | box-shadow: 0 0 3px #333 1025 | } 1026 | a.anchor { 1027 | color: #000 1028 | } 1029 | a.anchor:hover { 1030 | text-decoration: none 1031 | } 1032 | .figure { 1033 | padding-top: 1.5em 1034 | } 1035 | .caption, .figure { 1036 | display: block; 1037 | margin-bottom: 1.5em; 1038 | text-align: center 1039 | } 1040 | .figure * { 1041 | text-align: left 1042 | } 1043 | .caption { 1044 | font-size: .875em 1045 | } 1046 | .figure .caption { 1047 | text-align: center 1048 | } 1049 | .figure img { 1050 | margin-bottom: .5em; 1051 | max-width: 100% 1052 | } 1053 | .attribution::before { 1054 | content: "" 1055 | } 1056 | .sidebar ul { 1057 | font-size: .9em; 1058 | padding-left: 2em; 1059 | list-style-type: square 1060 | } 1061 | .disabled, .footer a, .footer a:active, .footer a:focus, .footer a:hover, .footer a:link, .footer a:visited, .info_bubble a, .info_bubble a:active, .info_bubble a:focus, .info_bubble a:hover, .info_bubble a:link, .info_bubble a:visited { 1062 | color: #BBB 1063 | } 1064 | .buybuttonswidget { 1065 | padding-bottom: 10px; 1066 | background: #FFF 1067 | } 1068 | @media (max-width:320px) { 1069 | .container { 1070 | max-width: 300px 1071 | } 1072 | #reviews { 1073 | display: none 1074 | } 1075 | } 1076 | @media (min-width:768px) and (max-width:1024px) { 1077 | .container { 1078 | max-width: 740px; 1079 | width: 740px 1080 | } 1081 | } 1082 | @media (min-width:1025px) { 1083 | .container { 1084 | width: 740px 1085 | } 1086 | } -------------------------------------------------------------------------------- /epub.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h2, h3, h4, h5, h6, p, a, em, strong, b, u, i, pre, code, del, strike, abbr, acronym, address, q, cite, blockquote, big, small, sub, sup, tt, var, center, img, dfn, ins, kbd, s, samp, dl, dt, dd, ol, ul, li, fieldset, legend, label, table, caption, tbody, tfoot, thead, tr, th, td { 2 | margin: 0; 3 | padding: 0; 4 | border: 0; 5 | outline: 0; 6 | font-size: 100%; 7 | vertical-align: baseline; 8 | background: transparent; 9 | } 10 | @page { 11 | margin: 5px; 12 | } 13 | p { 14 | margin-bottom: 9px; 15 | line-height: 1.4; 16 | } 17 | a { 18 | color: #0069d6; 19 | } 20 | a:hover { 21 | color: #0050a3; 22 | text-decoration: none; 23 | } 24 | a img { 25 | border: none; 26 | } 27 | h1, h2, h3, h4, h5, h6 { 28 | color: #404040; 29 | line-height: 1.5; 30 | margin: 1em 0 0.5em; 31 | -webkit-hyphens: none; 32 | hyphens: none; 33 | adobe-hyphenate: none; 34 | } 35 | h1 { 36 | font-size: 220%; 37 | margin-bottom: 1.5em; 38 | } 39 | h2 { 40 | font-size: 190%; 41 | } 42 | h3 { 43 | font-size: 160%; 44 | } 45 | h4 { 46 | font-size: 140%; 47 | } 48 | h5 { 49 | font-size: 130%; 50 | } 51 | h6 { 52 | font-size: 120%; 53 | } 54 | hr { 55 | margin: 0 0 19px; 56 | border: 0; 57 | border-bottom: 1px solid #ccc; 58 | } 59 | blockquote { 60 | padding: 13px 13px 21px 15px; 61 | margin-bottom: 18px; 62 | font-family: georgia, serif; 63 | font-style: italic; 64 | } 65 | blockquote:before { 66 | content: "\201C"; 67 | font-size: 300%; 68 | margin-left: -10px; 69 | font-family: serif; 70 | color: #eee; 71 | } 72 | blockquote p { 73 | font-size: 120%; 74 | margin-bottom: 0; 75 | font-style: italic; 76 | } 77 | code, pre { 78 | font-family: monospace; 79 | } 80 | code { 81 | background-color: #fee9cc; 82 | color: rgba(0, 0, 0, 0.75); 83 | padding: 1px 3px; 84 | -webkit-border-radius: 5px; 85 | border-radius: 5px; 86 | font-size: 85%; 87 | } 88 | pre { 89 | display: block; 90 | padding: 14px; 91 | margin: 0 0 18px; 92 | font-size: 85%; 93 | line-height: 1.3; 94 | border: 1px solid #d9d9d9; 95 | white-space: pre-wrap; 96 | word-wrap: break-word; 97 | -webkit-hyphens: none; 98 | hyphens: none; 99 | adobe-hyphenate: none; 100 | } 101 | pre code { 102 | background-color: #fff; 103 | color: #737373; 104 | padding: 0; 105 | } 106 | code.sourceCode span.kw { 107 | color: #007020; 108 | font-weight: bold; 109 | } 110 | code.sourceCode span.dt { 111 | color: #902000; 112 | } 113 | code.sourceCode span.dv { 114 | color: #40a070; 115 | } 116 | code.sourceCode span.bn { 117 | color: #40a070; 118 | } 119 | code.sourceCode span.fl { 120 | color: #40a070; 121 | } 122 | code.sourceCode span.ch { 123 | color: #4070a0; 124 | } 125 | code.sourceCode span.st { 126 | color: #4070a0; 127 | } 128 | code.sourceCode span.co { 129 | color: #60a0b0; 130 | font-style: italic; 131 | } 132 | code.sourceCode span.ot { 133 | color: #007020; 134 | } 135 | code.sourceCode span.al { 136 | color: red; 137 | font-weight: bold; 138 | } 139 | code.sourceCode span.fu { 140 | color: #06287e; 141 | } 142 | code.sourceCode span.re { 143 | } 144 | code.sourceCode span.er { 145 | color: red; 146 | font-weight: bold; 147 | } 148 | body { 149 | font-family: serif; 150 | } -------------------------------------------------------------------------------- /images/arduino/add-zip-library.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/images/arduino/add-zip-library.png -------------------------------------------------------------------------------- /images/arduino/arduino-5110-connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/images/arduino/arduino-5110-connection.png -------------------------------------------------------------------------------- /images/arduino/arduino-button.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/images/arduino/arduino-button.jpg -------------------------------------------------------------------------------- /images/arduino/arduino-ethernet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/images/arduino/arduino-ethernet.png -------------------------------------------------------------------------------- /images/arduino/arduino-hs-sr04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/images/arduino/arduino-hs-sr04.png -------------------------------------------------------------------------------- /images/arduino/arduino-install-package.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/images/arduino/arduino-install-package.png -------------------------------------------------------------------------------- /images/arduino/arduino-nokia-5110.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/images/arduino/arduino-nokia-5110.png -------------------------------------------------------------------------------- /images/arduino/arduinos.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/images/arduino/arduinos.jpg -------------------------------------------------------------------------------- /images/arduino/download-zip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/images/arduino/download-zip.png -------------------------------------------------------------------------------- /images/arduino/fritzing-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/images/arduino/fritzing-button.png -------------------------------------------------------------------------------- /images/arduino/fritzing-example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/images/arduino/fritzing-example.jpg -------------------------------------------------------------------------------- /images/arduino/manage-library.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/images/arduino/manage-library.png -------------------------------------------------------------------------------- /images/arduino/serial-output-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/images/arduino/serial-output-example.png -------------------------------------------------------------------------------- /images/arduino/shields.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/images/arduino/shields.jpg -------------------------------------------------------------------------------- /img/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/img/cover.jpg -------------------------------------------------------------------------------- /img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/make/6cddf97876acd73f9371e2b409d0f8b8c76e9036/img/favicon.ico -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 硬件开发指南 – 8 | 9 | 46 | 47 | 50 | 51 | 52 | 53 |

写给软件工程师看的硬件编程指南

54 |

By Phodal (Follow Me: 微博知乎SegmentFault) 55 |

56 | 57 |

我的其他电子书:

58 | 67 | 68 |

微信公众号

69 |

70 |

71 | 当前为预览版,在使用的过程中遇到任何遇到请及时与我联系。阅读过程中问题,不烦在GitHub上提出来: 72 | Issues 73 |

74 |

75 | 阅读过程中遇到语法错误、拼写错误、技术错误等等,不烦来个Pull Request,这样可以帮助到其他阅读这本电子书的童鞋。 76 |

77 |
78 | 196 |

前言

197 |

最近这些日子里,当我和女朋友搬到了新的合租屋里,有了更大的空间——可以再次折腾硬件。便开始去玩一些硬件家居相关的内容,也顺便接上了 Amazon Echo、小米传感器、Broadlink、ESP8266 等等的硬件。

198 |

再加上正在编写的『玩点什么』网站,便也想整理一下硬件相关的资料。想着想着,便有了这一系列的电子书。

199 |

这是一本写给软件工程师看的硬件指南。

200 |

关于作者

201 |

黄峰达(Phodal Huang)是一个创客、工程师、咨询师和作家。他毕业于西安文理学院电子信息工程专业,现作为一个咨询师就职于 ThoughtWorks 深圳。长期活跃于开源软件社区 GitHub,目前专注于物联网和前端领域。

202 |

作为一个开源软件作者,著有 Growth、Stepping、Lan、Echoesworks 等软件。其中开源学习应用 Growth,广受读者和用户好评,可在 APP Store 及各大 Android 应用商店下载。

203 |

作为一个技术作者,著有《自己动手设计物联网》(电子工业出版社)、《全栈应用开发:精益实践》(电子工业出版社,正在出版)。并在 GitHub 上开源有《Growth: 全栈增长工程师指南》、《GitHub 漫游指南》等七本电子书。

204 |

作为技术专家,他为英国 Packt 出版社审阅有物联网书籍《Learning IoT》、《Smart IoT》,前端书籍《Angular 2 Serices》、《Getting started with Angular》等技术书籍。

205 |

他热爱编程、写作、设计、旅行、hacking,你可以从他的个人网站:https://www.phodal.com/ 了解到更多的内容。

206 |

其它相关信息:

207 | 213 |

当前为预览版,在使用的过程中遇到任何问题请及时与我联系。阅读过程中的问题,不妨在GitHub上提出来: Issues

214 |

阅读过程中遇到语法错误、拼写错误、技术错误等等,不妨来个Pull Request,这样可以帮助到其他阅读这本电子书的童鞋。

215 |

其他电子书:

216 | 225 |

基础篇

226 |

Arduino vs ESP8266 vs Raspberry Pi

227 |

在这一系列的文章里,我们使用三个主要的开发板:

228 |
    229 |
  • Arduino。
  • 230 |
  • ESP8266。
  • 231 |
  • Raspberry Pi。
  • 232 |
233 |

这三个硬件分别代表着,三种不同类型的硬件爱好者的开发板。

234 |

Arduino

235 |
236 |

Arduino,是一个开放源代码的单片机微控制器,它使用了Atmel AVR单片机,采用了开放源代码的软硬件平台,建构于简易输出/输入(simple I/O)接口板,并且具有使用类似Java、C语言的Processing/Wiring开发环境。1

237 |
238 |

其包含了以下的特性2:

239 |
    240 |
  • 基于知识共享开放源代码的电路图设计。
  • 241 |
  • 免费下载,也可依需求自己修改,但需遵照姓名标示。您必须按照作者或授权人所指定的方式,表彰其姓名。
  • 242 |
  • 依相同方式分享,若您改变或转变著作,当散布该派生著作时,您需采用与本著作相同或类似的授权条款。
  • 243 |
  • Arduino 可使用 ICSP 在线烧入器,将 Bootloader 烧入新的 IC 芯片。
  • 244 |
  • 可依据 Arduino 官方网站,获取硬件的设计档,加以调整电路板及组件,以匹配自己实际设计的需求
  • 245 |
  • 可简单地与感测器,各式各样的电子组件连接,如红外线、超音波、热敏电阻、光敏电阻、伺服马达…等。
  • 246 |
  • 支持多样的交互程序,如Adobe Flash, Max/MSP, VVVV, Pure Data, C, Processing…等。
  • 247 |
  • 使用低价格的微处理控制器(Atmel AVR)(ATMEGA 8,168,328等)。
  • 248 |
  • USB接口,不需外接电源。另外有提供直流(DC)电源输入。
  • 249 |
250 |

当然,其最大的特色是,完善的社区及生态系统。几乎我们能想到的 Arduino 相关的创意,都可以在网上找到。如果没有的话,那么可能是你的创意不适合用于 Arduino。

251 |

Arduino 已然有一系列的相关硬件,就目前而言,最广泛的开发板,或者说是标准开发板是 Arduino UNO。

252 |

TODO: 相关 Arduino 硬件介绍

253 |

Arduino 最吸引人的是开创性的引入电子积木的概念,即开发板的扩展板可以直接叠加在开发板上使用,而无需额外的硬件。

254 |

TODO: 扩展板介绍。

255 |

Arduion IDE 基于 Processing 开发环境而开发的。

256 |

概念

257 |

引脚

258 |

模电

259 |

数电

260 |

电流、电阻、电压

261 |

etc..

262 |

PCB

263 |

为了更好的向读者展示硬件连线,我们将使用 Fritzing 汇制硬件电路图。

264 |

Fritzing

265 |
266 |

Fritzing 是一个开源的硬件计划(initiative),它可以使电子元件变成任何人的创意素材。Fritzing 提供一个软件工具、一个社区网站,以及本着 Processing 和 Arduino 精神的服务,培养创造性的生态系统,允许用户记录他们的原型,与他人分享、用于课堂上的电子相关教学,以及布局和制造专业的pcb。

267 |
268 |

Fritzing 的官网是:http://fritzing.org/

269 |

因此,在开始之前我们需要在 Fritzing 的官网下载 Fritzing。当前它可以支持主流的操作系统:Windows、Linux、macOS,当然如果你想自己从源码编译一个 Fritzing,那也是可以的。除了中文和英语外,它还支持其它 17 种语言。

270 |

在其官网,除了能下载到该软件,还可以看到其它用户上传、汇制的 Fritzing 电路图等,它们可以免费下载,并引用到你的项目中。

271 |

LCEDA

272 |

硬件

273 |

Nokia 5110

274 |

温度、红外、距离、土壤

275 |

MCU vs CPU

276 |

Input -> MCU -> Output

277 |

通讯

278 |

蓝牙、有线、

279 |

专用协议,blabla

280 |

第一部分 Arduino 篇

281 |
    282 |
  • Arduino + LED + 按钮控制
  • 283 |
  • Arduino 读取传感器
  • 284 |
  • Arduion + LCD 5100 显示传感器
  • 285 |
  • Arduino 蓝牙通讯
  • 286 |
  • Arduino Processing 可视化数据
  • 287 |
  • Arduino 上传数据
  • 288 |
  • Arduino 的可能性:+ Fritzing / Arduino + Johnny-Five
  • 289 |
290 |

Arduino Hello, World

291 |

幸运的是,今天对硬件编程已经有了更多的方式,可以使用更高级的语言。除了在 Arduion 上使用 Processing 语言(可以视为 C)进行编程,还可以使用 JavaScript 等语言。我们会在后面的介绍:如何使用这些高级的语言来编程。

292 |

开发板选择

293 |
294 | Arduino 开发板示例
Arduino 开发板示例
295 |
296 |
297 | 扩展板
扩展板
298 |
299 |

Arduino IDE 默认会安装相应环境的包,如果你的开发板不在这些环境里,如你使用的是 Arduino M0 开发板。那么,系统可能会检测出来,并自动为你安装相应的环境,如下图所示。

300 |
301 | Arduino IDE 安装 Arduino M0 环境
Arduino IDE 安装 Arduino M0 环境
302 |
303 |

注意:安装过程中会下载一系列的工具,在这个过程里,可能会遇到一些“网络问题”。请尝试更换网络,或直到到“墙”的那外一边。

304 |

对于非官方的开发板来说,则需要开发者自己手动通过『开发板管理器』来安装,如 ESP8266 开发板。

305 |

hello, world

306 |

与桌面端使用 print、puts、console.log 来输出 Hello, world 不同的是,点亮一个 LED 是 电子世界的 Hello, world。

307 |

在硬件世界里,没有 stdlib.h 这样的库,对于多数嵌入式系统来说,也没有屏幕。在这个时候,想要知道设备的状态,最简单的方式就是 LED。常见的,如路由器、电视等家电电器上的灯,都是嵌入式设备的状态。

308 |

官方的 Blink 示例如下:

309 |
void setup() {
310 |   // initialize digital pin LED_BUILTIN as an output.
311 |   pinMode(LED_BUILTIN, OUTPUT);
312 | }
313 | 
314 | // the loop function runs over and over again forever
315 | void loop() {
316 |   digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
317 |   delay(1000);                       // wait for a second
318 |   digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
319 |   delay(1000);                       // wait for a second
320 | }
321 |

接下来,要做的事情就比较简单了。从工具( Tools )中选择对应的开发板和端口,如下图所示:

322 |
323 | Arduino 选择设备
Arduino 选择设备
324 |
325 |

我这里使用设置是 Arduino M0 Pro,对应的端口是:/dev/tty.usbmodem1412

326 |

对于 Windows 用户来说,则是 COM 口——从设备管理器中找到对应的端口。

327 |

然后点击菜单中的 -> 号,即上传(Upload,又可以称为烧录)。

328 |
329 | Arduino 菜单
Arduino 菜单
330 |
331 |

再看看你的 Arduion,就发现上面的 LED 正在闪烁——一秒开一秒关。

332 |

setup()

333 |

源码中的:

334 |
int main(void)
335 | {
336 |     init();
337 |     setup();
338 |     for(;;)
339 |         loop();
340 | 
341 |     return 0;
342 | }
343 |

loop()

344 |

输入控制输出

345 |

GOAL

346 |

SMART 原则

347 |

仍然采用的是官方的示例:

348 |
349 | Arduino 控制 LED 示例
Arduino 控制 LED 示例
350 |
351 |

官方示例(Digital Read Serial)代码:

352 |
// digital pin 2 has a pushbutton attached to it. Give it a name:
353 | int pushButton = 2;
354 | 
355 | // the setup routine runs once when you press reset:
356 | void setup() {
357 |   // initialize serial communication at 9600 bits per second:
358 |   Serial.begin(9600);
359 |   // make the pushbutton's pin an input:
360 |   pinMode(pushButton, INPUT);
361 | }
362 | 
363 | // the loop routine runs over and over again forever:
364 | void loop() {
365 |   // read the input pin:
366 |   int buttonState = digitalRead(pushButton);
367 |   // print out the state of the button:
368 |   Serial.println(buttonState);
369 |   delay(1);        // delay in between reads for stability
370 | }
371 |

电路图

372 |

使用 Fritzing 画板时

373 |
374 | 375 |
376 |

我们所需要的连线图

377 |
378 | 379 |
380 |

Arduino 传感器控制

381 |

传感器入门

382 |
383 |

传感器是一种物理装置或生物器官,能够探测、感受外界的信号、物理条件(如光、热、湿度)或化学组成(如烟雾),并将探知的信息传递给其他装置或器官。“传感器”在新韦式大词典中定义为:“从一个系统接受功率,通常以另一种形式将功率送到第二个系统中的器件”。根据这个定义,传感器的作用是将一种能量转换成另一种能量形式,所以不少学者也用“换能器-Transducer”来称谓“传感器-Sensor”。[^wiki_sensor]

384 |
385 |

[^wiki_sensor] : https://zh.wikipedia.org/wiki/%E4%BC%A0%E6%84%9F%E5%99%A8

386 |

模拟元件与数字元件

387 |

超声波传感器

388 |
389 | Arduion 超声波传感器连线图
Arduion 超声波传感器连线图
390 |
391 |

使用到库:[NewPing],GitHub 地址:https://github.com/PaulStoffregen/NewPing

392 |
393 | 下载 Zip 格式
下载 Zip 格式
394 |
395 |

添加库

396 |
397 | 添加 NewPing 库
添加 NewPing 库
398 |
399 |
#include <NewPing.h>
400 | 
401 | #define TRIGGER_PIN  12  // Arduino pin tied to trigger pin on the ultrasonic sensor.
402 | #define ECHO_PIN     11  // Arduino pin tied to echo pin on the ultrasonic sensor.
403 | #define MAX_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
404 | 
405 | NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.
406 | 
407 | void setup() {
408 |   Serial.begin(115200); // Open serial monitor at 115200 baud to see ping results.
409 | }
410 | 
411 | void loop() {
412 |   delay(50);                     // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings.
413 |   Serial.print("Ping: ");
414 |   Serial.print(sonar.ping_cm()); // Send ping, get distance in cm and print result (0 = outside set distance range)
415 |   Serial.println("cm");
416 | }
417 |
418 | 输出示例
输出示例
419 |
420 |

控制继电器

421 |

Arduino 5110 显示

422 |

LCD

423 |

安装库

424 |

项目地址:Adafruit PCD8544

425 |
426 | 添加库
添加库
427 |
428 |
429 | Arduino IDE 安装 5110
Arduino IDE 安装 5110
430 |
431 |

一般来说,在安装完库后,都会有对应的示例。但是,这里的示例,稍微有一些复杂。

432 |
#include <Arduino.h>
433 | 
434 | #include <SPI.h>
435 | #include <Adafruit_GFX.h>
436 | #include <Adafruit_PCD8544.h>
437 | 
438 | // Pins
439 | const int8_t RST_PIN = D2;
440 | const int8_t CE_PIN = D1;
441 | const int8_t DC_PIN = D6;
442 | //const int8_t DIN_PIN = D7;  // Uncomment for Software SPI
443 | //const int8_t CLK_PIN = D5;  // Uncomment for Software SPI
444 | const int8_t BL_PIN = D0;
445 | 
446 | 
447 | // Software SPI with explicit CE pin.
448 | // Adafruit_PCD8544 display = Adafruit_PCD8544(CLK_PIN, DIN_PIN, DC_PIN, CE_PIN, RST_PIN);
449 | 
450 | // Software SPI with CE tied to ground. Saves a pin but other pins can't be shared with other hardware.
451 | // Adafruit_PCD8544(int8_t CLK_PIN, int8_t DIN_PIN, int8_t DC_PIN, int8_t RST_PIN);
452 | 
453 | // Hardware SPI based on hardware controlled SCK (SCLK) and MOSI (DIN) pins. CE is still controlled by any IO pin.
454 | // NOTE: MISO and SS will be set as an input and output respectively, so be careful sharing those pins!
455 | Adafruit_PCD8544 display = Adafruit_PCD8544(DC_PIN, CE_PIN, RST_PIN);
456 | 
457 | void setup() {
458 |   Serial.begin(9600);
459 |   Serial.println("\n\nWeMos D1 Mini + Nokia 5110 PCD8544 84x48 Monochrome LCD\nUsing Adafruit_PCD8544 and Adafruit_GFX libraries\n");
460 | 
461 |   // Turn LCD backlight on
462 |   pinMode(BL_PIN, OUTPUT);
463 |   digitalWrite(BL_PIN, HIGH);
464 | 
465 |   display.begin();
466 |   display.setContrast(60);  // Adjust for your display
467 |   Serial.println("Show Adafruit logo bitmap");
468 | 
469 |   // Show the Adafruit logo, which is preloaded into the buffer by their library
470 |   // display.clearDisplay();
471 |   delay(2000);
472 | 
473 |   display.clearDisplay();
474 |   display.setTextSize(1);
475 |   display.setTextColor(BLACK);
476 |   display.setCursor(0,0);
477 |   display.println("Hello, world!");
478 |   display.display();
479 | }
480 | 
481 | void loop() {
482 | }
483 |

输出 hello, world

484 |

Arduino Processing

485 |

串口输出

486 |

复杂的例子:MPU 6050

487 |

Arduino BLE 控制

488 |

蓝牙串口

489 |

Bluno 等自带蓝牙的开发板

490 |

与手机通讯

491 |

React Native / Cordova ???

492 |

Arduino 扩展板使用

493 |

I don’t know

494 |

继电器

495 |

网络

496 |
497 | Arduion 以太网扩展板
Arduion 以太网扩展板
498 |
499 |

Arduino ESP8266 WiFi

500 |

ESP8266 设置

501 |

Arduino WiFi

502 |

第二部分 ESP8266

503 |
    504 |
  • ESP8266 基础 + 连接 WiFi / NodeMCU
  • 505 |
  • ESP8266 Arduino IDE ??
  • 506 |
  • ESP8266 上传 Arduino 数据
  • 507 |
  • ? ESP8266 读取传感器(ADC?)),并上传数据
  • 508 |
  • ESP8266 MQTT 示例
  • 509 |
  • ESP8266 作为 HTTP 服务器示例
  • 510 |
  • 更多的可能性探索:Mongoose OS、Johnny-Five、智能家居
  • 511 |
512 |

ESP8266 基础

513 |

设置与 AT 指令

514 |

NodeMCU

515 |

ESP8266 Arduino IDE 展示

516 |

ESP8266 连接 Arduino UNO

517 |

ESP8266 上传传感器数据

518 |

ADC 数据

519 |

发送请求

520 |
/**
521 |  * BasicHTTPClient.ino
522 |  *
523 |  *  Created on: 24.05.2015
524 |  *
525 |  */
526 | 
527 | #include <Arduino.h>
528 | 
529 | #include <ESP8266WiFi.h>
530 | #include <ESP8266WiFiMulti.h>
531 | 
532 | #include <ESP8266HTTPClient.h>
533 | 
534 | #define USE_SERIAL Serial
535 | 
536 | ESP8266WiFiMulti WiFiMulti;
537 | 
538 | void setup() {
539 | 
540 |     USE_SERIAL.begin(115200);
541 |    // USE_SERIAL.setDebugOutput(true);
542 | 
543 |     USE_SERIAL.println();
544 |     USE_SERIAL.println();
545 |     USE_SERIAL.println();
546 | 
547 |     for(uint8_t t = 4; t > 0; t--) {
548 |         USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
549 |         USE_SERIAL.flush();
550 |         delay(1000);
551 |     }
552 | 
553 |     WiFiMulti.addAP("SSID", "PASSWORD");
554 | 
555 | }
556 | 
557 | void loop() {
558 |     // wait for WiFi connection
559 |     if((WiFiMulti.run() == WL_CONNECTED)) {
560 | 
561 |         HTTPClient http;
562 | 
563 |         USE_SERIAL.print("[HTTP] begin...\n");
564 |         // configure traged server and url
565 |         //http.begin("https://192.168.1.12/test.html", "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS
566 |         http.begin("http://192.168.1.12/test.html"); //HTTP
567 | 
568 |         USE_SERIAL.print("[HTTP] GET...\n");
569 |         // start connection and send HTTP header
570 |         int httpCode = http.GET();
571 | 
572 |         // httpCode will be negative on error
573 |         if(httpCode > 0) {
574 |             // HTTP header has been send and Server response header has been handled
575 |             USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode);
576 | 
577 |             // file found at server
578 |             if(httpCode == HTTP_CODE_OK) {
579 |                 String payload = http.getString();
580 |                 USE_SERIAL.println(payload);
581 |             }
582 |         } else {
583 |             USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
584 |         }
585 | 
586 |         http.end();
587 |     }
588 | 
589 |     delay(10000);
590 | }
591 |

上传

592 |

ESP8266 MQTT 控制

593 |

MQTT

594 |

moquitto

595 |

596 |

https://github.com/tuanpmt/esp_mqtt

597 |

ESP8266 HTTP 服务器

598 |

Simple HTTP Server

599 |
#include <ESP8266WiFi.h>
600 | #include <ESP8266WebServer.h>
601 |  
602 | ESP8266WebServer server(80);
603 |  
604 | void setup() {
605 |  
606 |   Serial.begin(115200);
607 |   WiFi.begin("Network name", "Password");  //Connect to the WiFi network
608 |  
609 |   while (WiFi.status() != WL_CONNECTED) {  //Wait for connection
610 |     delay(500);
611 |     Serial.println("Waiting to connect…");
612 |   }
613 |  
614 |   Serial.print("IP address: ");
615 |   Serial.println(WiFi.localIP());  //Print the local IP
616 |  
617 |   server.on(" / other", []() {   //Define the handling function for the path
618 |  
619 |     server.send(200, "text / plain", "Other URL");
620 |  
621 |   });
622 |  
623 |   server.on(" / ", handleRootPath);    //Associate the handler function to the path
624 |   server.begin();                                       //Start the server
625 |   Serial.println("Server listening");
626 | }
627 |  
628 | void loop() {
629 |   server.handleClient();
630 | }
631 |  
632 | void handleRootPath() {
633 |   server.send(200, "text/plain", "Hello world"); 
634 | }
635 |

超越 ESP8266

636 |

第三部分 Raspberry Pi

637 |
    638 |
  • Hello, world!
  • 639 |
  • Node.js 服务 - 开机启动,blabla
  • 640 |
  • 连接 Arduino、自动编程? OTA ?
  • 641 |
  • Raspberry Pi as PC Camera + Sensors ???
  • 642 |
  • Raspberry Pi <-> ESP8266 / Node.js Example
  • 643 |
  • MQTT Server
  • 644 |
  • IoT Server + Database?
  • 645 |
646 |

Raspberry Pi Hello, world!

647 |

镜像烧录

648 |

SSH 连接

649 |

Python 控制 GPIO

650 |

Node.js 服务基础

651 |

安装 Node.js

652 |

Express Hello, world

653 |

开机启动

654 |

摄像头 + 传感器 = 智能家居?

655 |

红外传感器

656 |

视频流

657 |

拍照,blabla

658 |

与 ESP8266 无线通讯

659 |

建立服务

660 |

广播?

661 |

Arduino OTA 更新

662 |

服务端读取代码

663 |

arduino-cli 更新硬件

664 |

使用 MQTT 服务器

665 |

MQTT 客户端

666 |

使用 mosquittto

667 |

跟上潮流:Docker、硬件微服务

668 |

使用 Docker

669 |

硬件微服务

670 |

Home Assistant 搭建

671 |

Home Assistant 整合

672 |

Homekit 搭建

673 |

Homekit 整合

674 |

Raspberry Pi 与智能音箱

675 |

Raspberry Pi 自制智能音箱

676 |

超越 Raspberry Pi

677 |
678 |
679 |
    680 |
  1. 源自维基百科,https://zh.wikipedia.org/wiki/Arduino

  2. 681 |
  3. 源自维基百科,https://zh.wikipedia.org/wiki/Arduino

  4. 682 |
683 |
684 | 685 | 686 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | read -p "名书:" bookname 4 | read -p "作者:" author 5 | 6 | sed -i "s/Phodal Huang/$author/g" "build/author.html" "build/metadata.xml" "build/title.txt" "Makefile" 7 | sed -i "s/电子书模板/$bookname/g" "build/author.html" "build/metadata.xml" "build/title.txt" "Makefile" -------------------------------------------------------------------------------- /listings-setup.tex: -------------------------------------------------------------------------------- 1 | % Contents of listings-setup.tex 2 | \usepackage{xcolor} 3 | 4 | \lstset{ 5 | basicstyle=\ttfamily, 6 | numbers=left, 7 | keywordstyle=\color[rgb]{0.13,0.29,0.53}\bfseries, 8 | stringstyle=\color[rgb]{0.31,0.60,0.02}, 9 | commentstyle=\color[rgb]{0.56,0.35,0.01}\itshape, 10 | numberstyle=\footnotesize, 11 | stepnumber=1, 12 | numbersep=5pt, 13 | backgroundcolor=\color[RGB]{248,248,248}, 14 | showspaces=false, 15 | showstringspaces=false, 16 | showtabs=false, 17 | tabsize=2, 18 | captionpos=b, 19 | breaklines=true, 20 | breakatwhitespace=true, 21 | breakautoindent=true, 22 | escapeinside={\%*}{*)}, 23 | linewidth=\textwidth, 24 | basewidth=0.5em, 25 | } -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | h1, 2 | h2, 3 | h3, 4 | h4, 5 | h5, 6 | h6, 7 | p, 8 | blockquote { 9 | margin: 0; 10 | padding: 0; 11 | } 12 | 13 | body { 14 | font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", Arial, sans-serif; 15 | font-size: 18px; 16 | line-height: 1.4; 17 | color: #000000; 18 | margin: 10px 13px 10px 13px; 19 | } 20 | 21 | a { 22 | color: #0069d6; 23 | } 24 | 25 | a:hover { 26 | color: #0050a3; 27 | text-decoration: none; 28 | } 29 | 30 | a img { 31 | border: none; 32 | } 33 | 34 | p { 35 | text-indent: 2em; 36 | margin-bottom: 12px; 37 | } 38 | 39 | h1, 40 | h2, 41 | h3, 42 | h4, 43 | h5, 44 | h6 { 45 | color: #404040; 46 | line-height: 36px; 47 | } 48 | 49 | h1 { 50 | margin-bottom: 32px; 51 | margin-top: 32px; 52 | font-size: 30px; 53 | color: #0050a3; 54 | } 55 | 56 | h1 a { 57 | margin-top: 100px; 58 | display: block; 59 | } 60 | 61 | h2 { 62 | font-size: 24px; 63 | margin-bottom: 28px; 64 | margin-left: 12px; 65 | } 66 | 67 | h3 { 68 | font-size: 18px; 69 | margin-bottom: 24px; 70 | margin-left: 16px; 71 | } 72 | 73 | h4 { 74 | font-size: 16px; 75 | } 76 | 77 | h5 { 78 | font-size: 14px; 79 | } 80 | 81 | h6 { 82 | font-size: 13px; 83 | } 84 | 85 | hr { 86 | margin: 0 0 19px; 87 | border: 0; 88 | border-bottom: 1px solid #ccc; 89 | } 90 | 91 | blockquote { 92 | padding: 2px 13px 21px 2px; 93 | margin-bottom: 12px; 94 | font-family: georgia,serif; 95 | font-style: italic; 96 | } 97 | 98 | blockquote:before { 99 | content: "\201C"; 100 | font-size: 40px; 101 | margin-left: -10px; 102 | font-family: georgia,serif; 103 | color: #eee; 104 | } 105 | 106 | blockquote p { 107 | font-size: 14px; 108 | font-weight: 300; 109 | line-height: 18px; 110 | margin-bottom: 0; 111 | font-style: italic; 112 | } 113 | 114 | code, pre { 115 | font-family: Monaco, Andale Mono, Courier New, monospace; 116 | } 117 | 118 | code { 119 | background-color: #fee9cc; 120 | color: rgba(0, 0, 0, 0.75); 121 | padding: 1px 3px; 122 | font-size: 12px; 123 | -webkit-border-radius: 3px; 124 | -moz-border-radius: 3px; 125 | border-radius: 3px; 126 | } 127 | 128 | pre { 129 | display: block; 130 | padding: 14px; 131 | margin: 0 0 18px; 132 | line-height: 16px; 133 | font-size: 11px; 134 | border: 1px solid #d9d9d9; 135 | white-space: pre-line; 136 | word-wrap: break-word; 137 | } 138 | 139 | pre code { 140 | background-color: transparent; 141 | color: #737373; 142 | font-size: 11px; 143 | padding: 0; 144 | white-space: pre-wrap; 145 | } 146 | 147 | 148 | figure img { 149 | display: block; 150 | margin: 0 auto; 151 | max-width: 100%; 152 | } 153 | 154 | figcaption { 155 | text-align: center; 156 | } 157 | 158 | ul li a { 159 | font-weight: bold; 160 | } 161 | 162 | ul li ul a { 163 | font-weight: normal; 164 | } 165 | 166 | @media screen and (min-width: 768px) { 167 | body { 168 | width: 748px; 169 | margin: 40px auto; 170 | } 171 | } 172 | 173 | 174 | table { 175 | margin: 10px auto; 176 | } 177 | 178 | thead { 179 | font-size: 120%; 180 | font-weight: 1000; 181 | cursor: pointer; 182 | background: #c9dff0 183 | } 184 | 185 | thead tr th { 186 | text-align: center; 187 | font-weight: bold; 188 | padding: 12px 30px; 189 | padding-left: 42px 190 | } 191 | 192 | thead tr th span { 193 | padding-right: 20px; 194 | background-repeat: no-repeat; 195 | background-position: 100% 100% 196 | } 197 | 198 | tbody tr td { 199 | padding: 15px 10px 200 | } 201 | 202 | tbody tr td.lalign { 203 | text-align: left 204 | } 205 | 206 | tbody tr:nth-child(even) { 207 | background: #fff 208 | } 209 | 210 | tbody tr:nth-child(odd) { 211 | background: #eee 212 | } 213 | 214 | td,th { 215 | border-left: 1px solid #cbcbcb; 216 | border-width: 0 0 0 1px; 217 | font-size: inherit; 218 | margin: 0; 219 | overflow: visible; 220 | padding: .5em 1em 221 | } 222 | 223 | 224 | 225 | /* 如果你的项目仅支持 IE9+ | Chrome | Firefox 等,推荐在 中添加 .borderbox 这个 class */ 226 | html.borderbox *, html.borderbox *:before, html.borderbox *:after { 227 | -moz-box-sizing: border-box; 228 | -webkit-box-sizing: border-box; 229 | box-sizing: border-box; 230 | } 231 | 232 | /* 重设 HTML5 标签, IE 需要在 js 中 createElement(TAG) */ 233 | article, aside, details, figcaption, figure, footer, header, menu, nav, section { 234 | display: block; 235 | } 236 | 237 | /* HTML5 媒体文件跟 img 保持一致 */ 238 | audio, canvas, video { 239 | display: inline-block; 240 | } 241 | 242 | /* 要注意表单元素并不继承父级 font 的问题 */ 243 | body, button, input, select, textarea { 244 | font: 300 1em/1.8 PingFang SC, Lantinghei SC, Microsoft Yahei, Hiragino Sans GB, Microsoft Sans Serif, WenQuanYi Micro Hei, sans-serif; 245 | } 246 | 247 | button::-moz-focus-inner, 248 | input::-moz-focus-inner { 249 | padding: 0; 250 | border: 0; 251 | } 252 | 253 | /* 去掉各Table cell 的边距并让其边重合 */ 254 | table { 255 | border-collapse: collapse; 256 | border-spacing: 0; 257 | } 258 | 259 | /* 去除默认边框 */ 260 | fieldset, img { 261 | border: 0; 262 | } 263 | 264 | /* 块/段落引用 */ 265 | blockquote { 266 | position: relative; 267 | color: #999; 268 | font-weight: 400; 269 | border-left: 1px solid #1abc9c; 270 | padding-left: 1em; 271 | margin: 1em 3em 1em 2em; 272 | } 273 | 274 | @media only screen and ( max-width: 640px ) { 275 | blockquote { 276 | margin: 1em 0; 277 | } 278 | } 279 | 280 | /* Firefox 以外,元素没有下划线,需添加 */ 281 | acronym, abbr { 282 | border-bottom: 1px dotted; 283 | font-variant: normal; 284 | } 285 | 286 | /* 添加鼠标问号,进一步确保应用的语义是正确的(要知道,交互他们也有洁癖,如果你不去掉,那得多花点口舌) */ 287 | abbr { 288 | cursor: help; 289 | } 290 | 291 | /* 一致的 del 样式 */ 292 | del { 293 | text-decoration: line-through; 294 | } 295 | 296 | address, caption, cite, code, dfn, em, th, var { 297 | font-style: normal; 298 | font-weight: 400; 299 | } 300 | 301 | /* 去掉列表前的标识, li 会继承,大部分网站通常用列表来很多内容,所以应该当去 */ 302 | ul, ol { 303 | list-style: none; 304 | } 305 | 306 | /* 对齐是排版最重要的因素, 别让什么都居中 */ 307 | caption, th { 308 | text-align: left; 309 | } 310 | 311 | q:before, q:after { 312 | content: ''; 313 | } 314 | 315 | /* 统一上标和下标 */ 316 | sub, sup { 317 | font-size: 75%; 318 | line-height: 0; 319 | position: relative; 320 | } 321 | 322 | :root sub, :root sup { 323 | vertical-align: baseline; /* for ie9 and other modern browsers */ 324 | } 325 | 326 | sup { 327 | top: -0.5em; 328 | } 329 | 330 | sub { 331 | bottom: -0.25em; 332 | } 333 | 334 | /* 让链接在 hover 状态下显示下划线 */ 335 | a { 336 | color: #1abc9c; 337 | } 338 | 339 | a:hover { 340 | text-decoration: underline; 341 | } 342 | 343 | a { 344 | border-bottom: 1px solid #1abc9c; 345 | } 346 | 347 | a:hover { 348 | border-bottom-color: #555; 349 | color: #555; 350 | text-decoration: none; 351 | } 352 | 353 | /* 默认不显示下划线,保持页面简洁 */ 354 | ins, a { 355 | text-decoration: none; 356 | } 357 | 358 | /* 专名号:虽然 u 已经重回 html5 Draft,但在所有浏览器中都是可以使用的, 359 | * 要做到更好,向后兼容的话,添加 class="typo-u" 来显示专名号 360 | * 关于 标签:http://www.whatwg.org/specs/web-apps/current-work/multipage/text-level-semantics.html#the-u-element 361 | * 被放弃的是 4,之前一直搞错 http://www.w3.org/TR/html401/appendix/changes.html#idx-deprecated 362 | * 一篇关于 标签的很好文章:http://html5doctor.com/u-element/ 363 | */ 364 | u, .typo-u { 365 | text-decoration: underline; 366 | } 367 | 368 | /* 标记,类似于手写的荧光笔的作用 */ 369 | mark { 370 | background: #fffdd1; 371 | border-bottom: 1px solid #ffedce; 372 | padding: 2px; 373 | margin: 0 5px; 374 | } 375 | 376 | /* 代码片断 */ 377 | pre, code, pre tt { 378 | font-family: Courier, 'Courier New', monospace; 379 | } 380 | 381 | pre { 382 | background: #000; 383 | border: 1px solid #ddd; 384 | padding: 1em 1.5em; 385 | display: block; 386 | -webkit-overflow-scrolling: touch; 387 | } 388 | 389 | /* 一致化 horizontal rule */ 390 | hr { 391 | border: none; 392 | border-bottom: 1px solid #cfcfcf; 393 | margin-bottom: 0.8em; 394 | height: 10px; 395 | } 396 | 397 | /* 底部印刷体、版本等标记 */ 398 | small, .typo-small, 399 | /* 图片说明 */ 400 | figcaption { 401 | font-size: 0.9em; 402 | color: #888; 403 | } 404 | 405 | strong, b { 406 | font-weight: bold; 407 | color: #000; 408 | } 409 | 410 | /* 可拖动文件添加拖动手势 */ 411 | [draggable] { 412 | cursor: move; 413 | } 414 | 415 | .clearfix:before, .clearfix:after { 416 | content: ""; 417 | display: table; 418 | } 419 | 420 | .clearfix:after { 421 | clear: both; 422 | } 423 | 424 | .clearfix { 425 | zoom: 1; 426 | } 427 | 428 | /* 强制文本换行 */ 429 | .textwrap, .textwrap td, .textwrap th { 430 | word-wrap: break-word; 431 | word-break: break-all; 432 | } 433 | 434 | .textwrap-table { 435 | table-layout: fixed; 436 | } 437 | 438 | /* 提供 serif 版本的字体设置: iOS 下中文自动 fallback 到 sans-serif */ 439 | .serif { 440 | font-family: Palatino, Optima, Georgia, serif; 441 | } 442 | 443 | /* 保证块/段落之间的空白隔行 */ 444 | p, pre, ul, ol, dl, form, hr, table, 445 | .typo-p, .typo-pre, .typo-ul, .typo-ol, .typo-dl, .typo-form, .typo-hr, .typo-table, blockquote { 446 | margin-bottom: 1.2em 447 | } 448 | 449 | h1, h2, h3, h4, h5, h6 { 450 | font-family: PingFang SC, Verdana, Helvetica Neue, Microsoft Yahei, Hiragino Sans GB, Microsoft Sans Serif, WenQuanYi Micro Hei, sans-serif; 451 | font-weight: 100; 452 | color: #000; 453 | line-height: 1.35; 454 | } 455 | 456 | /* 标题应该更贴紧内容,并与其他块区分,margin 值要相应做优化 */ 457 | h1, h2, h3, h4, h5, h6, 458 | .typo-h1, .typo-h2, .typo-h3, .typo-h4, .typo-h5, .typo-h6 { 459 | margin-top: 1.2em; 460 | margin-bottom: 0.6em; 461 | line-height: 1.35; 462 | } 463 | 464 | /* 在文章中,应该还原 ul 和 ol 的样式 */ 465 | ul, .typo-ul { 466 | margin-left: 1.3em; 467 | list-style: disc; 468 | } 469 | 470 | ol, .typo-ol { 471 | list-style: decimal; 472 | margin-left: 1.9em; 473 | } 474 | 475 | li ul, li ol, .typo-ul ul, .typo-ul ol, .typo-ol ul, .typo-ol ol { 476 | margin-bottom: 0.8em; 477 | margin-left: 0.6em; 478 | } 479 | 480 | li ul, .typo-ul ul, .typo-ol ul { 481 | list-style: circle; 482 | } 483 | 484 | /* 同 ul/ol,在文章中应用 table 基本格式 */ 485 | table th, table td, .typo-table th, .typo-table td, table caption { 486 | border: 1px solid #ddd; 487 | padding: 0.5em 1em; 488 | color: #666; 489 | } 490 | 491 | table th, .typo-table th { 492 | background: #fbfbfb; 493 | } 494 | 495 | table thead th, .typo-table thead th { 496 | background: #f1f1f1; 497 | } 498 | 499 | table caption { 500 | border-bottom: none; 501 | } 502 | 503 | /* 去除 webkit 中 input 和 textarea 的默认样式 */ 504 | .typo-input, .typo-textarea { 505 | -webkit-appearance: none; 506 | border-radius: 0; 507 | } 508 | 509 | .typo-em, em, legend, caption { 510 | color: #000; 511 | font-weight: inherit; 512 | } 513 | 514 | /* 着重号,只能在少量(少于100个字符)且全是全角字符的情况下使用 */ 515 | .typo-em { 516 | position: relative; 517 | } 518 | 519 | .typo-em:after { 520 | position: absolute; 521 | top: 0.65em; 522 | left: 0; 523 | width: 100%; 524 | overflow: hidden; 525 | white-space: nowrap; 526 | content: "・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・"; 527 | } 528 | 529 | /* Responsive images */ 530 | img { 531 | max-width: 100%; 532 | } 533 | -------------------------------------------------------------------------------- /template/template.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a4paper, 11pt]{article} 2 | \usepackage{geometry} % 設定邊界 3 | \geometry{ 4 | top=1in, 5 | inner=1in, 6 | outer=1in, 7 | bottom=1in, 8 | headheight=3ex, 9 | headsep=2ex 10 | } 11 | \usepackage{tabu} 12 | \usepackage[T1]{fontenc} 13 | \usepackage{lmodern} 14 | \usepackage{booktabs} 15 | \usepackage{amssymb,amsmath} 16 | \usepackage{ifxetex,ifluatex} 17 | \usepackage{fixltx2e} % provides \textsubscript 18 | % use upquote if available, for straight quotes in verbatim environments 19 | \IfFileExists{upquote.sty}{\usepackage{upquote}}{} 20 | \ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex 21 | \usepackage[utf8]{inputenc} 22 | $if(euro)$ 23 | \usepackage{eurosym} 24 | $endif$ 25 | \else % if luatex or xelatex 26 | \usepackage{fontspec} % 允許設定字體 27 | \usepackage{xeCJK} % 分開設置中英文字型 28 | \setCJKmainfont{STSong} % 設定中文字型 29 | \setmainfont[Mapping=tex-text]{Times New Roman}%\rmfamily 使用的字体,默认英文和数字的字体。 % 設定英文字型 30 | \setromanfont{Georgia} % 字型 31 | \setmonofont{Courier New} 32 | \linespread{1.2}\selectfont % 行距 33 | \XeTeXlinebreaklocale "zh" % 針對中文自動換行 34 | \XeTeXlinebreakskip = 0pt plus 1pt % 字與字之間加入0pt至1pt的間距,確保左右對整齊 35 | \parindent 0em % 段落縮進 36 | \setlength{\parskip}{20pt} % 段落之間的距離 37 | \ifxetex 38 | \usepackage{xltxtra,xunicode} 39 | \fi 40 | \defaultfontfeatures{Mapping=tex-text,Scale=MatchLowercase} 41 | \newcommand{\euro}{€} 42 | $if(mainfont)$ 43 | \setmainfont{$mainfont$} 44 | $endif$ 45 | $if(sansfont)$ 46 | \setsansfont{$sansfont$} 47 | $endif$ 48 | $if(monofont)$ 49 | \setmonofont{$monofont$} 50 | $endif$ 51 | $if(mathfont)$ 52 | \setmathfont{$mathfont$} 53 | $endif$ 54 | \fi 55 | % use microtype if available 56 | \IfFileExists{microtype.sty}{\usepackage{microtype}}{} 57 | $if(geometry)$ 58 | \usepackage[$for(geometry)$$geometry$$sep$,$endfor$]{geometry} 59 | $endif$ 60 | $if(natbib)$ 61 | \usepackage{natbib} 62 | \bibliographystyle{plainnat} 63 | $endif$ 64 | $if(biblatex)$ 65 | \usepackage{biblatex} 66 | $if(biblio-files)$ 67 | \bibliography{$biblio-files$} 68 | $endif$ 69 | $endif$ 70 | $if(listings)$ 71 | \usepackage{listings} 72 | $endif$ 73 | $if(lhs)$ 74 | \lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily}}{} 75 | $endif$ 76 | $if(highlighting-macros)$ 77 | $highlighting-macros$ 78 | $endif$ 79 | $if(verbatim-in-note)$ 80 | \usepackage{fancyvrb} 81 | $endif$ 82 | $if(tables)$ 83 | \usepackage{longtable} 84 | $endif$ 85 | 86 | \usepackage{graphicx} 87 | \usepackage{caption} 88 | % We will generate all images so they have a width \maxwidth. This means 89 | % that they will get their normal width if they fit onto the page, but 90 | % are scaled down if they would overflow the margins. 91 | \makeatletter 92 | \def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth 93 | \else\Gin@nat@width\fi} 94 | \makeatother 95 | \let\Oldincludegraphics\includegraphics 96 | \renewcommand{\includegraphics}[1]{\Oldincludegraphics[width=\maxwidth]{#1}} 97 | \ifxetex 98 | \usepackage[setpagesize=false, % page size defined by xetex 99 | unicode=false, % unicode breaks when used with xetex 100 | xetex]{hyperref} 101 | \else 102 | \usepackage[unicode=true]{hyperref} 103 | \fi 104 | \hypersetup{breaklinks=true, 105 | bookmarks=true, 106 | pdfauthor={$author-meta$}, 107 | pdftitle={$title-meta$}, 108 | colorlinks=true, 109 | urlcolor=$if(urlcolor)$$urlcolor$$else$blue$endif$, 110 | linkcolor=$if(linkcolor)$$linkcolor$$else$magenta$endif$, 111 | pdfborder={0 0 0}} 112 | \urlstyle{same} % don't use monospace font for urls 113 | $if(links-as-notes)$ 114 | % Make links footnotes instead of hotlinks: 115 | \renewcommand{\href}[2]{#2\footnote{\url{#1}}} 116 | $endif$ 117 | $if(strikeout)$ 118 | \usepackage[normalem]{ulem} 119 | % avoid problems with \sout in headers with hyperref: 120 | \pdfstringdefDisableCommands{\renewcommand{\sout}{}} 121 | $endif$ 122 | \setlength{\parindent}{0pt} 123 | %\setlength{\parskip}{6pt plus 2pt minus 1pt} 124 | \setlength{\emergencystretch}{3em} % prevent overfull lines 125 | \usepackage{titling} 126 | \setlength{\droptitle}{-8em} % 將標題移動至頁面的上面 127 | 128 | \usepackage{fancyhdr} 129 | \usepackage{lastpage} 130 | \pagestyle{fancyplain} 131 | 132 | $if(numbersections)$ 133 | \setcounter{secnumdepth}{5} 134 | $else$ 135 | \setcounter{secnumdepth}{0} 136 | $endif$ 137 | $if(verbatim-in-note)$ 138 | \VerbatimFootnotes % allows verbatim text in footnotes 139 | $endif$ 140 | $if(lang)$ 141 | \ifxetex 142 | \usepackage{polyglossia} 143 | \setmainlanguage{$mainlang$} 144 | \else 145 | \usepackage[$lang$]{babel} 146 | \fi 147 | $endif$ 148 | $for(header-includes)$ 149 | $header-includes$ 150 | $endfor$ 151 | 152 | $if(title)$ 153 | \title{$title$} 154 | $endif$ 155 | \author{$for(author)$$author$$sep$ \and $endfor$} 156 | \date{$date$} 157 | 158 | %%%% 段落首行缩进两个字 %%%% 159 | \makeatletter 160 | \let\@afterindentfalse\@afterindenttrue 161 | \@afterindenttrue 162 | \makeatother 163 | \setlength{\parindent}{2em} %中文缩进两个汉字位 164 | 165 | 166 | %%%% 下面的命令重定义页面边距,使其符合中文刊物习惯 %%%% 167 | \addtolength{\topmargin}{-2pt} 168 | \setlength{\oddsidemargin}{0.63cm} % 3.17cm - 1 inch 169 | \setlength{\evensidemargin}{\oddsidemargin} 170 | \setlength{\textwidth}{14.66cm} 171 | \setlength{\textheight}{24.00cm} % 24.62 172 | 173 | %%%% 下面的命令设置行间距与段落间距 %%%% 174 | \linespread{1.4} 175 | % \setlength{\parskip}{1ex} 176 | \setlength{\parskip}{0.5\baselineskip} 177 | 178 | 179 | \begin{document} 180 | %%%% 定理类环境的定义 %%%% 181 | \newtheorem{example}{例} % 整体编号 182 | \newtheorem{algorithm}{算法} 183 | \newtheorem{theorem}{定理}[section] % 按 section 编号 184 | \newtheorem{definition}{定义} 185 | \newtheorem{axiom}{公理} 186 | \newtheorem{property}{性质} 187 | \newtheorem{proposition}{命题} 188 | \newtheorem{lemma}{引理} 189 | \newtheorem{corollary}{推论} 190 | \newtheorem{remark}{注解} 191 | \newtheorem{condition}{条件} 192 | \newtheorem{conclusion}{结论} 193 | \newtheorem{assumption}{假设} 194 | 195 | \newcommand{\tightlist}{% 196 | \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} 197 | 198 | %%%% 重定义 %%%% 199 | \renewcommand{\contentsname}{目录} % 将Contents改为目录 200 | \renewcommand{\abstractname}{摘要} % 将Abstract改为摘要 201 | \renewcommand{\refname}{参考文献} % 将References改为参考文献 202 | \renewcommand{\indexname}{索引} 203 | \renewcommand{\figurename}{图} 204 | \renewcommand{\tablename}{表} 205 | \renewcommand{\appendixname}{附录} 206 | 207 | $for(include-before)$ 208 | $include-before$ 209 | 210 | $endfor$ 211 | $if(toc)$ 212 | { 213 | \newpage 214 | \hypersetup{linkcolor=black} 215 | \setcounter{tocdepth}{$toc-depth$} 216 | \tableofcontents 217 | } 218 | \newpage 219 | $endif$ 220 | $body$ 221 | 222 | $if(natbib)$ 223 | $if(biblio-files)$ 224 | $if(biblio-title)$ 225 | $if(book-class)$ 226 | \renewcommand\bibname{$biblio-title$} 227 | $else$ 228 | \renewcommand\refname{$biblio-title$} 229 | $endif$ 230 | $endif$ 231 | \bibliography{$biblio-files$} 232 | 233 | $endif$ 234 | $endif$ 235 | $if(biblatex)$ 236 | \printbibliography$if(biblio-title)$[title=$biblio-title$]$endif$ 237 | 238 | $endif$ 239 | $for(include-after)$ 240 | $include-after$ 241 | 242 | $endfor$ 243 | \end{document} --------------------------------------------------------------------------------