<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Stealthy Labs Blog</title>
    <description>A blog on our adventures with code.
</description>
    <link>https://www.stealthylabs.com/blog/</link>
    <atom:link href="https://www.stealthylabs.com/blog/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sun, 06 Oct 2024 17:19:57 -0400</pubDate>
    <lastBuildDate>Sun, 06 Oct 2024 17:19:57 -0400</lastBuildDate>
    <generator>Jekyll v3.8.7</generator>
    
      <item>
        <title>How to setup ZoneMinder on Ubuntu 24.04 LTS</title>
        <description>&lt;p&gt;If you are new to setting up your own video surveillance system for your home or
office or a customer’s home or office, and want to truly &lt;strong&gt;own&lt;/strong&gt; the setup, you
want to use &lt;a href=&quot;https://www.zoneminder.com&quot;&gt;ZoneMinder&lt;/a&gt;, an open-source and free
software that allows you to connect a variety of wired PoE cameras to a single
software server that can manage all of them.&lt;/p&gt;

&lt;p&gt;In this post, we outline how to set it up from scratch on Ubuntu 24.04 LTS.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h2 id=&quot;requirements&quot;&gt;REQUIREMENTS&lt;/h2&gt;

&lt;p&gt;For the server that runs ZoneMinder, you will need a minimum  of 8GB RAM, 2-4 Intel
CPUs, and a 1 TB hard drive. You can buy a used server on eBay and upgrade it,
or use an older system that can run 24/7. We recommend using a server or
workstation or desktop type product, since a laptop will run too hot and
eventually die too early.&lt;/p&gt;

&lt;p&gt;Disk is cheap, so buy the largest hard drives you can afford. Spinning hard
drives are sufficient. You can optimize, like we did, where we used a smaller
solid state drive (SSD) for the operating system and save the data such as video files and images to
the regular hard drives.&lt;/p&gt;

&lt;p&gt;Without 8GB RAM, ZoneMinder will struggle to process more than 2-3 cameras,
especially, if you are setting them up for motion detection or record on motion
detection.&lt;/p&gt;

&lt;h2 id=&quot;installation-of-ubuntu&quot;&gt;INSTALLATION OF UBUNTU&lt;/h2&gt;

&lt;p&gt;If you have 2 or more disk drives in your server/workstation that will run
ZoneMinder, and they are all of the same type, such as all of them are SSDs or
spinning hard drives, then we recommend installing Ubuntu 24.04 LTS &lt;strong&gt;with
LVM&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;LVM will allow you to add more drives in the future without having to change the
way the file system is.&lt;/p&gt;

&lt;p&gt;If you have a single drive, we still recommend installing Ubuntu 24.04 LTS with
LVM so that you can add more drives in the future.&lt;/p&gt;

&lt;p&gt;If you have two separate drives, one SSD and one a spinning hard drive, we recommend installing Ubuntu 24.04 LTS on the SSD and then setting up LVM on the spinning hard drive(s), and we will configure ZoneMinder to use those hard drives for the storage of video files.&lt;/p&gt;

&lt;p&gt;Guides for installing Ubuntu can be found &lt;a href=&quot;https://ubuntu.com/server/docs&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;installing-zoneminder&quot;&gt;INSTALLING ZoneMinder&lt;/h2&gt;

&lt;p&gt;Once you have installed Ubuntu 24.04 LTS on your system and rebooted it, you can
now install ZoneMinder.&lt;/p&gt;

&lt;h5 id=&quot;install-apache-and-mariadb&quot;&gt;Install Apache and MariaDB&lt;/h5&gt;

&lt;p&gt;The first step is to install &lt;code class=&quot;highlighter-rouge&quot;&gt;apache2&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;mariadb&lt;/code&gt; from the repository.
Apache2 is a web server that will host the ZoneMinder code as a web application,
and MariaDB is the database that ZoneMinder will use.&lt;/p&gt;

&lt;p&gt;You will need &lt;code class=&quot;highlighter-rouge&quot;&gt;root&lt;/code&gt; access to do this, and for now we will use the &lt;code class=&quot;highlighter-rouge&quot;&gt;sudo&lt;/code&gt;
command.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;apache2 mariadb-server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;setup-users-in-mariadb&quot;&gt;Setup Users in MariaDB&lt;/h5&gt;

&lt;p&gt;We then setup a database user in MariaDB so that ZoneMinder can access it.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;### login as root&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;su -
&lt;span class=&quot;c&quot;&gt;#### login to mariadb&lt;/span&gt;
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;mariadb
&lt;span class=&quot;c&quot;&gt;#### create the database that ZoneMinder needs&lt;/span&gt;
mariadb&amp;gt; CREATE DATABASE zm&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#### create a new user zmuser and a custom password for it&lt;/span&gt;
mariadb&amp;gt; CREATE USER zmuser@localhost IDENTIFIED BY &lt;span class=&quot;s1&quot;&gt;'change_this_passw0rd'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#### grant all privileges&lt;/span&gt;
mariadb&amp;gt; GRANT ALL PRIVILEGES ON zm.&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; TO zmuser@localhost&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
mariadb&amp;gt; FLUSH PRIVILEGES&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#### exit&lt;/span&gt;
mariadb&amp;gt; &lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Make sure you note down what password you chose for the &lt;code class=&quot;highlighter-rouge&quot;&gt;zmuser&lt;/code&gt;
account as you will need to edit a configuration file later with that password.&lt;/p&gt;

&lt;h5 id=&quot;install-zoneminder&quot;&gt;Install ZoneMinder&lt;/h5&gt;

&lt;p&gt;Now we install the &lt;code class=&quot;highlighter-rouge&quot;&gt;zoneminder&lt;/code&gt; package from the Ubuntu 24.04 LTS repository. As
of this writing the version &lt;code class=&quot;highlighter-rouge&quot;&gt;1.36.33&lt;/code&gt; gets installed from the Ubuntu package
repositories. If you need newer versions please refer to the &lt;a href=&quot;https://www.zoneminder.com&quot;&gt;ZoneMinder
website&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;zoneminder
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo chown&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; www-data:www-data /usr/share/zoneminder/www/api
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;configure-zoneminder-db-password&quot;&gt;Configure ZoneMinder DB Password&lt;/h5&gt;

&lt;p&gt;Now you need to configure the MariaDB password you had set above for &lt;code class=&quot;highlighter-rouge&quot;&gt;zmuser&lt;/code&gt; in
the &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/zm/zm.conf&lt;/code&gt; file. Change the variable &lt;code class=&quot;highlighter-rouge&quot;&gt;ZM_DB_PASS=zmpass&lt;/code&gt; to be
&lt;code class=&quot;highlighter-rouge&quot;&gt;ZM_DB_PASS=change_this_passw0rd&lt;/code&gt; or whatever password you had chosen and save
it.&lt;/p&gt;

&lt;h5 id=&quot;configure-php-time-zone&quot;&gt;Configure PHP Time Zone&lt;/h5&gt;

&lt;p&gt;On Ubuntu 24.04 LTS, as of this writing, the PHP version is 8.3.&lt;/p&gt;

&lt;p&gt;Edit &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/php/8.3/apache2/php.ini&lt;/code&gt; and change the &lt;code class=&quot;highlighter-rouge&quot;&gt;date.timezone&lt;/code&gt; field to
point to your time zone. Our timezone is &lt;code class=&quot;highlighter-rouge&quot;&gt;America/New_York&lt;/code&gt; so we set it to the
below. You may have to uncomment the default UTC timezone and change it.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;date.timezone = America/New_York
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;configure-apache2&quot;&gt;Configure Apache2&lt;/h5&gt;

&lt;p&gt;When you installed ZoneMinder above using &lt;code class=&quot;highlighter-rouge&quot;&gt;apt&lt;/code&gt;, the file
&lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/apache2/conf-available/zoneminder.conf&lt;/code&gt; was created as part of it.&lt;/p&gt;

&lt;p&gt;Edit this file and make it look like this so that all functionality regarding
the API and the web interface work as expected:&lt;/p&gt;

&lt;div class=&quot;language-conf highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ScriptAlias&lt;/span&gt; /&lt;span class=&quot;n&quot;&gt;zm&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;cgi&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/usr/lib/zoneminder/cgi-bin&quot;&lt;/span&gt;
&amp;lt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/usr/lib/zoneminder/cgi-bin&quot;&lt;/span&gt;&amp;gt;
    &lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt; +&lt;span class=&quot;n&quot;&gt;ExecCGI&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;MultiViews&lt;/span&gt; +&lt;span class=&quot;n&quot;&gt;SymLinksIfOwnerMatch&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AllowOverride&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;All&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Require&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;granted&lt;/span&gt;
&amp;lt;/&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&amp;gt;

&lt;span class=&quot;c&quot;&gt;# Order matters. This alias must come first.
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; /&lt;span class=&quot;n&quot;&gt;zm&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/var/cache/zoneminder&quot;&lt;/span&gt;
&amp;lt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/var/cache/zoneminder&quot;&lt;/span&gt;&amp;gt;
    &lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;Indexes&lt;/span&gt; +&lt;span class=&quot;n&quot;&gt;FollowSymLinks&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AllowOverride&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;
    &amp;lt;&lt;span class=&quot;n&quot;&gt;IfModule&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mod_authz_core&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&amp;gt;
       &lt;span class=&quot;c&quot;&gt;# Apache 2.4
&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;Require&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;granted&lt;/span&gt;
    &amp;lt;/&lt;span class=&quot;n&quot;&gt;IfModule&lt;/span&gt;&amp;gt;
&amp;lt;/&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&amp;gt;

&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; /&lt;span class=&quot;n&quot;&gt;zm&lt;/span&gt; /&lt;span class=&quot;n&quot;&gt;usr&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;share&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;zoneminder&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;www&lt;/span&gt;
&amp;lt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt; /&lt;span class=&quot;n&quot;&gt;usr&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;share&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;zoneminder&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;www&lt;/span&gt;&amp;gt;
  &lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;Indexes&lt;/span&gt; +&lt;span class=&quot;n&quot;&gt;FollowSymLinks&lt;/span&gt;
  &amp;lt;&lt;span class=&quot;n&quot;&gt;IfModule&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mod_dir&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&amp;gt;
    &lt;span class=&quot;n&quot;&gt;DirectoryIndex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;php&lt;/span&gt;
  &amp;lt;/&lt;span class=&quot;n&quot;&gt;IfModule&lt;/span&gt;&amp;gt;
&amp;lt;/&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&amp;gt;

&lt;span class=&quot;c&quot;&gt;# https://wiki.zoneminder.com/API#Example_Configuration_for_%2Fetc%2Fapache2%2Fconf-enabled%2Fzoneminder.conf
# For better visibility, the following directives have been migrated from the
# default .htaccess files included with the CakePHP project.
# Parameters not set here are inherited from the parent directive above.
&lt;/span&gt;&amp;lt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/usr/share/zoneminder/www/api&quot;&lt;/span&gt;&amp;gt;
   &lt;span class=&quot;n&quot;&gt;RewriteEngine&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;RewriteRule&lt;/span&gt; ^$ &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;webroot&lt;/span&gt;/ [&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;]
   &lt;span class=&quot;n&quot;&gt;RewriteRule&lt;/span&gt; (.*) &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;webroot&lt;/span&gt;/$&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; [&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;]
   &lt;span class=&quot;n&quot;&gt;RewriteBase&lt;/span&gt; /&lt;span class=&quot;n&quot;&gt;zm&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;
&amp;lt;/&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&amp;gt;

&amp;lt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/usr/share/zoneminder/www/api/app&quot;&lt;/span&gt;&amp;gt;
   &lt;span class=&quot;n&quot;&gt;RewriteEngine&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;RewriteRule&lt;/span&gt; ^$ &lt;span class=&quot;n&quot;&gt;webroot&lt;/span&gt;/ [&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;]
   &lt;span class=&quot;n&quot;&gt;RewriteRule&lt;/span&gt; (.*) &lt;span class=&quot;n&quot;&gt;webroot&lt;/span&gt;/$&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; [&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;]
   &lt;span class=&quot;n&quot;&gt;RewriteBase&lt;/span&gt; /&lt;span class=&quot;n&quot;&gt;zm&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;
&amp;lt;/&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&amp;gt;

&amp;lt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/usr/share/zoneminder/www/api/app/webroot&quot;&lt;/span&gt;&amp;gt;
    &lt;span class=&quot;n&quot;&gt;RewriteEngine&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;On&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RewriteCond&lt;/span&gt; %{&lt;span class=&quot;n&quot;&gt;REQUEST_FILENAME&lt;/span&gt;} !-&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RewriteCond&lt;/span&gt; %{&lt;span class=&quot;n&quot;&gt;REQUEST_FILENAME&lt;/span&gt;} !-&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RewriteRule&lt;/span&gt; ^ &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;php&lt;/span&gt; [&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;]
    &lt;span class=&quot;n&quot;&gt;RewriteBase&lt;/span&gt; /&lt;span class=&quot;n&quot;&gt;zm&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;
&amp;lt;/&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we enable the configurations in Apache2:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;su -
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;a2enmod cgi
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;a2enmod rewrite
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;a2enmod expires
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;a2enmod headers
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;a2enconf zoneminder
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;systemctl restart apache2.server
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;systemctl &lt;span class=&quot;nb&quot;&gt;enable &lt;/span&gt;zoneminder.service
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;systemctl restart zoneminder.service
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;systemctl status zoneminder.service
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;netstat &lt;span class=&quot;nt&quot;&gt;-vnatp&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;apache2
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;web-interface&quot;&gt;Web Interface&lt;/h4&gt;

&lt;p&gt;Now the web interface is setup and running, and you should be able to access it
at &lt;code class=&quot;highlighter-rouge&quot;&gt;http://localhost/zm/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once you are able to access it via the web browser, you can configure it for
your needs by following the &lt;a href=&quot;https://zoneminder.readthedocs.io/en/stable/&quot;&gt;ZoneMinder documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;configuring-additional-drives&quot;&gt;Configuring Additional Drives&lt;/h3&gt;

&lt;p&gt;If you chose the setup that we chose, where you install Ubuntu 24.04 LTS on an
SSD and have several spinning hard drives where you want to save all the video
files to, you have to follow the below steps.&lt;/p&gt;

&lt;p&gt;We assume the external drives are setup using LVM. If you have not done that,
then you can follow an LVM tutorial &lt;a href=&quot;https://ubuntu.com/server/docs/about-logical-volume-management-lvm&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once your drive(s) have been created, mounted and available, make sure they are
mounted at &lt;code class=&quot;highlighter-rouge&quot;&gt;/zoneminder&lt;/code&gt; or a similar folder. For this example, we mounted the
logical volumes at &lt;code class=&quot;highlighter-rouge&quot;&gt;/zoneminder&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;su -
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; /zoneminder
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /zoneminder/events
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /zoneminder/images
root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chown&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; www-data:www-data /zoneminder
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Edit the &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/zm/conf.d/01-system-paths.conf&lt;/code&gt; or similarly named file where the
variable &lt;code class=&quot;highlighter-rouge&quot;&gt;ZM_DIR_EVENTS&lt;/code&gt; is defined and change it to point to the folder created above. By default, it will point to &lt;code class=&quot;highlighter-rouge&quot;&gt;/var/cache/zoneminder/events&lt;/code&gt; and we change it to &lt;code class=&quot;highlighter-rouge&quot;&gt;/zoneminder/events&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ZM_DIR_EVENTS=/zoneminder/events
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Restart everything.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;systemctl restart zoneminder
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sun, 06 Oct 2024 00:00:00 -0400</pubDate>
        <link>https://www.stealthylabs.com/blog/2024/10/06/how-to-setup-zoneminder.html</link>
        <guid isPermaLink="true">https://www.stealthylabs.com/blog/2024/10/06/how-to-setup-zoneminder.html</guid>
        
        <category>surveillance</category>
        
        <category>zoneminder</category>
        
        
        <category>Surveillance</category>
        
      </item>
    
      <item>
        <title>Using libev and libssd1306 to display a clock</title>
        <description>&lt;p&gt;In the &lt;a href=&quot;/blog/2020/03/31/oled-ssd1306-drawtext-updated.html&quot;&gt;earlier post&lt;/a&gt; demonstrating the use of &lt;a href=&quot;https://github.com/stealthylabs/libssd1306&quot;&gt;libssd1306&lt;/a&gt;, we did not explain how one would use an event library such as &lt;code class=&quot;highlighter-rouge&quot;&gt;libev&lt;/code&gt; to write an application that would use events to perform display changes. With this post we would like to show how to do that easily with a simple clock application that updates the OLED screen with the local time every second.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h2 id=&quot;using-with-libev&quot;&gt;USING WITH &lt;code class=&quot;highlighter-rouge&quot;&gt;libev&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;The example code is present in &lt;code class=&quot;highlighter-rouge&quot;&gt;examples/libev_clock.c&lt;/code&gt; and is another
application to test if the OLED screen is working or not. You can run it as
below:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./examples/test_libev_clock
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will run the application which will show that every second the local time in the
format &lt;code class=&quot;highlighter-rouge&quot;&gt;HH:MM:SS&lt;/code&gt; is being sent to the OLED screen using the I&lt;sup&gt;2&lt;/sup&gt;C
interface on the screen. With &lt;code class=&quot;highlighter-rouge&quot;&gt;libssd1306&lt;/code&gt; it is incredibly easy to make this
display without handling any I&lt;sup&gt;2&lt;/sup&gt;C commands yourself or even reading the
datasheets.&lt;/p&gt;

&lt;h2 id=&quot;understanding-the-code&quot;&gt;UNDERSTANDING THE CODE&lt;/h2&gt;

&lt;p&gt;The code is well commented but we explain it in sections here.&lt;/p&gt;

&lt;h4 id=&quot;initializing-the-oled-screen&quot;&gt;Initializing the OLED screen&lt;/h4&gt;

&lt;p&gt;The first part of the &lt;code class=&quot;highlighter-rouge&quot;&gt;main()&lt;/code&gt; function involves initializing the I&lt;sup&gt;2&lt;/sup&gt;C
device and initializing it. By default the device is assumed to be &lt;code class=&quot;highlighter-rouge&quot;&gt;/dev/i2c-1&lt;/code&gt;
on the Raspberry Pi and the width x height is assumed to be 128x32 pixels.
However, the user can pass the device path and the height as the command line
arguments like below, for instance if they want to use a 128x64 pixel screen.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./examples/test_libev_clock /dev/i2c-1 64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The initialization code will fail if the device path is incorrect or if the
device is disconnected or other I&lt;sup&gt;2&lt;/sup&gt;C failure has occurred. The
application will exit if it cannot find the OLED screen on the I&lt;sup&gt;2&lt;/sup&gt;C
bus of the Raspberry Pi.&lt;/p&gt;

&lt;p&gt;In addition to initializing the OLED screen, we also have to create a
framebuffer object so that we can draw on the screen. Recall, from the &lt;a href=&quot;/blog/2020/03/31/oled-ssd1306-drawtext-updated.html&quot;&gt;earlier
post&lt;/a&gt;, that we use a framebuffer object so that we can fill the screen contents
in memory first before actually displaying it to the screen. This way we can
support multiple framebuffer objects and are able to build several screens even
before they have been displayed. This can be very useful for displaying games and motion
picture images on the OLED screen. The I&lt;sup&gt;2&lt;/sup&gt;C interface is slow, and
being able to hold a lot of pre-drawn screens in memory can be very useful for
good user experience.&lt;/p&gt;

&lt;p&gt;Below is the section of the &lt;code class=&quot;highlighter-rouge&quot;&gt;main()&lt;/code&gt; function doing the initialization of the
display and also the creation of the framebuffer.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;    ssd1306_i2c_t *oled = ssd1306_i2c_open(filename, 0x3c, 128, (uint8_t)height, NULL);
    if (!oled) {
        return -1;
    }
    /* initialize the I2C device */
    if (ssd1306_i2c_display_initialize(oled) &amp;lt; 0) {
        fprintf(stderr, &quot;ERROR: Failed to initialize the display. Check if it is connected !\n&quot;);
        ssd1306_i2c_close(oled);
        return -1;
    }
    /* clear the display */
    ssd1306_i2c_display_clear(oled);
    /* create a framebuffer */
    ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(oled-&amp;gt;width, oled-&amp;gt;height, oled-&amp;gt;err);
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;setting-up-the-event-loop&quot;&gt;Setting up the event loop&lt;/h4&gt;

&lt;p&gt;We now need to setup the event loop and timer callback. We want the event loop
to invoke a callback every 1 second. For our example code, we also have it
timeout after 30 seconds so that we can run this application as part of our test
suite by running &lt;code class=&quot;highlighter-rouge&quot;&gt;make check&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The idea is that the callback will then calculate the local time in &lt;code class=&quot;highlighter-rouge&quot;&gt;HH:MM:SS&lt;/code&gt; format, print it to the
framebuffer and then display the framebuffer on the OLED screen all at once in
the callback.&lt;/p&gt;

&lt;p&gt;However, you may notice that the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306&lt;/code&gt; object is in the &lt;code class=&quot;highlighter-rouge&quot;&gt;main()&lt;/code&gt; function
and we need to make it available for the callback. One way is to make the object
global, but we have used the &lt;code class=&quot;highlighter-rouge&quot;&gt;void *data&lt;/code&gt; member of the &lt;code class=&quot;highlighter-rouge&quot;&gt;ev_timer&lt;/code&gt; object to
pass the pointers we need into the callback. This allows us to make the callback
re-entrant.&lt;/p&gt;

&lt;p&gt;A single &lt;code class=&quot;highlighter-rouge&quot;&gt;void *data&lt;/code&gt; maybe good if we want to pass a single pointer variable, but if we
want to pass multiple variables to the callback we need to create a local
&lt;code class=&quot;highlighter-rouge&quot;&gt;struct&lt;/code&gt; with all the variables and then pass a pointer to that &lt;code class=&quot;highlighter-rouge&quot;&gt;struct&lt;/code&gt; to the
callback. We do that by creating our own &lt;code class=&quot;highlighter-rouge&quot;&gt;i2c_clock_t&lt;/code&gt; structure in the code.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;typedef struct {
    ssd1306_i2c_t *oled; /* the libssd1306 I2C object */
    ssd1306_framebuffer_t *fbp; /* the framebuffer object */
    int call_count; /* for calculating the 30 second timeout */
} i2c_clock_t;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then we create an object instance of this &lt;code class=&quot;highlighter-rouge&quot;&gt;struct&lt;/code&gt; in the &lt;code class=&quot;highlighter-rouge&quot;&gt;main()&lt;/code&gt; function,
followed by the setup of the event loop and the timer object. We initialize the
timer object, set the data pointer and call start on the timer to run each
second.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;    /* create an object to send to the callbacks */
    i2c_clock_t timer_data = {
        .oled = oled, /* the object created earlier */
        .fbp = fbp, /* created earlier */
        .call_count = 30 /* timeout after 30 seconds */
    };

    /* create the loop variable by using the default */
    struct ev_loop *loop = EV_DEFAULT;

    /* create the timer event object */
    ev_timer timer_watcher = { 0 };

    /* initialize the timer to update each second and invoke callback */
    /* here the callback name is onesec_timer_cb */
    ev_timer_init(&amp;amp;timer_watcher, onesec_timer_cb, 1., 1.);

    /* set the data pointer for the callback to use it */
    timer_watcher.data = &amp;amp;timer_data;

    /* start the timer */
    ev_timer_start(loop, &amp;amp;timer_watcher);
    ev_run(loop, 0);

    /* cleanup before exiting main() */
    ssd1306_framebuffer_destroy(fbp);
    ssd1306_i2c_close(oled);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the end, following the &lt;code class=&quot;highlighter-rouge&quot;&gt;ev_run&lt;/code&gt; call in the &lt;code class=&quot;highlighter-rouge&quot;&gt;main()&lt;/code&gt; function, we also cleanup the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_*&lt;/code&gt; objects.&lt;/p&gt;

&lt;h4 id=&quot;defining-the-timer-callback&quot;&gt;Defining the timer callback&lt;/h4&gt;

&lt;p&gt;As seen above, the timer callback is called &lt;code class=&quot;highlighter-rouge&quot;&gt;onesec_timer_cb&lt;/code&gt; which follows the
&lt;code class=&quot;highlighter-rouge&quot;&gt;libev&lt;/code&gt; structure of callbacks for all its event types. We reproduce it below.&lt;/p&gt;

&lt;p&gt;The callback first calculates the local time using &lt;code class=&quot;highlighter-rouge&quot;&gt;localtime_r()&lt;/code&gt; function from
the C library. We choose this function since it is re-entrant and it makes sense
to use a re-entrant function in the callback, especially if you are going to be
doing multi-threading. Then we print the time to a buffer of 16 bytes, even
though the format &lt;code class=&quot;highlighter-rouge&quot;&gt;HH:MM:SS&lt;/code&gt; needs no more than 8-bytes but just to be safe we use 16.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;w&lt;/code&gt; argument is the &lt;code class=&quot;highlighter-rouge&quot;&gt;ev_timer&lt;/code&gt; object that we created in the &lt;code class=&quot;highlighter-rouge&quot;&gt;main()&lt;/code&gt;
function, and will have the &lt;code class=&quot;highlighter-rouge&quot;&gt;void *data&lt;/code&gt; member point to the &lt;code class=&quot;highlighter-rouge&quot;&gt;timer_data&lt;/code&gt; object of type &lt;code class=&quot;highlighter-rouge&quot;&gt;i2c_clock_t&lt;/code&gt; that we had created above.
So we cast the &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; pointer to the &lt;code class=&quot;highlighter-rouge&quot;&gt;i2c_clock_t *&lt;/code&gt; type and access the
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306&lt;/code&gt; objects for the OLED screen and the framebuffer.&lt;/p&gt;

&lt;p&gt;We then print the text buffer to the framebuffer object using the default font,
and then push it via I&lt;sup&gt;2&lt;/sup&gt;C to the OLED screen so that it displays the
time in &lt;code class=&quot;highlighter-rouge&quot;&gt;HH:MM:SS&lt;/code&gt; format.&lt;/p&gt;

&lt;p&gt;For the timeout, we count down until we time out, so after 30 seconds.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;void onesec_timer_cb(EV_P_ ev_timer *w, int revents)
{
    /* calculate the local time */
    struct tm _tm = { 0 };
    time_t _tsec = time(NULL);
    localtime_r(&amp;amp;_tsec, &amp;amp;_tm);

    /* print it to a buffer safely */
    char buf[16] = { 0 };
    snprintf(buf, sizeof(buf) - 1, &quot;%02d:%02d:%02d&quot;, _tm.tm_hour, _tm.tm_min,
_tm.tm_sec);
    
    /* log the time to console to verify that it is correct */
    printf(&quot;INFO: Time is %s\n&quot;, buf);

    if (w) {
        /* retrieve the object that we set earlier in main() */
        i2c_clock_t *i2c = (i2c_clock_t *)(w-&amp;gt;data);

        /* write the time buffer to framebuffer first */
        ssd1306_framebuffer_clear(i2c-&amp;gt;fbp);
        ssd1306_framebuffer_box_t bbox;
        ssd1306_framebuffer_draw_text(i2c-&amp;gt;fbp, buf, 0, 32, 16, SSD1306_FONT_DEFAULT, 4, &amp;amp;bbox);

        /* update the OLED screen with the framebuffer */
        if (ssd1306_i2c_display_update(i2c-&amp;gt;oled, i2c-&amp;gt;fbp) &amp;lt; 0) {
            fprintf(stderr, &quot;ERROR: failed to update I2C display, exiting...\n&quot;);
            ev_break(EV_A_ EVBREAK_ALL);
        }

        /* count down until we time out */
        if ((i2c-&amp;gt;call_count--) &amp;lt;= 0) {
            ev_break(EV_A_ EVBREAK_ALL);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;demonstration&quot;&gt;DEMONSTRATION&lt;/h2&gt;

&lt;p&gt;Here is a short video of the demo at &lt;a href=&quot;https://youtu.be/T_ox1At1x-o&quot;&gt;https://youtu.be/T_ox1At1x-o&lt;/a&gt;.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/T_ox1At1x-o&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

</description>
        <pubDate>Sun, 09 Aug 2020 00:00:00 -0400</pubDate>
        <link>https://www.stealthylabs.com/blog/2020/08/09/oled-ssd1306-libev-clock.html</link>
        <guid isPermaLink="true">https://www.stealthylabs.com/blog/2020/08/09/oled-ssd1306-libev-clock.html</guid>
        
        <category>libssd1306</category>
        
        <category>graphics</category>
        
        <category>i2c</category>
        
        <category>raspberrypi</category>
        
        
        <category>Graphics</category>
        
        <category>SSD1306</category>
        
      </item>
    
      <item>
        <title>C Library for Reading GPS data from a GlobalTop MTK3339</title>
        <description>&lt;p&gt;We purchased the &lt;a href=&quot;https://www.adafruit.com/product/746&quot;&gt;Adafruit Ultimate GPS breakout board&lt;/a&gt; (&lt;em&gt;Figure 1&lt;/em&gt;) several months ago, but finally got to the point of using it for something. This GPS breakout uses a GlobalTop MTK3339 GPS chip, comes with an SMA connector
antenna, and also requires an SMA to uFL adapter (&lt;em&gt;Figure 2&lt;/em&gt;) also available at &lt;a href=&quot;https://www.adafruit.com/product/851&quot;&gt;Adafruit&lt;/a&gt;. You will also require an &lt;a href=&quot;https://www.ftdichip.com/Products/Cables/USBTTLSerial.htm&quot;&gt;FTDI USB-to-TTL serial cable&lt;/a&gt;, which can be available at various vendors like Sparkfun, Adafruit, Amazon, DigiKey, etc.&lt;/p&gt;

&lt;p&gt;To read the GPS data as fast as possible using the &lt;code class=&quot;highlighter-rouge&quot;&gt;libev&lt;/code&gt; event API, we chose
to write a C library from scratch as opposed to using a server like &lt;code class=&quot;highlighter-rouge&quot;&gt;gpsd&lt;/code&gt;. We
want our application to embed the reading of the GPS and display the GPS data to
the OLED library using our &lt;a href=&quot;https://github.com/stealthylabs/libssd1306&quot;&gt;libssd1306&lt;/a&gt; &lt;a href=&quot;/blog/2020/03/31/oled-ssd1306-drawtext-updated.html&quot;&gt;library&lt;/a&gt;.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libgps_mtk3339/adafruit_746.jpg&quot; alt=&quot;Adafruit Ultimate GPS Breakout Board&quot; /&gt;
&lt;small&gt;&lt;em&gt;Figure 1. Adafruit Ultimate GPS Breakout Board&lt;/em&gt; © Adafruit Industries&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libgps_mtk3339/adafruit_851.jpg&quot; alt=&quot;Adafruit SMA to uFL Connector&quot; /&gt;
&lt;small&gt;&lt;em&gt;Figure 2. Adafruit SMA to uFL Connector&lt;/em&gt; © Adafruit Industries&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;The code in this post is as of &lt;code class=&quot;highlighter-rouge&quot;&gt;git tag 0.2&lt;/code&gt; or commit &lt;code class=&quot;highlighter-rouge&quot;&gt;fd14b8884510bc42c55127890d9bdd51bc1473a5&lt;/code&gt; from &lt;a href=&quot;https://github.com/stealthylabs/libgps_mtk3339&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;datasheet-links&quot;&gt;DATASHEET LINKS&lt;/h2&gt;

&lt;p&gt;The datasheets for the GPS chip (version &lt;em&gt;PA6H&lt;/em&gt;) on the breakout board (version 3) can be found at &lt;a href=&quot;https://cdn-shop.adafruit.com/datasheets/GlobalTop-FGPMMOPA6H-Datasheet-V0A.pdf&quot;&gt;Adafruit’s website&lt;/a&gt; and we have mirrored it &lt;a href=&quot;/blog/files/gps/GlobalTop-FGPMMOPA6H-Datasheet-V0A.pdf&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The GPS chip also accepts custom commands that start with the string &lt;code class=&quot;highlighter-rouge&quot;&gt;$PMTK&lt;/code&gt; and
the details of the commands can be found at
&lt;a href=&quot;https://cdn-shop.adafruit.com/datasheets/PMTK_A11.pdf&quot;&gt;Adafruit&lt;/a&gt; and we have
mirrored it &lt;a href=&quot;/blog/files/gps/PMTK_A11.pdf&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;connecting-the-gps-breakout-board&quot;&gt;CONNECTING THE GPS BREAKOUT BOARD&lt;/h2&gt;

&lt;h3 id=&quot;using-the-usb-to-ttl-cable&quot;&gt;Using the USB-to-TTL Cable&lt;/h3&gt;

&lt;p&gt;The easiest way to connect and test the GPS breakout board is by using the &lt;a href=&quot;https://www.ftdichip.com/Products/Cables/USBTTLSerial.htm&quot;&gt;FTDI USB-to-TTL serial cable&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Connecting the cable has to be done using the wiring in &lt;em&gt;Figure 3&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libgps_mtk3339/adafruit_gps_usbttl.png&quot; alt=&quot;FTDI USB-to-TTL Cable Connecting to GPS breakout board&quot; /&gt;
&lt;small&gt;&lt;em&gt;Figure 3. How to connect the FTDI USB-to-TTL Cable Connector to the Ultimate GPS Breakout board&lt;/em&gt; © Adafruit Industries&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;You will need some extra connecting wires to be able to connect the USB-to-TTL
cable’s connectors to the GPS breakout board’s connecting pins.&lt;/p&gt;

&lt;h3 id=&quot;connecting-via-gpio-pins-on-raspberry-pi&quot;&gt;Connecting via GPIO Pins on Raspberry Pi&lt;/h3&gt;

&lt;p&gt;If you want to directly connect the GPS breakout board to a Raspberry Pi or
Beaglebone Black UART GPIO pins, you have to perform the wiring as in &lt;em&gt;Figure 4&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libgps_mtk3339/adafruit_gps_uart.png&quot; alt=&quot;UART GPIO pins to GPS breakout board&quot; /&gt;
&lt;small&gt;&lt;em&gt;Figure 4. How to connect the UART GPIO Pins on a Raspberry Pi to the Ultimate GPS Breakout board&lt;/em&gt; © Adafruit Industries&lt;/small&gt;&lt;/p&gt;

&lt;h2 id=&quot;compiling-the-library&quot;&gt;COMPILING THE LIBRARY&lt;/h2&gt;

&lt;p&gt;We have made it as simple as possible to compile the library.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;## install pre-requisites&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;ragel libev-dev build-essential &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    autoconf automake autotools-dev libtool

&lt;span class=&quot;c&quot;&gt;## clone the library source code&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git clone https://github.com/stealthylabs/libgps_mtk3339
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git submodule init
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git submodule update

&lt;span class=&quot;c&quot;&gt;## change to the source directory&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;libgps_mtk3339

&lt;span class=&quot;c&quot;&gt;## run the autotools&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./autogen.sh

&lt;span class=&quot;c&quot;&gt;## run configure in release mode&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./configure

&lt;span class=&quot;c&quot;&gt;## run make and make sure unit tests run&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;make &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; make check

&lt;span class=&quot;c&quot;&gt;## optionally run make install if you want to install the library&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;make &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;reading-the-data&quot;&gt;READING THE DATA&lt;/h2&gt;

&lt;p&gt;This library’s main purpose is to be able to seamlessly, and with the least amount of effort, be able to read data from the MTK3339 GPS chip made by GlobalTop. Here for example, we are using the open source AdaFruit Ultimate GPS breakout board, but you could be using your own custom board with this chip and want to read the data faster than a Python script could.&lt;/p&gt;

&lt;p&gt;That is why we have written this parser using one of the best parser generator
tools, &lt;code class=&quot;highlighter-rouge&quot;&gt;ragel&lt;/code&gt;, so that we can parse each individual NMEA sentence sent by the
GPS in under 6-10 &lt;strong class=&quot;text-danger&quot;&gt;microseconds&lt;/strong&gt; depending on the client computer.&lt;/p&gt;

&lt;p&gt;The library is also re-entrant so that the user can use event libraries like
&lt;code class=&quot;highlighter-rouge&quot;&gt;libev&lt;/code&gt; or just a &lt;code class=&quot;highlighter-rouge&quot;&gt;select()&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;epoll()&lt;/code&gt; or a blocking &lt;code class=&quot;highlighter-rouge&quot;&gt;read()&lt;/code&gt; function call
to read the data from the GPS chip when connected in any of the above two ways,
shown in &lt;em&gt;Figures 3 and 4&lt;/em&gt;. One such example we explain below.&lt;/p&gt;

&lt;h3 id=&quot;example-with-libev&quot;&gt;Example with &lt;code class=&quot;highlighter-rouge&quot;&gt;libev&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;We provide a usable example that prints location information on screen using
&lt;code class=&quot;highlighter-rouge&quot;&gt;libev&lt;/code&gt;. This example can be found in &lt;code class=&quot;highlighter-rouge&quot;&gt;src/libev_uart.c&lt;/code&gt; file. In this section,
we go over some important portions of the code to help the user understand how
to use the library.&lt;/p&gt;

&lt;p&gt;To run the example, you can do the following after compiling the code:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./src/libev_uart_gps
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This expects the device to be connected as &lt;code class=&quot;highlighter-rouge&quot;&gt;/dev/ttyUSB0&lt;/code&gt;. You can also specify
your own device path on the command line:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./src/libev_uart_gps /dev/serial0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This example automatically exits after 10 seconds. However, sometimes the GPS
chip may take more than 10 seconds to get a fix, so you can set the example to
run without the timeout by setting the &lt;code class=&quot;highlighter-rouge&quot;&gt;NO_TIMEOUT&lt;/code&gt; environment variable as
shown below. You can then press &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl C&lt;/code&gt; to interrupt the running application.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ NO_TIMEOUT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1 ./src/libev_uart_gps
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;header-files&quot;&gt;Header Files&lt;/h4&gt;

&lt;p&gt;The user only needs to include the &lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdata.h&lt;/code&gt; header file which indirectly includes
all the other dependency files such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsconfig.h&lt;/code&gt;: has the constants based on the build system&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsutils.h&lt;/code&gt;: has some utility functions such as for opening the device and
setting the defaults&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gps_utlist.h&lt;/code&gt;: we provide a copy of the build version of &lt;code class=&quot;highlighter-rouge&quot;&gt;utlist.h&lt;/code&gt; self-contained header/library from the &lt;a href=&quot;https://troydhanson.github.io/uthash/utlist.html&quot;&gt;utlist&lt;/a&gt; project. We use this to return a linked-list of parsed objects that the user can iterate through.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this example, the user needs to also include &lt;code class=&quot;highlighter-rouge&quot;&gt;ev.h&lt;/code&gt; to use &lt;code class=&quot;highlighter-rouge&quot;&gt;libev&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;gpsdata.h&amp;gt;
/* ... other headers like errno.h, fcntl.h, stdio.h etc.... */
#ifdef HAVE_EV_H
    #include &amp;lt;ev.h&amp;gt;
#endif
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;logging&quot;&gt;Logging&lt;/h4&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;gpsutils.h&lt;/code&gt; file defines a set of macros for performing conditional logging
at various levels. By default, the logging happens to &lt;code class=&quot;highlighter-rouge&quot;&gt;stderr&lt;/code&gt;. The default log
level is set to &lt;code class=&quot;highlighter-rouge&quot;&gt;INFO&lt;/code&gt; and the user can change it using the
&lt;code class=&quot;highlighter-rouge&quot;&gt;GPSUTILS_LOGLEVEL_SET()&lt;/code&gt; macro as shown below.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;// set the log level to DEBUG
GPSUTILS_LOGLEVEL_SET(DEBUG);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The user can also either reopen the &lt;code class=&quot;highlighter-rouge&quot;&gt;stderr&lt;/code&gt; to a different log file using
&lt;code class=&quot;highlighter-rouge&quot;&gt;freopen()&lt;/code&gt;, or perform the following C-language macro-magic to set it to their
favorite global &lt;code class=&quot;highlighter-rouge&quot;&gt;FILE *&lt;/code&gt; variable, which they can set up later. &lt;strong&gt;NOTE&lt;/strong&gt;: we do
not do this in the &lt;code class=&quot;highlighter-rouge&quot;&gt;src/libev_uart.c&lt;/code&gt; file, and this is for a special use case
where the user cannot use &lt;code class=&quot;highlighter-rouge&quot;&gt;freopen()&lt;/code&gt; on &lt;code class=&quot;highlighter-rouge&quot;&gt;stderr&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;/* use variable my_custom_log_ptr instead of stderr */
#define GPSUTILS_LOG_PTR my_custom_log_ptr
#include &amp;lt;gpsdata.h&amp;gt;

/* ... later in the code define the my_custom_log_ptr globally or locally to
every function before calling any GPSUTILS_LOG_* macro ... 
Here is a global pointer example
*/
FILE *my_custom_log_ptr = NULL;
/* ... main function ... */

int main(int argc, char **argv)
{
    my_custom_log_ptr = fopen(&quot;/path/to/log/file&quot;, &quot;+w&quot;);
    /* ... more code ... */
    GPSUTILS_INFO(&quot;Starting log file...\n&quot;);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;opening-the-device&quot;&gt;Opening the device&lt;/h4&gt;

&lt;p&gt;When using the USB-to-TTL cable on a Linux system, the device will show up as
&lt;code class=&quot;highlighter-rouge&quot;&gt;/dev/ttyUSB0&lt;/code&gt; or in the device file format &lt;code class=&quot;highlighter-rouge&quot;&gt;/dev/ttyUSB[0-9]&lt;/code&gt;. The
&lt;code class=&quot;highlighter-rouge&quot;&gt;src/libev_uart.c&lt;/code&gt; file assumes that the &lt;code class=&quot;highlighter-rouge&quot;&gt;/dev/ttyUSB0&lt;/code&gt; is the default, but also
accepts a command-line argument to change that default to another device path.&lt;/p&gt;

&lt;p&gt;When using the UART GPIO pins on a Raspberry Pi, the device path changes to
&lt;code class=&quot;highlighter-rouge&quot;&gt;/dev/serial&lt;/code&gt; or matches the  &lt;code class=&quot;highlighter-rouge&quot;&gt;/dev/serial[0-9]&lt;/code&gt; pattern.&lt;/p&gt;

&lt;p&gt;To open the device, we provide a wrapper function that also sets the default
chip baud rate to 9600 bps and the default NMEA sentences to GPRMC and GPGGA.
This function is the &lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdevice_open()&lt;/code&gt; function and it returns the file
descriptor after a successful open, or -1 on error. There is a corresponding
&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdevice_close()&lt;/code&gt; function that closes the open device. Below is a small
snippet of code from the &lt;code class=&quot;highlighter-rouge&quot;&gt;src/libev_uart.c&lt;/code&gt; file demonstrating the usage of
these functions. We open the device in non-blocking mode so that we can read the
data as fast as possible, but also handle other events when the data is not
coming in.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;int main (int argc, char **argv)
{
    /* ... */
    const char *dev = &quot;/dev/ttyUSB0&quot;;
    int device_fd = gpsdevice_open(dev, true /* non-blocking */);
    if (device_fd &amp;lt; 0) {
        return -1;
    } else {
        /* use the device_fd here */
    }
    gpsdevice_close(device_fd);
    /* ... */
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;creating-the-parser&quot;&gt;Creating the Parser&lt;/h4&gt;

&lt;p&gt;This library uses &lt;code class=&quot;highlighter-rouge&quot;&gt;ragel&lt;/code&gt;, which is a streaming parser that uses state machines
to parse data. It allows us to parse data read from the GPS chip, even if the
data is incomplete in the buffer until the next buffer can be read. Not only
that, it is also written in pure C which allows it to be really fast for our
needs.&lt;/p&gt;

&lt;p&gt;We encapsulate all the parser related code into an opaque object called
&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdata_parser_t&lt;/code&gt; and the user can create one using &lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdata_parser_create()&lt;/code&gt;.
Each device &lt;strong&gt;must&lt;/strong&gt; have its own &lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdata_parser_t&lt;/code&gt; object, as each file
descriptor will be reading data which may be at a different state. In general,
it is likely that you will be using only one GPS chip, but if you are using
more, you need to have one parser object per GPS chip.&lt;/p&gt;

&lt;p&gt;Once an array of bytes have been read from the GPS chip’s file descriptor, the
function that parses it is &lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdata_parser_parse()&lt;/code&gt; which then returns a
&lt;em&gt;linked list&lt;/em&gt; of &lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdata_data_t&lt;/code&gt; objects which may have location information,
antenna information, speed information, GPS chip firmware information and other
possible messages.&lt;/p&gt;

&lt;p&gt;The below code shows how to create the parser, and use the functions in blocking
mode without &lt;code class=&quot;highlighter-rouge&quot;&gt;libev&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;/* create the parser object */
gpsdata_parser_t *parser = gpsdata_parser_create();
/* open device */
int device_fd = gpsdevice_open(&quot;/dev/ttyUSB0&quot;, false /* blocking */);
/* a buffer pointer to read data */
char buf[80];
ssize_t nb = 0;
gpsdata_data_t *listp = NULL;
/* blocking read */
while ((nb = read(device_fd, buf, sizeof(buf))) &amp;gt; 0) {
    size_t onum = 0;
    /* parse the data that was read and store outputs in listp
     *  onum holds the number of items added to listp
     */
    if (gpsdata_parser_parse(parser, buf, nb, &amp;amp;listp, &amp;amp;onum) &amp;lt; 0) {
        /* if the buffer had bad data in it, we reset the parser state
         * and allow the parser to continue working to parse the next set of
         *  messages.
         */
        gpsdata_parser_reset(parser);
    }
    printf(&quot;Parsed %zu NMEA messages in the buffer\n&quot;, onum);
    /** do something with the listp data like store in a database or
     * update a display on screen etc.
     */
}
/* close the device */
gpsdevice_close(device_fd);
/* free the listp  object */
gpsdata_list_free(listp);
/* free the parser object */
gpsdata_parser_free(parser);

&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;sending-messages-to-the-gps-device&quot;&gt;Sending Messages to the GPS Device&lt;/h4&gt;

&lt;p&gt;We can also send several messages to the GPS device since it uses a UART
interface for receiving data. The messages that can be sent are found in the
&lt;a href=&quot;/blog/files/gps/PMTK_A11.pdf&quot;&gt;PMTK command datasheet&lt;/a&gt; (also linked above).&lt;/p&gt;

&lt;p&gt;We have taken most of the useful ones and wrapped them in their own functions,
and also provide a low level message send function that allows the user to send
their own custom message to the GPS device. All these functions begin with
&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdevice_&lt;/code&gt; and use the file descriptor returned by &lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdevice_open()&lt;/code&gt; to send
the messages to the GPS chip.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdevice_set_baudrate()&lt;/code&gt;: Allows the user to set a custom baudrate for the
GPS. The default value is set to be 9600 bps, and the other supported values
are 1200, 2400, 4800, 9600, 19200, 38400, 57600 and 115200. As of this
writing, we have only tested 9600 bps since that’s what the datasheet says we
should use. This function gets called with the 9600 default value in the
&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdevice_open()&lt;/code&gt; call, but the user can change that by calling it again.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdevice_set_restart()&lt;/code&gt;: Allows the user to perform different type of
restarts as specified in the PMTK document linked above &amp;amp;em; the warm start,
cold start and a &lt;em&gt;full&lt;/em&gt; cold start or a factory reset.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdevice_set_standby()&lt;/code&gt;: Sends a message to the GPS chip to be on standby.
To exit standby mode the user can send any message to the GPS chip, such as an
enable message or a firmware or antenna request or anything else.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdevice_set_fix_interval()&lt;/code&gt;: Tells the GPS the fix interval in milliseconds
that it should use. For a 9600 baud this should be 1000 milliseconds or 1
second. The minimum value is 100 and maximum value accepted is 10000
milliseconds.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdevice_set_enabled()&lt;/code&gt;: By default the only NMEA messages enabled are GPGGA and
GPRMC since they give us the location which is enough for most uses. The user
may want to enable other NMEA sentences like GPVTG, GPGSA and GPGSV. To do
that the user may want to use this function. As of this writing, GPGLL is
disabled at all times as it is unnecessary. We have tested enabling GPGLL, GPGGA
and GPRMC simultaneously and the value of the location is the same, hence have
deemed GPGLL unnecessary.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdevice_set_speed_threshold()&lt;/code&gt;: The navigation speed has a minimum accuracy
threshold that can be set here. By default, it is set to 0.2 m/s. The valid
values as per the data sheet are 0.2, 0.4, 0.6, 0.8, 1.0, 1.5 and 2.0 m/s. If
the user wants to disable the threshold they can use 0. If the speed is slower
than the threshold the location is considered constant or &lt;em&gt;frozen&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdevice_request_firmware_info()&lt;/code&gt;: The user can request the firmware version
that the GPS chip is running. The data gets returned and parsed by the
&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdata_parser_parse()&lt;/code&gt; function and the firmware can be read in the
&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdata_data_t&lt;/code&gt; object using the &lt;code class=&quot;highlighter-rouge&quot;&gt;fwinfo&lt;/code&gt; field, and the &lt;code class=&quot;highlighter-rouge&quot;&gt;msgid&lt;/code&gt; will be
&lt;code class=&quot;highlighter-rouge&quot;&gt;GPSDEVICE_MSGID_PMTK&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdevice_request_antenna_status()&lt;/code&gt;: The user can request antenna status of
the chip. If the user is using the Adafruit GPS breakout board like us, we are
using the external antenna, which will set the &lt;code class=&quot;highlighter-rouge&quot;&gt;antenna_status&lt;/code&gt; field in the
&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdata_data_t&lt;/code&gt; object as &lt;code class=&quot;highlighter-rouge&quot;&gt;GPSDATA_ANTENNA_ACTIVE&lt;/code&gt;. If the user is using the
chip’s internal antenna, the antenna status will be &lt;code class=&quot;highlighter-rouge&quot;&gt;GPSDATA_ANTENNA_INTERNAL&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gpsdevice_send_message()&lt;/code&gt;: This is the core message send function which is
used by all the wrappers internally. The user can set their own PMTK message
if any of our wrappers are insufficient for their use case.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;using-the-device-with-libev&quot;&gt;Using the Device with &lt;code class=&quot;highlighter-rouge&quot;&gt;libev&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;The sample application present in &lt;code class=&quot;highlighter-rouge&quot;&gt;src/libev_uart.c&lt;/code&gt; can be used as a starting
point for using &lt;code class=&quot;highlighter-rouge&quot;&gt;libev&lt;/code&gt; and the &lt;code class=&quot;highlighter-rouge&quot;&gt;libgps_mtk3339&lt;/code&gt; library. Remember to open the
device in &lt;em&gt;non-blocking&lt;/em&gt; mode and to create one parser per opened device.&lt;/p&gt;

&lt;p&gt;With this we come to the end of the introduction to the &lt;code class=&quot;highlighter-rouge&quot;&gt;libgps_mtk3339&lt;/code&gt;
library. Try it out instead of using the  &lt;code class=&quot;highlighter-rouge&quot;&gt;gpsd&lt;/code&gt; daemon or the Adafruit Python
libraries and let us know if you have any issues.&lt;/p&gt;
</description>
        <pubDate>Thu, 16 Apr 2020 00:00:00 -0400</pubDate>
        <link>https://www.stealthylabs.com/blog/2020/04/16/gps-mtk3339-library.html</link>
        <guid isPermaLink="true">https://www.stealthylabs.com/blog/2020/04/16/gps-mtk3339-library.html</guid>
        
        <category>libgps_mtk3339</category>
        
        <category>gps</category>
        
        <category>uart</category>
        
        <category>raspberrypi</category>
        
        <category>adafruit</category>
        
        <category>mtk3339</category>
        
        <category>globaltop</category>
        
        
        <category>Navigation</category>
        
        <category>GPS</category>
        
        <category>GlobalTop MTK3339</category>
        
        <category>AdaFruit Ultimate GPS</category>
        
      </item>
    
      <item>
        <title>Updated Drawing Text on OLED Chip SSD1306 Using FreeType2</title>
        <description>&lt;p&gt;We are updating the &lt;a href=&quot;/blog/2020/03/22/oled-ssd1306-drawtext.html&quot;&gt;previous post on drawing text&lt;/a&gt;, since we realized we needed to fix a major display bug in pixel locating the text. In addition to that we updated the API for drawing text to return a &lt;em&gt;bounding box&lt;/em&gt; around the drawn text so that the developer can know the pixel coordinates of where the text was drawn in the framebuffer. We also updated the bit-dump API to optionally add color to the terminal output.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;The code in this post is as of &lt;code class=&quot;highlighter-rouge&quot;&gt;git tag 0.6&lt;/code&gt; or commit &lt;code class=&quot;highlighter-rouge&quot;&gt;50e836f9299f61a3fe2901f794d2131ade7c23be&lt;/code&gt; from &lt;a href=&quot;https://github.com/stealthylabs/libssd1306&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;bitdump-color&quot;&gt;BITDUMP COLOR&lt;/h2&gt;

&lt;p&gt;The bitdump API now automatically colors the 0’s as blue and the 1’s as red in
the default &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_bitdump()&lt;/code&gt; function. If the user sets
&lt;code class=&quot;highlighter-rouge&quot;&gt;use_color&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; in the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_bitdump_custom()&lt;/code&gt; function the
colors are set to blue and red for 0 and 1, respectively. There is currently no
plan on supporting other color combinations.&lt;/p&gt;

&lt;h2 id=&quot;drawing-text&quot;&gt;DRAWING TEXT&lt;/h2&gt;

&lt;p&gt;To draw a font on a framebuffer, we chose to use
&lt;a href=&quot;https://www.freetype.org/freetype2/docs/documentation.html&quot;&gt;FreeType2&lt;/a&gt; as it is
open source, and supports a variety of fonts. It also lends itself well to
drawing the font directly to our framebuffer memory image.&lt;/p&gt;

&lt;p&gt;Just by following the FreeType2 tutorial we were able to implement the
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_draw_text()&lt;/code&gt; and the
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_draw_text_extra()&lt;/code&gt; functions.&lt;/p&gt;

&lt;p&gt;We have abstracted out the FreeType2 usage, so that if in the future we choose
to change the dependency on FreeType2, we can without exposing the APIs of the
library to idiosyncrasies of FreeType2.&lt;/p&gt;

&lt;p&gt;The simplest way to draw a string of text at &lt;em&gt;(x,y)&lt;/em&gt; coordinates is to use the
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_draw_text()&lt;/code&gt; function, which will start drawing the text at
&lt;em&gt;(x,y)&lt;/em&gt; for the given font and font size, as shown below.&lt;/p&gt;

&lt;p&gt;If the user provides a pointer to an &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_box_t&lt;/code&gt; object as the
&lt;strong&gt;optional last argument&lt;/strong&gt;, the function returns the &lt;em&gt;bounding box&lt;/em&gt; for the
drawn text in the framebuffer. This contains the pixel coordinates of the
rectangle in which the text has been drawn, and informs the user of the region
where the pixels are drawn.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_graphics.h&amp;gt;

/* ... some initialization code ... */

/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

ssd1306_framebuffer_box_t bbox; // bounding box

// draw ABCDeF with the default font and font size 4 at (x,y) of (32, 32)
ssd1306_framebuffer_draw_text(fbp, &quot;ABCDeF&quot;, 0, 32, 32, SSD1306_FONT_DEFAULT, 4, &amp;amp;bbox);

ssd1306_framebuffer_bitdump(fbp);

fprintf(stderr,
    &quot;Bounding box coordinates top: %d left: %d right: %d bottom %d&quot;,
    bbox.top, bbox.left, bbox.right, bbox.bottom);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The framebuffer now looks like:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libssd1306/20200331_drawtext_bitdump_01.png&quot; alt=&quot;Framebuffer dump on terminal for drawing text&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1. Bitdump of the &lt;strong&gt;colored&lt;/strong&gt; framebuffer on the terminal&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As you can see above the default font (BitStream Vera) gets rendered onto the
framebuffer as expected, and in the same way gets displayed on screen on the
OLED device as seen in the image below.&lt;/p&gt;

&lt;p&gt;We also note that the top and left of the bounding box are the same as the
&lt;em&gt;(x,y)&lt;/em&gt; coordinates at which the drawing of the text was requested. The &lt;em&gt;right&lt;/em&gt;
and &lt;em&gt;bottom&lt;/em&gt; values denote the maximum pixel coordinates where the drawing completed. If you want to know where to draw the next string, these values can be useful to locate the position of the string correctly.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libssd1306/20200331_drawtext_abcdef_180.jpg&quot; alt=&quot;Drawing Text on the SSD1306 OLED Screen&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2. Drawing aligned text on screen using Bitstream Vera font&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is an example of drawing a large font size on the screen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libssd1306/20200331_drawtext_large_a_180.jpg&quot; alt=&quot;Drawing large font on the SSD1306 OLED Screen&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 3. Drawing a large font size on screen using the Bitstream Vera font&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;supported-font-faces&quot;&gt;SUPPORTED FONT FACES&lt;/h3&gt;

&lt;p&gt;By default, the &lt;code class=&quot;highlighter-rouge&quot;&gt;libssd1306&lt;/code&gt; library supports the following fonts, and also
allows the user to load their own font file. The package requirements for the
default fonts require the user to install &lt;code class=&quot;highlighter-rouge&quot;&gt;fonts-freefont-ttf&lt;/code&gt; and
&lt;code class=&quot;highlighter-rouge&quot;&gt;ttf-bitstream-vera&lt;/code&gt;. If the user wants to have access to Microsoft®’s TrueType
fonts, they can install &lt;code class=&quot;highlighter-rouge&quot;&gt;ttf-mscorefonts-installer&lt;/code&gt; using the package manager
and get access to Microsoft® specific fonts like Arial and Comic Sans.&lt;/p&gt;

&lt;p&gt;We shall show you examples of using the custom fonts below. Using the
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_fontface_t&lt;/code&gt; enum type allows the user to not have to know where the
font file is located, since we already know that as part of the installation
packages. So we currently hard-code these locations in and map them to an enum.
If the user wants to load their own font file they can use the
&lt;code class=&quot;highlighter-rouge&quot;&gt;SSD1306_FONT_CUSTOM&lt;/code&gt; option and the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_draw_text_extra()&lt;/code&gt;
function which accepts custom options for drawing the text, including the font
file location.&lt;/p&gt;

&lt;p&gt;The default font paths are provided in the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_graphics.h&lt;/code&gt; header file.&lt;/p&gt;

&lt;p&gt;If the user wants to draw using the Microsoft® TrueType font &lt;em&gt;Comic Sans&lt;/em&gt;,
they can use the following code example:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_graphics.h&amp;gt;
/* ... some initialization ... */

/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

// setup the options
ssd1306_graphics_options_t opts;
opts.type = SSD1306_OPT_FONT_FILE;
opts.value.font_file = &quot;/usr/share/fonts/truetype/msttcorefonts/Comic_Sans_MS.ttf&quot;;
// call the draw_text_extra() function which accepts options
ssd1306_framebuffer_draw_text_extra(fbp. &quot;ABCDeF&quot;, 0, 32, 32, SSD1306_FONT_CUSTOM, 4, &amp;amp;opts, 1);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The only difference between the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_draw_text()&lt;/code&gt; and
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_draw_text_extra()&lt;/code&gt; functions is that the latter accepts
custom options for font files, rotation of fonts and rotation of pixels.&lt;/p&gt;

&lt;h4 id=&quot;rotating-the-font&quot;&gt;ROTATING THE FONT&lt;/h4&gt;

&lt;p&gt;In some rare cases, we may want to draw a text at an angle such as for
displaying logos, or just rotating a text on screen by say 30°. We can do
that with the &lt;code class=&quot;highlighter-rouge&quot;&gt;SSD1306_OPT_ROTATE_FONT&lt;/code&gt; option and provide the rotation angle in
the &lt;code class=&quot;highlighter-rouge&quot;&gt;rotation_degrees&lt;/code&gt; field. This uses the font transformation functions
provided by FreeType2 internally, and is highly accurate.&lt;/p&gt;

&lt;p&gt;Below is an example that uses both the custom font file option for Comic Sans
and rotates it by 30°, as seen in the framebuffer output.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_graphics.h&amp;gt;
/* ... some initialization ... */

/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

ssd1306_framebuffer_box_t bbox; // bounding box
// setup the options
ssd1306_graphics_options_t opts[2];
opts[0].type = SSD1306_OPT_FONT_FILE;
opts[0].value.font_file = &quot;/usr/share/fonts/truetype/msttcorefonts/Comic_Sans_MS.ttf&quot;;
opts[1].type = SSD1306_OPT_ROTATE_FONT;
opts[1].value.rotation_degrees = 30;
// call the draw_text_extra() function which accepts options
ssd1306_framebuffer_draw_text_extra(fbp. &quot;ABCDeF&quot;, 0, 32, 32, SSD1306_FONT_CUSTOM, 4, opts, 2, &amp;amp;bbox);
// dump the framebuffer to screen
ssd1306_framebuffer_bitdump(fbp);
fprintf(stderr,
    &quot;Bounding box coordinates top: %d left: %d right: %d bottom %d&quot;,
    bbox.top, bbox.left, bbox.right, bbox.bottom);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Below is the screenshot of the terminal displaying the rotation of the font and
the usage of the Microsoft® Comic Sans font. As you may notice, that the
bounding box is different in this example as the font selected is different and
rotating the font changes the size of the box.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libssd1306/20200331_drawtext_bitdump_fontrotate.png&quot; alt=&quot;Framebuffer dump on terminal for drawing text with rotated font&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 4. Bitdump of the rotated Comic Sans font on the terminal&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;All the code snippets can be found in the &lt;code class=&quot;highlighter-rouge&quot;&gt;examples/fb_graphics.c&lt;/code&gt; file.&lt;/p&gt;

&lt;h3 id=&quot;rotating-pixels&quot;&gt;ROTATING PIXELS&lt;/h3&gt;

&lt;p&gt;As shown in the &lt;a href=&quot;/blog/2020/03/21/oled-ssd1306-framebuffer.html&quot;&gt;previous post&lt;/a&gt;, we may want to rotate an image on the screen by using the pixel rotation function. We can absolutely use that feature for drawing text at different locations by rotating not just the font, but also the pixels.&lt;/p&gt;

&lt;p&gt;Below is an example that uses both the custom font file option for Comic Sans
and rotates it by 30°, and then rotates the drawing by 180° as seen in the framebuffer output.&lt;/p&gt;

&lt;p&gt;Note that the &lt;code class=&quot;highlighter-rouge&quot;&gt;SSD1306_OPT_ROTATE_PIXEL&lt;/code&gt; option only accepts multiples of
90° in the &lt;code class=&quot;highlighter-rouge&quot;&gt;rotation_degrees&lt;/code&gt; field. Any other value will get ignored, and
an error statement printed to the log.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_graphics.h&amp;gt;
/* ... some initialization ... */

/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

// setup the options
ssd1306_graphics_options_t opts[1];
opts[0].type = SSD1306_OPT_ROTATE_PIXEL;
opts[0].value.rotation_degrees = 180;
// call the draw_text_extra() function which accepts options
ssd1306_framebuffer_draw_text_extra(fbp. &quot;ABCDeF&quot;, 0, 0, 0, SSD1306_FONT_DEFAULT, 4, opts, 1);
// dump the framebuffer to screen
ssd1306_framebuffer_bitdump(fbp);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In &lt;em&gt;Figure 5&lt;/em&gt;, you can see an example of the pixels rotated by 180° to flip the drawing of the text.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libssd1306/20200331_drawtext_bitdump_rotatepixel.png&quot; alt=&quot;Framebuffer dump on terminal for drawing text with rotated pixels&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Below is the code for rotating both the font and the pixels by 30° each.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_graphics.h&amp;gt;
/* ... some initialization ... */

/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

// setup the options
ssd1306_graphics_options_t opts[2];
opts[0].type = SSD1306_OPT_ROTATE_FONT;
opts[0].value.rotation_degrees = 18;
opts[1].type = SSD1306_OPT_ROTATE_PIXEL;
opts[1].value.rotation_degrees = 30;
// call the draw_text_extra() function which accepts options
ssd1306_framebuffer_draw_text_extra(fbp. &quot;ABCDeF&quot;, 0, 0, 0, SSD1306_FONT_DEFAULT, 4, opts, 2);
// dump the framebuffer to screen
ssd1306_framebuffer_bitdump(fbp);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libssd1306/20200331_drawtext_bitdump_rotatefont_rotatepixel.png&quot; alt=&quot;Framebuffer dump on terminal for drawing text with rotated font and rotated pixels&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 6. Bitdump of the rotated default font and rotated pixels on the terminal&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 7&lt;/em&gt; is an example of using Comic Sans and rotating the font and pixels each by 30 degrees on the OLED screen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libssd1306/20200331_drawtext_rotate_comicsans_180.jpg&quot; alt=&quot;Drawing Text on the SSD1306 OLED Screen&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 7. Drawing Comic Sans rotated text on screen&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 31 Mar 2020 00:00:00 -0400</pubDate>
        <link>https://www.stealthylabs.com/blog/2020/03/31/oled-ssd1306-drawtext-updated.html</link>
        <guid isPermaLink="true">https://www.stealthylabs.com/blog/2020/03/31/oled-ssd1306-drawtext-updated.html</guid>
        
        <category>libssd1306</category>
        
        <category>graphics</category>
        
        <category>i2c</category>
        
        <category>raspberrypi</category>
        
        <category>freetype2</category>
        
        
        <category>Graphics</category>
        
        <category>SSD1306</category>
        
      </item>
    
      <item>
        <title>Drawing Lines on OLED Chip SSD1306</title>
        <description>&lt;p&gt;In an &lt;a href=&quot;/blog/2020/03/21/oled-ssd1306-framebuffer.html&quot;&gt;earlier post&lt;/a&gt; we had discussed the framebuffer object of the C library for performing graphics on the OLED screen SSD1306 using a Raspberry Pi. We also discussed how to &lt;a href=&quot;/blog/2020/03/22/oled-ssd1306-drawtext.html&quot;&gt;draw text&lt;/a&gt; using &lt;a href=&quot;https://www.freetype.org/freetype2/docs/documentation.html&quot;&gt;FreeType2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this post we show you how to draw straight lines on the OLED screen using our
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_draw_line()&lt;/code&gt; function.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;The code in this post is as of commit &lt;code class=&quot;highlighter-rouge&quot;&gt;597002e7e6792ff236f760c11d67ff6a6d719ab9&lt;/code&gt; from &lt;a href=&quot;https://github.com/stealthylabs/libssd1306&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There may be several algorithms to draw lines on a screen, but for our case we
use the Bresenham’s line drawing algorithm as described in the &lt;em&gt;great&lt;/em&gt; Michael Abrash’s
&lt;a href=&quot;http://www.phatcode.net/res/224/files/html/ch35/35-01.html#Heading1&quot;&gt;Graphics Programming Black Book&lt;/a&gt;. The book explains in great detail how the algorithm works and how to draw to a VGA or EVGA screen using the x86 processor. We have appropriately adjusted the algorithm to work correctly for our OLED screen and also be more generic to work on both ARM and x86 chips, but most of the algorithm does not change from what is in the book’s chapter.&lt;/p&gt;

&lt;p&gt;Our function here is &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_draw_line()&lt;/code&gt; which takes 6 arguments:
the framebuffer pointer, the &lt;em&gt;(x&lt;sub&gt;0&lt;/sub&gt;, y&lt;sub&gt;0&lt;/sub&gt;)&lt;/em&gt; starting point
coordinates, the &lt;em&gt;(x&lt;sub&gt;1&lt;/sub&gt;, y&lt;sub&gt;1&lt;/sub&gt;)&lt;/em&gt; ending point coordinates and
the pixel color for whether to draw a clear (&lt;em&gt;black&lt;/em&gt;) or filled (&lt;em&gt;blue&lt;/em&gt;)
pixel for the line.&lt;/p&gt;

&lt;p&gt;In the &lt;code class=&quot;highlighter-rouge&quot;&gt;examples/draw_line.c&lt;/code&gt; we draw three different lines - a horizontal line
at 0°, a vertical line at 90° and a diagonal line between two random points.&lt;/p&gt;

&lt;p&gt;If we want to draw a horizontal line from &lt;code class=&quot;highlighter-rouge&quot;&gt;(0,0)&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;(64,0)&lt;/code&gt; with a filled
pixel, our function call will look like:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_graphics.h&amp;gt;

/* ... some initialization code ... */
/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

// draw the line
ssd1306_framebuffer_draw_line(fbp, 0, 0, 64, 0, true);

// dump the framebuffer memory with no-spaces
ssd1306_framebuffer_bitdump_nospace(fbp);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The framebuffer dump looks like:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0000 &lt;span class=&quot;o&quot;&gt;||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||&lt;/span&gt;................................................................
0001 ................................................................................................................................
0002 ................................................................................................................................
0003 ................................................................................................................................
0004 ................................................................................................................................
0005 ................................................................................................................................
0006 ................................................................................................................................
0007 ................................................................................................................................
0008 ................................................................................................................................
0009 ................................................................................................................................
000A ................................................................................................................................
000B ................................................................................................................................
000C ................................................................................................................................
000D ................................................................................................................................
000E ................................................................................................................................
000F ................................................................................................................................
0010 ................................................................................................................................
0011 ................................................................................................................................
0012 ................................................................................................................................
0013 ................................................................................................................................
0014 ................................................................................................................................
0015 ................................................................................................................................
0016 ................................................................................................................................
0017 ................................................................................................................................
0018 ................................................................................................................................
0019 ................................................................................................................................
001A ................................................................................................................................
001B ................................................................................................................................
001C ................................................................................................................................
001D ................................................................................................................................
001E ................................................................................................................................
001F ................................................................................................................................
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we want to draw a vertical line from &lt;code class=&quot;highlighter-rouge&quot;&gt;(64,0)&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;(64,32)&lt;/code&gt; with a filled
pixel, our function call will look like:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_graphics.h&amp;gt;

/* ... some initialization code ... */
/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

// draw the line
ssd1306_framebuffer_draw_line(fbp, 64, 0, 64, 32, true);

// dump the framebuffer memory with no-spaces
ssd1306_framebuffer_bitdump_nospace(fbp);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The framebuffer dump looks like:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0000 ................................................................|...............................................................
0001 ................................................................|...............................................................
0002 ................................................................|...............................................................
0003 ................................................................|...............................................................
0004 ................................................................|...............................................................
0005 ................................................................|...............................................................
0006 ................................................................|...............................................................
0007 ................................................................|...............................................................
0008 ................................................................|...............................................................
0009 ................................................................|...............................................................
000A ................................................................|...............................................................
000B ................................................................|...............................................................
000C ................................................................|...............................................................
000D ................................................................|...............................................................
000E ................................................................|...............................................................
000F ................................................................|...............................................................
0010 ................................................................|...............................................................
0011 ................................................................|...............................................................
0012 ................................................................|...............................................................
0013 ................................................................|...............................................................
0014 ................................................................|...............................................................
0015 ................................................................|...............................................................
0016 ................................................................|...............................................................
0017 ................................................................|...............................................................
0018 ................................................................|...............................................................
0019 ................................................................|...............................................................
001A ................................................................|...............................................................
001B ................................................................|...............................................................
001C ................................................................|...............................................................
001D ................................................................|...............................................................
001E ................................................................|...............................................................
001F ................................................................|...............................................................
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A line that is not at 0° or 90° or a 1:1 diagonal will have to be
approximately drawn using Bresenham’s line drawing algorithm as noted above. As
part of the algorithm sometimes to make the line look neat, we have to color two
pixels in each row as you can see in the framebuffer dump below to get the
perfect look of the line.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_graphics.h&amp;gt;

/* ... some initialization code ... */
/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

// draw the line
ssd1306_framebuffer_draw_line(fbp, 0, 0, 64, 32, true);

// dump the framebuffer memory with no-spaces
ssd1306_framebuffer_bitdump_nospace(fbp);
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0000 |...............................................................................................................................
0001 .||.............................................................................................................................
0002 ...||...........................................................................................................................
0003 .....||.........................................................................................................................
0004 .......||.......................................................................................................................
0005 .........||.....................................................................................................................
0006 ...........||...................................................................................................................
0007 .............||.................................................................................................................
0008 ...............||...............................................................................................................
0009 .................||.............................................................................................................
000A ...................||...........................................................................................................
000B .....................||.........................................................................................................
000C .......................||.......................................................................................................
000D .........................||.....................................................................................................
000E ...........................||...................................................................................................
000F .............................||.................................................................................................
0010 ...............................||...............................................................................................
0011 .................................||.............................................................................................
0012 ...................................||...........................................................................................
0013 .....................................||.........................................................................................
0014 .......................................||.......................................................................................
0015 .........................................||.....................................................................................
0016 ...........................................||...................................................................................
0017 .............................................||.................................................................................
0018 ...............................................||...............................................................................
0019 .................................................||.............................................................................
001A ...................................................||...........................................................................
001B .....................................................||.........................................................................
001C .......................................................||.......................................................................
001D .........................................................||.....................................................................
001E ...........................................................||...................................................................
001F .............................................................||.................................................................
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the example file &lt;code class=&quot;highlighter-rouge&quot;&gt;examples/i2c_128x32_graphics.c&lt;/code&gt; we have added a call to the
line drawing function and below are some images from drawing the line on the
screen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libssd1306/drawline_1.jpg&quot; alt=&quot;Drawing diagonal line on the SSD1306 OLED Screen&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1. Drawing a diagonal line&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libssd1306/drawline_2.jpg&quot; alt=&quot;Drawing diagonal and vertical lines on the SSD1306 OLED Screen&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2. Drawing vertical and diagonal lines&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libssd1306/drawline_3.jpg&quot; alt=&quot;Drawing horizontal, vertical and diagonal lines on the SSD1306 OLED Screen&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 3. Drawing horizontal, vertical and diagonal lines&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 25 Mar 2020 00:00:00 -0400</pubDate>
        <link>https://www.stealthylabs.com/blog/2020/03/25/oled-ssd1306-drawline.html</link>
        <guid isPermaLink="true">https://www.stealthylabs.com/blog/2020/03/25/oled-ssd1306-drawline.html</guid>
        
        <category>libssd1306</category>
        
        <category>graphics</category>
        
        <category>i2c</category>
        
        <category>raspberrypi</category>
        
        
        <category>Graphics</category>
        
        <category>SSD1306</category>
        
      </item>
    
      <item>
        <title>Drawing Text on OLED Chip SSD1306 Using FreeType2</title>
        <description>&lt;p&gt;In the &lt;a href=&quot;/blog/2020/03/21/oled-ssd1306-framebuffer.html&quot;&gt;previous post&lt;/a&gt; we had discussed the framebuffer object of the C library for performing graphics on the OLED screen SSD1306 using a Raspberry Pi.&lt;/p&gt;

&lt;p&gt;In this blog post, we demonstrate how we have used &lt;a href=&quot;https://www.freetype.org/freetype2/docs/documentation.html&quot;&gt;FreeType2&lt;/a&gt; to draw
text on the screen using fonts that can be installed using your Raspbian distro
packages. FreeType2 provides an easy way to render any type of supported font on
the screen, and we encapsulate all of that in a &lt;strong&gt;single&lt;/strong&gt; function call
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_draw_text()&lt;/code&gt;.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;: This code is &lt;strong&gt;OBSOLETE&lt;/strong&gt; as of &lt;code class=&quot;highlighter-rouge&quot;&gt;git tag 0.6&lt;/code&gt; or commit &lt;code class=&quot;highlighter-rouge&quot;&gt;50e836f9299f61a3fe2901f794d2131ade7c23be&lt;/code&gt;. Please refer to the &lt;a href=&quot;/blog/2020/03/31/oled-ssd1306-drawtext-updated.html&quot;&gt;latest blog post on this topic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The code in this post is as of &lt;code class=&quot;highlighter-rouge&quot;&gt;git tag 0.51&lt;/code&gt; or commit &lt;code class=&quot;highlighter-rouge&quot;&gt;294ffccc1004b35028406fc4281cef894f473d2a&lt;/code&gt; from &lt;a href=&quot;https://github.com/stealthylabs/libssd1306&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;drawing-text&quot;&gt;DRAWING TEXT&lt;/h2&gt;

&lt;p&gt;To draw a font on a framebuffer, we chose to use
&lt;a href=&quot;https://www.freetype.org/freetype2/docs/documentation.html&quot;&gt;FreeType2&lt;/a&gt; as it is
open source, and supports a variety of fonts. It also lends itself well to
drawing the font directly to our framebuffer memory image.&lt;/p&gt;

&lt;p&gt;Just by following the FreeType2 tutorial we were able to implement the
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_draw_text()&lt;/code&gt; and the
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_draw_text_extra()&lt;/code&gt; functions.&lt;/p&gt;

&lt;p&gt;We have abstracted out the FreeType2 usage, so that if in the future we choose
to change the dependency on FreeType2, we can without exposing the APIs of the
library to idiosyncrasies of FreeType2.&lt;/p&gt;

&lt;p&gt;The simplest way to draw a string of text at &lt;em&gt;(x,y)&lt;/em&gt; coordinates is to use the
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_draw_text()&lt;/code&gt; function, which will start drawing the text at
&lt;em&gt;(x,y)&lt;/em&gt; for the given font and font size, as shown below.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_graphics.h&amp;gt;

/* ... some initialization code ... */

/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

// draw ABCDeF with the default font and font size 4 at (x,y) of (32, 32)
ssd1306_framebuffer_draw_text(fbp, &quot;ABCDeF&quot;, 0, 32, 32, SSD1306_FONT_DEFAULT, 4);

ssd1306_framebuffer_bitdump(fbp);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The framebuffer now looks like:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0000 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0001 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0002 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0003 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0004 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0005 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0006 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0007 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0008 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0009 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
000A ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
000B ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
000C ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
000D ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
000E ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
000F ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0010 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0011 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0012 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0013 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0014 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0015 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0016 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0017 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0018 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0019 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001A ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001B ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001C ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001D ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001E ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001F ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0020 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0021 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0022 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0023 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0024 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0025 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0026 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0027 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0028 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0029 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002A ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002B ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002C ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002D ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002E ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002F ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0030 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0031 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0032 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0033 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0034 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0035 ..|||||. ...||||| |....... &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;.| &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;.... ........ ...||||| &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;.... ........ ........ ........ ........ ........ ........ ........ ........ 
0036 ..|||||. ...||||| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|...|| &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;.| &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;.. ........ ...||||| &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;.... ........ ........ ........ ........ ........ ........ ........ ........ 
0037 ....|||. ....||.. .||...|| ...|||.. |...|||. ........ ....||.. ..||.... ........ ........ ........ ........ ........ ........ ........ ........ 
0038 ...||.|| ....||.. ..|..||. ....||.. |....||| ...||||| ....||.. |.||.... ........ ........ ........ ........ ........ ........ ........ ........ 
0039 ...||.|| ....||.. .||..||. ........ |.....|| ..|||.|| |...||.| |....... ........ ........ ........ ........ ........ ........ ........ ........ 
003A ...||.|| ....|||| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|..||. ........ |.....|| .||....| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;..|||| |....... ........ ........ ........ ........ ........ ........ ........ ........ 
003B ..|||||| |...||.. .|||.||. ........ |.....|| .||||||| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;..||.| |....... ........ ........ ........ ........ ........ ........ ........ ........ 
003C ..|||||| |...||.. ..||.||. ........ |.....|| .||||||| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;..||.. |....... ........ ........ ........ ........ ........ ........ ........ ........ 
003D .||..... &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;..||.. ..||.||| ....||.. |....||. .|||.... ....||.. ........ ........ ........ ........ ........ ........ ........ ........ ........ 
003E &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;|..| &lt;span class=&quot;o&quot;&gt;||||||||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;..|| &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;.| &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;|. ..|||.|| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;.||||| ........ ........ ........ ........ ........ ........ ........ ........ ........ 
003F &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;|..| &lt;span class=&quot;o&quot;&gt;||||||||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|....| &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;|..| &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;.. ...||||| |..||||| |....... ........ ........ ........ ........ ........ ........ ........ ........ 

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see above the default font (BitStream Vera) gets rendered onto the
framebuffer as expected, and in the same way gets displayed on screen on the
OLED device as seen in the image below.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libssd1306/drawtext_abcdef.jpg&quot; alt=&quot;Drawing Text on the SSD1306 OLED Screen&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1. Drawing aligned text on screen using Bitstream Vera font&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is an example of drawing a large font size on the screen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libssd1306/drawtext_large_a.jpg&quot; alt=&quot;Drawing large font on the SSD1306 OLED Screen&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2. Drawing a large font size on screen using the Bitstream Vera font&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;supported-font-faces&quot;&gt;SUPPORTED FONT FACES&lt;/h3&gt;

&lt;p&gt;By default, the &lt;code class=&quot;highlighter-rouge&quot;&gt;libssd1306&lt;/code&gt; library supports the following fonts, and also
allows the user to load their own font file. The package requirements for the
default fonts require the user to install &lt;code class=&quot;highlighter-rouge&quot;&gt;fonts-freefont-ttf&lt;/code&gt; and
&lt;code class=&quot;highlighter-rouge&quot;&gt;ttf-bitstream-vera&lt;/code&gt;. If the user wants to have access to Microsoft®’s TrueType
fonts, they can install &lt;code class=&quot;highlighter-rouge&quot;&gt;ttf-mscorefonts-installer&lt;/code&gt; using the package manager
and get access to Microsoft® specific fonts like Arial and Comic Sans.&lt;/p&gt;

&lt;p&gt;We shall show you examples of using the custom fonts below. Using the
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_fontface_t&lt;/code&gt; enum type allows the user to not have to know where the
font file is located, since we already know that as part of the installation
packages. So we currently hard-code these locations in and map them to an enum.
If the user wants to load their own font file they can use the
&lt;code class=&quot;highlighter-rouge&quot;&gt;SSD1306_FONT_CUSTOM&lt;/code&gt; option and the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_draw_text_extra()&lt;/code&gt;
function which accepts custom options for drawing the text, including the font
file location.&lt;/p&gt;

&lt;p&gt;The default font paths are provided in the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_graphics.h&lt;/code&gt; header file.&lt;/p&gt;

&lt;p&gt;If the user wants to draw using the Microsoft® TrueType font &lt;em&gt;Comic Sans&lt;/em&gt;,
they can use the following code example:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_graphics.h&amp;gt;
/* ... some initialization ... */

/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

// setup the options
ssd1306_graphics_options_t opts;
opts.type = SSD1306_OPT_FONT_FILE;
opts.value.font_file = &quot;/usr/share/fonts/truetype/msttcorefonts/Comic_Sans_MS.ttf&quot;;
// call the draw_text_extra() function which accepts options
ssd1306_framebuffer_draw_text_extra(fbp. &quot;ABCDeF&quot;, 0, 32, 32, SSD1306_FONT_CUSTOM, 4, &amp;amp;opts, 1);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The only difference between the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_draw_text()&lt;/code&gt; and
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_draw_text_extra()&lt;/code&gt; functions is that the latter accepts
custom options for font files, rotation of fonts and rotation of pixels.&lt;/p&gt;

&lt;h4 id=&quot;rotating-the-font&quot;&gt;ROTATING THE FONT&lt;/h4&gt;

&lt;p&gt;In some rare cases, we may want to draw a text at an angle such as for
displaying logos, or just rotating a text on screen by say 30°. We can do
that with the &lt;code class=&quot;highlighter-rouge&quot;&gt;SSD1306_OPT_ROTATE_FONT&lt;/code&gt; option and provide the rotation angle in
the &lt;code class=&quot;highlighter-rouge&quot;&gt;rotation_degrees&lt;/code&gt; field. This uses the font transformation functions
provided by FreeType2 internally, and is highly accurate.&lt;/p&gt;

&lt;p&gt;Below is an example that uses both the custom font file option for Comic Sans
and rotates it by 30°, as seen in the framebuffer output.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_graphics.h&amp;gt;
/* ... some initialization ... */

/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

// setup the options
ssd1306_graphics_options_t opts[2];
opts[0].type = SSD1306_OPT_FONT_FILE;
opts[0].value.font_file = &quot;/usr/share/fonts/truetype/msttcorefonts/Comic_Sans_MS.ttf&quot;;
opts[1].type = SSD1306_OPT_ROTATE_FONT;
opts[1].value.rotation_degrees = 30;
// call the draw_text_extra() function which accepts options
ssd1306_framebuffer_draw_text_extra(fbp. &quot;ABCDeF&quot;, 0, 32, 32, SSD1306_FONT_CUSTOM, 4, opts, 2);
// dump the framebuffer to screen
ssd1306_framebuffer_bitdump(fbp);
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0000 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0001 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0002 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0003 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0004 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0005 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0006 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0007 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0008 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0009 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
000A ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
000B ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
000C ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
000D ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
000E ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
000F ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0010 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0011 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0012 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0013 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0014 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0015 ........ ........ ........ ........ ........ ......|| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0016 ........ ........ ........ ........ ........ ....|||| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;...... ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0017 ........ ........ ........ ........ ........ ...||||| |....... ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0018 ........ ........ ........ ........ ........ ..|||||. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0019 ........ ........ ........ ........ ........ ..||||.. .|||.... ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001A ........ ........ ........ ........ ........ ...|||.| &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;.... ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001B ........ ........ ........ ........ ........ ...||||| &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;.... ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001C ........ ........ ........ ........ ........ ....|||| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;...... ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001D ........ ........ ........ ........ ........ ....|||| |....... ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001E ........ ........ ........ ........ ......|| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;...||| ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001F ........ ........ ........ ........ .....||| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;...||| |....... ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0020 ........ ........ ........ ........ .....||| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;....|| |....... ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0021 ........ ........ ........ ....|||| |....||| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;.....| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;...... ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0022 ........ ........ ........ &lt;span class=&quot;o&quot;&gt;||||||||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|..||. &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;.....| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;...... ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0023 ........ ........ ........ &lt;span class=&quot;o&quot;&gt;||||||||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;.||| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;...... &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;...... ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0024 ........ ........ ....|||| &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;.... .||||||| |.|||||. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0025 ........ ........ ...||||| .|||.... ..|||.|| &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;.. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0026 ........ ........ ..|||||| .||||... ...||.|| &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;|... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0027 ........ ........ ..|||... ..|||... ...||... &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0028 ........ ........ ..|||... ...|||.. ..|||... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0029 ........ ........ .|||.... ...|||.. .||||... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002A ........ ........ .|||.... ....|||| &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;|... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002B ........ &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;.. .|||.... ....|||| &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;.... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002C .......| &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;|. .|||.... .....||| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002D ......|| &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;|. .|||.... .....||| ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002E ......|| |...|||. ..|||... &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;.... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002F .......| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;..|||. ..||||.| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0030 &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|....| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;.||||| |..||||| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0031 &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;.... &lt;span class=&quot;o&quot;&gt;||||||||&lt;/span&gt; |..||||| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;...... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0032 &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;.... &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;|. |.....|| ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0033 &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;|... .||||..| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;...... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0034 &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;.. .|||..|| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;...... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0035 &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;.|||.. ..|||||| |....... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0036 &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;.. ..|||||| |....... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0037 &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;.. ...||||. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0038 &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;|... ...|||.. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0039 &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;...| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;...... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
003A &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|..... &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;...... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
003B &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
003C &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
003D &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
003E &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
003F ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All the code snippets can be found in the &lt;code class=&quot;highlighter-rouge&quot;&gt;examples/fb_graphics.c&lt;/code&gt; file.&lt;/p&gt;

&lt;h3 id=&quot;rotating-pixels&quot;&gt;ROTATING PIXELS&lt;/h3&gt;

&lt;p&gt;As shown in the &lt;a href=&quot;/blog/2020/03/21/oled-ssd1306-framebuffer.html&quot;&gt;previous post&lt;/a&gt;, we may want to rotate an image on the screen by using the pixel rotation function. We can absolutely use that feature for drawing text at different locations by rotating not just the font, but also the pixels.&lt;/p&gt;

&lt;p&gt;Below is an example that uses both the custom font file option for Comic Sans
and rotates it by 30°, and then rotates the drawing by 180° as seen in the framebuffer output.&lt;/p&gt;

&lt;p&gt;Note that the &lt;code class=&quot;highlighter-rouge&quot;&gt;SSD1306_OPT_ROTATE_PIXEL&lt;/code&gt; option only accepts multiples of
90° in the &lt;code class=&quot;highlighter-rouge&quot;&gt;rotation_degrees&lt;/code&gt; field. Any other value will get ignored, and
an error statement printed to the log.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_graphics.h&amp;gt;
/* ... some initialization ... */

/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

// setup the options
ssd1306_graphics_options_t opts[2];
opts[0].type = SSD1306_OPT_ROTATE_FONT;
opts[0].value.rotation_degrees = 30;
opts[1].type = SSD1306_OPT_ROTATE_PIXEL;
opts[1].value.rotation_degrees = 30;
// call the draw_text_extra() function which accepts options
ssd1306_framebuffer_draw_text_extra(fbp. &quot;ABCDeF&quot;, 0, 0, 0, SSD1306_FONT_DEFAULT, 4, opts, 2);
// dump the framebuffer to screen
ssd1306_framebuffer_bitdump(fbp);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you scroll to the right of this buffer dump, you can see the flip of the
rotated font string.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0000 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ......|| 
0001 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .....||| 
0002 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ....|||| 
0003 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .|....|| 
0004 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .......| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|...|| 
0005 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ....|||| 
0006 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ....|||. ...||||| 
0007 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ..||||.. ...||..| 
0008 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .|||.||. ...|||.| 
0009 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|..||. ....|||| 
000A ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .......| |.....|| .....||| 
000B ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .......| |...|||| |.....|| 
000C ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .|||||.. .||||||| |....... 
000D ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;|. .||||..| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|..... 
000E ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .......| |....||| ..||.... &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|..... 
000F ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ......|| |.||..|| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;...... 
0010 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ...|||.. .......| |..||||| ........ 
0011 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .||||... .......| |....... ........ 
0012 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;|.||.. .......| |....... ........ 
0013 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .......| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;..|||. .......| |....... ........ 
0014 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .......| |....||. ..|...|| |....... ........ 
0015 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .......| |....||| ..|||||| ........ ........ 
0016 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;|..| |.....|| ..||||.. ........ ........ 
0017 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ......|| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;.|||.| |......| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;.|.... ........ ........ 
0018 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ......|| ...||||. &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;....|| &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;...... ........ ........ 
0019 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ..|||||. &lt;span class=&quot;o&quot;&gt;||||||||&lt;/span&gt; |....... ........ ........ 
001A ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .|||.... .|||.||. ..|||||. ........ ........ ........ 
001B ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;.... .|..|||. ........ ........ ........ ........ 
001C ........ ........ ........ ........ ........ ........ ........ ........ ........ .......| &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;.... .|.|||.. ........ ........ ........ ........ 
001D ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ...||... .||||... ........ ........ ........ ........ 
001E ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ...||... ........ ........ ........ ........ ........ 
001F ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;.. ........ ........ ........ ........ ........ 
0020 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .||||||. ........ ........ ........ ........ ........ 
0021 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .||..||| |....... ........ ........ ........ ........ 
0022 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .....||| |....... ........ ........ ........ ........ 
0023 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ |..||||. ........ ........ ........ ........ ........ 
0024 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ &lt;span class=&quot;o&quot;&gt;||||||&lt;/span&gt;.. ........ ........ ........ ........ ........ 
0025 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ &lt;span class=&quot;o&quot;&gt;||||&lt;/span&gt;.... ........ ........ ........ ........ ........ 
0026 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .|...... ........ ........ ........ ........ ........ 
0027 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0028 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0029 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002A ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002B ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002C ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002D ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002E ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002F ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0030 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0031 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0032 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0033 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0034 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0035 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0036 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0037 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0038 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0039 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
003A ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
003B ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
003C ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
003D ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
003E ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
003F ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here is an example of using Comic Sans and rotating the text.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/libssd1306/drawtext_rotate_comicsans.jpg&quot; alt=&quot;Drawing Text on the SSD1306 OLED Screen&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 3. Drawing Comic Sans rotated text on screen&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 22 Mar 2020 00:00:00 -0400</pubDate>
        <link>https://www.stealthylabs.com/blog/2020/03/22/oled-ssd1306-drawtext.html</link>
        <guid isPermaLink="true">https://www.stealthylabs.com/blog/2020/03/22/oled-ssd1306-drawtext.html</guid>
        
        <category>libssd1306</category>
        
        <category>graphics</category>
        
        <category>i2c</category>
        
        <category>raspberrypi</category>
        
        <category>freetype2</category>
        
        
        <category>Graphics</category>
        
        <category>SSD1306</category>
        
      </item>
    
      <item>
        <title>Framebuffer and Drawing Pixels on OLED Chip SSD1306</title>
        <description>&lt;p&gt;In the &lt;a href=&quot;/blog/2020/02/19/oled-ssd1306-library.html&quot;&gt;previous post&lt;/a&gt; we had introduced the C library for performing graphics on the OLED screen SSD1306 using a Raspberry Pi.&lt;/p&gt;

&lt;p&gt;In this post we explain the framebuffer object and how to draw individual pixels
on the screen.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;The code in this post is as of &lt;code class=&quot;highlighter-rouge&quot;&gt;git tag 0.51&lt;/code&gt; or commit &lt;code class=&quot;highlighter-rouge&quot;&gt;294ffccc1004b35028406fc4281cef894f473d2a&lt;/code&gt; from &lt;a href=&quot;https://github.com/stealthylabs/libssd1306&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;understanding-framebuffer&quot;&gt;UNDERSTANDING FRAMEBUFFER&lt;/h2&gt;

&lt;p&gt;The SSD1306 OLED screen has a display RAM that is of the same size as the screen
pixels, with a &lt;em&gt;one-to-one&lt;/em&gt; correspondence between pixel and a bit in the
display RAM (GDRAM in the datasheet). To mimic the behavior of the RAM, we have
the framebuffer object that has a buffer of the same size as the display RAM,
and allows you, the developer, to &lt;em&gt;draw&lt;/em&gt; to the framebuffer memory image, before &lt;em&gt;writing&lt;/em&gt;
that memory image directly to the display RAM of the OLED screen. This allows
the programs to perform multiple drawing tasks in memory, which is faster, and
then writing &lt;em&gt;once&lt;/em&gt; via I&lt;sup&gt;2&lt;/sup&gt;C to the display RAM, which is slower.&lt;/p&gt;

&lt;p&gt;Having a framebuffer graphics object, independent of the actual SSD1306 command object, enables you to perform faster drawing by using multiple framebuffer objects. For instance, lets say you have drawn an image onto a single framebuffer object and then queued a &lt;em&gt;write&lt;/em&gt; to the display RAM. During the time it takes to update the display RAM using I&lt;sup&gt;2&lt;/sup&gt;C, you could be updating a second framebuffer object simultaneously or using multi-threading. This methodology of using more than one framebuffer has been used by game developers for several decades. This can enable fast &lt;em&gt;re-drawing&lt;/em&gt; of the screen.&lt;/p&gt;

&lt;p&gt;If you want to use the Raspberry Pi (or Beagleboard, etc.) with the SSD1306 OLED
screen, and want to build a fun game with our library you can utilize the
multiple framebuffer approach to make the game appear to display fast.&lt;/p&gt;

&lt;h2 id=&quot;debugging-framebuffer&quot;&gt;DEBUGGING FRAMEBUFFER&lt;/h2&gt;

&lt;p&gt;Sometimes, or all the time if you are me, you  may want to look at what the
framebuffer memory looks like, so that whatever you are drawing and expecting to
see on the OLED screen, you can actually see on your computer terminal too.&lt;/p&gt;

&lt;p&gt;To achieve that we have a function &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_bitdump()&lt;/code&gt; which takes
only one argument, the framebuffer pointer, and dumps the framebuffer memory to
the screen in the same way it would display on the OLED screen.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_graphics.h&amp;gt;
/* ... some initialization code ... */
/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);
/* ... do something to the framebuffer ... */
// dump the framebuffer memory
ssd1306_framebuffer_bitdump(fbp);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Having the framebuffer object work without the need of the I&lt;sup&gt;2&lt;/sup&gt;C device
can be very useful for running memory leak tests using
&lt;a href=&quot;https://valgrind.org&quot;&gt;Valgrind&lt;/a&gt;, which we have been doing to make sure that our
code is memory leak free, which it is. Valgrind works great on x86 devices but does not work
reliably, as of this writing, on the Raspberry Pi. Eventually, we plan to use a
USB-to-I&lt;sup&gt;2&lt;/sup&gt;C device, like the &lt;a href=&quot;https://www.diygadget.com/tiao-usb-multi-protocol-adapter-lite-jtag-spi-i2c-serial&quot;&gt;TUMPA Lite&lt;/a&gt; which we possess, so we can test the full library directly from an x86 computer too.&lt;/p&gt;

&lt;h2 id=&quot;drawing-pixels&quot;&gt;DRAWING PIXELS&lt;/h2&gt;

&lt;p&gt;We have four pixel-level functions in our graphics library, three for drawing a
pixel and one for knowing the pixel value. For the SSD1306 OLED screen, there
are only two possible values for the pixel color: &lt;em&gt;black&lt;/em&gt; (clear) or &lt;em&gt;blue&lt;/em&gt;
(colored). We handle that with a boolean flag.&lt;/p&gt;

&lt;h4 id=&quot;ssd1306_framebuffer_put_pixel&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_put_pixel()&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;This function takes the &lt;em&gt;(x,y)&lt;/em&gt; coordinates of where you want to draw the pixel
on the framebuffer, the framebuffer pointer and a boolean flag for coloring or
clearing the pixel. The &lt;em&gt;(x,y)&lt;/em&gt; coordinates need to be within the width and
height values of the OLED screen, which in our example below is 128x32.&lt;/p&gt;

&lt;p&gt;The function returns 0 on success and -1 on error.&lt;/p&gt;

&lt;p&gt;The below code snippet is present in the &lt;code class=&quot;highlighter-rouge&quot;&gt;examples/i2c_128x32_graphics.c&lt;/code&gt; file.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_graphics.h&amp;gt;
/* ... some initialization code ... */

/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

// for (x,y) (0, 0)
ssd1306_framebuffer_put_pixel(fbp, 0, 0, true);
// for (x,y) (127, 0)
ssd1306_framebuffer_put_pixel(fbp, fbp-&amp;gt;width - 1, 0, true);
// for (x,y) (0, 31)
ssd1306_framebuffer_put_pixel(fbp, 0, fbp-&amp;gt;height - 1, true);
// for (x,y) (127, 31)
ssd1306_framebuffer_put_pixel(fbp, fbp-&amp;gt;width - 1, fbp-&amp;gt;height - 1, true);
// for dumping the framebuffer to screen to view the changes
ssd1306_framebuffer_bitdump(fbp);

/* remove the framebuffer pointer */
ssd1306_framebuffer_destroy(fbp);
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;ssd1306_framebuffer_get_pixel&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_get_pixel()&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;This function returns the value of the color of the pixel, either 0 if clear
(&lt;em&gt;black&lt;/em&gt;) or 1 if colored (&lt;em&gt;blue&lt;/em&gt;), or -1 if the arguments are invalid. This can
be used by the developer in the event they want to analyze the display memory of
the framebuffer. An example of where this is used is in the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_bitdump()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;The usage is similar to the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_put_pixel()&lt;/code&gt; function, where
the inputs are the framebuffer pointer, and the &lt;em&gt;(x,y)&lt;/em&gt; coordinates. If the
coordinates are not within the height and width of the OLED screen, or the
framebuffer pointer is invalid the return value is -1.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;// get the value of the color at (0,10)
int8_t color = ssd1306_framebuffer_get_pixel(fbp, 0, 10);
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;ssd1306_framebuffer_invert_pixel&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_invert_pixel()&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;When you want to invert the color of a specific pixel location, they
could do two function calls: one to &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_get_pixel()&lt;/code&gt; to get the
current pixel color and one to &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_put_pixel()&lt;/code&gt; to change it.
However, to speed up this in a single call by using the &lt;em&gt;xor&lt;/em&gt; operation, we have
the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_invert_pixel()&lt;/code&gt; function available to us.&lt;/p&gt;

&lt;p&gt;If the color of the existing pixel at &lt;em&gt;(x,y)&lt;/em&gt; is colored (&lt;em&gt;blue&lt;/em&gt;) a call to this
function will clear it, and if the existing pixel is clear (&lt;em&gt;black&lt;/em&gt;) a call to
this function will color it.&lt;/p&gt;

&lt;p&gt;Below is a modified version of the above example, demonstrating coloring the
pixel using the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_invert_pixel()&lt;/code&gt; function.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_graphics.h&amp;gt;
/* ... some initialization code ... */

/* create the framebuffer for a OLED screen 128x32 */
// the framebuffer is clear by default
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

// for (x,y) (0, 0)
ssd1306_framebuffer_invert_pixel(fbp, 0, 0);
// for (x,y) (127, 0)
ssd1306_framebuffer_invert_pixel(fbp, fbp-&amp;gt;width - 1, 0);
// for (x,y) (0, 31)
ssd1306_framebuffer_invert_pixel(fbp, 0, fbp-&amp;gt;height - 1);
// for (x,y) (127, 31)
ssd1306_framebuffer_invert_pixel(fbp, fbp-&amp;gt;width - 1, fbp-&amp;gt;height - 1);
// for dumping the framebuffer to screen to view the changes
ssd1306_framebuffer_bitdump(fbp);

/* remove the framebuffer pointer */
ssd1306_framebuffer_destroy(fbp);
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;ssd1306_framebuffer_put_pixel_rotation&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_put_pixel_rotation()&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;There are cases that you may have when you are building display screens wher the
drawings need to be rotated in position along the screen by mirror imaging the
drawing  using a rotation method. This can be useful for drawing mirror images
of an existing diagram on one side of the screen, or for drawing rotating
images.&lt;/p&gt;

&lt;p&gt;To accomplish this task you could draw a pixel in a framebuffer and then
re-draw the same image by keeping the same &lt;em&gt;(x,y)&lt;/em&gt; coordinates, and instead rotating the drawn pixels by 90°, 180°, and 270° and back to 0°. This can be achieved by using the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_put_pixel_rotation()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;It has the same signature as the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_put_pixel()&lt;/code&gt; function,
with an extra argument of a rotation flag with values &lt;code class=&quot;highlighter-rouge&quot;&gt;1, 2 or 3&lt;/code&gt; which
represent the number of 90° rotations that need to be made. In fact, the
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_framebuffer_put_pixel()&lt;/code&gt; is the same as this function, with the
rotation flag set as 0.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;// let's draw a diagonal line on one corner
for (size_t i = 0; i &amp;lt; 16; ++i) {
    ssd1306_framebuffer_put_pixel(fbp, i, i, true);
}
ssd1306_framebuffer_bitdump(fbp);

// let's mirror the same line across all 4 corners using rotation
for (size_t i = 0; i &amp;lt; 16; ++i) {
    ssd1306_framebuffer_put_pixel_rotation(fbp, i, i, true, 1 /* rotate by 90 degrees */);
}
for (size_t i = 0; i &amp;lt; 16; ++i) {
    ssd1306_framebuffer_put_pixel_rotation(fbp, i, i, true, 2 /* rotate by 180 degrees */);
}
for (size_t i = 0; i &amp;lt; 16; ++i) {
    ssd1306_framebuffer_put_pixel_rotation(fbp, i, i, true, 3 /* rotate by 270 degrees */);
}
ssd1306_framebuffer_bitdump(fbp);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Below is what the framebuffer dump looks like with the first line on the
top-left corner, and the three mirror images of the line on the other corners.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0000 |....... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .......| 
0001 .|...... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ......|. 
0002 ..|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .....|.. 
0003 ...|.... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ....|... 
0004 ....|... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ...|.... 
0005 .....|.. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ..|..... 
0006 ......|. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .|...... 
0007 .......| ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ |....... 
0008 ........ |....... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .......| ........ 
0009 ........ .|...... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ......|. ........ 
000A ........ ..|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .....|.. ........ 
000B ........ ...|.... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ....|... ........ 
000C ........ ....|... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ...|.... ........ 
000D ........ .....|.. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ..|..... ........ 
000E ........ ......|. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .|...... ........ 
000F ........ .......| ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ |....... ........ 
0010 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0011 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0012 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0013 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0014 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0015 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0016 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0017 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0018 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0019 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001A ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001B ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001C ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001D ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001E ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001F ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0020 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0021 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0022 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0023 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0024 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0025 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0026 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0027 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0028 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0029 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002A ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002B ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002C ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002D ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002E ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002F ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0030 ........ .......| ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ |....... ........ 
0031 ........ ......|. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .|...... ........ 
0032 ........ .....|.. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ..|..... ........ 
0033 ........ ....|... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ...|.... ........ 
0034 ........ ...|.... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ....|... ........ 
0035 ........ ..|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .....|.. ........ 
0036 ........ .|...... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ......|. ........ 
0037 ........ |....... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .......| ........ 
0038 .......| ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ |....... 
0039 ......|. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .|...... 
003A .....|.. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ..|..... 
003B ....|... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ...|.... 
003C ...|.... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ....|... 
003D ..|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .....|.. 
003E .|...... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ......|. 
003F |....... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .......| 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 21 Mar 2020 00:00:00 -0400</pubDate>
        <link>https://www.stealthylabs.com/blog/2020/03/21/oled-ssd1306-framebuffer.html</link>
        <guid isPermaLink="true">https://www.stealthylabs.com/blog/2020/03/21/oled-ssd1306-framebuffer.html</guid>
        
        <category>libssd1306</category>
        
        <category>graphics</category>
        
        <category>i2c</category>
        
        <category>raspberrypi</category>
        
        
        <category>Graphics</category>
        
        <category>SSD1306</category>
        
      </item>
    
      <item>
        <title>C Library for Display on OLED Chip SSD1306</title>
        <description>&lt;p&gt;We purchased a 128x32 pixel OLED screen model SSD1306 from
&lt;a href=&quot;https://www.amazon.com/DIYmall-0-91inch-SSD1306-Display-Raspberry/dp/B07V4FRSKK&quot;&gt;DIYMall via Amazon&lt;/a&gt; to display live information on a Raspberry Pi, that we had been using for debugging a product. The requirement for a &lt;a href=&quot;https://www.raspberrypi.org&quot;&gt;Raspberry Pi&lt;/a&gt; to have an HDMI screen to see live outputs on say a GPIO or SPI pin is too cumbersome, especially when you are not in your lab environment with a TV or HDMI compatible monitor lying around.&lt;/p&gt;

&lt;p&gt;With this in mind we purchased the OLED screen that works using I2C pins on the
Raspberry Pi. If your task is simple and one-off we recommend using
&lt;a href=&quot;https://circuitpython.org/&quot;&gt;CircuitPython&lt;/a&gt; or &lt;a href=&quot;https://micropython.org&quot;&gt;MicroPython&lt;/a&gt; with the &lt;a href=&quot;https://github.com/adafruit/Adafruit_CircuitPython_SSD1306&quot;&gt;Adafruit SSD1306 library&lt;/a&gt;. More help on this can be found at &lt;a href=&quot;https://learn.adafruit.com/monochrome-oled-breakouts/python-usage-2&quot;&gt;https://learn.adafruit.com/monochrome-oled-breakouts/python-usage-2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, we wanted to write our own library for writing to the SSD1306 OLED chip
directly in C, as eventually this would be used in our internal product tools
and help with longer battery life. We tested the Adafruit library and it used
more CPU than expected.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h2 id=&quot;introduction&quot;&gt;INTRODUCTION&lt;/h2&gt;

&lt;p&gt;We are releasing this library on &lt;a href=&quot;https://github.com/stealthylabs/libssd1306&quot;&gt;Github&lt;/a&gt; as open source with the MIT license. We want anyone who uses the SSD1306 on a Raspberry Pi, and who wants a library that uses low power to use this library. This is also a library for those developers who love coding in C, such as us.&lt;/p&gt;

&lt;p&gt;The library is designed to be very easy to use. It separates out the graphics
required to display information from the communication with the chip which is done using
I2C. Eventually, we may add SPI to the communication options if needed.&lt;/p&gt;

&lt;p&gt;The library allows the user to create one or more framebuffers and generate
graphics for display on those framebuffers and then use the I2C functions to
send the framebuffers to the screen for display.&lt;/p&gt;

&lt;p&gt;Since we have a lot of &lt;a href=&quot;https://beagleboard.org/black&quot;&gt;Beaglebone Black&lt;/a&gt; boards lying around we will also be
supporting those boards, however a blog post on that will be coming soon. We
also plan to support multi-purpose debugging boards such as &lt;a href=&quot;https://www.tiaowiki.com/w/TIAO_USB_Multi_Protocol_Adapter_Lite_User%27s_Manual&quot;&gt;TUMPA Lite&lt;/a&gt;  with this library, so that when you are reverse engineering a product using TUMPA Lite, or similar board, using a Linux machine you can display live data on that board using its I2C ports and the SSD1306 screen.&lt;/p&gt;

&lt;h2 id=&quot;compile&quot;&gt;COMPILE&lt;/h2&gt;

&lt;p&gt;To compile the library we clone the repo from &lt;a href=&quot;https://github.com/stealthylabs/libssd1306&quot;&gt;Github&lt;/a&gt; and run the following commands:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;## install required pre-requisites&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;build-essential libfreetype6-dev  i2c-tools

&lt;span class=&quot;c&quot;&gt;## install optional libraries&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;libev-dev

&lt;span class=&quot;c&quot;&gt;## run our pre-configure scripts for your system&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./autogen.sh

&lt;span class=&quot;c&quot;&gt;## run the configure script with default options&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./configure

&lt;span class=&quot;c&quot;&gt;## run the configure script without libev&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./configure &lt;span class=&quot;nt&quot;&gt;--without-libev&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;## if you want to install somewhere else other than /usr/local&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./configure &lt;span class=&quot;nt&quot;&gt;--prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/path/to/local/install

&lt;span class=&quot;c&quot;&gt;## run make&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;make
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;make check

&lt;span class=&quot;c&quot;&gt;## install in /usr/local or your local path&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;make &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;i2c-tools&quot;&gt;I2C TOOLS&lt;/h2&gt;

&lt;p&gt;In the above step we installed &lt;code class=&quot;highlighter-rouge&quot;&gt;i2c-tools&lt;/code&gt; as a pre-requisite. That gives us
&lt;code class=&quot;highlighter-rouge&quot;&gt;i2cdetect&lt;/code&gt; which is a useful tool to determine the list of I2C devices attached
to the system, such as a Raspberry Pi, and also determine their address.&lt;/p&gt;

&lt;p&gt;Below is what our Raspberry Pi 3B+ showed with the OLED SSD1306 device connected
via the I2C pins.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;i2cdetect &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;
i2c-1   i2c             bcm2835 I2C adapter                     I2C adapter

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; /dev/i2c-1
/dev/i2c-1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Above the I2C device is &lt;code class=&quot;highlighter-rouge&quot;&gt;/dev/i2c-1&lt;/code&gt;, which is the first device in the list. This
is the filename we will be using to access the device in our code example.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;i2cdetect  &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;
10: &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;
20: &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;
30: &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; 3c &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;
40: &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;
50: &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;
60: &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;
70: &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above command output shows us the address that the I2C device, our OLED
screen, uses for every I/O request. The &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt; in the command is for the &lt;em&gt;first&lt;/em&gt;
device in the list that was generated in the previous &lt;code class=&quot;highlighter-rouge&quot;&gt;i2cdetect&lt;/code&gt; command shown
earlier.&lt;/p&gt;

&lt;h2 id=&quot;header-files&quot;&gt;HEADER FILES&lt;/h2&gt;

&lt;p&gt;As of this post, there are two header files &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_i2c.h&lt;/code&gt; and
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_graphics.h&lt;/code&gt; that are important for the developer. The developer can
include &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_i2c.h&lt;/code&gt; to include both the files.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_graphics.h&lt;/code&gt; file has the functions for the developer to draw to a
framebuffer in memory. The &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_i2c.h&lt;/code&gt; has functions required by the
developer to write to the OLED screen via the I2C bus.&lt;/p&gt;

&lt;h2 id=&quot;example&quot;&gt;EXAMPLE&lt;/h2&gt;

&lt;p&gt;We have provided examples in the &lt;code class=&quot;highlighter-rouge&quot;&gt;examples&lt;/code&gt; directory. Let’s look at the &lt;code class=&quot;highlighter-rouge&quot;&gt;test_ssd1306_i2c.c&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: this code is current as of commit &lt;code class=&quot;highlighter-rouge&quot;&gt;a7e882980245561ca58621f684101ecaa2b56690&lt;/code&gt; and may be updated in the future.&lt;/p&gt;

&lt;p&gt;Let’s look at the &lt;code class=&quot;highlighter-rouge&quot;&gt;main()&lt;/code&gt; function below.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &amp;lt;ssd1306_i2c.h&amp;gt;

int main ()
{
    fprintf(stderr, &quot;DEBUG: Using library version: %s\n&quot;, ssd1306_i2c_version());
    const char *filename = &quot;/dev/i2c-1&quot;;
    ssd1306_i2c_t *oled = ssd1306_i2c_open(filename, 0x3c, 128, 32, NULL);
    if (!oled) {
        return -1;
    }
    ssd1306_i2c_display_initialize(oled);
    sleep(3);
    ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(oled-&amp;gt;width, oled-&amp;gt;height, oled-&amp;gt;err);

    ssd1306_framebuffer_draw_bricks(fbp);
    ssd1306_framebuffer_hexdump(fbp);
    ssd1306_framebuffer_bitdump(fbp, 0, 0, true);
    ssd1306_i2c_display_update(oled, fbp);
    sleep(3);;
    ssd1306_i2c_run_cmd(oled, SSD1306_I2C_CMD_DISP_INVERTED, 0, 0);
    sleep(3);
    ssd1306_i2c_display_clear(oled);
    ssd1306_i2c_run_cmd(oled, SSD1306_I2C_CMD_DISP_NORMAL, 0, 0);
    ssd1306_framebuffer_clear(fbp);
    ssd1306_framebuffer_draw_pixel(fbp, 0, 0, false);
    ssd1306_framebuffer_draw_pixel(fbp, fbp-&amp;gt;width - 1, 0, false);
    ssd1306_framebuffer_draw_pixel(fbp, 0, fbp-&amp;gt;height - 1, false);
    ssd1306_framebuffer_draw_pixel(fbp, fbp-&amp;gt;width - 1, fbp-&amp;gt;height - 1, false);
    ssd1306_framebuffer_draw_pixel(fbp, 9, 10, false);
    ssd1306_framebuffer_bitdump(fbp, 0, 0, true);
    ssd1306_i2c_display_update(oled, fbp);
    sleep(3);
    ssd1306_framebuffer_clear(fbp);
    ssd1306_framebuffer_draw_pixel(fbp, 0, 0, true);
    ssd1306_framebuffer_draw_pixel(fbp, fbp-&amp;gt;width - 1, 0, true);
    ssd1306_framebuffer_draw_pixel(fbp, 0, fbp-&amp;gt;height - 1, true);
    ssd1306_framebuffer_draw_pixel(fbp, fbp-&amp;gt;width - 1, fbp-&amp;gt;height - 1, true);
    ssd1306_framebuffer_draw_pixel(fbp, 9, 10, true);
    ssd1306_framebuffer_bitdump(fbp, 0, 0, true);
    ssd1306_i2c_display_update(oled, fbp);
    sleep(3);
    ssd1306_i2c_run_cmd(oled, SSD1306_I2C_CMD_POWER_OFF, 0, 0);
    ssd1306_framebuffer_destroy(fbp);
    fbp = NULL;
    ssd1306_i2c_close(oled);
    oled = NULL;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We first open the device using &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_i2c_open()&lt;/code&gt;. The device filename and the
address are the same as that detected by the &lt;code class=&quot;highlighter-rouge&quot;&gt;i2cdetect&lt;/code&gt; tool as shown in the section above.&lt;/p&gt;

&lt;p&gt;The device address can be &lt;code class=&quot;highlighter-rouge&quot;&gt;0x3c&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;0x3d&lt;/code&gt; and depending on &lt;code class=&quot;highlighter-rouge&quot;&gt;i2cdetect -y 1&lt;/code&gt; you
must select the correct value. Based on the datasheet of the SSD1306, it is
highly likely to be &lt;code class=&quot;highlighter-rouge&quot;&gt;0x3c&lt;/code&gt;. The height and width of the OLED screen are provided
to the function since it initializes a copy of the memory needed to store
the graphical display RAM (GDRAM) of the chip, so we could update the GDRAM all
at once.&lt;/p&gt;

&lt;p&gt;The OLED screen comes in 3 different sizes: 128x64, 128x32 and 96x16. In our
example, we are using one that has the 128x32 size. This denotes the number of
pixels available to the user on the OLED screen.&lt;/p&gt;

&lt;p&gt;Next we initialize the display using the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_i2c_display_initialize()&lt;/code&gt;
function. This function internally executes commands that turn on the display
and make it ready for use.&lt;/p&gt;

&lt;p&gt;We now create a framebuffer object and using some framebuffer functions found in
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_graphics.h&lt;/code&gt; draw bricks onto the framebuffer object. Then we use the
&lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_i2c_display_update()&lt;/code&gt; function and pass the framebuffer object to the
display and make the screen display bricks.&lt;/p&gt;

&lt;p&gt;Some extra helper functions like &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_i2c_display_clear()&lt;/code&gt; are provided to
clear the screen. The user can also debug the framebuffer object by dumping it
to screen using the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssd1306_i2c_framebuffer_bitdump()&lt;/code&gt; function.&lt;/p&gt;

&lt;h2 id=&quot;demonstration&quot;&gt;DEMONSTRATION&lt;/h2&gt;

&lt;p&gt;Here is a short video of the demo at &lt;a href=&quot;https://youtu.be/PSZ2eYGcL0I&quot;&gt;https://youtu.be/PSZ2eYGcL0I&lt;/a&gt;.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/PSZ2eYGcL0I&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

</description>
        <pubDate>Wed, 19 Feb 2020 00:00:00 -0500</pubDate>
        <link>https://www.stealthylabs.com/blog/2020/02/19/oled-ssd1306-library.html</link>
        <guid isPermaLink="true">https://www.stealthylabs.com/blog/2020/02/19/oled-ssd1306-library.html</guid>
        
        <category>libssd1306</category>
        
        <category>graphics</category>
        
        <category>i2c</category>
        
        <category>raspberrypi</category>
        
        
        <category>Graphics</category>
        
        <category>SSD1306</category>
        
      </item>
    
      <item>
        <title>TeaTime - An Experiment</title>
        <description>&lt;p&gt;Suppose you are an independent game developer. You are facing piracy
and fake copies of your game, and you do not have the legal and economic power to
handle this problem. You want to continue making games without
getting discouraged by pirates, who most likely reside in other countries.&lt;/p&gt;

&lt;p&gt;What do you do ? How do you &lt;em&gt;prevent&lt;/em&gt; or &lt;em&gt;reduce the incentive&lt;/em&gt; to pirate your game through reverse engineering ?
&lt;!-- more --&gt;&lt;/p&gt;

&lt;p&gt;Maybe you could perform encryption of your game assets, like textures,
shaders and images, to thwart the piracy and copy-cat efforts ?
You could use standard encryption libraries like &lt;a href=&quot;https://www.openssl.org&quot;&gt;OpenSSL&lt;/a&gt;, but that
still leaves the decrypted data open to access, in CPU memory, by
anyone running a debugger on your software.&lt;/p&gt;

&lt;p&gt;What if you could use &lt;a href=&quot;https://www.opengl.org&quot;&gt;OpenGL&lt;/a&gt; to do the encryption and leave the data
in the framebuffer object and render it from there using OpenGL
itself ? Then you would never have to even extract the data from GPU
memory into CPU memory ! Debugging tools for OpenGL are not good
enough, and reverse engineering tools for OpenGL are non-existent.
&lt;em&gt;Let’s try it!&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;introducting-teatime&quot;&gt;Introducting &lt;code class=&quot;highlighter-rouge&quot;&gt;TeaTime&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;We are going to demonstrate how to perform encryption and decryption of data on the
GPU using OpenGL and &lt;a href=&quot;https://www.opengl.org/documentation/glsl/&quot;&gt;OpenGL Shading Language (GLSL)&lt;/a&gt;.
To simplify the proof of concept, we will choose &lt;a href=&quot;https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm&quot;&gt;Tiny Encryption Algorithm (TEA)&lt;/a&gt;
for the cryptography aspect.
The advantage of TEA over other algorithms is twofold:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;it is really light weight computationally, and&lt;/li&gt;
  &lt;li&gt;if you want to use it on mobile, it will not drain your battery as much as something like
&lt;a href=&quot;https://en.wikipedia.org/wiki/Advanced_Encryption_Standard&quot;&gt;AES-256&lt;/a&gt;, which is compute intensive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You could even use a simple XOR operation instead
of TEA, but that’s too easy to break. We have called this
proof-of-concept software &lt;strong&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;TeaTime&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: This blog post:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;is not for demonstrating the merits of TEA.&lt;/li&gt;
    &lt;li&gt;is not a tutorial on OpenGL.&lt;/li&gt;
    &lt;li&gt;is not a tutorial on cryptography and encryption algorithms.&lt;/li&gt;
    &lt;li&gt;only demonstrates a proof of concept.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is a simple method of implementing this idea:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create a new framebuffer in addition to the default framebuffer.&lt;/li&gt;
  &lt;li&gt;Create two textures in the framebuffer.&lt;/li&gt;
  &lt;li&gt;Load your encrypted data into the first texture.&lt;/li&gt;
  &lt;li&gt;Decrypt the texture using the TEA shader code, written in GLSL, with the output written into the second texture.&lt;/li&gt;
  &lt;li&gt;Directly operate on the second texture and transfer it to the default framebuffer or do something else with it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it! All the operations are default OpenGL operations which
you already use. You already are rendering to multiple textures and
framebuffers, why not use that rendering concept to encrypt/decrypt
data. That’s exactly what we have done with &lt;code class=&quot;highlighter-rouge&quot;&gt;TeaTime&lt;/code&gt;, and we have
abstracted it out into a high level API for you to use.&lt;/p&gt;

&lt;h2 id=&quot;description&quot;&gt;Description&lt;/h2&gt;

&lt;p&gt;The source code is available on &lt;a href=&quot;https://github.com/stealthylabs/teatime&quot;&gt;Github&lt;/a&gt; and has been tested on Debian 7.0 Linux
with AMD/ATI driver &lt;code class=&quot;highlighter-rouge&quot;&gt;fglrx&lt;/code&gt;, and using the open source NVIDIA driver &lt;code class=&quot;highlighter-rouge&quot;&gt;nouveau&lt;/code&gt;. You will need the GLUT, GLEW and
OpenGL libraries installed to run this. The minimum required OpenGL version is 3.0. Since it uses pure OpenGL calls and is written in C,
we see no reason why it will not work on iOS, Android, Windows and Mac OS X.&lt;/p&gt;

&lt;p&gt;Our test example displays a solid teapot on the window, while performing encryption and decryption of sample data in the background.&lt;/p&gt;

&lt;p&gt;If you look at the source code, the encryption part is placed in a function called &lt;code class=&quot;highlighter-rouge&quot;&gt;teatime_demo()&lt;/code&gt;. The function is reproduced here, and the comments explain what is happening.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#define INPUT_SZ 64
#define TEA_ROUNDS 32

void teatime_demo()
{
    int rc = 0;
    teatime_t *tea = NULL;
    do {
        uint32_t ilen = INPUT_SZ;
        uint32_t input[INPUT_SZ];
        uint32_t olen = INPUT_SZ;
        uint32_t output[INPUT_SZ];
        uint32_t elen = INPUT_SZ;
        uint32_t expected[INPUT_SZ];
        /* the encryption key */
        uint32_t ikey[4] = { 0xDEADBEEF, 0xCAFEFACE,
                             0xFACEB00C, 0xF00D1337 };
        uint32_t rounds = TEA_ROUNDS;
        for (uint32_t i = 0; i &amp;lt; ilen; ++i)
            input[i] = (i + 1) * 5;
        for (uint32_t i = 0; i &amp;lt; olen; ++i)
            output[i] = 0;
        for (uint32_t i = 0; i &amp;lt; elen; ++i)
            expected[i] = 0;
        /* create the framebuffer here */
        tea = teatime_setup();
        if (!tea) {
            rc = -ENOMEM;
            break;
        }
        teatime_print_version(stdout);
        /* you need to set the viewport for the framebuffer */
        rc = teatime_set_viewport(tea, ilen);
        if (rc &amp;lt; 0)
            break;
        /* Perform encryption */
        /* Create the texture with the input data */
        rc = teatime_create_textures(tea, input, ilen);
        if (rc &amp;lt; 0)
            break;
        /* here you are creating the shader program for encryption */
        /* the teatime_encrypt_source() returns a null-terminated string
         * that has the source code for the TEA shader */
        rc = teatime_create_program(tea, teatime_encrypt_source());
        if (rc &amp;lt; 0)
            break;
        /* You are providing the number of rounds of TEA that you want to run
         * and the key with which you want to encrypt*/
        rc = teatime_run_program(tea, ikey, rounds);
        if (rc &amp;lt; 0)
           break;
        /* Since we want to verify the output, we are reading it back into CPU
         * memory. Ideally you will just reuse the texture by accessing the
         * tea-&amp;gt;otexid variable for the texture. */ 
        rc = teatime_read_textures(tea, output, olen);
        if (rc &amp;lt; 0)
            break;
        /* Verify the output by doing CPU encryption and checking */
        for (uint32_t i = 0; i &amp;lt; olen &amp;amp;&amp;amp; i &amp;lt; ilen; i += 2) {
            TEA_cpu_encrypt(&amp;amp;input[i], ikey, &amp;amp;expected[i], rounds);
            printf(&quot;%u. Encrypting Input = %08x Output = %08x Expected = %08x\n&quot;, i,
                    input[i], output[i], expected[i]);
            printf(&quot;%u. Encrypting Input = %08x Output = %08x Expected = %08x\n&quot;, i + 1,
                    input[i + 1], output[i + 1], expected[i + 1]);
        }
        for (uint32_t i = 0; i &amp;lt; elen; ++i)
            expected[i] = 0;
        for (uint32_t i = 0; i &amp;lt; olen &amp;amp;&amp;amp; i &amp;lt; elen; i += 2) {
            TEA_cpu_decrypt(&amp;amp;output[i], ikey, &amp;amp;expected[i], rounds);
            printf(&quot;%u. Decrypting Input = %08x Output = %08x Expected = %08x\n&quot;, i, output[i],
                    expected[i], input[i]);
            printf(&quot;%u. Decrypting Input = %08x Output = %08x Expected = %08x\n&quot;, i + 1,
                    output[i + 1], expected[i + 1], input[i + 1]);
        }
        /* free up memory for now */
        teatime_delete_textures(tea);
        teatime_delete_program(tea);
        /* Perform decryption */
        for (uint32_t i = 0; i &amp;lt; elen; ++i)
            expected[i] = 0;
        /* the input for decryption is always the output of the encryption */
        rc = teatime_create_textures(tea, output, olen);
        if (rc &amp;lt; 0)
            break;
        /* here you are creating the shader program for decryption */
        /* the teatime_decrypt_source() returns a null-terminated string
         * that has the source code for the TEA shader */
        rc = teatime_create_program(tea, teatime_decrypt_source());
        if (rc &amp;lt; 0)
            break;
        rc = teatime_run_program(tea, ikey, rounds);
        if (rc &amp;lt; 0)
           break;
        rc = teatime_read_textures(tea, expected, elen);
        if (rc &amp;lt; 0)
            break;
        /* Verify the output by doing CPU encryption and checking */
        for (uint32_t i = 0; i &amp;lt; olen &amp;amp;&amp;amp; i &amp;lt; elen; i++) {
            printf(&quot;%u. Decrypting Input = %08x Output = %08x Expected = %08x\n&quot;, i, output[i],
                    expected[i], input[i]);
        }
        teatime_delete_textures(tea);
        teatime_delete_program(tea);
    } while (0);
    teatime_cleanup(tea);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see, we show the code for both encryption and decryption and
compare it with the CPU implementations of TEA to verify the results. The TEA
CPU implementations are available in the file &lt;code class=&quot;highlighter-rouge&quot;&gt;teapot.c&lt;/code&gt; and the
OpenGL implementation of all the &lt;code class=&quot;highlighter-rouge&quot;&gt;teatime_*&lt;/code&gt; functions are available in the files &lt;code class=&quot;highlighter-rouge&quot;&gt;teatime.h&lt;/code&gt; and
&lt;code class=&quot;highlighter-rouge&quot;&gt;teatime.c&lt;/code&gt; on &lt;a href=&quot;https://github.com/stealthylabs/teatime&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The variable &lt;code class=&quot;highlighter-rouge&quot;&gt;ikey&lt;/code&gt; is the
encryption key in the above code. Here it is a constant, but in reality you may want
to use something more dynamically generated, such as based on a user
email address or phone number or device identifier.
However, explaining key escrow and key management is out of the
scope of this blog post. Nevertheless, here are some suggestions:&lt;/p&gt;

&lt;blockquote&gt;

  &lt;ul&gt;
    &lt;li&gt;Use a unique key per asset you want to encrypt.&lt;/li&gt;
    &lt;li&gt;Try to use a set of unique keys per user of the product.&lt;/li&gt;
    &lt;li&gt;The keys should never be held in CPU memory.&lt;/li&gt;
    &lt;li&gt;Embed the key or set of keys in an encrypted asset. This will allow you to load the set of keys into texture memory and not in the CPU. Then you can decrypt the rest of the assets also on the GPU using methods like &lt;code class=&quot;highlighter-rouge&quot;&gt;TeaTime&lt;/code&gt;, and directly display them on screen.&lt;/li&gt;
    &lt;li&gt;Use a verified key exchange system like Diffie-Hellman to exchange the key that will decrypt the set of unique keys onto the GPU.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Not all of the above may be feasible for your situation, but it is definitely doable. You as a developer can make that choice.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This code is fully reusable and the shader code in GLSL is a string that gets compiled at runtime by your OpenGL driver.
This allows you, the developer, to actually use &lt;strong&gt;various&lt;/strong&gt; encryption/decryption algorithms for
different assets with the same API ! In other words, we have written
the TEA algorithm for you, you can use &lt;a href=&quot;https://en.wikipedia.org/wiki/XTEA&quot;&gt;Extended TEA (XTEA)&lt;/a&gt; or if
you are up for a challenge, you could use AES-128 or AES-256 even.&lt;/p&gt;

&lt;p&gt;The GPU is fully capable of running those operations &lt;strong&gt;as long as you
have enough GPU memory available&lt;/strong&gt;. You may also use multiple encryption algorithms for different assets, and if you’re
running an online game service, you could send the shader code over the internet as well using HTTPS or other encrypted channels.&lt;/p&gt;

&lt;p&gt;This is what the shader code for decryption looks like.&lt;/p&gt;

&lt;div class=&quot;language-glsl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#version 130
#extension GL_EXT_gpu_shader4 : enable
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;uniform&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;usampler2D&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;uniform&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uvec4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ikey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;uniform&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uvec4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;odata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;uvec4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;gl_TexCoord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x9e3779b9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ikey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ikey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ikey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ikey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ikey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ikey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ikey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ikey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
   &lt;span class=&quot;n&quot;&gt;odata&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;demonstration&quot;&gt;Demonstration&lt;/h2&gt;

&lt;p&gt;Here is a short video of the demo.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/QCYFOo-LtVQ&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;If you liked this, &lt;a href=&quot;http://0.0.0.0:12000#contact&quot;&gt;subscribe&lt;/a&gt; to our newsletter to receive our monthly research articles, or &lt;a href=&quot;https://twitter.com/stealthylabs&quot;&gt;follow us&lt;/a&gt; on Twitter.&lt;/p&gt;
</description>
        <pubDate>Wed, 03 Jun 2015 00:00:00 -0400</pubDate>
        <link>https://www.stealthylabs.com/blog/2015/06/03/teatime.html</link>
        <guid isPermaLink="true">https://www.stealthylabs.com/blog/2015/06/03/teatime.html</guid>
        
        <category>encryption</category>
        
        <category>glsl</category>
        
        <category>opengl</category>
        
        
        <category>OpenGL</category>
        
        <category>GLSL</category>
        
        <category>Encryption</category>
        
      </item>
    
      <item>
        <title>First Post!</title>
        <description>&lt;p&gt;Our interests in the field of reverse engineering, embedded hardware and
cryptography will be the main feature of this blog. As a team of reverse
engineers and developers, we encounter a variety of systems in use that are
past manufacturing date or are not supported anymore. We help recover, repurpose
or build modules to enhance such systems while maintaining their original
purpose.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;With this blog, we plan to showcase some of our past work and new work as we do
it in some cases where open sourcing our efforts is in everyone’s benefit.&lt;/p&gt;

&lt;p&gt;We hope developers will benefit from our blog.&lt;/p&gt;
</description>
        <pubDate>Fri, 01 May 2015 00:00:00 -0400</pubDate>
        <link>https://www.stealthylabs.com/blog/2015/05/01/introduction.html</link>
        <guid isPermaLink="true">https://www.stealthylabs.com/blog/2015/05/01/introduction.html</guid>
        
        
      </item>
    
  </channel>
</rss>
