Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Occasionally stuck in loop during file write #50

Closed
calcut opened this issue Oct 7, 2022 · 3 comments · Fixed by #54
Closed

Occasionally stuck in loop during file write #50

calcut opened this issue Oct 7, 2022 · 3 comments · Fixed by #54
Labels

Comments

@calcut
Copy link
Contributor

calcut commented Oct 7, 2022

Run into an error where my watchdog timer has been getting triggered.
I'm working on a minimal example... but I've only seen the error very occasionally (maybe once a day) so it might be tricky.

I'm running something like this

with open('/sd/log.txt', 'a') as f:
    f.write(text)

I get this traceback:

  File "adafruit_sdcard.py", line 446, in readblocks
  File "adafruit_sdcard.py", line 289, in _block_cmd
  File "adafruit_sdcard.py", line 260, in _cmd
<WatchDogTimeout>

Which tells me the code is getting stuck in the _cmd() function due to
while buf[1] != 0xFE: never being satisfied.
Or at least not within a 60s watchdog timeout.

It looks like there is intended to be a timeout on this funciton (_CMD_TIMEOUT)
but it can fail due to the while loop.

relevant part of _cmd() is here:

# wait for the response (response[7] == 0)
for _ in range(_CMD_TIMEOUT):
    card.readinto(buf, end=1, write_value=0xFF)
    if not (buf[0] & 0x80):
        if response_buf:
            if data_block:
                # Wait for the start block byte
                buf[1] = 0xFF
                while buf[1] != 0xFE: 
                    card.readinto(buf, start=1, end=2, write_value=0xFF)  <-------- line 260
            card.readinto(response_buf, write_value=0xFF)
            if data_block:
                # Read the checksum
                card.readinto(buf, start=1, end=3, write_value=0xFF)
        return buf[0]
return -1

I'm going to try mimicing the for _ in range(_CMD_TIMEOUT): pattern instead of the while loop.
Can do a PR if it works.

@RetiredWizard
Copy link
Contributor

I think I've run into this same issue and at least in my case it appears to be related to the length of the text being written to the SD card.

I threw a couple debug statements into the library and it looks to me like the library gets into trouble whenever the writeblocks method has to use the loop to write more than one block.

I wrote a little test script that writes records of various lengths to an SD card. If I write 10 records of 1022 bytes (actually 1023 including the \n the script adds at the end) or less the write works fine. Sending 10 records of 1023 bytes corrupts the data but doesn't crash the file system. Sending10 records with more than 1023 bytes crashes the SD file system which requires the SD card to be ejected and reinserted along with a CTRL-D before the test script can be run again.

import os
import adafruit_sdcard
import storage
import busio
import board
import digitalio

if not 'sd' in os.listdir('/'):
    spi = busio.SPI(board.GP18,board.GP19,board.GP16)
    cs = digitalio.DigitalInOut(board.GP6)
    sd = adafruit_sdcard.SDCard(spi,cs)

    vfs = storage.VfsFat(sd)
    storage.mount(vfs,'/sd')

again = "y"
while again.upper() != "N":
    nRecs = int(input("Number of lines to write: "))
    iWidth = int(input("Length of each line: "))

    if '_sdwrites.tst' in os.listdir('/sd'):
        os.remove('/sd/_sdwrite.tst')

    with open('/sd/_sdwrite.tst','w') as sdFile:
        for iRec in range(nRecs):
            print(iRec)
            sdFile.write('*'*iWidth+"\n")

    nCount = 0
    lengthError = iWidth
    if not '_sdwrite.tst' in os.listdir('/sd'):
        print("***ERROR*** No records written")
    else:
        with open('/sd/_sdwrite.tst','r') as sdFile:
            for line in sdFile:
                nCount += 1
                if len(line) != iWidth:
                    lengthError = len(line)

        if nCount == nRecs and lengthError == iWidth+1:
            print("Successfully wrote to SD card")
        else:
            print("***ERROR***  Number of lines written:",nCount,"Last bad line length:",lengthError)

    print()
    again = input("Run again? (y/n): ")

@RetiredWizard
Copy link
Contributor

I've stared at the multi-block loop in "writeblocks" but can't see any logic problems but then I'm unfamiliar with the SPI SD card stuff.

I went ahead and modified my write routine so that anytime I'm sending more than 1022 bytes I break the write up, basically lifting the logic from the "writeblocks" module and that resolved the issue. That tells me that the problem in the library has to be in the multi-block transaction logic, probably something with the SD "TOKEN_CMD"s.

This is my updated write routine that no longer gets stuck during file writes:

def filecpy(file1,file2):
    with open(file2, "wb") as fCopy:
        with open(file1, 'rb') as fOrig:
            for line in fOrig:
                # added these lines as a fix ***********************
                if len(line) > 1022:
                    linelen = len(line)
                    wstart = 0
                    while linelen > 0:
                        wend = min(1022,len(line[wstart:])) + wstart
                        fCopy.write(line[wstart:wend])
                        wstart += 1022
                        linelen -= 1022
                else:
                # ***************************************************
                    fCopy.write(line)
    return

@RetiredWizard
Copy link
Contributor

I threw the same patch logic from filecpy into my test driver and it now successfully writes and reads all combinations of line lengths.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants