Virtual Reality (short post)

Some weeks ago I bought an Oculus Quest 2 and beginning experimenting with WebXR technologies. It’s real fun!

Two little projects I’ve put together are these:

1) Painting stuff in 3d: https://greeter.website/pagu-webxr/three/

You can also read the blog post on my company’s blog (in italian): https://blog.dmnk.cloud/index.php/2021/04/01/realta-virtuale-oculus-quest-2/

2) The cubes game: https://app.dmnk.cloud/the-cubes-game/three/

Also here we’ve posted to the company’s blog (still in italian): https://blog.dmnk.cloud/index.php/2021/04/19/the-cubes-game-un-piccolo-gioco-in-realta-virtuale/

Radio Lockdown

Just keeping track of the projects.. On the 28th of October 2020 Radio Lockdown was founded. It’s a web radio in which me and my gf talk nonsense, but unbelivably on the second day we hit almost 200 active listeners.. wow. Anyways, going back to the technical details, it was so easy to make it from scratch:

  1. install icecast2 on the server
  2. configure icecast2 following the instructions (from apt-get)
  3. install darkice on the client
  4. configure /etc/darkice.cfg specifying the device (I used ‘pulse’ which uses the system sound in ubuntu) – if you don’t have a default darkice.cfg I think you can find an example somewhere in /usr/share
  5. run darkice and speak into the mic
  6. make a simple website with an audio element sourcing from your icecast2 address

Of course there are same small tweaks you can do, like could be useful to know that the default maximum number of simultaneous listeners is set to 100, that was why we had only 99 listeners for quite some time yesterday evening. And then if you wanna have same good sound too, you should probably connect a decent microphone or equipment, but that’s a different story.

So that’s how Radio Lockdown is born. But why? As the name itself tells, because of the lockdown imposed in France starting today. We stay isolated, but radio-connected!

If you want to listen for some trash talking, we don’t have a plan but we do have an url (we speak mostly italian):

http://radiolockdown.online

JS Genetic Curve Fitter

While I was working on Coronamap.it, in the new section dedicated to analysis, I felt the need to do some curve fitting, that is I had a series of points and I wanted to find a curve which best fit the points, given some constraints. Of course I expected this to be a well known and well solved problem, only to discover that of course yes it is, but it’s actually something not necessarily easy to do. It’s a complex problem which requires to search in a large space of solutions, and as that not an easy task to be done in JS (which I required). Fortunately, I found an implementation in JS which used a genetic algorithm approach, so I decided to rewrite another one from scratch using my old project JSGenetic. If it does not make any sense to you, it’s because it does not have any – except that I love to make this kind of stuff.

So enough for the story, let’s talk about the library:

Example here

Usage:

var cf = GeneticCurveFitter(points, functionGenerator, 3, { //3 is the number parameters you need to tune to find the fitting curve
    RANGES: [
        [1, 2000], //one range for every parameter - if not specified, they fall back to [-1000,1000] which is kind of silly
        [1580000000000, 1590000000000],
        [10000000, 1000000000]
    ]
});
functionGenerator

is a function which gets the generated parameters in input and returns the function which you want to optimize. Easier to explain with an example:

//polynomial functionGenerator
function polyGen(coeffs) {
  return function(x) {
    var result = 0;
    for(var i=0; i<coeffs.length; i++) {
      result += coeffs[i]*Math.pow(x,i);
    }
    return result;
  }
}

//gaussian from the example
var gaussGen = function (coeff) {
    return function (x) {
        return coeff[0] * Math.exp(-((x - coeff[1]) * (x - coeff[1])) / (2 * coeff[2] * coeff[2]));
    }
}

After initializing the object, it can be easily run like this:

var resultFn =   cf.fit();

which returns the best fit function it could find. The process takes some seconds and the time can vary by CPU power and number of coefficients to find. The returned function also has an helper method to generate a chart from it (read: to sample it) which goes like this:

var samples = resultFn.toDataset(startX,endX,numberOfSamples);
//samples is in the form [{x:,y:}, ...]

There are also other methods to better control the evolution of the coefficients: here are the library’s returned methods:

{
        step: step, //make a single step of the genetic algoritm, or N steps if you call cf.step(N)
        fit: fit, //already described before
        getCurrentSolution: getCurrentSolution, //gets current solution
        getCurrentFitness: getCurrentFitness,//gets current error
        getCurrentCoefficients: getCurrentCoefficients //get raw coefficients as an array
}

That’s all! Enjoy.

Update on Coronamap: Coronamap analysis

We added to coronamap.it the capability of loading custom analysis coded by users in JS.

A list of the analyses can be found here.

Analysis are sorted by points, which you can give by liking them when opened. You can also fork any analysis and modify it as you wish. Everything is in beta, we’re still working on improving the system.

The creation of new analyses can be made frome the Coronamap Analysis Editor.

Audio 3D

A very very simple JS API to process an audio file trying to make it sound like the sound source is moving in space. Work in progress, likely never to be finished.
Link here

Instructions:
0) put on some headphones
1) press play button
2) you can try and equalize manually if you want
3) move the position of the source hovering the box with the mouse
4) press Circle to make the source circle automatically
5) refresh the page to test again

The underlying API is very simple, it accepts a simple setXY(x,y) which expects two relative coordinates ranging [-1,1]

Coronamap.it

My colleague Marco Dalle Feste and I are still quite shocked by how the Coronavirus situation has developed in Italy in the last weeks. Everything was shut down, and our families and friends were suddenly projected in this new and surreal situation of isolation. In a situation like this, where everyone feels powerless, we decided to do something, a small gesture, at least to try to give some contribution to the world in this difficult times.

We created coronamap.it, a simple webapp to show data of the spreading of the COVID-19 contagion. The app is fairly simple, as it gets some data and displays it in an interactive map providing cases information, with various levels of precision: as you might expect, the italian situation is quite accurate, showing daily updated data from our Ministry of Health, discerned to the province (county) level. For the rest of the world, we stand upon giant’s shoulders: we are relying on data from the Johns Hopkins University, and we plan to increase the level of granularity of the data soon by implementing other sources for the worst impacted zones.

The coronamap app

By a technical point of view, our approach wants to be innovative in a sense that it wants to be ready to embed various sources, and thus crawlers, of data from a growing number of reliable national authorities. We are trying to create a centralized database of the contagion information which could concentrate a decent amount of high-accuracy uniform information.

As a last note, I want to say that we are very concerned about the situation, and personally I’d like to point out that I feel that other nations are seemingly underestimating the impact that this thing will have in every part of the world. Yes, Italy was first hit and we may have responded with some delay, but the situation was like no precedent, while information from China was scarce. Now other nations in Europe are living what we experienced some days ago and they have no excuse to wait to deploy and enforce strong measures because Italy’s situation was publicly shared without censorship of any kind, the data is there to be looked at.

For now that’s all, here you can see the webapp which is updated daily in the information and functionalities. Our company website: DMNK Engineering

Changing the DNS server on Raspberry PI with default Raspbian

Since I’ve been really astonished from the difficulties I’ve had searching for this on the web lately, I’m writing this (really short) blog post hoping to save other people’s time.

If you want to change the DNS server of the default Raspbian installation on a raspberry PI – this probably goes for most of the recent linux distributions too – you just have to edit your /etc/network/interfaces file specifying, under the desired interface, this code:

dns-nameservers 8.8.8.8

Of course replace 8.8.8.8 with your desired DNS server’s IP address.

That’s all.

See you in the next very exciting post!

Agadmatorizer

Hello everyone!

Ok, lately I’ve been approaching the fine art of chess, mostly by watching youtube videos on the matter, and with some playing on lichess.org too. So I couldn’t avoid watching tons of video of the croatian youtuber Agadmator, which is really putting a lot of effort in getting people closer to chess, by publishing one or more videos every day.

So if you’ve been watching some of his videos, you might have noticed that aside the obvious personal contribute he puts in analyzing the games, the rest of the videos is generally just showing the moves and maybe adding some kind-of-predictable comments on them. When the idea came to my mind, I couldn’t help it. I *had to* do algorithmize agadmator.

Long story short, I spent three nights developing a web app which receives a PGN game in input and then generates an (fake) agadmator video for it.

I think it’s quite funny, I’ve really been enjoying the making of it and the end result, however it’s obviously improvable.

The link (for chrome or firefox only, desktop only – sorry for that, didn’t have time to optimize it) is here: Agadmatorizer

Of course I don’t think anyone will really read this, but if someone does, and if agadmator does, I hope you enjoyed this webapp, thank you for reading this post and I’ll see you soon.

Building a Raspberry PI Car Radio

Today i’m going to write about my new project, a Raspberry PI powered radio car. For now it can provide only basic functionalities, but it can be easily improved by upgrading the software, on which i didn’t focus very much so far.

In this post i will explain how I configured the PI to accomplish the task.

What I used in this project?

  • a Raspberry PI
  • a standard 16×2 character LCD display (HDD44780 compatible)
  • usb speakers
  • a button
  • a wheel from an old mouse
  • a box

The idea

The idea is to make a car radio capable of playing music from a usb pen plugged in the PI. My radio can only shuffle the songs found, but that’s just about software, it can be improved in many ways, that is where a lot of customization may take place.

Getting the display working with the PI

The first thing to do is to understand how to drive the LCD display from the PI. This is made by connecting the display to the PI’s GPIO, and then sending the commands from the programming language, usually using a library for the HDD44780 standard.

I did this from python, but I won’t touch this part in this post because I followed a nice tutorial about this from Adafruit.

After I was able to write to the display, I wrote a little python script to print something to the display from the command line, which could be useful to write something to the lcd from a shell script later:


#!/usr/bin/python
from Adafruit_CharLCD import Adafruit_CharLCD
import sys
lcd = Adafruit_CharLCD()
lcd.begin(16,1)
lcd.clear()
lcd.message(sys.argv[1])

Playing music through USB speakers

To play music through a USB device, the PI must be configured properly. In my case, I had some operation to do before everything worked, and my speakers are still showing some problems when playing from the PI (I hear a little noise, but i didn’t search enough to tell it can’t be resolved).

Anyway, if you want to use your PI as a car radio, you probably want the USB speakers to be the default audio device for it. You can do this by editing the /etc/modprobe.d/alsabase.conf adding the following line, or modifying the relative line if it already exists with another value (which in raspbian should be the case):

options snd-usb-audio index=0

I had also another problem which seemed to be quite common, and I also had to add another line at the end of that file, to avoid an “expected delay” error which I discovered running dmesg:

options snd-usb-audio nrpacks=3

I choose the higher value which didn’t give me errors in the log. After modifying the file i restarted alsa with

sudo service alsa-utils restart

This wasn’t enough to play music through the speakers, I also had to specify the right device when i played the music with mplayer. I had to discover the name of the device running the following command:

aplay -L

which gives a list of the recognized devices, from which i selected the right one, and finally got mplayer to play some mp3s:

mplayer -ao alsa:device=front my_music_file.mp3

Usb automount and autoplay

Now that the display is working correctly and we can play music through the speakers, let’s figure out how to handle the mounting/umounting actions:

The next step is to configure the PI to automatically mount any usb drive plugged in. This can be done by using the usbmount software avaiable in the raspbian repository:

sudo apt-get install usbmount

Now, I want to autoplay music when the usb is plugged in, so I add a custom script to the default usbmount script folder /etc/usbmount/mount.d :


#!/bin/bash
python /home/pi/lcd/char/print.py "Madre foca:
Playing music.."
sudo -u pi mplayer -slave -input file=/home/pi/fifofile -ao alsa:device=front -shuffle $UM_MOUNTPOINT/* $UM_MOUNTPOINT/*/* $UM_MOUNTPOINT/*/*/*

The first command uses the python script I created before to print something to the LCD when music is starting, while the third line starts the playback.

As you can guess, the $UM_MOUNTPOINT variable contains the path to the mounted drive.

The -slave mplayer option is a very useful way to let you send commands to mplayer even if it’s backgrounded. It tells mplayer to read input commands from a file, but if we provide a fifo file instead of a normal one, mplayer will read from it only when we will write something. This allows mplayer to be controlled by writing to a file, from python, shell or whatever you prefer. To create a fifo file you should use

mkfifo /home/pi/fifofile

If you want to know more on the -slave option of mplayer, see here.

I had to add the sudo -u pi part because it seemed that the user of usbmount wasn’t allowed to read from the fifo, I don’t know exactly why.

Finally, I also created a script for the umount event, which simply killed mplayer, and placed it in the /etc/usbmount/umount.d directory:


#!/bin/bash
python /home/pi/lcd/char/print.py "Music stopped.
I'm a super PI."
killall mplayer

Connecting the inputs: the button and the wheel

I’m not very good with electronics, so i won’t explain this part very deeply, also because that’s pretty basic.

The scheme to read the state of a button should be something like this: 20130318_214714

The mouse wheel should have three connectors, and it behave like two button together, with the central pin in common. The only thing to know about it is that the two “buttons” are switched on and off while you turn the wheel, but one of them is triggered a little before the other, giving you information about the direction.

I decided to use a wheel (or two button) and an additional button to provide basic input for a “menu” structured OS for the radio, which is the minimal input for a fair usability.

Reacting to the inputs

The last thing to do is to react to the input of the user, writing the real “operating system” for our radio. I wrote this in python, and the interface is really minimal, providing the wheel to control the volume and the button to skip to the next song.

As usual the code is really bad written, and it should be used only to understand how it works. I encourage you to write a real interface, you could really do anything with that.

The script (bad written of course) is this:

import RPi.GPIO as GPIO
import time
import os
from Adafruit_CharLCD import Adafruit_CharLCD

lcd = Adafruit_CharLCD()
lcd.begin(16,1)
lcd.clear()
lcd.message("Hello!nI'm the SuperPI.")

def left():
    lcd.clear()
    lcd.message("Volume up")
    os.system("amixer set PCM 5+");
    print "left"

def right():
    lcd.clear()    
    lcd.message("Volume down")
    os.system("amixer set PCM 5-");
    print "right"

def pressed():
    print "pressed"
    lcd.clear()
    lcd.message("Skipping tonnext song")
    f = open('/home/pi/fifofile','w')
    f.write('seek 100 1n')
    f.close()

GPIO.setmode(GPIO.BCM)
GPIO.setup(4, GPIO.IN)
GPIO.setup(18,GPIO.IN)
GPIO.setup(2,GPIO.IN)

last_a = False
last_b = False
last_c = True
paired = True
i=0
while True:
    a = GPIO.input(4)
    b = GPIO.input(18)
    c = GPIO.input(2)
    #print "4: "+str(a)+ " 18: " + str(b)
    if c == False and c != last_c:
        pressed()

    if a != b and paired:
        if a != last_a:
            left()
        else:
            right()
        paired = False
    else:
        paired = True
    i+=1
    last_a = a
    last_b = b
    last_c = c
    time.sleep(0.01)

Note that it uses the previous created fifo to send to mplayer the command to skip to the next song.

Assembling

I’m sure that anyone can assemble this project better than I did. And to prove it i’ll show a picture:

The video

Here’s a little video (in italian) in which I show the (few) functionalities of the radio:

I’m thinking, when I have some spare time, to extend its functions by attaching a webcam and trying to do something more complex, given the wide range of possibilities the PI can offer.

Embedding Google+ Videos in your Web Page

UPDATE: This method works but the embedded video expires after about a month, and the code must be updated. It might be possible to try to modify the embedded code to make it expire later, but I didn’t test it.

With the introduction of Google+ instant upload, many people started automatically backing up their photos and videos in Google Plus (and many started actively using G+ for this reason).

This is of course great, but there’s also a limitation: you can share the uploaded photos/videos only in Google Plus. I think it works this way to increase the popularity of the social network but that’s not very comfortable, expecially when you have ~300Mb videos already uploaded but you can’t even move them to Youtube, to share and embed them where you do prefer (eg. facebook, twitter, personal website).

I searched the Internet a lot before understanding that probably Google doesn’t want you to share Google Plus videos outside its social network, and i decided to “manually” find a way.
After a little trying i found out how to to embed Google+ videos in your personal website. I found that you can easily extract the html code to embed the video directly from G+:

The steps are few and pretty easy (i assume you use Chrome/Chromium browser because it is very comfortable for inspecting the DOM, but it can certainly be done in Firefox too):

  1. Go to Google Plus, find the video you want to embed and simply play it.
  2. When the video is playing, pause it and inspect the html (you can do this with chrome/chromium browsers by right-clicking -> Inspect Element). Be sure to right click near the video, but not on it (you can’t, it’s flash content) gplusinspect
  3. Search the DOM tree for the “<embed>” tag (See the picture)codezoom
  4. Then right click on it -> Copy as HTML (on Chrome/Chromium)
  5. Now you have an HTML code representing the video: we’re almost done! It should look like this:

<object style=”width: 1280px; height: 758px;” width=”1280″ height=”758″ classid=”clsid:d27cdb6e-ae6d-11cf-96b8-444553540000″ codebase=”http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0″ bgcolor=”#fff”><param name=”src” value=”//www.youtube.com/get_player?enablejsapi=1&amp;cc_load_policy=2″ /><param name=”allowfullscreen” value=”true” /><param name=”allowscriptaccess” value=”always” /><param name=”scale” value=”noScale” /><param name=”wmode” value=”opaque” /><param name=”flashvars” value=”fmt_list=22%2F1280x720%2F9%2F0%2F115%2C35%2F854x480%2F9%2F0%2F115%2C34%2F640x360%2F9%2F0%2F115%2C5%2F426x240%2F7%2F0%2F0&amp;fmt_stream_map=22%7Chttp%3A%2F%2Fredirector.googlevideo.com%2Fvideoplayback%3Fid%3D10af9fef8ab88134%26itag%3D22%26source%3Dpicasa%26cmo%3Dsensitive_content%3Dyes%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1363183661%26sparams%3Did%252Citag%252Csource%252Cip%252Cipbits%252Cexpire%26signature%3D8477241CE149151477AC7B259B31E65C94DBCA32.B804A52B1EDAD148A6DFDC7B417E3A922CC0B2CF%26key%3Dlh1%2C35%7Chttp%3A%2F%2Fredirector.googlevideo.com%2Fvideoplayback%3Fid%3D10af9fef8ab88134%26itag%3D35%26source%3Dpicasa%26cmo%3Dsensitive_content%3Dyes%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1363183661%26sparams%3Did%252Citag%252Csource%252Cip%252Cipbits%252Cexpire%26signature%3DB6AFABA155F7C5294D346C9EF74763382E0A9A3A.7B6B5451FA3051A6A28AFB37C923501CA93FEE8C%26key%3Dlh1%2C34%7Chttp%3A%2F%2Fredirector.googlevideo.com%2Fvideoplayback%3Fid%3D10af9fef8ab88134%26itag%3D34%26source%3Dpicasa%26cmo%3Dsensitive_content%3Dyes%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1363183661%26sparams%3Did%252Citag%252Csource%252Cip%252Cipbits%252Cexpire%26signature%3DCCA2684867495F12648E4B0D52504D7330BBDFFB.A93A0E1B7C81630C878E1E8B8AE6EC1330008D67%26key%3Dlh1%2C5%7Chttp%3A%2F%2Fredirector.googlevideo.com%2Fvideoplayback%3Fid%3D10af9fef8ab88134%26itag%3D5%26source%3Dpicasa%26cmo%3Dsensitive_content%3Dyes%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1363183661%26sparams%3Did%252Citag%252Csource%252Cip%252Cipbits%252Cexpire%26signature%3D1DADD9325E60ABC22DDC8FE45B84E89AC03BCB89.223CC73A0690DFD6584BF8BEFBA379135311CD94%26key%3Dlh1&amp;video_id=picasacid&amp;fs=1&amp;hl=it&amp;autoplay=1&amp;ps=picasaweb&amp;playerapiid=uniquePlayerId&amp;t=1&amp;vq=large&amp;auth_timeout=86400000000&amp;ttsurl=https://plus.google.com/_/photos/timedtext/100072144891672956015/5841126284110989458?&amp;ttspolicyfile=https://plus.google.com/_/photos/timedtext/crossdomain.xml” /><embed style=”width: 1280px; height: 758px;” width=”1280″ height=”758″ type=”application/x-shockwave-flash” src=”//www.youtube.com/get_player?enablejsapi=1&amp;cc_load_policy=2″ allowfullscreen=”true” allowscriptaccess=”always” scale=”noScale” wmode=”opaque” flashvars=”fmt_list=22%2F1280x720%2F9%2F0%2F115%2C35%2F854x480%2F9%2F0%2F115%2C34%2F640x360%2F9%2F0%2F115%2C5%2F426x240%2F7%2F0%2F0&amp;fmt_stream_map=22%7Chttp%3A%2F%2Fredirector.googlevideo.com%2Fvideoplayback%3Fid%3D10af9fef8ab88134%26itag%3D22%26source%3Dpicasa%26cmo%3Dsensitive_content%3Dyes%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1363183661%26sparams%3Did%252Citag%252Csource%252Cip%252Cipbits%252Cexpire%26signature%3D8477241CE149151477AC7B259B31E65C94DBCA32.B804A52B1EDAD148A6DFDC7B417E3A922CC0B2CF%26key%3Dlh1%2C35%7Chttp%3A%2F%2Fredirector.googlevideo.com%2Fvideoplayback%3Fid%3D10af9fef8ab88134%26itag%3D35%26source%3Dpicasa%26cmo%3Dsensitive_content%3Dyes%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1363183661%26sparams%3Did%252Citag%252Csource%252Cip%252Cipbits%252Cexpire%26signature%3DB6AFABA155F7C5294D346C9EF74763382E0A9A3A.7B6B5451FA3051A6A28AFB37C923501CA93FEE8C%26key%3Dlh1%2C34%7Chttp%3A%2F%2Fredirector.googlevideo.com%2Fvideoplayback%3Fid%3D10af9fef8ab88134%26itag%3D34%26source%3Dpicasa%26cmo%3Dsensitive_content%3Dyes%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1363183661%26sparams%3Did%252Citag%252Csource%252Cip%252Cipbits%252Cexpire%26signature%3DCCA2684867495F12648E4B0D52504D7330BBDFFB.A93A0E1B7C81630C878E1E8B8AE6EC1330008D67%26key%3Dlh1%2C5%7Chttp%3A%2F%2Fredirector.googlevideo.com%2Fvideoplayback%3Fid%3D10af9fef8ab88134%26itag%3D5%26source%3Dpicasa%26cmo%3Dsensitive_content%3Dyes%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1363183661%26sparams%3Did%252Citag%252Csource%252Cip%252Cipbits%252Cexpire%26signature%3D1DADD9325E60ABC22DDC8FE45B84E89AC03BCB89.223CC73A0690DFD6584BF8BEFBA379135311CD94%26key%3Dlh1&amp;video_id=picasacid&amp;fs=1&amp;hl=it&amp;autoplay=1&amp;ps=picasaweb&amp;playerapiid=uniquePlayerId&amp;t=1&amp;vq=large&amp;auth_timeout=86400000000&amp;ttsurl=https://plus.google.com/_/photos/timedtext/100072144891672956015/5841126284110989458?&amp;ttspolicyfile=https://plus.google.com/_/photos/timedtext/crossdomain.xml” bgcolor=”#fff” /></object>

Now you can (if needed) modify some parameter you find in the code, i recommend to change the
autoplay=1
to prevent the video from autostarting when the page loads.

This is not a official way to share Google Plus videos, of course, so this method may not work in the future, or the videos may someday disappear. But so far, it has worked for me.