1
0

上传本地文件

This commit is contained in:
Helloworld_Dk 2022-10-21 16:54:46 +08:00 committed by GitHub
commit e2408bccf3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1639 additions and 0 deletions

View File

@ -0,0 +1,143 @@
#include "cmd.h"
bool high_baudrate = true;//high_baudrate=true
void setup() {
FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(255);
FastLED.showColor(0);
nfc.begin();
while (!nfc.getFirmwareVersion()) {
FastLED.showColor(0xFF0000);
delay(500);
FastLED.showColor(0);
delay(500);
}
nfc.setPassiveActivationRetries(0x10);//设定等待次数
nfc.SAMConfig();
memset(&req, 0, sizeof(req.bytes));
memset(&res, 0, sizeof(res.bytes));
SerialDevice.begin(high_baudrate ? 115200 : 38400);
FastLED.showColor(high_baudrate ? 0x0000FF : 0x00FF00);
}
void loop() {
SerialCheck();
packet_write();
}
static uint8_t len, r, checksum;
static bool escape = false;
static uint8_t packet_read() {
while (SerialDevice.available()) {
r = SerialDevice.read();
if (r == 0xE0) {
req.frame_len = 0xFF;
continue;
}
if (req.frame_len == 0xFF) {
req.frame_len = r;
len = 0;
checksum = r;
continue;
}
if (r == 0xD0) {
escape = true;
continue;
}
if (escape) {
r++;
escape = false;
}
req.bytes[++len] = r;
if (len == req.frame_len && checksum == r) {
return req.cmd;
}
checksum += r;
}
return 0;
}
static void packet_write() {
uint8_t checksum = 0, len = 0;
if (res.cmd == 0) {
return;
}
SerialDevice.write(0xE0);
while (len <= res.frame_len) {
uint8_t w;
if (len == res.frame_len) {
w = checksum;
} else {
w = res.bytes[len];
checksum += w;
}
if (w == 0xE0 || w == 0xD0) {
SerialDevice.write(0xD0);
SerialDevice.write(--w);
} else {
SerialDevice.write(w);
}
len++;
}
res.cmd = 0;
}
void SerialCheck() {
switch (packet_read()) {
case SG_NFC_CMD_RESET:
sg_nfc_cmd_reset();
break;
case SG_NFC_CMD_GET_FW_VERSION:
sg_nfc_cmd_get_fw_version();
break;
case SG_NFC_CMD_GET_HW_VERSION:
sg_nfc_cmd_get_hw_version();
break;
case SG_NFC_CMD_POLL:
sg_nfc_cmd_poll();
break;
case SG_NFC_CMD_MIFARE_READ_BLOCK:
sg_nfc_cmd_mifare_read_block();
break;
case SG_NFC_CMD_FELICA_ENCAP:
sg_nfc_cmd_felica_encap();
break;
case SG_NFC_CMD_AIME_AUTHENTICATE:
sg_nfc_cmd_aime_authenticate();
break;
case SG_NFC_CMD_BANA_AUTHENTICATE:
sg_nfc_cmd_bana_authenticate();
break;
case SG_NFC_CMD_MIFARE_SELECT_TAG:
sg_nfc_cmd_mifare_select_tag();
break;
case SG_NFC_CMD_MIFARE_SET_KEY_AIME:
sg_nfc_cmd_mifare_set_key_aime();
break;
case SG_NFC_CMD_MIFARE_SET_KEY_BANA:
sg_nfc_cmd_mifare_set_key_bana();
break;
case SG_NFC_CMD_RADIO_ON:
sg_nfc_cmd_radio_on();
break;
case SG_NFC_CMD_RADIO_OFF:
sg_nfc_cmd_radio_off();
break;
case SG_RGB_CMD_RESET:
sg_led_cmd_reset();
break;
case SG_RGB_CMD_GET_INFO:
sg_led_cmd_get_info();
break;
case SG_RGB_CMD_SET_COLOR:
sg_led_cmd_set_color();
break;
case 0:
break;
default:
sg_res_init();
}
}

View File

@ -0,0 +1,57 @@
# Arduino-Aime-Reader
使用 Arduino + PN532 + WS2812B 制作的 Aime 兼容读卡器。
**目前所有主要功能已经实现,如果没有 bug 应该不会再更新。**
English: [lawliuwuu/Arduino-Aime-Reader](https://github.com/lawliuwuu/Arduino-Aime-Reader)
支持卡片类型: [FeliCa](https://zh.wikipedia.org/wiki/FeliCa)Amusement IC、Suica、八达通等和 [MIFARE](https://zh.wikipedia.org/wiki/MIFARE)AimeBanapassport
实现逻辑为官方读卡器串口数据对比 + 脑补,不保证正确实现。
通信数据格式参考了 [Segatools]() 和官方读卡器抓包数据,可在 [Example.txt](doc/Example.txt) 和 [nfc.txt](doc/nfc.txt) 查看。
一个使用例:[ESP32-CardReader](https://github.com/Sucareto/ESP32-CardReader)
### 使用方法:
1. 按照 [PN532](https://github.com/elechouse/PN532) 的提示安装库
2. 按照使用方式,在 Arduino 和 PN532 接好连接线I2C或SPI并调整 PN532 上的拨码开关
3. 接上 WS2812B 灯条(可选)
4. 上传 [ReaderTest](tools/ReaderTest/ReaderTest.ino) 测试硬件是否工作正常
5. 若读卡正常,可按照支持列表打开设备管理器设置 COM 端口号
6. 按照游戏的波特率设置代码的`high_baudrate`选项,`115200`是`true``38400`是`false`
7. 上传程序打开游戏测试
8. 安装 [MifareClassicTool](https://github.com/ikarus23/MifareClassicTool),修改 [Aime 卡示例](doc/aime示例.mct) 后写入空白 MIFARE UID/CUID 卡
某些 Arduino 可能需要在游戏主程序连接前给串口以正确的波特率发送 DTR/RTS需要先打开一次 Arduino 串口监视器再启动主程序。
如果是 SDBT可以在启动前运行一次 [DTR-RTS.exe](tools/DTR-RTS.exe) 以向 COM1 和 COM12 发送DTR/RTS。
如果需要向其他端口和特定的波特率发送,可以修改 [DTR-RTS.c](tools/DTR-RTS.c) 然后编译。
### 支持列表:
| 游戏代号 | COM端口号 | 支持的卡 | 默认波特率 |
| - | - | - | - |
| SDDT/SDEZ | COM1 | FeliCa,MIFARE | 115200 |
| SDEY | COM2 | MIFARE | 38400 |
| SDHD | COM4 | FeliCa,MIFARE | cvt=38400,sp=115200 |
| SBZV/SDDF | COM10 | FeliCa,MIFARE | 38400 |
| SDBT | COM12 | FeliCa,MIFARE | 38400 |
- 如果读卡器没有正常工作,可以切换波特率试下
- 有使用 amdaemon 的,可以参考 config_common.json 内 aime > unit > port 确认端口号
- 如果 `"high_baudrate" : true` 则波特率是`115200`,否则就是`38400`
### 已测试开发板:
- SparkFun Pro MicroATmega32U4需要发送 DTR/RTS
- SparkFun SAMD21 Dev BreakoutATSAMD21G18
- NodeMCU 1.0ESP-12E + CP2102 & CH340SDA=D2SCL=D1
- NodeMCU-32SESP32-S + CH340
### 已知问题:
- 在 NDA_08 命令的写入 Felica 操作没有实现,因为未确认是否会影响卡片后续使用
- 未确定`res.status`的意义,因此`res.status = 1;`可能是错误的
- 未实现`mifare_select_tag`,未支持多卡选择,只会读到最先识别的卡片
### 引用库:
- 驱动 WS2812B[FastLED](https://github.com/FastLED/FastLED)
- 驱动 PN532[PN532](https://github.com/elechouse/PN532)
- 读取 FeliCa 参考:[PN532を使ってArduinoでFeliCa学生証を読む方法](https://qiita.com/gpioblink/items/91597a5275862f7ffb3c)
- 读取 FeliCa 数据的程序:[NFC TagInfo](https://play.google.com/store/apps/details?id=at.mroland.android.apps.nfctaginfo)[NFC TagInfo by NXP](https://play.google.com/store/apps/details?id=com.nxp.taginfolite)

323
Arduino-Aime-Reader/cmd.h Normal file
View File

@ -0,0 +1,323 @@
#include "FastLED.h"
#define NUM_LEDS 1
CRGB leds[NUM_LEDS];
#define SerialDevice Serial
#define LED_PIN 5
#if defined(PN532_SPI_SS)
#pragma message "使用 SPI 连接 PN532"
#include <SPI.h>
#include <PN532_SPI.h>
PN532_SPI pn532(SPI, PN532_SPI_SS);
#else
#include <Wire.h>
#include <PN532_I2C.h>
PN532_I2C pn532(Wire);
#endif
#include "PN532.h"
PN532 nfc(pn532);
uint8_t AimeKey[6], BanaKey[6];
enum {
SG_NFC_CMD_GET_FW_VERSION = 0x30,
SG_NFC_CMD_GET_HW_VERSION = 0x32,
SG_NFC_CMD_RADIO_ON = 0x40,
SG_NFC_CMD_RADIO_OFF = 0x41,
SG_NFC_CMD_POLL = 0x42,
SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43,
SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x50,
SG_NFC_CMD_BANA_AUTHENTICATE = 0x51,
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x54,
SG_NFC_CMD_AIME_AUTHENTICATE = 0x55,
SG_NFC_CMD_TO_UPDATER_MODE = 0x60,
SG_NFC_CMD_SEND_HEX_DATA = 0x61,
SG_NFC_CMD_RESET = 0x62,
SG_NFC_CMD_FELICA_ENCAP = 0x71,
SG_RGB_CMD_SET_COLOR = 0x81,
SG_RGB_CMD_GET_INFO = 0xF0,
SG_RGB_CMD_RESET = 0xF5,
//FELICA_ENCAP
FELICA_CMD_POLL = 0x00,
FELICA_CMD_NDA_06 = 0x06,
FELICA_CMD_NDA_08 = 0x08,
FELICA_CMD_GET_SYSTEM_CODE = 0x0C,
FELICA_CMD_NDA_A4 = 0xA4,
};
typedef union packet_req {
uint8_t bytes[128];
struct {
uint8_t frame_len;
uint8_t addr;
uint8_t seq_no;
uint8_t cmd;
uint8_t payload_len;
union {
uint8_t key[6]; //sg_nfc_req_mifare_set_key(bana or aime)
uint8_t color_payload[3];//sg_led_req_set_color
struct { //sg_nfc_cmd_mifare_select_tag,sg_nfc_cmd_mifare_authenticate,sg_nfc_cmd_mifare_read_block
uint8_t uid[4];
uint8_t block_no;
};
struct { //sg_nfc_req_felica_encap
uint8_t encap_IDm[8];
uint8_t encap_len;
uint8_t encap_code;
union {
struct { //FELICA_CMD_POLL猜测
uint8_t poll_systemCode[2];
uint8_t poll_requestCode;
uint8_t poll_timeout;
};
struct { //NDA_06,NDA_08,NDA_A4
uint8_t RW_IDm[8];
uint8_t numService;//and NDA_A4 unknown byte
uint8_t serviceCodeList[2];
uint8_t numBlock;
uint8_t blockList[1][2];//长度可变
uint8_t blockData[16];//WriteWithoutEncryption,ignore
};
uint8_t felica_payload[1];
};
};
};
};
} packet_req_t;
typedef union packet_res {
uint8_t bytes[128];
struct {
uint8_t frame_len;
uint8_t addr;
uint8_t seq_no;
uint8_t cmd;
uint8_t status;
uint8_t payload_len;
union {
char version[23]; //sg_nfc_res_get_fw_version,sg_nfc_res_get_hw_version
uint8_t info_payload[9]; //sg_led_res_get_info
uint8_t block[16]; //sg_nfc_res_mifare_read_block
struct { //sg_nfc_res_poll
uint8_t count;
uint8_t type;
uint8_t id_len;
union {
uint8_t mifare_uid[4];
struct {
uint8_t IDm[8];
uint8_t PMm[8];
};
};
};
struct { //sg_nfc_res_felica_encap
uint8_t encap_len;
uint8_t encap_code;
uint8_t encap_IDm[8];
union {
struct {//FELICA_CMD_POLL
uint8_t poll_PMm[8];
uint8_t poll_systemCode[2];
};
struct {
uint8_t RW_status[2];//猜测,NDA_06,NDA_08
uint8_t numBlock;//NDA_06
uint8_t blockData[1][1][16];//NDA_06
};
uint8_t felica_payload[1];
};
};
};
};
} packet_res_t;
static packet_req_t req;
static packet_res_t res;
static void sg_res_init(uint8_t payload_len = 0) { //初始化模板
res.frame_len = 6 + payload_len;
res.addr = req.addr;
res.seq_no = req.seq_no;
res.cmd = req.cmd;
res.status = 0;
res.payload_len = payload_len;
}
static void sg_nfc_cmd_reset() { //重置读卡器
nfc.begin();
nfc.setPassiveActivationRetries(0x01); //设定等待次数,0xFF永远等待
nfc.SAMConfig();
if (nfc.getFirmwareVersion()) {
nfc.SAMConfig();
sg_res_init();
res.status = 3;
return;
}
FastLED.showColor(0xFF0000);
}
static void sg_nfc_cmd_get_fw_version() {
sg_res_init(23);
memcpy(res.version, "TN32MSEC003S F/W Ver1.2", 23);
}
static void sg_nfc_cmd_get_hw_version() {
sg_res_init(23);
memcpy(res.version, "TN32MSEC003S H/W Ver3.0", 23);
}
static void sg_nfc_cmd_mifare_set_key_aime() {
sg_res_init();
memcpy(AimeKey, req.key, 6);
}
static void sg_nfc_cmd_mifare_set_key_bana() {
sg_res_init();
memcpy(BanaKey, req.key, 6);
}
static void sg_led_cmd_reset() {
sg_res_init();
}
static void sg_led_cmd_get_info() {
sg_res_init(9);
static uint8_t info[9] = {'1', '5', '0', '8', '4', 0xFF, 0x10, 0x00, 0x12};
memcpy(res.info_payload, info, 9);
}
static void sg_led_cmd_set_color() {
FastLED.showColor(CRGB(req.color_payload[0], req.color_payload[1], req.color_payload[2]));
}
static void sg_nfc_cmd_radio_on() {
sg_res_init();
nfc.setRFField(0x00, 0x01);
}
static void sg_nfc_cmd_radio_off() {
sg_res_init();
nfc.setRFField(0x00, 0x00);
}
static void sg_nfc_cmd_poll() { //卡号发送
uint16_t SystemCode;
if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, res.mifare_uid, &res.id_len)) {
sg_res_init(0x07);
res.count = 1;
res.type = 0x10;
return;
}
else if (nfc.felica_Polling(0xFFFF, 0x00, res.IDm, res.PMm, &SystemCode, 200) == 1) {//< 0: error
sg_res_init(0x13);
res.count = 1;
res.type = 0x20;
res.id_len = 0x10;
return;
} else {
sg_res_init(1);
res.count = 0;
return;
}
}
static void sg_nfc_cmd_mifare_select_tag() {
sg_res_init();
}
static void sg_nfc_cmd_aime_authenticate() {
sg_res_init();
//AuthenticateBlock(uid,uidLen,block,keyType(A=0,B=1),keyData)
if (nfc.mifareclassic_AuthenticateBlock(req.uid, 4, req.block_no, 1, AimeKey)) {
return;
} else {
res.status = 1;
}
}
static void sg_nfc_cmd_bana_authenticate() {
sg_res_init();
//AuthenticateBlock(uid,uidLen,block,keyType(A=0,B=1),keyData)
if (nfc.mifareclassic_AuthenticateBlock(req.uid, 4, req.block_no, 0, BanaKey)) {
return;
} else {
res.status = 1;
}
}
static void sg_nfc_cmd_mifare_read_block() {//读取卡扇区数据
if (nfc.mifareclassic_ReadDataBlock(req.block_no, res.block)) {
sg_res_init(0x10);
return;
}
sg_res_init();
res.status = 1;
}
static void sg_nfc_cmd_felica_encap() {
uint16_t SystemCode;
if (nfc.felica_Polling(0xFFFF, 0x01, res.encap_IDm, res.poll_PMm, &SystemCode, 200) == 1) {
SystemCode = SystemCode >> 8 | SystemCode << 8;//SystemCode大小端反转注意
}
else {
sg_res_init();
res.status = 1;
return;
}
uint8_t code = req.encap_code;
res.encap_code = code + 1;
switch (code) {
case FELICA_CMD_POLL:
{
sg_res_init(0x14);
res.poll_systemCode[0] = SystemCode;
res.poll_systemCode[1] = SystemCode >> 8;
}
break;
case FELICA_CMD_GET_SYSTEM_CODE:
{
sg_res_init(0x0D);
res.felica_payload[0] = 0x01;//未知
res.felica_payload[1] = SystemCode;//SystemCode
res.felica_payload[2] = SystemCode >> 8;
}
break;
case FELICA_CMD_NDA_A4:
{
sg_res_init(0x0B);
res.felica_payload[0] = 0x00;
}
break;
case FELICA_CMD_NDA_06:
{
uint16_t serviceCodeList[1] = {(uint16_t)(req.serviceCodeList[1] << 8 | req.serviceCodeList[0])};//大小端反转注意
for (uint8_t i = 0; i < req.numBlock; i++) {
uint16_t blockList[1] = {(uint16_t)(req.blockList[i][0] << 8 | req.blockList[i][1])};
if (nfc.felica_ReadWithoutEncryption(1, serviceCodeList, 1, blockList, res.blockData[i]) != 1) {
memset(res.blockData[i], 0, 16);//dummy data
}
}
res.RW_status[0] = 0;
res.RW_status[1] = 0;
res.numBlock = req.numBlock;
sg_res_init(0x0D + req.numBlock * 16);
}
break;
case FELICA_CMD_NDA_08:
{
sg_res_init(0x0C);//此处应有写入卡,但是不打算实现
res.RW_status[0] = 0;
res.RW_status[1] = 0;
}
break;
default:
sg_res_init();
res.status = 1;
}
res.encap_len = res.payload_len;
}

View File

@ -0,0 +1,51 @@
//SG_NFC_CMD_GET_FW_VERSION
E0 [05] 00 09 (30) 00 [ ]
E0 [1D] 00 [ ] (30) 00 17 [54 4E 33 32 4D 53 45 43 30 30 33 53 20 46 2F 57 20 56 65 72 31 2E 32] [ ]
//SG_NFC_CMD_GET_HW_VERSION
E0 [05] 00 [ ] (32) 00 [ ]
E0 [1D] 00 [ ] (32) 00 17 [54 4E 33 32 4D 53 45 43 30 30 33 53 20 48 2F 57 20 56 65 72 33 2E 30] [ ]
//SG_RGB_CMD_GET_INFO
E0 [05] 08 [ ] (F0) 00 [ ]
E0 [0F] 08 [ ] (F0) 00 [09 31 35 30 38 34 FF 10 00 12] [ ]
//SG_NFC_CMD_POLL,FELICA
E0 [05] 00 [ ] (42) 00 [ ]
E0 [19] 00 [ ] (42) 00 13 01 20 10 [ 8 byte IDm ] [ 8 byte PMm ] [ ]
//SG_NFC_CMD_FELICA_ENCAP,Skip reply
E0 [13] 00 [ ] (71) [ ] [ 8 byte IDm ] [ payload_len ] ( ) [ any data ] [ ]
E0 [06] 00 [ ] (71) [01] 00 [ ]
//SG_NFC_CMD_FELICA_ENCAP,FELICA_CMD_POLL
E0 [13] 00 [ ] (71) 0E [ 8 byte IDm ] 06 (00) [FF FF 01 0F] [ ]
E0 [1A] 00 [ ] (71) 00 [14 14] (01) [ 8 byte IDm ] [ 8 byte PMm ] [ 2 byte system_code ] [ ]
//SG_NFC_CMD_FELICA_ENCAP,FELICA_CMD_GET_SYSTEM_CODE
E0 [17] 00 [ ] (71) 12 [ 8 byte IDm ] 0A (0C) [ 8 byte IDm ] [ ]
E0 [13] 00 [ ] (71) 00 [0D 0D] (0D) [ 8 byte IDm ] 01 00 00 [ ]
//SG_NFC_CMD_FELICA_ENCAP,FELICA_CMD_NDA_A4
E0 [18] 00 [ ] (71) 13 [ 8 byte IDm ] 0B (A4) [ 8 byte IDm ] 00 [ ]
E0 [11] 00 [ ] (71) 00 [0B 0B] (A5) [ 8 byte IDm ] 00 [ ]
//SG_NFC_CMD_FELICA_ENCAP,FELICA_CMD_NDA_06,read block
E0 [1D] 00 [ ] (71) 18 [ 8 byte IDm ] 10 (06) [ 8 byte IDm ] [ Service len ] [ Service Code(little-endian) ] [ Block len ] [ Block Code(big-endian) ] [ ]
E0 [23] 00 [ ] (71) 00 [1D 1D] (07) [ 8 byte IDm ] [00 00] [ Block len ] [ 16 byte Block Data ] [ ]
//SG_NFC_CMD_FELICA_ENCAP,FELICA_CMD_NDA_06, read multiple blocks,Service Code=0x000B(read-only),Block Code={0x8082,0x8086,0x8090,0x8091}
E0 [23] 00 [ ] (71) 1E [ 8 byte IDm ] 16 (06) [ 8 byte IDm ] [01] [0B 00] [04] [80 82] [80 86] [80 90] [80 91] [ ]
E0 [53] 00 [ ] (71) 00 [4D 4D] (07) [ 8 byte IDm ] [00 00] [04] [ 8 byte IDm ] [ 16*len byte Block Data ] [ ]
//SG_NFC_CMD_FELICA_ENCAP,FELICA_CMD_NDA_08,write block,Service Code=0x0009(read/write),Block Code=0x8080
E0 [2D] 00 [ ] (71) 28 [ 8 byte IDm ] 20 (08) [ 8 byte IDm ] [01] [09 00] [01] [80 80] [ 16 byte Block Data ] [ ]
E0 [12] 00 [ ] (71) 00 [0C 0C] (09) [ 8 byte IDm ] [00 00] [ ]
//SG_NFC_CMD_POLL,MIFARE
E0 [05] 00 [ ] (42) 00 [ ]
E0 [0D] 00 [ ] (42) 00 07 01 10 04 [ 4 byte UID ] [ ]
//SG_NFC_CMD_MIFARE_READ_BLOCK
E0 [0A] 00 [ ] (52) 05 [ 4 byte UID ] [ block_no ] 84
E0 [16] 00 [ ] (52) 00 10 [ 16 byte block data ] [ ]

View File

@ -0,0 +1,80 @@
+Sector: 0
1DB40F0DAB880400C832002000000016
00000000000000000000000000000000
00000000000010114514191981023333
57434346763208778F11574343467632
+Sector: 1
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
57434346763208778F11574343467632
+Sector: 2
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
57434346763208778F11574343467632
+Sector: 3
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
57434346763208778F11574343467632
+Sector: 4
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
57434346763208778F11574343467632
+Sector: 5
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
57434346763208778F11574343467632
+Sector: 6
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
57434346763208778F11574343467632
+Sector: 7
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
57434346763208778F11574343467632
+Sector: 8
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
57434346763208778F11574343467632
+Sector: 9
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
57434346763208778F11574343467632
+Sector: 10
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
57434346763208778F11574343467632
+Sector: 11
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
57434346763208778F11574343467632
+Sector: 12
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
57434346763208778F11574343467632
+Sector: 13
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
57434346763208778F11574343467632
+Sector: 14
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
57434346763208778F11574343467632
+Sector: 15
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
57434346763208778F11574343467632

View File

@ -0,0 +1,244 @@
N.B. Quoted strings are NOT NUL-terminated unless otherwise noted.
Useful reading: https://www.nxp.com/docs/en/data-sheet/MF1S50YYX_V1.pdf
(AiMe branded cards are Mifare Classic cards. Other technologies exist though)
Summary
-------
Hardware:
Assembly connector:
5V Host in
Tx;Rx;GND Host RS232 in
Tx;Rx;GND Daisy-chain out
Main board (probably LED controller):
Silk "837-15084"
CN1: Host 5V power in, ?V NFC-sub power out
CN2: Host RS232 Tx;Rx;GND in, NFC-sub Tx;Rx out
CN3: LED-Sub power and data(?) out
DIPSW1: Set to hex nibble 8.
Contains ADM3222 RS232 transceiver IC
Contains ATMega32 MCU
NFC subboard:
Sticker: Model "TN32MSEC003S"
CN1: ?V power and Tx;Rx;GND in, Tx;Rx;GND ass'y CN daisy out.
DIPSW1: Set to hex nibble 0.
Contains ATmega168 MCU
Contains ADM3202A RS232 transceiver IC
Contains shielded RF circuit
Entire non-ground-plane PCB area is visible through the chassis
torx screws lol
LED subboard:
Silk: "837-15120"
CN1: ?V power and Tx;Rx;GND in.
Five RGB LEDs and a bunch of resistors
No visible logic ICs..?
No DIPSW.
JVS framing:
E0 sync
D0 escape (+1 to unescape)
Checksum is sum of bytes after unescaping
Frame header:
Frame length (including length byte itself)
Address
Sequence no, hopefully loops before hitting esc byte...
Command byte
Bus addressing:
Low nibble set using DIPSWs
High nibble ???
Daisy chaining mechanism unknown (RS232 wires probably multi-tap)
Startup
-------
Addr 00 Command 62:
Req:
00 Payload length
Resp:
00 Status byte
00 Payload length
Description:
Unknown. Reset?
Addr 00 Command 30:
Req:
00 Payload length
Resp:
00 Status byte
17 Payload length
.. "TN32MSEC003S F/W Ver1.2E"
Description:
Get firmware version
Addr 00 Command 32:
Req:
00 Payload length
Resp:
00 Status byte
17 Payload length
.. "TN32MSEC003S H/W Ver3.0J"
Description:
Get hardware version
Addr 08 Command f5:
Req:
00 Payload length
Resp:
00 Status byte
00 Payload length
Description:
LED sub-board reset.
Won't accept LED commands until you do this.
Addr 08 Command f0:
Req:
00 Payload length
Resp:
00 Status byte
09 Payload length
.. "15084" (part nr for LED board)
FF ??
11 ??
00 ??
12 ??
Description:
Get board "info"
Addr 00 Command 54:
Req:
06 Payload length
57 'W'
43 'C'
43 'C'
46 'F'
76 'v'
32 '2'
Resp:
00 Status byte?
00 ??
Description:
Set Mifare KeyA.
"WCCF" might refer this this SEGA arcade game:
https://en.wikipedia.org/wiki/World_Club_Champion_Football
It's quite old and has AiMe readers, maybe where they first appeared?
Addr 00 Command 50:
Req:
06 Payload length
60 ??
90 ??
D0 ?? (This is escaped of course)
06 ??
32 ??
F5 ??
Resp:
00 Status byte
00 Payload length
Description:
Possibly Mifare KeyB.
Polling
-------
Addr 00 Command 40:
Req:
01 Payload length
03 ??
Resp:
00 Status byte
00 Payload length
Description:
Poll some other NFC technology?
Addr 00 Command 42:
Req:
00 Payload length
Resp if no MiFare card:
00 Status byte
01 Payload length
00 (represents nothing i guess)
Resp if MiFare card:
00 Status byte?
07 Payload length
01 Chunk length?
10 ?? Block size maybe?
04 Chunk length?
.. Mifare UID, four bytes.
Description:
Check for Mifare card presence?
Addr 00 Command 41:
Req:
00 Payload length
Resp:
00 Status byte
00 Payload length
Description:
Unknown. Poll some other NFC technology?
Card read
---------
Addr 00 Command 43:
Req:
04 Payload length
.. Mifare UID, four bytes.
Resp:
00 Status byte
00 Payload length
Description:
Select MiFare by UID?
Addr 00 Command 55:
Req:
05 Payload length
.. Mifare UID, four bytes.
03 ??
Resp:
00 Status byte
00 Payload length
Description:
Unknown.
Block 3 on a Mifare sector contains keys and an access control list.
It is generally not accessed directly (unless being provisioned?)
Addr 00 Command 52:
Req:
05 Payload length
.. Mifare UID, four bytes.
.. Block number, 1 or 2.
Resp for Block 1:
00 Status byte
10 Payload length (1 block)
.. "SBSD"
00 00 00 00
00 00 00 00
00 4E C6 22
Resp for Block 2:
00 Status byte
10 Payload length (1 block)
.. 00 00 00 00 00 00 xx xx
xx xx xx xx xx xx xx xx
Description:
Probably reads blocks 1 and 2 from Mifare sector 0.
Block 0 contains the "vendor information" and UID.
Block 1 contents are unknown, probably AiMe DB info.
Block 2 last 10 bytes hex are printed on the card ("local unique id").
(Block 3 contains encryption keys so is not allowed to be read)
LED
---
Addr 08 Command 81:
Req:
03 Payload length
ff Red intensity
ff Green intensity
ff Blue intensity
Resp:
None! Command is not acknowledged
Description:
Set LED color

View File

@ -0,0 +1,44 @@
#include <windows.h>
int main() {
HANDLE com;
com = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
DCB dcbSerialParams = {0};
COMMTIMEOUTS timeouts = {0};
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
GetCommState(com, &dcbSerialParams);
dcbSerialParams.BaudRate = 115200;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
SetCommState(com, &dcbSerialParams);
timeouts.ReadIntervalTimeout = 1;
timeouts.ReadTotalTimeoutConstant = 1;
timeouts.ReadTotalTimeoutMultiplier = 1;
timeouts.WriteTotalTimeoutConstant = 1;
timeouts.WriteTotalTimeoutMultiplier = 1;
SetCommTimeouts(com, &timeouts);
EscapeCommFunction(com, SETDTR);
EscapeCommFunction(com, SETRTS);
com = CreateFile("\\\\.\\COM12", GENERIC_READ | GENERIC_WRITE, 0, 0,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
GetCommState(com, &dcbSerialParams);
dcbSerialParams.BaudRate = 38400;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
SetCommState(com, &dcbSerialParams);
timeouts.ReadIntervalTimeout = 1;
timeouts.ReadTotalTimeoutConstant = 1;
timeouts.ReadTotalTimeoutMultiplier = 1;
timeouts.WriteTotalTimeoutConstant = 1;
timeouts.WriteTotalTimeoutMultiplier = 1;
SetCommTimeouts(com, &timeouts);
EscapeCommFunction(com, SETDTR);
EscapeCommFunction(com, SETRTS);
return 0;
}

Binary file not shown.

View File

@ -0,0 +1,137 @@
#if defined(__AVR_ATmega32U4__) || defined(ARDUINO_SAMD_ZERO)
#pragma message "当前的开发板是 ATmega32U4 或 SAMD_ZERO"
#define SerialDevice SerialUSB
#define PN532_SPI_SS 10 //32U4 不使用 SPI 时,执行 ReadWithoutEncryption 会失败
#elif defined(__AVR_ATmega328P__)
#pragma message "当前的开发板是 UNO/NANO/Micro"
#define SerialDevice Serial
#elif defined(ARDUINO_NodeMCU_32S)
#pragma message "当前的开发板是 NodeMCU_32S"
#define SerialDevice Serial
#define PN532_SPI_SS 5
#else
#error "未经测试的开发板,请检查串口和阵脚定义"
#endif
#if defined(PN532_SPI_SS)
#pragma message "使用 SPI 连接 PN532"
#include <SPI.h>
#include <PN532_SPI.h>
PN532_SPI pn532(SPI, PN532_SPI_SS);
#else
#include <Wire.h>
#include <PN532_I2C.h>
PN532_I2C pn532(Wire);
#endif
#include "PN532.h"
PN532 nfc(pn532);
typedef union {
uint8_t block[18];
struct {
uint8_t IDm[8];
uint8_t PMm[8];
union {
uint16_t SystemCode;
uint8_t System_Code[2];
};
};
} Card;
Card card;
uint8_t AimeKey[6] = {0x57, 0x43, 0x43, 0x46, 0x76, 0x32};
uint8_t BanaKey[6] = {0x60, 0x90, 0xD0, 0x06, 0x32, 0xF5};
uint8_t MifareKey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
#define M2F_B 1
uint16_t blockList[4] = {0x8080, 0x8081, 0x8082, 0x8083};
uint16_t serviceCodeList[1] = {0x000B};
uint8_t blockData[1][16];
void setup() {
SerialDevice.begin(115200);
// Wire.setClock(800000);
while (!SerialDevice);
nfc.begin();
while (!nfc.getFirmwareVersion()) {
SerialDevice.println("Didn't find PN53x board");
delay(500);
}
SerialDevice.println("START!");
nfc.setPassiveActivationRetries(0x10);
nfc.SAMConfig();
}
void loop() {
uint8_t uid[4], uL;
if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uL) && nfc.mifareclassic_AuthenticateBlock(uid, uL, 1, 1, AimeKey)) {
SerialDevice.println("Aime card!");
SerialDevice.print("UID Value:");
nfc.PrintHex(uid, uL);
SerialDevice.print("Block 2 Data:");
if (nfc.mifareclassic_ReadDataBlock(2, card.block)) {
nfc.PrintHex(card.block, 16);
}
delay(2000);
return;
}
if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uL) && nfc.mifareclassic_AuthenticateBlock(uid, uL, 1, 0, BanaKey)) {
SerialDevice.println("Banapassport card!");
SerialDevice.print("UID Value:");
nfc.PrintHex(uid, uL);
SerialDevice.print("Block 2 Data:");
if (nfc.mifareclassic_ReadDataBlock(2, card.block)) {
nfc.PrintHex(card.block, 16);
}
delay(2000);
return;
}
if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uL) && nfc.mifareclassic_AuthenticateBlock(uid, uL, M2F_B, 0, MifareKey)) {
SerialDevice.println("Default Key Mifare!");
if (nfc.mifareclassic_ReadDataBlock(2, card.block)) {
SerialDevice.print("Fake IDm:");
nfc.PrintHex(card.IDm, 8);
SerialDevice.print("Fake PMm:");
nfc.PrintHex(card.PMm, 8);
}
delay(2000);
return;
}
if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uL)) {
SerialDevice.println("Unknown key Mifare.");
SerialDevice.print("UID Value:");
nfc.PrintHex(uid, uL);
delay(2000);
return;
}
if (nfc.felica_Polling(0xFFFF, 0x01, card.IDm, card.PMm, &card.SystemCode, 200)) {
SerialDevice.println("FeliCa card!");
SerialDevice.print("IDm:");
nfc.PrintHex(card.IDm, 8);
SerialDevice.print("PMm:");
nfc.PrintHex(card.PMm, 8);
SerialDevice.print("SystemCode:");
card.SystemCode = card.SystemCode >> 8 | card.SystemCode << 8;
nfc.PrintHex(card.System_Code, 2);
Serial.println("FeliCa Block:");
for (uint8_t i = 0; i < 4; i++) {
if (nfc.felica_ReadWithoutEncryption(1, serviceCodeList, 1, &blockList[i], blockData) == 1) {
Serial.println(blockList[i], HEX);
nfc.PrintHex(blockData[0], 16);
} else {
Serial.println("error");
}
}
delay(2000);
return;
}
SerialDevice.println("Didn't find card");
delay(500);
}

Binary file not shown.

View File

@ -0,0 +1,142 @@
#include <assert.h>
#include "board/sg-cmd.h"
#include "board/sg-frame.h"
#include "hook/iobuf.h"
#include "util/dprintf.h"
union sg_req_any {
struct sg_req_header req;
uint8_t bytes[256];
};
union sg_res_any {
struct sg_res_header res;
uint8_t bytes[256];
};
static HRESULT sg_req_validate(const void *ptr, size_t nbytes);
static void sg_res_error(
struct sg_res_header *res,
const struct sg_req_header *req);
static HRESULT sg_req_validate(const void *ptr, size_t nbytes)
{
const struct sg_req_header *req;
size_t payload_len;
assert(ptr != NULL);
if (nbytes < sizeof(*req)) {
dprintf("SG Cmd: Request header truncated\n");
return E_FAIL;
}
req = ptr;
if (req->hdr.frame_len != nbytes) {
dprintf("SG Cmd: Frame length mismatch: got %i exp %i\n",
req->hdr.frame_len,
(int) nbytes);
return E_FAIL;
}
payload_len = req->hdr.frame_len - sizeof(*req);
if (req->payload_len != payload_len) {
dprintf("SG Cmd: Payload length mismatch: got %i exp %i\n",
req->payload_len,
(int) payload_len);
return E_FAIL;
}
return S_OK;
}
void sg_req_transact(
struct iobuf *res_frame,
const uint8_t *req_bytes,
size_t req_nbytes,
sg_dispatch_fn_t dispatch,
void *ctx)
{
struct iobuf req_span;
union sg_req_any req;
union sg_res_any res;
HRESULT hr;
assert(res_frame != NULL);
assert(req_bytes != NULL);
assert(dispatch != NULL);
req_span.bytes = req.bytes;
req_span.nbytes = sizeof(req.bytes);
req_span.pos = 0;
hr = sg_frame_decode(&req_span, req_bytes, req_nbytes);
if (FAILED(hr)) {
return;
}
hr = sg_req_validate(req.bytes, req_span.pos);
if (FAILED(hr)) {
return;
}
hr = dispatch(ctx, &req, &res);
if (hr != S_FALSE) {
if (FAILED(hr)) {
sg_res_error(&res.res, &req.req);
}
sg_frame_encode(res_frame, res.bytes, res.res.hdr.frame_len);
printf("req: ");
for (uint8_t i = 0; i < req_nbytes; i++)
printf("%02X ", req_bytes[i]);
printf("\n");
printf("res: ");
for (uint8_t i = 0; i < res_frame->pos; i++)
printf("%02X ", res_frame->bytes[i]);
printf("\n");
}
}
void sg_res_init(
struct sg_res_header *res,
const struct sg_req_header *req,
size_t payload_len)
{
assert(res != NULL);
assert(req != NULL);
res->hdr.frame_len = sizeof(*res) + payload_len;
res->hdr.addr = req->hdr.addr;
res->hdr.seq_no = req->hdr.seq_no;
res->hdr.cmd = req->hdr.cmd;
res->status = 0;
res->payload_len = payload_len;
}
static void sg_res_error(
struct sg_res_header *res,
const struct sg_req_header *req)
{
assert(res != NULL);
assert(req != NULL);
res->hdr.frame_len = sizeof(*res);
res->hdr.addr = req->hdr.addr;
res->hdr.seq_no = req->hdr.seq_no;
res->hdr.cmd = req->hdr.cmd;
res->status = 1;
res->payload_len = 0;
}

184
Mai2TouchOK/Mai2TouchOK.ino Normal file
View File

@ -0,0 +1,184 @@
//高敏度低延迟可能串音版本
//#define SerialDevice SerialUSB //32u4,samd21
#define SerialDevice Serial //esp8266
// bitWrite 不支持 uint64_t以下定义来自 https://forum.arduino.cc/t/bitset-only-sets-bits-from-0-to-31-previously-to-15/193385/5
#define bitSet64(value, bit) ((value) |= (bit<32?1UL:1ULL) <<(bit))
#define bitClear64(value, bit) ((value) &= ~(bit<32?1UL:1ULL) <<(bit))
#define bitWrite64(value, bit, bitvalue) (bitvalue ? bitSet64(value, bit) : bitClear64(value, bit))
#define Threshold 10 //触摸触发阈值
#include "Adafruit_MPR121.h"//mpr121定义
Adafruit_MPR121 mprA, mprB, mprC;
#define CLAMP(val) (val < 0 ? 0 : (val > 255 ? 255 : val))
uint8_t packet[6];
uint8_t len = 0;//当前接收的包长度
#define sA1(x) bitWrite(TPD, 0, x)//设置 sensor
#define sB1(x) bitWrite(TPD, 8, x)
#define sC1(x) bitWrite(TPD, 16, x)
#define sD1(x) bitWrite(TPD, 18, x)
#define sE1(x) bitWrite(TPD, 26, x)
enum {
commandRSET = 0x45,//E
commandHALT = 0x4C,//L
commandSTAT = 0x41,//A
commandRatio = 0x72,//r
commandSens = 0x6B,//k
};
bool Conditioning = 1;
void setup() {
SerialDevice.begin(9600);
SerialDevice.setTimeout(0);
uint8_t TOUCH = 1;//按下敏感度
uint8_t RELEASE = 1;//松开敏感度
mprA.begin(0x5A, &Wire);
mprB.begin(0x5C, &Wire);
mprC.begin(0x5B, &Wire);
Wire.setClock(800000);
// delay(100);
// MprSetup(mprA);
// MprSetup(mprB);
// MprSetup(mprC);
// delay(100);
}
void loop() {
Recv();
Conditioning ? void() : TouchSend();//只有不处于设定模式时才发送触摸数据
}
void MprSetup(Adafruit_MPR121 cap) {//mpr121自定义初始化
cap.writeRegister(MPR121_SOFTRESET, 0x63);//MprReset
delay(1);
cap.writeRegister(MPR121_ECR, 0x0);//MprStop
cap.writeRegister(MPR121_MHDR, 1);// 上升最大变化值
cap.writeRegister(MPR121_NHDR, 4);//上升幅度
cap.writeRegister(MPR121_NCLR, 8);//上升修正样本个数
cap.writeRegister(MPR121_FDLR, 0);//修正前等待样本个数
cap.writeRegister(MPR121_MHDF, 1);//下降最大变化值
cap.writeRegister(MPR121_NHDF, 1);//下降幅度
cap.writeRegister(MPR121_NCLF, 4);//下降修正样本个数
cap.writeRegister(MPR121_FDLF, 4);//修正前等待样本个数
cap.writeRegister(MPR121_NHDT, 0);
cap.writeRegister(MPR121_NCLT, 0);
cap.writeRegister(MPR121_FDLT, 0);
// cap.writeRegister(MPR121_ESI,MPR121_ESI_1MS);/
cap.setThresholds(Threshold, Threshold); //设置触发阈值和充放电流时间
cap.writeRegister(MPR121_DEBOUNCE, (4 << 4) | 2); //设置采样数,0
cap.writeRegister(MPR121_CONFIG1, 16);//0x10
cap.writeRegister(MPR121_CONFIG2, 1<<5);
cap.writeRegister(MPR121_AUTOCONFIG0, 0x0B);
cap.writeRegister(MPR121_AUTOCONFIG1, (1 << 7));
cap.writeRegister(MPR121_UPLIMIT, 202);//上限,((Vdd - 0.7)/Vdd) * 256
cap.writeRegister(MPR121_TARGETLIMIT, 182);//目标UPLIMIT * 0.9
cap.writeRegister(MPR121_LOWLIMIT, 131);//下限UPLIMIT * 0.65
cap.writeRegister(MPR121_ECR, B01000000 + 12);//MprRun
}
void cmd_RSET() {//Reset
MprSetup(mprA);
MprSetup(mprB);
MprSetup(mprC);
}
void cmd_HALT() {//Start Conditioning Mode
mprA.writeRegister(MPR121_ECR, 0x0);//MprStop
mprB.writeRegister(MPR121_ECR, 0x0);
mprC.writeRegister(MPR121_ECR, 0x0);
Conditioning = true;
}
void cmd_Ratio() {//Set Touch Panel Ratio
SerialDevice.write((byte)'(');
SerialDevice.write((byte)packet[1]);//L,R
SerialDevice.write((byte)packet[2]);//sensor
SerialDevice.write((byte)'r');
SerialDevice.write((byte)packet[4]);//Ratio
SerialDevice.write((byte)')');
}
void cmd_Sens() {//Set Touch Panel Sensitivity
SerialDevice.write((byte)'(');
SerialDevice.write((byte)packet[1]);//L,R
SerialDevice.write((byte)packet[2]);//sensor
SerialDevice.write((byte)'k');
SerialDevice.write((byte)packet[4]);//Sensitivity
SerialDevice.write((byte)')');
}
void cmd_STAT() { //End Conditioning Mode
Conditioning = false;
mprA.writeRegister(MPR121_ECR, B10000000 + 12);//MprRun
mprB.writeRegister(MPR121_ECR, B10000000 + 12);
mprC.writeRegister(MPR121_ECR, B10000000 + 12);
}
void Recv() {
while (SerialDevice.available()) {
uint8_t r = SerialDevice.read();
if (r == '{') {
len = 0;
}
if (r == '}') {
break;
}
packet[len++] = r;
}
if (len == 5) {
switch (packet[3]) {
case commandRSET:
cmd_RSET();
break;
case commandHALT:
cmd_HALT();
break;
case commandRatio:
cmd_Ratio();
break;
case commandSens:
cmd_Sens();
break;
case commandSTAT:
cmd_STAT();
break;
}
len = 0;
memset(packet, 0, 6);
}
}
void TouchSend() {
uint64_t TouchData = 0; //触摸数据包
// 简单方法,从 mpr.touched() 一次读取 12个触摸点的按下状态需要正确配置 mpr121 的各种参数值才能获取准确的状态
TouchData = (TouchData | mprC.touched()) << 12;
TouchData = (TouchData | mprB.touched()) << 12;
TouchData = (TouchData | mprA.touched());
// 高级方法,读取每个触摸点的 baselineData 和 filteredData可以单独设置敏感度过滤
// for (uint8_t i = 0; i < 12; i++) {
// bitWrite64(TouchData, i, CLAMP(mprA.baselineData(i) - mprA.filteredData(i)+20) > 27);//另外一种检测方法
// Serial.print(CLAMP(mprA.baselineData(i) - mprA.filteredData(i)+20));
// Serial.print(" ");
// }
// for (uint8_t i = 0; i < 12; i++) {
// bitWrite64(TouchData, i+12, CLAMP(mprB.baselineData(i) - mprB.filteredData(i)+20) > 30);//另外一种检测方法
//// Serial.print(CLAMP(mprA.baselineData(i) - mprA.filteredData(i)+20));
//// Serial.print(" ");
// }
// for (uint8_t i = 0; i < 12; i++) {
// bitWrite64(TouchData, i+24, CLAMP(mprC.baselineData(i) - mprC.filteredData(i)+20) > 30);//另外一种检测方法
//// Serial.print(CLAMP(mprA.baselineData(i) - mprA.filteredData(i)+20));
//// Serial.print(" ");
// }
//Serial.println("");
SerialDevice.write((byte)'(');
for (uint8_t r = 0; r < 7; r++) {
SerialDevice.write((byte)TouchData & B11111);
TouchData >>= 5;
}
SerialDevice.write((byte)')');
}

234
README.md Normal file
View File

@ -0,0 +1,234 @@
# 自制省钱高性价比maimaiDX控制器$全!$解
## 第零部分 协议
### 对于请勿侵犯SEGA以及WAHLAP权益的声明
本作仅适用于个人制作游玩原则上不提倡也不反对制作后发售。若发售或由读者自己的行为造成不当影响和任何纠纷本教程作者和本教程不负任何责任。请勿侵犯SEGA以及WAHLAP权益。若作者行为或是本教程产生了不良影响我对我的行为表示抱歉并可修补甚至撤下本教程希望大家的理解。
当大家在展示自己手台效果的时候,不要发布不该发布的东西,让我们一起遵守。
### 对于协力者,贡献者的感谢
我的触摸代码部分几乎完全是mai2touch的项目搬来的然后更改修复了一些mpr121配置上的问题配置部分借鉴了Arduino-Chunithm-Controller的代码。读卡器使用的也是同作者的Arduino-Aime-Reader。在此对mai2touchArduino-Chunithm-Controller,Arduino-Aime-Reader和它们的作者表示衷心的感谢如果没有mai2touch和Arduino-Chunithm-Controller以及对它们下了很多心血的作者我的控制器将失去它最重要的灵魂。
在此感谢[Sucareto](https://github.com/Sucareto)以及他的项目[Arduino-Aime-Reader](https://github.com/Sucareto/Arduino-Aime-Reader)[Arduino-Chunithm-Controller](https://github.com/Sucareto/Arduino-Chunithm-Controller)和[mai2Touch](https://github.com/Sucareto/Mai2Touch)!
传承开源精神我将自用的程序开源但是仅仅用于我手里的硬件测试成功不同的走线制作方法使用的mpr配置等也不一样不建议照搬。
### 对于本教程版权的声明
本教程是由Helloworld_Dk纯手打写出的思路也是几乎完全自己想的。
如果我能给大家的想法产生帮助,我感到非常荣幸,如果您觉得我的方案对您有帮助,希望可以在您的项目发布时,跟上一句"本方案的部分灵感来自Dk"或类似的话,我将不甚感激
## 第一部分 总览
本方案是使用导电膜裁切漆包线引出的触摸部分为核心的低价maimai控制器制作方案。由于被~~逼到无奈~~很多也像制作maimai手台的玩家支持而写了这样的一个教程。
### [方案效果展示](https://www.bilibili.com/video/BV1pe4y1m7pr/)
### [使用32u4的延迟更新后的展示](https://www.bilibili.com/video/BV1PW4y1E7io/)
todo: 按键的相关制作教程
stat: 延迟的问题已经被消除
## 第二部分 目录
- 第零部分 协议
- 对于请勿侵犯SEGA以及WAHLAP权益的声明
- 对于协力者,贡献者的感谢
- 对于本教程版权的声明
- 第一部分 总览
- 介绍
- 方案效果展示
- todo和stat
- 第二部分 目录
- 目录
- 第三部分 触屏制作指导
- 第四部分 读卡器个人思路分享
- 第五部分 已删除
- 第六部分 按键和灯光(未完成)
## 第三部分 触屏制作指导
**这位更是重量级**
整个制作是非常的省钱非常的好用像是铝箔贴的chuni控制器纯手作省钱能用就行精神是这样的。
本教程与之前发布的[专栏](https://www.bilibili.com/read/cv19157015)相互照应,相互补充,建议一起阅读
```圣经
做 手 台 不 试 试 瞎 搞 手 作 便 宜 省 钱 , 就 像 四 大 名 著 不 看 红 楼 梦 说 明 这 个 人 文 学 造 诣 和 自 我 修 养 不 足 他 理 解 不 了 这 种 内 在 的 阳 春 白 雪 的 高 雅 艺 术 他 只 能 看 到 外 表 的 辞 藻 堆 砌 参 不 透 其 中 深 奥 的 精 神 内 核 他 整 个 人 的 层 次 就 卡 在 这 里 了 只 能 度 过 一 个 相 对 失 败 的 人 生。
```
**你需要的材料:**
- 触摸部分
- 尺寸合适的ito导电膜一定要买带胶除非你真的想好了胶怎么办。下面会有为什么要这么做的说明。
- 一张较薄的亚克力板或者玻璃板,尺寸至少覆盖圆形屏幕区域。此处感觉玻璃容易碎,亚克力容易划花,大家自行斟酌。本人使用的一张比亚克力优秀的塑料板。
- 较细的漆包线
- 你超棒的手工能力
- 透明胶带
- 屏幕部分
- 一块跟你触摸板相对应的合适大小的屏幕,本教程应该不适用于小于十寸的屏幕。
- 程序和mcu
- 本人使用的mai2touch项目并对其进行修改相关链接在教程开头协议部分。
- Arduino或类似的开发板我建议使用pro micro不建议使用使用了ch340串口ic的板子。但是我使用pro micro被喷过和嘲讽过.....别人说它性能太差,可是我真的喜欢。
**制作过程详解**
这部分在专栏讲的很详细,特别是图片详细。
统共有以下步骤:
### 1\. 打印对照纸
说的玄乎其实就是用一张A4纸或类似的纸将触摸区块图片打印下来。尺寸要与你目标屏幕显示的大小完全对应。建议打印至少两张。
<img desc = "打印对照纸">
### 2\. 裁切
把a4纸跟买到的触摸膜贴在一起固定好然后根据a4纸裁下每一片触摸区块
注意一定要固定好哦,不然剪着剪着位移了剪除奇形怪状的区块可就难办了!
## 3\.粘贴
首先你不是打印了两张A4纸吗一张被剪了另一张好好的请规划好位置之后把A4纸打印面朝向亚克力板或玻璃板粘贴在另一面应该可以透过亚克力板或者玻璃板看到另一面清晰的打印的区块。用于之后粘贴位置的对应。
导电膜是这样的结构:
不导电面朝上,导电面朝下,依次是:
0. 一层保护膜
1. (如果是带胶的才有)一层透明胶
2. 聚酯离型膜,自此间隔,上面称为不导电面,下面称为导电面,不导电面有胶,导电面无胶
3. ito导电涂层
4. 一层保护膜
在**粘贴**这一步,我们需要完成的是
0. 亚克力或玻璃板
1. (如果是带胶的自带,没带胶的会让你痛苦到怀疑人生)一层透明胶
2. 聚酯离型膜
3. ito导电涂层
4. 一层保护膜
假设没有买带胶的膜,就自行点胶(透明的,支持粘贴塑料的,不会腐蚀塑料的,固化不回缩的,粘贴可以调整的)固定每片触摸块到屏幕上。
在写这篇教程的时候我自己搞的胶腐蚀了我的塑料板导致A5区域透明性下降...**距今已过去十分钟,警钟敲烂**,所以千万别贪便宜买不带胶的!
需要撕下不导电面的保护膜。不导电面朝向塑料板粘贴,**请勿将导电面贴到塑料板上**。
如果是买的带胶的导电膜,不导电面恰好是带胶面,裁好了可以轻松粘在塑料板上。完全不用经历大力对抗胶水的那一步。
~~唉,这个地方别省钱,要不让你感受人间痛苦,宛如晚清十大酷刑。~~
~~自己搞胶的痛苦,详见专栏图片。~~
## 4\.引线
在**引线**这一步,我们需要完成的是
0. 亚克力或玻璃板
1. 一层透明胶
2. 聚酯离型膜
3. ito导电涂层
4. 漆包线
5. 单面带胶的透明胶带
之前我说的是
```
要去漆包线的绝缘漆,
撕掉导电面保护膜,
用透明胶带初固定,
尽力沿着区间缝隙走线,
评价是先走中心的c区然后慢慢向外区块做这个是中心思想
其次是按照从下到上 顺序操作的先是最底下的区56那些处理好慢慢往上处理。
这样在处理完一些之后,可以横着贴一张长长的透明胶带把已经完成的区固定好避免之后散掉。
```
依然可以参照,在此补充操作绝对步骤,引用我教别人的聊天记录
```
这样
先把漆包线准备好,头上刮漆打圈
把带胶不导电面贴好
然后准备一段透明胶带
然后把导电面保护膜解开
放漆包线
粘胶带
一气呵成
```
这里有关ito的一个特性是ito遇水二氧化碳容易变质影响导电性能和透明性。
所以在放上漆包线后,应当尽快用一些方法将导电面全部覆盖隔绝空气。我使用的是透明胶带,虽然不是特别好看但是能用。
关于导线具体从区块到触摸板边缘的引出方法详见b站专栏非常详细
```
总的说就是先从底下的中间考虑,也就是看下图先处理底下中间红圈再处理绿圈再处理橙圈再贴一张透明胶带上去。再往上一步一步地处理整个触摸面板。
运用了贪心的思想,中间的区肯定是最需要往外走的,所以需要先考虑,从哪条缝走过去。而边上的很简单就可以引出去,可以在把难搞的安顿好之后再处理。从下往上一是因为可以做一块贴一整块透明胶带,其次是下面不会像上面一样被屏幕挡住无法从边上轻松走线。先把东西往下安顿可以减少后续上面的工作量。
要点:处理顺序、不要吝啬在圆外面的初固定胶带,周围挡起来游玩看不见,减少线乱飞
```
## 4\.接线
详见b站专栏讲的足够详细基本没有要补充的。
```
1.先把arduino跟mpr121的I2C连好。
2.根据程序确定哪个区接在哪个mpr121的哪个io如果你的程序不知道是哪个io把程序写到板子里面用一根螺丝刀按在每个io上看游戏测试汇报哪个区被按下。通常是连续的比如A1到A8是一个mpr的0到7之类的很好确定这个是一种办法。另一种是根据你从触摸面板上接出的漆包线的位置在程序里面规划好哪个接哪个合适不用绕可以走线不像我一样乱糟糟。
3.走漆包线。面对从塑料板边缘引出的34根漆包线无从下手是吗我感觉这样比较好以横向中轴为开始的地方从这里向上下处理。
因为两边的线很容易可以从边上引到中间的mpr121上而偏上偏下的难搞要绕。所以从中间处理会方便很多。走线原则从空闲地方走尽量隔出空隙不要横跨一张mpr121板子上面不能直接 绕,绕最好从左右,下面也可以勉强绕一下。
之前留的漆包线建议足一些,不然这步容易踩坑:要绕一些地方焊接,就需要足够长的线。有时候线发现不够长,这时候如果你发现在透明胶带下走的路径对现在要走的地方不合适,觉得拽回来就可以了,不绕路了,够长了。但是小心!!你一拉那个漆包线,很可能把上面在触摸区域走的打好圈的线拽移位,到其他区或者圈没了。这会让你极其难办。你不得不拆下粘的很牢的透明胶带,把线重新拽回原位打圈放在该在的位置上。透明胶带这种东西撕下来粘第二次透明效果就会很差,所以千万不要乱拽漆包线,返工让你怀疑人生
但是遇到真的不够长怎么办呢,可以尝试用圆滑的焊接接上一段,做好绝缘应该没什么问题。
到这里,你的线接好了。
要点确定好io对应区写在之前打印过的A4纸上对照着纸上面的区块位置接会方便很多。不要拽漆包线。
```
## 5\.程序部分
我使用的是github上的开源项目mai2Touch。曾遇到过问题如下
QA1对于328p编译失败不支持的开发板改改上面的宏定义即可。不建议使用带ch340的328p板子... ~~感觉是延迟的来源,新买的板子还没到...不确定。~~ 就是延迟的来源不用洗我promicro已经到了非常低延迟爱来自32u4。这条QA仅限于你要用UNO/NANO/MICRO才可能会用到但是本人非常不建议使用因为延迟极大
QA2Serial.write 歧义 编译失败在write传入的参数前面强制类型转换为(byte)。如Serial.write('A');改为Serial.write((byte)'A');
QA3:触摸出现问题,莫名触发,串音,灵敏度错:~~我自己重写了mpr121配置部分代码然后就解决了某论坛有配置详细教程这就不发了。~~实际上是从Arduino-Chunithm-Controller搬来的寄存器设定代码并加以改动我摊牌了。
## 6\.游戏连接部分
你好,不可能发游戏的,这里只提一下连接的一些问题
QA1:怎么连的串口直连按照github上的readme走。
QA2:怎么按下在闪不是长按github截止到写这个专栏里面教的写错了。**DummyTouchPanel=1应该是0**1的话是开启了的mai程序自带的调试触摸然后导致自己的设备和自带的调试对着干一个说按了一个说没按就开始闪。设置为0避免程序调试触摸影响。
QA3: 怎么感觉我的触摸没问题,但是程序里面好像跟无响应一样:~~神奇问题你插上电脑之后先用ide传一遍程序进去就会好。用rts|dts小工具试过也不行就是ide传程序可以非常神奇。仅发生在我手上的ch340的arduino nano上其他板子也许没问题。传过程序之后只要不断电reset arduino 不会影响。断电需要重传程序解决。~~ 听国际友人说好像是ch340在arduino上锁i2c什么的我英语很差初中水平看不懂.....反正避免使用328p+ch340即可解决问题。非要用的话每次重新上电需要重刷一遍程序。
~~ 其实我当时这么乱的接线我觉得我必定失败,一定串音,结果上测试程序告诉我静默跟没接一样的一点干扰都没有\(7\-11\)按下能上最高100多的数值\(40\-120\),我超,太美丽了,这必成功,我都不敢相信,但是真的可以!!乱搞万岁 ~~ 删除线挂了,给其它符号转义了也没用,不知道为什么。
~~至此,已成艺术品~~
**至此,触摸部分硬件软件完成,撒花 *★,°*:.☆( ̄▽ ̄)/$:*.°★**
## 第四部分 读卡器个人思路分享
在最新一次发布的b站展示视频中可以看见我的读卡器。他最亮眼的部分是均匀的光。其原理是使用了一块拆机的 **屏幕背光板** 进行光的一个导,视频中并非完成品,要做的更完善,需要使用大一点的背光板,配合侧照的灯,而不是我这粗制滥造的测试机的操作,不过对于全尺寸的灯加上手动侧照已经能达到如此不错的效果,等到完善之后效果应该不言而喻的非常不错。
目前我看到其他大佬们制作的读卡器,没有使用了本方案的,我觉得这个思路对于光的均匀度方面效果不错,于是就发出来供大家参考。
关于读卡器的程序
使用的是Arduino-Aime-Reader项目。但是注意这个项目并不原生支持328p。由于~~我家328p奇多~~读卡器并不需要极低的延迟,所以我考虑了使用~~我家奇多~~ 的arduino nano(328p+ch340)制作,因为成本低。在过程中遇到了一些问题并解决了,如下:
QA1程序无法通过编需要改改宏这个上面说过了。
QA2程序编译正常readertest正常手动发包疑似正常就是游戏不可以发现游戏发送初始化命令发送dts/rts或相关内容的时候会让arduino重启reset触发显然不对劲因为在重启过程中游戏发包arduino不会给出回应。尝试短接rst与3v3强制禁止其重启问题解决。
```
但是usb转串口可以重启arduino不是bug而是一个不错的功能arduino在下载程序的时候需要重启才可以开始。电脑可以让arduino重启本身是为了方便下载程序而做出的美好设计但是在此处影响了功能。
如果直接短接会造成无法正常下载程序。我的解决方法是在rst跟3v3直接加一个开关下载程序就断开当读卡器的时候就闭合。
这样也许会?对复位电路产生略微的短路,但是目前使用效果极佳,就先这样用了。
```
## 第五部分 已删除
## 第六部分 按键和灯光
直接promicro做成键盘就完事这个人人都会不用教网上一抓一大把。
据说可以把灯光塞进跟按键同一个promicro但是我没试过
在todo列表中
待完成
<br>
------
END SEL &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DOCUMENT BY HELLOWORLD_DK