<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.0">Jekyll</generator><link href="https://blog.ja-ke.tech/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.ja-ke.tech/" rel="alternate" type="text/html" /><updated>2022-03-20T11:24:06+00:00</updated><id>https://blog.ja-ke.tech/feed.xml</id><title type="html">Jake’s Blog</title><subtitle></subtitle><author><name>Jake</name></author><entry><title type="html">Mainboard RGB Fan Control with OpenRGB</title><link href="https://blog.ja-ke.tech/2022/03/19/openrgb-fans.html" rel="alternate" type="text/html" title="Mainboard RGB Fan Control with OpenRGB" /><published>2022-03-19T00:00:00+00:00</published><updated>2022-03-19T00:00:00+00:00</updated><id>https://blog.ja-ke.tech/2022/03/19/openrgb-fans</id><content type="html" xml:base="https://blog.ja-ke.tech/2022/03/19/openrgb-fans.html">&lt;p&gt;Finally following up on my previous &lt;a href=&quot;/2019/06/11/re-coolmoon-rgb-controller.html&quot;&gt;post about the coolmoon controller mod&lt;/a&gt;, this post shows how to connect it with a mainboard header and do cool (pun intended) animations with OpenRGB.&lt;/p&gt;

&lt;h1 id=&quot;hardware-setup&quot;&gt;Hardware Setup&lt;/h1&gt;
&lt;p&gt;First of all a mainboard is required that has at least one header for 5V addressable RGB LEDs. I upgraded to an Gigabyte B550 Aorus Pro V2 and that what I am using for this project, it can provide up to 5A and control 1000 LEDs. 
As case I have a Fractal Design Meshify C Dark.&lt;/p&gt;

&lt;h2 id=&quot;mainboard-connection&quot;&gt;Mainboard Connection&lt;/h2&gt;
&lt;p&gt;The header connector cable to the previously made fan hub is really simple:
&lt;img src=&quot;/assets/openrgb/hw-cable.jpg&quot; alt=&quot;coolmoon fan cable ARGB header&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For the mainboard side I cut a 4 pin piece from a standard 0.1 in (2.54 mm) pitch header bar. Actually only 3 pins are used, they use a wider header and don’t equip one pin to make wrong connections impossible, as wrong polarity would most certainly destroy the fans.&lt;/p&gt;

&lt;p&gt;Then it goes to the 3 pin input directly:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Nr.&lt;/th&gt;
      &lt;th&gt;MainBoard Pin&lt;/th&gt;
      &lt;th&gt;LED Pin&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;v (5V)&lt;/td&gt;
      &lt;td&gt;+5V&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;Data&lt;/td&gt;
      &lt;td&gt;Data in&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;No Pin&lt;/td&gt;
      &lt;td&gt;-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;GND&lt;/td&gt;
      &lt;td&gt;GND&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;I wrapped some tape around the hub, so the exposed solder joints can’t touch anything, which could otherwise cause a short circut with other parts in the case.&lt;/p&gt;

&lt;p&gt;You can also see the cable in the output port, this is for the following part.&lt;/p&gt;

&lt;h2 id=&quot;3d-printed-sign&quot;&gt;3D printed sign&lt;/h2&gt;
&lt;p&gt;In addition to the normal fan I though it would be nice to have an custom sign with some letters from my name. Idea was to use compatible WS2812B strip part to illuminate it.
&lt;img src=&quot;/assets/openrgb/sign-cad.png&quot; alt=&quot;JK sign cad model&quot; /&gt;
Overall there are 17 LEDs (6+5+6) in the so called 5050 format, meaning 5 x 5 mm LEDs.&lt;/p&gt;

&lt;p&gt;Then I printed it in transparent PETG, cut 3 pieces of the LED strip, joined then with some 3 pin cables and secured everything to the sign with a bit of hot glue:
&lt;img src=&quot;/assets/openrgb/hw-sign.jpg&quot; alt=&quot;coolmoon fan cable ARGB header&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To my surprise the first revision already fitted well to LEDs. This is mainly because I could put in high tolerances, especially on the “J” bend part, after the strip is put on the holes are not visible anyway.&lt;/p&gt;

&lt;h1 id=&quot;openrgb-configuration&quot;&gt;OpenRGB configuration&lt;/h1&gt;
&lt;p&gt;Now to the software part. OpenRGB is very powerful open source control program, that supports not only the RGB header, but also for example the popular Trident Z RGB RAM and AMD CPU cooler.&lt;/p&gt;
&lt;h2 id=&quot;plugins&quot;&gt;Plugins&lt;/h2&gt;
&lt;p&gt;We need 2 plugins for the following setup. Installation is done through settings - plugins - install plugin (button) - select the downloaded package:
&lt;img src=&quot;/assets/openrgb/plugins.png&quot; alt=&quot;OpenRGB settings plugin install&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Successfully installed plugins show up as additional tabs on the top.&lt;/p&gt;

&lt;h3 id=&quot;visual-map&quot;&gt;Visual Map&lt;/h3&gt;
&lt;p&gt;Adds an interface to organize the LEDs in a virtual space, like they are physically installed.
Really crucial to to get a proper picture with these fans that wrap sequentially addressed LED strips around circles.
&lt;a href=&quot;https://gitlab.com/OpenRGBDevelopers/OpenRGBVisualMapPlugin&quot;&gt;https://gitlab.com/OpenRGBDevelopers/OpenRGBVisualMapPlugin&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;effects&quot;&gt;Effects&lt;/h3&gt;
&lt;p&gt;This adds various animation, vanilla openRGB has only manual control.
&lt;a href=&quot;https://gitlab.com/OpenRGBDevelopers/OpenRGBEffectsPlugin&quot;&gt;https://gitlab.com/OpenRGBDevelopers/OpenRGBEffectsPlugin&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;fans--sign&quot;&gt;Fans + sign&lt;/h2&gt;
&lt;p&gt;The mainboard header get’s auto detected on startup. Only thing to do is to tell the software how many LEDs are connected, with a click on resize:
&lt;img src=&quot;/assets/openrgb/main-resize.png&quot; alt=&quot;OpenRGB resize device&quot; /&gt;
Each coolmoon fan has 16 LEDs, so for me it is 4*16 = 64 plus the sign, makes it overall 81 LEDs. As mentioned above manual control can be done on this page, it is useful for testing if the hardware works correctly.&lt;/p&gt;
&lt;h2 id=&quot;wled&quot;&gt;WLED&lt;/h2&gt;
&lt;p&gt;External lights can be also hooked up, here I connect my ESP32 installation with &lt;a href=&quot;https://github.com/Aircoookie/WLED&quot;&gt;WLED firmware&lt;/a&gt;, control data is sent over the WiFi home network.
&lt;img src=&quot;/assets/openrgb/e1-31.png&quot; alt=&quot;OpenRGB DMX device&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The DMX protocol also has to be enabled in WLEDs interface, like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/openrgb/wled-dmx.png&quot; alt=&quot;WLED DMX server&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then it shows up as device and can be controlled as usual. I am using it only as ambient backlight, not for effects.&lt;/p&gt;

&lt;h2 id=&quot;positional-mapping&quot;&gt;Positional mapping&lt;/h2&gt;
&lt;p&gt;Now to the visual map plugin configuration. It provides a virtual space of custom size to map out the various LED devices and control it as one.
&lt;img src=&quot;/assets/openrgb/mapping.png&quot; alt=&quot;OpenRGB visual map&quot; /&gt;
To build a new map first select the devices on the left side with the + buttons. Then they show up in the mid section, can be selected and dragged around. 
Make sure there is enough virtual space for your creation with the width/height setting on the right. Also it is advisable to give the mapping a name in the top tab section and save the work regularly (button on the right).&lt;/p&gt;

&lt;p&gt;Newly added devices will come in as a line, for more complex shapes like fan click on “edit shape” on the left, this brings up an editor window:
&lt;img src=&quot;/assets/openrgb/custom-shape.png&quot; alt=&quot;OpenRGB visual map custom shape&quot; /&gt;
Here each individual LED can be dragged around. Some convenient functions are on the right, select multiple LEDs to use them. Dragging work also for selection and whole groups can be moved around. 
It will take some time to arrange all 81 LEDs initially, but trust me - it is worth it.&lt;/p&gt;

&lt;p&gt;After everything is positioned like the hardware, make sure “register controller” is ticked on, so the large virtual device actually appears and can be tested. “Toggle LED View” enabled the visual grid:
&lt;img src=&quot;/assets/openrgb/virtual.png&quot; alt=&quot;OpenRGB virtual device&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;effects-1&quot;&gt;Effects&lt;/h2&gt;
&lt;p&gt;Animations make it even more interesting. First select+add an builtin effect on the left side.
&lt;img src=&quot;/assets/openrgb/effects.png&quot; alt=&quot;OpenRGB audio effects&quot; /&gt;
It is normal that nothing happens first, each effect has to be started with the bottom right button. Required is also to enable a controller on the right. Then the effect should be visible, check the live preview either here or in the device or map view. Effect settings can be saved/loaded with the top tight buttons.&lt;/p&gt;

&lt;h1 id=&quot;conclusion-and-video&quot;&gt;Conclusion and Video&lt;/h1&gt;
&lt;p&gt;Overall I am very pleased with result. I like especially the music visualization effects (VU meter), but see for yourself:
&lt;a href=&quot;https://www.youtube.com/watch?v=c1xfLDjJs_k&quot;&gt;
&lt;img src=&quot;/assets/openrgb/vid-thumbnail.png&quot; alt=&quot;OpenRGB audio effects&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;</content><author><name>Jake</name></author><category term="software" /><category term="rgb" /><category term="led" /><category term="mod" /><category term="3d-print" /><summary type="html">Finally following up on my previous post about the coolmoon controller mod, this post shows how to connect it with a mainboard header and do cool (pun intended) animations with OpenRGB.</summary></entry><entry><title type="html">BLE Serial 2.0 Update</title><link href="https://blog.ja-ke.tech/2021/04/22/ble-serial-2.html" rel="alternate" type="text/html" title="BLE Serial 2.0 Update" /><published>2021-04-22T00:00:00+00:00</published><updated>2021-04-22T00:00:00+00:00</updated><id>https://blog.ja-ke.tech/2021/04/22/ble-serial-2</id><content type="html" xml:base="https://blog.ja-ke.tech/2021/04/22/ble-serial-2.html">&lt;p&gt;This is a detailed description of the major 2.0.0 release of BLE Serial. It is a tool to connect Bluetooth low energy 4. and 5.x (BLE) UART modules to virtual serial ports on Linux and now also COM ports on Windows.&lt;/p&gt;
&lt;h1 id=&quot;backend-change&quot;&gt;Backend Change&lt;/h1&gt;
&lt;p&gt;In the first BLE Serial versions (1.0 up to 1.3) it used &lt;a href=&quot;https://github.com/IanHarvey/bluepy&quot;&gt;bluepy&lt;/a&gt; as bluetooth backend library. Problem with it was an open issue that required the manual installation of a fork, like described in the &lt;a href=&quot;/2019/12/18/ble-serial.html&quot;&gt;previous post about it&lt;/a&gt;. Now over the course of 2020 some PRs got merged, but unfortunately there are many open issues and the version on PyPI is still more than 2 years old.&lt;/p&gt;

&lt;p&gt;Also the recommended interface to use BlueZ on Linux is D-Bus, while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bluepy&lt;/code&gt; uses a custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bluepy-helper&lt;/code&gt; written in C, that required additional permissions for some actions.&lt;/p&gt;

&lt;p&gt;So as discussed in &lt;a href=&quot;https://github.com/Jakeler/ble-serial/issues/5&quot;&gt;this issue&lt;/a&gt; a backend change was needed. The descision was made to switch to &lt;a href=&quot;https://pypi.org/project/bleak/&quot;&gt;bleak&lt;/a&gt; which is pure Python and uses D-Bus on Linux. In addition it has support for Windows and macOS.&lt;/p&gt;

&lt;p&gt;Installation is simply done with one command now:&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;pip &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;ble-serial
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;windows-support&quot;&gt;Windows Support&lt;/h2&gt;
&lt;p&gt;As already mentioned BLE Serial has now cross platform support. Following is the Windows setup described (excerpt from README).&lt;/p&gt;

&lt;p&gt;Windows does not have a builtin feature to create virtual serial ports (like Linux does), so it is required to install a additional driver. I decided to use the open source &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com0com&lt;/code&gt; Null-modem emulator, downloaded from &lt;a href=&quot;https://sourceforge.net/projects/signed-drivers/files/com0com/v3.0/&quot;&gt;here&lt;/a&gt; as signed version. This is required because unsigned drivers can not be installed anymore. Note that on latest Windows 10 you likely still have to disable secure boot for it to work.&lt;/p&gt;

&lt;p&gt;ble-serial includes the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ble-com-setup&lt;/code&gt; script to make the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com0com&lt;/code&gt; configuration easier:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; ble-com-setup.exe -h
usage: ble-com-setup [-h] [--install-path INSTALL_PATH]

Setup required COM port pair

optional arguments:
  -h, --help            show this help message and exit
  --install-path INSTALL_PATH
                        Installation directory of the null modem emulator (default: C:/Program Files (x86)/com0com/)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It will request administrator privileges (if it does not already have it) and setup the port in another CMD window:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Changing into C:/Program Files (x86)/com0com/

&amp;gt; Checking port list for BLE
       CNCA0 PortName=-
       CNCB0 PortName=-

BLE port does not exist

&amp;gt; Checking port list for COM9
       CNCA0 PortName=-
       CNCB0 PortName=-

&amp;gt; Trying to create port pair
       CNCA1 PortName=COM9
       CNCB1 PortName=BLE
ComDB: COM9 - logged as &quot;in use&quot;

Setup done!

Hit any key to close
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see it created the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BLE&lt;/code&gt;&amp;lt;-&amp;gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COM9&lt;/code&gt; pair. ble-serial will internally connect to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BLE&lt;/code&gt;, users can then send/receive the data on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COM9&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Otherwise there exist multiple proprietary serial port emulators, these should work too. Just manually create a pair that includes a port named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BLE&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;performance&quot;&gt;Performance&lt;/h1&gt;
&lt;p&gt;To test the performance and compare it with the older implementation I added automated transfer tests in the &lt;a href=&quot;https://github.com/Jakeler/ble-serial/tree/v2.0.0/tests&quot;&gt;repo tests directory&lt;/a&gt;. The idea is to connect a USB &amp;lt;-&amp;gt; serial adapter to the BLE module. Then run ble-serial on the same host, pass data between the adapter and virtual port and check if it is still the same, to identify configurations with packet loss and other issues.&lt;/p&gt;

&lt;p&gt;My test setup consists of a HM-10 module and CP2102 based USB/UART adapter. The scripts uses AT commands to automatically switch through the baud rates, for example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AT+BAUD0&lt;/code&gt; for 9600 bit/s. Then it sends packets with different intervall/delays to either the BLE or UART side, while checking (reading) on the other side and comparing how much was actually received.&lt;/p&gt;

&lt;p&gt;Note that I have also tested different packet sizes (which has the same effect as changing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--mtu&lt;/code&gt; option), but did not find a significant difference between the 4, 16 and 64 bytes, so I am not including it in the graphs here. The following was all tested with 32 bytes packets.
You can see the raw CSV data in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;results/&lt;/code&gt; subdirectory, if you are interested anyway.&lt;/p&gt;

&lt;p&gt;I have grouped the results in graphs by the transmission direction.
First writing to the virtual BLE port and receiving on hardware UART:&lt;/p&gt;
&lt;div&gt;                        &lt;script type=&quot;text/javascript&quot;&gt;window.PlotlyConfig = {MathJaxConfig: 'local'};&lt;/script&gt;
        &lt;script src=&quot;/static/js/plotly.min.js&quot;&gt;&lt;/script&gt;                &lt;div id=&quot;28e1a3f6-0807-40b1-bf54-de667d8cc1aa&quot; class=&quot;plotly-graph-div&quot; style=&quot;height:100%; width:100%;&quot;&gt;&lt;/div&gt;            &lt;script type=&quot;text/javascript&quot;&gt;                                    window.PLOTLYENV=window.PLOTLYENV || {};                                    if (document.getElementById(&quot;28e1a3f6-0807-40b1-bf54-de667d8cc1aa&quot;)) {                    Plotly.newPlot(                        &quot;28e1a3f6-0807-40b1-bf54-de667d8cc1aa&quot;,                        [{&quot;name&quot;: &quot;0.0 ms&quot;, &quot;type&quot;: &quot;bar&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [41.32727428620424, 63.30275229357798, 100.0, 99.48555260224641, 100.0]}, {&quot;name&quot;: &quot;0.5 ms&quot;, &quot;type&quot;: &quot;bar&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [45.21992626253966, 76.07819600445855, 99.759924547715, 99.72562805453143, 99.72562805453143]}, {&quot;name&quot;: &quot;1.0 ms&quot;, &quot;type&quot;: &quot;bar&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [81.1369287490354, 99.95712938352054, 99.79422104089856, 100.0, 99.72562805453143]}, {&quot;name&quot;: &quot;2.0 ms&quot;, &quot;type&quot;: &quot;bar&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [99.97427763011231, 99.94855526022465, 99.83709165737802, 99.72562805453143, 99.72562805453143]}],                        {&quot;annotations&quot;: [{&quot;font&quot;: {&quot;color&quot;: &quot;gray&quot;}, &quot;showarrow&quot;: false, &quot;text&quot;: &quot;[CC BY] blog.ja-ke.tech&quot;, &quot;x&quot;: 1.1, &quot;xref&quot;: &quot;paper&quot;, &quot;y&quot;: -0.25, &quot;yref&quot;: &quot;paper&quot;}], &quot;colorway&quot;: [&quot;rgb(255,255,217)&quot;, &quot;rgb(199,233,180)&quot;, &quot;rgb(65,182,196)&quot;, &quot;rgb(34,94,168)&quot;, &quot;rgb(8,29,88)&quot;], &quot;hovermode&quot;: &quot;x&quot;, &quot;legend&quot;: {&quot;title&quot;: {&quot;text&quot;: &quot;per byte delay&quot;}}, &quot;margin&quot;: {&quot;l&quot;: 100, &quot;r&quot;: 100}, &quot;template&quot;: {&quot;data&quot;: {&quot;bar&quot;: [{&quot;error_x&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}, &quot;error_y&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}, &quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;, &quot;width&quot;: 0.5}}, &quot;type&quot;: &quot;bar&quot;}], &quot;barpolar&quot;: [{&quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;, &quot;width&quot;: 0.5}}, &quot;type&quot;: &quot;barpolar&quot;}], &quot;carpet&quot;: [{&quot;aaxis&quot;: {&quot;endlinecolor&quot;: &quot;#A2B1C6&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;minorgridcolor&quot;: &quot;#506784&quot;, &quot;startlinecolor&quot;: &quot;#A2B1C6&quot;}, &quot;baxis&quot;: {&quot;endlinecolor&quot;: &quot;#A2B1C6&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;minorgridcolor&quot;: &quot;#506784&quot;, &quot;startlinecolor&quot;: &quot;#A2B1C6&quot;}, &quot;type&quot;: &quot;carpet&quot;}], &quot;choropleth&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;type&quot;: &quot;choropleth&quot;}], &quot;contour&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;contour&quot;}], &quot;contourcarpet&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;type&quot;: &quot;contourcarpet&quot;}], &quot;heatmap&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;heatmap&quot;}], &quot;heatmapgl&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;heatmapgl&quot;}], &quot;histogram&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;histogram&quot;}], &quot;histogram2d&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;histogram2d&quot;}], &quot;histogram2dcontour&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;histogram2dcontour&quot;}], &quot;mesh3d&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;type&quot;: &quot;mesh3d&quot;}], &quot;parcoords&quot;: [{&quot;line&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;parcoords&quot;}], &quot;pie&quot;: [{&quot;automargin&quot;: true, &quot;type&quot;: &quot;pie&quot;}], &quot;scatter&quot;: [{&quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;#283442&quot;}}, &quot;type&quot;: &quot;scatter&quot;}], &quot;scatter3d&quot;: [{&quot;line&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatter3d&quot;}], &quot;scattercarpet&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scattercarpet&quot;}], &quot;scattergeo&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scattergeo&quot;}], &quot;scattergl&quot;: [{&quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;#283442&quot;}}, &quot;type&quot;: &quot;scattergl&quot;}], &quot;scattermapbox&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scattermapbox&quot;}], &quot;scatterpolar&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatterpolar&quot;}], &quot;scatterpolargl&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatterpolargl&quot;}], &quot;scatterternary&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatterternary&quot;}], &quot;surface&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;surface&quot;}], &quot;table&quot;: [{&quot;cells&quot;: {&quot;fill&quot;: {&quot;color&quot;: &quot;#506784&quot;}, &quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;}}, &quot;header&quot;: {&quot;fill&quot;: {&quot;color&quot;: &quot;#2a3f5f&quot;}, &quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;}}, &quot;type&quot;: &quot;table&quot;}]}, &quot;layout&quot;: {&quot;annotationdefaults&quot;: {&quot;arrowcolor&quot;: &quot;#f2f5fa&quot;, &quot;arrowhead&quot;: 0, &quot;arrowwidth&quot;: 1}, &quot;autotypenumbers&quot;: &quot;strict&quot;, &quot;coloraxis&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;colorscale&quot;: {&quot;diverging&quot;: [[0, &quot;#8e0152&quot;], [0.1, &quot;#c51b7d&quot;], [0.2, &quot;#de77ae&quot;], [0.3, &quot;#f1b6da&quot;], [0.4, &quot;#fde0ef&quot;], [0.5, &quot;#f7f7f7&quot;], [0.6, &quot;#e6f5d0&quot;], [0.7, &quot;#b8e186&quot;], [0.8, &quot;#7fbc41&quot;], [0.9, &quot;#4d9221&quot;], [1, &quot;#276419&quot;]], &quot;sequential&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;sequentialminus&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]]}, &quot;colorway&quot;: [&quot;#636efa&quot;, &quot;#EF553B&quot;, &quot;#00cc96&quot;, &quot;#ab63fa&quot;, &quot;#FFA15A&quot;, &quot;#19d3f3&quot;, &quot;#FF6692&quot;, &quot;#B6E880&quot;, &quot;#FF97FF&quot;, &quot;#FECB52&quot;], &quot;font&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}, &quot;geo&quot;: {&quot;bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;lakecolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;landcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;showlakes&quot;: true, &quot;showland&quot;: true, &quot;subunitcolor&quot;: &quot;#506784&quot;}, &quot;hoverlabel&quot;: {&quot;align&quot;: &quot;left&quot;}, &quot;hovermode&quot;: &quot;closest&quot;, &quot;mapbox&quot;: {&quot;style&quot;: &quot;dark&quot;}, &quot;paper_bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;plot_bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;polar&quot;: {&quot;angularaxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}, &quot;bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;radialaxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}}, &quot;scene&quot;: {&quot;xaxis&quot;: {&quot;backgroundcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;gridwidth&quot;: 2, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;showbackground&quot;: true, &quot;ticks&quot;: &quot;&quot;, &quot;zerolinecolor&quot;: &quot;#C8D4E3&quot;}, &quot;yaxis&quot;: {&quot;backgroundcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;gridwidth&quot;: 2, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;showbackground&quot;: true, &quot;ticks&quot;: &quot;&quot;, &quot;zerolinecolor&quot;: &quot;#C8D4E3&quot;}, &quot;zaxis&quot;: {&quot;backgroundcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;gridwidth&quot;: 2, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;showbackground&quot;: true, &quot;ticks&quot;: &quot;&quot;, &quot;zerolinecolor&quot;: &quot;#C8D4E3&quot;}}, &quot;shapedefaults&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}}, &quot;sliderdefaults&quot;: {&quot;bgcolor&quot;: &quot;#C8D4E3&quot;, &quot;bordercolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;borderwidth&quot;: 1, &quot;tickwidth&quot;: 0}, &quot;ternary&quot;: {&quot;aaxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}, &quot;baxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}, &quot;bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;caxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}}, &quot;title&quot;: {&quot;x&quot;: 0.05}, &quot;updatemenudefaults&quot;: {&quot;bgcolor&quot;: &quot;#506784&quot;, &quot;borderwidth&quot;: 0}, &quot;xaxis&quot;: {&quot;automargin&quot;: true, &quot;gridcolor&quot;: &quot;#283442&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;, &quot;title&quot;: {&quot;standoff&quot;: 15}, &quot;zerolinecolor&quot;: &quot;#283442&quot;, &quot;zerolinewidth&quot;: 2}, &quot;yaxis&quot;: {&quot;automargin&quot;: true, &quot;gridcolor&quot;: &quot;#283442&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;, &quot;title&quot;: {&quot;standoff&quot;: 15}, &quot;zerolinecolor&quot;: &quot;#283442&quot;, &quot;zerolinewidth&quot;: 2}}}, &quot;title&quot;: {&quot;text&quot;: &quot;BLE \u279f UART: Data Transmission&quot;}, &quot;xaxis&quot;: {&quot;title&quot;: {&quot;text&quot;: &quot;module baud rate&quot;}, &quot;type&quot;: &quot;category&quot;}, &quot;yaxis&quot;: {&quot;rangemode&quot;: &quot;tozero&quot;, &quot;ticksuffix&quot;: &quot;%&quot;, &quot;title&quot;: {&quot;text&quot;: &quot;data received&quot;}}},                        {&quot;responsive&quot;: true}                    )                };                            &lt;/script&gt;        &lt;/div&gt;
&lt;p&gt;As you can see it is getting better with higher baud rates, which makes sense, because the BLE chip is receiving data at a constant rate. The module will just drop data, if the configured UART is too slow to keep up with outputting it.&lt;/p&gt;
&lt;div&gt;                        &lt;script type=&quot;text/javascript&quot;&gt;window.PlotlyConfig = {MathJaxConfig: 'local'};&lt;/script&gt;
        &lt;script src=&quot;/static/js/plotly.min.js&quot;&gt;&lt;/script&gt;                &lt;div id=&quot;dff79bec-c0bb-499b-afc4-bd0dee605e82&quot; class=&quot;plotly-graph-div&quot; style=&quot;height:100%; width:100%;&quot;&gt;&lt;/div&gt;            &lt;script type=&quot;text/javascript&quot;&gt;                                    window.PLOTLYENV=window.PLOTLYENV || {};                                    if (document.getElementById(&quot;dff79bec-c0bb-499b-afc4-bd0dee605e82&quot;)) {                    Plotly.newPlot(                        &quot;dff79bec-c0bb-499b-afc4-bd0dee605e82&quot;,                        [{&quot;name&quot;: &quot;\u221e kb/s&quot;, &quot;type&quot;: &quot;scatter&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [6664.0, 11792.0, 21290.0, 21324.0, 20180.0]}, {&quot;name&quot;: &quot;20 kb/s&quot;, &quot;type&quot;: &quot;scatter&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [6416.0, 11761.0, 17029.0, 17031.0, 17035.0]}, {&quot;name&quot;: &quot;10 kb/s&quot;, &quot;type&quot;: &quot;scatter&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [7396.0, 9960.0, 9950.0, 10189.0, 9944.0]}, {&quot;name&quot;: &quot;5 kb/s&quot;, &quot;type&quot;: &quot;scatter&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [4995.0, 4996.0, 4984.0, 4979.0, 4979.0]}],                        {&quot;annotations&quot;: [{&quot;font&quot;: {&quot;color&quot;: &quot;gray&quot;}, &quot;showarrow&quot;: false, &quot;text&quot;: &quot;[CC BY] blog.ja-ke.tech&quot;, &quot;x&quot;: 1.1, &quot;xref&quot;: &quot;paper&quot;, &quot;y&quot;: -0.25, &quot;yref&quot;: &quot;paper&quot;}], &quot;colorway&quot;: [&quot;rgb(255,255,217)&quot;, &quot;rgb(199,233,180)&quot;, &quot;rgb(65,182,196)&quot;, &quot;rgb(34,94,168)&quot;, &quot;rgb(8,29,88)&quot;], &quot;hovermode&quot;: &quot;x&quot;, &quot;legend&quot;: {&quot;title&quot;: {&quot;text&quot;: &quot;max. (from delay)&quot;}}, &quot;margin&quot;: {&quot;l&quot;: 100, &quot;r&quot;: 100}, &quot;template&quot;: {&quot;data&quot;: {&quot;bar&quot;: [{&quot;error_x&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}, &quot;error_y&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}, &quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;, &quot;width&quot;: 0.5}}, &quot;type&quot;: &quot;bar&quot;}], &quot;barpolar&quot;: [{&quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;, &quot;width&quot;: 0.5}}, &quot;type&quot;: &quot;barpolar&quot;}], &quot;carpet&quot;: [{&quot;aaxis&quot;: {&quot;endlinecolor&quot;: &quot;#A2B1C6&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;minorgridcolor&quot;: &quot;#506784&quot;, &quot;startlinecolor&quot;: &quot;#A2B1C6&quot;}, &quot;baxis&quot;: {&quot;endlinecolor&quot;: &quot;#A2B1C6&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;minorgridcolor&quot;: &quot;#506784&quot;, &quot;startlinecolor&quot;: &quot;#A2B1C6&quot;}, &quot;type&quot;: &quot;carpet&quot;}], &quot;choropleth&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;type&quot;: &quot;choropleth&quot;}], &quot;contour&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;contour&quot;}], &quot;contourcarpet&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;type&quot;: &quot;contourcarpet&quot;}], &quot;heatmap&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;heatmap&quot;}], &quot;heatmapgl&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;heatmapgl&quot;}], &quot;histogram&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;histogram&quot;}], &quot;histogram2d&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;histogram2d&quot;}], &quot;histogram2dcontour&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;histogram2dcontour&quot;}], &quot;mesh3d&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;type&quot;: &quot;mesh3d&quot;}], &quot;parcoords&quot;: [{&quot;line&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;parcoords&quot;}], &quot;pie&quot;: [{&quot;automargin&quot;: true, &quot;type&quot;: &quot;pie&quot;}], &quot;scatter&quot;: [{&quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;#283442&quot;}}, &quot;type&quot;: &quot;scatter&quot;}], &quot;scatter3d&quot;: [{&quot;line&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatter3d&quot;}], &quot;scattercarpet&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scattercarpet&quot;}], &quot;scattergeo&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scattergeo&quot;}], &quot;scattergl&quot;: [{&quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;#283442&quot;}}, &quot;type&quot;: &quot;scattergl&quot;}], &quot;scattermapbox&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scattermapbox&quot;}], &quot;scatterpolar&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatterpolar&quot;}], &quot;scatterpolargl&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatterpolargl&quot;}], &quot;scatterternary&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatterternary&quot;}], &quot;surface&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;surface&quot;}], &quot;table&quot;: [{&quot;cells&quot;: {&quot;fill&quot;: {&quot;color&quot;: &quot;#506784&quot;}, &quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;}}, &quot;header&quot;: {&quot;fill&quot;: {&quot;color&quot;: &quot;#2a3f5f&quot;}, &quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;}}, &quot;type&quot;: &quot;table&quot;}]}, &quot;layout&quot;: {&quot;annotationdefaults&quot;: {&quot;arrowcolor&quot;: &quot;#f2f5fa&quot;, &quot;arrowhead&quot;: 0, &quot;arrowwidth&quot;: 1}, &quot;autotypenumbers&quot;: &quot;strict&quot;, &quot;coloraxis&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;colorscale&quot;: {&quot;diverging&quot;: [[0, &quot;#8e0152&quot;], [0.1, &quot;#c51b7d&quot;], [0.2, &quot;#de77ae&quot;], [0.3, &quot;#f1b6da&quot;], [0.4, &quot;#fde0ef&quot;], [0.5, &quot;#f7f7f7&quot;], [0.6, &quot;#e6f5d0&quot;], [0.7, &quot;#b8e186&quot;], [0.8, &quot;#7fbc41&quot;], [0.9, &quot;#4d9221&quot;], [1, &quot;#276419&quot;]], &quot;sequential&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;sequentialminus&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]]}, &quot;colorway&quot;: [&quot;#636efa&quot;, &quot;#EF553B&quot;, &quot;#00cc96&quot;, &quot;#ab63fa&quot;, &quot;#FFA15A&quot;, &quot;#19d3f3&quot;, &quot;#FF6692&quot;, &quot;#B6E880&quot;, &quot;#FF97FF&quot;, &quot;#FECB52&quot;], &quot;font&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}, &quot;geo&quot;: {&quot;bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;lakecolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;landcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;showlakes&quot;: true, &quot;showland&quot;: true, &quot;subunitcolor&quot;: &quot;#506784&quot;}, &quot;hoverlabel&quot;: {&quot;align&quot;: &quot;left&quot;}, &quot;hovermode&quot;: &quot;closest&quot;, &quot;mapbox&quot;: {&quot;style&quot;: &quot;dark&quot;}, &quot;paper_bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;plot_bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;polar&quot;: {&quot;angularaxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}, &quot;bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;radialaxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}}, &quot;scene&quot;: {&quot;xaxis&quot;: {&quot;backgroundcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;gridwidth&quot;: 2, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;showbackground&quot;: true, &quot;ticks&quot;: &quot;&quot;, &quot;zerolinecolor&quot;: &quot;#C8D4E3&quot;}, &quot;yaxis&quot;: {&quot;backgroundcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;gridwidth&quot;: 2, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;showbackground&quot;: true, &quot;ticks&quot;: &quot;&quot;, &quot;zerolinecolor&quot;: &quot;#C8D4E3&quot;}, &quot;zaxis&quot;: {&quot;backgroundcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;gridwidth&quot;: 2, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;showbackground&quot;: true, &quot;ticks&quot;: &quot;&quot;, &quot;zerolinecolor&quot;: &quot;#C8D4E3&quot;}}, &quot;shapedefaults&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}}, &quot;sliderdefaults&quot;: {&quot;bgcolor&quot;: &quot;#C8D4E3&quot;, &quot;bordercolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;borderwidth&quot;: 1, &quot;tickwidth&quot;: 0}, &quot;ternary&quot;: {&quot;aaxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}, &quot;baxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}, &quot;bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;caxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}}, &quot;title&quot;: {&quot;x&quot;: 0.05}, &quot;updatemenudefaults&quot;: {&quot;bgcolor&quot;: &quot;#506784&quot;, &quot;borderwidth&quot;: 0}, &quot;xaxis&quot;: {&quot;automargin&quot;: true, &quot;gridcolor&quot;: &quot;#283442&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;, &quot;title&quot;: {&quot;standoff&quot;: 15}, &quot;zerolinecolor&quot;: &quot;#283442&quot;, &quot;zerolinewidth&quot;: 2}, &quot;yaxis&quot;: {&quot;automargin&quot;: true, &quot;gridcolor&quot;: &quot;#283442&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;, &quot;title&quot;: {&quot;standoff&quot;: 15}, &quot;zerolinecolor&quot;: &quot;#283442&quot;, &quot;zerolinewidth&quot;: 2}}}, &quot;title&quot;: {&quot;text&quot;: &quot;BLE \u279f UART: Throughput&quot;}, &quot;xaxis&quot;: {&quot;title&quot;: {&quot;text&quot;: &quot;module baud rate&quot;}, &quot;type&quot;: &quot;category&quot;}, &quot;yaxis&quot;: {&quot;rangemode&quot;: &quot;tozero&quot;, &quot;ticksuffix&quot;: &quot;b/s&quot;, &quot;title&quot;: {&quot;text&quot;: &quot;bandwidth&quot;}}},                        {&quot;responsive&quot;: true}                    )                };                            &lt;/script&gt;        &lt;/div&gt;
&lt;p&gt;The maximum real throughput is at at about 21 kbit/s, this explain what we saw above: 19200 is not enough, but 57600+ can receive 100%.&lt;/p&gt;

&lt;p&gt;Now to the tests in the opposite direction, writing to hardware UART:&lt;/p&gt;
&lt;div&gt;                        &lt;script type=&quot;text/javascript&quot;&gt;window.PlotlyConfig = {MathJaxConfig: 'local'};&lt;/script&gt;
        &lt;script src=&quot;/static/js/plotly.min.js&quot;&gt;&lt;/script&gt;                &lt;div id=&quot;ac95ab48-8503-4ab6-917c-944ddfee8e12&quot; class=&quot;plotly-graph-div&quot; style=&quot;height:100%; width:100%;&quot;&gt;&lt;/div&gt;            &lt;script type=&quot;text/javascript&quot;&gt;                                    window.PLOTLYENV=window.PLOTLYENV || {};                                    if (document.getElementById(&quot;ac95ab48-8503-4ab6-917c-944ddfee8e12&quot;)) {                    Plotly.newPlot(                        &quot;ac95ab48-8503-4ab6-917c-944ddfee8e12&quot;,                        [{&quot;name&quot;: &quot;0.0 ms&quot;, &quot;type&quot;: &quot;bar&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [100.0, 81.60850553030953, 29.58929949412672, 15.099031124067565, 7.965360541884607]}, {&quot;name&quot;: &quot;0.5 ms&quot;, &quot;type&quot;: &quot;bar&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [100.0, 81.10263225585183, 86.55577467203979, 68.31861442167539, 85.73265883563406]}, {&quot;name&quot;: &quot;1.0 ms&quot;, &quot;type&quot;: &quot;bar&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [100.0, 100.0, 100.0, 100.0, 100.0]}, {&quot;name&quot;: &quot;2.0 ms&quot;, &quot;type&quot;: &quot;bar&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [100.0, 100.0, 100.0, 100.0, 100.0]}],                        {&quot;annotations&quot;: [{&quot;font&quot;: {&quot;color&quot;: &quot;gray&quot;}, &quot;showarrow&quot;: false, &quot;text&quot;: &quot;[CC BY] blog.ja-ke.tech&quot;, &quot;x&quot;: 1.1, &quot;xref&quot;: &quot;paper&quot;, &quot;y&quot;: -0.25, &quot;yref&quot;: &quot;paper&quot;}], &quot;colorway&quot;: [&quot;rgb(255,247,236)&quot;, &quot;rgb(253,212,158)&quot;, &quot;rgb(252,141,89)&quot;, &quot;rgb(215,48,31)&quot;, &quot;rgb(127,0,0)&quot;], &quot;hovermode&quot;: &quot;x&quot;, &quot;legend&quot;: {&quot;title&quot;: {&quot;text&quot;: &quot;per byte delay&quot;}}, &quot;margin&quot;: {&quot;l&quot;: 100, &quot;r&quot;: 100}, &quot;template&quot;: {&quot;data&quot;: {&quot;bar&quot;: [{&quot;error_x&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}, &quot;error_y&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}, &quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;, &quot;width&quot;: 0.5}}, &quot;type&quot;: &quot;bar&quot;}], &quot;barpolar&quot;: [{&quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;, &quot;width&quot;: 0.5}}, &quot;type&quot;: &quot;barpolar&quot;}], &quot;carpet&quot;: [{&quot;aaxis&quot;: {&quot;endlinecolor&quot;: &quot;#A2B1C6&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;minorgridcolor&quot;: &quot;#506784&quot;, &quot;startlinecolor&quot;: &quot;#A2B1C6&quot;}, &quot;baxis&quot;: {&quot;endlinecolor&quot;: &quot;#A2B1C6&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;minorgridcolor&quot;: &quot;#506784&quot;, &quot;startlinecolor&quot;: &quot;#A2B1C6&quot;}, &quot;type&quot;: &quot;carpet&quot;}], &quot;choropleth&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;type&quot;: &quot;choropleth&quot;}], &quot;contour&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;contour&quot;}], &quot;contourcarpet&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;type&quot;: &quot;contourcarpet&quot;}], &quot;heatmap&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;heatmap&quot;}], &quot;heatmapgl&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;heatmapgl&quot;}], &quot;histogram&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;histogram&quot;}], &quot;histogram2d&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;histogram2d&quot;}], &quot;histogram2dcontour&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;histogram2dcontour&quot;}], &quot;mesh3d&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;type&quot;: &quot;mesh3d&quot;}], &quot;parcoords&quot;: [{&quot;line&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;parcoords&quot;}], &quot;pie&quot;: [{&quot;automargin&quot;: true, &quot;type&quot;: &quot;pie&quot;}], &quot;scatter&quot;: [{&quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;#283442&quot;}}, &quot;type&quot;: &quot;scatter&quot;}], &quot;scatter3d&quot;: [{&quot;line&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatter3d&quot;}], &quot;scattercarpet&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scattercarpet&quot;}], &quot;scattergeo&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scattergeo&quot;}], &quot;scattergl&quot;: [{&quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;#283442&quot;}}, &quot;type&quot;: &quot;scattergl&quot;}], &quot;scattermapbox&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scattermapbox&quot;}], &quot;scatterpolar&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatterpolar&quot;}], &quot;scatterpolargl&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatterpolargl&quot;}], &quot;scatterternary&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatterternary&quot;}], &quot;surface&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;surface&quot;}], &quot;table&quot;: [{&quot;cells&quot;: {&quot;fill&quot;: {&quot;color&quot;: &quot;#506784&quot;}, &quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;}}, &quot;header&quot;: {&quot;fill&quot;: {&quot;color&quot;: &quot;#2a3f5f&quot;}, &quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;}}, &quot;type&quot;: &quot;table&quot;}]}, &quot;layout&quot;: {&quot;annotationdefaults&quot;: {&quot;arrowcolor&quot;: &quot;#f2f5fa&quot;, &quot;arrowhead&quot;: 0, &quot;arrowwidth&quot;: 1}, &quot;autotypenumbers&quot;: &quot;strict&quot;, &quot;coloraxis&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;colorscale&quot;: {&quot;diverging&quot;: [[0, &quot;#8e0152&quot;], [0.1, &quot;#c51b7d&quot;], [0.2, &quot;#de77ae&quot;], [0.3, &quot;#f1b6da&quot;], [0.4, &quot;#fde0ef&quot;], [0.5, &quot;#f7f7f7&quot;], [0.6, &quot;#e6f5d0&quot;], [0.7, &quot;#b8e186&quot;], [0.8, &quot;#7fbc41&quot;], [0.9, &quot;#4d9221&quot;], [1, &quot;#276419&quot;]], &quot;sequential&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;sequentialminus&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]]}, &quot;colorway&quot;: [&quot;#636efa&quot;, &quot;#EF553B&quot;, &quot;#00cc96&quot;, &quot;#ab63fa&quot;, &quot;#FFA15A&quot;, &quot;#19d3f3&quot;, &quot;#FF6692&quot;, &quot;#B6E880&quot;, &quot;#FF97FF&quot;, &quot;#FECB52&quot;], &quot;font&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}, &quot;geo&quot;: {&quot;bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;lakecolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;landcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;showlakes&quot;: true, &quot;showland&quot;: true, &quot;subunitcolor&quot;: &quot;#506784&quot;}, &quot;hoverlabel&quot;: {&quot;align&quot;: &quot;left&quot;}, &quot;hovermode&quot;: &quot;closest&quot;, &quot;mapbox&quot;: {&quot;style&quot;: &quot;dark&quot;}, &quot;paper_bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;plot_bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;polar&quot;: {&quot;angularaxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}, &quot;bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;radialaxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}}, &quot;scene&quot;: {&quot;xaxis&quot;: {&quot;backgroundcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;gridwidth&quot;: 2, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;showbackground&quot;: true, &quot;ticks&quot;: &quot;&quot;, &quot;zerolinecolor&quot;: &quot;#C8D4E3&quot;}, &quot;yaxis&quot;: {&quot;backgroundcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;gridwidth&quot;: 2, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;showbackground&quot;: true, &quot;ticks&quot;: &quot;&quot;, &quot;zerolinecolor&quot;: &quot;#C8D4E3&quot;}, &quot;zaxis&quot;: {&quot;backgroundcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;gridwidth&quot;: 2, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;showbackground&quot;: true, &quot;ticks&quot;: &quot;&quot;, &quot;zerolinecolor&quot;: &quot;#C8D4E3&quot;}}, &quot;shapedefaults&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}}, &quot;sliderdefaults&quot;: {&quot;bgcolor&quot;: &quot;#C8D4E3&quot;, &quot;bordercolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;borderwidth&quot;: 1, &quot;tickwidth&quot;: 0}, &quot;ternary&quot;: {&quot;aaxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}, &quot;baxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}, &quot;bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;caxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}}, &quot;title&quot;: {&quot;x&quot;: 0.05}, &quot;updatemenudefaults&quot;: {&quot;bgcolor&quot;: &quot;#506784&quot;, &quot;borderwidth&quot;: 0}, &quot;xaxis&quot;: {&quot;automargin&quot;: true, &quot;gridcolor&quot;: &quot;#283442&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;, &quot;title&quot;: {&quot;standoff&quot;: 15}, &quot;zerolinecolor&quot;: &quot;#283442&quot;, &quot;zerolinewidth&quot;: 2}, &quot;yaxis&quot;: {&quot;automargin&quot;: true, &quot;gridcolor&quot;: &quot;#283442&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;, &quot;title&quot;: {&quot;standoff&quot;: 15}, &quot;zerolinecolor&quot;: &quot;#283442&quot;, &quot;zerolinewidth&quot;: 2}}}, &quot;title&quot;: {&quot;text&quot;: &quot;UART \u279f BLE: Data Transmission&quot;}, &quot;xaxis&quot;: {&quot;title&quot;: {&quot;text&quot;: &quot;module baud rate&quot;}, &quot;type&quot;: &quot;category&quot;}, &quot;yaxis&quot;: {&quot;rangemode&quot;: &quot;tozero&quot;, &quot;ticksuffix&quot;: &quot;%&quot;, &quot;title&quot;: {&quot;text&quot;: &quot;data received&quot;}}},                        {&quot;responsive&quot;: true}                    )                };                            &lt;/script&gt;        &lt;/div&gt;
&lt;p&gt;Losses show the opposite behavior here, 9600 is completely fine, higher ones only if some delay is used. From 19200 only about 80% is received.&lt;/p&gt;

&lt;div&gt;                        &lt;script type=&quot;text/javascript&quot;&gt;window.PlotlyConfig = {MathJaxConfig: 'local'};&lt;/script&gt;
        &lt;script src=&quot;/static/js/plotly.min.js&quot;&gt;&lt;/script&gt;                &lt;div id=&quot;75700e49-e358-4462-9f21-9fe0700b5cca&quot; class=&quot;plotly-graph-div&quot; style=&quot;height:100%; width:100%;&quot;&gt;&lt;/div&gt;            &lt;script type=&quot;text/javascript&quot;&gt;                                    window.PLOTLYENV=window.PLOTLYENV || {};                                    if (document.getElementById(&quot;75700e49-e358-4462-9f21-9fe0700b5cca&quot;)) {                    Plotly.newPlot(                        &quot;75700e49-e358-4462-9f21-9fe0700b5cca&quot;,                        [{&quot;name&quot;: &quot;\u221e kb/s&quot;, &quot;type&quot;: &quot;scatter&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [9562.0, 15466.0, 16356.0, 15538.0, 15084.0]}, {&quot;name&quot;: &quot;20 kb/s&quot;, &quot;type&quot;: &quot;scatter&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [9583.0, 15339.0, 17001.0, 16933.0, 16821.0]}, {&quot;name&quot;: &quot;10 kb/s&quot;, &quot;type&quot;: &quot;scatter&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [9586.0, 9951.0, 9947.0, 9946.0, 9948.0]}, {&quot;name&quot;: &quot;5 kb/s&quot;, &quot;type&quot;: &quot;scatter&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [4982.0, 4979.0, 4986.0, 4978.0, 4978.0]}],                        {&quot;annotations&quot;: [{&quot;font&quot;: {&quot;color&quot;: &quot;gray&quot;}, &quot;showarrow&quot;: false, &quot;text&quot;: &quot;[CC BY] blog.ja-ke.tech&quot;, &quot;x&quot;: 1.1, &quot;xref&quot;: &quot;paper&quot;, &quot;y&quot;: -0.25, &quot;yref&quot;: &quot;paper&quot;}], &quot;colorway&quot;: [&quot;rgb(255,247,236)&quot;, &quot;rgb(253,212,158)&quot;, &quot;rgb(252,141,89)&quot;, &quot;rgb(215,48,31)&quot;, &quot;rgb(127,0,0)&quot;], &quot;hovermode&quot;: &quot;x&quot;, &quot;legend&quot;: {&quot;title&quot;: {&quot;text&quot;: &quot;max. (from delay)&quot;}}, &quot;margin&quot;: {&quot;l&quot;: 100, &quot;r&quot;: 100}, &quot;template&quot;: {&quot;data&quot;: {&quot;bar&quot;: [{&quot;error_x&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}, &quot;error_y&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}, &quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;, &quot;width&quot;: 0.5}}, &quot;type&quot;: &quot;bar&quot;}], &quot;barpolar&quot;: [{&quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;, &quot;width&quot;: 0.5}}, &quot;type&quot;: &quot;barpolar&quot;}], &quot;carpet&quot;: [{&quot;aaxis&quot;: {&quot;endlinecolor&quot;: &quot;#A2B1C6&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;minorgridcolor&quot;: &quot;#506784&quot;, &quot;startlinecolor&quot;: &quot;#A2B1C6&quot;}, &quot;baxis&quot;: {&quot;endlinecolor&quot;: &quot;#A2B1C6&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;minorgridcolor&quot;: &quot;#506784&quot;, &quot;startlinecolor&quot;: &quot;#A2B1C6&quot;}, &quot;type&quot;: &quot;carpet&quot;}], &quot;choropleth&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;type&quot;: &quot;choropleth&quot;}], &quot;contour&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;contour&quot;}], &quot;contourcarpet&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;type&quot;: &quot;contourcarpet&quot;}], &quot;heatmap&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;heatmap&quot;}], &quot;heatmapgl&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;heatmapgl&quot;}], &quot;histogram&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;histogram&quot;}], &quot;histogram2d&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;histogram2d&quot;}], &quot;histogram2dcontour&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;histogram2dcontour&quot;}], &quot;mesh3d&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;type&quot;: &quot;mesh3d&quot;}], &quot;parcoords&quot;: [{&quot;line&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;parcoords&quot;}], &quot;pie&quot;: [{&quot;automargin&quot;: true, &quot;type&quot;: &quot;pie&quot;}], &quot;scatter&quot;: [{&quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;#283442&quot;}}, &quot;type&quot;: &quot;scatter&quot;}], &quot;scatter3d&quot;: [{&quot;line&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatter3d&quot;}], &quot;scattercarpet&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scattercarpet&quot;}], &quot;scattergeo&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scattergeo&quot;}], &quot;scattergl&quot;: [{&quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;#283442&quot;}}, &quot;type&quot;: &quot;scattergl&quot;}], &quot;scattermapbox&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scattermapbox&quot;}], &quot;scatterpolar&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatterpolar&quot;}], &quot;scatterpolargl&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatterpolargl&quot;}], &quot;scatterternary&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatterternary&quot;}], &quot;surface&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;surface&quot;}], &quot;table&quot;: [{&quot;cells&quot;: {&quot;fill&quot;: {&quot;color&quot;: &quot;#506784&quot;}, &quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;}}, &quot;header&quot;: {&quot;fill&quot;: {&quot;color&quot;: &quot;#2a3f5f&quot;}, &quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;}}, &quot;type&quot;: &quot;table&quot;}]}, &quot;layout&quot;: {&quot;annotationdefaults&quot;: {&quot;arrowcolor&quot;: &quot;#f2f5fa&quot;, &quot;arrowhead&quot;: 0, &quot;arrowwidth&quot;: 1}, &quot;autotypenumbers&quot;: &quot;strict&quot;, &quot;coloraxis&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;colorscale&quot;: {&quot;diverging&quot;: [[0, &quot;#8e0152&quot;], [0.1, &quot;#c51b7d&quot;], [0.2, &quot;#de77ae&quot;], [0.3, &quot;#f1b6da&quot;], [0.4, &quot;#fde0ef&quot;], [0.5, &quot;#f7f7f7&quot;], [0.6, &quot;#e6f5d0&quot;], [0.7, &quot;#b8e186&quot;], [0.8, &quot;#7fbc41&quot;], [0.9, &quot;#4d9221&quot;], [1, &quot;#276419&quot;]], &quot;sequential&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;sequentialminus&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]]}, &quot;colorway&quot;: [&quot;#636efa&quot;, &quot;#EF553B&quot;, &quot;#00cc96&quot;, &quot;#ab63fa&quot;, &quot;#FFA15A&quot;, &quot;#19d3f3&quot;, &quot;#FF6692&quot;, &quot;#B6E880&quot;, &quot;#FF97FF&quot;, &quot;#FECB52&quot;], &quot;font&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}, &quot;geo&quot;: {&quot;bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;lakecolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;landcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;showlakes&quot;: true, &quot;showland&quot;: true, &quot;subunitcolor&quot;: &quot;#506784&quot;}, &quot;hoverlabel&quot;: {&quot;align&quot;: &quot;left&quot;}, &quot;hovermode&quot;: &quot;closest&quot;, &quot;mapbox&quot;: {&quot;style&quot;: &quot;dark&quot;}, &quot;paper_bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;plot_bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;polar&quot;: {&quot;angularaxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}, &quot;bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;radialaxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}}, &quot;scene&quot;: {&quot;xaxis&quot;: {&quot;backgroundcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;gridwidth&quot;: 2, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;showbackground&quot;: true, &quot;ticks&quot;: &quot;&quot;, &quot;zerolinecolor&quot;: &quot;#C8D4E3&quot;}, &quot;yaxis&quot;: {&quot;backgroundcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;gridwidth&quot;: 2, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;showbackground&quot;: true, &quot;ticks&quot;: &quot;&quot;, &quot;zerolinecolor&quot;: &quot;#C8D4E3&quot;}, &quot;zaxis&quot;: {&quot;backgroundcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;gridwidth&quot;: 2, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;showbackground&quot;: true, &quot;ticks&quot;: &quot;&quot;, &quot;zerolinecolor&quot;: &quot;#C8D4E3&quot;}}, &quot;shapedefaults&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}}, &quot;sliderdefaults&quot;: {&quot;bgcolor&quot;: &quot;#C8D4E3&quot;, &quot;bordercolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;borderwidth&quot;: 1, &quot;tickwidth&quot;: 0}, &quot;ternary&quot;: {&quot;aaxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}, &quot;baxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}, &quot;bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;caxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}}, &quot;title&quot;: {&quot;x&quot;: 0.05}, &quot;updatemenudefaults&quot;: {&quot;bgcolor&quot;: &quot;#506784&quot;, &quot;borderwidth&quot;: 0}, &quot;xaxis&quot;: {&quot;automargin&quot;: true, &quot;gridcolor&quot;: &quot;#283442&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;, &quot;title&quot;: {&quot;standoff&quot;: 15}, &quot;zerolinecolor&quot;: &quot;#283442&quot;, &quot;zerolinewidth&quot;: 2}, &quot;yaxis&quot;: {&quot;automargin&quot;: true, &quot;gridcolor&quot;: &quot;#283442&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;, &quot;title&quot;: {&quot;standoff&quot;: 15}, &quot;zerolinecolor&quot;: &quot;#283442&quot;, &quot;zerolinewidth&quot;: 2}}}, &quot;title&quot;: {&quot;text&quot;: &quot;UART \u279f BLE: Throughput&quot;}, &quot;xaxis&quot;: {&quot;title&quot;: {&quot;text&quot;: &quot;module baud rate&quot;}, &quot;type&quot;: &quot;category&quot;}, &quot;yaxis&quot;: {&quot;rangemode&quot;: &quot;tozero&quot;, &quot;ticksuffix&quot;: &quot;b/s&quot;, &quot;title&quot;: {&quot;text&quot;: &quot;bandwidth&quot;}}},                        {&quot;responsive&quot;: true}                    )                };                            &lt;/script&gt;        &lt;/div&gt;
&lt;p&gt;Apparently the maximum throughput is lower in this direction with about 17 kbit/s, this explains why 19200 shows already some loss.&lt;/p&gt;

&lt;p&gt;An always safe option is to use 1 ms delay per byte (= max. 10000 baud). Note that that I calculate 10 bit per byte, because UART uses 8-N-1 encoding, so: 1 start + 8 data + 1 stop = 10 bits.&lt;/p&gt;

&lt;h2 id=&quot;compared-with-v13&quot;&gt;Compared with v1.3&lt;/h2&gt;
&lt;p&gt;Here it tested with 0 delay, so writing as fast as possible, because this shows differences most clearly.&lt;/p&gt;
&lt;div&gt;                        &lt;script type=&quot;text/javascript&quot;&gt;window.PlotlyConfig = {MathJaxConfig: 'local'};&lt;/script&gt;
        &lt;script src=&quot;/static/js/plotly.min.js&quot;&gt;&lt;/script&gt;                &lt;div id=&quot;d8b8af14-2e45-490e-b0c2-8dab50425f5b&quot; class=&quot;plotly-graph-div&quot; style=&quot;height:100%; width:100%;&quot;&gt;&lt;/div&gt;            &lt;script type=&quot;text/javascript&quot;&gt;                                    window.PLOTLYENV=window.PLOTLYENV || {};                                    if (document.getElementById(&quot;d8b8af14-2e45-490e-b0c2-8dab50425f5b&quot;)) {                    Plotly.newPlot(                        &quot;d8b8af14-2e45-490e-b0c2-8dab50425f5b&quot;,                        [{&quot;name&quot;: &quot;1.3: BLE \u279f UART&quot;, &quot;type&quot;: &quot;scatter&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [2.212123810340401, 2.100660207493803, 2.2292720569321602, 2.2292720569321602, 1.8863071250964367]}, {&quot;name&quot;: &quot;1.3: UART \u279f BLE&quot;, &quot;type&quot;: &quot;scatter&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [100.0, 81.78856211952328, 28.28603275315099, 14.78178856211953, 7.956786418588706]}, {&quot;name&quot;: &quot;2.0: BLE \u279f UART&quot;, &quot;type&quot;: &quot;scatter&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [41.32727428620424, 63.30275229357798, 100.0, 99.48555260224641, 100.0]}, {&quot;name&quot;: &quot;2.0: UART \u279f BLE&quot;, &quot;type&quot;: &quot;scatter&quot;, &quot;x&quot;: [9600, 19200, 57600, 115200, 230400], &quot;y&quot;: [100.0, 81.60850553030953, 29.58929949412672, 15.099031124067565, 7.965360541884607]}],                        {&quot;annotations&quot;: [{&quot;font&quot;: {&quot;color&quot;: &quot;gray&quot;}, &quot;showarrow&quot;: false, &quot;text&quot;: &quot;[CC BY] blog.ja-ke.tech&quot;, &quot;x&quot;: 1.1, &quot;xref&quot;: &quot;paper&quot;, &quot;y&quot;: -0.25, &quot;yref&quot;: &quot;paper&quot;}], &quot;colorway&quot;: [&quot;rgb(65,182,196)&quot;, &quot;rgb(252,141,89)&quot;, &quot;rgb(34,94,168)&quot;, &quot;rgb(215,48,31)&quot;], &quot;hovermode&quot;: &quot;x&quot;, &quot;margin&quot;: {&quot;l&quot;: 100, &quot;r&quot;: 100}, &quot;template&quot;: {&quot;data&quot;: {&quot;bar&quot;: [{&quot;error_x&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}, &quot;error_y&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}, &quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;, &quot;width&quot;: 0.5}}, &quot;type&quot;: &quot;bar&quot;}], &quot;barpolar&quot;: [{&quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;, &quot;width&quot;: 0.5}}, &quot;type&quot;: &quot;barpolar&quot;}], &quot;carpet&quot;: [{&quot;aaxis&quot;: {&quot;endlinecolor&quot;: &quot;#A2B1C6&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;minorgridcolor&quot;: &quot;#506784&quot;, &quot;startlinecolor&quot;: &quot;#A2B1C6&quot;}, &quot;baxis&quot;: {&quot;endlinecolor&quot;: &quot;#A2B1C6&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;minorgridcolor&quot;: &quot;#506784&quot;, &quot;startlinecolor&quot;: &quot;#A2B1C6&quot;}, &quot;type&quot;: &quot;carpet&quot;}], &quot;choropleth&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;type&quot;: &quot;choropleth&quot;}], &quot;contour&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;contour&quot;}], &quot;contourcarpet&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;type&quot;: &quot;contourcarpet&quot;}], &quot;heatmap&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;heatmap&quot;}], &quot;heatmapgl&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;heatmapgl&quot;}], &quot;histogram&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;histogram&quot;}], &quot;histogram2d&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;histogram2d&quot;}], &quot;histogram2dcontour&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;histogram2dcontour&quot;}], &quot;mesh3d&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;type&quot;: &quot;mesh3d&quot;}], &quot;parcoords&quot;: [{&quot;line&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;parcoords&quot;}], &quot;pie&quot;: [{&quot;automargin&quot;: true, &quot;type&quot;: &quot;pie&quot;}], &quot;scatter&quot;: [{&quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;#283442&quot;}}, &quot;type&quot;: &quot;scatter&quot;}], &quot;scatter3d&quot;: [{&quot;line&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatter3d&quot;}], &quot;scattercarpet&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scattercarpet&quot;}], &quot;scattergeo&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scattergeo&quot;}], &quot;scattergl&quot;: [{&quot;marker&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;#283442&quot;}}, &quot;type&quot;: &quot;scattergl&quot;}], &quot;scattermapbox&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scattermapbox&quot;}], &quot;scatterpolar&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatterpolar&quot;}], &quot;scatterpolargl&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatterpolargl&quot;}], &quot;scatterternary&quot;: [{&quot;marker&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;type&quot;: &quot;scatterternary&quot;}], &quot;surface&quot;: [{&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}, &quot;colorscale&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;type&quot;: &quot;surface&quot;}], &quot;table&quot;: [{&quot;cells&quot;: {&quot;fill&quot;: {&quot;color&quot;: &quot;#506784&quot;}, &quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;}}, &quot;header&quot;: {&quot;fill&quot;: {&quot;color&quot;: &quot;#2a3f5f&quot;}, &quot;line&quot;: {&quot;color&quot;: &quot;rgb(17,17,17)&quot;}}, &quot;type&quot;: &quot;table&quot;}]}, &quot;layout&quot;: {&quot;annotationdefaults&quot;: {&quot;arrowcolor&quot;: &quot;#f2f5fa&quot;, &quot;arrowhead&quot;: 0, &quot;arrowwidth&quot;: 1}, &quot;autotypenumbers&quot;: &quot;strict&quot;, &quot;coloraxis&quot;: {&quot;colorbar&quot;: {&quot;outlinewidth&quot;: 0, &quot;ticks&quot;: &quot;&quot;}}, &quot;colorscale&quot;: {&quot;diverging&quot;: [[0, &quot;#8e0152&quot;], [0.1, &quot;#c51b7d&quot;], [0.2, &quot;#de77ae&quot;], [0.3, &quot;#f1b6da&quot;], [0.4, &quot;#fde0ef&quot;], [0.5, &quot;#f7f7f7&quot;], [0.6, &quot;#e6f5d0&quot;], [0.7, &quot;#b8e186&quot;], [0.8, &quot;#7fbc41&quot;], [0.9, &quot;#4d9221&quot;], [1, &quot;#276419&quot;]], &quot;sequential&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]], &quot;sequentialminus&quot;: [[0.0, &quot;#0d0887&quot;], [0.1111111111111111, &quot;#46039f&quot;], [0.2222222222222222, &quot;#7201a8&quot;], [0.3333333333333333, &quot;#9c179e&quot;], [0.4444444444444444, &quot;#bd3786&quot;], [0.5555555555555556, &quot;#d8576b&quot;], [0.6666666666666666, &quot;#ed7953&quot;], [0.7777777777777778, &quot;#fb9f3a&quot;], [0.8888888888888888, &quot;#fdca26&quot;], [1.0, &quot;#f0f921&quot;]]}, &quot;colorway&quot;: [&quot;#636efa&quot;, &quot;#EF553B&quot;, &quot;#00cc96&quot;, &quot;#ab63fa&quot;, &quot;#FFA15A&quot;, &quot;#19d3f3&quot;, &quot;#FF6692&quot;, &quot;#B6E880&quot;, &quot;#FF97FF&quot;, &quot;#FECB52&quot;], &quot;font&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}, &quot;geo&quot;: {&quot;bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;lakecolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;landcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;showlakes&quot;: true, &quot;showland&quot;: true, &quot;subunitcolor&quot;: &quot;#506784&quot;}, &quot;hoverlabel&quot;: {&quot;align&quot;: &quot;left&quot;}, &quot;hovermode&quot;: &quot;closest&quot;, &quot;mapbox&quot;: {&quot;style&quot;: &quot;dark&quot;}, &quot;paper_bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;plot_bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;polar&quot;: {&quot;angularaxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}, &quot;bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;radialaxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}}, &quot;scene&quot;: {&quot;xaxis&quot;: {&quot;backgroundcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;gridwidth&quot;: 2, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;showbackground&quot;: true, &quot;ticks&quot;: &quot;&quot;, &quot;zerolinecolor&quot;: &quot;#C8D4E3&quot;}, &quot;yaxis&quot;: {&quot;backgroundcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;gridwidth&quot;: 2, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;showbackground&quot;: true, &quot;ticks&quot;: &quot;&quot;, &quot;zerolinecolor&quot;: &quot;#C8D4E3&quot;}, &quot;zaxis&quot;: {&quot;backgroundcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;gridcolor&quot;: &quot;#506784&quot;, &quot;gridwidth&quot;: 2, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;showbackground&quot;: true, &quot;ticks&quot;: &quot;&quot;, &quot;zerolinecolor&quot;: &quot;#C8D4E3&quot;}}, &quot;shapedefaults&quot;: {&quot;line&quot;: {&quot;color&quot;: &quot;#f2f5fa&quot;}}, &quot;sliderdefaults&quot;: {&quot;bgcolor&quot;: &quot;#C8D4E3&quot;, &quot;bordercolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;borderwidth&quot;: 1, &quot;tickwidth&quot;: 0}, &quot;ternary&quot;: {&quot;aaxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}, &quot;baxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}, &quot;bgcolor&quot;: &quot;rgb(17,17,17)&quot;, &quot;caxis&quot;: {&quot;gridcolor&quot;: &quot;#506784&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;}}, &quot;title&quot;: {&quot;x&quot;: 0.05}, &quot;updatemenudefaults&quot;: {&quot;bgcolor&quot;: &quot;#506784&quot;, &quot;borderwidth&quot;: 0}, &quot;xaxis&quot;: {&quot;automargin&quot;: true, &quot;gridcolor&quot;: &quot;#283442&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;, &quot;title&quot;: {&quot;standoff&quot;: 15}, &quot;zerolinecolor&quot;: &quot;#283442&quot;, &quot;zerolinewidth&quot;: 2}, &quot;yaxis&quot;: {&quot;automargin&quot;: true, &quot;gridcolor&quot;: &quot;#283442&quot;, &quot;linecolor&quot;: &quot;#506784&quot;, &quot;ticks&quot;: &quot;&quot;, &quot;title&quot;: {&quot;standoff&quot;: 15}, &quot;zerolinecolor&quot;: &quot;#283442&quot;, &quot;zerolinewidth&quot;: 2}}}, &quot;title&quot;: {&quot;text&quot;: &quot;BLE Serial 1.3 vs 2.0 [delay = 0 ms]&quot;}, &quot;xaxis&quot;: {&quot;title&quot;: {&quot;text&quot;: &quot;module baud rate&quot;}, &quot;type&quot;: &quot;category&quot;}, &quot;yaxis&quot;: {&quot;rangemode&quot;: &quot;tozero&quot;, &quot;ticksuffix&quot;: &quot;%&quot;, &quot;title&quot;: {&quot;text&quot;: &quot;data received&quot;}}},                        {&quot;responsive&quot;: true}                    )                };                            &lt;/script&gt;        &lt;/div&gt;
&lt;p&gt;In the case of writing to UART both implementations are almost equal, but there is a massive difference when writing to the virtual BLE serial port. This is not throttled, so you can multiple MB/s to it, of course bluetooth and UART are not that fast. 1.3 dropped about 98% in that case, it only worked reliably if the application wrote small amounts of data continuously.&lt;/p&gt;

&lt;p&gt;On 2.0 this is buffered, that makes writing continuously into BLE safe. There is still the hardware limitation though, the UART output baud rate should be above ~20000, for example it can receive all data with 57600 baudrate.&lt;/p&gt;

&lt;h1 id=&quot;colored-logs&quot;&gt;Colored Logs&lt;/h1&gt;
&lt;p&gt;Another improvement in this release are that log levels are now indicated also by colors:
&lt;img src=&quot;/assets/ble-serial/color-logs.png&quot; alt=&quot;ble-serial 2.0 colored log&quot; /&gt;
This should make it easier to spot important infos, even with the noisy verbose (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v&lt;/code&gt;) log level.&lt;/p&gt;

&lt;p&gt;I hope this update is helpful, let me know how it works.&lt;/p&gt;</content><author><name>Jake</name></author><category term="software" /><category term="bluetooth" /><category term="uart" /><category term="release" /><summary type="html">This is a detailed description of the major 2.0.0 release of BLE Serial. It is a tool to connect Bluetooth low energy 4. and 5.x (BLE) UART modules to virtual serial ports on Linux and now also COM ports on Windows. Backend Change In the first BLE Serial versions (1.0 up to 1.3) it used bluepy as bluetooth backend library. Problem with it was an open issue that required the manual installation of a fork, like described in the previous post about it. Now over the course of 2020 some PRs got merged, but unfortunately there are many open issues and the version on PyPI is still more than 2 years old.</summary></entry><entry><title type="html">I2C Communication Protocol of a Smartphone Battery</title><link href="https://blog.ja-ke.tech/2021/03/25/oneplus-battery-protection.html" rel="alternate" type="text/html" title="I2C Communication Protocol of a Smartphone Battery" /><published>2021-03-25T00:00:00+00:00</published><updated>2021-03-25T00:00:00+00:00</updated><id>https://blog.ja-ke.tech/2021/03/25/oneplus-battery-protection</id><content type="html" xml:base="https://blog.ja-ke.tech/2021/03/25/oneplus-battery-protection.html">&lt;p&gt;In this post I investigate how a typical Oneplus smartphone battery communicates the current voltage, state of charge etc. to the main device.
This can be also useful for repurposing these batteries on a Raspberry or Orange Pi, to power portable projects.&lt;/p&gt;

&lt;h1 id=&quot;hardware-dismantling&quot;&gt;Hardware Dismantling&lt;/h1&gt;
&lt;p&gt;I had the battery already removed from the Oneplus One (and replaced with a new third party part). 
First the outer wrap has to be removed, I need to see the bare cell and protection PCB. It reveals another (paper?) wrap at the top and a small temperature probe sticking out:
&lt;img src=&quot;/assets/i2c-phone-battery/disass.jpg&quot; alt=&quot;battery wrap removed&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After removing that as well the board is finally visible. Between the big battery taps is most likely a shunt resistor to measure the current. 
On the mid to left are some ICs (more on them later) and on the right four large solder pads, marked with SCL, SDA, P+ and P-. 
The flat flex cable is simply soldered to these pads and the connector has the same markings.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/i2c-phone-battery/pcb-front.jpg&quot; alt=&quot;battery pcb front&quot; /&gt;
P stands probably for power and these +/- pads are indeed connected to the battery and carry the about 4V, I have checked it with a multimeter. 
SCL/SDA is really interesting, because this suggests that there is some digital I2C communication with the smartphone going on.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/i2c-phone-battery/pcb-back.jpg&quot; alt=&quot;battery pcb back&quot; /&gt;
Not much on the back side, looks like a few SMD resistors are below some black potting compound.&lt;/p&gt;

&lt;h1 id=&quot;chip-investigation&quot;&gt;Chip Investigation&lt;/h1&gt;
&lt;p&gt;Now I got curious what exactly this board does. Unfortunately I could not read chip markings, because they are extremely small, no chance without a microscope…&lt;/p&gt;

&lt;p&gt;Instead I tried to find something on the internet, the search for “oneplus battery i2c site:forums.oneplus.com” brought up a boot/kernel log. 
Then searching for “battery” in the log found these messages:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[    4.792738] bq27541-battery 1-0055: DEVICE_TYPE is 0x541, FIRMWARE_VERSION is 0x200
[    4.792950] bq27541-battery 1-0055: Complete bq27541 configuration 0x601B
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is a kernel driver for “bq27541”, so I searched for that and got indeed a &lt;a href=&quot;https://www.ti.com/lit/ds/symlink/bq27541.pdf&quot;&gt;datasheet from Texas Instruments&lt;/a&gt;. The description fits well, it is a “Single Cell Li-Ion Battery Fuel Gauge” and “provides information such as remaining battery capacity(mAh), state-of-charge (%), run-time to empty (min.), battery voltage (mV), and temperature (°C)” over an I2C interface.&lt;/p&gt;

&lt;p&gt;The protocol description is detailed, important information for the start is on page 32:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;device address 01010101 = 0x55&lt;/li&gt;
  &lt;li&gt;support for normal I2C read or incremental read (multi byte)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Abd a excerpt from the standard 2 byte registers/commands (page 9):&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;TEMP (temperature) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x06&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x07&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;VOLT (voltage) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x08&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x09&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;SOC (state of charge) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x2c&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x2d&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To confirm that it speaks this protocol I hooked up my DSLogic Plus, connected the battery back to the phone and rebooted it, the capture looks like this:
&lt;img src=&quot;/assets/i2c-phone-battery/dslogic-read-voltage1.png&quot; alt=&quot;logic analyzer i2c voltage readout&quot; /&gt;
The 7 bit long address is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x55&lt;/code&gt; as expected, after that comes 1 bit indicating a read/write, then ACK (A) or NOT ACK (N) and finally the packet data. It is the register &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x08&lt;/code&gt; = voltage in this case. The request is always a write, but it is not actually writing, just specifies the register.&lt;/p&gt;

&lt;p&gt;I2C is a bus designed with a master (here: phone) and slave (here: battery).
The master controls the bus, it sends again the first start with device address, but now with read bit appended. Now the bus gets released from the master, indicating that the slave &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x55&lt;/code&gt; is allowed to send now. It responds shortly after with the actual data:
&lt;img src=&quot;/assets/i2c-phone-battery/dslogic-read-voltage2.png&quot; alt=&quot;logic analyzer i2c voltage readout&quot; /&gt;
First comes the low byte, high byte is second (practically little endian), that means &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x0e95 = 3733 mV&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;orange-pi-setup&quot;&gt;Orange Pi Setup&lt;/h1&gt;
&lt;p&gt;As already noted in the introduction this is also useful for usage with other Linux computers. I had a Orange Pi PC2 with Armbian laying around, so I used that, it has many GPIOs:
&lt;img src=&quot;/assets/i2c-phone-battery/opi-pinout-dark.jpg&quot; alt=&quot;Orange Pi PC2 pinout&quot; /&gt;
Most importantly, support for I2C. Bus 0 is on PA11 and PA12, highlighted in light blue. On modern kernels the so called device trees (DT) are used to define the hardware. For Armbian the configuration file which gets read by the bootloader is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot/armbianEnv.txt&lt;/code&gt;. To enable I2C bus 0 is must contain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;overlays=i2c0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After a reboot a new I2C bus should show up, this can be checked with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i2cdetect -l&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So I connected both the battery and the logic analyzer to it:
&lt;img src=&quot;/assets/i2c-phone-battery/connection-probe.jpg&quot; alt=&quot;Orange Pi I2C connection test with logic analyzer&quot; /&gt;
The red and black crocodile clips are coming from a lab power supply, simply providing 5V to power the Orange Pi, battery ground is also connected to the common ground.&lt;/p&gt;

&lt;p&gt;It is also possible to scan for devices with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i2cdetect&lt;/code&gt;, here with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-y&lt;/code&gt; to disable interactive confirmation and on bus 0:&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;orangepipc2:~:# i2cdetect &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; 
10: &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; 
20: &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; 
30: &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; 
40: &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; 
50: &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; 55 &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; 
60: &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; 
70: &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;                         
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It indeed detects the battery with address &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x55&lt;/code&gt;, very nice!&lt;/p&gt;

&lt;h2 id=&quot;command-line-readout&quot;&gt;Command Line Readout&lt;/h2&gt;
&lt;p&gt;With the included &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i2cdump&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i2cget&lt;/code&gt; tools it is possible to read data out from a shell.
The dump reads a range of bytes, here again with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-y&lt;/code&gt; to disable interactive confirmation, range &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x02-0x2d&lt;/code&gt; for the standard registers, bus 0 and address &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x55&lt;/code&gt;.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;W&lt;/code&gt; for 16 bit words, because the chip returns directly 2 bytes, no need to send request for every single address.&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;orangepipc2:~:# i2cdump &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; 0x02-0x2d 0 0x55 W
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00:       00 00 ff ff 71 0b 66 0f 80 01 13 06 27 09      ....q?f???????
10: 02 05 16 08 00 00 ff ff ff ff ff ff ff ff 8a f8    ????..........??
20: 28 00 46 12 00 00 ff ff 6f 0b c0 03 3e 00          &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;.F?....o???&amp;gt;.  

orangepipc2:~:# i2cget &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; 0 0x55 0x08 w
0x0f66

orangepipc2:~:# i2cget &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; 0 0x55 0x2c w
0x003e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you want only a single value then the get is the right tool, after specifying bus and address there is a single register and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w&lt;/code&gt;ord to read 2 bytes. 
Here I read out voltage &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x0f66 = 3942 mV&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x3e = 62 %&lt;/code&gt; state of charge (soc).&lt;/p&gt;

&lt;h2 id=&quot;python-readout&quot;&gt;Python Readout&lt;/h2&gt;
&lt;p&gt;Another good option to get the data programmatically is Python. A popular library exists for SMBus, which is based on the I2C protocol. It provides also the low level methods required here. On my Ubuntu 20.04 based Armbian it can be simply installed through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;orangepipc2:~:# apt search smbus
...
python3-smbus/focal 4.1-2build2 arm64
  Python 3 bindings for Linux SMBus access through i2c-dev

orangepipc2:~:# apt install python3-smbus
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Otherwise &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt; works as also as usual.&lt;/p&gt;

&lt;p&gt;For testing I use a interactive python prompt. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SMBus()&lt;/code&gt; receives a argument for the bus number, 0 in this case:&lt;/p&gt;
&lt;div class=&quot;language-py highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;orangepipc2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# python3
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Python&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Jan&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;27&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2021&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;41&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GCC&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;9.3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linux&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;help&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;copyright&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;credits&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;license&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;more&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;smbus&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;smbus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SMBus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read_word_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x55&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;3942&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read_word_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x55&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x2c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;62&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read_word_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x55&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;273.1&lt;/span&gt;
&lt;span class=&quot;mf&quot;&gt;19.69999999999999&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Again reading words (2 byte) here, in addition to the voltage and soc now also &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x06&lt;/code&gt; = temperature. The result is in 1/10 Kelvin, so it needs the 273.1 offset to get degrees celsius.&lt;/p&gt;

&lt;h2 id=&quot;kernel-driver&quot;&gt;Kernel Driver&lt;/h2&gt;
&lt;p&gt;Okay, enough of the manual playing around. From the the previous look at the Android log it clear that there exists full kernel drivers already. Actually they are in the mainline kernel! This makes installation easier, but just loading the kernel module does not do it. Trust me, I have tried it.&lt;/p&gt;

&lt;h3 id=&quot;device-tree-overlays&quot;&gt;Device Tree Overlays&lt;/h3&gt;
&lt;p&gt;Like on the basic I2C setup a device tree entry is required, but there is no ready-made overlay included for this driver, so it is necessary to make it yourself. Armbian has &lt;a href=&quot;https://docs.armbian.com/User-Guide_Armbian_overlays/&quot;&gt;documentation&lt;/a&gt; and &lt;a href=&quot;https://github.com/armbian/sunxi-DT-overlays/tree/master/examples&quot;&gt;examples&lt;/a&gt; that helps to do this.&lt;/p&gt;

&lt;p&gt;A more detailed description of the syntax is available at &lt;a href=&quot;https://elinux.org/Device_Tree_Usage&quot;&gt;elinux.org (embedded Linux wiki)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Last but not least in the kernel repo is &lt;a href=&quot;https://elixir.bootlin.com/linux/v5.10.23/source/Documentation/devicetree/overlay-notes.rst&quot;&gt;documentation&lt;/a&gt; on how to write overlays for the current version. The Armbian sunxi examples are bit outdated, now it is not needed (or recommended) anymore to wrap the definition with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fragment@0&lt;/code&gt;,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__overlay__&lt;/code&gt;, etc.
Also I have read the &lt;a href=&quot;https://elixir.bootlin.com/linux/v5.10.23/source/Documentation/devicetree/bindings/power/supply/battery.yaml&quot;&gt;battery node&lt;/a&gt; and &lt;a href=&quot;https://elixir.bootlin.com/linux/v5.10.23/source/Documentation/devicetree/bindings/power/supply/bq27xxx.yaml&quot;&gt;TI BQ27xxx driver documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It includes a example DT snippet, I adjusted and saved it in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i2c-bq27541.dts&lt;/code&gt; (.dts = DT source), that is the result:&lt;/p&gt;
&lt;div class=&quot;language-conf highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/&lt;span class=&quot;n&quot;&gt;dts&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;v1&lt;/span&gt;/;
/&lt;span class=&quot;n&quot;&gt;plugin&lt;/span&gt;/;

&amp;amp;&lt;span class=&quot;n&quot;&gt;i2c0&lt;/span&gt; {
    &lt;span class=&quot;c&quot;&gt;#address-cells = &amp;lt;1&amp;gt;;
&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;#size-cells = &amp;lt;0&amp;gt;;
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;bat&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;battery&lt;/span&gt; {
        &lt;span class=&quot;n&quot;&gt;compatible&lt;/span&gt; = &lt;span class=&quot;s2&quot;&gt;&quot;simple-battery&quot;&lt;/span&gt;;
        &lt;span class=&quot;n&quot;&gt;voltage&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;min&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;design&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;microvolt&lt;/span&gt; = &amp;lt;&lt;span class=&quot;m&quot;&gt;3200000&lt;/span&gt;&amp;gt;;
        &lt;span class=&quot;n&quot;&gt;energy&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;full&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;design&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;microwatt&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;hours&lt;/span&gt; = &amp;lt;&lt;span class=&quot;m&quot;&gt;11400000&lt;/span&gt;&amp;gt;;
        &lt;span class=&quot;n&quot;&gt;charge&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;full&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;design&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;microamp&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;hours&lt;/span&gt; = &amp;lt;&lt;span class=&quot;m&quot;&gt;3100000&lt;/span&gt;&amp;gt;;
    };

    &lt;span class=&quot;n&quot;&gt;bq27541&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;fuel&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;gauge&lt;/span&gt;@&lt;span class=&quot;m&quot;&gt;55&lt;/span&gt; {
        &lt;span class=&quot;n&quot;&gt;compatible&lt;/span&gt; = &lt;span class=&quot;s2&quot;&gt;&quot;ti,bq27541&quot;&lt;/span&gt;;
        &lt;span class=&quot;n&quot;&gt;reg&lt;/span&gt; = &amp;lt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x55&lt;/span&gt;&amp;gt;;
        &lt;span class=&quot;n&quot;&gt;monitored&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;battery&lt;/span&gt; = &amp;lt;&amp;amp;&lt;span class=&quot;n&quot;&gt;bat&lt;/span&gt;&amp;gt;;
    };
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Installation was done with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;armbian-add-overlay&lt;/code&gt;, which is just a shell script. It calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dtc&lt;/code&gt; (Device Tree Compiler) first and then copies the result to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot/overlay-user/i2c-bq27541.dtbo&lt;/code&gt; (.dtbo = DT binary/blob overlay) and enables it in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot/armbianEnv.txt&lt;/code&gt;. This is pretty convenient, but can be also done manually of course.&lt;/p&gt;

&lt;p&gt;Now &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;armbianEnv.txt&lt;/code&gt; should contain another line with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user_overlays=i2c-bq27541&lt;/code&gt;. A reboot is required to actually load it.&lt;/p&gt;

&lt;h3 id=&quot;readout&quot;&gt;Readout&lt;/h3&gt;
&lt;p&gt;Now if everything worked, the data can be easily read from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sysfs&lt;/code&gt; power_supply class. I found out that the uevent provides a nice human readable overview:&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;orangepipc2:jk:# &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /sys/class/power_supply/bq27541-0/uevent 
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;bq27541-0
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_TYPE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;Battery
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_STATUS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;Charging
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_PRESENT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_VOLTAGE_NOW&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3942000
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_CURRENT_NOW&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_CAPACITY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;63
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_CAPACITY_LEVEL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;Normal
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_TEMP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;188
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_TECHNOLOGY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;Li-ion
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_CHARGE_FULL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2058000
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_CHARGE_NOW&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1553000
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_CHARGE_FULL_DESIGN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3000000
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_CYCLE_COUNT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;960
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_POWER_AVG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_HEALTH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;Good
&lt;span class=&quot;nv&quot;&gt;POWER_SUPPLY_MANUFACTURER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;Texas Instruments
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Especially interesting is the cycle count of 960 and degraded capacity to just 2058 mAh from over 3000 mAh of a new battery, that info was not accessible through standard Android APIs.&lt;/p&gt;

&lt;p&gt;Of course it is also possible to get single values through the other files in this directory, for example just voltage:&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;orangepipc2:jk:# &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /sys/class/power_supply/bq27541-0/voltage_now                 
3942000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Note that this is in μV, usually the standard units are the smallest common denominator so there is enough resolution for all drivers and applications.&lt;/p&gt;

&lt;h1 id=&quot;further-ideas&quot;&gt;Further Ideas&lt;/h1&gt;
&lt;p&gt;It could be used with a lithium battery step up module to power the Orange Pi (or other SBCs with Linux). There are many choices for a few bucks from China that provide 5V output from the single cell input. I have also seen adjustable output (4.3 - 27V) versions, these even include USB charging.
Maybe add a display to build a clunky hackable smartphone?&lt;/p&gt;</content><author><name>Jake</name></author><category term="battery" /><category term="teardown" /><category term="armbian" /><category term="linux" /><category term="embedded" /><category term="protocol" /><summary type="html">In this post I investigate how a typical Oneplus smartphone battery communicates the current voltage, state of charge etc. to the main device. This can be also useful for repurposing these batteries on a Raspberry or Orange Pi, to power portable projects.</summary></entry><entry><title type="html">UT61E Toolkit: Update 1.4 with Temperature Measurement</title><link href="https://blog.ja-ke.tech/ut61e/android/2021/03/12/UT61E-toolkit-update-1_4.html" rel="alternate" type="text/html" title="UT61E Toolkit: Update 1.4 with Temperature Measurement" /><published>2021-03-12T00:00:00+00:00</published><updated>2021-03-12T00:00:00+00:00</updated><id>https://blog.ja-ke.tech/ut61e/android/2021/03/12/UT61E-toolkit-update-1_4</id><content type="html" xml:base="https://blog.ja-ke.tech/ut61e/android/2021/03/12/UT61E-toolkit-update-1_4.html">&lt;p&gt;This is another app release that adds new data conversion features.
This time: temperature measurement!&lt;/p&gt;

&lt;p&gt;I got the idea after watching Dave’s EEVBlog about the new UT61E+, he criticized the unnecessary hFE mode and missing temperature option. He also said that this would be probably just a software change. So I thought: True! This should be even possible with the older UT61E and so I have implemented it in my app.&lt;/p&gt;

&lt;h2 id=&quot;sensor-types&quot;&gt;Sensor Types&lt;/h2&gt;
&lt;p&gt;From my research I found 3 general measurement principles and popular sensor types. Each of them has different advantages (more on what to choose later) and needs different parameters. They are now configurable in the settings “data processing” category:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/ut61e-android/1_4/thermo-settings.png&quot; alt=&quot;App temperature sensor settings&quot; /&gt;&lt;/p&gt;

&lt;p&gt;All of them include some standard values, that should fit the typical sensors already.&lt;/p&gt;

&lt;p&gt;Note that multiple options can be enabled at the same time. The app then uses the first in the list that fits to the selected range and just applies that calculation. For example if everything is enabled, but the multimeter returns mV values it automatically uses the thermocouple conversion, because it is the only that produces a voltage. Thermistor and RTD are both measured as resistors, so you have to disable thermistors if you want to connect a RTD instead.&lt;/p&gt;

&lt;p&gt;The next sections give a quick introduction how the supported sensor types work.&lt;/p&gt;
&lt;h3 id=&quot;thermocouple&quot;&gt;Thermocouple&lt;/h3&gt;
&lt;p&gt;A very popular sensor, this is usually the only option on meters that already include temperature measurement.&lt;/p&gt;

&lt;p&gt;The sensor consists of 2 wires from different metals. They are connected at on end in the measurement junction and on the other side usually to copper and connected to a voltmeter. The connection to copper forms a reference junction, also called cold junction, but it does not need to be actually colder than the measurement junction. A voltage is generated depending on the temperature difference between the junctions, it is practically linear. Polarity can change, depending on which junction is hotter/colder.&lt;/p&gt;

&lt;p&gt;Multimeter banana jacks are typically placed 19 mm apart, so probes made for other devices fit nicely without modification:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/ut61e-android/1_4/thermocouple-hw.jpg&quot; alt=&quot;UT61E with thermocouple plugged in&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Note: Voltage is almost 0 here, because both junctions are at ambient temperature.&lt;/p&gt;

&lt;p&gt;To calculate the temperature we need to know how much the voltage increases per degree C. Preset is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.039 mV/K&lt;/code&gt; (I am using Kelvin for temperature differences here, but it is of course 1:1 with °C). This is the value for a standardized type K sensor.&lt;/p&gt;

&lt;p&gt;In addition we need the temperature of the reference junction.
There are multiple options, as you can see in the following app screenshots:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/ut61e-android/1_4/thermocouple.png&quot; alt=&quot;App thermocouple measurement&quot; /&gt;
&lt;img src=&quot;/assets/ut61e-android/1_4/thermo-ref-source.png&quot; alt=&quot;App temperature reference source&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you have a stable ambient temperature you can select “constant” and manually put in a temperature (default = 20 °C), this is the most reliable option.&lt;/p&gt;

&lt;p&gt;Otherwise it is possible to use sensors from the connected smartphone for this. Most devices include temperature sensors for CPU/GPU, battery, etc… in my case 81 sensors! Not all report a valid measurement though, some are deep in the negative or always 0.0. Select one that looks valid, here battery related ones are pretty good.
Obvious potential issue is the self heating of the smartphone, it will probably report a bit more than ambient. But the advantage is that it continuously updates, it should not be distorted too much if you don’t touch or otherwise use the phone while measuring.&lt;/p&gt;

&lt;p&gt;The multimeter should be in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;220 mV&lt;/code&gt; range and provide &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.01 mV&lt;/code&gt; resolution, combined with
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.039 mV/K&lt;/code&gt; this gives a resolution of about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1/4 = 0.25 K&lt;/code&gt;.
Other ranges are not necessary as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;220 mV / 0.039 =  ± 5600 K&lt;/code&gt; is much more then these sensors can withstand anyway.&lt;/p&gt;

&lt;h3 id=&quot;thermistor&quot;&gt;Thermistor&lt;/h3&gt;
&lt;p&gt;The name is a combination of thermal and resistor, this already explains what it is: a simple resistor.
Typically NTCs (negative temperature coefficient) are used as sensors, so the resistance gets lower if the temperature increases.&lt;/p&gt;

&lt;p&gt;Calculation of the temperature from the resistance is non-linear, which makes it more complex.
For example a bit of python code to get the resistance from temperature:&lt;/p&gt;
&lt;div class=&quot;language-py highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;r0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100_000&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Ohm
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;273.15&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# absolute temperature for r0
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3950&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# change rate coefficient
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;resistance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;273.15&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It contains the exponential &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exp()&lt;/code&gt; function (based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e = 2.71828...&lt;/code&gt;) and this causes massive changes in resistance, which makes it a challenge to measure over large temperature ranges. For example a NTC with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;100 kΩ @ 25°C&lt;/code&gt; has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;200 kΩ @ 10°C (with 10 kΩ/K change rate)&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.59 kΩ @ 125°C (with 0.09 kΩ/K change rate)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Read more: &lt;a href=&quot;https://reprap.org/wiki/Thermistor&quot;&gt;https://reprap.org/wiki/Thermistor&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://github.com/reprap/firmware/blob/master/createTemperatureLookup.py&quot;&gt;https://github.com/reprap/firmware/blob/master/createTemperatureLookup.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the usual fixed voltage dividers this reduces the resolution, but with the UT61E it is quite a bit different, I made a graph to make this better visible:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/ut61e-android/1_4/thermistor-ranges.svg&quot; alt=&quot;UT61E thermistor ranges and resolution&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The blue line shows the thermistor resistance from -100 °C up to 250°C , it is logarithmically scaled on the right Y axis, otherwise it would be impossible to see. On the low end it goes over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1e9 Ω = 1 GΩ&lt;/code&gt; while at the high temperatures it is just a few hundred Ohms. Nominal resistance is again &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1e5 Ω = 100 kΩ&lt;/code&gt;.
Then the green line is the max. value of the range the multimeter will be in, this must be always at least as much as the actual resistance, to be able to measure it. Note that below -85 °C this does not work anymore, because it can’t measure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt; 220 MΩ&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now the red line shows the resolution, as you can see the autoranging is really helpful here. The resolution is naturally getting worse with increasing temperature, but switching to a lower resistance range counteracts this. Worst case is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.02 K&lt;/code&gt; and best case &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt; 0.001 K&lt;/code&gt;, which is extremely fine.&lt;/p&gt;

&lt;p&gt;At 20 °C is also a good area with about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.002 K&lt;/code&gt;, in my tests this even detects the heat radiation from my body, could be made into some sort of distance sensor.&lt;/p&gt;

&lt;p&gt;Here is how it looks in the app, again showing the resolution:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/ut61e-android/1_4/thermistor.png&quot; alt=&quot;App thermistor measurement&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;resistance-temperature-detector-rtd&quot;&gt;Resistance Temperature Detector (RTD)&lt;/h3&gt;
&lt;p&gt;RTDs are also resistors, but with a positive temperature coefficient and made out of wire or thin film of a pure metal. This makes them practically linear and gives them good accuracy and repeatability (low drift). Typical material is platinum with the PT100, PT500 and PT1000. The number specifies the nominal resistance at 0 °C, this should be configured in the app settings.&lt;/p&gt;

&lt;p&gt;Otherwise there is a the alpha, similar to the thermocouple this just specifies how much the resistance changes per degree. The default of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.00385 Ω/(Ω·°C)&lt;/code&gt; is standardized and should fit almost anything.&lt;/p&gt;

&lt;p&gt;It looks like this in the app:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/ut61e-android/1_4/rtd.png&quot; alt=&quot;App RTD measurement&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Resolution on the UT61E with PT100 and PT1000 is equal:&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.00385 * 100 Ω = 0.385 Ω/K in 220 Ω range with 0.01 resolution = 1/38 = 0.026 K&lt;/code&gt;&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.00385 * 1000 Ω = 3.85 Ω/K in 2.2 kΩ range with 0.1 resolution = 1/38 = 0.026 K&lt;/code&gt;&lt;br /&gt;
It will just switch to the better matching range. Other ranges are not required up to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(220-100) Ω / 0.385 = +312 °C&lt;/code&gt; after that it will switch up (factor 10) and reduce the resolution to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.26 K&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;sensor-selection&quot;&gt;Sensor Selection&lt;/h2&gt;
&lt;p&gt;After all you might ask: What sensor should I choose? For that I have made an short overview:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Type&lt;/th&gt;
      &lt;th&gt;Sensor&lt;/th&gt;
      &lt;th&gt;Max. T&lt;/th&gt;
      &lt;th&gt;Resolution&lt;/th&gt;
      &lt;th&gt;Accuracy&lt;/th&gt;
      &lt;th&gt;Cost incl. wire&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Thermocouple&lt;/td&gt;
      &lt;td&gt;Type K&lt;/td&gt;
      &lt;td&gt;1200 °C&lt;/td&gt;
      &lt;td&gt;0.25 K&lt;/td&gt;
      &lt;td&gt;2 K (0.75%)&lt;/td&gt;
      &lt;td&gt;~ 1 $&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Thermistor&lt;/td&gt;
      &lt;td&gt;NTC 100k&lt;/td&gt;
      &lt;td&gt;300 °C&lt;/td&gt;
      &lt;td&gt;0.001-0.02 K&lt;/td&gt;
      &lt;td&gt;3-15 K (5%)&lt;/td&gt;
      &lt;td&gt;~ 0.5 $&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;RTD&lt;/td&gt;
      &lt;td&gt;PT1000&lt;/td&gt;
      &lt;td&gt;600 °C&lt;/td&gt;
      &lt;td&gt;0.026 K&lt;/td&gt;
      &lt;td&gt;&amp;lt; 0.5 K&lt;/td&gt;
      &lt;td&gt;~ 3 $&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Thermocouples are good for very high temperatures, but resolution and accuracy is not the best. Thermistors on the other hand provide insanely high resolutions, but only at relatively low temperatures and the accuracy is pretty bad, because of the non linearity. RTDs are the most accurate (and expensive), with acceptable resolution and max. temperature.&lt;/p&gt;

&lt;p&gt;As always: there is no clear winner, it really depends on the application.&lt;/p&gt;</content><author><name>Jake</name></author><category term="ut61e" /><category term="android" /><category term="software" /><category term="android" /><category term="multimeter" /><category term="bluetooth" /><category term="data" /><category term="release" /><summary type="html">This is another app release that adds new data conversion features. This time: temperature measurement!</summary></entry><entry><title type="html">UT61E Toolkit: Update 1.3</title><link href="https://blog.ja-ke.tech/ut61e/android/2021/03/11/UT61E-toolkit-update-1_3.html" rel="alternate" type="text/html" title="UT61E Toolkit: Update 1.3" /><published>2021-03-11T00:00:00+00:00</published><updated>2021-03-11T00:00:00+00:00</updated><id>https://blog.ja-ke.tech/ut61e/android/2021/03/11/UT61E-toolkit-update-1_3</id><content type="html" xml:base="https://blog.ja-ke.tech/ut61e/android/2021/03/11/UT61E-toolkit-update-1_3.html">&lt;p&gt;After almost 2 years I began working on my app for the UT61E (and others from the UT61 series) again.
This release includes only small visible changes, a lot of work went into updating the Android API, especially the new security features required changes.&lt;/p&gt;

&lt;h2 id=&quot;ui-improvements&quot;&gt;UI Improvements&lt;/h2&gt;
&lt;p&gt;First let’s look at the user interface changes. I have adjusted the background and colors for the categories, this should make it easier to read (better contrast):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/ut61e-android/1_3/start-colors.png&quot; alt=&quot;App start new colors&quot; /&gt;
&lt;img src=&quot;/assets/ut61e-android/1_3/scan-rssi.png&quot; alt=&quot;App scan RSSI bar&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Also I noticed that the scan results include a RSSI (Received Signal Strength Indication) value, this now gets showed as bar in list as well. It helps to find the correct device if there are many detected, the nearest device/multimeter should show the highest RSSI.&lt;/p&gt;
&lt;h2 id=&quot;bugfixes&quot;&gt;Bugfixes&lt;/h2&gt;
&lt;p&gt;There was a bug with other bluetooth modules. It was &lt;a href=&quot;https://github.com/Jakeler/UT61E-Toolkit/issues/10&quot;&gt;reported on GitHub&lt;/a&gt; from someone who used JDY-10 module, which worked with other apps, but here it did not return any data. 
The module needs a explicit write into a configuration register to enable notifications. This fix is included in 1.3 as well.&lt;/p&gt;
&lt;h2 id=&quot;sdk-platform&quot;&gt;SDK Platform&lt;/h2&gt;
&lt;p&gt;As already mentioned I haven’t touched the codebase for a while, it still targeted SDK 27 (Android 8.1), so I had to jump 3 levels to SDK 30 (Android 11).&lt;/p&gt;

&lt;p&gt;In previous versions there was a setting called “Log folder” in the app. You could put in a string with a name or path, then the app would internally create this folder and put all CSV logs into it. Now this is not possible anymore in the normal internal storage. Android 10 introduced &lt;a href=&quot;https://developer.android.com/training/data-storage#scoped-storage&quot;&gt;scoped storage&lt;/a&gt; and Android 11 enforces it. Apps only get access to their app-specific directory.&lt;/p&gt;

&lt;p&gt;Otherwise the user can select a folder or file with the system file browser and grant permission.
This is implemented now, instead of a string input the app show the file explorer. It think this is nicer anyway to select it like this, instead of error prone manually typing the path. After it got scoped access to a directory the actual log files can still be created automatically.&lt;/p&gt;</content><author><name>Jake</name></author><category term="ut61e" /><category term="android" /><category term="software" /><category term="android" /><category term="multimeter" /><category term="bluetooth" /><category term="data" /><category term="release" /><summary type="html">After almost 2 years I began working on my app for the UT61E (and others from the UT61 series) again. This release includes only small visible changes, a lot of work went into updating the Android API, especially the new security features required changes.</summary></entry><entry><title type="html">DIY GoTo Telescope Mount [3] = Electronics for first Stepper Test</title><link href="https://blog.ja-ke.tech/2020/11/02/diy-goto-3-stepper-test.html" rel="alternate" type="text/html" title="DIY GoTo Telescope Mount [3] = Electronics for first Stepper Test" /><published>2020-11-02T00:00:00+00:00</published><updated>2020-11-02T00:00:00+00:00</updated><id>https://blog.ja-ke.tech/2020/11/02/diy-goto-3-stepper-test</id><content type="html" xml:base="https://blog.ja-ke.tech/2020/11/02/diy-goto-3-stepper-test.html">&lt;p&gt;This is the next part in my telescope series, look at the other posts for more details. In this post I describe how I set up a basic controller to test the mechanical setup.&lt;/p&gt;

&lt;h1 id=&quot;electrical-prototype&quot;&gt;Electrical Prototype&lt;/h1&gt;
&lt;p&gt;As already mentioned I want to make it as silent as so possible, so I decided to go for TMC2208 in StealthChop mode. First I thought about using breadboard and jumper cables, it is not too complex and the pinout/schematics of my driver breakout modules is available in the &lt;a href=&quot;https://github.com/bigtreetech/BIGTREETECH-TMC2208-V3.0&quot;&gt;BigTreeTech GitHub repo&lt;/a&gt;. But because I had a spare RAMPS 1.4 shield from a 3D printer it was easier to just use that.&lt;/p&gt;

&lt;p&gt;These TMC drivers are pin compatible to the older/cheaper A4988 or DRV8825. Only noticeable setup difference that is has only 2 instead of 3 pins (or jumper options) for the microstepping, with this layout:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;MS1&lt;/th&gt;
      &lt;th&gt;MS2&lt;/th&gt;
      &lt;th&gt;Microstepping&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;V_IO&lt;/td&gt;
      &lt;td&gt;V_IO&lt;/td&gt;
      &lt;td&gt;16&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GND&lt;/td&gt;
      &lt;td&gt;GND&lt;/td&gt;
      &lt;td&gt;8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GND&lt;/td&gt;
      &lt;td&gt;V_IO&lt;/td&gt;
      &lt;td&gt;4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;V_IO&lt;/td&gt;
      &lt;td&gt;GND&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;V_IO&lt;/code&gt; means pullup to the IO Voltage, +5V from USB in this case. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GND&lt;/code&gt; is a pulldown (default).&lt;/p&gt;

&lt;p&gt;For now I run it with 8x microstepping, no jumpers plugged in, I might change that later. Then I simply inserted the drivers and connected the motors and USB to the Arduino Mega underneath:
&lt;img src=&quot;/assets/goto-telescope/e-test-ramps.jpg&quot; alt=&quot;RAMPS PCB with stepper drivers&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To supply the motors I used a adjustable DC/DC converter (B3603) and laptop PSU set to 24V.&lt;/p&gt;

&lt;p&gt;In theory it is also possible to run it on 5V only (section 3.3 in &lt;a href=&quot;https://www.trinamic.com/fileadmin/assets/Products/ICs_Documents/TMC220x_TMC2224_datasheet_Rev1.09.pdf&quot;&gt;Trinamics datasheet&lt;/a&gt;), but it would be less efficient and provide way lower torque/speed.&lt;/p&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Here is everything in one picture:
&lt;img src=&quot;/assets/goto-telescope/e-test-setup.jpg&quot; alt=&quot;electronics setup overview&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;arduino-code&quot;&gt;Arduino Code&lt;/h1&gt;
&lt;p&gt;Controlling the stepper drivers is really simple, no library required. Basically there is a DIR pin, which must be pulled high or low depending on the direction. Then just switch the STEP pin to execute a (micro)step, with some delays. I made this function to do one full rotation, taking the 8x microstepping into account:&lt;/p&gt;
&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// microsecond timing&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pulse&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pause&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;microsteps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;steps_per_rev&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;microsteps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clockwise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;digitalWrite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directionPin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clockwise&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HIGH&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LOW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;steps_per_rev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;digitalWrite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stepPin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HIGH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;delayMicroseconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pulse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;digitalWrite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stepPin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LOW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
   
    &lt;span class=&quot;n&quot;&gt;delayMicroseconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
   
    &lt;span class=&quot;n&quot;&gt;digitalWrite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ledPin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;digitalRead&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ledPin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Note: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;steps_per_rev&lt;/code&gt; is declared as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;long&lt;/code&gt;, because I experienced overflows when doing math with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt; here. On 8 bit Arduino (Atmel AVR) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt; defaults to 16 bit, that is only about +/- 32000. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;long&lt;/code&gt; is 32 bit. Another (probably even better) solution would be to specifically use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uint32_t&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This above code works well if there is no load, but with the inertia af the mount (about 10 kg mass) it would only allow very low speeds, otherwise it loses steps when starting a movement.&lt;/p&gt;

&lt;p&gt;The solution is to limit the acceleration and slowly increase the speed. I used the &lt;a href=&quot;http://www.airspayce.com/mikem/arduino/AccelStepper/&quot;&gt;AccelStepper&lt;/a&gt; library for this. Basic setup code looks like that:&lt;/p&gt;
&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;AccelStepper&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;stepper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stepPin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directionPin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;stepper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnablePin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enablePin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;stepper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setPinsInverted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;stepper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enableOutputs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;stepper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setAcceleration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;steps_per_rev&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;stepper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setMaxSpeed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;steps_per_rev&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Rotation/second&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It is important to set the enablePin to inverted = true, otherwise it will not run.&lt;/p&gt;

&lt;p&gt;Finally to rotate back/forth use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stepper.move(steps)&lt;/code&gt; or go to a absolute position with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stepper.moveTo(stepPos)&lt;/code&gt;. Constantly call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stepper.run()&lt;/code&gt; in a loop to actually execute it.&lt;/p&gt;

&lt;p&gt;You can download the whole code &lt;a href=&quot;/assets/goto-telescope/test_code.cpp&quot;&gt;here&lt;/a&gt;. In addition the AccelStepper library must be installed and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pins.h&lt;/code&gt; with the RAMPS pin definitions added, available at the
&lt;a href=&quot;https://www.reprap.org/wiki/RAMPS_1.4#Firmware_and_Pin_Assignments&quot;&gt;RepRap wiki&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Further it could be improved utilizing the UART interface on these drivers, it allows to configure the current, microstepping, etc. through software, like in this &lt;a href=&quot;https://github.com/teemuatlut/TMCStepper/blob/master/examples/Simple/Simple.ino&quot;&gt;TMCStepper example&lt;/a&gt;. But for now it legacy (also called standalone) is fine for testing.&lt;/p&gt;

&lt;h1 id=&quot;video-demo&quot;&gt;Video Demo&lt;/h1&gt;
&lt;figure&gt;
    &lt;div class=&quot;video-container&quot;&gt;
        &lt;iframe class=&quot;yt-video&quot; src=&quot;https://www.youtube.com/embed/nFY8TDP12cU&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;
        &lt;/iframe&gt;
    &lt;/div&gt;
&lt;/figure&gt;</content><author><name>Jake</name></author><category term="telescope" /><category term="astronomy" /><category term="hardware" /><category term="embedded" /><category term="software" /><summary type="html">This is the next part in my telescope series, look at the other posts for more details. In this post I describe how I set up a basic controller to test the mechanical setup.</summary></entry><entry><title type="html">DIY GoTo Telescope Mount [2] = Azimuth Axis</title><link href="https://blog.ja-ke.tech/2020/09/21/diy-goto-2-az-setup.html" rel="alternate" type="text/html" title="DIY GoTo Telescope Mount [2] = Azimuth Axis" /><published>2020-09-21T00:00:00+00:00</published><updated>2020-09-21T00:00:00+00:00</updated><id>https://blog.ja-ke.tech/2020/09/21/diy-goto-2-az-setup</id><content type="html" xml:base="https://blog.ja-ke.tech/2020/09/21/diy-goto-2-az-setup.html">&lt;p&gt;This post shows the build of the azimuth (horizontal) axis for my GOTO telescope mount.&lt;/p&gt;

&lt;h2 id=&quot;large-pulley-profile&quot;&gt;Large Pulley Profile&lt;/h2&gt;
&lt;h3 id=&quot;cad-design&quot;&gt;CAD Design&lt;/h3&gt;
&lt;p&gt;First I tried to copy the tooth profile from the altitude axis pulley model &lt;a href=&quot;/2020/08/02/diy-goto-1-alt-setup.html&quot;&gt;(previous post)&lt;/a&gt;, but it did not work, because it is not strictly defined, so I made a new new drawing, based on the parameters:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/goto-telescope/gt2tooth.jpg&quot; alt=&quot;gt2 belt tooth profile&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now fully constrained, with the exact measurements. Still with some tolerance for the 3D printing, implemented through a variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#fine_tol&lt;/code&gt; that intentionally makes the teeth a bit wider, here set to 0.05 mm.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/goto-telescope/cad-az-cutout.png&quot; alt=&quot;cad belt tooth drawing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Similar to the previous approach the tooth gets repeated with a curve pattern and cut into the part. 
But now (as already noted in the “learnings” section) I put a valley on the edge, this gives it a less sharp edge and is therefore easier to print.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/goto-telescope/cad-az-teeth.png&quot; alt=&quot;cad pulley teeth profile&quot; /&gt;
Important is to add one to the teeth count calculation, because half of the tooth is protruding on each end, so effectively one full tooth is lost.&lt;/p&gt;

&lt;p&gt;To make it fit on my printer build area I had to cut it again in segments. It is about 400 mm diameter = 1200 mm circumference. Making 7 parts would have worked, but I decided on 8 because it results in round numbers. So each part covers 45 degrees and is about 150 mm wide:
&lt;img src=&quot;/assets/goto-telescope/cad-az-pulley-45deg.png&quot; alt=&quot;cad azimuth pulley segment&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It fits in a circle as virtual assembly from all 8 segments:
&lt;img src=&quot;/assets/goto-telescope/cad-az-ass.png&quot; alt=&quot;cad all segments circle&quot; /&gt;
Imagine this ring around the static base plate of my existing telescope mount.&lt;/p&gt;

&lt;h3 id=&quot;print&quot;&gt;Print&lt;/h3&gt;
&lt;p&gt;All segments were printed with PLA and 0.3 mm layer height 
(two different color because of what I had left). 
&lt;img src=&quot;/assets/goto-telescope/az-profile-x8.jpg&quot; alt=&quot;azimuth pulley profile 8 segments&quot; /&gt;
The edges came out much better this time, did not require any postprocessing:
&lt;img src=&quot;/assets/goto-telescope/az-profile-edge.jpg&quot; alt=&quot;azimuth pulley profile print edge&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;motor-mount&quot;&gt;Motor Mount&lt;/h2&gt;
&lt;p&gt;The motor should be connected to top plate of the base. So the shaft points downwards to sit at the same height as the belt, which goes around the bottom base plate.&lt;/p&gt;

&lt;h3 id=&quot;cad-design-1&quot;&gt;CAD design&lt;/h3&gt;
&lt;p&gt;The design is not too complicated, I added again some space to slide the motor, which allows adjustment of the belt tension.
It has a slightly curved wall, where it touches the round base plate.
&lt;img src=&quot;/assets/goto-telescope/cad-az-mount-bottom.png&quot; alt=&quot;cad motor mount bottom&quot; /&gt;
&lt;img src=&quot;/assets/goto-telescope/cad-az-mount.png&quot; alt=&quot;cad motor mount top&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;print-and-assembly&quot;&gt;Print and Assembly&lt;/h3&gt;
&lt;p&gt;I drilled again four 2 mm holes and screwed it onto the plate.
&lt;img src=&quot;/assets/goto-telescope/az-motor-build.jpg&quot; alt=&quot;az axis drive assembly&quot; /&gt;
The printed profile is currently only attached by some double side tape. This is enough, because the belt clamps it as well, but I will nevertheless replace it with a more solid glue soon.
&lt;img src=&quot;/assets/goto-telescope/az-motor-ass.jpg&quot; alt=&quot;az axis drive assembly side&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;
&lt;p&gt;As already mentioned I used the online CAD tool Onshape, so if you want to print it yourself you can &lt;a href=&quot;https://cad.onshape.com/documents/d96f6b52fcedf27c1cb9d178/w/9f35ae8134da83f73b0b2555/e/a3e62897b58af1effd0b9960&quot;&gt;download it here&lt;/a&gt; 
(see the tabs on the bottom for all other parts).&lt;/p&gt;

&lt;p&gt;They support export as STL and many other CAD formats. 
It should be also possible to copy the project there (needs an account) and adjust all the variables, like diameter, belt size, sections, tolerances… or make other modifications.&lt;/p&gt;
&lt;h2 id=&quot;outlook&quot;&gt;Outlook&lt;/h2&gt;
&lt;p&gt;In the next post I will finally show it in action! With the basic electronics and controller code that I have so far, to test the mechanical setup.&lt;/p&gt;</content><author><name>Jake</name></author><category term="telescope" /><category term="astronomy" /><category term="plastics" /><category term="3d-print" /><category term="hardware" /><summary type="html">This post shows the build of the azimuth (horizontal) axis for my GOTO telescope mount.</summary></entry><entry><title type="html">DIY GoTo Telescope Mount [1] = Altitude Axis</title><link href="https://blog.ja-ke.tech/2020/08/02/diy-goto-1-alt-setup.html" rel="alternate" type="text/html" title="DIY GoTo Telescope Mount [1] = Altitude Axis" /><published>2020-08-02T00:00:00+00:00</published><updated>2020-08-02T00:00:00+00:00</updated><id>https://blog.ja-ke.tech/2020/08/02/diy-goto-1-alt-setup</id><content type="html" xml:base="https://blog.ja-ke.tech/2020/08/02/diy-goto-1-alt-setup.html">&lt;p&gt;This post is the first real part of my self made GoTo mount series.&lt;/p&gt;

&lt;p&gt;It explains the CAD design, 3D print and assembly of the motorized drive on the altitude (vertical) axis.&lt;/p&gt;

&lt;h2 id=&quot;main-pulley&quot;&gt;Main Pulley&lt;/h2&gt;
&lt;h3 id=&quot;cad-design&quot;&gt;CAD Design&lt;/h3&gt;
&lt;p&gt;I used Onshape to create a custom 3D model of the pulley. First I measured the length, hole spacing, diameter etc… of the existing tubus mounting rail and saved the values in variables. Then a sketch was built based on them:
&lt;img src=&quot;/assets/goto-telescope/cad-alt-pulley-sketch.png&quot; alt=&quot;cad-alt-pulley&quot; /&gt;
Because of the slightly smaller than 200x200 mm effective build area from my old Prusa i3 printer it was not possible to make the whole pulley in one piece, so I split it into four pieces and added holes to join them with screws afterwards.&lt;/p&gt;

&lt;p&gt;The rail length is only 200 mm, but to allow the edges to fit into the circle, it has of course a bigger diameter. I need the diameter to calculate the correct teeth spacing to fit the GT2 belt, which has 2 mm pitch, so I installed a FeatureScript plugin that can measure parts and create variables. From that the tooth angle and count are calculated for the next sketch. To get a close to integer count (of 86 teeth per quarter circle) I adjusted the base width to 203 mm. 
&lt;img src=&quot;/assets/goto-telescope/cad-alt-teeth-calc.png&quot; alt=&quot;cad-alt-teeth&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I made the profile loosely based on the GT2 belt definition, adding 0.1 mm tolerance where I saw fit:
&lt;img src=&quot;/assets/goto-telescope/cad-alt-teeth-cutout.png&quot; alt=&quot;cad-alt-teeth&quot; /&gt;
Notable here is that it looks like the one side is not on the circle outline, I checked the constraints and it is actually on the line, but Onshape renders the large circle in a low resolution, making it look like a straight line. This is only a optical issue, the exported model is fine.&lt;/p&gt;

&lt;p&gt;After a circular remove pattern this is the resulting belt profile:
&lt;img src=&quot;/assets/goto-telescope/cad-alt-teeth.png&quot; alt=&quot;cad-alt-teeth&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;tests-and-fixes&quot;&gt;Tests and Fixes&lt;/h3&gt;
&lt;p&gt;In Onshapes assembly tool the whole pulley seemed to fit fine:
&lt;img src=&quot;/assets/goto-telescope/cad-alt-pulley.png&quot; alt=&quot;cad-alt-pulley full&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So I proceeded to actually printing the parts. It needed a few tests and adjustments to get the tolerances right, especially where it interfaces the rail, after 4 iterations I was quite happy with the result:
&lt;img src=&quot;/assets/goto-telescope/alt-pulley-pieces.jpg&quot; alt=&quot;alt-pulley-pieces&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Following I began to tighten the screws for the first time on the rail and noticed that the pulley gets crooked.&lt;/p&gt;

&lt;p&gt;This is because the rail bottom is also slightly skewed inside:
&lt;img src=&quot;/assets/goto-telescope/alt-mount-side.jpg&quot; alt=&quot;alt-mount-side&quot; /&gt;
It is about 1 mm deeper on the sides, measured from the flat top. I did not want to print all parts again, so I quickly made some wedge shaped spacers, that are also secured through the screw:
&lt;img src=&quot;/assets/goto-telescope/cad-alt-pulley-spacer.png&quot; alt=&quot;cad-alt-pulley-spacer&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;assembly&quot;&gt;Assembly&lt;/h3&gt;
&lt;p&gt;After closer inspection I saw some things that required cleanup. The Teeth on seams are cut in half, so there is a quite fine an sharp edge and this resulted in some edge bulging, which I had to scrape off to produce a good belt fit:
&lt;img src=&quot;/assets/goto-telescope/alt-pulley-cleanup.jpg&quot; alt=&quot;alt-pulley-cleanup&quot; /&gt;
In addition I sanded the surfaces that touch other parts a bit.&lt;/p&gt;

&lt;p&gt;After all it fits nicely on the mounting rail:
&lt;img src=&quot;/assets/goto-telescope/alt-pulley-ass1.jpg&quot; alt=&quot;alt-pulley-ass1&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;small-pulley&quot;&gt;Small pulley&lt;/h2&gt;
&lt;h3 id=&quot;motor-mount&quot;&gt;Motor Mount&lt;/h3&gt;
&lt;p&gt;Mounting the motor is done with a pretty simple bracket on the base plate. In CAD:
&lt;img src=&quot;/assets/goto-telescope/cad-motor-mount.png&quot; alt=&quot;cad-motor-mount&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It has long slots, which allows different positions for the motor, so it can be used to produce an adequate belt tension. Printed and assembled it looks like that:
&lt;img src=&quot;/assets/goto-telescope/alt-motor.jpg&quot; alt=&quot;alt-motor&quot; /&gt;
As small pulley I use a standard 20 teeth aluminum part. Combined with the large custom 430 teeth pulley it creates a tranmission ration of 21.5.&lt;/p&gt;

&lt;h2 id=&quot;final-assembly&quot;&gt;Final Assembly&lt;/h2&gt;
&lt;p&gt;In the end I put the tubus back on the original wooden base:
&lt;img src=&quot;/assets/goto-telescope/alt-pully-ass2.jpg&quot; alt=&quot;alt-pully-ass2&quot; /&gt;
Then cut of a piece of belt and connected the end with a cable tie (just like on 3d printers).
&lt;img src=&quot;/assets/goto-telescope/alt-pulley-ass3.jpg&quot; alt=&quot;alt-pulley-ass3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I pre drilled four holes with a 2 mm drill (3.5 mm screws) for the motor mount:
&lt;img src=&quot;/assets/goto-telescope/alt-motor-drill.jpg&quot; alt=&quot;alt-pulley-ass3&quot; /&gt;
&lt;img src=&quot;/assets/goto-telescope/alt-motor-holes.jpg&quot; alt=&quot;alt-pulley-ass3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And secured it with wood screws. Now it is possible to put tension on the belt:
&lt;img src=&quot;/assets/goto-telescope/alt-motor-ass.jpg&quot; alt=&quot;alt-pulley-ass3&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;
&lt;p&gt;As already mentioned I used the online CAD tool Onshape, so if you want to print it yourself you can &lt;a href=&quot;https://cad.onshape.com/documents/d96f6b52fcedf27c1cb9d178/w/9f35ae8134da83f73b0b2555/e/7b7728c036d3f17930a13f46&quot;&gt;download it here&lt;/a&gt; 
(see the tabs on the bottom for all other parts).&lt;/p&gt;

&lt;p&gt;They support export as STL and many other CAD formats. 
It should be also possible to copy the project there (needs an account) and adjust all the variables, like diameter, belt size, sections, tolerance… or make other modifications.&lt;/p&gt;

&lt;h2 id=&quot;learnings&quot;&gt;Learnings&lt;/h2&gt;
&lt;p&gt;After all I want to shortly write about things that I learned and will do differently in the future projects.&lt;/p&gt;
&lt;h3 id=&quot;model-everything-in-cad-first&quot;&gt;Model everything in CAD first&lt;/h3&gt;
&lt;p&gt;It would have saved a lot of time and also some material, if I would have taken the time to model the whole telescope (including this mounting rail) in CAD first and then assembled it virtually. Without a full model I overlooked some pieces that would collide and had to edit the pulley geometry again.&lt;/p&gt;

&lt;h3 id=&quot;support-tolerances&quot;&gt;Support tolerances&lt;/h3&gt;
&lt;p&gt;The Z axis height for parts on the print bed is very accurate and usually doesn’t need any tolerance. But on supports some tolerance is absolutely required (at least with PrusaSlicer support), 0.5 mm would have been suitable in this case.&lt;/p&gt;

&lt;h3 id=&quot;avoid-thin-and-sharp-edges&quot;&gt;Avoid thin and sharp edges&lt;/h3&gt;
&lt;p&gt;Under normal circumstances this 3D printer does not cause noticeable corner bulging. But with very fine and sharp edges it is a problem. This could have been prevented already in the design, with placing a “valley” on the profile edge, instead of a (half) tooth.&lt;/p&gt;</content><author><name>Jake</name></author><category term="telescope" /><category term="astronomy" /><category term="plastics" /><category term="3d-print" /><category term="hardware" /><summary type="html">This post is the first real part of my self made GoTo mount series.</summary></entry><entry><title type="html">DIY GoTo Telescope Mount [0] = Introduction and Concept</title><link href="https://blog.ja-ke.tech/2020/07/09/diy-goto-0-intro.html" rel="alternate" type="text/html" title="DIY GoTo Telescope Mount [0] = Introduction and Concept" /><published>2020-07-09T00:00:00+00:00</published><updated>2020-07-09T00:00:00+00:00</updated><id>https://blog.ja-ke.tech/2020/07/09/diy-goto-0-intro</id><content type="html" xml:base="https://blog.ja-ke.tech/2020/07/09/diy-goto-0-intro.html">&lt;p&gt;This is the introduction to a multi part series about building a GoTo system for a Dobson style telescope mount. It should be computer controlled and be able to move to a selected target and also automatically track it. I want to use popular and inexpensive parts known from 3D printing for the mechanical setup, mostly leftovers from upgrade of my Prusa i3 printer.&lt;/p&gt;

&lt;h3 id=&quot;links-to-other-parts&quot;&gt;Links to other Parts&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2020/08/02/diy-goto-1-alt-setup.html&quot;&gt;DIY GoTo Telescope Mount [1] = Altitude Axis&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2020/09/21/diy-goto-2-az-setup.html&quot;&gt;DIY GoTo Telescope Mount [2] = Azimuth Axis&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2020/11/02/diy-goto-3-stepper-test.html&quot;&gt;DIY GoTo Telescope Mount [3] = Electronics for first Stepper Test&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;starting-point&quot;&gt;Starting Point&lt;/h3&gt;
&lt;p&gt;My telescope for this project is a Bresser Messier DOB 150/750, this is a Newtonian reflector with a 150 mm (6 inch) parabolic mirror and 750 mm focal length:
&lt;img src=&quot;/assets/goto-telescope/start.jpg&quot; alt=&quot;base telescope photo&quot; /&gt;
It includes a simple alt-azimuth “Dobson” mount, made out of wood. For the azimuth (horizontal) axis are two circular sheets of wood stacked in the base one above the other, separated with some sliding pads that allows the whole top to rotate.&lt;/p&gt;

&lt;p&gt;The altitude (vertical) axis is also simply a rotatable mounting point, where the optical tube is attached. After loosening the screw the tube can separated from the mount base:
&lt;img src=&quot;/assets/goto-telescope/alt-mount-top.jpg&quot; alt=&quot;tube mount top view&quot; /&gt;
This is a standard GP prism rail piece. Especially the four additional threaded holes are interesting, they contained M5 screws, which are not necessary for stability of the mount, so this is a good place to attach parts for the motorization.&lt;/p&gt;

&lt;h3 id=&quot;mechanical-concept&quot;&gt;Mechanical Concept&lt;/h3&gt;
&lt;p&gt;The idea is to keep the whole existing structure and just add motors with minimal additional material. I have seen other selfmade GoTo projects, where the whole mount was replaced with a custom structure. This is really impressive, but also very complex and I don’t have a suitable shop for serious wood working or even CNC machining metal.&lt;/p&gt;

&lt;p&gt;I decided to use Nema 17 steppers as drive for the system. To get a suitable reduction ratio a GT2 belt will be used, with an small aluminum pulley on the motor an a large pulley connected to the telescope. All parts (like mounting brackets) should be also 3D printed.&lt;/p&gt;

&lt;h4 id=&quot;altitude-axis&quot;&gt;Altitude Axis&lt;/h4&gt;
&lt;p&gt;Planned here is to construct a pulley around the prism rail, this gives about 200 mm of diameter and allows robust mounting with the M5 holes mentioned above. 
The stepper motor should be placed on the base plate directly below the rail/pulley and connected through a long belt loop.&lt;/p&gt;

&lt;h4 id=&quot;azimuth-axis&quot;&gt;Azimuth Axis&lt;/h4&gt;
&lt;p&gt;Because the base is already a large (about 400 mm diameter) circle I want to directly use it as pulley, only adding a thin profile to get solid grip with the belt. First idea was to simply glue a belt onto it with tooths facing outside. But I noticed quickly that this only works on flat surfaces, if the two layers of belt are bent around a circle the inner circumference would have to be shorter of course and the tooths with the same pitch don’t line up anymore. Only solution is to again print a custom profile taking that into account.
The motor has to be protruding above the edge of the circle here.&lt;/p&gt;

&lt;h3 id=&quot;electronics-and-software&quot;&gt;Electronics and Software&lt;/h3&gt;
&lt;p&gt;To control the motors I want to use the quiet TMC2208 drivers, connected to an Arduino or ESP32 as main controller. First I thought about developing my own software, but then I found &lt;a href=&quot;https://github.com/hjd1964/OnStep&quot;&gt;OnStep&lt;/a&gt; which looks like exactly what I need.&lt;/p&gt;

&lt;p&gt;For usage in the field I need a battery power option. Planned is to reuse 4 cell LiPo battery packs that I already have, they provide 14.8 - 16.8 V. Anything from 12 to 36 V should be fine for the drivers and steppers though. In addition a step down to 3.3 V is required to supply the logic I/O.&lt;/p&gt;

&lt;p&gt;As interface I want to use a laptop or phone. Stellarium is a good open source planetarium, that also supports telescope control through builtin plugins.&lt;/p&gt;</content><author><name>Jake</name></author><category term="telescope" /><category term="astronomy" /><category term="hardware" /><category term="oss" /><summary type="html">This is the introduction to a multi part series about building a GoTo system for a Dobson style telescope mount. It should be computer controlled and be able to move to a selected target and also automatically track it. I want to use popular and inexpensive parts known from 3D printing for the mechanical setup, mostly leftovers from upgrade of my Prusa i3 printer.</summary></entry><entry><title type="html">NAS with Autosuspend, WakeOnLAN and SSHFS Automount</title><link href="https://blog.ja-ke.tech/2020/05/30/nas-automation.html" rel="alternate" type="text/html" title="NAS with Autosuspend, WakeOnLAN and SSHFS Automount" /><published>2020-05-30T00:00:00+00:00</published><updated>2020-05-30T00:00:00+00:00</updated><id>https://blog.ja-ke.tech/2020/05/30/nas-automation</id><content type="html" xml:base="https://blog.ja-ke.tech/2020/05/30/nas-automation.html">&lt;p&gt;This post describes the setup of some convenient features that I deployed for the NAS, to automatically save power, make it wake up and mount it on the client only when needed.&lt;/p&gt;

&lt;h3 id=&quot;autosuspend&quot;&gt;Autosuspend&lt;/h3&gt;
&lt;p&gt;Autosuspend is a nice tool to let a server suspend if it is idle (with configurable conditions).
For Fedora there is no official package available, so I installed it from source:&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/languitar/autosuspend.git&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;autosuspend
python3 setup.py &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/usr
&lt;span class=&quot;nb&quot;&gt;mv&lt;/span&gt; /usr/local/lib/python3.7/site-packages/autosuspend-3.0.0.dev0-py3.7.egg/etc/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; /etc
&lt;span class=&quot;nb&quot;&gt;mv&lt;/span&gt; /usr/local/lib/python3.7/site-packages/autosuspend-3.0.0.dev0-py3.7.egg/lib/systemd/system/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; /usr/local/lib/systemd/system/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Starting with usual git clone and python module install, unfortunately the setup does not install the config and service files, so I had to move them manually afterwards.&lt;/p&gt;

&lt;p&gt;Based on the default config this is resulting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/autosuspend.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[general]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;interval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;30&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;idle_time&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;900&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;suspend_cmd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/usr/bin/systemctl suspend&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;wakeup_cmd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sh -c 'echo 0 &amp;gt; /sys/class/rtc/rtc0/wakealarm &amp;amp;&amp;amp; echo {timestamp:.0f} &amp;gt; /sys/class/rtc/rtc0/wakealarm'&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;woke_up_file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/var/run/autosuspend-just-woke-up&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;lock_file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/var/lock/autosuspend.lock&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;lock_timeout&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;30&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# This will use the Users check with the custom name RemoteUsers.
# Custom names are necessary in case a check class is used multiple times.
# Custom names can also be used for clarification.
&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;[check.RemoteUsers]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Users&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;enabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;.*&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;terminal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;.*&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;[0-9].*&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Custom
&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;[check.SSH]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ActiveConnection&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;ports&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;22&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;enabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[check.NetworkBandwidth]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;enabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;interfaces&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;eno1&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;threshold_send&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;200&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;threshold_receive&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;400&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Three checks are enabled here, RemoteUser checks is someone is currently logged in over ssh, the ActiveConnection check on port 22 will reliably detect SSHFS connections. Also if there are other services causing some bandwidth usage it will detect it and not suspend with the last check.&lt;/p&gt;

&lt;p&gt;After starting/enabling with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemctl enable --now autosuspend.service&lt;/code&gt; it should look like this in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemctl status autosuspend.service&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;● autosuspend.service - A daemon to suspend your server in case of inactivity
   Loaded: loaded (/etc/systemd/system/autosuspend.service; disabled; vendor preset: disabled)
   Active: active (running) since Fri 2020-05-15 12:23:19 CEST; 8h ago
     Docs: https://autosuspend.readthedocs.io/en/latest/systemd_integration.html
 Main PID: 1348 (autosuspend)
    Tasks: 1 (limit: 4536)
   Memory: 15.4M
   CGroup: /system.slice/autosuspend.service
           └─1348 /usr/bin/python3 /usr/bin/autosuspend -l /etc/autosuspend-logging.conf daemon

May 15 20:50:50 nas-server autosuspend[1348]: 2020-05-15 20:50:50,740 - autosuspend.Processor - INFO - Starting new check iteration
May 15 20:50:50 nas-server autosuspend[1348]: 2020-05-15 20:50:50,752 - autosuspend.Processor - INFO - Check SSH matched. Reason: Ports [22] are connected
May 15 20:50:50 nas-server autosuspend[1348]: 2020-05-15 20:50:50,753 - autosuspend.Processor - INFO - System is active. Resetting state
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Here we see that it currently detects the SSH connection and does not suspend when I am logged in.&lt;/p&gt;

&lt;h3 id=&quot;wake-on-lan&quot;&gt;Wake On LAN&lt;/h3&gt;
&lt;p&gt;First make sure WOL is set up on the server and works from the client with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wol xx:xx:xx:xx:xx:xx&lt;/code&gt; using the MAC address of the server. This usually has to be enabled in the BIOS and might also need some software, read more &lt;a href=&quot;https://wiki.archlinux.org/index.php/Wake-on-LAN&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now we want the client to wake up the server automatically. I searched for some guidance and found &lt;a href=&quot;https://rolandtapken.de/blog/2017-02/how-wake-lan-remote-host-demand-using-systemds-sockets&quot;&gt;this blog post&lt;/a&gt;, he used a custom socket that triggers WOL on connection. It works, but I observed reduced performance with this approach: instead of &amp;gt;900 Mbit/s it delivered only about 300 Mbit/s, because all data must go through netcat and the local socket.&lt;/p&gt;

&lt;p&gt;So I came up with a solution where the data does not get intercepted and therefore does not impact the performance. For that an extra systemd service is running to check if the server is already up and if not sends the WOL packet. 
For example on ArchLinux custom services should be placed into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/systemd/system&lt;/code&gt;, I created there a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nas-online.service&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[Unit]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Continuously check if the host is up, otherwise try WOL on start&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;After&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;network.target network-online.target&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;PartOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mnt.mount&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[Service]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;HOST=nas-server&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;PORT=22&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;MAC=xx:xx:xx:xx:xx:xx&lt;/span&gt;

&lt;span class=&quot;py&quot;&gt;ExecStartPre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/bin/sh -c 'for n in `seq 1 6`; do nc -z ${HOST} ${PORT} &amp;amp;&amp;amp; break || wol $MAC &amp;amp;&amp;amp; sleep 10; done'&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;ExecStart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/bin/sh -c 'while :; do (nc -z -w 1s ${HOST} ${PORT} &amp;amp;&amp;amp; echo &quot;Host is up!&quot; &amp;amp;&amp;amp; sleep 30) || break; done'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In the [Unit] section it is defined as PartOf &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mnt.mount&lt;/code&gt; (which we will create in the next section), this makes systemd propagate  start/stop commands from the mount to this unit. The [Service] contains 2 small shell scripts. Pre start runs a maximum of 6 times a loop where it checks with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nc -z&lt;/code&gt; (netcat zero IO) if the target is up, otherwise it sends the WOL packet and waits 10 secs. Then the main script just check every 30 secs if the target is still online and logs it. Per default this runs indefinitely, but as described above it does get stopped if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mnt.mount&lt;/code&gt; gets inactive.&lt;/p&gt;

&lt;p&gt;Test it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemctl start nas-online.service&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemctl stop nas-online.service&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemctl status nas-online.service&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;* nas-online.service - Continuously check if host is up, otherwise try WOL on start
     Loaded: loaded (/etc/systemd/system/nas-online.service; static; vendor preset: disabled)
     Active: inactive (dead) since Sat 2020-05-30 16:06:16 CEST; 4min 3s ago
    Process: 3589 ExecStartPre=/bin/sh -c for n in `seq 1 6`; do nc -z ${HOST} ${PORT} &amp;amp;&amp;amp; break || wol $MAC &amp;amp;&amp;amp; sleep 10; done (code=exited, status=0/SUCCESS)
    Process: 3768 ExecStart=/bin/sh -c while :; do (nc -z -w 1s ${HOST} ${PORT} &amp;amp;&amp;amp; echo &quot;Host is up!&quot; &amp;amp;&amp;amp; sleep 30) || break; done (code=killed, signal=TERM)
   Main PID: 3768 (code=killed, signal=TERM)

May 30 16:05:01 pc-arch systemd[1]: Starting Continuously check if host is up, otherwise try WOL on start...
May 30 16:05:04 pc-arch sh[3617]: Waking up xx:xx:xx:xx:xx:xx...
May 30 16:05:14 pc-arch systemd[1]: Started Continuously check if host is up, otherwise try WOL on start.
May 30 16:05:14 pc-arch sh[3774]: Host is up!
May 30 16:05:44 pc-arch sh[4140]: Host is up!
May 30 16:06:15 pc-arch sh[4198]: Host is up!
May 30 16:06:16 pc-arch systemd[1]: Stopping Continuously check if host is up, otherwise try WOL on start...
May 30 16:06:16 pc-arch systemd[1]: nas-online.service: Succeeded.
May 30 16:06:16 pc-arch systemd[1]: Stopped Continuously check if host is up, otherwise try WOL on start.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;automount&quot;&gt;Automount&lt;/h3&gt;
&lt;p&gt;The Systemd automount feature can automatically mount it when an access to the mountpoint is detected and unmount if it was idle for a specified time.&lt;/p&gt;

&lt;p&gt;First it is important to login as root with SSH and accept the fingerprint for the server, otherwise it will not work because it is not in the known hosts list (like describe &lt;a href=&quot;https://wiki.archlinux.org/index.php/SSHFS&quot;&gt;here&lt;/a&gt;):&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt;
ssh &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 2222 jk@nas-server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Also it is of course not possible to enter a password, so make sure pubkey authentication is properly configured on the server.&lt;/p&gt;

&lt;p&gt;Then an mount entry in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/fstab&lt;/code&gt; can be added:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;jk@nas-server:/mnt   /mnt    fuse.sshfs  noauto,x-systemd.requires=nas-online.service,identityfile=/home/jk/.ssh/nas_ed25519,user,allow_other,default_permissions  0 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Here we need a few options:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;noauto:&lt;/em&gt; Disable direct mount on boot&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;x-systemd.requires=:&lt;/em&gt; Require online check/WOL to start&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;identityfile=:&lt;/em&gt; Specify the SSH keys, it will run as root and does not read the user ssh config&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;user:&lt;/em&gt; Allow user to mount/unmount&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;allow_other:&lt;/em&gt; Allow other users (than root) to access&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;default_permissions:&lt;/em&gt; Use permissions from the server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemd-fstab-generator&lt;/code&gt; will automatically produce an unit for the mountpoint, in this case &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mnt.mount&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now we need another unit: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mnt.automount&lt;/code&gt; with the simple content:&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[Automount]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/mnt&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;TimeoutIdleSec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;60&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where&lt;/code&gt; just specifies the mountpoint, also the filename must match the mount unit filename, which is based on the path. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeoutIdleSec&lt;/code&gt; enables the automatic unmount, if it was idle for at least 60 secs in this case, you might want to set this higher.&lt;/p&gt;

&lt;p&gt;Note: With the &lt;em&gt;x-systemd.automount&lt;/em&gt; fstab option the generator could also create the automount.service, but then the requires is applied to the automount and that would wake up the NAS always at the boot. We want to have it on the mount, because that wakes up the NAS only if it is actually mounted/used.&lt;/p&gt;

&lt;p&gt;Finally start and enable it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemctl enable --now mnt.automount&lt;/code&gt;. On any access to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/mnt&lt;/code&gt; the automount will trigger the mount.&lt;/p&gt;

&lt;p&gt;You can see if it triggered with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemctl status mnt.automount&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;● mnt.automount
     Loaded: loaded (/etc/fstab; generated)
     Active: active (running) since Fri 2020-05-15 19:49:22 CEST; 28min ago
   Triggers: ● mnt.mount
      Where: /mnt
       Docs: man:fstab(5)
             man:systemd-fstab-generator(8)

Mai 15 19:49:22 pc-arch systemd[1]: Set up automount mnt.automount.
Mai 15 19:49:27 pc-arch systemd[1]: mnt.automount: Got automount request for /mnt, triggered by 55768 (dolphin)
Mai 15 20:02:14 pc-arch systemd[1]: mnt.automount: Got automount request for /mnt, triggered by 59368 (nvim)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemctl status mnt.mount&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;● mnt.mount - /mnt
     Loaded: loaded (/etc/fstab; generated)
     Active: active (mounted) since Fri 2020-05-15 20:30:41 CEST; 9min ago
TriggeredBy: ● mnt.automount
      Where: /mnt
       What: jk@localhost:/mnt
       Docs: man:fstab(5)
             man:systemd-fstab-generator(8)
      Tasks: 7 (limit: 19042)
     Memory: 2.3M
     CGroup: /system.slice/mnt.mount
             ├─61440 ssh -x -a -oClearAllForwardings=yes -oport=2222 -oidentityfile=/home/jk/.ssh/nas_ed25519 -2 jk@localhost -s sftp
             └─61448 /usr/bin/mount.fuse.sshfs jk@localhost:/mnt /mnt -o rw,noexec,nosuid,nodev,port=2222,identityfile=/home/jk/.ssh/nas_ed25519,allow_other,default_permissions,user

Mai 15 20:30:40 pc-arch systemd[1]: Mounting /mnt...
Mai 15 20:30:41 pc-arch systemd[1]: Mounted /mnt.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I hope it was helpful, let me know if it worked in the comments!&lt;/p&gt;</content><author><name>Jake</name></author><category term="linux" /><category term="storage" /><category term="nas" /><category term="software" /><category term="scripting" /><summary type="html">This post describes the setup of some convenient features that I deployed for the NAS, to automatically save power, make it wake up and mount it on the client only when needed.</summary></entry></feed>