├── .gitattributes ├── .gitignore ├── .idea └── .idea.MU3Input │ └── .idea │ ├── .gitignore │ ├── .name │ ├── indexLayout.xml │ └── vcs.xml ├── DllExport.bat ├── MU3Input.sln ├── MU3Input ├── AimeIO.cs ├── Config.cs ├── Extensions.cs ├── FodyWeavers.xml ├── IO │ ├── HidIO.cs │ ├── IO.cs │ ├── KeyboardIO.cs │ ├── MixedIO.cs │ ├── TcpIO.cs │ ├── UdpIO.cs │ └── UsbmuxIO.cs ├── IOTest.Designer.cs ├── IOTest.cs ├── IOTest.resx ├── Kernel32.cs ├── MU3IO.cs ├── MU3Input.csproj ├── Properties │ └── AssemblyInfo.cs ├── SimpleRawHid.cs ├── User32.cs └── Utils.cs ├── README.md ├── SegaToolsPatch └── 0001-Add-led-code.patch ├── Test ├── App.config ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── Test.csproj └── mu3controller ├── .gitignore ├── .idea ├── .gitignore ├── misc.xml ├── modules.xml ├── untitled1.iml └── vcs.xml ├── include └── README ├── lib ├── NDEF │ ├── Due.h │ ├── LICENSE.txt │ ├── MifareClassic.cpp │ ├── MifareClassic.h │ ├── MifareUltralight.cpp │ ├── MifareUltralight.h │ ├── Ndef.cpp │ ├── Ndef.h │ ├── NdefMessage.cpp │ ├── NdefMessage.h │ ├── NdefRecord.cpp │ ├── NdefRecord.h │ ├── NfcAdapter.cpp │ ├── NfcAdapter.h │ ├── NfcDriver.h │ ├── NfcTag.cpp │ ├── NfcTag.h │ ├── README.md │ ├── examples │ │ ├── CleanTag │ │ │ └── CleanTag.ino │ │ ├── EraseTag │ │ │ └── EraseTag.ino │ │ ├── FormatTag │ │ │ └── FormatTag.ino │ │ ├── P2P_Receive │ │ │ └── P2P_Receive.ino │ │ ├── P2P_Receive_LCD │ │ │ └── P2P_Receive_LCD.ino │ │ ├── P2P_Send │ │ │ └── P2P_Send.ino │ │ ├── ReadTag │ │ │ └── ReadTag.ino │ │ ├── ReadTagExtended │ │ │ └── ReadTagExtended.ino │ │ ├── WriteTag │ │ │ └── WriteTag.ino │ │ └── WriteTagMultipleRecords │ │ │ └── WriteTagMultipleRecords.ino │ ├── keywords.txt │ └── tests │ │ ├── NdefMemoryTest │ │ └── NdefMemoryTest.ino │ │ ├── NdefMessageTest │ │ └── NdefMessageTest.ino │ │ ├── NdefUnitTest │ │ └── NdefUnitTest.ino │ │ └── NfcTagTest │ │ └── NfcTagTest.ino ├── PN532 │ ├── PN532.cpp │ ├── PN532.h │ ├── PN532Interface.h │ ├── PN532_debug.h │ ├── README.md │ ├── emulatetag.cpp │ ├── emulatetag.h │ ├── examples │ │ ├── FeliCa_card_detection │ │ │ └── FeliCa_card_detection.pde │ │ ├── FeliCa_card_read │ │ │ └── FeliCa_card_read.pde │ │ ├── android_hce │ │ │ └── android_hce.ino │ │ ├── emulate_tag_ndef │ │ │ └── emulate_tag_ndef.ino │ │ ├── iso14443a_uid │ │ │ └── iso14443a_uid.pde │ │ ├── mifareclassic_formatndef │ │ │ └── mifareclassic_formatndef.pde │ │ ├── mifareclassic_memdump │ │ │ └── mifareclassic_memdump.pde │ │ ├── mifareclassic_ndeftoclassic │ │ │ └── mifareclassic_ndeftoclassic.pde │ │ ├── mifareclassic_updatendef │ │ │ └── mifareclassic_updatendef.pde │ │ ├── p2p_raw │ │ │ └── p2p_raw.ino │ │ ├── p2p_with_ndef_library │ │ │ └── p2p_with_ndef_library.ino │ │ └── readMifare │ │ │ └── readMifare.pde │ ├── license.txt │ ├── llcp.cpp │ ├── llcp.h │ ├── mac_link.cpp │ ├── mac_link.h │ ├── snep.cpp │ └── snep.h ├── PN532_HSU │ ├── PN532_HSU.cpp │ └── PN532_HSU.h └── README ├── platformio.ini ├── src ├── components │ ├── card_reader.cpp │ ├── card_reader.hpp │ ├── comio.hpp │ ├── keyboard.cpp │ ├── keyboard.hpp │ ├── led_board.cpp │ ├── led_board.hpp │ ├── manager.cpp │ ├── manager.hpp │ ├── ongeki_hardware.cpp │ ├── ongeki_hardware.hpp │ ├── raw_hid.cpp │ ├── raw_hid.hpp │ ├── serial.cpp │ └── serial.hpp ├── eeprom_address.h ├── main.cpp └── stdinclude.hpp └── test └── README /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.idea/.idea.MU3Input/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | # Rider 忽略的文件 5 | /projectSettingsUpdater.xml 6 | /contentModel.xml 7 | /modules.xml 8 | /.idea.MU3Input.iml 9 | # 基于编辑器的 HTTP 客户端请求 10 | /httpRequests/ 11 | # Datasource local storage ignored files 12 | /dataSources/ 13 | /dataSources.local.xml 14 | -------------------------------------------------------------------------------- /.idea/.idea.MU3Input/.idea/.name: -------------------------------------------------------------------------------- 1 | MU3Input -------------------------------------------------------------------------------- /.idea/.idea.MU3Input/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/.idea.MU3Input/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /MU3Input.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.31911.260 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MU3Input", "MU3Input\MU3Input.csproj", "{A90797A6-C546-4233-A183-453AF52319D0}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{678BBC0E-4096-4064-BD0A-D2B311141111}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {A90797A6-C546-4233-A183-453AF52319D0}.Debug|x64.ActiveCfg = Debug|x64 17 | {A90797A6-C546-4233-A183-453AF52319D0}.Debug|x64.Build.0 = Debug|x64 18 | {A90797A6-C546-4233-A183-453AF52319D0}.Release|x64.ActiveCfg = Release|x64 19 | {A90797A6-C546-4233-A183-453AF52319D0}.Release|x64.Build.0 = Release|x64 20 | {678BBC0E-4096-4064-BD0A-D2B311141111}.Debug|x64.ActiveCfg = Debug|x64 21 | {678BBC0E-4096-4064-BD0A-D2B311141111}.Debug|x64.Build.0 = Debug|x64 22 | {678BBC0E-4096-4064-BD0A-D2B311141111}.Release|x64.ActiveCfg = Release|x64 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | GlobalSection(ExtensibilityGlobals) = postSolution 28 | SolutionGuid = {6671AB3E-3857-4E77-8258-F50F9C854AE4} 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /MU3Input/AimeIO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace MU3Input 8 | { 9 | public static class AimiIO 10 | { 11 | [DllExport(CallingConvention = CallingConvention.Cdecl, ExportName = "aime_io_get_api_version")] 12 | public static ushort GetVersion() => 0x0200; 13 | 14 | [DllExport(CallingConvention = CallingConvention.Cdecl, ExportName = "aime_io_init")] 15 | public static uint Init() 16 | { 17 | if (Process.GetCurrentProcess().ProcessName != "amdaemon" && 18 | Process.GetCurrentProcess().ProcessName != "Debug" && 19 | Process.GetCurrentProcess().ProcessName != "Test") 20 | return 1; 21 | 22 | return 0; 23 | } 24 | 25 | [DllExport(CallingConvention = CallingConvention.Cdecl, ExportName = "aime_io_nfc_poll")] 26 | public static uint Poll(byte unitNumber) 27 | { 28 | return 0; 29 | } 30 | 31 | [DllExport(CallingConvention = CallingConvention.Cdecl, ExportName = "aime_io_nfc_get_felica_id")] 32 | public static unsafe uint GetFelicaId(byte unitNumber, ulong* id) 33 | { 34 | if (Mu3IO.IO == null || Mu3IO.IO.Aime.Scan != 2) 35 | { 36 | return 1; 37 | } 38 | else 39 | { 40 | *id = Mu3IO.IO.Aime.IDm; 41 | return 0; 42 | } 43 | } 44 | 45 | [DllExport(CallingConvention = CallingConvention.Cdecl, ExportName = "aime_io_nfc_get_felica_pm")] 46 | public static unsafe uint GetFelicaPm(byte unitNumber, ulong* pm) 47 | { 48 | if (Mu3IO.IO == null || Mu3IO.IO.Aime.Scan != 2) 49 | { 50 | return 1; 51 | } 52 | else 53 | { 54 | *pm = Mu3IO.IO.Aime.PMm; 55 | return 0; 56 | } 57 | } 58 | 59 | [DllExport(CallingConvention = CallingConvention.Cdecl, ExportName = "aime_io_nfc_get_felica_system_code")] 60 | public static unsafe uint GetFelicaSystemCode(byte unitNumber, ushort* systemCode) 61 | { 62 | if (Mu3IO.IO == null || Mu3IO.IO.Aime.Scan != 2) 63 | { 64 | return 1; 65 | } 66 | else 67 | { 68 | *systemCode = Mu3IO.IO.Aime.SystemCode; 69 | return 0; 70 | } 71 | } 72 | 73 | [DllExport(CallingConvention = CallingConvention.Cdecl, ExportName = "aime_io_nfc_get_aime_id")] 74 | public static unsafe uint GetAimeId(byte unitNumber, byte* id, ulong size) 75 | { 76 | if (Mu3IO.IO == null || Mu3IO.IO.Aime.Scan != 1) return 1; 77 | Aime aime = Mu3IO.IO.Aime; 78 | for(int i = 0; i < 10; i++) 79 | { 80 | id[i]=aime.ID[i]; 81 | } 82 | 83 | return 0; 84 | } 85 | 86 | [DllExport(CallingConvention = CallingConvention.Cdecl, ExportName = "aime_io_led_set_color")] 87 | public static void SetColor(byte unitNumber, byte r, byte g, byte b) 88 | { 89 | 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /MU3Input/Config.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Converters; 3 | using Newtonsoft.Json.Linq; 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | 9 | namespace MU3Input 10 | { 11 | public class Config 12 | { 13 | public static Config Instance; 14 | private static string configPath; 15 | static Config() 16 | { 17 | var location = typeof(Mu3IO).Assembly.Location; 18 | string directoryName = Path.GetDirectoryName(location); 19 | configPath = Path.Combine(directoryName, "mu3input_config.json"); 20 | if (File.Exists(configPath)) 21 | { 22 | Instance = JsonConvert.DeserializeObject(File.ReadAllText(configPath), new JsonSerializerSettings()); 23 | } 24 | else 25 | { 26 | Instance = new Config(); 27 | Instance.IO = new List() 28 | { 29 | new IOConfig() 30 | { 31 | Type = IOType.Udp, 32 | Param = 4354, 33 | Part = ControllerPart.All 34 | } 35 | }; 36 | Instance.Save(configPath); 37 | } 38 | } 39 | public void Save() 40 | { 41 | Save(configPath); 42 | } 43 | public void Save(string path) 44 | { 45 | File.WriteAllText(path, JsonConvert.SerializeObject(this, new JsonSerializerSettings() 46 | { 47 | Formatting = Formatting.Indented, 48 | })); 49 | } 50 | private Config() { } 51 | public List IO { get; set; } 52 | } 53 | public class IOConfig 54 | { 55 | [JsonConverter(typeof(StringEnumConverter))] 56 | public IOType Type { get; set; } 57 | public JToken Param { get; set; } 58 | [JsonConverter(typeof(StringEnumConverter))] 59 | public ControllerPart Part { get; set; } 60 | } 61 | 62 | 63 | public enum IOType 64 | { 65 | Hid, Udp, Tcp, Usbmux, Keyboard 66 | } 67 | 68 | [Flags] 69 | public enum ControllerPart 70 | { 71 | None = 0, 72 | L1 = 1 << 0, 73 | L2 = 1 << 1, 74 | L3 = 1 << 2, 75 | LSide = 1 << 3, 76 | LMenu = 1 << 4, 77 | R1 = 1 << 5, 78 | R2 = 1 << 6, 79 | R3 = 1 << 7, 80 | RSide = 1 << 8, 81 | RMenu = 1 << 9, 82 | Lever = 1 << 10, 83 | Aime = 1 << 11, 84 | LKeyBoard = L1 | L2 | L3, 85 | RKeyBoard = R1 | R2 | R3, 86 | Side = LSide | RSide, 87 | Menu = LMenu | RMenu, 88 | KeyBoard = LKeyBoard | RKeyBoard, 89 | Left = LKeyBoard | LSide | LMenu, 90 | Right = RKeyBoard | RSide | RMenu, 91 | GameButtons = KeyBoard | Side, 92 | Buttons = GameButtons | Menu, 93 | GamePlay = GameButtons | Lever, 94 | All = GamePlay | Menu | Aime, 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /MU3Input/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace MU3Input 5 | { 6 | public static class Extensions 7 | { 8 | public static T ToStructure(this byte[] bytes) where T : struct 9 | { 10 | var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); 11 | try 12 | { 13 | return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); 14 | } 15 | finally 16 | { 17 | handle.Free(); 18 | } 19 | } 20 | public static byte[] ToBcd(this BigInteger value) 21 | { 22 | var length = value.ToString().Length / 2 + value.ToString().Length % 2; 23 | byte[] ret = new byte[length]; 24 | for (int i = length - 1; i >= 0; i--) 25 | { 26 | ret[i] = (byte)(value % 10); 27 | value /= 10; 28 | ret[i] |= (byte)((value % 10) << 4); 29 | value /= 10; 30 | } 31 | return ret; 32 | } 33 | public static byte[] ToBcd(this ulong value) 34 | { 35 | var length = value.ToString().Length / 2 + value.ToString().Length % 2; 36 | byte[] ret = new byte[length]; 37 | for (int i = length - 1; i >= 0; i--) 38 | { 39 | ret[i] = (byte)(value % 10); 40 | value /= 10; 41 | ret[i] |= (byte)((value % 10) << 4); 42 | value /= 10; 43 | } 44 | return ret; 45 | } 46 | 47 | } 48 | } -------------------------------------------------------------------------------- /MU3Input/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /MU3Input/IO/HidIO.cs: -------------------------------------------------------------------------------- 1 | using SimpleHID.Raw; 2 | 3 | using System; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Threading; 7 | using System.Windows.Forms; 8 | 9 | using static MU3Input.KeyboardIO; 10 | 11 | namespace MU3Input 12 | { 13 | // ReSharper disable once InconsistentNaming 14 | public class HidIO : IO 15 | { 16 | private HidIOConfig config; 17 | protected int _openCount = 0; 18 | private byte[] _inBuffer = new byte[64]; 19 | private readonly SimpleRawHID _hid = new SimpleRawHID(); 20 | private const ushort VID = 0x2341; 21 | private const ushort PID = 0x8036; 22 | protected OutputData data; 23 | private bool reconnecting = false; 24 | 25 | 26 | public HidIO(HidIOConfig config) 27 | { 28 | this.config = config; 29 | data = new OutputData() { Buttons = new byte[10], Aime = new Aime() { Data = new byte[18] } }; 30 | Reconnect(); 31 | new Thread(PollThread).Start(); 32 | } 33 | public override bool IsConnected => _openCount > 0; 34 | public override OutputData Data => data; 35 | 36 | public override void Reconnect() 37 | { 38 | if (reconnecting) return; 39 | reconnecting = true; 40 | if (IsConnected) 41 | _hid.Close(); 42 | 43 | _openCount = _hid.Open(1, VID, PID); 44 | reconnecting = false; 45 | } 46 | 47 | public static int[] bitPosMap = 48 | { 49 | 23, 19, 22, 20, 21, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6 50 | }; 51 | 52 | 53 | private unsafe void PollThread() 54 | { 55 | while (true) 56 | { 57 | if (!IsConnected) 58 | continue; 59 | 60 | var len = _hid.Receive(0, ref _inBuffer, 64, 1000); 61 | if (len < 0) 62 | { 63 | _openCount = 0; 64 | _hid.Close(); 65 | continue; 66 | } 67 | 68 | OutputData temp = new OutputData(); 69 | temp.Buttons = new ArraySegment(_inBuffer, 0, 10).ToArray(); 70 | short lever; 71 | if (config.InvertLever) 72 | { 73 | lever = (short)(-BitConverter.ToInt16(_inBuffer, 10) - 1); 74 | } 75 | else 76 | { 77 | lever = BitConverter.ToInt16(_inBuffer, 10); 78 | } 79 | if (config.AutoCal) 80 | { 81 | if (lever < config.LeverLeft) 82 | { 83 | config.LeverLeft = lever; 84 | Console.WriteLine($"Set lever range: {config.LeverLeft}-{config.LeverRight}"); 85 | } 86 | if (lever > config.LeverRight) 87 | { 88 | config.LeverRight = lever; 89 | Console.WriteLine($"Set lever range: {config.LeverLeft}-{config.LeverRight}"); 90 | } 91 | } 92 | if (config.LeverRight != config.LeverLeft) 93 | { 94 | double normLever = (lever - config.LeverLeft) / (double)(config.LeverRight - config.LeverLeft); 95 | if (normLever < 0) normLever = 0; 96 | if (normLever > 1) normLever = 1; 97 | double leverd = -30000 + 60001 * normLever; 98 | temp.Lever = ((short)leverd); 99 | } 100 | else 101 | { 102 | temp.Lever = data.Lever; 103 | } 104 | temp.OptButtons = (OptButtons)_inBuffer[12]; 105 | temp.Aime.Scan = _inBuffer[13]; 106 | temp.Aime.Data = new byte[18]; 107 | if (temp.Aime.Scan == 1) 108 | { 109 | byte[] mifareID = new ArraySegment(_inBuffer, 14, 10).ToArray(); 110 | bool flag = true; 111 | for (int i = 0; i < 10; i++) 112 | { 113 | if (mifareID[i] != 255) 114 | { 115 | flag = false; 116 | break; 117 | } 118 | } 119 | if (flag) 120 | { 121 | mifareID = Utils.ReadOrCreateAimeTxt(); 122 | } 123 | temp.Aime.ID = mifareID; 124 | } 125 | if (temp.Aime.Scan == 2) 126 | { 127 | temp.Aime.IDm = BitConverter.ToUInt64(_inBuffer, 14); 128 | temp.Aime.PMm = BitConverter.ToUInt64(_inBuffer, 22); 129 | temp.Aime.SystemCode = BitConverter.ToUInt16(_inBuffer, 30); 130 | } 131 | data = temp; 132 | } 133 | } 134 | 135 | public unsafe override void SetLed(uint data) 136 | { 137 | if (!IsConnected) 138 | return; 139 | 140 | SetLedInput led; 141 | led.Type = 0; 142 | led.LedBrightness = 40; 143 | 144 | for (var i = 0; i < 9; i++) 145 | { 146 | led.LedColors[i] = (byte)(((data >> bitPosMap[i]) & 1) * 255); 147 | led.LedColors[i + 15] = (byte)(((data >> bitPosMap[i + 9]) & 1) * 255); 148 | } 149 | 150 | var outBuffer = new byte[64]; 151 | fixed (void* d = outBuffer) 152 | Kernel32.CopyMemory(d, &led, 64); 153 | 154 | _hid.Send(0, outBuffer, 64, 1000); 155 | } 156 | 157 | } 158 | public class HidIOConfig 159 | { 160 | public bool AutoCal { get; set; } = true; 161 | public short LeverLeft { get; set; } = short.MaxValue; 162 | public short LeverRight { get; set; } = short.MinValue; 163 | public bool InvertLever { get; set; } = true; 164 | } 165 | } -------------------------------------------------------------------------------- /MU3Input/IO/IO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace MU3Input 6 | { 7 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 64)] 8 | public struct OutputData 9 | { 10 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10, ArraySubType = UnmanagedType.U1)] 11 | public byte[] Buttons; 12 | 13 | public short Lever; 14 | 15 | public OptButtons OptButtons; 16 | 17 | public Aime Aime; 18 | } 19 | 20 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 64)] 21 | public unsafe struct SetLedInput 22 | { 23 | public byte Type; 24 | public byte LedBrightness; 25 | 26 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] 27 | public fixed byte LedColors[3 * 10]; 28 | } 29 | 30 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 64)] 31 | public unsafe struct SetOptionInput 32 | { 33 | public byte Type; 34 | 35 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] 36 | public fixed byte AimiId[10]; 37 | } 38 | public abstract class IO 39 | { 40 | public abstract OutputData Data { get; } 41 | 42 | private byte[] leftButtonsCache = new byte[5]; 43 | private byte[] rightButtonsCache = new byte[5]; 44 | public byte LeftButton 45 | { 46 | get 47 | { 48 | byte result = 0; 49 | for (int i = 4; i >= 0; i--) 50 | { 51 | result <<= 1; 52 | // 按钮触点数量不为0时 53 | if (Data.Buttons[i] > 0) 54 | { 55 | // 当已被按下并增加触点数量时自动松开一帧 56 | if (leftButtonsCache[i] > 0 && Data.Buttons[i] > leftButtonsCache[i]) 57 | { 58 | result += 0; 59 | } 60 | else 61 | { 62 | result += 1; 63 | } 64 | } 65 | } 66 | Array.Copy(Data.Buttons, 0, leftButtonsCache, 0, 5); 67 | return result; 68 | } 69 | } 70 | 71 | public byte RightButton 72 | { 73 | get 74 | { 75 | byte result = 0; 76 | for (int i = 4; i >= 0; i--) 77 | { 78 | result <<= 1; 79 | if (Data.Buttons[i + 5] > 0) 80 | { 81 | if (rightButtonsCache[i] > 0 && Data.Buttons[i + 5] > rightButtonsCache[i]) 82 | { 83 | result += 0; 84 | } 85 | else 86 | { 87 | result += 1; 88 | } 89 | } 90 | } 91 | Array.Copy(Data.Buttons, 5, rightButtonsCache, 0, 5); 92 | return result; 93 | } 94 | } 95 | 96 | public short Lever 97 | { 98 | get 99 | { 100 | return Data.Lever; 101 | } 102 | } 103 | public Aime Aime => Data.Aime; 104 | public OptButtons OptButtonsStatus => Data.OptButtons; 105 | 106 | public abstract bool IsConnected { get; } 107 | public abstract void Reconnect(); 108 | public abstract void SetLed(uint data); 109 | } 110 | [Flags] 111 | public enum OptButtons : byte 112 | { 113 | None = 0b000, 114 | Test = 0b001, 115 | Service = 0b010, 116 | Coin = 0b100 117 | } 118 | 119 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 19)] 120 | public struct Aime 121 | { 122 | [MarshalAs(UnmanagedType.U1)] 123 | public byte Scan; 124 | 125 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 18, ArraySubType = UnmanagedType.U1)] 126 | public byte[] Data; 127 | 128 | public byte[] ID 129 | { 130 | get => new ArraySegment(Data, 0, 10).ToArray(); 131 | set => value.CopyTo(Data, 0); 132 | } 133 | 134 | public ulong IDm 135 | { 136 | get => BitConverter.ToUInt64(Data, 0); 137 | set => BitConverter.GetBytes(value).CopyTo(Data, 0); 138 | } 139 | public ulong PMm 140 | { 141 | get => BitConverter.ToUInt64(Data, 8); 142 | set => BitConverter.GetBytes(value).CopyTo(Data, 8); 143 | } 144 | public ushort SystemCode 145 | { 146 | get => BitConverter.ToUInt16(Data, 16); 147 | set => BitConverter.GetBytes(value).CopyTo(Data, 16); 148 | } 149 | } 150 | enum MessageType : byte 151 | { 152 | // 控制器向IO发送的 153 | ButtonStatus = 1, 154 | MoveLever = 2, 155 | Scan = 3, 156 | Test = 4, 157 | Service = 5, 158 | RequestValues = 6, 159 | // IO向控制器发送的 160 | SetLed = 20, 161 | SetLever = 21, 162 | // 寻找在线设备 163 | Hello = 255 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /MU3Input/IO/KeyboardIO.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Converters; 3 | using Newtonsoft.Json.Linq; 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.Drawing; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Windows.Forms; 13 | 14 | using static System.Windows.Forms.VisualStyles.VisualStyleElement; 15 | 16 | namespace MU3Input 17 | { 18 | internal class KeyboardIO : IO 19 | { 20 | private KeyboardIOConfig config; 21 | public override OutputData Data => GetData(); 22 | 23 | public override bool IsConnected => true; 24 | 25 | public KeyboardIO(KeyboardIOConfig param) 26 | { 27 | config = param; 28 | } 29 | 30 | public override void Reconnect() { } 31 | 32 | public override void SetLed(uint data) { } 33 | 34 | 35 | StringBuilder sb = new StringBuilder(); 36 | bool coinAvailable = true; 37 | private OutputData GetData() 38 | { 39 | IntPtr handle = User32.GetForegroundWindow(); 40 | User32.GetWindowText(handle, sb, 16); 41 | string windowText = sb.ToString(); 42 | if (windowText != "Otoge" && windowText != "Ongeki IO Debug") 43 | { 44 | return new OutputData() { Buttons = new byte[10], Aime = new Aime() { Data = new byte[18] } }; 45 | } 46 | 47 | byte[] buttons = new byte[] { 48 | Pressed(config.L1), 49 | Pressed(config.L2), 50 | Pressed(config.L3), 51 | Pressed(config.LSide), 52 | Pressed(config.LMenu), 53 | Pressed(config.R1), 54 | Pressed(config.R2), 55 | Pressed(config.R3), 56 | Pressed(config.RSide), 57 | Pressed(config.RMenu), 58 | }; 59 | short lever = 0; 60 | byte testPressed = Pressed(config.Test); 61 | byte servicePressed = Pressed(config.Service); 62 | byte coinPressed = Pressed(config.Coin); 63 | if (coinPressed > 0) 64 | { 65 | if (coinAvailable) 66 | { 67 | coinAvailable = false; 68 | } 69 | else 70 | { 71 | coinPressed = 0; 72 | } 73 | } 74 | else 75 | { 76 | coinAvailable = true; 77 | } 78 | OptButtons optButtons = (OptButtons)(testPressed << 0 | servicePressed << 1 | coinPressed << 2); 79 | Aime aime = new Aime() 80 | { 81 | Scan = Pressed(config.Scan), 82 | Data = new byte[18] 83 | }; 84 | if (aime.Scan == 1) 85 | { 86 | byte[] bytes = Utils.ReadOrCreateAimeTxt(); 87 | aime.ID = bytes; 88 | } 89 | return new OutputData 90 | { 91 | Buttons = buttons, 92 | Lever = lever, 93 | OptButtons = optButtons, 94 | Aime = aime 95 | }; 96 | } 97 | private byte Pressed(Keys key) 98 | { 99 | 100 | return User32.GetAsyncKeyState(key) == 0 ? (byte)0 : (byte)1; 101 | } 102 | public class KeyboardIOConfig 103 | { 104 | public Keys L1 { get; set; } = (Keys)(-1); 105 | public Keys L2 { get; set; } = (Keys)(-1); 106 | public Keys L3 { get; set; } = (Keys)(-1); 107 | public Keys LSide { get; set; } = (Keys)(-1); 108 | public Keys LMenu { get; set; } = (Keys)(-1); 109 | public Keys R1 { get; set; } = (Keys)(-1); 110 | public Keys R2 { get; set; } = (Keys)(-1); 111 | public Keys R3 { get; set; } = (Keys)(-1); 112 | public Keys RSide { get; set; } = (Keys)(-1); 113 | public Keys RMenu { get; set; } = (Keys)(-1); 114 | public Keys Test { get; set; } = (Keys)(-1); 115 | public Keys Service { get; set; } = (Keys)(-1); 116 | public Keys Coin { get; set; } = (Keys)(-1); 117 | public Keys Scan { get; set; } = (Keys)(-1); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /MU3Input/IO/MixedIO.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | using static MU3Input.KeyboardIO; 9 | 10 | namespace MU3Input 11 | { 12 | public class MixedIO : IO 13 | { 14 | public override bool IsConnected => true; 15 | public override void Reconnect() 16 | { 17 | foreach (var item in Items) 18 | { 19 | item.Key.Reconnect(); 20 | } 21 | } 22 | public Dictionary Items { get; } 23 | public override OutputData Data 24 | { 25 | get 26 | { 27 | var buttons = new byte[10]; 28 | for (int i = 0; i < 10; i++) 29 | { 30 | var io = Items.FirstOrDefault(item => item.Value.HasFlag((ControllerPart)(1 << i))).Key; 31 | buttons[i] = io == null ? (byte)0 : io.Data.Buttons[i]; 32 | } 33 | short lever = default; 34 | IO aimeIO = null; 35 | 36 | foreach (var item in Items) 37 | { 38 | if (item.Value.HasFlag(ControllerPart.Lever)) 39 | lever = item.Key.Data.Lever; 40 | if (item.Value.HasFlag(ControllerPart.Aime)) 41 | aimeIO = item.Key; 42 | if (!item.Key.IsConnected) 43 | item.Key.Reconnect(); 44 | } 45 | return new OutputData 46 | { 47 | Buttons = buttons, 48 | Lever = lever, 49 | Aime = aimeIO?.Aime ?? default, 50 | OptButtons = Items.Select(item => item.Key.Data.OptButtons).Aggregate((item1, item2) => item1 | item2), 51 | }; 52 | } 53 | } 54 | 55 | public MixedIO() 56 | { 57 | Items = new Dictionary(); 58 | } 59 | 60 | public IO CreateIO(IOType type, JToken param) 61 | { 62 | switch (type) 63 | { 64 | case IOType.Hid: 65 | return new HidIO(param.ToObject()); 66 | case IOType.Udp: 67 | return new UdpIO(param.Value()); 68 | case IOType.Tcp: 69 | return new TcpIO(param.Value()); 70 | case IOType.Usbmux: 71 | return new UsbmuxIO(param.Value()); 72 | case IOType.Keyboard: 73 | return new KeyboardIO(param.ToObject()); 74 | default: throw new ArgumentException($"{type}: Unknown IO type"); 75 | } 76 | } 77 | public void Add(IO io, ControllerPart part) 78 | { 79 | if (Check(part, Items.Select(i => i.Value).ToArray())) 80 | { 81 | Items.Add(io, part); 82 | } 83 | } 84 | public void Remove(IO io) 85 | { 86 | Items.Remove(io); 87 | } 88 | 89 | public void Modify(IO io, ControllerPart part) 90 | { 91 | var parts = Items.Where(item => item.Key != io).Select(item => item.Value).ToArray(); 92 | if (Check(part, parts)) 93 | { 94 | Items[io] = part; 95 | } 96 | } 97 | 98 | public bool Check(ControllerPart part1, params ControllerPart[] parts) 99 | { 100 | if (parts.Length == 0) return true; 101 | ControllerPart part2 = parts.Aggregate((p1, p2) => p1 | p2); 102 | return (part1 & part2) == ControllerPart.None; 103 | } 104 | 105 | private uint currentLedData = 0; 106 | public override void SetLed(uint data) 107 | { 108 | currentLedData = data; 109 | foreach (IO io in Items.Keys) io.SetLed(currentLedData); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /MU3Input/IO/TcpIO.cs: -------------------------------------------------------------------------------- 1 | using SimpleHID.Raw; 2 | 3 | using System; 4 | using System.Collections.ObjectModel; 5 | using System.Diagnostics; 6 | using System.Linq; 7 | using System.Net.Sockets; 8 | using System.Threading; 9 | 10 | namespace MU3Input 11 | { 12 | public class TcpIO : IO 13 | { 14 | private string ip = "127.0.0.1"; 15 | private int port; 16 | private uint currentLedData = 0; 17 | private bool connecting = false; 18 | private TcpClient client; 19 | private NetworkStream networkStream; 20 | protected OutputData data; 21 | 22 | public TcpIO(int port) 23 | { 24 | this.port = port; 25 | data = new OutputData() { Buttons = new byte[10], Aime = new Aime() { Data = new byte[18] } }; 26 | new Thread(PollThread).Start(); 27 | 28 | } 29 | public override bool IsConnected => client?.Connected ?? false; 30 | public override OutputData Data => data; 31 | // 重连 32 | public override void Reconnect() 33 | { 34 | if (connecting) return; 35 | Disconnect(); 36 | ConnectAsync(ip, port); 37 | //connectTask?.Wait(); 38 | } 39 | public void ConnectAsync(string ip, int port) 40 | { 41 | if (connecting) return; 42 | connecting = true; 43 | try 44 | { 45 | var newClient = new TcpClient(ip, port); 46 | networkStream = newClient.GetStream(); 47 | client = newClient; 48 | SetLed(currentLedData); 49 | } 50 | catch (Exception) 51 | { 52 | Disconnect(); 53 | } 54 | connecting = false; 55 | } 56 | private void Disconnect() 57 | { 58 | if (IsConnected) 59 | { 60 | var tmpClient = client; 61 | var tmpStream = networkStream; 62 | client = null; 63 | networkStream = null; 64 | tmpClient?.Dispose(); 65 | tmpStream?.Dispose(); 66 | } 67 | } 68 | private byte[] _inBuffer = new byte[32]; 69 | private unsafe void PollThread() 70 | { 71 | while (true) 72 | { 73 | if (!IsConnected) 74 | { 75 | Reconnect(); 76 | continue; 77 | } 78 | int len = networkStream.Read(_inBuffer, 0, 1); 79 | if (len <= 0) 80 | { 81 | Reconnect(); 82 | continue; 83 | } 84 | Receive((MessageType)_inBuffer[0]); 85 | } 86 | } 87 | private unsafe void Receive(MessageType type) 88 | { 89 | if (type == MessageType.ButtonStatus && networkStream.Read(_inBuffer, 0, 2) > 0) 90 | { 91 | int index = _inBuffer[0]; 92 | data.Buttons[index] = _inBuffer[1]; 93 | } 94 | else if (type == MessageType.MoveLever && networkStream.Read(_inBuffer, 0, 2) > 0) 95 | { 96 | var value = (short)(_inBuffer[1] << 8 | _inBuffer[0]); 97 | data.Lever = value; 98 | } 99 | else if (type == MessageType.Scan && networkStream.Read(_inBuffer, 0, 1) > 0) 100 | { 101 | data.Aime.Scan = _inBuffer[0]; 102 | if (data.Aime.Scan == 0) 103 | { 104 | 105 | } 106 | else if (data.Aime.Scan == 1 && networkStream.Read(_inBuffer, 0, 10) > 0) 107 | { 108 | byte[] aimeId = new ArraySegment(_inBuffer, 0, 10).ToArray(); 109 | if (aimeId.All(n => n == 255)) 110 | { 111 | aimeId = Utils.ReadOrCreateAimeTxt(); 112 | } 113 | data.Aime.ID = aimeId; 114 | } 115 | else if (data.Aime.Scan == 2 && networkStream.Read(_inBuffer, 0, 18) > 0) 116 | { 117 | data.Aime.IDm = BitConverter.ToUInt64(_inBuffer, 0); 118 | data.Aime.PMm = BitConverter.ToUInt64(_inBuffer, 8); 119 | data.Aime.SystemCode = BitConverter.ToUInt16(_inBuffer, 16); 120 | } 121 | } 122 | else if (type == MessageType.Test && networkStream.Read(_inBuffer, 0, 1) > 0) 123 | { 124 | if (_inBuffer[1] == 0) data.OptButtons &= ~OptButtons.Test; 125 | else data.OptButtons |= OptButtons.Test; 126 | Debug.WriteLine(Data.OptButtons); 127 | } 128 | else if (type == MessageType.Service && networkStream.Read(_inBuffer, 0, 1) > 0) 129 | { 130 | if (_inBuffer[1] == 0) data.OptButtons &= ~OptButtons.Service; 131 | else data.OptButtons |= OptButtons.Service; 132 | Debug.WriteLine(Data.OptButtons); 133 | } 134 | else if (type == MessageType.RequestValues) 135 | { 136 | SetLed(currentLedData); 137 | SetLever(Data.Lever); 138 | } 139 | // 收到心跳数据直接回传原数据表示在线 140 | else if (type == MessageType.Hello && networkStream.Read(_inBuffer, 0, 1) > 0) 141 | { 142 | networkStream.Write(new byte[] { (byte)MessageType.Hello, _inBuffer[0] }, 0, 2); 143 | } 144 | } 145 | 146 | private void SetLever(short lever) 147 | { 148 | try 149 | { 150 | if (!IsConnected) 151 | return; 152 | networkStream.Write(new byte[] { (byte)MessageType.SetLever }.Concat(BitConverter.GetBytes(lever)).ToArray(), 0, 3); 153 | } 154 | catch 155 | { 156 | return; 157 | } 158 | } 159 | 160 | public override unsafe void SetLed(uint data) 161 | { 162 | try 163 | { 164 | // 缓存led数据将其设置到新连接的设备 165 | currentLedData = data; 166 | if (!IsConnected) 167 | return; 168 | networkStream.Write(new byte[] { (byte)MessageType.SetLed }.Concat(BitConverter.GetBytes(data)).ToArray(), 0, 5); 169 | } 170 | catch 171 | { 172 | return; 173 | } 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /MU3Input/IO/UdpIO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Numerics; 7 | using System.Runtime.InteropServices; 8 | using System.Threading; 9 | 10 | namespace MU3Input 11 | { 12 | public class UdpIO : IO 13 | { 14 | uint currentLedData = 0; 15 | UdpClient client; 16 | IPEndPoint savedEP; 17 | IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); 18 | System.Timers.Timer timer = new System.Timers.Timer(1500) 19 | { 20 | AutoReset = false 21 | }; 22 | protected OutputData data; 23 | 24 | public UdpIO(int port) 25 | { 26 | data = new OutputData() { Buttons = new byte[10], Aime = new Aime() { Data = new byte[18] } }; 27 | client = new UdpClient(port); 28 | timer.Elapsed += Timer_Elapsed; 29 | new Thread(PollThread).Start(); 30 | } 31 | 32 | // 一段时间没收到心跳包自动断开以接受新的连接 33 | private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 34 | { 35 | isConnected = false; 36 | savedEP = null; 37 | } 38 | 39 | bool isConnected = false; 40 | public override bool IsConnected => isConnected; 41 | public override OutputData Data => data; 42 | public override void Reconnect() { } 43 | 44 | private void PollThread() 45 | { 46 | while (true) 47 | { 48 | byte[] buffer = client?.Receive(ref remoteEP); 49 | // 如果已连接设备但收到了其他设备的消息则忽略 50 | if (IsConnected && (!remoteEP.Address.Equals(savedEP?.Address))) return; 51 | ParseBuffer(buffer); 52 | } 53 | } 54 | 55 | private unsafe void ParseBuffer(byte[] buffer) 56 | { 57 | if ((buffer?.Length ?? 0) == 0) return; 58 | if (buffer[0] == (byte)MessageType.ButtonStatus && buffer.Length == 3) 59 | { 60 | int index = buffer[1]; 61 | data.Buttons[index] = buffer[2]; 62 | } 63 | else if (buffer[0] == (byte)MessageType.MoveLever && buffer.Length == 3) 64 | { 65 | var value = (short)(buffer[2] << 8 | buffer[1]); 66 | data.Lever = value; 67 | } 68 | else if (buffer[0] == (byte)MessageType.Scan && (buffer.Length == 2 || buffer.Length == 12 || buffer.Length == 20)) 69 | { 70 | data.Aime.Scan = buffer[1]; 71 | if (data.Aime.Scan == 0) 72 | { 73 | 74 | } 75 | else if (data.Aime.Scan == 1) 76 | { 77 | byte[] aimeId = new ArraySegment(buffer, 2, 10).ToArray(); 78 | if (aimeId.All(n => n == 255)) 79 | { 80 | aimeId = Utils.ReadOrCreateAimeTxt(); 81 | } 82 | data.Aime.ID = aimeId; 83 | } 84 | else if (data.Aime.Scan == 2) 85 | { 86 | data.Aime.IDm = BitConverter.ToUInt64(buffer, 2); 87 | data.Aime.PMm = BitConverter.ToUInt64(buffer, 10); 88 | data.Aime.SystemCode = BitConverter.ToUInt16(buffer, 18); 89 | } 90 | } 91 | else if (buffer[0] == (byte)MessageType.Test && buffer.Length == 2) 92 | { 93 | if (buffer[1] == 0) data.OptButtons &= ~OptButtons.Test; 94 | else data.OptButtons |= OptButtons.Test; 95 | Debug.WriteLine(Data.OptButtons); 96 | } 97 | else if (buffer[0] == (byte)MessageType.Service && buffer.Length == 2) 98 | { 99 | if (buffer[1] == 0) data.OptButtons &= ~OptButtons.Service; 100 | else data.OptButtons |= OptButtons.Service; 101 | Debug.WriteLine(Data.OptButtons); 102 | } 103 | else if (buffer[0] == (byte)MessageType.RequestValues && buffer.Length == 1) 104 | { 105 | SetLed(currentLedData); 106 | SetLever(Data.Lever); 107 | } 108 | // 收到心跳数据直接回传原数据表示在线,并保存其地址阻止其他设备连接 109 | else if (buffer[0] == (byte)MessageType.Hello && buffer.Length == 2) 110 | { 111 | savedEP = new IPEndPoint(remoteEP.Address, remoteEP.Port); 112 | client.SendAsync(buffer, 2, savedEP); 113 | isConnected = true; 114 | timer.Stop(); 115 | timer.Start(); 116 | } 117 | } 118 | private void SetLever(short lever) 119 | { 120 | if (savedEP != null) 121 | { 122 | client?.SendAsync(new byte[] { (byte)MessageType.SetLever }.Concat(BitConverter.GetBytes(lever)).ToArray(), 3, savedEP); 123 | } 124 | } 125 | 126 | public override unsafe void SetLed(uint data) 127 | { 128 | currentLedData = data; 129 | if (savedEP != null) 130 | { 131 | client?.SendAsync(new byte[] { (byte)MessageType.SetLed }.Concat(BitConverter.GetBytes(data)).ToArray(), 5, savedEP); 132 | } 133 | } 134 | 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /MU3Input/IOTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Linq; 4 | using System.Reflection.Emit; 5 | using System.Windows.Forms; 6 | 7 | namespace MU3Input 8 | { 9 | public partial class IOTest : Form 10 | { 11 | private IO _io; 12 | 13 | private CheckBox[] _left; 14 | private CheckBox[] _right; 15 | 16 | public IOTest(IO io) 17 | { 18 | InitializeComponent(); 19 | 20 | _left = new[] { 21 | lA, 22 | lB, 23 | lC, 24 | lS, 25 | lM, 26 | }; 27 | 28 | _right = new[] { 29 | rA, 30 | rB, 31 | rC, 32 | rS, 33 | rM, 34 | }; 35 | 36 | _io = io; 37 | } 38 | 39 | public static byte[] StringToByteArray(string hex) 40 | { 41 | var numberChars = hex.Length; 42 | var bytes = new byte[numberChars / 2]; 43 | for (var i = 0; i < numberChars; i += 2) 44 | bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); 45 | return bytes; 46 | } 47 | 48 | internal void UpdateData() 49 | { 50 | if (!Enabled && Handle == IntPtr.Zero) return; 51 | 52 | try 53 | { 54 | BeginInvoke(new Action(() => 55 | { 56 | 57 | if (!_io.IsConnected) return; 58 | 59 | for (var i = 0; i < 5; i++) 60 | { 61 | _left[i].Checked = Convert.ToBoolean(_io.Data.Buttons[i]); 62 | _right[i].Checked = Convert.ToBoolean(_io.Data.Buttons[i + 5]); 63 | } 64 | 65 | trackBar1.Value = _io.Lever; 66 | 67 | if (_io.Aime.Scan == 0) 68 | { 69 | label1.Text = "Aime"; 70 | textAimiId.Text = "None"; 71 | } 72 | else if (_io.Aime.Scan == 1) 73 | { 74 | label1.Text = "AimeID"; 75 | textAimiId.Text = BitConverter.ToString(_io.Aime.ID).Replace("-", ""); 76 | } 77 | else if (_io.Aime.Scan == 2) 78 | { 79 | label1.Text = "IDm"; 80 | textAimiId.Text = "0x" + BitConverter.ToUInt64(BitConverter.GetBytes(_io.Aime.IDm).Reverse().ToArray(), 0).ToString("X16"); 81 | } 82 | 83 | lS.BackColor = Color.FromArgb(Mu3IO.LedData[0], Mu3IO.LedData[1], Mu3IO.LedData[2]); 84 | rS.BackColor = Color.FromArgb(Mu3IO.LedData[3], Mu3IO.LedData[4], Mu3IO.LedData[5]); 85 | })); 86 | } 87 | catch 88 | { 89 | // ignored 90 | } 91 | } 92 | 93 | public void SetColor(uint data) 94 | { 95 | try 96 | { 97 | BeginInvoke(new Action(() => 98 | { 99 | _left[0].BackColor = Color.FromArgb( 100 | (int)((data >> 23) & 1) * 255, 101 | (int)((data >> 19) & 1) * 255, 102 | (int)((data >> 22) & 1) * 255 103 | ); 104 | _left[1].BackColor = Color.FromArgb( 105 | (int)((data >> 20) & 1) * 255, 106 | (int)((data >> 21) & 1) * 255, 107 | (int)((data >> 18) & 1) * 255 108 | ); 109 | _left[2].BackColor = Color.FromArgb( 110 | (int)((data >> 17) & 1) * 255, 111 | (int)((data >> 16) & 1) * 255, 112 | (int)((data >> 15) & 1) * 255 113 | ); 114 | _right[0].BackColor = Color.FromArgb( 115 | (int)((data >> 14) & 1) * 255, 116 | (int)((data >> 13) & 1) * 255, 117 | (int)((data >> 12) & 1) * 255 118 | ); 119 | _right[1].BackColor = Color.FromArgb( 120 | (int)((data >> 11) & 1) * 255, 121 | (int)((data >> 10) & 1) * 255, 122 | (int)((data >> 9) & 1) * 255 123 | ); 124 | _right[2].BackColor = Color.FromArgb( 125 | (int)((data >> 8) & 1) * 255, 126 | (int)((data >> 7) & 1) * 255, 127 | (int)((data >> 6) & 1) * 255 128 | ); 129 | })); 130 | } 131 | catch 132 | { 133 | // ignored 134 | } 135 | } 136 | 137 | private void btnSetOption_Click(object sender, EventArgs e) 138 | { 139 | byte[] aimiId; 140 | 141 | try 142 | { 143 | aimiId = StringToByteArray(textAimiId.Text); 144 | } 145 | catch 146 | { 147 | MessageBox.Show("Invaild id, Id need to be a hex dump of 10 byte data.", "Error"); 148 | return; 149 | } 150 | 151 | if (aimiId.Length != 10) 152 | { 153 | MessageBox.Show("Invaild id, Id need to be a hex dump of 10 byte data."); 154 | return; 155 | } 156 | 157 | //_io.SetAimiId(aimiId); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /MU3Input/IOTest.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /MU3Input/Kernel32.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text; 3 | 4 | namespace MU3Input 5 | { 6 | public class Kernel32 7 | { 8 | [DllImport("kernel32.dll")] 9 | public static extern long GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); 10 | [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)] 11 | public static extern unsafe void CopyMemory(void* dest, void* src, int count); 12 | [DllImport("kernel32.dll")] 13 | public static extern long WritePrivateProfileString(string section, string key, string val, string filePath); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /MU3Input/MU3IO.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.IO.MemoryMappedFiles; 3 | using System.Runtime.InteropServices; 4 | using System.Threading.Tasks; 5 | 6 | namespace MU3Input 7 | { 8 | public static class Mu3IO 9 | { 10 | internal static IO IO; 11 | internal static byte[] LedData; 12 | private static IOTest _test; 13 | 14 | private static MemoryMappedFile mmf; 15 | private static MemoryMappedViewAccessor accessor; 16 | 17 | static Mu3IO() 18 | { 19 | var io = new MixedIO(); 20 | foreach (var ioConfig in Config.Instance.IO) 21 | { 22 | io.Add(io.CreateIO(ioConfig.Type, ioConfig.Param), ioConfig.Part); 23 | } 24 | IO = io; 25 | _test = new IOTest(io); 26 | 27 | //与mod共享内存以接收LED数据 28 | mmf = MemoryMappedFile.CreateOrOpen("mu3_led_data", 66 * 3); 29 | accessor = mmf.CreateViewAccessor(0, 66 * 3); 30 | LedData = new byte[6]; 31 | 32 | Task.Run(() => _test.ShowDialog()); 33 | } 34 | 35 | [DllExport(ExportName = "mu3_io_get_api_version")] 36 | public static ushort GetVersion() 37 | { 38 | return 0x0102; 39 | } 40 | 41 | [DllExport(CallingConvention.Cdecl, ExportName = "mu3_io_init")] 42 | public static uint Init() 43 | { 44 | if (Process.GetCurrentProcess().ProcessName != "amdaemon" && 45 | Process.GetCurrentProcess().ProcessName != "Debug" && 46 | Process.GetCurrentProcess().ProcessName != "TestSharp" && 47 | Process.GetCurrentProcess().ProcessName != "Test") 48 | return 1; 49 | else return 0; 50 | 51 | } 52 | 53 | [DllExport(CallingConvention.Cdecl, ExportName = "mu3_io_poll")] 54 | public static uint Poll() 55 | { 56 | if (IO == null) 57 | return 0; 58 | 59 | if (!IO.IsConnected) 60 | { 61 | IO.Reconnect(); 62 | } 63 | 64 | int leftBase = 0; 65 | accessor.ReadArray(leftBase, LedData, 0, 3); 66 | 67 | int rightBase = 59 * 3; 68 | accessor.ReadArray(rightBase, LedData, 3, 3); 69 | 70 | _test.UpdateData(); 71 | 72 | return 0; 73 | } 74 | 75 | [DllExport(CallingConvention.Cdecl, ExportName = "mu3_io_get_opbtns")] 76 | public static void GetOpButtons(out byte opbtn) 77 | { 78 | if (IO == null || !IO.IsConnected) 79 | { 80 | opbtn = 0; 81 | return; 82 | } 83 | 84 | opbtn = (byte)IO.OptButtonsStatus; 85 | } 86 | 87 | [DllExport(CallingConvention.Cdecl, ExportName = "mu3_io_get_gamebtns")] 88 | public static void GetGameButtons(out byte left, out byte right) 89 | { 90 | if (IO == null || !IO.IsConnected) 91 | { 92 | left = 0; 93 | right = 0; 94 | return; 95 | } 96 | 97 | left = IO.LeftButton; 98 | right = IO.RightButton; 99 | } 100 | 101 | [DllExport(CallingConvention.Cdecl, ExportName = "mu3_io_get_lever")] 102 | public static void GetLever(out short pos) 103 | { 104 | pos = 0; 105 | if (IO == null || !IO.IsConnected) 106 | { 107 | pos = 0; 108 | return; 109 | } 110 | 111 | pos = IO.Lever; 112 | } 113 | 114 | [DllExport(CallingConvention.Cdecl, ExportName = "mu3_io_set_led")] 115 | public static void SetLed(uint data) 116 | { 117 | IO.SetLed(data); 118 | _test.SetColor(data); 119 | } 120 | 121 | 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /MU3Input/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("MU3Input")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("MU3Input")] 13 | [assembly: AssemblyCopyright("Copyright © 2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("a90797a6-c546-4233-a183-453af52319d0")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /MU3Input/User32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | using System.Windows.Forms; 5 | 6 | namespace MU3Input 7 | { 8 | public class User32 9 | { 10 | [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "GetForegroundWindow")] 11 | public static extern IntPtr GetForegroundWindow(); 12 | 13 | 14 | [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "GetWindowText")] 15 | public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int maxCount); 16 | 17 | [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "GetAsyncKeyState")] 18 | public static extern int GetAsyncKeyState(Keys vKey); 19 | } 20 | } -------------------------------------------------------------------------------- /MU3Input/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Numerics; 5 | using System.Reflection; 6 | 7 | namespace MU3Input 8 | { 9 | internal class Utils 10 | { 11 | public static byte[] ReadOrCreateAimeTxt() 12 | { 13 | byte[] aimeId; 14 | var location = Assembly.GetCallingAssembly().Location; 15 | string directoryName = Path.GetDirectoryName(location); 16 | string deviceDirectory = Path.Combine(directoryName, "DEVICE"); 17 | string aimeIdPath = Path.Combine(deviceDirectory, "aime.txt"); 18 | try 19 | { 20 | var id = BigInteger.Parse(File.ReadAllText(aimeIdPath)); 21 | var bytes = id.ToBcd(); 22 | aimeId = new byte[10 - bytes.Length].Concat(bytes).ToArray(); 23 | } 24 | catch (Exception) 25 | { 26 | Random random = new Random(); 27 | byte[] temp = new byte[10]; 28 | random.NextBytes(temp); 29 | var id = new BigInteger(temp); 30 | if (id < -1) id = -(id + 1); 31 | id = id % BigInteger.Parse("99999999999999999999"); 32 | if (!Directory.Exists(deviceDirectory)) 33 | { 34 | Directory.CreateDirectory(deviceDirectory); 35 | } 36 | var bytes = id.ToBcd(); 37 | aimeId = new byte[10 - bytes.Length].Concat(bytes).ToArray(); 38 | File.WriteAllText(aimeIdPath, id.ToString()); 39 | } 40 | return aimeId; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### MU3Input `MU3Input.sln` 2 | #### IO library to use with segatools 3 | Usage: 4 | - Copy `MU3Input.dll` into game folder 5 | - Open segatools.ini and add following lines: 6 | ```ini 7 | [mu3io] 8 | path=MU3Input.dll 9 | 10 | [aimeio] 11 | path=MU3Input.dll 12 | ``` 13 | 14 | You can use Jetbrains Rider or Visual Studio to compile. 15 | 16 | Note: 17 | - My lever is a non-linear potentiometer, so it has correction code in `Lever` property of `HidIO.cs`, You may want to change it or remove it. 18 | 19 | ### mu3controller `mu3controller\ ` 20 | #### Arduino Leonardo firmware to use with above IO library. 21 | I'm using CLion to develop this, for CLion you will need to install platform io support plugin and run 22 | ``` 23 | pio -f -c clion init --ide clion 24 | ``` 25 | Note: 26 | - You can change pin settings in `src\components\ongeki_hardware.cpp` -------------------------------------------------------------------------------- /SegaToolsPatch/0001-Add-led-code.patch: -------------------------------------------------------------------------------- 1 | From 786c9ca98942dd9f88ba10036d1579d81fabcd7a Mon Sep 17 00:00:00 2001 2 | From: GEEKiDoS 3 | Date: Fri, 1 Oct 2021 14:57:41 +0800 4 | Subject: [PATCH] Add led code 5 | 6 | --- 7 | board/io4.c | 2 +- 8 | board/io4.h | 1 + 9 | mu3hook/io4.c | 13 +++++++++++++ 10 | mu3hook/mu3-dll.c | 3 +++ 11 | mu3hook/mu3-dll.h | 1 + 12 | mu3hook/mu3hook.def | 1 + 13 | mu3io/mu3io.c | 4 ++++ 14 | mu3io/mu3io.h | 2 ++ 15 | 8 files changed, 26 insertions(+), 1 deletion(-) 16 | 17 | diff --git a/board/io4.c b/board/io4.c 18 | index efad62f..9cdb054 100644 19 | --- a/board/io4.c 20 | +++ b/board/io4.c 21 | @@ -223,7 +223,7 @@ static HRESULT io4_handle_write(struct irp *irp) 22 | 23 | case IO4_CMD_SET_GENERAL_OUTPUT: 24 | dprintf("USB I/O: GPIO Out\n"); 25 | - 26 | + io4_ops->gpio_out(out.payload); 27 | return S_OK; 28 | 29 | case IO4_CMD_SET_PWM_OUTPUT: 30 | diff --git a/board/io4.h b/board/io4.h 31 | index 1a6cc05..87cd154 100644 32 | --- a/board/io4.h 33 | +++ b/board/io4.h 34 | @@ -24,6 +24,7 @@ struct io4_state { 35 | 36 | struct io4_ops { 37 | HRESULT (*poll)(void *ctx, struct io4_state *state); 38 | + HRESULT (*gpio_out)(uint8_t *payload); 39 | }; 40 | 41 | HRESULT io4_hook_init( 42 | diff --git a/mu3hook/io4.c b/mu3hook/io4.c 43 | index 7edcb0c..d7df696 100644 44 | --- a/mu3hook/io4.c 45 | +++ b/mu3hook/io4.c 46 | @@ -11,9 +11,11 @@ 47 | #include "util/dprintf.h" 48 | 49 | static HRESULT mu3_io4_poll(void *ctx, struct io4_state *state); 50 | +static HRESULT mu3_io4_gpio_out(uint8_t *payload); 51 | 52 | static const struct io4_ops mu3_io4_ops = { 53 | .poll = mu3_io4_poll, 54 | + .gpio_out = mu3_io4_gpio_out, 55 | }; 56 | 57 | HRESULT mu3_io4_hook_init(const struct io4_config *cfg) 58 | @@ -118,3 +120,14 @@ static HRESULT mu3_io4_poll(void *ctx, struct io4_state *state) 59 | 60 | return S_OK; 61 | } 62 | + 63 | +static HRESULT mu3_io4_gpio_out(uint8_t *payload) 64 | +{ 65 | + if (mu3_dll.set_led) 66 | + { 67 | + uint32_t data = payload[0] << 16 | payload[1] << 8 | payload[2]; 68 | + mu3_dll.set_led(data); 69 | + } 70 | + 71 | + return S_OK; 72 | +} 73 | diff --git a/mu3hook/mu3-dll.c b/mu3hook/mu3-dll.c 74 | index 9e8e93e..6abf26c 100644 75 | --- a/mu3hook/mu3-dll.c 76 | +++ b/mu3hook/mu3-dll.c 77 | @@ -24,6 +24,9 @@ const struct dll_bind_sym mu3_dll_syms[] = { 78 | }, { 79 | .sym = "mu3_io_get_lever", 80 | .off = offsetof(struct mu3_dll, get_lever), 81 | + }, { 82 | + .sym = "mu3_io_set_led", 83 | + .off = offsetof(struct mu3_dll, set_led), 84 | } 85 | }; 86 | 87 | diff --git a/mu3hook/mu3-dll.h b/mu3hook/mu3-dll.h 88 | index 41f280f..550772c 100644 89 | --- a/mu3hook/mu3-dll.h 90 | +++ b/mu3hook/mu3-dll.h 91 | @@ -11,6 +11,7 @@ struct mu3_dll { 92 | void (*get_opbtns)(uint8_t *opbtn); 93 | void (*get_gamebtns)(uint8_t *left, uint8_t *right); 94 | void (*get_lever)(int16_t *pos); 95 | + void (*set_led)(uint32_t info); 96 | }; 97 | 98 | struct mu3_dll_config { 99 | diff --git a/mu3hook/mu3hook.def b/mu3hook/mu3hook.def 100 | index e7367fb..8b03862 100644 101 | --- a/mu3hook/mu3hook.def 102 | +++ b/mu3hook/mu3hook.def 103 | @@ -18,3 +18,4 @@ EXPORTS 104 | mu3_io_get_opbtns 105 | mu3_io_init 106 | mu3_io_poll 107 | + mu3_io_set_led 108 | diff --git a/mu3io/mu3io.c b/mu3io/mu3io.c 109 | index 0bbd37f..bcbc668 100644 110 | --- a/mu3io/mu3io.c 111 | +++ b/mu3io/mu3io.c 112 | @@ -146,3 +146,7 @@ void mu3_io_get_lever(int16_t *pos) 113 | *pos = mu3_lever_xpos; 114 | } 115 | } 116 | + 117 | +void mu3_io_set_led(uint32_t led) 118 | +{ 119 | +} 120 | diff --git a/mu3io/mu3io.h b/mu3io/mu3io.h 121 | index d46a475..e540a3d 100644 122 | --- a/mu3io/mu3io.h 123 | +++ b/mu3io/mu3io.h 124 | @@ -82,3 +82,5 @@ void mu3_io_get_gamebtns(uint8_t *left, uint8_t *right); 125 | Minimum API version: 0x0100 */ 126 | 127 | void mu3_io_get_lever(int16_t *pos); 128 | + 129 | +void mu3_io_set_led(uint32_t led); 130 | -- 131 | 2.30.1.windows.1 132 | 133 | -------------------------------------------------------------------------------- /Test/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Test/Program.cs: -------------------------------------------------------------------------------- 1 | using MU3Input; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Test 11 | { 12 | internal class Program 13 | { 14 | static unsafe void Main(string[] args) 15 | { 16 | ulong id = 0; 17 | ushort code = 0; 18 | IntPtr aimeid = Marshal.AllocHGlobal(10); 19 | Mu3IO.Init(); 20 | AimiIO.Init(); 21 | while (true) 22 | { 23 | Task.Delay(1).Wait(); 24 | Mu3IO.Poll(); 25 | Mu3IO.GetGameButtons(out byte left, out byte right); 26 | AimiIO.GetFelicaId(0, &id); 27 | AimiIO.GetFelicaPm(0, &id); 28 | AimiIO.GetFelicaSystemCode(0, &code); 29 | AimiIO.GetAimeId(0, (byte*)aimeid,10); 30 | } 31 | Console.ReadKey(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("Test")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Test")] 13 | [assembly: AssemblyCopyright("Copyright © 2022")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("678bbc0e-4096-4064-bd0a-d2b311141111")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Test/Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {678BBC0E-4096-4064-BD0A-D2B311141111} 8 | Exe 9 | Test 10 | Test 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | publish\ 16 | true 17 | Disk 18 | false 19 | Foreground 20 | 7 21 | Days 22 | false 23 | false 24 | true 25 | 0 26 | 1.0.0.%2a 27 | false 28 | false 29 | true 30 | 31 | 32 | true 33 | bin\x64\Debug\ 34 | DEBUG;TRACE 35 | full 36 | x64 37 | 7.3 38 | prompt 39 | true 40 | true 41 | 42 | 43 | bin\x64\Release\ 44 | TRACE 45 | true 46 | pdbonly 47 | x64 48 | 7.3 49 | prompt 50 | true 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | {a90797a6-c546-4233-a183-453af52319d0} 73 | MU3Input 74 | 75 | 76 | 77 | 78 | False 79 | Microsoft .NET Framework 4.7.2 %28x86 和 x64%29 80 | true 81 | 82 | 83 | False 84 | .NET Framework 3.5 SP1 85 | false 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /mu3controller/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | CMakeLists.txt 3 | CMakeListsPrivate.txt 4 | cmake-build-*/ 5 | -------------------------------------------------------------------------------- /mu3controller/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /mu3controller/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /mu3controller/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /mu3controller/.idea/untitled1.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /mu3controller/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /mu3controller/include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the usual convention is to give header files names that end with `.h'. 29 | It is most portable to use only letters, digits, dashes, and underscores in 30 | header file names, and at most one dot. 31 | 32 | Read more about using header files in official GCC documentation: 33 | 34 | * Include Syntax 35 | * Include Operation 36 | * Once-Only Headers 37 | * Computed Includes 38 | 39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 40 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/Due.h: -------------------------------------------------------------------------------- 1 | // redefine some stuff so code works on Due 2 | // http://arduino.cc/forum/index.php?&topic=153761.0 3 | 4 | #ifndef Due_h 5 | #define Due_h 6 | 7 | #if defined(__SAM3X8E__) 8 | #define PROGMEM 9 | #define pgm_read_byte(x) (*((char *)x)) 10 | // #define pgm_read_word(x) (*((short *)(x & 0xfffffffe))) 11 | #define pgm_read_word(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x))) 12 | #define pgm_read_byte_near(x) (*((char *)x)) 13 | #define pgm_read_byte_far(x) (*((char *)x)) 14 | // #define pgm_read_word_near(x) (*((short *)(x & 0xfffffffe)) 15 | // #define pgm_read_word_far(x) (*((short *)(x & 0xfffffffe))) 16 | #define pgm_read_word_near(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x))) 17 | #define pgm_read_word_far(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x)))) 18 | #define PSTR(x) x 19 | #if defined F 20 | #undef F 21 | #endif 22 | #define F(X) (X) 23 | #endif 24 | 25 | #endif -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Software License Agreement (BSD License) 2 | 3 | Copyright (c) 2013-2014, Don Coleman 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holders nor the 17 | names of its contributors may be used to endorse or promote products 18 | derived from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY 21 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 24 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/MifareClassic.h: -------------------------------------------------------------------------------- 1 | #ifndef MifareClassic_h 2 | #define MifareClassic_h 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class MifareClassic 10 | { 11 | public: 12 | MifareClassic(PN532& nfcShield); 13 | ~MifareClassic(); 14 | NfcTag read(byte *uid, unsigned int uidLength); 15 | boolean write(NdefMessage& ndefMessage, byte *uid, unsigned int uidLength); 16 | boolean formatNDEF(byte * uid, unsigned int uidLength); 17 | boolean formatMifare(byte * uid, unsigned int uidLength); 18 | private: 19 | PN532* _nfcShield; 20 | int getBufferSize(int messageLength); 21 | int getNdefStartIndex(byte *data); 22 | bool decodeTlv(byte *data, int &messageLength, int &messageStartIndex); 23 | }; 24 | 25 | #endif -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/MifareUltralight.h: -------------------------------------------------------------------------------- 1 | #ifndef MifareUltralight_h 2 | #define MifareUltralight_h 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class MifareUltralight 9 | { 10 | public: 11 | MifareUltralight(PN532& nfcShield); 12 | ~MifareUltralight(); 13 | NfcTag read(byte *uid, unsigned int uidLength); 14 | boolean write(NdefMessage& ndefMessage, byte *uid, unsigned int uidLength); 15 | boolean clean(); 16 | private: 17 | PN532* nfc; 18 | unsigned int tagCapacity; 19 | unsigned int messageLength; 20 | unsigned int bufferSize; 21 | unsigned int ndefStartIndex; 22 | boolean isUnformatted(); 23 | void readCapabilityContainer(); 24 | void findNdefMessage(); 25 | void calculateBufferSize(); 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/Ndef.cpp: -------------------------------------------------------------------------------- 1 | #include "Ndef.h" 2 | 3 | // Borrowed from Adafruit_NFCShield_I2C 4 | void PrintHex(const byte * data, const long numBytes) 5 | { 6 | uint32_t szPos; 7 | for (szPos=0; szPos < numBytes; szPos++) 8 | { 9 | Serial.print("0x"); 10 | // Append leading 0 for small values 11 | if (data[szPos] <= 0xF) 12 | Serial.print("0"); 13 | Serial.print(data[szPos]&0xff, HEX); 14 | if ((numBytes > 1) && (szPos != numBytes - 1)) 15 | { 16 | Serial.print(" "); 17 | } 18 | } 19 | Serial.println(""); 20 | } 21 | 22 | // Borrowed from Adafruit_NFCShield_I2C 23 | void PrintHexChar(const byte * data, const long numBytes) 24 | { 25 | uint32_t szPos; 26 | for (szPos=0; szPos < numBytes; szPos++) 27 | { 28 | // Append leading 0 for small values 29 | if (data[szPos] <= 0xF) 30 | Serial.print("0"); 31 | Serial.print(data[szPos], HEX); 32 | if ((numBytes > 1) && (szPos != numBytes - 1)) 33 | { 34 | Serial.print(" "); 35 | } 36 | } 37 | Serial.print(" "); 38 | for (szPos=0; szPos < numBytes; szPos++) 39 | { 40 | if (data[szPos] <= 0x1F) 41 | Serial.print("."); 42 | else 43 | Serial.print((char)data[szPos]); 44 | } 45 | Serial.println(""); 46 | } 47 | 48 | // Note if buffer % blockSize != 0, last block will not be written 49 | void DumpHex(const byte * data, const long numBytes, const unsigned int blockSize) 50 | { 51 | int i; 52 | for (i = 0; i < (numBytes / blockSize); i++) 53 | { 54 | PrintHexChar(data, blockSize); 55 | data += blockSize; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/Ndef.h: -------------------------------------------------------------------------------- 1 | #ifndef Ndef_h 2 | #define Ndef_h 3 | 4 | /* NOTE: To use the Ndef library in your code, don't include Ndef.h 5 | See README.md for details on which files to include in your sketch. 6 | */ 7 | 8 | #include 9 | 10 | #define NULL (void *)0 11 | 12 | void PrintHex(const byte *data, const long numBytes); 13 | void PrintHexChar(const byte *data, const long numBytes); 14 | void DumpHex(const byte *data, const long numBytes, const int blockSize); 15 | 16 | #endif -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/NdefMessage.h: -------------------------------------------------------------------------------- 1 | #ifndef NdefMessage_h 2 | #define NdefMessage_h 3 | 4 | #include 5 | #include 6 | 7 | #define MAX_NDEF_RECORDS 4 8 | 9 | class NdefMessage 10 | { 11 | public: 12 | NdefMessage(void); 13 | NdefMessage(const byte *data, const int numBytes); 14 | NdefMessage(const NdefMessage& rhs); 15 | ~NdefMessage(); 16 | NdefMessage& operator=(const NdefMessage& rhs); 17 | 18 | int getEncodedSize(); // need so we can pass array to encode 19 | void encode(byte *data); 20 | 21 | boolean addRecord(NdefRecord& record); 22 | void addMimeMediaRecord(String mimeType, String payload); 23 | void addMimeMediaRecord(String mimeType, byte *payload, int payloadLength); 24 | void addTextRecord(String text); 25 | void addTextRecord(String text, String encoding); 26 | void addUriRecord(String uri); 27 | void addEmptyRecord(); 28 | 29 | unsigned int getRecordCount(); 30 | NdefRecord getRecord(int index); 31 | NdefRecord operator[](int index); 32 | 33 | void print(); 34 | private: 35 | NdefRecord _records[MAX_NDEF_RECORDS]; 36 | unsigned int _recordCount; 37 | }; 38 | 39 | #endif -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/NdefRecord.h: -------------------------------------------------------------------------------- 1 | #ifndef NdefRecord_h 2 | #define NdefRecord_h 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define TNF_EMPTY 0x0 9 | #define TNF_WELL_KNOWN 0x01 10 | #define TNF_MIME_MEDIA 0x02 11 | #define TNF_ABSOLUTE_URI 0x03 12 | #define TNF_EXTERNAL_TYPE 0x04 13 | #define TNF_UNKNOWN 0x05 14 | #define TNF_UNCHANGED 0x06 15 | #define TNF_RESERVED 0x07 16 | 17 | class NdefRecord 18 | { 19 | public: 20 | NdefRecord(); 21 | NdefRecord(const NdefRecord& rhs); 22 | ~NdefRecord(); 23 | NdefRecord& operator=(const NdefRecord& rhs); 24 | 25 | int getEncodedSize(); 26 | void encode(byte *data, bool firstRecord, bool lastRecord); 27 | 28 | unsigned int getTypeLength(); 29 | int getPayloadLength(); 30 | unsigned int getIdLength(); 31 | 32 | byte getTnf(); 33 | void getType(byte *type); 34 | void getPayload(byte *payload); 35 | void getId(byte *id); 36 | 37 | // convenience methods 38 | String getType(); 39 | String getId(); 40 | 41 | void setTnf(byte tnf); 42 | void setType(const byte *type, const unsigned int numBytes); 43 | void setPayload(const byte *payload, const int numBytes); 44 | void setId(const byte *id, const unsigned int numBytes); 45 | 46 | void print(); 47 | private: 48 | byte getTnfByte(bool firstRecord, bool lastRecord); 49 | byte _tnf; // 3 bit 50 | unsigned int _typeLength; 51 | int _payloadLength; 52 | unsigned int _idLength; 53 | byte *_type; 54 | byte *_payload; 55 | byte *_id; 56 | }; 57 | 58 | #endif -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/NfcAdapter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | NfcAdapter::NfcAdapter(PN532Interface &interface) 4 | { 5 | shield = new PN532(interface); 6 | } 7 | 8 | NfcAdapter::~NfcAdapter(void) 9 | { 10 | delete shield; 11 | } 12 | 13 | void NfcAdapter::begin(boolean verbose) 14 | { 15 | shield->begin(); 16 | 17 | uint32_t versiondata = shield->getFirmwareVersion(); 18 | 19 | if (! versiondata) 20 | { 21 | Serial.print(F("Didn't find PN53x board")); 22 | while (1); // halt 23 | } 24 | 25 | if (verbose) 26 | { 27 | Serial.print(F("Found chip PN5")); Serial.println((versiondata>>24) & 0xFF, HEX); 28 | Serial.print(F("Firmware ver. ")); Serial.print((versiondata>>16) & 0xFF, DEC); 29 | Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC); 30 | } 31 | // configure board to read RFID tags 32 | shield->SAMConfig(); 33 | } 34 | 35 | boolean NfcAdapter::tagPresent(unsigned long timeout) 36 | { 37 | uint8_t success; 38 | uidLength = 0; 39 | 40 | if (timeout == 0) 41 | { 42 | success = shield->readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, (uint8_t*)&uidLength); 43 | } 44 | else 45 | { 46 | success = shield->readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, (uint8_t*)&uidLength, timeout); 47 | } 48 | return success; 49 | } 50 | 51 | boolean NfcAdapter::erase() 52 | { 53 | boolean success; 54 | NdefMessage message = NdefMessage(); 55 | message.addEmptyRecord(); 56 | return write(message); 57 | } 58 | 59 | boolean NfcAdapter::format() 60 | { 61 | boolean success; 62 | if (uidLength == 4) 63 | { 64 | MifareClassic mifareClassic = MifareClassic(*shield); 65 | success = mifareClassic.formatNDEF(uid, uidLength); 66 | } 67 | else 68 | { 69 | Serial.print(F("Unsupported Tag.")); 70 | success = false; 71 | } 72 | return success; 73 | } 74 | 75 | boolean NfcAdapter::clean() 76 | { 77 | uint8_t type = guessTagType(); 78 | 79 | if (type == TAG_TYPE_MIFARE_CLASSIC) 80 | { 81 | #ifdef NDEF_DEBUG 82 | Serial.println(F("Cleaning Mifare Classic")); 83 | #endif 84 | MifareClassic mifareClassic = MifareClassic(*shield); 85 | return mifareClassic.formatMifare(uid, uidLength); 86 | } 87 | else if (type == TAG_TYPE_2) 88 | { 89 | #ifdef NDEF_DEBUG 90 | Serial.println(F("Cleaning Mifare Ultralight")); 91 | #endif 92 | MifareUltralight ultralight = MifareUltralight(*shield); 93 | return ultralight.clean(); 94 | } 95 | else 96 | { 97 | Serial.print(F("No driver for card type "));Serial.println(type); 98 | return false; 99 | } 100 | 101 | } 102 | 103 | 104 | NfcTag NfcAdapter::read() 105 | { 106 | uint8_t type = guessTagType(); 107 | 108 | if (type == TAG_TYPE_MIFARE_CLASSIC) 109 | { 110 | #ifdef NDEF_DEBUG 111 | Serial.println(F("Reading Mifare Classic")); 112 | #endif 113 | MifareClassic mifareClassic = MifareClassic(*shield); 114 | return mifareClassic.read(uid, uidLength); 115 | } 116 | else if (type == TAG_TYPE_2) 117 | { 118 | #ifdef NDEF_DEBUG 119 | Serial.println(F("Reading Mifare Ultralight")); 120 | #endif 121 | MifareUltralight ultralight = MifareUltralight(*shield); 122 | return ultralight.read(uid, uidLength); 123 | } 124 | else if (type == TAG_TYPE_UNKNOWN) 125 | { 126 | Serial.print(F("Can not determine tag type")); 127 | return NfcTag(uid, uidLength); 128 | } 129 | else 130 | { 131 | Serial.print(F("No driver for card type "));Serial.println(type); 132 | // TODO should set type here 133 | return NfcTag(uid, uidLength); 134 | } 135 | 136 | } 137 | 138 | boolean NfcAdapter::write(NdefMessage& ndefMessage) 139 | { 140 | boolean success; 141 | uint8_t type = guessTagType(); 142 | 143 | if (type == TAG_TYPE_MIFARE_CLASSIC) 144 | { 145 | #ifdef NDEF_DEBUG 146 | Serial.println(F("Writing Mifare Classic")); 147 | #endif 148 | MifareClassic mifareClassic = MifareClassic(*shield); 149 | success = mifareClassic.write(ndefMessage, uid, uidLength); 150 | } 151 | else if (type == TAG_TYPE_2) 152 | { 153 | #ifdef NDEF_DEBUG 154 | Serial.println(F("Writing Mifare Ultralight")); 155 | #endif 156 | MifareUltralight mifareUltralight = MifareUltralight(*shield); 157 | success = mifareUltralight.write(ndefMessage, uid, uidLength); 158 | } 159 | else if (type == TAG_TYPE_UNKNOWN) 160 | { 161 | Serial.print(F("Can not determine tag type")); 162 | success = false; 163 | } 164 | else 165 | { 166 | Serial.print(F("No driver for card type "));Serial.println(type); 167 | success = false; 168 | } 169 | 170 | return success; 171 | } 172 | 173 | // TODO this should return a Driver MifareClassic, MifareUltralight, Type 4, Unknown 174 | // Guess Tag Type by looking at the ATQA and SAK values 175 | // Need to follow spec for Card Identification. Maybe AN1303, AN1305 and ??? 176 | unsigned int NfcAdapter::guessTagType() 177 | { 178 | 179 | // 4 byte id - Mifare Classic 180 | // - ATQA 0x4 && SAK 0x8 181 | // 7 byte id 182 | // - ATQA 0x44 && SAK 0x8 - Mifare Classic 183 | // - ATQA 0x44 && SAK 0x0 - Mifare Ultralight NFC Forum Type 2 184 | // - ATQA 0x344 && SAK 0x20 - NFC Forum Type 4 185 | 186 | if (uidLength == 4) 187 | { 188 | return TAG_TYPE_MIFARE_CLASSIC; 189 | } 190 | else 191 | { 192 | return TAG_TYPE_2; 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/NfcAdapter.h: -------------------------------------------------------------------------------- 1 | #ifndef NfcAdapter_h 2 | #define NfcAdapter_h 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // Drivers 10 | #include 11 | #include 12 | 13 | #define TAG_TYPE_MIFARE_CLASSIC (0) 14 | #define TAG_TYPE_1 (1) 15 | #define TAG_TYPE_2 (2) 16 | #define TAG_TYPE_3 (3) 17 | #define TAG_TYPE_4 (4) 18 | #define TAG_TYPE_UNKNOWN (99) 19 | 20 | #define IRQ (2) 21 | #define RESET (3) // Not connected by default on the NFC Shield 22 | 23 | class NfcAdapter { 24 | public: 25 | NfcAdapter(PN532Interface &interface); 26 | 27 | ~NfcAdapter(void); 28 | void begin(boolean verbose=true); 29 | boolean tagPresent(unsigned long timeout=0); // tagAvailable 30 | NfcTag read(); 31 | boolean write(NdefMessage& ndefMessage); 32 | // erase tag by writing an empty NDEF record 33 | boolean erase(); 34 | // format a tag as NDEF 35 | boolean format(); 36 | // reset tag back to factory state 37 | boolean clean(); 38 | private: 39 | PN532* shield; 40 | byte uid[7]; // Buffer to store the returned UID 41 | unsigned int uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type) 42 | unsigned int guessTagType(); 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/NfcDriver.h: -------------------------------------------------------------------------------- 1 | // eventually the NFC drivers should extend this class 2 | class NfcDriver 3 | { 4 | public: 5 | virtual NfcTag read(uint8_t * uid, int uidLength) = 0; 6 | virtual boolean write(NdefMessage& message, uint8_t * uid, int uidLength) = 0; 7 | // erase() 8 | // format() 9 | } -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/NfcTag.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | NfcTag::NfcTag() 4 | { 5 | _uid = 0; 6 | _uidLength = 0; 7 | _tagType = "Unknown"; 8 | _ndefMessage = (NdefMessage*)NULL; 9 | } 10 | 11 | NfcTag::NfcTag(byte *uid, unsigned int uidLength) 12 | { 13 | _uid = uid; 14 | _uidLength = uidLength; 15 | _tagType = "Unknown"; 16 | _ndefMessage = (NdefMessage*)NULL; 17 | } 18 | 19 | NfcTag::NfcTag(byte *uid, unsigned int uidLength, String tagType) 20 | { 21 | _uid = uid; 22 | _uidLength = uidLength; 23 | _tagType = tagType; 24 | _ndefMessage = (NdefMessage*)NULL; 25 | } 26 | 27 | NfcTag::NfcTag(byte *uid, unsigned int uidLength, String tagType, NdefMessage& ndefMessage) 28 | { 29 | _uid = uid; 30 | _uidLength = uidLength; 31 | _tagType = tagType; 32 | _ndefMessage = new NdefMessage(ndefMessage); 33 | } 34 | 35 | // I don't like this version, but it will use less memory 36 | NfcTag::NfcTag(byte *uid, unsigned int uidLength, String tagType, const byte *ndefData, const int ndefDataLength) 37 | { 38 | _uid = uid; 39 | _uidLength = uidLength; 40 | _tagType = tagType; 41 | _ndefMessage = new NdefMessage(ndefData, ndefDataLength); 42 | } 43 | 44 | NfcTag::~NfcTag() 45 | { 46 | delete _ndefMessage; 47 | } 48 | 49 | NfcTag& NfcTag::operator=(const NfcTag& rhs) 50 | { 51 | if (this != &rhs) 52 | { 53 | delete _ndefMessage; 54 | _uid = rhs._uid; 55 | _uidLength = rhs._uidLength; 56 | _tagType = rhs._tagType; 57 | // TODO do I need a copy here? 58 | _ndefMessage = rhs._ndefMessage; 59 | } 60 | return *this; 61 | } 62 | 63 | uint8_t NfcTag::getUidLength() 64 | { 65 | return _uidLength; 66 | } 67 | 68 | void NfcTag::getUid(byte *uid, unsigned int uidLength) 69 | { 70 | memcpy(uid, _uid, _uidLength < uidLength ? _uidLength : uidLength); 71 | } 72 | 73 | String NfcTag::getUidString() 74 | { 75 | String uidString = ""; 76 | for (int i = 0; i < _uidLength; i++) 77 | { 78 | if (i > 0) 79 | { 80 | uidString += " "; 81 | } 82 | 83 | if (_uid[i] < 0xF) 84 | { 85 | uidString += "0"; 86 | } 87 | 88 | uidString += String((unsigned int)_uid[i], (unsigned char)HEX); 89 | } 90 | uidString.toUpperCase(); 91 | return uidString; 92 | } 93 | 94 | String NfcTag::getTagType() 95 | { 96 | return _tagType; 97 | } 98 | 99 | boolean NfcTag::hasNdefMessage() 100 | { 101 | return (_ndefMessage != NULL); 102 | } 103 | 104 | NdefMessage NfcTag::getNdefMessage() 105 | { 106 | return *_ndefMessage; 107 | } 108 | 109 | void NfcTag::print() 110 | { 111 | Serial.print(F("NFC Tag - "));Serial.println(_tagType); 112 | Serial.print(F("UID "));Serial.println(getUidString()); 113 | if (_ndefMessage == NULL) 114 | { 115 | Serial.println(F("\nNo NDEF Message")); 116 | } 117 | else 118 | { 119 | _ndefMessage->print(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/NfcTag.h: -------------------------------------------------------------------------------- 1 | #ifndef NfcTag_h 2 | #define NfcTag_h 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class NfcTag 9 | { 10 | public: 11 | NfcTag(); 12 | NfcTag(byte *uid, unsigned int uidLength); 13 | NfcTag(byte *uid, unsigned int uidLength, String tagType); 14 | NfcTag(byte *uid, unsigned int uidLength, String tagType, NdefMessage& ndefMessage); 15 | NfcTag(byte *uid, unsigned int uidLength, String tagType, const byte *ndefData, const int ndefDataLength); 16 | ~NfcTag(void); 17 | NfcTag& operator=(const NfcTag& rhs); 18 | uint8_t getUidLength(); 19 | void getUid(byte *uid, unsigned int uidLength); 20 | String getUidString(); 21 | String getTagType(); 22 | boolean hasNdefMessage(); 23 | NdefMessage getNdefMessage(); 24 | void print(); 25 | private: 26 | byte *_uid; 27 | unsigned int _uidLength; 28 | String _tagType; // Mifare Classic, NFC Forum Type {1,2,3,4}, Unknown 29 | NdefMessage* _ndefMessage; 30 | // TODO capacity 31 | // TODO isFormatted 32 | }; 33 | 34 | #endif -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/README.md: -------------------------------------------------------------------------------- 1 | # NDEF Library for Arduino 2 | 3 | Read and Write NDEF messages on NFC Tags with Arduino. 4 | 5 | NFC Data Exchange Format (NDEF) is a common data format that operates across all NFC devices, regardless of the underlying tag or device technology. 6 | 7 | This code works with the [Adafruit NFC Shield](https://www.adafruit.com/products/789), [Seeed Studio NFC Shield v2.0](http://www.seeedstudio.com/depot/nfc-shield-v20-p-1370.html) and the [Seeed Studio NFC Shield](http://www.seeedstudio.com/depot/nfc-shield-p-916.html?cPath=73). The library supports I2C for the Adafruit shield and SPI with the Seeed shields. The Adafruit Shield can also be modified to use SPI. It should also work with the [Adafruit NFC Breakout Board](https://www.adafruit.com/products/364). 8 | 9 | ### Supports 10 | - Reading from Mifare Classic Tags with 4 byte UIDs. 11 | - Writing to Mifare Classic Tags with 4 byte UIDs. 12 | - Reading from Mifare Ultralight tags. 13 | - Writing to Mifare Ultralight tags. 14 | - Peer to Peer with the Seeed Studio shield 15 | 16 | ### Requires 17 | 18 | [Yihui Xiong's PN532 Library](https://github.com/Seeed-Studio/PN532) 19 | 20 | ## Getting Started 21 | 22 | To use the Ndef library in your code, include the following in your sketch 23 | 24 | For the Adafruit Shield using I2C 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | PN532_I2C pn532_i2c(Wire); 32 | NfcAdapter nfc = NfcAdapter(pn532_i2c); 33 | 34 | For the Seeed Shield using SPI 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | PN532_SPI pn532spi(SPI, 10); 42 | NfcAdapter nfc = NfcAdapter(pn532spi); 43 | 44 | ### NfcAdapter 45 | 46 | The user interacts with the NfcAdapter to read and write NFC tags using the NFC shield. 47 | 48 | Read a message from a tag 49 | 50 | if (nfc.tagPresent()) { 51 | NfcTag tag = nfc.read(); 52 | tag.print(); 53 | } 54 | 55 | Write a message to a tag 56 | 57 | if (nfc.tagPresent()) { 58 | NdefMessage message = NdefMessage(); 59 | message.addTextRecord("Hello, Arduino!"); 60 | success = nfc.write(message); 61 | } 62 | 63 | Erase a tag. Tags are erased by writing an empty NDEF message. Tags are not zeroed out the old data may still be read off a tag using an application like [NXP's TagInfo](https://play.google.com/store/apps/details?id=com.nxp.taginfolite&hl=en). 64 | 65 | if (nfc.tagPresent()) { 66 | success = nfc.erase(); 67 | } 68 | 69 | 70 | Format a Mifare Classic tag as NDEF. 71 | 72 | if (nfc.tagPresent()) { 73 | success = nfc.format(); 74 | } 75 | 76 | 77 | Clean a tag. Cleaning resets a tag back to a factory-like state. For Mifare Classic, tag is zeroed and reformatted as Mifare Classic (non-NDEF). For Mifare Ultralight, the tag is zeroed and left empty. 78 | 79 | if (nfc.tagPresent()) { 80 | success = nfc.clean(); 81 | } 82 | 83 | 84 | ### NfcTag 85 | 86 | Reading a tag with the shield, returns a NfcTag object. The NfcTag object contains meta data about the tag UID, technology, size. When an NDEF tag is read, the NfcTag object contains a NdefMessage. 87 | 88 | ### NdefMessage 89 | 90 | A NdefMessage consist of one or more NdefRecords. 91 | 92 | The NdefMessage object has helper methods for adding records. 93 | 94 | ndefMessage.addTextRecord("hello, world"); 95 | ndefMessage.addUriRecord("http://arduino.cc"); 96 | 97 | The NdefMessage object is responsible for encoding NdefMessage into bytes so it can be written to a tag. The NdefMessage also decodes bytes read from a tag back into a NdefMessage object. 98 | 99 | ### NdefRecord 100 | 101 | A NdefRecord carries a payload and info about the payload within a NdefMessage. 102 | 103 | ### Peer to Peer 104 | 105 | Peer to Peer is provided by the LLCP and SNEP support in the [Seeed Studio library](https://github.com/Seeed-Studio/PN532). P2P requires SPI and has only been tested with the Seeed Studio shield. Peer to Peer was tested between Arduino and Android or BlackBerry 10. (Unfortunately Windows Phone 8 did not work.) See [P2P_Send](examples/P2P_Send/P2P_Send.ino) and [P2P_Receive](examples/P2P_Receive/P2P_Receive.ino) for more info. 106 | 107 | ### Specifications 108 | 109 | This code is based on the "NFC Data Exchange Format (NDEF) Technical Specification" and the "Record Type Definition Technical Specifications" that can be downloaded from the [NFC Forum](http://www.nfc-forum.org/specs/spec_license). 110 | 111 | ### Tests 112 | 113 | To run the tests, you'll need [ArduinoUnit](https://github.com/mmurdoch/arduinounit). To "install", I clone the repo to my home directory and symlink the source into ~/Documents/Arduino/libraries/ArduinoUnit. 114 | 115 | $ cd ~ 116 | $ git clone git@github.com:mmurdoch/arduinounit.git 117 | $ cd ~/Documents/Arduino/libraries/ 118 | $ ln -s ~/arduinounit/src ArduinoUnit 119 | 120 | Tests can be run on an Uno without a NFC shield, since the NDEF logic is what is being tested. 121 | 122 | ## Warning 123 | 124 | This software is in development. It works for the happy path. Error handling could use improvement. It runs out of memory, especially on the Uno board. Use small messages with the Uno. The Due board can write larger messages. Please submit patches. 125 | 126 | ## Book 127 | Need more info? Check out my book [Beginning NFC: Near Field Communication with Arduino, Android, and PhoneGap](http://shop.oreilly.com/product/0636920021193.do) 128 | 129 | ![Beginning NFC](http://akamaicovers.oreilly.com/images/0636920021193/cat.gif) 130 | 131 | ## License 132 | 133 | [BSD License](https://github.com/don/Ndef/blob/master/LICENSE.txt) (c) 2013-2014, Don Coleman 134 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/examples/CleanTag/CleanTag.ino: -------------------------------------------------------------------------------- 1 | // Clean resets a tag back to factory-like state 2 | // For Mifare Classic, tag is zero'd and reformatted as Mifare Classic 3 | // For Mifare Ultralight, tags is zero'd and left empty 4 | 5 | #if 0 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | PN532_SPI pn532spi(SPI, 10); 12 | NfcAdapter nfc = NfcAdapter(pn532spi); 13 | #else 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | PN532_I2C pn532_i2c(Wire); 21 | NfcAdapter nfc = NfcAdapter(pn532_i2c); 22 | #endif 23 | 24 | void setup(void) { 25 | Serial.begin(9600); 26 | Serial.println("NFC Tag Cleaner"); 27 | nfc.begin(); 28 | } 29 | 30 | void loop(void) { 31 | 32 | Serial.println("\nPlace a tag on the NFC reader to clean."); 33 | 34 | if (nfc.tagPresent()) { 35 | 36 | bool success = nfc.clean(); 37 | if (success) { 38 | Serial.println("\nSuccess, tag restored to factory state."); 39 | } else { 40 | Serial.println("\nError, unable to clean tag."); 41 | } 42 | 43 | } 44 | delay(5000); 45 | } 46 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/examples/EraseTag/EraseTag.ino: -------------------------------------------------------------------------------- 1 | // Erases a NFC tag by writing an empty NDEF message 2 | 3 | #if 0 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | PN532_SPI pn532spi(SPI, 10); 10 | NfcAdapter nfc = NfcAdapter(pn532spi); 11 | #else 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | PN532_I2C pn532_i2c(Wire); 19 | NfcAdapter nfc = NfcAdapter(pn532_i2c); 20 | #endif 21 | 22 | void setup(void) { 23 | Serial.begin(9600); 24 | Serial.println("NFC Tag Eraser"); 25 | nfc.begin(); 26 | } 27 | 28 | void loop(void) { 29 | Serial.println("\nPlace a tag on the NFC reader to erase."); 30 | 31 | if (nfc.tagPresent()) { 32 | 33 | bool success = nfc.erase(); 34 | if (success) { 35 | Serial.println("\nSuccess, tag contains an empty record."); 36 | } else { 37 | Serial.println("\nUnable to erase tag."); 38 | } 39 | 40 | } 41 | delay(5000); 42 | } 43 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/examples/FormatTag/FormatTag.ino: -------------------------------------------------------------------------------- 1 | // Formats a Mifare Classic tags as an NDEF tag 2 | // This will fail if the tag is already formatted NDEF 3 | // nfc.clean will turn a NDEF formatted Mifare Classic tag back to the Mifare Classic format 4 | 5 | #if 0 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | PN532_SPI pn532spi(SPI, 10); 12 | NfcAdapter nfc = NfcAdapter(pn532spi); 13 | #else 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | PN532_I2C pn532_i2c(Wire); 21 | NfcAdapter nfc = NfcAdapter(pn532_i2c); 22 | #endif 23 | 24 | void setup(void) { 25 | Serial.begin(9600); 26 | Serial.println("NDEF Formatter"); 27 | nfc.begin(); 28 | } 29 | 30 | void loop(void) { 31 | 32 | Serial.println("\nPlace an unformatted Mifare Classic tag on the reader."); 33 | if (nfc.tagPresent()) { 34 | 35 | bool success = nfc.format(); 36 | if (success) { 37 | Serial.println("\nSuccess, tag formatted as NDEF."); 38 | } else { 39 | Serial.println("\nFormat failed."); 40 | } 41 | 42 | } 43 | delay(5000); 44 | } 45 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/examples/P2P_Receive/P2P_Receive.ino: -------------------------------------------------------------------------------- 1 | // Receive a NDEF message from a Peer 2 | // Requires SPI. Tested with Seeed Studio NFC Shield v2 3 | 4 | #include "SPI.h" 5 | #include "PN532_SPI.h" 6 | #include "snep.h" 7 | #include "NdefMessage.h" 8 | 9 | PN532_SPI pn532spi(SPI, 10); 10 | SNEP nfc(pn532spi); 11 | uint8_t ndefBuf[128]; 12 | 13 | void setup() { 14 | Serial.begin(9600); 15 | Serial.println("NFC Peer to Peer Example - Receive Message"); 16 | } 17 | 18 | void loop() { 19 | Serial.println("Waiting for message from Peer"); 20 | int msgSize = nfc.read(ndefBuf, sizeof(ndefBuf)); 21 | if (msgSize > 0) { 22 | NdefMessage msg = NdefMessage(ndefBuf, msgSize); 23 | msg.print(); 24 | Serial.println("\nSuccess"); 25 | } else { 26 | Serial.println("Failed"); 27 | } 28 | delay(3000); 29 | } 30 | 31 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/examples/P2P_Receive_LCD/P2P_Receive_LCD.ino: -------------------------------------------------------------------------------- 1 | // Receive a NDEF message from a Peer and 2 | // display the payload of the first record on a LCD 3 | // 4 | // SeeedStudio NFC shield http://www.seeedstudio.com/depot/NFC-Shield-V20-p-1370.html 5 | // LCD using the Adafruit backpack http://adafru.it/292 6 | // Adafruit Liquid Crystal library https://github.com/adafruit/LiquidCrystal 7 | // Use a Android of BlackBerry phone to send a message to the NFC shield 8 | 9 | #include "SPI.h" 10 | #include "PN532_SPI.h" 11 | #include "snep.h" 12 | #include "NdefMessage.h" 13 | 14 | #include "Wire.h" 15 | #include "LiquidCrystal.h" 16 | 17 | PN532_SPI pn532spi(SPI, 10); 18 | SNEP nfc(pn532spi); 19 | uint8_t ndefBuf[128]; 20 | 21 | // Connect via i2c, default address #0 (A0-A2 not jumpered) 22 | LiquidCrystal lcd(0); 23 | 24 | void setup() { 25 | Serial.begin(9600); 26 | // set up the LCD's number of rows and columns: 27 | lcd.begin(16, 2); 28 | Serial.println("NFC Peer to Peer Example - Receive Message"); 29 | } 30 | 31 | void loop() { 32 | Serial.println("Waiting for message from a peer"); 33 | int msgSize = nfc.read(ndefBuf, sizeof(ndefBuf)); 34 | if (msgSize > 0) { 35 | NdefMessage msg = NdefMessage(ndefBuf, msgSize); 36 | msg.print(); 37 | 38 | NdefRecord record = msg.getRecord(0); 39 | 40 | int payloadLength = record.getPayloadLength(); 41 | byte payload[payloadLength]; 42 | record.getPayload(payload); 43 | 44 | // The TNF and Type are used to determine how your application processes the payload 45 | // There's no generic processing for the payload, it's returned as a byte[] 46 | int startChar = 0; 47 | if (record.getTnf() == TNF_WELL_KNOWN && record.getType() == "T") { // text message 48 | // skip the language code 49 | startChar = payload[0] + 1; 50 | } else if (record.getTnf() == TNF_WELL_KNOWN && record.getType() == "U") { // URI 51 | // skip the url prefix (future versions should decode) 52 | startChar = 1; 53 | } 54 | 55 | // Force the data into a String (might fail for some content) 56 | // Real code should use smarter processing 57 | String payloadAsString = ""; 58 | for (int c = startChar; c < payloadLength; c++) { 59 | payloadAsString += (char)payload[c]; 60 | } 61 | 62 | // print on the LCD display 63 | lcd.setCursor(0, 0); 64 | lcd.print(payloadAsString); 65 | 66 | Serial.println("\nSuccess"); 67 | } else { 68 | Serial.println("Failed"); 69 | } 70 | delay(3000); 71 | } 72 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/examples/P2P_Send/P2P_Send.ino: -------------------------------------------------------------------------------- 1 | // Sends a NDEF Message to a Peer 2 | // Requires SPI. Tested with Seeed Studio NFC Shield v2 3 | 4 | #include "SPI.h" 5 | #include "PN532_SPI.h" 6 | #include "snep.h" 7 | #include "NdefMessage.h" 8 | 9 | PN532_SPI pn532spi(SPI, 10); 10 | SNEP nfc(pn532spi); 11 | uint8_t ndefBuf[128]; 12 | 13 | void setup() { 14 | Serial.begin(9600); 15 | Serial.println("NFC Peer to Peer Example - Send Message"); 16 | } 17 | 18 | void loop() { 19 | Serial.println("Send a message to Peer"); 20 | 21 | NdefMessage message = NdefMessage(); 22 | message.addUriRecord("http://shop.oreilly.com/product/mobile/0636920021193.do"); 23 | //message.addUriRecord("http://arduino.cc"); 24 | //message.addUriRecord("https://github.com/don/NDEF"); 25 | 26 | 27 | int messageSize = message.getEncodedSize(); 28 | if (messageSize > sizeof(ndefBuf)) { 29 | Serial.println("ndefBuf is too small"); 30 | while (1) { 31 | } 32 | } 33 | 34 | message.encode(ndefBuf); 35 | if (0 >= nfc.write(ndefBuf, messageSize)) { 36 | Serial.println("Failed"); 37 | } else { 38 | Serial.println("Success"); 39 | } 40 | 41 | delay(3000); 42 | } 43 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/examples/ReadTag/ReadTag.ino: -------------------------------------------------------------------------------- 1 | 2 | #if 0 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | PN532_SPI pn532spi(SPI, 10); 9 | NfcAdapter nfc = NfcAdapter(pn532spi); 10 | #else 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | PN532_I2C pn532_i2c(Wire); 18 | NfcAdapter nfc = NfcAdapter(pn532_i2c); 19 | #endif 20 | 21 | void setup(void) { 22 | Serial.begin(9600); 23 | Serial.println("NDEF Reader"); 24 | nfc.begin(); 25 | } 26 | 27 | void loop(void) { 28 | Serial.println("\nScan a NFC tag\n"); 29 | if (nfc.tagPresent()) 30 | { 31 | NfcTag tag = nfc.read(); 32 | tag.print(); 33 | } 34 | delay(5000); 35 | } -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/examples/ReadTagExtended/ReadTagExtended.ino: -------------------------------------------------------------------------------- 1 | #if 0 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | PN532_SPI pn532spi(SPI, 10); 8 | NfcAdapter nfc = NfcAdapter(pn532spi); 9 | #else 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | PN532_I2C pn532_i2c(Wire); 17 | NfcAdapter nfc = NfcAdapter(pn532_i2c); 18 | #endif 19 | 20 | void setup(void) { 21 | Serial.begin(9600); 22 | Serial.println("NDEF Reader"); 23 | nfc.begin(); 24 | } 25 | 26 | void loop(void) { 27 | Serial.println("\nScan a NFC tag\n"); 28 | 29 | if (nfc.tagPresent()) 30 | { 31 | NfcTag tag = nfc.read(); 32 | Serial.println(tag.getTagType()); 33 | Serial.print("UID: ");Serial.println(tag.getUidString()); 34 | 35 | if (tag.hasNdefMessage()) // every tag won't have a message 36 | { 37 | 38 | NdefMessage message = tag.getNdefMessage(); 39 | Serial.print("\nThis NFC Tag contains an NDEF Message with "); 40 | Serial.print(message.getRecordCount()); 41 | Serial.print(" NDEF Record"); 42 | if (message.getRecordCount() != 1) { 43 | Serial.print("s"); 44 | } 45 | Serial.println("."); 46 | 47 | // cycle through the records, printing some info from each 48 | int recordCount = message.getRecordCount(); 49 | for (int i = 0; i < recordCount; i++) 50 | { 51 | Serial.print("\nNDEF Record ");Serial.println(i+1); 52 | NdefRecord record = message.getRecord(i); 53 | // NdefRecord record = message[i]; // alternate syntax 54 | 55 | Serial.print(" TNF: ");Serial.println(record.getTnf()); 56 | Serial.print(" Type: ");Serial.println(record.getType()); // will be "" for TNF_EMPTY 57 | 58 | // The TNF and Type should be used to determine how your application processes the payload 59 | // There's no generic processing for the payload, it's returned as a byte[] 60 | int payloadLength = record.getPayloadLength(); 61 | byte payload[payloadLength]; 62 | record.getPayload(payload); 63 | 64 | // Print the Hex and Printable Characters 65 | Serial.print(" Payload (HEX): "); 66 | PrintHexChar(payload, payloadLength); 67 | 68 | // Force the data into a String (might work depending on the content) 69 | // Real code should use smarter processing 70 | String payloadAsString = ""; 71 | for (int c = 0; c < payloadLength; c++) { 72 | payloadAsString += (char)payload[c]; 73 | } 74 | Serial.print(" Payload (as String): "); 75 | Serial.println(payloadAsString); 76 | 77 | // id is probably blank and will return "" 78 | String uid = record.getId(); 79 | if (uid != "") { 80 | Serial.print(" ID: ");Serial.println(uid); 81 | } 82 | } 83 | } 84 | } 85 | delay(3000); 86 | } 87 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/examples/WriteTag/WriteTag.ino: -------------------------------------------------------------------------------- 1 | #if 0 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | PN532_SPI pn532spi(SPI, 10); 8 | NfcAdapter nfc = NfcAdapter(pn532spi); 9 | #else 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | PN532_I2C pn532_i2c(Wire); 17 | NfcAdapter nfc = NfcAdapter(pn532_i2c); 18 | #endif 19 | 20 | void setup() { 21 | Serial.begin(9600); 22 | Serial.println("NDEF Writer"); 23 | nfc.begin(); 24 | } 25 | 26 | void loop() { 27 | Serial.println("\nPlace a formatted Mifare Classic NFC tag on the reader."); 28 | if (nfc.tagPresent()) { 29 | NdefMessage message = NdefMessage(); 30 | message.addUriRecord("http://arduino.cc"); 31 | 32 | bool success = nfc.write(message); 33 | if (success) { 34 | Serial.println("Success. Try reading this tag with your phone."); 35 | } else { 36 | Serial.println("Write failed."); 37 | } 38 | } 39 | delay(5000); 40 | } -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/examples/WriteTagMultipleRecords/WriteTagMultipleRecords.ino: -------------------------------------------------------------------------------- 1 | #if 0 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | PN532_SPI pn532spi(SPI, 10); 8 | NfcAdapter nfc = NfcAdapter(pn532spi); 9 | #else 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | PN532_I2C pn532_i2c(Wire); 17 | NfcAdapter nfc = NfcAdapter(pn532_i2c); 18 | #endif 19 | 20 | void setup() { 21 | Serial.begin(9600); 22 | Serial.println("NDEF Writer"); 23 | nfc.begin(); 24 | } 25 | 26 | void loop() { 27 | Serial.println("\nPlace a formatted Mifare Classic NFC tag on the reader."); 28 | if (nfc.tagPresent()) { 29 | NdefMessage message = NdefMessage(); 30 | message.addTextRecord("Hello, Arduino!"); 31 | message.addUriRecord("http://arduino.cc"); 32 | message.addTextRecord("Goodbye, Arduino!"); 33 | boolean success = nfc.write(message); 34 | if (success) { 35 | Serial.println("Success. Try reading this tag with your phone."); 36 | } else { 37 | Serial.println("Write failed"); 38 | } 39 | } 40 | delay(3000); 41 | } 42 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Ndef 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | MifareClassic KEYWORD1 10 | MifareUltralight KEYWORD1 11 | NdefMessage KEYWORD1 12 | NdefRecord KEYWORD1 13 | NfcAdapter KEYWORD1 14 | NfcDriver KEYWORD1 15 | NfcTag KEYWORD1 16 | 17 | ####################################### 18 | # Methods and Functions (KEYWORD2) 19 | ####################################### 20 | 21 | addEmptyRecord KEYWORD2 22 | addMimeMediaRecord KEYWORD2 23 | addRecord KEYWORD2 24 | addTextRecord KEYWORD2 25 | addUriRecord KEYWORD2 26 | begin KEYWORD2 27 | encode KEYWORD2 28 | erase KEYWORD2 29 | format KEYWORD2 30 | getEncodedSize KEYWORD2 31 | getId KEYWORD2 32 | getIdLength KEYWORD2 33 | getNdefMessage KEYWORD2 34 | getPayload KEYWORD2 35 | getPayloadLength KEYWORD2 36 | getRecord KEYWORD2 37 | getRecordCount KEYWORD2 38 | getTagType KEYWORD2 39 | getTnf KEYWORD2 40 | getType KEYWORD2 41 | getTypeLength KEYWORD2 42 | getUid KEYWORD2 43 | getUidLength KEYWORD2 44 | getUidString KEYWORD2 45 | hasNdefMessage KEYWORD2 46 | print KEYWORD2 47 | read KEYWORD2 48 | setId KEYWORD2 49 | setPayload KEYWORD2 50 | setTnf KEYWORD2 51 | setType KEYWORD2 52 | share KEYWORD2 53 | tagPresent KEYWORD2 54 | unshare KEYWORD2 55 | write KEYWORD2 56 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/tests/NdefMemoryTest/NdefMemoryTest.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void leakCheck(void (*callback)()) 8 | { 9 | int start = freeMemory(); 10 | (*callback)(); 11 | int end = freeMemory(); 12 | Serial.println((end - start), DEC); 13 | } 14 | 15 | // Custom Assertion 16 | void assertNoLeak(void (*callback)()) 17 | { 18 | int start = freeMemory(); 19 | (*callback)(); 20 | int end = freeMemory(); 21 | assertEqual(0, (start - end)); 22 | } 23 | 24 | void record() 25 | { 26 | NdefRecord* r = new NdefRecord(); 27 | delete r; 28 | } 29 | 30 | void emptyRecord() 31 | { 32 | NdefRecord* r = new NdefRecord(); 33 | r->print(); 34 | delete r; 35 | } 36 | 37 | void textRecord() 38 | { 39 | NdefRecord* r = new NdefRecord(); 40 | r->setTnf(0x1); 41 | uint8_t type[] = { 0x54 }; 42 | r->setType(type, sizeof(type)); 43 | uint8_t payload[] = { 0x1A, 0x1B, 0x1C }; 44 | r->setPayload(payload, sizeof(payload)); 45 | r->print(); 46 | delete r; 47 | } 48 | 49 | void recordMallocZero() 50 | { 51 | NdefRecord r = NdefRecord(); 52 | String type = r.getType(); 53 | String id = r.getId(); 54 | byte payload[r.getPayloadLength()]; 55 | r.getPayload(payload); 56 | } 57 | 58 | // this is OK 59 | void emptyMessage() 60 | { 61 | NdefMessage* m = new NdefMessage(); 62 | delete m; 63 | } 64 | 65 | // this is OK 66 | void printEmptyMessage() 67 | { 68 | NdefMessage* m = new NdefMessage(); 69 | m->print(); 70 | delete m; 71 | } 72 | 73 | // this is OK 74 | void printEmptyMessageNoNew() 75 | { 76 | NdefMessage m = NdefMessage(); 77 | m.print(); 78 | } 79 | 80 | void messageWithTextRecord() 81 | { 82 | NdefMessage m = NdefMessage(); 83 | m.addTextRecord("foo"); 84 | m.print(); 85 | } 86 | 87 | void messageWithEmptyRecord() 88 | { 89 | NdefMessage m = NdefMessage(); 90 | NdefRecord r = NdefRecord(); 91 | m.addRecord(r); 92 | m.print(); 93 | } 94 | 95 | void messageWithoutHelper() 96 | { 97 | NdefMessage m = NdefMessage(); 98 | NdefRecord r = NdefRecord(); 99 | r.setTnf(1); 100 | uint8_t type[] = { 0x54 }; 101 | r.setType(type, sizeof(type)); 102 | uint8_t payload[] = { 0x02, 0x65, 0x6E, 0x66, 0x6F, 0x6F }; 103 | r.setPayload(payload, sizeof(payload)); 104 | m.addRecord(r); 105 | m.print(); 106 | } 107 | 108 | void messageWithId() 109 | { 110 | NdefMessage m = NdefMessage(); 111 | NdefRecord r = NdefRecord(); 112 | r.setTnf(1); 113 | uint8_t type[] = { 0x54 }; 114 | r.setType(type, sizeof(type)); 115 | uint8_t payload[] = { 0x02, 0x65, 0x6E, 0x66, 0x6F, 0x6F }; 116 | r.setPayload(payload, sizeof(payload)); 117 | uint8_t id[] = { 0x0, 0x0, 0x0 }; 118 | r.setId(id, sizeof(id)); 119 | m.addRecord(r); 120 | m.print(); 121 | } 122 | 123 | void message80() 124 | { 125 | NdefMessage message = NdefMessage(); 126 | message.addTextRecord("This record is 80 characters.X01234567890123456789012345678901234567890123456789"); 127 | //message.print(); 128 | } 129 | 130 | void message100() 131 | { 132 | NdefMessage message = NdefMessage(); 133 | message.addTextRecord("This record is 100 characters.0123456789012345678901234567890123456789012345678901234567890123456789"); 134 | //message.print(); 135 | } 136 | 137 | void message120() 138 | { 139 | NdefMessage message = NdefMessage(); 140 | message.addTextRecord("This record is 120 characters.012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"); 141 | //message.print(); 142 | } 143 | 144 | void setup() { 145 | Serial.begin(9600); 146 | Serial.println("\n"); 147 | Serial.println(F("=========")); 148 | Serial.println(freeMemory()); 149 | Serial.println(F("=========")); 150 | } 151 | 152 | test(memoryKludgeEnd) 153 | { 154 | // TODO ensure the output matches start 155 | Serial.println(F("=========")); 156 | Serial.print("End ");Serial.println(freeMemory()); 157 | Serial.println(F("=========")); 158 | } 159 | 160 | test(recordLeaks) 161 | { 162 | assertNoLeak(&record); 163 | assertNoLeak(&emptyRecord); 164 | assertNoLeak(&textRecord); 165 | } 166 | 167 | test(recordAccessorLeaks) 168 | { 169 | assertNoLeak(&recordMallocZero); 170 | } 171 | 172 | test(messageLeaks) 173 | { 174 | assertNoLeak(&emptyMessage); 175 | assertNoLeak(&printEmptyMessage); 176 | assertNoLeak(&printEmptyMessageNoNew); 177 | assertNoLeak(&messageWithTextRecord); 178 | assertNoLeak(&messageWithEmptyRecord); 179 | assertNoLeak(&messageWithoutHelper); 180 | assertNoLeak(&messageWithId); 181 | } 182 | 183 | test(messageOneBigRecord) 184 | { 185 | assertNoLeak(&message80); 186 | // The next 2 fail. Maybe out of memory? Look into helper methods 187 | //assertNoLeak(&message100); 188 | //assertNoLeak(&message120); 189 | } 190 | 191 | test(memoryKludgeStart) 192 | { 193 | Serial.println(F("---------")); 194 | Serial.print("Start ");Serial.println(freeMemory()); 195 | Serial.println(F("---------")); 196 | } 197 | 198 | void loop() { 199 | Test::run(); 200 | } 201 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/tests/NdefUnitTest/NdefUnitTest.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void assertBytesEqual(const uint8_t* expected, const uint8_t* actual, uint8_t size) { 7 | for (int i = 0; i < size; i++) { 8 | assertEqual(expected[i], actual[i]); 9 | } 10 | } 11 | 12 | void setup() { 13 | Serial.begin(9600); 14 | } 15 | 16 | test(accessors) { 17 | NdefRecord record = NdefRecord(); 18 | record.setTnf(TNF_WELL_KNOWN); 19 | uint8_t recordType[] = { 0x54 }; // "T" Text Record 20 | assertEqual(0x54, recordType[0]); 21 | record.setType(recordType, sizeof(recordType)); 22 | // 2 + "en" + "Unit Test" 23 | uint8_t payload[] = { 0x02, 0x65, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x20, 0x54, 0x65, 0x73, 0x74 }; 24 | record.setPayload(payload, sizeof(payload)); 25 | 26 | assertEqual(TNF_WELL_KNOWN, record.getTnf()); 27 | assertEqual(sizeof(recordType), record.getTypeLength()); 28 | assertEqual(1, record.getTypeLength()); 29 | assertEqual(sizeof(payload), record.getPayloadLength()); 30 | assertEqual(12, record.getPayloadLength()); 31 | 32 | uint8_t typeCheck[record.getTypeLength()]; 33 | record.getType(typeCheck); 34 | 35 | assertEqual(0x54, typeCheck[0]); 36 | assertBytesEqual(recordType, typeCheck, sizeof(recordType)); 37 | 38 | uint8_t payloadCheck[record.getPayloadLength()]; 39 | record.getPayload(&payloadCheck[0]); 40 | assertBytesEqual(payload, payloadCheck, sizeof(payload)); 41 | } 42 | 43 | test(newaccessors) { 44 | NdefRecord record = NdefRecord(); 45 | record.setTnf(TNF_WELL_KNOWN); 46 | uint8_t recordType[] = { 0x54 }; // "T" Text Record 47 | assertEqual(0x54, recordType[0]); 48 | record.setType(recordType, sizeof(recordType)); 49 | // 2 + "en" + "Unit Test" 50 | uint8_t payload[] = { 0x02, 0x65, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x20, 0x54, 0x65, 0x73, 0x74 }; 51 | record.setPayload(payload, sizeof(payload)); 52 | 53 | assertEqual(TNF_WELL_KNOWN, record.getTnf()); 54 | assertEqual(sizeof(recordType), record.getTypeLength()); 55 | assertEqual(1, record.getTypeLength()); 56 | assertEqual(sizeof(payload), record.getPayloadLength()); 57 | assertEqual(12, record.getPayloadLength()); 58 | 59 | ::String typeCheck = record.getType(); 60 | assertTrue(typeCheck.equals("T")); 61 | 62 | byte payloadCheck[record.getPayloadLength()]; 63 | record.getPayload(payloadCheck); 64 | assertBytesEqual(payload, payloadCheck, sizeof(payload)); 65 | } 66 | 67 | test(assignment) 68 | { 69 | NdefRecord record = NdefRecord(); 70 | record.setTnf(TNF_WELL_KNOWN); 71 | uint8_t recordType[] = { 0x54 }; // "T" Text Record 72 | assertEqual(0x54, recordType[0]); 73 | record.setType(recordType, sizeof(recordType)); 74 | // 2 + "en" + "Unit Test" 75 | uint8_t payload[] = { 0x02, 0x65, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x20, 0x54, 0x65, 0x73, 0x74 }; 76 | record.setPayload(payload, sizeof(payload)); 77 | 78 | NdefRecord record2 = NdefRecord(); 79 | record2 = record; 80 | 81 | assertEqual(TNF_WELL_KNOWN, record.getTnf()); 82 | assertEqual(sizeof(recordType), record2.getTypeLength()); 83 | assertEqual(sizeof(payload), record2.getPayloadLength()); 84 | 85 | ::String typeCheck = record.getType(); 86 | assertTrue(typeCheck.equals("T")); 87 | 88 | byte payload2[record2.getPayloadLength()]; 89 | record2.getPayload(payload2); 90 | assertBytesEqual(payload, payload2, sizeof(payload)); 91 | } 92 | 93 | test(getEmptyPayload) 94 | { 95 | NdefRecord r = NdefRecord(); 96 | assertEqual(TNF_EMPTY, r.getTnf()); 97 | assertEqual(0, r.getPayloadLength()); 98 | 99 | byte payload[r.getPayloadLength()]; 100 | r.getPayload(payload); 101 | 102 | byte empty[0]; 103 | assertBytesEqual(empty, payload, sizeof(payload)); 104 | } 105 | 106 | void loop() { 107 | Test::run(); 108 | } 109 | -------------------------------------------------------------------------------- /mu3controller/lib/NDEF/tests/NfcTagTest/NfcTagTest.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void setup() { 7 | Serial.begin(9600); 8 | } 9 | 10 | // Test for pull requests #14 and #16 11 | test(getUid) 12 | { 13 | byte uid[4] = { 0x00, 0xFF, 0xAA, 0x17 }; 14 | byte uidFromTag[sizeof(uid)]; 15 | 16 | NfcTag tag = NfcTag(uid, sizeof(uid)); 17 | 18 | assertEqual(sizeof(uid), tag.getUidLength()); 19 | 20 | tag.getUid(uidFromTag, sizeof(uidFromTag)); 21 | 22 | // make sure the 2 uids are the same 23 | for (int i = 0; i < sizeof(uid); i++) { 24 | assertEqual(uid[i], uidFromTag[i]); 25 | } 26 | 27 | // check contents, to ensure the original uid wasn't overwritten 28 | assertEqual(0x00, uid[0]); 29 | assertEqual(0xFF, uid[1]); 30 | assertEqual(0xAA, uid[2]); 31 | assertEqual(0x17, uid[3]); 32 | } 33 | 34 | void loop() { 35 | Test::run(); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/PN532Interface.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef __PN532_INTERFACE_H__ 4 | #define __PN532_INTERFACE_H__ 5 | 6 | #include 7 | 8 | #define PN532_PREAMBLE (0x00) 9 | #define PN532_STARTCODE1 (0x00) 10 | #define PN532_STARTCODE2 (0xFF) 11 | #define PN532_POSTAMBLE (0x00) 12 | 13 | #define PN532_HOSTTOPN532 (0xD4) 14 | #define PN532_PN532TOHOST (0xD5) 15 | 16 | #define PN532_ACK_WAIT_TIME (10) // ms, timeout of waiting for ACK 17 | 18 | #define PN532_INVALID_ACK (-1) 19 | #define PN532_TIMEOUT (-2) 20 | #define PN532_INVALID_FRAME (-3) 21 | #define PN532_NO_SPACE (-4) 22 | 23 | #define REVERSE_BITS_ORDER(b) b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; \ 24 | b = (b & 0xCC) >> 2 | (b & 0x33) << 2; \ 25 | b = (b & 0xAA) >> 1 | (b & 0x55) << 1 26 | 27 | class PN532Interface 28 | { 29 | public: 30 | virtual void begin() = 0; 31 | virtual void wakeup() = 0; 32 | 33 | /** 34 | * @brief write a command and check ack 35 | * @param header packet header 36 | * @param hlen length of header 37 | * @param body packet body 38 | * @param blen length of body 39 | * @return 0 success 40 | * not 0 failed 41 | */ 42 | virtual int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0) = 0; 43 | 44 | /** 45 | * @brief read the response of a command, strip prefix and suffix 46 | * @param buf to contain the response data 47 | * @param len lenght to read 48 | * @param timeout max time to wait, 0 means no timeout 49 | * @return >=0 length of response without prefix and suffix 50 | * <0 failed to read response 51 | */ 52 | virtual int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout = 1000) = 0; 53 | }; 54 | 55 | #endif 56 | 57 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/PN532_debug.h: -------------------------------------------------------------------------------- 1 | #ifndef __DEBUG_H__ 2 | #define __DEBUG_H__ 3 | 4 | //#define DEBUG 5 | 6 | #include "Arduino.h" 7 | 8 | #ifdef DEBUG 9 | #define DMSG(args...) Serial.print(args) 10 | #define DMSG_STR(str) Serial.println(str) 11 | #define DMSG_HEX(num) Serial.print(' '); Serial.print((num>>4)&0x0F, HEX); Serial.print(num&0x0F, HEX) 12 | #define DMSG_INT(num) Serial.print(' '); Serial.print(num) 13 | #else 14 | #define DMSG(args...) 15 | #define DMSG_STR(str) 16 | #define DMSG_HEX(num) 17 | #define DMSG_INT(num) 18 | #endif 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/README.md: -------------------------------------------------------------------------------- 1 | ## NFC library for Arduino 2 | 3 | This is an Arduino library for PN532 to use NFC technology. 4 | It works with 5 | 6 | + [NFC Shield](http://goo.gl/Cac2OH) 7 | + [Xadow NFC](http://goo.gl/qBZMt0) 8 | + [PN532 NFC/RFID controller breakout board](http://goo.gl/tby9Sw) 9 | 10 | ### Features 11 | + Support I2C, SPI and HSU of PN532 12 | + Read/write Mifare Classic Card 13 | + Works with [Don's NDEF Library](http://goo.gl/jDjsXl) 14 | + Support Peer to Peer communication(exchange data with android 4.0+) 15 | + Support [mbed platform](http://goo.gl/kGPovZ) 16 | 17 | ### Getting Started 18 | 1. Download [zip file](http://goo.gl/F6beRM) and 19 | extract the 4 folders(PN532, PN532_SPI, PN532_I2C and PN532_HSU) into Arduino's libraries. 20 | 2. Downlaod [Don's NDEF library](http://goo.gl/ewxeAe) and extract it intro Arduino's libraries. 21 | 3. Follow the examples of the two libraries. 22 | 23 | ### To do 24 | + Card emulation 25 | 26 | ### Contribution 27 | It's based on [Adafruit_NFCShield_I2C](http://goo.gl/pk3FdB). 28 | [Seeed Studio](http://goo.gl/zh1iQh) adds SPI interface and peer to peer communication support. 29 | @Don writes the [NDEF library](http://goo.gl/jDjsXl) to make it more easy to use. 30 | @JiapengLi adds HSU interface. 31 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/emulatetag.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file emulatetag.h 4 | @author Armin Wieser 5 | @license BSD 6 | 7 | Implemented using NFC forum documents & library of libnfc 8 | */ 9 | /**************************************************************************/ 10 | 11 | #ifndef __EMULATETAG_H__ 12 | #define __EMULATETAG_H__ 13 | 14 | #include "PN532.h" 15 | 16 | #define NDEF_MAX_LENGTH 128 // altough ndef can handle up to 0xfffe in size, arduino cannot. 17 | typedef enum {COMMAND_COMPLETE, TAG_NOT_FOUND, FUNCTION_NOT_SUPPORTED, MEMORY_FAILURE, END_OF_FILE_BEFORE_REACHED_LE_BYTES} responseCommand; 18 | 19 | class EmulateTag{ 20 | 21 | public: 22 | EmulateTag(PN532Interface &interface) : pn532(interface), uidPtr(0), tagWrittenByInitiator(false), tagWriteable(true), updateNdefCallback(0) { } 23 | 24 | bool init(); 25 | 26 | bool emulate(const uint16_t tgInitAsTargetTimeout = 0); 27 | 28 | /* 29 | * @param uid pointer to byte array of length 3 (uid is 4 bytes - first byte is fixed) or zero for uid 30 | */ 31 | void setUid(uint8_t* uid = 0); 32 | 33 | void setNdefFile(const uint8_t* ndef, const int16_t ndefLength); 34 | 35 | void getContent(uint8_t** buf, uint16_t* length){ 36 | *buf = ndef_file + 2; // first 2 bytes = length 37 | *length = (ndef_file[0] << 8) + ndef_file[1]; 38 | } 39 | 40 | bool writeOccured(){ 41 | return tagWrittenByInitiator; 42 | } 43 | 44 | void setTagWriteable(bool setWriteable){ 45 | tagWriteable = setWriteable; 46 | } 47 | 48 | uint8_t* getNdefFilePtr(){ 49 | return ndef_file; 50 | } 51 | 52 | uint8_t getNdefMaxLength(){ 53 | return NDEF_MAX_LENGTH; 54 | } 55 | 56 | void attach(void (*func)(uint8_t *buf, uint16_t length)) { 57 | updateNdefCallback = func; 58 | }; 59 | 60 | private: 61 | PN532 pn532; 62 | uint8_t ndef_file[NDEF_MAX_LENGTH]; 63 | uint8_t* uidPtr; 64 | bool tagWrittenByInitiator; 65 | bool tagWriteable; 66 | void (*updateNdefCallback)(uint8_t *ndef, uint16_t length); 67 | 68 | void setResponse(responseCommand cmd, uint8_t* buf, uint8_t* sendlen, uint8_t sendlenOffset = 0); 69 | }; 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/examples/FeliCa_card_detection/FeliCa_card_detection.pde: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | This example will attempt to connect to an FeliCa 4 | card or tag and retrieve some basic information about it 5 | that can be used to determine what type of card it is. 6 | 7 | Note that you need the baud rate to be 115200 because we need to print 8 | out the data and read from the card at the same time! 9 | 10 | To enable debug message, define DEBUG in PN532/PN532_debug.h 11 | 12 | */ 13 | /**************************************************************************/ 14 | #include 15 | 16 | #if 1 17 | #include 18 | #include 19 | #include 20 | 21 | PN532_SPI pn532spi(SPI, 10); 22 | PN532 nfc(pn532spi); 23 | #elif 0 24 | #include 25 | #include 26 | 27 | PN532_HSU pn532hsu(Serial1); 28 | PN532 nfc(pn532hsu); 29 | #else 30 | #include 31 | #include 32 | #include 33 | 34 | PN532_I2C pn532i2c(Wire); 35 | PN532 nfc(pn532i2c); 36 | #endif 37 | 38 | #include 39 | 40 | uint8_t _prevIDm[8]; 41 | unsigned long _prevTime; 42 | 43 | void setup(void) 44 | { 45 | Serial.begin(115200); 46 | Serial.println("Hello!"); 47 | 48 | nfc.begin(); 49 | 50 | uint32_t versiondata = nfc.getFirmwareVersion(); 51 | if (!versiondata) 52 | { 53 | Serial.print("Didn't find PN53x board"); 54 | while (1) {delay(10);}; // halt 55 | } 56 | 57 | // Got ok data, print it out! 58 | Serial.print("Found chip PN5"); Serial.println((versiondata >> 24) & 0xFF, HEX); 59 | Serial.print("Firmware ver. "); Serial.print((versiondata >> 16) & 0xFF, DEC); 60 | Serial.print('.'); Serial.println((versiondata >> 8) & 0xFF, DEC); 61 | 62 | // Set the max number of retry attempts to read from a card 63 | // This prevents us from waiting forever for a card, which is 64 | // the default behaviour of the PN532. 65 | nfc.setPassiveActivationRetries(0xFF); 66 | nfc.SAMConfig(); 67 | 68 | memset(_prevIDm, 0, 8); 69 | } 70 | 71 | void loop(void) 72 | { 73 | uint8_t ret; 74 | uint16_t systemCode = 0xFFFF; 75 | uint8_t requestCode = 0x01; // System Code request 76 | uint8_t idm[8]; 77 | uint8_t pmm[8]; 78 | uint16_t systemCodeResponse; 79 | 80 | // Wait for an FeliCa type cards. 81 | // When one is found, some basic information such as IDm, PMm, and System Code are retrieved. 82 | Serial.print("Waiting for an FeliCa card... "); 83 | ret = nfc.felica_Polling(systemCode, requestCode, idm, pmm, &systemCodeResponse, 5000); 84 | 85 | if (ret != 1) 86 | { 87 | Serial.println("Could not find a card"); 88 | delay(500); 89 | return; 90 | } 91 | 92 | if ( memcmp(idm, _prevIDm, 8) == 0 ) { 93 | if ( (millis() - _prevTime) < 3000 ) { 94 | Serial.println("Same card"); 95 | delay(500); 96 | return; 97 | } 98 | } 99 | 100 | Serial.println("Found a card!"); 101 | Serial.print(" IDm: "); 102 | nfc.PrintHex(idm, 8); 103 | Serial.print(" PMm: "); 104 | nfc.PrintHex(pmm, 8); 105 | Serial.print(" System Code: "); 106 | Serial.print(systemCodeResponse, HEX); 107 | Serial.print("\n"); 108 | 109 | memcpy(_prevIDm, idm, 8); 110 | _prevTime = millis(); 111 | 112 | // Wait 1 second before continuing 113 | Serial.println("Card access completed!\n"); 114 | delay(1000); 115 | } 116 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/examples/FeliCa_card_read/FeliCa_card_read.pde: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | This example will attempt to connect to an FeliCa 4 | card or tag and retrieve some basic information about it 5 | that can be used to determine what type of card it is. 6 | 7 | Note that you need the baud rate to be 115200 because we need to print 8 | out the data and read from the card at the same time! 9 | 10 | To enable debug message, define DEBUG in PN532/PN532_debug.h 11 | 12 | */ 13 | /**************************************************************************/ 14 | #include 15 | 16 | #if 1 17 | #include 18 | #include 19 | #include 20 | 21 | PN532_SPI pn532spi(SPI, 10); 22 | PN532 nfc(pn532spi); 23 | #elif 0 24 | #include 25 | #include 26 | 27 | PN532_HSU pn532hsu(Serial1); 28 | PN532 nfc(pn532hsu); 29 | #else 30 | #include 31 | #include 32 | #include 33 | 34 | PN532_I2C pn532i2c(Wire); 35 | PN532 nfc(pn532i2c); 36 | #endif 37 | 38 | #include 39 | 40 | uint8_t _prevIDm[8]; 41 | unsigned long _prevTime; 42 | 43 | void PrintHex8(const uint8_t d) { 44 | Serial.print(" "); 45 | Serial.print( (d >> 4) & 0x0F, HEX); 46 | Serial.print( d & 0x0F, HEX); 47 | } 48 | 49 | void setup(void) 50 | { 51 | Serial.begin(115200); 52 | Serial.println("Hello!"); 53 | 54 | nfc.begin(); 55 | 56 | uint32_t versiondata = nfc.getFirmwareVersion(); 57 | if (!versiondata) 58 | { 59 | Serial.print("Didn't find PN53x board"); 60 | while (1) {delay(10);}; // halt 61 | } 62 | 63 | // Got ok data, print it out! 64 | Serial.print("Found chip PN5"); Serial.println((versiondata >> 24) & 0xFF, HEX); 65 | Serial.print("Firmware ver. "); Serial.print((versiondata >> 16) & 0xFF, DEC); 66 | Serial.print('.'); Serial.println((versiondata >> 8) & 0xFF, DEC); 67 | 68 | // Set the max number of retry attempts to read from a card 69 | // This prevents us from waiting forever for a card, which is 70 | // the default behaviour of the PN532. 71 | nfc.setPassiveActivationRetries(0xFF); 72 | nfc.SAMConfig(); 73 | 74 | memset(_prevIDm, 0, 8); 75 | } 76 | 77 | void loop(void) 78 | { 79 | uint8_t ret; 80 | uint16_t systemCode = 0xFFFF; 81 | uint8_t requestCode = 0x01; // System Code request 82 | uint8_t idm[8]; 83 | uint8_t pmm[8]; 84 | uint16_t systemCodeResponse; 85 | 86 | // Wait for an FeliCa type cards. 87 | // When one is found, some basic information such as IDm, PMm, and System Code are retrieved. 88 | Serial.print("Waiting for an FeliCa card... "); 89 | ret = nfc.felica_Polling(systemCode, requestCode, idm, pmm, &systemCodeResponse, 5000); 90 | 91 | if (ret != 1) 92 | { 93 | Serial.println("Could not find a card"); 94 | delay(500); 95 | return; 96 | } 97 | 98 | if ( memcmp(idm, _prevIDm, 8) == 0 ) { 99 | if ( (millis() - _prevTime) < 3000 ) { 100 | Serial.println("Same card"); 101 | delay(500); 102 | return; 103 | } 104 | } 105 | 106 | Serial.println("Found a card!"); 107 | Serial.print(" IDm: "); 108 | nfc.PrintHex(idm, 8); 109 | Serial.print(" PMm: "); 110 | nfc.PrintHex(pmm, 8); 111 | Serial.print(" System Code: "); 112 | Serial.print(systemCodeResponse, HEX); 113 | Serial.print("\n"); 114 | 115 | memcpy(_prevIDm, idm, 8); 116 | _prevTime = millis(); 117 | 118 | uint8_t blockData[3][16]; 119 | uint16_t serviceCodeList[1]; 120 | uint16_t blockList[3]; 121 | 122 | Serial.println("Write Without Encryption command "); 123 | serviceCodeList[0] = 0x0009; 124 | blockList[0] = 0x8000; 125 | unsigned long now = millis(); 126 | blockData[0][3] = now & 0xFF; 127 | blockData[0][2] = (now >>= 8) & 0xFF; 128 | blockData[0][1] = (now >>= 8) & 0xFF; 129 | blockData[0][0] = (now >>= 8) & 0xFF; 130 | Serial.print(" Writing current millis ("); 131 | PrintHex8(blockData[0][0]); 132 | PrintHex8(blockData[0][1]); 133 | PrintHex8(blockData[0][2]); 134 | PrintHex8(blockData[0][3]); 135 | Serial.print(" ) to Block 0 -> "); 136 | ret = nfc.felica_WriteWithoutEncryption(1, serviceCodeList, 1, blockList, blockData); 137 | if (ret != 1) 138 | { 139 | Serial.println("error"); 140 | } else { 141 | Serial.println("OK!"); 142 | } 143 | memset(blockData[0], 0, 16); 144 | 145 | Serial.print("Read Without Encryption command -> "); 146 | serviceCodeList[0] = 0x000B; 147 | blockList[0] = 0x8000; 148 | blockList[1] = 0x8001; 149 | blockList[2] = 0x8002; 150 | ret = nfc.felica_ReadWithoutEncryption(1, serviceCodeList, 3, blockList, blockData); 151 | if (ret != 1) 152 | { 153 | Serial.println("error"); 154 | } else { 155 | Serial.println("OK!"); 156 | for(int i=0; i<3; i++ ) { 157 | Serial.print(" Block no. "); Serial.print(i, DEC); Serial.print(": "); 158 | nfc.PrintHex(blockData[i], 16); 159 | } 160 | } 161 | 162 | // Wait 1 second before continuing 163 | Serial.println("Card access completed!\n"); 164 | delay(1000); 165 | } 166 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/examples/android_hce/android_hce.ino: -------------------------------------------------------------------------------- 1 | #if 0 2 | #include 3 | #include 4 | #include "PN532.h" 5 | 6 | PN532_SPI pn532spi(SPI, 10); 7 | PN532 nfc(pn532spi); 8 | #elif 1 9 | #include 10 | #include 11 | 12 | PN532_HSU pn532hsu(Serial1); 13 | PN532 nfc(pn532hsu); 14 | #else 15 | #include 16 | #include 17 | #include 18 | #endif 19 | 20 | void setup() 21 | { 22 | Serial.begin(115200); 23 | Serial.println("-------Peer to Peer HCE--------"); 24 | 25 | nfc.begin(); 26 | 27 | uint32_t versiondata = nfc.getFirmwareVersion(); 28 | if (! versiondata) { 29 | Serial.print("Didn't find PN53x board"); 30 | while (1); // halt 31 | } 32 | 33 | // Got ok data, print it out! 34 | Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 35 | Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 36 | Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC); 37 | 38 | // Set the max number of retry attempts to read from a card 39 | // This prevents us from waiting forever for a card, which is 40 | // the default behaviour of the PN532. 41 | //nfc.setPassiveActivationRetries(0xFF); 42 | 43 | // configure board to read RFID tags 44 | nfc.SAMConfig(); 45 | } 46 | 47 | void loop() 48 | { 49 | bool success; 50 | 51 | uint8_t responseLength = 32; 52 | 53 | Serial.println("Waiting for an ISO14443A card"); 54 | 55 | // set shield to inListPassiveTarget 56 | success = nfc.inListPassiveTarget(); 57 | 58 | if(success) { 59 | 60 | Serial.println("Found something!"); 61 | 62 | uint8_t selectApdu[] = { 0x00, /* CLA */ 63 | 0xA4, /* INS */ 64 | 0x04, /* P1 */ 65 | 0x00, /* P2 */ 66 | 0x07, /* Length of AID */ 67 | 0xF0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* AID defined on Android App */ 68 | 0x00 /* Le */ }; 69 | 70 | uint8_t response[32]; 71 | 72 | success = nfc.inDataExchange(selectApdu, sizeof(selectApdu), response, &responseLength); 73 | 74 | if(success) { 75 | 76 | Serial.print("responseLength: "); Serial.println(responseLength); 77 | 78 | nfc.PrintHexChar(response, responseLength); 79 | 80 | do { 81 | uint8_t apdu[] = "Hello from Arduino"; 82 | uint8_t back[32]; 83 | uint8_t length = 32; 84 | 85 | success = nfc.inDataExchange(apdu, sizeof(apdu), back, &length); 86 | 87 | if(success) { 88 | 89 | Serial.print("responseLength: "); Serial.println(length); 90 | 91 | nfc.PrintHexChar(back, length); 92 | } 93 | else { 94 | 95 | Serial.println("Broken connection?"); 96 | } 97 | } 98 | while(success); 99 | } 100 | else { 101 | 102 | Serial.println("Failed sending SELECT AID"); 103 | } 104 | } 105 | else { 106 | 107 | Serial.println("Didn't find anything!"); 108 | } 109 | 110 | delay(1000); 111 | } 112 | 113 | void printResponse(uint8_t *response, uint8_t responseLength) { 114 | 115 | String respBuffer; 116 | 117 | for (int i = 0; i < responseLength; i++) { 118 | 119 | if (response[i] < 0x10) 120 | respBuffer = respBuffer + "0"; //Adds leading zeros if hex value is smaller than 0x10 121 | 122 | respBuffer = respBuffer + String(response[i], HEX) + " "; 123 | } 124 | 125 | Serial.print("response: "); Serial.println(respBuffer); 126 | } 127 | 128 | void setupNFC() { 129 | 130 | nfc.begin(); 131 | 132 | uint32_t versiondata = nfc.getFirmwareVersion(); 133 | if (! versiondata) { 134 | Serial.print("Didn't find PN53x board"); 135 | while (1); // halt 136 | } 137 | 138 | // Got ok data, print it out! 139 | Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 140 | Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 141 | Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC); 142 | 143 | // configure board to read RFID tags 144 | nfc.SAMConfig(); 145 | } 146 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/examples/emulate_tag_ndef/emulate_tag_ndef.ino: -------------------------------------------------------------------------------- 1 | #include "emulatetag.h" 2 | #include "NdefMessage.h" 3 | 4 | #if 0 5 | #include 6 | #include 7 | #include "PN532.h" 8 | 9 | PN532_SPI pn532spi(SPI, 10); 10 | EmulateTag nfc(pn532spi); 11 | #elif 1 12 | #include 13 | #include 14 | 15 | PN532_HSU pn532hsu(Serial1); 16 | EmulateTag nfc(pn532hsu); 17 | #endif 18 | 19 | 20 | 21 | 22 | 23 | uint8_t ndefBuf[120]; 24 | NdefMessage message; 25 | int messageSize; 26 | 27 | uint8_t uid[3] = { 0x12, 0x34, 0x56 }; 28 | 29 | void setup() 30 | { 31 | Serial.begin(115200); 32 | Serial.println("------- Emulate Tag --------"); 33 | 34 | message = NdefMessage(); 35 | message.addUriRecord("http://www.elechouse.com"); 36 | messageSize = message.getEncodedSize(); 37 | if (messageSize > sizeof(ndefBuf)) { 38 | Serial.println("ndefBuf is too small"); 39 | while (1) { } 40 | } 41 | 42 | Serial.print("Ndef encoded message size: "); 43 | Serial.println(messageSize); 44 | 45 | message.encode(ndefBuf); 46 | 47 | // comment out this command for no ndef message 48 | nfc.setNdefFile(ndefBuf, messageSize); 49 | 50 | // uid must be 3 bytes! 51 | nfc.setUid(uid); 52 | 53 | nfc.init(); 54 | } 55 | 56 | void loop(){ 57 | // uncomment for overriding ndef in case a write to this tag occured 58 | //nfc.setNdefFile(ndefBuf, messageSize); 59 | 60 | // start emulation (blocks) 61 | nfc.emulate(); 62 | 63 | // or start emulation with timeout 64 | /*if(!nfc.emulate(1000)){ // timeout 1 second 65 | Serial.println("timed out"); 66 | }*/ 67 | 68 | // deny writing to the tag 69 | // nfc.setTagWriteable(false); 70 | 71 | if(nfc.writeOccured()){ 72 | Serial.println("\nWrite occured !"); 73 | uint8_t* tag_buf; 74 | uint16_t length; 75 | 76 | nfc.getContent(&tag_buf, &length); 77 | NdefMessage msg = NdefMessage(tag_buf, length); 78 | msg.print(); 79 | } 80 | 81 | delay(1000); 82 | } 83 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/examples/iso14443a_uid/iso14443a_uid.pde: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | This example will attempt to connect to an ISO14443A 4 | card or tag and retrieve some basic information about it 5 | that can be used to determine what type of card it is. 6 | 7 | Note that you need the baud rate to be 115200 because we need to print 8 | out the data and read from the card at the same time! 9 | 10 | To enable debug message, define DEBUG in PN532/PN532_debug.h 11 | 12 | */ 13 | /**************************************************************************/ 14 | 15 | 16 | /* When the number after #if set as 1, it will be switch to SPI Mode*/ 17 | #if 0 18 | #include 19 | #include 20 | #include "PN532.h" 21 | 22 | PN532_SPI pn532spi(SPI, 10); 23 | PN532 nfc(pn532spi); 24 | 25 | /* When the number after #elif set as 1, it will be switch to HSU Mode*/ 26 | #elif 0 27 | #include 28 | #include 29 | 30 | PN532_HSU pn532hsu(Serial1); 31 | PN532 nfc(pn532hsu); 32 | 33 | /* When the number after #if & #elif set as 0, it will be switch to I2C Mode*/ 34 | #else 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | PN532_I2C pn532i2c(Wire); 41 | PN532 nfc(pn532i2c); 42 | #endif 43 | 44 | void setup(void) { 45 | Serial.begin(115200); 46 | Serial.println("Hello!"); 47 | 48 | nfc.begin(); 49 | 50 | uint32_t versiondata = nfc.getFirmwareVersion(); 51 | if (! versiondata) { 52 | Serial.print("Didn't find PN53x board"); 53 | while (1); // halt 54 | } 55 | 56 | // Got ok data, print it out! 57 | Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 58 | Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 59 | Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC); 60 | 61 | // Set the max number of retry attempts to read from a card 62 | // This prevents us from waiting forever for a card, which is 63 | // the default behaviour of the PN532. 64 | nfc.setPassiveActivationRetries(0xFF); 65 | 66 | // configure board to read RFID tags 67 | nfc.SAMConfig(); 68 | 69 | Serial.println("Waiting for an ISO14443A card"); 70 | } 71 | 72 | void loop(void) { 73 | boolean success; 74 | uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID 75 | uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type) 76 | 77 | // Wait for an ISO14443A type cards (Mifare, etc.). When one is found 78 | // 'uid' will be populated with the UID, and uidLength will indicate 79 | // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight) 80 | success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, &uid[0], &uidLength); 81 | 82 | if (success) { 83 | Serial.println("Found a card!"); 84 | Serial.print("UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes"); 85 | Serial.print("UID Value: "); 86 | for (uint8_t i=0; i < uidLength; i++) 87 | { 88 | Serial.print(" 0x");Serial.print(uid[i], HEX); 89 | } 90 | Serial.println(""); 91 | // Wait 1 second before continuing 92 | delay(1000); 93 | } 94 | else 95 | { 96 | // PN532 probably timed out waiting for a card 97 | Serial.println("Timed out waiting for a card"); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/examples/mifareclassic_memdump/mifareclassic_memdump.pde: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | This example attempts to dump the contents of a Mifare Classic 1K card 4 | 5 | Note that you need the baud rate to be 115200 because we need to print 6 | out the data and read from the card at the same time! 7 | 8 | To enable debug message, define DEBUG in PN532/PN532_debug.h 9 | */ 10 | /**************************************************************************/ 11 | 12 | #if 0 13 | #include 14 | #include 15 | #include "PN532.h" 16 | 17 | PN532_SPI pn532spi(SPI, 10); 18 | PN532 nfc(pn532spi); 19 | #elif 1 20 | #include 21 | #include 22 | 23 | PN532_HSU pn532hsu(Serial1); 24 | PN532 nfc(pn532hsu); 25 | #else 26 | #include 27 | #include 28 | #include 29 | #endif 30 | 31 | void setup(void) { 32 | // has to be fast to dump the entire memory contents! 33 | Serial.begin(115200); 34 | Serial.println("Looking for PN532..."); 35 | 36 | nfc.begin(); 37 | 38 | uint32_t versiondata = nfc.getFirmwareVersion(); 39 | if (! versiondata) { 40 | Serial.print("Didn't find PN53x board"); 41 | while (1); // halt 42 | } 43 | // Got ok data, print it out! 44 | Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 45 | Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 46 | Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC); 47 | 48 | // configure board to read RFID tags 49 | nfc.SAMConfig(); 50 | 51 | Serial.println("Waiting for an ISO14443A Card ..."); 52 | } 53 | 54 | 55 | void loop(void) { 56 | uint8_t success; // Flag to check if there was an error with the PN532 57 | uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID 58 | uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type) 59 | uint8_t currentblock; // Counter to keep track of which block we're on 60 | bool authenticated = false; // Flag to indicate if the sector is authenticated 61 | uint8_t data[16]; // Array to store block data during reads 62 | 63 | // Keyb on NDEF and Mifare Classic should be the same 64 | uint8_t keyuniversal[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 65 | 66 | // Wait for an ISO14443A type cards (Mifare, etc.). When one is found 67 | // 'uid' will be populated with the UID, and uidLength will indicate 68 | // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight) 69 | success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength); 70 | 71 | if (success) { 72 | // Display some basic information about the card 73 | Serial.println("Found an ISO14443A card"); 74 | Serial.print(" UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes"); 75 | Serial.print(" UID Value: "); 76 | for (uint8_t i = 0; i < uidLength; i++) { 77 | Serial.print(uid[i], HEX); 78 | Serial.print(' '); 79 | } 80 | Serial.println(""); 81 | 82 | if (uidLength == 4) 83 | { 84 | // We probably have a Mifare Classic card ... 85 | Serial.println("Seems to be a Mifare Classic card (4 byte UID)"); 86 | 87 | // Now we try to go through all 16 sectors (each having 4 blocks) 88 | // authenticating each sector, and then dumping the blocks 89 | for (currentblock = 0; currentblock < 64; currentblock++) 90 | { 91 | // Check if this is a new block so that we can reauthenticate 92 | if (nfc.mifareclassic_IsFirstBlock(currentblock)) authenticated = false; 93 | 94 | // If the sector hasn't been authenticated, do so first 95 | if (!authenticated) 96 | { 97 | // Starting of a new sector ... try to to authenticate 98 | Serial.print("------------------------Sector ");Serial.print(currentblock/4, DEC);Serial.println("-------------------------"); 99 | if (currentblock == 0) 100 | { 101 | // This will be 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF for Mifare Classic (non-NDEF!) 102 | // or 0xA0 0xA1 0xA2 0xA3 0xA4 0xA5 for NDEF formatted cards using key a, 103 | // but keyb should be the same for both (0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) 104 | success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, currentblock, 1, keyuniversal); 105 | } 106 | else 107 | { 108 | // This will be 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF for Mifare Classic (non-NDEF!) 109 | // or 0xD3 0xF7 0xD3 0xF7 0xD3 0xF7 for NDEF formatted cards using key a, 110 | // but keyb should be the same for both (0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) 111 | success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, currentblock, 1, keyuniversal); 112 | } 113 | if (success) 114 | { 115 | authenticated = true; 116 | } 117 | else 118 | { 119 | Serial.println("Authentication error"); 120 | } 121 | } 122 | // If we're still not authenticated just skip the block 123 | if (!authenticated) 124 | { 125 | Serial.print("Block ");Serial.print(currentblock, DEC);Serial.println(" unable to authenticate"); 126 | } 127 | else 128 | { 129 | // Authenticated ... we should be able to read the block now 130 | // Dump the data into the 'data' array 131 | success = nfc.mifareclassic_ReadDataBlock(currentblock, data); 132 | if (success) 133 | { 134 | // Read successful 135 | Serial.print("Block ");Serial.print(currentblock, DEC); 136 | if (currentblock < 10) 137 | { 138 | Serial.print(" "); 139 | } 140 | else 141 | { 142 | Serial.print(" "); 143 | } 144 | // Dump the raw data 145 | nfc.PrintHexChar(data, 16); 146 | } 147 | else 148 | { 149 | // Oops ... something happened 150 | Serial.print("Block ");Serial.print(currentblock, DEC); 151 | Serial.println(" unable to read this block"); 152 | } 153 | } 154 | } 155 | } 156 | else 157 | { 158 | Serial.println("Ooops ... this doesn't seem to be a Mifare Classic card!"); 159 | } 160 | } 161 | // Wait a bit before trying again 162 | Serial.println("\n\nSend a character to run the mem dumper again!"); 163 | Serial.flush(); 164 | while (!Serial.available()); 165 | while (Serial.available()) { 166 | Serial.read(); 167 | } 168 | Serial.flush(); 169 | } -------------------------------------------------------------------------------- /mu3controller/lib/PN532/examples/mifareclassic_updatendef/mifareclassic_updatendef.pde: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | Updates a sector that is already formatted for NDEF (using 4 | mifareclassic_formatndef.pde for example), inserting a new url 5 | 6 | To enable debug message, define DEBUG in PN532/PN532_debug.h 7 | */ 8 | /**************************************************************************/ 9 | 10 | #if 0 11 | #include 12 | #include 13 | #include "PN532.h" 14 | 15 | PN532_SPI pn532spi(SPI, 10); 16 | PN532 nfc(pn532spi); 17 | #elif 1 18 | #include 19 | #include 20 | 21 | PN532_HSU pn532hsu(Serial1); 22 | PN532 nfc(pn532hsu); 23 | #else 24 | #include 25 | #include 26 | #include 27 | #endif 28 | 29 | 30 | /* 31 | We can encode many different kinds of pointers to the card, 32 | from a URL, to an Email address, to a phone number, and many more 33 | check the library header .h file to see the large # of supported 34 | prefixes! 35 | */ 36 | // For a http://www. url: 37 | const char * url = "elechouse.com"; 38 | uint8_t ndefprefix = NDEF_URIPREFIX_HTTP_WWWDOT; 39 | 40 | // for an email address 41 | //const char * url = "mail@example.com"; 42 | //uint8_t ndefprefix = NDEF_URIPREFIX_MAILTO; 43 | 44 | // for a phone number 45 | //const char * url = "+1 212 555 1212"; 46 | //uint8_t ndefprefix = NDEF_URIPREFIX_TEL; 47 | 48 | 49 | void setup(void) { 50 | Serial.begin(115200); 51 | Serial.println("Looking for PN532..."); 52 | 53 | nfc.begin(); 54 | 55 | uint32_t versiondata = nfc.getFirmwareVersion(); 56 | if (! versiondata) { 57 | Serial.print("Didn't find PN53x board"); 58 | while (1); // halt 59 | } 60 | 61 | // Got ok data, print it out! 62 | Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 63 | Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 64 | Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC); 65 | 66 | // configure board to read RFID tags 67 | nfc.SAMConfig(); 68 | } 69 | 70 | void loop(void) { 71 | uint8_t success; // Flag to check if there was an error with the PN532 72 | uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID 73 | uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type) 74 | bool authenticated = false; // Flag to indicate if the sector is authenticated 75 | 76 | // Use the default NDEF keys (these would have have set by mifareclassic_formatndef.pde!) 77 | uint8_t keya[6] = { 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5 }; 78 | uint8_t keyb[6] = { 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7 }; 79 | 80 | Serial.println("Place your NDEF formatted Mifare Classic card on the reader to update the"); 81 | Serial.println("NDEF record and press any key to continue ..."); 82 | // Wait for user input before proceeding 83 | while (!Serial.available()); 84 | // a key was pressed1 85 | while (Serial.available()) Serial.read(); 86 | 87 | // Wait for an ISO14443A type card (Mifare, etc.). When one is found 88 | // 'uid' will be populated with the UID, and uidLength will indicate 89 | // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight) 90 | success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength); 91 | 92 | if (success) 93 | { 94 | // Display some basic information about the card 95 | Serial.println("Found an ISO14443A card"); 96 | Serial.print(" UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes"); 97 | Serial.print(" UID Value: "); 98 | nfc.PrintHex(uid, uidLength); 99 | Serial.println(""); 100 | 101 | // Make sure this is a Mifare Classic card 102 | if (uidLength != 4) 103 | { 104 | Serial.println("Ooops ... this doesn't seem to be a Mifare Classic card!"); 105 | return; 106 | } 107 | 108 | // We probably have a Mifare Classic card ... 109 | Serial.println("Seems to be a Mifare Classic card (4 byte UID)"); 110 | 111 | // Check if this is an NDEF card (using first block of sector 1 from mifareclassic_formatndef.pde) 112 | // Must authenticate on the first key using 0xD3 0xF7 0xD3 0xF7 0xD3 0xF7 113 | success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, 4, 0, keyb); 114 | if (!success) 115 | { 116 | Serial.println("Unable to authenticate block 4 ... is this card NDEF formatted?"); 117 | return; 118 | } 119 | 120 | Serial.println("Authentication succeeded (seems to be an NDEF/NFC Forum tag) ..."); 121 | 122 | // Authenticated seems to have worked 123 | // Try to write an NDEF record to sector 1 124 | // Use 0x01 for the URI Identifier Code to prepend "http://www." 125 | // to the url (and save some space). For information on URI ID Codes 126 | // see http://www.ladyada.net/wiki/private/articlestaging/nfc/ndef 127 | if (strlen(url) > 38) 128 | { 129 | // The length is also checked in the WriteNDEFURI function, but lets 130 | // warn users here just in case they change the value and it's bigger 131 | // than it should be 132 | Serial.println("URI is too long ... must be less than 38 characters!"); 133 | return; 134 | } 135 | 136 | Serial.println("Updating sector 1 with URI as NDEF Message"); 137 | 138 | // URI is within size limits ... write it to the card and report success/failure 139 | success = nfc.mifareclassic_WriteNDEFURI(1, ndefprefix, url); 140 | if (success) 141 | { 142 | Serial.println("NDEF URI Record written to sector 1"); 143 | Serial.println(""); 144 | } 145 | else 146 | { 147 | Serial.println("NDEF Record creation failed! :("); 148 | } 149 | } 150 | 151 | // Wait a bit before trying again 152 | Serial.println("\n\nDone!"); 153 | delay(1000); 154 | Serial.flush(); 155 | while(Serial.available()) Serial.read(); 156 | } 157 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/examples/p2p_raw/p2p_raw.ino: -------------------------------------------------------------------------------- 1 | // snep_test.ino 2 | // send a SNEP message to adnroid and get a message from android 3 | 4 | #include "SPI.h" 5 | #include "PN532_SPI.h" 6 | #include "llcp.h" 7 | #include "snep.h" 8 | 9 | PN532_SPI pn532spi(SPI, 10); 10 | SNEP nfc(pn532spi); 11 | 12 | void setup() 13 | { 14 | Serial.begin(115200); 15 | Serial.println("-------Peer to Peer--------"); 16 | } 17 | 18 | uint8_t message[] = { 19 | 0xD2, 0xA, 0xB, 0x74,0x65, 0x78, 0x74, 0x2F, 0x70, 0x6C, 20 | 0x61, 0x69, 0x6E, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 21 | 0x6F, 0x72, 0x6C, 0x64}; 22 | 23 | uint8_t buf[128]; 24 | 25 | void loop() 26 | { 27 | 28 | nfc.write(message, sizeof(message)); 29 | delay(3000); 30 | 31 | int16_t len = nfc.read(buf, sizeof(buf)); 32 | if (len > 0) { 33 | Serial.println("get a SNEP message:"); 34 | for (uint8_t i = 0; i < len; i++) { 35 | Serial.print(buf[i], HEX); 36 | Serial.print(' '); 37 | } 38 | Serial.print('\n'); 39 | for (uint8_t i = 0; i < len; i++) { 40 | char c = buf[i]; 41 | if (c <= 0x1f || c > 0x7f) { 42 | Serial.print('.'); 43 | } else { 44 | Serial.print(c); 45 | } 46 | } 47 | Serial.print('\n'); 48 | } 49 | delay(3000); 50 | } -------------------------------------------------------------------------------- /mu3controller/lib/PN532/examples/p2p_with_ndef_library/p2p_with_ndef_library.ino: -------------------------------------------------------------------------------- 1 | // send a NDEF message to adnroid or get a NDEF message 2 | // 3 | // note: [NDEF library](https://github.com/Don/NDEF) is needed. 4 | 5 | #include "SPI.h" 6 | #include "PN532_SPI.h" 7 | #include "snep.h" 8 | #include "NdefMessage.h" 9 | 10 | PN532_SPI pn532spi(SPI, 10); 11 | SNEP nfc(pn532spi); 12 | uint8_t ndefBuf[128]; 13 | 14 | void setup() 15 | { 16 | Serial.begin(115200); 17 | Serial.println("-------Peer to Peer--------"); 18 | } 19 | 20 | void loop() 21 | { 22 | #if 1 23 | Serial.println("Send a message to Android"); 24 | NdefMessage message = NdefMessage(); 25 | message.addUriRecord("http://www.seeedstudio.com"); 26 | int messageSize = message.getEncodedSize(); 27 | if (messageSize > sizeof(ndefBuf)) { 28 | Serial.println("ndefBuf is too small"); 29 | while (1) { 30 | } 31 | 32 | } 33 | 34 | message.encode(ndefBuf); 35 | if (0 >= nfc.write(ndefBuf, messageSize)) { 36 | Serial.println("Failed"); 37 | } else { 38 | Serial.println("Success"); 39 | } 40 | 41 | delay(3000); 42 | #else 43 | // it seems there are some issues to use NdefMessage to decode the received data from Android 44 | Serial.println("Get a message from Android"); 45 | int msgSize = nfc.read(ndefBuf, sizeof(ndefBuf)); 46 | if (msgSize > 0) { 47 | NdefMessage msg = NdefMessage(ndefBuf, msgSize); 48 | msg.print(); 49 | Serial.println("\nSuccess"); 50 | } else { 51 | Serial.println("failed"); 52 | } 53 | delay(3000); 54 | #endif 55 | } 56 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/examples/readMifare/readMifare.pde: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | This example will wait for any ISO14443A card or tag, and 4 | depending on the size of the UID will attempt to read from it. 5 | 6 | If the card has a 4-byte UID it is probably a Mifare 7 | Classic card, and the following steps are taken: 8 | 9 | - Authenticate block 4 (the first block of Sector 1) using 10 | the default KEYA of 0XFF 0XFF 0XFF 0XFF 0XFF 0XFF 11 | - If authentication succeeds, we can then read any of the 12 | 4 blocks in that sector (though only block 4 is read here) 13 | 14 | If the card has a 7-byte UID it is probably a Mifare 15 | Ultralight card, and the 4 byte pages can be read directly. 16 | Page 4 is read by default since this is the first 'general- 17 | purpose' page on the tags. 18 | 19 | To enable debug message, define DEBUG in PN532/PN532_debug.h 20 | */ 21 | /**************************************************************************/ 22 | 23 | #if 0 24 | #include 25 | #include 26 | #include "PN532.h" 27 | 28 | PN532_SPI pn532spi(SPI, 10); 29 | PN532 nfc(pn532spi); 30 | #elif 1 31 | #include 32 | #include 33 | 34 | PN532_HSU pn532hsu(Serial1); 35 | PN532 nfc(pn532hsu); 36 | #else 37 | #include 38 | #include 39 | #include 40 | PN532_I2C pn532i2c(Wire); 41 | PN532 nfc(pn532i2c); 42 | #endif 43 | void setup(void) { 44 | Serial.begin(115200); 45 | Serial.println("Hello!"); 46 | 47 | nfc.begin(); 48 | 49 | uint32_t versiondata = nfc.getFirmwareVersion(); 50 | if (! versiondata) { 51 | Serial.print("Didn't find PN53x board"); 52 | while (1); // halt 53 | } 54 | // Got ok data, print it out! 55 | Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 56 | Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 57 | Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC); 58 | 59 | // configure board to read RFID tags 60 | nfc.SAMConfig(); 61 | 62 | Serial.println("Waiting for an ISO14443A Card ..."); 63 | } 64 | 65 | 66 | void loop(void) { 67 | uint8_t success; 68 | uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID 69 | uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type) 70 | 71 | // Wait for an ISO14443A type cards (Mifare, etc.). When one is found 72 | // 'uid' will be populated with the UID, and uidLength will indicate 73 | // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight) 74 | success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength); 75 | 76 | if (success) { 77 | // Display some basic information about the card 78 | Serial.println("Found an ISO14443A card"); 79 | Serial.print(" UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes"); 80 | Serial.print(" UID Value: "); 81 | nfc.PrintHex(uid, uidLength); 82 | Serial.println(""); 83 | 84 | if (uidLength == 4) 85 | { 86 | // We probably have a Mifare Classic card ... 87 | Serial.println("Seems to be a Mifare Classic card (4 byte UID)"); 88 | 89 | // Now we need to try to authenticate it for read/write access 90 | // Try with the factory default KeyA: 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 91 | Serial.println("Trying to authenticate block 4 with default KEYA value"); 92 | uint8_t keya[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 93 | 94 | // Start with block 4 (the first block of sector 1) since sector 0 95 | // contains the manufacturer data and it's probably better just 96 | // to leave it alone unless you know what you're doing 97 | success = nfc.mifareclassic_AuthenticateBlock(uid, uidLength, 4, 0, keya); 98 | 99 | if (success) 100 | { 101 | Serial.println("Sector 1 (Blocks 4..7) has been authenticated"); 102 | uint8_t data[16]; 103 | 104 | // If you want to write something to block 4 to test with, uncomment 105 | // the following line and this text should be read back in a minute 106 | // data = { 'a', 'd', 'a', 'f', 'r', 'u', 'i', 't', '.', 'c', 'o', 'm', 0, 0, 0, 0}; 107 | // success = nfc.mifareclassic_WriteDataBlock (4, data); 108 | 109 | // Try to read the contents of block 4 110 | success = nfc.mifareclassic_ReadDataBlock(4, data); 111 | 112 | if (success) 113 | { 114 | // Data seems to have been read ... spit it out 115 | Serial.println("Reading Block 4:"); 116 | nfc.PrintHexChar(data, 16); 117 | Serial.println(""); 118 | 119 | // Wait a bit before reading the card again 120 | delay(1000); 121 | } 122 | else 123 | { 124 | Serial.println("Ooops ... unable to read the requested block. Try another key?"); 125 | } 126 | } 127 | else 128 | { 129 | Serial.println("Ooops ... authentication failed: Try another key?"); 130 | } 131 | } 132 | 133 | if (uidLength == 7) 134 | { 135 | // We probably have a Mifare Ultralight card ... 136 | Serial.println("Seems to be a Mifare Ultralight tag (7 byte UID)"); 137 | 138 | // Try to read the first general-purpose user page (#4) 139 | Serial.println("Reading page 4"); 140 | uint8_t data[32]; 141 | success = nfc.mifareultralight_ReadPage (4, data); 142 | if (success) 143 | { 144 | // Data seems to have been read ... spit it out 145 | nfc.PrintHexChar(data, 4); 146 | Serial.println(""); 147 | 148 | // Wait a bit before reading the card again 149 | delay(1000); 150 | } 151 | else 152 | { 153 | Serial.println("Ooops ... unable to read the requested page!?"); 154 | } 155 | } 156 | } 157 | } 158 | 159 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/license.txt: -------------------------------------------------------------------------------- 1 | Software License Agreement (BSD License) 2 | 3 | Copyright (c) 2012, Adafruit Industries 4 | Copyright (c) 2013, Seeed Technology Inc. 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 3. Neither the name of the copyright holders nor the 15 | names of its contributors may be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY 19 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/llcp.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __LLCP_H__ 3 | #define __LLCP_H__ 4 | 5 | #include "mac_link.h" 6 | 7 | #define LLCP_DEFAULT_TIMEOUT 20000 8 | #define LLCP_DEFAULT_DSAP 0x04 9 | #define LLCP_DEFAULT_SSAP 0x20 10 | 11 | class LLCP { 12 | public: 13 | LLCP(PN532Interface &interface) : link(interface) { 14 | headerBuf = link.getHeaderBuffer(&headerBufLen); 15 | ns = 0; 16 | nr = 0; 17 | }; 18 | 19 | /** 20 | * @brief Actiave PN532 as a target 21 | * @param timeout max time to wait, 0 means no timeout 22 | * @return > 0 success 23 | * = 0 timeout 24 | * < 0 failed 25 | */ 26 | int8_t activate(uint16_t timeout = 0); 27 | 28 | int8_t waitForConnection(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); 29 | 30 | int8_t waitForDisconnection(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); 31 | 32 | int8_t connect(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); 33 | 34 | int8_t disconnect(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); 35 | 36 | /** 37 | * @brief write a packet, the packet should be less than (255 - 2) bytes 38 | * @param header packet header 39 | * @param hlen length of header 40 | * @param body packet body 41 | * @param blen length of body 42 | * @return true success 43 | * false failed 44 | */ 45 | bool write(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); 46 | 47 | /** 48 | * @brief read a packet, the packet will be less than (255 - 2) bytes 49 | * @param buf the buffer to contain the packet 50 | * @param len lenght of the buffer 51 | * @return >=0 length of the packet 52 | * <0 failed 53 | */ 54 | int16_t read(uint8_t *buf, uint8_t len); 55 | 56 | uint8_t *getHeaderBuffer(uint8_t *len) { 57 | uint8_t *buf = link.getHeaderBuffer(len); 58 | len -= 3; // I PDU header has 3 bytes 59 | return buf; 60 | }; 61 | 62 | private: 63 | MACLink link; 64 | uint8_t mode; 65 | uint8_t ssap; 66 | uint8_t dsap; 67 | uint8_t *headerBuf; 68 | uint8_t headerBufLen; 69 | uint8_t ns; // Number of I PDU Sent 70 | uint8_t nr; // Number of I PDU Received 71 | 72 | static uint8_t SYMM_PDU[2]; 73 | }; 74 | 75 | #endif // __LLCP_H__ 76 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/mac_link.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "mac_link.h" 3 | #include "PN532_debug.h" 4 | 5 | int8_t MACLink::activateAsTarget(uint16_t timeout) 6 | { 7 | pn532.begin(); 8 | pn532.SAMConfig(); 9 | return pn532.tgInitAsTarget(timeout); 10 | } 11 | 12 | bool MACLink::write(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) 13 | { 14 | return pn532.tgSetData(header, hlen, body, blen); 15 | } 16 | 17 | int16_t MACLink::read(uint8_t *buf, uint8_t len) 18 | { 19 | return pn532.tgGetData(buf, len); 20 | } 21 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/mac_link.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef __MAC_LINK_H__ 4 | #define __MAC_LINK_H__ 5 | 6 | #include "PN532.h" 7 | 8 | class MACLink { 9 | public: 10 | MACLink(PN532Interface &interface) : pn532(interface) { 11 | 12 | }; 13 | 14 | /** 15 | * @brief Activate PN532 as a target 16 | * @param timeout max time to wait, 0 means no timeout 17 | * @return > 0 success 18 | * = 0 timeout 19 | * < 0 failed 20 | */ 21 | int8_t activateAsTarget(uint16_t timeout = 0); 22 | 23 | /** 24 | * @brief write a PDU packet, the packet should be less than (255 - 2) bytes 25 | * @param header packet header 26 | * @param hlen length of header 27 | * @param body packet body 28 | * @param blen length of body 29 | * @return true success 30 | * false failed 31 | */ 32 | bool write(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); 33 | 34 | /** 35 | * @brief read a PDU packet, the packet will be less than (255 - 2) bytes 36 | * @param buf the buffer to contain the PDU packet 37 | * @param len lenght of the buffer 38 | * @return >=0 length of the PDU packet 39 | * <0 failed 40 | */ 41 | int16_t read(uint8_t *buf, uint8_t len); 42 | 43 | uint8_t *getHeaderBuffer(uint8_t *len) { 44 | return pn532.getBuffer(len); 45 | }; 46 | 47 | private: 48 | PN532 pn532; 49 | }; 50 | 51 | #endif // __MAC_LINK_H__ 52 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/snep.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "snep.h" 3 | #include "PN532_debug.h" 4 | 5 | int8_t SNEP::write(const uint8_t *buf, uint8_t len, uint16_t timeout) 6 | { 7 | if (0 >= llcp.activate(timeout)) { 8 | DMSG("failed to activate PN532 as a target\n"); 9 | return -1; 10 | } 11 | 12 | if (0 >= llcp.connect(timeout)) { 13 | DMSG("failed to set up a connection\n"); 14 | return -2; 15 | } 16 | 17 | // response a success SNEP message 18 | headerBuf[0] = SNEP_DEFAULT_VERSION; 19 | headerBuf[1] = SNEP_REQUEST_PUT; 20 | headerBuf[2] = 0; 21 | headerBuf[3] = 0; 22 | headerBuf[4] = 0; 23 | headerBuf[5] = len; 24 | if (0 >= llcp.write(headerBuf, 6, buf, len)) { 25 | return -3; 26 | } 27 | 28 | uint8_t rbuf[16]; 29 | if (6 > llcp.read(rbuf, sizeof(rbuf))) { 30 | return -4; 31 | } 32 | 33 | // check SNEP version 34 | if (SNEP_DEFAULT_VERSION != rbuf[0]) { 35 | DMSG("The received SNEP message's major version is different\n"); 36 | // To-do: send Unsupported Version response 37 | return -4; 38 | } 39 | 40 | // expect a put request 41 | if (SNEP_RESPONSE_SUCCESS != rbuf[1]) { 42 | DMSG("Expect a success response\n"); 43 | return -4; 44 | } 45 | 46 | llcp.disconnect(timeout); 47 | 48 | return 1; 49 | } 50 | 51 | int16_t SNEP::read(uint8_t *buf, uint8_t len, uint16_t timeout) 52 | { 53 | if (0 >= llcp.activate(timeout)) { 54 | DMSG("failed to activate PN532 as a target\n"); 55 | return -1; 56 | } 57 | 58 | if (0 >= llcp.waitForConnection(timeout)) { 59 | DMSG("failed to set up a connection\n"); 60 | return -2; 61 | } 62 | 63 | uint16_t status = llcp.read(buf, len); 64 | if (6 > status) { 65 | return -3; 66 | } 67 | 68 | 69 | // check SNEP version 70 | if (SNEP_DEFAULT_VERSION != buf[0]) { 71 | DMSG("The received SNEP message's major version is different\n"); 72 | // To-do: send Unsupported Version response 73 | return -4; 74 | } 75 | 76 | // expect a put request 77 | if (SNEP_REQUEST_PUT != buf[1]) { 78 | DMSG("Expect a put request\n"); 79 | return -4; 80 | } 81 | 82 | // check message's length 83 | uint32_t length = (buf[2] << 24) + (buf[3] << 16) + (buf[4] << 8) + buf[5]; 84 | // length should not be more than 244 (header + body < 255, header = 6 + 3 + 2) 85 | if (length > (status - 6)) { 86 | DMSG("The SNEP message is too large: "); 87 | DMSG_INT(length); 88 | DMSG_INT(status - 6); 89 | DMSG("\n"); 90 | return -4; 91 | } 92 | for (uint8_t i = 0; i < length; i++) { 93 | buf[i] = buf[i + 6]; 94 | } 95 | 96 | // response a success SNEP message 97 | headerBuf[0] = SNEP_DEFAULT_VERSION; 98 | headerBuf[1] = SNEP_RESPONSE_SUCCESS; 99 | headerBuf[2] = 0; 100 | headerBuf[3] = 0; 101 | headerBuf[4] = 0; 102 | headerBuf[5] = 0; 103 | llcp.write(headerBuf, 6); 104 | 105 | return length; 106 | } 107 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532/snep.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef __SNEP_H__ 4 | #define __SNEP_H__ 5 | 6 | #include "llcp.h" 7 | 8 | #define SNEP_DEFAULT_VERSION 0x10 // Major: 1, Minor: 0 9 | 10 | #define SNEP_REQUEST_PUT 0x02 11 | #define SNEP_REQUEST_GET 0x01 12 | 13 | #define SNEP_RESPONSE_SUCCESS 0x81 14 | #define SNEP_RESPONSE_REJECT 0xFF 15 | 16 | class SNEP { 17 | public: 18 | SNEP(PN532Interface &interface) : llcp(interface) { 19 | headerBuf = llcp.getHeaderBuffer(&headerBufLen); 20 | }; 21 | 22 | /** 23 | * @brief write a SNEP packet, the packet should be less than (255 - 2 - 3) bytes 24 | * @param buf the buffer to contain the packet 25 | * @param len lenght of the buffer 26 | * @param timeout max time to wait, 0 means no timeout 27 | * @return >0 success 28 | * =0 timeout 29 | * <0 failed 30 | */ 31 | int8_t write(const uint8_t *buf, uint8_t len, uint16_t timeout = 0); 32 | 33 | /** 34 | * @brief read a SNEP packet, the packet will be less than (255 - 2 - 3) bytes 35 | * @param buf the buffer to contain the packet 36 | * @param len lenght of the buffer 37 | * @param timeout max time to wait, 0 means no timeout 38 | * @return >=0 length of the packet 39 | * <0 failed 40 | */ 41 | int16_t read(uint8_t *buf, uint8_t len, uint16_t timeout = 0); 42 | 43 | private: 44 | LLCP llcp; 45 | uint8_t *headerBuf; 46 | uint8_t headerBufLen; 47 | }; 48 | 49 | #endif // __SNEP_H__ 50 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532_HSU/PN532_HSU.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "PN532_HSU.h" 3 | #include "PN532_debug.h" 4 | 5 | 6 | PN532_HSU::PN532_HSU(HardwareSerial &serial) 7 | { 8 | _serial = &serial; 9 | command = 0; 10 | } 11 | 12 | void PN532_HSU::begin() 13 | { 14 | _serial->begin(115200); 15 | } 16 | 17 | void PN532_HSU::wakeup() 18 | { 19 | _serial->write(0x55); 20 | _serial->write(0x55); 21 | _serial->write(0); 22 | _serial->write(0); 23 | _serial->write(0); 24 | 25 | /** dump serial buffer */ 26 | if(_serial->available()){ 27 | DMSG("Dump serial buffer: "); 28 | } 29 | while(_serial->available()){ 30 | uint8_t ret = _serial->read(); 31 | DMSG_HEX(ret); 32 | } 33 | 34 | } 35 | 36 | int8_t PN532_HSU::writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) 37 | { 38 | 39 | /** dump serial buffer */ 40 | if(_serial->available()){ 41 | DMSG("Dump serial buffer: "); 42 | } 43 | while(_serial->available()){ 44 | uint8_t ret = _serial->read(); 45 | DMSG_HEX(ret); 46 | } 47 | 48 | command = header[0]; 49 | 50 | _serial->write(PN532_PREAMBLE); 51 | _serial->write(PN532_STARTCODE1); 52 | _serial->write(PN532_STARTCODE2); 53 | 54 | uint8_t length = hlen + blen + 1; // length of data field: TFI + DATA 55 | _serial->write(length); 56 | _serial->write(~length + 1); // checksum of length 57 | 58 | _serial->write(PN532_HOSTTOPN532); 59 | uint8_t sum = PN532_HOSTTOPN532; // sum of TFI + DATA 60 | 61 | DMSG("\nWrite: "); 62 | 63 | _serial->write(header, hlen); 64 | for (uint8_t i = 0; i < hlen; i++) { 65 | sum += header[i]; 66 | 67 | DMSG_HEX(header[i]); 68 | } 69 | 70 | _serial->write(body, blen); 71 | for (uint8_t i = 0; i < blen; i++) { 72 | sum += body[i]; 73 | 74 | DMSG_HEX(body[i]); 75 | } 76 | 77 | uint8_t checksum = ~sum + 1; // checksum of TFI + DATA 78 | _serial->write(checksum); 79 | _serial->write(PN532_POSTAMBLE); 80 | 81 | return readAckFrame(); 82 | } 83 | 84 | int16_t PN532_HSU::readResponse(uint8_t buf[], uint8_t len, uint16_t timeout) 85 | { 86 | uint8_t tmp[3]; 87 | 88 | DMSG("\nRead: "); 89 | 90 | /** Frame Preamble and Start Code */ 91 | if(receive(tmp, 3, timeout)<=0){ 92 | return PN532_TIMEOUT; 93 | } 94 | if(0 != tmp[0] || 0!= tmp[1] || 0xFF != tmp[2]){ 95 | DMSG("Preamble error"); 96 | return PN532_INVALID_FRAME; 97 | } 98 | 99 | /** receive length and check */ 100 | uint8_t length[2]; 101 | if(receive(length, 2, timeout) <= 0){ 102 | return PN532_TIMEOUT; 103 | } 104 | if( 0 != (uint8_t)(length[0] + length[1]) ){ 105 | DMSG("Length error"); 106 | return PN532_INVALID_FRAME; 107 | } 108 | length[0] -= 2; 109 | if( length[0] > len){ 110 | return PN532_NO_SPACE; 111 | } 112 | 113 | /** receive command byte */ 114 | uint8_t cmd = command + 1; // response command 115 | if(receive(tmp, 2, timeout) <= 0){ 116 | return PN532_TIMEOUT; 117 | } 118 | if( PN532_PN532TOHOST != tmp[0] || cmd != tmp[1]){ 119 | DMSG("Command error"); 120 | return PN532_INVALID_FRAME; 121 | } 122 | 123 | if(receive(buf, length[0], timeout) != length[0]){ 124 | return PN532_TIMEOUT; 125 | } 126 | uint8_t sum = PN532_PN532TOHOST + cmd; 127 | for(uint8_t i=0; i return value buffer. 165 | len --> length expect to receive. 166 | timeout --> time of reveiving 167 | @retval number of received bytes, 0 means no data received. 168 | */ 169 | int8_t PN532_HSU::receive(uint8_t *buf, int len, uint16_t timeout) 170 | { 171 | int read_bytes = 0; 172 | int ret; 173 | unsigned long start_millis; 174 | 175 | while (read_bytes < len) { 176 | start_millis = millis(); 177 | do { 178 | ret = _serial->read(); 179 | if (ret >= 0) { 180 | break; 181 | } 182 | } while((timeout == 0) || ((millis()- start_millis ) < timeout)); 183 | 184 | if (ret < 0) { 185 | if(read_bytes){ 186 | return read_bytes; 187 | }else{ 188 | return PN532_TIMEOUT; 189 | } 190 | } 191 | buf[read_bytes] = (uint8_t)ret; 192 | DMSG_HEX(ret); 193 | read_bytes++; 194 | } 195 | return read_bytes; 196 | } 197 | -------------------------------------------------------------------------------- /mu3controller/lib/PN532_HSU/PN532_HSU.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __PN532_HSU_H__ 3 | #define __PN532_HSU_H__ 4 | 5 | #include "PN532Interface.h" 6 | #include "Arduino.h" 7 | 8 | #define PN532_HSU_DEBUG 9 | 10 | #define PN532_HSU_READ_TIMEOUT (1000) 11 | 12 | class PN532_HSU : public PN532Interface { 13 | public: 14 | PN532_HSU(HardwareSerial &serial); 15 | 16 | void begin(); 17 | void wakeup(); 18 | virtual int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); 19 | int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout); 20 | 21 | private: 22 | HardwareSerial* _serial; 23 | uint8_t command; 24 | 25 | int8_t readAckFrame(); 26 | 27 | int8_t receive(uint8_t *buf, int len, uint16_t timeout=PN532_HSU_READ_TIMEOUT); 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /mu3controller/lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into executable file. 4 | 5 | The source code of each library should be placed in a an own separate directory 6 | ("lib/your_library_name/[here are source files]"). 7 | 8 | For example, see a structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | and a contents of `src/main.c`: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /mu3controller/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env:leonardo] 12 | platform = atmelavr 13 | board = leonardo 14 | framework = arduino 15 | lib_deps = 16 | fastled/FastLED@^3.4.0 17 | nicohood/HID-Project@^2.8.4 18 | dxinteractive/ResponsiveAnalogRead@^1.2.1 19 | -------------------------------------------------------------------------------- /mu3controller/src/components/card_reader.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __CARD_READER_H__ 2 | #define __CARD_READER_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define SerialDevice SerialUSB 9 | 10 | #define NFC_STATE_NONE 0 11 | #define NFC_STATE_AIME 1 12 | #define NFC_STATE_FELICA 2 13 | #define NFC_STATE_BANA 4 14 | #define NFC_STATE_FAKE_AIME 8 15 | #define NFC_STATE_FAKE_FELICA 16 16 | #define NFC_STATE_OTHER 32 17 | #define NFC_STATE_ERROR 64 18 | #define NFC_STATE_NO_DEVICE 128 19 | 20 | #define NFC_POLL_INTERVAL 2000 21 | 22 | #define AIME_ALLOW 1 23 | #define FELICA_ALLOW 1 24 | #define BANA_ALLOW 1 25 | #define MIFARE_FAKE_AIME 1 26 | #define MIFARE_FAKE_FELICA 0 27 | 28 | typedef union 29 | { 30 | uint8_t luid[10]; 31 | struct 32 | { 33 | uint8_t IDm[8]; 34 | uint8_t PMm[8]; 35 | union 36 | { 37 | uint16_t SystemCode; 38 | uint8_t System_Code[2]; 39 | }; 40 | }; 41 | } Card; 42 | 43 | #define M2F_B 1 44 | 45 | extern bool nfc_enable; 46 | extern uint8_t card_type; 47 | extern Card card; 48 | 49 | extern void nfc_setup(void); 50 | extern void nfc_poll(void); 51 | extern void nfc_end(void); 52 | extern void PrintHex(const uint8_t *data, const uint32_t numBytes); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /mu3controller/src/components/comio.hpp: -------------------------------------------------------------------------------- 1 | #include "stdinclude.hpp" 2 | 3 | #ifdef LED_BOARD 4 | enum io_packet_type_t : uint8_t 5 | { 6 | PACKET_TYPE_REQUEST, 7 | PACKET_TYPE_RESPONSE, 8 | }; 9 | 10 | enum io_ack_status_t : uint8_t 11 | { 12 | ACK_OK = 1, 13 | ACK_SUM_ERROR, 14 | ACK_PARITY_ERROR, 15 | ACK_FARMING_ERROR, 16 | ACK_OVER_RUN_ERROR, 17 | ACK_RECV_BUFFER_OVERFLOW, 18 | ACK_INVALID = 255, 19 | }; 20 | 21 | enum io_report_status_t : uint8_t 22 | { 23 | REPORT_OK = 1, 24 | REPORT_BUSY, 25 | REPORT_UNKNOWN_COMMAND, 26 | REPORT_PARAM_ERROR, 27 | REPORT_INVALID = 255, 28 | }; 29 | 30 | enum io_command_num_t : uint8_t 31 | { 32 | CMD_RESET = 16, 33 | CMD_SET_TIMEOUT, 34 | 35 | CMD_SET_DISABLE = 20, 36 | 37 | CMD_SET_LED_DIRECT = 130, 38 | 39 | CMD_BOARD_INFO = 240, 40 | CMD_BOARD_STATUS, 41 | CMD_FIRM_SUM, 42 | CMD_PROTOCOL_VERSION, 43 | }; 44 | 45 | struct io_reqeust_t 46 | { 47 | io_command_num_t command; 48 | uint8_t data[1]; 49 | // last byte: uint8_t checksum; 50 | }; 51 | 52 | struct io_response_t 53 | { 54 | io_ack_status_t status; 55 | io_command_num_t command; 56 | io_report_status_t report; 57 | uint8_t data[1]; 58 | // last byte: uint8_t checksum; 59 | }; 60 | 61 | union io_packet_t 62 | { 63 | uint8_t buffer[1]; 64 | struct { 65 | uint8_t sync; 66 | uint8_t dstNodeId; 67 | uint8_t srcNodeId; 68 | uint8_t length; 69 | union { 70 | io_reqeust_t request; 71 | io_response_t response; 72 | }; 73 | }; 74 | }; 75 | 76 | inline io_packet_t* io_alloc(io_packet_type_t type, size_t size) 77 | { 78 | size += 5; 79 | 80 | if (type == PACKET_TYPE_RESPONSE) 81 | size += 2; 82 | 83 | return (io_packet_t*)malloc(size); 84 | } 85 | 86 | inline void io_fill_data(io_packet_t* packet, uint8_t dstNodeId, uint8_t srcNodeId) 87 | { 88 | packet->sync = 224; 89 | packet->dstNodeId = dstNodeId; 90 | packet->srcNodeId = srcNodeId; 91 | } 92 | 93 | inline size_t io_get_packet_size(io_packet_t* packet, io_packet_type_t type) 94 | { 95 | return packet->length + 5; 96 | } 97 | 98 | inline void io_apply_checksum(io_packet_t* packet) 99 | { 100 | packet->length += 3; 101 | auto totalLen = io_get_packet_size(packet, PACKET_TYPE_RESPONSE); 102 | 103 | uint8_t checksum = 0; 104 | checksum += packet->dstNodeId; 105 | checksum += packet->srcNodeId; 106 | checksum += packet->length; 107 | checksum += packet->response.status; 108 | checksum += packet->response.command; 109 | checksum += packet->response.report; 110 | 111 | for (auto i = 0; i < packet->length - 3; i++) 112 | checksum += packet->response.data[i]; 113 | 114 | packet->request.data[totalLen - 6] = checksum; 115 | } 116 | 117 | inline size_t io_build_board_info(uint8_t* buffer, size_t bufferSize, const char* boardNo, const char* chipNo, uint8_t revision) 118 | { 119 | size_t boardNoLen = strlen(boardNo); 120 | size_t chipNoLen = strlen(chipNo); 121 | size_t totalLen = strlen(boardNo) + strlen(chipNo) + 3; 122 | 123 | if (bufferSize <= totalLen) 124 | return -1; 125 | 126 | memcpy(buffer, boardNo, boardNoLen); 127 | memcpy(buffer + (boardNoLen + 1), chipNo, chipNoLen); 128 | 129 | buffer[boardNoLen] = 10; 130 | buffer[boardNoLen + chipNoLen + 1] = 255; 131 | buffer[boardNoLen + chipNoLen + 2] = revision; 132 | 133 | return totalLen; 134 | } 135 | 136 | inline size_t io_build_firmsum(uint8_t* buffer, size_t bufferSize, uint16_t sum) 137 | { 138 | const size_t totalLen = 2; 139 | 140 | if (bufferSize <= totalLen) 141 | return -1; 142 | 143 | // big-endian 144 | uint8_t* buf = (uint8_t*)∑ 145 | buffer[0] = buf[1]; 146 | buffer[1] = buf[0]; 147 | 148 | return totalLen; 149 | } 150 | 151 | inline size_t io_build_protocol_version(uint8_t* buffer, size_t bufferSize, uint8_t major, uint8_t minor) 152 | { 153 | const size_t totalLen = 3; 154 | 155 | if (bufferSize <= totalLen) 156 | return -1; 157 | 158 | buffer[0] = 1; 159 | buffer[1] = major; 160 | buffer[2] = minor; 161 | 162 | return totalLen; 163 | } 164 | 165 | inline size_t io_build_timeout(uint8_t* buffer, size_t bufferSize, uint16_t timeout) 166 | { 167 | const size_t totalLen = 2; 168 | 169 | if (bufferSize <= totalLen) 170 | return -1; 171 | 172 | // big-endian 173 | uint8_t* buf = (uint8_t*)&timeout; 174 | buffer[0] = buf[1]; 175 | buffer[1] = buf[0]; 176 | 177 | return totalLen; 178 | } 179 | 180 | inline size_t io_build_board_status(uint8_t* buffer, size_t bufferSize, uint8_t boardFlag, uint8_t uartFlag, uint8_t cmdFlag) 181 | { 182 | const size_t totalLen = 3; 183 | 184 | if (bufferSize <= totalLen) 185 | return -1; 186 | 187 | buffer[0] = boardFlag; 188 | buffer[1] = uartFlag; 189 | buffer[2] = cmdFlag; 190 | 191 | return totalLen; 192 | } 193 | 194 | inline size_t io_build_set_disable(uint8_t* buffer, size_t bufferSize, uint8_t disable) 195 | { 196 | const size_t totalLen = 1; 197 | 198 | if (bufferSize <= totalLen) 199 | return -1; 200 | 201 | buffer[0] = disable; 202 | 203 | return totalLen; 204 | } 205 | #endif -------------------------------------------------------------------------------- /mu3controller/src/components/keyboard.cpp: -------------------------------------------------------------------------------- 1 | #include "stdinclude.hpp" 2 | namespace component 3 | { 4 | namespace keyboard 5 | { 6 | const KeyboardKeycode KEY_MAP[12] = { 7 | // L: A B C SIDE MENU 8 | KeyboardKeycode::KEY_A, 9 | KeyboardKeycode::KEY_S, 10 | KeyboardKeycode::KEY_D, 11 | KeyboardKeycode::KEY_LEFT_SHIFT, 12 | KeyboardKeycode::KEY_ESC, 13 | // R: A B C SIDE MENU 14 | KeyboardKeycode::KEY_L, 15 | KeyboardKeycode::KEY_SEMICOLON, 16 | KeyboardKeycode::KEY_QUOTE, 17 | KeyboardKeycode::KEY_RIGHT_SHIFT, 18 | KeyboardKeycode::KEY_BACKSPACE, 19 | // FN1 FN2 20 | KeyboardKeycode::KEY_RESERVED, 21 | KeyboardKeycode::KEY_E, 22 | }; 23 | 24 | 25 | void start() 26 | { 27 | Keyboard.begin(); 28 | // 所有键盘按键松开/Releasekeyboard 29 | Keyboard.releaseAll(); 30 | } 31 | 32 | void end() 33 | { 34 | Keyboard.releaseAll(); 35 | Keyboard.end(); 36 | } 37 | 38 | void update() 39 | { 40 | for (auto i = 0; i < 12; i++) 41 | { 42 | bool status = manager::key_status[i]; 43 | if (status) 44 | { 45 | Keyboard.press(KEY_MAP[i]); 46 | } 47 | else 48 | { 49 | Keyboard.release(KEY_MAP[i]); 50 | } 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /mu3controller/src/components/keyboard.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace component 4 | { 5 | namespace keyboard 6 | { 7 | void start(); 8 | void update(); 9 | void end(); 10 | } 11 | } -------------------------------------------------------------------------------- /mu3controller/src/components/led_board.hpp: -------------------------------------------------------------------------------- 1 | #include "stdinclude.hpp" 2 | #include "comio.hpp" 3 | 4 | #ifdef LED_BOARD 5 | namespace component 6 | { 7 | namespace led_board 8 | { 9 | void start(); 10 | void init_color(); 11 | void set_color(uint8_t lr, uint8_t lg, uint8_t lb, uint8_t rr, uint8_t rg, uint8_t rb); 12 | void update(); 13 | void end(); 14 | } 15 | } 16 | #endif -------------------------------------------------------------------------------- /mu3controller/src/components/manager.cpp: -------------------------------------------------------------------------------- 1 | #include "stdinclude.hpp" 2 | #include 3 | #include "led_board.hpp" 4 | 5 | namespace component 6 | { 7 | namespace manager 8 | { 9 | 10 | extern const uint8_t PIN_MAP[12] = { 11 | // L: A B C SIDE MENU 12 | 9, 13 | 8, 14 | 7, 15 | 16, 16 | 15, 17 | // R: A B C SIDE MENU 18 | 6, 19 | 5, 20 | 4, 21 | 14, 22 | 10, 23 | // FN1 FN2 24 | 3, 25 | 2, 26 | }; 27 | bool key_status[12] = {0}; 28 | 29 | bool running = false; 30 | bool keyboard_mode = false; 31 | bool switch_enabled = false; 32 | void start() 33 | { 34 | keyboard_mode = EEPROM.read(EEPROM_ADDR_KEYBOARD_MODE); 35 | // setup pin modes for button 36 | for (unsigned char i : manager::PIN_MAP) 37 | { 38 | pinMode(i, INPUT_PULLUP); 39 | } 40 | 41 | if (keyboard_mode) 42 | { 43 | keyboard::start(); 44 | } 45 | else 46 | { 47 | raw_hid::start(); 48 | ongeki_hardware::start(); 49 | nfc_setup(); 50 | #ifdef LED_BOARD 51 | led_board::start(); 52 | #endif 53 | } 54 | running = true; 55 | } 56 | 57 | void update() 58 | { 59 | if (!running) 60 | return; 61 | 62 | // 按钮状态 63 | for (auto i = 0; i < 12; i++) 64 | { 65 | key_status[i] = digitalRead(PIN_MAP[i]) == LOW; 66 | } 67 | 68 | #ifdef HIGH_SIDE 69 | // 侧键 70 | key_status[3] = key_status[3] ^ 1; 71 | key_status[8] = key_status[8] ^ 1; 72 | #endif 73 | 74 | if (key_status[4] && key_status[9]) 75 | { 76 | key_status[10] = true; 77 | } 78 | 79 | // 重置设置 80 | if (key_status[10] && key_status[11]) 81 | { 82 | reset(); 83 | return; 84 | } 85 | 86 | // 切换键盘与ongeki-io模式 87 | if (key_status[10] && key_status[7]) 88 | { 89 | if (switch_enabled) 90 | { 91 | switch_mode(); 92 | switch_enabled = false; 93 | } 94 | return; 95 | } 96 | else if (!switch_enabled) 97 | { 98 | switch_enabled = true; 99 | } 100 | 101 | if (keyboard_mode) 102 | { 103 | keyboard::update(); 104 | } 105 | else 106 | { 107 | raw_hid::update(); 108 | nfc_poll(); 109 | #ifdef LED_BOARD 110 | led_board::update(); 111 | #endif 112 | } 113 | } 114 | 115 | void end() 116 | { 117 | running = false; 118 | if (keyboard_mode) 119 | { 120 | keyboard::end(); 121 | } 122 | else 123 | { 124 | raw_hid::end(); 125 | nfc_end(); 126 | ongeki_hardware::end(); 127 | #ifdef LED_BOARD 128 | led_board::end(); 129 | #endif 130 | } 131 | } 132 | 133 | void switch_mode() 134 | { 135 | if (!switch_enabled) 136 | return; 137 | end(); 138 | EEPROM.put(EEPROM_ADDR_KEYBOARD_MODE, !keyboard_mode); 139 | start(); 140 | } 141 | 142 | void reset() 143 | { 144 | end(); 145 | EEPROM.put(EEPROM_ADDR_KEYBOARD_MODE, 0); 146 | EEPROM.put(EEPROM_ADDR_LEVER_LIMIT_1, 0); 147 | EEPROM.put(EEPROM_ADDR_LEVER_LIMIT_2, 1023); 148 | start(); 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /mu3controller/src/components/manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace component { 4 | namespace manager { 5 | extern const uint8_t PIN_MAP[12]; 6 | extern bool key_status[]; 7 | void start(); 8 | void update(); 9 | void end(); 10 | void switch_mode(); 11 | void reset(); 12 | } 13 | } 14 | 15 | #include "raw_hid.hpp" 16 | #include "ongeki_hardware.hpp" 17 | #include "keyboard.hpp" -------------------------------------------------------------------------------- /mu3controller/src/components/ongeki_hardware.cpp: -------------------------------------------------------------------------------- 1 | #include "stdinclude.hpp" 2 | #include 3 | #include 4 | #include "components/card_reader.hpp" 5 | #include "components/led_board.hpp" 6 | #include 7 | 8 | namespace component 9 | { 10 | namespace ongeki_hardware 11 | { 12 | using namespace std; 13 | const int LEVER = PIN_A0; 14 | const int LED_PIN = PIN_A1; 15 | 16 | ResponsiveAnalogRead analog(LEVER, true, 0.1F); 17 | 18 | uint16_t lever_limit1 = 0; 19 | uint16_t lever_limit2 = 1023; 20 | uint16_t lever_limit_left; 21 | uint16_t lever_limit_right; 22 | 23 | CRGB lightColors[6]; 24 | 25 | void start() 26 | { 27 | // setup led_t 28 | FastLED.addLeds(lightColors, 6); 29 | EEPROM.get(EEPROM_ADDR_LEVER_LIMIT_1, lever_limit1); 30 | EEPROM.get(EEPROM_ADDR_LEVER_LIMIT_2, lever_limit2); 31 | } 32 | 33 | void read_io(raw_hid::output_data_t *data) 34 | { 35 | for (int i = 0; i < 10; i++) 36 | { 37 | analog.update(); 38 | } 39 | uint16_t lever = analog.getValue(); 40 | 41 | // 设定摇杆范围 42 | if (manager::key_status[10] && manager::key_status[5]) 43 | { 44 | lever_limit1 = lever; 45 | EEPROM.put(EEPROM_ADDR_LEVER_LIMIT_1, lever_limit1); 46 | } 47 | else if (manager::key_status[10] && manager::key_status[6]) 48 | { 49 | lever_limit2 = lever; 50 | EEPROM.put(EEPROM_ADDR_LEVER_LIMIT_2, lever_limit2); 51 | } 52 | 53 | // lever_limit1大于lever_limit2 时,摇杆方向取反 54 | if (lever_limit1 < lever_limit2) 55 | { 56 | lever_limit_left = lever_limit1; 57 | lever_limit_right = lever_limit2; 58 | } 59 | else 60 | { 61 | lever = 1023 - lever; 62 | lever_limit_left = 1023 - lever_limit1; 63 | lever_limit_right = 1023 - lever_limit2; 64 | } 65 | 66 | // 禁止数值超出限制 67 | if (lever < lever_limit_left) 68 | { 69 | lever = lever_limit_left; 70 | } 71 | else if (lever > lever_limit_right) 72 | { 73 | lever = lever_limit_right; 74 | } 75 | 76 | // 摇杆数值映射到int16的范围 77 | data->lever = map(lever, lever_limit_left, lever_limit_right, -32768, 32767); 78 | 79 | // 设定opt按钮的状态 80 | uint8_t test_pressed = manager::key_status[10] && manager::key_status[0]; 81 | uint8_t service_pressed = manager::key_status[10] && manager::key_status[1]; 82 | uint8_t coin_pressed = manager::key_status[11]; 83 | data->opt_buttons = (test_pressed << 0) | (service_pressed << 1) | (coin_pressed << 2); 84 | 85 | // 读取按钮状态 86 | for (auto i = 0; i < 10; i++) 87 | { 88 | data->buttons[i] = manager::key_status[i] && !manager::key_status[10]; 89 | } 90 | } 91 | 92 | void set_led(raw_hid::led_t &data) 93 | { 94 | FastLED.setBrightness(data.ledBrightness); 95 | 96 | for (int i = 0; i < 3; i++) 97 | { 98 | memcpy(&lightColors[i], &data.ledColors[i], 3); 99 | memcpy(&lightColors[i + 3], &data.ledColors[i + 5], 3); 100 | } 101 | 102 | FastLED.show(); 103 | } 104 | 105 | void end() 106 | { 107 | FastLED.clear(); 108 | FastLED.show(); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /mu3controller/src/components/ongeki_hardware.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace component { 4 | namespace ongeki_hardware { 5 | void start(); 6 | void read_io(raw_hid::output_data_t *data); 7 | void set_led(raw_hid::led_t &data); 8 | void end(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /mu3controller/src/components/raw_hid.cpp: -------------------------------------------------------------------------------- 1 | #include "stdinclude.hpp" 2 | #include "card_reader.hpp" 3 | #include 4 | 5 | namespace component { 6 | namespace raw_hid { 7 | uint8_t rawBuffer[255]; 8 | uint8_t outBuffer[64]; 9 | uint8_t inBuffer[64]; 10 | 11 | output_data_t *pOutputData = reinterpret_cast(outBuffer); 12 | input_data_t *pInputData = reinterpret_cast(inBuffer); 13 | 14 | void start() { 15 | RawHID.begin(rawBuffer, sizeof(rawBuffer)); 16 | } 17 | 18 | void update() { 19 | ongeki_hardware::read_io(pOutputData); 20 | 21 | pOutputData->card_type = card_type; 22 | pOutputData->card = card; 23 | 24 | RawHID.write(outBuffer, 64); 25 | 26 | if (RawHID.available()) { 27 | RawHID.readBytes(inBuffer, 64); 28 | 29 | switch (pInputData->type) { 30 | case 0: { 31 | ongeki_hardware::set_led(pInputData->led); 32 | break; 33 | } 34 | case 1: { 35 | EEPROM.put(0, pInputData->option.aimiId); 36 | break; 37 | } 38 | } 39 | } 40 | } 41 | 42 | void end() { 43 | RawHID.end(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /mu3controller/src/components/raw_hid.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "card_reader.hpp" 3 | 4 | namespace component { 5 | namespace raw_hid { 6 | 7 | #pragma pack(push, 1) 8 | struct aimi_id_t { 9 | uint8_t buffer[10]; 10 | }; 11 | 12 | struct output_data_t { 13 | union { 14 | char buffer[64]; 15 | struct { 16 | uint8_t buttons[10]; 17 | int16_t lever; 18 | uint8_t opt_buttons; 19 | uint8_t card_type; 20 | Card card; 21 | }; 22 | }; 23 | }; 24 | 25 | typedef uint8_t color_t[3]; 26 | 27 | struct led_t { 28 | uint8_t ledBrightness; 29 | color_t ledColors[10]; 30 | }; 31 | 32 | struct option_t { 33 | aimi_id_t aimiId; 34 | }; 35 | 36 | struct input_data_t { 37 | uint8_t type; 38 | union { 39 | char buffer[61]; 40 | led_t led; 41 | option_t option; 42 | }; 43 | }; 44 | #pragma pack(pop) 45 | 46 | void start(); 47 | void update(); 48 | void end(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /mu3controller/src/components/serial.cpp: -------------------------------------------------------------------------------- 1 | #define configUSE_CORE_AFFINITY 1 2 | 3 | #include "stdinclude.hpp" 4 | 5 | namespace component { 6 | namespace serial { 7 | void init() { 8 | Serial.begin(115200); 9 | } 10 | 11 | void write(uint8_t byte) { 12 | if(byte == 0xE0 || byte == 0xD0) { 13 | Serial.write((uint8_t)0xD0); 14 | Serial.write((uint8_t)(byte - 1)); 15 | } else { 16 | Serial.write((uint8_t)byte); 17 | } 18 | } 19 | 20 | void write_head() { 21 | Serial.write((uint8_t)0xE0); 22 | } 23 | 24 | bool read(uint8_t &out) { 25 | auto byte = (uint8_t) Serial.read(); 26 | 27 | if(byte == 0xD0) { 28 | out = (uint8_t)(Serial.read() + 1); 29 | return true; 30 | } 31 | 32 | out = byte; 33 | return false; 34 | } 35 | 36 | bool available() { 37 | auto avail = Serial.available(); 38 | if(avail == 1) { 39 | uint8_t peek = Serial.peek(); 40 | 41 | if(peek == 0xD0) 42 | return false; 43 | 44 | return true; 45 | } else if(avail > 0) { 46 | return true; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | void flush() { 53 | Serial.flush(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /mu3controller/src/components/serial.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "stdinclude.hpp" 3 | namespace component 4 | { 5 | namespace serial 6 | { 7 | 8 | bool read(uint8_t &out); 9 | void write(uint8_t byte); 10 | void write_head(); 11 | bool available(); 12 | 13 | void flush(); 14 | 15 | void init(); 16 | } 17 | } -------------------------------------------------------------------------------- /mu3controller/src/eeprom_address.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | const int EEPROM_ADDR_KEYBOARD_MODE = 0; 4 | const int EEPROM_ADDR_LEVER_LIMIT_1 = 1; 5 | const int EEPROM_ADDR_LEVER_LIMIT_2 = 3; -------------------------------------------------------------------------------- /mu3controller/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "stdinclude.hpp" 2 | 3 | void setup() 4 | { 5 | component::manager::start(); 6 | } 7 | 8 | void loop() 9 | { 10 | component::manager::update(); 11 | } 12 | -------------------------------------------------------------------------------- /mu3controller/src/stdinclude.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include "components/manager.hpp" 8 | #include "eeprom_address.h" 9 | 10 | #define HIGH_SIDE 11 | #define LED_BOARD -------------------------------------------------------------------------------- /mu3controller/test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PlatformIO Unit Testing and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PlatformIO Unit Testing: 11 | - https://docs.platformio.org/page/plus/unit-testing.html 12 | --------------------------------------------------------------------------------