Module lib.tools.sdcard

Sdcard management class

Expand source code
# Distributed under Pycameresp License
# Copyright (c) 2023 Remi BERTHOLET
# pylint:disable=consider-using-f-string

""" Sdcard management class """
try:
        import machine
except:
        pass
import uos
import tools.logger
import tools.filesystem
import tools.info

class SdCard:
        """ Manage the sdcard """
        opened = [False]
        mountpoint = [""]
        slot = [{}]

        @staticmethod
        def set_slot(**slot):
                """ Set the default sdcard slot
                - slot  :  selects which of the available interfaces to use. Leaving this unset will select the default interface.
                - width : selects the bus width for the SD/MMC interface.
                - cd    : can be used to specify a card-detect pin.
                - wp    : can be used to specify a write-protect pin.
                - sck   : can be used to specify an SPI clock pin.
                - miso  : can be used to specify an SPI miso pin.
                - mosi  : can be used to specify an SPI mosi pin.
                - cs    : can be used to specify an SPI chip select pin.
                - freq  : selects the SD/MMC interface frequency in Hz (only supported on the ESP32).
                (look doc http://docs.micropython.org/en/latest/library/machine.SDCard.html?highlight=sdcard) """
                SdCard.slot[0] = slot

        @staticmethod
        def get_max_size():
                """ Return the maximal size of sdcard """
                if SdCard.is_mounted():
                        status = uos.statvfs(SdCard.get_mountpoint())
                        if tools.filesystem.ismicropython():
                                return status[1]*status[2]
                        else:
                                return status.f_frsize*status.f_blocks
                else:
                        return 0

        @staticmethod
        def get_free_size():
                """ Return the free size of sdcard """
                if SdCard.is_mounted():
                        status = uos.statvfs(SdCard.get_mountpoint())
                        if tools.filesystem.ismicropython():
                                return status[0]*status[3]
                        else:
                                return status.f_bsize * status.f_bfree
                else:
                        return 0

        @staticmethod
        def is_mounted():
                """ Indicates if the sd card is mounted """
                return SdCard.opened[0]

        @staticmethod
        def is_available():
                """ Indicates if the sd card is available on device """
                if "slot" in SdCard.slot[0]:
                        if SdCard.slot[0]["slot"] is None:
                                return False
                return True

        @staticmethod
        def get_mountpoint():
                """ Return the mount point """
                return SdCard.mountpoint[0]

        @staticmethod
        def formatsd(fs="FAT"):
                """ Format the sd card (erase all data) """
                result = False
                if SdCard.is_mounted() is False:
                        try:
                                # pylint:disable=redefined-outer-name

                                import os
                                import machine
                                sd = machine.SDCard(**(SdCard.slot[0]))
                                if fs== "FAT":
                                        # pylint:disable=no-member
                                        os.VfsFat.mkfs(sd)
                                        os.VfsFat(sd)
                                        result = True
                                elif fs== "LFS":
                                        # pylint:disable=no-member
                                        os.VfsLfs2.mkfs(sd)
                                        os.VfsLfs2(sd)
                                result = True
                        except Exception as err:
                                tools.logger.syslog(err)
                return result

        @staticmethod
        def umount(mountpoint = "/sd"):
                """ Umount sd card """
                result = False
                if SdCard.is_mounted() is True and mountpoint != "/" and mountpoint != "":
                        if SdCard.is_available():
                                if tools.filesystem.ismicropython():
                                        try:
                                                uos.umount(mountpoint)
                                                SdCard.mountpoint[0] = ""
                                                SdCard.opened[0]= False
                                                result = True
                                        except Exception as err:
                                                tools.logger.syslog(err, "Cannot umount %s"%mountpoint)
                                else:
                                        SdCard.mountpoint[0] = ""
                                        SdCard.opened[0] = False
                                        result = True
                        else:
                                tools.logger.syslog("SdCard disabled")
                                SdCard.mountpoint[0] = ""
                                SdCard.opened[0] = False
                                result = True
                elif SdCard.is_mounted() is False:
                        result = True
                return result

        @staticmethod
        def mount(mountpoint = "/sd"):
                """ Mount sd card """
                result = False
                if SdCard.is_mounted() is False and mountpoint != "/" and mountpoint != "":
                        if SdCard.is_available():
                                if tools.filesystem.ismicropython():

                                        try:
                                                # If the sdcard not already mounted
                                                if uos.statvfs("/") == uos.statvfs(mountpoint):
                                                        uos.mount(machine.SDCard(**(SdCard.slot[0])), mountpoint)
                                                        SdCard.mountpoint[0] = mountpoint
                                                        SdCard.opened[0]= True
                                                        result = True
                                        except Exception as err:
                                                tools.info.increase_issues_counter()
                                                tools.logger.syslog("Cannot mount %s"%mountpoint)
                                else:
                                        SdCard.mountpoint[0] = mountpoint[1:]
                                        SdCard.opened[0] = True
                                        result = True
                        else:
                                tools.logger.syslog("SdCard disabled")
                                if tools.filesystem.ismicropython():
                                        SdCard.mountpoint[0] = "/data"
                                else:
                                        from os import getcwd
                                        SdCard.mountpoint[0] = "%s/data"%getcwd()
                                SdCard.opened[0] = True
                                tools.filesystem.makedir(SdCard.mountpoint[0], True)
                                result = True
                elif SdCard.is_mounted():
                        if tools.filesystem.ismicropython():
                                if SdCard.is_available():
                                        if mountpoint == SdCard.get_mountpoint():
                                                result = True
                                else:
                                        result = True
                        else:
                                result = True
                return result

        @staticmethod
        def create_file(directory, filename, mode="w"):
                """ Create file with directory """
                result = None
                filepath = directory + "/" + filename
                directories = [directory]
                direct = directory
                while 1:
                        parts = tools.filesystem.split(direct)

                        if parts[1] == "" or parts[0] == "":
                                break
                        directories.append(parts[0])
                        direct = parts[0]

                if "/" in directories:
                        directories.remove("/")
                if SdCard.mountpoint[0] in directories:
                        directories.remove(SdCard.mountpoint[0])

                newdirs = []
                for l in range(len(directories)):
                        part = directories[:l]
                        part.reverse()
                        newdirs.append(part)
                directories.reverse()
                newdirs.append(directories)

                for directories in newdirs:
                        for direct in directories:
                                try:
                                        uos.mkdir(direct)
                                except OSError as err:
                                        if err.args[0] not in [2,17]:
                                                tools.logger.syslog(err)
                                                break
                        try:
                                # pylint:disable=unspecified-encoding
                                result = open(filepath,mode)
                                break
                        except OSError as err:
                                if err.args[0] not in [2,17]:
                                        tools.logger.syslog(err)
                                        break
                return result

        @staticmethod
        def is_not_enough_space(low):
                """ Indicates if remaining space is not sufficient """
                free = SdCard.get_free_size()
                total = SdCard.get_max_size()
                if low:
                        if SdCard.is_available():
                                threshold = 5
                        else:
                                threshold = 5
                else:
                        if SdCard.is_available():
                                threshold = 8
                        else:
                                threshold = 25

                if free < 0 or total < 0:
                        return True

                if free < 32*1024*4:
                        return True
                else:
                        return ((free * 100 // total) <= threshold)

        @staticmethod
        def save(directory, filename, data):
                """ Save file on sd card """
                result = False
                if SdCard.is_mounted():
                        file = None
                        if SdCard.is_not_enough_space(low=True) is False:
                                try:
                                        file = SdCard.create_file(SdCard.get_mountpoint() + "/" + directory, filename, "w")
                                        file.write(data)
                                        file.close()
                                        result = True
                                except OSError as err:
                                        tools.logger.syslog(err, "Cannot save %s/%s/%s"%(SdCard.get_mountpoint(), directory, filename))
                                        # If sd card not responding properly
                                        if err.errno == 2:
                                                tools.info.increase_issues_counter()
                                except Exception as err:
                                        tools.logger.syslog(err, "Cannot save %s/%s/%s"%(SdCard.get_mountpoint(), directory, filename))
                                finally:
                                        if file is not None:
                                                file.close()
                        else:
                                if SdCard.is_available() is False:
                                        result = True
                return result

Classes

class SdCard

Manage the sdcard

Expand source code
class SdCard:
        """ Manage the sdcard """
        opened = [False]
        mountpoint = [""]
        slot = [{}]

        @staticmethod
        def set_slot(**slot):
                """ Set the default sdcard slot
                - slot  :  selects which of the available interfaces to use. Leaving this unset will select the default interface.
                - width : selects the bus width for the SD/MMC interface.
                - cd    : can be used to specify a card-detect pin.
                - wp    : can be used to specify a write-protect pin.
                - sck   : can be used to specify an SPI clock pin.
                - miso  : can be used to specify an SPI miso pin.
                - mosi  : can be used to specify an SPI mosi pin.
                - cs    : can be used to specify an SPI chip select pin.
                - freq  : selects the SD/MMC interface frequency in Hz (only supported on the ESP32).
                (look doc http://docs.micropython.org/en/latest/library/machine.SDCard.html?highlight=sdcard) """
                SdCard.slot[0] = slot

        @staticmethod
        def get_max_size():
                """ Return the maximal size of sdcard """
                if SdCard.is_mounted():
                        status = uos.statvfs(SdCard.get_mountpoint())
                        if tools.filesystem.ismicropython():
                                return status[1]*status[2]
                        else:
                                return status.f_frsize*status.f_blocks
                else:
                        return 0

        @staticmethod
        def get_free_size():
                """ Return the free size of sdcard """
                if SdCard.is_mounted():
                        status = uos.statvfs(SdCard.get_mountpoint())
                        if tools.filesystem.ismicropython():
                                return status[0]*status[3]
                        else:
                                return status.f_bsize * status.f_bfree
                else:
                        return 0

        @staticmethod
        def is_mounted():
                """ Indicates if the sd card is mounted """
                return SdCard.opened[0]

        @staticmethod
        def is_available():
                """ Indicates if the sd card is available on device """
                if "slot" in SdCard.slot[0]:
                        if SdCard.slot[0]["slot"] is None:
                                return False
                return True

        @staticmethod
        def get_mountpoint():
                """ Return the mount point """
                return SdCard.mountpoint[0]

        @staticmethod
        def formatsd(fs="FAT"):
                """ Format the sd card (erase all data) """
                result = False
                if SdCard.is_mounted() is False:
                        try:
                                # pylint:disable=redefined-outer-name

                                import os
                                import machine
                                sd = machine.SDCard(**(SdCard.slot[0]))
                                if fs== "FAT":
                                        # pylint:disable=no-member
                                        os.VfsFat.mkfs(sd)
                                        os.VfsFat(sd)
                                        result = True
                                elif fs== "LFS":
                                        # pylint:disable=no-member
                                        os.VfsLfs2.mkfs(sd)
                                        os.VfsLfs2(sd)
                                result = True
                        except Exception as err:
                                tools.logger.syslog(err)
                return result

        @staticmethod
        def umount(mountpoint = "/sd"):
                """ Umount sd card """
                result = False
                if SdCard.is_mounted() is True and mountpoint != "/" and mountpoint != "":
                        if SdCard.is_available():
                                if tools.filesystem.ismicropython():
                                        try:
                                                uos.umount(mountpoint)
                                                SdCard.mountpoint[0] = ""
                                                SdCard.opened[0]= False
                                                result = True
                                        except Exception as err:
                                                tools.logger.syslog(err, "Cannot umount %s"%mountpoint)
                                else:
                                        SdCard.mountpoint[0] = ""
                                        SdCard.opened[0] = False
                                        result = True
                        else:
                                tools.logger.syslog("SdCard disabled")
                                SdCard.mountpoint[0] = ""
                                SdCard.opened[0] = False
                                result = True
                elif SdCard.is_mounted() is False:
                        result = True
                return result

        @staticmethod
        def mount(mountpoint = "/sd"):
                """ Mount sd card """
                result = False
                if SdCard.is_mounted() is False and mountpoint != "/" and mountpoint != "":
                        if SdCard.is_available():
                                if tools.filesystem.ismicropython():

                                        try:
                                                # If the sdcard not already mounted
                                                if uos.statvfs("/") == uos.statvfs(mountpoint):
                                                        uos.mount(machine.SDCard(**(SdCard.slot[0])), mountpoint)
                                                        SdCard.mountpoint[0] = mountpoint
                                                        SdCard.opened[0]= True
                                                        result = True
                                        except Exception as err:
                                                tools.info.increase_issues_counter()
                                                tools.logger.syslog("Cannot mount %s"%mountpoint)
                                else:
                                        SdCard.mountpoint[0] = mountpoint[1:]
                                        SdCard.opened[0] = True
                                        result = True
                        else:
                                tools.logger.syslog("SdCard disabled")
                                if tools.filesystem.ismicropython():
                                        SdCard.mountpoint[0] = "/data"
                                else:
                                        from os import getcwd
                                        SdCard.mountpoint[0] = "%s/data"%getcwd()
                                SdCard.opened[0] = True
                                tools.filesystem.makedir(SdCard.mountpoint[0], True)
                                result = True
                elif SdCard.is_mounted():
                        if tools.filesystem.ismicropython():
                                if SdCard.is_available():
                                        if mountpoint == SdCard.get_mountpoint():
                                                result = True
                                else:
                                        result = True
                        else:
                                result = True
                return result

        @staticmethod
        def create_file(directory, filename, mode="w"):
                """ Create file with directory """
                result = None
                filepath = directory + "/" + filename
                directories = [directory]
                direct = directory
                while 1:
                        parts = tools.filesystem.split(direct)

                        if parts[1] == "" or parts[0] == "":
                                break
                        directories.append(parts[0])
                        direct = parts[0]

                if "/" in directories:
                        directories.remove("/")
                if SdCard.mountpoint[0] in directories:
                        directories.remove(SdCard.mountpoint[0])

                newdirs = []
                for l in range(len(directories)):
                        part = directories[:l]
                        part.reverse()
                        newdirs.append(part)
                directories.reverse()
                newdirs.append(directories)

                for directories in newdirs:
                        for direct in directories:
                                try:
                                        uos.mkdir(direct)
                                except OSError as err:
                                        if err.args[0] not in [2,17]:
                                                tools.logger.syslog(err)
                                                break
                        try:
                                # pylint:disable=unspecified-encoding
                                result = open(filepath,mode)
                                break
                        except OSError as err:
                                if err.args[0] not in [2,17]:
                                        tools.logger.syslog(err)
                                        break
                return result

        @staticmethod
        def is_not_enough_space(low):
                """ Indicates if remaining space is not sufficient """
                free = SdCard.get_free_size()
                total = SdCard.get_max_size()
                if low:
                        if SdCard.is_available():
                                threshold = 5
                        else:
                                threshold = 5
                else:
                        if SdCard.is_available():
                                threshold = 8
                        else:
                                threshold = 25

                if free < 0 or total < 0:
                        return True

                if free < 32*1024*4:
                        return True
                else:
                        return ((free * 100 // total) <= threshold)

        @staticmethod
        def save(directory, filename, data):
                """ Save file on sd card """
                result = False
                if SdCard.is_mounted():
                        file = None
                        if SdCard.is_not_enough_space(low=True) is False:
                                try:
                                        file = SdCard.create_file(SdCard.get_mountpoint() + "/" + directory, filename, "w")
                                        file.write(data)
                                        file.close()
                                        result = True
                                except OSError as err:
                                        tools.logger.syslog(err, "Cannot save %s/%s/%s"%(SdCard.get_mountpoint(), directory, filename))
                                        # If sd card not responding properly
                                        if err.errno == 2:
                                                tools.info.increase_issues_counter()
                                except Exception as err:
                                        tools.logger.syslog(err, "Cannot save %s/%s/%s"%(SdCard.get_mountpoint(), directory, filename))
                                finally:
                                        if file is not None:
                                                file.close()
                        else:
                                if SdCard.is_available() is False:
                                        result = True
                return result

Class variables

var mountpoint
var opened
var slot

Static methods

def create_file(directory, filename, mode='w')

Create file with directory

Expand source code
@staticmethod
def create_file(directory, filename, mode="w"):
        """ Create file with directory """
        result = None
        filepath = directory + "/" + filename
        directories = [directory]
        direct = directory
        while 1:
                parts = tools.filesystem.split(direct)

                if parts[1] == "" or parts[0] == "":
                        break
                directories.append(parts[0])
                direct = parts[0]

        if "/" in directories:
                directories.remove("/")
        if SdCard.mountpoint[0] in directories:
                directories.remove(SdCard.mountpoint[0])

        newdirs = []
        for l in range(len(directories)):
                part = directories[:l]
                part.reverse()
                newdirs.append(part)
        directories.reverse()
        newdirs.append(directories)

        for directories in newdirs:
                for direct in directories:
                        try:
                                uos.mkdir(direct)
                        except OSError as err:
                                if err.args[0] not in [2,17]:
                                        tools.logger.syslog(err)
                                        break
                try:
                        # pylint:disable=unspecified-encoding
                        result = open(filepath,mode)
                        break
                except OSError as err:
                        if err.args[0] not in [2,17]:
                                tools.logger.syslog(err)
                                break
        return result
def formatsd(fs='FAT')

Format the sd card (erase all data)

Expand source code
@staticmethod
def formatsd(fs="FAT"):
        """ Format the sd card (erase all data) """
        result = False
        if SdCard.is_mounted() is False:
                try:
                        # pylint:disable=redefined-outer-name

                        import os
                        import machine
                        sd = machine.SDCard(**(SdCard.slot[0]))
                        if fs== "FAT":
                                # pylint:disable=no-member
                                os.VfsFat.mkfs(sd)
                                os.VfsFat(sd)
                                result = True
                        elif fs== "LFS":
                                # pylint:disable=no-member
                                os.VfsLfs2.mkfs(sd)
                                os.VfsLfs2(sd)
                        result = True
                except Exception as err:
                        tools.logger.syslog(err)
        return result
def get_free_size()

Return the free size of sdcard

Expand source code
@staticmethod
def get_free_size():
        """ Return the free size of sdcard """
        if SdCard.is_mounted():
                status = uos.statvfs(SdCard.get_mountpoint())
                if tools.filesystem.ismicropython():
                        return status[0]*status[3]
                else:
                        return status.f_bsize * status.f_bfree
        else:
                return 0
def get_max_size()

Return the maximal size of sdcard

Expand source code
@staticmethod
def get_max_size():
        """ Return the maximal size of sdcard """
        if SdCard.is_mounted():
                status = uos.statvfs(SdCard.get_mountpoint())
                if tools.filesystem.ismicropython():
                        return status[1]*status[2]
                else:
                        return status.f_frsize*status.f_blocks
        else:
                return 0
def get_mountpoint()

Return the mount point

Expand source code
@staticmethod
def get_mountpoint():
        """ Return the mount point """
        return SdCard.mountpoint[0]
def is_available()

Indicates if the sd card is available on device

Expand source code
@staticmethod
def is_available():
        """ Indicates if the sd card is available on device """
        if "slot" in SdCard.slot[0]:
                if SdCard.slot[0]["slot"] is None:
                        return False
        return True
def is_mounted()

Indicates if the sd card is mounted

Expand source code
@staticmethod
def is_mounted():
        """ Indicates if the sd card is mounted """
        return SdCard.opened[0]
def is_not_enough_space(low)

Indicates if remaining space is not sufficient

Expand source code
@staticmethod
def is_not_enough_space(low):
        """ Indicates if remaining space is not sufficient """
        free = SdCard.get_free_size()
        total = SdCard.get_max_size()
        if low:
                if SdCard.is_available():
                        threshold = 5
                else:
                        threshold = 5
        else:
                if SdCard.is_available():
                        threshold = 8
                else:
                        threshold = 25

        if free < 0 or total < 0:
                return True

        if free < 32*1024*4:
                return True
        else:
                return ((free * 100 // total) <= threshold)
def mount(mountpoint='/sd')

Mount sd card

Expand source code
@staticmethod
def mount(mountpoint = "/sd"):
        """ Mount sd card """
        result = False
        if SdCard.is_mounted() is False and mountpoint != "/" and mountpoint != "":
                if SdCard.is_available():
                        if tools.filesystem.ismicropython():

                                try:
                                        # If the sdcard not already mounted
                                        if uos.statvfs("/") == uos.statvfs(mountpoint):
                                                uos.mount(machine.SDCard(**(SdCard.slot[0])), mountpoint)
                                                SdCard.mountpoint[0] = mountpoint
                                                SdCard.opened[0]= True
                                                result = True
                                except Exception as err:
                                        tools.info.increase_issues_counter()
                                        tools.logger.syslog("Cannot mount %s"%mountpoint)
                        else:
                                SdCard.mountpoint[0] = mountpoint[1:]
                                SdCard.opened[0] = True
                                result = True
                else:
                        tools.logger.syslog("SdCard disabled")
                        if tools.filesystem.ismicropython():
                                SdCard.mountpoint[0] = "/data"
                        else:
                                from os import getcwd
                                SdCard.mountpoint[0] = "%s/data"%getcwd()
                        SdCard.opened[0] = True
                        tools.filesystem.makedir(SdCard.mountpoint[0], True)
                        result = True
        elif SdCard.is_mounted():
                if tools.filesystem.ismicropython():
                        if SdCard.is_available():
                                if mountpoint == SdCard.get_mountpoint():
                                        result = True
                        else:
                                result = True
                else:
                        result = True
        return result
def save(directory, filename, data)

Save file on sd card

Expand source code
@staticmethod
def save(directory, filename, data):
        """ Save file on sd card """
        result = False
        if SdCard.is_mounted():
                file = None
                if SdCard.is_not_enough_space(low=True) is False:
                        try:
                                file = SdCard.create_file(SdCard.get_mountpoint() + "/" + directory, filename, "w")
                                file.write(data)
                                file.close()
                                result = True
                        except OSError as err:
                                tools.logger.syslog(err, "Cannot save %s/%s/%s"%(SdCard.get_mountpoint(), directory, filename))
                                # If sd card not responding properly
                                if err.errno == 2:
                                        tools.info.increase_issues_counter()
                        except Exception as err:
                                tools.logger.syslog(err, "Cannot save %s/%s/%s"%(SdCard.get_mountpoint(), directory, filename))
                        finally:
                                if file is not None:
                                        file.close()
                else:
                        if SdCard.is_available() is False:
                                result = True
        return result
def set_slot(**slot)

Set the default sdcard slot - slot : selects which of the available interfaces to use. Leaving this unset will select the default interface. - width : selects the bus width for the SD/MMC interface. - cd : can be used to specify a card-detect pin. - wp : can be used to specify a write-protect pin. - sck : can be used to specify an SPI clock pin. - miso : can be used to specify an SPI miso pin. - mosi : can be used to specify an SPI mosi pin. - cs : can be used to specify an SPI chip select pin. - freq : selects the SD/MMC interface frequency in Hz (only supported on the ESP32). (look doc http://docs.micropython.org/en/latest/library/machine.SDCard.html?highlight=sdcard)

Expand source code
@staticmethod
def set_slot(**slot):
        """ Set the default sdcard slot
        - slot  :  selects which of the available interfaces to use. Leaving this unset will select the default interface.
        - width : selects the bus width for the SD/MMC interface.
        - cd    : can be used to specify a card-detect pin.
        - wp    : can be used to specify a write-protect pin.
        - sck   : can be used to specify an SPI clock pin.
        - miso  : can be used to specify an SPI miso pin.
        - mosi  : can be used to specify an SPI mosi pin.
        - cs    : can be used to specify an SPI chip select pin.
        - freq  : selects the SD/MMC interface frequency in Hz (only supported on the ESP32).
        (look doc http://docs.micropython.org/en/latest/library/machine.SDCard.html?highlight=sdcard) """
        SdCard.slot[0] = slot
def umount(mountpoint='/sd')

Umount sd card

Expand source code
@staticmethod
def umount(mountpoint = "/sd"):
        """ Umount sd card """
        result = False
        if SdCard.is_mounted() is True and mountpoint != "/" and mountpoint != "":
                if SdCard.is_available():
                        if tools.filesystem.ismicropython():
                                try:
                                        uos.umount(mountpoint)
                                        SdCard.mountpoint[0] = ""
                                        SdCard.opened[0]= False
                                        result = True
                                except Exception as err:
                                        tools.logger.syslog(err, "Cannot umount %s"%mountpoint)
                        else:
                                SdCard.mountpoint[0] = ""
                                SdCard.opened[0] = False
                                result = True
                else:
                        tools.logger.syslog("SdCard disabled")
                        SdCard.mountpoint[0] = ""
                        SdCard.opened[0] = False
                        result = True
        elif SdCard.is_mounted() is False:
                result = True
        return result