将-Arduino-连接到-Web-全-
将 Arduino 连接到 Web(全)
一、Arduino,电路和元件
本章介绍 Arduino 的电子学。它将解释 Arduino 是如何设置的,以及电流是如何流经电路和组件的。到本章结束时,你将使用一些基本元件,并创建四个基本电路:模拟输入,模拟输出,数字输入和数字输出。
Arduino
Arduino 允许您创建自己的电子项目。它是一个开源硬件和软件的集合,允许您连接和控制其他组件来创建一个电路。自动植物浇水系统、披萨烤箱或遥控玩具车等项目都可以用 Arduino 制作。当您在项目中使用 Arduino 时,您需要执行以下操作:
- 将组件连接到它。
- 编写一个程序来控制这些组件。
- 验证程序是否编写正确。
- 将程序上传到 Arduino。
Arduino 需要通过 USB 端口连接到电脑,才能将程序上传到电脑。Arduinos 的程序叫做草图。一旦草图被上传,它就被存储在微控制器上,并一直保存到另一个草图被上传。新草图上传后,旧草图不再可用。
一旦草图被上传,你可以将 Arduino 从电脑上断开,如果它连接到另一个电源,程序仍然会运行。
Note
一旦草图被上传到 Arduino,它就不会以你所写的方式显示。您无法从 Arduino 中以可作为原始草图读取的形式取回草图,因此如果您想要保留它,请确保保存您的原始代码。
硬件烧坏
Arduino 板由许多组件组成,包括微控制器、数字和模拟引脚、电源引脚、电阻、二极管、电容和 LED。图 1-1 显示了一个 Arduino Uno。
图 1-1
An Arduino Uno
微控制器具有中央处理单元(CPU);它存储上传的草图,并处理和指导命令。
数字和模拟引脚用于发送和接收数字和模拟数据。
Arduino 还有一个串行接口,允许 Arduino 通过串行端口向计算机发送数据;这就是我们在本书中向计算机发送数据和从计算机接收数据的方式。
电流
使用 Arduino,您可以创建一个电子电路,为连接到它的组件供电。由导电材料制成的电线连接着让电流通过的部件。
电是电子通过导电材料的运动。在导电材料中,电子可以很容易地在原子之间移动,但在非导电材料中却不能。
原子由质子、中子和电子组成。原子的中心是原子核、质子和中子;电子在外面。质子带正电荷,电子带负电荷。这两种电荷相互吸引。电子在围绕原子核的轨道上。在非导电材料如木头或瓷器中,电子很难移动;它们紧紧地束缚在原子上。在像铜和其他金属这样的导电材料中,电子与原子的结合非常松散,因此它们可以很容易地移动。这些电子在原子的外缘,被称为价电子。
电子在电路中从负极向正极移动。当电第一次被发现时,人们认为它是从正极到负极移动的,所以按照惯例,电子电路通常是从正极到负极,从正极到地(GND)。在本书的电路中,电流将向一个方向流动;这被称为直流电(DC),而在交流电(AC)中,方向每秒钟改变一定次数。
为了让导电材料中的电子开始移动,它们需要一个推力,这个推力就是电压。电压是电路中较高势能和较低势能之差。电子想从较高的势能流向较低的势能,从正极流向负极。
产生电压的方式有很多种。在电池中,它是由化学反应产生的。在电池的负极会产生电子的积聚。当连接到电池的正极时,负电子被吸引到正极,从较高的势能到较低的势能。这导致它们推动电线上的电子;电子沿着导线分流。
在电学中,电流是指每秒钟通过某一点的电子数量。电流是以安培为单位测量的。电路中的每一个元件都会消耗一部分电能,并将其转化为另一种形式的能量,如光或声音。电路上的元件使用电路中的所有能量。
电路也有电阻。电阻是电流流过的物质减慢电流的程度。阻力就像水流路上的障碍。电阻以欧姆为单位,使用符号ω。电总是选择最容易流动的方式,阻力最小的路径。图 1-2 解释了电压、电流和电阻之间的关系。
图 1-2
An interpretation of voltage, current, and resistance
欧姆定律
物理学家和数学家乔治·西蒙·欧姆发现了电压、电流和电阻之间的关系;这个关系叫做欧姆定律。欧姆定律说电压等于安培乘以电阻,写成 V = I * R 其中 V 是伏特,I 是电流,R 是电阻。利用该公式,您还可以找到电阻 R = V / I,并找到电流 I = V / R,这些可以在图 1-3 中看到。
图 1-3
Ohms law
电路上的元件使用电路中的部分能量,并将其转化为另一种形式的能量,如光或声。电路上的所有能量都需要被电路使用。如果所有的能量没有用完,它需要去某个地方,否则会导致过热或着火。例如,如果电路上有一个 LED,并且它接收了太多的能量,那么灯将会非常亮,并且可能会熄灭。根据欧姆定律,你可以计算电阻,而不是 V = I * R,你可以用 R = V / I 来计算电阻,电阻等于电压除以电流。
电阻
电阻器是电路的关键元件,因为它们限制电路上的电流量。电阻器对电流有一定的阻力。每个组件都有一个最大的电流量,用它可以安全使用的安培数来衡量。例如,如果一个元件可以承受最大 0.023 安培的电流,即 23 毫安,而您的电路接收的是 5V(伏特),则需要在电路中增加一个 220 欧姆的电阻,以便安全地使用 LED。电子元件将使用来自电路的一些功率;这被称为电压降,因此在计算电阻时可以考虑到这一点。图 1-4 显示了如何利用欧姆定律,在不同的电压和电流下计算的示例。
图 1-4
The formulae to find the resistance needed for a circuit
当你得到一个元件时,它也应该有一个数据手册,可能是在线的。这将为您提供有关电压降和最大电流的信息。这将允许你计算出你的电路需要什么样的电阻。
电阻器的阻值以欧姆为单位;他们有一个颜色代码来显示价值。电阻值告诉你它将消耗多少电流。
电子电路图
电路图直观地描述了一个电路。有几组图标用来显示电子电路中的元件。在本书中,我不会使用电子电路图来展示项目中使用的电路,而是使用 Arduino 和组件的图像。图 1-5 给你一个电路图的概念。这是一个 LED 的电路图。
图 1-5
A circuit diagram for an LED with a resistor
图 1-6 显示了一些可以在电路图中使用的图标。
图 1-6
Some circuit diagram icons
Arduino 软体
Arduino 有自己的编程语言;它是一组 C 和 C++函数。Arduino 程序被称为草图,它们有一个. ion 扩展名。Arduino 有自己的集成开发环境(IDE ),它有一个编辑器和其他工具来帮助你编写和上传代码。
下载和设置 Arduino IDE
您可以在计算机上安装 Arduino IDE。Arduino IDE 可以在线获得,并且易于下载和安装;您可以遵循以下说明:
- 前往
https://www.arduino.cc/en/Main/Software
。 - “下载 Arduino IDE”部分包含 Mac 和 PC 的链接。
对于 MAC 电脑:
- 点按“Mac OS X 10.7 Lion 或更新版本”链接,然后选择“仅下载”或“贡献并下载”;两个按钮都在图片下方。
- 解压缩下载的文件。
- Arduino 图标将会出现,只需点击它即可打开 IDE。
对于 PC:
- 根据您在计算机上拥有的管理员权限,单击链接 Windows Installer 或 Windows ZIP 文件进行非管理员安装。选择“仅下载”或“贡献并下载”。
- 解压缩下载文件。
- 您应该能够打开带有图标的 IDE。
当您打开 IDE 时,应该会打开一个新的草图。图 1-7 是一个编辑窗口的例子。
图 1-7
An Arduino IDE edit window
草图将包含两个功能,设置和循环。页面顶部有一个勾号图标。按下该按钮是为了验证您的代码是否编写正确。如果有任何问题,您会在控制台中看到一条红色消息。当您想要将草图上传到 Arduino 时,请单击箭头图标。控制台将显示与您的草图相关的消息。当草图被验证和上传后,它会显示你的代码和信息中的任何错误。
将 Arduino 连接到电脑
您需要一根 USB 2.0 型电缆来将 Arduino Uno 连接到您的电脑。USB 将用于向 Arduino 发送数据和从 Arduino 接收数据,以及为其供电。不同类型的 Arduinos 将使用不同类型的电缆。
双击图标打开 IDE 并用 USB 将 Arduino 连接到计算机后,您需要检查工具菜单,查看 Arduino Uno 是否作为主板列出,以及它连接到哪个端口。在菜单中进入工具/板,并检查板说“Arduino/genu ino Uno”;如果没有,从下拉菜单中选择 Uno。
港口
您将通过其中一个 USB 端口将 Arduino 连接到计算机;这些端口有一个编号,在 Arduino IDE 的工具菜单中,您需要检查端口下拉列表来选择端口。
查看“工具/端口”菜单,确保 USB 端口被选中。根据您将 Arduino 插入的 USB 端口以及您使用的是 Mac 还是 PC,它看起来会略有不同。在 Mac 上,它应该显示类似“dev/Cu . usbmodem 621(Arduino/genu ino Uno)”的内容。在个人电脑上,它会显示类似“COM4 (Arduino/Genuino Uno)”的信息
Write a Sketch
Arduino 有一个内置的 LED,所以最容易写的草图是一个控制它并使它闪烁的草图。这是大多数人会写的第一个草图,并且如此普遍,以至于你拥有的 Arduino 在你得到它的时候可能已经安装了它。
Arduino IDE 有许多示例草图,blink 就是其中之一。在 IDE 中,如果您转到文件/Examples/01。你会看到眨眼的草图。您可以从那里打开它,或者从清单 1-1 中的代码复制它。您需要在上传之前保存它。
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH);
delay(1000);
digitalWrite(13, LOW);
delay(1000);
}
Listing 1-1
blink.ino
代码解释
每当你打开一个新的草图,你总是会得到设置和循环功能。首次运行程序时,会调用一次 setup 函数。当 Arduino 通电时,loop 函数将继续循环并执行其中的命令。blink.ino 正在使用 Arduino 上的 LED 并控制它。该 LED 位于 Arduino 上的数字引脚 13,因此在设置中,您可以使用 pinMode 功能来说明引脚 13 正被用作输出,它将会亮起。
该环路具有 digitalWrite 功能,可告知引脚 13 是高电平还是低电平(开或关)。延迟功能将循环暂停一会儿;否则,一旦它完成了上一行,它就会运行到下一行代码。延迟以毫秒为单位。表 1-1 更详细地解释了 blink.ino。
表 1-1
blink.ino
explained
您可以验证代码,检查它的语法是否正确,然后将它上传到 Arduino。要验证它,请按代码顶部的勾号箭头,要上传它,请单击箭头图标。这些都显示在图 1-7 中。
注意 Arduino IDE 对语法错误非常敏感,如果你忘记了一个“;”在一个命令的结尾,或者在应该是大写字母的时候写小写字母,你会得到一个错误。IDE 非常善于让您知道错误在哪里,并且通常很容易修复。
试验板
试验板用于电子产品的原型制作;这是一种无需焊接即可将组件连接到 Arduino 的方法。它由塑料制成,上面有一系列的孔,用于部件的插脚和电线。它们通常有两个条形孔,分别用于电源和接地。试验板内部是导电的金属条。电线和插脚与这些金属条连接形成电路。它们可以有不同的尺寸。图 1-8 显示了一个试验板。
图 1-8
A Breadboard
电缆
Arduino 初学者工具包附带了许多电缆,您可以在大多数项目中使用这些电缆在 Arduino 和使用试验板的组件之间建立电路。有些元件的引脚很难安装到试验板上。在第十章中,当你制作游戏控制器时,你可能需要使用不同头的电缆。有三种类型的电缆:男性对男性,女性对男性,女性对女性。如图 1-9 所示。
图 1-9
Cables
数字和模拟
在 Arduino 上,您可以使用数字输入、数字输出、模拟输入和模拟输出。数字输入或输出可以有两种状态,开或关(高或低)。当使用 5V 时,模拟输入或输出可以在 0 和 1023 之间。本章中的以下练习展示了数字输出和输入以及模拟输入和输出的示例。
Caution
连接组件时,必须拔掉 Arduino 的插头。当它连接到您的电脑时,会有电流通过,这可能会导致触电。如果一个组件的能量过大,它可能会爆裂或爆炸,所以如果发生这种情况,你不要太靠近。
Digital Output
用于数字输出的元件接收高电平信号表示开,接收低电平信号表示关。清单 1-1 中的代码使用数字输出。在本练习中,您将使用与清单 1-1 中相同的代码 blink.ino,它是一个连接到 Arduino 的 LED。为此,您需要:
- 1 x 火神一号
- 1 个 LED
- 1 x 220 欧姆电阻器
部件如图 1-10 所示,部件的设置如图 1-11 所示。在连接组件之前,请确保您已将 Arduino 从计算机或任何电源上拔下。Arduino 的长腿是正的,短腿是负的。
图 1-11
Setup for the components for the digital output exercise
图 1-10
Components for the digital output exercise: 1. Breadboard, 2. LED, 3. 220 Ohm resistor, 4. Arduino Uno
用 USB 将 Arduino 插回电脑。如果你上传的最后一个程序是 blink.ino,那么你应该会看到 LED 在闪烁。如果没有,重新上传 blink.ino。
模拟输出
模拟输出和输入产生一系列按顺序上升和下降的数字。在 Arduino 上,一些数字引脚旁边有一个“~”符号。这些引脚用于模拟输出,并使用 PWM(脉宽调制)。
脉宽灯
PWM 用于通过数字引脚模拟模拟输出。数字信号可以是开或关的,它发送一个开的脉冲。PWM 通过改变脉冲的长度来模拟使用数字信号的模拟系统;现在是模拟 5V 和 0V 之间脉冲的“开启”时间。图 1-12 显示了模拟不同电压的脉冲宽度。
图 1-12
Pulse width modulation Analog Output
由于模拟信号可以产生一系列的数字,所以你可以循序渐进。在本练习中,LED 将在关闭前慢慢变亮,然后再次变亮。本练习的组件与图 1-10 相同。确保 Arduino 被拔出,然后如图 1-13 所示设置组件。LED 连接到数字引脚 9,旁边有一个~。
图 1-13
Setup for analog output
创建一个新的 ino 草图,我将其命名为 mine chapter_01_1.ino,并将清单 1-2 中的代码复制到其中。
int analogOutPin = 9;
int outputValue = 0;
void setup() {
pinMode(analogOutPin, OUTPUT);
}
void loop() {
if (outputValue >= 40){
outputValue = 0;
} else {
outputValue = outputValue + 1;
}
analogWrite(analogOutPin, outputValue);
delay(200);
}
Listing 1-2
chapter_01_1.ino
代码解释
为了让 LED 逐渐变亮,你需要给它一个在每次循环中增加的值。这段代码中有许多新的编程概念。如果您还没有完全理解它们,不要太担心,因为接下来的几章将更详细地介绍编程。表 1-2 更详细地解释了 chapter_01_1.ino 中的代码。
表 1-2
chapter_01_1.ino explained
| `int analogOutPin = 9;` | 引脚 9 将用于 LED 通常的做法是将这个数字存储在整个程序中使用的变量中。这使得在整个程序中更容易看到该号码所代表的含义,并且如果您决定使用不同的 pin 号码,也允许您在代码中更改一次 pin 号码。 | | `int outputValue = 0;` | 变量保存 LED 的值。 | | `if (outputValue >= 40){``outputValue = 0;``} else {``outputValue = outputValue + 1;` | if else 语句检查某事是否为真;如果是,它做一件事,如果不是,它做另一件事。在这种情况下,它检查变量 outPutValue 的值是否大于或等于 40;如果是,它使变量包含值 0,这关闭 LED,如果不是,它增加 1,打开 LED 的亮度。 | | `analogWrite(analogOutPin,``outputValue` | analogWrite 函数有两个参数:管脚号和值,在这种情况下,outputValue 中的值被发送到连接到管脚 9 的组件。在这种情况下,它是一个 LED,这将改变 LED 的亮度。 |将草图上传到 Arduino 您应该会看到 LED 亮度增加,然后熄灭。
数字输入
显示数字输入的良好电路是开关按钮。开关按钮不是向上就是向下,它有两种状态:按下或未按下。它引入了另一个概念,叫做输入上拉。
带开关的 Arduino 有问题。当开关打开时,它没有完成电路,没有电压,所以 Arduino 不知道输入是什么;可能是 0,也可能是 1。由于它不知道您可能会得到奇怪的结果,所以它会产生噪声,因为输入值是未知的,并且它会尝试输入一些东西。上拉电阻解决了这个问题;当开关打开时,它设置一个电压。
上拉电阻内置于 Arduino 中,当使用 pinMode()函数时,可以通过将其设置为 INPUT_PULLUP 而不仅仅是 INPUT 来访问。当开关打开时,引脚读数为高,当开关按下时,引脚读数为低。
Digital Input
在本练习中,您将使用一个按钮来打开和关闭 Arduino 上的 LED。为此,您需要:
- 1 x 火神一号
- 1 个开关按钮
更换组件时,请记住断开 Arduino 与电脑的连接。开关安装在试验板上。一个引脚接地,另一个引脚连接数字引脚 2。图 1-14 显示了所需的组件,图 1-15 显示了 Arduino 的设置。
图 1-15
Setup for the components for the digital input exercise
图 1-14
Components for the digital input exercise: 1. Breadboard, 2. Switch, 3. Arduino Uno
在 Arduino IDE 中创建一个新的草图,我称之为我的 chapter_01_2,并复制清单 1-3 中的代码。验证并将代码上传到 Arduino。当您按下按钮时,Arduino 上的 LED 应该会亮起。
int buttonInput = 2;
int LEDOutput = 13;
void setup() {
pinMode(buttonInput, INPUT_PULLUP);
pinMode(LEDOutput, OUTPUT);
}
void loop() {
int sensorVal = digitalRead(buttonInput);
if (sensorVal == HIGH) {
digitalWrite(13, LOW);
} else {
digitalWrite(13, HIGH);
}
}
Listing 1-3
chapter_01_2.ino
代码解释表 1-3 更详细地解释了 chapter_01_2.ino 的代码。
表 1-3
chapter_01_2.ino
explained
模拟输入
模拟输入与光敏电阻和电位计等元件一起使用,这些元件提供不同的值。Arduino Uno 可以记录 0 到 5 伏之间的值;这样你可以得到一个介于 0 和 1023 之间的模拟输入值。模拟输入发送信号电压。当接收到信号电压时,会根据内部参考进行检查。图 1-16 中显示了一个示例。
图 1-16
An illustration of the analog input process
当接收到一个信号电压时,在线路上的多个点相对于内部基准电压进行测试。例如,它检查输入是否大于 0;如果不是,它检查它是否大于引用中的下一个数字,并一直检查直到它是。该点成为输入数字。
Analog Input
本练习使用电位计作为模拟输入。当电位计转到一半时,它会打开和关闭 Arduino 上的 LED。
本练习所需的组件如下:
- 1 x 火神一号
- 1 个电位计
图 1-17 显示了组件,图 1-18 显示了 Arduino 的设置。
图 1-18
Setup for the components for the analog input exercise
图 1-17
Components for the analog input exercise: 1. Breadboard, 2. Potentiometer, 3. Arduino Uno
打开新草图。我叫它 chapter _ 01 _ 3;然后复制清单 1-4 中的代码。
int pinAnalogInput = A0;
int LEDOutput = 13;
int valueLight = 0;
void setup() {
pinMode(LEDOutput, OUTPUT);
}
void loop() {
valueLight = analogRead(pinAnalogInput);
if (valueLight < 500) {
digitalWrite(LEDOutput, LOW);
} else {
digitalWrite(LEDOutput, HIGH);
}
delay(500);
}
Listing 1-4chapter_01_3.ino
验证草图并将 USB 插回电脑,将草图上传到 Arduino。现在,当您将电位计转到一半时,Arduino 上的 LED 应该会忽明忽暗。
代码与本章前面的草图非常相似。主要区别在于模拟引脚 int pinAnalogInput = A0 的变量;模拟输入通过引脚 A0;
摘要
本章是对 Arduino 的基本介绍。它着眼于电路如何工作,以及模拟和数字输入和输出。这些是 Arduino 的基本模块,将在整本书中构建。下一章将带您开始学习 JavaScript,并构建一个能够接收 Arduino 发送的数据的 web 服务器。
二、创建 Web 服务器
要开始将 Arduino 连接到 web,对 Web 技术有一个基本的了解是很有用的。本章将介绍一些原理,包括什么是 web 服务器,URL 是如何构造的,什么是路由,什么是 Node.js。然后它将变得实用,您将学习如何用 Node.js 创建一个 web 服务器,并从服务器向网页来回发送数据。它将涵盖 Node.js、ejs 和 socket.io。
什么是 Web 服务器?
web 服务器向 web 浏览器提供页面;它还处理信息并存储页面的数据和素材。它允许使用超文本传输协议(HTTP)处理请求。此协议允许网络使用称为统一资源定位符(URL)的地址(指向您的网页的地址)相互通信。URL 有一个定义好的结构,以协议开头,后面是域名、域扩展名以及可选的文件和文件夹名;见图 2-1 。
图 2-1
URL structure
域名是互联网协议(IP)地址的自然英语翻译。IP 地址是一系列数字,任何连接到互联网的东西都有一个 IP 地址。这包括智能手机和智能电视;任何连接到互联网的设备都会有一个 IP 地址。
当互联网上的计算机相互通信时,它们使用它们的 IP 地址。当你在浏览器中输入网址时,它会被转换成 IP 地址。这将告诉 web 服务器您想要的页面的地址。这是到那个页面的路径。如果服务器找到这个页面,它会把它返回给你的浏览器。如果不能,它将返回一个错误页面。
Web 服务器有许多惯例来连接网络上的一台计算机并向另一台计算机传输数据。其中之一就是代表性状态转移,也就是 RESTful。它使计算机系统具有互操作性;这意味着,无论服务器如何设置,如果它实现了 RESTful web 服务,它就可以与任何其他也实现它们的服务器对话。当 HTTP 使用 RESTful 时,可以使用 GET、POST、PUT 和 DELETE 请求。例如,POST 允许您在网页上填写表单,并将其发送到浏览器。
您可以在自己的计算机上创建 web 服务器,使用 Node.js 就是一种方法。这意味着您可以在本地机器上开发应用程序,并在部署之前进行测试。本地 web 服务器使用域名 localhost,该域名解析为 IP 地址 127.0.0.1。
选择途径
如果没有路由,您将无法查看网页。路由决定了 web 服务器如何响应来自 web 浏览器的 URL 请求。回到 example.com,当有人输入 URL 时,服务器必须知道服务哪个页面。如果您添加到其他网页,如 example.com/about,将必须有一个在服务器上为这个网页的路线。路由还告诉服务器应该如何响应请求。它使用 RESTful 命令做到这一点;如果一个路由以 GET 开始,服务器知道它需要获取页面的内容。如果它以 POST 开始,服务器知道它将从网页接收数据,并且路由将定义下一步应该做什么。
Node.js 是什么?
Node.js 是用于执行 JavaScript 服务器代码的运行时环境。这意味着你可以在浏览器和服务器上使用同一种语言,JavaScript。使用 Node.js,您可以创建到网页的路由、连接到本地主机、连接到数据库,以及用 JavaScript 向网页发送数据。它允许你使用相同的语言构建 web 应用程序。
Node.js 与 Arduinos 配合得非常好。使用串行端口,您可以使用服务器将数据从 Arduino 传递到网页,并将数据从网页传递到 Arduino。
除了下载 Node.js 之外,您还需要下载其他软件包,以便更简单地创建您想要的应用程序。您可以使用包管理器来完成这项工作;有几个不同的,但本书使用的是节点包管理器(npm)。
使用命令行界面
命令行界面是一种使用文本向计算机发送指令的方式。你可以用它做很多事情,包括移动你的电脑目录,创建新文件和运行代码。
要使用 Node.js,您需要使用命令行界面。您可以使用它来安装新模块、启动服务器,以及查看来自应用程序的消息和错误。
Windows 和 Mac 都有内置的命令行界面应用程序。在 Windows 中它被称为命令提示符或 cmd.exe,在 Mac 中它被称为终端。两者都将打开一个控制台窗口,用于输入命令。
这是一个非常强大的工具,需要谨慎使用,因为你可以清除你的系统或做出难以撤销的更改。
命令行界面在所谓的命令行 shell 中实现。shell 是一个程序,它接受文本命令并将它们翻译成操作系统能够理解的语言。
当您打开控制台窗口时,它应该显示登录用户的主目录。这是该用户的顶层目录,您可以从中导航到该用户的文件和文件夹。
Using The Mac Terminal
Mac 终端应用程序位于应用程序文件夹内的“实用工具”文件夹中。它的路径如下:
硬盘驱动器/应用程序/实用程序/终端
当应用程序打开时,您应该会看到一个控制台窗口,显示您的主目录,后跟
尝试以下命令并查看表 2-1 中您可能会发现有用的其他命令:
表 2-1
Some useful terminal Commands
| 命令 | 结果 | | :-- | :-- | | 显示当前工作目录 | 写入当前目录的完整路径 | | 限位开关(Limit Switch) | 列出目录的内容 | | cd | 下一级目录的路径 | | cd- 打开终端,一个新的控制台窗口将会打开。
- 键入 ls + return,您将看到当前目录下所有文件和文件夹的列表。
- 键入 cd
+ return,您将进入该目录。 - 使用 ls 列出文件和目录,然后键入 cd 和目录名以移动到另一个目录。
- cd 标牌..+ return,您将在目录中上移一个文件夹。
- 键入 cd + return,您将移动到您的主目录。
- 键入 Ctrl + l 或 Cmd+k,这两个键都会清除控制台屏幕。Crtl+l 只是清空屏幕,Cmd+k 也是清空终端缓冲区。
Using The Windows Command Prompt
windows 控制台被称为命令提示符或 cmd.exe。有多种方法可以打开命令提示符控制台窗口,这些方法会根据您运行的 windows 版本而变化(参见表 2-2 )。您可以使用 Windows 搜索来查找命令提示符;搜索的是 Windows 10 上的 Cortana。开始在搜索栏中键入“命令提示符”;最匹配的应该是桌面应用“命令提示符”单击“命令提示符”打开控制台窗口。
当应用程序打开时,您应该会看到一个控制台窗口,显示您的主目录,后面跟着一个>应该是这样的:C:\Users\Username >,您开始在>后面键入。
尝试几个命令:
- 打开命令提示符,将会打开一个新的控制台窗口。
- 键入 dir + enter,您应该会看到当前目录中的所有文件和文件夹。
- 键入 cd
+ enter,你将进入那个文件夹。 - 使用 ls 列出文件和目录,使用 cd 移动到另一个目录。
- cd 标牌..+ enter,您将在目录中上移一个文件夹。
- 键入 cd %userprofile% + enter 返回主目录。
- 键入 cls + enter,控制台屏幕将被清除。
命令中可以有可选参数。如果您使用 dir 命令,您将看到目录中每个文件或文件夹的大量信息。如果您只想查看姓名,您可以键入 dir /b。
表 2-2
Some useful command prompt commands
| 命令 | 结果 | | :-- | :-- | | `echo %cd%` | 写入当前目录 dir 的完整路径 | | `cd注意:如果您开始在控制台窗口中输入一个目录名,然后按 tab 键,目录名的其余部分将会自动填写,只要它是唯一包含这些字母的目录。
要向后或向前移动到上一个命令,请使用键盘上的上下箭头。
Mac 控制台区分大小写。Mac 控制台对空白敏感。
设置 Node.js 服务器
现在背景已经覆盖了,是时候开始编码了。如果您的计算机上没有安装 Node.js,您需要将它与节点包管理器一起安装。根据你已经在电脑上安装了什么,这可能需要一些时间,但一旦完成,进一步下载会快得多。
安装 Node.js
首先,如果您不确定您的计算机上是否安装了 Node.js,您可以在控制台窗口中检查。
Check If Node.js Is Installed
- 打开控制台窗口。
- 在控制台提示符下键入 node -v,如果安装了 Node.js,您应该会看到您的版本号(例如,v6.10.3)。
- 在控制台提示符下键入 npm -v,如果安装了 npm,您应该会看到版本号(例如,3.10.10)。
如果您看到版本号,请跳过安装 Node.js 的下一步,直接进入“创建应用程序”一节。
Install Node.js On Windows
在 Windows 中,您可以直接从 Node.js 网站安装 Node.js。
- 前往
https://nodejs.org/en/
。 - 下载 Windows 版本(x64)。为了这本书,我下载了 v6.10.3.LTS。msi)。
- 运行安装程序;为此:
- 双击下载的文件;它应该位于您的下载文件夹中,名称类似于 node-v6.10.3.x64。
- 安装程序窗口应该出现“按下运行按钮,这将打开 Node.js 安装向导,然后按下一步按钮。
- 将出现许可协议,您需要同意安装 Node.js 的许可。如果同意,请选中复选框并按下一步按钮。
- 您可以通过按下 next 按钮接受默认设置,直到您看到 finish 按钮,默认设置会将 Node.js 安装到一个默认目录。
- 按“完成”按钮完成安装。
- 出现提示时,让应用程序对您的设备进行更改。
- 重新启动计算机。
- 按照上面“检查是否安装了 Node.js”一节中的说明,检查 node.js 和 npm 是否已安装。
Install Node.js On a Mac
在 Mac 上,有多种方法可以安装 Node.js。最简单的方法是从 Node.js 网站下载应用程序。虽然这是最简单的方法,但它也有缺点。它安装 Node.js 的方式意味着您可能需要管理员权限来安装补充模块和库。您可以在 install 命令之前使用 sudo 命令安装这些模块和库。sudo 命令为您提供了该安装的管理员权限。sudo 代表超级用户 do,用于 UNIX 风格的操作系统。这允许您以管理员用户的身份安装软件包。
使用 sudo 并不是最佳实践,因为拥有管理员权限意味着您可以对您的计算机进行不必要的更改。使用 sudo 还会影响一些模块的工作方式。
另一种安装 Node.js 的方法是使用节点版本管理器(NVM);它安装它,所以你不需要管理员权限来安装其他模块和库。安装起来稍微困难一些,因为你需要安装 Xcode,还需要一个. bashprofile 文件。还有一个好处:可以很容易地在 Node.js 的不同版本之间进行切换。
从 Node.js 网站安装 Node.js
如果以这种方式安装 Node.js,当您为应用程序安装新模块时可能会出现错误,并且它们不会下载。如果发生这种情况,您需要以管理员权限重新安装该模块。为此,您可以在 install 命令前键入 sudo。然后会提示您输入密码。
- 前往
https://nodejs.org/en/
。 - 下载 macOS 版本(x64);为了这本书,我下载了 v6.10.3.LTS。pkg)。
- 运行安装程序并遵循它的请求,让它安装在默认目录下。
- 让应用程序对您的设备进行更改。
- 重新启动计算机。
- 检查 Node.js 和 npm 是否已安装。
使用节点版本管理器(NVM)安装 Node.js
要安装 NVM,您需要在 Mac 上安装 Xcode 和. bashprofile。
如果没有安装 Xcode,从 app store 安装;这可能需要几个小时。
- 打开一个终端窗口,确保你是在根目录~
$如果不是在提示符下键入 cd。 - 请检查您是否有. bashprofile 文件。在$提示符下,键入 open -a TextEdit.app。bash_profile,如果您有一个. bashprofile,它将打开,或者您可以关闭该文件。
- 如果在提示符下没有. bash_profile,请键入 touch。bash_profile。
- 要在控制台提示符下安装 NVM,请键入:curl
https://raw.githubusercontent.com/creationix/nvm/v0.25.0/install.sh
| bash。 - 要安装 Node.js 的最新稳定版本,请在提示符下键入 nvm install stable。
- 在提示符下,键入 nvm 使用节点。
- 在 Node.js 上安装本书中使用的版本,在提示符下键入 nvm install 6.11.0。
- 打开控制台窗口时,将 6.11.0 版设为默认版本。在提示符下,键入 nvm 别名默认值 6.11.0。
- 开始使用这个版本的 Node.js,并在提示符下键入 nvm use 6.11.0。
- 可以看到已经安装的 Node.js 的版本:type nvm ls。
- 检查 Node.js 和 npm 是否已安装。
你可以在 github 页面github . com/creation IX/nvm
找到更多关于 nvm 以及如何使用它的信息。
创建 Node.js 应用程序
到本章结束时,你将已经构建了一个使用 socket.io 从服务器向连接的浏览器发送更新的小应用程序。您将通过一系列的步骤来完成这一过程,包括创建 web 服务器、创建到 web 页面的路由、web 页面的一些基本样式,以及将数据从服务器发送到浏览器。您将以两种不同的方式从服务器发送数据:首先通过 route 函数发送到网页,然后使用 web 套接字。
要编写和编辑代码,你需要一个专门的文本编辑器。这可以是源代码编辑器或集成开发环境(IDE)。它们很容易下载和安装。有许多可用的代码,如 Sublime Text、Atom 和 Visual Studio 代码。
您需要做的第一件事是为应用程序创建目录。我调用了我的 chapter_02,所以这将是应用程序的名称。
Create The Application Directory
- 打开控制台窗口。
- 在终端键入 cd
/ / ,移动到你想要存储项目的目录。 - 在终端类型 mkdir
中为项目创建一个新文件夹。 - 移入新目录,在终端键入 cd
。
目录结构
当你创建一个 web 应用程序时,你需要一个目录结构。应用程序的主文件夹将是应用程序的根目录。与应用程序相关的所有其他文件和文件夹都应该在该文件夹中。组成应用程序的文件将引用此结构中的文件和文件夹。您将创建其中的一些文件和文件夹,其他文件和文件夹将在初始设置或下载模块时自动创建。
图 2-2 显示了本章的目录结构。package.json 文件将在安装时创建,节点模块文件夹将在您下载新模块时自动创建。“/”字符代表应用程序的根。
图 2-2
The directory structure for the application Using NPM Init To Create An Application
npm 代表节点程序包管理器。它承载了成千上万的可重用代码包,您可以下载并在项目中使用。
npm 还有一个名为 npm init 的命令,这是一种创建 Node.js 应用程序的有用方法。它将询问一系列问题,然后创建一个 package.json 文件。您可以随时按 Ctrl+C 退出该过程。
使用 npm 很容易创建 Node.js 服务器的框架:
- 打开控制台窗口。
- 导航到您将用于应用程序的文件夹。
- 在控制台提示符下,键入 npm init+enter。
- 更改默认答案或按 enter 键接受它们。保持默认入口点为 index.js。
- 在代码编辑器中打开项目文件夹。打开应用程序的根文件夹,因为您希望能够看到连接到项目的所有文件和文件夹。此时,您应该会看到一个文件 package.json。
图 2-3 显示了一个 package.json 文件的例子。
package.json 是一个重要的文件,它保存应用程序的元数据,并用于处理应用程序的依赖关系。当您开始安装库时,package.json 文件中会有对它们的引用。如果您共享您的应用程序文件,您不会包括所有的库文件。因为 package.json 有对它们的引用,所以可以使用 npm install 在复制的位置安装它们。
图 2-3
An example of a package.json file
请注意,名称必须是小写字母,没有空格,但您可以使用下划线和破折号。
Create A Node.js Server
在 Node.js 中,创建一个启动服务器的 JavaScript 文件。它存储在项目的根目录中,并将成为应用程序的入口点。这个文件通常被称为 app.js 或 index.js。当我使用 npm init 创建 package.json 时,我保留了 index.js 条目脚本的默认值,因此现在需要创建 index.js 文件。
在应用程序的根目录下创建一个名为 index.js 的新文件。确保您位于与 package.json 相同级别的应用程序目录中。
在新创建的 index.js 文件中,写入清单 2-1 中的以下代码。
var http = require('http');
var express = require('express');
var app = express();
var server = http.createServer(app);
server.listen(3000, function() {
console.log('Listening on port 3000...');
});
Listing 2-1
index.js code
清单 2-1 显示了使用 Express.js 设置 web 服务器的基本代码。Express.js 不是 Node.js 库的一部分,所以必须马上安装,但首先我将解释表 2-3 中的代码。
代码解释
服务器所需的模块(如 Express.js)被加载到页面顶部,并分配给变量。JavaScript 使用 var 关键字创建变量。
表 2-3
index.js code explained
| `var http = require('http');` | 这一行将 HTTP 接口引入应用程序;这允许服务器通过 HTTP 协议与浏览器通信。 | | :-- | :-- | | `var express = require('express');` | 这包括我们将用来创建服务器和路由的 express 框架。Express 附带了许多功能,使设置节点服务器变得更加容易。它不是 Node.js 库的一部分,因此需要安装。 | | `var app = express();` | 调用 express 应用程序,返回值放在一个变量中。这包含一个新的 express 应用程序。 | | `var server = http.createServer(app);` | 这将创建一个服务器对象,在发出服务器请求时调用该对象。 | | `server.listen(3000, function() {` `});` | 这告诉服务器在端口 3000 上监听对服务器的请求。 | | `console.log('Listening on port 3000...');` | console.log 是一个将消息输出到控制台的 JavaScript 函数。这里用它来告诉你服务器正在运行。 |Using NPM To Install Libraries
此时,如果运行这段代码,就会出现错误。它使用一个名为 Express.js 的外部库,该库不属于 node . js。express . js 使得创建 web 服务器变得容易得多。需要下载它,并在 package.json 中保存对它的引用。
- 打开一个控制台窗口,并确保您与 package.json 文件位于同一目录中。
- 在命令行中键入 NPM install express @ 4 . 15 . 3-save+enter。
- 下载完成后,启动服务器。在控制台中,确保您位于应用程序的根目录,与 index.js 处于同一级别。
如果您在 Mac 上安装 Express.js 时遇到错误,可能是因为您需要管理员权限来安装它。尝试再次安装,这次键入 sudo NPM install express @ 4 . 15 . 3-save,然后在密码提示符下键入计算机的密码。
在控制台中,您现在应该看到监听端口 3000 的控制台日志。
如果您使用的是 Windows,您可能会看到一条安全警报,提示 Windows 防火墙已阻止此应用的某些功能。勾选表示私有网络的方框,如我的家庭或工作网络。
如果您打开 web 浏览器并键入 localhost:3000,您应该会看到:无法获取/
这是因为还没有路由,所以服务器不知道将哪个页面提供给浏览器。您将在一分钟内创建路线。
如果您查看您的应用程序目录,您会看到有一个名为节点模块的新文件夹。这是第一次在应用程序中安装新库时创建的。如果你往里面看,你会看到 Express.js 的文件和文件夹都在里面。
注意使用- save 在 package.json 文件中保存对下载模块的引用。
在本书中,我将使用@来安装新模块。这意味着你将安装我一直在使用的相同版本。没有@它将安装最新的模块。
Creating a Route To a Web Page
使用清单 2-1 中的代码,创建一个将一些文本发送到网页的简单路由,将以下粗体命令添加到您的文件中,这些命令在表 2-4 中有进一步的描述:
var http = require('http');
var express = require('express');
var app = express();
var server = http.createServer(app);
app.get('/', function (req, res) {
res.send("Hi There!");
});
server.listen(3000, function() {
console.log('Listening on port 3000...');
});
代码解释
现在,如果您重新启动服务器,您应该会在网页上看到文本 Hi。
表 2-4
index.js code explained
| `app.get('/', function (req, res) {``res.send('Hi There!');` | 函数 app.get 创建一个到应用程序根目录的路径。/'表示根,它将是主 URL,或者在本例中是 localhost:3000。如果您想将消息发送到不同的网页,例如,发送到 about 页面,您可以使用 app.get('/about ',function (req,res)。 |- 键入 Ctlr+c 停止服务器。
- 在终端中再次键入 node index.js 来重启服务器。
- 刷新 web 浏览器。
添加第二个应用程序。转到第一个应用程序下面。转到清单 2-1 中的代码。
app.get('/about', function (req, res) {
res.send("this is an about page");
});
现在您需要重新启动服务器,以便它选择新的路由:
- 按 Ctlr+c 停止服务器。
- 在终端中再次键入 node index.js 来重启服务器。
- 刷新 web 浏览器并转到 localhost:3000/about。
现在,您应该会在网页上看到“这是一个关于页面”的字样。您可以删除关于路线。
NodeMon(节点名)
每次对服务器进行更改时,您都必须停止并重新启动服务器,以使更改生效。有一个很有用的库叫做 nodemon,它会注意到你对它正在监视的文件的修改,并为你重启服务器。在控制台窗口中使用它很容易安装。它应该全局安装,以便所有 Node.js 应用程序都可以访问它。
Installing Nodemon
- 打开一个控制台窗口,并确保您在主目录;您可以在 Mac 上通过键入 cd 转到主目录,在 Windows pc 上通过键入 cd %userprofile%转到主目录
- 在提示符下键入 npm install nodemon -g (-g 全局安装)。
- 在控制台中,导航到 chapter_02 应用程序的根目录,使用 cd 命令执行此操作,例如 cd Documents/code/chapter_02
- 当您在控制台中位于 Node.js 应用程序的根目录时,通过键入 nodemon 并按 enter 键启动服务器。
现在,如果您对 index.js 文件进行了更改,您可以刷新浏览器并查看更新。Nodemon 自动启动 package.json 中列出的主 JavaScript 文件。
您不需要将它保存到 package.json,因为它不是您的应用程序的一部分;它是开发应用程序时的助手。
创建网页
到目前为止,您已经将数据发送到 web 浏览器,但它只是从路由器打印出一条消息。现在你需要创建一个网页。通常网页是在带有。html 扩展。这些是静态网页。因为您将使用来自服务器的数据更新页面,所以需要创建一个可以接受这些数据的动态页面。
一种方法是为。html 页面向服务器发出 AJAX 请求,然后服务器返回一些数据。这依赖于浏览器页面向服务器请求数据。
另一种更有效的方法是让服务器用它拥有的数据更新网页。在 Node.js 中,这是通过模板引擎完成的。
模板引擎
模板引擎允许您在整个网页中创建变量,服务器可以更新这些变量,而无需网页发出请求。在本书的后面,我们将把数据从 Arduino 传递到服务器。由于网页将使用模板引擎创建,因此该页面将使用新数据自动更新。
有许多不同的模板引擎可以使用,其中一些我已经在附录 b 中列出了。
Set Up The Server
要使用 ejs,需要将一些代码添加到 index.js 文件中,但是首先要将 ejs 添加到项目中:
- 打开控制台窗口并导航到应用程序目录的根目录,或者导航到应用程序目录的根目录。或者,如果服务器在应用程序的根目录下运行,请按 Ctlr + c 停止它。
- 在应用程序根目录下的控制台提示符处,键入 npm install ejs@2.5.6 - save + enter。
现在,您应该能够在 package.json 文件中看到 ejs 了。在项目中包含 ejs 只是一行代码。用粗体代码行更新 index.js 文件(清单 2-1 ):
var http = require('http');
var express = require('express');
var app = express();
var server = http.createServer(app);
app.set('view engine', 'ejs');
app.get('/', function (req, res) {
res.send('Hi There!')
});
server.listen(3000, function() {
console.log('Listening on port 3000...');
});
这一行新代码将允许您在项目中使用 ejs。
到目前为止,你已经在 route 中使用了 res.send。在 route 中使用 res.send 允许你发送简单的数据到一个网页,但是对于大多数应用程序来说,你希望能够发送包含更多信息的页面。为此,您可以使用函数 res.render 这允许您指定将在浏览器中呈现的文件,并向其发送新数据。
将 index.js 文件从代码(清单 2-1 )更改为粗体代码;这添加了几个变量并更改了路由,因此它使用 res.render 而不是 res.send:
....
app.set('view engine', 'ejs');
var title = "Some Arduino components starting with p"
var componentArray = ['potentiometer', 'piezo', 'phototransistor', 'pushbutton'];
app.get('/', function (req, res) {
res.render('index', {
title: title,
components: componentArray
});
});
server.listen(3000, function() {
console.log('Listening on port 3000...');
});
代码解释
新代码从两个变量开始,这两个变量保存要传递给浏览器的数据(见表 2-5 )。
- 在应用程序路由处启动服务器。确保您与 index.js 文件位于同一个文件夹中。在控制台提示符下,键入 nodemon。
- 刷新您的网页,您应该会看到一个错误。
表 2-5
index.js code explained
| `var title = "Some Arduino components starting with p"` | title 变量保存一些文本。在 JavaScript 中,字符串被" "或" "包围。 | | :-- | :-- | | `var componentArray = ['potentiometer', 'piezo', 'phototransistor', 'pushbutton'];` | 在 JavaScript 中,[ ]用于创建数组。数组是数据的集合,可以通过其在数组中的索引(位置)来访问。componentArray 是一个由四个都是字符串的元素组成的数组。JavaScript 数组中的索引从 0 开始。要访问数组的第一个元素,可以使用 componentArray[0],这将返回字符串电位计。componentArray[3]将返回字符串按钮。 | | `res.render('index', {``title: title,``components: componentArray` | 现在不是呈现一个字符串,而是告诉 Node.js 你想要呈现哪个页面。在这种情况下,它是 index.js 文件。然后列出要传递给页面的数据,在本例中是 title 和 componentArray。 |您应该会在页面和控制台窗口中看到如下所示的错误:
错误:无法在视图目录“/Users/indie/Documents/web/book/chapter 2/03 _ set-up-ejs/views”中查找视图“索引”
这是因为现在你要求它找到一个不存在的索引页。
Set Up The Web Page
ejs 页面看起来与 html 页面相似,只是它使用了。您可以将来自服务器的 ejs 页面数据作为变量传递。在页面上,您仍然使用相同的 html 标记,但是您也可以使用 ejs 语法。用 ejs 创建的页面需要放在一个名为 views 的文件夹中。
- 在应用程序的根目录下,创建一个名为 views 的新文件夹。
- 在 views 文件夹中创建一个名为 index.ejs 的新文件。要在控制台窗口中执行此操作,请执行以下操作:
- 在应用程序类型 cd 视图的根
- 在 Mac 上键入 touch index.ejs
- 我们有 Windows pc 类型 nul > index。例如
在 Windows 中,您可能会得到拒绝访问的控制台响应,但文件应该已经创建。
在新创建的 index.ejs 文件中,写入清单 2-2 中的代码。
<!DOCTYPE html>
<html>
<head>
<title>an ejs page</title>
</head>
<body>
<h1>EJS</h1>
<p>This page is an ejs page and will show data from the server</p>
</body>
</html>
Listing 2-2index.ejs code
在控制台窗口中,确保您位于项目的根目录,并重新启动服务器。在浏览器上,转到 http://localhost:3000/现在,您应该可以在网页上看到文本。您用一些简单的 HTML 创建了这个页面。HTML 代表超文本标记语言,是用于创建网页的通用标记语言。我将在第四章中更详细地介绍 HTML。它列出了页面的结构和其中的元素。
Adding Data To The Web Page
服务器已经将来自 res.render 的数据传递给了浏览器。它传递了一个名为 title 的字符串,其中包含文本“一些以 P 开头的 Arduino 组件”和一个名为 components 的数组,其中包含['potentiometer ',' piezo ',' photo tomy ',' pushbutton']。使用 ejs,浏览器现在可以访问这些数据。
用粗体文本更新清单 2-2 中的 index.ejs:
<!DOCTYPE html>
<head>
<title>an ejs page</title>
</head>
<body>
<h1>EJS</h1>
<p>This page is an ejs page and will show data from the server</p>
<h2><%= title %></h2>
<% components.forEach(function(component) { %>
<p>component: <%= component %> </p>
<% }); %>
</body>
</html>
代码解释
,或者是 ejs 库的一部分。
在你写 JavaScript 的时候使用标签,它会在标签里面运行 JavaScript,文本不会出现在页面上。通常,当你想在网页上添加 JavaScript 时,你必须将它封装在脚本标签中,稍后你会用到。EJS 有自己版本的脚本标签,当您编写代码来访问从服务器传来的数据时,可以使用它。
当标签被使用时,它们里面的内容就会出现在页面上。在标签内部,您可以引用从服务器传递到浏览器的变量,并在网页上看到它。参见表 2-6 。
表 2-6
index.ejs code explained
| `<%= title %>
` | 标题是从服务器传递的数据的一部分。如你想看标题你用。您可以将 EJS 包装在任何您喜欢的 HTML 标签中。在这种情况下,ejs 被包装在一个 H2 标签中。 | | :-- | :-- | | 组件: | 它使用一个 JavaScript forEach 函数来迭代从服务器传来的数组数据,并写出数组的每个元素。 |Adding CSS
CSS 代表级联样式表,它用于设计网页上的元素。一个没有 CSS 的网页看起来很基础。虽然 CSS 可以添加到。最佳实践是创建一个单独的。保存样式的 CSS 文件。然后,您需要一个到。中的 css 文件。ejs 页面。
在 web 应用程序中,有一些称为静态文件的文件,它们不是由服务器创建的,而是在网页上使用的。这些文件包括 CSS 文件、图像和 JavaScript 文件。要在. ejs 文件中使用它们,您需要知道从静态文件到。ejs 文件。Express.js 有一个名为 express.static 的中间件函数来帮助解决这个问题。您在应用程序的根目录下创建一个文件夹来保存所有静态文件;该文件夹通常称为公共文件夹。在 index.js 文件中,express.static 函数用于注册公共文件夹。这意味着。ejs 将这个文件夹识别为静态文件的根文件夹,您不必将的绝对路径写到。当你调用 css 文件时。你会写出这样的东西:<link href="/css/main.css" rel="stylesheet" type="text/css">
要将 CSS 添加到页面中,首先创建一个静态文件夹。
- 在应用程序的根目录下,创建一个名为 public 的新文件夹。
- 在这个文件夹中创建另一个名为 css 的文件夹。
- 在 css 文件夹中创建一个名为 main.css 的文件。
- 用下面的更新代码更新 index.js 文件,使其包含静态函数。
现在用粗体代码更新清单 2-1 中的 index.js 文件:
...
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/public'));
var title = "Some Arduino components starting with P"
var componentArray = ['potentiometer', 'piezo', 'phototransistor', 'pushbutton'];
...
server.listen(3000, function() {
console.log('Listening on port 3000...');
});
代码解释
注册静态文件文件夹需要一行代码(表 2-7 )。
表 2-7
index.js code explained
| `app.use(express.static(__dirname + '/public'));` | 这告诉应用程序所有的静态文件都将从一个名为 public 的文件夹中提供。 |现在打开您刚刚创建的 main.css 文件并添加到 css 中(清单 2-3 ):
*{
margin: 0;
padding:0;
}
body{
background-color: #F2F3F4;
font-family: Verdana, Arial, Helvetica, sans-serif;
}
h1, h2, p{
padding: 10px;
}
h1{
background-color: #4ABCAC;
color: white;
}
#components{
margin: 10px;
border: #F78733 solid 2px;
display: inline-block;
}
Listing 2-3main.css
您还需要更新 index.ejs 文件,告诉它在哪里可以找到 CSS 文件。用粗体 HTML 更新 index.ejs 文件(清单 2-2 ):
<!DOCTYPE html>
<head>
<title>an ejs page</title>
<link href="/css/main.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>EJS</h1>
<p>This page is an ejs page and will show data from the server</p>
<h2><%= title %></h2>
<div id="components">
<% components.forEach(function(component) { %>
<p>component: <%= component %> </p>
<% }); %>
</div>
</body>
</html>
因此,在服务器运行的情况下,刷新您的网页,您现在应该会看到新的样式;它使用 HTML 标签和一个 id 来设置内容的样式。
package.json 和版本控制
现在,您已经为您的应用程序安装了几个包,是时候再看一下 package.json 文件了。它保存了关于应用程序的信息,包括您安装的依赖项(模块)的名称及其版本号。
这些依赖关系由不同的人编写,并且在不同的时间更新。这些更新可能会破坏您的代码。语义版本控制用于跟踪变更。这意味着版本号的每个数字都有特定的含义。如图 2-4 所示,版本号由三个数字组成,用句号分隔。每个新版本的数字都会增加,每个数字代表一种不同的更新。
图 2-4
Version control numbers
在 package.json 文件中,您可以看到依赖关系。
{
"name": "set-up-routes",
"version": "1.0.0",
"description": "setting up simple routes",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Indira",
"license": "ISC",
"dependencies": {
"ejs": "².5.6",
"express": "⁴.15.3"
}
}
每个安装的模块旁边是它的版本号。您可能还会看到一个符号,如*或~。当您从 package.json 运行 npm install 时,这些符号为可以下载的版本提供了一些灵活性。
=或 v 这确保安装了完全相同版本的软件包。例如,v2.5.6 将确保下载软件包的版本 2.5.6。
~这修复了主要版本和次要版本,但允许更高的修补版本。例如,2.5.6 将确保安装的版本高于或等于 2.5.6,但低于 2.6.0。
^这修复了主要版本号,但允许不同的次要或修补版本。例如,².5.6 将确保已安装的版本可以大于或等于 2.5.6 且小于 3.0.0。
这是一个通配符,表示可以安装任何版本。比如 2。表示可以安装任何以 2 开头的版本。
Setting Up a WebSocket With Socket.io
现在回到创建应用程序。此时,服务器在加载时传递网页数据。如果数据更新,网页将不会反映这种变化。您可以编写一个脚本,定期 pinged 服务器以查看是否有变化,但这不是很有效;如果没有新数据,您将会浪费调用,并且当新数据到达时,页面将不得不等到下一次调用来更新。
WebSocket 协议解决了这个问题:新数据将直接发送到网页,网页可以将数据发送回服务器,以更新连接到服务器的其他浏览器。这本书将使用 socket.io 库进行 web 套接字调用。
首先,socket.io 需要安装为 socket.io 不随 Node.js 一起安装。
- 打开控制台窗口,导航到应用程序的根目录。
- 在提示符下键入 NPM install socket . io @ 1 . 7 . 3–save。
现在,您可以将 socket.io 包含到 index.js 文件中。Index.js 将不再使用变量 title 和 componentArray 向浏览器发送数据,因此可以删除它们。app.get 函数也被更新,因此变量不再被发送到 index.ejs。更新 index.js 文件,使其与清单 2-4 中的代码匹配,新代码以粗体显示:
var http = require('http');
var express = require('express');
var app = express();
var server = http.createServer(app);
var io = require('socket.io')(server);
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/public'));
app.get('/', function (req, res) {
res.render('index')
});
io.on('connection', function(socket){
console.log('Connection to client established');
socket.on('disconnect',function(){
console.log('Server has disconnected');
});
});
server.listen(3000, function() {
console.log('Listening on port 3000...');
});
Listing 2-4index.js updated
代码解释
Socket.io 改变了数据传递给浏览器的方式;它不再在路由中发送,而是通过套接字发送(表 2-8 )。
表 2-8
index.js updated code explained
| `var io = require('socket.io')(server);` | 这段代码包含 socket.io 并将服务器附加到它上面。 | | :-- | :-- | | `app.get('/', function (req, res) {``res.render('index')` | 这将创建一个从服务器到 URL 根目录下的 index.ejs 页面的路由。这一次,您没有通过路由发送数据。 | | `io.on('connection', function(socket){` `console.log('Connection to client established');` | 当网页连接到服务器时,io.on 函数将告诉套接字做什么。每次浏览器连接到服务器时,您都会看到一个控制台日志。 | | `socket.on('disconnect',function(){``console.log('Server has``disconnected');` | 当浏览器断开与服务器的连接时,该功能将运行。 |Rewrite The Index.ejs File To Include Socket.io
index.ejs 需要显示来自套接字的数据。您不再需要显示来自服务器的数据的 CSS 或许多 HTML 组件。有新的 HTML 组件将显示来自套接字的数据。套接字使用 JavaScript。index.ejs 文件中必须有一个对应的 socket 引用 index.js 中的 socket,所以 index.ejs 中必须有一个对 socket.io 的引用标签用于将 JavaScript 代码添加到 index.ejs 中(见表 2-9 )。用清单 2-5 中的代码更新 index.ejs,注意,index.ejs 以前版本中的很多代码已经被删除了。
<!DOCTYPE html>
<head>
<title>WebSockets</title>
</head>
<body>
<div class="wrapper">
<h1>Using socket.io</h1>
<p>This page will update with socket.io</p>
</div>
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script>
var socket = io();
</script>
</body>
</html>
Listing 2-5index.ejs
代码解释
在控制台中,确保您位于应用程序的根目录并启动应用程序。
尝试在不同的浏览器和浏览器标签上打开和关闭页面,并查看控制台。每次有到服务器的新连接时,您应该看到到客户机的连接已经建立。每次通过关闭页面来关闭到服务器的连接时,您应该会看到服务器已经断开连接。
表 2-9
index.ejs code explained
| | 在 socket.io 库中调用网页;没有这个页面就不能访问库。 | | :-- | :-- | | var socket = io(); | 为 socket.io 函数创建一个变量。 |插座如何工作
Socket.io 有许多广播和监听数据的函数。socket.emit 广播数据,socket.on 监听数据。
这些函数在服务器和浏览器上使用一对匹配的 id。这些匹配的 id 对将相互监听更新,并相互发送数据。
其结构是:
socket.emit('an_example_id', message);
socket.on('an_ example_id', function(message){
Do something with the message from socket.emit
});
socket.emit 将用匹配的 id 将数据发送给函数 socket.on。Socket.on 将侦听来自 socket.emit 的具有匹配 id 的数据。
这意味着您可以拥有多个具有不同 id 的套接字,并且不同套接字之间的数据不会混淆。
Sending Data To a Web Page With Socket.io
现在,您将在服务器和浏览器页面上创建一个简单的套接字,在它们之间传递信息。网页上会有一个按钮,当它被点击时会更新一个数字。按钮已被点击的消息将通过 socket.io 发送到服务器。号码将被改变,然后服务器端的 socket.io 将信息发送回连接的网页。
在 index.js 中添加粗体代码:
var http = require('http');
var express = require('express');
var app = express();
var server = http.createServer(app);
var io = require('socket.io')(server);
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/public'));
app.get('/', function (req, res) {
res.render('index')
});
var buttonValue = 0;
io.on('connection', function(socket){
console.log('Connection to client established');
io.emit('clicked message', buttonValue);
socket.on('clicked message', function(msg){
buttonValue = 1 - buttonValue;
io.emit('clicked message', buttonValue);
console.log('Received message from client!',msg);
});
socket.on('disconnect',function(){
console.log('Server has disconnected');
});
});
server.listen(3000, function() {
console.log('Listening on port 3000...');
});
代码解释
表 2-10 分解了你刚刚添加的代码。
表 2-10
index.js code explained
| `var buttonValue = 0;` | 这个变量包含一个值,当有人点击浏览器上的一个按钮时,这个值就会改变。 | | :-- | :-- | | `socket.on('clicked message', function(msg){``buttonValue = 1 - buttonValue;``io.emit('clicked message', buttonValue);``console.log('Received message from client!', buttonValue);` | 在这段代码中,套接字 id 是“clicked message”。此套接字将侦听由函数 io.emit('clicked message ',msg)从浏览器发送的消息。当它收到一个时,它将执行指令 button value = 1-button value;这会将 buttonValue 的值更改为零或一。然后,它将使用 io.emit('clicked message ',buttonValue)将新值发送给监听更改的 web 浏览器。 |清单 2-5 中的 index.ejs 也需要用粗体代码进行更新:
<!DOCTYPE html>
<head>
<title>WebSockets</title>
</head>
<body>
<div class="wrapper">
<h1>Using socket.io</h1>
<p>This page will update with socket.io</p>
<button id="clicked">click me</button>
<div id="updates"></div>
</div>
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script>
var socket = io();
var button = document.getElementById('clicked');
button.onclick = function(e){
socket.emit('clicked message', 'clicked');
}
socket.on('clicked message', function(msg){
document.getElementById('updates').innerHTML = msg;
});
</script>
</body>
</html>
代码解释
您现在应该有一个可以与网页交互并更新网页的工作服务器(表 2-11 )。如果你点击页面上的按钮,它将会更新,同时也会更新具有相同 URL 的其他页面;您还应该在控制台中看到一条消息。
表 2-11
index.ejs code explained
| `var button = document.getElementById('clicked');` | 这行代码是一些基本的 JavaScript。在 HTML 中有一个 id 为“clicked”的按钮元素。变量按钮将保存对该元素的引用,因此可以在 JavaScript 中引用它。 | | :-- | :-- | | `button.onclick = function(e){``socket.emit('clicked message','clicked');` | onclick 是当点击网页上的按钮时执行的功能。函数 socket.emit('clicked message ',' clicked ');叫做。这将把“已点击”的消息传递给服务器的对应 socket.io 函数 socket.on(“已点击的消息”)。 | | `socket.on('clicked message', function(msg){document.getElementById('updates').innerHTML = msg; });` | 这段代码监听来自服务器的 Id 为“clicked message”的消息,当它得到一个消息时,它使用 JavaScript 函数 document.getElementById 在页面上查找 id 为“updates”的元素,并将其内部 html 更改为从服务器传入的数据。 |摘要
本章向您介绍了 web 技术,以及如何创建 web 服务器来与 web 浏览器收发数据。
在下一章中,您将使用这些技能来创建一个服务器,该服务器将从 Arduino 导入数据并在网页上显示它。
三、Arduino 到前端:第一部分
在第二章中,你学习了如何用 Node.js 创建一个 web 服务器,并使用它向网页发送数据。在本章中,您将开始从 Arduino 向 Node.js 服务器发送数据,并在网页上使用这些数据。
数据将来自连接到 Arduino 的开关按钮,并通过串行端口进入您的计算机。您可以将这些数据导入 Node.js 服务器并在网页上使用。本章结束时,您将拥有一个带有彩色方块的网页,每次按下 Arduino 按钮,方块都会改变颜色。图 3-1 是你在本章结束时所做的一个例子。
图 3-1
Two possible outcomes of the exercise in Chapter 3
串行端口介绍
串行端口将数据一个接一个地以单个位的形式传入和传出计算机。一个位的值可以是 0(低/关/假)或 1(高/开/真)。这些位可以连接在一起传输更复杂的数据,不同的位数有不同的名称。八位是一个字节,一千字节(KB)是 1024 字节(1024 x 8 位),一兆字节(MB)是 1024 千字节。由于这些位只能是 0 或 1,所以它们被称为二进制数据。
使用 Arduino,您可以通过 USB 端口在电脑上来回发送串行数据。每个 Arduino 都有一个串口,有的不止一个。Arduino Uno 使用 RX(引脚 0)和 TX(引脚 1)进行通信。如果使用串行接口,则不能将任何东西连接到管脚 0 和 1。Arduino IDE 有一个内置的串行监视器来查看串行数据。
当您将 Arduino 连接到电脑时,它将连接到电脑的一个串行端口。您需要知道它连接到哪个端口,因为您需要在 Node.js 应用程序中引用它。
查找串行端口
在 Mac 和 Windows PC 上,串行端口号看起来略有不同。在 Mac 上,它看起来像这样:/dev/tty。
有多种方法可以找到 Arduino 连接的串行端口:
- 连接 Arduino 后,打开 Arduino IDE。在菜单中单击工具菜单,然后将鼠标悬停在端口菜单上;您将看到所有设备都连接到串行端口,Arduino 的串行端口看起来像这样:/dev/tty . USB modem
(Arduino/Uno)在 Mac 上,它看起来像 COM 。 - 在 Mac 上,打开终端窗口,键入 ls /dev/tty.usbmodem*。您应该得到类似/dev/tty.usbmodem
的输出。 - 在 PC 上,打开设备管理器并打开端口(COM & LPT)菜单,您应该会看到类似 Arduino Uno COM
的内容。
串行数据和 Arduino
有许多功能可以帮助您在 Arduino 之间传输串行数据。他们使用一个叫做串行的库。表 3-1 显示了库中可用的一些功能。
表 3-1
Arduino serial functions
| 命令 | 结果 | | :-- | :-- | | `Serial.begin(9600)` | begin 函数设置串行数据的传输速率;它以每秒的位数来衡量,称为波特率。 | | `Serial.end()` | 表示串行通信结束,并释放引脚 RX 和 TX,以便用于其它输入和输出。 | | `Serial.write()` | 将二进制数据写入串行端口。 | | `Serial.println()` | 打印出串行数据。 |波特率
波特率设置通过串行端口传输数据的速率。它是以每秒位数来衡量的。Arduino 可以使用的速率有 300、600、1200、2400、4800、9600、14400、19200、28800、38400、57600 或 115200。您可以设置的波特率的最大速度取决于您的设备。如果设备不能处理更高的速度,那么一些数据将不会被注册,您将会丢失数据。速率 9600 是 Arduino 的常用波特率。
Setting Up The Arduino Circuit
在本章中,您将把一个开关按钮连接到一个 Arduino,并使用串行功能来确定按钮是否被按下。
本章中 Arduino 的设置将使用 Arduino Uno、试验板、开关按钮、220 欧姆电阻器和跳线。图 3-2 显示了您需要的套件。
图 3-2
1. Breadboard, 2. Switch button, 3. 220 ohm resistor, 4. Arduino
一旦你准备好了工具包,你需要如图 3-3 所示进行设置,并将其连接到你电脑上的 USB 端口。连接组件时,请确保 Arduino 没有连接到电脑或任何其他电源。
图 3-3
The circuit setup Write The Arduino Code
当 Arduino 连接到电脑时,打开 Arduino IDE。在 IDE 中有两件事情需要设置:连接的板的类型和它连接的端口。以下是 IDE 的设置:
- 在 Arduino IDE 菜单中,选择工具/电路板,然后选择 Arduino/Genuino Uno。
- 在工具/端口菜单中选择 Arduino 连接的端口,它会显示类似于 PC 上的 COM3 (Arduino/Genuino Uno)或 Mac 上的/dev/Cu . USB modem 621(Arduino/genu ino Uno)的内容。
然后选择文件/新建打开一个新文件。将文件另存为 chapter_3。复制清单 3-1 中的代码。
int switchButton = 2;
void setup() {
Serial.begin(9600);
pinMode(switchButton, INPUT);
}
void loop() {
int buttonState = digitalRead(switchButton);
if(buttonState == HIGH){
Serial.println("1");
}else{
Serial.println("0");
}
delay(500);
}
Listing 3-1chapter_3 code
代码解释
表 3-2 描述了清单 3-1 中的代码。
表 3-2
chapter_3.ino code explained
| `int switchButton = 2;` | 这段代码创建了一个变量来保存输入到 Arduino 中的开关数。在 Arduino 上,它连接到数字引脚 2。 | | `Serial.begin(9600);` | 该功能设置数据传输的波特率。 | | `pinMode(switchButton, INPUT);` | pinMode 是一个为引脚设置模式的功能,INPUT 是默认设置,用于接收数据。它被传递给保存数字 pin 号的 switchButton 变量。 | | `int buttonState = digitalRead(switchButton);` | 变量 buttonState 保存来自按钮所连接的数字引脚 2 的数据。如果按钮被按下,它将为高电平,否则为低电平。 | | `if(buttonState == HIGH){` `Serial.println("1");` `}else{` `Serial.println("0")` `;` | if 语句检查 buttonState 是否为高。如果是,按钮被按下,Serial.println 将向串行端口发送“1”。如果不是,else 语句将改为发送“0”。 | | `delay(500);and` | 由于代码处于循环中,您可以延迟循环的再次开始。如果不这样做,代码可能在循环再次开始之前还没有执行完,您可能会丢失数据。延迟函数使用毫秒,500 是半秒。你需要在延迟中找到平衡点;你不想丢失数据,但如果你让延迟太长,你可能会错过按钮被按下。 |Run The Arduino Code
单击勾号图标检查代码是否正确,然后单击箭头图标将代码发送到 Arduino。
代码上传后,在 IDE 中单击打开串行监视器;在图 3-4 中以红色显示。
图 3-4
How to open the serial port monitor
您应该开始在串行端口窗口中看到数据。它可能不是你期望看到的 0 或 1,但如果串行端口监视器中的波特率与代码中的波特率不匹配,就会发生这种情况。图 3-5 显示了可以在串行端口窗口中更改的位置。转到此下拉菜单,将比率更改为 9600。当按钮被按下时,你应该看到一系列的 1 被打印出来;否则输出应该是 0。
图 3-5
The drop-down changes the baud rate.
注意在使用使用串行端口的 web 应用程序之前,您需要关闭 Arduino IDE 中的串行端口监视器。如果不这样做,您将得到一个错误消息,说明端口已经在使用中。
使用前端的数据
现在您可以在 Arduino 中看到串行数据,下一步是将它发送到 Node.js 服务器,以便可以在 web 浏览器上显示。本章中的 Node.js 应用程序将从 Arduino 接收数据,并使用 Socket.io 将数据传递到前端。
串行端口库
您将导入的库之一是 SerialPort 库。这个库允许你通过串口从 Arduino 导入数据到 Node.js。
要使用 Node.js 中的库打开一个端口,您需要包含库的路径并创建一个新的 port 对象。
来自串行端口库的数据是一个缓冲区对象。缓冲区对象是通过串行端口进入的位流(二进制数据)。JavaScript 不能很好地处理二进制数据。SerialPort 有一个 readLine 解析器,可以将二进制数据转换成字符串。代码如下所示:
serialport.parsers.readline("\n")
“\n”是在 JavaScript 中创建新行的方式。readLine 将二进制数据转换成文本行。当它看到换行符时,它知道这是当前数据流的结尾,因此它分离不同的数据流。
串行端口库中有许多函数,但我们将在本书中使用其中的几个。您可以在附录 b 中找到关于 SerialPort 库的更多信息。
下载串行端口库
您将使用 npm 来安装串行端口库。在 PC 上,在使用 npm 安装 SerialPort 之前,您需要下载几个其他的包。在 Mac 上,你将能够下载它,而不需要任何额外的库,所以你不需要做以下步骤。
如果您使用的是 PC,请按照以下步骤下载 SerialPort 库所需的支持库。
- 首先,安装 node-gyp,因为它用于编译 Node.js 中的本机附加模块。你可以在
https://github.com/nodejs/node-gyp#installation
找到更多信息。 - 还需要安装额外的 windows 构建工具。这些必须安装在以管理模式运行的控制台窗口中。通过右键单击 Windows 菜单并选择 CMD.exe(以管理员身份运行)或在搜索栏中键入来打开 CMD.exe。在控制台中键入 NPM install-g-production windows-build-tools。您可以在
https://github.com/felixrieseberg/windows-build-tools
了解更多关于工具的信息;安装可能需要几分钟时间。
Create a Node.js Application
本章的目录结构如下:
/chapter_03
/node_modules
/views
index.ejs
index.js
package.json
首先要为本章创建一个新的 Node.js 应用程序,并安装必要的库。
- 创建一个新文件夹来存放应用程序。我把我的叫做 chapter_03。
- 打开命令提示符(Windows 操作系统)或终端窗口(Mac)并导航到新创建的文件夹。
- 当你在正确的目录键入 npm init 创建一个新的应用程序;您可以按下 return 键浏览每个问题,或者对它们进行更改。
- 您现在可以开始添加必要的库;要在命令行中下载 Express.js,请键入 NPM install express @ 4 . 15 . 3–save。
- 然后安装 ejs,键入 NPM install ejs @ 2 . 5 . 6–save。
- 下载完成后,安装串口。在 Mac 上键入 NPM install serial port @ 4 . 0 . 7-save 在 Windows PC 上键入 NPM install serial port @ 4 . 0 . 7-build-from-source。
- 然后最后安装 socket.io,输入 NPM install socket . io @ 1 . 7 . 3–save。
如果查看 package.json 文件,您应该会看到以下依赖项:
"dependencies": {
"ejs": "².5.6",
"express": "⁴.15.3",
"serialport": "⁴.0.7",
"socket.io": "¹.7.3"
}
现在,您可以为应用程序编写代码了。在 chapter_03 文件夹的根目录下创建一个名为 index.js 的文件,复制清单 3-2 中的代码。
注意在本书中,您将使用 index.js 中的串行端口库。您需要在 index.js 中添加一个对 Arduino 所连接的串行端口的引用。在 Mac 上显示
var http = require('http');
var express = require('express');
var app = express();
var server = http.createServer(app);
var io = require('socket.io')(server);
var SerialPort = require('serialport');
var serialport = new SerialPort('<add in the serial port
for your Arduino>', {
parser: SerialPort.parsers.readline('\n')
});
app.engine('ejs', require('ejs').__express);
app.set('view engine', 'ejs');
app.get('/', function (req, res){
res.render('index');
});
serialport.on('open', function(){
console.log('serial port opened');
});
io.on('connection', function(socket){
console.log('socket.io connection');
serialport.on('data', function(data){
data = data.trim();
socket.emit('data', data);
});
socket.on('disconnect', function(){
console.log('disconnected');
});
});
server.listen(3000, function(){
console.log('listening on port 3000...');
});
Listing 3-2index.js code
记得添加你正在使用的串行端口。如果您现在运行这段代码,将会出现错误。它引用了一个尚未创建的 index.ejs 文件。
代码解释
表 3-3 描述了清单 3-2 中的代码。
表 3-3
index.js explained
| `var SerialPort = require('serialport');` | 这会将 SerialPort 库引入 Node.js 应用程序,并将其存储为一个变量。 | | `var serialport = new SerialPort('Interacting With A Web Page
来自 Arduino 的数据将用于更新网页。每次按下按钮,方块的颜色都会改变。一个变量将跟踪从 Arduino 发送的当前数据。当新数据进入页面时,有一个 JavaScript 函数检查新数据是否与当前数据不同。
如果是,并且数据是字符串“1”,那么该函数将从颜色列表数组中随机选取一个元素。然后更新方块的颜色。它还更新一段文本和当前变量,因此新数据成为当前数据。
如果新数据为“0 ”,正方形的颜色不会改变,但一段文本会更新。当前变量将再次用新数据更新。
您现在需要在 views 文件夹中创建一个 index.ejs 文件;首先在应用程序的根目录下创建一个 views 文件夹,然后在其中创建一个名为 index.ejs 的文件。将清单 3-3 中的代码复制到 index.ejs 文件中。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Get data</title>
</head>
<body>
<h1>Arduino data</h1>
<p>Press the button on the Arduino to change the
color of the square</p>
<p>The button is <span id="button-state"></span> </p>
<svg width="120" height="120" viewBox="0 0 120
120">
<rect id="change-color"
fill="LightSkyBlue"
width="120"
height="120"
/>
</svg>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
var current = "0";
var shape = document.getElementById('change-color');
var buttonState =
document.getElementById('button-state');
var colorArray = ["LightSkyBlue",
"LightSlateGray","DarkOliveGreen", "orange",
"DarkRed", "gold", “purple”];
socket.on("data", function(data){
if(data === "1"){
buttonState.innerHTML = "pressed";
if(data !== current){
var newColor = colorArray[Math.floor(Math.random()*colorArray.length)];
shape.style.fill = newColor;
}
} else{
buttonState.innerHTML = "not pressed";
}
current = data;
});
</script>
</body>
</html>
Listing 3-3index.ejs code
现在,在控制台窗口中,导航到应用程序的路径,并键入 nodemon index.js 或 node index.js 来启动应用程序。打开浏览器并转到 http://localhost:3000/查看应用程序的运行情况。
每次按下按钮,矩形的颜色都会改变。由于数组中的颜色是随机选取的,因此它可能会选取与当前颜色相同的颜色。如果您想确保矩形改变颜色,您可以为当前颜色创建一个变量,并检查新颜色是否不同。
代码解释
表 3-4 描述了清单 3-3 中的代码。
表 3-4
index.ejs explained
| `摘要
您现在应该有一个工作应用程序,当您按下 Arduino 上的按钮时,它的网页会更新。这一章中有很多新概念,第四章将更详细地介绍这些概念。
四、创建 Web 内容简介
在继续之前,理解超文本标记语言(HTML)、级联样式表(CSS)、可缩放矢量图形(SVG)和 JavaScript 是有好处的。这四个概念将贯穿全书,用于创建交互式 web 应用程序、处理数据以及向 Arduino 发送数据和从 Arduino 接收数据。如果你对其中一些领域有信心,就直接跳到你想了解更多的部分。如果你对所有这些科目都感到满意,请跳到第五章。
超文本标记语言
超文本标记语言(HTML)用于在网页上创建内容。如果您创建一个扩展名为. txt 的文件,它可以像许多其他文件类型一样在 web 浏览器中打开,那么您不需要 HTML 来创建内容。使用 HTML 的原因是它提供了你的页面结构。它允许您定义标题和段落,在页面上创建不同的内容块,以及放置图像。结构由 HTML 元素组成;这些元素可以用 CSS 样式化,并与 JavaScript 和 CSS 交互。图 4-1 显示了一个 HTML 段落元素的格式。
图 4-1
The structure of a paragraph element
HTML 元素
HTML 元素通常由开始和结束标记组成。开始标记包含对元素类型的引用和元素的属性。
HTML 中的元素通常是块元素或行内元素。块元素在页面上一个接一个:例如,标题和段落。内联元素在块内工作并格式化元素。
块状元素
有各种各样的块元素可用。表 4-1 列出了其中的一些。
表 4-1
Some HTML block elements
| 命令 | 结果 | | :-- | :-- | | `, , , , , ` | 这些用于创建标题;数字越小,标题越重要。 | | `` | 段落元素用于创建文本段落, | | `- ``
- Orange ``
- Melon ` | 标签创建了一个无序的元素列表;* Element is a list item in the list. | | `
- Orange ``
- Melon ` | 一个
- +添加
- -减号
- *相乘
- /划分
- =给…赋值
- ==等于
- ===强相等,类型和值相同
- !==不等于
- &&逻辑与
- ||逻辑或
- ++增量为 1
- -递减 1
- 打开浏览器,在 Mac 上按 Cmd + Opt + I,在 Windows PC 上按 Ctrl + Shift + I。
- 选择控制台选项卡。
- 在>处键入 2+2,您应该会在下一行看到数字 4。
- 创建一个新文件夹来存放应用程序。我把我的叫做 chapter_05。
- 打开命令提示符(Windows 操作系统)或终端窗口(Mac)并导航到新创建的文件夹。
- 当您在正确的目录中时,键入 npm init 创建一个新的应用程序,您可以按 return 键回答每个问题,或者对它们进行更改。
- 您现在可以开始添加必要的库;要在命令行下载 Express.js,请键入 npm install express@4.15.3 - save。
- 然后安装 ejs,并键入 npm install ejs@2.5.6 - save。
- 下载完成后,安装串口。在 Mac 类型 npm 上安装 serial port @ 4 . 0 . 7-save;在 Windows PC 上键入 npm,安装 serial port @ 4 . 0 . 7-build-from-source。
- 然后最后安装 socket.io,输入 npm install socket.io@1.7.3 - save。
- 创建一个新文件夹来存放应用程序。我叫我的章 _05_lcd。
- 打开命令提示符(Windows 操作系统)或终端窗口(Mac)并导航到新创建的文件夹。
- 当您在正确的目录中时,键入 npm init 创建一个新的应用程序,您可以按 return 键回答每个问题,或者对它们进行更改。
- 您现在可以开始添加必要的库;要在命令行下载 Express.js,请键入 npm install express@4.15.3 - save。
- 然后安装 ejs,键入 npm install ejs@2.5.6 - save。
- 下载完成后,安装串口。在 Mac 上键入 npm 安装 serial port @ 4 . 0 . 7–save,在 Windows PC 上键入 npm 安装 serial port @ 4 . 0 . 7–build-from-source。
- 然后最后安装 socket.io,输入 npm install socket.io@1.7.3 - save。
- 创建一个新文件夹来存放应用程序。我叫我的章 _06。
- 打开命令提示符(Windows 操作系统)或终端窗口(Mac)并导航到新创建的文件夹。
- 当你在正确的目录键入 npm init 创建一个新的应用程序;您可以按下 return 键浏览每个问题,或者对它们进行更改。
- 您现在可以开始添加必要的库;要在命令行下载 Express.js,请键入 npm install express@4.15.3 - save。
- 然后安装 ejs,键入 npm install ejs@2.5.6 - save。
- 下载完成后,安装串口。在 Mac 上键入 npm 安装 serial port @ 4 . 0 . 7–save,在 Windows PC 上键入 npm 安装 serial port @ 4 . 0 . 7–build-from-source。
- 然后最后安装 socket.io,输入 npm install socket.io@1.7.3 - save。
- ``
|
| <div></div>
| div 标签创建一个元素,它是其他元素的容器。它用于定义内容块。 |
内嵌元素
表 4-2 列出了一些 HTML 中可用的行内元素。
表 4-2
Some HTML inline elements
| 命令 | 结果 | | :-- | :-- | | `` | Span 本身不会改变它所环绕的文本,但是 CSS 或 JavaScript 可以使用它来选择一段文本。 | | `` | 将环绕的文本设为斜体。 | | `` | 用于使文本加粗。 | | `` | 给文本加下划线, | | `` | 此元素创建一个换行符;它没有结束标记。 | | `` | 这是一个锚元素,用于创建指向另一个页面的 URL 链接;它还包含另一个页面的 URL。 | | `
Basic HTML Page Structure
所有的网页都有一个基本的结构。
在文本编辑器中,创建一个名为 structure.html 的新 HTML 文件,并复制清单 4-1 中的 HTML。
<!DOCTYPE html>
<html>
<head>
<title>A basic web page</title>
<meta charset="utf-8">
</head>
<body>
<h1>Basics</h1>
<p>This is a basic web page</p>
</body>
</html>
Listing 4-1structure.html
HTML 解释道
清单 4-1 展示了一个网页的基本结构。文档的类型在顶部声明,然后内容被包装在一个 HTML 元素中。表 4-3 解释了清单 4-1 中的一些元素。
表 4-3
structure.html
| `` | 这应该是任何或您的 HTML 文件的开头;它让浏览器知道它正在读取 HTML。 | | `` | 这是 html 元素,它包含了你的网页的所有内容。 | | `` | head 元素包含关于页面的信息,但是它的内容不会出现在页面上。它可以保存关于页面的元数据,比如字符编码,或者保存到 JavaScript 库和 CSS 文件的链接。 | | `您可以在 web 浏览器中打开该页面,方法是从 web 浏览器的菜单中选择“文件”“➤”“打开文件”,然后导航到 structure.html,您将看到一个基本的网页。
HTML 属性
HTML 属性是向元素添加附加信息的一种方式。它们被添加到元素的开始标记中。它们通常是一个键值对,格式为“属性名”=“值”一个例子是链接属性;如果您想创建一个从您的网页到另一个网站的链接,您可以使用 anchor 标记,并在这个标记中添加一个带有其他网站的 URL 值的属性。图 4-2 显示了锚元素的属性。
图 4-2
A link attribute
打开您创建的 structure.html 文件,将下面一行添加到 HTML 的正文中:
<a href = "http://example.com/">go to example.com</a>
当你刷新页面时,你会看到一个到 example.com 的链接。
Note
属性值中不能有空格,但可以有下划线或破折号。
您会经常用到的两个属性是 ID 和 class。这两个属性都允许您为元素创建标识符。该标识符可用于选择 CSS 和 JavaScript 中的元素。
ID 属性
你可以给任何 HTML 元素一个 ID。ID 是该元素的唯一标识符。因为它是唯一的标识符,所以只能在一个页面上使用一次。这是一个具有 ID 的段落元素的示例:
<p id="first_paragraph">This is the text of the first paragraph on a page</p>
类别属性
类属性也是元素的标识符,但不同于 ID,因为它可以添加到页面上的多个元素中。这意味着您可以选择具有相同类的所有元素,并对它们进行更改。这是一个带有类的段落元素的示例:
<p class="first_paragraph">This is the text of the first paragraph on a page</p>
嵌套元素
当你创建一个网页的时候,你会把元素放在元素里面,元素可以放在其他元素里面;这些是嵌套元素。
父元素、子元素和同级元素
HTML 有一个由父元素、子元素和兄弟元素组成的树状结构。子元素从其父元素继承一些样式属性,但也可以覆盖这些属性。以下 HTML 显示了嵌套元素:
<!DOCTYPE html>
<html>
<head>
<title>A basic web page</title>
<meta charset="utf-8">
</head>
<body>
<h1>Basics</h1>
<p>This is a basic web page</p>
<div id="link-viewer">
<div class="a-link">
<h2>a new link</h2>
<a href = "http://example.com/">go to example.com</a>
<p>This is an example web page</p>
</div>
<div class="a-link">
<h2>a new link</h2>
<a href = "http://example.com/">go to example.com</a>
<p>This is an example web page</p>
</div>
</div>
</body>
</html>
图 4-3 描述了不同元素之间的系列链接。
图 4-3
The family of the div “link-viewer”
文档对象模型
当浏览器显示网页时,它已经将 HTML 和 CSS 转换为文档对象模型(DOM)。浏览器首先读入并解析 HTML,然后创建一个树状结构的节点来表示元素。然后它解析 CSS 并将相关的 CSS 组合到 DOM 中的元素。然后,浏览器使用 DOM 创建网页。
浏览器开发工具
大多数浏览器都有帮助开发人员查看页面和调试代码的工具。在 Firefox 和 Chrome 中,你可以在 Mac 上通过按 option + command + i 和 Ctrl + shift + i 打开它们。
尝试在 Firefox 或 Chrome 中打开 structure.html。打开开发者工具;在 Firefox 中点击 Inspector 标签,在 Chrome 中点击 Elements 标签——你可以看到网页的结构。您可以在工具中编辑 CSS 和 HTML,并立即查看更改是如何进行的。当您刷新页面时,它将返回到 HTML 的保存版本。图 4-4 显示了 Firefox 和 Chrome 中开发者工具的标签。
图 4-4
Developer tools in Firefox and Chrome Note
可以用<!-- -->
注释掉 HTML。那些括号内的任何内容都不会出现在页面上。
在大多数文本编辑器中,如果你在一行代码上按下 Ctrl + /键,它会对你的代码进行注释或取消注释。
半铸钢ˌ钢性铸铁(Cast Semi-Steel)
级联样式表(CSS)用于定义网页的外观,并设置页面的布局和样式。这意味着内容(HTML)和样式(CSS)是分离的。有了 CSS,同样的 HTML 看起来会有很大的不同,而没有 CSS 就非常简单。
CSS 还允许您创建动态布局,该布局根据查看页面的设备而变化。这些响应式布局改变 HTML 元素的大小和位置,使它们适合查看者在任何设备上看到它们。
CSS 通过使用标记名或元素的属性来改变网页上元素的样式;它可以挂在这些上面,并为它们附加一个样式。HTML 元素的挂钩叫做选择器。CSS 由规则组成。图 4-5 显示了 HTML body 元素的 CSS 规则的构造。
图 4-5
A CSS rule
大多数浏览器会为 HTML 元素实现一些样式,这些样式会被你的 CSS 覆盖。
Add CSS To HTML
在同一个文件夹中创建两个新文件:一个名为 styles.css,一个名为 styles.css。在 styles.css,复制清单 4-2 中的 HTML。
<!DOCTYPE html>
<html>
<head>
<title>starting CSS</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<h1>Using CSS</h1>
<p>This paragraph text will is styled with CSS so that it is blue.</p>
</body>
</html>
Listing 4-2styles.html
接下来,在 styles.css 中复制清单 4-3 中的代码。
body{
font-family: Verdana, Arial, sans-serif;
}
h1{
color: green;
border: black solid 1px;
}
p{
color: blue;
}
Listing 4-3styles.css
在网页浏览器中打开 styles.html,你会看到 CSS 对 HTML 内容的影响。
您可以在 HTML 的头部编写 CSS,但是创建一个单独的文件并将其链接到 HTML 页面是一个很好的做法。
代码解释表 4-4 解释了 styles.css 和 styles.css
表 4-4
styles.html and styles CSS explained
| `` | 标签用来将 CSS 导入到 HTML 文件中。属性 href 有一个 CSS 文件的路径值。 | | `body{``font-family: Verdana, Arial, sans-serif;` | 为 HTML 页面的主体创建 CSS is 规则。字体系列声明列出了您希望页面使用的字体。如果浏览器没有字体,它将尝试列表中的下一种。 | | `{``parser: SerialPort. parsers.readline('\n')` | 使用 readline 解析数据,并且“\n”创建一个新行来分隔每行数据。 | | `h1{``color: green;``border: black solid 1px;` | 为 H1 元素创建 CSS 规则。有两个声明;color 更改字体颜色,border 在元素周围创建边框。 | | `p{``color: blue;` | 为 p 元素创建一个 CSS 规则,将字体颜色改为蓝色。 |CSS 选择器
在 CSS 选择器中,选择 HTML 页面上的一个或多个元素。有不同类型的选择器。
类型选择器
这些选择器将选择页面上具有相同标签名称的任何 HTML 元素。例如,h1 标记的选择器是:
h1{
background-color: orange;
}
类别选择器
您可以使用 HTML 元素类名作为选择器。类选择器使用元素的类名和句点(。)在它之前;例如,以下规则将选择 HTML 页面上具有类 chosenElement 的所有元素:
.chosenElement{
background-color: orange;
}
ID 选择器
您可以使用 HTML 元素 ID 作为选择器。ID 选择器使用前面带有哈希(#)的元素的 ID;例如,以下规则将选择 HTML 页面上 ID 为 chosenElement 的元素:
#chosenElement{
background-color: orange;
}
属性选择器
您可以通过关键字或关键字和值来选择 HTML 属性。下面的示例显示了正在设置样式的锚点标记的 href 属性:
a[href]{
background-color: orange;
}
通用选择器
通用选择器是一个星号(*),可以放在任何选择器之前,匹配该类型的任何元素;它可以在你的 CSS 开始时作为一个简单的 CSS 规则重置。
*{
margin: 0;
padding: 0;
}
选择器为您提供了大量的控制:在一个 CSS 规则中可以有多个选择器,并且可以选择子元素、同级元素和特定嵌套位置的元素。
这可能会与附加了多种样式的元素混淆。CSS 中的层叠实现了许多规则,让您可以控制什么元素有什么样式。
级联规则
当您有嵌套元素时,一个样式附加到上面的元素,并将向下级联到下面的元素。决定将哪种样式应用于元素取决于一组三个规则:重要性、特异性和源顺序。重要性将战胜特异性和来源顺序,特异性将战胜来源顺序。
特征
专一性看一个选择器有多专一。ID 比类更具体,因为 ID 是唯一的。一个类比一个标签名更具体。
重要
在 CSS 中!重要的可以加在声明的末尾;例如:
color: orange !important;
它将覆盖应用于元素的任何其他颜色规则。如果你和 CSS 有冲突,最好不要使用!重要;只有当样式不能以任何其他方式覆盖时,才在真正必要的时候使用它。
来源订单
在样式表中,你可能有同样重要和特殊的选择器;如果是这种情况,样式表中后面的规则将生效。
盒子模型
CSS 盒子模型形成了页面的布局。页面上的所有元素看起来都有一个方框。盒子模型由内容、填充、边框和边距组成。图 4-6 为箱体模型。
图 4-6
The CSS box model Note
浏览器以不同的速率采用 CSS 规则和 JavaScript。在 caniuse.com 上,你可以查看哪些浏览器支持你想要使用的 CSS 或 JavaScript。
显示布局
有几种类型的显示布局;这些决定了 HTML 元素在网页上相对于其他元素的位置。块布局和内联布局在本章前面已经解释过了。其他包括位置、浮动、内嵌块、Flexbox 和 CSS 网格。
Flexbox 和 CSS grid 是新的,克服了 CSS 布局的许多问题。由于它们是新的,旧的浏览器不支持它们,虽然它们的规则可能会改变,但基本概念不会改变。
网页现在可以在不同大小的不同设备上浏览。当 CSS 第一次被引入时,网页被设计成在计算机屏幕上浏览。可能会有一些不同的尺寸,但没有今天这么多。随着移动设备变得越来越流行,web 开发人员开始为 web 页面创建响应式设计,以便可以根据查看页面的设备的大小和方向来调整相同内容的大小和位置。Flexbox 和 CSS grid 是使 web 内容更加灵活的模块。
CSS 网格有利于布局整个页面;您可以使用它们来创建行和列。Flexbox 非常适合对齐元素块中的内容,如果您只处理列或行。
flex box(flex box)的缩写形式
Flexbox 是在 CSS3 中引入的,目前处于候选人推荐阶段。当对齐元素、排序元素、调整元素大小和定向元素时,它允许布局中的灵活性。
Flexbox 是一个模块,而不是一个 CSS 属性。模块的一些属性是为父容器设计的,而另一些是为子元素设计的。使用 Flexbox,您可以拥有 flex 容器和 flex 项目。
Using Flexbox
创建一个名为 flex.html 的 HTML 文件,并复制清单 4-4 中的代码。
<!DOCTYPE html>
<html>
<head>
<style>
.container {
display: flex;
justify-content: space-between;
flex-direction: row;
}
.item {
background: YellowGreen;
width: 200px;
height: 220px;
margin-top: 10px;
line-height: 220px;
color: white;
font-weight: bold;
font-size: 32px;
text-align: center;
list-style: none;
}
</style>
</head>
<body>
<ul class="container">
<li class="item">box 1</li>
<li class="item">box 2</li>
<li class="item">box 3</li>
</ul>
</body>
</html>
Listing 4-4flex.html
在 web 浏览器中打开“flex.html ”,尝试将“内容对齐”属性更改为“居中”、“伸缩起点”、“伸缩终点”和“环绕空格”,每次刷新页面并查看差异。表 4-5 解释了一些 Flexbox CSS。
CSS 解释道
表 4-5
Flexbox CSS
| `display: flex` | 这将显示模式设置为 Flexbox。 | | `body{` `justify-content: space-between;` | justify-content 属性定义主轴的对齐方式。 | | `flex-direction: row;` | 这设置了内容的方向,一行中有四个值:default、row-reverse、column 和 column-reverse。 |CSS 网格
由于 CSS grid 是新的,它不能在旧的浏览器上工作。CSS grid 将页面分为列和行。您定义列和行的宽度和高度,并定义 HTML 元素将占用多少列和行。
网格线划分每一行和每一列;这些行用于定义 HTML 元素在页面上占据的空间。图 4-7 显示了 CSS 网格上的网格线。
图 4-7
The layout of a CSS grid
三列 1Fr 每列将占据可用空间的 1/4。页面上所有的列都将具有相同的宽度。您可以将元素放置在网格的单元格中。图 4-8 显示了放置在 CSS 网格上的 div 元素。
图 4-8
HTML elements on a CSS Grid Using CSS Grid
创建一个名为 grid.html 的新 HTML 文件,并复制清单 4-5 中的代码,它应该复制图 4-8 。
<!DOCTYPE html>
<html>
<head>
<style>
.wrapper > div {
background-color: YellowGreen;
text-align: center;
color: white;
line-height: auto;
font-weight: bold;
font-size: 32px;
padding-top: 20px;
}
.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 5px;
grid-auto-rows: 100px;
}
.one {
grid-column: 1 / 4;
grid-row: 1;
}
.two {
grid-column: 2 / 4;
grid-row: 2 / 4;
}
.three {
grid-column: 1;
grid-row: 2 / 5;
}
.four {
grid-column: 3;
grid-row: 4;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="one">One</div>
<div class="two">Two</div>
<div class="three">Three</div>
<div class="four">Four</div>
</div>
</body>
</html>
Listing 4-5grid.html
css 解释表 4-6 解释了网格 CSS。
表 4-6
Grid CSS explained
| `.wrapper > div {` | 这将向任何 div 添加样式,这些 div 是带有类“wrapper”的 div 的子 div | | :-- | :-- | | `display: grid;` | 这将显示模式设置为 CSS 网格。 | | `grid-template-columns: repeat(3, 1fr);` | 此属性设置列数以及它们将在页面上使用的宽度比例。Repeat 是一种向多个列添加相同格式的方法,将有 3 列,每列 1fr。 | | `grid-gap: 5px;` | 这是每个网格项之间的间隙。 | | `grid-auto-rows: 100px;` | 设置行高;有许多选项,包括最大含量、最小含量和自动。 | | `grid-column: 1 / 4;` | 这指定了 div 将穿过多少列网格线。在这个例子中,有 3 列和 4 条网格线,所以设置为 1 / 4 的 div 将从第一条网格线开始,穿过整个页面到达第四条网格线。 | | `grid-row: 2 / 5;` | 这指定了 div 将跨越多少行网格线;在这种情况下,它将从第二条网格线开始,到第五条网格线。 |附录 B 列出了一些 Flexbox 和 CSS Grid 的好资源。
颜色
构建 web 应用程序时,颜色可以用多种方式表示。到目前为止,元素都被赋予了颜色和名称。最常见的颜色有一定数量的颜色名称。您不受这些颜色的限制,可以使用精确的颜色值来定义页面上的颜色。您可以通过红、绿、蓝(RGB)值来定义颜色。它们的十六进制值;以及色调、饱和度和亮度(HSL)值。
RGB
RGB 颜色由三个数字组成。第一个数字代表红色,第二个代表绿色,第三个代表蓝色。该数字可以在 0 到 255 之间。在 CSS 中它被写成 rgb(120,0,0),这将是一种深红色。
十六进制的
十六进制颜色由三个十六进制数字组成。十六进制数字使用以 16 为基数的数字系统;它用 16 个符号来代表所有的数字。这些符号是 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,f,在 CSS 中它被写成#780000,这将是一个暗红色。前两个数字(78)代表红色,第二个数字(00)代表绿色,第三对数字(00)代表蓝色。十六进制数 78 在十进制中是 120。
high-speedlaunch 高速快艇
HSL 通过色调饱和度和亮度来定义颜色。在 CSS 中它被写成 hsl(0,100%,47%)。第一个数字是色调,然后饱和度和亮度用百分比表示。
可缩放矢量图形(SVG)
SVG 是一种基于 XML 的标记语言。这是一种描述 2D 矢量图形的方式。SVG 也有优点。它们是可搜索的,并且可以被 JavaScript 引用。它们在缩放时不会丢失质量,并且可以在网页上进行操作和制作动画。您可以使用 SVG 轻松创建基本形状,还可以在 InkScape 和 Illustrator 等软件中创建更复杂的插图,并将插图导出为 SVG 格式。SVG 中定义的一些基本形状是矩形、圆形和直线。
Create An SVG
创建一个新文件,将其命名为 rectangle.svg,并复制清单 4-6 中的代码。
<svg width="120" height="120" viewBox="0 0 120 120"
xmlns:="http://www.w3.org/2000/svg">
<rect x="10" y="10"
width="100" height="100"
fill="orange" fill-opacity="0.8"
/>
</svg>
Listing 4-6rectangle.svg
在浏览器中打开该文件,您应该会看到类似于图 4-9 的内容。
图 4-9
rectangle.svg in a web browser
解释表格 4-7 解释矩形。
表 4-7
rectangle.svg
| `` | 创建一个 SVG 元素。 | | `width="120" height="120" viewBox="0 0 120 120"` | 定义柠檬及其视图框的宽度和高度。 | | `SVG 画布是绘制 SVG 的地方;它有一个定义 SVG 可视区域的视口。视窗之外的任何图形部分都将被剪切或不可见。
视口有一个坐标系统,x 轴和 y 轴从 SVG 左上角的 0,0 开始。正 x 轴从左向右移动,正 y 轴从上向下移动。
SVG 缩放
因为 SVG 的坐标系从左上角的 0,0 开始,所以如果您更改 SVG 的高度,它会在 y 轴上向上缩放到 0。将清单 4-6 中代码的高度值变小,刷新浏览器;矩形的底部已经上移。
有时您希望高度从 SVG 的底部开始缩放,例如,如果您正在制作条形图上的条形动画。使用缩放变换可以做到这一点。打开清单 4-6 中的代码,用粗体进行修改。
<svg width="120" height="120" viewBox="0 0 120 120"
xmlns:="http://www.w3.org/2000/svg">
<rect x="10" y="-110"
width="100" height="100"
fill="orange" fill-opacity="0.8"
transform="scale(1, -1)"
/>
</svg>
SVG 中添加了一个缩放变换。标尺有两个 x 轴和 y 轴参数。通过在 y 轴(第二个参数)上将其缩放-1,矩形的高度与之前相同,但它是在负方向上缩放的。y 位置必须向下移动 120°,这样您就可以看到矩形,因为负比例已经将其向上缩放,现在它位于视图框之外。如果保存代码并刷新浏览器,SVG 看起来应该是一样的。现在尝试降低高度,在浏览器中你会注意到矩形从底部开始缩放。
view box-检视方块
视框允许新的映射到视口坐标系。viewBox 参数为 viewBox = "min-x min-y width height。"min-x 和 min-y 定义了视图框的左上角。如果您将代码中的 viewBox 更改为 viewBox="10 10 120 120 ",结果将是矩形将向左上方移动 10 个像素。坐标 0,0 已经被重新映射,所以左角现在是 10,10。如果您将代码更改为 SVG width = " 120 " height = " 120 " viewBox = " 0 0 60 60 ",则 60 的 viewBox 宽度和高度将映射到 120 的视口宽度和高度;这将放大矩形。
计算机编程
计算机需要理解发送给它的指令,这样它才能执行命令。编程语言是一种编写人类可以理解的指令的方式,并且可以被处理(编译)成计算机可以理解的语言。
有一些概念和规则适用于大多数编程语言;每种语言都有不同的语法和实现,但概念通常是相同的。如果你已经知道一种计算机语言,你会在 JavaScript 中看到相似之处。这一节将描述一些通用的编程概念,以及它们是如何在 JavaScript 中实现的。
变量
变量是程序中数据的命名存储位置。它由一个键和一个值组成。该键可以是该编程语言中非保留字的任何字符串。可以在整个代码中使用变量来代替该值。在一些语言中,你可以创建一旦定义就不能更改的变量,或者定义变量可以保存什么类型的数据。
在 JavaScript 中,你不必定义变量的类型。您使用 var 或 let 关键字;比如 var x = 10 创建一个名为 x 的变量,它保存数值 10。一旦创建了 x,就可以更改它的内容。
经营者
运算符对变量或值执行操作。JavaScript 中的一些常见运算符如下:
类型
类型是在编程语言中表示数据的方式。例如,2 可以是数字类型或字符串类型。不同的语言有不同的类型。字符串、数字和布尔值都是类型。您可以对变量做什么取决于它是什么类型以及它将如何响应操作符。
JavaScript 有七种数据类型:布尔、空、未定义、数字、字符串、符号和对象。
布尔代数学体系的
布尔值可以有 true 或 false 值。如果您有一个带有布尔值的变量,您可以在条件语句中使用它,例如:
var isDay = true;
if(isDay){
console.log("it is daytime");
} else {
console.log("it is nighttime");
}
空
Null 只能有 null 值。您定义了一个变量,并赋予它一个 null 值。必须指定 Null。
不明确的
如果变量已经声明但没有赋值,则变量的值为 undefined。它不同于 Null,因为 Null 必须被赋值,而 undefined 是没有赋值时的值。
数字
在 JavaScript 中,只有一种数字叫做 Number。其值介于-(2 53 -1)和 2 53 -1 之间。浮点数可以表示为 can +Infinity、-Infinity 和 NaN(非数字)。
线
String 类型表示 JavaScript 中的所有文本,无论是单个字符还是段落。字符串由双引号" "或单引号" "限定
标志
符号对 JavaScript 来说是新的;它们是唯一的、不可变的,可以用作对象的键。
目标
这些是您想要组合在一起的数据集合。它们是键/值对的集合。
声明
语句是执行一个动作的简单指令:例如,var x = 1 + 1 是一个语句。执行该语句时,变量 x 将保存 1 + 1 的值。
公式
表达式产生或计算一个值;1+1 是一个表达式。
数据结构
数据结构是组织数据的方式。它们将被分组在一起,并且将有一个从结构中提取数据的过程。
数组和对象是 JavaScript 中的数据结构。一个数组包含多个值,称为数组元素,它们没有键。一个对象可以包含多个键/值对。这些值可以是同一数组或对象中的不同类型。
因为数组中的值没有键,所以通过它们在数组中的位置来引用它们,例如:
var fruits = ["oranges", "peaches", "mangoes", "bananas"];
变量 fruits 包含许多水果名称。通过数组中的位置来访问数组中的元素。数组位置从 0 开始。
Fruits[0]; //returns "oranges"
Fruits[3]; //returns "bananas"
对象由键/值对组成,例如:
var navelOrange = {
fruit: "orange",
color: "orange",
genus: "citrus"
};
您可以使用键来访问这些值,例如:
var navelOrangeType = navelOrange.genus; //returns "citrus"
您还可以向对象添加数据:
navelOrange.pips = "yes";
条件语句
这些语句将在设定的条件下执行代码。比如一个变量等于某个东西。它们通常被称为 if/then 语句或 if/then/else 语句。
在 JavaScript 中,您可以创建 if 语句、if/else 语句或 if/ else if,例如:
var fruit = "orange";
if(fruit === "orange") {
console.log("it is an orange");
}
if(fruit === "orange") {
console.log("it is an orange");
} else if (fruit === "apple"){
console.log("it is an apple");
} else {
console.log("it is not an apple or an orange");
}
环
循环让你一遍又一遍地运行同一段代码,直到某个条件变为真或假。在 JavaScript 中有 for 循环和 while 循环。例如,循环是迭代数组的好方法:
var fruits = ["oranges", "peaches", "mangoes", "bananas"];
for(var i = 0; i < fruits.length; i++){
console.log(fruits[i]);
}
for 循环定义了一个变量 I,它是一个计数器;检查 I 是否小于数组 i < fruits.length 中的元素数;,那么 I 递增,i++;。然后,函数 console.log 打印出数组 fruits 的元素以及当前的计数器编号。当它大于数组的长度时,循环停止运行。
Note
您可以在浏览器的开发工具中测试 JavaScript 代码。
还有 while 循环:
var x = 0;
while(x < 10){
console.log(x);
x++;
}
只要 x 小于 10,while 循环就会运行。输出将是 0–9。
对于循环,你需要确保有一个结束它们的条件。如果没有,循环将永远继续下去。在 while 循环中,x 在每次循环执行时递增,因此它将大于 10。
功能
你可以创建一小段代码来做一件特定的事情,并给它一个名字。这些被称为函数。比如说。如果你有把两个数相加的代码,你可以把它放在一个命名函数中。然后在整个代码中调用它。这意味着您没有重复代码,并且允许您以更容易调试的块的形式创建代码。
在 JavaScript 中,有多种方法可以创建函数,其中一种是通过创建命名函数。例如:
function add(number1, number2){
return number1 + number2;
}
var addUp = add(2, 6); //addUp holds the value of 8
函数的名字是 add,它由两个参数传递,number1 和 number2 它会将这些相加并返回。
你通过名字调用一个函数,在圆括号中输入你想要累加的参数。
范围
作用域是可访问变量的代码范围。如果你在一个函数中创建一个变量,它只在这个函数内部有作用域,如果你试图在函数外部访问它,你会得到一个错误。
摘要
本章深入探讨了组成 web 应用程序的不同组件。它解释了 web 页面背后的概念、构建和样式,以及一些计算机编程的概念;这些将贯穿全书。在下一章中,您将创建一个应用程序来创建一个控制 Arduino 组件的网页。
五、Arduino 前端
到目前为止,您已经使用串行端口向 web 服务器发送数据,但是串行端口可以是双向数据流;它可以发送和接收数据。在本章中,您将开始通过串行端口通过 web 服务器向 Arduino 发送数据。通过交互式网页,您可以控制连接到 Arduino 的组件。
应用程序
在本章中,您将创建两个 web 应用程序。一个将打开和关闭连接到 Arduino 的 LED。图 5-1 显示网页。
图 5-1
The web page for the first application
第二个将是一个应用程序,让你输入文本,然后显示在液晶显示屏上。图 5-2 显示了最终的网页。
图 5-2
The second will be an application that lets you input text that will then be displayed on an LCD screen
LED 网络应用
第一步是构建应用程序的框架,其结构如下:
/chapter_05
/node_modules
/public
/css
main.css
/javascript
main.js
/views
index.ejs
index.js
安装模块时会自动创建节点模块文件夹。创建服务器的设置与第三章相同:
Create a Node.js Server
在应用程序的路径中创建一个名为 index.js 的文件,并复制清单 5-1 中的代码。
var http = require('http');
var express = require('express');
var app = express();
var server = http.createServer(app);
var io = require('socket.io')(server);
app.engine('ejs', require('ejs').__express);
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/public'));
app.get('/', function (req, res){
res.render('index');
});
io.on('connection', function(socket){
console.log('socket.io connection');
socket.on('disconnect', function(){
console.log('disconnected');
});
});
server.listen(3000, function(){
console.log('listening on port 3000...');
});
Listing 5-1index.js code
代码解释
服务器有一个到网页的路由,并且还创建一个 web 套接字。函数 app.get 创建一个路由,以便在应用程序打开时呈现一个索引文件。
Create a Web Page
初始页面将包含两个颜色块,一个红色和一个绿色。当用户点击一个块时,它会变成开或关的颜色。每个颜色块将由一个带有背景色的 div 创建。
如果您还没有创建 index.ejs、main.css 和 main.js 文件,现在就创建;确保它们在正确的目录中。由于 index.ejs 文件中引用了 main.css 和 main.js,因此需要创建它们,即使此时它们中没有任何代码。在 index.ejs 文件中,复制清单 5-2 中的代码。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>lights</title>
<link href="/css/main.css" rel="stylesheet" type="text/css">
</head>
<body>
<header>
<h1>SENDING DATA</h1>
<h2>Getting data from the front end to an Arduino</h2>
</header>
<div id="content">
<div class="container">
<div id="red-block" class="color-block"></div>
<div id="green-block" class="color-block"></div>
<div class="text-block">
<p>Pressing a button will send data to a connected Arduino.</p>
<p>This data will determine which LED is turned on or off.</p>
</div>
</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="javascript/main.js"></script>
</body>
</html>
Listing 5-2index.ejs code
代码解释
每个颜色块都有自己的 div。id 在 CSS 中用于给块着色,而类用于给两个颜色块设置样式。div 中没有子内容。
您可以通过在控制台窗口中导航到应用程序并键入 nodemon index.js 或 node index.js 来启动应用程序,从而检查到目前为止页面的外观。打开浏览器并转到 http://localhost:3000/查看应用程序的运行情况。
Add Style
该样式将被添加到 public/css 文件夹的 main.css 文件中。打开或创建 main.css 文件,并复制清单 5-3 中的代码。
*{
margin: 0;
padding: 0;
}
body{
font-family: "Arial Narrow", Arial, "Helvetica Condensed", Helvetica, sans-serif;
color: #5a5b5a;
background-color: #f4f4f4;
}
h1{
letter-spacing: 1px;
padding: 10px;
direction:rtl;
text-align:justify;
}
h2{
letter-spacing: 0.5px;
padding: 0 0 15px 10px;
}
p{
font-weight: bold;
margin-bottom: 5px;
color: black;
}
header{
border-bottom: 2px solid #5a5b5a;
background-color: white;
}
.container {
display: flex;
flex-wrap:wrap;
margin-top: 40px;
}
#red-block{
background-color: #C80002;
}
.red-block-on{
background-color: #ff0036 !important;
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 12px;
}
#green-block{
background-color: #1f5900;
}
.green-block-on{
background-color: #1eff00 !important;
border: 10px;
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #304701 0 -1px 9px, #89FF00 0 2px 12px;
}
.color-block, .text-block{
margin: 20px;
}
.color-block{
flex-basis:40px;
height: 60px;
border-radius: 8px;
flex-grow: 1;
cursor: pointer;
}
.text-block{
flex-basis:200px;
height: 200px;
flex-grow: 8;
font-size: 18px;
}
Listing 5-3main.css CSS
代码 ExplainedTable 5-1 解释了 main.css 中的 CSS
表 5-1
main.css CSS explained
| `h1{` `letter-spacing: 1px;` `padding: 10px;` `direction:rtl;` `text-align:justify;` | 字母间距增加了字母之间的间距,这可以使大写字母更容易阅读。方向:rtl 是一种将文本方向从右向左改变而不是从左向右的样式。 | | flex-wrap:缠绕; | 当浏览器更小时,这将使 Flexbox 中的项目一个接一个地落下。 | | 。`red-block-on{``background-color: #F00 !important;``box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 12px;` | 单击红色和绿色框时,它们都处于打开状态。这是通过添加一个带有方框阴影的类来创建灯光打开的效果来实现的。!重要信息与背景色一起使用,以覆盖 div 的原始颜色。框阴影用于在 div 周围创建较亮的边缘。 | | `.color-block{``border-radius: 22px;``flex-grow: 1;``cursor: pointer;` | flex-grow:1 命令确定项目将在 Flexbox 中占用多少空间。光标:指针用于当光标悬停在一个框上时改变光标。 |如果您的服务器仍然运行 nodemon,您应该能够刷新页面并看到 CSS 所做的更改。如果您没有使用 nodemon,请重启服务器并刷新页面。
关于 Flexbox 的更多信息
这是一个很好的机会,让我们对 Flexbox 有更多的了解,并了解它的一些价值。表 5-2 列出了一些 Flexbox 值。
表 5-2
Flexbox values
| `flex-direction` | 默认值为 row 这意味着默认情况下,项目将水平放置。 | | `justify-content: flex-start` | 这些项目将堆叠在该行的开头。 | | `align-items:stretch` | 这些物品将被拉伸以填满容器。 | | `flex-wrap: nowrap` | 这些物品将会排成一行。 | | `Flex-shrink: 1` | 允许项目收缩。 | | `flex-grow` | flex-grow 确定该项相对于其他项在 flex 容器中占据的空间量。在清单 5-3 的 CSS 中,两个色块的 flex-grow 值为 1,文本框的 flex-grow 值为 8。文本框将被赋予比颜色块更多的空间。 |Add Interaction
JavaScript 用于在点击每个彩色块时添加动作。JavaScript 将被添加到 public/javascript 文件夹中的 main.js 文件中。打开您之前创建的空 main.js 文件,或者创建一个 main.js 文件,并复制清单 5-4 中的代码。
var redBlock = document.getElementById("red-block");
var greenBlock = document.getElementById("green-block");
redBlock.addEventListener("click", function(){
redBlock.classList.toggle("red-block-on");
});
greenBlock.addEventListener("click", function(){
greenBlock.classList.toggle("green-block-on");
});
Listing 5-4main.js code
代码解释
有两个变量保存对页面上颜色块元素的引用,“redBlock”和“greenBlock”事件侦听器被添加到这些变量中。JavaScript 事件监听器总是监听特定事件的发生。当它发生时,它可以调用一个函数。在这种情况下,该函数改变块的颜色。它通过添加或删除一个名为“红块开”或“绿块开”的类来实现这一点添加或移除类来更改元素是常见的做法。这意味着改变的所有 CSS 都在一个地方。表 5-3 解释 main.js。
表 5-3
main.js explained
| `redBlock.addEventListener("click", function()` | 单击事件侦听器被添加到 redBlock div 中。这个监听器监听 div 被点击。当它被点击时,一个函数被调用。 | | `redBlock.addEventListener("click", function()` | 单击事件侦听器被添加到 redBlock div 中。这个监听器监听 div 被点击。当它被点击时,一个函数被调用。 | | `redBlock.classList.toggle("red-block-on");` | 这行代码使用了两个 JavaScript 函数 classList 和 toggle。classList 通常与 add 或 remove 一起使用来添加或删除类。通过与 toggle 一起使用,它将确定 div 是否具有该类;如果有,它删除它,如果没有,它添加它。 |现在,在控制台窗口中,导航到应用程序的路径,并键入 nodemon index.js 或 node index.js 来启动应用程序。打开浏览器并转到 http://localhost:3000/查看应用程序的运行情况。
当你点击红色或绿色按钮时,你应该会看到它的变化,看起来就像一盏灯被打开或关闭。
Sending Data From The Front End
现在基本的应用程序已经设置好了,是时候从前端向 Arduino 发送数据了。每次在 web 浏览器中按下按钮,数据将被发送到 SerialPort 函数,因此可以通过串行端口将数据发送到 Arduino。
为此,代码将被添加到清单 5-1 和清单 5-4 中。
首先更新清单 5-4 中的 main.js 文件;更新以粗体显示。
(function(){
var socket = io();
var redBlock = document.getElementById("red-block");
var greenBlock = document.getElementById("green-block");
redBlock.addEventListener("click", function(){
var redClick = redBlock.classList.toggle("red-block-on");
socket.emit('red', redClick + "_red");
});
greenBlock.addEventListener("click", function(){
var greenClick = greenBlock.classList.toggle("green-block-on");
socket.emit('green', greenClick + "_green");
});
})();
代码解释表 5-4 解释 main.js 中的代码
表 5-4
main.js update explained
| `(function(){` `})();` | JavaScript 被包装在一个匿名函数中,该函数调用自身。它被称为立即调用函数表达式(IIFE)。它将 JavaScript 保存在一个块中,如果您将代码中的某个内容命名为您正在导入的库,它可以避免冲突。例如,如果您正在导入的库像在您创建的函数中一样有一个名为 greenClick 的变量,您就不必担心;它在自己的命名空间和范围内。 | | `var greenClick = greenBlock.classList.toggle("green-block-on");` | Node.js 应用程序将向 Arduino 发送数据,告诉它根据前端的按钮是处于打开还是关闭模式来打开或关闭 LED。函数 toggle()根据开关的状态返回一个布尔值,真或假。该值可用于确定将按钮置于 on 状态的类是否已应用于 HTML 元素。变量 greenClick 和 redClick 将保存该值。 | | `socket.emit('red', redClick + "_red");` | 当按钮被点击时,socket.emit()函数被触发,向服务器发送关于哪个按钮被点击及其状态的信息。 |最后更新清单 5-1 中的 index.js 文件。更新以粗体显示。
var http = require('http');
var express = require('express');
var app = express();
var server = http.createServer(app);
var io = require('socket.io')(server);
var SerialPort = require('serialport');
var serialport = new SerialPort('<add in the serial port for your Arduino>', {
parser: SerialPort.parsers.readline('\n')
});
app.engine('ejs', require('ejs').__express);
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/public'));
app.get('/', function (req, res){
res.render('index');
});
serialport.on('open', function(){
console.log('serial port opened');
});
io.on('connection', function(socket){
console.log('socket.io connection');
socket.on('red', function(data){
serialport.write(data + 'T');
});
socket.on('green', function(data){
serialport.write(data + 'T');
});
socket.on('disconnect', function(){
console.log('disconnected');
});
});
server.listen(3000, function(){
console.log('listening on port 3000...');
});
删除
大多数增加的内容都是你在前面章节中使用过的代码。SerialPort 库与 Arduino 的端口 id 一起包含在应用程序中。调用 serialport.on()函数打开串口。
前端 JavaScript 代码中有两个链接到 socket.emit()函数的 socket.on()函数。新增的是 serialport.write()函数。
代码解释表 5-5 解释 index.js。
表 5-5
index.js update explained
| `serialport.write(data + 'T');` | 该函数将数据从应用程序发送到串行端口。在这种情况下,来自前端的数据将被发送到串行端口。字符“T”也被添加到数据中。这是一个终结性的角色。它可以是你选择的任何角色。您需要一个终止字符,因为当 Arduino 接收数据时,它不知道数据的结尾是什么。它接收一个数据流,需要知道一段数据是什么,下一段是什么。“T”在 Arduino 程序中使用,让它知道这是一段数据的结尾 |设置 LED
本章中 Arduino 的设置将使用 Arduino Uno、试验板、绿色和红色 LED、两个 220 欧姆电阻和跳线。图 5-3 显示了您需要的套件。
图 5-3
1. Breadboard, 2. A red and a green LED, 3. Two 220 ohm resistors, 4. An Arduino
一旦你准备好了工具包,你需要如图 5-4 所示进行设置,并将其连接到电脑上的 USB 端口。连接组件时,请确保 Arduino 没有连接到电脑或任何其他电源。
图 5-4
The LED circuit The Arduino Code
打开 Arduino IDE。在“工具”菜单中,确保在电路板上选择了正确的 Arduino 类型,并且 Arduino 连接的端口正在注册。
创建一个新的草图并将其命名为 chapter_05,从清单 5-5 中复制代码。
const int redLed = 6;
const int greenLed = 5;
char charRead;
String inputString ="";
void setup() {
Serial.begin(9600);
pinMode(redLed, OUTPUT);
pinMode(greenLed, OUTPUT);
}
void loop() {
if (Serial.available()) {
charRead = Serial.read();
if(charRead != 'T'){
inputString += charRead;
} else {
if(inputString == "true_red"){
digitalWrite(redLed, 1);
} else if(inputString == "false_red") {
digitalWrite(redLed, 0);
} else if(inputString == "true_green") {
digitalWrite(greenLed, 1);
} else if(inputString == "false_green") {
digitalWrite(greenLed, 0);
}
inputString = "";
}
}
}
Listing 5-5chapter_05.ino
验证脚本,然后将其上传到 Arduino。确保 Node.js 应用程序已关闭。如果它仍在运行,代码将不会上传到 Arduino,因为串行端口已经被应用程序使用。
代码解释表 5-6 解释了第五章中的代码。
表 5-6
chapter_05.ino explained
| `const int redLed = 6;` `const int greenLed = 5;` | 有两个常量变量保存两个 led 的数字引脚编号。 | | `char charRead;` | 创建一个 char(单个字符)类型的变量来保存来自串行端口的数据的每个字符。 | | `String inputString ="";` | inputString 是一个 String 类型的变量,它将用于连接通过特定数据的串行端口进入的所有字符。 | | `if (Serial.available()) {` | if 语句检查是否有串行数据进入 Arduino。 | | `charRead = Serial.read();` | 调用 Serial.read()函数从串行端口获取数据。数据将是单个字符,存储在变量 charRead 中。 | | `if(charRead != 'T'){``inputString += charRead;` | if 语句检查 charRead 是否与字符“t”不相等。如果不相等,则意味着当前字符不是终止字符,所以它被添加到 inputString 中。 | | `else{` | 如果字符是“T ”,则意味着所有当前数据都已收到,并且触发了 else 语句。 | | `if(inputString == "true_red"){``...``digitalWrite(greenLed, 0);` | else 语句中有一系列 if 语句,它们检查数据是什么,并确定应该打开还是关闭哪个灯。 | | `inputString = "";` | 一旦数据被用来打开或关闭 LED,inputString 被重置为空,因此它为下一条数据做好了准备。 |一旦程序被上传到 Arduino,重启 Node.js 服务器并转到 http://localhost:3000/。当您点击红色或绿色按钮时,电路上相应的按钮应该打开或关闭。
LCD Web 应用程序
本章中的第二个应用程序将文本数据发送到 Arduino,这些数据将在液晶显示器(LCD)上显示。应用程序将需要一个新目录,其结构如下:
/chapter_05_lcd
/node_modules
/public
/css
main.css
/javascript
main.js
/views
index.ejs
index.js
为应用程序创建框架应用程序:
为目录创建文件夹,也可以创建文件。
Note
使用 npm init 时,名称不能包含大写字母。
创建服务器
Node.js 服务器与清单 5-1 的最终版本几乎相同。为 chapter_05_lcd 应用程序打开或创建一个 index.js 文件,并复制清单 5-6 中的代码。
var http = require('http');
var express = require('express');
var app = express();
var server = http.createServer(app);
var io = require('socket.io')(server);
var SerialPort = require('serialport');
var serialport = new SerialPort('<add in the serial port for your Arduino>', {
parser: SerialPort.parsers.readline('\n')
});
app.engine('ejs', require('ejs').__express);
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/public'));
app.get('/', function (req, res){
res.render('index');
});
serialport.on('open', function(){
console.log('serial port opened');
});
io.on('connection', function(socket){
console.log('socket.io connection');
socket.on('input-text', function(data){
serialport.write(data + 'T');
});
socket.on('disconnect', function(){
console.log('disconnected');
});
});
server.listen(3000, function(){
console.log('listening on port 3000...');
});
Listing 5-6index.js
有一个名为“input-text”的新 socket.on()函数 id。这将监听具有相同 id 的 socket.emit,它将位于前端。它会将文本发送到 Arduino。
Create The Web Page
网页将非常简单,一个文本框和一个输入按钮。在 views 文件夹中打开或创建 index.js 文件,并复制清单 5-7 中的代码。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>text</title>
<link href="/css/main.css" rel="stylesheet" type="text/css">
</head>
<body>
<header>
<h1>SENDING TEXT</h1>
<h2>Getting data from the front end to an Arduino</h2>
</header>
<div id="content">
<input type="text" id="input-text" placeholder="add text" maxlength="32">
<input id="send-text" type="submit" value="send text">
<div class="text-block">
<p>The text sent from this text box will appear on an LCD connected to an Arduino.</p>
<p>There is a text limit on this box of 32 charaters.</p>
</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="javascript/main.js"></script>
</body>
</html>
Listing 5-7index.ejs
HTML 使用输入框。在 HTML 中有许多创建表单的元素。这些表单可以发送到服务器进行处理。
代码解释表 5-7 解释了 index.ejs 中的代码。
表 5-7
index.ejs explained
| `` | 这通过使 type = "text "创建了一个文本输入。它有保存默认文本的占位符文本。与 maxLength 一起发送的还有一个字符限制。 | | `` | 这个输入是一个提交按钮,它的类型被设置为提交。它有一个值会出现在按钮上。 |Make The Web Page Interactive
在 public/javascript 文件夹中打开或创建一个 main.js 文件,并将清单 5-8 中的代码复制到其中。
(function(){
var socket = io();
var sendTextButton = document.getElementById("send-text");
sendTextButton.addEventListener("click", function(){
var sendText = document.getElementById("input-text").value;
socket.emit('input-text', sendText);
});
})();
Listing 5-8main.js
代码解释表 5-8 解释 main.js 中的代码。
表 5-8
main.js explained
| `var sendTextButton = document.getElementById("send-text");` | 变量 sendTextButton 保存对输入按钮的引用。 | | `sendTextButton.addEventListener("click", function(){` | 点击功能被添加到输入按钮。 | | `var sendText = document.getElementById("input-text").value;` | 当单击输入按钮时,变量将获得文本输入框中的值。 | | `socket.emit('input-text', sendText);` | 输入框中的文本通过 socket.emit 函数发送到服务器。 |Style The Page
这个应用程序的 CSS 与清单 5-3 有些不同。它不使用 Flexbox,并且有输入样式。在 public/css 文件夹中打开或创建一个 main.css 文件,并复制到清单 5-9 中的 css 中。
*{
margin: 0;
padding: 0
}
body{
font-family: Arial, "Helvetica Condensed", Helvetica, sans-serif;
color: #3a3b3a;
background-color: #F4F4F4;
}
h1{
letter-spacing: 1px;
padding: 10px;
}
h2{
letter-spacing: 0.5px;
font-size: 19px;
padding: 0 0 15px 10px;
color: #E37222;
}
p{
font-weight: bold;
margin-bottom: 5px;
}
header{
border-bottom: 2px solid #07889B;
background-color: white;
}
#content {
margin: 40px;
}
input[type=text], select {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #E37222;
border-radius: 12px;
box-sizing: border-box;
}
input[type=submit] {
font-size: 14px;
width: 100%;
background-color: #07889B;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
border-radius: 12px;
cursor: pointer;
}
.text-block{
width: 100%;
font-size: 18px;
margin-top: 20px;
}
Listing 5-9main.css
CSS 解释表 5-9 解释了 main.css 中的 CSS。
表 5-9
main.css explained
| `input[type=text], select {` | 这将选择输入文本框。 | | `display: inline-block;` | 具有内嵌块的元素可以有宽度和高度。 | | `box-sizing: border-box;` | 这使得盒子的角变圆。 | | `input[type=submit] {` | 这将选择提交按钮。 |设置液晶显示器
液晶屏上有很多控制组件的针脚。有一个寄存器选择(rs)引脚;这控制了数据在 LCD 存储器中的位置。Enable (en)引脚允许写入寄存器。共有八个数据引脚(d0–D7)。
电位计也是电路的一部分。这会改变屏幕的对比度。该应用所需的设备如图 5-5 所示。
图 5-5
1.breadboard, 2. 10k ohm potentiometer, 3. A 220 ohm resistor, 4. An Arduino, 5. An LCD screen
Arduino 的设置如图 5-6 所示。确保 Arduino 在组装时没有连接电源。
图 5-6
Setup for the LCD The Arduino Code
打开 Arduino IDE 并创建一个新的草图。将 Arduino 连接到您的计算机,并确保它出现在端口中并且是正确的板。将清单 5-10 中的代码复制到新草图中。保存为 chapter_05_lcd.ino,验证,然后上传到 Arduino。
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
char charRead;
String inputString = "";
String outputString = "";
String newOutputString = "";
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
}
void loop() {
if (Serial.available()) {
charRead = Serial.read();
if(charRead != 'T'){
inputString += charRead;
} else {
lcd.clear();
outputString = inputString;
inputString = "";
}
}
if(newOutputString != outputString){
lcd.print(outputString);
newOutputString = outputString;
}
lcd.scrollDisplayLeft();
delay(500);
}
Listing 5-10chapter_05_lcd.ino
代码解释表 5-10 解释了 chapter_05_lcd.ino 中的代码
表 5-10
chapter_05_lcd.ino explained
| `#includeArduino 更新后,在控制台窗口中转至应用程序的根目录,通过键入 nodemon index.js 或 node index.js 启动应用程序。打开 web 浏览器并转至 http://localhost:3000/。在框中键入一些文本,然后发送;这将需要几秒钟,但它会出现在液晶显示屏上。
使用电位计改变屏幕的对比度。如果您在屏幕上看不到任何东西,可能是对比度被调低了。
注意,由于该设置中有相当多的焊线,如果您没有在屏幕上显示数据,或者如果屏幕上有奇怪的字符,请检查所有的焊线。同时转动电位计,因为这会改变屏幕的对比度。
摘要
在本章中,您开始从服务器向 Arduino 发送数据以更改组件。现在您知道了使用 web 服务器向 Arduino 发送数据和从 Arduino 接收数据的主要原理。下一章将结合这些知识创建一个项目,将数据从 Arduino 发送到网页,并使用该信息更新连接到 Arduino 的组件。
六、Arduino 到前端:第二部分
本章深入探讨了 Arduino 组件如何与网页元素交互。您将使用模拟和数字数据、JavaScript 数据结构以及对数据的简单计算。在本章结束时,您将创建一个使用电位计的交互式应用程序来回答基于 web 的问题,使用数据进行计算,并在 Arduino 上将其可视化。
模拟和数字信号
Arduinos 具有数字和模拟引脚,可以发送和接收模拟或数字信号。模拟信号是连续可变的;数字信号以固定的单位计数。图 6-1 显示了一个模拟和数字信号。
图 6-1
An analog and digital signal
一个数字信号有有限数量的值,有限数量的范围内的步骤。模拟信号是连续可变的。因此,数字信号可以记录 11 或 12,而模拟信号可以记录 11 和 12 之间的任何数字。
Arduino 既有模拟引脚,也有数字引脚。analogRead 功能允许您从模拟引脚读入数据,analogWrite 功能允许您使用该数据控制其他元件,例如 LED 的亮度。
analogRead 函数的范围是 0 到 1023,analogWrite 函数的范围是 0 到 255。read 函数将输入电压映射为 0 到 1023 之间的值。必须映射读取的数据,使其符合模拟写入的范围。
应用程序
在本章中,您将创建一个事件反馈应用程序。它可以让你从一个活动的参与者那里得到反馈,并计算出这个活动有多成功。屏幕上会出现一些问题,这些问题将使用物理电位计来回答。然后,这些数据将被用于计算活动的成功程度,显示在网页上,并被发送回 Arduino。
Set Up The Arduino
事件计量应用电路将由两个电位计和一个按钮组成。电位计用于回答问题,按钮用于提交数据。你需要两个电位计、一个 Arduino、一个按钮和导线。组成如图 6-2 所示。
图 6-2
Components for the application: 1. Breadboard, 2. 2 x potentiometers, 3. Button, 4. Arduino Uno
如图 6-3 所示,将组件连接到 Arduino。
图 6-3
The setup for the circuits The Arduino Code
打开 Arduino IDE 并创建一个名为 chapter_06 的新草图,然后复制清单 6-1 中的代码。
const int analogInA0 = A0;
const int analogInA1 = A1;
const int pushButton = 2;
bool lastButtonState = 0;
int a0Value = 0;
int a0LastValue = 0;
int a1Value = 0;
int a1LastValue = 0;
String a0String = "A0";
String a1String = "A1";
String pushButtonString = "BP";
void setup(){
Serial.begin(9600);
pinMode(pushButton, INPUT_PULLUP);
}
void loop(){
int buttonStateUp = digitalRead(pushButton);
a0Value = analogRead(analogInA0);
a1Value = analogRead(analogInA1);
a0Value = map(a0Value, 0, 1023, 0, 10);
a1Value = map(a1Value, 0, 1023, 0, 10);
a0LastValue = CheckValue(a0Value, a0LastValue, a0String);
a1LastValue = CheckValue(a1Value, a1LastValue, a1String);
if(lastButtonState != buttonStateUp){
lastButtonState = buttonStateUp;
if(buttonStateUp == false){
Serial.println(pushButtonString + a0Value + "," + a1Value);
}
}
}
int CheckValue(int aValue, int aLastValue, String aString){
if(aValue != aLastValue){
Serial.println(aString + aValue);
aLastValue = aValue;
}
return aLastValue;
}
Listing 6-1
Chapter_06.ino code
将 Arduino 连接到电脑,编译并上传代码。
代码解释
在这张草图中,有两个电位计连接到模拟输出 A0 和 A1。还有一个按钮连接到数字输出 2。
该代码向服务器发送两种类型的数据,一种类型是旋转电位计,另一种类型是按下按钮。在每个循环中,来自每个电位计的值被发送到一个名为 CheckValue 的函数。
该函数检查值是否已经改变。如果有,它会将新值发送到串行端口。该函数在一个循环中被调用两次,以检查每个电位计。每个电位计都有一个标识符字符串,该字符串与其值一起发送到串行端口。为了检查该值是否已经改变,该函数需要前一个循环中的电位计值以及当前循环中的值。
在每次循环中还会检查按钮的状态。如果它发生变化,并且变化是被按下,则每个电位计的当前值与数据连接到按钮按下的标识符一起被发送到串行端口。表 6-1 详细解释了代码。
表 6-1
Chapter_06.ino explained
| `const int analogInA0 = A0;``const int analogInA1 = A1;` | 有三个变量保存电位计和按钮的引脚编号参考。 | | `bool lastButtonState = 0;` | 这个变量保存按钮的状态,0 代表向上,1 代表向下。 | | `int a0Value = 0;``int a0LastValue = 0;``int a1Value = 0;` | 这些变量将保存电位计的当前值和最终值。 | | `String a0String = "A0";``String a1String = "A1";` | 每个交互式组件都有一个参考字符串。 | | `int buttonStateUp = digitalRead(pushButton);` | 按钮的当前状态在每个循环开始时被记录。 | | `a0Value = analogRead(analogInA0);` `a1Value = analogRead(analogInA1);` | 每个电位计的值被读入一个变量。 | | `a0Value = map(a0Value, 0, 1023, 0, 10);` `a1Value = map(a1Value, 0, 1023, 0, 10);` | 这些值被映射。这是因为电位计值介于 0 和 1023 之间,对于应用,它们需要介于 0 和 10 之间。 | | `a0LastValue = CheckValue(a0Value, a0LastValue, a0String);` `a1LastValue = CheckValue(a1Value, a1LastValue, a1String);` | 电位计的值被发送到一个名为 CheckValue 的函数。检查值传递了三个参数:当前值、最后一个值和标识符字符串 | | `int CheckValue(int aValue, int aLastValue, String aString){``if(aValue != aLastValue){``Serial.println(aString + aValue);``aLastValue = aValue;``}``return aLastValue;` | CheckValue()函数有一个 if 语句,用于检查新值是否不同于旧值(使用不等于)。如果不同,则调用 Serial.println()函数,传递字符串标识符(“A0”或 A1”)。最后一个值被更改为当前值,并从函数中返回。 | | `if(lastButtonState != buttonStateUp){` | 这个 if 语句检查按钮的状态是否已经改变。 | | `lastButtonState = buttonStateUp;` | 如果按钮状态已经改变,则更新变量 lastButtonState 以反映该改变。 | | `if(buttonStateUp == false){``Serial.println(pushButtonString + a0Value + "," + a1Value);` | 如果 buttonStateUp 为 false,则意味着按钮被按下;如果它是向下的,那么标识符“BP”与两个电位计的值连接,这段数据通过串行端口发送。 |注意与 JavaScript 函数不同,函数的返回值需要在。ino 文件。到目前为止,有两个功能,设置和循环。这两个都不返回任何内容,所以它们以关键字 void 开头。在本章中,将调用函数 CheckValue。它返回一个整数,所以当它被定义时,int 关键字出现在函数名之前:int CheckValue(int aValue,int aLastValue,String aString)。
Node.js 应用程序
当电位计转动时,网络应用程序将收到数据。web 应用程序将需要计算出哪个电位计被转动,然后更新网页。图 6-4 显示了应用程序的外观。
图 6-4
The event feedback front end
应用程序的目录结构如下所示:
/chapter_06
/node_modules
/public
/css
main.css
/javascript
main.js
/views
index.ejs
index.js
Set Up The Node.js Server
本章中的 Node.js 服务器的设置方式与前几章相同:
Create a Node.js Server
Node.js 服务器类似于前面的章节。主要区别在于,来自 Arduino 的数据可以通过电位计或按钮发送。这意味着数据必须根据输入进行清理,并发送给正确的函数。在应用程序的根目录下,创建一个名为 index.js 的文件,并复制清单 6-2 中的代码。
var http = require('http');
var express = require('express');
var app = express();
var server = http.createServer(app);
var io = require('socket.io')(server);
var SerialPort = require('serialport');
var serialport = new SerialPort('<add in the serial port for your Arduino>', {
parser: SerialPort.parsers.readline('\n')
});
app.engine('ejs', require('ejs').__express);
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/public'));
app.get('/', function (req, res){
res.render('index');
});
serialport.on('open', function(){
console.log('serial port opened');
});
io.on('connection', function(socket){
console.log('socket.io connection');
serialport.on('data', function(data){
console.log(data);
var dataKey = data.slice(0,2);
var dataString = data.slice(2);
dataString = dataString.replace(/(\r\n|\n|\r)/gm,"");
if(dataKey === "BP"){
var dataArray = dataString.split(",");
console.log(dataArray);
socket.emit("button-data", dataArray);
} else {
var dataObject = {
dataKey: dataKey,
dataString: dataString
}
console.log(dataObject);
socket.emit("bar-data", dataObject);
}
});
socket.on('disconnect', function(){
console.log('disconnected');
});
});
server.listen(3000, function(){
console.log('listening on port 3000...');
});
Listing 6-2
index.js code
删除
代码解释
服务器需要处理两种不同类型的数据,电位计数据和按钮数据。创建了两个套接字,每个套接字对应一种数据类型,ID 分别为“条形数据”和“按钮数据”
这段代码中使用了一些新的函数和概念。它们与转动电位计或按下按钮有关,并使用这些数据创建数据结构。
控制台日志有助于了解哪些数据被发送到前端。表 6-2 解释 index.js。
表 6-2
index.js explained
| `serialport.on('data', function(data){` `console.log(data);` | 通过串行端口传递的数据可能来自电位计或按钮。如果电位计连接到 A0,数据将类似于“A03”,其中“A0”是标识符,“3”是电位计的数据。如果是连接到 A1 的电位计,它将类似于“A13”如果按钮被按下,那么数据将类似于“BP2,4”“BP”是按钮的标识符,“2,4”是按钮传递的数据。 | | `var dataKey = data.slice(0,2);` | slice()是一个删除 JavaScript 字符串元素的 JavaScript 函数。当函数被传递(0,2)时,它从字符串的索引 0 开始切片,并切片两个字符。这两个切分字符存储在 dataKey 变量中。 | | `var dataString = data.slice(2);` | 通过向 slice 函数传递一个参数,将返回从该索引点到字符串末尾的字符。 | | `dataString = dataString.replace(/(\r\n|\n|\r)/gm,"");` | JavaScript 中的 replace 函数可以用另一个字符替换某个字符。在这种情况下,字符串末尾有一个新行,需要用空字符串""替换。第一个参数是正则表达式(\r\n|\n|\r)/它在字符串上查找任何类型的回车符或换行符,而替换它的第二个参数是空字符串" "。 | | `if(dataKey === "BP"){``...``} else {``...` | 数据键定义了 Arduino 上的交互内容。有一个 if/else 语句来检查组件。if 语句检查 dataKey 变量包含的内容。如果是“BP ”,则表示按钮已被按下,如果不是,则表示电位计已被转动。 | | `var dataArray = dataString.split(",");` | 函数的作用是:将一个字符串分割成一个数组。参数“”告诉 split 根据什么来拆分字符串,在本例中是一个逗号。这里的结果将是一个数组,其中包含电位计的两个数字;比如[ '3 ',' 4' ]。 | | `socket.emit("button-data", dataArray);` | 这会将数组发送到一个名为“button-data”的套接字。 | | `var dataObject = {``dataKey: dataKey,``dataString: dataString` | 如果使用 else 语句,则数据来自电位计。在这种情况下,您需要传递一个标识符来显示哪个电位计被转动过,以及数据。创建一个名为 dataObject 的对象,该对象可以与标识符和数据一起传递到前端。 | | `socket.emit("bar-data", dataObject);` | 然后,这些数据通过一个名为“bar-data”的套接字发送出去。 |注意正则表达式用于搜索字符串中的不同字符。它们由描述搜索模式的已定义集合符号组成。例如,使用\n 匹配换行符,使用\r 匹配回车符。
Create The Front End
本章中的前端将做许多事情;它将向 Arduino 用户提供反馈,并显示之前用户所说的信息。首先,您将创建一个提供 Arduino 输入反馈的网页。在应用程序的根目录下创建一个 views 文件夹,并在其中创建一个文件。从清单 6-3 中复制 HTML。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>data</title>
<link href="/css/main.css" rel="stylesheet" type="text/css">
</head>
<body>
<header>
<h1>EVENT METRICS</h1>
<h2>getting information through an Arduino</h2>
</header>
<div id="content">
<h2>AT TONIGHTS EVENT DID YOU ...</H2>
<p>Answer the questions by turning the knobs, to submit your answer press the button.</p>
<div id="bar-A0" class="container">
<div class="bar">
<p>talk to someone new?</p>
<div class="response-container">
<p class="flex-item">not really</p>
<p class="flex-item">loads</p>
</div>
<svg width="400" height="20" viewBox="0 0 400 20">
<rect id="A0"
x="0"
y="0"
fill="#6BCAE2"
width="320"
height="20"/>
</svg>
</div>
<div class="text-block">
<h3>Current Input<h3>
<p></p>
</div>
<div class="text-block-response hidden">
<h3>Thanks<h3>
</div>
</div>
<div id="bar-A1" class="container">
<div class="bar">
<p>find out something new new?</p>
<div class="response-container">
<p class="flex-item">not really</p>
<p class="flex-item">loads</p>
</div>
<svg width="400" height="20" viewBox="0 0 400 20">
<rect id="A1"
x="0"
y="0"
fill="#6BCAE2"
width="320"
height="20"/>
</svg>
</div>
<div class="text-block">
<h3>Current Input<h3>
<p></p>
</div>
<div class="text-block-response hidden">
<h3>Thanks<h3>
<p></p>
</div>
</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="javascript/main.js"></script>
</body>
</html>
Listing 6-3index.ejs code
代码解释
服务器需要处理两种不同类型的数据,电位计数据和按钮数据。创建了两个套接字,每个套接字对应一种数据类型,ID 分别为“条形数据”和“按钮数据”
前端需要向用户反馈他们与 Arduino 的交互;这包括关于电位计值的信息和反馈,以让他们知道按钮按压已经被记录。
要注意的主要事情是有两个非常相似的 HTML 块,每个电位计一个。它们都有相同的结构,有一个类“容器”、“栏”、“文本块”和“文本块响应”以及一个 SVG。每个模块还具有 id 参考,作为其可视化电位计的参考。表 6-3 详细介绍了 index.ejs 中的代码。
表 6-3
index.ejs explained
| `
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~