{"id":18,"date":"2019-03-27T22:03:40","date_gmt":"2019-03-27T22:03:40","guid":{"rendered":"https:\/\/manueldobusch.eu\/blog\/?p=18"},"modified":"2024-05-25T15:37:48","modified_gmt":"2024-05-25T15:37:48","slug":"arma-3-desktop-compass","status":"publish","type":"post","link":"https:\/\/manueldobusch.eu\/blog\/index.php\/2019\/03\/27\/arma-3-desktop-compass\/","title":{"rendered":"ARMA 3 Desktop Compass"},"content":{"rendered":"\n<p>This is a little project of mine, creating a custom computer peripheral for one of my <g class=\"gr_ gr_5 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling multiReplace\" id=\"5\" data-gr-id=\"5\">favourite<\/g> PC games, ARMA 3.  It brings one of the most important tools in the game out of the virtual world into the <g class=\"gr_ gr_7 gr-alert gr_gramm gr_inline_cards gr_run_anim Style multiReplace\" id=\"7\" data-gr-id=\"7\">real  world<\/g>: the compass.<\/p>\n\n\n\n<figure class=\"wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Arma 3 Desktop Compass Update 2\" width=\"525\" height=\"295\" src=\"https:\/\/www.youtube.com\/embed\/cqPUOiLcUgA?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_66_1 counter-hierarchy ez-toc-counter ez-toc-custom ez-toc-container-direction\">\n<p class=\"ez-toc-title\">Table of Contents<\/p>\n<label for=\"ez-toc-cssicon-toggle-item-69f3e9d2b7823\" class=\"ez-toc-cssicon-toggle-label\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #161616;color:#161616\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #161616;color:#161616\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/label><input type=\"checkbox\"  id=\"ez-toc-cssicon-toggle-item-69f3e9d2b7823\"  aria-label=\"Toggle\" \/><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/manueldobusch.eu\/blog\/index.php\/2019\/03\/27\/arma-3-desktop-compass\/#Motivation\" title=\"Motivation\">Motivation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/manueldobusch.eu\/blog\/index.php\/2019\/03\/27\/arma-3-desktop-compass\/#Project_Overview\" title=\"Project Overview\">Project Overview<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/manueldobusch.eu\/blog\/index.php\/2019\/03\/27\/arma-3-desktop-compass\/#Wiring\" title=\"Wiring\">Wiring<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/manueldobusch.eu\/blog\/index.php\/2019\/03\/27\/arma-3-desktop-compass\/#Code\" title=\"Code\">Code<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/manueldobusch.eu\/blog\/index.php\/2019\/03\/27\/arma-3-desktop-compass\/#Arduino_Code\" title=\"Arduino Code\">Arduino Code<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/manueldobusch.eu\/blog\/index.php\/2019\/03\/27\/arma-3-desktop-compass\/#Compass_Server\" title=\"Compass Server\">Compass Server<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/manueldobusch.eu\/blog\/index.php\/2019\/03\/27\/arma-3-desktop-compass\/#Arma_Plugin\" title=\"Arma Plugin\">Arma Plugin<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/manueldobusch.eu\/blog\/index.php\/2019\/03\/27\/arma-3-desktop-compass\/#Known_Issues_and_further_Development\" title=\"Known Issues and further Development\">Known Issues and further Development<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/manueldobusch.eu\/blog\/index.php\/2019\/03\/27\/arma-3-desktop-compass\/#Acknowledgement\" title=\"Acknowledgement\">Acknowledgement<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/manueldobusch.eu\/blog\/index.php\/2019\/03\/27\/arma-3-desktop-compass\/#Resources\" title=\"Resources\">Resources<\/a><\/li><\/ul><\/nav><\/div>\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Motivation\"><\/span>Motivation<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>I have been playing ARMA 3 for years now, not in small parts thanks to <a rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\" href=\"http:\/\/ciahome.net\/\" target=\"_blank\">those guys<\/a>. One evening playing with them I find myself, as so often, in need of passing on a compass direction to my teammates.<\/p>\n\n\n\n<p>It just so happened that that evening I had my hiking compass lying on my desk in front of me. Almost out of reflex I looked down on that compass instead of opening the one ingame.<\/p>\n\n\n\n<p>My immediate thought on noticing my mistake was somewhere along the line of &#8220;oh man, would be cool if it would display my ingame orientation&#8221;. The idea stuck and the result can be seen in the video linked above.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Project_Overview\"><\/span>Project Overview<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"768\" src=\"https:\/\/manueldobusch.eu\/blog\/wp-content\/uploads\/2019\/03\/IMG_20190328_135835-1024x768.jpg\" alt=\"\" class=\"wp-image-72\" srcset=\"https:\/\/manueldobusch.eu\/blog\/wp-content\/uploads\/2019\/03\/IMG_20190328_135835-1024x768.jpg 1024w, https:\/\/manueldobusch.eu\/blog\/wp-content\/uploads\/2019\/03\/IMG_20190328_135835-300x225.jpg 300w, https:\/\/manueldobusch.eu\/blog\/wp-content\/uploads\/2019\/03\/IMG_20190328_135835-768x576.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><figcaption>The finished compass<\/figcaption><\/figure>\n\n\n\n<p>The project is built upon an Arduino Metro Mini. The compass scale is printed onto an overhead projector sheet and sits on a stepper motor. A rotary encoder turns the scale to manually adjust it to the initial <g class=\"gr_ gr_7 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling\" id=\"7\" data-gr-id=\"7\">ingame<\/g> direction. This is necessary since the stepper motor isn&#8217;t innately aware of its orientation. Lastly, I put in a green LED as <g class=\"gr_ gr_9 gr-alert gr_gramm gr_inline_cards gr_run_anim Grammar only-ins doubleReplace replaceWithoutSep\" id=\"9\" data-gr-id=\"9\">backlight<\/g> for the scale and a potentiometer to dim that LED.<\/p>\n\n\n\n<p>On the software side, the project is split into three parts. There is the code running on the Arduino. A separate application receives new directions via <g class=\"gr_ gr_8 gr-alert gr_gramm gr_inline_cards gr_run_anim Grammar only-ins doubleReplace replaceWithoutSep\" id=\"8\" data-gr-id=\"8\">socket<\/g> and passes it on to the Arduino via Serial Port. Lastly, there is the plugin for ARMA 3 which periodically sends out direction updates to the socket.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Wiring\"><\/span>Wiring<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>I will not go into great detail about the hardware used. I am not very qualified to write about those things and other people have written great tutorials for all parts used here. I will link everything you need to build this on your own. <\/p>\n\n\n\n<p>The parts used are:<\/p>\n\n\n\n<ul><li><a rel=\"noreferrer noopener\" aria-label=\"Adafruit Metro Mini (opens in a new tab)\" href=\"https:\/\/www.adafruit.com\/product\/2590\" target=\"_blank\">Adafruit Metro Mini<\/a><\/li><li><a rel=\"noreferrer noopener\" aria-label=\"28BYJ-48 (opens in a new tab)\" href=\"https:\/\/www.amazon.de\/Elegoo-Stepper-Schrittmotor-28BYJ-48-Treiberplatine\/dp\/B01MEGIHLF\/ref=sr_1_1_sspa?ie=UTF8&amp;qid=1541087356&amp;sr=8-1-spons&amp;keywords=stepper+motor&amp;psc=1\" target=\"_blank\">28BYJ-48<\/a><\/li><li><a rel=\"noreferrer noopener\" aria-label=\"KY-040 (opens in a new tab)\" href=\"https:\/\/www.amazon.de\/gp\/product\/B07D356LRH\/ref=oh_aui_detailpage_o05_s01?ie=UTF8&amp;psc=1\" target=\"_blank\">KY-040<\/a><\/li><li><a href=\"https:\/\/www.conrad.com\/p\/piher-pc16sh-10ip06104a2020mta-mono-potentiometer-445608\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"PC16SH (opens in a new tab)\">PC16SH<\/a><\/li><li>LED (+ resistor)<\/li><\/ul>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1003\" height=\"1024\" src=\"https:\/\/manueldobusch.eu\/blog\/wp-content\/uploads\/2019\/03\/compassWiring-1003x1024.png\" alt=\"\" class=\"wp-image-51\" srcset=\"https:\/\/manueldobusch.eu\/blog\/wp-content\/uploads\/2019\/03\/compassWiring-1003x1024.png 1003w, https:\/\/manueldobusch.eu\/blog\/wp-content\/uploads\/2019\/03\/compassWiring-294x300.png 294w, https:\/\/manueldobusch.eu\/blog\/wp-content\/uploads\/2019\/03\/compassWiring-768x784.png 768w, https:\/\/manueldobusch.eu\/blog\/wp-content\/uploads\/2019\/03\/compassWiring.png 1539w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><figcaption>Complete wiring diagram<\/figcaption><\/figure>\n\n\n\n<p>The <strong>stepper motor<\/strong> used is a <a rel=\"noreferrer noopener\" aria-label=\"28BYJ-48 (opens in a new tab)\" href=\"https:\/\/www.amazon.de\/Elegoo-Stepper-Schrittmotor-28BYJ-48-Treiberplatine\/dp\/B01MEGIHLF\/ref=sr_1_1_sspa?ie=UTF8&amp;qid=1541087356&amp;sr=8-1-spons&amp;keywords=stepper+motor&amp;psc=1\" target=\"_blank\">28BYJ-48<\/a>. The motor itself is connected to the driver board (a ULN2003 in this case). The driver board connects to the Arduino itself. The + pin on the board is connected to the 5V pin on the Arduino, while ground connects to ground of course.<\/p>\n\n\n\n<p>The pins IN1 through IN4 on the driver board are connected to digital pins 8 through 11 on the Arduino respectively.<\/p>\n\n\n\n<p><a href=\"https:\/\/www.mschoeffler.de\/2017\/09\/23\/tutorial-how-to-drive-the-28byj-48-stepper-motor-with-a-uln2003a-driver-board-and-an-arduino-uno\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Michael Schoeffler (opens in a new tab)\">Michael Schoeffler<\/a> has a great article on using this stepper motor with Arduinos if you want a more in-depth explanation. As he points out there is also a caveat when connecting the motor directly to the Arduino for power supply.<\/p>\n\n\n\n<p>Ideally, the motor would be connected to an external source. However, in this <g class=\"gr_ gr_4 gr-alert gr_gramm gr_inline_cards gr_run_anim Punctuation only-ins replaceWithoutSep\" id=\"4\" data-gr-id=\"4\">case<\/g> it is never put under any stress so I figure it is safe to power it directly via the Arduino.<\/p>\n\n\n\n<p>The<strong> rotary encoder<\/strong> is a <a rel=\"noreferrer noopener\" aria-label=\"KY-040 (opens in a new tab)\" href=\"https:\/\/www.amazon.de\/gp\/product\/B07D356LRH\/ref=oh_aui_detailpage_o05_s01?ie=UTF8&amp;psc=1\" target=\"_blank\">KY-040<\/a>. It connects to the USB pin on the Arduino for power, and to ground of course. The DT and CLK pins on the encoder connect to digital pins 2 and 3.<\/p>\n\n\n\n<p>Note that the DT and CLK pins ideally connect to interrupt pins on the Arduino. See the documentation of the <a rel=\"noreferrer noopener\" aria-label=\"Encoder library (opens in a new tab)\" href=\"https:\/\/www.pjrc.com\/teensy\/td_libs_Encoder.html\" target=\"_blank\">Encoder library<\/a> for details. <\/p>\n\n\n\n<p>Connecting the<strong> LED<\/strong> is pretty straight forward. The ground pin connects to ground, while the + pin connects to one of the digital pins, pin 6 in this case. This way the LED can be <a rel=\"noreferrer noopener\" aria-label=\"dimmed (opens in a new tab)\" href=\"https:\/\/www.arduino.cc\/en\/tutorial\/fade\" target=\"_blank\">dimmed<\/a>. Make sure that you put the correct resistor between the digital pin and the + pin for the LED you use.<\/p>\n\n\n\n<p>The <strong>potentiometer<\/strong> connects to the analog pin A1, and ground and USB (power) of course.<\/p>\n\n\n\n<p>The video below shows the components (minus the LED and potentiometer) at an early stage of development. So you see all the innards exposed.<\/p>\n\n\n\n<figure class=\"wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Arma 3 Desktop Compass Update 1\" width=\"525\" height=\"295\" src=\"https:\/\/www.youtube.com\/embed\/AcUBMppHpaI?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Code\"><\/span>Code<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>The coding part of this project is split into three modules: the Arduino code, the game plugin, and a compass server to communicate between the former two.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"320\" src=\"https:\/\/manueldobusch.eu\/blog\/wp-content\/uploads\/2019\/03\/compassDiagram-1024x320.png\" alt=\"\" class=\"wp-image-113\" srcset=\"https:\/\/manueldobusch.eu\/blog\/wp-content\/uploads\/2019\/03\/compassDiagram-1024x320.png 1024w, https:\/\/manueldobusch.eu\/blog\/wp-content\/uploads\/2019\/03\/compassDiagram-300x94.png 300w, https:\/\/manueldobusch.eu\/blog\/wp-content\/uploads\/2019\/03\/compassDiagram-768x240.png 768w, https:\/\/manueldobusch.eu\/blog\/wp-content\/uploads\/2019\/03\/compassDiagram.png 1113w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><figcaption>Sweet professional high-level diagram of the software stack<\/figcaption><\/figure>\n\n\n\n<p>Adding the server application in between removes a lot of responsibility from the game plugin. This is good since the server as such is much easier to debug and maintain than the plugin.<\/p>\n\n\n\n<p>I&#8217;m providing all the code in git repos. I will try to concisely describe all the essential parts below.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Arduino_Code\"><\/span>Arduino Code<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>You can find the complete source code <a rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\" href=\"https:\/\/github.com\/wrongway88\/armaCompassArduino\" target=\"_blank\">On Github<\/a>.  I will only discuss the key elements here.  I will not discuss how to control the hardware parts here. Check out the <a rel=\"noreferrer noopener\" aria-label=\"stepper motor (opens in a new tab)\" href=\"https:\/\/www.arduino.cc\/en\/Reference\/Stepper\" target=\"_blank\">stepper motor<\/a> and <a href=\"https:\/\/www.pjrc.com\/teensy\/td_libs_Encoder.html\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"rotary encoder (opens in a new tab)\">rotary encoder<\/a> libraries if you want more details on that.<\/p>\n\n\n\n<p>The main loop of the code performs a few steps to update the compass scale and the backlight. In abridged code it reads something like this.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void loop()\n{\n  \/\/ update backlight\n  _brightness = readPotentiometer();\n  setLEDBrightness (_brightness);\n\n  \/\/ get encoder position\n  _encoderPos = readEncoderPos();\n  _encoderDiff = _encoderPos - _lastEncoderPos;\n  _lastEncoderPos = _encoderPos;\n\n  \/\/ get target direction to be displayed by the compass\n  _targetDir = readSerialInput();\n  _rotationSteps = calculateSteps(_targetDir);\n\n  \/\/ rotate the compass scale\n  _stepper.step(_rotationSteps + (_encoderDiff * _calibrationSpeed));\n}<\/pre>\n\n\n\n<p>Noteworthy are the <g class=\"gr_ gr_23 gr-alert gr_gramm gr_inline_cards gr_run_anim Grammar only-ins replaceWithoutSep\" id=\"23\" data-gr-id=\"23\">functions<\/g> <g class=\"gr_ gr_9 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling ins-del multiReplace\" id=\"9\" data-gr-id=\"9\"><em class=\"\">readSerialInput<\/em><\/g> and <em><g class=\"gr_ gr_33 gr-alert gr_spell gr_inline_cards gr_disable_anim_appear ContextualSpelling ins-del multiReplace\" id=\"33\" data-gr-id=\"33\">calculateSteps<\/g><\/em>.<\/p>\n\n\n\n<p><em><g class=\"gr_ gr_5 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling ins-del multiReplace\" id=\"5\" data-gr-id=\"5\">readSerialInput<\/g> <\/em>reads messages send by the Compass Server application via <g class=\"gr_ gr_7 gr-alert gr_gramm gr_inline_cards gr_run_anim Grammar only-ins doubleReplace replaceWithoutSep\" id=\"7\" data-gr-id=\"7\">serial<\/g> port. By default, Arduino uses a one-second timeout to read messages from the serial port. I do not use any timeout to minimize the delay of the compass scale after the player moves.<\/p>\n\n\n\n<p>This however means that I might end up cutting of the end of messages and therefore ending up with an incorrect direction. I mitigate this by the use of tokens to mark the end of a message.<\/p>\n\n\n\n<p>Messages are aggregated in a string buffer. The buffer may contain multiple messages and end with an incomplete message. To deal with this, only the last complete message is used and taken to be the new target direction for the compass.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\"113;121;127;12\"<\/pre>\n\n\n\n<p>Take this string as an example. &#8216;;&#8217; is the token to separate messages. The last complete message is &#8220;127&#8221;. Everything before that will be discarded as it is already in the past. &#8220;127&#8221; is set as the new target direction. Everything after that will be kept in the buffer because the rest of the message is expected to come in later.<\/p>\n\n\n\n<p>In abridged code it looks something like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void readSerialInput()\n{\n  _input += Serial.readString();\n\n  if((idx = _input.indexOf(';')) > -1)\n  {\n    if(idx == _input.length() -1)\n    {\n      \/\/ token is at the very end\n      _targetDir = _input;\n    }\n    else\n    {\n      \/\/ cut away incomplete message\n      _tmpStr = _input.substring(0, _input.lastIndexOf(';'));\n\n      \/\/ cut away old messages\n      while((tmpIdx = _tmpStr.indexOf(';')) > -1)\n      {\n        _tmpStr = _input.substring(tmpIdx+1, _tmpStr.length());\n      }\n\n      _targetDir = _tmpString;\n\n      \/\/ keep incomplete message for later\n      _input = _input.substring(idx+1, _input.length());\n    }\n  }\n}<\/pre>\n\n\n\n<p><em><g class=\"gr_ gr_6 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling ins-del multiReplace\" id=\"6\" data-gr-id=\"6\">calculateSteps<\/g><\/em> simply calculates how many steps the stepper motor has to take to reach the new target direction. The goal is to do the minimum amount of steps. So when the compass is supposed to turn from north to east it should turn to clockwise, not counterclockwise.<\/p>\n\n\n\n<p>Firstly the target direction is converted to steps from <g class=\"gr_ gr_4 gr-alert gr_gramm gr_inline_cards gr_run_anim Grammar only-ins replaceWithoutSep\" id=\"4\" data-gr-id=\"4\">north<\/g>. So if we have 2048 steps per revolution, south (180\u00b0) would be 1024.<\/p>\n\n\n\n<p>Next, simply subtract the current direction in steps from the target direction in steps. If the resulting step count is more than half a revolution, the direction is reversed.<\/p>\n\n\n\n<p>In abridged code it looks something like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int calculateSteps(int targetDir)\n{\n  stepsToTurn = targetDir - _currentDir;\n\n  if(stepsToTurn > _stepsPerRotation \/ 2)\n  {\n    stepsToTurn = targetDir - (_currentDir + _stepsPerRotation);\n  }\n  else if(stepsToTurn &lt; -(_stepsPerRotation \/ 2))\n  {\n    stepsToTurn = (targetDir + _stepsPerRotation) - _currentDir;\n  }\n\n  return stepsToTurn;\n}<\/pre>\n\n\n\n<p>One last thing to handle is that the player may turn around quickly, while the motor speed is pretty limited. In the worst <g class=\"gr_ gr_53 gr-alert gr_gramm gr_inline_cards gr_run_anim Punctuation only-ins replaceWithoutSep\" id=\"53\" data-gr-id=\"53\">case<\/g> the player quickly <g class=\"gr_ gr_52 gr-alert gr_gramm gr_inline_cards gr_run_anim Grammar multiReplace\" id=\"52\" data-gr-id=\"52\">turn<\/g> around 180 degrees twice. The compass will take three or four seconds to catch up.<\/p>\n\n\n\n<p>To mitigate this the amount of steps the motor is allowed to turn during each update cycle is limited. This allows the target direction to change before the compass scale has reached the old one, therefore the compass can react much quicker to fast player movement.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Compass_Server\"><\/span>Compass Server<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>You can find the complete source code <a rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\" href=\"https:\/\/github.com\/wrongway88\/armaCompassServer\" target=\"_blank\">On Github<\/a>. I will only discuss the key elements here. <\/p>\n\n\n\n<p>The compass server exists to receive messages via socket from the game plugin, described below, to the compass itself, described above.<\/p>\n\n\n\n<p>To achieve this it has three distinct modules. Firstly it creates a server listening to a predefined port.  Secondly, it sends and receives messages via <g class=\"gr_ gr_203 gr-alert gr_gramm gr_inline_cards gr_run_anim Grammar only-ins doubleReplace replaceWithoutSep\" id=\"203\" data-gr-id=\"203\">serial<\/g> port.  Lastly, it uses a double-message-buffer to accommodate asynchronous communication.<\/p>\n\n\n\n<p>The server code is directly taken from this <a rel=\"noreferrer noopener\" aria-label=\"example (opens in a new tab)\" href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/framework\/network-programming\/asynchronous-server-socket-example\" target=\"_blank\">example<\/a> on the Microsoft website. The only noteworthy addition is a slot for a callback function to pass on incoming messages to the applications message buffer.<\/p>\n\n\n\n<p>Similarly the serial port code is simple and mostly taken from an available <a rel=\"noreferrer noopener\" aria-label=\"example (opens in a new tab)\" href=\"https:\/\/www.instructables.com\/id\/Serial-Port-Programming-With-NET\/\" target=\"_blank\">example<\/a>.<\/p>\n\n\n\n<p>The message buffer connects the server and the serial port. It maintains two message queues, a read- and a write-queue. Incoming messages from the server are put into the <g class=\"gr_ gr_5 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling ins-del\" id=\"5\" data-gr-id=\"5\">write-queue<\/g>.<\/p>\n\n\n\n<p>Periodically, a message is taken from the read-queue to be sent to the compass via <g class=\"gr_ gr_4 gr-alert gr_gramm gr_inline_cards gr_run_anim Grammar only-ins doubleReplace replaceWithoutSep\" id=\"4\" data-gr-id=\"4\">serial<\/g> port. Is the read-queue empty, the two queues switch roles. In abridged code it looks like below.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void MessageBufferLoop()\n{\n  while(true)\n  {\n    if(_readBuffer.empty() == false)\n    {\n      writeToSerialPort(_readBuffer.Dequeue());\n    }\n    else\n    {\n      _readBuffer = _writeBuffer();\n      _writeBuffer = new Queue&lt;string>();\n    }\n  }\n}<\/pre>\n\n\n\n<p>At the same time, the serial port is checked for incoming messages.<\/p>\n\n\n\n<p>The server application also has a rudimentary GUI to display incoming and outgoing messages. It also allows to manually send messages to the compass for testing and debugging.<br><\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Arma_Plugin\"><\/span>Arma Plugin<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>You can find the complete source code <a rel=\"noreferrer noopener\" aria-label=\"On Github (opens in a new tab)\" href=\"https:\/\/github.com\/wrongway88\/armaCompassPlugin\" target=\"_blank\">On Github<\/a>. I will only discuss the key elements here.<\/p>\n\n\n\n<p>This project consists of two parts. Firstly the ARMA 3 mod, and secondly a .dll file.<\/p>\n\n\n\n<p>The mod is nothing more than a script that periodically calls the .dll with the current compass direction of the player. <br><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">while{true}\ndo\n{\n  _d = direction player;\n\n  _result = \"CompassClient\" callExtension [\"updateCompass\", [_d]];\n\n  sleep 0.1;\n};<\/pre>\n\n\n\n<p>The important part here is <em>callExtension<\/em>. This is how the .dll is called. Check out the <a rel=\"noreferrer noopener\" aria-label=\"documentation (opens in a new tab)\" href=\"https:\/\/community.bistudio.com\/wiki\/callExtension\" target=\"_blank\">documentation<\/a> for details.<\/p>\n\n\n\n<p>The dll passes on the compass direction via socket to the Compass Server.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void updateCompass(float direction)\n{\n  \/\/ open socket on local host with port 56172\n\n  \/\/ send direction\n\n  \/\/ close socket (no answer expected)\n}<\/pre>\n\n\n\n<p>This function uses <g class=\"gr_ gr_4 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling ins-del multiReplace\" id=\"4\" data-gr-id=\"4\"><g class=\"gr_ gr_4 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling ins-del multiReplace\" id=\"4\" data-gr-id=\"4\">winsock<\/g><\/g>. See the <a rel=\"noreferrer noopener\" aria-label=\"documentation (opens in a new tab)\" href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/desktop\/winsock\/winsock-client-application\" target=\"_blank\">documentation<\/a> for <g class=\"gr_ gr_5 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling ins-del multiReplace\" id=\"5\" data-gr-id=\"5\">winsock<\/g> and the source code on <g class=\"gr_ gr_6 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling ins-del multiReplace\" id=\"6\" data-gr-id=\"6\">github<\/g> for details.<\/p>\n\n\n\n<p>To be usable as an ARMA 3 extension the dll needs to implement the API described in the <a href=\"https:\/\/community.bistudio.com\/wiki\/callExtension\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"documentation (opens in a new tab)\">documentation<\/a>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">extern \"C\"\n{\n    __declspec (dllexport) void __stdcall RVExtensionVersion(char* output, int outputSize);\n    __declspec (dllexport) void __stdcall RVExtension(char* output, int outputSize, const char* function);\n    __declspec (dllexport) int __stdcall RVExtensionArgs(char* output, int outputSize, const char* function, const char** args, int argsCnt);\n}<\/pre>\n\n\n\n<p><em>RVExtensionArgs <\/em>parses the argument given by the calling script above and passes it on to <em>updateCompass<\/em>.<br><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int __stdcall RVExtensionArgs(char* output, int outputSize, const char* function, const char** args, int argsCnt)\n{\n  if (strcmp(function, \"updateCompass\") == 0)\n  {\n    if (argsCnt >= 1)\n    {\n      updateCompass(atof(args[0]));\n      strncpy_s(output, outputSize, \"updating compass\", _TRUNCATE);\n    }\n\n    return 100;\n  }\n\n  strncpy_s(output, outputSize, \"void\", _TRUNCATE);\n\n  return 0;\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Known_Issues_and_further_Development\"><\/span>Known Issues and further Development<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>The stepper motor used in the project, the 28BYJ-48, is not the most precise piece of hardware you will ever find (it is however possibly the cheapest stepper motor you will find). As a result the compass does not always perfectly display an exact direction. In <g class=\"gr_ gr_8 gr-alert gr_gramm gr_inline_cards gr_run_anim Punctuation only-ins replaceWithoutSep\" id=\"8\" data-gr-id=\"8\">fact<\/g> it can drift somewhat over time and manual readjustment can be necessary.<\/p>\n\n\n\n<p>To learn more about the issues involved when using this specific motor check out this great <a rel=\"noreferrer noopener\" aria-label=\"article (opens in a new tab)\" href=\"http:\/\/www.jangeox.be\/2013\/10\/stepper-motor-28byj-48_25.html\" target=\"_blank\">article<\/a> by <g class=\"gr_ gr_15 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling ins-del multiReplace\" id=\"15\" data-gr-id=\"15\">Jangeox<\/g>.<br><\/p>\n\n\n\n<p>Precision-issues aside, there are some features I didn&#8217;t end up implementing. The possibly most interesting one would have been automatic calibration.<\/p>\n\n\n\n<p>Since stepper motors do not have knowledge of their current orientation some other way to determine this information would be necessary. I only have vague ideas for that. I could imagine using a simple mechanical switch or maybe a hall-sensor.<br><\/p>\n\n\n\n<p>I intend to look into bringing this project to different games in the future. Basically, any game that offers sufficient modding-support is fair game.<br><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Acknowledgement\"><\/span>Acknowledgement<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Thanks to the guys of <a rel=\"noreferrer noopener\" aria-label=\"FHQ (opens in a new tab)\" href=\"http:\/\/friedenhq.org\/\" target=\"_blank\">FHQ<\/a> for helping out with the ARMA plugin.<br>Thanks to my father, basically all the credit for the awesome aluminium case goes to him.<br>Also thanks to all the guys in <a rel=\"noreferrer noopener\" aria-label=\"CiA (opens in a new tab)\" href=\"http:\/\/ciahome.net\/\" target=\"_blank\">CiA<\/a> for keeping the game fun to play after all those years.<br>And of course thanks to <a rel=\"noreferrer noopener\" aria-label=\"Bohemia Interactive (opens in a new tab)\" href=\"https:\/\/www.bohemia.net\/\" target=\"_blank\">Bohemia Interactive<\/a> and <a rel=\"noreferrer noopener\" aria-label=\"Arduino (opens in a new tab)\" href=\"https:\/\/www.arduino.cc\/\" target=\"_blank\">Arduino<\/a> for making this project possible.<br><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Resources\"><\/span>Resources<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<ul><li><a href=\"https:\/\/www.mschoeffler.de\/2017\/09\/23\/tutorial-how-to-drive-the-28byj-48-stepper-motor-with-a-uln2003a-driver-board-and-an-arduino-uno\/\">https:\/\/www.mschoeffler.de\/2017\/09\/23\/tutorial-how-to-drive-the-28byj-48-stepper-motor-with-a-uln2003a-driver-board-and-an-arduino-uno\/<\/a><\/li><li>  <a rel=\"noreferrer noopener\" href=\"https:\/\/www.arduino.cc\/en\/Reference\/Stepper\" target=\"_blank\">https:\/\/www.arduino.cc\/en\/Reference\/Stepper<\/a> <br><\/li><li><a href=\"http:\/\/henrysbench.capnfatz.com\/henrys-bench\/arduino-sensors-and-input\/keyes-ky-040-arduino-rotary-encoder-user-manual\/\">http:\/\/henrysbench.capnfatz.com\/henrys-bench\/arduino-sensors-and-input\/keyes-ky-040-arduino-rotary-encoder-user-manual\/<\/a><\/li><li><a href=\"https:\/\/www.pjrc.com\/teensy\/td_libs_Encoder.html\">https:\/\/www.pjrc.com\/teensy\/td_libs_Encoder.html<\/a><\/li><li><a href=\"https:\/\/www.arduino.cc\/en\/tutorial\/fade\">https:\/\/www.arduino.cc\/en\/tutorial\/fade<\/a><\/li><li><a href=\"https:\/\/www.instructables.com\/id\/How-to-use-Potentiometer-Arduino-Tutorial\/\">https:\/\/www.instructables.com\/id\/How-to-use-Potentiometer-Arduino-Tutorial\/<\/a><\/li><li><a rel=\"noreferrer noopener\" aria-label=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/framework\/network-programming\/asynchronous-server-socket-example (opens in a new tab)\" href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/framework\/network-programming\/asynchronous-server-socket-example\" target=\"_blank\">https:\/\/docs.microsoft.com\/en-us\/dotnet\/framework\/network-programming\/asynchronous-server-socket-example<\/a><\/li><li><a rel=\"noreferrer noopener\" aria-label=\"https:\/\/www.instructables.com\/id\/Serial-Port-Programming-With-NET\/ (opens in a new tab)\" href=\"https:\/\/www.instructables.com\/id\/Serial-Port-Programming-With-NET\/\" target=\"_blank\">https:\/\/www.instructables.com\/id\/Serial-Port-Programming-With-NET\/<\/a><\/li><li><a rel=\"noreferrer noopener\" aria-label=\"https:\/\/docs.microsoft.com\/en-us\/windows\/desktop\/winsock\/winsock-client-application (opens in a new tab)\" href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/desktop\/winsock\/winsock-client-application\" target=\"_blank\">https:\/\/docs.microsoft.com\/en-us\/windows\/desktop\/winsock\/winsock-client-application<\/a><\/li><li><a rel=\"noreferrer noopener\" aria-label=\"https:\/\/community.bistudio.com\/wiki\/callExtension (opens in a new tab)\" href=\"https:\/\/community.bistudio.com\/wiki\/callExtension\" target=\"_blank\">https:\/\/community.bistudio.com\/wiki\/callExtension<\/a><\/li><\/ul>\n\n\n\n<p><br><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is a little project of mine, creating a custom computer peripheral for one of my favourite PC games, ARMA 3. It brings one of the most important tools in the game out of the virtual world into the real  world: the compass.<\/p>\n","protected":false},"author":1,"featured_media":72,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[3,2],"_links":{"self":[{"href":"https:\/\/manueldobusch.eu\/blog\/index.php\/wp-json\/wp\/v2\/posts\/18"}],"collection":[{"href":"https:\/\/manueldobusch.eu\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/manueldobusch.eu\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/manueldobusch.eu\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/manueldobusch.eu\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=18"}],"version-history":[{"count":128,"href":"https:\/\/manueldobusch.eu\/blog\/index.php\/wp-json\/wp\/v2\/posts\/18\/revisions"}],"predecessor-version":[{"id":149,"href":"https:\/\/manueldobusch.eu\/blog\/index.php\/wp-json\/wp\/v2\/posts\/18\/revisions\/149"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/manueldobusch.eu\/blog\/index.php\/wp-json\/wp\/v2\/media\/72"}],"wp:attachment":[{"href":"https:\/\/manueldobusch.eu\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=18"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/manueldobusch.eu\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=18"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/manueldobusch.eu\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=18"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}