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

ESP32-CAM can't upload new IDE sketch after an sd initialization.. SPI&SD keep MISO level HIGH #6290

Closed
1 task done
qbox4u opened this issue Feb 15, 2022 · 13 comments
Closed
1 task done
Labels
Status: Awaiting triage Issue is waiting for triage

Comments

@qbox4u
Copy link

qbox4u commented Feb 15, 2022

Board

ESP32-CAM

Device Description

ESP32-CAM mounted on a ESP32-cam-MB

Hardware Configuration

on board SD connected to :
1 DATA2 HS2DATA2 GPIO12 x
2 CD/DATA3 HS2DATA3 GPIO13 CS CS
3 CMD HS2CMD GPIO15 DI MOSI
4 3.3v
5 CLK HS@CLK GPIO14 SCLK SCLK
6 GND
7 DATA0 HS2DATA0 GPIO02 DO MISO
8 DATA1 x

Version

latest master

IDE Name

Arduino IDE 1.8.19

Operating System

WIN10

Flash frequency

240mhz

PSRAM enabled

yes

Upload speed

115200

Description

The ESP32-CAM has a SD-micro slot on the main board. and is mounted on a ESP32-CAM-MB board
for providing serial communication.
(eg https://universal-solder.ca/wp-content/uploads/2021/03/5083_b61d93cb-2cb4-4503-b537-ca77679cc7ad0.jpg)

The SD is blocking a new reload of a Sketch after the SD-micro has been initialized.

Schematic SD-micro connections on ESP32-CAM
1 DATA2 HS2DATA2 GPIO12 x
2 CD/DATA3 HS2DATA3 GPIO13 CS CS
3 CMD HS2CMD GPIO15 DI MOSI
4 3.3v
5 CLK HS@CLK GPIO14 SCLK SCLK
6 GND
7 DATA0 HS2DATA0 GPIO02 DO MISO
8 DATA1 x
After the following commands the board cant reload a new IDE sketch

SPI.begin(14, 2, 15, 13);
if (!SD.begin(13) ) {
Serial.println("initialization failed!");
while (1);
}
Pressing the reset-button of the ESP32-CAM board does not solve the issue as the SD-micro card
still blocks a new sketch upload.
The only way to reload a new sketch is to remove the SD-micro (basically a power down) after which the MISO signal
of the SD card is disabled (high impedance again).....

Sketch

/*
 *  Device: Ai-Thinker ESP32-CAM
 *  
 *  Schematic SD connections
 * 1  DATA2      HS2DATA2  GPIO12   x
 * 2  CD/DATA3   HS2DATA3  GPIO13   CS    CS
 * 3  CMD        HS2CMD    GPIO15   DI    MOSI
 * 4  3.3v
 * 5  CLK        HS@CLK    GPIO14   SCLK  SCLK
 * 6  GND
 * 7  DATA0      HS2DATA0  GPIO02   DO    MISO
 * 8  DATA1                         x
 * 
 * HS2DATA1I.begin(14, 2, 15, 13);
 * Reboot code
 *  x1 GPIO  5 
 *  x2 GPIO 15 MTDO
 *  x4 GPIO  4
 *  x8 GPIO  2
 *  1x GPIO  0
 *  2x GPIO 12 MTDI
 * 
 * Problem: Can't upload Arduino IDE sketch after First run of initialization of the SD card
*/
#include <SPI.h>
#include <SD.h>
File root;
#define ONBOARD_LED  33               // main board red LED 
#define FLASHLIGHT_LED  4             // Main board White LED

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  pinMode(ONBOARD_LED, OUTPUT) ;      // THE LED GOES OFF AFTER WRITING TO HIGH
  pinMode(FLASHLIGHT_LED,OUTPUT ) ;   // THE LED GOES OFF AFTER WRITING TO LOW
  SetupBySPI_SD();
}

void loop() {
  // nothing happens after setup finishes.
  digitalWrite(ONBOARD_LED, !digitalRead(ONBOARD_LED) );    // turn the LED off by making the voltage LOW
  delay(100); 
}

void SetupBySPI_SD(){
  //https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/
  Serial.print("Initializing SD card...");
  // Master Out, Slave In (MOSI) - which is the data going OUT (send) from the master esp32-cam  to the sd-slave
  // Master In, Slave Out (MISO) - which is the data going OUT (send) from the sd-slave into the master
  // SPI.begin(SCK,MISO,MOSI,CS);  ==> (sclk,d0,di,cs)
  SPI.begin(14, 2, 15, 13);
  if (!SD.begin(13) ) {
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization by SPI.begin(14, 2, 15, 13) done.");
  root = SD.open("/");
  printDirectory(root, 0);
  Serial.println("done!");
  Serial.println("SPI Terminating.");

  // Lets try to disable the SD again by stopping the SD & SPI
  SD.end();
  SPI.end();
  
  // Disable the SD by an HIGH CS
  pinMode(13,OUTPUT);   digitalWrite(13,HIGH);
  Serial.print("PIN13 : "); Serial.println( digitalRead(13) ); 

  // This goes wrong !!!! Signal stays HIGH !!!!!!!
  pinMode(2,OUTPUT);   digitalWrite(2,LOW);
  //This means that MISO of the SD-micro is not released
  //Lets make it easier to check with my scope.
  //The output will be pulled down at the moment MISO is released from the SD-micro
  pinMode(2,INPUT_PULLDOWN);  
  Serial.print("PIN2 : "); Serial.println( digitalRead(2) ); 

  // we need a number (>4) of extra clock pulses after CS was disabled before MISO is released !!!!!
  pinMode(14,OUTPUT);   digitalWrite(14,LOW);
  Serial.print("PIN14 : "); Serial.println( digitalRead(14) ); 
  pinMode(14,OUTPUT);   digitalWrite(14,HIGH);
  pinMode(14,OUTPUT);   digitalWrite(14,LOW);
  pinMode(14,OUTPUT);   digitalWrite(14,HIGH);
  pinMode(14,OUTPUT);   digitalWrite(14,LOW);
  pinMode(14,OUTPUT);   digitalWrite(14,HIGH);
  pinMode(14,OUTPUT);   digitalWrite(14,LOW);
  pinMode(14,OUTPUT);   digitalWrite(14,HIGH);
  pinMode(14,INPUT_PULLUP);

  // If the SD and SPI application would run correct, the signal should go LOW !!!!!!!
  // If the SD-micro is not detached correctly, MISO will stay HIGH!!! 
  pinMode(2,OUTPUT);   digitalWrite(2,LOW); //Check if you can set 0. If s0, leave the signal as input
  pinMode(2,INPUT_PULLDOWN);  
  Serial.print("PIN2 : "); Serial.println( digitalRead(2) ); 
  pinMode(15,OUTPUT);   digitalWrite(15,LOW); Serial.print("PIN15 : "); Serial.println( digitalRead(15) ); 

// Easy method to reboot again
// ESP.restart();

}


void printDirectory(File dir, int numTabs) {
  while (true) {

    File entry =  dir.openNextFile();
    if (! entry) {
      // no more files
      Serial.println("Close Print directory");
      break;
    }
    for (uint8_t i = 0; i < numTabs; i++) {
      Serial.print('\t');
    }
    Serial.print(entry.name());
    if (entry.isDirectory()) {
      Serial.println("/");
      printDirectory(entry, numTabs + 1);
    } else {
      // files have sizes, directories do not
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);
    }
    entry.close();
  }
}

Debug Message

Sketch uses 312142 bytes (9%) of program storage space. Maximum is 3145728 bytes.
Global variables use 14060 bytes (4%) of dynamic memory, leaving 313620 bytes for local variables. Maximum is 327680 bytes.
C:\Users\qbox4\AppData\Local\Arduino15\packages\esp32\tools\esptool_py\3.0.0/esptool.exe --chip esp32 --port COM5 --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect 0xe000 C:\Users\qbox4\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.6/tools/partitions/boot_app0.bin 0x1000 C:\Users\qbox4\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.6/tools/sdk/bin/bootloader_qio_80m.bin 0x10000 C:\Users\qbox4\AppData\Local\Temp\arduino_build_354809/ESP32_CAM_SPI_MISO_BUG.ino.bin 0x8000 C:\Users\qbox4\AppData\Local\Temp\arduino_build_354809/ESP32_CAM_SPI_MISO_BUG.ino.partitions.bin 
esptool.py v3.0-dev
Serial port COM5
Connecting........_____....._____....._____....._____....._____....._____.....____An error occurred while uploading the sketch
_

A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet header

Other Steps to Reproduce

Identified the issue at Arduino SD.h. They pointed out that this is for the ESP32 group

I have checked existing issues, online documentation and the Troubleshooting Guide

  • I confirm I have checked existing issues, online documentation and Troubleshooting guide.
@qbox4u qbox4u added the Status: Awaiting triage Issue is waiting for triage label Feb 15, 2022
@qbox4u
Copy link
Author

qbox4u commented Feb 15, 2022

I know, i have seen this work around before, But the issue was and still is that the SPI setting (&SD) should have a simple call/method to disable the used ( and modified) SPI signals (also the SD) and leave the hardware in a state that does not interfere with functions that normally work.
agree?

@me-no-dev
Copy link
Member

When will it switch the pins? You are uploading a firmware and resetting the Chip through the hardware enable pin, so it will check the pin state when entering Download mode and IO2 high will not fly :)

@qbox4u
Copy link
Author

qbox4u commented Feb 15, 2022

basically i stumbled on this issue while checking some other camera functionality. Downloading all kind of other Sketches
was no problem... all kind of stuff worked pretty good.
The hangup of the Sketch download just started after i checked the SD interface implementation.
We Initialized the SD and bingo, no sketch upload suddenly possible. grrrrrrr
Resetting the board , etc did not help until i tried to remove the SD-micro by hand.( the idea was a broken piece _)
The weird thing was that uploads were possible again after i remove the SD and even after refitting it to the board, BUT with another sketch "WITHOUT" usage of SPI &SD
This just means that the SD card is after init in a state that prevents the Sketch upload again..

@qbox4u
Copy link
Author

qbox4u commented Feb 15, 2022

the uploaded sketch in here is just to show the issue..

@me-no-dev
Copy link
Member

When the SD Card is initialized, it itself pulls MISO. It's not the SPI peripheral that is at fault. At the moment that the ESP reboots into download, all GPIOs AFAIK are reset to their default modes, but since the SD Card is still powered, it's still pulling MISO high.

@qbox4u
Copy link
Author

qbox4u commented Feb 15, 2022

Correct, it have a feeling that MISO just shifts out the data and leaves the last bit (LEVEL) at the output
Therefore i was looking for a existing function in the LIB that would leave the pins in a pre-defined user state
performing CD.end & SPI.end , put CS to high, perform a number of dummy clocks is just hacking to compensate
of the lack in available functionality

Perhaps it is therefore a good idea to implement something usefull to set the SD in a pre-defined state in your lLIB

@qbox4u
Copy link
Author

qbox4u commented Feb 15, 2022

typo.. sorry
"SD.end" , SPI.end , put CS to high, perform a number of dummy clocks

@qbox4u
Copy link
Author

qbox4u commented Feb 15, 2022

PS: after ("SD.end" , SPI.end , put CS to high, perform a number of dummy clocks ) the SD MISO pin is tri-state

@me-no-dev
Copy link
Member

this would still not work for you and is only needed if IO2 is used for MISO. The chip does not know that it's about to be reset into download, so there is no way to trigger execution of such SD.end()

@qbox4u
Copy link
Author

qbox4u commented Feb 16, 2022

thanks for your info. I will keep this issue in mind. Just taking care to unmount the SD when not needed anymore

void MountSD_By_SPI() {
  unsigned long time1;
  Serial.println("Initalizing SD card...");
  // The way to start SPI is: SPI.begin(SCK,MISO,MOSI,CS);
  // Start SPI for the ESP32-CAM SD card
  time1 = micros();
  SPI.begin(/*SCK*/14, /*MISO*/2, /*MOSI*/15, /*CS-SS*/13);
  if (!SD.begin(13) ) {
    Serial.println("initialization of the SD-Micro failed!");
    Serial.println("Check the SD-micro on a laptop to assure the SD-micro is OK");
    while (1);
  }
  Serial.printf("Mounting the SD-micro by SPI takes : %ld [uSec]\n", (micros() - time1) );
  Serial.println(F("WARNING: THE ESP32-CAM will not upload new sketches as long an activated SD-micro is mounted "));
  Serial.println(F("         The reason for this error is the MISO pin HIGH level and is preventing  a correct upload "));
  Serial.println(F("         You will see the white LED Flashing and IDE text: Connecting........_____....._____....._____..... "));
  Serial.println(F("         Use Unmount_SD() as in the example or remove the SD-Micro before you try to upload a new Sketch\n"));
}

void Unmount_SD() {
  // Tested with Kingston 32GB
  unsigned long time1;
  time1 = micros();
  Serial.println("Ejecting the ESP32-CAM SD");
  SD.end();
  SPI.end();
  // Set MOSI High
  pinMode(15, INPUT_PULLUP);
  // Disable the SD by an HIGH CS/SS
  pinMode(13, OUTPUT); digitalWrite(13, HIGH);
  // Provide dummy clocks
  pinMode(14, OUTPUT);
  for (int i = 1; i <= 8; i++) {
    digitalWrite(14, LOW);
    digitalWrite(14, HIGH);
  }
  // Set CLK High
  pinMode(14, INPUT_PULLUP);
  // Check if MISO became "LOW"
  pinMode(2, INPUT_PULLDOWN);
  delay(1);
  if (digitalRead(2)) {
    Serial.println(F("Failed to unmount the SD"));
  } else
  { Serial.printf("Unmounting of the SD-micro card takes : %ld [uSec]\n", (micros() - time1) );
    Serial.println(F("SD has been unmounted safely"));
  }
}

@oleksii-pi
Copy link

Thanks for sharing it, @qbox4u . Solution with Unmount_SD works with me. But I think it should be part of the library, or SD.end() should clean up all the resources so this issue is not happening anymore.

@IvanAdiego
Copy link

I was able to circumvent this problem by using a PNP transistor. Instead of connecting the MISO pin of the SD card reader directly to the ESP32, I connected it to the emitter of the transistor. Then I connected the MISO pin of the ESP32 to the collector and the CS pin to the base via a 10K Ohm resistor. This way, when CS goes low it switches on the transistor allowing the communication on the MISO line, but when the communication is over and the CS returns to high, the transistor turns off, interrupting the connection in the MISO line.
I did this to be able to use the HSPI bus, because the MISO remaining high on pin 12 was giving me a lot of problems, since it is a strapping pin on my NodeMCU board. I tested this on a generic micro sd card module and also on a modified SD card adapter, both of whom have this problem of leaving the MISO pin high after the micro SD is initialized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Awaiting triage Issue is waiting for triage
Projects
None yet
Development

No branches or pull requests

4 participants