SD Card Support For M5Stack Morse Tutor

Some Morse Tutor projects are adding SD card support (the text file on the SD Card is sent via Morse Code).

I went to gutenberg.org and loaded a large Sherlock Holmes book onto a Samsung 32 GB EVO Plus MicroSDHC UHS-1 Card. Formatted with PC/MAC app SDFORMATTER for Type1 SDHC FAT32

Inserted the card into the M5Stack SD card socket and had no trouble reading/writing it with this code:

#include <M5Stack.h>
 
//Micro SD / TF Card Test
/*
 M5Stack initializing...OK
Listing directory: /
  DIR : /.Spotlight-V100
  DIR : /.fseventsd
  FILE: /SherlockHolmes.txt  SIZE: 590492
  FILE: /._SherlockHolmes.txt  SIZE: 4096
  FILE: /hello.txt  SIZE: 11
Writing file: /hello.txt
File written
Reading file: /hello.txt
Read from file: Hello world from M5Stack !!
 */


void listDir(fs::FS &fs, const char * dirname, uint8_t levels){

    // Print blank line on screen
    M5.Lcd.printf(" \n  ");
    M5.Lcd.printf(" \n  ");
    
    Serial.printf("Listing directory: %s\n", dirname);
    M5.Lcd.printf("Listing directory: %s\n", dirname);

    File root = fs.open(dirname);
    if(!root){
        Serial.println("Failed to open directory");
        M5.Lcd.println("Failed to open directory");
        return;
    }
    if(!root.isDirectory()){
        Serial.println("Not a directory");
        M5.Lcd.println("Not a directory");
        return;
    }

    File file = root.openNextFile();
    while(file){
        if(file.isDirectory()){
            Serial.print("  DIR : ");
            M5.Lcd.print("  DIR : ");
            Serial.println(file.name());
            M5.Lcd.println(file.name());
            if(levels){
                listDir(fs, file.name(), levels -1);
            }
        } else {
            Serial.print("  FILE: ");
            M5.Lcd.print("  FILE: ");
            Serial.print(file.name());
            M5.Lcd.print(file.name());
            Serial.print("  SIZE: ");
            M5.Lcd.print("  SIZE: ");
            Serial.println(file.size());
            M5.Lcd.println(file.size());
        }
        file = root.openNextFile();
    }
}

void readFile(fs::FS &fs, const char * path) {
    Serial.printf("Reading file: %s\n", path);
    M5.Lcd.printf("Reading file: %s\n", path);

    File file = fs.open(path);
    if(!file){
        Serial.println("Failed to open file for reading");
        M5.Lcd.println("Failed to open file for reading");
        return;
    }

    Serial.print("Read from file: ");
    M5.Lcd.print("Read from file: ");
    while(file.available()){
        int ch = file.read();
        Serial.write(ch);
        M5.Lcd.write(ch);
    }
}

void writeFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Writing file: %s\n", path);
    M5.Lcd.printf("Writing file: %s\n", path);

    File file = fs.open(path, FILE_WRITE);
    if(!file){
        Serial.println("Failed to open file for writing");
        M5.Lcd.println("Failed to open file for writing");
        return;
    }
    if(file.print(message)){
        Serial.println("File written");
        M5.Lcd.println("File written");
    } else {
        Serial.println("Write failed");
        M5.Lcd.println("Write failed");
    }
}

// the setup routine runs once when M5Stack starts up

void setup() { 
 
    // initialize the M5Stack object
    M5.begin();

    //M5.startupLogo();
    Wire.begin();

    // Lcd display
    M5.Lcd.setBrightness(100);
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(0, 10);
    M5.Lcd.setTextColor(WHITE);
    M5.Lcd.setTextSize(1);

    // Page Header
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(0, 05);
    M5.Lcd.printf("           Testing Micro SD Card Functions:\r\n");
    // digitalWrite(TFT_CS, 1);
 
    // Print blank line on screen
    M5.Lcd.printf(" \n    ");
    M5.Lcd.printf(" \n    ");
    
    listDir(SD, "/", 0);
    M5.Lcd.printf("");
    M5.Lcd.printf("");

    // Print blank line on screen
    M5.Lcd.printf(" \n  ");
    M5.Lcd.printf(" \n  ");
    
    writeFile(SD, "/hello.txt", "Hello world from M5Stack !!");
    M5.Lcd.printf("");
    M5.Lcd.printf("");

    // Print blank line on screen
    M5.Lcd.printf(" \n  ");
    M5.Lcd.printf(" \n  ");

    // Print blank line on screen
    M5.Lcd.printf(" \n  ");
    M5.Lcd.printf(" \n  ");
    
    readFile(SD, "/hello.txt");
}

void loop() {

  // put your main code here, to run repeatedly:
    M5.update();
}

A lot wasn’t known but they went anyway

Armstrong and Aldrin did their own test. Just a moment after he became the first human being to step onto the Moon, Armstrong had scooped a bit of lunar dirt into a sample bag and put it in a pocket of his spacesuit—a contingency sample, in the event the astronauts had to leave suddenly without collecting rocks. Back inside the lunar module the duo opened the bag and spread the lunar soil on top of the ascent engine. As they repressurized the cabin, they watched to see if the dirt started to smolder. “If it did, we’d stop pressurization, open the hatch and toss it out,” Aldrin explained. “But nothing happened.”

Watch App Design

Winning strategies for wearables. If you’re considering a move into wearables, here are a few recommendations:

  • Tech fit for the real world – Consider how to best marry amazing technical capabilities with people’s real-world habits and desires.
  • Understand needs – Ensure you know who you’re designing the wearables for, and what their needs and desires are.
  • Sharp focus – Prioritize ruthlessly around your core service and its core benefit, your wearable device will inevitably die if it contracts featuritis.
  • Segment functionality – As wearables should be small, carefully consider what’s best done and seen on the device itself, and what can be “delegated” to a smartphone.
  • Make the experience desirable and stylish – As Bill Geiser of MetaWatch says, “we carry devices but we wear fashion.”
  • Know the data core – Obsess about the hardware, but obsess even more about the smart data service enabled by the technology. The gadget is merely the keyhole into a kingdom of interconnected data.
  • Data fluidity – Allow users’ data to move fluidly between devices and be freely exported and aggregated, as users will get more benefit from solutions that can be compared and mashed up.
  • Design for the glance – Ensure a simple glance at the device can provide information and value.
  • Have fun inventing – The category needs leadership. Follow the advice of legendary inventor and designer Buckminster Fuller, who said “The best way to predict the future is to design it.”
  • _________________________________
  • Elegance and simplicity are core attributes of the quarter-sized activity tracker Misfit Shine, yet it can be used flexibly for several different activities and situations.
  • Scales by Withings piggyback cleverly on an existing daily ritual – weighing oneself – rather than trying to introduce completely new digital rituals.
  • The Fjord-designed smartphone app Macaw aggregates data from several sources in one experience, allowing users to get an overview of various things they track.
  • Category pioneers FitBit have long been known for reliable and accurate data, which is a foundation for trust.
  • Excellent design drivers around glanceability and “hands freedom” guide smart watch pioneers MetaWatch.

ESP32 Sample Service

        // SampleBuffferInitI2S
	// 
	// Do the initial setup required if we're going to capture the audio with I2S

	//i2s number
	#define EXAMPLE_I2S_NUM           (I2S_NUM_0)
	//i2s sample rate
	#define EXAMPLE_I2S_SAMPLE_BITS   (I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB)
	//enable display buffer for debug
	#define EXAMPLE_I2S_BUF_DEBUG     (0)
	//I2S read buffer length
	#define EXAMPLE_I2S_READ_LEN      (MAX_SAMPLES)
	//I2S built-in ADC unit
	#define I2S_ADC_UNIT              ADC_UNIT_1
	//I2S built-in ADC channel
	#define I2S_ADC_CHANNEL           ADC1_CHANNEL_0

	//flash record size, for recording 5 second
	void SampleBufferInitI2S()
	{
		i2s_config_t i2s_config;
		i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN);
    	        i2s_config.sample_rate = SAMPLING_FREQUENCY;            
    	        i2s_config.dma_buf_len = MAX_SAMPLES;                   
    	        i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT;
    	        i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;  
    	        i2s_config.use_apll = false,
    	        i2s_config.communication_format = I2S_COMM_FORMAT_I2S_MSB;
    	        i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1;
    	        i2s_config.dma_buf_count = 2;

		//install and start i2s driver

		ESP_ERROR_CHECK( adc_gpio_init(ADC_UNIT_1, ADC_CHANNEL_0) );
		ESP_ERROR_CHECK( i2s_driver_install(EXAMPLE_I2S_NUM, &i2s_config,  0, NULL) );
		ESP_ERROR_CHECK( i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL) );		

	}


	void FillBufferI2S()
	{
		uint16_t byteBuffer[MAX_SAMPLES];

		if (IsBufferFull())
		{
			Serial.printf("BUG: FillBUfferI2S found buffer already full.");
			return;
		}
		size_t bytesRead = 0;
    	        ESP_ERROR_CHECK( i2s_adc_enable(EXAMPLE_I2S_NUM) );
                ESP_ERROR_CHECK( i2s_read(EXAMPLE_I2S_NUM, (void*) byteBuffer, sizeof(byteBuffer), &bytesRead, portMAX_DELAY) );
	        ESP_ERROR_CHECK( i2s_adc_disable(EXAMPLE_I2S_NUM) );

		_cSamples = _MaxSamples;
		if (bytesRead != sizeof(byteBuffer))
		{
			Serial.printf("Could only read %u byttes of %u in FillBufferI2S()\n", bytesRead, sizeof(byteBuffer));
			return;
		}

		for (int i = 0; i < ARRAYSIZE(byteBuffer); i++)
		{
			_vReal[i] = byteBuffer[i];
		}
		Serial.println("");

		//for (int i = 0; i < 128; i++)
		//	Serial.printf("%d, %f\n", i, _vReal[i]);

		_cSamples = MAX_SAMPLES;

	}

Need to try this (from Reddit): ADC configured as an I2S peripheral that writes directly to system memory (DMA). https://www.reddit.com/r/esp32/comments/bid08m/finally_got_audio_sampling_via_dma_with_no_cpu/