SEm/escapeLab/safe
(→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 = [ | + | opening_sequence = [4, 3, 2, 1] |
− | test_sequence = [ | + | test_sequence = [1, 2, 3, 4] |
positon_min = 0 | positon_min = 0 | ||
− | positon_max = | + | 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 | ||
− | + | timer_period = 10 | |
− | + | ||
− | 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_sampling_period = 100 | |
+ | |||
+ | radio_channel = 6 | ||
+ | radio_address = 0xFC000000 | ||
+ | |||
+ | software_version = '2.0' | ||
# ----------------------------------------------------------------------------- | # ----------------------------------------------------------------------------- | ||
# Functions | # Functions | ||
# | # | ||
− | def | + | 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() | ||
− | + | # 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 | + | def display_scroll(position): |
− | + | if position >= 10: | |
− | + | display.scroll(str(position), 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 | + | def print_sequence(sequence): |
+ | uart.write(indent + 'Accumulated sequence:') | ||
for index in range(len(sequence)): | for index in range(len(sequence)): | ||
− | + | uart.write(' ' + str(sequence[index])) | |
− | + | uart.write("\n\r") | |
− | + | ||
− | + | ||
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( | + | 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_pin_low.set_pull(1) | |
− | + | encoder_pin_high.set_pull(1) | |
+ | encoder_value = 1 | ||
encoder_position = 0 | encoder_position = 0 | ||
− | |||
display_units(encoder_position) | display_units(encoder_position) | ||
activity_counter = 0 | activity_counter = 0 | ||
history = [0, 0, 0, 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: | 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 = | + | (encoder_position, encoder_value, has_changed) = \ |
− | # update | + | update_position(encoder_position, encoder_value) |
− | if | + | # update display |
− | + | if has_changed: | |
− | + | display_scroll(encoder_position) | |
− | + | activity_counter = 1 | |
− | if | + | if activity_counter > 0: |
− | + | if activity_counter < count_to_display_units: | |
+ | activity_counter = activity_counter + 1 | ||
else: | else: | ||
− | + | display_units(encoder_position) | |
− | + | activity_counter = 0 | |
− | + | # check encoder button | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | # | + | |
if encoder_button.is_pressed(): | if encoder_button.is_pressed(): | ||
− | + | 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 | + | if history == opening_sequence: |
− | + | uart.write("Safe opening sequence found.\n\r") | |
open_safe() | open_safe() | ||
− | elif | + | elif history == test_sequence: |
− | + | uart.write("Test sequence found.\n\r") | |
+ | display_test_sequence_found(history, encoder_position) | ||
else: | else: | ||
− | display_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
|
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.
As an example, data value 3 has Hamming code 7:
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()