LEARN.PARALLAX.COM
Published on LEARN.PARALLAX.COM (https://learn.parallax.com)
Home > Cybersecurity: Radio Data

Cybersecurity: Radio Data

What it’s about

In Cybersecurity: Radio Basics, micro:bit modules exchanged text messages.  There are other micro:bit radio applications where the transmitter and receiver need to exchange more than just text; they need to exchange data.  Some cyber:bot examples of data might include values like left and right robot wheel speeds with run times, or x and y accelerometer tilt values for tilt robot control.

This tutorial shows how to make micro:bits exchange data.  The approach is similar to how apps and servers exchange data, with packets containing key-value pairs.  Thanks to MicroPython’s dictionary and string features, this easy and powerful technique takes just a couple lines of code.

Before you start

You will need:

  • Two or more micro:bit modules (on or off a cyber:bot)
  • Two USB A to MicroB cables
  • One or more computers with:
    • Access to python.microbit.org
    • An up-to-date Chrome or Microsoft Edge browser.
    • A total of at least two available USB Ports.  If you have two computers, each computer only needs one available port.  If you have one computer, make sure it’s got two available USB ports.

Complete these tutorials first:

  • Computer – micro:bit Talk
  • Dictionary Primer
  • Cybersecurity: Radio Basics

After you finish

You will be able to write MicroPython scripts that make micro:bit modules send and receive packet data containing key-value pairs.  Your scripts that receive messages will be able to access values based on their keys, copy them to variables, and use them to calculate, decide, repeat, and other familiar programming tasks to make your apps work.

After this, you will also be ready to move on to the next tutorial (coming soon!):

  • Navigation Control from a Keyboard
  • Radio Tilt Control

 

Radio Data Packets

Devices typically use packets to exchange data.  Packets can contain multiple pieces of information, including headers, trailers, sequence numbers, checksums, and other features to make communication between apps and over the web robust.  

Remember countdown_timer.py from Input/Print for Applications in the Computer-micro:bit Talk tutorial?  It was a script that allowed you to set the start number for the count down and a message to display afterwards.  In this tutorial’s example, you will enter data into a terminal connected to the sender micro:bit.  It will packetize the start number and ending message before sending it over the radio.  The receiver micro:bit will unpack the data and use it to perform the countdown.

 

Send and Receive Packets

The sender micro:bit and receiver micro:bit each have their own script, and they must be running at the same time.

  • Connect two micro:bit modules to two USB ports with two USB cables.
  • Open two separate browsers and navigate both to python.microbit.org.
  • In each micro:bit Python Editor, click the three dots   ⋮   by the Send to micro:bit button, select Connect, and then set up each micro:bit connection.
  • If you are part of a class, and have been assigned a channel, make sure to adjust the script’s channel=7 to your assigned channel before you save and flash the scripts.
  • Enter, name, save, and flash the sender script countdown_sender into the sending micro:bit.
    (See Save & Edit Scripts and Flash Scripts with Python Editor.) 
  • Enter, name, save, and flash the receiver script countdown_reciever into the receiving micro:bit.  It’s below the countdown_sender script.

Example Sender Script: countdown_sender

# countdown_sender

from microbit import *
import radio

radio.on()
radio.config(channel=7,length=50)

sleep(1000)

print("Countdown App")
print("micro:bit sender")

while True:
    text = input("Enter countdown start: ")
    value = int(text)
    message = input("Enter message after countdown: ")
    
    dictionary = {  }
    dictionary['start'] = value
    dictionary['after'] = message

    packet = str(dictionary)
    
    print("Send: ", packet)
    radio.send(packet)
    
    print()

 

Example Receiver Script: countdown_receiver

# countdown_receiver


from microbit import *
import radio

radio.on()
radio.config(channel=7,length=50)

sleep(1000)

print("Countdown App")
print("micro:bit receiver\n")

while True:
    packet = radio.receive()
    if packet is not None:
        print("Receive: ", packet)

        print()
        print("Parse: ")

        dictionary = eval(packet)

        value = dictionary['start']
        message = dictionary['after']
        
        print("value = ", value)
        print("message = ", message, "\n")
        
        while value >= 0:
            print(value)
            sleep(1000)
            value = value - 1
            
        print(message)
        
        print()

 

  • Click Show serial in both browsers.
  • Follow the prompts in the sender micro:bit’s serial monitor for entering the countdown start value and the message to display afterwards.
  • Check the receiver micro:bot’s serial monitor and verify that it completes the countdown and displays the message you entered.

 

How Radio Data Packets Work

The Dictionary is Key

The countdown_sender script prompts for countdown start and message values and adds them to a dictionary.  The dictionary has a ‘start’ key that is paired with the number the receiver starts counting from.  It also has a string paired with an ‘after’ key that the receiver displays after it’s done counting down.  It then creates a string that represents the dictionary (the data packet) and sends it over the radio.

The countdown_reciever script receives the packet string and creates a dictionary from it.  Then, it creates a value variable from the int value paired with the ‘start’ key and a string from the string value paired with the ‘after’ key.

Let’s take a closer look at how statements in each script do their jobs.

How countdown_sender works

The packet in the example has a string with 37 characters.  That’s larger than the default length of 32.  So length= was increased to 50 to make enough room for typical countdown values and messages.

radio.config(channel=7,length=50)

This is the same code that’s in countdown_timer.py from Input/Print for Applications.  It gets a value and message from the serial monitor.

    text = input("Enter countdown start: ")
    value = int(text)
    message = input("Enter message after countdown: ")

There are many ways to transmit a value and a message to another micro:bit.  However, coding the receiver script to figure out what the purpose of each item is can be tricky.  Dictionaries make it much easier, especially when you have more than just the two items we are starting with here. 

First, dictionary = {   } creates an empty dictionary (though it did not have to use dictionary as the name, it’s like naming any other variable.)  Next, two key-value pairs were added with dictionary[‘start’] = value and dictionary[‘after’] = message.  Assuming you entered 3 and Liftoff! when the script was running, dictionary would look like this: {‘start’: 3, ‘after’: ‘Liftoff!’}.  The receiver script will now be able to use the ‘start’ key to get the int value 3, and the ‘after’ key to get the string value ‘Liftoff!”

    dictionary = {  }
    dictionary['start'] = value
    dictionary['after'] = message

This could actually have been done in one step, with dictionary = {‘start’ : value, ‘after’ : message}.    The reason we don’t do it that way here is because the receiver script will use complementary statements to store and retrieve values using their keys.  See how closely adding dictionary elements resembles accessing them?

Transmitter
dictionary[‘start’] = value
dictionary[‘after’] = message

Receiver
value = dictionary[‘start’]
message = dictionary[‘after’]

The radio.send method cannot send a dictionary, but it can send a string.  Fortunately, creating a string from other data types is simple with the str function.  The packet = str(dictionary) creates a string representation of the dictionary and names it packet.  The print statement is there so you can see what the packet string looks like before radio.send transmits it.

packet = str(dictionary)
    
    print("Send: ", packet)
    radio.send(packet)

This just displays an empty line before the loop repeats.

    print()

 

How countdown_receiver works

If the send length is 50, the receive length should also be 50.

radio.config(channel=7,length=50)

Like the other examples that call radio.receive, action only has to be taken when radio.receive returns something other than None.  In other words, when radio.receive returns a string that gets stored in a packet, that will have to be processed.

    packet = radio.receive()
    if packet is not None:

This is not a required first step in processing the packet.  It’s just so you can see what’s in it before parsing.  Below that, headings for the parse results are also printed.

        print("Receive: ", packet)
    
        print()
        print("Parse: ")
    

The first step in parsing the packet is converting it from a string back to a dictionary.  That’s what dictionary = eval(packet) does.  Let’s say the packet variable contains this string: “{‘start’: 3, ‘after’: ‘Liftoff!’}”.  The eval function evaluates what’s in a string as a Python expression and returns the result.  In this case, it returns the dictionary object: {‘start’: 3, ‘after’: ‘Liftoff!’}.  Lastly, dictionary = makes a name reference for that dictionary object.  

        dictionary = eval(packet)

Again, the name dictionary is not a requirement.  For example, you could choose a brief memory-saving name like d, or a longer name like my_countdown_dictionary.  Of course, if you change it in one place, you’ll also have to update the other parts of the script that use the name dictionary to make it work again.  

Now that there’s a dictionary, the script can use the ‘start’ key to access the value (3 in the example).  Then, it can use the ‘after’ key to access the “Liftoff!” string value.

value = dictionary['start']
        message = dictionary['after']

These print statements can help you verify that the value the receiver received matches the one the sender sent.  Likewise with the message.

        print("value = ", value)
        print("message = ", message, "\n")

       
Now that the receiver script has value and message, it can finish the job, which is the loop in countdown_timer.py from Input/Print for Applications.  

    while value >= 0:
            print(value)
            sleep(1000)
            value = value - 1
            
        print(message)
        
        print()

 

Try This: Out of Order

Curious how a dictionary might start out as {‘start’ : 3, ‘after’ : ‘Liftoff!’} in the transmitter but end up as {‘after’ : ‘Liftoff!’, ‘start’ : 3} in the receiver?  You might be able to see it by printing the dictionary immediately after the eval statement.  

  • Modify the countdown_receiver script by adding the statement with the # add comment shown here.  Don’t change anything else in the script.
        dictionary = eval(packet)
        
        print("dictionary: \n", dictionary, "\n")       # add

        value = dictionary['start']
        message = dictionary['after']
  • Flash the receiver with the modified script.
  • Enter 2 at the start prompt and Go!!! at the message prompt.
  • Verity that the sender and receiver terminals resemble these:

What happened?

When you run the modified scripts and enter the start count and message after, it might look like this.  The Send and Receive strings are the same {‘after’: ‘Go!!!’, ‘start’: 2}.  Then, after dictionary = eval(packet), the order of the dictionary has changed to {‘start’: 2, ‘after’: ‘Go!!!’}.  Each value is still paired with its key, but the order of the key-value pairs is different.  

The application still worked correctly because value = dictionary[‘start’] still retrieved the 2 value that was paired with the ‘start’ key.   Likewise, message = dictionary[‘after’] also retrieved the ‘Go!!!’ string value that was paired with the ‘after’ key.   

 

Your Turn: Add More Data

Let’s put ease of expanding the application to the test by adding a feature.  Instead of just counting down seconds, the expanded application will ask you for the millisecond (ms) time between counts.  You can still choose 1000 to have it count in seconds, but you can also specify a value like 100 to have it count in tenths of seconds.  

  • Make the changes to the sender and receiver scripts shown below.
  • Flash the updated code to the sender and receiver micro:bit modules with Send to micro:bit.
  • Use Show serial to open both serial monitors. 

Sender changes for countdown_sender

    text = input("Enter countdown start: ")
    value = int(text)
    text = input("Enter ms time between counts: ")         # add
    ms = int(text)                                         # add
    message = input("Enter message after countdown: ")
    
    dictionary = {  }
    dictionary['start'] = value
    dictionary['time'] = ms                                # add
    dictionary['after'] = message

Receiver Changes for countdown_receiver

        value = dictionary['start']
        ms = dictionary['time']              # add
        message = dictionary['after']
        
        print("value = ", value)
        print("ms = ", ms)                   # add
        print("message = ", message, "\n")
        
        while value >= 0:
            print(value)
            sleep(ms)                       # change
            value = value - 1
  • Try setting the countdown start to 30, the ms time between counts to 100, and the end message to ‘That was faster!’.  
  • Verify that the receiver result resembles the one shown below.

Only one line had to be added to the sender to send the ms value, and another to the receiver to recover it.  The rest of the changes would have been made, even if it was just one script on a single micro:bit.

 

DISCUSSION FORUMS | PARALLAX INC. STORE

About | Terms of Use | Feedback: learn@parallax.com | Copyright©Parallax Inc. 2024


Source URL:https://learn.parallax.com/courses/cybersecurity-radio-data/
Links