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