IoT Development with Windows 10 and Raspberry Pi: Working offline - Perficient Blogs
Blog
  • Topics
  • Industries
  • Partners

Explore

Topics

Industries

Partners

IoT Development with Windows 10 and Raspberry Pi: Working offline

SQLite370.svgThis is my sixth blog post covering Windows 10 IoT Core.
Previous posts:
Part 1: https://blogs.perficient.com/microsoft/2016/01/windows-10-iot-editions-explained/
Part 2: https://blogs.perficient.com/microsoft/2016/01/iot-development-with-windows-10-and-raspberry-pi-what-you-need/
Part 3: https://blogs.perficient.com/microsoft/2016/01/iot-development-with-windows-10-and-raspberry-pi-setting-up/
Part 4: https://blogs.perficient.com/microsoft/2016/01/iot-development-with-windows-10-and-raspberry-pi-hello-world/
Part 5: https://blogs.perficient.com/microsoft/2016/01/iot-development-with-windows-10-and-raspberry-pi-public-display/
In previous blog post we development a Raspberry Pi 2 application which could be used to present a slideshow on a big public display (outdoor or indoor). That application was pulling images in real time from Flickr feed (to emulate a real image source). The problem with this approach is that application requires a constant internet connectivity, which may not be a case for a public display. The more realistic use case for such application is the following: it got connected to internet (or LAN) only for some period of time to upload images on devices. Then the device was disconnected and used offline for displaying images from local storage.
So, we need to implement the following:
– Check internet/network connectivity
– Synchronize local image storage with images on the network (i.e. load images from network to load storage)
– Display images from local storage

The most common way to storage anything on a low power device like Raspberry Pi is to use SQLite. SQLite a popular library (not database server) for mobile, low power devices which allows application to store date in structured relational storage and query that data with SQL. Fortunately, there is a port of this library to Windows Universal platform and hence to Raspberry Pi.
These are the steps to add SQLite support to your Raspberry Pi project:

  1. Install SQLite extension for Visual Studio 2015 for Universal App Platform (UAP) from here: http://sqlite.org/download.html. File name is sqlite-uap-3100100.vsix.
  2. Install SQLite.NET-PCL NuGet package (type Install-Package SQLite.Net-PCL in NuGet package manager console).
  3. Add reference to the extension downloaded to step 1 and Visual C++ 2015 Runtime for UAP (you’ll need to select Add References and then choose Universal Windows -> Extensions):

2016-01-18
Now we all set up to use SQLite in our IoT application.
First, we need to define a schema for a table where we going to to be storing our images. Just like with Entity Framework Code First, we can do this by creating an entity model class (underlying table is going to be generated automatically):

    public class StoredImage
    {
        ///
        /// Image ID
        ///
        [PrimaryKey, AutoIncrement]
        public int ImageID { get; set; }
        ///
        /// Image itself as BLOB
        ///
        public byte[] Image { get; set; }
    }

Note the [PrimaryKey] and [AutoIncrement] attributes above. They are telling SQLite framework that ImageID is going to be a primary key for StoredImage table.
Then, we need to modify our Flickr reader. In our previous example FlickrReader was preloading image names into local memory array and then was returning images one by one to caller. In this example we need to store images in local database, so it would make more sense to return all image references to caller at once:

    public sealed class FlickrReader
    {
        private const string FeedBaseUrl = "https://api.flickr.com/services/feeds/photos_public.gne";
        private readonly string _tags;
        public FlickrReader(string tags)
        {
            _tags = tags;
        }
        public async Task<ienumerable> GetImages()
        {
            // build request to flickr image feed
            var url = FeedBaseUrl + "?tags=" + _tags;
            // load image feed
            var feed = await XmlDocument.LoadFromUriAsync(new Uri(url));
            // parse out images from ATOM 1.0 feed
            var images = feed
                .DocumentElement
                .ChildNodes
                .Where(n => n.NodeName == "entry")
                .SelectMany(n => n.ChildNodes)
                .Where(n => n.NodeName == "link")
                .Where(l => l.Attributes.Any(a => a.NodeName == "rel" && (string)a.NodeValue == "enclosure"))
                .SelectMany(e => e.Attributes.Where(a => a.NodeName == "href")
                .Select(a => (string)a.NodeValue))
                .ToList();
            return images;
        }
    }

We also need to know if we are connected to network, so we are able to load images into our local database:

public class InternetConnectivity
    {
        public static bool IsConnected()
        {
            ConnectionProfile connections = NetworkInformation.GetInternetConnectionProfile();
            return
                connections != null
                && connections.GetNetworkConnectivityLevel() == NetworkConnectivityLevel.InternetAccess;
        }
    }

Now the new fun part: downloading images locally and storing them to the database:

    public sealed class ImageLoader
    {
        private int? _currentImageIdx = null;
        private bool _isWritingImages = false;
        private SQLiteConnection GetConnection()
        {
            var dbPath = Path.Combine(ApplicationData.Current.LocalFolder.Path, "db.sqlite");
            return new SQLiteConnection(new SQLite.Net.Platform.WinRT.SQLitePlatformWinRT(), dbPath);
        }
        ///
        /// Return next image from database
        ///
        ///
        public byte[] GetNextImage()
        {
            try
            {
                // get SQLite connection
                using (var conn = GetConnection())
                {
                    // get table reference
                    var table = conn.Table();
                    // select image ids
                    var ids = table.Select(i => i.ImageID).ToList();
                    int? imageID = null;
                    if (!ids.Any())
                    {
                        return null;
                    }
                    // get next image id
                    if (!_currentImageIdx.HasValue || _currentImageIdx >= ids.Count)
                    {
                        imageID = ids.First();
                        _currentImageIdx = 0;
                    }
                    else
                    {
                        imageID = ids[_currentImageIdx.Value];
                        _currentImageIdx++;
                    }
                    // select image bytes from database
                    return table
                        .Where(i => i.ImageID == imageID)
                        .Select(i => i.Image)
                        .FirstOrDefault();
                }
            }
            catch(Exception)
            {
                // we may be geting database deadlocks from time to time
                // because we reading and writing to the same table at the same time
                return null;
            }
        }
        ///
        /// Download and store images to local database
        ///
        ///
        ///
        public async Task StoreImages(IEnumerable images)
        {
            if(_isWritingImages)
            {
                return;
            }
            _isWritingImages = true;
            // get SQLite connection
            using (var conn = GetConnection())
            {
                // ensure the table is created
                conn.CreateTable();
                // get table reference
                var table = conn.Table();
                // clear the table
                conn.DeleteAll();
                foreach (string imageUrl in images)
                {
                    using (var client = new HttpClient())
                    {
                        // load image from Flickr into byte array
                        var response = await client.GetAsync(new Uri(imageUrl));
                        var buffer = await response.Content.ReadAsBufferAsync();
                        byte[] rawBytes = new byte[buffer.Length];
                        using (var reader = DataReader.FromBuffer(buffer))
                        {
                            reader.ReadBytes(rawBytes);
                        }
                        // create new database record
                        var image = new StoredImage()
                        {
                            Image = rawBytes
                        };
                        try
                        {
                            // try to insert it to database
                            conn.Insert(image);
                        }
                        catch(Exception)
                        {
                            // we may be geting database deadlocks from time to time
                            // because we reading and writing to the same table at the same time
                        }
                    }
                }
            }
            _isWritingImages = false;
        }
    }

Finally, we need to wire it all together:

    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            var imageLoader = new ImageLoader();
            // start image loading task, don't wait
            var task = ThreadPool.RunAsync(async (source) =>
            {
                await LoadImages(imageLoader);
            });
            // schedule image databaase refresh every 20 seconds
            TimeSpan imageLoadPeriod = TimeSpan.FromSeconds(20);
            ThreadPoolTimer imageLoadTimes = ThreadPoolTimer.CreatePeriodicTimer(
                async (source) =>
                {
                    await LoadImages(imageLoader);
                }, imageLoadPeriod);
            TimeSpan displayImagesPeriod = TimeSpan.FromSeconds(5);
            // display new images every five seconds
            ThreadPoolTimer imageDisplayTimer = ThreadPoolTimer.CreatePeriodicTimer(
                async (source) =>
                {
                    // get next image (byte aray) from database
                    var imageBytes = imageLoader.GetNextImage();
                    if (imageBytes != null)
                    {
                        // we have to update UI in UI thread only
                        await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
                            async () =>
                            {
                                // create bitmap from byte array
                                BitmapImage bitmap = new BitmapImage();
                                MemoryStream ms = new MemoryStream(imageBytes);
                                await bitmap.SetSourceAsync(ms.AsRandomAccessStream());
                                // display image
                                splashImage.Source = bitmap;
                            }
                        );
                    }
                }, displayImagesPeriod);
        }
        private async Task LoadImages(ImageLoader imageLoader)
        {
            // only load images when we are connected
            if (InternetConnectivity.IsConnected())
            {
                // create Flickr reader
                var reader = new FlickrReader("unicorn");
                //load Flickr images
                var images = await reader.GetImages();
                // store images to database
                await imageLoader.StoreImages(images);
            }
        }
    }

This is what we are doing in above code:

  • When application starts, we check if device is connected to internet, and if it is then we getting Flickr image feed by tag, downloading images from it and storing them in local SQLite database.
  • We are also scheduling the above process to run every 20 seconds, so if we didn’t have network connectivity originally, but got connected at some point of time, then we still can load images locally.
  • In parallel, we starting process which is loading images from local database and displaying them on the screen.
  • Having all of the above, application is able to handle both connected and disconnected states gracefully.

Full code for this blog post is available in my Github repository: https://github.com/starnovsky/IoTDisplayWithLocalStorage

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Subscribe to the Weekly Blog Digest:

Sign Up