Skip to content

Commit

Permalink
Examples: SDSentFilesToPC: first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
chipaudette committed Oct 15, 2024
1 parent d146934 commit 77b5ed2
Show file tree
Hide file tree
Showing 3 changed files with 380 additions and 0 deletions.
110 changes: 110 additions & 0 deletions examples/02-Utility/SD_Card/SDSendFilesToPC/SDSendFilesToPC.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
SDSendFilesToPC
Created: Chip Audette, OpenHearing, Oct 2024
Purpose: Transfer files from the SD to your PC via the Serial link. To receive the files,
you will want to use some sort of program (Python script included here) to receive the
data from the Tympan and write it to your PC's disk. The Arduino Serial Monitor will
not help you save the files. Sorry.
HOW TO USE: Obviously, you need a Tympan and you need to put an SD card in your Tympan.
Since this example will be transfering files off the SD card back to your PC, your
SD card needs some example files on it. So, with the Tympan plugged into the PC
via USB and turned on, you can use your serial communicaiton program to:
1) Send 'L' to list the files on the SD card
2) Send 'o' to ask to open a file
3) When prompted, send the filename you want (ending with a newline character)
4) Send 't' to transfer all the data bytes of the file over serial to the PC
In many cases, it is easier for your serial communication program to receive the
file if it knows how many bytes are in the file. So, in that case, youu would
insert the following step:
3.5) Send 's' to ask for the size of the file in bytes
The file will be automatically closed when the data has all been transferred.
If you cannot connect to the Tympan via the included Python script, be sure that
you have closed the Arduino Serial Monitor. Only one program can use your PC's
serial link to the Tympan. If the Arduino Serial Monitor is open, it will
block any other program's attempts to open a connection to the Tympan!
For Tympan Rev D, program in Arduino IDE as a Teensy 3.6.
For Tympan Rev E, program in Arduino IDE as a Teensy 4.1.
For Tympan Rev F, program in Arduino IDE as a Teensy 4.1.
MIT License. use at your own risk.
*/

// Include all the of the needed libraries
#include <Tympan_Library.h>
#include "SdFat.h" // included as part of the Teensy installation
#include "SDtoSerial.h" //This will be part of the Tympan library
#include "SerialManager.h"

// Create the entities that we need
Tympan myTympan(TympanRev::F); //use TympanRev::D or E or F
SdFs sd; //This is the SD card. SdFs is part of the Teensy install
SDtoSerial SD_to_serial(&sd, &Serial); //transfers raw bytes of files on the sd over to Serial (part of Tympan Library)
SerialManager serialManager; //create the serial manager for real-time control (via USB)


// ///////////////////////// Main setup() and loop() as required for all Arduino programs

// define the setup() function, the function that is called once when the device is booting
void setup() {
//myTympan.beginBothSerial(); //only needed if using bluetooth
delay(500); while ((millis() < 2000) && !Serial) delay(10); //stall a bit to see if Serial is connected
Serial.println("SDSendFilesToPC: setup():...");

//enable the Tympan
myTympan.enable();

//Set the state of the LEDs (just to see that the Tympan is alive)
myTympan.setRedLED(HIGH); myTympan.setAmberLED(LOW);

//End of setup
Serial.println("Setup: complete.");
serialManager.printHelp();

} //end setup()

// define the loop() function, the function that is repeated over and over for the life of the device
void loop() {

//respond to Serial commands
if (Serial.available()) serialManager.respondToByte((char)Serial.read()); //USB Serial

//service the LEDs...blink slow normally, blink fast if recording
myTympan.serviceLEDs(millis(),LOW); //update blinking at LOW speed

} //end loop()

// ///////////////////////////////////////////////////////////////////////////////////////////

// This is only needed to create a dummy file on the SD card so that you have
// something available to download off the SD card
void createDummyFileOnSD(void) {
String fname = "dummy.txt";
Serial.println("SerialManager: creating dummy text file " + fname);

//begin the SD card support
if (!(sd.begin(SdioConfig(FIFO_SDIO)))) { Serial.println("SerialManager: cannot open SD."); return; }

//open a file
FsFile file;
file.open(fname.c_str(),O_RDWR | O_CREAT | O_TRUNC); //open for writing
if (!file.isOpen()) { Serial.println("SerialManager: cannot open file for writing: " + fname); return; }

//write some text to the file
unsigned int total_bytes = 0;
total_bytes += file.println("abcdefghijklmnopqrstuvwxyz");
total_bytes += file.println("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
total_bytes += file.println("0123456789!@#$%^&*()");
total_bytes += file.println("Have a great day!");

//close the file
file.close();
Serial.println("SerialManager: wrote " + String(total_bytes) + " bytes to " + fname);
}
96 changes: 96 additions & 0 deletions examples/02-Utility/SD_Card/SDSendFilesToPC/SerialManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@

#ifndef _SerialManager_h
#define _SerialManager_h

#include <Tympan_Library.h>

//Extern variables from the main *.ino file
extern Tympan myTympan;
extern SDtoSerial SD_to_serial;
extern void createDummyFileOnSD(void);

class SerialManager {
public:
SerialManager() {};

void printHelp(void);
bool respondToByte(char c); //this is called automatically by SerialManagerBase.respondToByte(char c)
int receiveFilename(String &filename,const unsigned long timeout_millis);
};

void SerialManager::printHelp(void) {
Serial.println("SerialManager Help: Available Commands:");
Serial.println(" General: No Prefix");
Serial.println(" h : Print this help");
Serial.println(" d : Create a dummy text file on the SD");
Serial.println("SerialManager: Typical file transfer procedure:");
Serial.println(" L : First, Get a list of the files on SD");
Serial.println(" f : Second, Open the file from SD (will prompt you for filename)");
Serial.println(" b : Third, Get the size of the file in bytes");
Serial.println(" t : Fourth, Transfer the whole file from SD to Serial");

Serial.println();
}

//switch yard to determine the desired action
bool SerialManager::respondToByte(char c) {
bool ret_val = true; //assume at first that we will find a match
switch (c) {
case 'h':
printHelp();
break;
case 'd':
createDummyFileOnSD(); //send a pointer to the SD system
break;
case 'L':
Serial.print("SerialMonitor: Listing Files on SD:"); //don't include end-of-line
SD_to_serial.sendFilenames(','); //send file names seperated by commas
break;
case 'f':
{
Serial.println("SerialMonitor: Opening file: Send filename (ending with newline character) within 10 seconds");
String fname; receiveFilename(fname, 10000); //wait 10 seconds
if (SD_to_serial.open(fname)) {
Serial.println("SerialMonitor: " + fname + " successfully opened");
} else {
Serial.println("SerialMonitor: *** ERROR ***: " + fname + " could not be opened");
}
}
break;
case 'b':
if (SD_to_serial.isFileOpen()) {
SD_to_serial.sendFileSize();
} else {
Serial.println("SerialMonitor: *** ERROR ***: Cannot get file size because no file is open");
}
break;
case 't':
if (SD_to_serial.isFileOpen()) {
SD_to_serial.sendFile();
Serial.println();
} else {
Serial.println("SerialMonitor: *** ERROR ***: Cannot send file because no file is open");
}
break;
default:
//received unknown command. Ignore if carrige return or newline. Otherwise, notify the user.
if ( (c != '\r') && (c != '\n') ) Serial.println("SerialMonitor: command " + String(c) + " not known");
break;
}
return ret_val;
}

int SerialManager::receiveFilename(String &filename,const unsigned long timeout_millis) {
Serial.setTimeout(timeout_millis); //set timeout in milliseconds
filename.remove(0); //clear the string
filename += Serial.readStringUntil('\n'); //append the string
if (filename.length() == 0) filename += Serial.readStringUntil('\n'); //append the string
Serial.setTimeout(1000); //return the timeout to the default
return 0;
}





#endif
174 changes: 174 additions & 0 deletions examples/02-Utility/SD_Card/SDSendFilesToPC/getFileFromTympan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#
# getFileFromTympan.py
#
# Chip Audette, OpenAudio, Oct 2024
# MIT License
#
# This script communicates with the Tympan over the USB Serial link.
# The purpose is to get the audio data from a WAV file on the Tympan's
# SD card.
#
# I'm using this as a Serial communication example:
# https://projecthub.arduino.cc/ansh2919/serial-communication-between-python-and-arduino-663756
#

import serial #pip install pyserial
import time
import codecs
import numpy as np


# ##################### Functions

# send text over the serial link with an EOL character in the format that is expected for Tympan
def sendTextToSerial(serial_to_tympan, text, EOL='\n'):
serial_to_tympan.write(bytes(text + EOL, 'utf-8')) #send the text
time.sleep(0.05) #give some time for the device to respond

# receive a line of text from the serial link (must end in newline)
def readLineFromSerial(serial_to_tympan):
return codecs.decode(serial_to_tympan.readline(),encoding='utf-8')

# keeping receiving lines of text until enough time has passed that we exceed the wait period
def readMultipleLinesFromSerial(serial_to_tympan, wait_period_sec=0.5):
all_lines = readLineFromSerial(serial_to_tympan)
last_reply_time = time.time()
while (time.time() < (last_reply_time + wait_period_sec)):
new_readline = readLineFromSerial(serial_to_tympan)
all_lines += new_readline
if len(new_readline) > 0:
last_reply_time = time.time()
return all_lines

# receive raw btes from the serial until we have receive the number of bytes
# specified or until the serial link times out
def readBytesFromSerial(serial_to_tympan, n_bytes_to_receive, blocksize=1024): # blocksize specifies how many bytes to try to read from the serial port at a time
all_data = bytearray() #initialize empty
bytesleft = n_bytes_to_receive
while (bytesleft > 0):
bytes_to_read= min(blocksize, bytesleft)
raw_bytes = serial_to_tympan.read(bytes_to_read) #this will timeout (if needed) according to the serial port timeout parameter
if (len(raw_bytes) == bytes_to_read):
# we read the whole block. Great.
bytesleft = bytesleft - bytes_to_read
else:
# We got too few bytes. Assume no more bytes are coming
print("getRawReply: recieved " + str(len(raw_bytes)) + " but expected " + str(bytes_to_read))
bytesleft = 0 #assume no more bytes are coming. this will break out of the while loop
#
all_data += raw_bytes
#
return all_data

# given a comman-delimited string of file names, parse out the file names
# and return as a list of strings
def processLineIntoFilenames(line):
# Assumes we are given one line that contains comma-seperated filenames.
# The line of text might contain preamble text, which will end with a colon.
# So, find the last colon and only keep the text after the last colon.
line = line.split(':') #split into sections
line = line[-1] #get everything after the last split
#
#Now, split the text at the colons
names = line.split(',')
all_fnames = []
for name in names:
name = name.strip()
if len(name) > 0:
all_fnames.append(name.strip()) #remove whitespace before and after and save
#
#
return all_fnames


# ####################################################################
# ######################## Here is the Main ##########################
# ####################################################################



#specify the COM port for your Tympan...every one is different!
my_com_port = 'COM9' #Look in the Arduino IDE!



# ################ Let's set up the serial communication to the Tympan

#release the comm port from any previous runs of this Python script
if 'serial_to_tympan' in locals():
serial_to_tympan.close()

# create a serial instance for communicating to your Tympan
print("Opening serial port...make sure the Serial Monitor is closed in Arduino IDE")
wait_period_sec = 0.5 #how long before serial comms time out (could set this faster, if you want)
serial_to_tympan = serial.Serial(port=my_com_port, baudrate=115200, timeout=wait_period_sec) #baudrate doesn't matter for Tympan

# let's test the connection by asking for the help menu
sendTextToSerial(serial_to_tympan, 'h') #send the command to the Tympan
reply = readMultipleLinesFromSerial(serial_to_tympan) #get the mutli-line reply from the Tympan
print("REPLY:",reply) #print the lines to the screen here in Python



# ############# If you don't have any files on the SD, create one!
if 1: #set this to zero to skip this step
sendTextToSerial(serial_to_tympan, 'd') #send the command to the Tympan
reply = readMultipleLinesFromSerial(serial_to_tympan) #get the one-line reply from the Tympan
print("REPLY:",reply) #print the line to the screen here in Python



# ############# Now, let's follow the typical download procedure

# First, let's ask for a list of files on the Tympan's SD card
sendTextToSerial(serial_to_tympan, 'L') #send the command to the Tympan
reply = readLineFromSerial(serial_to_tympan) #get the one-line reply from the Tympan
print("REPLY:",reply.strip()) #print the line to the screen here in Python

# let's break up the full text reply into the filenames
fnames = processLineIntoFilenames(reply) #parse out the filenames
print("RESULT: Files on Tympan SD:", fnames) #print the line to the screen here in Python

# choose the file that you want to download to the PC
if len(fnames) > 0:
fname = fnames[-1] #load the last (ie, the most recent?)
print("ACTION: Asking for file:",fname)
else:
print("ERROR: no filenames were received from the Tympan!")

# Second, start to open the file on the Tympan
sendTextToSerial(serial_to_tympan, 'f') #send the command to the Tympan
reply = readLineFromSerial(serial_to_tympan) #get the one-line reply from the Tympan
print("REPLY:",reply.strip()) #print the line to the screen here in Python

# send the filename that we want
sendTextToSerial(serial_to_tympan, fname) #send the command to the Tympan
reply = readLineFromSerial(serial_to_tympan) #get the one-line reply from the Tympan
print("REPLY:",reply.strip()) #print the line to the screen here in Python

# Third, get the file size in bytes
sendTextToSerial(serial_to_tympan, 'b') #send the command to the Tympan
reply = readLineFromSerial(serial_to_tympan) #get the one-line reply from the Tympan
print("REPLY:",reply.strip()) #print the line to the screen here in Python
n_bytes = int(reply) #interpret the value as an integer
print("RESULT: value=",n_bytes) #print the value to the screen here in Python

# Fourth, transfer the file itself
sendTextToSerial(serial_to_tympan, 't') #send the command to the Tympan
reply = readBytesFromSerial(serial_to_tympan,n_bytes) #get the one-line reply from the Tympan
print("REPLY:",reply) #print the bytes to the screen here in Pyton



# ############################# Finally, let's finish up

# save to local file
print("ACTION: Writing bytes to file here on PC:", fname)
with open(fname,'wb') as file:
file.write(reply)

# close the serial port
print("ACTION: Closing serial port...")
serial_to_tympan.close()


0 comments on commit 77b5ed2

Please sign in to comment.