<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Rodrigo Sousa Coutinho on Medium]]></title>
        <description><![CDATA[Stories by Rodrigo Sousa Coutinho on Medium]]></description>
        <link>https://medium.com/@rodrigo_sc?source=rss-e8d44524b682------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*Xh9unbGspoumCadBVqDfLA.jpeg</url>
            <title>Stories by Rodrigo Sousa Coutinho on Medium</title>
            <link>https://medium.com/@rodrigo_sc?source=rss-e8d44524b682------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 19 Apr 2026 17:12:52 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@rodrigo_sc/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Reading temperature with Arduino and an LM35 (or TMP36)]]></title>
            <link>https://medium.com/arduino-playground/reading-temperature-with-arduino-and-an-lm35-or-tmp36-69ae34ec728e?source=rss-e8d44524b682------2</link>
            <guid isPermaLink="false">https://medium.com/p/69ae34ec728e</guid>
            <category><![CDATA[temperature-monitoring]]></category>
            <category><![CDATA[arduino]]></category>
            <category><![CDATA[lm35]]></category>
            <category><![CDATA[tmp36]]></category>
            <dc:creator><![CDATA[Rodrigo Sousa Coutinho]]></dc:creator>
            <pubDate>Tue, 31 Dec 2024 16:37:20 GMT</pubDate>
            <atom:updated>2024-12-31T17:02:31.393Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FEVd8cfCaWot9qByYOMdlA.png" /></figure><p>The circuit to get the room temperature using the LM35 (or TMP36) is pretty basic, as you can see from the picture (the position of the flat part of the sensor is important!)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/531/1*q6sEY_gzimqw5-SZbzp39w.png" /><figcaption>Simple is best!</figcaption></figure><p>You can now run the following code in Arduino to get the 1st results.</p><pre>int tempPin = A0; //LM35 Vout<br><br>void setup()<br>{<br>  Serial.begin(9600);<br>  pinMode(tempPin,INPUT);<br>}<br><br>void loop()<br>{<br>  delay(1000);<br>  Serial.println(analogRead(tempPin));<br>}</pre><p>This will start outputting values without a lot of meaning. For instance, right now its showing 44 at my desk using the LM35.</p><p>The trick is that we’re using an analog port — A0. This gives us a 10 bit analog to digital converter, which means we have 2¹⁰ (1024) different values. These values range from 0 to the operating voltage you’re using, 5V if you’re powering your Arduino using USB.</p><p>So if you want to know how many volts are in A0, you need to multiply the value you get in analogRead(A0) by the operating voltage (5) and divide by 1024. Just change that last line by these two:</p><pre>// The .0 after the 1024 is required to convert to float<br>float A0volts = analogRead(tempPin)*5/1024.0;<br>Serial.println(A0volts);</pre><p>The output that I get is 0.21 (44*5/1024), so my A0 input is currently receiving 0.21V.</p><p>The way you can turn this into a temperature depends on the sensor you’re using.</p><h3>LM35</h3><p>According to the <a href="https://www.ti.com/lit/ds/symlink/lm35.pdf">spec</a>, the LM35 output formula is 0mV + 10.0mV/C, which is equivalent to 0.01V per each centigrade. That’s very convenient, because all you have to do is multiply volts by 100 and you get the temperature.</p><pre>Serial.println(A0volts*100);</pre><p>So now I know that the temperature at my desk is at a nice 21.5º (44*5*100/1024).</p><h3>TMP36</h3><p>The sensor I have at home is the LM35, but while playing with <a href="https://www.tinkercad.com/">tinkercad</a> I found they only have the TMP36. The <a href="https://www.analog.com/media/en/technical-documentation/data-sheets/TMP35_36_37.pdf">spec</a> tells us that the formula for this sensor is 50mV + 10.0mV/C so you need to offset A0Volts by 0.5:</p><pre>Serial.println((A0volts-0.5)*100);</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=69ae34ec728e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/arduino-playground/reading-temperature-with-arduino-and-an-lm35-or-tmp36-69ae34ec728e">Reading temperature with Arduino and an LM35 (or TMP36)</a> was originally published in <a href="https://medium.com/arduino-playground">Arduino Playground</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Setting limits on ggplot]]></title>
            <link>https://medium.com/data-trekking/setting-limits-on-ggplot-e4cfea8b7370?source=rss-e8d44524b682------2</link>
            <guid isPermaLink="false">https://medium.com/p/e4cfea8b7370</guid>
            <category><![CDATA[ggplot2]]></category>
            <category><![CDATA[data-science]]></category>
            <dc:creator><![CDATA[Rodrigo Sousa Coutinho]]></dc:creator>
            <pubDate>Wed, 20 Nov 2019 20:00:36 GMT</pubDate>
            <atom:updated>2019-11-20T20:00:36.910Z</atom:updated>
            <content:encoded><![CDATA[<p>If you want to plot a chart with a few outliers in ggplot, you might be temped to use <a href="https://www.rdocumentation.org/packages/ggplot2/versions/0.9.0/topics/ylim"><em>ylim</em></a>. The problem with <em>ylim</em> is that it <strong>removes the data points</strong> that go beyond the limits.</p><p>For instance, if you have the following data:</p><pre>data &lt;- data.frame(x = 1:20, y = c(rnorm(19), 500))</pre><p>If you plot this with no changes, this is what you get:</p><pre>ggplot(data, aes(x,y)) + geom_line()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*5XP3E-sCKmqJGi1Rb43arQ.png" /></figure><p>It’s kind of hard to understand what’s going on in other points other than the last, so some zooming comes in handy. If you use <em>ylim</em>, this is what happens:</p><pre>ggplot(data, aes(x,y)) + geom_line() + ylim(-5, 5)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*qajlNDTGi9qFvFdtxb6FYg.png" /></figure><p>Granted, you get to understand what’s going on, but that last of the chart data is just gone! A better alternative is to use the <em>ylim</em> parameter of <a href="https://ggplot2.tidyverse.org/reference/coord_cartesian.html"><em>coord_cartesian</em></a>:</p><pre>ggplot(data, aes(x,y)) + <br>   geom_line() + <br>   coord_cartesian(ylim=c(-5, 5))</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*ZBh5Q_zaRda4q0TyGHDPOA.png" /></figure><p>This makes it much clearer that something’s going on at the end of that chart.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e4cfea8b7370" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-trekking/setting-limits-on-ggplot-e4cfea8b7370">Setting limits on ggplot</a> was originally published in <a href="https://medium.com/data-trekking">Data Trekking</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating Time Maps in R]]></title>
            <link>https://medium.com/data-trekking/creating-time-maps-in-r-b6ef9185e983?source=rss-e8d44524b682------2</link>
            <guid isPermaLink="false">https://medium.com/p/b6ef9185e983</guid>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[visualization]]></category>
            <category><![CDATA[ggplot2]]></category>
            <dc:creator><![CDATA[Rodrigo Sousa Coutinho]]></dc:creator>
            <pubDate>Sat, 13 Apr 2019 12:51:14 GMT</pubDate>
            <atom:updated>2019-04-13T12:51:14.484Z</atom:updated>
            <content:encoded><![CDATA[<p>Time Maps are a great way to understand events that happen with a certain cadence over time. Take a look at <a href="https://districtdatalabs.silvrback.com/time-maps-visualizing-discrete-events-across-many-timescales">this article</a> by Mark Watson for an excellent explanation on what they are and when they can be useful.</p><p>The end result of this article will be to create a Time Map that looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*3xCEkY9NLmSh8XQdA-FLYg.png" /><figcaption>A Time Map for our event. Complete code at the end of the article.</figcaption></figure><h4>Sample Data</h4><p>The type of data I usually want to put in this type of chart comes in the form &lt;TimeStamp&gt; | &lt;Event&gt;. Here’s an example data frame:</p><pre>library(dplyr)<br>set.seed(123)<br>df &lt;- data.frame(helper = c(rnorm(500, 120, 100), <br>                            rnorm(500, 1, .8))) %&gt;%<br>    mutate(elapsed = cumsum(helper), <br>           date = as.POSIXct(&#39;2019-05-25 10:00:00&#39;) + elapsed, <br>           event = &#39;Ping&#39;) %&gt;%<br>    select(date, event)</pre><p>This will create a data frame with events that happen about 2 minutes apart and then 1 second apart. The data frame looks like this (the <em>event</em> is useless, it’s there just to make the data frame more interesting):</p><pre>&gt; head(df)<br>                 date event<br>1 2019-05-25 10:01:03  Ping<br>2 2019-05-25 10:02:40  Ping<br>3 2019-05-25 10:07:16  Ping<br>4 2019-05-25 10:09:23  Ping<br>5 2019-05-25 10:11:36  Ping<br>6 2019-05-25 10:16:28  Ping</pre><h4>Transforming the data</h4><p>Next step, we need to have a “time before” and “time after” value for our data frame. Here’s how to do it:</p><pre>before_after &lt;- df %&gt;%<br>    arrange(date) %&gt;% <br>    mutate(before = as.numeric(date - lag(date)),<br>           after =  as.numeric(lead(date) - date)) %&gt;%<br>    filter(!is.na(before) &amp; !is.na(after))</pre><p>This will create the following data frame:</p><pre>&gt;  head(before_after)<br>                 date event    before      after<br>1 2019-05-25 10:02:40  Ping  96.98225 275.870831<br>2 2019-05-25 10:07:16  Ping 275.87083 127.050839<br>3 2019-05-25 10:09:23  Ping 127.05084 132.928774<br>4 2019-05-25 10:11:36  Ping 132.92877 291.506499<br>5 2019-05-25 10:16:28  Ping 291.50650 159.585497<br>6 2019-05-25 10:19:07  Ping 159.58550   6.506124</pre><h4>Plotting the data</h4><p>The simplest plot would be:</p><pre>library(ggplot2)<br>ggplot(before_after, aes(x = before, y = after)) +<br>    geom_hex()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*ESE3VIVudEOMucRefXE_Ag.png" /><figcaption>If you’re not a fan of hexagons, replace geom_hex by stat_bin2d</figcaption></figure><p>But this is not very informative. There’s a very high concentration near (0,0) that we can barely see. Let’s use a logarithmic scale, and also add a bit more color to the chart:</p><pre>library(RColorBrewer)<br>ggplot(before_after, aes(x = before, y = after)) +<br>    geom_hex() +<br>    scale_fill_gradientn(colours = rev(brewer.pal(5, &quot;Spectral&quot;))) +<br>    scale_y_continuous(trans = &quot;log10&quot;) +<br>    scale_x_continuous(trans = &quot;log10&quot;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*Loq-7QOvBL_CYTg63dXh3A.png" /></figure><p>Better, but the axis labels aren’t very informative… Using <em>breaks</em> and <em>labels</em> on the axis, the time intervals become much more clear:</p><pre>axis_labels = list(&#39;1/100s&#39; = 0.1, &#39;1 sec&#39; = 1, &#39;1 min&#39; = 60, <br>             &#39;2 min&#39; = 120, &#39;10 min&#39; = 600)<br>ggplot(before_after, aes(x = before, y = after)) +<br>    geom_hex() +<br>    scale_fill_gradientn(colours = rev(brewer.pal(5, &quot;Spectral&quot;))) +<br>    scale_y_continuous(trans = &quot;log10&quot;, minor_breaks = NULL,<br>                       breaks = unlist(axis_labels),<br>                       labels = names(axis_labels)) +<br>    scale_x_continuous(trans = &quot;log10&quot;, minor_breaks = NULL,<br>                       breaks = unlist(axis_labels),<br>                       labels = names(axis_labels)) +<br>    theme(axis.text.x = element_text(angle = 45, hjust = 1))</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*3xCEkY9NLmSh8XQdA-FLYg.png" /></figure><p>Here’s the complete code for the chart:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1f18320822ee9cfda2f305a8d93d6471/href">https://medium.com/media/1f18320822ee9cfda2f305a8d93d6471/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b6ef9185e983" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-trekking/creating-time-maps-in-r-b6ef9185e983">Creating Time Maps in R</a> was originally published in <a href="https://medium.com/data-trekking">Data Trekking</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Testing Rmd files on Shiny with monit]]></title>
            <link>https://medium.com/data-trekking/testing-rmd-files-on-shiny-with-monit-85a224da31e9?source=rss-e8d44524b682------2</link>
            <guid isPermaLink="false">https://medium.com/p/85a224da31e9</guid>
            <category><![CDATA[shiny-server]]></category>
            <category><![CDATA[puppeteer]]></category>
            <category><![CDATA[monit]]></category>
            <category><![CDATA[rmd]]></category>
            <dc:creator><![CDATA[Rodrigo Sousa Coutinho]]></dc:creator>
            <pubDate>Fri, 15 Feb 2019 09:03:10 GMT</pubDate>
            <atom:updated>2019-02-15T09:03:10.097Z</atom:updated>
            <content:encoded><![CDATA[<p>We are <a href="https://medium.com/data-trekking/monitoring-a-shiny-server-with-monit-4ad7cedaffdb">using monit to make sure our Shiny Server is running</a> properly. We also want to make sure our applications and reports aren’t broken.</p><p>Testing “normal” Shiny applications can be achieved with check host, but Rmd files are trickier... they return a success page, and only then do they build the report, which later can lead to an undetected error.</p><p>Our solution was to use <a href="https://github.com/GoogleChrome/puppeteer">puppeteer</a> to test these pages.</p><h3>Installing puppeteer</h3><p>This may differ from distribution to distribution. We are using CentOS, so the 1st thing was upgrading nodejs to a later version. This was done using <a href="https://github.com/creationix/nvm">nvm</a>.</p><p>The commands look something like:</p><pre>curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash<br>source ~/.nvm/nvm.sh<br>nvm install 7<br>nvm use 7</pre><p>Next, you can install puppeteer:</p><pre>npm i puppeteer</pre><p>The following script tests an Rmd file based on a URL:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/361d8603b89e8aad1d5271618617a8e8/href">https://medium.com/media/361d8603b89e8aad1d5271618617a8e8/href</a></iframe><p>Save the code to a file and run it using node test-Rmd.js &lt;url&gt;. You&#39;ll probably get an error like error while loading shared libraries, which means you&#39;re missing some dependencies... you can check which dependencies by running</p><pre>ldd ~/node_modules/puppeteer/.local-chromium/linux-624492/chrome-linux/chrome  | grep not</pre><p>For me, it was enough to install the following dependencies:</p><pre>sudo yum install gtk3<br>sudo yum install libXScrnSaver</pre><p><strong>Note:</strong> The “ — no-sandbox” option launching puppeteer is not recommended, but since we’re using it to access our site only, it should be ok.</p><p>Once you have the script running, it’s time to configure <em>monit</em>.</p><h3>Configuring monit</h3><p>We’ll be using check program from <em>monit</em> which, unfortunately, doesn&#39;t take any arguments. So before we configure <em>monit</em>, we need to make a wrapper to our nodejs call.</p><p>You can do so by creating a shell script called test-Rmd.shwith something like:</p><pre>#!/bin/sh<br>URLS=(http://&lt;yourserver&gt;/report.Rmd <br>http://&lt;yourserver&gt;/another_report.Rmd)</pre><pre>RES=0<br>for i in ${URLS[@]}<br>do<br>    node /srv/shiny-server/ETLs/monit-validations/test-Rmd.js $i<br>    if [ $? == 1 ]; then <br>        RES=1 <br>    fi<br>done<br>exit $RES</pre><p>Next, you just need to configure <em>monit</em>. Edit a file for the Rmd validation, e.g. sudo vi /etc/monit.d/rmdmonitor and call the script you just created.</p><pre>check program RmdCheck with path /&lt;path_to_script&gt;/test-Rmd.sh<br>        every 2 cycles<br>        if status != 0 then alert</pre><p>Reload <em>monit</em> (sudo monit reload) and you&#39;re good to go!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=85a224da31e9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-trekking/testing-rmd-files-on-shiny-with-monit-85a224da31e9">Testing Rmd files on Shiny with monit</a> was originally published in <a href="https://medium.com/data-trekking">Data Trekking</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Monitoring a Shiny Server with Monit]]></title>
            <link>https://medium.com/data-trekking/monitoring-a-shiny-server-with-monit-4ad7cedaffdb?source=rss-e8d44524b682------2</link>
            <guid isPermaLink="false">https://medium.com/p/4ad7cedaffdb</guid>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[monit]]></category>
            <category><![CDATA[monitoring]]></category>
            <category><![CDATA[shiny-server]]></category>
            <dc:creator><![CDATA[Rodrigo Sousa Coutinho]]></dc:creator>
            <pubDate>Mon, 11 Feb 2019 09:25:10 GMT</pubDate>
            <atom:updated>2019-02-15T09:04:56.498Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zOZqJG8tjljRWGfzmESoVg.png" /><figcaption>Barking at daemons</figcaption></figure><p>My team uses a <a href="https://www.rstudio.com/products/shiny/shiny-server/">Shiny Server</a> to share some data analysis and dashboards with other teams.</p><p>The server has automatic processes to get data from external sources, and any team member can publish new code to production at any time.</p><p>Most of the time things go right, but I want to make sure everything keeps working, regardless of the changes that happen. To monitor the server, we decided to use <a href="https://mmonit.com/monit/">monit</a>.</p><h3>Installing Monit</h3><p>The installation depends on the operating system you’re using. For CentOS, here are the steps to install:</p><pre>sudo yum install monit<br>sudo systemctl enable monit<br>sudo systemctl start monit</pre><p>You can check if everything is working by running curl http://localhost:2812, although the result is not very interesting... A 401 error.</p><p>We can configure <em>monit</em> to allow remote access to the web interface, so that we can see the results in a remote browser.</p><p>Edit /etc/monitrc, and search for the embedded HTTP interface configuration (just look for 2812). Replace what&#39;s there by:</p><pre>set httpd port 2812<br>    allow admin:monit</pre><p>And reload <em>monit</em>: sudo monit reload. If you now launch a browser a navigate to http://&lt;yourhost&gt;/:2812, you should see something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*owJupsybJ6EYHmUGwsR44g.png" /><figcaption>Monit, up and running</figcaption></figure><p><strong>Note:</strong> You should later get back to this configuration and make it more secure!</p><h3>Monitoring the Shiny process</h3><p>To monitor the shiny process, the 1st thing you need is to launch Shiny with a configuration that uses a PID file.</p><p>Run systemctl status shiny-server and this will give you the name of the service. In my machine it&#39;s /etc/systemd/system/shiny-server.service</p><p>You can edit this file and change the <em>ExecLaunch</em> section to add a PID file (--pidfile=/var/run/shiny-server.pid)</p><pre>(...)<br>[Service]<br>Type=simple<br>ExecStart=/usr/bin/env bash -c &#39;exec /opt/shiny-server/bin/shiny-server <strong>--pidfile=/var/run/shiny-server.pid</strong> &gt;&gt; /var/log/shiny-server.log 2&gt;&amp;1&#39;<br>(...)</pre><p>Run sudo systemctl daemon-reload and sudo systemctl restart shiny-server. You should now have a PID file.</p><p>Next, we need to configure <em>monit</em>. Create a file called /etc/monit.d/shinymonitor and add the following content:</p><pre>check process shiny-server with pidfile /var/run/shiny-server.pid<br>start program &quot;/usr/bin/systemctl start shiny-server.service&quot;<br>stop program &quot;/usr/bin/systemctl stop shiny-server.service&quot;<br>if failed port 80 protocol http then restart</pre><p>You can check that everything is ok by running sudo monit -t, and then restart with monit reload.</p><p>You are now monitoring your Shiny Server! Go back to the web console and check it out. Click the link, and you can see additional information about the process.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oYd1r7zwmZPWIINFb5h40g.png" /></figure><h3>Monitoring Applications</h3><p>In addition to monitoring if the service is running, you can make sure specific applications are running. Just to be sure a move to production or a data fetch hasn’t caused any problems.</p><p>You can do this by creating a file called /etc/monit.d/shinyappsmonitor with the following content:</p><pre>check host myhost-myimportantapp address myhost.com<br>        if failed port 80 protocol http request /myapp/ then alert</pre><pre>check host myhost-otherimportantapp address myhost.com<br>        if failed port 80 protocol http request /otherapp/ then alert</pre><pre>check host myhost-reports address myhost.com<br>        if failed port 80 protocol http request /report/ then alert<br>        if failed port 80 protocol http request /anotherreport/ then alert</pre><p>The name after check host is what you&#39;ll see in the report. You could put all applications under the same host, but I find it easier to look at the report like this.</p><h3>What’s next?</h3><p>If you have Rmd files running on your Shiny Server, these take a bit more work to monitor. <a href="https://medium.com/@rodrigo_sc/testing-rmd-files-on-shiny-with-monit-85a224da31e9">Take a look at this article to learn how to do it</a>.</p><p>Other things I’d like to do with <em>monit</em>:</p><ol><li>Send alerts via slack. You can only configure <em>monit</em> to send alerts through email</li><li>Monitor cron jobs. We have some data processing jobs running through cron, would like to know if everything succeeded.</li></ol><p>I’ll write about these things as soon as I build them! :)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4ad7cedaffdb" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-trekking/monitoring-a-shiny-server-with-monit-4ad7cedaffdb">Monitoring a Shiny Server with Monit</a> was originally published in <a href="https://medium.com/data-trekking">Data Trekking</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A/B Testing OutSystems Mobile Apps with Google Optimize]]></title>
            <link>https://medium.com/outsystems-engineering/a-b-testing-outsystems-mobile-apps-with-google-optimize-fdb52d45a86a?source=rss-e8d44524b682------2</link>
            <guid isPermaLink="false">https://medium.com/p/fdb52d45a86a</guid>
            <category><![CDATA[google-optimize]]></category>
            <category><![CDATA[ab-testing]]></category>
            <category><![CDATA[google-analytics]]></category>
            <category><![CDATA[mobile]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Rodrigo Sousa Coutinho]]></dc:creator>
            <pubDate>Mon, 19 Mar 2018 16:18:10 GMT</pubDate>
            <atom:updated>2018-03-28T08:50:20.120Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BgaDLarvanoJzY5TMMSjCQ.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/photos/YbgPWfWlvkE?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">elizabeth lies</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>The goal is simple. I have a mobile application built with <a href="http://www.outsystems.com">OutSystems</a> and I want to check whether a red button or a green button converts better.</p><p>The application has two screens and looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vCLA6HJgY2zZQLZMNlVkRA.png" /><figcaption>A minimalistic shopping app</figcaption></figure><h3>Step 1: Set Up Google Analytics</h3><p>The first thing to do is configure <a href="http://analytics.google.com/analytics/">Google Analytics</a> so you can track your goals and test which o f the options has a higher conversion rate.</p><p>Create a new account and select “Website”. At the end of the process, you will have a tracking code.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*775T2G2PxqcGQ2x0a61guw.png" /><figcaption>A couple of steps and a licensing agreement will get you a tracking ID.</figcaption></figure><p>Next, set up a goal. This will measure each variant’s rate of success, and you’ll be able to understand which option is better. For this particular situation, we want people to click the Buy button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zqiA6kd2KX9Wow6kpYtZVg.png" /><figcaption>Set up your goal.</figcaption></figure><h3>Step 2: Set Up Google Optimize</h3><p>Next, go to <a href="https://optimize.google.com/">Google Optimize</a>. Create a new account if you don’t have one and add a container. At the end of the process, you’ll have a Container ID.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8iFSLzBS9zM2ROPyPmo1OQ.png" /><figcaption>Getting a container ID.</figcaption></figure><p>To collect experiment data, link your Optimize account to your Analytics account.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ltLpz797YOBgYNfo85Ip4Q.png" /><figcaption>Linking Optimize to Analytics.</figcaption></figure><h3>Step 3: Configure the Application</h3><p>With the basic setup done on Google’s side, we need to set up the application. Start by installing the “<a href="https://www.outsystems.com/forge/component/3463/google-optimize/">Google Optimize</a>” component from the <a href="https://www.outsystems.com/forge/">OutSystems Forge</a>.</p><p>Next, add “SetupGoogleOptimize” to <em>OnApplicationReady</em>, “SendPageView” on the <em>OnReady </em>of the <em>Common/Layout</em> block, and “SendEvent” on the button click event. Be sure to use the <strong>Category</strong>, <strong>Action </strong>and <strong>Label </strong>configured in the Analytics goal.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ameqi-1S2V6XGsOoloMEpA.png" /><figcaption>Preparing your app!</figcaption></figure><p>We can now publish and run the application and make sure the events are being properly tracked. Open the application in a browser and check the real-time reports in Google Analytics.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KjmSeN1dG8PCJYW2gB2NKA.png" /><figcaption>If everything is properly configured, you should see the events in Google Analytics.</figcaption></figure><h3>Step 4: Configure the A/B Test</h3><p>It’s time to go back to <a href="https://optimize.google.com/">Google Optimize</a> and create the experiment. I called mine “Green Button” and configured the URL for the homepage.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1GZEKiCQnz11Q1bsaMfVPA.png" /><figcaption>The experiment is created!</figcaption></figure><p>Once that’s done, we can start configuring variants. We’ll just add one called “GreenButton” that will change the color of the button to green.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aGVHWokJm_cytSmf2s3BoA.png" /><figcaption>A variant aimed at conversion!</figcaption></figure><p>Next, we need to configure the objective and the targeting of our experiment. The objective will be the goal we set up earlier: clicking the button. On the Targeting tab, change the activation event to “Custom” with an “optimize.activate” value. This is the value the Google Optimize component uses.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gx9cFNbVNQDxW0F3KEvyJA.png" /><figcaption>Set the objective and targeting for your experiment.</figcaption></figure><p>And that’s it! You’re ready to go!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4vR1FX-SRJlfN5TNQGA2Kg.png" /><figcaption>Finally! All is ready!</figcaption></figure><h3>Step 5: Get Some Results</h3><p>You can now start seeing your different variants running. The easiest way to do it is by launching an incognito window and access your app’s URL. One of the variants will show. Close all incognito windows, open another incognito window, and you might get the alternative variant (if you don’t, try a couple more times). If all is properly configured, you’ll be able to see both variants.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Lh7H90lcDrWmqMBpV4aB9g.png" /><figcaption>You can see on the dashboard how many users got which variant.</figcaption></figure><p>Google Optimize is now collecting data and managing your experiment. But it will be a while before you can see the results — up to 48 hours. If you’re in a rush, you can always take a peek at the results in Google Analytics while it’s still available!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OxM3m1gEm1JEYipVb1fgXw.png" /><figcaption>The green button appears to have a higher conversion rate!</figcaption></figure><p>When your results are finally processed, you get all the information you need about your experiment.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/904/1*ehjWt-7G2NGlskQNnH8QYA.png" /><figcaption>Google Optimize chart for the green button experiment</figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fdb52d45a86a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/outsystems-engineering/a-b-testing-outsystems-mobile-apps-with-google-optimize-fdb52d45a86a">A/B Testing OutSystems Mobile Apps with Google Optimize</a> was originally published in <a href="https://medium.com/outsystems-engineering">OutSystems Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A great place to work]]></title>
            <link>https://medium.com/@rodrigo_sc/a-great-place-to-work-344b23bfad4c?source=rss-e8d44524b682------2</link>
            <guid isPermaLink="false">https://medium.com/p/344b23bfad4c</guid>
            <category><![CDATA[personal-development]]></category>
            <category><![CDATA[workplace]]></category>
            <dc:creator><![CDATA[Rodrigo Sousa Coutinho]]></dc:creator>
            <pubDate>Thu, 01 Feb 2018 14:53:05 GMT</pubDate>
            <atom:updated>2018-02-01T23:08:45.754Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/512/1*23Rd4JF22wo5ApjgxNMkeA@2x.jpeg" /></figure><p>When looking for a new job, it’s easy to be impressed with the perks some companies offer. After all, working in a place that offers ping-pong tables, unlimited vacations, and free breakfast seems like fun!</p><p><strong>But perks are just ingredients. You’re not tasting the full dish!</strong></p><p>As discussed in <a href="https://rework.fm/mailbag-1-2/">this Rework podcast</a>, pilling perks is the wrong way to go through the job seeking process. Perks alone will not make you happy.Since I’m lucky enough to work for <a href="http://www.outsystems.com">OutSystems</a>, a company with an extraordinarily high <a href="http://www.netpromotersystem.com/about/employee-engagement.aspx">employee NPS</a> (yes, even higher than Google), I wanted to know what people value in their job.</p><p>It was easy, I just had to ask our amazing People Ops team. They had already done this work. So here they are, the 4 characteristics that make people love their job:</p><ul><li><strong>Purpose</strong>: This is the thing that gets us out of bed. A passion for doing something that’s bigger than ourselves. <a href="https://medium.com/outsystems-engineering/what-on-earth-are-we-doing-here-8c5b2f05cea7?source=linkShare-e8d44524b682-1517474141">Our work matters</a>, and we’re changing the world for better every single day;</li><li><strong>Growth</strong>: We’re always learning. Either because there’s a new challenge, or because we want to find a better way to do an old job. Our People Ops team created career tracks to help us grow, but in the end each person controls their path. We’re always working to be masters of our practice;</li><li><strong>Great Culture</strong>: We all share the same values (we even have a <a href="https://www.outsystems.com/the-small-book/">little book</a> about them) and we invest a lot in relationships. We are a team, not a group of individuals or departments;</li><li><strong>Recognition</strong>: We believe in rewarding achievements. We do it through fair paychecks, by offering <a href="https://www.outsystems.com/company/news/2017/stock-option-plan/">stock options to all employees</a>, and by saying “thank you”. We say “thank you” a lot. Throughout the day, in meetings, and in #kudos, our appreciation slack channel.</li></ul><p>OutSystems also has perks, of course. Ping-pong tables, fruit, fridges full of yogurts, a great office… But when we ask people what they value most about their job, that’s not the main ingredient.</p><p>So if you’re looking for a new job, <strong>look beyond the perks</strong>. Find people that have worked for the company you’re targeting, and ask them about the things <strong>you</strong> value in a job. It’s the best way to find a position compatible with what makes you happy. Oh, and be sure to take a peek at our <a href="https://www.outsystems.com/company/careers/">careers page</a> :-)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=344b23bfad4c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Your best option? Delay the project]]></title>
            <link>https://medium.com/@rodrigo_sc/your-best-option-delay-the-project-d53680f4dfcc?source=rss-e8d44524b682------2</link>
            <guid isPermaLink="false">https://medium.com/p/d53680f4dfcc</guid>
            <category><![CDATA[product-management]]></category>
            <category><![CDATA[quality]]></category>
            <dc:creator><![CDATA[Rodrigo Sousa Coutinho]]></dc:creator>
            <pubDate>Wed, 10 Jan 2018 17:39:47 GMT</pubDate>
            <atom:updated>2018-01-11T18:08:16.102Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7gvtCjwIrgDthRtQ_uyOLA.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/photos/FoKO4DpXamQ?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Eric Rothermel</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p><strong>People will quickly forget that a project was delivered late… But a project delivered with poor quality will leave a long-lasting memory!</strong></p><p>A few years ago I was platform release manager at <a href="https://www.outsystems.com/">OutSystems</a>. My main responsibility was making sure we released versions of the platform on time. And that’s what I did. If we announced the platform would be released on a given date, we made it happen.</p><p><strong>But whenever you rush delivery, there is a price to pay: Quality.</strong></p><p>At OutSystems we’ve always been very focused on quality, so the impact of rushing deliveries wasn’t that big. There were a few issues, but problems were quickly solved once detected. Seemed like a small price to pay for early delivery.</p><p>But losing quality isn’t the major issue. <strong>The big trouble is losing your customers’ trust!</strong></p><p>Even this tiny loss of quality made customers avoid early releases of our product. It was better to wait for more stable versions rather than risk an early upgrade. And if nobody installs your product when you release it, what’s the point of releasing early?</p><p>So we changed our approach. We improved the way we guarantee product quality and we no longer compromise. If there’s any warning sign, regardless if it‘s a minor hiccup or an issue with the tests, we don’t release.</p><p>We’ve been having a lot of success with this approach. Customers feel confident to upgrade as soon as we launch. And we’re confident enough to <a href="https://success.outsystems.com/Documentation/Whats_New">release new product features continuously</a>!</p><p>Release dates are still important, for sure. But <strong>delaying a couple of weeks for quality’s sake has proven to be the right choice, every single time.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/380/0*tfhZFQpis01xM_Ux." /></figure><p>But there’s a catch! After all this work, we still have customers that are afraid to upgrade immediately. They installed a slightly glitched version, got a sub-par experience, and never changed their behavior. They’re still suspicious of new releases, and for the most part wait before upgrading to new versions.</p><p><strong>Making our product and quality processes bulletproof is hard. But changing customers’ behavior proved even harder!</strong></p><p>So now we’re working to win back the trust of these customers. One by one, we’re getting them back on board of our fast release train. And it is working, but only because we’re delivering with such high quality standards!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d53680f4dfcc" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[This post was written on a typewriter]]></title>
            <link>https://medium.com/@rodrigo_sc/this-post-was-written-on-a-typewriter-eb4beee2a045?source=rss-e8d44524b682------2</link>
            <guid isPermaLink="false">https://medium.com/p/eb4beee2a045</guid>
            <category><![CDATA[typewriter]]></category>
            <category><![CDATA[writing]]></category>
            <category><![CDATA[ramblings]]></category>
            <dc:creator><![CDATA[Rodrigo Sousa Coutinho]]></dc:creator>
            <pubDate>Fri, 18 Aug 2017 18:41:22 GMT</pubDate>
            <atom:updated>2017-08-18T18:43:57.828Z</atom:updated>
            <content:encoded><![CDATA[<p>Yes, it is true. We’re in the middle of 2017 and I’m using a l935 typewriter to create a post. Don’t believe me? Check the image! You’ll get to see all the nuances thqt go with using a typewriter, including that “q” on the word “that”… It is a AZERTY keyboard…</p><p>But… why? Well, mostly just for the fun of it. Typing on this mechanical complexity, where everything is configured using levers and springs and clicks and clacks is a blast. Just the sound of the keys makes you want to type more!</p><p>Then there’s the fact that this is the only thing I’m doing! May seem like an odd thing to say, but not having distractions, and the attention I need to put on each key of this strange keyboard, it’s almost meditation…</p><p>It’s been a while, since I wanted a typewriter. I think I was inspired by a documentary where <a href="https://www.youtube.com/watch?v=uK6ip4Lvyns">Woody Allen showed how he wrote his scripts</a>. Yeah, you guessed it. He still use one of these machines, and when he needs to cut and paste . . . he uses actual scissors and glue.</p><p>I’m not sure if these were his sayings, and right now I don’t feel like using my computer to check it out, but I believe his reasons for using a typewriter included the fact that it is slower. The fact that on any modern editor you get to go bqck and edit what you just did, means that you tend to edit the whole time you are writting. Not having this ability makes you focus more on what you wanto to say, makes you think harder about XXXXXXXX how you want to say It (because editing is so hard) and alledgely makes your writig better. (btw, those mistakes on this paragraph, just when I was making a point about how focused I was? Got distracted by my phone. … :)</p><p>So, is it tru? Do I feel like I can do great writing on this relic? Well, sadly no… At least, not yet. ironically; I need to focus so much to write on this machine, that sometimes I feel like I lose my train of thought… Maybe that’s just a matter of practise. But I still miss the spell checker, and I’m not sure my family and neighbours appreciat my new toy… It is a bit noisy…</p><p>Anyway; I’ll continue to play with my Royal Deluxe. The sound of the keys is just too good not to use it. Let’s see where this leads… But if I do use it for my next post(s), I’ll edit out all the terrible mistakes on the fina version!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zuNL-ctXpy2dvNvwlU1M3A.jpeg" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=eb4beee2a045" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using a 74HC595 to control a LED Matrix]]></title>
            <link>https://medium.com/arduino-playground/using-a-74hc595-to-control-a-led-matrix-84559feee7cd?source=rss-e8d44524b682------2</link>
            <guid isPermaLink="false">https://medium.com/p/84559feee7cd</guid>
            <category><![CDATA[74hc595]]></category>
            <category><![CDATA[getting-started]]></category>
            <category><![CDATA[led]]></category>
            <category><![CDATA[arduino]]></category>
            <dc:creator><![CDATA[Rodrigo Sousa Coutinho]]></dc:creator>
            <pubDate>Sun, 05 Mar 2017 22:31:20 GMT</pubDate>
            <atom:updated>2017-03-05T22:41:12.410Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8li4RwAGu48hFlasbCf_jQ.jpeg" /></figure><p><a href="https://medium.com/arduino-playground/using-a-led-matrix-with-arduino-640537b46f37">Setting up a LED Matrix</a> can quickly get you out of pins to do other things with your Arduino. One way to overcome this is to use a <a href="http://www.ti.com/lit/ds/symlink/sn74hc595.pdf">74HC595</a> shift register.</p><p>This chip transforms bits that are inserted in series trough the data pin into 8 parallel bits. More on that later, but first, the circuit:</p><figure><img alt="The chip in the middle is the one doing the magic!" src="https://cdn-images-1.medium.com/proxy/1*6dWOSke5JNi4Ezmt_JJzsw.png" /><figcaption>The chip in the middle is the one doing the magic!</figcaption></figure><p>The connection between Arduino and the matrix columns is <a href="https://medium.com/arduino-playground/using-a-led-matrix-with-arduino-640537b46f37">the same as without the 595</a>. Just plug it to the right pins with a resistor in the middle (I used a 330Ω) and you’re done.</p><p>Pins <strong>QA</strong>-<strong>QH</strong> on the 595 are the outputs that we want, so they’re connected to the matrix rows. Don’t worry about QH*.</p><p>The other interesting pins are:</p><ul><li><strong>SER</strong> (SERial) where the data gets in;</li><li><strong>SRCLK</strong> (SeRial CLocK) the pin you set to high to store what’s in SER;</li><li><strong>RCLK</strong> (Register CLocK) the pin you set to high once you’re done setting all the pins.</li></ul><p>So here’s how it works. Let’s imagine you want to set the QA-QH to 10010010. You start with the least significant bit (0) so you set SER to LOW (D10 on the Arduino). Next, you set SCK (D11 on the Arduino) to HIGH and then to LOW, to “save” the value.</p><p>Time to go to the second bit, which has value 1. So you set SER to HIGH, and again you set SCK to HIGH and then to LOW.</p><p>You keep going, until you’ve sent the 8 bits. At this point, you set the RCLK to HIGH to sent your changes to pins QA through QH.</p><p>This may seem complex, but fortunately Arduino has a function that takes care of sending that bits out to the shift register:</p><pre>shiftOut(dataPin, clockPin, bitOrder, value)</pre><p>This means that, to set data on the 595, all you have to do is:</p><pre>digitalWrite(RCK_PIN, LOW); <br>// Write byte_to_write to SER, using SCK_PIN as the clock and <br>// send the least significant byte first (LSBFIRST)<br>shiftOut(SER_PIN, SCK_PIN, LSBFIRST, byte_to_write); <br>digitalWrite(RCK_PIN, HIGH); </pre><p>Cool, isn’t it?</p><p>So here’s the full code for the LED matrix, using a 595:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e9b26f49b213a46cd7bd21e377bb6feb/href">https://medium.com/media/e9b26f49b213a46cd7bd21e377bb6feb/href</a></iframe><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*D6oLp685vcGx8GdRh-jswA.jpeg" /></figure><p>If you run this code you’ll get a nice smile! If you want to build a different character, head over to the <a href="https://rodrigosc.github.io/ArduinoLedMatrix/char_builder/builder.html">character builder</a> and create your own.</p><p>You can get the <a href="https://github.com/RodrigoSC/ArduinoLedMatrix">code and schematics for this project on github</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=84559feee7cd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/arduino-playground/using-a-74hc595-to-control-a-led-matrix-84559feee7cd">Using a 74HC595 to control a LED Matrix</a> was originally published in <a href="https://medium.com/arduino-playground">Arduino Playground</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>