Published © GPL3+

IoT-Based Quiz App for Multiple micro:bits

Run a quiz by connecting a bunch of BBC micro:bits, via Wi-Fi, to the Ubidots IoT platform using a single XinaBox IoT Starter kit.

EasyFull instructions provided45 minutes37
IoT-Based Quiz App for Multiple micro:bits

Things used in this project

Hardware components

XinaBox CW01
Wi-Fi Core (ESP8266/ESP-12F)
XinaBox BM01
A bridge to connect your XinaBox xChips to a micro:bit
XinaBox IP01
An interface module equipped to power and program other modules via a USB A connector.
XinaBox XC10
This SKU is a 10 pack - we only need 2 for this project, although I use 3. These are standard uBus connectors.
BBC micro:bit board
BBC micro:bit board
You need 1 to act as a gateway then 1 or more others to send data to the gateway

Software apps and online services

You will need an UbiDots account - I use a free 30 day trial account in this blog


Read more


Code for the Classroom Micro:bit

Edit then flash this microPython code onto one of the classroom micro:bits you are using
from microbit import*
import radio
radio.config(length=64, queue=8, channel=11, power=6)
#   NOTE - radio settings are identical to those in the code

thisMicrobitID = 0
# EDIT GUIDELINES: remember to name each classroom microbit with a unique integer.  Start from 0 and count upwards.

while True:

        radio.send(str(thisMicrobitID) + "_a")"a")

        radio.send(str(thisMicrobitID) + "_b")"b")

Code for Micro:bit Gateway

Work through the code and edit is where you see "# EDIT GUIDELINES:~. Then flash it on to your micro:bit gateway.
from microbit import *
import radio
radio.config(length=64, queue=8, channel=11, power=6)
#   NOTE: radio settings are identical to those in the code

uart.init(baudrate=9600, bits=8, parity=None, stop=1, tx=pin20, rx=pin19)
# NOTE: gets the uart (serial / USB) port on the micro:bit ready to communicate with the CW01

numberOfClassroomMicrobits = 10
#   EDIT GUIDELINES: change the '10' above - enter the number of classroom microbits in your network
#   NOTE: start numbering your clssroom microbits at 0.  so, if you have 3 in total their 'thisMicrobitName' will be 0, 1 and 2 and you must enter 3 above.

global countOfButton_A      #   NOTE: Count the number of classroom mirobits that have sent a message indicating their a-button was pressed. Similar below.
global countOfButton_B      #   NOTE: For these counts we restrict each microbit to 1 'vote'.  Counts and votes are reset when the A-button is clicked.
countOfButton_A = 0
countOfButton_B = 0

global voteRecorded         #   NOTE: Use this list to keep a check on who has voted & prevent repeat.
voteRecorded = [numberOfClassroomMicrobits]
for i in range(0, int(numberOfClassroomMicrobits)):      voteRecorded.append(False)

global testMode
testMode = True
#   NOTE: each classroom micro:bit can only vote one time per separate quiz... only 1 time until the a-button is clicked.
#   NOTE: but, for testing purposes and to set up our dashboard we want lots of data to be sent to Ubidots
#   NOTE: so, when testMode = True we allow each classroom micro:bit to vote as many times as they like.
#   NOTE: set this to False to limit each to 1 vote per quiz.

def resetVoting(numberOfClassroomMicrobits):    #   NOTE: when the a-button on the gateway is clicked voting resets.
    global voteRecorded
    #   NOTE - there is no point resetting if no votes have been cast;
    if((countOfButton_A + countOfButton_B) == 0):   return
    sendMessageToCW01("+4@YOURMICROBIT@resetVote@1$")        #   NOTE: tells Ubidots a new vote is starting
    #   NOTE: below is a workaround - in Ubidots when we trigger an event it is easy to show the last value receieved.
    #   NOTE: so we send it a value we can use in our email alert:
    percVotesForA = countOfButton_A / (countOfButton_A + countOfButton_B) *100
    sendMessageToCW01("+4@YOURMICROBIT@SummaryOfVote@" + str(percVotesForA) + "$")
    sendMessageToCW01("+4@YOURMICROBIT@SummaryOfVote@-1$")      #   NOTE: reset the value that triggers our email.
    for i in range(0, int(numberOfClassroomMicrobits)): voteRecorded[i] = False

def sendMessageToCW01(parm):
    data = uart.readline()
    while(data is None):
        data = uart.readline()
    if(len(str(data)) >0):
        uartMessageID = getIntegerFromString(data[:1])
        if(uartMessageID == 1):     # NOTE: a tick is displayed when data is being transmitted correctly to the CW01
        else:                   # NOTE: this means that a cross is displayed when an attempt to send data fails


def processRadioSignal(radioSignal, numberOfClassroomMicrobits):        #   NOTE: a 'valid' signal looks like this "0_a" or this "11_b"
    global testMode
    if(len(str(radioSignal)) < 3):   return False                       #   NOTE: valid radio signals are at least 3 or more characters long
    locationOfUnderscore = getLocationOfUnderscore(radioSignal)
    if(locationOfUnderscore == -1): return False                        #   NOTE: valid radio signals contain an underscore    

    currentMicrobitID = getIntegerFromString(radioSignal[0:locationOfUnderscore])
    if(currentMicrobitID < 0):    return False                          #   NOTE: valid radio messages begin with an integer starting at 0.
    if(currentMicrobitID >= numberOfClassroomMicrobits): return False   #   NOTE: IDs should go from 0 to (numberOfClassroomMicrobits - 1)

    #   NOTE: In the line below you will see why we named the classroom microbits as integers - it allows us to use currentMicrobitID as a list reference.
    #   NOTE: this also means we need to be very careful using them - the validation above prevents an invalid list reference being passed in.
    if((not testMode) and voteRecorded[currentMicrobitID]): return False    
    #   NOTE: the above line ensures that the same classroom microbit can't vote twice for the same quiz, UNLESS we are in testMode
    #   NOTE: If we've reached this point of the code the radioSignal has passed all our validation checks.  It is 'safe' to process it.
    return sendValidMessageToCW01(radioSignal, locationOfUnderscore, currentMicrobitID)

def sendValidMessageToCW01(radioSignal, locationOfUnderscore, currentMicrobitID):
    global voteRecorded
    global countOfButton_A
    global countOfButton_B

    messageType = str(radioSignal[locationOfUnderscore +1 : locationOfUnderscore +2])
    #   NOTE: you could experiment with sending new types of data from the classroom microbit - how about temperature ("0_c_25")
    sendMessageToCW01("+4@YOURMICROBIT@VoterID@" + str(currentMicrobitID) + "$")     
    #   NOTE: above we tell Ubidots that someone has voted, but not how.

    if(messageType == "a"):
        countOfButton_A += 1
        sendMessageToCW01("+4@YOURMICROBIT@CountOf_A@" + str(countOfButton_A) + "$")
        voteRecorded[currentMicrobitID] = True
        return True

    if(messageType == "b"):
        countOfButton_B += 1
        sendMessageToCW01("+4@YOURMICROBIT@CountOf_B@" + str(countOfButton_B) + "$")
        voteRecorded[currentMicrobitID] = True
        return True
    return False

def getLocationOfUnderscore(radioSignal):
    #   NOTE: The underscore can only be in 1 of 2 places in the string, so KISS:
    radioSignalStr = str(radioSignal)
    if(radioSignalStr[1:2] == "_"):    return 1
    if(radioSignalStr[2:3] == "_"):    return 2
    return -1

def getIntegerFromString(uncheckedString):
    try: return int(uncheckedString)
    except ValueError: return -1

#   INIT:          # NOTE: the square is shown on your micro:bit while it is connecting to Ubidots
uart.write("$")                     # NOTE: Cleans out the serial buffer
uart.write("+9@?$")                 # NOTE: Reboots the CW01
sleep(5000)                         # NOTE: long delay is necessary - we need to give Wi-Fi time to sort itself out.
uart.write("$")                     # NOTE: Clean out Serial buffer, again.

# EDIT GUIDELINES: you MUST enter the name and password of the Wi-Fi network you are trying to connect to in the line above.

# EDIT GUIDELINES: you MUST enter the DEFAULT TOKEN from Ubidots in the line above.

# NOTE: above line tells the micro;bit where to send the data to - its a Ubidots URL.

# NOTE: above is an ID for our CW01 - you can leave it as is.

# NOTE: This variable is used to trigger an email sent after the vote - we set it to -1 as any value > -1 triggers the email.

while True:
    if(processRadioSignal(radio.receive(), numberOfClassroomMicrobits)):   
        countOfButton_A = 0
        countOfButton_B = 0





4 projects • 2 followers
Hobbyist + EdTech consultant