├── Data_transfer_between_pc_and_EncroPi ├── EncroPi.py └── data_transfer_to_EncroPi.py ├── Display Images ├── Display_Images_From_PC │ ├── adafruit_display_text │ │ ├── __init__.py │ │ ├── bmp │ │ │ ├── __init__.py │ │ │ ├── indexed.py │ │ │ └── negative_height_check.py │ │ ├── gif.py │ │ └── pnm │ │ │ ├── __init__.py │ │ │ ├── pbm_ascii.py │ │ │ ├── pbm_binary.py │ │ │ ├── pgm │ │ │ ├── __init__.py │ │ │ ├── ascii.py │ │ │ └── binary.py │ │ │ ├── ppm_ascii.py │ │ │ └── ppm_binary.py │ ├── images │ │ ├── img1.bmp │ │ ├── img2.bmp │ │ ├── img3.bmp │ │ ├── img4.bmp │ │ ├── img5.bmp │ │ └── img6.bmp │ ├── images_display.py │ └── lib │ │ └── adafruit_st7789.mpy └── Display_Images_from_SDCard │ ├── adafruit_display_text │ ├── __init__.py │ ├── bmp │ │ ├── __init__.py │ │ ├── indexed.py │ │ └── negative_height_check.py │ ├── gif.py │ └── pnm │ │ ├── __init__.py │ │ ├── pbm_ascii.py │ │ ├── pbm_binary.py │ │ ├── pgm │ │ ├── __init__.py │ │ ├── ascii.py │ │ └── binary.py │ │ ├── ppm_ascii.py │ │ └── ppm_binary.py │ ├── display_image_sdcard_circuitpython.py │ ├── images │ ├── img1.bmp │ ├── img2.bmp │ ├── img3.bmp │ ├── img4.bmp │ ├── img5.bmp │ └── img6.bmp │ └── lib │ ├── adafruit_sdcard.py │ └── adafruit_st7789.mpy ├── EncroPi.py ├── Encryption ├── EncroPi.py ├── encryption_sdcard.py └── encryption_test.py ├── LICENSE ├── README.md ├── adafruit-circuitpython-raspberry_pi_pico-en_US-7.1.1.uf2 ├── data_save_to_sdcard.py ├── firmware.uf2 ├── gpio_test.py ├── images ├── EncroPi (1).png └── Screenshot (29).png └── rtc_test.py /Data_transfer_between_pc_and_EncroPi/EncroPi.py: -------------------------------------------------------------------------------- 1 | ''' 2 | #------------------------------------------------------------------------ 3 | # 4 | # This is a python Library code for EncroPi Board 5 | # Written by SB Components Ltd 6 | # 7 | #================================================================================== 8 | # Copyright (c) SB Components Ltd 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in all 18 | # copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | # SOFTWARE. 27 | #================================================================================== 28 | ''' 29 | 30 | 31 | from time import sleep 32 | import threading 33 | import logging 34 | import ctypes 35 | import serial 36 | import os 37 | 38 | if os.name == "posix": 39 | COMPORT_BASE = "/dev/" 40 | else: 41 | COMPORT_BASE = "" 42 | 43 | dirName = 'imp' 44 | if not os.path.exists(dirName): 45 | os.mkdir(dirName) 46 | 47 | class SerialComm(object): 48 | """ 49 | Low level serial operations 50 | """ 51 | log = logging.getLogger("serial") 52 | log.addHandler(logging.StreamHandler()) 53 | logging.basicConfig(filename='imp/.log.log', level=logging.DEBUG) 54 | 55 | def __init__(self, handlerNotification=None, *args, **kwargs): 56 | self.__ser = None 57 | 58 | self.alive = False 59 | self.timeout = 0.01 60 | self.rxThread = None 61 | self.rxData = [] 62 | self._txLock = threading.Lock() 63 | self.handlerNotification = handlerNotification 64 | 65 | def connect_port(self, port='COM8', baud_rate=115200, timeout=0.5): 66 | """ 67 | Connects to the Comm Port 68 | """ 69 | try: 70 | # open serial port 71 | self.__ser = serial.Serial(port=port, baudrate=baud_rate, 72 | timeout=timeout) 73 | self.alive = True 74 | self.rxThread = threading.Thread(target=self._readLoop) 75 | self.rxThread.daemon = True 76 | self.rxThread.start() 77 | self.log.info("Connected with {} at {} " 78 | "baudrate.".format(port, baud_rate)) 79 | return True 80 | except serial.serialutil.SerialException: 81 | self.alive = False 82 | self.log.error("Couldn't connect with {}.".format(port)) 83 | return False 84 | 85 | def disconnect(self): 86 | """ 87 | Stops read thread, waits for it to exit cleanly and close serial port 88 | """ 89 | self.alive = False 90 | if self.rxThread: 91 | self.rxThread.join() 92 | self.close_port() 93 | self.log.info("Serial Port Disconnected") 94 | 95 | def read_port(self, n=1): 96 | """ 97 | Read n number of bytes from serial port 98 | :param n: Number of bytes to read 99 | :return: read bytes 100 | """ 101 | return self.__ser.read(n) 102 | 103 | def read_line(self): 104 | return self.__ser.readline() 105 | # return self.__ser.readall() 106 | 107 | def write_port(self, data): 108 | """ 109 | :param data: data to send to servo, type: bytearray 110 | :return: Number of bits sent 111 | """ 112 | return self.__ser.write(data) 113 | 114 | def close_port(self): 115 | """ 116 | Check if the port is open. 117 | Close the Port if open 118 | """ 119 | if self.__ser and self._connected: 120 | self.__ser.close() 121 | self.alive = False 122 | 123 | def flush_input(self): 124 | self.__ser.reset_input_buffer() 125 | 126 | def flush_output(self): 127 | self.__ser.reset_output_buffer() 128 | 129 | @property 130 | def _connected(self): 131 | if self.__ser: 132 | return self.__ser.is_open 133 | 134 | @property 135 | def _waiting(self): 136 | if self.__ser: 137 | return self.__ser.inWaiting() 138 | 139 | def _readLoop(self): 140 | """ 141 | Read thread main loop 142 | """ 143 | try: 144 | while self.alive: 145 | data = self.read_line() 146 | if data != b'': 147 | self.log.info("Serial Response: %s", data) 148 | self.rxData.append(data) 149 | self.update_rx_data(data) 150 | #self.rxData = [] 151 | 152 | except serial.SerialException as SE: 153 | self.log.error("Serial Exception: {}.".format(SE)) 154 | self.close_port() 155 | 156 | def write(self, data): 157 | """ 158 | Write data to serial port 159 | """ 160 | with self._txLock: 161 | self.log.info("Serial Write: {}".format(data)) 162 | self.write_port(data) 163 | self.flush_input() 164 | return True 165 | 166 | def update_rx_data(self, data): 167 | pass 168 | 169 | class USB(SerialComm): 170 | def __init__(self): 171 | SerialComm.__init__(self) 172 | def connect(self, port, baud_rate): 173 | self.connect_port(port=port, baud_rate=baud_rate, timeout=0.5) 174 | 175 | def disconnect(self): 176 | self.disconnect() 177 | 178 | def transmit_message(self, data): 179 | self.write(data) 180 | 181 | def set_variables(self): 182 | pass 183 | -------------------------------------------------------------------------------- /Data_transfer_between_pc_and_EncroPi/data_transfer_to_EncroPi.py: -------------------------------------------------------------------------------- 1 | ''' 2 | #------------------------------------------------------------------------ 3 | # 4 | # This is a python Example code for EncroPi Board 5 | # Written by SB Components Ltd 6 | # 7 | #================================================================================== 8 | # Copyright (c) SB Components Ltd 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in all 18 | # copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | # SOFTWARE. 27 | #================================================================================== 28 | ''' 29 | 30 | from time import sleep 31 | import threading 32 | import logging 33 | import EncroPi 34 | import ctypes 35 | import serial 36 | import time 37 | import logging 38 | import os 39 | 40 | if os.name == "posix": 41 | COMPORT_BASE = "/dev/" 42 | else: 43 | COMPORT_BASE = "" 44 | 45 | s = EncroPi.USB() 46 | s.connect('COM8',9600) 47 | 48 | while True: 49 | data = input('Enter data = ') 50 | s.transmit_message(data.encode("utf-8")+b'\n\r') 51 | time.sleep(0.5) 52 | -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/adafruit_display_text/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_imageload` 7 | ==================================================== 8 | 9 | Load pixel values (indices or colors) into a bitmap and colors into a palette. 10 | 11 | * Author(s): Scott Shawcroft 12 | 13 | """ 14 | # pylint: disable=import-outside-toplevel 15 | 16 | __version__ = "0.0.0-auto.0" 17 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 18 | 19 | 20 | def load(file_or_filename, *, bitmap=None, palette=None): 21 | """Load pixel values (indices or colors) into a bitmap and colors into a palette. 22 | 23 | bitmap is the desired type. It must take width, height and color_depth in the constructor. It 24 | must also have a _load_row method to load a row's worth of pixel data. 25 | 26 | palette is the desired pallete type. The constructor should take the number of colors and 27 | support assignment to indices via []. 28 | """ 29 | if not bitmap or not palette: 30 | try: 31 | # use displayio if available 32 | import displayio 33 | 34 | if not bitmap: 35 | bitmap = displayio.Bitmap 36 | if not palette: 37 | palette = displayio.Palette 38 | except ModuleNotFoundError: 39 | # meh, we tried 40 | pass 41 | 42 | if isinstance(file_or_filename, str): 43 | open_file = open(file_or_filename, "rb") 44 | else: 45 | open_file = file_or_filename 46 | 47 | with open_file as file: 48 | header = file.read(3) 49 | file.seek(0) 50 | if header.startswith(b"BM"): 51 | from . import bmp 52 | 53 | return bmp.load(file, bitmap=bitmap, palette=palette) 54 | if header.startswith(b"P"): 55 | from . import pnm 56 | 57 | return pnm.load(file, header, bitmap=bitmap, palette=palette) 58 | if header.startswith(b"GIF"): 59 | from . import gif 60 | 61 | return gif.load(file, bitmap=bitmap, palette=palette) 62 | raise RuntimeError("Unsupported image format") 63 | -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/adafruit_display_text/bmp/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_imageload.bmp` 7 | ==================================================== 8 | 9 | Load pixel values (indices or colors) into a bitmap and colors into a palette from a BMP file. 10 | 11 | * Author(s): Scott Shawcroft 12 | 13 | """ 14 | # pylint: disable=import-outside-toplevel 15 | 16 | __version__ = "0.0.0-auto.0" 17 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 18 | 19 | 20 | def load(file, *, bitmap=None, palette=None): 21 | """Loads a bmp image from the open ``file``. 22 | 23 | Returns tuple of bitmap object and palette object. 24 | 25 | :param object bitmap: Type to store bitmap data. Must have API similar to `displayio.Bitmap`. 26 | Will be skipped if None 27 | :param object palette: Type to store the palette. Must have API similar to 28 | `displayio.Palette`. Will be skipped if None""" 29 | file.seek(10) 30 | data_start = int.from_bytes(file.read(4), "little") 31 | # f.seek(14) 32 | # bmp_header_length = int.from_bytes(file.read(4), 'little') 33 | # print(bmp_header_length) 34 | file.seek(0x12) # Width of the bitmap in pixels 35 | _width = int.from_bytes(file.read(4), "little") 36 | try: 37 | _height = int.from_bytes(file.read(4), "little") 38 | except OverflowError as error: 39 | raise NotImplementedError( 40 | "Negative height BMP files are not supported on builds without longint" 41 | ) from error 42 | file.seek(0x1C) # Number of bits per pixel 43 | color_depth = int.from_bytes(file.read(2), "little") 44 | file.seek(0x1E) # Compression type 45 | compression = int.from_bytes(file.read(2), "little") 46 | file.seek(0x2E) # Number of colors in the color palette 47 | colors = int.from_bytes(file.read(4), "little") 48 | 49 | if colors == 0 and color_depth >= 16: 50 | raise NotImplementedError("True color BMP unsupported") 51 | 52 | if compression > 2: 53 | raise NotImplementedError("bitmask compression unsupported") 54 | 55 | if colors == 0: 56 | colors = 2 ** color_depth 57 | from . import indexed 58 | 59 | return indexed.load( 60 | file, 61 | _width, 62 | _height, 63 | data_start, 64 | colors, 65 | color_depth, 66 | compression, 67 | bitmap=bitmap, 68 | palette=palette, 69 | ) 70 | -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/adafruit_display_text/bmp/indexed.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_imageload.bmp.indexed` 7 | ==================================================== 8 | 9 | Load pixel values (indices or colors) into a bitmap and colors into a palette from an indexed BMP. 10 | 11 | * Author(s): Scott Shawcroft 12 | 13 | """ 14 | 15 | __version__ = "0.0.0-auto.0" 16 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 17 | 18 | import sys 19 | 20 | try: 21 | from bitmaptools import readinto as _bitmap_readinto 22 | except ImportError: 23 | _bitmap_readinto = None # pylint: disable=invalid-name 24 | 25 | 26 | def load( 27 | file, 28 | width, 29 | height, 30 | data_start, 31 | colors, 32 | color_depth, 33 | compression, 34 | *, 35 | bitmap=None, 36 | palette=None 37 | ): 38 | """Loads indexed bitmap data into bitmap and palette objects. 39 | 40 | :param file file: The open bmp file 41 | :param int width: Image width in pixels 42 | :param int height: Image height in pixels 43 | :param int data_start: Byte location where the data starts (after headers) 44 | :param int colors: Number of distinct colors in the image 45 | :param int color_depth: Number of bits used to store a value 46 | :param int compression: 0 - none, 1 - 8bit RLE, 2 - 4bit RLE""" 47 | # pylint: disable=too-many-arguments,too-many-locals,too-many-branches 48 | if palette: 49 | palette = palette(colors) 50 | 51 | file.seek(data_start - colors * 4) 52 | for value in range(colors): 53 | c_bytes = file.read(4) 54 | # Need to swap red & blue bytes (bytes 0 and 2) 55 | palette[value] = bytes( 56 | b"".join([c_bytes[2:3], c_bytes[1:2], c_bytes[0:1], c_bytes[3:1]]) 57 | ) 58 | 59 | if bitmap: 60 | minimum_color_depth = 1 61 | while colors > 2 ** minimum_color_depth: 62 | minimum_color_depth *= 2 63 | 64 | if sys.maxsize > 1073741823: 65 | # pylint: disable=import-outside-toplevel, relative-beyond-top-level 66 | from .negative_height_check import negative_height_check 67 | 68 | # convert unsigned int to signed int when height is negative 69 | height = negative_height_check(height) 70 | bitmap = bitmap(width, abs(height), colors) 71 | file.seek(data_start) 72 | line_size = width // (8 // color_depth) 73 | if width % (8 // color_depth) != 0: 74 | line_size += 1 75 | if line_size % 4 != 0: 76 | line_size += 4 - line_size % 4 77 | 78 | mask = (1 << minimum_color_depth) - 1 79 | if height > 0: 80 | range1 = height - 1 81 | range2 = -1 82 | range3 = -1 83 | else: 84 | range1 = 0 85 | range2 = abs(height) 86 | range3 = 1 87 | 88 | if compression == 0: 89 | 90 | if _bitmap_readinto: 91 | _bitmap_readinto( 92 | bitmap, 93 | file, 94 | bits_per_pixel=color_depth, 95 | element_size=4, 96 | reverse_pixels_in_element=True, 97 | reverse_rows=True, 98 | ) 99 | 100 | else: # use the standard file.readinto 101 | chunk = bytearray(line_size) 102 | for y in range(range1, range2, range3): 103 | file.readinto(chunk) 104 | pixels_per_byte = 8 // color_depth 105 | offset = y * width 106 | 107 | for x in range(width): 108 | i = x // pixels_per_byte 109 | pixel = ( 110 | chunk[i] >> (8 - color_depth * (x % pixels_per_byte + 1)) 111 | ) & mask 112 | bitmap[offset + x] = pixel 113 | elif compression in (1, 2): 114 | decode_rle( 115 | bitmap=bitmap, 116 | file=file, 117 | compression=compression, 118 | y_range=(range1, range2, range3), 119 | width=width, 120 | ) 121 | 122 | return bitmap, palette 123 | 124 | 125 | def decode_rle(bitmap, file, compression, y_range, width): 126 | """Helper to decode RLE images""" 127 | # pylint: disable=too-many-locals,too-many-nested-blocks,too-many-branches 128 | 129 | # RLE algorithm, either 8-bit (1) or 4-bit (2) 130 | # 131 | # Ref: http://www.fileformat.info/format/bmp/egff.htm 132 | 133 | is_4bit = compression == 2 134 | 135 | # This will store the 2-byte run commands, which are either an 136 | # amount to repeat and a value to repeat, or a 0x00 and command 137 | # marker. 138 | run_buf = bytearray(2) 139 | 140 | # We need to be prepared to load up to 256 pixels of literal image 141 | # data. (0xFF is max literal length, but odd literal runs are padded 142 | # up to an even byte count, so we need space for 256 in the case of 143 | # 8-bit.) 4-bit images can get away with half that. 144 | literal_buf = bytearray(128 if is_4bit else 256) 145 | 146 | # We iterate with numbers rather than a range because the "delta" 147 | # command can cause us to jump forward arbitrarily in the output 148 | # image. 149 | # 150 | # In theory RLE images are only stored in bottom-up scan line order, 151 | # but we support either. 152 | (range1, range2, range3) = y_range 153 | y = range1 154 | x = 0 155 | 156 | while y * range3 < range2 * range3: 157 | offset = y * width + x 158 | 159 | # We keep track of how much space is left in our row so that we 160 | # can avoid writing extra data outside of the Bitmap. While the 161 | # reference above seems to say that the "end run" command is 162 | # optional and that image data should wrap from one scan line to 163 | # the next, in practice (looking at the output of ImageMagick 164 | # and GIMP, and what Preview renders) the bitmap part of the 165 | # image can contain data that goes beyond the image’s stated 166 | # width that should just be ignored. For example, the 8bit RLE 167 | # file is 15px wide but has data for 16px. 168 | width_remaining = width - x 169 | 170 | file.readinto(run_buf) 171 | 172 | if run_buf[0] == 0: 173 | # A repeat length of "0" is a special command. The next byte 174 | # tells us what needs to happen. 175 | if run_buf[1] == 0: 176 | # end of the current scan line 177 | y = y + range3 178 | x = 0 179 | elif run_buf[1] == 1: 180 | # end of image 181 | break 182 | elif run_buf[1] == 2: 183 | # delta command jumps us ahead in the bitmap output by 184 | # the x, y amounts stored in the next 2 bytes. 185 | file.readinto(run_buf) 186 | 187 | x = x + run_buf[0] 188 | y = y + run_buf[1] * range3 189 | else: 190 | # command values of 3 or more indicate that many pixels 191 | # of literal (uncompressed) image data. For 8-bit mode, 192 | # this is raw bytes, but 4-bit mode counts in nibbles. 193 | literal_length_px = run_buf[1] 194 | 195 | # Inverting the value here to get round-up integer division 196 | if is_4bit: 197 | read_length_bytes = -(-literal_length_px // 2) 198 | else: 199 | read_length_bytes = literal_length_px 200 | 201 | # If the run has an odd length then there’s a 1-byte padding 202 | # we need to consume but not write into the output 203 | if read_length_bytes % 2 == 1: 204 | read_length_bytes += 1 205 | 206 | # We use memoryview to artificially limit the length of 207 | # literal_buf so that readinto only reads the amount 208 | # that we want. 209 | literal_buf_mem = memoryview(literal_buf) 210 | file.readinto(literal_buf_mem[0:read_length_bytes]) 211 | 212 | if is_4bit: 213 | for i in range(0, min(literal_length_px, width_remaining)): 214 | # Expanding the two nibbles of the 4-bit data 215 | # into two bytes for our output bitmap. 216 | if i % 2 == 0: 217 | bitmap[offset + i] = literal_buf[i // 2] >> 4 218 | else: 219 | bitmap[offset + i] = literal_buf[i // 2] & 0x0F 220 | else: 221 | # 8-bit values are just a raw copy (limited by 222 | # what’s left in the row so we don’t overflow out of 223 | # the buffer) 224 | for i in range(0, min(literal_length_px, width_remaining)): 225 | bitmap[offset + i] = literal_buf[i] 226 | 227 | x = x + literal_length_px 228 | else: 229 | # first byte was not 0, which means it tells us how much to 230 | # repeat the next byte into the output 231 | run_length_px = run_buf[0] 232 | 233 | if is_4bit: 234 | # In 4 bit mode, we repeat the *two* values that are 235 | # packed into the next byte. The repeat amount is based 236 | # on pixels, not bytes, though, so if we were to repeat 237 | # 0xab 3 times, the output pixel values would be: 0x0a 238 | # 0x0b 0x0a (notice how it ends at 0x0a) rather than 239 | # 0x0a 0x0b 0x0a 0x0b 0x0a 0x0b 240 | run_values = [run_buf[1] >> 4, run_buf[1] & 0x0F] 241 | for i in range(0, min(run_length_px, width_remaining)): 242 | bitmap[offset + i] = run_values[i % 2] 243 | else: 244 | run_value = run_buf[1] 245 | for i in range(0, min(run_length_px, width_remaining)): 246 | bitmap[offset + i] = run_value 247 | 248 | x = x + run_length_px 249 | -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/adafruit_display_text/bmp/negative_height_check.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | Check for negative height on the BMP. 7 | Seperated into it's own file to support builds 8 | without longint. 9 | """ 10 | 11 | 12 | def negative_height_check(height): 13 | """Check the height return modified if negative.""" 14 | if height > 0x7FFFFFFF: 15 | return height - 4294967296 16 | return height 17 | -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/adafruit_display_text/gif.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 Radomir Dopieralski for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_imageload.gif` 7 | ==================================================== 8 | 9 | Load pixel values (indices or colors) into a bitmap and colors into a palette 10 | from a GIF file. 11 | 12 | * Author(s): Radomir Dopieralski 13 | 14 | """ 15 | 16 | import struct 17 | 18 | 19 | __version__ = "0.0.0-auto.0" 20 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 21 | 22 | 23 | def load(file, *, bitmap=None, palette=None): 24 | """Loads a GIF image from the open ``file``. 25 | 26 | Returns tuple of bitmap object and palette object. 27 | 28 | :param object bitmap: Type to store bitmap data. Must have API similar to `displayio.Bitmap`. 29 | Will be skipped if None 30 | :param object palette: Type to store the palette. Must have API similar to 31 | `displayio.Palette`. Will be skipped if None""" 32 | header = file.read(6) 33 | if header not in {b"GIF87a", b"GIF89a"}: 34 | raise ValueError("Not a GIF file") 35 | width, height, flags, _, _ = struct.unpack("> 4) + 1 44 | bitmap_obj = bitmap(width, height, (1 << color_bits) - 1) 45 | while True: 46 | block_type = file.read(1)[0] 47 | if block_type == 0x2C: # frame 48 | _read_frame(file, bitmap_obj) 49 | elif block_type == 0x21: # extension 50 | _ = file.read(1)[0] 51 | # 0x01 = label, 0xfe = comment 52 | _ = bytes(_read_blockstream(file)) 53 | elif block_type == 0x3B: # terminator 54 | break 55 | else: 56 | raise ValueError("Bad block type") 57 | return bitmap_obj, palette_obj 58 | 59 | 60 | def _read_frame(file, bitmap): 61 | """Read a signle frame and apply it to the bitmap.""" 62 | ddx, ddy, width, _, flags = struct.unpack("= width: 77 | x = 0 78 | y += 1 79 | 80 | 81 | def _read_blockstream(file): 82 | """Read a block from a file.""" 83 | while True: 84 | size = file.read(1)[0] 85 | if size == 0: 86 | break 87 | for _ in range(size): 88 | yield file.read(1)[0] 89 | 90 | 91 | class EndOfData(Exception): 92 | """Signified end of compressed data.""" 93 | 94 | 95 | class LZWDict: 96 | """A dictionary of LZW codes.""" 97 | 98 | def __init__(self, code_size): 99 | self.code_size = code_size 100 | self.clear_code = 1 << code_size 101 | self.end_code = self.clear_code + 1 102 | self.codes = [] 103 | self.last = None 104 | self.clear() 105 | 106 | def clear(self): 107 | """Reset the dictionary to default codes.""" 108 | self.last = b"" 109 | self.code_len = self.code_size + 1 110 | self.codes[:] = [] 111 | 112 | def decode(self, code): 113 | """Decode a code.""" 114 | if code == self.clear_code: 115 | self.clear() 116 | return b"" 117 | if code == self.end_code: 118 | raise EndOfData() 119 | if code < self.clear_code: 120 | value = bytes([code]) 121 | elif code <= len(self.codes) + self.end_code: 122 | value = self.codes[code - self.end_code - 1] 123 | else: 124 | value = self.last + self.last[0:1] 125 | if self.last: 126 | self.codes.append(self.last + value[0:1]) 127 | if ( 128 | len(self.codes) + self.end_code + 1 >= 1 << self.code_len 129 | and self.code_len < 12 130 | ): 131 | self.code_len += 1 132 | self.last = value 133 | return value 134 | 135 | 136 | def lzw_decode(data, code_size): 137 | """Decode LZW-compressed data.""" 138 | dictionary = LZWDict(code_size) 139 | bit = 0 140 | try: 141 | byte = next(data) # pylint: disable=stop-iteration-return 142 | try: 143 | while True: 144 | code = 0 145 | for i in range(dictionary.code_len): 146 | code |= ((byte >> bit) & 0x01) << i 147 | bit += 1 148 | if bit >= 8: 149 | bit = 0 150 | byte = next(data) # pylint: disable=stop-iteration-return 151 | yield dictionary.decode(code) 152 | except EndOfData: 153 | while True: 154 | next(data) # pylint: disable=stop-iteration-return 155 | except StopIteration: 156 | pass 157 | -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/adafruit_display_text/pnm/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and colors into a palette. 13 | 14 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 15 | 16 | """ 17 | # pylint: disable=import-outside-toplevel 18 | 19 | __version__ = "0.0.0-auto.0" 20 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 21 | 22 | 23 | def load(file, header, *, bitmap=None, palette=None): 24 | """ 25 | Scan for netpbm format info, skip over comments, and and delegate to a submodule 26 | to do the actual data loading. 27 | Formats P1, P4 have two space padded pieces of information: width and height. 28 | All other formats have three: width, height, and max color value. 29 | This load function will move the file stream pointer to the start of data in all cases. 30 | """ 31 | # pylint: disable=too-many-branches 32 | magic_number = header[:2] 33 | file.seek(2) 34 | pnm_header = [] 35 | next_value = bytearray() 36 | while True: 37 | # We have all we need at length 3 for formats P2, P3, P5, P6 38 | if len(pnm_header) == 3: 39 | if magic_number in [b"P2", b"P5"]: 40 | from . import pgm 41 | 42 | return pgm.load( 43 | file, magic_number, pnm_header, bitmap=bitmap, palette=palette 44 | ) 45 | 46 | if magic_number == b"P3": 47 | from . import ppm_ascii 48 | 49 | return ppm_ascii.load( 50 | file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette 51 | ) 52 | 53 | if magic_number == b"P6": 54 | from . import ppm_binary 55 | 56 | return ppm_binary.load( 57 | file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette 58 | ) 59 | 60 | if len(pnm_header) == 2 and magic_number in [b"P1", b"P4"]: 61 | bitmap = bitmap(pnm_header[0], pnm_header[1], 1) 62 | if palette: 63 | palette = palette(1) 64 | palette[0] = b"\xFF\xFF\xFF" 65 | if magic_number.startswith(b"P1"): 66 | from . import pbm_ascii 67 | 68 | return pbm_ascii.load( 69 | file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette 70 | ) 71 | 72 | from . import pbm_binary 73 | 74 | return pbm_binary.load( 75 | file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette 76 | ) 77 | 78 | next_byte = file.read(1) 79 | if next_byte == b"": 80 | raise RuntimeError("Unsupported image format {}".format(magic_number)) 81 | if next_byte == b"#": # comment found, seek until a newline or EOF is found 82 | while file.read(1) not in [b"", b"\n"]: # EOF or NL 83 | pass 84 | elif not next_byte.isdigit(): # boundary found in header data 85 | if next_value: 86 | # pull values until space is found 87 | pnm_header.append(int("".join(["%c" % char for char in next_value]))) 88 | next_value = bytearray() # reset the byte array 89 | else: 90 | next_value += next_byte # push the digit into the byte array 91 | -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/adafruit_display_text/pnm/pbm_ascii.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm.pbm_ascii` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and for an ascii ppm, 13 | return None for pallet. 14 | 15 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 16 | 17 | """ 18 | 19 | __version__ = "0.0.0-auto.0" 20 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 21 | 22 | 23 | def load(file, width, height, bitmap=None, palette=None): 24 | """ 25 | Load a P1 'PBM' ascii image into the displayio.Bitmap 26 | """ 27 | next_byte = True 28 | for y in range(height): 29 | x = 0 30 | while next_byte: 31 | next_byte = file.read(1) 32 | if not next_byte.isdigit(): 33 | continue 34 | bitmap[x, y] = 1 if next_byte == b"1" else 0 35 | if x == width - 1: 36 | break 37 | x += 1 38 | return bitmap, palette 39 | -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/adafruit_display_text/pnm/pbm_binary.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm.pbm_binary` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and for an ascii ppm, 13 | return None for pallet. 14 | 15 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 16 | 17 | """ 18 | 19 | __version__ = "0.0.0-auto.0" 20 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 21 | 22 | 23 | def load(file, width, height, bitmap=None, palette=None): 24 | """ 25 | Load a P4 'PBM' binary image into the displayio.Bitmap 26 | """ 27 | x = 0 28 | y = 0 29 | while True: 30 | next_byte = file.read(1) 31 | if not next_byte: 32 | break # out of bits 33 | for bit in iterbits(next_byte): 34 | bitmap[x, y] = bit 35 | x += 1 36 | if x > width - 1: 37 | y += 1 38 | x = 0 39 | if y > height - 1: 40 | break 41 | return bitmap, palette 42 | 43 | 44 | def iterbits(b): 45 | """ 46 | generator to iterate over the bits in a byte (character) 47 | """ 48 | in_char = reverse(int.from_bytes(b, "little")) 49 | for i in range(8): 50 | yield (in_char >> i) & 1 51 | 52 | 53 | def reverse(b): 54 | """ 55 | reverse bit order so the iterbits works 56 | """ 57 | b = (b & 0xF0) >> 4 | (b & 0x0F) << 4 58 | b = (b & 0xCC) >> 2 | (b & 0x33) << 2 59 | b = (b & 0xAA) >> 1 | (b & 0x55) << 1 60 | return b 61 | -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/adafruit_display_text/pnm/pgm/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm.pgm` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and colors into a palette. 13 | 14 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 15 | 16 | """ 17 | # pylint: disable=import-outside-toplevel 18 | 19 | 20 | def load(file, magic_number, header, *, bitmap=None, palette=None): 21 | """ 22 | Perform the load of Netpbm greyscale images (P2, P5) 23 | """ 24 | if header[2] > 256: 25 | raise NotImplementedError("16 bit files are not supported") 26 | width = header[0] 27 | height = header[1] 28 | 29 | if magic_number == b"P2": # To handle ascii PGM files. 30 | from . import ascii as pgm_ascii 31 | 32 | return pgm_ascii.load(file, width, height, bitmap=bitmap, palette=palette) 33 | 34 | if magic_number == b"P5": # To handle binary PGM files. 35 | from . import binary 36 | 37 | return binary.load(file, width, height, bitmap=bitmap, palette=palette) 38 | 39 | raise NotImplementedError("Was not able to send image") 40 | -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/adafruit_display_text/pnm/pgm/ascii.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm.pgm.ascii` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and colors into a palette. 13 | 14 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 15 | 16 | """ 17 | 18 | 19 | def load(file, width, height, bitmap=None, palette=None): 20 | """ 21 | Load a PGM ascii file (P2) 22 | """ 23 | data_start = file.tell() # keep this so we can rewind 24 | _palette_colors = set() 25 | pixel = bytearray() 26 | # build a set of all colors present in the file, so palette and bitmap can be constructed 27 | while True: 28 | byte = file.read(1) 29 | if byte == b"": 30 | break 31 | if not byte.isdigit(): 32 | int_pixel = int("".join(["%c" % char for char in pixel])) 33 | _palette_colors.add(int_pixel) 34 | pixel = bytearray() 35 | pixel += byte 36 | if palette: 37 | palette = build_palette(palette, _palette_colors) 38 | if bitmap: 39 | bitmap = bitmap(width, height, len(_palette_colors)) 40 | _palette_colors = list(_palette_colors) 41 | file.seek(data_start) 42 | for y in range(height): 43 | for x in range(width): 44 | pixel = bytearray() 45 | while True: 46 | byte = file.read(1) 47 | if not byte.isdigit(): 48 | break 49 | pixel += byte 50 | int_pixel = int("".join(["%c" % char for char in pixel])) 51 | bitmap[x, y] = _palette_colors.index(int_pixel) 52 | return bitmap, palette 53 | 54 | 55 | def build_palette(palette_class, palette_colors): # pylint: disable=duplicate-code 56 | """ 57 | construct the Palette, and populate it with the set of palette_colors 58 | """ 59 | palette = palette_class(len(palette_colors)) 60 | for counter, color in enumerate(palette_colors): 61 | palette[counter] = bytes([color, color, color]) 62 | return palette 63 | -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/adafruit_display_text/pnm/pgm/binary.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm.pgm.binary` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and colors into a palette. 13 | 14 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 15 | 16 | """ 17 | 18 | 19 | def load(file, width, height, bitmap=None, palette=None): 20 | """ 21 | Load a P5 format file (binary), handle PGM (greyscale) 22 | """ 23 | palette_colors = set() 24 | data_start = file.tell() 25 | for y in range(height): 26 | data_line = iter(bytes(file.read(width))) 27 | for pixel in data_line: 28 | palette_colors.add(pixel) 29 | 30 | if palette: 31 | palette = build_palette(palette, palette_colors) 32 | if bitmap: 33 | bitmap = bitmap(width, height, len(palette_colors)) 34 | palette_colors = list(palette_colors) 35 | file.seek(data_start) 36 | for y in range(height): 37 | data_line = iter(bytes(file.read(width))) 38 | for x, pixel in enumerate(data_line): 39 | bitmap[x, y] = palette_colors.index(pixel) 40 | return bitmap, palette 41 | 42 | 43 | def build_palette(palette_class, palette_colors): 44 | """ 45 | construct the Palette, and populate it with the set of palette_colors 46 | """ 47 | _palette = palette_class(len(palette_colors)) 48 | for counter, color in enumerate(palette_colors): 49 | _palette[counter] = bytes([color, color, color]) 50 | return _palette 51 | -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/adafruit_display_text/pnm/ppm_ascii.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm.ppm_ascii` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and for an ascii ppm, 13 | return None for pallet. 14 | 15 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 16 | 17 | """ 18 | 19 | __version__ = "0.0.0-auto.0" 20 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 21 | 22 | 23 | def load(file, width, height, bitmap=None, palette=None): 24 | """ 25 | :param stream file: infile with the position set at start of data 26 | :param int width: 27 | :param int height: 28 | :param int max_colors: color space of file 29 | :param bitmap: displayio.Bitmap class 30 | :param palette: displayio.Palette class 31 | :return tuple: 32 | """ 33 | palette_colors = set() 34 | data_start = file.tell() 35 | for triplet in read_three_colors(file): 36 | palette_colors.add(triplet) 37 | 38 | if palette: 39 | palette = palette(len(palette_colors)) 40 | for counter, color in enumerate(palette_colors): 41 | palette[counter] = color 42 | if bitmap: 43 | file.seek(data_start) 44 | bitmap = bitmap(width, height, len(palette_colors)) 45 | palette_colors = list(palette_colors) 46 | for y in range(height): 47 | for x in range(width): 48 | for color in read_three_colors(file): 49 | bitmap[x, y] = palette_colors.index(color) 50 | break # exit the inner generator 51 | return bitmap, palette 52 | 53 | 54 | def read_three_colors(file): 55 | """ 56 | Generator to read integer values from file, in groups of three. 57 | Each value can be len 1-3, for values 0 - 255, space padded. 58 | :return tuple[int]: 59 | """ 60 | triplet = [] 61 | color = bytearray() 62 | while True: 63 | this_byte = file.read(1) 64 | if this_byte.isdigit(): 65 | color += this_byte 66 | # not a digit means we completed one number (found a space separator or EOF) 67 | elif color or (triplet and this_byte == b""): 68 | triplet.append(int("".join(["%c" % char for char in color]))) 69 | color = bytearray() 70 | if len(triplet) == 3: # completed one pixel 71 | yield bytes(tuple(triplet)) 72 | triplet = [] 73 | # short circuit must be after all other cases, so we yield the last pixel before returning 74 | if this_byte == b"": 75 | return 76 | -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/adafruit_display_text/pnm/ppm_binary.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm.ppm_binary` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and for a binary ppm, 13 | return None for pallet. 14 | 15 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 16 | 17 | """ 18 | 19 | __version__ = "0.0.0-auto.0" 20 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 21 | 22 | 23 | def load(file, width, height, bitmap=None, palette=None): 24 | """Load pixel values (indices or colors) into a bitmap and for a binary 25 | ppm, return None for pallet.""" 26 | 27 | data_start = file.tell() 28 | palette_colors = set() 29 | line_size = width * 3 30 | 31 | for y in range(height): 32 | data_line = iter(bytes(file.read(line_size))) 33 | for red in data_line: 34 | # red, green, blue 35 | palette_colors.add((red, next(data_line), next(data_line))) 36 | 37 | if palette: 38 | palette = palette(len(palette_colors)) 39 | for counter, color in enumerate(palette_colors): 40 | palette[counter] = bytes(color) 41 | if bitmap: 42 | bitmap = bitmap(width, height, len(palette_colors)) 43 | file.seek(data_start) 44 | palette_colors = list(palette_colors) 45 | for y in range(height): 46 | x = 0 47 | data_line = iter(bytes(file.read(line_size))) 48 | for red in data_line: 49 | # red, green, blue 50 | bitmap[x, y] = palette_colors.index( 51 | (red, next(data_line), next(data_line)) 52 | ) 53 | x += 1 54 | 55 | return bitmap, palette 56 | -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/images/img1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/Display Images/Display_Images_From_PC/images/img1.bmp -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/images/img2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/Display Images/Display_Images_From_PC/images/img2.bmp -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/images/img3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/Display Images/Display_Images_From_PC/images/img3.bmp -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/images/img4.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/Display Images/Display_Images_From_PC/images/img4.bmp -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/images/img5.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/Display Images/Display_Images_From_PC/images/img5.bmp -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/images/img6.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/Display Images/Display_Images_From_PC/images/img6.bmp -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/images_display.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import board 4 | import terminalio 5 | import displayio 6 | from adafruit_display_text import label 7 | from adafruit_st7789 import ST7789 8 | import busio 9 | import time 10 | 11 | 12 | # Release any resources currently in use for the displays 13 | displayio.release_displays() 14 | 15 | img_filenames = ( "/images/img1.bmp","/images/img2.bmp","/images/img3.bmp","/images/img4.bmp","/images/img5.bmp","/images/img6.bmp") 16 | 17 | board_type = os.uname().machine 18 | if 'Pico' in board_type: 19 | # Raspberry Pi Pico pinout, one possibility, at "southwest" of board 20 | tft_clk = board.GP10 # must be a SPI CLK 21 | tft_mosi= board.GP11 # must be a SPI TX 22 | tft_rst = board.GP12 23 | tft_dc = board.GP8 24 | tft_cs = board.GP9 25 | tft_bl = board.GP13 26 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 27 | else: 28 | print("ERROR: Unknown board!") 29 | 30 | display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=tft_rst) 31 | display = ST7789(display_bus, rotation=270, width=240, height=135, rowstart=40, colstart=53) 32 | 33 | # Make the display context 34 | splash = displayio.Group() 35 | display.show(splash) 36 | 37 | bmpfiles = sorted("/images/" + fn for fn in os.listdir("/images") if fn.lower().endswith("bmp")) 38 | 39 | while True: 40 | if len(bmpfiles) == 0: 41 | print("N0, BMP Files") 42 | break 43 | 44 | for filename in bmpfiles: 45 | print("showing bmp image", filename) 46 | 47 | bitmap_file = open(filename, "rb") 48 | bitmap = displayio.OnDiskBitmap(bitmap_file) 49 | tile_grid = displayio.TileGrid(bitmap,pixel_shader=getattr(bitmap, 'pixel_shader', displayio.ColorConverter())) 50 | 51 | group = displayio.Group() 52 | group.append(tile_grid) 53 | display.show(group) 54 | 55 | time.sleep(6)# Show the image for 2 seconds 56 | 57 | -------------------------------------------------------------------------------- /Display Images/Display_Images_From_PC/lib/adafruit_st7789.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/Display Images/Display_Images_From_PC/lib/adafruit_st7789.mpy -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/adafruit_display_text/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_imageload` 7 | ==================================================== 8 | 9 | Load pixel values (indices or colors) into a bitmap and colors into a palette. 10 | 11 | * Author(s): Scott Shawcroft 12 | 13 | """ 14 | # pylint: disable=import-outside-toplevel 15 | 16 | __version__ = "0.0.0-auto.0" 17 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 18 | 19 | 20 | def load(file_or_filename, *, bitmap=None, palette=None): 21 | """Load pixel values (indices or colors) into a bitmap and colors into a palette. 22 | 23 | bitmap is the desired type. It must take width, height and color_depth in the constructor. It 24 | must also have a _load_row method to load a row's worth of pixel data. 25 | 26 | palette is the desired pallete type. The constructor should take the number of colors and 27 | support assignment to indices via []. 28 | """ 29 | if not bitmap or not palette: 30 | try: 31 | # use displayio if available 32 | import displayio 33 | 34 | if not bitmap: 35 | bitmap = displayio.Bitmap 36 | if not palette: 37 | palette = displayio.Palette 38 | except ModuleNotFoundError: 39 | # meh, we tried 40 | pass 41 | 42 | if isinstance(file_or_filename, str): 43 | open_file = open(file_or_filename, "rb") 44 | else: 45 | open_file = file_or_filename 46 | 47 | with open_file as file: 48 | header = file.read(3) 49 | file.seek(0) 50 | if header.startswith(b"BM"): 51 | from . import bmp 52 | 53 | return bmp.load(file, bitmap=bitmap, palette=palette) 54 | if header.startswith(b"P"): 55 | from . import pnm 56 | 57 | return pnm.load(file, header, bitmap=bitmap, palette=palette) 58 | if header.startswith(b"GIF"): 59 | from . import gif 60 | 61 | return gif.load(file, bitmap=bitmap, palette=palette) 62 | raise RuntimeError("Unsupported image format") 63 | -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/adafruit_display_text/bmp/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_imageload.bmp` 7 | ==================================================== 8 | 9 | Load pixel values (indices or colors) into a bitmap and colors into a palette from a BMP file. 10 | 11 | * Author(s): Scott Shawcroft 12 | 13 | """ 14 | # pylint: disable=import-outside-toplevel 15 | 16 | __version__ = "0.0.0-auto.0" 17 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 18 | 19 | 20 | def load(file, *, bitmap=None, palette=None): 21 | """Loads a bmp image from the open ``file``. 22 | 23 | Returns tuple of bitmap object and palette object. 24 | 25 | :param object bitmap: Type to store bitmap data. Must have API similar to `displayio.Bitmap`. 26 | Will be skipped if None 27 | :param object palette: Type to store the palette. Must have API similar to 28 | `displayio.Palette`. Will be skipped if None""" 29 | file.seek(10) 30 | data_start = int.from_bytes(file.read(4), "little") 31 | # f.seek(14) 32 | # bmp_header_length = int.from_bytes(file.read(4), 'little') 33 | # print(bmp_header_length) 34 | file.seek(0x12) # Width of the bitmap in pixels 35 | _width = int.from_bytes(file.read(4), "little") 36 | try: 37 | _height = int.from_bytes(file.read(4), "little") 38 | except OverflowError as error: 39 | raise NotImplementedError( 40 | "Negative height BMP files are not supported on builds without longint" 41 | ) from error 42 | file.seek(0x1C) # Number of bits per pixel 43 | color_depth = int.from_bytes(file.read(2), "little") 44 | file.seek(0x1E) # Compression type 45 | compression = int.from_bytes(file.read(2), "little") 46 | file.seek(0x2E) # Number of colors in the color palette 47 | colors = int.from_bytes(file.read(4), "little") 48 | 49 | if colors == 0 and color_depth >= 16: 50 | raise NotImplementedError("True color BMP unsupported") 51 | 52 | if compression > 2: 53 | raise NotImplementedError("bitmask compression unsupported") 54 | 55 | if colors == 0: 56 | colors = 2 ** color_depth 57 | from . import indexed 58 | 59 | return indexed.load( 60 | file, 61 | _width, 62 | _height, 63 | data_start, 64 | colors, 65 | color_depth, 66 | compression, 67 | bitmap=bitmap, 68 | palette=palette, 69 | ) 70 | -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/adafruit_display_text/bmp/indexed.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_imageload.bmp.indexed` 7 | ==================================================== 8 | 9 | Load pixel values (indices or colors) into a bitmap and colors into a palette from an indexed BMP. 10 | 11 | * Author(s): Scott Shawcroft 12 | 13 | """ 14 | 15 | __version__ = "0.0.0-auto.0" 16 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 17 | 18 | import sys 19 | 20 | try: 21 | from bitmaptools import readinto as _bitmap_readinto 22 | except ImportError: 23 | _bitmap_readinto = None # pylint: disable=invalid-name 24 | 25 | 26 | def load( 27 | file, 28 | width, 29 | height, 30 | data_start, 31 | colors, 32 | color_depth, 33 | compression, 34 | *, 35 | bitmap=None, 36 | palette=None 37 | ): 38 | """Loads indexed bitmap data into bitmap and palette objects. 39 | 40 | :param file file: The open bmp file 41 | :param int width: Image width in pixels 42 | :param int height: Image height in pixels 43 | :param int data_start: Byte location where the data starts (after headers) 44 | :param int colors: Number of distinct colors in the image 45 | :param int color_depth: Number of bits used to store a value 46 | :param int compression: 0 - none, 1 - 8bit RLE, 2 - 4bit RLE""" 47 | # pylint: disable=too-many-arguments,too-many-locals,too-many-branches 48 | if palette: 49 | palette = palette(colors) 50 | 51 | file.seek(data_start - colors * 4) 52 | for value in range(colors): 53 | c_bytes = file.read(4) 54 | # Need to swap red & blue bytes (bytes 0 and 2) 55 | palette[value] = bytes( 56 | b"".join([c_bytes[2:3], c_bytes[1:2], c_bytes[0:1], c_bytes[3:1]]) 57 | ) 58 | 59 | if bitmap: 60 | minimum_color_depth = 1 61 | while colors > 2 ** minimum_color_depth: 62 | minimum_color_depth *= 2 63 | 64 | if sys.maxsize > 1073741823: 65 | # pylint: disable=import-outside-toplevel, relative-beyond-top-level 66 | from .negative_height_check import negative_height_check 67 | 68 | # convert unsigned int to signed int when height is negative 69 | height = negative_height_check(height) 70 | bitmap = bitmap(width, abs(height), colors) 71 | file.seek(data_start) 72 | line_size = width // (8 // color_depth) 73 | if width % (8 // color_depth) != 0: 74 | line_size += 1 75 | if line_size % 4 != 0: 76 | line_size += 4 - line_size % 4 77 | 78 | mask = (1 << minimum_color_depth) - 1 79 | if height > 0: 80 | range1 = height - 1 81 | range2 = -1 82 | range3 = -1 83 | else: 84 | range1 = 0 85 | range2 = abs(height) 86 | range3 = 1 87 | 88 | if compression == 0: 89 | 90 | if _bitmap_readinto: 91 | _bitmap_readinto( 92 | bitmap, 93 | file, 94 | bits_per_pixel=color_depth, 95 | element_size=4, 96 | reverse_pixels_in_element=True, 97 | reverse_rows=True, 98 | ) 99 | 100 | else: # use the standard file.readinto 101 | chunk = bytearray(line_size) 102 | for y in range(range1, range2, range3): 103 | file.readinto(chunk) 104 | pixels_per_byte = 8 // color_depth 105 | offset = y * width 106 | 107 | for x in range(width): 108 | i = x // pixels_per_byte 109 | pixel = ( 110 | chunk[i] >> (8 - color_depth * (x % pixels_per_byte + 1)) 111 | ) & mask 112 | bitmap[offset + x] = pixel 113 | elif compression in (1, 2): 114 | decode_rle( 115 | bitmap=bitmap, 116 | file=file, 117 | compression=compression, 118 | y_range=(range1, range2, range3), 119 | width=width, 120 | ) 121 | 122 | return bitmap, palette 123 | 124 | 125 | def decode_rle(bitmap, file, compression, y_range, width): 126 | """Helper to decode RLE images""" 127 | # pylint: disable=too-many-locals,too-many-nested-blocks,too-many-branches 128 | 129 | # RLE algorithm, either 8-bit (1) or 4-bit (2) 130 | # 131 | # Ref: http://www.fileformat.info/format/bmp/egff.htm 132 | 133 | is_4bit = compression == 2 134 | 135 | # This will store the 2-byte run commands, which are either an 136 | # amount to repeat and a value to repeat, or a 0x00 and command 137 | # marker. 138 | run_buf = bytearray(2) 139 | 140 | # We need to be prepared to load up to 256 pixels of literal image 141 | # data. (0xFF is max literal length, but odd literal runs are padded 142 | # up to an even byte count, so we need space for 256 in the case of 143 | # 8-bit.) 4-bit images can get away with half that. 144 | literal_buf = bytearray(128 if is_4bit else 256) 145 | 146 | # We iterate with numbers rather than a range because the "delta" 147 | # command can cause us to jump forward arbitrarily in the output 148 | # image. 149 | # 150 | # In theory RLE images are only stored in bottom-up scan line order, 151 | # but we support either. 152 | (range1, range2, range3) = y_range 153 | y = range1 154 | x = 0 155 | 156 | while y * range3 < range2 * range3: 157 | offset = y * width + x 158 | 159 | # We keep track of how much space is left in our row so that we 160 | # can avoid writing extra data outside of the Bitmap. While the 161 | # reference above seems to say that the "end run" command is 162 | # optional and that image data should wrap from one scan line to 163 | # the next, in practice (looking at the output of ImageMagick 164 | # and GIMP, and what Preview renders) the bitmap part of the 165 | # image can contain data that goes beyond the image’s stated 166 | # width that should just be ignored. For example, the 8bit RLE 167 | # file is 15px wide but has data for 16px. 168 | width_remaining = width - x 169 | 170 | file.readinto(run_buf) 171 | 172 | if run_buf[0] == 0: 173 | # A repeat length of "0" is a special command. The next byte 174 | # tells us what needs to happen. 175 | if run_buf[1] == 0: 176 | # end of the current scan line 177 | y = y + range3 178 | x = 0 179 | elif run_buf[1] == 1: 180 | # end of image 181 | break 182 | elif run_buf[1] == 2: 183 | # delta command jumps us ahead in the bitmap output by 184 | # the x, y amounts stored in the next 2 bytes. 185 | file.readinto(run_buf) 186 | 187 | x = x + run_buf[0] 188 | y = y + run_buf[1] * range3 189 | else: 190 | # command values of 3 or more indicate that many pixels 191 | # of literal (uncompressed) image data. For 8-bit mode, 192 | # this is raw bytes, but 4-bit mode counts in nibbles. 193 | literal_length_px = run_buf[1] 194 | 195 | # Inverting the value here to get round-up integer division 196 | if is_4bit: 197 | read_length_bytes = -(-literal_length_px // 2) 198 | else: 199 | read_length_bytes = literal_length_px 200 | 201 | # If the run has an odd length then there’s a 1-byte padding 202 | # we need to consume but not write into the output 203 | if read_length_bytes % 2 == 1: 204 | read_length_bytes += 1 205 | 206 | # We use memoryview to artificially limit the length of 207 | # literal_buf so that readinto only reads the amount 208 | # that we want. 209 | literal_buf_mem = memoryview(literal_buf) 210 | file.readinto(literal_buf_mem[0:read_length_bytes]) 211 | 212 | if is_4bit: 213 | for i in range(0, min(literal_length_px, width_remaining)): 214 | # Expanding the two nibbles of the 4-bit data 215 | # into two bytes for our output bitmap. 216 | if i % 2 == 0: 217 | bitmap[offset + i] = literal_buf[i // 2] >> 4 218 | else: 219 | bitmap[offset + i] = literal_buf[i // 2] & 0x0F 220 | else: 221 | # 8-bit values are just a raw copy (limited by 222 | # what’s left in the row so we don’t overflow out of 223 | # the buffer) 224 | for i in range(0, min(literal_length_px, width_remaining)): 225 | bitmap[offset + i] = literal_buf[i] 226 | 227 | x = x + literal_length_px 228 | else: 229 | # first byte was not 0, which means it tells us how much to 230 | # repeat the next byte into the output 231 | run_length_px = run_buf[0] 232 | 233 | if is_4bit: 234 | # In 4 bit mode, we repeat the *two* values that are 235 | # packed into the next byte. The repeat amount is based 236 | # on pixels, not bytes, though, so if we were to repeat 237 | # 0xab 3 times, the output pixel values would be: 0x0a 238 | # 0x0b 0x0a (notice how it ends at 0x0a) rather than 239 | # 0x0a 0x0b 0x0a 0x0b 0x0a 0x0b 240 | run_values = [run_buf[1] >> 4, run_buf[1] & 0x0F] 241 | for i in range(0, min(run_length_px, width_remaining)): 242 | bitmap[offset + i] = run_values[i % 2] 243 | else: 244 | run_value = run_buf[1] 245 | for i in range(0, min(run_length_px, width_remaining)): 246 | bitmap[offset + i] = run_value 247 | 248 | x = x + run_length_px 249 | -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/adafruit_display_text/bmp/negative_height_check.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | Check for negative height on the BMP. 7 | Seperated into it's own file to support builds 8 | without longint. 9 | """ 10 | 11 | 12 | def negative_height_check(height): 13 | """Check the height return modified if negative.""" 14 | if height > 0x7FFFFFFF: 15 | return height - 4294967296 16 | return height 17 | -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/adafruit_display_text/gif.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 Radomir Dopieralski for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_imageload.gif` 7 | ==================================================== 8 | 9 | Load pixel values (indices or colors) into a bitmap and colors into a palette 10 | from a GIF file. 11 | 12 | * Author(s): Radomir Dopieralski 13 | 14 | """ 15 | 16 | import struct 17 | 18 | 19 | __version__ = "0.0.0-auto.0" 20 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 21 | 22 | 23 | def load(file, *, bitmap=None, palette=None): 24 | """Loads a GIF image from the open ``file``. 25 | 26 | Returns tuple of bitmap object and palette object. 27 | 28 | :param object bitmap: Type to store bitmap data. Must have API similar to `displayio.Bitmap`. 29 | Will be skipped if None 30 | :param object palette: Type to store the palette. Must have API similar to 31 | `displayio.Palette`. Will be skipped if None""" 32 | header = file.read(6) 33 | if header not in {b"GIF87a", b"GIF89a"}: 34 | raise ValueError("Not a GIF file") 35 | width, height, flags, _, _ = struct.unpack("> 4) + 1 44 | bitmap_obj = bitmap(width, height, (1 << color_bits) - 1) 45 | while True: 46 | block_type = file.read(1)[0] 47 | if block_type == 0x2C: # frame 48 | _read_frame(file, bitmap_obj) 49 | elif block_type == 0x21: # extension 50 | _ = file.read(1)[0] 51 | # 0x01 = label, 0xfe = comment 52 | _ = bytes(_read_blockstream(file)) 53 | elif block_type == 0x3B: # terminator 54 | break 55 | else: 56 | raise ValueError("Bad block type") 57 | return bitmap_obj, palette_obj 58 | 59 | 60 | def _read_frame(file, bitmap): 61 | """Read a signle frame and apply it to the bitmap.""" 62 | ddx, ddy, width, _, flags = struct.unpack("= width: 77 | x = 0 78 | y += 1 79 | 80 | 81 | def _read_blockstream(file): 82 | """Read a block from a file.""" 83 | while True: 84 | size = file.read(1)[0] 85 | if size == 0: 86 | break 87 | for _ in range(size): 88 | yield file.read(1)[0] 89 | 90 | 91 | class EndOfData(Exception): 92 | """Signified end of compressed data.""" 93 | 94 | 95 | class LZWDict: 96 | """A dictionary of LZW codes.""" 97 | 98 | def __init__(self, code_size): 99 | self.code_size = code_size 100 | self.clear_code = 1 << code_size 101 | self.end_code = self.clear_code + 1 102 | self.codes = [] 103 | self.last = None 104 | self.clear() 105 | 106 | def clear(self): 107 | """Reset the dictionary to default codes.""" 108 | self.last = b"" 109 | self.code_len = self.code_size + 1 110 | self.codes[:] = [] 111 | 112 | def decode(self, code): 113 | """Decode a code.""" 114 | if code == self.clear_code: 115 | self.clear() 116 | return b"" 117 | if code == self.end_code: 118 | raise EndOfData() 119 | if code < self.clear_code: 120 | value = bytes([code]) 121 | elif code <= len(self.codes) + self.end_code: 122 | value = self.codes[code - self.end_code - 1] 123 | else: 124 | value = self.last + self.last[0:1] 125 | if self.last: 126 | self.codes.append(self.last + value[0:1]) 127 | if ( 128 | len(self.codes) + self.end_code + 1 >= 1 << self.code_len 129 | and self.code_len < 12 130 | ): 131 | self.code_len += 1 132 | self.last = value 133 | return value 134 | 135 | 136 | def lzw_decode(data, code_size): 137 | """Decode LZW-compressed data.""" 138 | dictionary = LZWDict(code_size) 139 | bit = 0 140 | try: 141 | byte = next(data) # pylint: disable=stop-iteration-return 142 | try: 143 | while True: 144 | code = 0 145 | for i in range(dictionary.code_len): 146 | code |= ((byte >> bit) & 0x01) << i 147 | bit += 1 148 | if bit >= 8: 149 | bit = 0 150 | byte = next(data) # pylint: disable=stop-iteration-return 151 | yield dictionary.decode(code) 152 | except EndOfData: 153 | while True: 154 | next(data) # pylint: disable=stop-iteration-return 155 | except StopIteration: 156 | pass 157 | -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/adafruit_display_text/pnm/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and colors into a palette. 13 | 14 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 15 | 16 | """ 17 | # pylint: disable=import-outside-toplevel 18 | 19 | __version__ = "0.0.0-auto.0" 20 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 21 | 22 | 23 | def load(file, header, *, bitmap=None, palette=None): 24 | """ 25 | Scan for netpbm format info, skip over comments, and and delegate to a submodule 26 | to do the actual data loading. 27 | Formats P1, P4 have two space padded pieces of information: width and height. 28 | All other formats have three: width, height, and max color value. 29 | This load function will move the file stream pointer to the start of data in all cases. 30 | """ 31 | # pylint: disable=too-many-branches 32 | magic_number = header[:2] 33 | file.seek(2) 34 | pnm_header = [] 35 | next_value = bytearray() 36 | while True: 37 | # We have all we need at length 3 for formats P2, P3, P5, P6 38 | if len(pnm_header) == 3: 39 | if magic_number in [b"P2", b"P5"]: 40 | from . import pgm 41 | 42 | return pgm.load( 43 | file, magic_number, pnm_header, bitmap=bitmap, palette=palette 44 | ) 45 | 46 | if magic_number == b"P3": 47 | from . import ppm_ascii 48 | 49 | return ppm_ascii.load( 50 | file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette 51 | ) 52 | 53 | if magic_number == b"P6": 54 | from . import ppm_binary 55 | 56 | return ppm_binary.load( 57 | file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette 58 | ) 59 | 60 | if len(pnm_header) == 2 and magic_number in [b"P1", b"P4"]: 61 | bitmap = bitmap(pnm_header[0], pnm_header[1], 1) 62 | if palette: 63 | palette = palette(1) 64 | palette[0] = b"\xFF\xFF\xFF" 65 | if magic_number.startswith(b"P1"): 66 | from . import pbm_ascii 67 | 68 | return pbm_ascii.load( 69 | file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette 70 | ) 71 | 72 | from . import pbm_binary 73 | 74 | return pbm_binary.load( 75 | file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette 76 | ) 77 | 78 | next_byte = file.read(1) 79 | if next_byte == b"": 80 | raise RuntimeError("Unsupported image format {}".format(magic_number)) 81 | if next_byte == b"#": # comment found, seek until a newline or EOF is found 82 | while file.read(1) not in [b"", b"\n"]: # EOF or NL 83 | pass 84 | elif not next_byte.isdigit(): # boundary found in header data 85 | if next_value: 86 | # pull values until space is found 87 | pnm_header.append(int("".join(["%c" % char for char in next_value]))) 88 | next_value = bytearray() # reset the byte array 89 | else: 90 | next_value += next_byte # push the digit into the byte array 91 | -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/adafruit_display_text/pnm/pbm_ascii.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm.pbm_ascii` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and for an ascii ppm, 13 | return None for pallet. 14 | 15 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 16 | 17 | """ 18 | 19 | __version__ = "0.0.0-auto.0" 20 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 21 | 22 | 23 | def load(file, width, height, bitmap=None, palette=None): 24 | """ 25 | Load a P1 'PBM' ascii image into the displayio.Bitmap 26 | """ 27 | next_byte = True 28 | for y in range(height): 29 | x = 0 30 | while next_byte: 31 | next_byte = file.read(1) 32 | if not next_byte.isdigit(): 33 | continue 34 | bitmap[x, y] = 1 if next_byte == b"1" else 0 35 | if x == width - 1: 36 | break 37 | x += 1 38 | return bitmap, palette 39 | -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/adafruit_display_text/pnm/pbm_binary.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm.pbm_binary` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and for an ascii ppm, 13 | return None for pallet. 14 | 15 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 16 | 17 | """ 18 | 19 | __version__ = "0.0.0-auto.0" 20 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 21 | 22 | 23 | def load(file, width, height, bitmap=None, palette=None): 24 | """ 25 | Load a P4 'PBM' binary image into the displayio.Bitmap 26 | """ 27 | x = 0 28 | y = 0 29 | while True: 30 | next_byte = file.read(1) 31 | if not next_byte: 32 | break # out of bits 33 | for bit in iterbits(next_byte): 34 | bitmap[x, y] = bit 35 | x += 1 36 | if x > width - 1: 37 | y += 1 38 | x = 0 39 | if y > height - 1: 40 | break 41 | return bitmap, palette 42 | 43 | 44 | def iterbits(b): 45 | """ 46 | generator to iterate over the bits in a byte (character) 47 | """ 48 | in_char = reverse(int.from_bytes(b, "little")) 49 | for i in range(8): 50 | yield (in_char >> i) & 1 51 | 52 | 53 | def reverse(b): 54 | """ 55 | reverse bit order so the iterbits works 56 | """ 57 | b = (b & 0xF0) >> 4 | (b & 0x0F) << 4 58 | b = (b & 0xCC) >> 2 | (b & 0x33) << 2 59 | b = (b & 0xAA) >> 1 | (b & 0x55) << 1 60 | return b 61 | -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/adafruit_display_text/pnm/pgm/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm.pgm` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and colors into a palette. 13 | 14 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 15 | 16 | """ 17 | # pylint: disable=import-outside-toplevel 18 | 19 | 20 | def load(file, magic_number, header, *, bitmap=None, palette=None): 21 | """ 22 | Perform the load of Netpbm greyscale images (P2, P5) 23 | """ 24 | if header[2] > 256: 25 | raise NotImplementedError("16 bit files are not supported") 26 | width = header[0] 27 | height = header[1] 28 | 29 | if magic_number == b"P2": # To handle ascii PGM files. 30 | from . import ascii as pgm_ascii 31 | 32 | return pgm_ascii.load(file, width, height, bitmap=bitmap, palette=palette) 33 | 34 | if magic_number == b"P5": # To handle binary PGM files. 35 | from . import binary 36 | 37 | return binary.load(file, width, height, bitmap=bitmap, palette=palette) 38 | 39 | raise NotImplementedError("Was not able to send image") 40 | -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/adafruit_display_text/pnm/pgm/ascii.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm.pgm.ascii` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and colors into a palette. 13 | 14 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 15 | 16 | """ 17 | 18 | 19 | def load(file, width, height, bitmap=None, palette=None): 20 | """ 21 | Load a PGM ascii file (P2) 22 | """ 23 | data_start = file.tell() # keep this so we can rewind 24 | _palette_colors = set() 25 | pixel = bytearray() 26 | # build a set of all colors present in the file, so palette and bitmap can be constructed 27 | while True: 28 | byte = file.read(1) 29 | if byte == b"": 30 | break 31 | if not byte.isdigit(): 32 | int_pixel = int("".join(["%c" % char for char in pixel])) 33 | _palette_colors.add(int_pixel) 34 | pixel = bytearray() 35 | pixel += byte 36 | if palette: 37 | palette = build_palette(palette, _palette_colors) 38 | if bitmap: 39 | bitmap = bitmap(width, height, len(_palette_colors)) 40 | _palette_colors = list(_palette_colors) 41 | file.seek(data_start) 42 | for y in range(height): 43 | for x in range(width): 44 | pixel = bytearray() 45 | while True: 46 | byte = file.read(1) 47 | if not byte.isdigit(): 48 | break 49 | pixel += byte 50 | int_pixel = int("".join(["%c" % char for char in pixel])) 51 | bitmap[x, y] = _palette_colors.index(int_pixel) 52 | return bitmap, palette 53 | 54 | 55 | def build_palette(palette_class, palette_colors): # pylint: disable=duplicate-code 56 | """ 57 | construct the Palette, and populate it with the set of palette_colors 58 | """ 59 | palette = palette_class(len(palette_colors)) 60 | for counter, color in enumerate(palette_colors): 61 | palette[counter] = bytes([color, color, color]) 62 | return palette 63 | -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/adafruit_display_text/pnm/pgm/binary.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm.pgm.binary` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and colors into a palette. 13 | 14 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 15 | 16 | """ 17 | 18 | 19 | def load(file, width, height, bitmap=None, palette=None): 20 | """ 21 | Load a P5 format file (binary), handle PGM (greyscale) 22 | """ 23 | palette_colors = set() 24 | data_start = file.tell() 25 | for y in range(height): 26 | data_line = iter(bytes(file.read(width))) 27 | for pixel in data_line: 28 | palette_colors.add(pixel) 29 | 30 | if palette: 31 | palette = build_palette(palette, palette_colors) 32 | if bitmap: 33 | bitmap = bitmap(width, height, len(palette_colors)) 34 | palette_colors = list(palette_colors) 35 | file.seek(data_start) 36 | for y in range(height): 37 | data_line = iter(bytes(file.read(width))) 38 | for x, pixel in enumerate(data_line): 39 | bitmap[x, y] = palette_colors.index(pixel) 40 | return bitmap, palette 41 | 42 | 43 | def build_palette(palette_class, palette_colors): 44 | """ 45 | construct the Palette, and populate it with the set of palette_colors 46 | """ 47 | _palette = palette_class(len(palette_colors)) 48 | for counter, color in enumerate(palette_colors): 49 | _palette[counter] = bytes([color, color, color]) 50 | return _palette 51 | -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/adafruit_display_text/pnm/ppm_ascii.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm.ppm_ascii` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and for an ascii ppm, 13 | return None for pallet. 14 | 15 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 16 | 17 | """ 18 | 19 | __version__ = "0.0.0-auto.0" 20 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 21 | 22 | 23 | def load(file, width, height, bitmap=None, palette=None): 24 | """ 25 | :param stream file: infile with the position set at start of data 26 | :param int width: 27 | :param int height: 28 | :param int max_colors: color space of file 29 | :param bitmap: displayio.Bitmap class 30 | :param palette: displayio.Palette class 31 | :return tuple: 32 | """ 33 | palette_colors = set() 34 | data_start = file.tell() 35 | for triplet in read_three_colors(file): 36 | palette_colors.add(triplet) 37 | 38 | if palette: 39 | palette = palette(len(palette_colors)) 40 | for counter, color in enumerate(palette_colors): 41 | palette[counter] = color 42 | if bitmap: 43 | file.seek(data_start) 44 | bitmap = bitmap(width, height, len(palette_colors)) 45 | palette_colors = list(palette_colors) 46 | for y in range(height): 47 | for x in range(width): 48 | for color in read_three_colors(file): 49 | bitmap[x, y] = palette_colors.index(color) 50 | break # exit the inner generator 51 | return bitmap, palette 52 | 53 | 54 | def read_three_colors(file): 55 | """ 56 | Generator to read integer values from file, in groups of three. 57 | Each value can be len 1-3, for values 0 - 255, space padded. 58 | :return tuple[int]: 59 | """ 60 | triplet = [] 61 | color = bytearray() 62 | while True: 63 | this_byte = file.read(1) 64 | if this_byte.isdigit(): 65 | color += this_byte 66 | # not a digit means we completed one number (found a space separator or EOF) 67 | elif color or (triplet and this_byte == b""): 68 | triplet.append(int("".join(["%c" % char for char in color]))) 69 | color = bytearray() 70 | if len(triplet) == 3: # completed one pixel 71 | yield bytes(tuple(triplet)) 72 | triplet = [] 73 | # short circuit must be after all other cases, so we yield the last pixel before returning 74 | if this_byte == b"": 75 | return 76 | -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/adafruit_display_text/pnm/ppm_binary.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries 2 | # SPDX-FileCopyrightText: Matt Land 3 | # SPDX-FileCopyrightText: Brooke Storm 4 | # SPDX-FileCopyrightText: Sam McGahan 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | """ 9 | `adafruit_imageload.pnm.ppm_binary` 10 | ==================================================== 11 | 12 | Load pixel values (indices or colors) into a bitmap and for a binary ppm, 13 | return None for pallet. 14 | 15 | * Author(s): Matt Land, Brooke Storm, Sam McGahan 16 | 17 | """ 18 | 19 | __version__ = "0.0.0-auto.0" 20 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" 21 | 22 | 23 | def load(file, width, height, bitmap=None, palette=None): 24 | """Load pixel values (indices or colors) into a bitmap and for a binary 25 | ppm, return None for pallet.""" 26 | 27 | data_start = file.tell() 28 | palette_colors = set() 29 | line_size = width * 3 30 | 31 | for y in range(height): 32 | data_line = iter(bytes(file.read(line_size))) 33 | for red in data_line: 34 | # red, green, blue 35 | palette_colors.add((red, next(data_line), next(data_line))) 36 | 37 | if palette: 38 | palette = palette(len(palette_colors)) 39 | for counter, color in enumerate(palette_colors): 40 | palette[counter] = bytes(color) 41 | if bitmap: 42 | bitmap = bitmap(width, height, len(palette_colors)) 43 | file.seek(data_start) 44 | palette_colors = list(palette_colors) 45 | for y in range(height): 46 | x = 0 47 | data_line = iter(bytes(file.read(line_size))) 48 | for red in data_line: 49 | # red, green, blue 50 | bitmap[x, y] = palette_colors.index( 51 | (red, next(data_line), next(data_line)) 52 | ) 53 | x += 1 54 | 55 | return bitmap, palette 56 | -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/display_image_sdcard_circuitpython.py: -------------------------------------------------------------------------------- 1 | import time 2 | import board 3 | import math 4 | import busio 5 | import terminalio 6 | import displayio 7 | import adafruit_imageload 8 | from adafruit_st7789 import ST7789 9 | import os 10 | import adafruit_sdcard 11 | import digitalio 12 | import storage 13 | 14 | spi = busio.SPI(board.GP18, board.GP19, board.GP16) 15 | cs = digitalio.DigitalInOut(board.GP17) 16 | sdcard = adafruit_sdcard.SDCard(spi, cs) 17 | vfs = storage.VfsFat(sdcard) 18 | storage.mount(vfs, "/sd") 19 | 20 | # Release any resources currently in use for the displays 21 | displayio.release_displays() 22 | 23 | tft_clk = board.GP10 # must be a SPI CLK 24 | tft_mosi= board.GP11 # must be a SPI TX 25 | tft_rst = board.GP12 26 | tft_dc = board.GP8 27 | tft_cs = board.GP9 28 | tft_bl = board.GP13 29 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 30 | 31 | 32 | # Make the displayio SPI bus and the GC9A01 display 33 | display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=tft_rst) 34 | display = ST7789(display_bus, rotation=270, width=240, height=135, rowstart=40, colstart=53) 35 | 36 | # Make the main display context 37 | main = displayio.Group() 38 | display.show(main) 39 | 40 | bmpfiles = sorted("/sd/" + fn for fn in os.listdir("/sd") if fn.lower().endswith("bmp")) 41 | 42 | while True: 43 | if len(bmpfiles) == 0: 44 | print("N0, BMP Files") 45 | break 46 | 47 | for filename in bmpfiles: 48 | print("showing bmp image", filename) 49 | 50 | bitmap_file = open(filename, "rb") 51 | bitmap = displayio.OnDiskBitmap(bitmap_file) 52 | tile_grid = displayio.TileGrid(bitmap,pixel_shader=getattr(bitmap, 'pixel_shader', displayio.ColorConverter())) 53 | 54 | group = displayio.Group() 55 | group.append(tile_grid) 56 | display.show(group) 57 | 58 | time.sleep(2)# Show the image for 2 seconds 59 | 60 | -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/images/img1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/Display Images/Display_Images_from_SDCard/images/img1.bmp -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/images/img2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/Display Images/Display_Images_from_SDCard/images/img2.bmp -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/images/img3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/Display Images/Display_Images_from_SDCard/images/img3.bmp -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/images/img4.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/Display Images/Display_Images_from_SDCard/images/img4.bmp -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/images/img5.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/Display Images/Display_Images_from_SDCard/images/img5.bmp -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/images/img6.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/Display Images/Display_Images_from_SDCard/images/img6.bmp -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/lib/adafruit_sdcard.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2014-2016 Damien George 2 | # SPDX-FileCopyrightText: 2014-2016 Peter Hinch 3 | # SPDX-FileCopyrightText: 2014-2016 Radomir Dopieralski 4 | # SPDX-FileCopyrightText: 2017 Scott Shawcroft for Adafruit Industries 5 | # 6 | # SPDX-License-Identifier: MIT 7 | 8 | import time 9 | from micropython import const 10 | from adafruit_bus_device import spi_device 11 | 12 | try: 13 | from typing import Union, Optional 14 | from busio import SPI 15 | from digitalio import DigitalInOut 16 | from circuitpython_typing import ReadableBuffer, WriteableBuffer 17 | except ImportError: 18 | pass 19 | 20 | __version__ = "0.0.0-auto.0" 21 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_SD.git" 22 | 23 | _CMD_TIMEOUT = const(200) 24 | 25 | _R1_IDLE_STATE = const(1 << 0) 26 | # R1_ERASE_RESET = const(1 << 1) 27 | _R1_ILLEGAL_COMMAND = const(1 << 2) 28 | # R1_COM_CRC_ERROR = const(1 << 3) 29 | # R1_ERASE_SEQUENCE_ERROR = const(1 << 4) 30 | # R1_ADDRESS_ERROR = const(1 << 5) 31 | # R1_PARAMETER_ERROR = const(1 << 6) 32 | _TOKEN_CMD25 = const(0xFC) 33 | _TOKEN_STOP_TRAN = const(0xFD) 34 | _TOKEN_DATA = const(0xFE) 35 | 36 | # pylint: disable-msg=superfluous-parens 37 | class SDCard: 38 | """Controls an SD card over SPI. 39 | :param ~busio.SPI spi: The SPI bus 40 | :param ~digitalio.DigitalInOut cs: The chip select connected to the card 41 | :param int baudrate: The SPI data rate to use after card setup 42 | Example usage: 43 | .. code-block:: python 44 | import busio 45 | import storage 46 | import adafruit_sdcard 47 | import os 48 | import board 49 | spi = busio.SPI(SCK, MOSI, MISO) 50 | sd = adafruit_sdcard.SDCard(spi, board.SD_CS) 51 | vfs = storage.VfsFat(sdcard) 52 | storage.mount(vfs, '/sd') 53 | os.listdir('/') 54 | """ 55 | 56 | def __init__(self, spi: SPI, cs: DigitalInOut, baudrate: int = 1320000) -> None: 57 | # Create an SPIDevice running at a lower initialization baudrate first. 58 | self._spi = spi_device.SPIDevice(spi, cs, baudrate=250000, extra_clocks=8) 59 | 60 | self._cmdbuf = bytearray(6) 61 | self._single_byte = bytearray(1) 62 | 63 | # Card is byte addressing, set to 1 if addresses are per block 64 | self._cdv = 512 65 | 66 | # initialise the card 67 | self._init_card(cs) 68 | 69 | # Create a new SPIDevice with the (probably) higher operating baudrate. 70 | self._spi = spi_device.SPIDevice(spi, cs, baudrate=baudrate, extra_clocks=8) 71 | 72 | def _init_card(self, chip_select: DigitalInOut) -> None: 73 | """Initialize the card in SPI mode.""" 74 | # clock card at least 80 cycles with cs high 75 | with self._spi as card: 76 | # Force CS high. 77 | chip_select.value = True 78 | self._single_byte[0] = 0xFF 79 | for _ in range(80 // 8 + 1): 80 | card.write(self._single_byte) 81 | 82 | with self._spi as card: 83 | # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts) 84 | for _ in range(5): 85 | if self._cmd(card, 0, 0, 0x95) == _R1_IDLE_STATE: 86 | break 87 | else: 88 | raise OSError("no SD card") 89 | 90 | # CMD8: determine card version 91 | rb7 = bytearray(4) 92 | r = self._cmd(card, 8, 0x01AA, 0x87, rb7, data_block=False) 93 | if r == _R1_IDLE_STATE: 94 | self._init_card_v2(card) 95 | elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND): 96 | self._init_card_v1(card) 97 | else: 98 | raise OSError("couldn't determine SD card version") 99 | 100 | # get the number of sectors 101 | # CMD9: response R2 (R1 byte + 16-byte block read) 102 | csd = bytearray(16) 103 | if self._cmd(card, 9, 0, 0xAF, response_buf=csd) != 0: 104 | raise OSError("no response from SD card") 105 | # self.readinto(csd) 106 | csd_version = (csd[0] & 0xC0) >> 6 107 | if csd_version >= 2: 108 | raise OSError("SD card CSD format not supported") 109 | 110 | if csd_version == 1: 111 | self._sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024 112 | else: 113 | block_length = 2 ** (csd[5] & 0xF) 114 | c_size = ((csd[6] & 0x3) << 10) | (csd[7] << 2) | ((csd[8] & 0xC) >> 6) 115 | mult = 2 ** (((csd[9] & 0x3) << 1 | (csd[10] & 0x80) >> 7) + 2) 116 | self._sectors = block_length // 512 * mult * (c_size + 1) 117 | 118 | # CMD16: set block length to 512 bytes 119 | if self._cmd(card, 16, 512, 0x15) != 0: 120 | raise OSError("can't set 512 block size") 121 | 122 | def _init_card_v1(self, card: SPI) -> None: 123 | """Initialize v1 SDCards which use byte addressing.""" 124 | for _ in range(_CMD_TIMEOUT): 125 | self._cmd(card, 55, 0, 0) 126 | if self._cmd(card, 41, 0, 0) == 0: 127 | # print("[SDCard] v1 card") 128 | return 129 | raise OSError("timeout waiting for v1 card") 130 | 131 | def _init_card_v2(self, card: SPI) -> None: 132 | """Initialize v2 SDCards which use 512-byte block addressing.""" 133 | ocr = bytearray(4) 134 | for _ in range(_CMD_TIMEOUT): 135 | time.sleep(0.050) 136 | self._cmd(card, 58, 0, 0xFD, response_buf=ocr, data_block=False) 137 | self._cmd(card, 55, 0, 0x65) 138 | # On non-longint builds, we cannot use 0x40000000 directly as the arg 139 | # so break it into bytes, which are interpreted by self._cmd(). 140 | if self._cmd(card, 41, b"\x40\x00\x00\x00", 0x77) == 0: 141 | self._cmd(card, 58, 0, 0xFD, response_buf=ocr, data_block=False) 142 | 143 | # Check for block addressing 144 | if (ocr[0] & 0x40) != 0: 145 | self._cdv = 1 146 | # print("[SDCard] v2 card") 147 | return 148 | raise OSError("timeout waiting for v2 card") 149 | 150 | def _wait_for_ready(self, card: SPI, timeout: float = 0.3) -> None: 151 | """ 152 | Wait for the card to clock out 0xff to indicate its ready. 153 | :param busio.SPI card: The locked SPI bus. 154 | :param float timeout: Maximum time to wait in seconds. 155 | """ 156 | start_time = time.monotonic() 157 | self._single_byte[0] = 0x00 158 | while time.monotonic() - start_time < timeout and self._single_byte[0] != 0xFF: 159 | card.readinto(self._single_byte, write_value=0xFF) 160 | 161 | # pylint: disable-msg=too-many-arguments 162 | # pylint: disable=no-member 163 | # no-member disable should be reconsidered when it can be tested 164 | def _cmd( 165 | self, 166 | card: SPI, 167 | cmd: int, 168 | arg: Union[int, ReadableBuffer] = 0, 169 | crc: int = 0, 170 | response_buf: Optional[WriteableBuffer] = None, 171 | data_block: bool = True, 172 | wait: bool = True, 173 | ) -> int: 174 | """ 175 | Issue a command to the card and read an optional data response. 176 | :param busio.SPI card: The locked SPI bus. 177 | :param int cmd: The command number. 178 | :param int|buf(4) arg: The command argument 179 | :param int crc: The crc to allow the card to verify the command and argument. 180 | :param WriteableBuffer response_buf: Buffer to read a data block response into. 181 | :param bool data_block: True if the response data is in a data block. 182 | :param bool wait: True if the command should wait until the card is ready 183 | """ 184 | # create and send the command 185 | buf = self._cmdbuf 186 | buf[0] = 0x40 | cmd 187 | if isinstance(arg, int): 188 | buf[1] = (arg >> 24) & 0xFF 189 | buf[2] = (arg >> 16) & 0xFF 190 | buf[3] = (arg >> 8) & 0xFF 191 | buf[4] = arg & 0xFF 192 | elif len(arg) == 4: 193 | # arg can be a 4-byte buf 194 | buf[1:5] = arg 195 | else: 196 | raise ValueError() 197 | 198 | if crc == 0: 199 | buf[5] = calculate_crc(buf[:-1]) 200 | else: 201 | buf[5] = crc 202 | 203 | if wait: 204 | self._wait_for_ready(card) 205 | 206 | card.write(buf) 207 | 208 | # wait for the response (response[7] == 0) 209 | for _ in range(_CMD_TIMEOUT): 210 | card.readinto(buf, end=1, write_value=0xFF) 211 | if not (buf[0] & 0x80): 212 | if response_buf: 213 | if data_block: 214 | # Wait for the start block byte 215 | buf[1] = 0xFF 216 | while buf[1] != 0xFE: 217 | card.readinto(buf, start=1, end=2, write_value=0xFF) 218 | card.readinto(response_buf, write_value=0xFF) 219 | if data_block: 220 | # Read the checksum 221 | card.readinto(buf, start=1, end=3, write_value=0xFF) 222 | return buf[0] 223 | return -1 224 | 225 | # pylint: enable-msg=too-many-arguments 226 | 227 | # pylint: disable-msg=too-many-arguments 228 | def _block_cmd( 229 | self, 230 | card: SPI, 231 | cmd: int, 232 | block: int, 233 | crc: int, 234 | response_buf: Optional[WriteableBuffer] = None, 235 | ) -> int: 236 | """ 237 | Issue a command to the card with a block argument. 238 | :param busio.SPI card: The locked SPI bus. 239 | :param int cmd: The command number. 240 | :param int block: The relevant block. 241 | :param int crc: The crc to allow the card to verify the command and argument. 242 | :param WriteableBuffer response_buf: Buffer to read a data block response into. 243 | """ 244 | if self._cdv == 1: 245 | return self._cmd(card, cmd, block, crc, response_buf=response_buf) 246 | 247 | # create and send the command 248 | buf = self._cmdbuf 249 | buf[0] = 0x40 | cmd 250 | # We address by byte because cdv is 512. Instead of multiplying, shift 251 | # the data to the correct spot so that we don't risk creating a long 252 | # int. 253 | buf[1] = (block >> 15) & 0xFF 254 | buf[2] = (block >> 7) & 0xFF 255 | buf[3] = (block << 1) & 0xFF 256 | buf[4] = 0 257 | 258 | if crc == 0: 259 | buf[5] = calculate_crc(buf[:-1]) 260 | else: 261 | buf[5] = crc 262 | 263 | result = -1 264 | self._wait_for_ready(card) 265 | 266 | card.write(buf) 267 | 268 | # wait for the response (response[7] == 0) 269 | for _ in range(_CMD_TIMEOUT): 270 | card.readinto(buf, end=1, write_value=0xFF) 271 | if not (buf[0] & 0x80): 272 | result = buf[0] 273 | break 274 | 275 | # pylint: disable=singleton-comparison 276 | # Disable should be removed when refactor can be tested. 277 | if response_buf != None and result == 0: 278 | self._readinto(card, response_buf) 279 | 280 | return result 281 | 282 | # pylint: enable-msg=too-many-arguments 283 | 284 | def _cmd_nodata(self, card: SPI, cmd: int, response: int = 0xFF) -> int: 285 | """ 286 | Issue a command to the card with no argument. 287 | :param busio.SPI card: The locked SPI bus. 288 | :param int cmd: The command number. 289 | :param int response: The expected response, default is ``0xFF`` 290 | """ 291 | buf = self._cmdbuf 292 | buf[0] = cmd 293 | buf[1] = 0xFF 294 | 295 | card.write(buf, end=2) 296 | for _ in range(_CMD_TIMEOUT): 297 | card.readinto(buf, end=1, write_value=0xFF) 298 | if buf[0] == response: 299 | return 0 # OK 300 | return 1 # timeout 301 | 302 | def _readinto( 303 | self, card: SPI, buf: WriteableBuffer, start: int = 0, end: Optional[int] = None 304 | ) -> None: 305 | """ 306 | Read a data block into buf. 307 | :param busio.SPI card: The locked SPI bus. 308 | :param WriteableBuffer buf: The buffer to write into 309 | :param int start: The first index to write data at 310 | :param int end: The index after the last byte to write to. 311 | """ 312 | if end is None: 313 | end = len(buf) 314 | 315 | # read until start byte (0xfe) 316 | buf[start] = 0xFF # busy 317 | while buf[start] != 0xFE: 318 | card.readinto(buf, start=start, end=start + 1, write_value=0xFF) 319 | 320 | card.readinto(buf, start=start, end=end, write_value=0xFF) 321 | 322 | # read checksum and throw it away 323 | card.readinto(self._cmdbuf, end=2, write_value=0xFF) 324 | 325 | # pylint: disable-msg=too-many-arguments 326 | def _write( 327 | self, 328 | card: SPI, 329 | token: int, 330 | buf: ReadableBuffer, 331 | start: int = 0, 332 | end: Optional[int] = None, 333 | ) -> int: 334 | """ 335 | Write a data block to the card. 336 | :param busio.SPI card: The locked SPI bus. 337 | :param int token: The start token 338 | :param ReadableBuffer buf: The buffer to write from 339 | :param int start: The first index to read data from 340 | :param int end: The index after the last byte to read from. 341 | """ 342 | cmd = self._cmdbuf 343 | if end is None: 344 | end = len(buf) 345 | 346 | self._wait_for_ready(card) 347 | 348 | # send: start of block, data, checksum 349 | cmd[0] = token 350 | card.write(cmd, end=1) 351 | card.write(buf, start=start, end=end) 352 | cmd[0] = 0xFF 353 | cmd[1] = 0xFF 354 | card.write(cmd, end=2) 355 | 356 | # check the response 357 | # pylint: disable=no-else-return 358 | # Disable should be removed when refactor can be tested 359 | for _ in range(_CMD_TIMEOUT): 360 | card.readinto(cmd, end=1, write_value=0xFF) 361 | if not (cmd[0] & 0x80): 362 | if (cmd[0] & 0x1F) != 0x05: 363 | return -1 364 | else: 365 | break 366 | 367 | # wait for write to finish 368 | card.readinto(cmd, end=1, write_value=0xFF) 369 | while cmd[0] == 0: 370 | card.readinto(cmd, end=1, write_value=0xFF) 371 | 372 | return 0 # worked 373 | 374 | # pylint: enable-msg=too-many-arguments 375 | 376 | def count(self) -> int: 377 | """ 378 | Returns the total number of sectors. 379 | :return: The number of 512-byte blocks 380 | :rtype: int 381 | """ 382 | return self._sectors 383 | 384 | def readblocks(self, start_block: int, buf: WriteableBuffer) -> int: 385 | """ 386 | Read one or more blocks from the card 387 | :param int start_block: The block to start reading from 388 | :param WriteableBuffer buf: The buffer to write into. Length must be multiple of 512. 389 | """ 390 | nblocks, err = divmod(len(buf), 512) 391 | assert nblocks and not err, "Buffer length is invalid" 392 | with self._spi as card: 393 | if nblocks == 1: 394 | # CMD17: set read address for single block 395 | # We use _block_cmd to read our data so that the chip select line 396 | # isn't toggled between the command, response and data. 397 | if self._block_cmd(card, 17, start_block, 0, response_buf=buf) != 0: 398 | return 1 399 | else: 400 | # CMD18: set read address for multiple blocks 401 | if self._block_cmd(card, 18, start_block, 0) != 0: 402 | return 1 403 | offset = 0 404 | while nblocks: 405 | self._readinto(card, buf, start=offset, end=(offset + 512)) 406 | offset += 512 407 | nblocks -= 1 408 | ret = self._cmd(card, 12, 0, 0x61, wait=False) 409 | # return first status 0 or last before card ready (0xff) 410 | while ret != 0: 411 | card.readinto(self._single_byte, write_value=0xFF) 412 | if self._single_byte[0] & 0x80: 413 | return ret 414 | ret = self._single_byte[0] 415 | return 0 416 | 417 | def writeblocks(self, start_block: int, buf: ReadableBuffer) -> int: 418 | """ 419 | Write one or more blocks to the card 420 | :param int start_block: The block to start writing to 421 | :param ReadableBuffer buf: The buffer to write into. Length must be multiple of 512. 422 | """ 423 | nblocks, err = divmod(len(buf), 512) 424 | assert nblocks and not err, "Buffer length is invalid" 425 | with self._spi as card: 426 | if nblocks == 1: 427 | # CMD24: set write address for single block 428 | if self._block_cmd(card, 24, start_block, 0) != 0: 429 | return 1 430 | 431 | # send the data 432 | self._write(card, _TOKEN_DATA, buf) 433 | else: 434 | # CMD25: set write address for first block 435 | if self._block_cmd(card, 25, start_block, 0) != 0: 436 | return 1 437 | # send the data 438 | offset = 0 439 | while nblocks: 440 | self._write( 441 | card, _TOKEN_CMD25, buf, start=offset, end=(offset + 512) 442 | ) 443 | offset += 512 444 | nblocks -= 1 445 | self._cmd_nodata(card, _TOKEN_STOP_TRAN, 0x0) 446 | return 0 447 | 448 | 449 | def _calculate_crc_table() -> bytearray: 450 | """Precompute the table used in calculate_crc.""" 451 | # Code converted from https://github.com/hazelnusse/crc7/blob/master/crc7.cc by devoh747 452 | # With permission from Dale Lukas Peterson 453 | # 8/6/2019 454 | 455 | crc_table = bytearray(256) 456 | crc_poly = const(0x89) # the value of our CRC-7 polynomial 457 | 458 | # generate a table value for all 256 possible byte values 459 | for i in range(256): 460 | if i & 0x80: 461 | crc_table[i] = i ^ crc_poly 462 | else: 463 | crc_table[i] = i 464 | for _ in range(1, 8): 465 | crc_table[i] = crc_table[i] << 1 466 | if crc_table[i] & 0x80: 467 | crc_table[i] = crc_table[i] ^ crc_poly 468 | return crc_table 469 | 470 | 471 | CRC_TABLE = _calculate_crc_table() 472 | 473 | 474 | def calculate_crc(message: ReadableBuffer) -> int: 475 | """ 476 | Calculate the CRC of message[0:5], using a precomputed table in CRC_TABLE. 477 | :param bytearray message: Where each index is a byte 478 | """ 479 | 480 | crc = 0 481 | # All messages in _cmd are 5 bytes including the cmd.. The 6th byte is the crc value. 482 | for i in range(0, 5): 483 | crc = CRC_TABLE[(crc << 1) ^ message[i]] 484 | 485 | return (crc << 1) | 1 486 | -------------------------------------------------------------------------------- /Display Images/Display_Images_from_SDCard/lib/adafruit_st7789.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/Display Images/Display_Images_from_SDCard/lib/adafruit_st7789.mpy -------------------------------------------------------------------------------- /EncroPi.py: -------------------------------------------------------------------------------- 1 | ''' 2 | #------------------------------------------------------------------------ 3 | # 4 | # This is a python Library code for EncroPi Board 5 | # Written by SB Components Ltd 6 | # 7 | #================================================================================== 8 | # Copyright (c) SB Components Ltd 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in all 18 | # copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | # SOFTWARE. 27 | #================================================================================== 28 | ''' 29 | 30 | from machine import Pin, UART,SPI,I2C 31 | from micropython import const 32 | import binascii 33 | 34 | import time 35 | 36 | 37 | _CMD_TIMEOUT = const(100) 38 | _R1_IDLE_STATE = const(1 << 0) 39 | _R1_ILLEGAL_COMMAND = const(1 << 2) 40 | _TOKEN_CMD25 = const(0xFC) 41 | _TOKEN_STOP_TRAN = const(0xFD) 42 | _TOKEN_DATA = const(0xFE) 43 | 44 | class SDCard: 45 | def __init__(self): 46 | spi=SPI(0,sck=Pin(18),mosi=Pin(19),miso=Pin(16)) 47 | cs = Pin(17) 48 | self.spi = spi 49 | self.cs = cs 50 | 51 | self.cmdbuf = bytearray(6) 52 | self.dummybuf = bytearray(512) 53 | self.tokenbuf = bytearray(1) 54 | for i in range(512): 55 | self.dummybuf[i] = 0xFF 56 | self.dummybuf_memoryview = memoryview(self.dummybuf) 57 | # initialise the card 58 | self.init_card() 59 | 60 | def init_spi(self, baudrate): 61 | try: 62 | master = self.spi.MASTER 63 | except AttributeError: 64 | # on ESP8266 65 | self.spi.init(baudrate=baudrate, phase=0, polarity=0) 66 | else: 67 | # on pyboard 68 | self.spi.init(master, baudrate=baudrate, phase=0, polarity=0) 69 | 70 | def init_card(self): 71 | # init CS pin 72 | self.cs.init(self.cs.OUT, value=1) 73 | 74 | # init SPI bus; use low data rate for initialisation 75 | self.init_spi(100000) 76 | 77 | # clock card at least 100 cycles with cs high 78 | for i in range(16): 79 | self.spi.write(b"\xff") 80 | 81 | # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts) 82 | for _ in range(5): 83 | if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE: 84 | break 85 | else: 86 | raise OSError("no SD card") 87 | 88 | # CMD8: determine card version 89 | r = self.cmd(8, 0x01AA, 0x87, 4) 90 | if r == _R1_IDLE_STATE: 91 | self.init_card_v2() 92 | elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND): 93 | self.init_card_v1() 94 | else: 95 | raise OSError("couldn't determine SD card version") 96 | 97 | # get the number of sectors 98 | # CMD9: response R2 (R1 byte + 16-byte block read) 99 | if self.cmd(9, 0, 0, 0, False) != 0: 100 | raise OSError("no response from SD card") 101 | csd = bytearray(16) 102 | self.readinto(csd) 103 | if csd[0] & 0xC0 == 0x40: # CSD version 2.0 104 | self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024 105 | elif csd[0] & 0xC0 == 0x00: # CSD version 1.0 (old, <=2GB) 106 | c_size = csd[6] & 0b11 | csd[7] << 2 | (csd[8] & 0b11000000) << 4 107 | c_size_mult = ((csd[9] & 0b11) << 1) | csd[10] >> 7 108 | self.sectors = (c_size + 1) * (2 ** (c_size_mult + 2)) 109 | else: 110 | raise OSError("SD card CSD format not supported") 111 | # print('sectors', self.sectors) 112 | 113 | # CMD16: set block length to 512 bytes 114 | if self.cmd(16, 512, 0) != 0: 115 | raise OSError("can't set 512 block size") 116 | 117 | # set to high data rate now that it's initialised 118 | self.init_spi(1320000) 119 | 120 | def init_card_v1(self): 121 | for i in range(_CMD_TIMEOUT): 122 | self.cmd(55, 0, 0) 123 | if self.cmd(41, 0, 0) == 0: 124 | self.cdv = 512 125 | # print("[SDCard] v1 card") 126 | return 127 | raise OSError("timeout waiting for v1 card") 128 | 129 | def init_card_v2(self): 130 | for i in range(_CMD_TIMEOUT): 131 | time.sleep_ms(50) 132 | self.cmd(58, 0, 0, 4) 133 | self.cmd(55, 0, 0) 134 | if self.cmd(41, 0x40000000, 0) == 0: 135 | self.cmd(58, 0, 0, 4) 136 | self.cdv = 1 137 | # print("[SDCard] v2 card") 138 | return 139 | raise OSError("timeout waiting for v2 card") 140 | 141 | def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False): 142 | self.cs(0) 143 | 144 | # create and send the command 145 | buf = self.cmdbuf 146 | buf[0] = 0x40 | cmd 147 | buf[1] = arg >> 24 148 | buf[2] = arg >> 16 149 | buf[3] = arg >> 8 150 | buf[4] = arg 151 | buf[5] = crc 152 | self.spi.write(buf) 153 | 154 | if skip1: 155 | self.spi.readinto(self.tokenbuf, 0xFF) 156 | 157 | # wait for the response (response[7] == 0) 158 | for i in range(_CMD_TIMEOUT): 159 | self.spi.readinto(self.tokenbuf, 0xFF) 160 | response = self.tokenbuf[0] 161 | if not (response & 0x80): 162 | # this could be a big-endian integer that we are getting here 163 | for j in range(final): 164 | self.spi.write(b"\xff") 165 | if release: 166 | self.cs(1) 167 | self.spi.write(b"\xff") 168 | return response 169 | 170 | # timeout 171 | self.cs(1) 172 | self.spi.write(b"\xff") 173 | return -1 174 | 175 | def readinto(self, buf): 176 | self.cs(0) 177 | 178 | # read until start byte (0xff) 179 | for i in range(_CMD_TIMEOUT): 180 | self.spi.readinto(self.tokenbuf, 0xFF) 181 | if self.tokenbuf[0] == _TOKEN_DATA: 182 | break 183 | time.sleep_ms(1) 184 | else: 185 | self.cs(1) 186 | raise OSError("timeout waiting for response") 187 | 188 | # read data 189 | mv = self.dummybuf_memoryview 190 | if len(buf) != len(mv): 191 | mv = mv[: len(buf)] 192 | self.spi.write_readinto(mv, buf) 193 | 194 | # read checksum 195 | self.spi.write(b"\xff") 196 | self.spi.write(b"\xff") 197 | 198 | self.cs(1) 199 | self.spi.write(b"\xff") 200 | 201 | def write(self, token, buf): 202 | self.cs(0) 203 | 204 | # send: start of block, data, checksum 205 | self.spi.read(1, token) 206 | self.spi.write(buf) 207 | self.spi.write(b"\xff") 208 | self.spi.write(b"\xff") 209 | 210 | # check the response 211 | if (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05: 212 | self.cs(1) 213 | self.spi.write(b"\xff") 214 | return 215 | 216 | # wait for write to finish 217 | while self.spi.read(1, 0xFF)[0] == 0: 218 | pass 219 | 220 | self.cs(1) 221 | self.spi.write(b"\xff") 222 | 223 | def write_token(self, token): 224 | self.cs(0) 225 | self.spi.read(1, token) 226 | self.spi.write(b"\xff") 227 | # wait for write to finish 228 | while self.spi.read(1, 0xFF)[0] == 0x00: 229 | pass 230 | 231 | self.cs(1) 232 | self.spi.write(b"\xff") 233 | 234 | def readblocks(self, block_num, buf): 235 | nblocks = len(buf) // 512 236 | assert nblocks and not len(buf) % 512, "Buffer length is invalid" 237 | if nblocks == 1: 238 | # CMD17: set read address for single block 239 | if self.cmd(17, block_num * self.cdv, 0, release=False) != 0: 240 | # release the card 241 | self.cs(1) 242 | raise OSError(5) # EIO 243 | # receive the data and release card 244 | self.readinto(buf) 245 | else: 246 | # CMD18: set read address for multiple blocks 247 | if self.cmd(18, block_num * self.cdv, 0, release=False) != 0: 248 | # release the card 249 | self.cs(1) 250 | raise OSError(5) # EIO 251 | offset = 0 252 | mv = memoryview(buf) 253 | while nblocks: 254 | # receive the data and release card 255 | self.readinto(mv[offset : offset + 512]) 256 | offset += 512 257 | nblocks -= 1 258 | if self.cmd(12, 0, 0xFF, skip1=True): 259 | raise OSError(5) # EIO 260 | 261 | def writeblocks(self, block_num, buf): 262 | nblocks, err = divmod(len(buf), 512) 263 | assert nblocks and not err, "Buffer length is invalid" 264 | if nblocks == 1: 265 | # CMD24: set write address for single block 266 | if self.cmd(24, block_num * self.cdv, 0) != 0: 267 | raise OSError(5) # EIO 268 | 269 | # send the data 270 | self.write(_TOKEN_DATA, buf) 271 | else: 272 | # CMD25: set write address for first block 273 | if self.cmd(25, block_num * self.cdv, 0) != 0: 274 | raise OSError(5) # EIO 275 | # send the data 276 | offset = 0 277 | mv = memoryview(buf) 278 | while nblocks: 279 | self.write(_TOKEN_CMD25, mv[offset : offset + 512]) 280 | offset += 512 281 | nblocks -= 1 282 | self.write_token(_TOKEN_STOP_TRAN) 283 | 284 | def ioctl(self, op, arg): 285 | if op == 4: # get number of blocks 286 | return self.sectors 287 | 288 | class RTC(object): 289 | # 12:00:00 Thrusday 20 October 2022 290 | # sec min hour week day month year 291 | 292 | NowTime = b'\x00\x00\x12\x11\x20\x10\x22' 293 | w = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]; 294 | 295 | address = 0x68 296 | start = 0x00 297 | alarm = 0x07 298 | control = 0x0e 299 | status = 0x0f 300 | 301 | def __init__(self): 302 | self.bus = I2C(1) 303 | 304 | def set_time(self,new_time): 305 | hour = new_time[0] + new_time[1] 306 | minute = new_time[3] + new_time[4] 307 | second = new_time[6] + new_time[7] 308 | week = "0" + str(self.w.index(new_time.split(",",2)[1])+1) 309 | year = new_time.split(",",2)[2][2] + new_time.split(",",2)[2][3] 310 | month = new_time.split(",",2)[2][5] + new_time.split(",",2)[2][6] 311 | day = new_time.split(",",2)[2][8] + new_time.split(",",2)[2][9] 312 | now_time = binascii.unhexlify((second + " " + minute + " " + hour + " " + week + " " + day + " " + month + " " + year).replace(' ','')) 313 | self.bus.writeto_mem(int(self.address),int(self.start),now_time) 314 | 315 | def read_time(self): 316 | data = self.bus.readfrom_mem(int(self.address),int(self.start),7) 317 | a = data[0]&0x7F #second 318 | b = data[1]&0x7F #minute 319 | c = data[2]&0x3F #hour 320 | d = data[3]&0x07 #week 321 | e = data[4]&0x3F #day 322 | f = data[5]&0x1F #month 323 | 324 | return "20%x/%02x/%02x %02x:%02x:%02x %s" %(data[6],data[5],data[4],data[2],data[1],data[0],self.w[data[3]-1]) 325 | 326 | def _twos_complement(self, input_value: int, num_bits: int) -> int: 327 | mask = 2 ** (num_bits - 1) 328 | return -(input_value & mask) + (input_value & ~mask) 329 | 330 | def temperature(self): 331 | t = self.bus.readfrom_mem(self.address, 0x11, 2) 332 | i = t[0] << 8 | t[1] 333 | return self._twos_complement(i >> 6, 10) * 0.25 334 | -------------------------------------------------------------------------------- /Encryption/EncroPi.py: -------------------------------------------------------------------------------- 1 | ''' 2 | #------------------------------------------------------------------------ 3 | # 4 | # This is a python Library code for EncroPi Board 5 | # Written by SB Components Ltd 6 | # 7 | #================================================================================== 8 | # Copyright (c) SB Components Ltd 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in all 18 | # copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | # SOFTWARE. 27 | #================================================================================== 28 | ''' 29 | 30 | from machine import Pin, UART,SPI,I2C 31 | from micropython import const 32 | import binascii 33 | 34 | import time 35 | 36 | 37 | _CMD_TIMEOUT = const(100) 38 | _R1_IDLE_STATE = const(1 << 0) 39 | _R1_ILLEGAL_COMMAND = const(1 << 2) 40 | _TOKEN_CMD25 = const(0xFC) 41 | _TOKEN_STOP_TRAN = const(0xFD) 42 | _TOKEN_DATA = const(0xFE) 43 | 44 | class SDCard: 45 | def __init__(self): 46 | spi=SPI(0,sck=Pin(18),mosi=Pin(19),miso=Pin(16)) 47 | cs = Pin(17) 48 | self.spi = spi 49 | self.cs = cs 50 | 51 | self.cmdbuf = bytearray(6) 52 | self.dummybuf = bytearray(512) 53 | self.tokenbuf = bytearray(1) 54 | for i in range(512): 55 | self.dummybuf[i] = 0xFF 56 | self.dummybuf_memoryview = memoryview(self.dummybuf) 57 | # initialise the card 58 | self.init_card() 59 | 60 | def init_spi(self, baudrate): 61 | try: 62 | master = self.spi.MASTER 63 | except AttributeError: 64 | # on ESP8266 65 | self.spi.init(baudrate=baudrate, phase=0, polarity=0) 66 | else: 67 | # on pyboard 68 | self.spi.init(master, baudrate=baudrate, phase=0, polarity=0) 69 | 70 | def init_card(self): 71 | # init CS pin 72 | self.cs.init(self.cs.OUT, value=1) 73 | 74 | # init SPI bus; use low data rate for initialisation 75 | self.init_spi(100000) 76 | 77 | # clock card at least 100 cycles with cs high 78 | for i in range(16): 79 | self.spi.write(b"\xff") 80 | 81 | # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts) 82 | for _ in range(5): 83 | if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE: 84 | break 85 | else: 86 | raise OSError("no SD card") 87 | 88 | # CMD8: determine card version 89 | r = self.cmd(8, 0x01AA, 0x87, 4) 90 | if r == _R1_IDLE_STATE: 91 | self.init_card_v2() 92 | elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND): 93 | self.init_card_v1() 94 | else: 95 | raise OSError("couldn't determine SD card version") 96 | 97 | # get the number of sectors 98 | # CMD9: response R2 (R1 byte + 16-byte block read) 99 | if self.cmd(9, 0, 0, 0, False) != 0: 100 | raise OSError("no response from SD card") 101 | csd = bytearray(16) 102 | self.readinto(csd) 103 | if csd[0] & 0xC0 == 0x40: # CSD version 2.0 104 | self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024 105 | elif csd[0] & 0xC0 == 0x00: # CSD version 1.0 (old, <=2GB) 106 | c_size = csd[6] & 0b11 | csd[7] << 2 | (csd[8] & 0b11000000) << 4 107 | c_size_mult = ((csd[9] & 0b11) << 1) | csd[10] >> 7 108 | self.sectors = (c_size + 1) * (2 ** (c_size_mult + 2)) 109 | else: 110 | raise OSError("SD card CSD format not supported") 111 | # print('sectors', self.sectors) 112 | 113 | # CMD16: set block length to 512 bytes 114 | if self.cmd(16, 512, 0) != 0: 115 | raise OSError("can't set 512 block size") 116 | 117 | # set to high data rate now that it's initialised 118 | self.init_spi(1320000) 119 | 120 | def init_card_v1(self): 121 | for i in range(_CMD_TIMEOUT): 122 | self.cmd(55, 0, 0) 123 | if self.cmd(41, 0, 0) == 0: 124 | self.cdv = 512 125 | # print("[SDCard] v1 card") 126 | return 127 | raise OSError("timeout waiting for v1 card") 128 | 129 | def init_card_v2(self): 130 | for i in range(_CMD_TIMEOUT): 131 | time.sleep_ms(50) 132 | self.cmd(58, 0, 0, 4) 133 | self.cmd(55, 0, 0) 134 | if self.cmd(41, 0x40000000, 0) == 0: 135 | self.cmd(58, 0, 0, 4) 136 | self.cdv = 1 137 | # print("[SDCard] v2 card") 138 | return 139 | raise OSError("timeout waiting for v2 card") 140 | 141 | def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False): 142 | self.cs(0) 143 | 144 | # create and send the command 145 | buf = self.cmdbuf 146 | buf[0] = 0x40 | cmd 147 | buf[1] = arg >> 24 148 | buf[2] = arg >> 16 149 | buf[3] = arg >> 8 150 | buf[4] = arg 151 | buf[5] = crc 152 | self.spi.write(buf) 153 | 154 | if skip1: 155 | self.spi.readinto(self.tokenbuf, 0xFF) 156 | 157 | # wait for the response (response[7] == 0) 158 | for i in range(_CMD_TIMEOUT): 159 | self.spi.readinto(self.tokenbuf, 0xFF) 160 | response = self.tokenbuf[0] 161 | if not (response & 0x80): 162 | # this could be a big-endian integer that we are getting here 163 | for j in range(final): 164 | self.spi.write(b"\xff") 165 | if release: 166 | self.cs(1) 167 | self.spi.write(b"\xff") 168 | return response 169 | 170 | # timeout 171 | self.cs(1) 172 | self.spi.write(b"\xff") 173 | return -1 174 | 175 | def readinto(self, buf): 176 | self.cs(0) 177 | 178 | # read until start byte (0xff) 179 | for i in range(_CMD_TIMEOUT): 180 | self.spi.readinto(self.tokenbuf, 0xFF) 181 | if self.tokenbuf[0] == _TOKEN_DATA: 182 | break 183 | time.sleep_ms(1) 184 | else: 185 | self.cs(1) 186 | raise OSError("timeout waiting for response") 187 | 188 | # read data 189 | mv = self.dummybuf_memoryview 190 | if len(buf) != len(mv): 191 | mv = mv[: len(buf)] 192 | self.spi.write_readinto(mv, buf) 193 | 194 | # read checksum 195 | self.spi.write(b"\xff") 196 | self.spi.write(b"\xff") 197 | 198 | self.cs(1) 199 | self.spi.write(b"\xff") 200 | 201 | def write(self, token, buf): 202 | self.cs(0) 203 | 204 | # send: start of block, data, checksum 205 | self.spi.read(1, token) 206 | self.spi.write(buf) 207 | self.spi.write(b"\xff") 208 | self.spi.write(b"\xff") 209 | 210 | # check the response 211 | if (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05: 212 | self.cs(1) 213 | self.spi.write(b"\xff") 214 | return 215 | 216 | # wait for write to finish 217 | while self.spi.read(1, 0xFF)[0] == 0: 218 | pass 219 | 220 | self.cs(1) 221 | self.spi.write(b"\xff") 222 | 223 | def write_token(self, token): 224 | self.cs(0) 225 | self.spi.read(1, token) 226 | self.spi.write(b"\xff") 227 | # wait for write to finish 228 | while self.spi.read(1, 0xFF)[0] == 0x00: 229 | pass 230 | 231 | self.cs(1) 232 | self.spi.write(b"\xff") 233 | 234 | def readblocks(self, block_num, buf): 235 | nblocks = len(buf) // 512 236 | assert nblocks and not len(buf) % 512, "Buffer length is invalid" 237 | if nblocks == 1: 238 | # CMD17: set read address for single block 239 | if self.cmd(17, block_num * self.cdv, 0, release=False) != 0: 240 | # release the card 241 | self.cs(1) 242 | raise OSError(5) # EIO 243 | # receive the data and release card 244 | self.readinto(buf) 245 | else: 246 | # CMD18: set read address for multiple blocks 247 | if self.cmd(18, block_num * self.cdv, 0, release=False) != 0: 248 | # release the card 249 | self.cs(1) 250 | raise OSError(5) # EIO 251 | offset = 0 252 | mv = memoryview(buf) 253 | while nblocks: 254 | # receive the data and release card 255 | self.readinto(mv[offset : offset + 512]) 256 | offset += 512 257 | nblocks -= 1 258 | if self.cmd(12, 0, 0xFF, skip1=True): 259 | raise OSError(5) # EIO 260 | 261 | def writeblocks(self, block_num, buf): 262 | nblocks, err = divmod(len(buf), 512) 263 | assert nblocks and not err, "Buffer length is invalid" 264 | if nblocks == 1: 265 | # CMD24: set write address for single block 266 | if self.cmd(24, block_num * self.cdv, 0) != 0: 267 | raise OSError(5) # EIO 268 | 269 | # send the data 270 | self.write(_TOKEN_DATA, buf) 271 | else: 272 | # CMD25: set write address for first block 273 | if self.cmd(25, block_num * self.cdv, 0) != 0: 274 | raise OSError(5) # EIO 275 | # send the data 276 | offset = 0 277 | mv = memoryview(buf) 278 | while nblocks: 279 | self.write(_TOKEN_CMD25, mv[offset : offset + 512]) 280 | offset += 512 281 | nblocks -= 1 282 | self.write_token(_TOKEN_STOP_TRAN) 283 | 284 | def ioctl(self, op, arg): 285 | if op == 4: # get number of blocks 286 | return self.sectors 287 | 288 | class RTC(object): 289 | # 12:00:00 Thrusday 20 October 2022 290 | # sec min hour week day month year 291 | 292 | NowTime = b'\x00\x00\x12\x11\x20\x10\x22' 293 | w = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]; 294 | 295 | address = 0x68 296 | start = 0x00 297 | alarm = 0x07 298 | control = 0x0e 299 | status = 0x0f 300 | 301 | def __init__(self): 302 | self.bus = I2C(1) 303 | 304 | def set_time(self,new_time): 305 | hour = new_time[0] + new_time[1] 306 | minute = new_time[3] + new_time[4] 307 | second = new_time[6] + new_time[7] 308 | week = "0" + str(self.w.index(new_time.split(",",2)[1])+1) 309 | year = new_time.split(",",2)[2][2] + new_time.split(",",2)[2][3] 310 | month = new_time.split(",",2)[2][5] + new_time.split(",",2)[2][6] 311 | day = new_time.split(",",2)[2][8] + new_time.split(",",2)[2][9] 312 | now_time = binascii.unhexlify((second + " " + minute + " " + hour + " " + week + " " + day + " " + month + " " + year).replace(' ','')) 313 | self.bus.writeto_mem(int(self.address),int(self.start),now_time) 314 | 315 | def read_time(self): 316 | data = self.bus.readfrom_mem(int(self.address),int(self.start),7) 317 | a = data[0]&0x7F #second 318 | b = data[1]&0x7F #minute 319 | c = data[2]&0x3F #hour 320 | d = data[3]&0x07 #week 321 | e = data[4]&0x3F #day 322 | f = data[5]&0x1F #month 323 | 324 | return "20%x/%02x/%02x %02x:%02x:%02x %s" %(data[6],data[5],data[4],data[2],data[1],data[0],self.w[data[3]-1]) 325 | 326 | def _twos_complement(self, input_value: int, num_bits: int) -> int: 327 | mask = 2 ** (num_bits - 1) 328 | return -(input_value & mask) + (input_value & ~mask) 329 | 330 | def temperature(self): 331 | t = self.bus.readfrom_mem(self.address, 0x11, 2) 332 | i = t[0] << 8 | t[1] 333 | return self._twos_complement(i >> 6, 10) * 0.25 334 | -------------------------------------------------------------------------------- /Encryption/encryption_sdcard.py: -------------------------------------------------------------------------------- 1 | ''' 2 | #------------------------------------------------------------------------ 3 | # 4 | # This is a python Example code for EncroPi Board 5 | # Written by SB Components Ltd 6 | # 7 | #================================================================================== 8 | # Copyright (c) SB Components Ltd 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in all 18 | # copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | # SOFTWARE. 27 | #================================================================================== 28 | ''' 29 | 30 | from machine import Pin, SPI ,UART 31 | from ucryptolib import aes 32 | import random 33 | import time 34 | import EncroPi 35 | import uos 36 | import os 37 | import st7789 38 | import vga1_bold_16x32 as font 39 | 40 | MODE_CBC = 2 41 | BLOCK_SIZE = 16 42 | 43 | sd=EncroPi.SDCard() 44 | spi = SPI(1, baudrate=40000000, sck=Pin(10), mosi=Pin(11)) 45 | tft = st7789.ST7789(spi,135,240,reset=Pin(12, Pin.OUT),cs=Pin(9, Pin.OUT),dc=Pin(8, Pin.OUT),backlight=Pin(13, Pin.OUT),rotation=1) 46 | 47 | def info(): 48 | tft.init() 49 | time.sleep(0.2) 50 | tft.text(font,"SB COMPONENTS", 15,20) 51 | tft.fill_rect(15, 60, 210,10, st7789.RED) 52 | 53 | tft.text(font,"EncroPi", 15,80,st7789.YELLOW) 54 | #tft.text(font,"CHECK", 15,100,st7789.YELLOW) 55 | tft.fill_rect(15, 140, 210, 10, st7789.BLUE) 56 | time.sleep(2) 57 | tft.fill(0) #clear screen 58 | 59 | info() 60 | 61 | key = b'this_is_the_key_123456_asdfgh123' 62 | 63 | plaintext = 'SB COMPONENTS' 64 | 65 | print('Plain Text:', plaintext) 66 | tft.text(font,"Plain Text:", 15,20,st7789.YELLOW) 67 | tft.text(font,plaintext, 15,80,st7789.YELLOW) 68 | time.sleep(2) 69 | tft.fill(0) 70 | # Padding plain text with space 71 | pad = BLOCK_SIZE - len(plaintext) % BLOCK_SIZE 72 | plaintext = plaintext + " "*pad 73 | 74 | iv = uos.urandom(BLOCK_SIZE) 75 | cipher = aes(key,MODE_CBC,iv) 76 | 77 | ct_bytes = iv + cipher.encrypt(plaintext) 78 | print ('AES-CBC encrypted:', ct_bytes) 79 | tft.text(font,'Encrypted:', 15,20,st7789.YELLOW) 80 | tft.text(font,ct_bytes, 15,80,st7789.YELLOW) 81 | time.sleep(2) 82 | tft.fill(0) 83 | 84 | 85 | vfs = os.VfsFat(sd) 86 | os.mount(vfs, "/fc") 87 | print("Filesystem check") 88 | print(os.listdir("/fc")) 89 | 90 | fn = "/fc/Encripted.bin" # make encripted file 91 | 92 | 93 | print() 94 | print("Single block read/write") 95 | with open(fn, "ab") as f: 96 | n = f.write(ct_bytes) 97 | print(n, "bytes written") 98 | 99 | with open(fn, "rb") as f: 100 | result2 = f.read() 101 | print(result2) 102 | print(len(result2), "bytes read") 103 | 104 | print(result2) 105 | iv = result2[:BLOCK_SIZE] 106 | cipher = aes(key,MODE_CBC,iv) 107 | decrypted = cipher.decrypt(result2)[BLOCK_SIZE:] 108 | print('AES-CBC decrypted:', decrypted) 109 | tft.text(font,'Decrypted', 15,20,st7789.YELLOW) 110 | tft.text(font,decrypted, 15,80,st7789.YELLOW) 111 | time.sleep(2) 112 | #tft.fill(0) 113 | os.umount("/fc") 114 | -------------------------------------------------------------------------------- /Encryption/encryption_test.py: -------------------------------------------------------------------------------- 1 | ''' 2 | #------------------------------------------------------------------------ 3 | # 4 | # This is a python Example code for EncroPi Board 5 | # Written by SB Components Ltd 6 | # 7 | #================================================================================== 8 | # Copyright (c) SB Components Ltd 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in all 18 | # copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | # SOFTWARE. 27 | #================================================================================== 28 | ''' 29 | 30 | # In this example we have two AES Mode ECB,CBC and CTR 31 | import uos 32 | from ucryptolib import aes 33 | 34 | ECB_MODE = 1 35 | CBC_MODE = 2 36 | CTR_CTR = 6 37 | BLOCK_SIZE = 16 38 | 39 | # key size must be 16 or 32 40 | # key = uos.urandom(32) # or you can use random key 41 | key = b'this_is_the_key_123456_asdfgh123' 42 | 43 | ################################################### 44 | # AES ECB Cryptographic # 45 | ################################################### 46 | cipher = aes(key, ECB_MODE) 47 | 48 | plaintext = 'SB COMPONENTS' 49 | print('Plain Text:', plaintext) 50 | 51 | # Padding plain text with space 52 | pad = BLOCK_SIZE - len(plaintext) % BLOCK_SIZE 53 | plaintext = plaintext + " "*pad 54 | 55 | 56 | encrypted = cipher.encrypt(plaintext) 57 | print('AES-ECB encrypted:', encrypted ) 58 | 59 | cipher = aes(key,1) # cipher has to renew for decrypt 60 | decrypted = cipher.decrypt(encrypted) 61 | print('AES-ECB decrypted:', decrypted) 62 | 63 | 64 | print('\n\r') 65 | 66 | ################################################### 67 | # AES CBC Cryptographic # 68 | ################################################### 69 | 70 | # Generate iv with HW random generator 71 | iv = uos.urandom(BLOCK_SIZE) 72 | cipher = aes(key,CBC_MODE,iv) 73 | 74 | ct_bytes = iv + cipher.encrypt(plaintext) 75 | print ('AES-CBC encrypted:', ct_bytes) 76 | 77 | iv = ct_bytes[:BLOCK_SIZE] 78 | print(iv) 79 | cipher = aes(key,CBC_MODE,iv) 80 | decrypted = cipher.decrypt(ct_bytes)[BLOCK_SIZE:] 81 | print('AES-CBC decrypted:', decrypted) 82 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 SB Components 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EncroPi 2 | 3 | 4 | 5 | It is an innovative and highly efficient USB RTC stick based on the Raspberry Pi RP2040 microcontroller. It is a power booster product for all electronic enthusiasts and time setters. We have designed EncroPi in such a way that its powerful and advanced features will help you in your projects seamlessly. It is not just a Real Time Clock but combined with the power of the RP2040 microcontroller, it is a lightning-fast and extremely reliable product loaded with many features. 6 | 7 | 8 | 9 | ### RP2040 10 | Raspberry Pi RP2040 Microcontroller Chip is the debut microcontroller from Raspberry Pi. It brings high performance, low cost, and ease of use to the microcontroller space. The RP2040 has a large on-chip memory, symmetric dual-core processor complex, deterministic bus fabric, and rich peripheral set. It's augmented with a unique Programmable I/O (PIO) subsystem and provides unrivaled power and flexibility. 11 | ### DS3231(RTC) 12 | It is a Real time clock IC that has very low-power consumption, a digital temperature sensor, and two-time day alarms. Its operating voltage is 2.3V-5.5V. 13 | ### SD Card Slot 14 | It is provided for storing data in your SD card that you want to run. 15 | ### Battery Holder 16 | It is provided for holding the power backup battery For keeping time. It holds a button cell to do so. 17 | ### LCD Display 18 | The 1.14-inch LCD has a resolution of 240x135 pixels for showing date and time or other data the user wants to show on the Display of EncroPi. 19 | ### USB Type-C Port 20 | Just like USB 2.0 user can also debug programmes via type-cable and can be used for power-up EncroPi via C-type USB cable. 21 | 22 | ### Boot Button, Status LED and Power LED 23 | The onboard boot button is provided for uploading the firmware into RP2040 of EncroPi. Status is connected on GPIO-15 and it's fully user configurable. Power LED is for indicating power. 24 | 25 | ### Additional GPIO's 26 | In this board we are providing some extra GPIO pins for connecting any external Input/output(such as sensors) to RP2040 of EncroPi board. 27 | These GPIO's are 5v, GND, 3v, GP4, GP3, GP14, GP15. 28 | 29 | ## Features Of EncroPi 30 | 31 | EncropPi has many powerful and advance features wich will help users in many Projects. Some of the key features of EncroPi are: 32 | Data Read/Write, Data Encryption, Data Logger, Real Time Clock(RTC), etc. 33 | 34 | #### Encryption: 35 | Now encrypt the data which you run on your USB RTC. Make use of EncroPi as an encrypted key and secure your codes and application sources. 36 | 37 | classcryptolib.aes¶ 38 | classmethod__init__(key, mode[, IV])¶ 39 | Initialize cipher object, suitable for encryption/decryption. Note: after initialization, cipher object can be use only either for encryption or decryption. Running decrypt() operation after encrypt() or vice versa is not supported. 40 | 41 | Parameters are: 42 | 43 | key is an encryption/decryption key (bytes-like). 44 | 45 | mode is: 46 | 47 | 1 (or cryptolib.MODE_ECB if it exists) for Electronic Code Book (ECB). 48 | 49 | 2 (or cryptolib.MODE_CBC if it exists) for Cipher Block Chaining (CBC). 50 | 51 | 6 (or cryptolib.MODE_CTR if it exists) for Counter mode (CTR). 52 | 53 | IV is an initialization vector for CBC mode. 54 | 55 | For Counter mode, IV is the initial value for the counter. 56 | 57 | encrypt(in_buf[, out_buf])¶ 58 | Encrypt in_buf. If no out_buf is given, the result is returned as a newly allocated bytes object. Otherwise, the result is written into mutable buffer out_buf. in_buf and out_buf can also refer to the same mutable buffer, in which case data is encrypted in-place. 59 | 60 | decrypt(in_buf[, out_buf])¶ 61 | Like encrypt(), but for decryption. 62 | 63 | #### Data Logger: 64 | No more worries about losing your data or loading it over and over again every time. EncroPi has a dedicated SD card slot to store your data safely. 65 | 66 | #### RTC: 67 | Real-time for your personal computers, embedded systems, servers, or any electronic device that may require accurate time keeping 68 | 69 | ## Downloading IDE 70 | * To open upython(.py) files you should have Thonny IDE installed in your system, If you don’t have Thonny IDE follow the link below to install it 71 | * https://thonny.org/ 72 | 73 | ### Uploading Micropython Firmware 74 | * Hold Boot Button and plug-in in your system after that release Boot button, you will get pop-up window of showing RaspberryPi as a mass storage device. Copy the downloaded firmware file(firmware.uf2) from this repository and paste it in the raspberry pi board or you can also do this by simply drag and drop method. Now, your board has updated firmware in it. 75 | 76 | 77 | 78 | ### Installing CircuitPython Firmware 79 | * For using the ***Image Display*** functionality in EncroPi, insert the circuit python to the EncroPi(it is circuit python firmware ***adafruit-circuitpython-raspberry_pi_pico-en_US-7.1.1.uf2***). for this first you need to press the boot button then connect the USB, don,t release the button until you connect the USB to the laptop. then you see a new device named "RPI-RP2" drag this file "adafruit-circuitpython- raspberry_pi_pico-en_US-7.1.1.uf2" to this device as shown in figure: this is the official website, or you can download from here https://circuitpython.org/board/raspberry_pi_pico/ 80 | 81 | 82 | 83 | When you properly insert the circuitpython then you see a new device that looks like the below image: 84 | 85 | 86 | 87 | After this go to run->select interpreter,choose device and port 88 | 89 | 90 | 91 | 92 | ## For setup the Board in thonny 93 | * Now connect USB Cable on USB Port of Pico. 94 | * Open Thonny IDE and Choose interpreter as MicroPython (Raspberry Pi pico). 95 | 96 | 97 | 98 | ## Types of Code files and their functioning: 99 | 100 | * File "EncroPi.py" is the library file of this board 101 | 102 | * The "data_save_to_sdcard.py" file is for writing any data/file in SD card and Reading data from it 103 | 104 | * The function of "gpio_test.py" file is to work with Additional GPIO pins for interfacing external I/o devices 105 | 106 | * "rtc_test.py" file is for using the RTC function of this board. 107 | 108 | ### The "Data_transfer_between_pc_and_EncroPi" Directory 109 | Under this folder you will get two micropython files: 110 | 111 | * One file is the library file i.e, "Encropi.py". This library is for the example code provided in this directory, library file have to save in your encropi board before running the example. 112 | * File 2nd, i.e "data_transfer_to_EncroPi.py" is the standard python code file for sending data from our Desktop/laptop to our EncroPi board 113 | 114 | ### The "Encryption" directory 115 | In this directory there are three code files : 116 | * First one is the Library file for the examples provided in this folder 117 | * 2nd file is for Encrypting data files from sdcard and store it in sdcard. 118 | * 3rd file is for encrypting string data within the IDE. 119 | 120 | ### Display Images 121 | Now, open the folder Display_Images, inside this folder their is sub-folder one is ***Display_Images from PC and another one is Display images from SDCard*** folder. 122 | 123 | * **Display Images from PC** -> For display images in EncroPi we use CircuitPython because it is very easy, it is developed by adafruit industries, First of all, we need to insert the circuit python to the roundypi(it is circuit python firmware "adafruit-circuitpython-raspberry_pi_pico-en_US-7.1.1.uf2"). 124 | You can also display your custom images, for this you need to go "images" folder and save your images by changing its formate and resolution according to lcd display. 125 | You can online convert any image to BMP image (the size must be 240x135), i a website below(there are various website) 126 | https://image.online-convert.com/convert-to-bmp 127 | Now, you need to run the python code file provided in this folder (i.e, images_display.py). 128 | 129 | * **Display Images From SD Card** -> For this, we need to insert the circuit python to the roundypi(it is circuit python firmware "adafruit-circuitpython-raspberry_pi_pico-en_US-7.1.1.uf2"). 130 | Now, follow all the process of **Display Images from PC**,you only have to simply save the images containing in this directory (Do not save images in any directory when storing in SDCard). Finally, run the python code provided in this directory (i.e, display_image_sdcard_circuitpython.py) 131 | 132 | 133 | ## Documentation 134 | 135 | * [EncroPi Hardware](https://github.com/sbcshop/EncroPi-Hardware) 136 | * [RaspberryPi PICO Getting Started with Micropython](https://www.raspberrypi.com/documentation/microcontrollers/micropython.html) 137 | * [RaspberryPi PICO Official website](https://www.raspberrypi.com/documentation/microcontrollers/) 138 | * [RP2040 Datasheet](https://www.raspberrypi.com/documentation/microcontrollers/rp2040.html) 139 | * [Raspberry Pi Pico Datasheet](https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html) 140 | * [RP2040 Hardware Design](https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html) 141 | * [Raspberry Pi Pico Pinout](https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html) 142 | 143 | ## Related Products 144 | 145 | * [SquaryPi](https://shop.sb-components.co.uk/products/squary?variant=40443840921683) 146 | 147 | ![SquaryPi](https://cdn.shopify.com/s/files/1/1217/2104/products/1_5874b3b5-2a2f-453e-bf54-abbf2a26acb9.png?v=1670307456&width=400) 148 | 149 | * [USB RTC](https://shop.sb-components.co.uk/products/usb-rtc-for-raspberry-pi-1?_pos=1&_sid=b93d6138a&_ss=r) 150 | 151 | ![USB RTC](https://cdn.shopify.com/s/files/1/1217/2104/products/3_211d5991-c671-4c67-a336-e60faa793cfd.jpg?v=1665636609&width=400) 152 | 153 | ## Product License 154 | 155 | This is ***open source*** product. Kindly check LICENSE.md file for more information. 156 | 157 | Please contact support@sb-components.co.uk for technical support. 158 |

159 | 160 |

161 | -------------------------------------------------------------------------------- /adafruit-circuitpython-raspberry_pi_pico-en_US-7.1.1.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/adafruit-circuitpython-raspberry_pi_pico-en_US-7.1.1.uf2 -------------------------------------------------------------------------------- /data_save_to_sdcard.py: -------------------------------------------------------------------------------- 1 | ''' 2 | #------------------------------------------------------------------------ 3 | # 4 | # This is a python Example code for EncroPi Board 5 | # Written by SB Components Ltd 6 | # 7 | #================================================================================== 8 | # Copyright (c) SB Components Ltd 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in all 18 | # copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | # SOFTWARE. 27 | #================================================================================== 28 | ''' 29 | 30 | #data store to sdcard from pc 31 | from machine import UART,SPI 32 | from machine import Pin 33 | import EncroPi 34 | import st7789 35 | import time,utime 36 | import os 37 | 38 | import vga1_8x16 as font1 39 | import vga2_8x8 as font 40 | import vga1_16x32 as font 41 | import vga1_16x16 as font2 42 | 43 | spi = SPI(1, baudrate=40000000, sck=Pin(10), mosi=Pin(11)) 44 | tft = st7789.ST7789(spi,135,240,reset=Pin(12, Pin.OUT),cs=Pin(9, Pin.OUT),dc=Pin(8, Pin.OUT),backlight=Pin(13, Pin.OUT),rotation=1) 45 | 46 | 47 | uart = UART(0,baudrate = 9600,tx = Pin(0),rx = Pin(1)) 48 | 49 | def info(): 50 | tft.init() 51 | utime.sleep(0.2) 52 | tft.text(font,"SB-COMPONENTS", 0,0) 53 | tft.fill_rect(0, 40, 210,10, st7789.RED) 54 | 55 | tft.text(font,"EncroPi", 0,55,st7789.YELLOW) 56 | tft.text(font,"Data Logger", 0,100,st7789.YELLOW) 57 | tft.fill_rect(0, 90, 210, 10, st7789.BLUE) 58 | time.sleep(1) 59 | tft.fill(0) #clear screen 60 | tft.text(font,"SEND MESSAGE", 5,10,st7789.WHITE) 61 | 62 | info() 63 | 64 | while True: 65 | data = input() 66 | uart.write(data)#send data 67 | tft.text(font,data, 10,60,st7789.YELLOW) 68 | utime.sleep(0.2)#wait 200ms 69 | tft.text(font,data, 10,60,st7789.BLACK) 70 | 71 | sd=EncroPi.SDCard() 72 | vfs = os.VfsFat(sd) 73 | os.mount(vfs, "/fc") 74 | #print("Filesystem check") 75 | #print(os.listdir("/fc")) # check the files in sd card 76 | 77 | fn = "/fc/File.txt" 78 | #print("Single block read/write") 79 | 80 | #data = "SB COMPONENTS" 81 | ################################################# 82 | 83 | with open(fn, "a") as f: # append data to file 84 | n = f.write(data+'\n') 85 | #print(n, "bytes written") 86 | os.umount("/fc") 87 | ################################################# 88 | 89 | ################################################# 90 | with open(fn, "r") as f: # read data from file 91 | result = f.read() 92 | print(result) 93 | print(len(result), "bytes read") 94 | os.umount("/fc") 95 | ################################################# 96 | -------------------------------------------------------------------------------- /firmware.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/firmware.uf2 -------------------------------------------------------------------------------- /gpio_test.py: -------------------------------------------------------------------------------- 1 | ''' 2 | #------------------------------------------------------------------------ 3 | # 4 | # This is a python Example code for EncroPi Board 5 | # Written by SB Components Ltd 6 | # 7 | #================================================================================== 8 | # Copyright (c) SB Components Ltd 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in all 18 | # copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | # SOFTWARE. 27 | #================================================================================== 28 | ''' 29 | 30 | from machine import Pin 31 | import utime,time 32 | 33 | status = machine.Pin(25, machine.Pin.OUT) #gp25 is used for status led 34 | gp4 = machine.Pin(4, machine.Pin.OUT) 35 | gp5 = machine.Pin(5, machine.Pin.OUT) 36 | gp14 = machine.Pin(14, machine.Pin.OUT) 37 | gp15 = machine.Pin(15, machine.Pin.OUT) 38 | 39 | status.value(1) 40 | gp4.value(1) 41 | gp5.value(1) 42 | gp14.value(1) 43 | gp15.value(1) 44 | 45 | -------------------------------------------------------------------------------- /images/EncroPi (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/images/EncroPi (1).png -------------------------------------------------------------------------------- /images/Screenshot (29).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbcshop/EncroPi-Software/7346980b33f866a04e933cb4171db2d34e1440c8/images/Screenshot (29).png -------------------------------------------------------------------------------- /rtc_test.py: -------------------------------------------------------------------------------- 1 | ''' 2 | #------------------------------------------------------------------------ 3 | # 4 | # This is a python Example code for EncroPi Board 5 | # Written by SB Components Ltd 6 | # 7 | #================================================================================== 8 | # Copyright (c) SB Components Ltd 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in all 18 | # copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | # SOFTWARE. 27 | #================================================================================== 28 | ''' 29 | 30 | # this file show and set time 31 | # and also display temperature 32 | from machine import Pin, UART,SPI 33 | import time,utime 34 | import EncroPi 35 | 36 | rtc = EncroPi.RTC() 37 | print(rtc.read_time()) 38 | rtc.set_time('12:24:00,Thursday,2022-10-20') # set time, after setting time comment this line 39 | print(rtc.temperature()) 40 | 41 | --------------------------------------------------------------------------------