SEm/escapeLab/safe

(Difference between revisions)
Jump to: navigation, search
(Opening the safe)
(Testing the opening of the safe)
Line 59: Line 59:
 
<source lang="python">
 
<source lang="python">
 
# http://microbit-micropython.readthedocs.io/
 
# http://microbit-micropython.readthedocs.io/
 +
import radio
 
from microbit import *
 
from microbit import *
  
Line 64: Line 65:
 
# Constants
 
# Constants
 
#
 
#
opening_sequence = [0, 0, 0, 0]
+
opening_sequence = [4, 3, 2, 1]
test_sequence = [12, 4, 15, 8]
+
test_sequence = [1, 2, 3, 4]
 
positon_min = 0
 
positon_min = 0
positon_max = 99
+
positon_max = 9
  
 
safe_open_button = button_a
 
safe_open_button = button_a
Line 73: Line 74:
 
encoder_pin_high = pin8
 
encoder_pin_high = pin8
 
encoder_pin_low = pin12
 
encoder_pin_low = pin12
 +
encoder_up = [0, 2, 3, 1]
 
open_safe_pin = pin16
 
open_safe_pin = pin16
  
rotating_right = True
+
timer_period = 10
rotating_left = not rotating_right
+
 
count_to_display_units = 200
+
indent = '  '
 +
count_to_display_units = 100
 +
encoder_button_pressed_images = [
 +
    Image("00000:00000:00900:00000:00000"),
 +
    Image("00000:00900:09590:00900:00000"),
 +
    Image("00900:09590:95559:09590:00900"),
 +
    Image("09590:95559:55555:95559:09590"),
 +
    Image("95559:55555:55555:55555:95559")
 +
]
 +
encoder_button_pressed_display_interval = 200
 +
empty_display = Image("00000:00000:00000:00000:00000")
 +
sequence_display_scroll_time = 1000
 
sequence_item_display_interval = 1000
 
sequence_item_display_interval = 1000
 
test_sequence_found_image = Image.HAPPY
 
test_sequence_found_image = Image.HAPPY
 +
test_sequence_found_display_interval = 1000
 
safe_open_sequence_found_image = Image.FABULOUS
 
safe_open_sequence_found_image = Image.FABULOUS
open_safe_duration = 2000
+
open_safe_sampling_period = 100
 +
 
 +
radio_channel = 6
 +
radio_address = 0xFC000000
 +
 
 +
software_version = '2.0'
  
 
# -----------------------------------------------------------------------------
 
# -----------------------------------------------------------------------------
 
# Functions
 
# Functions
 
#
 
#
def get_encoder_value():
+
def update_position(position, encoder_previous_value):
 +
    #                                                        read encoder value
 
     encoder_value = \
 
     encoder_value = \
 
         2*encoder_pin_high.read_digital() + \
 
         2*encoder_pin_high.read_digital() + \
 
         encoder_pin_low.read_digital()
 
         encoder_pin_low.read_digital()
     return(encoder_value)
+
     #                                                          update position
 +
    has_changed = False
 +
    if encoder_value == encoder_up[2]:
 +
        if encoder_previous_value == encoder_up[1]:
 +
            if position < positon_max:
 +
                position = position + 1
 +
            else:
 +
                position = positon_min
 +
            has_changed = True
 +
        if encoder_previous_value == encoder_up[-1]:
 +
            if position > positon_min:
 +
                position = position - 1
 +
            else:
 +
                position = positon_max
 +
            has_changed = True
  
def update_position(position, encoder_previous_value, encoder_value):
+
    return(position, encoder_value, has_changed)
    encoder_up = [1, 3, 0, 2]
+
    if encoder_value == encoder_up[encoder_previous_value]:
+
        position = position + 1
+
    else:
+
        position = position - 1
+
    if position < positon_min:
+
        position = positon_max
+
    if position > positon_max:
+
        position = positon_min
+
    return(position)
+
  
def display_binary(position):
+
def display_scroll(position):
     no_intensity = '0'
+
     if position >= 10:
    max_intensity = '9'
+
         display.scroll(str(position), wait=False)
    bits_per_line = 4
+
    #                                                          prepare bits 3:0
+
    hex_line_0 = ""
+
    for index in range(bits_per_line):
+
        if position % 2 > 0:
+
            hex_line_0 = max_intensity + hex_line_0
+
        else:
+
            hex_line_0 = no_intensity + hex_line_0
+
         position = int(position/2)
+
    hex_line_0 = '0' + hex_line_0
+
    #                                                          prepare bits 7:4
+
    hex_line_1 = ""
+
    for index in range(bits_per_line):
+
        if position % 2 > 0:
+
            hex_line_1 = max_intensity + hex_line_1
+
        else:
+
            hex_line_1 = no_intensity + hex_line_1
+
        position = int(position/2)
+
    hex_line_1 = '0' + hex_line_1
+
    #                                                            display image
+
    display.show(Image(
+
        "00000:" + hex_line_1 + ":00000:" + hex_line_0 + ":00000"
+
    ))
+
 
+
def display_tens(position):
+
    if position > 9:
+
        display.show(str(int(position/10)), wait=False)
+
 
     else:
 
     else:
 
         display.show(str(position % 10), wait=False)
 
         display.show(str(position % 10), wait=False)
Line 140: Line 137:
 
     display.show(str(position % 10), wait=False)
 
     display.show(str(position % 10), wait=False)
  
def display_sequence(sequence):
+
def print_sequence(sequence):
 +
    uart.write(indent + 'Accumulated sequence:')
 
     for index in range(len(sequence)):
 
     for index in range(len(sequence)):
         if sequence[index] > 9:
+
         uart.write(' ' + str(sequence[index]))
            display.scroll(str(int(sequence[index]/10)))
+
    uart.write("\n\r")
        display_units(sequence[index])
+
        sleep(sequence_item_display_interval)
+
  
 
def add_to_history(value, history):
 
def add_to_history(value, history):
     new_history = history[1:] + [value]
+
    #                                                            update history
 +
     new_history = history
 +
    if value != history[-1]:
 +
        new_history = history[1:] + [value]
 +
    #                                            print sequence on serial port
 +
    print_sequence(new_history)
 +
    #                                                        display animation
 +
    #for index in range(len(encoder_button_pressed_images)):
 +
    #    display.show(encoder_button_pressed_images[index])
 +
    #    sleep(encoder_button_pressed_display_interval)
 +
    display.show(
 +
        encoder_button_pressed_images,
 +
        delay=encoder_button_pressed_display_interval
 +
    )
 +
    #                                                    show last digit again
 +
    display_units(value)
 +
 
 
     return(new_history)
 
     return(new_history)
 +
 +
def display_sequence(sequence, position):
 +
    #                                            print sequence on serial port
 +
    print_sequence(sequence)
 +
    #                                                  display sequence values
 +
    for index in range(len(sequence)):
 +
        display.clear()
 +
        sleep(sequence_item_display_interval)
 +
        if sequence[index] >= 10:
 +
            display_scroll(sequence[index])
 +
            sleep(sequence_display_scroll_time)
 +
        display_units(sequence[index])
 +
        sleep(sequence_item_display_interval)
 +
    display.clear()
 +
    sleep(sequence_item_display_interval)
 +
    #                                            show last position digit again
 +
    display_units(position)
 +
 +
def display_test_sequence_found(sequence, position):
 +
    #                                            print sequence on serial port
 +
    print_sequence(sequence)
 +
    #                                                        display animation
 +
    display.clear()
 +
    sleep(test_sequence_found_display_interval)
 +
    display.show(test_sequence_found_image)
 +
    sleep(test_sequence_found_display_interval)
 +
    #                                            show last position digit again
 +
    display_units(position)
  
 
def open_safe():
 
def open_safe():
 +
    display.show(safe_open_sequence_found_image)
 
     open_safe_pin.write_digital(1)
 
     open_safe_pin.write_digital(1)
     sleep(open_safe_duration)
+
     while safe_open_button.is_pressed():
 +
        sleep(open_safe_sampling_period)
 
     open_safe_pin.write_digital(0)
 
     open_safe_pin.write_digital(0)
 +
    display.clear()
  
 
# -----------------------------------------------------------------------------
 
# -----------------------------------------------------------------------------
 
# Main loop
 
# Main loop
 
#
 
#
encoder_value = get_encoder_value()
+
encoder_pin_low.set_pull(1)
previous_encoder_value = encoder_value
+
encoder_pin_high.set_pull(1)
 +
encoder_value = 1
 
encoder_position = 0
 
encoder_position = 0
previous_encoder_position = encoder_position
 
 
display_units(encoder_position)
 
display_units(encoder_position)
 
activity_counter = 0
 
activity_counter = 0
 
history = [0, 0, 0, 0]
 
history = [0, 0, 0, 0]
rotating_direction = rotating_right
+
uart.init(baudrate=115200, bits=8, parity=None, stop=2, tx=pin1, rx=pin2)
previous_rotating_direction = rotating_direction
+
uart.write("\n\r")
 +
uart.write('Safe controller V ' + software_version + "\n\r")
 +
radio.on()
 +
radio.config(channel=radio_channel, address=radio_address)
 +
 
 
while True:
 
while True:
 +
    #                                            sleep to next sampling moment
 +
    time_to_next_action = timer_period - (running_time() % timer_period)
 +
    sleep(time_to_next_action)
 
     #                                                              read encoder
 
     #                                                              read encoder
     encoder_value = get_encoder_value()
+
     (encoder_position, encoder_value, has_changed) = \
     # update position
+
        update_position(encoder_position, encoder_value)
     if previous_encoder_value != encoder_value:
+
     #                                                           update display
         encoder_position = update_position(
+
     if has_changed:
            encoder_position, previous_encoder_value, encoder_value
+
         display_scroll(encoder_position)
        )
+
         activity_counter = 1
         if encoder_position > previous_encoder_position:
+
    if activity_counter > 0:
             rotating_direction = rotating_right
+
        if activity_counter < count_to_display_units:
 +
             activity_counter = activity_counter + 1
 
         else:
 
         else:
             rotating_direction = rotating_left
+
             display_units(encoder_position)
        if rotating_direction != previous_rotating_direction:
+
            activity_counter = 0
            history = add_to_history(previous_encoder_position, history)
+
     #                                                     check encoder button
        display_binary(encoder_position)
+
        previous_encoder_value = encoder_value
+
        previous_encoder_position = encoder_position
+
        previous_rotating_direction = rotating_direction
+
        activity_counter = 0
+
     #                                                           display history
+
 
     if encoder_button.is_pressed():
 
     if encoder_button.is_pressed():
         display_sequence(history)
+
         history = add_to_history(encoder_position, history)
 
     #                                              check history and open safe
 
     #                                              check history and open safe
 
     if safe_open_button.is_pressed():
 
     if safe_open_button.is_pressed():
         if add_to_history(encoder_position, history) == opening_sequence:
+
         if history == opening_sequence:
             display.show(safe_open_sequence_found_image)
+
             uart.write("Safe opening sequence found.\n\r")
 
             open_safe()
 
             open_safe()
         elif add_to_history(encoder_position, history) == test_sequence:
+
         elif history == test_sequence:
             display.show(test_sequence_found_image)
+
             uart.write("Test sequence found.\n\r")
 +
            display_test_sequence_found(history, encoder_position)
 
         else:
 
         else:
             display_sequence(test_sequence)
+
            uart.write("Wrong sequence!\n\r")
 +
             display_sequence(history, encoder_position)
 +
    new_message = radio.receive()
 +
    if new_message == 'Sesame':
 +
        open_safe()
 
</source>
 
</source>
  
 
[[Category:Bachelor]] [[Category:SEm]] [[Category:EscapeLab]]
 
[[Category:Bachelor]] [[Category:SEm]] [[Category:EscapeLab]]

Revision as of 08:20, 4 May 2022

Contents

Opening the safe

The safe is opened by turning an encoder to the first value, then clicking on the button, and so on, until the 4 values have been reached. Once the final position has been reached, the rotating lever allows to open the safe.

This can be tested with the sequence [1, 2, 3, 4] which doesn't open the safe but displays a smiley.

Verifying the codes

The numbers of the test sequence can be verified by their Hamming code. Calculating the Hamming code is similar to a vector by matrix multiplication, where the product is implemented by an AND function and the sum by a XOR.


\begin{bmatrix} h_3 & h_2 & h_1 & h_0 \end{bmatrix} =
\begin{bmatrix} d_7 & d_6 & d_5 & d_4 & d_3 & d_2 & d_1 & d_0 \end{bmatrix} \cdot 
\begin{bmatrix}
  0 & 0 & 1 & 1 \\
  0 & 1 & 0 & 1 \\
  0 & 1 & 1 & 0 \\
  0 & 1 & 1 & 1 \\
  1 & 0 & 0 & 1 \\
  1 & 0 & 1 & 0 \\
  1 & 0 & 1 & 1 \\
  1 & 1 & 0 & 0
\end{bmatrix}

As an example, data value 3 has Hamming code 7:


\begin{bmatrix} 0 & 0 & 0 & 0 & 0 & 0 & 1 & 1 \end{bmatrix} \cdot 
\begin{bmatrix}
  0 & 0 & 1 & 1 \\
  0 & 1 & 0 & 1 \\
  0 & 1 & 1 & 0 \\
  0 & 1 & 1 & 1 \\
  1 & 0 & 0 & 1 \\
  1 & 0 & 1 & 0 \\
  1 & 0 & 1 & 1 \\
  1 & 1 & 0 & 0
\end{bmatrix} =
\begin{bmatrix} 0 & 1 & 1 & 1 \end{bmatrix}

The tasks have the follwing Hamming codes:

  • task 1 : 7
  • task 2 : 5
  • task 3 : 1
  • task 4 : 2

Testing the opening of the safe

The following code is programmed in the safe's microcontroller, except for the opening_sequence. It allows to find out how the unlocking system works.

# http://microbit-micropython.readthedocs.io/
import radio
from microbit import *
 
# -----------------------------------------------------------------------------
# Constants
#
opening_sequence = [4, 3, 2, 1]
test_sequence = [1, 2, 3, 4]
positon_min = 0
positon_max = 9
 
safe_open_button = button_a
encoder_button = button_b
encoder_pin_high = pin8
encoder_pin_low = pin12
encoder_up = [0, 2, 3, 1]
open_safe_pin = pin16
 
timer_period = 10
 
indent = '  '
count_to_display_units = 100
encoder_button_pressed_images = [
    Image("00000:00000:00900:00000:00000"),
    Image("00000:00900:09590:00900:00000"),
    Image("00900:09590:95559:09590:00900"),
    Image("09590:95559:55555:95559:09590"),
    Image("95559:55555:55555:55555:95559")
]
encoder_button_pressed_display_interval = 200
empty_display = Image("00000:00000:00000:00000:00000")
sequence_display_scroll_time = 1000
sequence_item_display_interval = 1000
test_sequence_found_image = Image.HAPPY
test_sequence_found_display_interval = 1000
safe_open_sequence_found_image = Image.FABULOUS
open_safe_sampling_period = 100
 
radio_channel = 6
radio_address = 0xFC000000
 
software_version = '2.0'
 
# -----------------------------------------------------------------------------
# Functions
#
def update_position(position, encoder_previous_value):
    #                                                        read encoder value
    encoder_value = \
        2*encoder_pin_high.read_digital() + \
        encoder_pin_low.read_digital()
    #                                                           update position
    has_changed = False
    if encoder_value == encoder_up[2]:
        if encoder_previous_value == encoder_up[1]:
            if position < positon_max:
                position = position + 1
            else:
                position = positon_min
            has_changed = True
        if encoder_previous_value == encoder_up[-1]:
            if position > positon_min:
                position = position - 1
            else:
                position = positon_max
            has_changed = True
 
    return(position, encoder_value, has_changed)
 
def display_scroll(position):
    if position >= 10:
        display.scroll(str(position), wait=False)
    else:
        display.show(str(position % 10), wait=False)
 
def display_units(position):
    display.show(str(position % 10), wait=False)
 
def print_sequence(sequence):
    uart.write(indent + 'Accumulated sequence:')
    for index in range(len(sequence)):
        uart.write(' ' + str(sequence[index]))
    uart.write("\n\r")
 
def add_to_history(value, history):
    #                                                            update history
    new_history = history
    if value != history[-1]:
        new_history = history[1:] + [value]
    #                                             print sequence on serial port
    print_sequence(new_history)
    #                                                         display animation
    #for index in range(len(encoder_button_pressed_images)):
    #    display.show(encoder_button_pressed_images[index])
    #    sleep(encoder_button_pressed_display_interval)
    display.show(
        encoder_button_pressed_images,
        delay=encoder_button_pressed_display_interval
    )
    #                                                     show last digit again
    display_units(value)
 
    return(new_history)
 
def display_sequence(sequence, position):
    #                                             print sequence on serial port
    print_sequence(sequence)
    #                                                   display sequence values
    for index in range(len(sequence)):
        display.clear()
        sleep(sequence_item_display_interval)
        if sequence[index] >= 10:
            display_scroll(sequence[index])
            sleep(sequence_display_scroll_time)
        display_units(sequence[index])
        sleep(sequence_item_display_interval)
    display.clear()
    sleep(sequence_item_display_interval)
    #                                            show last position digit again
    display_units(position)
 
def display_test_sequence_found(sequence, position):
    #                                             print sequence on serial port
    print_sequence(sequence)
    #                                                         display animation
    display.clear()
    sleep(test_sequence_found_display_interval)
    display.show(test_sequence_found_image)
    sleep(test_sequence_found_display_interval)
    #                                            show last position digit again
    display_units(position)
 
def open_safe():
    display.show(safe_open_sequence_found_image)
    open_safe_pin.write_digital(1)
    while safe_open_button.is_pressed():
        sleep(open_safe_sampling_period)
    open_safe_pin.write_digital(0)
    display.clear()
 
# -----------------------------------------------------------------------------
# Main loop
#
encoder_pin_low.set_pull(1)
encoder_pin_high.set_pull(1)
encoder_value = 1
encoder_position = 0
display_units(encoder_position)
activity_counter = 0
history = [0, 0, 0, 0]
uart.init(baudrate=115200, bits=8, parity=None, stop=2, tx=pin1, rx=pin2)
uart.write("\n\r")
uart.write('Safe controller V ' + software_version + "\n\r")
radio.on()
radio.config(channel=radio_channel, address=radio_address)
 
while True:
    #                                             sleep to next sampling moment
    time_to_next_action = timer_period - (running_time() % timer_period)
    sleep(time_to_next_action)
    #                                                              read encoder
    (encoder_position, encoder_value, has_changed) = \
        update_position(encoder_position, encoder_value)
    #                                                            update display
    if has_changed:
        display_scroll(encoder_position)
        activity_counter = 1
    if activity_counter > 0:
        if activity_counter < count_to_display_units:
            activity_counter = activity_counter + 1
        else:
            display_units(encoder_position)
            activity_counter = 0
    #                                                      check encoder button
    if encoder_button.is_pressed():
        history = add_to_history(encoder_position, history)
    #                                               check history and open safe
    if safe_open_button.is_pressed():
        if history == opening_sequence:
            uart.write("Safe opening sequence found.\n\r")
            open_safe()
        elif history == test_sequence:
            uart.write("Test sequence found.\n\r")
            display_test_sequence_found(history, encoder_position)
        else:
            uart.write("Wrong sequence!\n\r")
            display_sequence(history, encoder_position)
    new_message = radio.receive()
    if new_message == 'Sesame':
        open_safe()
Personal tools
Namespaces
Variants
Actions
Navigation
Modules / Projects
Browse
Toolbox