-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Examples: SDSentFilesToPC: first commit
- Loading branch information
1 parent
d146934
commit 77b5ed2
Showing
3 changed files
with
380 additions
and
0 deletions.
There are no files selected for viewing
110 changes: 110 additions & 0 deletions
110
examples/02-Utility/SD_Card/SDSendFilesToPC/SDSendFilesToPC.ino
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
96
examples/02-Utility/SD_Card/SDSendFilesToPC/SerialManager.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
174
examples/02-Utility/SD_Card/SDSendFilesToPC/getFileFromTympan.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
|
||
|