File: network.rst

package info (click to toggle)
firmware-microbit-micropython 1.0.1-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 25,448 kB
  • sloc: ansic: 83,496; cpp: 27,664; python: 2,475; asm: 274; makefile: 245; javascript: 41; sh: 25
file content (302 lines) | stat: -rw-r--r-- 11,778 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
Network
-------

It is possible to connect devices together to send and receive
messages to and from each other. This is called a network. A network of
interconnected networks is called an internet. The Internet is an internet
of all the internets.

Networking is hard and this is reflected in the program described below.
However, the beautiful thing about this project is it contains all the common
aspects of network programming you need to know about. It's also remarkably
simple and fun.

But first, let's set the scene...

Connection
++++++++++

Imagine a network as a series of layers. At the very bottom is the most
fundamental aspect of communication: there needs to be some sort of way for
a signal to get from one device to the other. Sometimes this is done via a
radio connection, but in this example we're simply going to use two wires.

.. image:: network.png

It is upon this foundation that we can build all the other layers in the
*network stack*.

As the diagram shows, blue and red micro:bits are connected via crocodile
leads. Both use pin 1 for output and pin 2 for input. The output from one
device is connected to the input on the other. It's a bit like knowing which
way round to hold a telephone handset - one end has a microphone (the input)
and the other a speaker (the output). The recording of your voice via your
microphone is played out of the other person's speaker. If you hold the
phone the wrong way up, you'll get strange results!

It's exactly the same in this instance: you must connect the wires properly!

Signal
++++++

The next layer in the *network stack* is the signal. Often this will depend
upon the characteristics of the connection. In our example it's simply
digital on and off signals sent down the wires via the IO pins.

If you remember, it's possible to use the IO pins like this::

    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)

The next step involves describing how to use and handle a signal. For that we
need a...

Protocol
++++++++

If you ever meet the Queen there are expectations about how you ought to
behave. For example, when she arrives you may bow or curtsey, if she offers her
hand politely shake it, refer to her as "your majesty" and thereafter as
"ma'am" and so on. This set of rules is called the royal protocol. A protocol
explains how to behave given a specific situation (such as meeting the
Queen). A protocol is pre-defined to ensure everyone understands what's going
on before a given situation arises.

.. image:: queen.jpg

It is for this reason that we define and use protocols for communicating
messages via a computer network. Computers need to agree before hand how to
send and receive messages. Perhaps the best known protocol is the
hypertext transfer protocol (HTTP) used by the world wide web.

Another famous protocol for sending messages (that pre-dates computers) is
Morse code. It defines how to send character-based messages via on/off signals
of long or short durations. Often such signals are played as bleeps. Long
durations are called dashes (``-``) whereas short durations are dots (``.``).
By combining dashes and dots Morse defines a way to send characters. For
example, here's how the standard Morse alphabet is defined::

    .-    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

Given the chart above, to send the character "H" the signal is switched on four
times for a short duration, indicating four dots (``....``). For the letter
"L" the signal is also switched on four times, but the second signal has a
longer duration (``.-..``).

Obviously, the timing of the signal is important: we need to tell a dot from a
dash. That's another point of a protocol, to agree such things so everyone's
implementation of the protocol will work with everyone elses. In this instance
we'll just say that:

* A signal with a duration less than 250 milliseconds is a dot.
* A signal with a duration from 250 milliseconds to less than 500 milliseconds is a dash.
* Any other duration of signal is ignored.
* A pause / gap in the signal of greater than 500 milliseconds indicates the end of a character.

In this way, the sending of a letter "H" is defined as four "on" signals that
last no longer than 250 milliseconds each, followed by a pause of greater than
500 milliseconds (indicating the end of the character).

Message
+++++++

We're finally at a stage where we can build a message - a message that actually
means something to us humans. This is the top-most layer of our *network
stack*.

Using the protocol defined above I can send the following sequence of signals
down the physical wire to the other micro:bit::

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

Can you work out what it says?

Application
+++++++++++

It's all very well having a network stack, but you also need a way to
interact with it - some form of application to send and receive messages.
While HTTP is interesting *most* people don't know about it and let their
web-browser handle it - the underlying *network stack* of the world wide web
is hidden (as it should be).

So, what sort of application should we write for the BBC micro:bit? How should
it work, from the user's point of view?

Obviously, to send a message you should be able to input dots and dashes (we
can use button A for that). If we want to see the message we sent or just
received we should be able to trigger it to scroll across the display (we can
use button B for that). Finally, this being Morse code, if a speaker is
attached, we should be able to play the beeps as a form of aural feedback while
the user is entering their message.

The End Result
++++++++++++++

Here's the program, in all its glory and annotated with plenty of comments so
you can see what's going on::

    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 = ''

How would you improve it? Can you change the definition of a dot and a dash so
speedy Morse code users can use it? What happens if both devices are sending at
the same time? What might you do to handle this situation?

.. footer:: The image of Queen Elizabeth II is licensed as per the details here: https://commons.wikimedia.org/wiki/File:Queen_Elizabeth_II_March_2015.jpg