diff --git a/.gitignore b/.gitignore
index 3a49b8e..7262245 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,7 +14,6 @@ dist/
 downloads/
 eggs/
 .eggs/
-lib/
 lib64/
 parts/
 sdist/
diff --git a/src/api.py b/src/api.py
index 30e8a25..d21c6a9 100644
--- a/src/api.py
+++ b/src/api.py
@@ -25,6 +25,8 @@ def request(self, method: str, url: str, data=None, params=None) -> urequests.Re
         if query_string:
             url = f"{url}?{query_string}"
 
+        print(f"Requesting {method} {url} {'with data' if data else ''}...")
+
         if method == "GET":
             if data:
                 raise ValueError("GET requests cannot have data, use params instead")
@@ -33,6 +35,8 @@ def request(self, method: str, url: str, data=None, params=None) -> urequests.Re
         else:
             resp = urequests.request(method, url, headers=self.__HEADERS, data=json.dumps(data))
 
+        print(f"Response ({resp.status_code}): {resp.text}")
+
         return resp
 
     def check_health(self) -> bool:
diff --git a/src/lib/__init__.py b/src/lib/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/lib/button.py b/src/lib/button.py
new file mode 100644
index 0000000..ef13c77
--- /dev/null
+++ b/src/lib/button.py
@@ -0,0 +1,39 @@
+from machine import Pin
+
+
+class Button(object):
+    rest_state = False
+    # pin = None
+    # pin_number = 0
+    RELEASED = 'released'
+    PRESSED = 'pressed'
+
+    def __init__(self, pin, rest_state=False, callback=None, internal_pullup=False, internal_pulldown=False):
+        self.pin_number = pin
+        self.rest_state = rest_state
+        if internal_pulldown:
+            self.internal_pull = Pin.PULL_DOWN
+            self.rest_state = False
+        elif internal_pullup:
+            self.internal_pull = Pin.PULL_UP
+            self.rest_state = True
+        else:
+            self.internal_pull = None
+
+        self.pin = Pin(pin, mode=Pin.IN, pull=self.internal_pull)
+
+        self.callback = callback
+        self.active = False
+
+    def update(self):
+        # print(self.pin.value())
+        if self.pin.value() == (not self.rest_state) and (not self.active):
+            self.active = True
+            if self.callback != None:
+                self.callback(self.pin_number, Button.PRESSED)
+            return
+        if self.pin.value() == self.rest_state and self.active:
+            self.active = False
+            if self.callback != None:
+                self.callback(self.pin_number, Button.RELEASED)
+            return
diff --git a/src/lib/buzzer.py b/src/lib/buzzer.py
new file mode 100644
index 0000000..99cf022
--- /dev/null
+++ b/src/lib/buzzer.py
@@ -0,0 +1,64 @@
+#
+# MIT License
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+# Сreated by Immersive-programs:
+#   github: https://github.com/Immersive-programs/micropython-buzzer
+from machine import PWM, Timer
+
+
+class Buzzer:
+    """Tone playback"""
+
+    def __init__(self, pin):
+        self.buzzer = PWM(pin)
+        self.beeptimer = Timer()
+        self.beeps = []
+        self.isenabled = True
+        self.isbeeping = False
+
+    def __beep(self):
+        """Воспроизводит последовательность звуковых сигналов"""
+        if self.isenabled:
+            if len(self.beeps) > 0:
+                self.isbeeping = True
+                self.beeptimer.init(mode=Timer.ONE_SHOT, period=self.beeps[0][1], callback=lambda x: self.__beep())
+                if self.beeps[0][0] <= 20:
+                    self.buzzer.duty_u16(1)
+                else:
+                    self.buzzer.duty_u16(32768)
+                self.buzzer.freq(self.beeps[0][0])
+                del self.beeps[0]
+            else:
+                self.buzzer.freq(20)
+                self.buzzer.duty_u16(0)
+                self.beeptimer.deinit()
+                self.isbeeping = False
+
+    def beep(self, beeps):
+        """Добавляет и запускает последовательность звуковых сигналов"""
+        self.beeps += beeps
+        if not self.isbeeping:
+            self.__beep()
+
+    def deinit(self):
+        self.buzzer.deinit()
+        self.beeptimer.deinit()
diff --git a/src/lib/i2c_lcd.py b/src/lib/i2c_lcd.py
new file mode 100644
index 0000000..f923770
--- /dev/null
+++ b/src/lib/i2c_lcd.py
@@ -0,0 +1,87 @@
+import utime
+import gc
+
+from lcd_api import LcdApi
+from machine import I2C
+
+# PCF8574 pin definitions
+MASK_RS = 0x01  # P0
+MASK_RW = 0x02  # P1
+MASK_E = 0x04  # P2
+
+SHIFT_BACKLIGHT = 3  # P3
+SHIFT_DATA = 4  # P4-P7
+
+
+class I2cLcd(LcdApi):
+
+    # Implements a HD44780 character LCD connected via PCF8574 on I2C
+
+    def __init__(self, i2c, i2c_addr, num_lines, num_columns):
+        self.i2c = i2c
+        self.i2c_addr = i2c_addr
+        self.i2c.writeto(self.i2c_addr, bytes([0]))
+        utime.sleep_ms(20)  # Allow LCD time to powerup
+        # Send reset 3 times
+        self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
+        utime.sleep_ms(5)  # Need to delay at least 4.1 msec
+        self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
+        utime.sleep_ms(1)
+        self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
+        utime.sleep_ms(1)
+        # Put LCD into 4-bit mode
+        self.hal_write_init_nibble(self.LCD_FUNCTION)
+        utime.sleep_ms(1)
+        LcdApi.__init__(self, num_lines, num_columns)
+        cmd = self.LCD_FUNCTION
+        if num_lines > 1:
+            cmd |= self.LCD_FUNCTION_2LINES
+        self.hal_write_command(cmd)
+        gc.collect()
+
+    def hal_write_init_nibble(self, nibble):
+        # Writes an initialization nibble to the LCD.
+        # This particular function is only used during initialization.
+        byte = ((nibble >> 4) & 0x0f) << SHIFT_DATA
+        self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
+        self.i2c.writeto(self.i2c_addr, bytes([byte]))
+        gc.collect()
+
+    def hal_backlight_on(self):
+        # Allows the hal layer to turn the backlight on
+        self.i2c.writeto(self.i2c_addr, bytes([1 << SHIFT_BACKLIGHT]))
+        gc.collect()
+
+    def hal_backlight_off(self):
+        # Allows the hal layer to turn the backlight off
+        self.i2c.writeto(self.i2c_addr, bytes([0]))
+        gc.collect()
+
+    def hal_write_command(self, cmd):
+        # Write a command to the LCD. Data is latched on the falling edge of E.
+        byte = ((self.backlight << SHIFT_BACKLIGHT) |
+                (((cmd >> 4) & 0x0f) << SHIFT_DATA))
+        self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
+        self.i2c.writeto(self.i2c_addr, bytes([byte]))
+        byte = ((self.backlight << SHIFT_BACKLIGHT) |
+                ((cmd & 0x0f) << SHIFT_DATA))
+        self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
+        self.i2c.writeto(self.i2c_addr, bytes([byte]))
+        if cmd <= 3:
+            # The home and clear commands require a worst case delay of 4.1 msec
+            utime.sleep_ms(5)
+        gc.collect()
+
+    def hal_write_data(self, data):
+        # Write data to the LCD. Data is latched on the falling edge of E.
+        byte = (MASK_RS |
+                (self.backlight << SHIFT_BACKLIGHT) |
+                (((data >> 4) & 0x0f) << SHIFT_DATA))
+        self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
+        self.i2c.writeto(self.i2c_addr, bytes([byte]))
+        byte = (MASK_RS |
+                (self.backlight << SHIFT_BACKLIGHT) |
+                ((data & 0x0f) << SHIFT_DATA))
+        self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
+        self.i2c.writeto(self.i2c_addr, bytes([byte]))
+        gc.collect()
diff --git a/src/lib/lcd_api.py b/src/lib/lcd_api.py
new file mode 100644
index 0000000..61945d5
--- /dev/null
+++ b/src/lib/lcd_api.py
@@ -0,0 +1,208 @@
+"""Provides an API for talking to HD44780 compatible character LCDs."""
+
+import time
+
+class LcdApi:
+    """Implements the API for talking with HD44780 compatible character LCDs.
+    This class only knows what commands to send to the LCD, and not how to get
+    them to the LCD.
+
+    It is expected that a derived class will implement the hal_xxx functions.
+    """
+
+    # The following constant names were lifted from the avrlib lcd.h
+    # header file, however, I changed the definitions from bit numbers
+    # to bit masks.
+    #
+    # HD44780 LCD controller command set
+
+    LCD_CLR = 0x01              # DB0: clear display
+    LCD_HOME = 0x02             # DB1: return to home position
+
+    LCD_ENTRY_MODE = 0x04       # DB2: set entry mode
+    LCD_ENTRY_INC = 0x02        # --DB1: increment
+    LCD_ENTRY_SHIFT = 0x01      # --DB0: shift
+
+    LCD_ON_CTRL = 0x08          # DB3: turn lcd/cursor on
+    LCD_ON_DISPLAY = 0x04       # --DB2: turn display on
+    LCD_ON_CURSOR = 0x02        # --DB1: turn cursor on
+    LCD_ON_BLINK = 0x01         # --DB0: blinking cursor
+
+    LCD_MOVE = 0x10             # DB4: move cursor/display
+    LCD_MOVE_DISP = 0x08        # --DB3: move display (0-> move cursor)
+    LCD_MOVE_RIGHT = 0x04       # --DB2: move right (0-> left)
+
+    LCD_FUNCTION = 0x20         # DB5: function set
+    LCD_FUNCTION_8BIT = 0x10    # --DB4: set 8BIT mode (0->4BIT mode)
+    LCD_FUNCTION_2LINES = 0x08  # --DB3: two lines (0->one line)
+    LCD_FUNCTION_10DOTS = 0x04  # --DB2: 5x10 font (0->5x7 font)
+    LCD_FUNCTION_RESET = 0x30   # See "Initializing by Instruction" section
+
+    LCD_CGRAM = 0x40            # DB6: set CG RAM address
+    LCD_DDRAM = 0x80            # DB7: set DD RAM address
+
+    LCD_RS_CMD = 0
+    LCD_RS_DATA = 1
+
+    LCD_RW_WRITE = 0
+    LCD_RW_READ = 1
+
+    def __init__(self, num_lines, num_columns):
+        self.num_lines = num_lines
+        if self.num_lines > 4:
+            self.num_lines = 4
+        self.num_columns = num_columns
+        if self.num_columns > 40:
+            self.num_columns = 40
+        self.cursor_x = 0
+        self.cursor_y = 0
+        self.implied_newline = False
+        self.backlight = True
+        self.display_off()
+        self.backlight_on()
+        self.clear()
+        self.hal_write_command(self.LCD_ENTRY_MODE | self.LCD_ENTRY_INC)
+        self.hide_cursor()
+        self.display_on()
+
+    def clear(self):
+        """Clears the LCD display and moves the cursor to the top left
+        corner.
+        """
+        self.hal_write_command(self.LCD_CLR)
+        self.hal_write_command(self.LCD_HOME)
+        self.cursor_x = 0
+        self.cursor_y = 0
+
+    def show_cursor(self):
+        """Causes the cursor to be made visible."""
+        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
+                               self.LCD_ON_CURSOR)
+
+    def hide_cursor(self):
+        """Causes the cursor to be hidden."""
+        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)
+
+    def blink_cursor_on(self):
+        """Turns on the cursor, and makes it blink."""
+        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
+                               self.LCD_ON_CURSOR | self.LCD_ON_BLINK)
+
+    def blink_cursor_off(self):
+        """Turns on the cursor, and makes it no blink (i.e. be solid)."""
+        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
+                               self.LCD_ON_CURSOR)
+
+    def display_on(self):
+        """Turns on (i.e. unblanks) the LCD."""
+        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)
+
+    def display_off(self):
+        """Turns off (i.e. blanks) the LCD."""
+        self.hal_write_command(self.LCD_ON_CTRL)
+
+    def backlight_on(self):
+        """Turns the backlight on.
+
+        This isn't really an LCD command, but some modules have backlight
+        controls, so this allows the hal to pass through the command.
+        """
+        self.backlight = True
+        self.hal_backlight_on()
+
+    def backlight_off(self):
+        """Turns the backlight off.
+
+        This isn't really an LCD command, but some modules have backlight
+        controls, so this allows the hal to pass through the command.
+        """
+        self.backlight = False
+        self.hal_backlight_off()
+
+    def move_to(self, cursor_x, cursor_y):
+        """Moves the cursor position to the indicated position. The cursor
+        position is zero based (i.e. cursor_x == 0 indicates first column).
+        """
+        self.cursor_x = cursor_x
+        self.cursor_y = cursor_y
+        addr = cursor_x & 0x3f
+        if cursor_y & 1:
+            addr += 0x40    # Lines 1 & 3 add 0x40
+        if cursor_y & 2:    # Lines 2 & 3 add number of columns
+            addr += self.num_columns
+        self.hal_write_command(self.LCD_DDRAM | addr)
+
+    def putchar(self, char):
+        """Writes the indicated character to the LCD at the current cursor
+        position, and advances the cursor by one position.
+        """
+        if char == '\n':
+            if self.implied_newline:
+                # self.implied_newline means we advanced due to a wraparound,
+                # so if we get a newline right after that we ignore it.
+                pass
+            else:
+                self.cursor_x = self.num_columns
+        else:
+            self.hal_write_data(ord(char))
+            self.cursor_x += 1
+        if self.cursor_x >= self.num_columns:
+            self.cursor_x = 0
+            self.cursor_y += 1
+            self.implied_newline = (char != '\n')
+        if self.cursor_y >= self.num_lines:
+            self.cursor_y = 0
+        self.move_to(self.cursor_x, self.cursor_y)
+
+    def putstr(self, string):
+        """Write the indicated string to the LCD at the current cursor
+        position and advances the cursor position appropriately.
+        """
+        for char in string:
+            self.putchar(char)
+
+    def custom_char(self, location, charmap):
+        """Write a character to one of the 8 CGRAM locations, available
+        as chr(0) through chr(7).
+        """
+        location &= 0x7
+        self.hal_write_command(self.LCD_CGRAM | (location << 3))
+        self.hal_sleep_us(40)
+        for i in range(8):
+            self.hal_write_data(charmap[i])
+            self.hal_sleep_us(40)
+        self.move_to(self.cursor_x, self.cursor_y)
+
+    def hal_backlight_on(self):
+        """Allows the hal layer to turn the backlight on.
+
+        If desired, a derived HAL class will implement this function.
+        """
+        pass
+
+    def hal_backlight_off(self):
+        """Allows the hal layer to turn the backlight off.
+
+        If desired, a derived HAL class will implement this function.
+        """
+        pass
+
+    def hal_write_command(self, cmd):
+        """Write a command to the LCD.
+
+        It is expected that a derived HAL class will implement this
+        function.
+        """
+        raise NotImplementedError
+
+    def hal_write_data(self, data):
+        """Write data to the LCD.
+
+        It is expected that a derived HAL class will implement this
+        function.
+        """
+        raise NotImplementedError
+
+    def hal_sleep_us(self, usecs):
+        """Sleep for some time (given in microseconds)."""
+        time.sleep_us(usecs)
diff --git a/src/lib/mfrc522.py b/src/lib/mfrc522.py
new file mode 100644
index 0000000..cfc1c01
--- /dev/null
+++ b/src/lib/mfrc522.py
@@ -0,0 +1,367 @@
+from machine import Pin, SPI
+from os import uname
+
+
+class MFRC522:
+    DEBUG = False
+    OK = 0
+    NOTAGERR = 1
+    ERR = 2
+
+    REQIDL = 0x26
+    REQALL = 0x52
+    AUTHENT1A = 0x60
+    AUTHENT1B = 0x61
+
+    PICC_ANTICOLL1 = 0x93
+    PICC_ANTICOLL2 = 0x95
+    PICC_ANTICOLL3 = 0x97
+
+    def __init__(self, sck, mosi, miso, rst, cs, baudrate=1000000, spi_id=0):
+
+        self.sck = Pin(sck, Pin.OUT)
+        self.mosi = Pin(mosi, Pin.OUT)
+        self.miso = Pin(miso)
+        self.rst = Pin(rst, Pin.OUT)
+        self.cs = Pin(cs, Pin.OUT)
+
+        self.rst.value(0)
+        self.cs.value(1)
+
+        board = uname()[0]
+
+        if board == 'WiPy' or board == 'LoPy' or board == 'FiPy':
+            self.spi = SPI(0)
+            self.spi.init(SPI.MASTER, baudrate=1000000, pins=(self.sck, self.mosi, self.miso))
+        elif (board == 'esp8266') or (board == 'esp32'):
+            self.spi = SPI(baudrate=100000, polarity=0, phase=0, sck=self.sck, mosi=self.mosi, miso=self.miso)
+            self.spi.init()
+        elif board == 'rp2':
+            self.spi = SPI(spi_id, baudrate=baudrate, sck=self.sck, mosi=self.mosi, miso=self.miso)
+        else:
+            raise RuntimeError("Unsupported platform")
+
+        self.rst.value(1)
+        self.init()
+
+    def _wreg(self, reg, val):
+
+        self.cs.value(0)
+        self.spi.write(b'%c' % int(0xff & ((reg << 1) & 0x7e)))
+        self.spi.write(b'%c' % int(0xff & val))
+        self.cs.value(1)
+
+    def _rreg(self, reg):
+
+        self.cs.value(0)
+        self.spi.write(b'%c' % int(0xff & (((reg << 1) & 0x7e) | 0x80)))
+        val = self.spi.read(1)
+        self.cs.value(1)
+
+        return val[0]
+
+    def _sflags(self, reg, mask):
+        self._wreg(reg, self._rreg(reg) | mask)
+
+    def _cflags(self, reg, mask):
+        self._wreg(reg, self._rreg(reg) & (~mask))
+
+    def _tocard(self, cmd, send):
+
+        recv = []
+        bits = irq_en = wait_irq = n = 0
+        stat = self.ERR
+
+        if cmd == 0x0E:
+            irq_en = 0x12
+            wait_irq = 0x10
+        elif cmd == 0x0C:
+            irq_en = 0x77
+            wait_irq = 0x30
+
+        self._wreg(0x02, irq_en | 0x80)
+        self._cflags(0x04, 0x80)
+        self._sflags(0x0A, 0x80)
+        self._wreg(0x01, 0x00)
+
+        for c in send:
+            self._wreg(0x09, c)
+        self._wreg(0x01, cmd)
+
+        if cmd == 0x0C:
+            self._sflags(0x0D, 0x80)
+
+        i = 2000
+        while True:
+            n = self._rreg(0x04)
+            i -= 1
+            if ~((i != 0) and ~(n & 0x01) and ~(n & wait_irq)):
+                break
+
+        self._cflags(0x0D, 0x80)
+
+        if i:
+            if (self._rreg(0x06) & 0x1B) == 0x00:
+                stat = self.OK
+
+                if n & irq_en & 0x01:
+                    stat = self.NOTAGERR
+                elif cmd == 0x0C:
+                    n = self._rreg(0x0A)
+                    lbits = self._rreg(0x0C) & 0x07
+                    if lbits != 0:
+                        bits = (n - 1) * 8 + lbits
+                    else:
+                        bits = n * 8
+
+                    if n == 0:
+                        n = 1
+                    elif n > 16:
+                        n = 16
+
+                    for _ in range(n):
+                        recv.append(self._rreg(0x09))
+            else:
+                stat = self.ERR
+
+        return stat, recv, bits
+
+    def _crc(self, data):
+
+        self._cflags(0x05, 0x04)
+        self._sflags(0x0A, 0x80)
+
+        for c in data:
+            self._wreg(0x09, c)
+
+        self._wreg(0x01, 0x03)
+
+        i = 0xFF
+        while True:
+            n = self._rreg(0x05)
+            i -= 1
+            if not ((i != 0) and not (n & 0x04)):
+                break
+
+        return [self._rreg(0x22), self._rreg(0x21)]
+
+    def init(self):
+
+        self.reset()
+        self._wreg(0x2A, 0x8D)
+        self._wreg(0x2B, 0x3E)
+        self._wreg(0x2D, 30)
+        self._wreg(0x2C, 0)
+        self._wreg(0x15, 0x40)
+        self._wreg(0x11, 0x3D)
+        self.antenna_on()
+
+    def reset(self):
+        self._wreg(0x01, 0x0F)
+
+    def antenna_on(self, on=True):
+
+        if on and ~(self._rreg(0x14) & 0x03):
+            self._sflags(0x14, 0x03)
+        else:
+            self._cflags(0x14, 0x03)
+
+    def request(self, mode):
+
+        self._wreg(0x0D, 0x07)
+        (stat, recv, bits) = self._tocard(0x0C, [mode])
+
+        if (stat != self.OK) | (bits != 0x10):
+            stat = self.ERR
+
+        return stat, bits
+
+    def anticoll(self, anticolN):
+
+        ser_chk = 0
+        ser = [anticolN, 0x20]
+
+        self._wreg(0x0D, 0x00)
+        (stat, recv, bits) = self._tocard(0x0C, ser)
+
+        if stat == self.OK:
+            if len(recv) == 5:
+                for i in range(4):
+                    ser_chk = ser_chk ^ recv[i]
+                if ser_chk != recv[4]:
+                    stat = self.ERR
+            else:
+                stat = self.ERR
+
+        return stat, recv
+
+    def PcdSelect(self, serNum, anticolN):
+        backData = []
+        buf = []
+        buf.append(anticolN)
+        buf.append(0x70)
+        # i = 0
+        ###xorsum=0;
+        for i in serNum:
+            buf.append(i)
+        # while i<5:
+        #    buf.append(serNum[i])
+        #    i = i + 1
+        pOut = self._crc(buf)
+        buf.append(pOut[0])
+        buf.append(pOut[1])
+        (status, backData, backLen) = self._tocard(0x0C, buf)
+        if (status == self.OK) and (backLen == 0x18):
+            return 1
+        else:
+            return 0
+
+    def SelectTag(self, uid):
+        byte5 = 0
+
+        # (status,puid)= self.anticoll(self.PICC_ANTICOLL1)
+        # print("uid",uid,"puid",puid)
+        for i in uid:
+            byte5 = byte5 ^ i
+        puid = uid + [byte5]
+
+        if self.PcdSelect(puid, self.PICC_ANTICOLL1) == 0:
+            return (self.ERR, [])
+        return (self.OK, uid)
+
+    def tohexstring(self, v):
+        s = "["
+        for i in v:
+            if i != v[0]:
+                s = s + ", "
+            s = s + "0x{:02X}".format(i)
+        s = s + "]"
+        return s
+
+    def SelectTagSN(self):
+        valid_uid = []
+        (status, uid) = self.anticoll(self.PICC_ANTICOLL1)
+        # print("Select Tag 1:",self.tohexstring(uid))
+        if status != self.OK:
+            return (self.ERR, [])
+
+        if self.DEBUG:   print("anticol(1) {}".format(uid))
+        if self.PcdSelect(uid, self.PICC_ANTICOLL1) == 0:
+            return (self.ERR, [])
+        if self.DEBUG:   print("pcdSelect(1) {}".format(uid))
+
+        # check if first byte is 0x88
+        if uid[0] == 0x88:
+            # ok we have another type of card
+            valid_uid.extend(uid[1:4])
+            (status, uid) = self.anticoll(self.PICC_ANTICOLL2)
+            # print("Select Tag 2:",self.tohexstring(uid))
+            if status != self.OK:
+                return (self.ERR, [])
+            if self.DEBUG: print("Anticol(2) {}".format(uid))
+            rtn = self.PcdSelect(uid, self.PICC_ANTICOLL2)
+            if self.DEBUG: print("pcdSelect(2) return={} uid={}".format(rtn, uid))
+            if rtn == 0:
+                return (self.ERR, [])
+            if self.DEBUG: print("PcdSelect2() {}".format(uid))
+            # now check again if uid[0] is 0x88
+            if uid[0] == 0x88:
+                valid_uid.extend(uid[1:4])
+                (status, uid) = self.anticoll(self.PICC_ANTICOLL3)
+                # print("Select Tag 3:",self.tohexstring(uid))
+                if status != self.OK:
+                    return (self.ERR, [])
+                if self.DEBUG: print("Anticol(3) {}".format(uid))
+                if self.MFRC522_PcdSelect(uid, self.PICC_ANTICOLL3) == 0:
+                    return (self.ERR, [])
+                if self.DEBUG: print("PcdSelect(3) {}".format(uid))
+        valid_uid.extend(uid[0:5])
+        # if we are here than the uid is ok
+        # let's remove the last BYTE whic is the XOR sum
+
+        return (self.OK, valid_uid[:len(valid_uid) - 1])
+        # return (self.OK , valid_uid)
+
+    def auth(self, mode, addr, sect, ser):
+        return self._tocard(0x0E, [mode, addr] + sect + ser[:4])[0]
+
+    def authKeys(self, uid, addr, keyA=None, keyB=None):
+        status = self.ERR
+        if keyA is not None:
+            status = self.auth(self.AUTHENT1A, addr, keyA, uid)
+        elif keyB is not None:
+            status = self.auth(self.AUTHENT1B, addr, keyB, uid)
+        return status
+
+    def stop_crypto1(self):
+        self._cflags(0x08, 0x08)
+
+    def read(self, addr):
+
+        data = [0x30, addr]
+        data += self._crc(data)
+        (stat, recv, _) = self._tocard(0x0C, data)
+        return stat, recv
+
+    def write(self, addr, data):
+
+        buf = [0xA0, addr]
+        buf += self._crc(buf)
+        (stat, recv, bits) = self._tocard(0x0C, buf)
+
+        if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A):
+            stat = self.ERR
+        else:
+            buf = []
+            for i in range(16):
+                buf.append(data[i])
+            buf += self._crc(buf)
+            (stat, recv, bits) = self._tocard(0x0C, buf)
+            if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A):
+                stat = self.ERR
+        return stat
+
+    def writeSectorBlock(self, uid, sector, block, data, keyA=None, keyB=None):
+        absoluteBlock = sector * 4 + (block % 4)
+        if absoluteBlock > 63:
+            return self.ERR
+        if len(data) != 16:
+            return self.ERR
+        if self.authKeys(uid, absoluteBlock, keyA, keyB) != self.ERR:
+            return self.write(absoluteBlock, data)
+        return self.ERR
+
+    def readSectorBlock(self, uid, sector, block, keyA=None, keyB=None):
+        absoluteBlock = sector * 4 + (block % 4)
+        if absoluteBlock > 63:
+            return self.ERR, None
+        if self.authKeys(uid, absoluteBlock, keyA, keyB) != self.ERR:
+            return self.read(absoluteBlock)
+        return self.ERR, None
+
+    def MFRC522_DumpClassic1K(self, uid, Start=0, End=64, keyA=None, keyB=None):
+        for absoluteBlock in range(Start, End):
+            status = self.authKeys(uid, absoluteBlock, keyA, keyB)
+            # Check if authenticated
+            print("{:02d} S{:02d} B{:1d}: ".format(absoluteBlock, absoluteBlock // 4, absoluteBlock % 4), end="")
+            if status == self.OK:
+                status, block = self.read(absoluteBlock)
+                if status == self.ERR:
+                    break
+                else:
+                    for value in block:
+                        print("{:02X} ".format(value), end="")
+                    print("  ", end="")
+                    for value in block:
+                        if (value > 0x20) and (value < 0x7f):
+                            print(chr(value), end="")
+                        else:
+                            print('.', end="")
+                    print("")
+            else:
+                break
+        if status == self.ERR:
+            print("Authentication error")
+            return self.ERR
+        return self.OK
+
diff --git a/src/main.py b/src/main.py
index b1eb888..271d3d1 100644
--- a/src/main.py
+++ b/src/main.py
@@ -82,8 +82,6 @@ def __change_device(self, device_data: dict) -> None:
             self.__display.print("No devices", line=2)
             return
 
-        print(device_data)
-
         text = f"{device_data['index'] + 1}/{device_data['total']} {device_data['device']['name']}"
         self.__display.print(text, line=2)
 
