<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" >

<channel><title><![CDATA[Alex Spataru - Blog]]></title><link><![CDATA[https://www.aspatru.com/blog]]></link><description><![CDATA[Blog]]></description><pubDate>Thu, 01 Jan 2026 01:58:21 -0800</pubDate><generator>Weebly</generator><item><title><![CDATA[Using Qt Widgets in your QML/QtQuick applications]]></title><link><![CDATA[https://www.aspatru.com/blog/using-qt-widgets-in-your-qmlqtquick-applications]]></link><comments><![CDATA[https://www.aspatru.com/blog/using-qt-widgets-in-your-qmlqtquick-applications#comments]]></comments><pubDate>Sat, 20 Mar 2021 22:45:12 GMT</pubDate><category><![CDATA[Hacks]]></category><category><![CDATA[QML]]></category><category><![CDATA[Qt]]></category><guid isPermaLink="false">https://www.aspatru.com/blog/using-qt-widgets-in-your-qmlqtquick-applications</guid><description><![CDATA[There is no denying that QtQuick has a lot of handy features and allows programmers to build nice GUI applications with ease. However, sometimes we would like to integrate the functionality of "legacy" Qt Widgets with our QML applications.For example, QtQuick does not have a direct replacement of the QPlainTextEdit&nbsp;widget (which is very useful if you need to log large amounts of data or if you want to build a custom code editor). Other examples of such widgets are the QTableWidget&nbsp;and  [...] ]]></description><content:encoded><![CDATA[<div class="paragraph" style="text-align:left;">There is no denying that QtQuick has a lot of handy features and allows programmers to build nice GUI applications with ease. However, sometimes we would like to integrate the functionality of "legacy" Qt Widgets with our QML applications.<br><br>For example, QtQuick does not have a direct replacement of the <a href="https://doc.qt.io/qt-5/qplaintextedit.html" target="_blank">QPlainTextEdit</a>&nbsp;widget (which is very useful if you need to log large amounts of data or if you want to build a custom code editor). Other examples of such widgets are the <a href="https://doc.qt.io/qt-5/qtablewidget.html" target="_blank">QTableWidget</a>&nbsp;and third-party widgets (such as <a href="https://qwt.sourceforge.io" target="_blank">Qwt</a>&nbsp;or <a href="https://riverbankcomputing.com/software/qscintilla" target="_blank">QScintilla</a>).<br><br>I bumped into this issue while developing an appropriate way to create a QML-friendly serial output console for <a href="https://www.alex-spataru.com/serial-studio.html" target="_blank">Serial Studio</a>. Using the standard QML <a href="https://doc.qt.io/qt-5/qml-qtquick-controls2-textarea.html" target="_blank">TextArea</a>&nbsp;resulted too slow for displaying high-frequency data from a serial port device.<br><br>To fix this, I decided to use the <a href="https://doc.qt.io/qt-5/qplaintextedit.html" target="_blank">QPlainTextEdit</a> widget for displaying incoming serial data and use <a href="https://doc.qt.io/qt-5/qquickpainteditem.html" target="_blank">QQuickPaintedItem</a> to render the widget in the QML interface.&nbsp;<br><br>We'll begin our unorthodox programming ritual with the following header code:</div><div><div id="926494825202731669" align="left" style="width: 100%; overflow-y: hidden;" class="wcustomhtml"><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%"><span style="color: #557799">#ifndef UI_QML_PLAINTEXTEDIT_H</span><span style="color: #557799">#define UI_QML_PLAINTEXTEDIT_H</span><span style="color: #557799">#include &lt;QPainter&gt;</span><span style="color: #557799">#include &lt;QPlainTextEdit&gt;</span><span style="color: #557799">#include &lt;QQuickPaintedItem&gt;</span><span style="color: #008800; font-weight: bold">class</span> <span style="color: #BB0066; font-weight: bold">QmlPlainTextEdit</span> <span style="color: #333333">:</span> <span style="color: #008800; font-weight: bold">public</span> QQuickPaintedItem{<span style="color: #997700; font-weight: bold">public:</span>    QmlPlainTextEdit(QQuickItem <span style="color: #333333">*</span>parent <span style="color: #333333">=</span> <span style="color: #0000DD; font-weight: bold">0</span>);    <span style="color: #333333">~</span>QmlPlainTextEdit();    <span style="color: #008800; font-weight: bold">virtual</span> <span style="color: #333399; font-weight: bold">bool</span> event(QEvent <span style="color: #333333">*</span>event) override;    <span style="color: #008800; font-weight: bold">virtual</span> <span style="color: #333399; font-weight: bold">void</span> paint(QPainter <span style="color: #333333">*</span>painter) override;    <span style="color: #008800; font-weight: bold">virtual</span> <span style="color: #333399; font-weight: bold">bool</span> eventFilter(QObject <span style="color: #333333">*</span>watched, QEvent <span style="color: #333333">*</span>event) override;    QPlainTextEdit <span style="color: #333333">*</span>textEdit() <span style="color: #008800; font-weight: bold">const</span>;<span style="color: #008800; font-weight: bold">private</span> slots<span style="color: #333333">:</span>    <span style="color: #333399; font-weight: bold">void</span> updateWidgetSize();<span style="color: #997700; font-weight: bold">protected:</span>    <span style="color: #333399; font-weight: bold">void</span> <span style="color: #0066BB; font-weight: bold">processMouseEvents</span>(QMouseEvent <span style="color: #333333">*</span>event);    <span style="color: #333399; font-weight: bold">void</span> <span style="color: #0066BB; font-weight: bold">processWheelEvents</span>(QWheelEvent <span style="color: #333333">*</span>event);<span style="color: #997700; font-weight: bold">private:</span>    QPlainTextEdit <span style="color: #333333">*</span>m_textEdit;};<span style="color: #557799">#endif</span></pre></div></div></div><div class="paragraph" style="text-align:left;"><br>&#8203;And here is the implementation code for this class:</div><div><div id="345846053957109136" align="left" style="width: 100%; overflow-y: hidden;" class="wcustomhtml"><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%"><span style="color: #557799">#include "QmlPlainTextEdit.h"</span><span style="color: #888888">/**</span><span style="color: #888888"> * Constructor function</span><span style="color: #888888"> */</span>QmlPlainTextEdit<span style="color: #333333">::</span>QmlPlainTextEdit(QQuickItem <span style="color: #333333">*</span>parent)    <span style="color: #333333">:</span> QQuickPaintedItem(parent)    , m_textEdit(<span style="color: #008800; font-weight: bold">new</span> QPlainTextEdit){    <span style="color: #888888">// Set item flags</span>    setFlag(ItemHasContents, <span style="color: #007020">true</span>);    setFlag(ItemAcceptsInputMethod, <span style="color: #007020">true</span>);    setFlag(ItemIsFocusScope, <span style="color: #007020">true</span>);    setAcceptedMouseButtons(Qt<span style="color: #333333">::</span>AllButtons);    <span style="color: #888888">// Install event filter for widget</span>    textEdit()<span style="color: #333333">-&gt;</span>installEventFilter(<span style="color: #008800; font-weight: bold">this</span>);    <span style="color: #888888">// Set the QML item's implicit size</span>    <span style="color: #008800; font-weight: bold">auto</span> hint <span style="color: #333333">=</span> textEdit()<span style="color: #333333">-&gt;</span>sizeHint();    setImplicitSize(hint.width(), hint.height());    <span style="color: #888888">// Setup default options</span>    textEdit()<span style="color: #333333">-&gt;</span>setVerticalScrollBarPolicy(Qt<span style="color: #333333">::</span>ScrollBarAlwaysOff);    textEdit()<span style="color: #333333">-&gt;</span>setSizeAdjustPolicy(QPlainTextEdit<span style="color: #333333">::</span>AdjustToContents);    <span style="color: #888888">// Resize QPlainTextEdit to fit QML item</span>    connect(<span style="color: #008800; font-weight: bold">this</span>, <span style="color: #333333">&amp;</span>QQuickPaintedItem<span style="color: #333333">::</span>widthChanged, <span style="color: #008800; font-weight: bold">this</span>,            <span style="color: #333333">&amp;</span>QmlPlainTextEdit<span style="color: #333333">::</span>updateWidgetSize);    connect(<span style="color: #008800; font-weight: bold">this</span>, <span style="color: #333333">&amp;</span>QQuickPaintedItem<span style="color: #333333">::</span>heightChanged, <span style="color: #008800; font-weight: bold">this</span>,            <span style="color: #333333">&amp;</span>QmlPlainTextEdit<span style="color: #333333">::</span>updateWidgetSize);}<span style="color: #888888">/**</span><span style="color: #888888"> * Destructor function</span><span style="color: #888888"> */</span>QmlPlainTextEdit<span style="color: #333333">::~</span>QmlPlainTextEdit(){    m_textEdit<span style="color: #333333">-&gt;</span>deleteLater();}<span style="color: #888888">/**</span><span style="color: #888888"> * Handle application events manually</span><span style="color: #888888"> */</span><span style="color: #333399; font-weight: bold">bool</span> QmlPlainTextEdit<span style="color: #333333">::</span>event(QEvent <span style="color: #333333">*</span>event){    <span style="color: #008800; font-weight: bold">switch</span> (event<span style="color: #333333">-&gt;</span>type())    {        <span style="color: #008800; font-weight: bold">case</span> QEvent:<span style="color: #333333">:</span>FocusIn<span style="color: #333333">:</span>            forceActiveFocus();            <span style="color: #008800; font-weight: bold">return</span> QQuickPaintedItem<span style="color: #333333">::</span>event(event);            <span style="color: #008800; font-weight: bold">break</span>;        <span style="color: #008800; font-weight: bold">case</span> QEvent:<span style="color: #333333">:</span>Wheel<span style="color: #333333">:</span>            processWheelEvents(<span style="color: #008800; font-weight: bold">static_cast</span><span style="color: #333333">&lt;</span>QWheelEvent <span style="color: #333333">*&gt;</span>(event));            <span style="color: #008800; font-weight: bold">return</span> <span style="color: #007020">true</span>;            <span style="color: #008800; font-weight: bold">break</span>;        <span style="color: #008800; font-weight: bold">case</span> QEvent:<span style="color: #333333">:</span>MouseButtonPress<span style="color: #333333">:</span>        <span style="color: #008800; font-weight: bold">case</span> QEvent:<span style="color: #333333">:</span>MouseButtonRelease<span style="color: #333333">:</span>        <span style="color: #008800; font-weight: bold">case</span> QEvent:<span style="color: #333333">:</span>MouseButtonDblClick<span style="color: #333333">:</span>        <span style="color: #008800; font-weight: bold">case</span> QEvent:<span style="color: #333333">:</span>MouseMove<span style="color: #333333">:</span>            processMouseEvents(<span style="color: #008800; font-weight: bold">static_cast</span><span style="color: #333333">&lt;</span>QMouseEvent <span style="color: #333333">*&gt;</span>(event));            <span style="color: #008800; font-weight: bold">return</span> <span style="color: #007020">true</span>;            <span style="color: #008800; font-weight: bold">break</span>;        <span style="color: #997700; font-weight: bold">default:</span>            <span style="color: #008800; font-weight: bold">break</span>;    }    <span style="color: #008800; font-weight: bold">return</span> QApplication<span style="color: #333333">::</span>sendEvent(textEdit(), event);}<span style="color: #888888">/**</span><span style="color: #888888"> * Render the text edit on the given @a painter</span><span style="color: #888888"> */</span><span style="color: #333399; font-weight: bold">void</span> QmlPlainTextEdit<span style="color: #333333">::</span>paint(QPainter <span style="color: #333333">*</span>painter){    <span style="color: #008800; font-weight: bold">if</span> (painter)        textEdit()<span style="color: #333333">-&gt;</span>render(painter);}<span style="color: #888888">/**</span><span style="color: #888888"> * Custom event filter to manage redraw requests</span><span style="color: #888888"> */</span><span style="color: #333399; font-weight: bold">bool</span> QmlPlainTextEdit<span style="color: #333333">::</span>eventFilter(QObject <span style="color: #333333">*</span>watched, QEvent <span style="color: #333333">*</span>event){    Q_ASSERT(m_textEdit);    <span style="color: #008800; font-weight: bold">if</span> (watched <span style="color: #333333">==</span> textEdit())    {        <span style="color: #008800; font-weight: bold">switch</span> (event<span style="color: #333333">-&gt;</span>type())        {            <span style="color: #008800; font-weight: bold">case</span> QEvent:<span style="color: #333333">:</span>Paint<span style="color: #333333">:</span>            <span style="color: #008800; font-weight: bold">case</span> QEvent:<span style="color: #333333">:</span>UpdateRequest<span style="color: #333333">:</span>                update();                <span style="color: #008800; font-weight: bold">break</span>;            <span style="color: #997700; font-weight: bold">default:</span>                <span style="color: #008800; font-weight: bold">break</span>;        }    }    <span style="color: #008800; font-weight: bold">return</span> QQuickPaintedItem<span style="color: #333333">::</span>eventFilter(watched, event);}<span style="color: #888888">/**</span><span style="color: #888888"> * Resizes the text editor widget to fit inside the QML item.</span><span style="color: #888888"> */</span><span style="color: #333399; font-weight: bold">void</span> QmlPlainTextEdit<span style="color: #333333">::</span>updateWidgetSize(){    textEdit()<span style="color: #333333">-&gt;</span>setFixedSize(width(), height());    update();}<span style="color: #888888">/**</span><span style="color: #888888"> * Hack: call the appropriate protected mouse event handler function </span><span style="color: #888888"> *       of the QPlainTextEdit item depending on event type.</span><span style="color: #888888"> */</span><span style="color: #333399; font-weight: bold">void</span> QmlPlainTextEdit<span style="color: #333333">::</span>processMouseEvents(QMouseEvent <span style="color: #333333">*</span>event){    <span style="color: #888888">// Subclass QPlainTextEdit so that we can call protected functions</span>    <span style="color: #008800; font-weight: bold">class</span> <span style="color: #BB0066; font-weight: bold">Hack</span> <span style="color: #333333">:</span> <span style="color: #008800; font-weight: bold">public</span> QPlainTextEdit    {    <span style="color: #997700; font-weight: bold">public:</span>        <span style="color: #008800; font-weight: bold">using</span> QPlainTextEdit<span style="color: #333333">::</span>mouseDoubleClickEvent;        <span style="color: #008800; font-weight: bold">using</span> QPlainTextEdit<span style="color: #333333">::</span>mouseMoveEvent;        <span style="color: #008800; font-weight: bold">using</span> QPlainTextEdit<span style="color: #333333">::</span>mousePressEvent;        <span style="color: #008800; font-weight: bold">using</span> QPlainTextEdit<span style="color: #333333">::</span>mouseReleaseEvent;    };    <span style="color: #888888">// Call appropiate function</span>    <span style="color: #008800; font-weight: bold">auto</span> hack <span style="color: #333333">=</span> <span style="color: #008800; font-weight: bold">static_cast</span><span style="color: #333333">&lt;</span>Hack <span style="color: #333333">*&gt;</span>(textEdit());    <span style="color: #008800; font-weight: bold">switch</span> (event<span style="color: #333333">-&gt;</span>type())    {        <span style="color: #008800; font-weight: bold">case</span> QEvent:<span style="color: #333333">:</span>MouseButtonPress<span style="color: #333333">:</span>            hack<span style="color: #333333">-&gt;</span>mousePressEvent(event);            <span style="color: #008800; font-weight: bold">break</span>;        <span style="color: #008800; font-weight: bold">case</span> QEvent:<span style="color: #333333">:</span>MouseMove<span style="color: #333333">:</span>            hack<span style="color: #333333">-&gt;</span>mouseMoveEvent(event);            <span style="color: #008800; font-weight: bold">break</span>;        <span style="color: #008800; font-weight: bold">case</span> QEvent:<span style="color: #333333">:</span>MouseButtonRelease<span style="color: #333333">:</span>            hack<span style="color: #333333">-&gt;</span>mouseReleaseEvent(event);            <span style="color: #008800; font-weight: bold">break</span>;        <span style="color: #008800; font-weight: bold">case</span> QEvent:<span style="color: #333333">:</span>MouseButtonDblClick<span style="color: #333333">:</span>            hack<span style="color: #333333">-&gt;</span>mouseDoubleClickEvent(event);            <span style="color: #008800; font-weight: bold">break</span>;        <span style="color: #997700; font-weight: bold">default:</span>            <span style="color: #008800; font-weight: bold">break</span>;    }}<span style="color: #888888">/**</span><span style="color: #888888"> * Hack: call the protected wheel event handler function of the</span><span style="color: #888888"> *       QPlainTextEdit item</span><span style="color: #888888"> */</span><span style="color: #333399; font-weight: bold">void</span> QmlPlainTextEdit<span style="color: #333333">::</span>processWheelEvents(QWheelEvent <span style="color: #333333">*</span>event){    <span style="color: #888888">// Subclass QPlainTextEdit so that we can call protected functions</span>    <span style="color: #008800; font-weight: bold">class</span> <span style="color: #BB0066; font-weight: bold">Hack</span> <span style="color: #333333">:</span> <span style="color: #008800; font-weight: bold">public</span> QPlainTextEdit    {    <span style="color: #997700; font-weight: bold">public:</span>        <span style="color: #008800; font-weight: bold">using</span> QPlainTextEdit<span style="color: #333333">::</span>wheelEvent;    };    <span style="color: #888888">// Call wheel event handler</span>    <span style="color: #008800; font-weight: bold">static_cast</span><span style="color: #333333">&lt;</span>Hack <span style="color: #333333">*&gt;</span>(textEdit())<span style="color: #333333">-&gt;</span>wheelEvent(event);}</pre></div></div></div><div class="paragraph" style="text-align:left;"><br><strong>Note:&nbsp;</strong>in the <em>processMouseEvents()</em> and <em>processWheelEvents()</em> functions, we are writing potentially illegal C++ code because we are indirectly accessing protected functions of the <em>QPlainTextEdit</em> class. Unfortunately, this is the only way that I managed to get the widget to accept mouse/wheel events. If you have a better solution, you are welcome to share it :)<br><br>To use this class from the QML interface, we need to add the following code in the <em>main()&nbsp;</em>function (or before initializing the QML engine):</div><div><div id="967456276265169940" align="left" style="width: 100%; overflow-y: hidden;" class="wcustomhtml"><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%">qmlRegisterType<span style="color: #333333">&lt;QmlPlainTextEdit<span style="color: #333333">&gt;</span>(<span style="background-color: #fff0f0">"QtHacks"</span>, <span style="color: #0000DD; font-weight: bold">1</span>, <span style="color: #0000DD; font-weight: bold">0</span>, <span style="background-color: #fff0f0">"QmlPlainTextEdit"</span>);</span></pre></div></div></div><div class="paragraph" style="text-align:left;"><br>Finally, here is an example of using&nbsp;<em>QmlPlainTextEdit</em>&nbsp;from QML:</div><div><div id="441617323560833535" align="left" style="width: 100%; overflow-y: hidden;" class="wcustomhtml"><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%"><span style="color: #008800; font-weight: bold">import</span> QtQuick <span style="color: #6600EE; font-weight: bold">2.12</span><span style="color: #008800; font-weight: bold">import</span> QtQuick.Window <span style="color: #6600EE; font-weight: bold">2.12</span><span style="color: #008800; font-weight: bold">import</span> QtHacks <span style="color: #6600EE; font-weight: bold">1.0</span>ApplicationWindow {    <span style="color: #008800; font-weight: bold">visible:</span> <span style="color: #008800; font-weight: bold">true</span>    <span style="color: #008800; font-weight: bold">width:</span> <span style="color: #0000DD; font-weight: bold">640</span>    <span style="color: #008800; font-weight: bold">height:</span> <span style="color: #0000DD; font-weight: bold">480</span>    <span style="color: #008800; font-weight: bold">title:</span> qsTr(<span style="background-color: #fff0f0">"QML PlainTextEdit Example"</span>)    QmlPlainTextEdit {        <span style="color: #008800; font-weight: bold">anchors.fill:</span> parent        <span style="color: #008800; font-weight: bold">anchors.margins:</span> <span style="color: #0000DD; font-weight: bold">8</span>    }}</pre></div></div></div><div class="paragraph" style="text-align:left;"><br>You can also add functions to read/write widget properties from QML using the <a href="https://doc.qt.io/qt-5/qtqml-cppintegration-exposecppattributes.html" target="_blank">Q_PROPERTY</a> macro. You can check the complete <em>QmlPlainTextEdit&nbsp;</em>code in the following links:<ul><li><a href="https://github.com/Serial-Studio/Serial-Studio/blob/master/assets/qml/Windows/Console.qml" target="_blank">Console.qml</a></li><li><a href="https://github.com/Serial-Studio/Serial-Studio/blob/master/src/UI/QmlPlainTextEdit.h" target="_blank">QmlPlainTextEdit.h</a></li><li><a href="https://github.com/Serial-Studio/Serial-Studio/blob/master/src/UI/QmlPlainTextEdit.cpp" target="_blank">QmlPlainTextEdit.cpp</a></li></ul><br>Please take into account that these files are specific to Serial Studio, so you will need to modify them to use them in a "portable" manner in your projects.<br><br>Finally, you can follow this approach with any <em>QWidget</em>&nbsp;based object that you need to use from your QML interface. I have used this approach with a&nbsp;<a href="https://doc.qt.io/qt-5/qtablewidget.html" target="_blank">QTableWidget</a>&nbsp;for a proprietary project without any major issues.</div>]]></content:encoded></item><item><title><![CDATA[Introducing Serial Studio, a dashboard software for serial port projects]]></title><link><![CDATA[https://www.aspatru.com/blog/introducing-serial-studio]]></link><comments><![CDATA[https://www.aspatru.com/blog/introducing-serial-studio#comments]]></comments><pubDate>Tue, 29 Dec 2020 17:45:43 GMT</pubDate><category><![CDATA[FOSS]]></category><category><![CDATA[Microcontrollers]]></category><category><![CDATA[Qt]]></category><guid isPermaLink="false">https://www.aspatru.com/blog/introducing-serial-studio</guid><description><![CDATA[Did you ever have the need to display data from a microcontroller on a dashboard, and spent more time developing (and fixing) your dashboard software, than actually working on your MCU project?Well, I did, multiple times. Let me put you in context,&nbsp;I participate in several CanSat competition programs through KA'AN SAT, a representative team at my university. A CanSat is “a simulation of a real satellite, integrated within the volume and shape of a soft drink can” (European Space Agency, [...] ]]></description><content:encoded><![CDATA[<div class="paragraph" style="text-align:left;">Did you ever have the need to display data from a microcontroller on a dashboard, and spent more time developing (and fixing) your dashboard software, than actually working on your MCU project?<br><br>Well, I did, multiple times. Let me put you in context,&nbsp;I participate in several CanSat competition programs through <a href="https://www.unaq.edu.mx/soyunaq/equipos-representativos/kaan-sat/" target="_blank">KA'AN SAT</a>, a representative team at my university. A CanSat is &ldquo;a simulation of a real satellite, integrated within the volume and shape of a soft drink can&rdquo; (European Space Agency, <a href="https://www.esa.int/Education/CanSat/What_is_a_CanSat" target="_blank">more info</a>). One of the main tasks in these competitions is to develop software for the ground station. The ground station software (GSS) receives telemetry from the CanSat in real time through a serial device (generally a&nbsp;<a href="http://www.digi.com/xbee/" target="_blank">XBee</a>), displays it and exports it to a CSV/Excel file for post-mission analysis.&nbsp;<br><br>From the start, we developed the GSS with <a href="https://qt.io/" target="_blank">Qt</a> to support multiple operating systems (in case that one of our computers experienced problems during the competition) and because Qt/QML is very convenient for developing eye-catching user interfaces.<br><br>Here is a screenshot of the <a href="https://github.com/Kaan-Sat/CanSat-GSS-2019" target="_blank">2019 ground station software</a>, and a photo of the GSS running &amp; displaying telemetry during the <a href="http://cansat.cucei.udg.mx" target="_blank">CUCEI CanSat competition</a>:</div><div><div style="height:20px;overflow:hidden"></div><div id='248381623303662702-slideshow'></div><div style="height:20px;overflow:hidden"></div></div><div class="paragraph" style="text-align:left;">If you are interested, the source code for the 2019 GSS is available <a href="https://github.com/Kaan-Sat/CanSat-GSS-2019" target="_blank">here</a>.&nbsp;The software worked quite well for all its intents &amp; purposes (we got the first place after all).<br><br>The problems came half a year later, when I found myself working on multiple projects that required some kind of data&nbsp;acquisition with serial devices.&nbsp;For example, some members of <a href="https://www.unaq.edu.mx/soyunaq/equipos-representativos/roch-team-unaq/" target="_blank">ROCH</a> (another representative team in our university, which participates in the <a href="https://www.nasa.gov/stem/roverchallenge/home/index.html" target="_blank">NASA Human Rover Exploration Challenge</a>) wanted to integrate our GSS with their rover as a side-project during quarantine.<br><br>The result was disastrous; I got a call late at night and we ended up pulling an all-nighter to come up with a way to adapt the GSS with the telemetry that they were receiving. Finally, the software worked, but the UI integration was horrible &amp; data export was not really functional&nbsp;(see the screenshot below to get an idea):</div><div><div class="wsite-image wsite-image-border-none" style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"><a><img src="https://www.aspatru.com/uploads/5/8/7/5/58759615/img-20200410-wa0004_orig.jpg" alt="Picture" style="width:auto;max-width:100%"></a><div style="display:block;font-size:90%"></div></div></div><div class="paragraph" style="text-align:left;">Both projects (the CanSat and the rover) had a similar telemetry format (sensor readings &amp; OBC status data separated with commas). However, the information itself had a different order.<br>&nbsp;<br>After that experience, I decided that I had enough of writing separate dashboard software for every project that I got involved with. I needed to come up with a way to have the same dashboard/GSS software to work with all projects, without the need of modifying the GSS code at the last minute.<br>&nbsp;<br>The initial solution that I came up with was to create a JSON-based communication protocol between the GSS and the microcontroller, and thus <em><a href="https://github.com/Serial-Studio/Serial-Studio" target="_blank">Serial Studio</a></em> was born (its initial name was <em><a href="https://github.com/alex-spataru/siglab" target="_blank">SigLAB</a>,</em> but I changed my mind later).&nbsp;<br><br>&#8203;Basically, the microcontroller sends the following information through the serial port:<ul><li>Project title.</li><li>Current sensor readings &amp; OBC status.</li><li>What each reading meant, its measurement units and what should the GSS do with that reading (e.g. create a real-time graph of the measured atmospheric pressure).</li></ul><br>All this information can be easily represented in a JSON document, for example:</div><div><div id="989831508113540251" align="left" style="width: 100%; overflow-y: hidden;" class="wcustomhtml"><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%">{   <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"KAANSATQRO"</span>,   <span style="color: #007700">"g"</span>:[      {         <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Mission Status"</span>,         <span style="color: #007700">"d"</span>:[            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Runtime"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%value%"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"ms"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Packet count"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%value%"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Battery voltage"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%value%"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"V"</span>            }         ]      },      {         <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Sensor Readings"</span>,         <span style="color: #007700">"d"</span>:[            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Temperature"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%value%"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"&deg;C"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Altitude"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%value%"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"m"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Pressure"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%value%"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"KPa"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"External Temperature"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%value%"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"&deg;C"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Humidity"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%9"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"%value%"</span>            }         ]      }   ]}</pre></div></div></div><div class="paragraph" style="text-align:left;"><span>&#8203;<br>&#8203;As you can see, we have the following structure:</span><ul><li>Project title</li><li>Array of data groups<ul><li>For each group:<ul><li>Group title</li><li>Array of datasets<ul><li>For each dataset:<ul><li>Title</li><li>Value</li><li>Units</li><li>Graph request (or not)</li></ul></li></ul></li></ul></li></ul></li></ul><br>A group consists of values that are closely related to each other, for example:<ul><li>OBC status (first group in our example)</li><li>Sensor readings (second group in our example)</li><li>Accelerometer readings (X, Y, Z)</li><li>GPS readings</li><li>Etc.</li></ul><br>&#8203;On the other hand, datasets represent what each individual value means, and what we should do with it.<br><br>On <em><a href="https://github.com/Serial-Studio/Serial-Studio" target="_blank">Serial Studio</a></em>, this information is displayed in the following manner:</div><div><div class="wsite-image wsite-image-border-none" style="padding-top:10px;padding-bottom:10px;margin-left:0px;margin-right:0px;text-align:center"><a href='https://www.aspatru.com/uploads/5/8/7/5/58759615/data-visualization_orig.png' rel='lightbox' onclick='if (!lightboxLoaded) return false'><img src="https://www.aspatru.com/uploads/5/8/7/5/58759615/data-visualization_orig.png" alt="Picture" style="width:auto;max-width:100%"></a><div style="display:block;font-size:90%"></div></div></div><div class="paragraph" style="text-align:left;">As you can probably deduce, each &ldquo;window&rdquo; corresponds to a group in our JSON document (I hid the graphs in the screenshot to avoid confusion).&nbsp;<br><br>This approach works beautifully for small projects. However, for more complex projects, creating and sending a large JSON document through serial (or through radio signals, and then through a serial port) becomes quite problematic. The solution? Load the same JSON document from your computer, instruct the microcontroller to send <em>ONLY</em> the sensor/data readings &amp; let <em><a href="https://github.com/Serial-Studio/Serial-Studio" target="_blank">Serial Studio</a></em> figure out the rest by using the indices of each received value in a comma-separated data frame.<br><br>Doing so lets you have the best from both worlds:<ul><li>You <span>don&rsquo;t</span>&nbsp;need to write specific dashboard/GSS&nbsp;software for each project (and you get all the nice features that we described earlier).</li><li>And you don&rsquo;t need to create&nbsp;&amp; transmit a large JSON document from your microcontroller (heck, the person working on the microcontroller software doesn't even&nbsp;need to know what the f*** is JSON,&nbsp;or how it works).</li></ul>&#8203;<br>A JSON &ldquo;map&rdquo; document looks like this:</div><div><div id="359982128564593116" align="left" style="width: 100%; overflow-y: hidden;" class="wcustomhtml"><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%">{   <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"%1"</span>,   <span style="color: #007700">"g"</span>:[      {         <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Mission Status"</span>,         <span style="color: #007700">"d"</span>:[            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Runtime"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%2"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"ms"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Packet count"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%3"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Battery voltage"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%4"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"V"</span>,               <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"bar"</span>,               <span style="color: #007700">"min"</span>:<span style="color: #6600EE; font-weight: bold">3.6</span>,               <span style="color: #007700">"max"</span>:<span style="color: #6600EE; font-weight: bold">4.3</span>            }         ]      },      {         <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Sensor Readings"</span>,         <span style="color: #007700">"d"</span>:[            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Temperature"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%5"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"&deg;C"</span>,               <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"bar"</span>,               <span style="color: #007700">"min"</span>:<span style="color: #0000DD; font-weight: bold">0</span>,               <span style="color: #007700">"max"</span>:<span style="color: #0000DD; font-weight: bold">80</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Altitude"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%6"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"m"</span>,               <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"bar"</span>,               <span style="color: #007700">"min"</span>:<span style="color: #0000DD; font-weight: bold">0</span>,               <span style="color: #007700">"max"</span>:<span style="color: #0000DD; font-weight: bold">3000</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Pressure"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%7"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"KPa"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"bar"</span>,               <span style="color: #007700">"min"</span>:<span style="color: #0000DD; font-weight: bold">54</span>,               <span style="color: #007700">"max"</span>:<span style="color: #0000DD; font-weight: bold">102</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"External Temperature"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%8"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"&deg;C"</span>,               <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"bar"</span>,               <span style="color: #007700">"min"</span>:<span style="color: #0000DD; font-weight: bold">0</span>,               <span style="color: #007700">"max"</span>:<span style="color: #0000DD; font-weight: bold">80</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Humidity"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%9"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"%"</span>,               <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"bar"</span>,               <span style="color: #007700">"min"</span>:<span style="color: #0000DD; font-weight: bold">0</span>,               <span style="color: #007700">"max"</span>:<span style="color: #0000DD; font-weight: bold">100</span>            }         ]      },      {         <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"GPS"</span>,         <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"map"</span>,         <span style="color: #007700">"d"</span>:[            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"GPS Time"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%10"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Longitude"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%11"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"&deg;E"</span>,               <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"lon"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Latitude"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%12"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"&deg;N"</span>,               <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"lat"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Altitude"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%13"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"m"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"No. Sats"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%14"</span>            }         ]      },      {         <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Accelerometer"</span>,         <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"accelerometer"</span>,         <span style="color: #007700">"d"</span>:[            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"X"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%15"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"m/s^2"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"x"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Y"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%16"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"m/s^2"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"y"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Z"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%17"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"m/s^2"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"z"</span>            }         ]      },      {         <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Gyroscope"</span>,         <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"gyro"</span>,         <span style="color: #007700">"d"</span>:[            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"X"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%18"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"&deg;"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"yaw"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Y"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%19"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"&deg;"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"roll"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Z"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%20"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"&deg;"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"pitch"</span>            }         ]      }   ]}</pre></div></div></div><div class="paragraph" style="text-align:left;"><br>&#8203;As you can guess, <em><a href="https://github.com/Serial-Studio/Serial-Studio" target="_blank">Serial Studio</a></em> will replace the <strong>%1</strong>,<strong>%2</strong>,<strong>%3</strong>,<strong>...</strong>,<strong>%20</strong> values with the values at the corresponding index in a comma-separated data frame. The corresponding &#8203;<strong>sprintf()</strong>&nbsp;format sent by the microcontroller for the given JSON map is:</div><div><div id="724047061613963126" align="left" style="width: 100%; overflow-y: hidden;" class="wcustomhtml"><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%">/*KAANSATQRO,%s,%s,%s,%s,%s,%s,%,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s*/</pre></div></div></div><div class="paragraph" style="text-align:left;"><br><span>You may have noticed some <strong>"w"</strong> keys in some places, these are used to build <em>widgets</em> (which are explained&nbsp;</span><a href="https://github.com/Serial-Studio/Serial-Studio/wiki/Introduction-to-widgets" target="_blank">here</a><span>).&nbsp;</span>Finally, here is the obligatory <em>GIF</em> showing the usage of <em><a href="https://github.com/Serial-Studio/Serial-Studio" target="_blank">Serial Studio</a></em>:</div><div><div class="wsite-image wsite-image-border-none" style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"><a><img src="https://www.aspatru.com/uploads/5/8/7/5/58759615/app-usage_orig.gif" alt="Picture" style="width:auto;max-width:100%"></a><div style="display:block;font-size:90%"></div></div></div><div class="paragraph" style="text-align:left;">Pretty cool, right? If you are interested in using <em><a href="https://github.com/Serial-Studio/Serial-Studio" target="_blank">Serial Studio</a></em> for your projects, here are the relevant links:<ul><li>Github repo: <a href="https://github.com/Serial-Studio/Serial-Studio/" target="_blank">https://github.com/Serial-Studio/Serial-Studio/</a></li><li>Documentation (GitHub wiki): <a href="https://github.com/Serial-Studio/Serial-Studio/wiki/Communication-Protocol" target="_blank">https://github.com/Serial-Studio/Serial-Studio/wiki/Communication-Protocol</a>&#8203;</li></ul><br><span style="color:rgb(74, 74, 74)">The prebuilt binaries/installers for Windows, macOS &amp; GNU/Linux are available through GitHub releases:&nbsp;</span><a href="https://github.com/Serial-Studio/Serial-Studio/releases">https://github.com/Serial-Studio/Serial-Studio/releases/<br>&#8203;</a></div><h2 class="wsite-content-title">Minimal example (with Arduino)</h2><div class="paragraph" style="text-align:left;"><span style="color:rgb(74, 74, 74)">Suppose that we want to graph the an ADC reading with an Arduino &amp; export the data to a CSV table. Here is the Arduino code:</span></div><div><div id="699870943569054165" align="left" style="width: 100%; overflow-y: hidden;" class="wcustomhtml"><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%"><span style="color: #557799">#define ADC_PIN A0</span><span style="color: #333399; font-weight: bold">void</span> <span style="color: #0066BB; font-weight: bold">setup</span>() {   <span style="color: #888888">// Initialize Serial port at 9600 bauds</span>   Serial.begin(<span style="color: #0000DD; font-weight: bold">9600</span>);      <span style="color: #888888">// Configure analog input</span>   pinMode(ADC_PIN, INPUT);}<span style="color: #333399; font-weight: bold">void</span> <span style="color: #0066BB; font-weight: bold">loop</span>() {   <span style="color: #888888">// Read voltage @ ADC_PIN</span>   <span style="color: #333399; font-weight: bold">int</span> adc_value <span style="color: #333333">=</span> analogRead(ADC_PIN);   <span style="color: #333399; font-weight: bold">float</span> voltage <span style="color: #333333">=</span> adc_value <span style="color: #333333">*</span> (<span style="color: #6600EE; font-weight: bold">5.0</span> <span style="color: #333333">/</span> <span style="color: #6600EE; font-weight: bold">1023.0</span>);   <span style="color: #888888">// Send current ms &amp; reading through serial</span>   Serial.print(<span style="background-color: #fff0f0">"/*"</span>);        <span style="color: #888888">// Frame start sequence  [/*]</span>   Serial.print(millis());    <span style="color: #888888">// Add MCU runtime       [ms]</span>   Serial.print(<span style="background-color: #fff0f0">","</span>);         <span style="color: #888888">// Separator character   [,]</span>   Serial.print(voltage);     <span style="color: #888888">// Add voltage           [V]</span>   Serial.print(<span style="background-color: #fff0f0">"*/"</span>);        <span style="color: #888888">// Frame finish sequence [*/]</span>      <span style="color: #888888">// Wait 50 ms</span>   delay(<span style="color: #0000DD; font-weight: bold">50</span>);}</pre></div></div></div><div class="paragraph" style="text-align:left;"><br><span style="color:rgb(74, 74, 74)">&#8203;Deploy this code to your Arduino and create a JSON file with the following contents:</span></div><div><div id="488948964266297243" align="left" style="width: 100%; overflow-y: hidden;" class="wcustomhtml"><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%">{   <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Minimal Example"</span>,   <span style="color: #007700">"g"</span>:[      {         <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"MCU Status"</span>,         <span style="color: #007700">"d"</span>:[            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"Runtime"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%1"</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"ms"</span>            },            {               <span style="color: #007700">"t"</span>:<span style="background-color: #fff0f0">"ADC reading"</span>,               <span style="color: #007700">"v"</span>:<span style="background-color: #fff0f0">"%2"</span>,               <span style="color: #007700">"g"</span>:<span style="color: #008800; font-weight: bold">true</span>,               <span style="color: #007700">"u"</span>:<span style="background-color: #fff0f0">"V"</span>,               <span style="color: #007700">"w"</span>:<span style="background-color: #fff0f0">"bar"</span>,               <span style="color: #007700">"min"</span>:<span style="color: #0000DD; font-weight: bold">0</span>,               <span style="color: #007700">"max"</span>:<span style="color: #0000DD; font-weight: bold">5</span>            }         ]      }   ]} </pre></div></div></div><div class="paragraph" style="text-align:left;"><br><span style="color:rgb(74, 74, 74)">&#8203;Open Serial Studio &amp; import the JSON file into Serial Studio by selecting the "manual" radio button in the top-left corner of the app &amp; clicking on the "Change map file" button. Finally, select the appropriate COM port. If everything goes well, you should see a screen similar to this one:</span></div><div><div class="wsite-image wsite-image-border-none" style="padding-top:10px;padding-bottom:10px;margin-left:0px;margin-right:0px;text-align:center"><a href='https://www.aspatru.com/uploads/5/8/7/5/58759615/screen-shot-2021-01-10-at-15-44-45-m4uefkhlk1_orig.png' rel='lightbox' onclick='if (!lightboxLoaded) return false'><img src="https://www.aspatru.com/uploads/5/8/7/5/58759615/screen-shot-2021-01-10-at-15-44-45-m4uefkhlk1_orig.png" alt="Picture" style="width:auto;max-width:100%"></a><div style="display:block;font-size:90%"></div></div></div><div class="paragraph" style="text-align:left;"><span style="color:rgb(74, 74, 74)">If you click on the "Open current CSV" button, you will be able to see all the received information in an Excel/Calc table:</span></div><div><div class="wsite-image wsite-image-border-none" style="padding-top:10px;padding-bottom:10px;margin-left:0px;margin-right:0px;text-align:center"><a href='https://www.aspatru.com/uploads/5/8/7/5/58759615/screen-shot-2021-01-10-at-15-46-07_orig.png' rel='lightbox' onclick='if (!lightboxLoaded) return false'><img src="https://www.aspatru.com/uploads/5/8/7/5/58759615/screen-shot-2021-01-10-at-15-46-07_orig.png" alt="Picture" style="width:auto;max-width:100%"></a><div style="display:block;font-size:90%"></div></div></div><div class="paragraph" style="text-align:left;"><span style="color:rgb(74, 74, 74)">If you have any doubts, ideas or bug reports, feel free to add a comment, contact me or open up an issue at GitHub. Hopefully some random person on the internet will find this useful :)</span><br><br><span style="color:rgb(74, 74, 74)"><strong>UPDATE:</strong> The project &amp; communication protocol has changed a lot during the last few years. If you are just getting started, please ignore the examples in this blog post and use the "Project Editor" to generate your JSON files. I have added several <a href="https://github.com/Serial-Studio/Serial-Studio/tree/master/examples" target="_blank">examples</a> with both MCU code + Project files in the GitHub repo.</span></div><div style="text-align:left;"><div style="height: 10px; overflow: hidden;"></div><a class="wsite-button wsite-button-small wsite-button-normal" href="https://github.com/Serial-Studio/Serial-Studio/releases/" target="_blank"><span class="wsite-button-inner">Download Serial Studio (Windows, macOS &amp; GNU/Linux)</span></a><div style="height: 10px; overflow: hidden;"></div></div><div class="wsite-spacer" style="height:50px;"></div>]]></content:encoded></item></channel></rss>