Repository: SolidSoils/Arduino
Branch: master
Commit: 984f578267df
Files: 514
Total size: 4.1 MB
Directory structure:
gitextract_0siuikil/
├── .gitattributes
├── .gitignore
├── Documentation/
│ └── Documentation.shfbproj
├── LICENSE.md
├── README.md
├── Solid.Arduino/
│ ├── ArduinoSession.cs
│ ├── EnhancedSerialConnection.cs
│ ├── EnhancedSerialPort.cs
│ ├── Firmata/
│ │ ├── AnalogPinMapping.cs
│ │ ├── AnalogState.cs
│ │ ├── AnalogStateTracker.cs
│ │ ├── BoardAnalogMapping.cs
│ │ ├── BoardCapability.cs
│ │ ├── ByteArrayExtensions.cs
│ │ ├── DigitalPortState.cs
│ │ ├── DigitalStateTracker.cs
│ │ ├── FirmataMessage.cs
│ │ ├── FirmataMessageEventArgs.cs
│ │ ├── FirmataMessageEventArgs_.cs
│ │ ├── Firmware.cs
│ │ ├── IFirmataProtocol.cs
│ │ ├── PinCapability.cs
│ │ ├── PinState.cs
│ │ ├── ProtocolVersion.cs
│ │ ├── Servo/
│ │ │ └── IServoProtocol.cs
│ │ ├── StringData.cs
│ │ └── SysExCommand.cs
│ ├── I2c/
│ │ ├── I2cEventArgs.cs
│ │ ├── I2cReply.cs
│ │ ├── I2cReplyTracker.cs
│ │ └── II2cProtocol.cs
│ ├── ISerialConnection.cs
│ ├── IStringProtocol.cs
│ ├── Messages.Designer.cs
│ ├── Messages.resx
│ ├── ObservableEventTracker.cs
│ ├── ReceivedStringTracker.cs
│ ├── SerialBaudRate.cs
│ ├── SerialConnection.cs
│ ├── SerialPortFixer.cs
│ ├── Solid.Arduino.csproj
│ ├── Solid.Arduino.xml
│ ├── StringEventArgs.cs
│ └── StringExtensions.cs
├── Solid.Arduino.IntegrationTest/
│ ├── HC06-Bluetooth.ino
│ ├── SerialConnectionTester.cs
│ ├── SerialReply.ino
│ ├── SocketConnectionTester.cs
│ └── Solid.Arduino.IntegrationTest.csproj
├── Solid.Arduino.Run/
│ ├── Program.cs
│ └── Solid.Arduino.Run.csproj
├── Solid.Arduino.Test/
│ ├── ArduinoSessionTester.cs
│ ├── ByteArrayExtensionsTester.cs
│ ├── DigitalPortStateTester.cs
│ ├── EnhancedSerialConnectionTester.cs
│ ├── ExceptionMonitor.cs
│ ├── I2cProtocolTester.cs
│ ├── IFirmataProtocolTester.cs
│ ├── IServoProtocolTester.cs
│ ├── IStringProtocolTester.cs
│ ├── MockSerialConnection.cs
│ ├── ObservableArduinoSessionTester.cs
│ ├── SerialConnectionTester.cs
│ ├── Solid.Arduino.Test.csproj
│ └── StringExtensionsTester.cs
├── Solid.Arduino.sln
├── _config.yml
└── docs/
├── SearchHelp.aspx
├── SearchHelp.inc.php
├── SearchHelp.php
├── Solid.Arduino.xml
├── SolidSoils4Arduino.chm
├── Web.Config
├── WebKI.xml
├── WebTOC.xml
├── fti/
│ ├── FTI_100.json
│ ├── FTI_101.json
│ ├── FTI_102.json
│ ├── FTI_103.json
│ ├── FTI_104.json
│ ├── FTI_105.json
│ ├── FTI_108.json
│ ├── FTI_109.json
│ ├── FTI_110.json
│ ├── FTI_111.json
│ ├── FTI_112.json
│ ├── FTI_113.json
│ ├── FTI_114.json
│ ├── FTI_115.json
│ ├── FTI_116.json
│ ├── FTI_117.json
│ ├── FTI_118.json
│ ├── FTI_119.json
│ ├── FTI_97.json
│ ├── FTI_98.json
│ ├── FTI_99.json
│ └── FTI_Files.json
├── html/
│ ├── 0010e8c6-eb36-7d91-486f-57a208faeca8.htm
│ ├── 002fd01e-b970-b699-d1ab-402342ab2dec.htm
│ ├── 00b485cd-b70e-e463-f9aa-ba11cee21ff7.htm
│ ├── 00e5bd9e-f9ab-5194-8375-b9fbf1f0161e.htm
│ ├── 012dd994-5a18-29df-21ef-6f6e228030c9.htm
│ ├── 01aaeb2a-fd21-bbd7-2dc2-9e6597c264d4.htm
│ ├── 03dc5f50-ca95-43b1-418c-d8465fb55383.htm
│ ├── 04428aca-6102-c5fd-3222-18c2e545b0e8.htm
│ ├── 0456bfc8-b008-c8a3-77ad-0bfefc1310cb.htm
│ ├── 05b14b65-ca9d-7ff2-68b7-f70d7cfcc100.htm
│ ├── 05b5327f-d814-405f-e2bd-e56a351a861e.htm
│ ├── 07dc3d59-f60a-0e42-9042-3bb4ac8f9137.htm
│ ├── 08af42a6-bb1e-b13e-b90d-ac47e8583dd9.htm
│ ├── 0cace141-f273-7ad7-d632-31e0ff0815fb.htm
│ ├── 0cc5ebbb-9d0b-0d4e-0b8a-59b7c490f7bd.htm
│ ├── 0cd4e1e3-e5af-40f1-dc97-f2f08fb64119.htm
│ ├── 0ce955e9-de04-3300-073d-2932518e0070.htm
│ ├── 0db70497-3543-ccc9-2141-40d1fd170c64.htm
│ ├── 0e0528df-22b4-60f2-8cc6-9c7e3d140d01.htm
│ ├── 0ea9cbae-1535-ba11-3134-4520d69334f0.htm
│ ├── 0eb18a1c-9837-d7d9-6e0d-f201303effe6.htm
│ ├── 0f03b0e7-a914-e46b-c2d6-e0bed40eac37.htm
│ ├── 10dde3c8-66dc-d300-0d82-de410352094d.htm
│ ├── 11348d42-72bd-635f-c392-da90b6c27daa.htm
│ ├── 119049c4-1ba0-964e-94c7-cfc08e55a37a.htm
│ ├── 12415ea1-5b04-0b26-d464-d851efbbce73.htm
│ ├── 1251dc84-c748-62a3-d876-c92c0cab97c6.htm
│ ├── 1685f037-3a5d-6004-7e2c-c72f2c403fed.htm
│ ├── 17a49847-9f5e-41b4-a84a-1bb15979af70.htm
│ ├── 17eed47b-8bf5-ea44-13cd-722e37bb68c9.htm
│ ├── 183523fa-f6c5-0635-d6ba-336781e21c95.htm
│ ├── 1a181c03-5d41-d3a2-e280-049cad8d349a.htm
│ ├── 1c02d293-4876-a918-1b8e-c84637cd0860.htm
│ ├── 1c2dd961-3134-4b2a-ab4d-c8025ab4cfe1.htm
│ ├── 1cd0d237-552e-0958-a3ef-7dec5153ffa3.htm
│ ├── 1e2164e0-907c-a688-2f86-07d7ba8e85ae.htm
│ ├── 1f020e05-4452-f270-4785-162fd272270d.htm
│ ├── 1fa615e5-a5c8-2d2c-fa47-0e38434c27c8.htm
│ ├── 209690c4-2404-13af-d697-d0889345d94e.htm
│ ├── 21bb8dd6-fb5d-9c2d-ffca-f536c5533d6a.htm
│ ├── 2387accb-7f3f-aff7-0482-b5a6bc2fdaee.htm
│ ├── 2396779a-20f5-49b8-2a1b-ad7d188cde76.htm
│ ├── 23a2c9b4-3a0c-9075-c87f-a4fc4d89c978.htm
│ ├── 24763d00-2f5b-09be-8ace-29f24895ed1b.htm
│ ├── 25858b6c-f545-022f-1421-1980e7d2f9ec.htm
│ ├── 26b1753d-4f47-ecdb-98fb-1c150eb3ca88.htm
│ ├── 280d4128-f443-8cf2-6a53-33e6e7eb5e4e.htm
│ ├── 29676853-5f19-d961-917d-d2a3b1005bc2.htm
│ ├── 2bd00801-6ea4-9aeb-869f-6be672f4b934.htm
│ ├── 2c665003-8e12-9549-f318-997e33c1766b.htm
│ ├── 2cf2e931-e0e8-d47d-aaa6-73cc82ef2e55.htm
│ ├── 2e643178-1fcf-6971-d9c1-921734b7c710.htm
│ ├── 2f361478-0a2b-cf8c-c0b5-c347820086fe.htm
│ ├── 30b16045-d369-9612-cb2a-2f396b746a9d.htm
│ ├── 3198d1e7-848f-41e3-6b13-1e23f6eaaadd.htm
│ ├── 32c1b709-03cf-fa67-24d3-12552bab13dd.htm
│ ├── 335a34f6-a012-4732-1fea-87527cebbecd.htm
│ ├── 3370b8dd-d792-b2c8-df67-f542ff674ba9.htm
│ ├── 34435006-8185-e827-453f-bcf4043b59e3.htm
│ ├── 35350d0e-6fa0-0a27-7b7d-57a59840cdea.htm
│ ├── 35d22925-210f-956e-92b6-12c95fedddaa.htm
│ ├── 3695c805-d9b7-b3e6-32be-183a64f8873a.htm
│ ├── 36b30020-3a33-da99-478f-3806990dcf90.htm
│ ├── 36d82773-9fd2-223a-44bd-acbc25407dda.htm
│ ├── 36e366e7-eeb4-cfe5-dc07-3978cb22ce94.htm
│ ├── 3840dfdb-97e9-882b-796e-9ad6e28ea2a1.htm
│ ├── 3a2ac349-bf62-313a-9f20-4c7c3932b60a.htm
│ ├── 3a4802af-ada6-81eb-0d1a-ca7c114653cf.htm
│ ├── 3a711afe-a630-a052-9ad9-87bd4345613b.htm
│ ├── 3bcf8a5b-b062-5af3-2db8-15cf583f2f31.htm
│ ├── 3cfbfb3e-5b55-59d8-2a64-d298cca31bde.htm
│ ├── 3d0a9909-c581-51c4-1343-950e2653b152.htm
│ ├── 3d50005b-be1d-3245-76c1-a4e60bcd5c18.htm
│ ├── 3e9fb5e9-92dc-8797-9059-3f3a27da6673.htm
│ ├── 3eeb99eb-b6d7-85a1-0001-29f3c220b63a.htm
│ ├── 3f24f883-ede8-64a4-fdd9-f73ddadc6c6c.htm
│ ├── 407075b2-2a77-655b-0bb4-fb45ded42876.htm
│ ├── 40d05494-2f3b-10bd-1fdb-485097690ee9.htm
│ ├── 42b47bc8-9e23-e9db-a8db-1972173530f1.htm
│ ├── 43733aea-c265-b3e4-c53b-17a82b3360b0.htm
│ ├── 43e2abd9-863a-2f7e-aabc-f0c145bb4d29.htm
│ ├── 449f192e-e39e-4f35-d4bd-fd8fd14ef6ad.htm
│ ├── 4622e83d-5f7a-6c28-8838-b7bda56a96fd.htm
│ ├── 473706ce-6549-acda-7330-ee8597293463.htm
│ ├── 48a1f3a8-4ebd-f887-9042-461476ec06e1.htm
│ ├── 4a772778-1995-b7b1-fec3-19dda6d127f9.htm
│ ├── 4b09c588-2b2a-300c-db9d-4567209dba1d.htm
│ ├── 4b854918-3dfc-0e52-8333-edc9d7d419fb.htm
│ ├── 4be8895c-9e74-2785-cce2-fc78782aa936.htm
│ ├── 4c9c15c4-eda9-59d4-8a15-8d789489f0aa.htm
│ ├── 4ec15f56-5d16-3f8b-7f74-a535d3698537.htm
│ ├── 4fc6a36d-12cb-113e-4379-c04f55226882.htm
│ ├── 505b8bcb-2c04-8a59-85ee-f86d7a81133c.htm
│ ├── 50af8943-91c9-96fc-307d-423c066c5933.htm
│ ├── 51289ed1-279a-240e-f276-9147c12f6217.htm
│ ├── 524203d1-c389-0f63-fa4a-616459b7b0e2.htm
│ ├── 52906872-565e-78c6-6f2c-cfd646f7e3d4.htm
│ ├── 542fd7a2-0004-6425-e123-87277c6662cc.htm
│ ├── 5560f824-7c26-030f-a351-7a43f48d3616.htm
│ ├── 55aa38a0-1f7a-ec22-10d8-8bae26960810.htm
│ ├── 55c86eb1-5578-8ae0-dd84-5582fa070efe.htm
│ ├── 569db704-90d1-483e-ace7-0ccba57549c5.htm
│ ├── 56d39bd9-c75f-545e-3b10-d8bc999931d1.htm
│ ├── 580f8ee8-6e40-6d26-8290-6b7b75def84f.htm
│ ├── 5a3e57ca-2a41-36d6-d0a8-f413e253efc6.htm
│ ├── 5a68ef5b-372d-e6ea-0c35-79d60143c238.htm
│ ├── 5a9a83eb-057c-1b58-f5ca-4c8149729116.htm
│ ├── 5d246b79-ab99-fb9d-d02f-6af063d1fa6c.htm
│ ├── 5da15ad8-5869-11b4-3347-2390a0333c45.htm
│ ├── 60dd3c0d-a640-05d4-db8d-c2637f7f2b39.htm
│ ├── 60de7d10-c386-9961-e219-777be8815603.htm
│ ├── 6192a41b-b08d-c9b4-3907-2b945d8fcf80.htm
│ ├── 62d13dd3-f1d7-9949-5659-a07a902efa91.htm
│ ├── 63516731-32a5-c7a6-6170-f8a002200d8c.htm
│ ├── 65ff277a-8063-787e-af75-87fa326a97b7.htm
│ ├── 66d7d0c0-44ca-0d27-5596-1957f0c526a4.htm
│ ├── 66ebce33-e458-872b-5952-866144b9ada0.htm
│ ├── 66f3d7c2-96b2-9c5e-65be-7d2071ec8f9b.htm
│ ├── 6871db6c-e61a-84b5-0ca6-4c240f90fde0.htm
│ ├── 68b42756-a237-c7e8-a3f7-267ca18734b8.htm
│ ├── 69e1e3a3-9710-3929-5a04-6ee7a1e68c3a.htm
│ ├── 6a12da48-72f2-3808-70bd-2c02bfde97d7.htm
│ ├── 6a423ec2-dfae-7237-31d4-736018091ea0.htm
│ ├── 6b3d5d48-0b4d-3b8d-e13f-9648aa1e9723.htm
│ ├── 6bc5ef18-fb9b-7465-3576-f2fcc3b30b44.htm
│ ├── 6bdc660a-cd9d-455a-7908-b6d762c1d8af.htm
│ ├── 6c5bac35-ce3a-6988-f1ab-557b5c8c89ef.htm
│ ├── 6cb87d56-cba5-8b38-17aa-047743f53227.htm
│ ├── 6cd07b17-b3e8-55c6-ec51-b4294f445a2d.htm
│ ├── 6d15bb99-b68e-873c-6c87-e374f6ded979.htm
│ ├── 6d552a41-b41f-9fab-d4ce-41d17f28dfc1.htm
│ ├── 6df4dbc0-c70e-59f8-73ff-a15849692f52.htm
│ ├── 6e591dea-0a75-7d0f-6de5-09ca0069e873.htm
│ ├── 6fa948b9-48cd-ec3b-47c3-b25d3b1a85dc.htm
│ ├── 6ff6011b-6cc8-7f93-8265-dbc3a1df4666.htm
│ ├── 700edc62-50ef-619d-5d88-1907d15ce4ab.htm
│ ├── 70512b72-78fb-5a4a-8fbd-8c01ff72fb9e.htm
│ ├── 70740235-9698-52af-7cd1-614f7550f80b.htm
│ ├── 712af620-4f36-ce65-1d71-1af90ef6e16a.htm
│ ├── 72c4ea1b-c375-10ed-d9f6-9f5bba287781.htm
│ ├── 72f7cd86-5a81-b6fb-50cb-a849074a1f87.htm
│ ├── 747853e5-c5f4-7ae4-6956-be594e9df208.htm
│ ├── 7619d119-9fb4-a181-7523-07273f09e83b.htm
│ ├── 76f722a5-1f41-3369-469a-50225dc7f079.htm
│ ├── 7a82d399-3fe1-048e-ecb1-00b55cbfaba0.htm
│ ├── 7b2ad63d-78ca-4a9b-7bf8-2a3619d8ae78.htm
│ ├── 7c606b90-720d-be95-c43d-4a18e8cb2ccc.htm
│ ├── 7cb762fd-74b7-2119-6570-82592544a596.htm
│ ├── 7fbb1bc0-5346-9adb-23ca-afde8a1cda88.htm
│ ├── 8164731e-9412-adab-9432-1fa0a03aa01d.htm
│ ├── 82864edc-a8c0-fb1e-c8d2-f70b564a089f.htm
│ ├── 82a6b8d0-f606-38df-3050-7192e8c47e61.htm
│ ├── 830f17c7-c111-019a-9c23-522994fd13a3.htm
│ ├── 87c96f49-ef19-0f8c-a88d-27cb504c1ffa.htm
│ ├── 8b6e7069-faa8-05a2-21cd-e6bee8fa1578.htm
│ ├── 8c1d05f9-9974-d9ee-4b69-cbd727d46d5f.htm
│ ├── 8cbb0e35-0dba-781e-d204-2a9d4ea3c191.htm
│ ├── 8ce35937-c031-c897-3338-d93fc03adfaa.htm
│ ├── 8dbe1dc8-266a-4668-ee8a-1fd53d220651.htm
│ ├── 8e596088-4f9e-1214-eb48-e8369d8d3277.htm
│ ├── 90348614-3a75-cec2-bea4-b5847353f958.htm
│ ├── 9196cfe1-64f6-4623-a1c0-a941ecd8c7c7.htm
│ ├── 92bd2ded-5ae5-cfc1-8eb7-f68e747845ca.htm
│ ├── 98ce7e9b-a9d6-7c3f-04f1-2da2a06944e6.htm
│ ├── 990ca6d1-4785-c37c-0a9d-5db6af732336.htm
│ ├── 9c1284e9-e336-1648-c7a6-e2d50005ccb3.htm
│ ├── 9d307145-50e5-2464-e284-b13666d1b1eb.htm
│ ├── 9d6a2465-15ed-a5da-5353-5e0073c7e9a1.htm
│ ├── 9e51c12b-d4aa-b3dc-5578-3081a2bb752f.htm
│ ├── 9eccbac6-fe16-acb8-6b8c-643606251dda.htm
│ ├── 9eda75cf-38c6-c8fd-826b-0d151e3dfc8b.htm
│ ├── 9eea4668-107b-8de8-59e3-22aa6c969ea0.htm
│ ├── 9f48a138-2814-e12f-3ec6-bcb2fc457a47.htm
│ ├── 9fdf1328-d5a8-08a6-692e-00088ef146b7.htm
│ ├── GeneralError.htm
│ ├── PageNotFound.htm
│ ├── a04d813f-23c5-6fc3-bc98-55d2c67527cf.htm
│ ├── a14876ac-b585-07c6-75dd-a0b2c5ce64fe.htm
│ ├── a2213ce7-3ec9-981b-062f-5b90e71d9ef0.htm
│ ├── a24a6f37-2514-e037-21c6-52cf6f7da1ce.htm
│ ├── a322471f-ebc5-8cdb-40f4-a9fa07e0e64f.htm
│ ├── a357a146-da10-d1c5-6dbe-293d4072e351.htm
│ ├── a53a9e81-536d-aec6-959d-32e03a3a7888.htm
│ ├── a5aa12b2-f49e-5944-919e-f689fca5e38c.htm
│ ├── a6c005fe-f1fb-c257-4693-5dd2ccf4a508.htm
│ ├── a761b35c-6058-6fce-044e-7204c140e2f9.htm
│ ├── a7c3cac8-d32c-cfa4-b82d-a575dbbff983.htm
│ ├── a814f7b1-656c-0782-6d3f-be715b415a1d.htm
│ ├── a82bbc27-df2a-11fe-7060-d460e274a7fd.htm
│ ├── a8b5fb3d-a1eb-e372-c1a5-228efd902dd6.htm
│ ├── ab95f6d2-30cf-24bf-a427-621540e1cc36.htm
│ ├── acc5321a-11b6-194d-9978-a32b2a7ec2a3.htm
│ ├── ad6eda66-4832-2efc-b3c0-55e7c8ef7ba4.htm
│ ├── ae1ba7d3-433e-fd72-3f50-1a8ec37aa5f4.htm
│ ├── ae47b3db-c68e-3be4-8385-2321b8963662.htm
│ ├── af48914f-fe94-9752-7573-db5edf2b7fd9.htm
│ ├── af66df3c-8f61-d237-0415-a240644ebb01.htm
│ ├── b22cca88-7580-d134-a7be-c45c7d9c8be2.htm
│ ├── b35dd5f1-998f-89d6-dad2-e05be835d949.htm
│ ├── b45c02d5-d804-8090-5057-0b3e3cbc19a0.htm
│ ├── b5ad6c4c-2609-706b-f56b-547ace8e1dee.htm
│ ├── b64f5e5c-6d8a-0177-8932-f59e1971c505.htm
│ ├── b68dd438-ddc6-b2f7-4a92-8396c448faf5.htm
│ ├── b74ea0f2-07f1-a01d-83c7-d8830d90e57d.htm
│ ├── b82a5fcb-a39d-b771-11ed-ae23c755838f.htm
│ ├── b8f2dc6f-68a0-31f3-6a5b-55042ad516ed.htm
│ ├── ba11da02-35ff-5b18-068e-812d1f92f7df.htm
│ ├── bb27603a-6a4d-9e7c-1cc2-a08ee8ef4109.htm
│ ├── bb8ee71c-0808-feda-2e0a-4b05fc986980.htm
│ ├── bcbabe45-8718-bce0-5974-bae720038d00.htm
│ ├── bcee4a6a-47c8-e1cb-621c-2f876839efd6.htm
│ ├── bd23b31c-aa0c-ec47-793e-36bb05f1ac59.htm
│ ├── be39b34a-20e0-0491-49a7-5a456eef0133.htm
│ ├── be43753c-2f1b-03df-9e55-56be16980d27.htm
│ ├── bf222e0f-2117-7a4d-78cb-0a8e02803aa8.htm
│ ├── c0168d33-3fbb-d636-6455-d1e6d811596e.htm
│ ├── c0169464-5e8c-4c99-0d22-2997afa6d642.htm
│ ├── c016e57d-a1aa-1502-b266-eac555320bd8.htm
│ ├── c1885039-892e-e2b5-0fa0-96da02a925c3.htm
│ ├── c1bf57a5-40dc-a8d6-1e15-b48884f5892b.htm
│ ├── c231898c-c0fd-8636-cd01-91e62005ca68.htm
│ ├── c3c646da-8314-4fd5-f46e-5f8fd1d5730b.htm
│ ├── c482d8a0-7998-bb79-5e92-1376e076a5de.htm
│ ├── c4d800a5-a5de-dc32-6725-c24ca5b6b07f.htm
│ ├── c6965049-109b-ccc8-ab08-add00fa5fe2d.htm
│ ├── c7bdc16d-4098-6768-85e3-0b449ad30c1f.htm
│ ├── c7cb67b3-109d-5722-69ed-5ac3b5fdbc0e.htm
│ ├── c7f31e4f-9f61-353e-7a50-70a068be8196.htm
│ ├── c88283e6-8a6d-e7e4-3a38-9c343e31d7cf.htm
│ ├── ca00d61b-80d6-3c26-6474-e9592a8cd67b.htm
│ ├── cab481d4-3500-53ac-365e-cbf3f4131689.htm
│ ├── cac50def-a782-0642-1a3e-e95e2679c778.htm
│ ├── cad99045-36ff-2d40-f881-6d9616efe8f0.htm
│ ├── cc033925-07e6-b137-a60c-6ffb2f4afb99.htm
│ ├── ce05c987-6053-fb21-59f1-0c83c611e513.htm
│ ├── cf70a8e1-e4eb-2183-4c35-df109d3d7818.htm
│ ├── cf75ec9d-e7a8-37cb-f0f2-4bf4f0279cd5.htm
│ ├── cfafcb67-8636-fcae-c56c-aad4f2241248.htm
│ ├── d1d06fd1-3384-00fc-2611-25539b67b954.htm
│ ├── d1f00718-36e3-c0a5-bf63-061a713f4bd2.htm
│ ├── d1faec22-b4a8-57d4-8509-fc1e35b530b0.htm
│ ├── d205b57d-0a11-6b7d-ceab-edfecfd42975.htm
│ ├── d28eb91b-bf62-51fa-e18e-c4a417be7337.htm
│ ├── d298d81b-b8d3-69e1-1d56-b27dd784823e.htm
│ ├── d2d31ad9-8abb-b53a-7417-7720b259e979.htm
│ ├── d47ffb57-0449-9050-402b-fd4b77b8ce85.htm
│ ├── d574222f-0884-4d82-406d-598e7e49b9ca.htm
│ ├── d5b7f09a-bd5a-8bfd-17c4-218040fe6a3e.htm
│ ├── d6753490-3f4c-d5df-cc50-3cd0a2fbb98e.htm
│ ├── d7e6400a-af87-1003-be8a-a9effb1b622a.htm
│ ├── d806a0fd-cda7-50ff-ec75-63821e97ea83.htm
│ ├── d896819b-a4b3-fafe-2a40-ad78fd8303c4.htm
│ ├── d8f33960-4595-c5e9-ce95-7e4193308d98.htm
│ ├── da17a595-dd19-6c94-c319-ba92780ff1b1.htm
│ ├── dafbf808-d45b-fe61-dfe2-6d1930d50f81.htm
│ ├── db417185-54cd-7eef-2cf0-cb2d7474cff2.htm
│ ├── dc621830-82ae-2687-3604-92d7fefcbc76.htm
│ ├── dd9bc9ad-239f-96e9-955d-5d82260ce525.htm
│ ├── dea5e843-06a7-db5d-8213-d6778ec8243d.htm
│ ├── deaf5148-850e-a69d-5c63-5a1a7264006c.htm
│ ├── deb6bbbc-361f-e1fa-1a28-944836557e27.htm
│ ├── df0364a4-478b-4057-055f-90c0cdf91e0c.htm
│ ├── dffc6c6c-da1a-0e05-486a-66c89481c970.htm
│ ├── e0096895-6dd0-6b06-703d-ccd773068601.htm
│ ├── e100ef8b-93b9-5cb8-5f77-ba3c6992a598.htm
│ ├── e10b3718-42ae-bdb7-e8c5-8fda7d054d52.htm
│ ├── e159fcbc-2053-31ec-dcaa-c3583d88b072.htm
│ ├── e1637911-8e22-f5a6-46a8-f22a852f09b0.htm
│ ├── e2c9ceb2-6c34-7bb9-f1d7-041270a1088f.htm
│ ├── e3237dff-8b9e-7ddf-ff84-7c62943af619.htm
│ ├── e32ad933-56c4-059c-fcf5-99ea235b80b6.htm
│ ├── e3624fc2-b4fb-895d-3b74-40390ea3994d.htm
│ ├── e4216de2-ac9b-991f-ff65-6465c0dca53d.htm
│ ├── e5ea2b24-4ddf-bc82-32fe-c787c3e974b8.htm
│ ├── e65a4995-c3ff-c6e1-ee8b-b5d5c2b3f7b2.htm
│ ├── e6bd7820-438b-49a9-5605-ed00a5e9caea.htm
│ ├── e75af7f2-45fb-5ecf-4f9e-41e596e9f453.htm
│ ├── e82cd517-dc93-1d39-8d56-50744a6a3193.htm
│ ├── e8c9a889-19bc-c8cd-34c1-6912de66612c.htm
│ ├── eb1339b7-b6a8-a213-53c8-70fdd94961fc.htm
│ ├── ebf2f037-d888-6733-6882-3faf1f3e6952.htm
│ ├── ec6875fc-d3d0-99bd-0300-cbe303b3ab73.htm
│ ├── ecad3166-5f2c-2fd8-b94c-de688c02211e.htm
│ ├── ecf46ad6-5296-888e-e4ab-149e15fc50e3.htm
│ ├── ed3c9749-6890-8dc7-f108-7da5fb5ebcaa.htm
│ ├── ed8f32fc-eda4-0426-1a15-4eeb15460ec8.htm
│ ├── edd086d3-b73e-a9c8-583a-728b34baf2b4.htm
│ ├── f0277d7f-20e7-7d21-7723-eff6a7d07f49.htm
│ ├── f0983913-5110-b3fc-40ac-31e1d74e3e11.htm
│ ├── f0ad8da5-0e56-8c1e-74a0-29953ad98f99.htm
│ ├── f2343bf9-88ac-78c3-a6fe-965b78aeaafe.htm
│ ├── f322eee0-b2a6-0288-20a1-9379975c4ccd.htm
│ ├── f3a69b92-0354-52fc-b2c3-8f442702ea51.htm
│ ├── f3b51430-f6ad-bc63-f4a8-1afd7b7c2a85.htm
│ ├── f5861e12-17f7-6986-f198-a79101b9a107.htm
│ ├── f677cae4-0b7e-0f2f-a248-5fa2f0281327.htm
│ ├── fa14ff3e-0df7-c3fa-7383-e4e0f123567a.htm
│ ├── fbb61933-2aad-26b0-f4ef-a12c45469c8f.htm
│ ├── fc3e09de-f376-65f7-47b1-60c2511a5acd.htm
│ ├── fcc98eb4-51e1-b53e-5338-964284c91981.htm
│ ├── fccc0a99-97e3-0285-7073-e3b2d3aa43cf.htm
│ ├── fdfbef58-1137-cab4-864a-66bcdc536cab.htm
│ ├── fea98ee5-d17a-e9c6-54f5-db262d481cd4.htm
│ └── fecece2f-667b-dda0-f43c-d33ef4c15171.htm
├── index.html
├── scripts/
│ ├── branding-Website.js
│ └── branding.js
├── search.html
├── styles/
│ ├── branding-Help1.css
│ ├── branding-HelpViewer.css
│ ├── branding-Website.css
│ ├── branding-cs-CZ.css
│ ├── branding-de-DE.css
│ ├── branding-en-US.css
│ ├── branding-es-ES.css
│ ├── branding-fr-FR.css
│ ├── branding-it-IT.css
│ ├── branding-ja-JP.css
│ ├── branding-ko-KR.css
│ ├── branding-pl-PL.css
│ ├── branding-pt-BR.css
│ ├── branding-ru-RU.css
│ ├── branding-tr-TR.css
│ ├── branding-zh-CN.css
│ ├── branding-zh-TW.css
│ └── branding.css
└── toc/
├── 0456bfc8-b008-c8a3-77ad-0bfefc1310cb.xml
├── 05b14b65-ca9d-7ff2-68b7-f70d7cfcc100.xml
├── 0ea9cbae-1535-ba11-3134-4520d69334f0.xml
├── 0f03b0e7-a914-e46b-c2d6-e0bed40eac37.xml
├── 10dde3c8-66dc-d300-0d82-de410352094d.xml
├── 11348d42-72bd-635f-c392-da90b6c27daa.xml
├── 1685f037-3a5d-6004-7e2c-c72f2c403fed.xml
├── 17a49847-9f5e-41b4-a84a-1bb15979af70.xml
├── 1c02d293-4876-a918-1b8e-c84637cd0860.xml
├── 25858b6c-f545-022f-1421-1980e7d2f9ec.xml
├── 280d4128-f443-8cf2-6a53-33e6e7eb5e4e.xml
├── 29676853-5f19-d961-917d-d2a3b1005bc2.xml
├── 2c665003-8e12-9549-f318-997e33c1766b.xml
├── 2cf2e931-e0e8-d47d-aaa6-73cc82ef2e55.xml
├── 2e643178-1fcf-6971-d9c1-921734b7c710.xml
├── 2f361478-0a2b-cf8c-c0b5-c347820086fe.xml
├── 3198d1e7-848f-41e3-6b13-1e23f6eaaadd.xml
├── 32c1b709-03cf-fa67-24d3-12552bab13dd.xml
├── 335a34f6-a012-4732-1fea-87527cebbecd.xml
├── 34435006-8185-e827-453f-bcf4043b59e3.xml
├── 35d22925-210f-956e-92b6-12c95fedddaa.xml
├── 3a2ac349-bf62-313a-9f20-4c7c3932b60a.xml
├── 3a711afe-a630-a052-9ad9-87bd4345613b.xml
├── 3d0a9909-c581-51c4-1343-950e2653b152.xml
├── 3eeb99eb-b6d7-85a1-0001-29f3c220b63a.xml
├── 407075b2-2a77-655b-0bb4-fb45ded42876.xml
├── 40d05494-2f3b-10bd-1fdb-485097690ee9.xml
├── 449f192e-e39e-4f35-d4bd-fd8fd14ef6ad.xml
├── 473706ce-6549-acda-7330-ee8597293463.xml
├── 48a1f3a8-4ebd-f887-9042-461476ec06e1.xml
├── 4b854918-3dfc-0e52-8333-edc9d7d419fb.xml
├── 505b8bcb-2c04-8a59-85ee-f86d7a81133c.xml
├── 55c86eb1-5578-8ae0-dd84-5582fa070efe.xml
├── 60dd3c0d-a640-05d4-db8d-c2637f7f2b39.xml
├── 6871db6c-e61a-84b5-0ca6-4c240f90fde0.xml
├── 68b42756-a237-c7e8-a3f7-267ca18734b8.xml
├── 6b3d5d48-0b4d-3b8d-e13f-9648aa1e9723.xml
├── 6cd07b17-b3e8-55c6-ec51-b4294f445a2d.xml
├── 6df4dbc0-c70e-59f8-73ff-a15849692f52.xml
├── 70740235-9698-52af-7cd1-614f7550f80b.xml
├── 712af620-4f36-ce65-1d71-1af90ef6e16a.xml
├── 7fbb1bc0-5346-9adb-23ca-afde8a1cda88.xml
├── 8164731e-9412-adab-9432-1fa0a03aa01d.xml
├── 82a6b8d0-f606-38df-3050-7192e8c47e61.xml
├── 830f17c7-c111-019a-9c23-522994fd13a3.xml
├── 8e596088-4f9e-1214-eb48-e8369d8d3277.xml
├── 90348614-3a75-cec2-bea4-b5847353f958.xml
├── 990ca6d1-4785-c37c-0a9d-5db6af732336.xml
├── 9d307145-50e5-2464-e284-b13666d1b1eb.xml
├── 9eda75cf-38c6-c8fd-826b-0d151e3dfc8b.xml
├── 9eea4668-107b-8de8-59e3-22aa6c969ea0.xml
├── a2213ce7-3ec9-981b-062f-5b90e71d9ef0.xml
├── a357a146-da10-d1c5-6dbe-293d4072e351.xml
├── a6c005fe-f1fb-c257-4693-5dd2ccf4a508.xml
├── ad6eda66-4832-2efc-b3c0-55e7c8ef7ba4.xml
├── af66df3c-8f61-d237-0415-a240644ebb01.xml
├── b22cca88-7580-d134-a7be-c45c7d9c8be2.xml
├── b45c02d5-d804-8090-5057-0b3e3cbc19a0.xml
├── b5ad6c4c-2609-706b-f56b-547ace8e1dee.xml
├── b82a5fcb-a39d-b771-11ed-ae23c755838f.xml
├── ba11da02-35ff-5b18-068e-812d1f92f7df.xml
├── bb27603a-6a4d-9e7c-1cc2-a08ee8ef4109.xml
├── bcbabe45-8718-bce0-5974-bae720038d00.xml
├── bcee4a6a-47c8-e1cb-621c-2f876839efd6.xml
├── bd23b31c-aa0c-ec47-793e-36bb05f1ac59.xml
├── be43753c-2f1b-03df-9e55-56be16980d27.xml
├── bf222e0f-2117-7a4d-78cb-0a8e02803aa8.xml
├── c0168d33-3fbb-d636-6455-d1e6d811596e.xml
├── c231898c-c0fd-8636-cd01-91e62005ca68.xml
├── c7cb67b3-109d-5722-69ed-5ac3b5fdbc0e.xml
├── c88283e6-8a6d-e7e4-3a38-9c343e31d7cf.xml
├── cac50def-a782-0642-1a3e-e95e2679c778.xml
├── cad99045-36ff-2d40-f881-6d9616efe8f0.xml
├── d205b57d-0a11-6b7d-ceab-edfecfd42975.xml
├── d28eb91b-bf62-51fa-e18e-c4a417be7337.xml
├── d2d31ad9-8abb-b53a-7417-7720b259e979.xml
├── deb6bbbc-361f-e1fa-1a28-944836557e27.xml
├── dffc6c6c-da1a-0e05-486a-66c89481c970.xml
├── e10b3718-42ae-bdb7-e8c5-8fda7d054d52.xml
├── e2c9ceb2-6c34-7bb9-f1d7-041270a1088f.xml
├── e5ea2b24-4ddf-bc82-32fe-c787c3e974b8.xml
├── e65a4995-c3ff-c6e1-ee8b-b5d5c2b3f7b2.xml
├── e82cd517-dc93-1d39-8d56-50744a6a3193.xml
├── ed3c9749-6890-8dc7-f108-7da5fb5ebcaa.xml
├── f0983913-5110-b3fc-40ac-31e1d74e3e11.xml
├── f322eee0-b2a6-0288-20a1-9379975c4ccd.xml
├── f3a69b92-0354-52fc-b2c3-8f442702ea51.xml
├── f3b51430-f6ad-bc63-f4a8-1afd7b7c2a85.xml
├── fa14ff3e-0df7-c3fa-7383-e4e0f123567a.xml
├── fc3e09de-f376-65f7-47b1-60c2511a5acd.xml
└── roottoc.xml
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
x64/
build/
[Bb]in/
[Oo]bj/
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
!packages/*/build/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
*.ncrunch*
.*crunch*.local.xml
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.Publish.xml
# NuGet Packages Directory
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
#packages/
# Windows Azure Build Output
csx
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.[Pp]ublish.xml
*.pfx
*.publishsettings
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
App_Data/*.mdf
App_Data/*.ldf
#LightSwitch generated files
GeneratedArtifacts/
_Pvt_Extensions/
ModelManifest.xml
# =========================
# Windows detritus
# =========================
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Mac desktop service store files
.DS_Store
/.vs
================================================
FILE: Documentation/Documentation.shfbproj
================================================
DebugAnyCPU2.0721253fb-22d4-4db6-8c7e-5f90118f52d52017.9.26.0DocumentationDocumentationDocumentation.NET Framework 4.8..\docs\SolidSoils4Arduinoen-USC#BlankFalseVS2013TrueGuidSolid Soils .NET Library for Arduino Serial Port CommunicationSolid Soils SolutionsSolid Soils Solutions.info%40solidsoils.nl%28C%29 2013 - 2017 Henk van BoeijenAboveNamespacesVisualStudio12-1100100-1SolidSoils4ArduinoSolid Soils SolutionsMsdn100VSAttributes, ExplicitInterfaceImplementations, InheritedMembers, Protected, EditorBrowsableNever, NonBrowsable{@HelpFormatOutputPaths}{@TokenFiles}
This is the root namespace of the SolidSoils4Arduino library.This namespace contains types related to the Firmata protocol.This namespace contains types related to the servo extension of the Firmata protocol.This namespace contains types related to the I2C protocol.This namespace contains types related to the I2C protocol.3FalseOnlyWarningsAndErrorsHtmlHelp1, WebsiteFalseTrueFalseFalseTrueSolidSoils4Arduino is a client library targeting .NET 4.5 and above that provides an easy way to interact with the Arduino. The library implements a few communication protocols, the first of which is the Firmata protocol. It aims to make communication with Arduino boards in MS .NET projects easier through a comprehensive and consistent set of methods and events.
The library supports the following protocols:
1. Serial %28ASCII%29 messaging protocol
2. Firmata protocol
3. I2C protocol %28as it has become part of Firmata%29
The protocols mentioned can be used simultaneously. The library brokers all incoming message types and directs them to the proper requestors %28synchronous as well as asynchronous%29 and events.1001.0.0.0Summary, Parameter, Returns, AutoDocumentCtors, Namespace, TypeParameterFalseSolidSoilsLogoSolid Soils Logo
================================================
FILE: LICENSE.md
================================================
####Copyright (c) 2013 - 2015, Henk van Boeijen
#####All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
# SolidSoils4Arduino
SolidSoils4Arduino is a client library built on .NET Standard 2.1. It offers an easy way to interact with Arduino boards.
The library implements the main communication protocols, the first of which is the Firmata protocol.
It aims to make communication with Arduino boards in MS .NET projects easier
through a comprehensive and consistent set of methods and events.
The library supports the following protocols:
1. Serial (ASCII) messaging
2. Firmata
3. I2C (as it has become part of Firmata)
All protocols can be mixed. The library brokers all incoming message types
and directs them to the appropriate requestors (synchronous as well as asynchronous).
Currently [Standard Firmata 2.6](https://github.com/firmata/protocol/blob/master/protocol.md) is supported.
(Extra capabilities of Standard Firmata Plus and Configurable Firmata are currently not supported by this client library.)
Technology: C#/Microsoft .NET Standard 2.1
Dependencies: none
### Downloads
The library is available as a [NuGet package](https://www.nuget.org/packages/SolidSoils.Arduino.Client/#).
### API Documentation
See [reference documentation](https://solidsoils.github.io/Arduino/index.html).
## Getting started
#### Setup your Arduino with StandardFirmata
1. [Download the Arduino IDE](https://www.arduino.cc/en/main/software) and install it.
2. Connect your Arduino board to your computer using an USB cable.
3. Start the Arduino IDE and navigate to File > Examples > Firmata > StandardFirmata.
4. Upload the sketch.
#### Basic test (C#)
Preparation
- Your Arduino is setup with the StandardFirmata sketch (see above).
- An LED is connected to pin 10 of your Arduino.
Further steps
1. Open Visual Studio and create a new C# console program project.
2. Add NuGet package [SolidSoils.Arduino.Client](https://www.nuget.org/packages/SolidSoils.Arduino.Client/).
3. In Program.cs put the following code:
```csharp
using System;
using Solid.Arduino.Firmata;
namespace Demo
{
class Program
{
static void Main(string[] args)
{
ISerialConnection connection = GetConnection();
if (connection != null)
using (var session = new ArduinoSession(connection))
PerformBasicTest(session);
Console.WriteLine("Press a key");
Console.ReadKey(true);
}
private static ISerialConnection GetConnection()
{
Console.WriteLine("Searching Arduino connection...");
ISerialConnection connection = EnhancedSerialConnection.Find();
if (connection == null)
Console.WriteLine("No connection found. Make shure your Arduino board is attached to a USB port.");
else
Console.WriteLine($"Connected to port {connection.PortName} at {connection.BaudRate} baud.");
return connection;
}
private static void PerformBasicTest(IFirmataProtocol session)
{
var firmware = session.GetFirmware();
Console.WriteLine($"Firmware: {firmware.Name} version {firmware.MajorVersion}.{firmware.MinorVersion}");
var protocolVersion = session.GetProtocolVersion();
Console.WriteLine($"Firmata protocol version {protocolVersion.Major}.{protocolVersion.Minor}");
session.SetDigitalPinMode(10, PinMode.DigitalOutput);
session.SetDigitalPin(10, true);
Console.WriteLine("Command sent: Light on (pin 10)");
Console.WriteLine("Press a key");
Console.ReadKey(true);
session.SetDigitalPin(10, false);
Console.WriteLine("Command sent: Light off");
}
}
}
```
#### Display board capabilities
Preparation
- Your Arduino is setup with the StandardFirmata sketch (see above).
- In this example the Arduino is connected to COM3 at 57600 baud. Modify as needed.
Further steps
1. Open Visual Studio and create a new C# console program project.
2. Add NuGet package [SolidSoils.Arduino.Client](https://www.nuget.org/packages/SolidSoils.Arduino.Client/).
3. In Program.cs put the following code:
```csharp
using System;
using Solid.Arduino.Firmata;
namespace Demo
{
class Program
{
static void Main(string[] args)
{
DisplayPortCapabilities();
Console.WriteLine("Press a key");
Console.ReadKey(true);
}
private static void DisplayPortCapabilities()
{
using (var session = new ArduinoSession(new EnhancedSerialConnection("COM3", SerialBaudRate.Bps_57600)))
{
BoardCapability cap = session.GetBoardCapability();
Console.WriteLine();
Console.WriteLine("Board Capability:");
foreach (var pin in cap.Pins)
{
Console.WriteLine("Pin {0}: Input: {1}, Output: {2}, Analog: {3}, Analog-Res: {4}, PWM: {5}, PWM-Res: {6}, Servo: {7}, Servo-Res: {8}, Serial: {9}, Encoder: {10}, Input-pullup: {11}",
pin.PinNumber,
pin.DigitalInput,
pin.DigitalOutput,
pin.Analog,
pin.AnalogResolution,
pin.Pwm,
pin.PwmResolution,
pin.Servo,
pin.ServoResolution,
pin.Serial,
pin.Encoder,
pin.InputPullup);
}
}
}
}
}
```
## Current status
**v1.0.0**
## Milestones
1. Firmata protocol implemented, unit- and integration-tested.
2. I2C protocol implemented and unit-tested.
3. Serial ASCII protocol implemented and unit-tested.
4. API fully documented.
5. IObservable methods implemented (to be unittested).
6. Mono support added.
7. NuGet package published.
## License
[BSD-2 license](https://github.com/SolidSoils/Arduino/blob/master/LICENSE.md)
## Contributing
If you discover a bug or would like to propose a new feature,
please open a new [issue](https://github.com/solidsoils/arduino/issues?sort=created&state=open).
To contribute, fork this respository and create a new topic branch for the bug,
feature or other existing issue you are addressing. Submit the pull request against the *master* branch.
If you would like to contribute but don't have a specific bugfix or new feature to contribute,
you can take on an existing issue. Add a comment to
the issue to express your intent to begin work and/or to get any additional information about the issue.
Please, test your contributed code thoroughly. In your pull request, describe tests performed to ensure
that no existing code is broken and that any changes maintain backwards compatibility with the existing API.
================================================
FILE: Solid.Arduino/ArduinoSession.cs
================================================
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Solid.Arduino.Firmata;
using Solid.Arduino.Firmata.Servo;
using Solid.Arduino.I2C;
namespace Solid.Arduino
{
///
/// Represents an active layer for serial communication with an Arduino board.
///
///
/// This class supports a few common protocols used for communicating with Arduino boards.
/// The protocols can be used simultaneous and independently of each other.
///
/// Official Arduino website
/// SolidSoils4Arduino project on GitHub
///
///
/// var connection = new SerialConnection("COM3", SerialBaudRate.Bps_57600);
/// var session = new ArduinoSession(connection, timeOut: 250);
/// // Cast to interface done, just for the sake of this demo.
/// IFirmataProtocol firmata = (IFirmataProtocol)session;
///
/// Firmware firm = firmata.GetFirmware();
/// Console.WriteLine("Firmware: {0} {1}.{2}", firm.Name, firm.MajorVersion, firm.MinorVersion);
///
/// ProtocolVersion version = firmata.GetProtocolVersion();
/// Console.WriteLine("Protocol version: {0}.{1}", version.Major, version.Minor);
///
/// BoardCapability caps = firmata.GetBoardCapability();
/// Console.WriteLine("Board Capabilities:");
///
/// foreach (var pincap in caps.PinCapabilities)
/// {
/// Console.WriteLine("Pin {0}: Input: {1}, Output: {2}, Analog: {3}, Analog-Res: {4}, PWM: {5}, PWM-Res: {6}, Servo: {7}, Servo-Res: {8}",
/// pincap.PinNumber,
/// pincap.DigitalInput,
/// pincap.DigitalOutput,
/// pincap.Analog,
/// pincap.AnalogResolution,
/// pincap.Pwm,
/// pincap.PwmResolution,
/// pincap.Servo,
/// pincap.ServoResolution);
/// }
///
/// Console.WriteLine();
/// Console.ReadLine();
///
///
public class ArduinoSession : IFirmataProtocol, IServoProtocol, II2CProtocol, IStringProtocol, IDisposable
{
#region Private types
private delegate void ProcessMessageHandler(byte messageByte);
private enum MessageHeader
{
AnalogState = 0xE0, // 224
DigitalState = 0x90, // 144
SystemExtension = 0xF0,
ProtocolVersion = 0xF9
}
private enum StringReadMode {
ReadLine,
ReadToTerminator,
ReadBlock
}
private class StringRequest
{
private readonly StringReadMode _mode;
private readonly int _blockLength;
private readonly char _terminator;
public static StringRequest CreateReadLineRequest()
{
return new StringRequest(StringReadMode.ReadLine, '\\', 0);
}
public static StringRequest CreateReadRequest(int blockLength)
{
return new StringRequest(StringReadMode.ReadBlock, '\\', blockLength);
}
public static StringRequest CreateReadRequest(char terminator)
{
return new StringRequest(StringReadMode.ReadToTerminator, terminator, 0);
}
private StringRequest(StringReadMode mode, char terminator, int blockLength)
{
_mode = mode;
_blockLength = blockLength;
_terminator = terminator;
}
public char Terminator => _terminator;
public int BlockLength => _blockLength;
public StringReadMode Mode => _mode;
}
#endregion
private const byte AnalogMessage = 0xE0;
private const byte DigitalMessage = 0x90;
private const byte SysExStart = 0xF0;
private const byte SysExEnd = 0xF7;
private const int Buffersize = 2048;
private const int MaxQueuelength = 100;
private readonly ISerialConnection _connection;
private readonly bool _gotOpenConnection;
private readonly LinkedList _receivedMessageList = new LinkedList();
private readonly Queue _receivedStringQueue = new Queue();
private ConcurrentQueue _awaitedMessagesQueue = new ConcurrentQueue();
private ConcurrentQueue _awaitedStringsQueue = new ConcurrentQueue();
private StringRequest _currentStringRequest;
private int _messageTimeout = -1;
private ProcessMessageHandler _processMessage;
private int _messageBufferIndex, _stringBufferIndex;
private readonly byte[] _messageBuffer = new byte[Buffersize];
private readonly char[] _stringBuffer = new char[Buffersize];
///
/// Initializes a new instance of the class.
///
/// The serial port connection
/// connection
public ArduinoSession(ISerialConnection connection)
{
_connection = connection ?? throw new ArgumentNullException(nameof(connection));
_gotOpenConnection = connection.IsOpen;
if (!connection.IsOpen)
connection.Open();
_connection.DataReceived += SerialDataReceived;
}
///
/// Initializes a new instance of the class.
///
/// The serial port connection
/// The response time out in milliseconds
/// connection
/// timeOut
public ArduinoSession(ISerialConnection connection, int timeOut)
: this(connection)
{
if (timeOut < SerialPort.InfiniteTimeout)
throw new ArgumentOutOfRangeException(nameof(timeOut));
_messageTimeout = timeOut;
}
///
/// Gets or sets the number of milliseconds before a time-out occurs when a read operation does not finish.
///
///
/// The default is a value (-1).
///
public int TimeOut
{
get => _messageTimeout;
set
{
if (value < SerialPort.InfiniteTimeout)
throw new ArgumentOutOfRangeException();
_messageTimeout = value;
}
}
///
/// Closes and reopens the underlying connection and clears all buffers and queues.
///
public void Clear()
{
lock (_receivedMessageList)
{
_connection.Close();
_receivedMessageList.Clear();
_processMessage = null;
_awaitedMessagesQueue = new ConcurrentQueue();
_awaitedStringsQueue = new ConcurrentQueue();
_connection.Open();
}
}
#region IStringProtocol
///
public event StringReceivedHandler StringReceived;
///
public IObservable CreateReceivedStringMonitor()
{
return new ReceivedStringTracker(this);
}
///
///
/// The value of this property is mapped to the property of the
/// connection the instance is relying on.
///
public string NewLine
{
get => _connection.NewLine;
set => _connection.NewLine = value;
}
///
public void Write(string value)
{
if (!string.IsNullOrEmpty(value))
_connection.Write(value);
}
///
public void WriteLine(string value)
{
_connection.WriteLine(value);
}
///
public string ReadLine()
{
return GetStringFromQueue(StringRequest.CreateReadLineRequest());
}
///
public async Task ReadLineAsync()
{
return await Task.Run(() => GetStringFromQueue(StringRequest.CreateReadLineRequest()));
}
///
public string Read(int length = 1)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), Messages.ArgumentEx_PositiveValue);
return GetStringFromQueue(StringRequest.CreateReadRequest(length));
}
///
public async Task ReadAsync(int length = 1)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), Messages.ArgumentEx_PositiveValue);
return await Task.Run(() => GetStringFromQueue(StringRequest.CreateReadRequest(length)));
}
///
public string ReadTo(char terminator = char.MinValue)
{
return GetStringFromQueue(StringRequest.CreateReadRequest(terminator));
}
///
public async Task ReadToAsync(char terminator = char.MinValue)
{
return await Task.Run(() => GetStringFromQueue(StringRequest.CreateReadRequest(terminator)));
}
#endregion
#region IFirmataProtocol
///
public event MessageReceivedHandler MessageReceived;
///
public event AnalogStateReceivedHandler AnalogStateReceived;
///
public event DigitalStateReceivedHandler DigitalStateReceived;
///
public IObservable CreateDigitalStateMonitor()
{
return new DigitalStateTracker(this);
}
///
public IObservable CreateDigitalStateMonitor(int port)
{
if (port < 0 || port > 15)
throw new ArgumentOutOfRangeException(nameof(port), Messages.ArgumentEx_PortRange0_15);
return new DigitalStateTracker(this, port);
}
///
public IObservable CreateAnalogStateMonitor()
{
return new AnalogStateTracker(this);
}
///
public IObservable CreateAnalogStateMonitor(int channel)
{
if (channel < 0 || channel > 15)
throw new ArgumentOutOfRangeException(nameof(channel), Messages.ArgumentEx_ChannelRange0_15);
return new AnalogStateTracker(this, channel);
}
///
public void ResetBoard()
{
_connection.Write(new [] { (byte)0xFF }, 0, 1);
}
///
public void SetDigitalPin(int pinNumber, long value)
{
if (pinNumber < 0 || pinNumber > 127)
throw new ArgumentOutOfRangeException(nameof(pinNumber), Messages.ArgumentEx_PinRange0_127);
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value), Messages.ArgumentEx_NoNegativeValue);
byte[] message;
if (pinNumber < 16 && value < 0x4000)
{
// Send value in a conventional Analog Message.
message = new[] {
(byte)(AnalogMessage | pinNumber),
(byte)(value & 0x7F),
(byte)((value >> 7) & 0x7F)
};
_connection.Write(message, 0, 3);
return;
}
// Send long value in an Extended Analog Message.
message = new byte[14];
message[0] = SysExStart;
message[1] = 0x6F;
message[2] = (byte)pinNumber;
int index = 3;
do
{
message[index] = (byte)(value & 0x7F);
value >>= 7;
index++;
} while (value > 0 || index < 5);
message[index] = SysExEnd;
_connection.Write(message, 0, index + 1);
}
///
public void SetDigitalPin(int pinNumber, bool value)
{
if (pinNumber < 0 || pinNumber > 127)
throw new ArgumentOutOfRangeException(nameof(pinNumber), Messages.ArgumentEx_PinRange0_127);
_connection.Write(new[] { (byte)0xF5, (byte)pinNumber, (byte)(value ? 1 : 0) }, 0, 3);
}
///
public void SetAnalogReportMode(int channel, bool enable)
{
if (channel < 0 || channel > 15)
throw new ArgumentOutOfRangeException(nameof(channel), Messages.ArgumentEx_ChannelRange0_15);
_connection.Write(new[] { (byte)(0xC0 | channel), (byte)(enable ? 1 : 0) }, 0, 2);
}
///
public void SetDigitalPort(int portNumber, int pins)
{
if (portNumber < 0 || portNumber > 15)
throw new ArgumentOutOfRangeException(nameof(portNumber), Messages.ArgumentEx_PortRange0_15);
if (pins < 0 || pins > 0xFF)
throw new ArgumentOutOfRangeException(nameof(pins), Messages.ArgumentEx_ValueRange0_255);
_connection.Write(new[] { (byte)(DigitalMessage | portNumber), (byte)(pins & 0x7F), (byte)((pins >> 7) & 0x03) }, 0, 3);
}
///
public void SetDigitalReportMode(int portNumber, bool enable)
{
if (portNumber < 0 || portNumber > 15)
throw new ArgumentOutOfRangeException(nameof(portNumber), Messages.ArgumentEx_PortRange0_15);
_connection.Write(new[] { (byte)(0xD0 | portNumber), (byte)(enable ? 1 : 0) }, 0, 2);
}
///
public void SetDigitalPinMode(int pinNumber, PinMode mode)
{
if (pinNumber < 0 || pinNumber > 127)
throw new ArgumentOutOfRangeException(nameof(pinNumber), Messages.ArgumentEx_PinRange0_127);
_connection.Write(new byte[] { 0xF4, (byte)pinNumber, (byte)mode }, 0, 3);
}
///
public void SetSamplingInterval(int milliseconds)
{
if (milliseconds < 0 || milliseconds > 0x3FFF)
throw new ArgumentOutOfRangeException(nameof(milliseconds), Messages.ArgumentEx_SamplingInterval);
var command = new[]
{
SysExStart,
(byte)0x7A,
(byte)(milliseconds & 0x7F),
(byte)((milliseconds >> 7) & 0x7F),
SysExEnd
};
_connection.Write(command, 0, 5);
}
///
public void SendStringData(string data)
{
if (data == null)
data = string.Empty;
byte[] command = new byte[data.Length * 2 + 3];
command[0] = SysExStart;
command[1] = 0x71;
for (int x = 0; x < data.Length; x++)
{
short c = Convert.ToInt16(data[x]);
command[x * 2 + 2] = (byte)(c & 0x7F);
command[x * 2 + 3] = (byte)((c >> 7) & 0x7F);
}
command[command.Length - 1] = SysExEnd;
_connection.Write(command, 0, command.Length);
}
///
public void RequestFirmware()
{
SendSysExCommand(0x79);
}
///
public Firmware GetFirmware()
{
RequestFirmware();
return (Firmware)((FirmataMessage)GetMessageFromQueue(new FirmataMessage(MessageType.FirmwareResponse))).Value;
}
///
public async Task GetFirmwareAsync()
{
RequestFirmware();
return await Task.Run(() =>
(Firmware)((FirmataMessage)GetMessageFromQueue(new FirmataMessage(MessageType.FirmwareResponse))).Value);
}
///
public void RequestProtocolVersion()
{
_connection.Write(new byte[] { 0xF9 }, 0, 1);
}
///
public ProtocolVersion GetProtocolVersion()
{
RequestProtocolVersion();
return (ProtocolVersion)((FirmataMessage)GetMessageFromQueue(new FirmataMessage(MessageType.ProtocolVersion))).Value;
}
///
public async Task GetProtocolVersionAsync()
{
RequestProtocolVersion();
return await Task.Run(() =>
(ProtocolVersion)((FirmataMessage)GetMessageFromQueue(new FirmataMessage(MessageType.ProtocolVersion))).Value);
}
///
public void RequestBoardCapability()
{
SendSysExCommand(0x6B);
}
///
public BoardCapability GetBoardCapability()
{
RequestBoardCapability();
return (BoardCapability)((FirmataMessage) GetMessageFromQueue(new FirmataMessage(MessageType.CapabilityResponse))).Value;
}
///
public async Task GetBoardCapabilityAsync()
{
RequestBoardCapability();
return await Task.Run(() =>
(BoardCapability)((FirmataMessage)GetMessageFromQueue(new FirmataMessage(MessageType.CapabilityResponse))).Value);
}
///
public void RequestBoardAnalogMapping()
{
SendSysExCommand(0x69);
}
///
public BoardAnalogMapping GetBoardAnalogMapping()
{
RequestBoardAnalogMapping();
return (BoardAnalogMapping)((FirmataMessage)GetMessageFromQueue(new FirmataMessage(MessageType.AnalogMappingResponse))).Value;
}
///
public async Task GetBoardAnalogMappingAsync()
{
RequestBoardAnalogMapping();
return await Task.Run(() =>
(BoardAnalogMapping)((FirmataMessage)GetMessageFromQueue(new FirmataMessage(MessageType.AnalogMappingResponse))).Value);
}
///
public void RequestPinState(int pinNumber)
{
if (pinNumber < 0 || pinNumber > 127)
throw new ArgumentOutOfRangeException(nameof(pinNumber), Messages.ArgumentEx_PinRange0_127);
var command = new[]
{
SysExStart,
(byte)0x6D,
(byte)pinNumber,
SysExEnd
};
_connection.Write(command, 0, 4);
}
///
public PinState GetPinState(int pinNumber)
{
RequestPinState(pinNumber);
return (PinState)((FirmataMessage)GetMessageFromQueue(new FirmataMessage(MessageType.PinStateResponse))).Value;
}
///
public async Task GetPinStateAsync(int pinNumber)
{
RequestPinState(pinNumber);
return await Task.Run
(
() =>
(PinState)((FirmataMessage)GetMessageFromQueue(new FirmataMessage(MessageType.PinStateResponse))).Value
);
}
#endregion
#region IServoProtocol
///
public void ConfigureServo(int pinNumber, int minPulse, int maxPulse)
{
if (pinNumber < 0 || pinNumber > 127)
throw new ArgumentOutOfRangeException(nameof(pinNumber), Messages.ArgumentEx_PinRange0_127);
if (minPulse < 0 || minPulse > 0x3FFF)
throw new ArgumentOutOfRangeException(nameof(minPulse), Messages.ArgumentEx_MinPulseWidth);
if (maxPulse < 0 || maxPulse > 0x3FFF)
throw new ArgumentOutOfRangeException(nameof(maxPulse), Messages.ArgumentEx_MaxPulseWidth);
if (minPulse > maxPulse)
throw new ArgumentException(Messages.ArgumentEx_MinMaxPulse);
var command = new[]
{
SysExStart,
(byte)0x70,
(byte)pinNumber,
(byte)(minPulse & 0x7F),
(byte)((minPulse >> 7) & 0x7F),
(byte)(maxPulse & 0x7F),
(byte)((maxPulse >> 7) & 0x7F),
SysExEnd
};
_connection.Write(command, 0, 8);
}
#endregion
#region II2cProtocol
///
public event I2CReplyReceivedHandler I2CReplyReceived;
///
public IObservable CreateI2CReplyMonitor()
{
return new I2CReplyTracker(this);
}
///
public void SetI2CReadInterval(int microseconds)
{
if (microseconds < 0 || microseconds > 0x3FFF)
throw new ArgumentOutOfRangeException(nameof(microseconds), Messages.ArgumentEx_I2cInterval);
var command = new []
{
SysExStart,
(byte)0x78,
(byte)(microseconds & 0x7F),
(byte)((microseconds >> 7) & 0x7F),
SysExEnd
};
_connection.Write(command, 0, 5);
}
///
public void WriteI2C(int slaveAddress, params byte[] data)
{
if (slaveAddress < 0 || slaveAddress > 0x3FF)
throw new ArgumentOutOfRangeException(nameof(slaveAddress), Messages.ArgumentEx_I2cAddressRange);
byte[] command = new byte[data.Length * 2 + 5];
command[0] = SysExStart;
command[1] = 0x76;
command[2] = (byte)(slaveAddress & 0x7F);
command[3] = (byte)(slaveAddress < 0x80 ? 0 : ((slaveAddress >> 7) & 0x07) | 0x20);
for (int x = 0; x < data.Length; x++)
{
command[x * 2 + 4] = (byte)(data[x] & 0x7F);
command[x * 2 + 5] = (byte)((data[x] >> 7) & 0x7F);
}
command[command.Length - 1] = SysExEnd;
_connection.Write(command, 0, command.Length);
}
///
public void ReadI2COnce(int slaveAddress, int bytesToRead)
{
I2CRead(false, slaveAddress, -1, bytesToRead);
}
///
public I2CReply GetI2CReply(int slaveAddress, int bytesToRead)
{
ReadI2COnce(slaveAddress, bytesToRead);
_awaitedMessagesQueue.Enqueue(new FirmataMessage(MessageType.I2CReply));
return (I2CReply)((FirmataMessage)GetMessageFromQueue(new FirmataMessage(MessageType.I2CReply))).Value;
}
///
public async Task GetI2CReplyAsync(int slaveAddress, int bytesToRead)
{
ReadI2COnce(slaveAddress, bytesToRead);
_awaitedMessagesQueue.Enqueue(new FirmataMessage(MessageType.I2CReply));
return await Task.Run(() =>
(I2CReply)((FirmataMessage)GetMessageFromQueue(new FirmataMessage(MessageType.I2CReply))).Value);
}
///
public void ReadI2COnce(int slaveAddress, int slaveRegister, int bytesToRead)
{
I2CSlaveRead(false, slaveAddress, slaveRegister, bytesToRead);
}
///
public I2CReply GetI2CReply(int slaveAddress, int slaveRegister, int bytesToRead)
{
ReadI2COnce(slaveAddress, slaveRegister, bytesToRead);
_awaitedMessagesQueue.Enqueue(new FirmataMessage(MessageType.I2CReply));
return (I2CReply)((FirmataMessage)GetMessageFromQueue(new FirmataMessage(MessageType.I2CReply))).Value;
}
///
public async Task GetI2CReplyAsync(int slaveAddress, int slaveRegister, int bytesToRead)
{
ReadI2COnce(slaveAddress, slaveRegister, bytesToRead);
_awaitedMessagesQueue.Enqueue(new FirmataMessage(MessageType.I2CReply));
return await Task.Run(() =>
(I2CReply)((FirmataMessage)GetMessageFromQueue(new FirmataMessage(MessageType.I2CReply))).Value);
}
///
public void ReadI2CContinuous(int slaveAddress, int bytesToRead)
{
I2CRead(true, slaveAddress, -1, bytesToRead);
}
///
public void ReadI2CContinuous(int slaveAddress, int slaveRegister, int bytesToRead)
{
I2CSlaveRead(true, slaveAddress, slaveRegister, bytesToRead);
}
///
///
///
/// Please note:
/// The Firmata specification states that the I2C_READ_STOP message
/// should only stop the specified query. However, the current Firmata.h implementation
/// stops all registered queries.
///
///
public void StopI2CReading()
{
byte[] command = new byte[5];
command[0] = SysExStart;
command[1] = 0x76;
command[2] = 0x00;
command[3] = 0x18;
command[4] = SysExEnd;
_connection.Write(command, 0, command.Length);
}
#endregion
#region IDisposable
///
/// Closes the underlying connection.
///
public void Dispose()
{
if (!_gotOpenConnection)
_connection.Close();
GC.SuppressFinalize(this);
}
#endregion
#region Private Methods
private void WriteMessageByte(byte dataByte)
{
if (_messageBufferIndex == Buffersize)
throw new OverflowException(Messages.OverflowEx_CmdBufferFull);
_messageBuffer[_messageBufferIndex] = dataByte;
_messageBufferIndex++;
}
private string GetStringFromQueue(StringRequest request)
{
_awaitedStringsQueue.Enqueue(request);
bool lockTaken = false;
try
{
Monitor.TryEnter(_receivedStringQueue, _messageTimeout, ref lockTaken);
while (lockTaken)
{
if (_receivedStringQueue.Count > 0)
{
string message = _receivedStringQueue.Dequeue();
Monitor.PulseAll(_receivedStringQueue);
return message;
}
lockTaken = Monitor.Wait(_receivedStringQueue, _messageTimeout);
}
throw new TimeoutException(string.Format(Messages.TimeoutEx_WaitStringRequest, request.Mode));
}
finally
{
if (lockTaken)
{
Monitor.Exit(_receivedStringQueue);
}
}
}
private object GetMessageFromQueue(FirmataMessage awaitedMessage)
{
_awaitedMessagesQueue.Enqueue(awaitedMessage);
bool lockTaken = false;
try
{
Monitor.TryEnter(_receivedMessageList, _messageTimeout, ref lockTaken);
while (lockTaken)
{
if (_receivedMessageList.Count > 0)
{
FirmataMessage message = _receivedMessageList.FirstOrDefault(m => m.Type == awaitedMessage.Type);
if (message != null)
{
_receivedMessageList.Remove(message);
Monitor.PulseAll(_receivedMessageList);
return message;
}
}
lockTaken = Monitor.Wait(_receivedMessageList, _messageTimeout);
}
throw new TimeoutException(string.Format(Messages.TimeoutEx_WaitMessage, awaitedMessage.Type));
}
finally
{
if (lockTaken)
{
Monitor.Exit(_receivedMessageList);
}
}
}
private void SendSysExCommand(byte command)
{
var message = new[]
{
SysExStart,
command,
SysExEnd
};
_connection.Write(message, 0, 3);
}
private void I2CSlaveRead(bool continuous, int slaveAddress, int slaveRegister = -1, int bytesToRead = 0)
{
if (slaveRegister < 0 || slaveRegister > 0x3FFF)
throw new ArgumentOutOfRangeException(nameof(slaveRegister), Messages.ArgumentEx_ValueRange0_16383);
I2CRead(continuous, slaveAddress, slaveRegister, bytesToRead);
}
private void I2CRead(bool continuous, int slaveAddress, int slaveRegister = -1, int bytesToRead = 0)
{
if (slaveAddress < 0 || slaveAddress > 0x3FF)
throw new ArgumentOutOfRangeException(nameof(slaveAddress), Messages.ArgumentEx_I2cAddressRange);
if (bytesToRead < 0 || bytesToRead > 0x3FFF)
throw new ArgumentOutOfRangeException(nameof(bytesToRead), Messages.ArgumentEx_ValueRange0_16383);
byte[] command = new byte[(slaveRegister == -1 ? 7 : 9)];
command[0] = SysExStart;
command[1] = 0x76;
command[2] = (byte)(slaveAddress & 0x7F);
command[3] = (byte)(((slaveAddress >> 7) & 0x07) | (slaveAddress < 128 ? (continuous ? 0x10 : 0x08) : (continuous ? 0x30 : 0x28)));
if (slaveRegister != -1)
{
command[4] = (byte)(slaveRegister & 0x7F);
command[5] = (byte)(slaveRegister >> 7);
command[6] = (byte)(bytesToRead & 0x7F);
command[7] = (byte)(bytesToRead >> 7);
}
else
{
command[4] = (byte)(bytesToRead & 0x7F);
command[5] = (byte)(bytesToRead >> 7);
}
command[command.Length - 1] = SysExEnd;
_connection.Write(command, 0, command.Length);
}
///
/// Event handler processing data bytes received on the serial port.
///
private void SerialDataReceived(object sender, SerialDataReceivedEventArgs e)
{
while (_connection.IsOpen && _connection.BytesToRead > 0)
{
var serialByte = (byte)_connection.ReadByte();
#if TRACE
if (_messageBufferIndex > 0 && _messageBufferIndex % 8 == 0)
Debug.WriteLine(string.Empty);
Debug.Write(string.Format("{0:x2} ", serialByte));
#endif
if (_processMessage != null)
{
_processMessage(serialByte);
}
else
{
if ((serialByte & 0x80) != 0)
{
// Process Firmata command byte.
ProcessCommand(serialByte);
}
else
{
// Process ASCII character.
ProcessAsciiString(serialByte);
}
}
}
}
private void ProcessAsciiString(int serialByte)
{
if (_stringBufferIndex == Buffersize)
throw new OverflowException(Messages.OverflowEx_StringBufferFull);
char c = Convert.ToChar(serialByte);
_stringBuffer[_stringBufferIndex] = c;
_stringBufferIndex++;
if (_currentStringRequest == null)
{
if (!_awaitedStringsQueue.TryDequeue(out _currentStringRequest))
{
// No pending Read/ReadLine/ReadTo requests.
// Handle StringReceived event.
if (c == _connection.NewLine[_connection.NewLine.Length - 1]
|| serialByte == 0x1A
|| serialByte == 0x00) // NewLine, EOF or terminating 0-byte?
{
StringReceived?.Invoke(this, new StringEventArgs(new string(_stringBuffer, 0, _stringBufferIndex - 1)));
_stringBufferIndex = 0;
}
return;
}
}
switch (_currentStringRequest.Mode)
{
case StringReadMode.ReadLine:
if (c == _connection.NewLine[0] || serialByte == 0x1A)
EnqueueReceivedString(new string(_stringBuffer, 0, _stringBufferIndex - 1));
else if (c == '\n') // Ignore linefeed, just in case cr+lf pair was expected.
_stringBufferIndex--;
break;
case StringReadMode.ReadBlock:
if (_stringBufferIndex == _currentStringRequest.BlockLength)
EnqueueReceivedString(new string(_stringBuffer, 0, _stringBufferIndex));
break;
case StringReadMode.ReadToTerminator:
if (c == _currentStringRequest.Terminator)
EnqueueReceivedString(new string(_stringBuffer, 0, _stringBufferIndex - 1));
break;
}
}
private void EnqueueReceivedString(string value)
{
bool lockTaken = false;
try
{
Monitor.TryEnter(_receivedStringQueue, _messageTimeout, ref lockTaken);
if (!lockTaken)
throw new TimeoutException();
if (_receivedStringQueue.Count >= MaxQueuelength)
throw new OverflowException(Messages.OverflowEx_StringBufferFull);
_receivedStringQueue.Enqueue(value);
Monitor.PulseAll(_receivedStringQueue);
_currentStringRequest = null;
_stringBufferIndex = 0;
}
finally
{
if (lockTaken)
Monitor.Exit(_receivedStringQueue);
}
}
private void ProcessCommand(byte serialByte)
{
_messageBuffer[0] = serialByte;
_messageBufferIndex = 1;
MessageHeader header = (MessageHeader)(serialByte & 0xF0);
switch (header)
{
case MessageHeader.AnalogState:
_processMessage = ProcessAnalogStateMessage;
return;
case MessageHeader.DigitalState:
_processMessage = ProcessDigitalStateMessage;
return;
case MessageHeader.SystemExtension:
header = (MessageHeader)serialByte;
switch (header)
{
case MessageHeader.SystemExtension:
_processMessage = ProcessSysExMessage;
return;
case MessageHeader.ProtocolVersion:
_processMessage = ProcessProtocolVersionMessage;
return;
//case MessageHeader.SetPinMode:
//case MessageHeader.SystemReset:
default:
// 0xF? command not supported.
break;
}
break;
default:
// Command not supported.
break;
}
// Ignore unexpected command.
_messageBuffer[0] = 0;
_messageBufferIndex = 0;
}
private void ProcessAnalogStateMessage(byte messageByte)
{
if (_messageBufferIndex < 2)
{
WriteMessageByte(messageByte);
}
else
{
var currentState = new AnalogState
{
Channel = _messageBuffer[0] & 0x0F,
Level = (_messageBuffer[1] | (messageByte << 7))
};
_processMessage = null;
MessageReceived?.Invoke(this, new FirmataMessageEventArgs(new FirmataMessage(currentState, MessageType.AnalogState)));
AnalogStateReceived?.Invoke(this, new FirmataEventArgs(currentState));
}
}
private void ProcessDigitalStateMessage(byte messageByte)
{
if (_messageBufferIndex < 2)
{
WriteMessageByte(messageByte);
}
else
{
var currentState = new DigitalPortState
{
Port = _messageBuffer[0] & 0x0F,
Pins = _messageBuffer[1] | (messageByte << 7)
};
_processMessage = null;
MessageReceived?.Invoke(this, new FirmataMessageEventArgs(new FirmataMessage(currentState, MessageType.DigitalPortState)));
DigitalStateReceived?.Invoke(this, new FirmataEventArgs(currentState));
}
}
private void ProcessProtocolVersionMessage(byte messageByte)
{
if (_messageBufferIndex < 2)
{
WriteMessageByte(messageByte);
}
else
{
var version = new ProtocolVersion
{
Major = _messageBuffer[1],
Minor = messageByte
};
DeliverMessage(new FirmataMessage(version, MessageType.ProtocolVersion));
}
}
private void ProcessSysExMessage(byte messageByte)
{
if (messageByte != SysExEnd)
{
WriteMessageByte(messageByte);
return;
}
switch ((SysExCommand)_messageBuffer[1])
{
case SysExCommand.AnalogMappingResponse:
DeliverMessage(CreateAnalogMappingResponse());
return;
case SysExCommand.CapabilityResponse:
DeliverMessage(CreateCapabilityResponse());
return;
case SysExCommand.PinStateResponse:
DeliverMessage(CreatePinStateResponse());
return;
case SysExCommand.StringData:
DeliverMessage(CreateStringDataMessage());
return;
case SysExCommand.I2cReply:
DeliverMessage(CreateI2CReply());
return;
case SysExCommand.ReportFirmware:
DeliverMessage(CreateFirmwareResponse());
return;
default: // Unknown or unsupported message
throw new NotSupportedException(string.Format(Messages.NotSupportedResponse, _messageBuffer[1]));
}
}
private void DeliverMessage(FirmataMessage message)
{
_processMessage = null;
lock (_receivedMessageList)
{
if (_receivedMessageList.Count >= MaxQueuelength)
throw new OverflowException(Messages.OverflowEx_MsgBufferFull);
// Remove all unprocessed and timed-out messages.
while (_receivedMessageList.Count > 0 &&
((DateTime.UtcNow - _receivedMessageList.First.Value.Time).TotalMilliseconds > TimeOut))
{
_receivedMessageList.RemoveFirst();
}
_receivedMessageList.AddLast(message);
Monitor.PulseAll(_receivedMessageList);
}
if (MessageReceived != null && message.Type != MessageType.I2CReply)
MessageReceived(this, new FirmataMessageEventArgs(message));
}
private FirmataMessage CreateI2CReply()
{
var reply = new I2CReply
{
Address = _messageBuffer[2] | (_messageBuffer[3] << 7),
Register = _messageBuffer[4] | (_messageBuffer[5] << 7)
};
var data = new byte[(_messageBufferIndex - 5) / 2];
for (int x = 0; x < data.Length; x++)
{
data[x] = (byte)(_messageBuffer[x * 2 + 6] | _messageBuffer[x * 2 + 7] << 7);
}
reply.Data = data;
I2CReplyReceived?.Invoke(this, new I2CEventArgs(reply));
return new FirmataMessage(reply, MessageType.I2CReply);
}
private FirmataMessage CreatePinStateResponse()
{
if (_messageBufferIndex < 5)
throw new InvalidOperationException(Messages.InvalidOpEx_PinNotSupported);
int value = 0;
for (int x = _messageBufferIndex - 1; x > 3; x--)
{
value = (value << 7) | _messageBuffer[x];
}
var pinState = new PinState
{
PinNumber = _messageBuffer[2],
Mode = (PinMode)_messageBuffer[3],
Value = value
};
return new FirmataMessage(pinState, MessageType.PinStateResponse);
}
private FirmataMessage CreateAnalogMappingResponse()
{
var pins = new List(8);
for (int x = 2; x < _messageBufferIndex; x++)
{
if (_messageBuffer[x] != 0x7F)
{
pins.Add
(
new AnalogPinMapping
{
PinNumber = x - 2,
Channel = _messageBuffer[x]
}
);
}
}
var board = new BoardAnalogMapping { PinMappings = pins.ToArray() };
return new FirmataMessage(board, MessageType.AnalogMappingResponse);
}
private FirmataMessage CreateCapabilityResponse()
{
var pins = new List(12);
int pinIndex = 0;
int x = 2;
while (x < _messageBufferIndex)
{
if (_messageBuffer[x] != 127)
{
var capability = new PinCapability { PinNumber = pinIndex };
while (x < _messageBufferIndex && _messageBuffer[x] != 127)
{
PinMode pinMode = (PinMode)_messageBuffer[x];
switch (pinMode)
{
case PinMode.AnalogInput:
capability.Analog = true;
capability.AnalogResolution = _messageBuffer[x + 1];
break;
case PinMode.DigitalInput:
capability.DigitalInput = true;
break;
case PinMode.DigitalOutput:
capability.DigitalOutput = true;
break;
case PinMode.PwmOutput:
capability.Pwm = true;
capability.PwmResolution = _messageBuffer[x + 1];
break;
case PinMode.ServoControl:
capability.Servo = true;
capability.ServoResolution = _messageBuffer[x + 1];
break;
case PinMode.I2C:
capability.I2C = true;
break;
case PinMode.OneWire:
capability.OneWire = true;
break;
case PinMode.StepperControl:
capability.StepperControl = true;
capability.MaxStepNumber = _messageBuffer[x + 1];
break;
case PinMode.Encoder:
capability.Encoder = true;
break;
case PinMode.Serial:
capability.Serial = true;
break;
case PinMode.InputPullup:
capability.InputPullup = true;
break;
default:
throw new NotImplementedException();
}
x += 2;
}
pins.Add(capability);
}
pinIndex++;
x++;
}
return new FirmataMessage(new BoardCapability { Pins = pins.ToArray() }, MessageType.CapabilityResponse);
}
private FirmataMessage CreateStringDataMessage()
{
var builder = new StringBuilder(_messageBufferIndex >> 1);
for (int x = 2; x < _messageBufferIndex; x += 2)
{
builder.Append((char)(_messageBuffer[x] | (_messageBuffer[x + 1] << 7)));
}
var stringData = new StringData
{
Text = builder.ToString()
};
return new FirmataMessage(stringData, MessageType.StringData);
}
private FirmataMessage CreateFirmwareResponse()
{
var firmware = new Firmware
{
MajorVersion = _messageBuffer[2],
MinorVersion = _messageBuffer[3]
};
var builder = new StringBuilder(_messageBufferIndex);
for (int x = 4; x < _messageBufferIndex; x += 2 )
{
builder.Append((char)(_messageBuffer[x] | (_messageBuffer[x + 1] << 7)));
}
firmware.Name = builder.ToString();
return new FirmataMessage(firmware, MessageType.FirmwareResponse);
}
#endregion
}
}
================================================
FILE: Solid.Arduino/EnhancedSerialConnection.cs
================================================
using System.IO.Ports;
using System.Linq;
namespace Solid.Arduino
{
///
/// Represents a serial port connection, supporting Mono.
///
/// The official Mono project site
///
public class EnhancedSerialConnection : EnhancedSerialPort, ISerialConnection
{
///
/// Initializes a new instance of class using the highest serial port available at 115,200 bits per second.
///
public EnhancedSerialConnection()
: base(GetLastPortName(), (int)SerialBaudRate.Bps_115200)
{
ReadTimeout = 100;
WriteTimeout = 100;
}
///
/// Initializes a new instance of class on the given serial port and at the given baud rate.
///
/// The port name (e.g. 'COM3')
/// The baud rate
public EnhancedSerialConnection(string portName, SerialBaudRate baudRate)
: base(portName, (int)baudRate)
{
ReadTimeout = 100;
WriteTimeout = 100;
}
///
public static ISerialConnection Find()
{
return SerialConnection.Find();
}
///
public static ISerialConnection Find(string query, string expectedReply)
{
return SerialConnection.Find(query, expectedReply);
}
///
public new void Close()
{
if (IsOpen)
{
BaseStream.Flush();
DiscardInBuffer();
base.Close();
}
}
private static string GetLastPortName() => GetPortNames()
.Where(p => p.StartsWith(@"/dev/ttyUSB") || p.StartsWith(@"/dev/ttyAMA") || p.StartsWith(@"/dev/ttyACM") || p.StartsWith("COM"))
.OrderByDescending(p => p)
.FirstOrDefault();
}
}
================================================
FILE: Solid.Arduino/EnhancedSerialPort.cs
================================================
// /*
// Copyright 2013 Antanas Veiverys antanas.veiverys.com
//
// Refactored 2014 by Henk van Boeijen.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// */
//
using System;
using System.IO.Ports;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Threading;
namespace Solid.Arduino
{
///
/// Represents a system serial port, supporting .NET and Mono.
///
///
/// This class is a workaround for Mono's implementation of event .
///
/// Copyright 2013 Antanas Veiverys antanas.veiverys.com
///
///
///
public class EnhancedSerialPort : SerialPort
{
[DllImport("MonoPosixHelper", SetLastError = true)]
private static extern bool poll_serial(int fd, out int error, int timeout);
[DllImport("libc")]
private static extern IntPtr strerror(int errnum);
#region Private Fields
// Private member access through reflection.
private int _fdStreamField;
private FieldInfo _disposedFieldInfo;
private object _dataReceived;
#endregion
#region Constructors
///
public EnhancedSerialPort()
{
}
///
public EnhancedSerialPort(IContainer container)
: base(container)
{
}
///
public EnhancedSerialPort(string portName)
: this(portName, 9600, Parity.None, 8, StopBits.One)
{
}
///
public EnhancedSerialPort(string portName, int baudRate)
: this(portName, baudRate, Parity.None, 8, StopBits.One)
{
}
///
public EnhancedSerialPort(string portName, int baudRate, Parity parity)
: this(portName, baudRate, parity, 8, StopBits.One)
{
}
///
public EnhancedSerialPort(string portName, int baudRate, Parity parity, int dataBits)
: this(portName, baudRate, parity, dataBits, StopBits.One)
{
}
///
public EnhancedSerialPort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
: base(GetInitializedPortName(portName), baudRate, parity, dataBits, stopBits)
{
}
private static string GetInitializedPortName(string portName)
{
if (IsWindows)
SerialPortFixer.Initialize(portName);
return portName;
}
#endregion
#region Public Methods
///
public new void Open()
{
base.Open();
if (IsWindows) return;
FieldInfo fieldInfo = BaseStream.GetType().GetField("fd", BindingFlags.Instance | BindingFlags.NonPublic);
if (fieldInfo != null)
{
_fdStreamField = (int) fieldInfo.GetValue(BaseStream);
_disposedFieldInfo = BaseStream.GetType()
.GetField("disposed", BindingFlags.Instance | BindingFlags.NonPublic);
fieldInfo = typeof (SerialPort).GetField("data_received", BindingFlags.Instance | BindingFlags.NonPublic);
if (fieldInfo != null)
_dataReceived = fieldInfo.GetValue(this);
}
new Thread(EventThreadFunction).Start();
}
#endregion
#region Private Methods
private static bool IsWindows
{
get
{
PlatformID id = Environment.OSVersion.Platform;
return id == PlatformID.Win32Windows || id == PlatformID.Win32NT; // WinCE not supported
}
}
private void EventThreadFunction()
{
do
{
try
{
var stream = BaseStream;
if (stream == null)
{
return;
}
if (Poll(stream))
{
OnDataReceived(null);
}
}
catch
{
return;
}
}
while (IsOpen);
}
private void OnDataReceived(SerialDataReceivedEventArgs args)
{
((SerialDataReceivedEventHandler)Events[_dataReceived])?.Invoke(this, args);
}
private bool Poll(Stream stream)
{
CheckDisposed(stream);
if (IsOpen == false)
{
throw new InvalidOperationException("Port is closed.");
}
bool pollResult = poll_serial(_fdStreamField, out int error, ReadTimeout);
if (error != -1)
return pollResult;
int errnum = Marshal.GetLastWin32Error();
string errorMessage = Marshal.PtrToStringAnsi(strerror(errnum));
throw new IOException(errorMessage);
}
private void CheckDisposed(Stream stream)
{
bool disposed = (bool)_disposedFieldInfo.GetValue(stream);
if (disposed)
{
throw new ObjectDisposedException(stream.GetType().FullName);
}
}
#endregion
}
}
================================================
FILE: Solid.Arduino/Firmata/AnalogPinMapping.cs
================================================
namespace Solid.Arduino.Firmata
{
///
/// Represents a mapping between a MIDI channel and a physical pin number.
///
public sealed class AnalogPinMapping
{
///
/// Gets the MIDI channel number (0 - 15).
///
public int Channel { get; internal set; }
///
/// Gets the board's pin number (0 - 127).
///
public int PinNumber { get; internal set; }
}
}
================================================
FILE: Solid.Arduino/Firmata/AnalogState.cs
================================================
namespace Solid.Arduino.Firmata
{
///
/// Represents the analog level read from or set to an analog pin.
///
public sealed class AnalogState
{
///
/// Gets the MIDI channel number (0 - 15).
///
///
/// The mapping of analog pins to channel numbers can be retrieved using the method.
///
public int Channel { get; internal set; }
///
/// Gets the analog level.
///
public long Level { get; internal set; }
}
}
================================================
FILE: Solid.Arduino/Firmata/AnalogStateTracker.cs
================================================
namespace Solid.Arduino.Firmata
{
internal class AnalogStateTracker : ObservableEventTracker
{
private readonly int _channel;
internal AnalogStateTracker(IFirmataProtocol source, int channel = -1)
: base(source)
{
_channel = channel;
TrackingSource.AnalogStateReceived += Firmata_AnalogStateReceived;
}
public override void Dispose()
{
if (!IsDisposed)
{
TrackingSource.AnalogStateReceived -= Firmata_AnalogStateReceived;
base.Dispose();
}
}
void Firmata_AnalogStateReceived(object parSender, FirmataEventArgs parEventArgs)
{
if (_channel >= 0 && _channel != parEventArgs.Value.Channel)
return;
Observers.ForEach(o => o.OnNext(parEventArgs.Value));
}
}
}
================================================
FILE: Solid.Arduino/Firmata/BoardAnalogMapping.cs
================================================
namespace Solid.Arduino.Firmata
{
///
/// Represents a summary of mappings between MIDI channels and physical pin numbers.
///
public sealed class BoardAnalogMapping
{
///
/// Gets the channel mapping array of the board's analog pins.
///
public AnalogPinMapping[] PinMappings { get; internal set; }
}
}
================================================
FILE: Solid.Arduino/Firmata/BoardCapability.cs
================================================
namespace Solid.Arduino.Firmata
{
///
/// Represents a summary of pinmode capabilities supported by an Arduino board.
///
public sealed class BoardCapability
{
///
/// Gets the capability array of the board's pins.
///
public PinCapability[] Pins { get; internal set; }
}
}
================================================
FILE: Solid.Arduino/Firmata/ByteArrayExtensions.cs
================================================
using System;
namespace Solid.Arduino.Firmata
{
///
/// Provides extension methods for arrays.
///
public static class ByteArrayExtensions
{
///
/// Converts a array holding binary coded digits to a readable string.
///
/// The binary coded digit bytes
/// Value indicating if the first nibble contains the least significant part
/// A string containing numeric data
/// The array contains one or more non-BCD bytes.
public static string ConvertBinaryCodedDecimalToString(this byte[] data, bool isLittleEndian = false)
{
if (data is null)
throw new ArgumentNullException(nameof(data));
if (data.Length == 0)
return string.Empty;
char[] chars = new char[data.Length * 2];
int charIndex = 0;
if (isLittleEndian)
{
for (int x = data.Length - 1; x >= 0; x--)
{
chars[charIndex++] = ConvertToChar(data[x] & 0x0F);
chars[charIndex++] = ConvertToChar(data[x] >> 4);
}
}
else
{
for (int x = 0; x < data.Length; x++)
{
chars[charIndex++] = ConvertToChar(data[x] >> 4);
chars[charIndex++] = ConvertToChar(data[x] & 0x0F);
}
}
return new string(chars);
}
private static char ConvertToChar(int code)
{
if (code > 9)
throw new ArgumentException(Messages.ArgumentEx_CannotConvertBcd);
return Convert.ToChar(code | 0x30);
}
}
}
================================================
FILE: Solid.Arduino/Firmata/DigitalPortState.cs
================================================
using System;
namespace Solid.Arduino.Firmata
{
///
/// Represents the pin states of a digital port.
///
public sealed class DigitalPortState
{
///
/// Gets the digital port number.
///
public int Port { get; internal set; }
///
/// Gets the bit-pattern value of the digital port.
///
public int Pins { get; internal set; }
///
/// Gets a value indicating if a pin is set (1 or 'high').
///
/// The 0-based pin number
/// true when the pin has a binary 1 value, otherwise false
public bool IsSet(int pin)
{
if (pin < 0 || pin > 7)
throw new ArgumentOutOfRangeException(nameof(pin), Messages.ArgumentEx_PinRange0_7);
return (Pins & 1 << pin) > 0;
}
}
}
================================================
FILE: Solid.Arduino/Firmata/DigitalStateTracker.cs
================================================
namespace Solid.Arduino.Firmata
{
internal class DigitalStateTracker : ObservableEventTracker
{
private readonly int _port;
internal DigitalStateTracker(IFirmataProtocol source, int port = -1)
: base(source)
{
_port = port;
TrackingSource.DigitalStateReceived += Firmata_DigitalStateReceived;
}
public override void Dispose()
{
if (!IsDisposed)
{
TrackingSource.DigitalStateReceived -= Firmata_DigitalStateReceived;
base.Dispose();
}
}
void Firmata_DigitalStateReceived(object parSender, FirmataEventArgs parEventArgs)
{
if (_port >= 0 && _port != parEventArgs.Value.Port)
return;
Observers.ForEach(o => o.OnNext(parEventArgs.Value));
}
}
}
================================================
FILE: Solid.Arduino/Firmata/FirmataMessage.cs
================================================
using System;
namespace Solid.Arduino.Firmata
{
///
/// Represents a Firmata message received from an Arduino or Arduino compatible system.
///
public sealed class FirmataMessage
{
private readonly MessageType _type;
private readonly object _value;
private readonly DateTime _time;
///
/// Initializes a new instance.
///
/// The type of message to be created.
internal FirmataMessage(MessageType type)
: this(null, type, DateTime.UtcNow) { }
///
/// Initializes a new instance.
///
///
///
internal FirmataMessage(object value, MessageType type)
: this(value, type, DateTime.UtcNow) { }
///
/// Initializes a new instance.
///
///
///
///
internal FirmataMessage(object value, MessageType type, DateTime time)
{
_value = value;
_type = type;
_time = time;
}
///
/// Gets the specific value delivered by the message.
///
public object Value => _value;
///
/// Gets the type enumeration of the message.
///
public MessageType Type => _type;
///
/// Gets the time of the delivered message.
///
public DateTime Time => _time;
}
///
/// Indicates the type of a Firmata Message.
///
public enum MessageType
{
AnalogState,
DigitalPortState,
ProtocolVersion,
FirmwareResponse,
CapabilityResponse,
AnalogMappingResponse,
PinStateResponse,
StringData,
I2CReply
}
}
================================================
FILE: Solid.Arduino/Firmata/FirmataMessageEventArgs.cs
================================================
using System;
namespace Solid.Arduino.Firmata
{
///
/// Event arguments passed to a type event.
///
///
public sealed class FirmataMessageEventArgs : EventArgs
{
private readonly FirmataMessage _value;
internal FirmataMessageEventArgs(FirmataMessage value)
{
_value = value;
}
///
/// Gets the received message.
///
public FirmataMessage Value => _value;
}
}
================================================
FILE: Solid.Arduino/Firmata/FirmataMessageEventArgs_.cs
================================================
using System;
namespace Solid.Arduino.Firmata
{
///
/// Contains event data for a and type events.
///
/// Type of the event data
///
/// This class is primarily implemented by the and events.
///
///
///
public sealed class FirmataEventArgs : EventArgs
where T : class
{
private readonly T _value;
internal FirmataEventArgs(T value)
{
_value = value;
}
///
/// Gets the received message.
///
public T Value => _value;
}
}
================================================
FILE: Solid.Arduino/Firmata/Firmware.cs
================================================
namespace Solid.Arduino.Firmata
{
///
/// Identifies the Arduino board's firmware.
///
public sealed class Firmware
{
///
/// Gets the major version number.
///
public int MajorVersion { get; internal set; }
///
/// Gets the minor version number.
///
public int MinorVersion { get; internal set; }
///
/// Gets the name of the board's firmware.
///
public string Name { get; internal set; }
}
}
================================================
FILE: Solid.Arduino/Firmata/IFirmataProtocol.cs
================================================
using System;
using System.Threading.Tasks;
namespace Solid.Arduino.Firmata
{
///
/// Signature of event handlers capable of processing Firmata messages.
///
/// The object raising the event
/// Event arguments holding a
public delegate void MessageReceivedHandler(object sender, FirmataMessageEventArgs eventArgs);
///
/// Signature of event handlers capable of processing analog I/O messages.
///
/// The object raising the event
/// Event arguments holding a
public delegate void AnalogStateReceivedHandler(object sender, FirmataEventArgs eventArgs);
///
/// Signature of event handlers capable of processing digital I/O messages.
///
/// The object raising the event
/// Event arguments holding a
public delegate void DigitalStateReceivedHandler(object sender, FirmataEventArgs eventArgs);
///
/// The modes a pin can be in or can be set to.
///
public enum PinMode
{
Undefined = -1,
DigitalInput = 0,
DigitalOutput = 1,
AnalogInput = 2,
PwmOutput = 3,
ServoControl = 4,
I2C = 6,
OneWire = 7,
StepperControl = 8,
Encoder = 9,
Serial = 10,
InputPullup = 11
}
///
/// Defines a comprehensive set of members supporting the Firmata Protocol.
/// Currently version 2.3 is supported.
///
/// Firmata project on GitHub
/// Firmata protocol details
/// Firmata reference for Arduino
public interface IFirmataProtocol
{
///
/// Event, raised for every SysEx (0xF0) and ProtocolVersion (0xF9) message not handled by an 's Get method.
///
///
/// When e.g. method is invoked, the party system's response message raises this event.
/// However, when method or is invoked, the response is returned
/// to the respective method and event is not raised.
///
/// This event is not raised for either analog or digital I/O messages.
///
event MessageReceivedHandler MessageReceived;
///
/// Event, raised when an analog state message (command 0xE0) is received.
///
///
/// The frequency at which analog state messages are being sent by the party system can be set with method .
///
event AnalogStateReceivedHandler AnalogStateReceived;
///
/// Event, raised when a digital I/O message (command 0x90) is received.
///
///
/// Please note that the StandardFirmata implementation for Arduino only sends updates of digital port states if necessary.
/// When none of a port's digital input pins have changed state since a previous polling cycle, no Firmata.sendDigitalPort message
/// is sent.
///
/// Also, calling method does not guarantee this event will receive a (first) Firmata.sendDigitalPort message.
/// Use method or inquiring the current pin states.
///
///
event DigitalStateReceivedHandler DigitalStateReceived;
///
/// Creates an observable object tracking messages.
///
/// An interface
IObservable CreateAnalogStateMonitor();
///
/// Creates an observable object tracking messages for a specific channel.
///
/// The channel to track
///
/// An interface
///
IObservable CreateAnalogStateMonitor(int channel);
///
/// Creates an observable object tracking messages.
///
/// An interface
IObservable CreateDigitalStateMonitor();
///
/// Creates an observable object tracking messages for a specific port.
///
/// The port to track
///
/// An interface
///
IObservable CreateDigitalStateMonitor(int port);
///
/// Sends a message string.
///
/// The message string
void SendStringData(string data);
///
/// Enables or disables analog sampling reporting.
///
/// The channel attached to the analog pin
/// True if enabled, otherwise false
///
/// When enabled, the party system is expected to return analog I/O messages (0xE0)
/// for the given channel. The frequency at which these messages are returned can
/// be controlled by method .
///
void SetAnalogReportMode(int channel, bool enable);
///
/// Sets the digital output pins of a given port LOW or HIGH.
///
/// The 0-based port number
/// Binary value for the port's pins (0 to 7)
///
/// A binary 1 sets the digital output pin HIGH (+5 or +3.3 volts).
/// A binary 0 sets the digital output pin LOW.
///
/// The Arduino operates with 8-bit ports, so only bits 0 to 7 of the pins parameter are mapped.
/// Higher bits are ignored.
///
///
/// For port 0 bit 2 maps to the Arduino Uno's pin 2.
/// For port 1 bit 2 maps to pin 10.
///
/// The complete mapping of port 1 of the Arduino Uno looks like this:
///
/// bit 0: pin 8
/// bit 1: pin 9
/// bit 2: pin 10
/// bit 3: pin 11
/// bit 4: pin 12
/// bit 5: pin 13
/// bit 6: not mapped
/// bit 7: not mapped
///
///
///
void SetDigitalPort(int portNumber, int pins);
///
/// Enables or disables digital input pin reporting for the given port.
///
/// The number of the port
/// true if enabled, otherwise false
///
/// When enabled, the party system is expected to return digital I/O messages (0x90)
/// for the given port.
///
/// Note: as for Firmata version 2.3 digital I/O messages are only returned when
/// at least one digital input pin's state has changed from high to low or vice versa.
///
///
void SetDigitalReportMode(int portNumber, bool enable);
///
/// Sets a pin's mode (digital input/digital output/analog/PWM/servo etc.).
///
/// The number of the pin
/// The pin's mode
void SetDigitalPinMode(int pinNumber, PinMode mode);
///
/// Sets the frequency at which analog samples must be reported.
///
/// The sampling interval in milliseconds
void SetSamplingInterval(int milliseconds);
///
/// Sets an analog value on a PWM or Servo enabled analog output pin.
///
/// The pin number.
/// The value
void SetDigitalPin(int pinNumber, long value);
///
/// Sets a HI or LO value on a digital output pin.
///
/// The pin number
/// The value (false = Low, true = High)
void SetDigitalPin(int pinNumber, bool value);
///
/// Sends a reset message to the party system.
///
void ResetBoard();
///
/// Requests the party system to send a protocol version message.
///
///
/// The party system is expected to return a single protocol version message (0xF9).
/// This message triggers the event. The protocol version
/// is passed in the in a object.
///
void RequestProtocolVersion();
///
/// Gets the protocol version implemented on the party system.
///
/// The implemented protocol version
ProtocolVersion GetProtocolVersion();
///
/// Asynchronously gets the protocol version implemented on the party system.
///
/// The implemented protocol version
Task GetProtocolVersionAsync();
///
/// Requests the party system to send a firmware message.
///
///
/// The party system is expected to return a single SYSEX REPORT_FIRMWARE message.
/// This message triggers the event. The firmware signature
/// is passed in the in a object.
///
void RequestFirmware();
///
/// Gets the firmware signature of the party system.
///
/// The firmware signature
Firmware GetFirmware();
///
/// Asynchronously gets the firmware signature of the party system.
///
/// The firmware signature
Task GetFirmwareAsync();
///
/// Requests the party system to send a summary of its capabilities.
///
///
/// The party system is expected to return a single SYSEX CAPABILITY_RESPONSE message.
/// This message triggers the event. The capabilities
/// are passed in the in a object.
///
void RequestBoardCapability();
///
/// Gets a summary of the party system's capabilities.
///
/// The system's capabilities
BoardCapability GetBoardCapability();
///
/// Asynchronously gets a summary of the party system's capabilities.
///
/// The system's capabilities
Task GetBoardCapabilityAsync();
///
/// Requests the party system to send the channel-to-pin mappings of its analog lines.
///
///
/// The party system is expected to return a single SYSEX ANALOG_MAPPING_RESPONSE message.
/// This message triggers the event. The analog mappings are
/// passed in the in a object.
///
void RequestBoardAnalogMapping();
///
/// Gets the channel-to-pin mappings of the party system's analog lines.
///
/// The channel-to-pin mappings
BoardAnalogMapping GetBoardAnalogMapping();
///
/// Asynchronously gets the channel-to-pin mappings of the party system's analog lines.
///
/// The channel-to-pin mappings
Task GetBoardAnalogMappingAsync();
///
/// Requests the party system to send the state of a given pin.
///
/// The pin number
///
/// The party system is expected to return a single SYSEX PINSTATE_RESPONSE message.
/// This message triggers the event. The pin state
/// is passed in the in a object.
///
void RequestPinState(int pinNumber);
///
/// Gets a pin's mode (digital input/output, analog etc.) and actual value.
///
/// The pin number
/// The pin's state
PinState GetPinState(int pinNumber);
///
/// Asynchronously gets a pin's mode (digital input/output, analog etc.) and actual value.
///
/// The pin number
/// The pin's state
Task GetPinStateAsync(int pinNumber);
}
}
================================================
FILE: Solid.Arduino/Firmata/PinCapability.cs
================================================
namespace Solid.Arduino.Firmata
{
///
/// Contains information about the capabilities of a pin.
///
public sealed class PinCapability
{
///
/// Gets the 0-based number of the pin.
///
public int PinNumber { get; internal set; }
///
/// Gets a value indicating if the pin can be in digital input mode.
///
public bool DigitalInput { get; internal set; }
///
/// Gets a value indicating if the pin can be in digital output mode.
///
public bool DigitalOutput { get; internal set; }
///
/// Gets a value indicating if it is an analog pin.
///
public bool Analog { get; internal set; }
///
/// Gets a value indicating if the pin supports pulse width modulation.
///
public bool Pwm { get; internal set; }
///
/// Gets a value indicating if the pin supports servo motor control.
///
public bool Servo { get; internal set; }
///
/// Gets the bit resolution for analog pins.
///
public int AnalogResolution { get; internal set; }
///
/// Gets the bit resolution for PWM enabled pins.
///
public int PwmResolution { get; internal set; }
///
/// Gets the bit resolution for servo enabled pins.
///
public int ServoResolution { get; internal set; }
///
/// Gets a value indicating if it is an I2c pin.
///
public bool I2C { get; internal set; }
///
/// Gets a value indicating if it is an OneWire pin.
///
public bool OneWire { get; internal set; }
///
/// Gets a value indicating if it is a Stepper Control pin.
///
public bool StepperControl { get; internal set; }
///
/// Gets a value indicating if it is an encoder pin.
///
public bool Encoder { get; internal set; }
///
/// Gets a value indicating if it is a serial pin.
///
public bool Serial { get; internal set; }
///
/// Gets a value indicating if it is an input pullup pin.
///
public bool InputPullup { get; internal set; }
///
/// Gets the maximum number of steps if it is a Stepper Control pin.
///
public int MaxStepNumber { get; internal set; }
}
}
================================================
FILE: Solid.Arduino/Firmata/PinState.cs
================================================
namespace Solid.Arduino.Firmata
{
///
/// Contains information about a pin's state.
///
public sealed class PinState
{
///
/// The 0-based pin number
///
public int PinNumber { get; internal set; }
///
/// Gets pin's operating mode.
///
public PinMode Mode { get; internal set; }
///
/// Gets the value of the pin.
///
///
/// For analog pins the value is 0 or a positive number. For digital pins a low is represented by 0 and a high is respresented by 1.
///
public long Value { get; internal set; }
}
}
================================================
FILE: Solid.Arduino/Firmata/ProtocolVersion.cs
================================================
namespace Solid.Arduino.Firmata
{
///
/// Represents the Firmata communication protocol version.
///
public sealed class ProtocolVersion
{
///
/// Gets or sets the major version number.
///
public int Major { get; set; }
///
/// Gets or sets the minor version number.
///
public int Minor { get; set; }
}
}
================================================
FILE: Solid.Arduino/Firmata/Servo/IServoProtocol.cs
================================================
namespace Solid.Arduino.Firmata.Servo
{
///
/// Defines Servo control related members of the Firmata protocol.
///
///
/// This interface is separated from the interface, in order to
/// protect the latter against feature bloat.
///
public interface IServoProtocol
{
///
/// Configures the minimum and maximum pulse length for a servo pin.
///
/// The pin number
/// Minimum pulse length
/// Maximum pulse length
void ConfigureServo(int pinNumber, int minPulse, int maxPulse);
}
}
================================================
FILE: Solid.Arduino/Firmata/StringData.cs
================================================
namespace Solid.Arduino.Firmata
{
///
/// Represents a string exchanged with the Firmata SYSEX STRING_DATA command.
///
public sealed class StringData
{
///
/// Gets or sets the string.
///
public string Text { get; set; }
}
}
================================================
FILE: Solid.Arduino/Firmata/SysExCommand.cs
================================================
namespace Solid.Arduino.Firmata
{
///
/// SysEx message command bytes
///
public enum SysExCommand: byte
{
///
/// User defined ID 0x01
///
UserDefined_0x01 = 0x01,
///
/// User defined ID 0x02
///
UserDefined_0x02 = 0x02,
///
/// User defined ID 0x03
///
UserDefined_0x03 = 0x03,
///
/// User defined ID 0x04
///
UserDefined_0x04 = 0x04,
///
/// User defined ID 0x05
///
UserDefined_0x05 = 0x05,
///
/// User defined ID 0x06
///
UserDefined_0x06 = 0x06,
///
/// User defined ID 0x07
///
UserDefined_0x07 = 0x07,
///
/// User defined ID 0x08
///
UserDefined_0x08 = 0x08,
///
/// User defined ID 0x09
///
UserDefined_0x09 = 0x09,
///
/// User defined ID 0x0A
///
UserDefined_0x0A = 0x0A,
///
/// User defined ID 0x0B
///
UserDefined_0x0B = 0x0B,
///
/// User defined ID 0x0C
///
UserDefined_0x0C = 0x0C,
///
/// User defined ID 0x0D
///
UserDefined_0x0D = 0x0D,
///
/// User defined ID 0x0E
///
UserDefined_0x0E = 0x0E,
///
/// User defined ID 0x0F
///
UserDefined_0x0F = 0x0F,
///
/// Analog mapping query
///
AnalogMappingQuery = 0x69,
///
/// Analog mapping response
///
AnalogMappingResponse = 0x6A,
///
/// Capability query
///
CapabilityQuery = 0x6B,
///
/// Capability response
///
CapabilityResponse = 0x6C,
///
/// Pin state query
///
PinStateQuery = 0x6D,
///
/// Pin state response
///
PinStateResponse = 0x6E,
///
/// Extended analog
///
ExtendedAnalog = 0x6F,
///
/// String data
///
StringData = 0x71,
///
/// I2C request
///
I2cRequest = 0x76,
///
/// I2C reply
///
I2cReply = 0x77,
///
/// Report firmware
///
ReportFirmware = 0x79,
///
/// Sampling interval
///
SamplingInterval = 0x7A,
///
/// SysEx not-realtime
///
SysExNonRealtime = 0x7E,
///
/// SysEx realtime
///
SysExRealtime = 0x7F,
}
}
================================================
FILE: Solid.Arduino/I2c/I2cEventArgs.cs
================================================
using System;
namespace Solid.Arduino.I2C
{
///
/// Event arguments passed to a type event.
///
public class I2CEventArgs : EventArgs
{
private readonly I2CReply _value;
internal I2CEventArgs(I2CReply value)
{
_value = value;
}
///
/// Gets the I2C message value being received.
///
public I2CReply Value => _value;
}
}
================================================
FILE: Solid.Arduino/I2c/I2cReply.cs
================================================
namespace Solid.Arduino.I2C
{
///
/// Container for an I2C message
///
public struct I2CReply
{
///
/// Gets or sets the memory address.
///
public int Address { get; set; }
///
/// Gets or sets the register number.
///
public int Register { get; set; }
///
/// Gets or sets the binary data.
///
public byte[] Data { get; set; }
}
}
================================================
FILE: Solid.Arduino/I2c/I2cReplyTracker.cs
================================================
namespace Solid.Arduino.I2C
{
internal class I2CReplyTracker : ObservableEventTracker
{
internal I2CReplyTracker(II2CProtocol ii2C): base(ii2C)
{
TrackingSource.I2CReplyReceived += I2CReplyReceived;
}
public override void Dispose()
{
if (!IsDisposed)
{
TrackingSource.I2CReplyReceived -= I2CReplyReceived;
base.Dispose();
}
}
private void I2CReplyReceived(object parSender, I2CEventArgs parEventArgs)
{
Observers.ForEach(o => o.OnNext(parEventArgs.Value));
}
}
}
================================================
FILE: Solid.Arduino/I2c/II2cProtocol.cs
================================================
using System;
using System.Threading.Tasks;
using Solid.Arduino.Firmata;
namespace Solid.Arduino.I2C
{
///
/// Signature of event handlers capable of processing I2C_REPLY messages.
///
/// The object raising the event
/// Event arguments holding an
public delegate void I2CReplyReceivedHandler(object sender, I2CEventArgs eventArgs);
///
/// Defines a comprehensive set of members supporting the I2C Protocol.
///
/// I2C bus website by telos Systementwicklung GmbH
/// Arduino Wire reference
/// I2C Scanner sample sketch for Arduino
public interface II2CProtocol
{
///
/// Event, raised for every SYSEX I2C message not handled by an 's Get method.
///
///
/// When e.g. methods and are invoked,
/// the party system's response messages raise this event.
/// However, when method or is invoked,
/// the response received is returned to the method that issued the command and event is not raised.
///
event I2CReplyReceivedHandler I2CReplyReceived;
///
/// Creates an observable object tracking messages.
///
/// An interface
IObservable CreateI2CReplyMonitor();
///
/// Sets the frequency at which data is read in the continuous mode.
///
/// The interval, expressed in microseconds
void SetI2CReadInterval(int microseconds);
///
/// Writes an arbitrary array of bytes to the given memory address.
///
/// The slave's target address
/// The data array
void WriteI2C(int slaveAddress, params byte[] data);
///
/// Requests the party system to send bytes read from the given memory address.
///
/// The slave's memory address
/// Number of bytes to read
///
/// The party system is expected to return a single I2C_REPLY message.
/// This message triggers the event. The data
/// are passed in the in an object.
///
void ReadI2COnce(int slaveAddress, int bytesToRead);
///
/// Requests the party system to send bytes read from the given memory address and register.
///
/// The slave's memory address
/// The slave's register
/// Number of bytes to read
void ReadI2COnce(int slaveAddress, int slaveRegister, int bytesToRead);
///
/// Requests the party system to repeatedly send bytes read from the given memory address.
///
/// The slave's address
/// Number of bytes to read
///
/// The party system is expected to return a continuous stream of I2C_REPLY messages at
/// an interval which can be set using the method.
/// Received I2C_REPLY messages trigger the event. The data
/// are served in the 's Value property as an object.
///
/// The party system can be stopped sending I2C_REPLY messages by issuing a command.
///
///
void ReadI2CContinuous(int slaveAddress, int bytesToRead);
///
/// Requests the party system to repeatedly send bytes read from the given memory address and register.
///
/// The slave's memory address
/// The slave's register
/// Number of bytes to read
void ReadI2CContinuous(int slaveAddress, int slaveRegister, int bytesToRead);
///
/// Commands the party system to stop sending I2C_REPLY messages.
///
void StopI2CReading();
///
/// Gets byte data from the party system, read from the given memory address.
///
/// The slave's memory address
/// Number of bytes to read
/// An object holding the data read
I2CReply GetI2CReply(int slaveAddress, int bytesToRead);
///
/// Asynchronously gets byte data from the party system, read from the given memory address.
///
/// The slave's memory address
/// Number of bytes to read
/// An awaitable holding the data read
Task GetI2CReplyAsync(int slaveAddress, int bytesToRead);
///
/// Gets byte data from the party system, read from the given memory address and register.
///
/// The slave's memory address and register
/// The slave's register
/// Number of bytes to read
/// An object holding the data read
I2CReply GetI2CReply(int slaveAddress, int slaveRegister, int bytesToRead);
///
/// Asynchronously gets byte data from the party system, read from the given memory address and register.
///
/// The slave's memory address
/// The slave's register
/// Number of bytes to read
/// An awaitable holding the data read
Task GetI2CReplyAsync(int slaveAddress, int slaveRegister, int bytesToRead);
}
}
================================================
FILE: Solid.Arduino/ISerialConnection.cs
================================================
using System;
using System.IO.Ports;
namespace Solid.Arduino
{
///
/// Defines a serial port connection.
///
/// Serial reference for Arduino
public interface ISerialConnection: IDisposable
{
///
/// Represents the method that will handle the data received event of a object.
///
event SerialDataReceivedEventHandler DataReceived;
///
int BaudRate { get; set; }
///
string PortName { get; set; }
///
/// Gets a value indicating the open or closed status of the object.
///
bool IsOpen { get; }
///
/// Gets or sets the value used to interpret the end of strings received and sent
/// using and methods.
///
///
/// The default is a line feed, ().
///
string NewLine { get; set; }
///
/// Gets the number of bytes of data in the receive buffer.
///
int BytesToRead { get; }
///
/// Opens the connection.
///
void Open();
///
/// Closes the connection.
///
void Close();
///
/// Reads a byte from the underlying serial input data stream.
///
/// A byte value
int ReadByte();
///
/// Writes a string to the serial output data stream.
///
/// A string to be written
void Write(string text);
///
/// Writes a specified number of bytes to the serial output stream using data from a byte array.
///
/// The byte array that contains the data to write
/// The zero-based byte offset in the array at which to begin copying bytes
/// The number of bytes to write
void Write(byte[] buffer, int offset, int count);
///
/// Writes the specified string and the value to the serial output stream.
///
/// The string to write
void WriteLine(string text);
}
}
================================================
FILE: Solid.Arduino/IStringProtocol.cs
================================================
using System;
using System.IO.Ports;
using System.Threading.Tasks;
namespace Solid.Arduino
{
///
/// Signature of event handlers capable of processing received strings.
///
/// The object raising the event
/// Event arguments holding a message
public delegate void StringReceivedHandler(object sender, StringEventArgs eventArgs);
///
/// Defines members for sending and receiving ASCII string messages.
///
public interface IStringProtocol
{
///
/// Event, raised for every ASCII stringmessage not handled by an 's
/// Read, ReadAsync, ReadLine, ReadLineAsync, ReadTo or ReadToAsync method
///
///
/// Any spontaneous received string message, terminated with a newline or eof character raises this event.
///
event StringReceivedHandler StringReceived;
///
/// Creates an observable object tracking received ASCII messages.
///
/// An interface
IObservable CreateReceivedStringMonitor();
///
/// Gets or sets the value used to interpret the end of strings received and sent.
///
string NewLine { get; set; }
///
/// Writes a string to the serial output data stream.
///
/// A string to be written
void Write(string value = null);
///
/// Writes the specified string and the value to the serial output stream.
///
/// The string to write
void WriteLine(string value = null);
///
/// Reads a string up to the next character.
///
/// The string read
string ReadLine();
///
/// Reads a string asynchronous up to the next character.
///
/// An awaitable returning the string read
Task ReadLineAsync();
///
/// Reads a specified number of characters.
///
/// The number of characters to be read (default is 1)
/// The string read
string Read(int length = 1);
///
/// Reads a specified number of characters asynchronous.
///
/// The number of characters to be read (default is 1)
/// An awaitable returning the string read
Task ReadAsync(int length = 1);
///
/// Reads a string up to the first terminating character.
///
/// The character identifying the end of the string
/// The string read
string ReadTo(char terminator);
///
/// Reads a string asynchronous up to the first terminating character.
///
/// The character identifying the end of the string
/// An awaitable returning the string read
Task ReadToAsync(char terminator);
}
}
================================================
FILE: Solid.Arduino/Messages.Designer.cs
================================================
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
//------------------------------------------------------------------------------
namespace Solid.Arduino {
using System;
///
/// A strongly-typed resource class, for looking up localized strings, etc.
///
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Messages {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Messages() {
}
///
/// Returns the cached ResourceManager instance used by this class.
///
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Solid.Arduino.Messages", typeof(Messages).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
///
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
///
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
///
/// Looks up a localized string similar to Cannot convert non-BCD data..
///
internal static string ArgumentEx_CannotConvertBcd {
get {
return ResourceManager.GetString("ArgumentEx_CannotConvertBcd", resourceCulture);
}
}
///
/// Looks up a localized string similar to Channel number must be between 0 and 15..
///
internal static string ArgumentEx_ChannelRange0_15 {
get {
return ResourceManager.GetString("ArgumentEx_ChannelRange0_15", resourceCulture);
}
}
///
/// Looks up a localized string similar to String must contain digits only..
///
internal static string ArgumentEx_DigitStringOnly {
get {
return ResourceManager.GetString("ArgumentEx_DigitStringOnly", resourceCulture);
}
}
///
/// Looks up a localized string similar to Address must be between 0 and 1,023..
///
internal static string ArgumentEx_I2cAddressRange {
get {
return ResourceManager.GetString("ArgumentEx_I2cAddressRange", resourceCulture);
}
}
///
/// Looks up a localized string similar to Interval must be between 0 and 16,383 milliseconds..
///
internal static string ArgumentEx_I2cInterval {
get {
return ResourceManager.GetString("ArgumentEx_I2cInterval", resourceCulture);
}
}
///
/// Looks up a localized string similar to Maximum pulse width must be between 0 and 16,383 milliseconds..
///
internal static string ArgumentEx_MaxPulseWidth {
get {
return ResourceManager.GetString("ArgumentEx_MaxPulseWidth", resourceCulture);
}
}
///
/// Looks up a localized string similar to Minimum pulse width is greater than maximum pulse width..
///
internal static string ArgumentEx_MinMaxPulse {
get {
return ResourceManager.GetString("ArgumentEx_MinMaxPulse", resourceCulture);
}
}
///
/// Looks up a localized string similar to Minimum pulse width must be between 0 and 16,383 milliseconds..
///
internal static string ArgumentEx_MinPulseWidth {
get {
return ResourceManager.GetString("ArgumentEx_MinPulseWidth", resourceCulture);
}
}
///
/// Looks up a localized string similar to Value cannot be negative..
///
internal static string ArgumentEx_NoNegativeValue {
get {
return ResourceManager.GetString("ArgumentEx_NoNegativeValue", resourceCulture);
}
}
///
/// Looks up a localized string similar to String argument can not be null or empty..
///
internal static string ArgumentEx_NotNullOrEmpty {
get {
return ResourceManager.GetString("ArgumentEx_NotNullOrEmpty", resourceCulture);
}
}
///
/// Looks up a localized string similar to Pin number must be between 0 and 127..
///
internal static string ArgumentEx_PinRange0_127 {
get {
return ResourceManager.GetString("ArgumentEx_PinRange0_127", resourceCulture);
}
}
///
/// Looks up a localized string similar to Pin must be between 0 and 7..
///
internal static string ArgumentEx_PinRange0_7 {
get {
return ResourceManager.GetString("ArgumentEx_PinRange0_7", resourceCulture);
}
}
///
/// Looks up a localized string similar to Port number must be between 0 and 15..
///
internal static string ArgumentEx_PortRange0_15 {
get {
return ResourceManager.GetString("ArgumentEx_PortRange0_15", resourceCulture);
}
}
///
/// Looks up a localized string similar to Value must be greater than zero..
///
internal static string ArgumentEx_PositiveValue {
get {
return ResourceManager.GetString("ArgumentEx_PositiveValue", resourceCulture);
}
}
///
/// Looks up a localized string similar to Sampling interval must be between 0 and 16,383 milliseconds..
///
internal static string ArgumentEx_SamplingInterval {
get {
return ResourceManager.GetString("ArgumentEx_SamplingInterval", resourceCulture);
}
}
///
/// Looks up a localized string similar to Value must be between 0 and 16,383..
///
internal static string ArgumentEx_ValueRange0_16383 {
get {
return ResourceManager.GetString("ArgumentEx_ValueRange0_16383", resourceCulture);
}
}
///
/// Looks up a localized string similar to Value must be betwen 0 and 255..
///
internal static string ArgumentEx_ValueRange0_255 {
get {
return ResourceManager.GetString("ArgumentEx_ValueRange0_255", resourceCulture);
}
}
///
/// Looks up a localized string similar to Pin is not supported..
///
internal static string InvalidOpEx_PinNotSupported {
get {
return ResourceManager.GetString("InvalidOpEx_PinNotSupported", resourceCulture);
}
}
///
/// Looks up a localized string similar to Invalid serial port.
///
internal static string InvalidSerialPort {
get {
return ResourceManager.GetString("InvalidSerialPort", resourceCulture);
}
}
///
/// Looks up a localized string similar to Commandbyte 0x{0:X} is not implemented..
///
internal static string NotImplementedEx_Command {
get {
return ResourceManager.GetString("NotImplementedEx_Command", resourceCulture);
}
}
///
/// Looks up a localized string similar to Response message code 0x{0:X} is not supported..
///
internal static string NotSupportedResponse {
get {
return ResourceManager.GetString("NotSupportedResponse", resourceCulture);
}
}
///
/// Looks up a localized string similar to The command parsing buffer is full..
///
internal static string OverflowEx_CmdBufferFull {
get {
return ResourceManager.GetString("OverflowEx_CmdBufferFull", resourceCulture);
}
}
///
/// Looks up a localized string similar to Received message queue is full..
///
internal static string OverflowEx_MsgBufferFull {
get {
return ResourceManager.GetString("OverflowEx_MsgBufferFull", resourceCulture);
}
}
///
/// Looks up a localized string similar to The received strings buffer is full..
///
internal static string OverflowEx_StringBufferFull {
get {
return ResourceManager.GetString("OverflowEx_StringBufferFull", resourceCulture);
}
}
///
/// Looks up a localized string similar to Wait condition for {0} message timed out..
///
internal static string TimeoutEx_WaitMessage {
get {
return ResourceManager.GetString("TimeoutEx_WaitMessage", resourceCulture);
}
}
///
/// Looks up a localized string similar to Wait condition for string read in {0} mode timed out..
///
internal static string TimeoutEx_WaitStringRequest {
get {
return ResourceManager.GetString("TimeoutEx_WaitStringRequest", resourceCulture);
}
}
}
}
================================================
FILE: Solid.Arduino/Messages.resx
================================================
text/microsoft-resx2.0System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089Cannot convert non-BCD data.Channel number must be between 0 and 15.String must contain digits only.Address must be between 0 and 1,023.Interval must be between 0 and 16,383 milliseconds.Maximum pulse width must be between 0 and 16,383 milliseconds.Minimum pulse width is greater than maximum pulse width.Minimum pulse width must be between 0 and 16,383 milliseconds.Value cannot be negative.String argument can not be null or empty.Pin number must be between 0 and 127.Pin must be between 0 and 7.Port number must be between 0 and 15.Value must be greater than zero.Sampling interval must be between 0 and 16,383 milliseconds.Value must be between 0 and 16,383.Value must be betwen 0 and 255.Pin is not supported.Invalid serial portCommandbyte 0x{0:X} is not implemented.Response message code 0x{0:X} is not supported.The command parsing buffer is full.Received message queue is full.The received strings buffer is full.Wait condition for {0} message timed out.Wait condition for string read in {0} mode timed out.
================================================
FILE: Solid.Arduino/ObservableEventTracker.cs
================================================
using System;
using System.Collections.Generic;
namespace Solid.Arduino
{
internal abstract class ObservableEventTracker : IObservable, IDisposable
{
#region Protected Fields
protected readonly TSource TrackingSource;
protected readonly List> Observers = new List>();
protected bool IsDisposed = false;
#endregion
#region Constructors
internal ObservableEventTracker(TSource trackingSource)
{
TrackingSource = trackingSource;
}
#endregion
#region Public Methods
public IDisposable Subscribe(IObserver observer)
{
Observers.Add(observer);
return this;
}
public virtual void Dispose()
{
if (!IsDisposed)
{
foreach (IObserver observer in Observers)
observer.OnCompleted();
GC.SuppressFinalize(this);
IsDisposed = true;
}
}
#endregion
}
}
================================================
FILE: Solid.Arduino/ReceivedStringTracker.cs
================================================
namespace Solid.Arduino
{
internal class ReceivedStringTracker : ObservableEventTracker
{
#region Constructors
internal ReceivedStringTracker(IStringProtocol source)
: base(source)
{
TrackingSource.StringReceived += TrackingSource_StringReceived;
}
#endregion
#region Public Methods
public override void Dispose()
{
if (!IsDisposed)
{
TrackingSource.StringReceived -= TrackingSource_StringReceived;
base.Dispose();
}
}
#endregion
#region Private Methods
void TrackingSource_StringReceived(object parSender, StringEventArgs parEventArgs)
{
Observers.ForEach(o => o.OnNext(parEventArgs.Text));
}
#endregion
}
}
================================================
FILE: Solid.Arduino/SerialBaudRate.cs
================================================
namespace Solid.Arduino
{
///
/// Enumeration of common baud rates, supported by Arduino boards
///
public enum SerialBaudRate
{
Bps_300 = 300,
Bps_600 = 600,
Bps_1200 = 1200,
Bps_2400 = 2400,
Bps_4800 = 4800,
Bps_9600 = 9600,
Bps_14400 = 14400,
Bps_19200 = 19200,
Bps_28800 = 28800,
Bps_31250 = 31250,
Bps_38400 = 38400,
Bps_57600 = 57600,
Bps_115200 = 115200
}
}
================================================
FILE: Solid.Arduino/SerialConnection.cs
================================================
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Threading;
using Solid.Arduino.Firmata;
namespace Solid.Arduino
{
///
/// Represents a serial port connection.
///
///
public class SerialConnection : SerialPort, ISerialConnection
{
private static readonly SerialBaudRate[] PopularBaudRates =
{
SerialBaudRate.Bps_9600,
SerialBaudRate.Bps_57600,
SerialBaudRate.Bps_115200
};
private static readonly SerialBaudRate[] OtherBaudRates =
{
SerialBaudRate.Bps_28800,
SerialBaudRate.Bps_14400,
SerialBaudRate.Bps_38400,
SerialBaudRate.Bps_31250,
SerialBaudRate.Bps_4800,
SerialBaudRate.Bps_2400
};
private bool _isDisposed;
///
/// Initializes a new instance of class using the highest COM-port available at 115,200 bits per second.
///
public SerialConnection()
: base(GetHighestComPortName(), (int) SerialBaudRate.Bps_115200)
{
ReadTimeout = 100;
WriteTimeout = 100;
}
///
/// Initializes a new instance of class on the given serial port and at the given baud rate.
///
/// The port name (e.g. 'COM3')
/// The baud rate
public SerialConnection(string portName, SerialBaudRate baudRate)
: base(portName, (int) baudRate)
{
ReadTimeout = 100;
WriteTimeout = 100;
}
///
public new void Open()
{
if (IsOpen)
return;
try
{
base.Open();
}
catch (UnauthorizedAccessException)
{
// Connection closure has probably not yet been finalized.
// Wait 250 ms and try again once.
Thread.Sleep(250);
base.Open();
}
}
///
public new void Close()
{
if (!IsOpen)
return;
Thread.Sleep(250);
BaseStream.Flush();
DiscardInBuffer();
BaseStream.Close();
base.Close();
}
///
public new void Dispose()
{
if (_isDisposed)
return;
_isDisposed = true;
BaseStream.Dispose();
GC.SuppressFinalize(BaseStream);
base.Dispose();
GC.SuppressFinalize(this);
}
///
///
///
/// Finds a serial connection to a device supporting the Firmata protocol.
///
/// A instance or null if no connection is found
///
///
/// This method searches all available serial ports until it finds a working serial connection.
/// For every available serial port an attempt is made to open a connection at a range of common baudrates.
/// The connection is tested by issueing an command.
/// (I.e. a Firmata SysEx Firmware query (0xF0 0x79 0xF7).)
///
///
/// The connected device is expected to respond by sending the version number of the supported protocol.
/// When a major version of 2 or higher is received, the connection is regarded to be valid.
///
///
///
/// Query Firmware Name and Version
public static ISerialConnection Find()
{
bool isAvailableFunc(ArduinoSession session)
{
Firmware firmware = session.GetFirmware();
return firmware.MajorVersion >= 2;
}
string[] portNames = GetPortNames();
ISerialConnection connection = FindConnection(isAvailableFunc, portNames, PopularBaudRates);
return connection ?? FindConnection(isAvailableFunc, portNames, OtherBaudRates);
}
///
/// Finds a serial connection to a device supporting plain serial communications.
///
/// The query text used to inquire the connection
/// The reply text the connected device is expected to respond with
/// A instance or null if no connection is found
///
///
/// This method searches all available serial ports until it finds a working serial connection.
/// For every available serial port an attempt is made to open a connection at a range of common baudrates.
/// The connection is tested by sending the query string passed to this method.
///
///
/// The connected device is expected to respond by sending the reply string passed to this method.
/// When the string received is equal to the expected reply string, the connection is regarded to be valid.
///
///
///
/// The Arduino sketch below can be used to demonstrate this method.
/// Upload the sketch to your Arduino device.
///
/// char query[] = "Hello?";
/// char reply[] = "Arduino!";
///
/// void setup()
/// {
/// Serial.begin(9600);
/// while (!Serial) {}
/// }
///
/// void loop()
/// {
/// if (Serial.find(query))
/// {
/// Serial.println(reply);
/// }
/// else
/// {
/// Serial.println("Listening...");
/// Serial.flush();
/// }
///
/// delay(25);
/// }
///
///
///
public static ISerialConnection Find(string query, string expectedReply)
{
if (string.IsNullOrEmpty(query))
throw new ArgumentException(Messages.ArgumentEx_NotNullOrEmpty, nameof(query));
if (string.IsNullOrEmpty(expectedReply))
throw new ArgumentException(Messages.ArgumentEx_NotNullOrEmpty, nameof(expectedReply));
bool isAvailableFunc(ArduinoSession session)
{
session.Write(query);
return session.Read(expectedReply.Length) == expectedReply;
}
string[] portNames = GetPortNames();
ISerialConnection connection = FindConnection(isAvailableFunc, portNames, PopularBaudRates);
return connection ?? FindConnection(isAvailableFunc, portNames, OtherBaudRates);
}
private static string GetHighestComPortName()
{
return GetPortNames().Where(n => n.StartsWith("COM")).OrderByDescending(n => n).FirstOrDefault();
}
private static ISerialConnection FindConnection(Func isDeviceAvailable, string[] portNames, SerialBaudRate[] baudRates)
{
bool found = false;
for (int x = portNames.Length - 1; x >= 0; x--)
{
foreach (SerialBaudRate rate in baudRates)
{
try
{
using (var connection = new EnhancedSerialConnection(portNames[x], rate))
{
using (var session = new ArduinoSession(connection, 100))
{
#if TRACE
Debug.WriteLine("{0}:{1}; ", portNames[x], (int)rate);
#endif
if (isDeviceAvailable(session))
found = true;
}
}
if (found)
return new EnhancedSerialConnection(portNames[x], rate);
}
catch (UnauthorizedAccessException)
{
// Port is not available.
#if TRACE
Debug.WriteLine("{0} NOT AVAILABLE; ", portNames[x]);
#endif
break;
}
catch (TimeoutException)
{
// Baudrate or protocol error.
}
catch (IOException ex)
{
#if TRACE
Debug.WriteLine($"HResult 0x{ex.HResult:X} - {ex.Message}");
#endif
}
}
}
return null;
}
}
}
================================================
FILE: Solid.Arduino/SerialPortFixer.cs
================================================
// Copyright 2010-2014 Zach Saw
// Refactored 2017 Henk van Boeijen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;
namespace Solid.Arduino
{
///
/// SerialPort IOException Workaround
///
///
internal static class SerialPortFixer
{
[StructLayout(LayoutKind.Sequential)]
private struct Comstat
{
public readonly uint Flags;
public readonly uint cbInQue;
public readonly uint cbOutQue;
}
[StructLayout(LayoutKind.Sequential)]
private struct Dcb
{
public readonly uint DCBlength;
public readonly uint BaudRate;
public uint Flags;
public readonly ushort wReserved;
public readonly ushort XonLim;
public readonly ushort XoffLim;
public readonly byte ByteSize;
public readonly byte Parity;
public readonly byte StopBits;
public readonly byte XonChar;
public readonly byte XoffChar;
public readonly byte ErrorChar;
public readonly byte EofChar;
public readonly byte EvtChar;
public readonly ushort wReserved1;
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int FormatMessage(int dwFlags, HandleRef lpSource, int dwMessageId, int dwLanguageId, StringBuilder lpBuffer, int nSize, IntPtr arguments);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool GetCommState(SafeFileHandle hFile, ref Dcb lpDcb);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool SetCommState(SafeFileHandle hFile, ref Dcb lpDcb);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool ClearCommError(SafeFileHandle hFile, ref int lpErrors, ref Comstat lpStat);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr securityAttrs, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern int GetFileType(SafeFileHandle hFile);
private const int DcbFlagAbortOnError = 14;
private const int CommStateRetries = 10;
public static void Initialize(string portName)
{
const int dwFlagsAndAttributes = 0x40000000;
const int dwAccess = unchecked((int)0xC0000000);
if ((portName == null) || !portName.StartsWith("COM", StringComparison.OrdinalIgnoreCase))
throw new ArgumentException(Messages.InvalidSerialPort, nameof(portName));
SafeFileHandle fileHandle = CreateFile(@"\\.\" + portName, dwAccess, 0, IntPtr.Zero, 3, dwFlagsAndAttributes, IntPtr.Zero);
if (fileHandle.IsInvalid)
ThrowIoException();
try
{
int fileType = GetFileType(fileHandle);
if ((fileType != 2) && (fileType != 0))
throw new ArgumentException(Messages.InvalidSerialPort, nameof(portName));
var dcb = new Dcb();
MarshalCommState(fileHandle, () => GetCommState(fileHandle, ref dcb));
dcb.Flags &= ~(1u << DcbFlagAbortOnError);
MarshalCommState(fileHandle, () => SetCommState(fileHandle, ref dcb));
}
finally
{
fileHandle.Close();
}
}
private static void MarshalCommState(SafeFileHandle handle, Func performCommState)
{
int commErrors = 0;
var comStat = new Comstat();
for (int i = 0; i < CommStateRetries; i++)
{
if (!ClearCommError(handle, ref commErrors, ref comStat))
ThrowIoException();
if (performCommState())
return;
if (i == CommStateRetries - 1)
ThrowIoException();
}
}
private static void ThrowIoException()
{
int errorCode = Marshal.GetLastWin32Error();
var lpBuffer = new StringBuilder(0x200);
string errorMessage = (FormatMessage(0x3200, new HandleRef(null, IntPtr.Zero), errorCode, 0, lpBuffer, lpBuffer.Capacity, IntPtr.Zero) != 0)
? lpBuffer.ToString()
: $"0x{errorCode:X} - Unknown error";
throw new IOException(errorMessage, (int)(0x80070000 | (uint)errorCode));
}
}
}
================================================
FILE: Solid.Arduino/Solid.Arduino.csproj
================================================
netstandard2.1SolidSoils.Arduino.ClientHenk van BoeijenSolid Soils SolutionsClient library built on .NET Standard 2.1, providing an easy way to interact with Arduino boards.
The library implements the serial ASCII, Firmata and I2C protocols, which can be used simultaneously.Copyright 2013-2021 Solid Soils Solutionstruehttps://github.com/SolidSoils/ArduinoArduino Firmata I2C serialUpgrade to .NET Standard 2.1
Documentation fix & cleanup
Upgrade to .NET Standard 2.0
Fixed compatibility with Firmata 2.6https://github.com/SolidSoils/Arduinohttps://secure.gravatar.com/avatar/112117ab5b25236518383750d7c82b27SolidSoils4ArduinoSolid Soils for Arduino1.1.1BSD-2-Clause1.1.1.01.1.1.01.1.1falseC:\Data\Git\Arduino\Solid.Arduino\Solid.Arduino.xmlD:\Data\repos\Arduino\Solid.Arduino\Solid.Arduino.xmltrue
<_Parameter1>Solid.Arduino.Test
TrueTrueMessages.resxResXFileCodeGeneratorMessages.Designer.cs
================================================
FILE: Solid.Arduino/Solid.Arduino.xml
================================================
Solid.Arduino
Represents an active layer for serial communication with an Arduino board.
This class supports a few common protocols used for communicating with Arduino boards.
The protocols can be used simultaneous and independently of each other.
Official Arduino websiteSolidSoils4Arduino project on GitHub
var connection = new SerialConnection("COM3", SerialBaudRate.Bps_57600);
var session = new ArduinoSession(connection, timeOut: 250);
// Cast to interface done, just for the sake of this demo.
IFirmataProtocol firmata = (IFirmataProtocol)session;
Firmware firm = firmata.GetFirmware();
Console.WriteLine("Firmware: {0} {1}.{2}", firm.Name, firm.MajorVersion, firm.MinorVersion);
ProtocolVersion version = firmata.GetProtocolVersion();
Console.WriteLine("Protocol version: {0}.{1}", version.Major, version.Minor);
BoardCapability caps = firmata.GetBoardCapability();
Console.WriteLine("Board Capabilities:");
foreach (var pincap in caps.PinCapabilities)
{
Console.WriteLine("Pin {0}: Input: {1}, Output: {2}, Analog: {3}, Analog-Res: {4}, PWM: {5}, PWM-Res: {6}, Servo: {7}, Servo-Res: {8}",
pincap.PinNumber,
pincap.DigitalInput,
pincap.DigitalOutput,
pincap.Analog,
pincap.AnalogResolution,
pincap.Pwm,
pincap.PwmResolution,
pincap.Servo,
pincap.ServoResolution);
}
Console.WriteLine();
Console.ReadLine();
Initializes a new instance of the class.
The serial port connection
connection
Initializes a new instance of the class.
The serial port connection
The response time out in milliseconds
connectiontimeOut
Gets or sets the number of milliseconds before a time-out occurs when a read operation does not finish.
The default is a value (-1).
Closes and reopens the underlying connection and clears all buffers and queues.
The value of this property is mapped to the property of the
connection the instance is relying on.
Please note:
The Firmata specification states that the I2C_READ_STOP message
should only stop the specified query. However, the current Firmata.h implementation
stops all registered queries.
Closes the underlying connection.
Event handler processing data bytes received on the serial port.
Represents a serial port connection, supporting Mono.
The official Mono project site
Initializes a new instance of class using the highest serial port available at 115,200 bits per second.
Initializes a new instance of class on the given serial port and at the given baud rate.
The port name (e.g. 'COM3')
The baud rate
Represents a system serial port, supporting .NET and Mono.
This class is a workaround for Mono's implementation of event .
Copyright 2013 Antanas Veiverys antanas.veiverys.com
Represents a mapping between a MIDI channel and a physical pin number.
Gets the MIDI channel number (0 - 15).
Gets the board's pin number (0 - 127).
Represents the analog level read from or set to an analog pin.
Gets the MIDI channel number (0 - 15).
The mapping of analog pins to channel numbers can be retrieved using the method.
Gets the analog level.
Represents a summary of mappings between MIDI channels and physical pin numbers.
Gets the channel mapping array of the board's analog pins.
Represents a summary of pinmode capabilities supported by an Arduino board.
Gets the capability array of the board's pins.
Provides extension methods for arrays.
Converts a array holding binary coded digits to a readable string.
The binary coded digit bytes
Value indicating if the first nibble contains the least significant part
A string containing numeric dataThe array contains one or more non-BCD bytes.
Represents the pin states of a digital port.
Gets the digital port number.
Gets the bit-pattern value of the digital port.
Gets a value indicating if a pin is set (1 or 'high').
The 0-based pin number
true when the pin has a binary 1 value, otherwise false
Represents a Firmata message received from an Arduino or Arduino compatible system.
Initializes a new instance.
The type of message to be created.
Initializes a new instance.
Initializes a new instance.
Gets the specific value delivered by the message.
Gets the type enumeration of the message.
Gets the time of the delivered message.
Indicates the type of a Firmata Message.
Event arguments passed to a type event.
Gets the received message.
Contains event data for a and type events.
Type of the event data
This class is primarily implemented by the and events.
Gets the received message.
Identifies the Arduino board's firmware.
Gets the major version number.
Gets the minor version number.
Gets the name of the board's firmware.
Signature of event handlers capable of processing Firmata messages.
The object raising the event
Event arguments holding a
Signature of event handlers capable of processing analog I/O messages.
The object raising the event
Event arguments holding a
Signature of event handlers capable of processing digital I/O messages.
The object raising the event
Event arguments holding a
The modes a pin can be in or can be set to.
Defines a comprehensive set of members supporting the Firmata Protocol.
Currently version 2.3 is supported.
Firmata project on GitHubFirmata protocol detailsFirmata reference for Arduino
Event, raised for every SysEx (0xF0) and ProtocolVersion (0xF9) message not handled by an 's Get method.
When e.g. method is invoked, the party system's response message raises this event.
However, when method or is invoked, the response is returned
to the respective method and event is not raised.
This event is not raised for either analog or digital I/O messages.
Event, raised when an analog state message (command 0xE0) is received.
The frequency at which analog state messages are being sent by the party system can be set with method .
Event, raised when a digital I/O message (command 0x90) is received.
Please note that the StandardFirmata implementation for Arduino only sends updates of digital port states if necessary.
When none of a port's digital input pins have changed state since a previous polling cycle, no Firmata.sendDigitalPort message
is sent.
Also, calling method does not guarantee this event will receive a (first) Firmata.sendDigitalPort message.
Use method or inquiring the current pin states.
Creates an observable object tracking messages.
An interface
Creates an observable object tracking messages for a specific channel.
The channel to track
An interface
Creates an observable object tracking messages.
An interface
Creates an observable object tracking messages for a specific port.
The port to track
An interface
Sends a message string.
The message string
Enables or disables analog sampling reporting.
The channel attached to the analog pin
True if enabled, otherwise false
When enabled, the party system is expected to return analog I/O messages (0xE0)
for the given channel. The frequency at which these messages are returned can
be controlled by method .
Sets the digital output pins of a given port LOW or HIGH.
The 0-based port number
Binary value for the port's pins (0 to 7)
A binary 1 sets the digital output pin HIGH (+5 or +3.3 volts).
A binary 0 sets the digital output pin LOW.
The Arduino operates with 8-bit ports, so only bits 0 to 7 of the pins parameter are mapped.
Higher bits are ignored.
For port 0 bit 2 maps to the Arduino Uno's pin 2.
For port 1 bit 2 maps to pin 10.
The complete mapping of port 1 of the Arduino Uno looks like this:
bit 0: pin 8bit 1: pin 9bit 2: pin 10bit 3: pin 11bit 4: pin 12bit 5: pin 13bit 6: not mappedbit 7: not mapped
Enables or disables digital input pin reporting for the given port.
The number of the port
true if enabled, otherwise false
When enabled, the party system is expected to return digital I/O messages (0x90)
for the given port.
Note: as for Firmata version 2.3 digital I/O messages are only returned when
at least one digital input pin's state has changed from high to low or vice versa.
Sets a pin's mode (digital input/digital output/analog/PWM/servo etc.).
The number of the pin
The pin's mode
Sets the frequency at which analog samples must be reported.
The sampling interval in milliseconds
Sets an analog value on a PWM or Servo enabled analog output pin.
The pin number.
The value
Sets a HI or LO value on a digital output pin.
The pin number
The value (false = Low, true = High)
Sends a reset message to the party system.
Requests the party system to send a protocol version message.
The party system is expected to return a single protocol version message (0xF9).
This message triggers the event. The protocol version
is passed in the in a object.
Gets the protocol version implemented on the party system.
The implemented protocol version
Asynchronously gets the protocol version implemented on the party system.
The implemented protocol version
Requests the party system to send a firmware message.
The party system is expected to return a single SYSEX REPORT_FIRMWARE message.
This message triggers the event. The firmware signature
is passed in the in a object.
Gets the firmware signature of the party system.
The firmware signature
Asynchronously gets the firmware signature of the party system.
The firmware signature
Requests the party system to send a summary of its capabilities.
The party system is expected to return a single SYSEX CAPABILITY_RESPONSE message.
This message triggers the event. The capabilities
are passed in the in a object.
Gets a summary of the party system's capabilities.
The system's capabilities
Asynchronously gets a summary of the party system's capabilities.
The system's capabilities
Requests the party system to send the channel-to-pin mappings of its analog lines.
The party system is expected to return a single SYSEX ANALOG_MAPPING_RESPONSE message.
This message triggers the event. The analog mappings are
passed in the in a object.
Gets the channel-to-pin mappings of the party system's analog lines.
The channel-to-pin mappings
Asynchronously gets the channel-to-pin mappings of the party system's analog lines.
The channel-to-pin mappings
Requests the party system to send the state of a given pin.
The pin number
The party system is expected to return a single SYSEX PINSTATE_RESPONSE message.
This message triggers the event. The pin state
is passed in the in a object.
Gets a pin's mode (digital input/output, analog etc.) and actual value.
The pin number
The pin's state
Asynchronously gets a pin's mode (digital input/output, analog etc.) and actual value.
The pin number
The pin's state
Contains information about the capabilities of a pin.
Gets the 0-based number of the pin.
Gets a value indicating if the pin can be in digital input mode.
Gets a value indicating if the pin can be in digital output mode.
Gets a value indicating if it is an analog pin.
Gets a value indicating if the pin supports pulse width modulation.
Gets a value indicating if the pin supports servo motor control.
Gets the bit resolution for analog pins.
Gets the bit resolution for PWM enabled pins.
Gets the bit resolution for servo enabled pins.
Gets a value indicating if it is an I2c pin.
Gets a value indicating if it is an OneWire pin.
Gets a value indicating if it is a Stepper Control pin.
Gets a value indicating if it is an encoder pin.
Gets a value indicating if it is a serial pin.
Gets a value indicating if it is an input pullup pin.
Gets the maximum number of steps if it is a Stepper Control pin.
Contains information about a pin's state.
The 0-based pin number
Gets pin's operating mode.
Gets the value of the pin.
For analog pins the value is 0 or a positive number. For digital pins a low is represented by 0 and a high is respresented by 1.
Represents the Firmata communication protocol version.
Gets or sets the major version number.
Gets or sets the minor version number.
Defines Servo control related members of the Firmata protocol.
This interface is separated from the interface, in order to
protect the latter against feature bloat.
Configures the minimum and maximum pulse length for a servo pin.
The pin number
Minimum pulse length
Maximum pulse length
Represents a string exchanged with the Firmata SYSEX STRING_DATA command.
Gets or sets the string.
SysEx message command bytes
User defined ID 0x01
User defined ID 0x02
User defined ID 0x03
User defined ID 0x04
User defined ID 0x05
User defined ID 0x06
User defined ID 0x07
User defined ID 0x08
User defined ID 0x09
User defined ID 0x0A
User defined ID 0x0B
User defined ID 0x0C
User defined ID 0x0D
User defined ID 0x0E
User defined ID 0x0F
Analog mapping query
Analog mapping response
Capability query
Capability response
Pin state query
Pin state response
Extended analog
String data
I2C request
I2C reply
Report firmware
Sampling interval
SysEx not-realtime
SysEx realtime
Event arguments passed to a type event.
Gets the I2C message value being received.
Container for an I2C message
Gets or sets the memory address.
Gets or sets the register number.
Gets or sets the binary data.
Signature of event handlers capable of processing I2C_REPLY messages.
The object raising the event
Event arguments holding an
Defines a comprehensive set of members supporting the I2C Protocol.
I2C bus website by telos Systementwicklung GmbHArduino Wire referenceI2C Scanner sample sketch for Arduino
Event, raised for every SYSEX I2C message not handled by an 's Get method.
When e.g. methods and are invoked,
the party system's response messages raise this event.
However, when method or is invoked,
the response received is returned to the method that issued the command and event is not raised.
Creates an observable object tracking messages.
An interface
Sets the frequency at which data is read in the continuous mode.
The interval, expressed in microseconds
Writes an arbitrary array of bytes to the given memory address.
The slave's target address
The data array
Requests the party system to send bytes read from the given memory address.
The slave's memory address
Number of bytes to read
The party system is expected to return a single I2C_REPLY message.
This message triggers the event. The data
are passed in the in an object.
Requests the party system to send bytes read from the given memory address and register.
The slave's memory address
The slave's register
Number of bytes to read
Requests the party system to repeatedly send bytes read from the given memory address.
The slave's address
Number of bytes to read
The party system is expected to return a continuous stream of I2C_REPLY messages at
an interval which can be set using the method.
Received I2C_REPLY messages trigger the event. The data
are served in the 's Value property as an object.
The party system can be stopped sending I2C_REPLY messages by issuing a command.
Requests the party system to repeatedly send bytes read from the given memory address and register.
The slave's memory address
The slave's register
Number of bytes to read
Commands the party system to stop sending I2C_REPLY messages.
Gets byte data from the party system, read from the given memory address.
The slave's memory address
Number of bytes to read
An object holding the data read
Asynchronously gets byte data from the party system, read from the given memory address.
The slave's memory address
Number of bytes to read
An awaitable holding the data read
Gets byte data from the party system, read from the given memory address and register.
The slave's memory address and register
The slave's register
Number of bytes to read
An object holding the data read
Asynchronously gets byte data from the party system, read from the given memory address and register.
The slave's memory address
The slave's register
Number of bytes to read
An awaitable holding the data read
Defines a serial port connection.
Serial reference for Arduino
Represents the method that will handle the data received event of a object.
Gets a value indicating the open or closed status of the object.
Gets or sets the value used to interpret the end of strings received and sent
using and methods.
The default is a line feed, ().
Gets the number of bytes of data in the receive buffer.
Opens the connection.
Closes the connection.
Reads a byte from the underlying serial input data stream.
A byte value
Writes a string to the serial output data stream.
A string to be written
Writes a specified number of bytes to the serial output stream using data from a byte array.
The byte array that contains the data to write
The zero-based byte offset in the array at which to begin copying bytes
The number of bytes to write
Writes the specified string and the value to the serial output stream.
The string to write
Signature of event handlers capable of processing received strings.
The object raising the event
Event arguments holding a message
Defines members for sending and receiving ASCII string messages.
Event, raised for every ASCII stringmessage not handled by an 's
Read, ReadAsync, ReadLine, ReadLineAsync, ReadTo or ReadToAsync method
Any spontaneous received string message, terminated with a newline or eof character raises this event.
Creates an observable object tracking received ASCII messages.
An interface
Gets or sets the value used to interpret the end of strings received and sent.
Writes a string to the serial output data stream.
A string to be written
Writes the specified string and the value to the serial output stream.
The string to write
Reads a string up to the next character.
The string read
Reads a string asynchronous up to the next character.
An awaitable returning the string read
Reads a specified number of characters.
The number of characters to be read (default is 1)
The string read
Reads a specified number of characters asynchronous.
The number of characters to be read (default is 1)
An awaitable returning the string read
Reads a string up to the first terminating character.
The character identifying the end of the string
The string read
Reads a string asynchronous up to the first terminating character.
The character identifying the end of the string
An awaitable returning the string read
A strongly-typed resource class, for looking up localized strings, etc.
Returns the cached ResourceManager instance used by this class.
Overrides the current thread's CurrentUICulture property for all
resource lookups using this strongly typed resource class.
Looks up a localized string similar to Cannot convert non-BCD data..
Looks up a localized string similar to Channel number must be between 0 and 15..
Looks up a localized string similar to String must contain digits only..
Looks up a localized string similar to Address must be between 0 and 1,023..
Looks up a localized string similar to Interval must be between 0 and 16,383 milliseconds..
Looks up a localized string similar to Maximum pulse width must be between 0 and 16,383 milliseconds..
Looks up a localized string similar to Minimum pulse width is greater than maximum pulse width..
Looks up a localized string similar to Minimum pulse width must be between 0 and 16,383 milliseconds..
Looks up a localized string similar to Value cannot be negative..
Looks up a localized string similar to String argument can not be null or empty..
Looks up a localized string similar to Pin number must be between 0 and 127..
Looks up a localized string similar to Pin must be between 0 and 7..
Looks up a localized string similar to Port number must be between 0 and 15..
Looks up a localized string similar to Value must be greater than zero..
Looks up a localized string similar to Sampling interval must be between 0 and 16,383 milliseconds..
Looks up a localized string similar to Value must be between 0 and 16,383..
Looks up a localized string similar to Value must be betwen 0 and 255..
Looks up a localized string similar to Pin is not supported..
Looks up a localized string similar to Invalid serial port.
Looks up a localized string similar to Commandbyte 0x{0:X} is not implemented..
Looks up a localized string similar to Response message code 0x{0:X} is not supported..
Looks up a localized string similar to The command parsing buffer is full..
Looks up a localized string similar to Received message queue is full..
Looks up a localized string similar to The received strings buffer is full..
Looks up a localized string similar to Wait condition for {0} message timed out..
Looks up a localized string similar to Wait condition for string read in {0} mode timed out..
Enumeration of common baud rates, supported by Arduino boards
Represents a serial port connection.
Initializes a new instance of class using the highest COM-port available at 115,200 bits per second.
Initializes a new instance of class on the given serial port and at the given baud rate.
The port name (e.g. 'COM3')
The baud rate
Finds a serial connection to a device supporting the Firmata protocol.
A instance or null if no connection is found
This method searches all available serial ports until it finds a working serial connection.
For every available serial port an attempt is made to open a connection at a range of common baudrates.
The connection is tested by issueing an command.
(I.e. a Firmata SysEx Firmware query (0xF0 0x79 0xF7).)
The connected device is expected to respond by sending the version number of the supported protocol.
When a major version of 2 or higher is received, the connection is regarded to be valid.
Query Firmware Name and Version
Finds a serial connection to a device supporting plain serial communications.
The query text used to inquire the connection
The reply text the connected device is expected to respond with
A instance or null if no connection is found
This method searches all available serial ports until it finds a working serial connection.
For every available serial port an attempt is made to open a connection at a range of common baudrates.
The connection is tested by sending the query string passed to this method.
The connected device is expected to respond by sending the reply string passed to this method.
When the string received is equal to the expected reply string, the connection is regarded to be valid.
The Arduino sketch below can be used to demonstrate this method.
Upload the sketch to your Arduino device.
char query[] = "Hello?";
char reply[] = "Arduino!";
void setup()
{
Serial.begin(9600);
while (!Serial) {}
}
void loop()
{
if (Serial.find(query))
{
Serial.println(reply);
}
else
{
Serial.println("Listening...");
Serial.flush();
}
delay(25);
}
SerialPort IOException Workaround
Event arguments passed to a type event.
Gets the string value being received.
Provides extension methods for objects.
Converts the argument string into its binary-coded decimal (BCD) representation, e.g.
"1234" -> { 0x12, 0x34 } (for Big Endian byte order)
"1234" -> { 0x43, 0x21 } (for Little Endian byte order)
True if the byte order is "little end first (leftmost)".
String representation of BCD bytes.
Byte array representation of the string as BCD.Thrown if the argument string isn't entirely made up of BCD pairs.
Converts a to a 14 bit bigendian array.
The string being converted
A array.
Every character in the string is converted into two 7-bit bytes, starting with the most significant byte.
================================================
FILE: Solid.Arduino/StringEventArgs.cs
================================================
namespace Solid.Arduino
{
///
/// Event arguments passed to a type event.
///
///
///
public class StringEventArgs
{
private readonly string _text;
internal StringEventArgs(string text)
{
_text = text;
}
///
/// Gets the string value being received.
///
public string Text => _text;
}
}
================================================
FILE: Solid.Arduino/StringExtensions.cs
================================================
using System;
using System.Linq;
namespace Solid.Arduino
{
///
/// Provides extension methods for objects.
///
public static class StringExtensions
{
///
/// Converts the argument string into its binary-coded decimal (BCD) representation, e.g.
/// "1234" -> { 0x12, 0x34 } (for Big Endian byte order)
/// "1234" -> { 0x43, 0x21 } (for Little Endian byte order)
///
/// True if the byte order is "little end first (leftmost)".
/// String representation of BCD bytes.
/// Byte array representation of the string as BCD.
/// Thrown if the argument string isn't entirely made up of BCD pairs.
public static byte[] ToBinaryCodedDecimal(this string o, bool isLittleEndian = false)
{
if (o == null)
throw new ArgumentNullException();
if (o.Length == 0)
return Array.Empty();
if (o.Length % 2 == 1)
o = "0" + o;
char[] chars = o.ToCharArray();
if (!chars.All(char.IsDigit))
throw new ArgumentException(Messages.ArgumentEx_DigitStringOnly);
byte[] bytes = new byte[o.Length >> 1];
if (isLittleEndian)
{
int byteIndex = bytes.Length - 1;
for (int x = 0; x < o.Length; x += 2)
{
bytes[byteIndex--] = (byte)(((Convert.ToInt32(chars[x + 1]) - 48) << 4) | (Convert.ToInt32(chars[x]) - 48));
}
}
else
{
int byteIndex = 0;
for (int x = 0; x < o.Length; x += 2)
{
bytes[byteIndex++] = (byte)(((Convert.ToInt32(chars[x]) - 48) << 4) | (Convert.ToInt32(chars[x + 1]) - 48));
}
}
return bytes;
}
///
/// Converts a to a 14 bit bigendian array.
///
/// The string being converted
/// A array.
///
/// Every character in the string is converted into two 7-bit bytes, starting with the most significant byte.
///
public static byte[] To14BitIso(this string o)
{
if (o == null)
throw new ArgumentNullException();
if (o.Length == 0)
return Array.Empty();
byte[] dataBytes = new byte[o.Length * 2];
for (int x = 0; x < o.Length; x++)
{
short c = Convert.ToInt16(o[x]);
dataBytes[x * 2] = (byte)(c & 0x7F);
dataBytes[x * 2 + 1] = (byte)((c >> 7) & 0x7F);
}
return dataBytes;
}
}
}
================================================
FILE: Solid.Arduino.IntegrationTest/HC06-Bluetooth.ino
================================================
/*
The HC-06 is a slave-only Bluetooth module.
It can only be programmed through its serial connection.
This sketch shows how this can be done using SoftwareSerial.
Connections:
HC-06 pin Arduino pin
--------- -----------
VCC +3.3 Volt
GND GND
TXD Pin 10
RXD Pin 11
NOTE: make sure to cut the voltage of RXD (5 V) to 3.3 V.
The HC-06 can be configured using AT-commands. The commands need to be in upper case
and must not be terminated with cr/lf. Commands are terminated using a 600 ms delay.
The following AT-commands are supported:
AT-command Description
---------- ----------------------------------
AT+BAUD1 Sets serial comm. to 1,200 Baud
AT+BAUD2 Sets serial comm. to 2,400 Baud
AT+BAUD3 Sets serial comm. to 4,800 Baud
AT+BAUD4 Sets serial comm. to 9,600 Baud
AT+BAUD5 Sets serial comm. to 19,200 Baud
AT+BAUD6 Sets serial comm. to 38,600 Baud
AT+BAUD7 Sets serial comm. to 57,600 Baud
AT+BAUD8 Sets serial comm. to 115,200 Baud
AT+PN Sets serial comm. protocol to Parity = None
AT+PE Sets serial comm. protocol to Parity = Even
AT+PO Sets serial comm. protocol to Parity = Odd
AT+PIN1234 Sets connection password to 1234
AT+NAMExxx Sets Bluetooth device name to xxx
AT+VERSION Returns the device's version (e.g. "linvorV1")
By default the HC-06 operates at 9,600 baud and no parity. The default PIN is 1234.
Upload this sketch to your Arduino and use the Arduino IDE's serial monitor to issue AT-commands.
*/
#include
SoftwareSerial btSerial(10, 11); // RX, TX
void setup() {
Serial.begin(57600);
btSerial.begin(9600);
delay(5000);
// btSerial.print("AT+NAMESolidSoils");
// delay(600);
Serial.println("Uno initialized.");
}
void loop() {
while (btSerial.available() > 0) {
char c = btSerial.read();
Serial.print(c);
}
while (Serial.available() > 0) {
char c = Serial.read();
btSerial.print(c);
Serial.print(c);
}
delay(10);
}
================================================
FILE: Solid.Arduino.IntegrationTest/SerialConnectionTester.cs
================================================
using System.IO.Ports;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Solid.Arduino.IntegrationTest
{
///
/// Performs tests with a connected Arduino device.
///
[TestClass]
public class SerialConnectionTester
{
///
/// Finds a live serial connection by issuing a Firmata SysEx Firmware query (0xF0 0x79 0xF7).
///
///
/// Requires sketch StandardFirmata.ino to run on the connected device.
///
[TestMethod]
public void FindSerialConnection_FirmataEnabled()
{
if (SerialPort.GetPortNames().Length == 0)
{
// No serial ports available.
return;
}
using (var arduinoConnection = SerialConnection.Find())
{
Assert.IsNotNull(arduinoConnection);
}
}
///
/// Finds a live serial connection by issuing a query string.
///
///
/// Requires sketch SerialReply.ino to run on the connected device.
/// (This sketch can be found in this project.)
///
[TestMethod]
public void FindSerialConnection_Serial()
{
if (SerialPort.GetPortNames().Length == 0)
{
// No serial ports available.
return;
}
using (var arduinoConnection = SerialConnection.Find("Hello?", "Arduino!"))
{
Assert.IsNotNull(arduinoConnection);
}
}
}
}
================================================
FILE: Solid.Arduino.IntegrationTest/SerialReply.ino
================================================
/*
This example is intended to demonstrate the SolidSoils4Arduino library's
SerialConnection.FindSerialConnection(string query, string expectedReply) method.
Created February 11th, 2015
by Henk van Boeijen. (info@solidsoils.nl)
See https://github.com/SolidSoils/Arduino
This example code is in the public domain.
*/
#include
char query[] = "Hello?";
char reply[] = "Arduino!";
void setup()
{
Serial.begin(9600);
while (!Serial) {}
}
void loop()
{
if (Serial.find(query))
{
Serial.println(reply);
}
else
{
Serial.println("Listening...");
Serial.flush();
}
delay(25);
}
================================================
FILE: Solid.Arduino.IntegrationTest/SocketConnectionTester.cs
================================================
using System.Net;
using System.Net.Sockets;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Solid.Arduino.IntegrationTest
{
///
/// Summary description for SocketConnectionTester
///
[TestClass]
public class SocketConnectionTester
{
private TestContext testContextInstance;
///
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
#region Additional test attributes
//
// You can use the following additional attributes as you write your tests:
//
// Use ClassInitialize to run code before running the first test in the class
// [ClassInitialize()]
// public static void MyClassInitialize(TestContext testContext) { }
//
// Use ClassCleanup to run code after all tests in a class have run
// [ClassCleanup()]
// public static void MyClassCleanup() { }
//
// Use TestInitialize to run code before running each test
// [TestInitialize()]
// public void MyTestInitialize() { }
//
// Use TestCleanup to run code after each test has run
// [TestCleanup()]
// public void MyTestCleanup() { }
//
#endregion
[TestMethod]
public void TestMethod1()
{
var permission = new SocketPermission(NetworkAccess.Accept, TransportType.Tcp, "", SocketPermission.AllPorts);
IPHostEntry ipHost = Dns.GetHostEntry("");
var endpoint = new IPEndPoint(ipHost.AddressList[0], 4510);
var socket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(endpoint);
}
}
}
================================================
FILE: Solid.Arduino.IntegrationTest/Solid.Arduino.IntegrationTest.csproj
================================================
netcoreapp3.1false
================================================
FILE: Solid.Arduino.Run/Program.cs
================================================
using System;
using System.Threading;
using Solid.Arduino.Firmata;
using Solid.Arduino.I2C;
using System.Linq;
namespace Solid.Arduino.Run
{
class Program
{
static void Main(string[] args)
{
// DisplayPortCapabilities();
ISerialConnection connection = new EnhancedSerialConnection("COM4", SerialBaudRate.Bps_57600);
using (var session = new ArduinoSession(connection))
PerformBasicTest(session);
Console.WriteLine("Press a key");
Console.ReadKey(true);
}
private static ISerialConnection GetConnection()
{
Console.WriteLine("Searching Arduino connection...");
ISerialConnection connection = EnhancedSerialConnection.Find();
if (connection == null)
Console.WriteLine("No connection found. Make shure your Arduino board is attached to a USB port.");
else
Console.WriteLine($"Connected to port {connection.PortName} at {connection.BaudRate} Baud.");
return connection;
}
private static void PerformBasicTest(IFirmataProtocol session)
{
var firmware = session.GetFirmware();
Console.WriteLine($"Firmware: {firmware.Name} version {firmware.MajorVersion}.{firmware.MinorVersion}");
var protocolVersion = session.GetProtocolVersion();
Console.WriteLine($"Firmata protocol version {protocolVersion.Major}.{protocolVersion.Minor}");
session.SetDigitalPinMode(10, PinMode.DigitalOutput);
session.SetDigitalPin(10, true);
Console.WriteLine("Command sent: Light on (pin 10)");
Console.WriteLine("Press a key");
Console.ReadKey(true);
session.SetDigitalPin(10, false);
Console.WriteLine("Command sent: Light off");
}
private static void DisplayPortCapabilities()
{
using (var session = new ArduinoSession(new EnhancedSerialConnection("COM4", SerialBaudRate.Bps_57600)))
{
session.StringReceived += (object sender, StringEventArgs eventArgs) =>
{
Console.WriteLine(eventArgs.Text);
};
BoardCapability cap = session.GetBoardCapability();
Console.WriteLine();
Console.WriteLine("Board Capability:");
foreach (var pin in cap.Pins)
{
Console.WriteLine("Pin {0}: Input: {1}, Output: {2}, Analog: {3}, Analog-Res: {4}, PWM: {5}, PWM-Res: {6}, Servo: {7}, Servo-Res: {8}, Serial: {9}, Encoder: {10}, Input-pullup: {11}",
pin.PinNumber,
pin.DigitalInput,
pin.DigitalOutput,
pin.Analog,
pin.AnalogResolution,
pin.Pwm,
pin.PwmResolution,
pin.Servo,
pin.ServoResolution,
pin.Serial,
pin.Encoder,
pin.InputPullup);
}
}
}
private void TimeTest()
{
var session = new ArduinoSession(new SerialConnection("COM4", SerialBaudRate.Bps_57600)) {TimeOut = 1000};
session.MessageReceived += Session_OnMessageReceived;
var firmata = (II2CProtocol)session;
var x = firmata.GetI2CReply(0x68, 7);
Console.WriteLine();
Console.WriteLine("{0} bytes received.", x.Data.Length);
Console.WriteLine("Starting");
Console.WriteLine("Press a key to abort.");
Console.ReadKey(true);
session.Dispose();
}
void Session_OnMessageReceived(object sender, FirmataMessageEventArgs eventArgs)
{
string o;
switch (eventArgs.Value.Type)
{
case MessageType.StringData:
o = ((StringData)eventArgs.Value.Value).Text;
break;
default:
o = "?";
break;
}
Console.WriteLine("Message {0} received: {1}", eventArgs.Value.Type, o);
}
static void SimpelTest(ISerialConnection connection)
{
var session = new ArduinoSession(connection, timeOut: 2500);
IFirmataProtocol firmata = session;
firmata.AnalogStateReceived += Session_OnAnalogStateReceived;
firmata.DigitalStateReceived += Session_OnDigitalStateReceived;
Firmware firm = firmata.GetFirmware();
Console.WriteLine();
Console.WriteLine("Firmware: {0} {1}.{2}", firm.Name, firm.MajorVersion, firm.MinorVersion);
Console.WriteLine();
ProtocolVersion version = firmata.GetProtocolVersion();
Console.WriteLine();
Console.WriteLine("Protocol version: {0}.{1}", version.Major, version.Minor);
Console.WriteLine();
BoardCapability cap = firmata.GetBoardCapability();
Console.WriteLine();
Console.WriteLine("Board Capability:");
foreach (var pin in cap.Pins)
{
Console.WriteLine("Pin {0}: Input: {1}, Output: {2}, Analog: {3}, Analog-Res: {4}, PWM: {5}, PWM-Res: {6}, Servo: {7}, Servo-Res: {8}, Serial: {9}, Encoder: {10}, Input-pullup: {11}",
pin.PinNumber,
pin.DigitalInput,
pin.DigitalOutput,
pin.Analog,
pin.AnalogResolution,
pin.Pwm,
pin.PwmResolution,
pin.Servo,
pin.ServoResolution,
pin.Serial,
pin.Encoder,
pin.InputPullup);
}
Console.WriteLine();
var analogMapping = firmata.GetBoardAnalogMapping();
Console.WriteLine("Analog channel mappings:");
foreach (var mapping in analogMapping.PinMappings)
{
Console.WriteLine("Channel {0} is mapped to pin {1}.", mapping.Channel, mapping.PinNumber);
}
firmata.ResetBoard();
Console.WriteLine();
Console.WriteLine("Digital port states:");
foreach (var pincap in cap.Pins.Where(c => (c.DigitalInput || c.DigitalOutput) && !c.Analog))
{
var pinState = firmata.GetPinState(pincap.PinNumber);
Console.WriteLine("Pin {0}: Mode = {1}, Value = {2}", pincap.PinNumber, pinState.Mode, pinState.Value);
}
Console.WriteLine();
firmata.SetDigitalPort(0, 0x04);
firmata.SetDigitalPort(1, 0xff);
firmata.SetDigitalPinMode(10, PinMode.DigitalOutput);
firmata.SetDigitalPinMode(11, PinMode.ServoControl);
firmata.SetDigitalPin(11, 90);
Thread.Sleep(500);
int hi = 0;
for (int a = 0; a <= 179; a += 1)
{
firmata.SetDigitalPin(11, a);
Thread.Sleep(100);
firmata.SetDigitalPort(1, hi);
hi ^= 4;
Console.Write("{0};", a);
}
Console.WriteLine();
Console.WriteLine();
firmata.SetDigitalPinMode(6, PinMode.DigitalInput);
//firmata.SetDigitalPortState(2, 255);
//firmata.SetDigitalPortState(3, 255);
firmata.SetSamplingInterval(500);
firmata.SetAnalogReportMode(0, true);
Console.WriteLine("Setting digital report modes:");
firmata.SetDigitalReportMode(0, true);
firmata.SetDigitalReportMode(1, true);
firmata.SetDigitalReportMode(2, true);
Console.WriteLine();
foreach (var pinCap in cap.Pins.Where(c => (c.DigitalInput || c.DigitalOutput) && !c.Analog))
{
PinState state = firmata.GetPinState(pinCap.PinNumber);
Console.WriteLine("Digital {1} pin {0}: {2}", state.PinNumber, state.Mode, state.Value);
}
Console.WriteLine();
Console.ReadLine();
firmata.SetAnalogReportMode(0, false);
firmata.SetDigitalReportMode(0, false);
firmata.SetDigitalReportMode(1, false);
firmata.SetDigitalReportMode(2, false);
Console.WriteLine("Ready.");
}
static void Session_OnDigitalStateReceived(object sender, FirmataEventArgs eventArgs)
{
Console.WriteLine("Digital level of port {0}: {1}", eventArgs.Value.Port, eventArgs.Value.IsSet(6) ? 'X' : 'O');
}
static void Session_OnAnalogStateReceived(object sender, FirmataEventArgs eventArgs)
{
Console.WriteLine("Analog level of pin {0}: {1}", eventArgs.Value.Channel, eventArgs.Value.Level);
}
}
}
================================================
FILE: Solid.Arduino.Run/Solid.Arduino.Run.csproj
================================================
netcoreapp3.1Exe
================================================
FILE: Solid.Arduino.Test/ArduinoSessionTester.cs
================================================
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Solid.Arduino.Firmata;
namespace Solid.Arduino.Test
{
[TestClass]
public class ArduinoSessionTester
{
private readonly Queue _messagesReceived = new Queue();
[TestMethod]
public void CreateSessionWithClosedConnection()
{
var connection = new MockSerialConnection();
var session = new ArduinoSession(connection);
Assert.AreEqual(true, connection.IsOpen);
}
//[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ArduinoSession_Constructor_NullArgument()
{
var session = new ArduinoSession(null);
}
[TestMethod]
public void CreateSessionWithOpenConnection()
{
var connection = new MockSerialConnection();
connection.Open();
var session = new ArduinoSession(connection);
Assert.AreEqual(true, connection.IsOpen);
}
[TestMethod]
public void ClearSession()
{
var connection = new MockSerialConnection();
var session = new ArduinoSession(connection);
session.Clear();
session.Dispose();
}
[TestMethod]
public void TimeOutReached()
{
var connection = new MockSerialConnection();
var session = new ArduinoSession(connection);
session.TimeOut = 1;
// TODO: verder uitwerken.
}
[TestMethod]
public void TimeOut_GetAndSet()
{
var connection = new MockSerialConnection();
var session = new ArduinoSession(connection);
Assert.AreEqual(-1, session.TimeOut);
session.TimeOut = 7;
Assert.AreEqual(7, session.TimeOut);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void TimeOut_SetNegative()
{
var connection = new MockSerialConnection();
var session = new ArduinoSession(connection);
session.TimeOut = -2;
}
[TestMethod]
public void Dispose()
{
var connection = new MockSerialConnection();
var session = new ArduinoSession(connection);
session.Dispose();
}
[TestMethod]
public void Dispose_OnOpenConnection()
{
var connection = new MockSerialConnection();
connection.Open();
var session = new ArduinoSession(connection);
session.Dispose();
}
private void session_OnMessageReceived(object par_Sender, FirmataMessageEventArgs par_EventArgs)
{
_messagesReceived.Enqueue(par_EventArgs.Value);
}
}
}
================================================
FILE: Solid.Arduino.Test/ByteArrayExtensionsTester.cs
================================================
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Solid.Arduino.Firmata;
namespace Solid.Arduino.Test
{
///
/// Summary description for ByteArrayExtensionsTester
///
[TestClass]
public class ByteArrayExtensionsTester
{
private TestContext testContextInstance;
///
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
#region Additional test attributes
//
// You can use the following additional attributes as you write your tests:
//
// Use ClassInitialize to run code before running the first test in the class
// [ClassInitialize()]
// public static void MyClassInitialize(TestContext testContext) { }
//
// Use ClassCleanup to run code after all tests in a class have run
// [ClassCleanup()]
// public static void MyClassCleanup() { }
//
// Use TestInitialize to run code before running each test
// [TestInitialize()]
// public void MyTestInitialize() { }
//
// Use TestCleanup to run code after each test has run
// [TestCleanup()]
// public void MyTestCleanup() { }
//
#endregion
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ConvertBinaryCodedDecimalToString_NullData()
{
byte[] data = null;
data.ConvertBinaryCodedDecimalToString();
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ConvertBinaryCodedDecimalToString_NonDigitBytes()
{
byte[] data = new byte[] { 0x99, 0xA0, 0x0A };
data.ConvertBinaryCodedDecimalToString();
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ConvertBinaryCodedDecimalToString_LeastSignificantNotDigit()
{
byte[] data = new byte[] { 0x34, 0x5A, 0x98 };
data.ConvertBinaryCodedDecimalToString();
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ConvertBinaryCodedDecimalToString_MostSignificantNotDigit()
{
byte[] data = new byte[] { 0x34, 0x56, 0xE8 };
data.ConvertBinaryCodedDecimalToString();
}
[TestMethod]
public void ConvertBinaryCodedDecimalToString_NoData()
{
byte[] data = new byte[0];
Assert.AreEqual(0, data.ConvertBinaryCodedDecimalToString().Length);
}
[TestMethod]
public void ConvertBinaryCodedDecimalToString_1Byte()
{
byte[] data = new byte[]
{
0x12
};
Assert.AreEqual("12", data.ConvertBinaryCodedDecimalToString());
}
[TestMethod]
public void ConvertBinaryCodedDecimalToString_AllDigits()
{
byte[] data = new byte[]
{
0x12, 0x34, 0x56, 0x78, 0x90
};
Assert.AreEqual("1234567890", data.ConvertBinaryCodedDecimalToString());
}
[TestMethod]
public void ConvertBinaryCodedDecimalToString_AllDigits_LittleEndian()
{
byte[] data = new byte[]
{
0x12, 0x34, 0x56, 0x78, 0x90
};
Assert.AreEqual("0987654321", data.ConvertBinaryCodedDecimalToString(true));
}
}
}
================================================
FILE: Solid.Arduino.Test/DigitalPortStateTester.cs
================================================
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Solid.Arduino.Firmata;
namespace Solid.Arduino.Test
{
///
/// Performs unit tests for struct DigitalPortState.
///
[TestClass]
public class DigitalPortStateTester
{
public DigitalPortStateTester()
{
}
private TestContext _testContextInstance;
///
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///
public TestContext TestContext
{
get
{
return _testContextInstance;
}
set
{
_testContextInstance = value;
}
}
#region Additional test attributes
//
// You can use the following additional attributes as you write your tests:
//
// Use ClassInitialize to run code before running the first test in the class
// [ClassInitialize()]
// public static void MyClassInitialize(TestContext testContext) { }
//
// Use ClassCleanup to run code after all tests in a class have run
// [ClassCleanup()]
// public static void MyClassCleanup() { }
//
// Use TestInitialize to run code before running each test
// [TestInitialize()]
// public void MyTestInitialize() { }
//
// Use TestCleanup to run code after each test has run
// [TestCleanup()]
// public void MyTestCleanup() { }
//
#endregion
[TestMethod]
public void IsHigh_Pins_Is0()
{
var state = new DigitalPortState() { Pins = 0 };
Assert.AreEqual(false, state.IsSet(0));
Assert.AreEqual(false, state.IsSet(1));
Assert.AreEqual(false, state.IsSet(2));
Assert.AreEqual(false, state.IsSet(3));
Assert.AreEqual(false, state.IsSet(4));
Assert.AreEqual(false, state.IsSet(5));
Assert.AreEqual(false, state.IsSet(6));
Assert.AreEqual(false, state.IsSet(7));
}
[TestMethod]
public void IsHigh_Pins_Is255()
{
var state = new DigitalPortState() { Pins = 255 };
Assert.AreEqual(true, state.IsSet(0));
Assert.AreEqual(true, state.IsSet(1));
Assert.AreEqual(true, state.IsSet(2));
Assert.AreEqual(true, state.IsSet(3));
Assert.AreEqual(true, state.IsSet(4));
Assert.AreEqual(true, state.IsSet(5));
Assert.AreEqual(true, state.IsSet(6));
Assert.AreEqual(true, state.IsSet(7));
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void IsHigh_Pin_Argument_IsNegative()
{
var state = new DigitalPortState();
state.IsSet(-1);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void IsHigh_Pin_Argument_Is8()
{
var state = new DigitalPortState();
typeof(DigitalPortState).GetProperty("Pins").SetValue(state, 0);
state.IsSet(8);
}
}
}
================================================
FILE: Solid.Arduino.Test/EnhancedSerialConnectionTester.cs
================================================
using System;
using System.IO.Ports;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Solid.Arduino.Test
{
[TestClass]
public class EnhancedSerialConnectionTester
{
[TestMethod]
public void EnhancedSerialConnection_Constructor_WithoutParameters()
{
if (!AreSerialPortsAvailable())
{
Assert.ThrowsException(() => new SerialConnection());
return;
}
var connection = new EnhancedSerialConnection();
Assert.AreEqual(100, connection.ReadTimeout);
Assert.AreEqual(100, connection.WriteTimeout);
Assert.AreEqual(115200, connection.BaudRate);
}
[TestMethod]
public void EnhancedSerialConnection_Constructor_WithParameters()
{
if (!AreSerialPortsAvailable())
return;
var connection = new EnhancedSerialConnection("COM1", SerialBaudRate.Bps_115200);
Assert.AreEqual(100, connection.ReadTimeout);
Assert.AreEqual(100, connection.WriteTimeout);
Assert.AreEqual(115200, connection.BaudRate);
}
[TestMethod]
public void EnhancedSerialConnection_OpenAndClose()
{
if (!AreSerialPortsAvailable())
return;
var connection = new EnhancedSerialConnection();
connection.Open();
connection.Close();
}
[TestMethod]
public void EnhancedSerialConnection_OpenAndDoubleClose()
{
if (!AreSerialPortsAvailable())
return;
var connection = new EnhancedSerialConnection();
connection.Open();
connection.Close();
connection.Close();
}
private static bool AreSerialPortsAvailable()
{
return SerialPort.GetPortNames().Length > 0;
}
}
}
================================================
FILE: Solid.Arduino.Test/ExceptionMonitor.cs
================================================
using System;
using System.Runtime.ExceptionServices;
using System.Reflection;
namespace Solid.Arduino.Test
{
class ExceptionMonitor
{
public void Setup()
{
// Het FirstChanceException event gaat af als er een Exception optreedt in je applicatie.
// Dit gebeurt nog voordat foutafhandeling (catch) in actie heeft kunnen komen. Dit maakt
// deze event bij uitstek geschikt om te analyseren in welke mate een applicatie leunt op
// 'handled exceptions', wat een forse impact op de performance kan hebben.
AppDomain.CurrentDomain.FirstChanceException += FirstChanceHandler;
}
static void FirstChanceHandler(object o, FirstChanceExceptionEventArgs e)
{
MethodBase site = e.Exception.TargetSite;
Console.WriteLine("Thrown in module {0}, by {1}({2})", site.Module, site.DeclaringType, site.ToString());
Console.WriteLine("Stack trace:\n{0}", e.Exception.StackTrace);
}
}
}
================================================
FILE: Solid.Arduino.Test/I2cProtocolTester.cs
================================================
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Solid.Arduino.Firmata;
using Solid.Arduino.I2C;
namespace Solid.Arduino.Test
{
[TestClass]
public class I2CProtocolTester
{
private readonly Queue _messagesReceived = new Queue();
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetI2CReadInterval_Interval_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x78, 0x00, 0x00, 0xF7 });
session.SetI2CReadInterval(-1);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetI2CReadInterval_Interval_Is0x4000()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x78, 0x00, 0x00, 0xF7 });
session.SetI2CReadInterval(0x4000);
}
[TestMethod]
public void SetI2CReadInterval_Interval_Is0()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x78, 0x00, 0x00, 0xF7 });
session.SetI2CReadInterval(0);
}
[TestMethod]
public void SetI2CReadInterval_Interval_Is0x3FFF()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x78, 0x7F, 0x7F, 0xF7 });
session.SetI2CReadInterval(0x3FFF);
}
[TestMethod]
public void WriteI2C_SlaveAddress_Parameter_Is0()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x00, 0x00, 0x7F, 0x01, 0xF7);
session.WriteI2C(0, 255);
}
[TestMethod]
public void WriteI2C_SlaveAddress_Parameter_Is0x3FF()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x7F, 0x27, 0x7F, 0x01, 0xF7);
session.WriteI2C(0x3FF, 255);
}
[TestMethod]
public void WriteI2C_SlaveAddress_Parameter_Is127()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x7F, 0x00, 0x11, 0x00, 0x22, 0x00, 0x33, 0x00, 0x44, 0x00, 0x7F, 0x01, 0xF7);
session.WriteI2C(127, 0x11, 0x22, 0x33, 0x44, 0xFF);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void WriteI2C_SlaveAddress_Parameter_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x02, 0x21, 0xF7);
session.WriteI2C(-1);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void WriteI2C_SlaveAddress_Parameter_Is0x400()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x02, 0x21, 0xF7);
session.WriteI2C(0x400);
}
[TestMethod]
public void WriteI2C_Data_Parameter_IsNull()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x02, 0x21, 0xF7);
session.WriteI2C(130);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ReadI2COnce_SlaveAddress_Parameter_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x00, 0x08, 0x00, 0x00, 0xF7);
session.ReadI2COnce(-1, 1);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ReadI2COnce_SlaveAddress_Parameter_Is0x400()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x00, 0x08, 0x00, 0x00, 0xF7);
session.ReadI2COnce(0x400, 1);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ReadI2COnce_BytesToRead_Parameter_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x00, 0x08, 0x00, 0x00, 0xF7);
session.ReadI2COnce(0, -1);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ReadI2COnce_BytesToRead_Parameter_Is0x4000()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x00, 0x08, 0x00, 0x00, 0xF7);
session.ReadI2COnce(0, 0x4000);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ReadI2COnce_SlaveRegister_Parameter_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x00, 0x08, 0x00, 0x00, 0xF7);
session.ReadI2COnce(0, -1, 1);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ReadI2COnce_SlaveRegister_Parameter_Is0x4000()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x00, 0x08, 0x00, 0x00, 0xF7);
session.ReadI2COnce(0, 0x4000, 1);
}
[TestMethod]
public void ReadI2COnce_MinimumParameters()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x00, 0x08, 0x00, 0x00, 0xF7);
session.ReadI2COnce(0, 0);
}
[TestMethod]
public void ReadI2COnce_MaximumParameters()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x7F, 0x2F, 0x7F, 0x7F, 0xF7);
session.ReadI2COnce(0x3FF, 0x3FFF);
}
[TestMethod]
public void ReadI2COnce_MinimumParameters_WithSlaveRegister()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0xF7);
session.ReadI2COnce(0, 0, 0);
}
[TestMethod]
public void ReadI2COnce_MaximumParameters_WithSlaveRegister()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x7F, 0x2F, 0x7F, 0x7F, 0x7F, 0x7F, 0xF7);
session.ReadI2COnce(0x3FF, 0x3FFF, 0x3FFF);
}
[TestMethod]
public void ReadI2CContinous()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x45, 0x10, 0x7F, 0x01, 0xF7);
session.ReadI2CContinuous(0x45, 255);
}
[TestMethod]
public void ReadI2CContinous_WithSlaveRegister()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x45, 0x10, 0x07, 0x0C, 0x7F, 0x01, 0xF7);
session.ReadI2CContinuous(0x45, 0x0607, 255);
}
[TestMethod]
public void GetI2CReply()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x01, 0x08, 0x03, 0x00, 0xF7);
connection.EnqueueResponse(0xF0, 0x77, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0xF7);
I2CReply reply = session.GetI2CReply(1, 3);
Assert.AreEqual(1, reply.Address);
Assert.AreEqual(0, reply.Register);
Assert.AreEqual(3, reply.Data.Length);
Assert.AreEqual(5, reply.Data[0]);
Assert.AreEqual(6, reply.Data[1]);
Assert.AreEqual(7, reply.Data[2]);
}
[TestMethod]
public void GetI2CReply_BytesToRead_Parameter_Is0()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x01, 0x08, 0x00, 0x00, 0xF7);
connection.EnqueueResponse(0xF0, 0x77, 0x01, 0x00, 0x00, 0x00, 0xF7);
I2CReply reply = session.GetI2CReply(1, 0);
Assert.AreEqual(1, reply.Address);
Assert.AreEqual(0, reply.Register);
Assert.AreEqual(0, reply.Data.Length);
}
[TestMethod]
public void GetI2CReply_WithSlaveRegister()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x01, 0x08, 0x02, 0x00, 0x01, 0x00, 0xF7);
connection.EnqueueResponse(0xF0, 0x77, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0xF7);
I2CReply reply = session.GetI2CReply(1, 2, 1);
Assert.AreEqual(1, reply.Address);
Assert.AreEqual(2, reply.Register);
Assert.AreEqual(1, reply.Data.Length);
Assert.AreEqual(4, reply.Data[0]);
}
[TestMethod]
public void GetI2CReplyAsync()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x7F, 0x08, 0x02, 0x00, 0xF7);
connection.EnqueueResponse(0xF0, 0x77, 0x7F, 0x00, 0x00, 0x00, 0x7F, 0x01, 0x7E, 0x01, 0xF7);
I2CReply reply = session.GetI2CReplyAsync(127, 2).Result;
Assert.AreEqual(127, reply.Address);
Assert.AreEqual(0, reply.Register);
Assert.AreEqual(2, reply.Data.Length);
Assert.AreEqual(255, reply.Data[0]);
Assert.AreEqual(254, reply.Data[1]);
}
[TestMethod]
public void GetI2CReplyAsync_WithSlaveRegister()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x7F, 0x08, 0x04, 0x02, 0x02, 0x00, 0xF7);
connection.EnqueueResponse(0xF0, 0x77, 0x7F, 0x00, 0x04, 0x02, 0x7F, 0x01, 0x7E, 0x01, 0xF7);
I2CReply reply = session.GetI2CReplyAsync(127, 260, 2).Result;
Assert.AreEqual(127, reply.Address);
Assert.AreEqual(260, reply.Register);
Assert.AreEqual(2, reply.Data.Length);
Assert.AreEqual(255, reply.Data[0]);
Assert.AreEqual(254, reply.Data[1]);
}
[TestMethod]
public void I2CReplyReceived()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
int eventHits = 0;
session.I2CReplyReceived += (o, e) =>
{
Assert.AreEqual(1, e.Value.Address);
Assert.AreEqual(0, e.Value.Register);
Assert.AreEqual(3, e.Value.Data.Length);
Assert.AreEqual(5, e.Value.Data[0]);
Assert.AreEqual(6, e.Value.Data[1]);
Assert.AreEqual(7, e.Value.Data[2]);
eventHits++;
};
connection.EnqueueRequest(0xF0, 0x76, 0x01, 0x08, 0x03, 0x00, 0xF7);
connection.EnqueueResponse(0xF0, 0x77, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0xF7);
I2CReply reply = session.GetI2CReply(1, 3);
Assert.AreEqual(1, eventHits);
}
[TestMethod]
public void StopI2CReading()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x76, 0x00, 0x18, 0xF7);
session.StopI2CReading();
}
private II2CProtocol CreateFirmataSession(ISerialConnection connection, int timeout = -1)
{
var session = new ArduinoSession(connection);
session.TimeOut = timeout;
session.MessageReceived += (o, e) =>
{
_messagesReceived.Enqueue(e.Value);
};
return session;
}
}
}
================================================
FILE: Solid.Arduino.Test/IFirmataProtocolTester.cs
================================================
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Solid.Arduino.Firmata;
namespace Solid.Arduino.Test
{
[TestClass]
public class IFirmataProtocolTester
{
private readonly Queue _messagesReceived = new Queue();
[TestMethod]
public void ResetBoard()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xFF }, new byte[] { 0xF9, 2, 3 });
session.ResetBoard();
Assert.AreEqual(1, _messagesReceived.Count);
FirmataMessage message = _messagesReceived.Dequeue();
Assert.AreEqual(MessageType.ProtocolVersion, message.Type);
var version = (ProtocolVersion)message.Value;
Assert.AreEqual(2, version.Major);
Assert.AreEqual(3, version.Minor);
}
[TestMethod]
public void GetProtocolVersion()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF9 }, new byte[] { 0xF9, 2, 3 });
ProtocolVersion version = session.GetProtocolVersion();
Assert.AreEqual(2, version.Major);
Assert.AreEqual(3, version.Minor);
}
[TestMethod]
public void GetProtocolVersionAsync()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF9 }, new byte[] { 0xF9, 2, 3 });
ProtocolVersion version = session.GetProtocolVersionAsync().Result;
Assert.AreEqual(2, version.Major);
Assert.AreEqual(3, version.Minor);
Assert.AreEqual(1, _messagesReceived.Count);
}
[TestMethod]
public void RequestProtocolVersion()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF9 }, new byte[] { 0xF9, 2, 3 });
session.RequestProtocolVersion();
Assert.AreEqual(1, _messagesReceived.Count, "Message event error");
FirmataMessage message = _messagesReceived.Dequeue();
Assert.AreEqual(MessageType.ProtocolVersion, message.Type);
var version = (ProtocolVersion)message.Value;
Assert.AreEqual(2, version.Major);
Assert.AreEqual(3, version.Minor);
}
[TestMethod]
public void GetFirmware()
{
const int majorVersion = 3;
const int minorVersion = 7;
const string Name = "Arduino Firmata";
var connection = new MockSerialConnection();
var session = new ArduinoSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x79, 0xF7 }, new byte[] { 0xF0, 0x79, majorVersion, minorVersion });
connection.EnqueueResponse(Name.To14BitIso());
connection.EnqueueResponse(0xF7);
Firmware firmware = session.GetFirmware();
Assert.AreEqual(firmware.MajorVersion, majorVersion);
Assert.AreEqual(firmware.MinorVersion, minorVersion);
Assert.AreEqual(firmware.Name, Name);
}
[TestMethod]
public void GetFirmwareAsync()
{
const int majorVersion = 5;
const int minorVersion = 1;
const string Name = "Arduïno Firmata";
var connection = new MockSerialConnection();
var session = new ArduinoSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x79, 0xF7 }, new byte[] { 0xF0, 0x79, majorVersion, minorVersion });
connection.EnqueueResponse(Name.To14BitIso());
connection.EnqueueResponse(0xF7);
Firmware firmware = session.GetFirmwareAsync().Result;
Assert.AreEqual(firmware.MajorVersion, majorVersion);
Assert.AreEqual(firmware.MinorVersion, minorVersion);
Assert.AreEqual(firmware.Name, Name);
}
[TestMethod]
public void RequestFirmware()
{
const int majorVersion = 5;
const int minorVersion = 1;
const string Name = "Arduïno Firmata";
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x79, 0xF7 }, new byte[] { 0xF0, 0x79, majorVersion, minorVersion });
connection.EnqueueResponse(Name.To14BitIso());
connection.EnqueueResponse(0xF7);
session.RequestFirmware();
Assert.AreEqual(1, _messagesReceived.Count, "Message event error");
FirmataMessage message = _messagesReceived.Dequeue();
Assert.AreEqual(MessageType.FirmwareResponse, message.Type);
var firmware = (Firmware)message.Value;
Assert.AreEqual(firmware.MajorVersion, majorVersion);
Assert.AreEqual(firmware.MinorVersion, minorVersion);
Assert.AreEqual(firmware.Name, Name);
}
[TestMethod]
public void GetBoardCapability()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
// DIGITAL INPUT/DIGITAL OUTPUT/ANALOG/PWM/SERVO/I2C, 0/1/2/3/4/6
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x6B, 0xF7 }, 0xF0, 0x6C);
connection.EnqueueResponse(0, 1, 1, 1, 3, 10, 6, 1, 0x7F);
connection.EnqueueResponse(0xF7);
BoardCapability capability = session.GetBoardCapability();
Assert.AreEqual(1, capability.Pins.Length);
Assert.AreEqual(0, capability.Pins[0].PinNumber);
Assert.AreEqual(true, capability.Pins[0].DigitalInput);
Assert.AreEqual(true, capability.Pins[0].DigitalOutput);
Assert.AreEqual(true, capability.Pins[0].Pwm);
Assert.AreEqual(10, capability.Pins[0].PwmResolution);
Assert.AreEqual(true, capability.Pins[0].I2C);
Assert.AreEqual(false, capability.Pins[0].Analog);
Assert.AreEqual(0, capability.Pins[0].AnalogResolution);
Assert.AreEqual(false, capability.Pins[0].Servo);
Assert.AreEqual(0, capability.Pins[0].ServoResolution);
}
[TestMethod]
public void GetBoardCapabilityAsync()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
// DIGITAL INPUT/DIGITAL OUTPUT/ANALOG/PWM/SERVO, 0/1/2/3/4
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x6B, 0xF7 }, 0xF0, 0x6C);
connection.EnqueueResponse(2, 8, 0x7F);
connection.EnqueueResponse(0, 1, 1, 1, 0x7F);
connection.EnqueueResponse(1, 1, 3, 7, 4, 7, 0x7F);
connection.EnqueueResponse(0xF7);
BoardCapability capability = session.GetBoardCapabilityAsync().Result;
Assert.AreEqual(3, capability.Pins.Length);
Assert.AreEqual(0, capability.Pins[0].PinNumber);
Assert.AreEqual(false, capability.Pins[0].DigitalInput);
Assert.AreEqual(false, capability.Pins[0].DigitalOutput);
Assert.AreEqual(true, capability.Pins[0].Analog);
Assert.AreEqual(8, capability.Pins[0].AnalogResolution);
Assert.AreEqual(false, capability.Pins[0].Pwm);
Assert.AreEqual(0, capability.Pins[0].PwmResolution);
Assert.AreEqual(false, capability.Pins[0].Servo);
Assert.AreEqual(0, capability.Pins[0].ServoResolution);
Assert.AreEqual(1, capability.Pins[1].PinNumber);
Assert.AreEqual(true, capability.Pins[1].DigitalInput);
Assert.AreEqual(true, capability.Pins[1].DigitalOutput);
Assert.AreEqual(false, capability.Pins[1].Analog);
Assert.AreEqual(0, capability.Pins[1].AnalogResolution);
Assert.AreEqual(false, capability.Pins[1].Pwm);
Assert.AreEqual(0, capability.Pins[1].PwmResolution);
Assert.AreEqual(false, capability.Pins[1].Servo);
Assert.AreEqual(0, capability.Pins[1].ServoResolution);
Assert.AreEqual(2, capability.Pins[2].PinNumber);
Assert.AreEqual(false, capability.Pins[2].DigitalInput);
Assert.AreEqual(true, capability.Pins[2].DigitalOutput);
Assert.AreEqual(false, capability.Pins[2].Analog);
Assert.AreEqual(0, capability.Pins[2].AnalogResolution);
Assert.AreEqual(true, capability.Pins[2].Pwm);
Assert.AreEqual(7, capability.Pins[2].PwmResolution);
Assert.AreEqual(true, capability.Pins[2].Servo);
Assert.AreEqual(7, capability.Pins[2].ServoResolution);
}
[TestMethod]
public void RequestBoardCapability()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
// DIGITAL INPUT/DIGITAL OUTPUT/ANALOG/PWM/SERVO, 0/1/2/3/4
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x6B, 0xF7 }, 0xF0, 0x6C);
connection.EnqueueResponse(2, 8, 0x7F);
connection.EnqueueResponse(0, 127, 1, 127, 0x7F);
connection.EnqueueResponse(0xF7);
session.RequestBoardCapability();
FirmataMessage message = _messagesReceived.Dequeue();
Assert.AreEqual(MessageType.CapabilityResponse, message.Type);
BoardCapability capability = (BoardCapability)message.Value;
Assert.AreEqual(2, capability.Pins.Length);
Assert.AreEqual(0, capability.Pins[0].PinNumber);
Assert.AreEqual(false, capability.Pins[0].DigitalInput);
Assert.AreEqual(false, capability.Pins[0].DigitalOutput);
Assert.AreEqual(true, capability.Pins[0].Analog);
Assert.AreEqual(8, capability.Pins[0].AnalogResolution);
Assert.AreEqual(false, capability.Pins[0].Pwm);
Assert.AreEqual(0, capability.Pins[0].PwmResolution);
Assert.AreEqual(false, capability.Pins[0].Servo);
Assert.AreEqual(0, capability.Pins[0].ServoResolution);
Assert.AreEqual(1, capability.Pins[1].PinNumber);
Assert.AreEqual(true, capability.Pins[1].DigitalInput);
Assert.AreEqual(true, capability.Pins[1].DigitalOutput);
Assert.AreEqual(false, capability.Pins[1].Analog);
Assert.AreEqual(0, capability.Pins[1].AnalogResolution);
Assert.AreEqual(false, capability.Pins[1].Pwm);
Assert.AreEqual(0, capability.Pins[1].PwmResolution);
Assert.AreEqual(false, capability.Pins[1].Servo);
Assert.AreEqual(0, capability.Pins[1].ServoResolution);
}
[TestMethod]
public void GetBoardAnalogMapping()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x69, 0xF7 }, 0xF0, 0x6A);
connection.EnqueueResponse(0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x01, 0x02);
connection.EnqueueResponse(0x03, 0x04, 0x05, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F);
connection.EnqueueResponse(0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x0F);
connection.EnqueueResponse(0xF7);
BoardAnalogMapping mapping = session.GetBoardAnalogMapping();
Assert.AreEqual(7, mapping.PinMappings.Length);
Assert.AreEqual(5, mapping.PinMappings[0].PinNumber);
Assert.AreEqual(0, mapping.PinMappings[0].Channel);
Assert.AreEqual(6, mapping.PinMappings[1].PinNumber);
Assert.AreEqual(1, mapping.PinMappings[1].Channel);
Assert.AreEqual(7, mapping.PinMappings[2].PinNumber);
Assert.AreEqual(2, mapping.PinMappings[2].Channel);
Assert.AreEqual(8, mapping.PinMappings[3].PinNumber);
Assert.AreEqual(3, mapping.PinMappings[3].Channel);
Assert.AreEqual(9, mapping.PinMappings[4].PinNumber);
Assert.AreEqual(4, mapping.PinMappings[4].Channel);
Assert.AreEqual(10, mapping.PinMappings[5].PinNumber);
Assert.AreEqual(5, mapping.PinMappings[5].Channel);
Assert.AreEqual(23, mapping.PinMappings[6].PinNumber);
Assert.AreEqual(15, mapping.PinMappings[6].Channel);
}
[TestMethod]
public void GetBoardAnalogMappingAsync()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x69, 0xF7 }, 0xF0, 0x6A);
connection.EnqueueResponse(0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x7F, 0x7F);
connection.EnqueueResponse(0xF7);
BoardAnalogMapping mapping = session.GetBoardAnalogMappingAsync().Result;
Assert.AreEqual(6, mapping.PinMappings.Length);
Assert.AreEqual(0, mapping.PinMappings[0].PinNumber);
Assert.AreEqual(0, mapping.PinMappings[0].Channel);
Assert.AreEqual(1, mapping.PinMappings[1].PinNumber);
Assert.AreEqual(1, mapping.PinMappings[1].Channel);
Assert.AreEqual(2, mapping.PinMappings[2].PinNumber);
Assert.AreEqual(2, mapping.PinMappings[2].Channel);
Assert.AreEqual(3, mapping.PinMappings[3].PinNumber);
Assert.AreEqual(3, mapping.PinMappings[3].Channel);
Assert.AreEqual(4, mapping.PinMappings[4].PinNumber);
Assert.AreEqual(4, mapping.PinMappings[4].Channel);
Assert.AreEqual(5, mapping.PinMappings[5].PinNumber);
Assert.AreEqual(5, mapping.PinMappings[5].Channel);
}
[TestMethod]
public void RequestBoardAnalogMapping()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x69, 0xF7 }, 0xF0, 0x6A);
connection.EnqueueResponse(0x0F);
connection.EnqueueResponse(0xF7);
session.RequestBoardAnalogMapping();
FirmataMessage message = _messagesReceived.Dequeue();
Assert.AreEqual(MessageType.AnalogMappingResponse, message.Type);
BoardAnalogMapping mapping = (BoardAnalogMapping)message.Value;
Assert.AreEqual(1, mapping.PinMappings.Length);
Assert.AreEqual(0, mapping.PinMappings[0].PinNumber);
Assert.AreEqual(15, mapping.PinMappings[0].Channel);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void GetPinState_NegativePinNumber_Argument()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x6D, 0x00, 0xF7 }, 0xF0, 0x6E);
// Pin 0 analog mode, value = 1023
connection.EnqueueResponse(0x00, 0x02, 0x7F, 0x07);
connection.EnqueueResponse(0xF7);
PinState state = session.GetPinState(-1);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void GetPinState_PinNumber_Argument_Is128()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x6D, 0x00, 0xF7 }, 0xF0, 0x6E);
// Pin 0 analog mode, value = 1023
connection.EnqueueResponse(0x00, 0x02, 0x7F, 0x07);
connection.EnqueueResponse(0xF7);
PinState state = session.GetPinState(128);
}
[TestMethod]
public void GetPinState_PinNumber_Argument_Is0()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x6D, 0x00, 0xF7 }, 0xF0, 0x6E);
// Pin 0 analog mode, value = 1023
connection.EnqueueResponse(0x00, 0x02, 0x7F, 0x07);
connection.EnqueueResponse(0xF7);
PinState state = session.GetPinState(0);
Assert.AreEqual(0, state.PinNumber);
Assert.AreEqual(PinMode.AnalogInput, state.Mode);
Assert.AreEqual(1023U, state.Value);
}
[TestMethod]
public void GetPinState_PinNumber_Argument_Is127_Returned_Pin0()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x6D, 0x7F, 0xF7 }, 0xF0, 0x6E);
// Pin 0 analog mode, value = 1023
connection.EnqueueResponse(0x00, 0x02, 0x7F, 0x07);
connection.EnqueueResponse(0xF7);
PinState state = session.GetPinState(127);
Assert.AreEqual(0, state.PinNumber);
Assert.AreEqual(PinMode.AnalogInput, state.Mode);
Assert.AreEqual(1023U, state.Value);
}
[TestMethod]
public void GetPinState_PinNumber_Argument_Is127()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x6D, 0x7F, 0xF7 }, 0xF0, 0x6E);
// Pin 127 PWM mode, value = 256
connection.EnqueueResponse(0x7F, 0x03, 0x00, 0x02);
connection.EnqueueResponse(0xF7);
PinState state = session.GetPinState(127);
Assert.AreEqual(127, state.PinNumber);
Assert.AreEqual(PinMode.PwmOutput, state.Mode);
Assert.AreEqual(256U, state.Value);
}
[TestMethod]
public void GetPinStateAsync()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x6D, 0x05, 0xF7 }, 0xF0, 0x6E);
// Pin 5 Digital Input mode, value = 1
connection.EnqueueResponse(0x05, 0x00, 0x01);
connection.EnqueueResponse(0xF7);
PinState state = session.GetPinStateAsync(5).Result;
Assert.AreEqual(5, state.PinNumber);
Assert.AreEqual(PinMode.DigitalInput, state.Mode);
Assert.AreEqual(1U, state.Value);
}
[TestMethod]
public void RequestPinState()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x6D, 0x01, 0xF7 }, 0xF0, 0x6E);
// Pin 1 Digital Output mode, value = 0
connection.EnqueueResponse(0x01, 0x01, 0x00);
connection.EnqueueResponse(0xF7);
session.RequestPinState(1);
FirmataMessage message = _messagesReceived.Dequeue();
Assert.AreEqual(MessageType.PinStateResponse, message.Type);
PinState state = (PinState)message.Value;
Assert.AreEqual(1, state.PinNumber);
Assert.AreEqual(PinMode.DigitalOutput, state.Mode);
Assert.AreEqual(0, state.Value);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetDigitalPinMode_PinNumber_Negative()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF4, 0x00, (byte)PinMode.DigitalInput });
session.SetDigitalPinMode(-1, PinMode.DigitalInput);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetDigitalPinMode_PinNumber_Is128()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF4, 0x00, (byte)PinMode.DigitalInput });
session.SetDigitalPinMode(128, PinMode.DigitalInput);
}
[TestMethod]
public void SetDigitalPinMode_SetPin0ToDigitalOutput()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF4, 0x00, (byte)PinMode.DigitalOutput });
session.SetDigitalPinMode(0, PinMode.DigitalOutput);
}
[TestMethod]
public void SetDigitalPinMode_SetPin127ToAnalog()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF4, 0x7F, (byte)PinMode.AnalogInput });
session.SetDigitalPinMode(127, PinMode.AnalogInput);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetDigitalPin_PinNumber_Argument_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xE0, 0x00, 0x00 });
session.SetDigitalPin(-1, 0);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetDigitalPin_PinNumber_Argument_Is128()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x6F, 0x10, 0x00, 0x00, 0xF7 });
session.SetDigitalPin(128, 0);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetDigitalPin_Level_Argument_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xE0, 0x00, 0x00 });
session.SetDigitalPin(0, -1);
}
[TestMethod]
public void SetDigitalPin_Level_PinNumber_Argument_Is0()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xE0, 0x00, 0x00 });
session.SetDigitalPin(0, 0);
}
[TestMethod]
public void SetDigitalPin_Level_Is0_PinNumber_Argument_Is16()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x6F, 0x10, 0x00, 0x00, 0xF7);
session.SetDigitalPin(16, 0);
}
[TestMethod]
public void SetDigitalPin_Level_PinNumber_Argument_Is15()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xEF, 0x00, 0x00 });
session.SetDigitalPin(15, 0);
}
[TestMethod]
public void SetDigitalPin_Level_PinNumber_Argument_Is0_Level_Argument_Is0x3FFF()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xE0, 0x7F, 0x7F });
session.SetDigitalPin(0, 0x3FFF);
}
[TestMethod]
public void SetDigitalPin_Level_PinNumber_Argument_Is0_Level_Argument_Is0x4000()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x6F, 0x00, 0x00, 0x00, 0x01, 0xF7 });
session.SetDigitalPin(0, 0x4000);
}
[TestMethod]
public void SetDigitalPin_Level_PinNumber_Argument_Is0_Level_Argument_Is0xFFFF()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x6F, 0x00, 0x7F, 0x7F, 0x03, 0xF7 });
session.SetDigitalPin(0, 0xFFFF);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetDigitalPort_PortNumber_Argument_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0x90, 0x00, 0x00 });
session.SetDigitalPort(-1, 0);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetDigitalPort_PortNumber_Argument_Is128()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0x90, 0x00, 0x00 });
session.SetDigitalPort(-1, 0);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetDigitalPort_Pins_Argument_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0x90, 0x00, 0x00 });
session.SetDigitalPort(0, -1);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetDigitalPort_Pins_Argument_Is256()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0x90, 0x00, 0x00 });
session.SetDigitalPort(0, 256);
}
[TestMethod]
public void SetDigitalPort_PortNumber_Is0_Pins_Is0()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0x90, 0x00, 0x00 });
session.SetDigitalPort(0, 0);
}
[TestMethod]
public void SetDigitalPort_PortNumber_Is15_Pins_Is255()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0x9F, 0x7F, 0x01 });
session.SetDigitalPort(15, 255);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetDigitalReportMode_PortNumber_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xD0, 0x00 });
session.SetDigitalReportMode(-1, false);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetDigitalReportMode_PortNumber_Is16()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xD0, 0x00 });
session.SetDigitalReportMode(16, false);
}
[TestMethod]
public void SetDigitalReportMode_PortNumber_Is1_Enable()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xD1, 0x01 });
session.SetDigitalReportMode(1, true);
}
[TestMethod]
public void SetDigitalReportMode_PortNumber_Is0_Disable()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xD0, 0x00 });
session.SetDigitalReportMode(0, false);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetAnalogReportMode_Channel_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xC0, 0x00 });
session.SetAnalogReportMode(-1, false);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetAnalogReportMode_Channel_Is16()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xC0, 0x00 });
session.SetAnalogReportMode(16, false);
}
[TestMethod]
public void SetAnalogReportMode_Channel_Is0_Enable()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xC0, 0x01 });
session.SetAnalogReportMode(0, true);
}
[TestMethod]
public void SetAnalogReportMode_Channel_Is15_Disable()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xCF, 0x00 });
session.SetAnalogReportMode(15, false);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetSamplingInterval_Interval_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x7A, 0x00, 0x00, 0xF7 });
session.SetSamplingInterval(-1);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SetSamplingInterval_Interval_Is0x4000()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x7A, 0x00, 0x00, 0xF7 });
session.SetSamplingInterval(0x4000);
}
[TestMethod]
public void SetSamplingInterval_Interval_Is0()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x7A, 0x00, 0x00, 0xF7 });
session.SetSamplingInterval(0);
}
[TestMethod]
public void SetSamplingInterval_Interval_Is0x3FFF()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x7A, 0x7F, 0x7F, 0xF7 });
session.SetSamplingInterval(0x3FFF);
}
[TestMethod]
public void AnalogStateReceived_MinValues()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
int eventHits = 0;
session.AnalogStateReceived += (o, e) =>
{
Assert.AreEqual(0, e.Value.Channel);
Assert.AreEqual(0, e.Value.Level);
eventHits++;
};
session.DigitalStateReceived += (o, e) =>
{
Assert.Fail("Analog Message Digital processed.");
};
connection.EnqueueRequestAndResponse(new byte[] { 0xC0, 0x01 }, 0xE0, 0x00, 0x00);
session.SetAnalogReportMode(0, true);
Assert.AreEqual(1, eventHits, "AnalogStateReceived event not hit once.");
}
[TestMethod]
public void AnalogStateReceived_MaxValues()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
int eventHits = 0;
session.AnalogStateReceived += (o, e) =>
{
Assert.AreEqual(15, e.Value.Channel);
Assert.AreEqual(0x3FFF, e.Value.Level);
eventHits++;
};
session.DigitalStateReceived += (o, e) =>
{
Assert.Fail("Analog Message Digital processed.");
};
connection.EnqueueRequestAndResponse(new byte[] { 0xC0, 0x01 }, 0xEF, 0x7F, 0x7F);
session.SetAnalogReportMode(0, true);
Assert.AreEqual(1, eventHits, "AnalogStateReceived event not hit once.");
}
[TestMethod]
public void AnalogStateReceived_Channel_LsbMsbOrder()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
int eventHits = 0;
session.AnalogStateReceived += (o, e) =>
{
Assert.AreEqual(1, e.Value.Channel);
Assert.AreEqual(0x1345, e.Value.Level);
eventHits++;
};
session.DigitalStateReceived += (o, e) =>
{
Assert.Fail("Analog Message Digital processed.");
};
connection.EnqueueRequestAndResponse(new byte[] { 0xC0, 0x01 }, 0xE1, 0x45, 0x26);
session.SetAnalogReportMode(0, true);
Assert.AreEqual(1, eventHits, "DigitalStateReceived event not hit once.");
}
[TestMethod]
public void DigitalStateReceived_MinValues()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
int eventHits = 0;
session.AnalogStateReceived += (o, e) =>
{
Assert.Fail("Digital Message Analog processed.");
};
session.DigitalStateReceived += (o, e) =>
{
Assert.AreEqual(0, e.Value.Port);
Assert.AreEqual(0, e.Value.Pins);
eventHits++;
};
connection.EnqueueRequestAndResponse(new byte[] { 0xD0, 0x01 }, 0x90, 0x00, 0x00);
session.SetDigitalReportMode(0, true);
Assert.AreEqual(1, eventHits, "AnalogStateReceived event not hit once.");
}
[TestMethod]
public void DigitalStateReceived_MaxValues()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
int eventHits = 0;
session.AnalogStateReceived += (o, e) =>
{
Assert.Fail("Digital Message Analog processed.");
};
session.DigitalStateReceived += (o, e) =>
{
Assert.AreEqual(15, e.Value.Port);
Assert.AreEqual(255, e.Value.Pins);
eventHits++;
};
connection.EnqueueRequestAndResponse(new byte[] { 0xDF, 0x01 }, 0x9F, 0x7F, 0x01);
session.SetDigitalReportMode(15, true);
Assert.AreEqual(1, eventHits, "DigitalStateReceived event not hit once.");
}
[TestMethod]
public void SendStringData()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(new byte[] { 0xF0, 0x71 });
connection.EnqueueRequest("Test".To14BitIso());
connection.EnqueueRequest(new byte[] { 0xF7 });
session.SendStringData("Test");
}
[TestMethod]
public void SendStringData_NullString()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
connection.EnqueueRequest(0xF0, 0x71, 0xF7);
session.SendStringData(null);
}
[TestMethod]
public void Receive_StringDataMessage()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection);
int eventHits = 0;
session.MessageReceived += (o, e) =>
{
Assert.AreEqual(MessageType.StringData, e.Value.Type);
Assert.AreEqual("Hello!", ((StringData)e.Value.Value).Text);
eventHits++;
};
connection.EnqueueRequest(0xF0, 0x71, 0xF7);
connection.EnqueueResponse(0xF0, 0x71, 0x48, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x21, 0x00, 0xF7);
session.SendStringData(null);
Assert.AreEqual(1, eventHits, "MessageReceived event not hit once.");
}
//[TestMethod]
//[ExpectedException(typeof(TimeoutException))]
public void TimedoutResponse()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection, 550);
connection.EnqueueRequest(0xF0, 0x6B, 0xF7);
session.GetBoardCapability();
}
[TestMethod]
public void MixedOrderResponses()
{
var connection = new MockSerialConnection();
var session = CreateFirmataSession(connection, 3);
// We get first ProtocolVersion response and then FirmwareResponse
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x79, 0xF7 },
new byte[]
{
0xF9, 0x02, 0x04, 0xF0,
0x79,
0x01, 0x03,
0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
0xF7
});
var f = session.GetFirmware();
Assert.AreEqual(1, f.MajorVersion);
Assert.AreEqual(3, f.MinorVersion);
Assert.AreEqual("test", f.Name);
}
private IFirmataProtocol CreateFirmataSession(ISerialConnection connection, int timeout = -1)
{
var session = new ArduinoSession(connection);
session.TimeOut = timeout;
session.MessageReceived += (o, e) =>
{
_messagesReceived.Enqueue(e.Value);
};
return session;
}
}
}
================================================
FILE: Solid.Arduino.Test/IServoProtocolTester.cs
================================================
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Solid.Arduino.Firmata;
using Solid.Arduino.Firmata.Servo;
namespace Solid.Arduino.Test
{
[TestClass]
public class IServoProtocolTester
{
private readonly Queue _messagesReceived = new Queue();
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ConfigureServo_PinNumber_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateServoSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00 });
session.ConfigureServo(-1, 0, 0);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ConfigureServo_PinNumber_Is128()
{
var connection = new MockSerialConnection();
var session = CreateServoSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00 });
session.ConfigureServo(128, 0, 0);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ConfigureServo_MinPulse_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateServoSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00 });
session.ConfigureServo(0, -1, 0);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ConfigureServo_MinPulse_Is0x4000()
{
var connection = new MockSerialConnection();
var session = CreateServoSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00 });
session.ConfigureServo(0, 0x4000, 0);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ConfigureServo_MaxPulse_Is0x4000()
{
var connection = new MockSerialConnection();
var session = CreateServoSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7 });
session.ConfigureServo(15, 0, 0x4000);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ConfigureServo_MaxPulse_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateServoSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7 });
session.ConfigureServo(15, 0, -1);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ConfigureServo_MinPulse_GT_MaxPulse()
{
var connection = new MockSerialConnection();
var session = CreateServoSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7 });
session.ConfigureServo(1, 0x3FFF, 0x3FFE);
}
[TestMethod]
public void ConfigureServo_MinPulse_EQ_MaxPulse()
{
var connection = new MockSerialConnection();
var session = CreateServoSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x70, 0x01, 0x01, 0x02, 0x01, 0x02, 0xF7 });
session.ConfigureServo(1, 257, 257);
}
[TestMethod]
public void ConfigureServo()
{
var connection = new MockSerialConnection();
var session = CreateServoSession(connection);
connection.EnqueueRequestAndResponse(new byte[] { 0xF0, 0x70, 0x05, 0x00, 0x02, 0x01, 0x02, 0xF7 });
session.ConfigureServo(5, 256, 257);
}
private IServoProtocol CreateServoSession(ISerialConnection connection, int timeout = -1)
{
var session = new ArduinoSession(connection);
session.TimeOut = timeout;
session.MessageReceived += (o, e) =>
{
_messagesReceived.Enqueue(e.Value);
};
return session;
}
}
}
================================================
FILE: Solid.Arduino.Test/IStringProtocolTester.cs
================================================
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Solid.Arduino.Test
{
///
/// Summary description for IStringProtocolTester
///
[TestClass]
public class IStringProtocolTester
{
private TestContext testContextInstance;
///
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
#region Additional test attributes
//
// You can use the following additional attributes as you write your tests:
//
// Use ClassInitialize to run code before running the first test in the class
// [ClassInitialize()]
// public static void MyClassInitialize(TestContext testContext) { }
//
// Use ClassCleanup to run code after all tests in a class have run
// [ClassCleanup()]
// public static void MyClassCleanup() { }
//
// Use TestInitialize to run code before running each test
// [TestInitialize()]
// public void MyTestInitialize() { }
//
// Use TestCleanup to run code after each test has run
// [TestCleanup()]
// public void MyTestCleanup() { }
//
#endregion
[TestMethod]
public void Write()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
connection.EnqueueStringRequest("Test!");
session.Write("Test!");
}
[TestMethod]
public void Write_NoArgument()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
session.Write();
}
[TestMethod]
public void Write_NullString()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
session.Write(null);
}
[TestMethod]
public void Write_EmptyString()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
session.Write(string.Empty);
}
[TestMethod]
public void WriteLine()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
connection.EnqueueStringRequest("Test!\n");
session.WriteLine("Test!");
}
[TestMethod]
public void WriteLine_NoArgument()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
connection.EnqueueStringRequest(session.NewLine);
session.WriteLine();
}
[TestMethod]
public void WriteLine_NullString()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
connection.EnqueueStringRequest(session.NewLine);
session.WriteLine(null);
}
[TestMethod]
public void WriteLine_EmptyString()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
connection.EnqueueStringRequest(session.NewLine);
session.WriteLine(string.Empty);
}
[TestMethod]
public void WriteLine_CarriageReturnAsNewLine()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
connection.EnqueueStringRequest("\r");
session.NewLine = "\r";
session.WriteLine();
}
[TestMethod]
public async Task Read_OneCharacter()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
var t = Task.Run(() =>
{
string data = session.Read();
Assert.AreEqual("Z", data);
}
);
connection.MockReceiveDelayed("Zxxxx");
await t;
}
//[TestMethod]
public void StringReceived()
{
string s = null;
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
session.NewLine = "\n";
session.StringReceived += (o, e) =>
{
s = e.Text;
};
connection.MockReceiveDelayed("ACME\n");
Assert.AreEqual("ACME", s);
}
[TestMethod]
[ExpectedException(typeof(OverflowException))]
public async Task ReceivedStringBufferOverflow()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
var t = Task.Run(() =>
{
string data = session.Read(2048);
}
);
connection.MockReceiveDelayed(new string('*', 40480));
await t;
}
//[TestMethod] // Verdacht
public async Task RequestBufferOverflow()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
var tasks = new List>(100);
for (int x = 0; x < 100; x++)
tasks.Add(session.ReadAsync());
Thread.Sleep(250);
connection.MockReceiveDelayed(new string('*', 100));
await Task.WhenAll(tasks);
foreach (Task t in tasks)
{
Assert.AreEqual("*", t.Result);
}
}
[TestMethod]
public async Task Read_StringBlock()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
var t = Task.Run(() =>
{
string data = session.Read(6);
Assert.AreEqual("Hello!", data);
}
);
connection.MockReceiveDelayed("Hello!!!");
await t;
}
//[TestMethod]
//[ExpectedException(typeof(TimeoutException))]
public async Task Read_WithTimeout()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection, 550);
var t = Task.Run(() =>
{
string data = session.Read(6);
Assert.AreEqual("Hello!", data);
}
);
await t;
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public async Task Read_NegativeLength()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
var t = Task.Run(() =>
{
string data = session.Read(-1);
}
);
connection.MockReceiveDelayed("Hello!!!");
await t;
}
[TestMethod]
public async Task ReadLine()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
var t = Task.Run(() =>
{
string data = session.ReadLine();
Assert.AreEqual("A line.", data);
}
);
connection.MockReceiveDelayed("A line." + session.NewLine + " Trailing text.");
await t;
}
[TestMethod]
public async Task ReadLine_EmptyString()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
var t = Task.Run(() =>
{
string data = session.ReadLine();
Assert.AreEqual(string.Empty, data);
}
);
connection.MockReceiveDelayed(session.NewLine);
await t;
}
[TestMethod]
public async Task ReadLine_CarriageReturn()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
session.NewLine = "\r";
var t = Task.Run(() =>
{
string data = session.ReadLine();
Assert.AreEqual("A line.", data);
}
);
connection.MockReceiveDelayed("A line." + session.NewLine);
await t;
}
[TestMethod]
public async Task ReadLine_CarriageReturnPlusLinefeed()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
session.NewLine = "\r\n";
var t = Task.Run(() =>
{
string data = session.ReadLine();
Assert.AreEqual("A line.", data);
data = session.ReadLine();
Assert.AreEqual("Second line.", data);
}
);
connection.MockReceiveDelayed("A line." + session.NewLine + "Second line." + session.NewLine);
await t;
}
[TestMethod]
public async Task ReadTo()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
var t = Task.Run(() =>
{
string data = session.ReadTo('.');
Assert.AreEqual("A line", data);
}
);
connection.MockReceiveDelayed("A line.");
await t;
}
[TestMethod]
public async Task ReadTo_EmptyString()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
var t = Task.Run(() =>
{
string data = session.ReadTo(';');
Assert.AreEqual(string.Empty, data);
}
);
connection.MockReceiveDelayed(";");
await t;
}
[TestMethod]
public async Task ReadAsync_NoParameters()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
var t = session.ReadAsync();
connection.MockReceiveDelayed("Test");
string data = await t;
Assert.AreEqual("T", data);
}
[TestMethod]
public async Task ReadAsync_4Characters()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
var t = session.ReadAsync(4);
connection.MockReceiveDelayed("Test");
string data = await t;
Assert.AreEqual("Test", data);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public async Task ReadAsync_Length_IsNegative()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
var t = session.ReadAsync(-1);
connection.MockReceiveDelayed("Test");
string data = await t;
}
[TestMethod]
public async Task ReadLineAsync()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
var t = session.ReadLineAsync();
connection.MockReceiveDelayed("Test" + session.NewLine);
string data = await t;
Assert.AreEqual("Test", data);
}
[TestMethod]
public async Task ReadToAsync()
{
var connection = new MockSerialConnection();
var session = CreateSerialSession(connection);
var t = session.ReadToAsync(',');
connection.MockReceiveDelayed("alpha,beta,gamma");
string data = await t;
Assert.AreEqual("alpha", data);
}
private IStringProtocol CreateSerialSession(ISerialConnection connection, int timeout = -1)
{
var session = new ArduinoSession(connection);
session.TimeOut = timeout;
session.MessageReceived += (o, e) =>
{
Assert.Fail("MessageReceived event triggered");
};
session.AnalogStateReceived += (o, e) =>
{
Assert.Fail("AnalogStateReceived event triggered");
};
session.DigitalStateReceived += (o, e) =>
{
Assert.Fail("DigitalStateReceived event triggered");
};
session.I2CReplyReceived += (o, e) =>
{
Assert.Fail("I2CReplyReceived event triggered");
};
session.StringReceived += (o, e) =>
{
Console.WriteLine("Received: '{0}'", e.Text);
};
return session;
}
private async Task Read()
{
return await Task.Run(() => "Test");
}
}
}
================================================
FILE: Solid.Arduino.Test/MockSerialConnection.cs
================================================
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Solid.Arduino.Test
{
internal class MockSerialConnection: ISerialConnection
{
private static readonly ConstructorInfo SerialDataReceivedEventArgsConstructor = typeof(SerialDataReceivedEventArgs)
.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(SerialData) }, null);
private bool _isOpen;
private string _newLine = "\n";
private readonly Queue _expectedRequestQueue = new Queue();
private readonly Queue _responseQueue = new Queue();
private byte[] _currentRequest, _currentResponse;
private int _responseByteCount, _currentResponseIndex, _currentRequestIndex;
#region ISerialConnection Members
public int BaudRate
{
get { return 9600; }
set {}
}
public string PortName
{
get { return "COM3"; }
set {}
}
public event SerialDataReceivedEventHandler DataReceived;
public bool IsOpen
{
get { return _isOpen; }
}
internal int QueuedRequestCount
{
get { return _expectedRequestQueue.Count; }
}
public string NewLine
{
get { return _newLine; }
set { _newLine = value; }
}
public int BytesToRead
{
get
{
return _responseByteCount;
}
}
public void Open()
{
if (_isOpen)
throw new InvalidOperationException("MOCK VALIDATION: Connection is already open.");
_isOpen = true;
}
public void Close()
{
_isOpen = false;
}
public int ReadByte()
{
if (_responseByteCount == 0)
throw new InvalidOperationException("MOCK VALIDATION: No data.");
if (_currentResponse == null || _currentResponseIndex >= _currentResponse.Length)
{
_currentResponse = _responseQueue.Dequeue();
_currentResponseIndex = 0;
}
_responseByteCount--;
return _currentResponse[_currentResponseIndex++];
}
public void Write(string text)
{
if (text == null)
throw new ArgumentNullException("MOCK VALIDATION: text");
if (text.Length == 0)
throw new ArgumentException("MOCK VALIDATION: Text can not be empty.");
foreach (char c in text.ToCharArray())
{
AssertEqualToExpectedRequestByte(Convert.ToByte(c));
}
}
public void Write(byte[] buffer, int offset, int count)
{
if (buffer == null)
throw new ArgumentNullException("MOCK VALIDATION: buffer");
if (offset < 0)
throw new ArgumentException("MOCK VALIDATION: offset");
if (count < 1)
throw new ArgumentException("MOCK VALIDATION: count");
if (count - offset > buffer.Length)
throw new InvalidOperationException("MOCK VALIDATION: Out of range");
for (int x = 0; x < count; x++)
{
AssertEqualToExpectedRequestByte(buffer[x]);
}
}
public void WriteLine(string text)
{
if (text == null)
text = string.Empty;
foreach (char c in string.Concat(text, NewLine).ToCharArray())
{
AssertEqualToExpectedRequestByte(Convert.ToByte(c));
}
}
public void Dispose()
{
}
#endregion
#region Public Testmethods
public void EnqueueResponse(params byte[] data)
{
_responseQueue.Enqueue(data);
_responseByteCount += data.Length;
}
public void EnqueueRequest(params byte[] request)
{
_expectedRequestQueue.Enqueue(request);
}
public void EnqueueRequestAndResponse(byte[] request, params byte[] response)
{
_expectedRequestQueue.Enqueue(request);
if (response.Length > 0)
{
_responseQueue.Enqueue(response);
_responseByteCount += response.Length;
}
}
public void EnqueueStringResponse(string data)
{
_responseQueue.Enqueue(Encoding.ASCII.GetBytes(data));
_responseByteCount += data.Length;
}
public void EnqueueStringRequest(string data)
{
_expectedRequestQueue.Enqueue(Encoding.ASCII.GetBytes(data));
}
public void HandleStringResponse(string response, Action handler)
{
EnqueueStringResponse(response);
Task t = Task.Run(handler);
while (t.Status != TaskStatus.Running)
Thread.Sleep(1);
Thread.Sleep(3);
while (_responseQueue.Count > 0)
ReceiveData(_responseQueue.Peek());
try
{
t.Wait();
}
catch (AggregateException ex)
{
throw ex.InnerException;
}
}
public void MockReceiveDelayed(string data)
{
Thread.Sleep(20);
_responseQueue.Enqueue(Encoding.ASCII.GetBytes(data));
_responseByteCount += data.Length;
while (_responseQueue.Count > 0)
ReceiveData(_responseQueue.Peek());
}
#endregion
#region Private Methods
private void AssertEqualToExpectedRequestByte(byte p)
{
if (_currentRequest == null || _currentRequestIndex == _currentRequest.Length)
{
if (_expectedRequestQueue.Count < 1)
throw new InvalidOperationException("MOCK VALIDATION: No request data expected.");
_currentRequest = _expectedRequestQueue.Dequeue();
_currentRequestIndex = 0;
}
if (p != _currentRequest[_currentRequestIndex])
throw new InvalidOperationException(string.Format("MOCK VALIDATION: Issued request byte {0:X} not equal to expected request byte {1:X}.", p, _currentRequest[_currentRequestIndex]));
_currentRequestIndex++;
if (_currentRequestIndex == _currentRequest.Length)
{
// Current request has been fully sent; now deliver response.
while (_responseQueue.Count > 0)
ReceiveData(_responseQueue.Peek());
}
}
private void ReceiveData(byte[] response)
{
if (this.DataReceived == null
|| response.Length == 0)
return;
ConstructorInfo _serialDataReceivedEventArgsConstructor = typeof(SerialDataReceivedEventArgs)
.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(SerialData) }, null);
var charReceivedEventArgs = (SerialDataReceivedEventArgs)_serialDataReceivedEventArgsConstructor.Invoke(new object[] { SerialData.Chars });
for (int x = 0; x < response.Length; x++)
{
if (response[x] == 26)
DataReceived(this, (SerialDataReceivedEventArgs)_serialDataReceivedEventArgsConstructor.Invoke(new object[] { SerialData.Eof }));
else
DataReceived(this, charReceivedEventArgs);
}
}
#endregion
}
}
================================================
FILE: Solid.Arduino.Test/ObservableArduinoSessionTester.cs
================================================
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Solid.Arduino.Test
{
[TestClass]
public class ObservableArduinoSessionTester
{
[TestMethod]
public void TestMethod1()
{
var x = new ArduinoSession(new MockSerialConnection());
var tracker = x.CreateAnalogStateMonitor();
}
}
}
================================================
FILE: Solid.Arduino.Test/SerialConnectionTester.cs
================================================
using System;
using System.IO.Ports;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Solid.Arduino.Test
{
[TestClass]
public class SerialConnectionTester
{
[TestMethod]
public void SerialConnection_Constructor_WithoutParameters()
{
if (!AreSerialPortsAvailable())
{
Assert.ThrowsException(() => new SerialConnection());
return;
}
var connection = new SerialConnection();
Assert.AreEqual(100, connection.ReadTimeout);
Assert.AreEqual(100, connection.WriteTimeout);
Assert.AreEqual(115200, connection.BaudRate);
}
[TestMethod]
public void SerialConnection_Constructor_WithParameters()
{
var connection = new SerialConnection("COM1", SerialBaudRate.Bps_115200);
Assert.AreEqual(100, connection.ReadTimeout);
Assert.AreEqual(100, connection.WriteTimeout);
Assert.AreEqual(115200, connection.BaudRate);
}
[TestMethod]
public void SerialConnection_OpenAndClose()
{
if (!AreSerialPortsAvailable())
return;
var connection = new SerialConnection();
connection.Open();
connection.Close();
}
[TestMethod]
public void SerialConnection_OpenAndDoubleClose()
{
if (!AreSerialPortsAvailable())
return;
var connection = new SerialConnection();
connection.Open();
connection.Close();
connection.Close();
}
private static bool AreSerialPortsAvailable()
{
return SerialPort.GetPortNames().Length > 0;
}
}
}
================================================
FILE: Solid.Arduino.Test/Solid.Arduino.Test.csproj
================================================
netcoreapp3.1false
================================================
FILE: Solid.Arduino.Test/StringExtensionsTester.cs
================================================
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Solid.Arduino.Test
{
[TestClass]
public class StringExtensionsTester
{
[TestMethod]
public void ToBinaryCodedDecimal()
{
byte[] getal = "34".ToBinaryCodedDecimal();
Assert.AreEqual(1, getal.Length);
Assert.AreEqual(0x34, getal[0]);
}
[TestMethod]
public void ToBinaryCodedDecimal_AllDigits()
{
byte[] getal = "0123456789".ToBinaryCodedDecimal();
Assert.AreEqual(5, getal.Length);
Assert.AreEqual(0x01, getal[0]);
Assert.AreEqual(0x23, getal[1]);
Assert.AreEqual(0x45, getal[2]);
Assert.AreEqual(0x67, getal[3]);
Assert.AreEqual(0x89, getal[4]);
}
[TestMethod]
public void ToBinaryCodedDecimal_AllDigits_LittleEndian()
{
byte[] getal = "0123456789".ToBinaryCodedDecimal(true);
Assert.AreEqual(5, getal.Length);
Assert.AreEqual(0x98, getal[0]);
Assert.AreEqual(0x76, getal[1]);
Assert.AreEqual(0x54, getal[2]);
Assert.AreEqual(0x32, getal[3]);
Assert.AreEqual(0x10, getal[4]);
}
[TestMethod]
public void ToBinaryCodedDecimal_OneDigit()
{
byte[] getal = "5".ToBinaryCodedDecimal();
Assert.AreEqual(1, getal.Length);
Assert.AreEqual(0x05, getal[0]);
}
[TestMethod]
public void ToBinaryCodedDecimal_LittleEndian()
{
byte[] getal = "67".ToBinaryCodedDecimal(true);
Assert.AreEqual(1, getal.Length);
Assert.AreEqual(0x76, getal[0]);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ToBinaryCodedDecimal_NullString()
{
string g = null;
byte[] getal = g.ToBinaryCodedDecimal();
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ToBinaryCodedDecimal_AlphaNumeric()
{
string g = "987A33";
byte[] getal = g.ToBinaryCodedDecimal();
}
[TestMethod]
public void ToBinaryCodedDecimal_EmptyString()
{
byte[] getal = string.Empty.ToBinaryCodedDecimal();
Assert.AreEqual(0, getal.Length);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void To14BitIso_NullString()
{
string g = null;
byte[] getal = g.To14BitIso();
Assert.AreEqual(0, getal.Length);
}
[TestMethod]
public void To14BitIso_EmptyString()
{
byte[] getal = string.Empty.To14BitIso();
Assert.AreEqual(0, getal.Length);
}
[TestMethod]
public void To14BitIso_SingleCharacter()
{
byte[] getal = "A".To14BitIso();
Assert.AreEqual(2, getal.Length);
Assert.AreEqual(65, getal[0]);
Assert.AreEqual(0, getal[1]);
}
[TestMethod]
public void To14BitIso_3Characters()
{
byte[] getal = "ABC".To14BitIso();
Assert.AreEqual(6, getal.Length);
Assert.AreEqual(65, getal[0]);
Assert.AreEqual(0, getal[1]);
Assert.AreEqual(66, getal[2]);
Assert.AreEqual(0, getal[3]);
Assert.AreEqual(67, getal[4]);
Assert.AreEqual(0, getal[5]);
}
}
}
================================================
FILE: Solid.Arduino.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30011.22
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Solid.Arduino", "Solid.Arduino\Solid.Arduino.csproj", "{FDF9E431-2390-4C7B-AE60-10183439A683}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Solid.Arduino.Test", "Solid.Arduino.Test\Solid.Arduino.Test.csproj", "{74525FE1-E248-449B-B499-F3FB205054F8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Solid.Arduino.Run", "Solid.Arduino.Run\Solid.Arduino.Run.csproj", "{2688801E-DD4C-4BEE-8E1D-3C1E3E448D73}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Solid.Arduino.IntegrationTest", "Solid.Arduino.IntegrationTest\Solid.Arduino.IntegrationTest.csproj", "{40D8E3F0-DCCF-4F19-8EC0-2E6EC476B629}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{74531E41-B867-4266-890C-0B32CBE7EE99}"
ProjectSection(SolutionItems) = preProject
LICENSE.md = LICENSE.md
README.md = README.md
EndProjectSection
EndProject
Project("{7CF6DF6D-3B04-46F8-A40B-537D21BCA0B4}") = "Documentation", "Documentation\Documentation.shfbproj", "{721253FB-22D4-4DB6-8C7E-5F90118F52D5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FDF9E431-2390-4C7B-AE60-10183439A683}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FDF9E431-2390-4C7B-AE60-10183439A683}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FDF9E431-2390-4C7B-AE60-10183439A683}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FDF9E431-2390-4C7B-AE60-10183439A683}.Release|Any CPU.Build.0 = Release|Any CPU
{74525FE1-E248-449B-B499-F3FB205054F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{74525FE1-E248-449B-B499-F3FB205054F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{74525FE1-E248-449B-B499-F3FB205054F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{74525FE1-E248-449B-B499-F3FB205054F8}.Release|Any CPU.Build.0 = Release|Any CPU
{2688801E-DD4C-4BEE-8E1D-3C1E3E448D73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2688801E-DD4C-4BEE-8E1D-3C1E3E448D73}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2688801E-DD4C-4BEE-8E1D-3C1E3E448D73}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2688801E-DD4C-4BEE-8E1D-3C1E3E448D73}.Release|Any CPU.Build.0 = Release|Any CPU
{40D8E3F0-DCCF-4F19-8EC0-2E6EC476B629}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{40D8E3F0-DCCF-4F19-8EC0-2E6EC476B629}.Debug|Any CPU.Build.0 = Debug|Any CPU
{40D8E3F0-DCCF-4F19-8EC0-2E6EC476B629}.Release|Any CPU.ActiveCfg = Release|Any CPU
{40D8E3F0-DCCF-4F19-8EC0-2E6EC476B629}.Release|Any CPU.Build.0 = Release|Any CPU
{721253FB-22D4-4DB6-8C7E-5F90118F52D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{721253FB-22D4-4DB6-8C7E-5F90118F52D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{721253FB-22D4-4DB6-8C7E-5F90118F52D5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CF58A0B9-B321-427F-ACFC-AD580097EEDE}
EndGlobalSection
EndGlobal
================================================
FILE: _config.yml
================================================
theme: jekyll-theme-minimal
================================================
FILE: docs/SearchHelp.aspx
================================================
<%@ Page Language="C#" EnableViewState="False" %>
================================================
FILE: docs/SearchHelp.inc.php
================================================
// Contributed to the Sandcastle Help File Builder project by Thomas Levesque
class Ranking
{
public $filename;
public $pageTitle;
public $rank;
function __construct($file, $title, $rank)
{
$this->filename = $file;
$this->pageTitle = $title;
$this->rank = $rank;
}
}
///
/// Split the search text up into keywords
///
/// The keywords to parse
/// A list containing the words for which to search
function ParseKeywords($keywords)
{
$keywordList = array();
$words = preg_split("/[^\w]+/", $keywords);
foreach($words as $word)
{
$checkWord = strtolower($word);
$first = substr($checkWord, 0, 1);
if(strlen($checkWord) > 2 && !ctype_digit($first) && !in_array($checkWord, $keywordList))
{
array_push($keywordList, $checkWord);
}
}
return $keywordList;
}
///
/// Search for the specified keywords and return the results as a block of
/// HTML.
///
/// The keywords for which to search
/// The file list
/// The dictionary used to find the words
/// True to sort by title, false to sort by
/// ranking
/// A block of HTML representing the search results.
function Search($keywords, $fileInfo, $wordDictionary, $sortByTitle)
{
$sb = "";
$matches = array();
$matchingFileIndices = array();
$rankings = array();
$isFirst = true;
foreach($keywords as $word)
{
if (!array_key_exists($word, $wordDictionary))
{
return "Nothing found";
}
$occurrences = $wordDictionary[$word];
$matches[$word] = $occurrences;
$occurrenceIndices = array();
// Get a list of the file indices for this match
foreach($occurrences as $entry)
array_push($occurrenceIndices, ($entry >> 16));
if($isFirst)
{
$isFirst = false;
foreach($occurrenceIndices as $i)
{
array_push($matchingFileIndices, $i);
}
}
else
{
// After the first match, remove files that do not appear for
// all found keywords.
for($idx = 0; $idx < count($matchingFileIndices); $idx++)
{
if (!in_array($matchingFileIndices[$idx], $occurrenceIndices))
{
array_splice($matchingFileIndices, $idx, 1);
$idx--;
}
}
}
}
if(count($matchingFileIndices) == 0)
{
return "Nothing found";
}
// Rank the files based on the number of times the words occurs
foreach($matchingFileIndices as $index)
{
// Split out the title, filename, and word count
$fileIndex = explode("\x00", $fileInfo[$index]);
$title = $fileIndex[0];
$filename = $fileIndex[1];
$wordCount = intval($fileIndex[2]);
$matchCount = 0;
foreach($keywords as $words)
{
$occurrences = $matches[$word];
foreach($occurrences as $entry)
{
if(($entry >> 16) == $index)
$matchCount += $entry & 0xFFFF;
}
}
$r = new Ranking($filename, $title, $matchCount * 1000 / $wordCount);
array_push($rankings, $r);
if(count($rankings) > 99)
break;
}
// Sort by rank in descending order or by page title in ascending order
if($sortByTitle)
{
usort($rankings, "cmprankbytitle");
}
else
{
usort($rankings, "cmprank");
}
// Format the file list and return the results
foreach($rankings as $r)
{
$f = $r->filename;
$t = $r->pageTitle;
$sb .= "
================================================
FILE: docs/html/GeneralError.htm
================================================
General Error
SolidSoils4Arduino
We're sorry, a general error has occurred.
Please try to load the page again. If the error persists, please contact the site administrator.
================================================
FILE: docs/html/PageNotFound.htm
================================================
Page Not Found
SolidSoils4Arduino
We're sorry, the page you requested cannot be found.
The URL might be misspelled or the page you are looking for is no longer available. If you entered
the web address, check that it doesn't contain a typo. You can use the search box at the top of the page to
try and locate the page.
[This is preliminary documentation and is subject to change.]
[Missing <summary> documentation for "M:Solid.Arduino.EnhancedSerialConnection.Solid#Arduino#ISerialConnection#Write(System.Byte[],System.Int32,System.Int32)"]
Namespace:Solid.Arduino Assembly:
Solid.Arduino (in Solid.Arduino.dll) Version: 1.0.0
[This is preliminary documentation and is subject to change.]
[Missing <summary> documentation for "M:Solid.Arduino.SerialConnection.Solid#Arduino#ISerialConnection#Write(System.Byte[],System.Int32,System.Int32)"]
Namespace:Solid.Arduino Assembly:
Solid.Arduino (in Solid.Arduino.dll) Version: 1.0.0
================================================
FILE: docs/index.html
================================================
Solid Soils .NET Library for Arduino Serial Port Communication - Redirect
If you are not redirected automatically, follow this link to the default topic.
================================================
FILE: docs/scripts/branding-Website.js
================================================
//===============================================================================================================
// System : Sandcastle Help File Builder
// File : branding-Website.js
// Author : Eric Woodruff (Eric@EWoodruff.us)
// Updated : 03/04/2015
// Note : Copyright 2014-2015, Eric Woodruff, All rights reserved
// Portions Copyright 2014 Sam Harwell, All rights reserved
//
// This file contains the methods necessary to implement the lightweight TOC and search functionality.
//
// This code is published under the Microsoft Public License (Ms-PL). A copy of the license should be
// distributed with the code. It can also be found at the project website: https://GitHub.com/EWSoftware/SHFB. This
// notice, the author's name, and all copyright notices must remain intact in all applications, documentation,
// and source files.
//
// Date Who Comments
// ==============================================================================================================
// 05/04/2014 EFW Created the code based on a combination of the lightweight TOC code from Sam Harwell and
// the existing search code from SHFB.
//===============================================================================================================
// Width of the TOC
var tocWidth;
// Search method (0 = To be determined, 1 = ASPX, 2 = PHP, anything else = client-side script
var searchMethod = 0;
// Table of contents script
// Initialize the TOC by restoring its width from the cookie if present
function InitializeToc()
{
tocWidth = parseInt(GetCookie("TocWidth", "280"));
ResizeToc();
$(window).resize(SetNavHeight)
}
function SetNavHeight()
{
$leftNav = $("#leftNav")
$topicContent = $("#TopicContent")
leftNavPadding = $leftNav.outerHeight() - $leftNav.height()
contentPadding = $topicContent.outerHeight() - $topicContent.height()
// want outer height of left navigation div to match outer height of content
leftNavHeight = $topicContent.outerHeight() - leftNavPadding
$leftNav.css("min-height", leftNavHeight + "px")
}
// Increase the TOC width
function OnIncreaseToc()
{
if(tocWidth < 1)
tocWidth = 280;
else
tocWidth += 100;
if(tocWidth > 680)
tocWidth = 0;
ResizeToc();
SetCookie("TocWidth", tocWidth);
}
// Reset the TOC to its default width
function OnResetToc()
{
tocWidth = 0;
ResizeToc();
SetCookie("TocWidth", tocWidth);
}
// Resize the TOC width
function ResizeToc()
{
var toc = document.getElementById("leftNav");
if(toc)
{
// Set TOC width
toc.style.width = tocWidth + "px";
var leftNavPadding = 10;
document.getElementById("TopicContent").style.marginLeft = (tocWidth + leftNavPadding) + "px";
// Position images
document.getElementById("TocResize").style.left = (tocWidth + leftNavPadding) + "px";
// Hide/show increase TOC width image
document.getElementById("ResizeImageIncrease").style.display = (tocWidth >= 680) ? "none" : "";
// Hide/show reset TOC width image
document.getElementById("ResizeImageReset").style.display = (tocWidth < 680) ? "none" : "";
}
SetNavHeight()
}
// Toggle a TOC entry between its collapsed and expanded state
function Toggle(item)
{
var isExpanded = $(item).hasClass("tocExpanded");
$(item).toggleClass("tocExpanded tocCollapsed");
if(isExpanded)
{
Collapse($(item).parent());
}
else
{
var childrenLoaded = $(item).parent().attr("data-childrenloaded");
if(childrenLoaded)
{
Expand($(item).parent());
}
else
{
var tocid = $(item).next().attr("tocid");
$.ajax({
url: "../toc/" + tocid + ".xml",
async: true,
dataType: "xml",
success: function(data)
{
BuildChildren($(item).parent(), data);
}
});
}
}
}
// HTML encode a value for use on the page
function HtmlEncode(value)
{
// Create an in-memory div, set it's inner text (which jQuery automatically encodes) then grab the encoded
// contents back out. The div never exists on the page.
return $('').text(value).html();
}
// Build the child entries of a TOC entry
function BuildChildren(tocDiv, data)
{
var childLevel = +tocDiv.attr("data-toclevel") + 1;
var childTocLevel = childLevel >= 10 ? 10 : childLevel;
var elements = data.getElementsByTagName("HelpTOCNode");
var isRoot = true;
if(data.getElementsByTagName("HelpTOC").length == 0)
{
// The first node is the root node of this group, don't show it again
isRoot = false;
}
for(var i = elements.length - 1; i > 0 || (isRoot && i == 0); i--)
{
var childHRef, childId = elements[i].getAttribute("Url");
if(childId != null && childId.length > 5)
{
// The Url attribute has the form "html/{childId}.htm"
childHRef = "../" + childId;
childId = childId.substring(childId.lastIndexOf("/") + 1, childId.lastIndexOf("."));
}
else
{
// The Id attribute is in raw form. There is no URL (empty container node). In this case, we'll
// just ignore it and go nowhere. It's a rare case that isn't worth trying to get the first child.
// Instead, we'll just expand the node (see below).
childHRef = "#";
childId = elements[i].getAttribute("Id");
}
var existingItem = null;
tocDiv.nextAll().each(function()
{
if(!existingItem && $(this).children().last("a").attr("tocid") == childId)
{
existingItem = $(this);
}
});
if(existingItem != null)
{
// First move the children of the existing item
var existingChildLevel = +existingItem.attr("data-toclevel");
var doneMoving = false;
var inserter = tocDiv;
existingItem.nextAll().each(function()
{
if(!doneMoving && +$(this).attr("data-toclevel") > existingChildLevel)
{
inserter.after($(this));
inserter = $(this);
$(this).attr("data-toclevel", +$(this).attr("data-toclevel") + childLevel - existingChildLevel);
if($(this).hasClass("current"))
$(this).attr("class", "toclevel" + (+$(this).attr("data-toclevel") + " current"));
else
$(this).attr("class", "toclevel" + (+$(this).attr("data-toclevel")));
}
else
{
doneMoving = true;
}
});
// Now move the existing item itself
tocDiv.after(existingItem);
existingItem.attr("data-toclevel", childLevel);
existingItem.attr("class", "toclevel" + childLevel);
}
else
{
var hasChildren = elements[i].getAttribute("HasChildren");
var childTitle = HtmlEncode(elements[i].getAttribute("Title"));
var expander = "";
if(hasChildren)
expander = "";
var text = "
";
tocDiv.after(text);
}
}
tocDiv.attr("data-childrenloaded", true);
}
// Collapse a TOC entry
function Collapse(tocDiv)
{
// Hide all the TOC elements after item, until we reach one with a data-toclevel less than or equal to the
// current item's value.
var tocLevel = +tocDiv.attr("data-toclevel");
var done = false;
tocDiv.nextAll().each(function()
{
if(!done && +$(this).attr("data-toclevel") > tocLevel)
{
$(this).hide();
}
else
{
done = true;
}
});
}
// Expand a TOC entry
function Expand(tocDiv)
{
// Show all the TOC elements after item, until we reach one with a data-toclevel less than or equal to the
// current item's value
var tocLevel = +tocDiv.attr("data-toclevel");
var done = false;
tocDiv.nextAll().each(function()
{
if(done)
{
return;
}
var childTocLevel = +$(this).attr("data-toclevel");
if(childTocLevel == tocLevel + 1)
{
$(this).show();
if($(this).children("a").first().hasClass("tocExpanded"))
{
Expand($(this));
}
}
else if(childTocLevel > tocLevel + 1)
{
// Ignore this node, handled by recursive calls
}
else
{
done = true;
}
});
}
// This is called to prepare for dragging the sizer div
function OnMouseDown(event)
{
document.addEventListener("mousemove", OnMouseMove, true);
document.addEventListener("mouseup", OnMouseUp, true);
event.preventDefault();
}
// Resize the TOC as the sizer is dragged
function OnMouseMove(event)
{
tocWidth = (event.clientX > 700) ? 700 : (event.clientX < 100) ? 100 : event.clientX;
ResizeToc();
}
// Finish the drag operation when the mouse button is released
function OnMouseUp(event)
{
document.removeEventListener("mousemove", OnMouseMove, true);
document.removeEventListener("mouseup", OnMouseUp, true);
SetCookie("TocWidth", tocWidth);
}
// Search functions
// Transfer to the search page from a topic
function TransferToSearchPage()
{
var searchText = document.getElementById("SearchTextBox").value.trim();
if(searchText.length != 0)
document.location.replace(encodeURI("../search.html?SearchText=" + searchText));
}
// Initiate a search when the search page loads
function OnSearchPageLoad()
{
var queryString = decodeURI(document.location.search);
if(queryString != "")
{
var idx, options = queryString.split(/[\?\=\&]/);
for(idx = 0; idx < options.length; idx++)
if(options[idx] == "SearchText" && idx + 1 < options.length)
{
document.getElementById("txtSearchText").value = options[idx + 1];
PerformSearch();
break;
}
}
}
// Perform a search using the best available method
function PerformSearch()
{
var searchText = document.getElementById("txtSearchText").value;
var sortByTitle = document.getElementById("chkSortByTitle").checked;
var searchResults = document.getElementById("searchResults");
if(searchText.length == 0)
{
searchResults.innerHTML = "Nothing found";
return;
}
searchResults.innerHTML = "Searching...";
// Determine the search method if not done already. The ASPX and PHP searches are more efficient as they
// run asynchronously server-side. If they can't be used, it defaults to the client-side script below which
// will work but has to download the index files. For large help sites, this can be inefficient.
if(searchMethod == 0)
searchMethod = DetermineSearchMethod();
if(searchMethod == 1)
{
$.ajax({
type: "GET",
url: encodeURI("SearchHelp.aspx?Keywords=" + searchText + "&SortByTitle=" + sortByTitle),
success: function(html)
{
searchResults.innerHTML = html;
}
});
return;
}
if(searchMethod == 2)
{
$.ajax({
type: "GET",
url: encodeURI("SearchHelp.php?Keywords=" + searchText + "&SortByTitle=" + sortByTitle),
success: function(html)
{
searchResults.innerHTML = html;
}
});
return;
}
// Parse the keywords
var keywords = ParseKeywords(searchText);
// Get the list of files. We'll be getting multiple files so we need to do this synchronously.
var fileList = [];
$.ajax({
type: "GET",
url: "fti/FTI_Files.json",
dataType: "json",
async: false,
success: function(data)
{
$.each(data, function(key, val)
{
fileList[key] = val;
});
}
});
var letters = [];
var wordDictionary = {};
var wordNotFound = false;
// Load the keyword files for each keyword starting letter
for(var idx = 0; idx < keywords.length && !wordNotFound; idx++)
{
var letter = keywords[idx].substring(0, 1);
if($.inArray(letter, letters) == -1)
{
letters.push(letter);
$.ajax({
type: "GET",
url: "fti/FTI_" + letter.charCodeAt(0) + ".json",
dataType: "json",
async: false,
success: function(data)
{
var wordCount = 0;
$.each(data, function(key, val)
{
wordDictionary[key] = val;
wordCount++;
});
if(wordCount == 0)
wordNotFound = true;
}
});
}
}
if(wordNotFound)
searchResults.innerHTML = "Nothing found";
else
searchResults.innerHTML = SearchForKeywords(keywords, fileList, wordDictionary, sortByTitle);
}
// Determine the search method by seeing if the ASPX or PHP search pages are present and working
function DetermineSearchMethod()
{
var method = 3;
try
{
$.ajax({
type: "GET",
url: "SearchHelp.aspx",
async: false,
success: function(html)
{
if(html.substring(0, 8) == "")
method = 1;
}
});
if(method == 3)
$.ajax({
type: "GET",
url: "SearchHelp.php",
async: false,
success: function(html)
{
if(html.substring(0, 8) == "")
method = 2;
}
});
}
catch(e)
{
}
return method;
}
// Split the search text up into keywords
function ParseKeywords(keywords)
{
var keywordList = [];
var checkWord;
var words = keywords.split(/[\s!@#$%^&*()\-=+\[\]{}\\|<>;:'",.<>/?`~]+/);
for(var idx = 0; idx < words.length; idx++)
{
checkWord = words[idx].toLowerCase();
if(checkWord.length > 2)
{
var charCode = checkWord.charCodeAt(0);
if((charCode < 48 || charCode > 57) && $.inArray(checkWord, keywordList) == -1)
keywordList.push(checkWord);
}
}
return keywordList;
}
// Search for keywords and generate a block of HTML containing the results
function SearchForKeywords(keywords, fileInfo, wordDictionary, sortByTitle)
{
var matches = [], matchingFileIndices = [], rankings = [];
var isFirst = true;
for(var idx = 0; idx < keywords.length; idx++)
{
var word = keywords[idx];
var occurrences = wordDictionary[word];
// All keywords must be found
if(occurrences == null)
return "Nothing found";
matches[word] = occurrences;
var occurrenceIndices = [];
// Get a list of the file indices for this match. These are 64-bit numbers but JavaScript only does
// bit shifts on 32-bit values so we divide by 2^16 to get the same effect as ">> 16" and use floor()
// to truncate the result.
for(var ind in occurrences)
occurrenceIndices.push(Math.floor(occurrences[ind] / Math.pow(2, 16)));
if(isFirst)
{
isFirst = false;
for(var matchInd in occurrenceIndices)
matchingFileIndices.push(occurrenceIndices[matchInd]);
}
else
{
// After the first match, remove files that do not appear for all found keywords
for(var checkIdx = 0; checkIdx < matchingFileIndices.length; checkIdx++)
if($.inArray(matchingFileIndices[checkIdx], occurrenceIndices) == -1)
{
matchingFileIndices.splice(checkIdx, 1);
checkIdx--;
}
}
}
if(matchingFileIndices.length == 0)
return "Nothing found";
// Rank the files based on the number of times the words occurs
for(var fileIdx = 0; fileIdx < matchingFileIndices.length; fileIdx++)
{
// Split out the title, filename, and word count
var matchingIdx = matchingFileIndices[fileIdx];
var fileIndex = fileInfo[matchingIdx].split(/\0/);
var title = fileIndex[0];
var filename = fileIndex[1];
var wordCount = parseInt(fileIndex[2]);
var matchCount = 0;
for(var idx = 0; idx < keywords.length; idx++)
{
occurrences = matches[keywords[idx]];
for(var ind in occurrences)
{
var entry = occurrences[ind];
// These are 64-bit numbers but JavaScript only does bit shifts on 32-bit values so we divide
// by 2^16 to get the same effect as ">> 16" and use floor() to truncate the result.
if(Math.floor(entry / Math.pow(2, 16)) == matchingIdx)
matchCount += (entry & 0xFFFF);
}
}
rankings.push({ Filename: filename, PageTitle: title, Rank: matchCount * 1000 / wordCount });
if(rankings.length > 99)
break;
}
rankings.sort(function(x, y)
{
if(!sortByTitle)
return y.Rank - x.Rank;
return x.PageTitle.localeCompare(y.PageTitle);
});
// Format and return the results
var content = "";
for(var r in rankings)
content += "
Omitted " + (matchingFileIndices.length - rankings.length) + " more results
";
return content;
}
================================================
FILE: docs/scripts/branding.js
================================================
//===============================================================================================================
// System : Sandcastle Help File Builder
// File : branding.js
// Author : Eric Woodruff (Eric@EWoodruff.us)
// Updated : 10/08/2015
// Note : Copyright 2014-2015, Eric Woodruff, All rights reserved
// Portions Copyright 2010-2014 Microsoft, All rights reserved
//
// This file contains the methods necessary to implement the language filtering, collapsible section, and
// copy to clipboard options.
//
// This code is published under the Microsoft Public License (Ms-PL). A copy of the license should be
// distributed with the code and can be found at the project website: https://GitHub.com/EWSoftware/SHFB. This
// notice, the author's name, and all copyright notices must remain intact in all applications, documentation,
// and source files.
//
// Date Who Comments
// ==============================================================================================================
// 05/04/2014 EFW Created the code based on the MS Help Viewer script
//===============================================================================================================
// The IDs of all code snippet sets on the same page are stored so that we can keep them in synch when a tab is
// selected.
var allTabSetIds = new Array();
// The IDs of language-specific text (LST) spans are used as dictionary keys so that we can get access to the
// spans and update them when the user changes to a different language tab. The values of the dictionary
// objects are pipe separated language-specific attributes (lang1=value|lang2=value|lang3=value). The language
// ID can be specific (cs, vb, cpp, etc.) or may be a neutral entry (nu) which specifies text common to multiple
// languages. If a language is not present and there is no neutral entry, the span is hidden for all languages
// to which it does not apply.
var allLSTSetIds = new Object();
// Help 1 persistence support. This code must appear inline.
var isHelp1;
var curLoc = document.location + ".";
if(curLoc.indexOf("mk:@MSITStore") == 0)
{
isHelp1 = true;
curLoc = "ms-its:" + curLoc.substring(14, curLoc.length - 1);
document.location.replace(curLoc);
}
else
if(curLoc.indexOf("ms-its:") == 0)
isHelp1 = true;
else
isHelp1 = false;
// The OnLoad method
function OnLoad(defaultLanguage)
{
var defLang;
if(typeof (defaultLanguage) == "undefined" || defaultLanguage == null || defaultLanguage == "")
defLang = "vb";
else
defLang = defaultLanguage;
// In MS Help Viewer, the transform the topic is ran through can move the footer. Move it back where it
// belongs if necessary.
try
{
var footer = document.getElementById("pageFooter")
if(footer)
{
var footerParent = document.body;
if(footer.parentElement != footerParent)
{
footer.parentElement.removeChild(footer);
footerParent.appendChild(footer);
}
}
}
catch(e)
{
}
var language = GetCookie("CodeSnippetContainerLanguage", defLang);
// If LST exists on the page, set the LST to show the user selected programming language
UpdateLST(language);
// If code snippet groups exist, set the current language for them
if(allTabSetIds.length > 0)
{
var i = 0;
while(i < allTabSetIds.length)
{
var tabCount = 1;
// The tab count may vary so find the last one in this set
while(document.getElementById(allTabSetIds[i] + "_tab" + tabCount) != null)
tabCount++;
tabCount--;
// If not grouped, skip it
if(tabCount > 1)
SetCurrentLanguage(allTabSetIds[i], language, tabCount);
i++;
}
}
InitializeToc();
}
// This is just a place holder. The website script implements this function to initialize it's in-page TOC pane
function InitializeToc()
{
}
// This function executes in the OnLoad event and ChangeTab action on code snippets. The function parameter
// is the user chosen programming language. This function iterates through the "allLSTSetIds" dictionary object
// to update the node value of the LST span tag per the user's chosen programming language.
function UpdateLST(language)
{
for(var lstMember in allLSTSetIds)
{
var devLangSpan = document.getElementById(lstMember);
if(devLangSpan != null)
{
// There may be a carriage return before the LST span in the content so the replace function below
// is used to trim the whitespace at the end of the previous node of the current LST node.
if(devLangSpan.previousSibling != null && devLangSpan.previousSibling.nodeValue != null)
devLangSpan.previousSibling.nodeValue = devLangSpan.previousSibling.nodeValue.replace(/\s+$/, "");
var langs = allLSTSetIds[lstMember].split("|");
var k = 0;
var keyValue;
while(k < langs.length)
{
keyValue = langs[k].split("=");
if(keyValue[0] == language)
{
devLangSpan.innerHTML = keyValue[1];
// Help 1 and MS Help Viewer workaround. Add a space if the following text element starts
// with a space to prevent things running together.
if(devLangSpan.parentNode != null && devLangSpan.parentNode.nextSibling != null)
{
if(devLangSpan.parentNode.nextSibling.nodeValue != null &&
!devLangSpan.parentNode.nextSibling.nodeValue.substring(0, 1).match(/[.,);:!/?]/) &&
(isHelp1 || devLangSpan.innerHTML == '>' || devLangSpan.innerHTML == ')'))
{
devLangSpan.innerHTML = keyValue[1] + " ";
}
}
break;
}
k++;
}
// If not found, default to the neutral language. If there is no neutral language entry, clear the
// content to hide it.
if(k >= langs.length)
{
if(language != "nu")
{
k = 0;
while(k < langs.length)
{
keyValue = langs[k].split("=");
if(keyValue[0] == "nu")
{
devLangSpan.innerHTML = keyValue[1];
// Help 1 and MS Help Viewer workaround. Add a space if the following text element
// starts with a space to prevent things running together.
if(devLangSpan.parentNode != null && devLangSpan.parentNode.nextSibling != null)
{
if(devLangSpan.parentNode.nextSibling.nodeValue != null &&
!devLangSpan.parentNode.nextSibling.nodeValue.substring(0, 1).match(/[.,);:!/?]/) &&
(isHelp1 || devLangSpan.innerHTML == '>' || devLangSpan.innerHTML == ')'))
{
devLangSpan.innerHTML = keyValue[1] + " ";
}
}
break;
}
k++;
}
}
if(k >= langs.length)
devLangSpan.innerHTML = "";
}
}
}
}
// Get the specified cookie. If not found, return the specified default value.
function GetCookie(cookieName, defaultValue)
{
if(isHelp1)
{
try
{
var globals = Help1Globals;
var value = globals.Load(cookieName);
if(value == null)
value = defaultValue;
return value;
}
catch(e)
{
return defaultValue;
}
}
var cookie = document.cookie.split("; ");
for(var i = 0; i < cookie.length; i++)
{
var crumb = cookie[i].split("=");
if(cookieName == crumb[0])
return unescape(crumb[1])
}
return defaultValue;
}
// Set the specified cookie to the specified value
function SetCookie(name, value)
{
if(isHelp1)
{
try
{
var globals = Help1Globals;
globals.Save(name, value);
}
catch(e)
{
}
return;
}
var today = new Date();
today.setTime(today.getTime());
// Set the expiration time to be 60 days from now (in milliseconds)
var expires_date = new Date(today.getTime() + (60 * 1000 * 60 * 60 * 24));
document.cookie = name + "=" + escape(value) + ";expires=" + expires_date.toGMTString() + ";path=/";
}
// Add a language-specific text ID
function AddLanguageSpecificTextSet(lstId)
{
var keyValue = lstId.split("?")
allLSTSetIds[keyValue[0]] = keyValue[1];
}
var clipboardHandler;
// Add a language tab set ID
function AddLanguageTabSet(tabSetId)
{
allTabSetIds.push(tabSetId);
// Create the clipboard handler on first use
if(clipboardHandler == null && typeof (Clipboard) == "function")
{
clipboardHandler = new Clipboard('.copyCodeSnippet',
{
text: function (trigger)
{
// Get the code to copy to the clipboard from the active tab of the given tab set
var i = 1, tabSetId = trigger.id;
var pos = tabSetId.indexOf('_');
if(pos == -1)
return "";
tabSetId = tabSetId.substring(0, pos);
do
{
contentId = tabSetId + "_code_Div" + i;
tabTemp = document.getElementById(contentId);
if(tabTemp != null && tabTemp.style.display != "none")
break;
i++;
} while(tabTemp != null);
if(tabTemp == null)
return "";
return document.getElementById(contentId).innerText;
}
});
}
}
// Switch the active tab for all of other code snippets
function ChangeTab(tabSetId, language, snippetIdx, snippetCount)
{
SetCookie("CodeSnippetContainerLanguage", language);
SetActiveTab(tabSetId, snippetIdx, snippetCount);
// If LST exists on the page, set the LST to show the user selected programming language
UpdateLST(language);
var i = 0;
while(i < allTabSetIds.length)
{
// We just care about other snippets
if(allTabSetIds[i] != tabSetId)
{
// Other tab sets may not have the same number of tabs
var tabCount = 1;
while(document.getElementById(allTabSetIds[i] + "_tab" + tabCount) != null)
tabCount++;
tabCount--;
// If not grouped, skip it
if(tabCount > 1)
SetCurrentLanguage(allTabSetIds[i], language, tabCount);
}
i++;
}
}
// Sets the current language in the specified tab set
function SetCurrentLanguage(tabSetId, language, tabCount)
{
var tabIndex = 1;
while(tabIndex <= tabCount)
{
var tabTemp = document.getElementById(tabSetId + "_tab" + tabIndex);
if(tabTemp != null && tabTemp.innerHTML.indexOf("'" + language + "'") != -1)
break;
tabIndex++;
}
if(tabIndex > tabCount)
{
// Select the first non-disabled tab
tabIndex = 1;
if(document.getElementById(tabSetId + "_tab1").className == "codeSnippetContainerTabPhantom")
{
tabIndex++;
while(tabIndex <= tabCount)
{
var tab = document.getElementById(tabSetId + "_tab" + tabIndex);
if(tab.className != "codeSnippetContainerTabPhantom")
{
tab.className = "codeSnippetContainerTabActive";
document.getElementById(tabSetId + "_code_Div" + j).style.display = "block";
break;
}
tabIndex++;
}
}
}
SetActiveTab(tabSetId, tabIndex, tabCount);
}
// Set the active tab within a tab set
function SetActiveTab(tabSetId, tabIndex, tabCount)
{
var i = 1;
while(i <= tabCount)
{
var tabTemp = document.getElementById(tabSetId + "_tab" + i);
if (tabTemp != null)
{
if(tabTemp.className == "codeSnippetContainerTabActive")
tabTemp.className = "codeSnippetContainerTab";
else
if(tabTemp.className == "codeSnippetContainerTabPhantom")
tabTemp.style.display = "none";
var codeTemp = document.getElementById(tabSetId + "_code_Div" + i);
if(codeTemp.style.display != "none")
codeTemp.style.display = "none";
}
i++;
}
// Phantom tabs are shown or hidden as needed
if(document.getElementById(tabSetId + "_tab" + tabIndex).className != "codeSnippetContainerTabPhantom")
document.getElementById(tabSetId + "_tab" + tabIndex).className = "codeSnippetContainerTabActive";
else
document.getElementById(tabSetId + "_tab" + tabIndex).style.display = "block";
document.getElementById(tabSetId + "_code_Div" + tabIndex).style.display = "block";
}
// Copy the code from the active tab of the given tab set to the clipboard
function CopyToClipboard(tabSetId)
{
var tabTemp, contentId;
var i = 1;
if(typeof (Clipboard) == "function")
return;
do
{
contentId = tabSetId + "_code_Div" + i;
tabTemp = document.getElementById(contentId);
if(tabTemp != null && tabTemp.style.display != "none")
break;
i++;
} while(tabTemp != null);
if(tabTemp == null)
return;
if(window.clipboardData)
{
try
{
window.clipboardData.setData("Text", document.getElementById(contentId).innerText);
}
catch(e)
{
alert("Permission denied. Enable copying to the clipboard.");
}
}
else if(window.netscape)
{
try
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var clip = Components.classes["@mozilla.org/widget/clipboard;1"].createInstance(
Components.interfaces.nsIClipboard);
if(!clip)
return;
var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(
Components.interfaces.nsITransferable);
if(!trans)
return;
trans.addDataFlavor("text/unicode");
var str = new Object();
var len = new Object();
var str = Components.classes["@mozilla.org/supports-string;1"].createInstance(
Components.interfaces.nsISupportsString);
var copytext = document.getElementById(contentId).textContent;
str.data = copytext;
trans.setTransferData("text/unicode", str, copytext.length * 2);
var clipid = Components.interfaces.nsIClipboard;
clip.setData(trans, null, clipid.kGlobalClipboard);
}
catch(e)
{
alert("Permission denied. Enter \"about:config\" in the address bar and double-click the \"signed.applets.codebase_principal_support\" setting to enable copying to the clipboard.");
}
}
}
// Expand or collapse a section
function SectionExpandCollapse(togglePrefix)
{
var image = document.getElementById(togglePrefix + "Toggle");
var section = document.getElementById(togglePrefix + "Section");
if(image != null && section != null)
if(section.style.display == "")
{
image.src = image.src.replace("SectionExpanded.png", "SectionCollapsed.png");
section.style.display = "none";
}
else
{
image.src = image.src.replace("SectionCollapsed.png", "SectionExpanded.png");
section.style.display = "";
}
}
// Expand or collapse a section when it has the focus and Enter is hit
function SectionExpandCollapse_CheckKey(togglePrefix, eventArgs)
{
if(eventArgs.keyCode == 13)
SectionExpandCollapse(togglePrefix);
}
// Help 1 persistence object. This requires a hidden input element on the page with a class of "userDataStyle"
// defined in the style sheet that implements the user data binary behavior:
//
var Help1Globals =
{
UserDataCache: function()
{
var userData = document.getElementById("userDataCache");
return userData;
},
Load: function(key)
{
var userData = this.UserDataCache();
userData.load("userDataSettings");
var value = userData.getAttribute(key);
return value;
},
Save: function(key, value)
{
var userData = this.UserDataCache();
userData.setAttribute(key, value);
userData.save("userDataSettings");
}
};
================================================
FILE: docs/search.html
================================================
Solid Soils .NET Library for Arduino Serial Port Communication - Search
Solid Soils .NET Library for Arduino Serial Port Communication - Search