Skip to content

Commit 2d35471

Browse files
committedJan 8, 2023
Added support for more boards
1 parent cc457e8 commit 2d35471

7 files changed

+775
-689
lines changed
 

‎DEVELOPERS.md

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#### Interfacing with MemCARDuino:
2+
3+
|Commands|Data|Description|
4+
| -- | -- | -- |
5+
|GETID | 0xA0 | Get identifier
6+
|GETVER | 0xA1 | Get firmware version
7+
|MCREAD | 0xA2 | Memory Card Read (frame)
8+
|MCWRITE | 0xA3 | Memory Card Write (frame)
9+
10+
|Responses|Data|Description|
11+
| -- | -- | -- |
12+
|ERROR | 0xE0 | Invalid command received (error)
13+
|GOOD | 0x47 | Operation successful
14+
|BAD_CHECKSUM | 0x4E | Data corruption occured
15+
|BAD_SECTOR | 0xFF | Outside of max sector range
16+
17+
Communication is done at **115200 bps**.
18+
To check if MemCARDuino is connected to the selected COM port send **GETID** command.
19+
Device should respond with **IDENTIFIER**.
20+
Optionally you can send a **GETVER** to get the version of the firmware.
21+
22+
To Read a 128byte frame send a **MCR** command with *MSB* byte and *LSB* byte of the frame you want to read.
23+
MemCARDduino will respond with 128 byte frame data, [MSB xor LSB xor Data] and Memory Card status byte.
24+
25+
To Write a frame send a **MCW** command with *MSB* byte and *LSB* byte, 128 byte data and [MSB xor LSB xor Data].
26+
MemCARDduino will respond with Memory Card status byte.
27+
28+
### Checking if Memory Card is connected:
29+
Read a frame from the card and verify the returned status byte.
30+
If it's 0x47 then card is connected. If it's 0xFF card is not connected.

‎Images/memcarduino.jpg

178 KB
Loading

‎MemCARDuino.ino

+164-87
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,179 @@
11
/*
22
MemCARDuino - Arduino PlayStation 1 Memory Card reader
3-
Shendo 2013. - 2016.
4-
5-
Compatible with Arduino/Genuino Uno, Duemilanove, Diecimila, Nano, Mini,
6-
basically any board with ATmega168/P or ATmega328/P.
7-
8-
9-
THIS IS THE FIXED COMMIT FOR 328/P variants of arduino (Like Uno, Nano v3)
10-
The latest commit made by krzys-h broke the compatibility with the 328/P mcu
3+
Shendo 2013. - 2023.
4+
5+
Compatible with:
6+
* Arduino Uno, Duemilanove, Diecimila, Nano, Mini, Fio (ATmega168/P or ATmega328/P).
7+
* Arduino Leonardo, Micro (ATmega32U4)
8+
* Arduino Mega 2560
9+
* Espressif ESP8266, ESP32
10+
* Raspberry Pi Pico
1111
*/
1212

13-
1413
#include "Arduino.h"
14+
#include <SPI.h>
1515

1616
//Device Firmware identifier
17-
#define IDENTIFIER "MCDINO" //MemCARDuino
18-
#define VERSION 0x05 //Firmware version byte (Major.Minor). Same as the 0x04 version made by Shendo
17+
#define IDENTIFIER "MCDINO" //MemCARDuino
18+
#define VERSION 0x06 //Firmware version byte (Major.Minor).
1919

2020
//Commands
21-
#define GETID 0xA0 //Get identifier
22-
#define GETVER 0xA1 //Get firmware version
23-
#define MCREAD 0xA2 //Memory Card Read (frame)
24-
#define MCWRITE 0xA3 //Memory Card Write (frame)
21+
#define GETID 0xA0 //Get identifier
22+
#define GETVER 0xA1 //Get firmware version
23+
#define MCREAD 0xA2 //Memory Card Read (frame)
24+
#define MCWRITE 0xA3 //Memory Card Write (frame)
2525

2626
//Responses
27-
#define ERROR 0xE0 //Invalid command received (error)
27+
#define ERROR 0xE0 //Invalid command received (error)
2828

2929
//Memory Card Responses
3030
//0x47 - Good
3131
//0x4E - BadChecksum
3232
//0xFF - BadSector
3333

34-
//Define pins
35-
#define DataPin 12 //Data
36-
#define CmdPin 11 //Command
37-
#define AttPin 10 //Attention (Select)
38-
#define ClockPin 13 //Clock
39-
#define AckPin 2 //Acknowledge
34+
#ifndef ICACHE_RAM_ATTR
35+
#define ICACHE_RAM_ATTR
36+
#endif
37+
38+
//Define pins for each known platform
39+
#ifdef ESP8266
40+
#define DataPin 12 //MISO aka Data
41+
#define AttPin 15 //Attention (SS)
42+
#define AckPin 2 //Acknowledge
43+
#elif defined (ESP32)
44+
#define DataPin 19
45+
#define CmndPin 23
46+
#define AttPin 5
47+
#define ClockPin 18
48+
#define AckPin 22
49+
#elif defined (ARDUINO_ARCH_MBED_RP2040)
50+
#define DataPin 16
51+
#define CmndPin 19
52+
#define AttPin 17
53+
#define ClockPin 18
54+
#define AckPin 20
55+
#elif defined (ARDUINO_AVR_MEGA2560)
56+
#define DataPin 50
57+
#define AttPin 53
58+
#define AckPin 2
59+
#else
60+
#define DataPin 12
61+
#define AttPin 10
62+
#define AckPin 2
63+
#endif
4064

41-
byte ReadByte = 0;
4265
volatile int state = HIGH;
4366
bool CompatibleMode = false;
67+
byte ReadData[128];
4468

4569
//Set up pins for communication
4670
void PinSetup()
4771
{
48-
byte clr = 0;
49-
50-
pinMode(DataPin, INPUT);
51-
pinMode(CmdPin, OUTPUT);
5272
pinMode(AttPin, OUTPUT);
53-
pinMode(ClockPin, OUTPUT);
54-
pinMode(AckPin, INPUT);
73+
pinMode(AckPin, INPUT_PULLUP);
5574

56-
//Set up SPI on Arduino (250 kHz, clock active when low, reading on falling edge of the clock)
57-
SPCR = 0x7F;
58-
clr=SPSR;
59-
clr=SPDR;
60-
61-
digitalWrite(DataPin, HIGH); //Activate pullup resistor
62-
digitalWrite(CmdPin, LOW);
6375
digitalWrite(AttPin, HIGH);
64-
digitalWrite(ClockPin, HIGH);
65-
digitalWrite(AckPin, HIGH); //Activate pullup resistor
6676

67-
//Set up interrupt on pin 2 (INT.0) for Acknowledge signal
68-
attachInterrupt(0, ACK, FALLING);
77+
#ifdef ARDUINO_ARCH_MBED_RP2040
78+
pinMode(ClockPin, OUTPUT);
79+
pinMode(CmndPin, OUTPUT);
80+
81+
digitalWrite(ClockPin, HIGH);
82+
digitalWrite(CmndPin, HIGH);
83+
#else
84+
85+
#if ESP32
86+
SPI.begin(ClockPin, DataPin, CmndPin, -1);
87+
#else
88+
SPI.begin();
89+
#endif
90+
/* Memory Cards on PS1 are accessed at 250Khz but for the compatibility sake
91+
* we will use 125Khz. For higher speeds external pull-ups are recommended.*/
92+
SPI.beginTransaction(SPISettings(125000, LSBFIRST, SPI_MODE3));
93+
#endif
94+
95+
//Enable pullup on MISO Data line
96+
#if defined(__AVR_ATmega32U4__)
97+
/* Arduino Leonardo and Micro do not have exposed ICSP pins
98+
* so we have to enable pullup by referencing the port*/
99+
PORTB = (1<<PB3);
100+
#elif defined(ESP8266)
101+
/* ESP8266 needs to have each pin reconfigured for a specific purpose.
102+
* If we just write pin as INPUT_PULLUP it will lose it's function as MISO line
103+
* Therefore we need to adjust it as MISO with pullup...*/
104+
PIN_PULLUP_EN(PERIPHS_IO_MUX_MTDI_U);
105+
#else
106+
pinMode(DataPin, INPUT_PULLUP);
107+
#endif
108+
109+
/* Set up interrupt for ACK signal from the Memory Card.
110+
*
111+
* Should be FALLING because signal goes LOW for ~5uS during ACK
112+
* but ESP8266 for some reason doesn't register it.
113+
* So since it goes HIGH anyway after that we will use RISING */
114+
attachInterrupt(digitalPinToInterrupt(AckPin), ACK, RISING);
69115
}
70116

71117
//Acknowledge routine
72-
void ACK()
118+
ICACHE_RAM_ATTR void ACK()
73119
{
74120
state = !state;
75121
}
76122

123+
//Software SPI bit bang, for devices without SPI or with SPI issues
124+
#ifdef ARDUINO_ARCH_MBED_RP2040
125+
byte SoftTransfer(byte data)
126+
{
127+
byte outData = 0;
128+
129+
for (int i = 0; i < 8; i++) {
130+
digitalWrite(CmndPin, !!(data & (1 << i)));
131+
132+
//Clock
133+
digitalWrite(ClockPin, LOW);
134+
delayMicroseconds(2);
135+
136+
//Sample input data
137+
outData |= digitalRead(DataPin) << i;
138+
digitalWrite(ClockPin, HIGH);
139+
delayMicroseconds(2);
140+
}
141+
142+
return outData;
143+
}
144+
#endif
145+
77146
//Send a command to PlayStation port using SPI
78-
byte SendCommand(byte CommandByte, int Delay)
147+
byte SendCommand(byte CommandByte, int Timeout, int Delay)
79148
{
80-
if(!CompatibleMode) Delay = 3000; //Timeout
81-
state = HIGH; //Set high state for ACK signal
149+
if(!CompatibleMode) Timeout = 3000;
150+
state = HIGH; //Set high state for ACK signal
151+
152+
//Delay for a bit (values simulating delays between real PS1 and Memory Card)
153+
delayMicroseconds(Delay);
82154

83-
SPDR = CommandByte; //Start the transmission
84-
while (!(SPSR & (1<<SPIF))); //Wait for the end of the transmission
155+
//Send data on the SPI bus
156+
#ifdef ARDUINO_ARCH_MBED_RP2040
157+
/* Raspberry Pi Pico currently has some issues with SPI data corruption.
158+
* So for now we are gonna do some bit banging, Pico has plenty of power to do it in software.*/
159+
byte data = SoftTransfer(CommandByte);
160+
#else
161+
byte data = SPI.transfer(CommandByte);
162+
#endif
85163

86164
//Wait for the ACK signal from the Memory Card
87165
while(state == HIGH)
88166
{
89-
Delay--;
167+
Timeout--;
90168
delayMicroseconds(1);
91-
if(Delay == 0){
169+
if(Timeout == 0){
92170
//Timeout reached, card doesn't report ACK properly
93171
CompatibleMode = true;
94172
break;
95173
}
96174
}
97175

98-
return SPDR; //Return the received byte
176+
return data; //Return the received byte
99177
}
100178

101179
//Read a frame from Memory Card and send it to serial port
@@ -108,52 +186,53 @@ void ReadFrame(unsigned int Address)
108186
CompatibleMode = false;
109187

110188
//Activate device
111-
PORTB &= 0xFB; //Set pin 10 (AttPin, LOW)
112-
113-
SendCommand(0x81, 500); //Access Memory Card
114-
SendCommand(0x52, 500); //Send read command
115-
SendCommand(0x00, 500); //Memory Card ID1
116-
SendCommand(0x00, 500); //Memory Card ID2
117-
SendCommand(AddressMSB, 500); //Address MSB
118-
SendCommand(AddressLSB, 500); //Address LSB
119-
SendCommand(0x00, 2800); //Memory Card ACK1
120-
SendCommand(0x00, 2800); //Memory Card ACK2
121-
SendCommand(0x00, 2800); //Confirm MSB
122-
SendCommand(0x00, 2800); //Confirm LSB
189+
digitalWrite(AttPin, LOW);
190+
delayMicroseconds(20);
191+
192+
SendCommand(0x81, 500, 70); //Access Memory Card
193+
SendCommand(0x52, 500, 45); //Send read command
194+
SendCommand(0x00, 500, 45); //Memory Card ID1
195+
SendCommand(0x00, 500, 45); //Memory Card ID2
196+
SendCommand(AddressMSB, 500, 45); //Address MSB
197+
SendCommand(AddressLSB, 500, 45); //Address LSB
198+
SendCommand(0x00, 2800, 45); //Memory Card ACK1
199+
SendCommand(0x00, 2800, 0); //Memory Card ACK2
200+
SendCommand(0x00, 2800, 0); //Confirm MSB
201+
SendCommand(0x00, 2800, 0); //Confirm LSB
123202

124203
//Get 128 byte data from the frame
125204
for (int i = 0; i < 128; i++)
126205
{
127-
Serial.write(SendCommand(0x00, 150));
206+
Serial.write(SendCommand(0x00, 150, 0));
128207
}
129208

130-
Serial.write(SendCommand(0x00, 500)); //Checksum (MSB xor LSB xor Data)
131-
Serial.write(SendCommand(0x00, 500)); //Memory Card status byte
209+
Serial.write(SendCommand(0x00, 500, 0)); //Checksum (MSB xor LSB xor Data)
210+
Serial.write(SendCommand(0x00, 0, 0)); //Memory Card status byte
132211

133212
//Deactivate device
134-
PORTB |= 4; //Set pin 10 (AttPin, HIGH)
213+
digitalWrite(AttPin, HIGH);
135214
}
136215

137216
//Write a frame from the serial port to the Memory Card
138217
void WriteFrame(unsigned int Address)
139218
{
140219
byte AddressMSB = Address & 0xFF;
141220
byte AddressLSB = (Address >> 8) & 0xFF;
142-
byte ReadData[128];
143221
int DelayCounter = 30;
144222

145223
//Use ACK detection mode by default
146224
CompatibleMode = false;
147225

148226
//Activate device
149-
PORTB &= 0xFB; //Set pin 10 (AttPin, LOW)
227+
digitalWrite(AttPin, LOW);
228+
delayMicroseconds(20);
150229

151-
SendCommand(0x81, 300); //Access Memory Card
152-
SendCommand(0x57, 300); //Send write command
153-
SendCommand(0x00, 300); //Memory Card ID1
154-
SendCommand(0x00, 300); //Memory Card ID2
155-
SendCommand(AddressMSB, 300); //Address MSB
156-
SendCommand(AddressLSB, 300); //Address LSB
230+
SendCommand(0x81, 300, 45); //Access Memory Card
231+
SendCommand(0x57, 300, 45); //Send write command
232+
SendCommand(0x00, 300, 45); //Memory Card ID1
233+
SendCommand(0x00, 300, 45); //Memory Card ID2
234+
SendCommand(AddressMSB, 300, 45); //Address MSB
235+
SendCommand(AddressLSB, 300, 45); //Address LSB
157236

158237
//Copy 128 bytes from the serial input
159238
for (int i = 0; i < 128; i++)
@@ -171,22 +250,22 @@ void WriteFrame(unsigned int Address)
171250
//Write 128 byte data to the frame
172251
for (int i = 0; i < 128; i++)
173252
{
174-
SendCommand(ReadData[i], 150);
253+
SendCommand(ReadData[i], 150, 0);
175254
}
176255

177-
SendCommand(Serial.read(), 200); //Checksum (MSB xor LSB xor Data)
178-
SendCommand(0x00, 200); //Memory Card ACK1
179-
SendCommand(0x00, 200); //Memory Card ACK2
180-
Serial.write(SendCommand(0x00, 200)); //Memory Card status byte
256+
SendCommand(Serial.read(), 200, 0); //Checksum (MSB xor LSB xor Data)
257+
SendCommand(0x00, 200, 0); //Memory Card ACK1
258+
SendCommand(0x00, 200, 0); //Memory Card ACK2
259+
Serial.write(SendCommand(0x00, 0, 0)); //Memory Card status byte
181260

182261
//Deactivate device
183-
PORTB |= 4; //Set pin 10 (AttPin, HIGH)
262+
digitalWrite(AttPin, HIGH);
184263
}
185264

186265
void setup()
187266
{
188267
//Set up serial communication
189-
Serial.begin(38400);
268+
Serial.begin(115200);
190269

191270
//Set up pins
192271
PinSetup();
@@ -197,9 +276,7 @@ void loop()
197276
//Listen for commands
198277
if(Serial.available() > 0)
199278
{
200-
ReadByte = Serial.read();
201-
202-
switch(ReadByte)
279+
switch(Serial.read())
203280
{
204281
default:
205282
Serial.write(ERROR);
@@ -214,13 +291,13 @@ void loop()
214291
break;
215292

216293
case MCREAD:
217-
delay(5);
218-
ReadFrame(Serial.read() | Serial.read() << 8);
294+
delay(5);
295+
ReadFrame(Serial.read() | Serial.read() << 8);
219296
break;
220297

221298
case MCWRITE:
222-
delay(5);
223-
WriteFrame(Serial.read() | Serial.read() << 8);
299+
delay(5);
300+
WriteFrame(Serial.read() | Serial.read() << 8);
224301
break;
225302
}
226303
}

‎Python 2/memcarduino.py

+375
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
1+
#!/usr/bin/python2
2+
3+
#MemCARDuino python interface
4+
#a simple command line tool for quick dumping of psx memory cards connected to a serially connected MemCARDuino project
5+
# made by Jason D'Amico on the 28/12/2014
6+
# mr-fuji on 21/03/2015
7+
#use and modification of this script is allowed, if improved do send a copy back.
8+
#use at own risk, not my fault if burns down house or erases card (it shouldn't, but...)
9+
10+
import time
11+
import serial
12+
import sys
13+
import array
14+
from struct import pack
15+
from datetime import datetime
16+
import getopt
17+
18+
global GID # get identifier
19+
global GFV # get firmware version
20+
global MCR # mcr read command, should be followed by a verify memcard
21+
global MCW # mcr write command, should be followed by a verify memcard
22+
global MCID # read mc identifier
23+
24+
global start #first block to read from (default 0)
25+
global end #number of blocks to read (default 1024)
26+
global block_size #size (in Bytes) of each block (should remain 128)
27+
28+
global inputport #input port to use
29+
global rate #bitrate to use
30+
global ser #serial
31+
global mode #operation
32+
33+
GID = "\xA0"
34+
GFV = "\xA1"
35+
MCR = "\xA2"
36+
MCW = "\xA3"
37+
MCID = "\xA4"
38+
39+
#cus im lazy -TheBlueTroll
40+
#SRC: http://code.activestate.com/recipes/510399-byte-to-hex-and-hex-to-byte-string-conversion/
41+
"""
42+
HexByteConversion
43+
44+
Convert a byte string to it's hex representation for output or visa versa.
45+
46+
ByteToHex converts byte string "\xFF\xFE\x00\x01" to the string "FF FE 00 01"
47+
HexToByte converts string "FF FE 00 01" to the byte string "\xFF\xFE\x00\x01"
48+
"""
49+
50+
#-------------------------------------------------------------------------------
51+
52+
def ByteToHex( byteStr ):
53+
"""
54+
Convert a byte string to it's hex string representation e.g. for output.
55+
"""
56+
57+
# Uses list comprehension which is a fractionally faster implementation than
58+
# the alternative, more readable, implementation below
59+
#
60+
# hex = []
61+
# for aChar in byteStr:
62+
# hex.append( "%02X " % ord( aChar ) )
63+
#
64+
# return ''.join( hex ).strip()
65+
66+
return ''.join( [ "%02X " % ord( x ) for x in byteStr ] ).strip()
67+
68+
def help():
69+
print "memcarduino usage:"
70+
print "memcarduino.py -p,--port <serial port> , -r,--read <output file> OR -w,--write <input file> OR -f,--format , [-c,--capacity <capacity>] , [-b,--bitrate <bitrate:bps>]"
71+
print "<serial port> accepts COM port names, or for linux, file references (/dev/tty[...] or others)"
72+
print "<output file> read from memory card and save to file"
73+
print "<input file> read from file and write to memory card (accepts both windows and linux file URI's)"
74+
print "<capacyty> sets memory card capacity [blocks] *1 block = 128 B* (default 1024 blocks)"
75+
print "<bitrate> sets bitrate on serial port (default 115200 bps)"
76+
print "format command formats memorycard with all \\x00\n\n\n"
77+
78+
def test():
79+
ser.close()
80+
ser.open() # sometimes when serial port is opened, the arduino resets,so open, then wait for it to reset, then continue on with the check
81+
time.sleep(2)
82+
ser.isOpen()
83+
84+
check_connection()
85+
def testFormat():
86+
ser.close()
87+
ser.open() # sometimes when serial port is opened, the arduino resets,so open, then wait for it to reset, then continue on with the check
88+
time.sleep(2)
89+
ser.isOpen()
90+
91+
#check_connection() #thows error when the first frame is erased, which format does
92+
93+
def check_connection():
94+
95+
temp = ""
96+
#print "running mcduino check"
97+
#start mcduino verify
98+
99+
ser.write(GID)
100+
temp=ser.read(6)
101+
if temp !="MCDINO":
102+
print "error: mcduino communication error, got \""+temp + "\" as identifier (should be \"MCDINO\")\n\n"
103+
sys.exit()
104+
#end mcduino verify
105+
##print "passed mcduino check\nRunning mcr header check"
106+
#start mcr verify
107+
ser.write(MCR + "\x00" + chr(1))
108+
temp=ser.read(129)
109+
b = ser.read(1)
110+
111+
if b!="\x47":
112+
print"error: mc read failure, check connections\n\n"
113+
sys.exit()
114+
#print "passed header check"
115+
116+
def memcard_read(file):
117+
f = file
118+
temp = ""
119+
print "reading data from memory card...\n"
120+
passed = 0
121+
for i in xrange(start, end):
122+
tstart = datetime.now()
123+
if (i <= 255):
124+
ia = "\x00" + chr(i)
125+
else:
126+
ia = pack('H', i)
127+
ia = ia[1] + ia[0] # invert that crap on the cheap
128+
# convert to a 2byte hex string, then decode
129+
hex_data = ia
130+
# conv to a array
131+
arry = array.array('B', hex_data)
132+
map(ord, hex_data)
133+
# end of black magic
134+
ser.write(MCR)
135+
ser.write(hex_data[0])
136+
ser.write(hex_data[1])
137+
138+
temp = ser.read(block_size)
139+
chk = ser.read(1)
140+
my_chk = ord(hex_data[1])^ord(hex_data[0])
141+
for byte in temp:
142+
my_chk ^= ord(byte)
143+
my_chk = chr(my_chk)
144+
if chk != my_chk:
145+
print "Warning, checksum mismatch at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" Got:"+hex(ord(my_chk))+" ShouldBe:"+hex(ord(chk))
146+
b = ser.read(1)
147+
tend = datetime.now()
148+
tPrint=tend-tstart
149+
if(b == "\x47"):
150+
f.write(temp)
151+
print "OK at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" TimeTaken:"+str(tPrint)
152+
passed += 1
153+
elif(b == "\x4E"):
154+
print "BAD CHECKSUM at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" TimeTaken:"+str(tPrint)
155+
f.write("\x00"*128)
156+
elif(b == "\xFF"):
157+
print "BAD SECTOR at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" TimeTaken:"+str(tPrint)
158+
f.write("\x00"*128)
159+
else:
160+
print "UNKNOWN ERROR "+ByteToHex(b)+" at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" TimeTaken:"+str(tPrint) # WTF?
161+
f.write("\x00"*128)
162+
163+
result(passed)
164+
165+
def memcard_write(file):
166+
f = file
167+
temp = ""
168+
print "writing data to memory card...\n"
169+
passed = 0
170+
for i in xrange(start, end):
171+
tstart = datetime.now()
172+
173+
if (i <= 255):
174+
ia = "\x00" + chr(i)
175+
else:
176+
ia = pack('H', i)
177+
ia = ia[1] + ia[0] # invert that crap on the cheap
178+
# convert to a 2byte hex string, then decode
179+
hex_data = ia
180+
# conv to a array
181+
arry = array.array('B', hex_data)
182+
map(ord, hex_data)
183+
# end of black magic
184+
185+
data_block = f.read(block_size)
186+
chk = ord(hex_data[1])^ord(hex_data[0])
187+
for byte in data_block:
188+
chk ^= ord(byte)
189+
chk = chr(chk)
190+
ser.write(MCW)
191+
ser.write(hex_data[0])
192+
ser.write(hex_data[1])
193+
ser.write(data_block)
194+
ser.write(chk)
195+
b = ser.read(1)
196+
tend = datetime.now()
197+
tPrint=tend-tstart
198+
if(b == "\x47"):
199+
print "OK at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" CHECKSUM:"+ByteToHex(chk)+" TimeTaken:"+str(tPrint)
200+
passed += 1
201+
elif(b == "\x4E"):
202+
print "BAD CHECKSUM at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" CHECKSUM:"+ByteToHex(chk)+" TimeTaken:"+str(tPrint)+str(res)
203+
elif(b == "\xFF"):
204+
print "BAD SECTOR at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" CHECKSUM:"+ByteToHex(chk)+" TimeTaken:"+str(tPrint)
205+
else:
206+
print "UNKNOWN ERROR "+ByteToHex(b)+" at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" CHECKSUM:"+ByteToHex(chk)+" TimeTaken:"+str(tPrint) # WTF?
207+
208+
result(passed)
209+
210+
def memcard_format():
211+
temp = ""
212+
print "formatting memory card...\n"
213+
passed = 0
214+
for i in xrange(start, end):
215+
tstart = datetime.now()
216+
if (i==0):
217+
218+
#how about actually not blanking the identifier frame.
219+
#instead, lets write it
220+
hex_data="\x00"+"\x00"
221+
data_block = "\x4D" + "\x43" #"MC"
222+
data_block = data_block+"\x00"*125 #125 blanks
223+
data_block = data_block+"\x0E" #checksum
224+
225+
#just copy pasted the write code here
226+
chk = ord(hex_data[1])^ord(hex_data[0])
227+
for byte in data_block:
228+
chk ^= ord(byte)
229+
chk = chr(chk)
230+
ser.write(MCW)
231+
ser.write(hex_data[0])
232+
ser.write(hex_data[1])
233+
ser.write(data_block)
234+
ser.write(chk)
235+
b = ser.read(1)
236+
tend = datetime.now()
237+
tPrint=tend-tstart
238+
if(b == "\x47"):
239+
print "OK at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" TimeTaken:"+str(tPrint)
240+
passed += 1
241+
elif(b == "\x4E"):
242+
print "BAD CHECKSUM at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" TimeTaken:"+str(tPrint)
243+
elif(b == "\xFF"):
244+
print "BAD SECTOR at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" TimeTaken:"+str(tPrint)
245+
else:
246+
print "UNKNOWN ERROR "+ByteToHex(b)+" at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" TimeTaken:"+str(tPrint) # WTF?
247+
else:
248+
if (i <= 255):
249+
ia = "\x00" + chr(i)
250+
else:
251+
ia = pack('H', i)
252+
ia = ia[1] + ia[0] # invert that crap on the cheap
253+
# convert to a 2byte hex string, then decode
254+
hex_data = ia
255+
# conv to a array
256+
arry = array.array('B', hex_data)
257+
map(ord, hex_data)
258+
# end of black magic
259+
260+
data_block = "\x00"*128
261+
chk = ord(hex_data[1])^ord(hex_data[0])
262+
for byte in data_block:
263+
chk ^= ord(byte)
264+
chk = chr(chk)
265+
ser.write(MCW)
266+
ser.write(hex_data[0])
267+
ser.write(hex_data[1])
268+
ser.write(data_block)
269+
ser.write(chk)
270+
b = ser.read(1)
271+
tend = datetime.now()
272+
tPrint=tend-tstart
273+
if(b == "\x47"):
274+
print "OK at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" TimeTaken:"+str(tPrint)
275+
passed += 1
276+
elif(b == "\x4E"):
277+
print "BAD CHECKSUM at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" TimeTaken:"+str(tPrint)
278+
elif(b == "\xFF"):
279+
print "BAD SECTOR at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" TimeTaken:"+str(tPrint)
280+
else:
281+
print "UNKNOWN ERROR "+ByteToHex(b)+" at frame "+str(i+1)+"/"+str(end)+" Address:"+ByteToHex(hex_data)+" TimeTaken:"+str(tPrint) # WTF?
282+
283+
result(passed)
284+
285+
def result(passed):
286+
print "\n\n\n"
287+
if(passed == end):
288+
print "SUCCESS"
289+
else:
290+
print mode + " ERROR: "+str(1024-passed)+" failed\n"
291+
292+
#MAIN VARIABLES
293+
start = 0
294+
end = 1024
295+
block_size = 128
296+
inputport = ""
297+
rate = 115200
298+
file = ""
299+
mode = ""
300+
301+
opts, args = getopt.getopt(sys.argv[1:] , "hfp:r:w:c:b" , [ "help" , "format" , "port=" , "read=" , "write=" , "capacity=" , "bitrate="])
302+
303+
304+
#OPTIONS CHECK
305+
306+
print "\n\n"
307+
308+
for opt, arg in opts:
309+
#print opt
310+
#print arg
311+
if opt in ("-h" , "--help"):
312+
help()
313+
sys.exit()
314+
elif opt in ("-f" , "--format"):
315+
mode = "FORMAT"
316+
elif opt in ("-p" , "--port"):
317+
inputport = arg
318+
elif opt in("-r", "--read"):
319+
file = arg
320+
mode = "READ"
321+
elif opt in("-w", "--write"):
322+
file = arg
323+
mode = "WRITE"
324+
elif opt in ("-c" , "--capacity"):
325+
end = arg
326+
elif opt in("-b", "--bitrate"):
327+
print "warning: bitrate shuold not be changed unless necessary"
328+
rate = arg
329+
else:
330+
help()
331+
sys.exit()
332+
333+
if inputport == "":
334+
print "warning: no serial port specified"
335+
help()
336+
sys.exit()
337+
if file == "" and (mode == "WRITE" or mode == "READ"):
338+
print 'warning: input/output file missing'
339+
help()
340+
sys.exit()
341+
342+
343+
#BEGIN
344+
345+
ser = serial.Serial(port=inputport, baudrate=rate,timeout=2)
346+
347+
test()
348+
349+
if mode == "WRITE":
350+
f = open(file, 'rb')
351+
tOpStart = datetime.now()
352+
memcard_write(f)
353+
tOpEnd = datetime.now()
354+
f.close()
355+
tOpDelta=tOpEnd-tOpStart
356+
print "Total Time:"+str(tOpDelta)
357+
elif mode == "READ":
358+
f = open(file, 'wb')
359+
tOpStart = datetime.now()
360+
memcard_read(f)
361+
tOpEnd = datetime.now()
362+
f.close()
363+
tOpDelta=tOpEnd-tOpStart
364+
print "Total Time:"+str(tOpDelta)
365+
elif mode == "FORMAT":
366+
tOpStart = datetime.now()
367+
memcard_format()
368+
tOpEnd = datetime.now()
369+
tOpDelta=tOpEnd-tOpStart
370+
print "Total Time:"+str(tOpDelta)
371+
else:
372+
print "warning: no operation selected"
373+
374+
ser.close()
375+

‎README.md

+37-59
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,59 @@
11
# MemCARDuino
22
### Arduino PlayStation 1 Memory Card reader
3+
![memcarduino](https://raw.githubusercontent.com/ShendoXT/memcarduino/master/images/memcarduino.png)
34

4-
## Thanks to:
5-
* Martin Korth of the NO$PSX - documented Memory Card protocol.
6-
* Andrew J McCubbin - documented PS1 SPI interface.
5+
## Supported platforms:
6+
* Arduino Uno, Duemilanove, Diecimila, Nano, Mini, Fio (ATmega168/P or ATmega328/P)
7+
* Arduino Leonardo, Micro (ATmega32U4)
8+
* Arduino Mega 2560
9+
* Espressif [ESP8266](https://github.com/esp8266/Arduino), [ESP32](https://github.com/espressif/arduino-esp32) (requires additional board URL)
10+
* [Raspberry Pi Pico](https://github.com/earlephilhower/arduino-pico) (requires additional board URL)
711

12+
Various other boards can be supported if they have Arduino core available with SPI library with minimal or no editing to the sketch.
813
## Connecting a Memory Card to Arduino:
914
Looking at the Memory Card:
1015
_________________
1116
|_ _|_ _ _|_ _ _|
1217
1 2 3 4 5 6 7 8
18+
19+
| Memory Card | Uno, Nano, etc| Leonardo, Micro| Mega 2560 | ESP8266 | ESP32 | Pi Pico |
20+
| ------------- | ------------- |--| -- | -- | -- | -- |
21+
|1: Data | D12 | ICSP MISO | D50 | GPIO12 (D6)| GPIO19 | GP16
22+
|2: Command | D11 | ICSP MOSI | D52 | GPIO13 (D7)| GPIO23 | GP19
23+
|3: 7.6V | See 7.6V note | See 7.6V note | See 7.6V note | See 7.6V note | See 7.6V note | See 7.6V note
24+
|4: Gnd | Gnd | Gnd | Gnd | Gnd | Gnd | Gnd
25+
|5: 3.6V | See VCC note | 3.3V | 3.3V | 3.3V | 3.3V | 3.3V
26+
|6: Attention | D10 | D10 | D53 | GPIO15 (D8) | GPIO5 | GP17
27+
|7: Clock | D13 | ICSP SCK | D52 | GPIO14 (D5) | GPIO18 | GP18
28+
|8: Acknowledge | D2 | D2 | D2 | GPIO2 (D4) | GPIO22 | GP20
1329

14-
MC 1: DATA - D12 on Arduino<br>
15-
MC 2: CMND - D11 on Arduino<br>
16-
MC 3: 7.6V - External 7.6V power (only required for 3rd party cards and knockoffs)<br>
17-
MC 4: GND - GND Pin on Arduino<br>
18-
MC 5: 3.6V - 5V/3.3V Pin on Arduino<br>
19-
MC 6: ATT - D10 on Arduino<br>
20-
MC 7: CLK - D13 on Arduino<br>
21-
MC 8: ACK - D2 on Arduino<br>
30+
**VCC note:** Memory Card is a 3.6V device. Connect it to either 3.3V provided by the board or use external 3.6 power supply.<br>
31+
Old Arduino uses 5V logic and it is not recommended. It may shorten lifespan of your MemoryCard or damage it permanently.
2232

23-
# Warning:
24-
PS1 MemoryCard is a 3.6V device but (old) Arduino uses 5V logic.<br>That may shorten lifespan of your MemoryCard or damage it permanently.<br>
25-
It is recommended to use 3.3V Arduino devices to avoid any damage to your MemoryCard.
33+
**7.6V note:** This is only required for 3rd party Memory Cards and knockoffs.<br>
34+
Use external 7.6V power supply or if you are lucky you can get by with using 5V provided by the USB.
2635

27-
## Reading save from a PC:
28-
To read saves from the Memory Card to your PC use [MemcardRex](https://github.com/ShendoXT/memcardrex/releases) if you are using Windows.
29-
For other operating systems you can use a provided Python script.
30-
If you are writing your own application the protocol is as follows:
36+
## Reading saves from a PC:
37+
To read saves from the Memory Card to your PC use [MemcardRex](https://github.com/ShendoXT/memcardrex/releases) if you are using Windows.<br>
38+
Make sure to use the latest version because MemCARDuino nuw runs at 115200bps while older releases used 38400bps.
3139

32-
# Python interface:
33-
Two python scripts are designed to raw copy the Memory Card data to PC and vice versa.
34-
memcarduino.py is Python 2 Script<br>
35-
memcarduino_py3.py is Python 3 Script
36-
**Note:** As usual, use at your own risk, it might not work out of the box.
40+
For other operating systems you can use a provided Python script.
3741

38-
## Usage
39-
### Python 2:<br>
40-
Before using install pyserial:<br>
41-
pip install pyserial<br>
42-
43-
python memcarduino.py -p,--port <serial port> , -r,--read <output file> OR -w,--write <input file> OR -f,--format , [-c,--capacity <capacity>] , [-b,--bitrate <bitrate:bps>]
42+
Before using install pyserial:
4443

45-
<serial port> accepts COM port names, or for linux, file references (/dev/tty[...] or others)
46-
<output file> read from memory card and save to file
47-
<input file> read from file and write to memory card (accepts both windows and linux file URI's)
48-
<capacyty> sets memory card capacity [blocks] *1 block = 128 B* (default 1024 blocks)
49-
<bitrate> sets bitrate on serial port (default 38400 bps)
50-
51-
### Python 3:<br>
52-
Before using install pyserial:<br>
53-
pip3 install pyserial<br>
54-
55-
python3 memcarduino_py3.py -p,--port <serial port> , -r,--read <output file> OR -w,--write <input file> OR -f,--format , [-c,--capacity <capacity>] , [-b,--bitrate <bitrate:bps>]
44+
pip3 install pyserial
45+
Usage:
46+
47+
python3 memcarduino.py -p,--port <serial port> , -r,--read <output file> OR -w,--write <input file> OR -f,--format , [-c,--capacity <capacity>] , [-b,--bitrate <bitrate:bps>]
5648

5749
<serial port> accepts COM port names, or for linux, file references (/dev/tty[...] or others)
5850
<output file> read from memory card and save to file
5951
<input file> read from file and write to memory card (accepts both windows and linux file URI's)
6052
<capacyty> sets memory card capacity [blocks] *1 block = 128 B* (default 1024 blocks)
61-
<bitrate> sets bitrate on serial port (default 38400 bps)
53+
<bitrate> sets bitrate on serial port (default 115200 bps)
6254

55+
This requires a serial port (/dev/ttyACM0 for Arduino uno's, /dev/ttyUSBX for others, COMX for Windows, and various for macOS).
6356

64-
This requires a serial port (/dev/ttyACM0 for Arduino uno's, /dev/ttyUSBX for others, COMX for Windows, and various for macOS) it also requires a specific output file.
65-
Changing the bitrate(baudrate) isn't recommended, but is available anyway (it does mean changing the Arduino code manually...)
66-
67-
## Interfacing with MemCARDuino (for developers):
68-
Communication is done at **38400 bps**.
69-
To check if the MemCARDuino is connected to the selected COM port send **GETID** command.
70-
Device should respond with **IDENTIFIER**.
71-
Optionally you can send a **GETVER** to get the version of the firmware.
72-
73-
To Read a 128byte frame send a **MCR** command with *MSB* byte and *LSB* byte of the frame you want to read.
74-
MemCARDduino will respond with 128 byte frame data, [MSB xor LSB xor Data] and Memory Card status byte.
75-
76-
To Write a frame send a **MCW** command with *MSB* byte and *LSB* byte, 128 byte data and [MSB xor LSB xor Data].
77-
MemCARDduino will respond with Memory Card status byte.
78-
79-
### Checking if Memory Card is connected:
80-
Read a frame from the card and verify the returned status byte.
81-
If it's 0x47 then card is connected. If it's 0xFF card is not connected.
57+
## Thanks to:
58+
* Martin Korth of the NO$PSX - documented Memory Card protocol.
59+
* Andrew J McCubbin - documented PS1 SPI interface.

‎memcarduino.py

+169-242
Large diffs are not rendered by default.

‎memcarduino_py3.py

-301
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.