网络

将设备连在一起以相互发送和接收消息,这被称为网络。互联网络称为互联网。 互联网是所有互联网组成的互联网。

建立网络很难,这在下面描述的程序中会有所反映。 然而,这个项目(建立网络)的美妙之处在于它包含了你需要了解的网络编程的所有常见状况。 这也极为简单和有趣。

但是首先,让我们设置以下场景…

连接(网络)

把网络想象为一系列的层。 其中,最底层是沟通最基础的层面,即需要以某种方式让信号能从一个设备传送到另一个设备。 有时可以通过无线电连接完成,在下面这个例子中,我们只需要使用两根导线。

../_images/network.png

在此基础上,我们可以在 网络堆栈(network stack) 中构建所有其他层。

如图所示,蓝色和红色的micro:bit通过鳄鱼线连接。两者都使用引脚(pin)1输出,引脚(pin)2输入。将一个设备中的“输出”连接到另一个设备中的“输入”。这有点像知道该如何正确握着电话听筒 — 一端有麦克风(输入端),另一端有扬声器(输出端), 通过麦克风录制的声音从另一个人的扬声器中播放出来。如果你拿反了手机,就会得到奇怪的结果!

在这种情况下完全一样:你必须正确连接导线!

信号

网络堆栈 的下一层是信号,这通常取决于连接的特性。在我们这个例子中,只是通过引脚IO由导线发送的数字开关信号。

如果你还记得,我们可以像这样使用引脚IO:

pin1.write_digital(1)  # switch the signal on
pin1.write_digital(0)  # switch the signal off
input = pin2.read_digital()  # read the value of the signal (either 1 or 0)

下一步是描述如何使用和处理信号,为此,我们需要一套…

协议

如果遇到女王,你就会对你的举止有所期待。例如,当她到达时,你可能会鞠躬或行屈膝礼;如果她礼貌地与你握手,就称她“女王陛下”,然后称“女士(夫人)”等。这套规则被称为皇家礼仪(注:计算机语言中我们使用“协议”一词)。协议解释了在特定情况下应如何表现(如遇见女王的情况),它是被预先定义的,以确保每个人都能在给定情况出现之前理解发生了什么。

../_images/queen.jpg

正是出于这个原因,我们定义并使用协议来通过计算机网络传递信息。计算机需要预先同意发送和接收消息的规则。可能最有名的协议是万维网使用的超文本传输协议(HTTP)。

另一个著名的传递信息的协议(早在计算机之前)是摩尔斯电码(摩斯密码)。 它定义了如何通过长或短的时通时断(on/off)的信号发送基于字符的信息。 通常这些信号会以哔哔声播放。长称为划 (-) ,短称为点 (.)。 通过组合划和点,摩尔斯电码定义了一种发送字符的方法。例如,下面是标准摩尔斯电码字母表:

.-    A     .---  J     ...   S     .----  1      ----.  9
-...  B     -.-   K     -     T     ..---  2      -----  0
-.-.  C     .-..  L     ..-   U     ...--  3
-..   D     --    M     ...-  V     ....-  4
.     E     -.    N     .--   W     .....  5
..-.  F     ---   O     -..-  X     -....  6
--.   G     .--.  P     -.--  Y     --...  7
....  H     --.-  Q     --..  Z     ---..  8
..    I     .-.   R

根据上面的图表,发送字符 “H” ,信号在短时间内接通四次,表示四个点 (....)。字母 “L” 也是接通四次信号,但第二个信号是“长” (.-..)。

显然,信号的时间掌握很重要,我们需要区别点和划。这是协议的另一个要点,即达成共识以使每个人实施该协议时都能与其他人共同协作。在此情况下,我们规定:

  • 持续时间<250毫秒的信号用 (.)表示;
  • 持续时间≥250毫秒且<500毫秒的信号用 (-) 表示;
  • 其他情况的信号都被忽略;
  • 信号中的暂停/间隙>500毫秒表示一个字符的结束。

用这个方法,发送字母“H”被定义为四个“接通(on)”信号,每个持续时间不超过250毫秒,随后是大于500毫秒的暂停(指示该字符结束)。

信息

我们终于到了建立信息的阶段 — 信息实际上对我们人类来说意义重大。它是 网络堆栈 的最高层。

使用上面的协议,我可以将下面的信号序列沿着连接的导线传递给另一个micro:bit:

...././.-../.-../---/.--/---/.-./.-../-..

你知道它传递了什么信息吗?

应用程序

尽管拥有网络堆栈非常好,但还是需要一个能够与之交互的方式-某种用于发送和接收信息的应用程序。虽然HTTP很有趣, 大多数 人并不知道它,只是留给web浏览器处理—万维网的 网络堆栈 是隐藏的(正如它所应该的那样)。

因此,我们该为BBC micro:bit写什么样的应用程序呢?从使用者的角度来说,它又该如何工作呢?

显然,要发送信息,你需要输入点和划(这个我们用按钮A来实现)。如果我们希望看到发送或刚收到的信息,则需要能够触发该信息并使之在显示屏上滚动(这个我们用按钮B来实现)。最终,我们做成了摩尔斯电码。如果连接了扬声器,我们应该能够在用户输入他们的信息时播放哔哔声以作为听觉反馈的一种形式。

最终结果

下面这个程序写得非常精彩,并且带有大量的注释,以方便大家理解:

from microbit import *
import music


# A lookup table of morse codes and associated characters.
MORSE_CODE_LOOKUP = {
    ".-": "A",
    "-...": "B",
    "-.-.": "C",
    "-..": "D",
    ".": "E",
    "..-.": "F",
    "--.": "G",
    "....": "H",
    "..": "I",
    ".---": "J",
    "-.-": "K",
    ".-..": "L",
    "--": "M",
    "-.": "N",
    "---": "O",
    ".--.": "P",
    "--.-": "Q",
    ".-.": "R",
    "...": "S",
    "-": "T",
    "..-": "U",
    "...-": "V",
    ".--": "W",
    "-..-": "X",
    "-.--": "Y",
    "--..": "Z",
    ".----": "1",
    "..---": "2",
    "...--": "3",
    "....-": "4",
    ".....": "5",
    "-....": "6",
    "--...": "7",
    "---..": "8",
    "----.": "9",
    "-----": "0"
}


def decode(buffer):
    # Attempts to get the buffer of Morse code data from the lookup table. If
    # it's not there, just return a full stop.
    return MORSE_CODE_LOOKUP.get(buffer, '.')


# How to display a single dot.
DOT = Image("00000:"
            "00000:"
            "00900:"
            "00000:"
            "00000:")


# How to display a single dash.
DASH = Image("00000:"
             "00000:"
             "09990:"
             "00000:"
             "00000:")


# To create a DOT you need to hold the button for less than 250ms.
DOT_THRESHOLD = 250
# To create a DASH you need to hold the button for less than 500ms.
DASH_THRESHOLD = 500


# Holds the incoming Morse signals.
buffer = ''
# Holds the translated Morse as characters.
message = ''
# The time from which the device has been waiting for the next keypress.
started_to_wait = running_time()


# Put the device in a loop to wait for and react to key presses.
while True:
    # Work out how long the device has been waiting for a keypress.
    waiting = running_time() - started_to_wait
    # Reset the timestamp for the key_down_time.
    key_down_time = None
    # If button_a is held down, then...
    while button_a.is_pressed():
        # Play a beep - this is Morse code y'know ;-)
        music.pitch(880, 10)
        # Set pin1 (output) to "on"
        pin1.write_digital(1)
        # ...and if there's not a key_down_time then set it to now!
        if not key_down_time:
            key_down_time = running_time()
    # Alternatively, if pin2 (input) is getting a signal, pretend it's a
    # button_a key press...
    while pin2.read_digital():
        if not key_down_time:
            key_down_time = running_time()
    # Get the current time and call it key_up_time.
    key_up_time = running_time()
    # Set pin1 (output) to "off"
    pin1.write_digital(0)
    # If there's a key_down_time (created when button_a was first pressed
    # down).
    if key_down_time:
        # ... then work out for how long it was pressed.
        duration = key_up_time - key_down_time
        # If the duration is less than the max length for a "dot" press...
        if duration < DOT_THRESHOLD:
            # ... then add a dot to the buffer containing incoming Morse codes
            # and display a dot on the display.
            buffer += '.'
            display.show(DOT)
        # Else, if the duration is less than the max length for a "dash"
        # press... (but longer than that for a DOT ~ handled above)
        elif duration < DASH_THRESHOLD:
            # ... then add a dash to the buffer and display a dash.
            buffer += '-'
            display.show(DASH)
        # Otherwise, any other sort of keypress duration is ignored (this isn't
        # needed, but added for "understandability").
        else:
            pass
        # The button press has been handled, so reset the time from which the
        # device is starting to wait for a  button press.
        started_to_wait = running_time()
    # Otherwise, there hasn't been a button_a press during this cycle of the
    # loop, so check there's not been a pause to indicate an end of the
    # incoming Morse code character. The pause must be longer than a DASH
    # code's duration.
    elif len(buffer) > 0 and waiting > DASH_THRESHOLD:
        # There is a buffer and it's reached the end of a code so...
        # Decode the incoming buffer.
        character = decode(buffer)
        # Reset the buffer to empty.
        buffer = ''
        # Show the decoded character.
        display.show(character)
        # Add the character to the message.
        message += character
    # Finally, if button_b was pressed while all the above was going on...
    if button_b.was_pressed():
        # ... display the message,
        display.scroll(message)
        # then reset it to empty (ready for a new message).
        message = ''

你将如何改进它? 你能否改变点和划的定义,以便敏捷的莫尔斯电码用户可以使用它? 如果两个设备同时发送会发生什么? 你可以做些什么来处理这种情况?