Easiest way to make two gadgets talk to each other is via Serial Communications. Serial communications is everywhere in our daily lives: USB, Bluetooth, TV-Remote, WIFI-Routers, etc are all devices that make use of serial communications. And it is no surprise that we can learn how to make use of this technology with the help of MicroBlocks Serial Library.
In this exciting tutorial, we will learn how to transmit letters using a micro:bit v2 device and the Serial Library. Additionally, we will show how this taks can be coded without using the library. And in the process, we will explore how letters are encoded, sent and received, bit by bit, using the serial communications techniques.
NOTE: If you are interested in trying this tutorial on other hardware platforms, please refer to the Pin Information article for correct assignment of the available pins on various platforms.
To complete this tutorial we will need:
While it is possible to make do with just a micro:bit and an alligator cable, this setup will be a bit unstable and hard to manage. The more secure and easier method would be to use one of many Edge Breakout boards to make the connections. This will help you achieve a more stable connectivity and performance.
Here are both ways to setup the connections.
For this exercise, we have provided a single script that demonstrates two techniques:
In the script pictures below, the blocks under:
NOTE: In the when started block set, there are blocks pertinent to both the Button-A and Button-B versions of the code. For simplicity sake, When describing blocks of each version, the blocks not pertaining to the particular version have been left out. Similarly, there are various information display blocks in both versions that have not been addressed due to the simplicity of their functions (eg: wait and say blocks).
For those interested only in the high-level functionality of sending and receiving letters across two ports of the micro:bit, what follows is a brief description of the Serial techniques. For those interested in more in depth treatment of the Serial Communications and UART operations, we'd like to redirect you to the Writing Our Own Code section of Two Ways To Do The Same Thing further below.
Before we dive into the details of the blocks, let's review the very basic principles of the character transmissions using a serial interface.
Characters are transmitted and received in binary format, one bit at a time. The least significant bit (LSB) is transmitted first, followed by all other in sequence.
To get the binary format of any letter, we need to go through a process of converting unicode values of characters into their binary equivalents. To better explain what we are talking about, here is a sample table, showing letters of the alphabet and their unicode and binary values.
Letter | ASCII Code |
Binary | Letter | ASCII Code |
Binary | |
---|---|---|---|---|---|---|
A | 065 | 01000001 | a | 097 | 01100001 | |
B | 066 | 01000010 | b | 098 | 01100010 | |
C | 067 | 01000011 | c | 099 | 01100011 | |
D | 068 | 01000100 | d | 100 | 01100100 | |
E | 069 | 01000101 | e | 101 | 01100101 | |
F | 070 | 01000110 | f | 102 | 01100110 | |
G | 071 | 01000111 | g | 103 | 01100111 | |
H | 072 | 01001000 | h | 104 | 01101000 | |
I | 073 | 01001001 | i | 105 | 01101001 | |
J | 074 | 01001010 | j | 106 | 01101010 | |
K | 075 | 01001011 | k | 107 | 01101011 | |
L | 076 | 01001100 | l | 108 | 01101100 | |
M | 077 | 01001101 | m | 109 | 01101101 | |
N | 078 | 01001110 | n | 110 | 01101110 | |
O | 079 | 01001111 | o | 111 | 01101111 | |
P | 080 | 01010000 | p | 112 | 01110000 | |
Q | 081 | 01010001 | q | 113 | 01110001 | |
R | 082 | 01010010 | r | 114 | 01110010 | |
S | 083 | 01010011 | s | 115 | 01110011 | |
T | 084 | 01010100 | t | 116 | 01110100 | |
U | 085 | 01010101 | u | 117 | 01110101 | |
V | 086 | 01010110 | v | 118 | 01110110 | |
W | 087 | 01010111 | w | 119 | 01110111 | |
X | 088 | 01011000 | x | 120 | 01111000 | |
Y | 089 | 01011001 | y | 121 | 01111001 | |
Z | 090 | 01011010 | z | 122 | 01111010 |
As can be seen, each letter has a corresponding binary value associated with it. This binary value, which consists of 1's and 0's, is what gets transmitted over the cable. 1 and 0 nature of this data is very suitable for transmission purposes. As we all know, computers like 1's and 0's and know how to deal with them. In serial communications, these 1's and 0's are represented as the presence of changing voltages on the pins of the micro:bit:
For example, if we take the letter M with its ASCII value of 77 and binary encoding of 01001101, the computer would transmit this binary value as a sequence of digital signals that look like this:
There are a few more technical details that go into making a successful serial transmission. But for our purposes, understanding the encoding and its digital representation is enough to go through this tutorial.
Now, how do we get from a letter to its binary encoded format?
Simple! We write a custom block and do the conversion. It is simply a decimal to binary conversion routine. We input a decimal ASCII value of a letter into our custom block, and it returns a list that consists of the 1's and 0's representing the corresponding binary value. Since MicroBlocks digital signal blocks work with True / False values instead of the 1's and 0's, our custom block also returns the binary list encoded in true/false format. To elaborate on the previous example, the letter M would be encoded as: FTFFTTFT where F:false (0) and T:true (1).
The code for our custom block is very simple and is presented below:
As we mentioned in the beginning, we will present two different ways to accomplish the transmission of our letters:
This is the easy way! All the hard work is done by MicroBlocks and all we need to do is pick a few blocks from the Library and go about our business. We are spared from all the technical details of the serial transmission thanks to the Serial library.
This version of the task is coded in our project under the Button-A activity.
Button-A activity consists of two separate tasks run in parallel:
When you press Button-A, these two parallel tasks will start running. While the sending side will be telling the library block to send out the character selected, the receiving side will be telling the library block to receive and store the character.
Once you complete the connections as described in the section Making the connections, you can try it out by doing the following:
Pressing RESET should do the following:
When Started Blocks
Variable BAUD will be set to the transmission speed
Initialize transmit and receive pins to be used
A randomly selected character (A-Z) will be placed into the char variable
A checkmark will be displayed on the micro:bit LED display to indicate setup part is completed
Pressing Button-A should do the following:
Sending Activity:
Graph display control will be initialized
The selected character will be displayed on the micro:bit LED display
Serial Library block serial open will open the connection at stated speed and serial write will send the character out.
Receiving Activity:
micro:bit will display a smiley to indicate receiving has started
Variable pinValue will be assigned the character that is received from Pin-0
Character received will be displayed on the micro:bit LED display
Graph activity will be stopped
ASCII value of the received character will be converted to binary format using the custom blocks dec2TF and TF2INT
The script will display the details of the received character and its encoded information.
This is the way that you will get the most out of this tutorial! All the hard work has to be done by us. Also, in this section, we will provide much more detailed information about the blocks used and their functionalities.
This version of the task is coded in our project under the Button-B activity.
Button-B activity consists of two separate tasks run in parallel:
When you press Button-B, these two parallel tasks will start running. While the sending side will be busy transmitting the bits of the character being sent, the receiving side will be processing each bit received and storing them until a full transmission is complete.
While all this sounds almost like the Button-A description, there is a huge difference. In this version, the computer will be performing the character transmission according to the way we are going to instruct it. There will be no Library blocks to help us out.
Once you complete the connections as described in the section Making the connections, you can try it out by doing the following:
Pressing RESET should do the following:
When Started Blocks
Variable BAUD will be set to the transmission speed
What does the BAUD value represent?
It simply states the number of bits we want to transmit in one second. We have a possibility of various values here: from 20 to 2400.
There are higher values possible, but due to the performance details and coding complications, our code will run reliably only within these limits.
Variable DELAY will be calculated.
Here is a bit of explanation why we need this variable.
When we transmit the bits of the character one after another, there needs to be a way for the receiving side to figure out when to know a bit has ended and when a new one starts. Since we only use a single cable for the transmission, there are no other signals involved, that we can rely on. So, it is up to us to create a way to send and receive the bits, without mixing up the signals.
Here is a simple way this can be accomplished:
We figure out a starting signal (the start-bit) and then wait a specific amount of time before sending out each bit. We also wait exactly the same amount after each bit. This amount of time is what we refer to as the DELAY, and it just got calculated in this step.
To be able to use the microseconds as the unit of our delay period, we have divided 1,000,000 into the BAUD rate. This tells us that we need to wait DELAY microseconds between the tranmission of the bits.
Initialize the Graph Information pin
This pin is used to graph the bit patterns received. It can be any pin available in the system. For specific Pin Information please refer to the WIKI article for correct assignment of the available pins on various platforms.
Initialize transmit and receive pins to be used
Serial communcations can occur between any two sets of pins of a micro computer. In our case, these are the Pin-0 for receiving and Pin-1 for transmitting.
While the Library version of our code has to obey the Library conventions and use pins 0 and 1 for the Serial blocks, we do not have to go by that rule. We are coding our own method and we can use any two pins we want. We just need to be careful of not conflicting with any other micro:bit functionalities when selecting our pins. So, for the initial tests, use the pins 0 and 1. And then you can experiment with others.
A randomly selected character (A-Z) will be placed into the char variable
The table with the letters A-Z and a-z in section Binary Encoding of Letters, gave us a listing of the encodings for each letter. Here we use a random function to pick a number between 65 and 90. These numbers represent the ASCII values of the upper-case letters. This way, we will select a different letter each time we run our code.
Variable bitsList is calculated by converting the ASCII value of the character selected into the binary equivalent.
There are no number system conversion functions in MicroBlocks, except the one from HEX to Decimal. The actual conversion from the ASCII value to the binary has to be done by a custom block we have written. Here is the code for it:
It is a simple version of the many algorithms that can be found on the Internet. You can look it up and study it, if interested in the details.
Also, the section Binary Encoding Process provides further details on this custom block.
A checkmark will be displayed on the micro:bit LED display to indicate setup part is completed
Pin-1 (transmit pin) is set to a true (high) state.
Explanation for this will be in the Button-B section below.
The graphing of the bits is started.
We mentioned in the opening section that the code we have could also graph the bits sent and received. This block set checks the status of the boolean variable stopGraph, and keeps graphing the results of reading the digital GRpin (Pin-2 here; but can be any assigned pin). GRpin is constantly updated by a parallel running block set, that starts when the program is started.
This simple loop keeps copying the status of the Pin-0, the receive pin; to GRpin. This way, when we continuously graph the value of GRpin, we are actually graphing the bit values received.
This completes the explanations of the when started portion of the code. Now we can proceed to the description of the activities when the Button-B is pressed.
Pressing Button-B should do the following:
Let's review at a high level all that we have to accomplish in this section:
As far as the sending activity goes, we need to signal the receiving side that we are starting with the transmission of the bits. And then we need to send all eight bits of the character with proper delays. At the end, we will signal that we are done.
As for the receiving activities, here is what has to happen.
We wait for the start of the transmission signal. When we detect it, we start reading the bits from Pin-0 until all eight are received. After the full character is received, we display the information and stop.
Let's look at the blocks in detail.
Sending Activity:
The selected character will be displayed on the micro:bit LED display
After a one second wait, we will display the character ">" to signal that we are starting the bit transmissions.
Pin-1 (transmit pin) is set to a true (high) state.
This gets us ready to signal the other side that a character transmission is about to begin. We set the Pin-1 signal to true. Then we wait DELAY microseconds, and then in the next step, we set it to false.
Here is the start signal: Pin-1 is set to false.
Then we wait DELAY microseconds.
We start sending all eight bits of the character in a loop, waiting DELAY microseconds after each bit.
Under when started block set, we had stored the bit representation of our character in the variable bitsList. Here, we iterate through that list and send the bits out by changing the status of the Pin-1 to the value of the bit (true/false).
Send out the stop signal: Pin-1 is set to true
Once we are done with all of the eight bits, we signal the other side that we are done. We do this by setting Pin-1 to a true state, and waiting DELAY microseconds.
This completes the sending activities side of the process for Button-B.
Now, let's examine the parallel events happening on the receiving side.
Receiving Activity:
We start by preparing the variable bits as an empty list, that will be used to store the incoming bits.
Then we start monitoring Pin-0 (receive pin) for the start signal: Pin-0 goes false.
When we detect the start signal, we wait for the DELAY microseconds, and then get ready to receive the bits.
Receive all eight bits in a loop and store them in the bits list.
We store the incoming bit values (true/false) by adding the Pin-0 value to the bits list.
When all the bits are received, we wait about a second and set the boolean variable stopGraph to true. This makes the graph window display a small mount of graph activity after the last bit and then stop graphing.
The graph picture will look something like this, depending on the bit values of the character received. Below is a graph for letter M.
Received bits are converted from bits list true/false format to the decimal ASCII value. This is done by a custom code block we have written: TF2dec. Here is the code for it:
This block set calculates the decimal value of the bits received. Since the bits are received as true/false values, another small custom block set, TF2INT is used to convert them from true/false to 1's and 0's. Here is the code for that:
As can be seen, it performs a simple conversion of true/false values to 1's and 0's.
These bit values are converted to the decimal positional values by using a ** bitwise left shift operator**. We shift each bit position as many times as the bit position value and derive a decimal value for that bit. Then we add it to a variable called output and total it.
Refer to bitwise left shift for details of this block.
The decimal value calculated is used to lookup the character symbol, and store it in the pinValue variable.
Next we display the character we have received on the micro:bit LED display, so we can confirm the transmission was correct.
We then convert the true/false values stored in the bits list into 1's and 0's, so that we can display the bit encoding for the character received. Refer to TFT2INT custom block described above.
Finally, the character info and its bit pattern is displayed on the MicroBlocks scripting area. We have also shown the graph of the bits received.
NOTE: Since the bits are sent and received in the order of LSB to MSB, the received character's binary encoding is displayed in REVERSE order. To match the values listed in the table in the beginning section, read the 1's and 0's displayed in right to left order.
This tutorial was based on a single character transmission, in order to simplify the coding necessary, and focus on the basics. Here are some other changes that would be interesting to implement, proving that concepts described have been comprehended.
The Library use version of the code, Button-A activities, are capable of sending and receiving at very high speeds: 20 baud to 115200 baud. Try to modify the code to achieve these higher speeds.
Current exercise sends out the uppercase letters A-Z. Try modifying the code to send out the lower case letters, as well as the numbers 0-9.
Another good expansion idea would be to modify either the Library based code or the custom coded version to send and receive more than one character; as well as longer length strings.
MicroBlocks Program For This Tutorial