Nowadays we hear a lot of microcomputers and IoT, they are everywhere. You see smart home projects, smart tv, smart fridge, smart calendar? This is what I will discuss in this article. How to build a smart calendar with raspberry pi and dotnet core. The product will be a web app, which starts in full screen and shows a monthly calendar, google calendar events, holidays and birthdays (you can't miss those), weather and images from a google drive folder. We will start with the hardware components and the source code necessary for this implementation which you can find in github.

What I've used for this project

  • raspberry pi 3 (model B+)
  • 2 Heatsinks
  • CPU fan
  • case
  • 16 GB (8GB is fine) micro sd memory card
  • monitor with HDMI input and HDMI cable
  • ssh & sftp client (ex: putty & bitvise ssh)
  • Web app and console apps to run in raspberry
  • Google developer account, drive and google photos

Follow https://github.com/ermirbeqiraj/digital-calendar to download the source code. Here I've composed 2 console apps that will download photos from google drive and google calendar respectively in a schedule, and a web app that will run fullscreen once the raspberry boots up.

Prepare the OS

Open https://www.raspberrypi.org/downloads/ and click Noobs, Scroll to the downloads section, pick "NOOBS Lite Network install only". This is the lightest version at this stage, as we will get the real os after we boot up the pi.

Access your sd card from your main pc. From the raspberry pi docs, it is suggested to use Sd card formatter to prepare your sd card.

Extract the noobs archive and paste the files in the newly formatted sd card.

Assemble physical parts

Remove the small cover under heat sinks and stick it to CPU and ethernet card.

  • Insert the micro sd card
  • Assemble the case (optional)
  • Assemble the fan (if applies)
  • Connect your keyboard, mouse, HDMI & ethernet cable (In case you are willing to use this instead of a wireless connection)
  • connect the power and wait for the wizard window to select your network

The installation process is pretty straight forward, just follow all the steps during the installation and after the first start. I've chosen the full raspbian OS, the light version will also work. Make sure to chose raspbian as the operating system. Once you finish this, your pi is ready to be used.

Example with connected display, mouse, and keyboard

Software components

If you are only interested in the final product, then follow already made dashboards (ex: dakboard) and you are done. There is less work to do, and you will complete it in less time. I like the journey, I want to see what can I finish in a relatively short time with dotnet core and raspberry pi. The beauty of this will be that I still have full control over the project, which I can use to extend it further after some time.

The solution for this topic consists of 3 projects. A web app, and two console apps, one to retrieve photos, and the other to get calendar events.

For displaying only a few pictures I like, I will use google drive, so I can save from photos to a specific folder in drive and then the console application will download them every day.

For calendar events, another console app will be used. we will schedule a cron job to execute this on a schedule and read the latest events and holidays.

  • visit google developers console and create a project
  • make sure the project is selected
  • click 'Enable APIs and services'
  • search and enable 'Google Drive API' & 'Google Calendar API'
  • head to the credentials section
  • click create credentials and choose 'OAuth client ID'
  • download the credentials file
  • save it in /GetImages and /GetCalendarEvents project, right click and choose 'Copy to output directory'
  • I will name it: 'dd-client-id.json' (demo drive client, yes it's a strange name), if you have chosen a different name, update CLIENT_ID_FILE_NAME constant.

Open your selected directory in google drive, and note the folder id: .../drive/folders/1mF9gGUoGggHGG_jnP_c2HKyL6o_8HqhZ

Update GetImages to use your folder id:

const string DRIVE_FOLDER_ID = "1mF9vGUoGpaHLG_jnP_c2HKyL6o_8HqhZ";

Then open GetCalendarEvents project and update the array of calendars with your favourites. Ex:

static readonly string[] CALENDARS = new string[] { "primary", "en.al#[email protected]", "#[email protected]" };

Prepare the raspbian to execute dotnet core apps

According to raspberry pi instructions page in GitHub, to execute dotnet core apps there are some prerequisite packages that need to be installed first. Packages may vary based on raspbian version. To check your version read /etc/os-release content.

Check os-version and install the required packages:

sudo cat /etc/os-release
sudo apt-get install curl libunwind8 gettext apt-transport-https

Create the following directories to accommodate web and console applications

  • /var/netcore/web
  • /var/netcore/console/calendar
  • /var/netcore/console/images

Publish all 3 projects for arm architecture using linux-arm runtime:

dotnet publish -c Release -r linux-arm

Now move the content of publish directory for each project in their respective folder in raspberry pi, and mark each project main file as executable from owner and group:

cd /var/netcore/web
chmod 775 ./WebApp

cd /var/netcore/console/calendar
chmod 775 ./GetCalendarEvents

cd /var/netcore/console/images
chmod 775 ./GetImages

Calendar and Images

Now when opening calendar and images app for the first time, make sure you are connected to the pi with a desktop environment or vnc viewer at least. You run each of them as executable apps now, ex: ./GetCalendarEvents and ./GetImages. Login to your google account and approve permissions when asked for by each application. This is a onetime action. Make sure they run successfully, if this step is not completed there is a good chance the apps lack necessary permissions, they will both need to create a token.json folder in their base directory, and have write permissions inside /var/netcore/web/wwwroot/calendar and /var/netcore/web/wwwroot/images

Web

First, execute the web app in a terminal to see if everything works as expected. Run ./WebApp in web app base directory and check the output. Next, let's build a daemon that will keep this app alive:

cd /etc/systemd/system
sudo nano webapp.service

Paste the following content:

[Unit]
Description=webapp
After=nginx.service

[Service]
Type=simple
User=pi
WorkingDirectory=/var/netcore/web
ExecStart=/var/netcore/web/WebApp
Restart=always

[Install]
WantedBy=multi-user.target

Save and exit the editor, then enable the service:

sudo systemctl enable webapp.service
sudo systemctl start webapp.service

Installing Nginx

I am going for the official repo at nginx.org so:

  • Download the key used to sign NGINX packages and the repository, and add it to the apt program’s key ring:

    sudo wget https://nginx.org/keys/nginx_signing.key
    sudo apt-key add nginx_signing.key
    
  • Edit the /etc/apt/sources.list file:

    sudo nano /etc/apt/sources.list
    
  • Add these lines to sources.list to name the repositories from which the NGINX Open Source can be obtained:

    deb https://nginx.org/packages/mainline/debian/ stretch nginx
    deb-src https://nginx.org/packages/mainline/debian/ stretch nginx
    

    note the stretch keyword, that is my version. remember, to check tours you can read /etc/os-release

  • Save and exit the editor, then install nginx

    sudo apt-get update
    sudo apt-get install nginx
    
  • Verify is running $ sudo systemctl status nginx

    If so, you will see an output simmilar to this one:

    nginx.service - A high performance web server and a reverse proxy server
    Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
    Active: active (running) since Sat 2019-01-15 15:06:15 CET; 1min 9s ago
    ......
    

Opening in your browser the IP of your raspberry now will show the default nginx website, edit the default configuration file so this serves our web app:

sudo nano /etc/nginx/sites-available/default

Remove everything it contains then add the following:

server {
    listen        80 default_server;
    server_name _;
    location / {
        proxy_pass         http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $http_host;
        proxy_cache_bypass $http_upgrade;
    }
}

Save and exit the editor, then restart the nginx service:

sudo systemctl restart nginx.service

Schedule retrieve of images and calendar events

We will schedule calendar reads daily at 4 am, and images retrieval every 6 hours. In your terminal run:

crontab -e

Now we will register 2 cron jobs:

0 4 * * * /var/netcore/console/calendar/GetCalendarEvents
0 */6 * * * /var/netcore/console/images/GetImages

Now, these crons will run without the need for interaction, because we authenticated the first time manually.

Startup in kiosk mode

In order for this to start where it is supposed when the raspberry is restarted, we need to register chromium to start with the system, in fullscreen mode:

sudo nano ~/.config/lxsession/LXDE-pi/autostart
@xset s off
@xset -dpms
@xset s noblank
@chromium-browser --noerrdialogs --incognito --kiosk http://<YOUR-RASPBERRY-IP>

What's missing

  • Ability to authenticate with service account type in google calendar and google drive. as for now, I've used OAuth2InstalledApp which is the closest one to service accounts but yet not enough
  • Temperature and humidity sensor. I hope we will be able to use those with dotnet core 3

You can find the source code for the web app and 2 console apps in https://github.com/ermirbeqiraj/digital-calendar