Before getting into the excitement of the details, the other major half of the fun of building these projects is naming them, so I'm calling this interface the 'MyTeSed', standing for My Model 'T' Serial Drive. Now that out of the way, on with where I'm up to with the project to date.
Testing the functionality of the MyTeSed on a Breadboard |
The Hardware
Micro SD Card Reader Module |
After a little experimentation I've also decided to set up software serial on pins D8 and D9 of the Nano. Using software serial as the conduit to the T100 / T102 / T200 leaves the Nanos' standard serial pins D1 and D2 free to use as a pass-through (actually via the Nanos' USB) which will be used as direct connections to a PC. As in the previous version (blog entry Using SD Cards with the TRS-80 Model 100) a MAX323 IC handles the RS232 to TTL serial conversion.
The when complete the MyTeSeD can be powered from a 6v to 9v battery supply (like most Arduino projects), in addition usage of the Nano conveniently provides a means to power the interface via the mini USB port, allowing battery packs or a USB power source etc.
MyTeSed Circuit Diagram |
Arduino Code
In essence the current idea is for MyTeSed to listen for instructions in plain text over the serial line, 'LOAD' and 'SAVE' for example, then either send data back or shift files around on the SD card. Regardless there is a lot left to implement, such as file deletion, access to directories and error checking.
// **************************************************************************
// **** MyTeSed: T100 SD Card Reader for Arduino Nano and SD Card Reader ****
// **************************************************************************
// ** David Stephenson 2017-08-23 **
// ** Version 0.01 **
// **********************************
#include <SPI.h>
#include <SD.h>
#include <SoftwareSerial.h>
enum SERIAL_MODE {FREE, COMMAND, DATA_IN, DATA_OUT, FILES_OUT};
const byte LF=10;
const byte CR=13;
const byte EF=26;
SoftwareSerial mySerial(12, 13); // RX, TX
class CardReader {
private:
SERIAL_MODE eSerialMode = FREE;
//DRIVE_COMMAND eDriveMode = NONE;
File MyFile;
String sFileName;
String sInString;
unsigned long TimeLastUpdate = 0;
// Class Private Functions
void LoadBas() {
char cRead, cLast;
if(eSerialMode == DATA_OUT){
MyFile = SD.open(sFileName);
if(MyFile){
while(MyFile.available()){
cRead=MyFile.read();
if (cRead== LF){
if (cLast != CR){
mySerial.write(CR);
}
mySerial.write(LF);
} else {
mySerial.write(cRead);
cLast = cRead;
}
}
}
MyFile.close();
mySerial.write(EF);
eSerialMode = FREE;
}
}
void SaveBas(char cInChar) {
if(eSerialMode == DATA_IN){
if(cInChar != EF){
MyFile.print(cInChar);
} else {
MyFile.close();
eSerialMode = FREE;
}
}
}
void FilesOut() {
}
void commandIn(){
String KEYWORDS[7] = {"LOAD","SAVE","KILL","FILES","MOVE","CP2SD","CP2R"};
String sInSubString;
sInString.trim();
Serial.print(sInString);
if (sInString.length() >= 3){
for (byte bKeyword = 0 ; bKeyword < 8 ; bKeyword++){
sInSubString = sInString.substring(0,KEYWORDS[bKeyword].length() );
sInSubString.toUpperCase();
if (sInSubString.indexOf(KEYWORDS[bKeyword])!=-1){
if (KEYWORDS[bKeyword] == "LOAD") {
sFileName = sInString.substring(4);
sFileName.trim();
delay(500);
eSerialMode = DATA_OUT;
LoadBas();
}
else if (KEYWORDS[bKeyword] == "SAVE" || KEYWORDS[bKeyword] == "CP2D") {
sFileName = sInString.substring(4);
sFileName.trim();
if (SD.exists(sFileName)) {
SD.remove(sFileName);
}
MyFile = SD.open(sFileName, FILE_WRITE);
eSerialMode = DATA_IN;
}
else if (KEYWORDS[bKeyword] == "FILES") {
eSerialMode = FILES_OUT;
FilesOut();
}
}
}
}
//sInCommand = false;
sInString = "";
}
public:
void SerialIn(char cInChar){
switch(eSerialMode) {
case FREE:
if(cInChar == CR){
eSerialMode = COMMAND;
commandIn();
} else {
sInString += cInChar;
}
break;
case DATA_IN:
SaveBas(cInChar);
break;
}
}
};
CardReader MyCard;
void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(1200);
//57I1D
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
// set the data rate for the SoftwareSerial port
mySerial.begin(1200);
if(!SD.begin(10)){
mySerial.println("fail");
return;
} else {
mySerial.println("mango");
Serial.println("mangos");
}
}
void loop()
{
char cInChar;
if (mySerial.available()) {
cInChar = (char)mySerial.read();
//sInString += cInChar;
MyCard.SerialIn(cInChar);
Serial.write(cInChar);
}
}
Talking With a Model 100
For example, saving the active BASIC file:
OPEN "COM:57I1D" FOR OUTPUT AS 1: PRINT #1,"SAVE MYFILE.BA": CLOSE 1: SAVE "COM:57I1D"
Or loading a BASIC file from the Interface:
OPEN "COM:57I1D" FOR OUTPUT AS 1: PRINT #1,"LOAD MYFILE.BA": CLOSE 1: LOAD "COM:57I1D"
Possibly the easiest method to save or load BASIC program is to add some of the below lines to a program listings. Alternatively an option once the code base is a little more on the stable side would be the development of a menu-ing system, but that's for latter.
Save a BASIC file to SD Card
900 OPEN "COM:57I1D" FOR OUTPUT AS 1
910 PRINT #1,"save testme.ba"
920 CLOSE 1
930 SAVE "COM:57I1D"
Load BASIC file to SD Card
10 OPEN "COM:57I1D" FOR OUTPUT AS 1
20 PRINT #1,"load testme.ba"
30 CLOSE 1
40 LOAD "COM:57I1D"
Copy a RAM Text File to the SD Card
10 MAXFILES=2
20 OPEN "RAM:TXT2RD.DO" FOR INPUT AS 1
30 OPEN "COM:57I1D" FOR OUTPUT AS 2
40 PRINT #2, "SAVE TXTFIL.DO"
50 LINE INPUT #1, Z$
60 PRINT #2,Z$
70 IF EOF(1) THEN GOTO 90
80 GOTO 50
90 PRINT #2, CHR$(26)
100 CLOSE 1: CLOSE 2
Copy a File from the SD Card to a RAM Text File
10 MAXFILES=2
20 OPEN "RAM:TXT2RD.DO" FOR OUTPUT AS 1
30 OPEN "COM:57I1D" FOR OUTPUT AS 2
40 PRINT #2, "LOAD TXTFIL.DO"
50 LINE INPUT #2, Z$
60 PRINT #1,Z$
70 IF EOF(1) THEN GOTO 90
80 GOTO 50
90 PRINT #1, CHR$(26)
100 CLOSE 1: CLOSE 2
Next Time
The next stage of proceedings is to design a circuit board, should be a simple enough task. Unlike all the other projects in this blog I'm planning on getting the PCB produced by a fabrication house, and hopefully all that will go well.