A simple Android <-> Arduino Bluetooth project

This is a simple project that use a bluetooth channel in order to connect an Android application with an Arduino.
The aim of this sample is:

  • Learn how Bluetooth serial channel works
  • Learn how to configure a bluetooth device on Android
  • Send a string from Arduino to Android (in this example the value of light sensor)
  • Send a byte from Android to Arduino (in this example the LED ON-OFF status)

The Arduino schema is very simple

schema_bb

 

I’ve used an HC-06 bluetooth adapter, the cheapest (and slower) available on ebay. You can find the source code of the android application and Arduino Sketch file on my GITHUB repositories.

Now I’m ready to add bluetooth feature on my “HomeTemp” project.

 

 

Send email when webcam detects movement

In my previous post I’ve explained how to configure a DIY IP camera using motion.

Now let configure an email alert system that send email upon motion detection and the video recorded. It’s very simple.

First of all, install some software:

sudo apt-get update
sudo apt-get install sendemail libio-socket-ssl-perl libnet-ssleay-perl

If you want to use GMAIL as smtp you need to fix SSL config. Open /usr/share/perl5/IO/Socket/SSL.pm and then change

m{^(!?)(?:(SSL(?:v2|v3|v23|v2/3))|(TLSv1[12]?))$}i

to:

m{^(!?)(?:(SSL(?:v2|v3|v23|v2/3))|(TLSv1[12]?))}i

Now change motion.conf file.

# email sent when an event starts
on_event_start sendEmail -f YOURFROMEMAIL@gmail.com -t YOURTOEMAIL@gmail.com -u "[Motion] Event detected" -m "Movement has been detected on: %d %m %Y. The time of the movement was: %H:%M (Hour:Minute). Video will follow" -s smtp.gmail.com -xu YOURGMAIL@gmail.com -xp YOURGMAILPASSWORD -o tls=yes

# send video as attachment
on_movie_end sendEmail -f YOURFROMEMAIL@gmail.com -t YOURTOEMAIL@gmail.com -u "[Motion] video" -m "Video" -s smtp.gmail.com -xu YOURGMAIL@gmail.com -xp YOURGMAILPASSWORD -o tls=yes -a %f
 

DIY IP camera with Raspberry Pi

I found an old webcam somewhere in a drawer and I decided to plug the webcam to my Raspberry IP in order to build a video-surveillance system with auto motion detection.

The Hardware is simple:

  • Raspberry PI (with sdcard and power supply)
  • Microsoft LifeCam VX-500
Install motion

Motion is an open source software that can collect images and video from a webcam when a movement is detected. It has also an embedded web server that can share the livestream.

sudo apt-get update
sudo apt-get install motion
Setup motion

You also need to enable the motion daemon so that motion will always run:

sudo nano /etc/default/motion

and change the line to:

start_motion_daemon=yes

Set also the right image resolution (in my example, 640×480) and if you want change also these settings:

pre_capture 2
post_capture 2
ffmpeg_video_codec mpeg4
  • pre_capture: how many frames record before the movement
  • post_capture: how many frames record after movement stops
  • ffmpeg_video_codec: video codec (default flv)
Web server (optional)

If you want to share your image over the web you can also change the config for “Live Webcam Server” and “HTTP Based Control”. The live stream page is not protected by username-password, so BE CAREFUL!!!
Another thing: the live stream page does not work on Chrome. In order to see the stream you have to embed the “http://host:port/” address in an HTML page (using IMG tag)

Set target dir

Raspberry PI has a very small storage on SD card. It’s better to use a network folder as destination for images and video. Configure the mount point in /etc/fstab

//YOURSERVERNAME/YOURSHAREDFOLDERNAME /media/camshare cifs username=YOURSHAREDFOLDERUSERNAME,password=YOURSHAREDFOLDERPASSWORD,iocharset=utf8,file_mode=0777,dir_mode=0777 0 0

and change motion config file in order to use the new mounted network folder

target_dir /media/camshare
Schedule Webcam capture time

If you want to schedule motion start and stop, you can do it easily with crontab (as root)

crontab –e

For example, if you want to start motion at 11AM (from monday to friday) until 5PM:

00 11 * * 1-5 /usr/bin/motion
00 17 * * 1-5 killall motion

Don’t use “service motion start|stop” or “/etc/init.d/motion start|stop” as cron command because….doesn’t work. I don’t know why, maybe some weird environment variables configuration

 

Rsync : daily backup

This is a simple reminder.
I’m doing a lot of changes in my NAS and my “domotic” RaspBerry (One of these days I’m going to write down something about the Raspberry project) and I need a simple backup solution for a running linux machine.

It’s very simple, use this script and add to /etc/cron.daily

rsync -aAXv --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found"} --delete /* /path/to/backup/folder
 

Delete unwanted JPG files

How to select an huge amount of photos in CR2 (canon RAW format) and JPEG?

After any photo shooting session I have always the same problem: select the right photos and delete the bad ones among hundreds clicks.
Normally I review the RAW files, then I select which photos need some post-processing actions and delete the wrong photos. After this first step, I need to delete the JPEG files corresponding to deleted CR2 raw photos.
I wrote this simple python script that searches for missing CR2 files and add a suffix “_DELETEME” to the corresponding JPG photo, in order to easily identifying the photos to delete.

Disclaimer: this is my FIRST working python script

Prerequisite: shoot in CR and JPG format using the same file name

#!/usr/bin/python
import glob
import os.path
for image in glob.glob("*.JPG"):
   fileName = os.path.splitext(image)[0]
   print "Checking " + fileName
   if (os.path.exists(fileName+".CR2") == False):
      print "Mark as DELETEME " + image
      os.rename(image, image+"_DELETEME")

print "Done! Review the DELETEME files and if it's all OK...delete them!"
 

Cloud backup using CrashPlan

My photo archive on my MacBook is huge and my worst nightmare is loosing all my photo.
Yes, I have a time machine drive configured on my Nas.
Yes, I have also a rsync folder on my Nas.
But I want also an online solution in case of earthquake….fire…alien invasion…and so on…
Yes…I’m paranoid.

My first choice was Google drive with the 1TB storage plan (9$ / month). It was perfect: a reliable service with an acceptable upload speed and feature set. It has only a problem: no external drive support.
My photo library is growing very fast and my internal 500gb SSD is becoming too small. So I decided to move all my library on an external drive…but with the official Google Drive client I can’t sync my library anymore.

After a little search I found Insync (15$ one time). A third part Google Client with external drive support. So beautiful!!! I started the free trial and I started to upload my library (about 100gb and 30k+ files). The first bad news was that upload speed was very….very….very slow. It took about 140 hours (5+ days) to upload all files using a 100mbit Internet connection….argh!!!
The second and worst bad news was that after some days the folder simply disappears from Google drive!!! 140 hours and then … no file on the cloud.
Insync support told me that the upload speed issue will be fixed in next release…but the lost folder was a signal: Insync is not reliable…

At the end my needs are simple:
A cloud backup solution (with unlimited space?). Mac, windows and mobile client. External drive support. Not so expensive.

My first try was with Just Cloud. In one word: s**t. The unlimited plan is not truly unlimited. No automatic video backup. No automatic backup for file larger than 1gb. A lot of restriction. No such cheaper (after the first year it will cost about 150 euros every years). After some complains with support service about the not-really-unlimited plan they give me a full money refund. Kudos to customer service. But I’ll never suggest JustCloud as a solution.

My last chance is a choice between two similar services: Backblaze and Crashplan.
Both have unlimited plan and external drive support, the monthly fee is similar ( Backblaze 50$ for 1 year – Crashplan 59.99$ for 1 year ), they have multi platform and mobile client support.

So I started the free trials (30 days for Crashplan, 15 days for Backblaze) and these are some thoughts about my experience (on internet you can find a lot of more exaustive comparison):

  • Backblaze is simpler to configure but you cannot select a single folder to backup. The Italian translation is horrible. The upload speed is faster than Crashplan but the restore options have only the “download zip” choice (100gb???) or shipped hard disk drive. No tablet (iPad and android tablet) support.
  • Crashplan has a lot of possible customization via application settings. You can select any folder to backup easily. The Mac OS X client has a good Italian translation and a better UI. The restore action can be done for any folder or file using the client, file by file. Full tablet support (and also Linux client).

It has only a great drawback: the upload speed…. I notice that it use only few bandwith: at home with a 8mbit connection the upload speed was about 400kbps. At office with a 100mbit connection the top upload speed is about 2mbps.

I tried to configure the CPU usage and upload buffer in order to speed up the upload but nothing changes: seems that this is a known issue and it is very annoying especially with the first huge backup of data. There is also the possibility to receive from Crashplan an external drive, do the initial backup and send back the drive with all your data, but for my needs I can wait some days while the client finish the upload.

Finally I found the solution and now my upload speed is about 30-40mbps (UPDATE: today is 70mbps!).

  1. First of all, for the first backup disable encryption, compression and data de-duplication from the advanced backup settings (from CrashPlan support site)
  2. Then use this link in order tu speed-up the backup
  3. If you have again speed issue try to change CrashPlan destination server using this guide and repeat until your speed grows. It works for me after 4 or 5 attempts

Finally my backup is now completed. 150GB – 40k files uploaded in about 16 hours (2 working day on a 100mbit internet connection).

I send also a mail to Code42 support team regarding my speed issue: this is its reply

Thank you for contacting Code42 support. We are a shared bandwidth service, so the speeds are not going to match your ISP’s advertised speeds. That being said, the changes you reference (http://networkrockstar.ca/2013/09/speeding-up-crashplan-backups/ and http://lifehacker.com/speed-up-crashplan-backups-and-free-up-cpu-power-with-t-1658888517) are unsupported changes to the application XML, and we highly discourage making these changes. I would suggest taking a look here:

http://support.code42.com/CrashPlan/Latest/Configuring/Unsupported_Changes_To_CrashPlan_De-Duplication_Settings

This will give you an idea and what exactly is being effected by this change, not including any other unknown consequences. Furthermore, we can’t really troubleshoot unsupported changes to the application, so I would first suggest removing the current installation and reinstalling it with a supported version, and if you continue to have issues let us know.

http://support.code42.com/CrashPlan/Latest/Troubleshooting/Uninstalling_And_Reinstalling_CrashPlan

Keep in mind that our minimum threshold is around 1Mbps, and while most users experience speeds well above this, others see speeds only marginally above this. My point is that we cannot guarantee your speeds will improve, but we can at least then look to make sure nothing is going wrong.

By the way my configuration now works fine, so I will not change it and let’s see if something goes wrong. Otherwise I will revert my config, but with the initial backup done and a lot of time saved.

Only two thing are missing: a “photo gallery” feature for mobile client and web access, and folder sharing option (two features available with Google Drive). But this is a cloud backup solution, and it simply does a cloud backup. I hope that in the next releases something will be implemented 🙂

 

DIY Home NAS: add MiniDLNA support

A little addendum about the Alix + Voyage linux guide. I forget to add a little service on my NAS, useful with my Samsung smart TV: a DLNA server.

Install MiniDLNA
# sudo apt-get install minidlna
# sudo apt-get clean
Configure MiniDLNA

Now we need to configure the MUSIC and FILM folders and let MiniDLNA scans and populates its database. Minidlna config file is /etc/minidlna.conf. Stop MiniDLNA with # service minidlna stop and edit the config file. Remember to change db_dir and log_dir options (because Voyage filesystem is in read-only mode, you need to put the MiniDLNA database on your external USB drive. For example

# Path to the directory that should hold the database and album art cache.
db_dir=/mnt/MUSIC/minidlna

# Path to the directory that should hold the log file.
log_dir=/mnt/MUSIC/minidlna/logs

You need also to define the video and audio folders, for example

media_dir=V,/mnt/MUSIC/SERIE_TV
media_dir=V,/mnt/MUSIC/FILM
media_dir=A,/mnt/MUSIC/mp3
Some others minor configurations

You can also change the friendly_name option and model_name if you want to rename your MiniDLNA server name.

Now you can remountro your filesystem and start MiniDLNA server. The server will scan all your media folder and will be ready for sharing your media files with any DLNA client.

 

Some thoughts about WhatsApp + VoIP

Today WhatsApp has released a new version with the green blue read notification icon.

I downloaded it and tried to inspect the APK in searching of something interesting.

Tools:

  • WhatsApp APK
  • Apktool – in order to analyze XML resources
  • Dex2Jar – if you want to analyze the (obfuscated) source code

These are some thoughts about the next VoIP feature after a fast and quick analysis. I think that with enough time it’s possible to find a lot of interesting things (for example: it is possible to enable VoIP feature hacking the smali files?).

VoIP engine is fully implemented (I think). The native library libwhatsapp.so contains a lot of PJSIP references. This is an excerpt of a “strings libwhatsapp.so” command

Error creating stream: %d
pjmedia_stream_get_port error
Error creating conf bridge
stream_port
Error adding stream port to conf bridge
pjmedia_stream_start error
....
Unspecified audio device error (PJMEDIA_EAUD_ERR)
Unknown error from audio driver (PJMEDIA_EAUD_SYSERR)
Audio subsystem not initialized (PJMEDIA_EAUD_INIT)
Invalid audio device (PJMEDIA_EAUD_INVDEV)
Found no audio devices (PJMEDIA_EAUD_NODEV)

Another interesting thing is that there are a lot of references to SRTP protocol. SRTP is implemented by default in PjSIP but there are also some little references in .class files, so maybe the VoIP calls will be made using SRTP encryption.

error during srtp key copy for offer
error during srtp key copy for offer accept
pjmedia_transport_srtp_create failed
pjmedia_transport_srtp_start failed: %d
incompatible-srtp-key-exchange
....
%s: function srtp_protect
%s: srtp auth tag: %s
%s: function srtp_unprotect

The last interesting thing is the VoipActivity layout and the visual rendering made by Eclipse. This will be the “In call activity” ?

WhatsApp Voip Activity

Below you can analyze the Activity XML file (sorry for the bad XML rendering on this page…I need to find a better WordPress Template)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:background="@*android:color/black" android:layout_width="fill_parent" android:layout_height="fill_parent"
  xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
    <TextView android:textSize="27.0sp" android:textColor="@*android:color/white" android:gravity="left" android:id="@id/name" android:paddingLeft="16.0dip" android:paddingTop="10.0dip" android:paddingRight="16.0dip" android:paddingBottom="10.0dip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:fontFamily="sans-serif-light" />
    <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignTop="@id/name" android:layout_alignRight="@id/name" android:layout_alignBottom="@id/name">
        <View android:layout_width="0.0dip" android:layout_height="fill_parent" android:layout_weight="2.0" />
        <Button android:id="@id/report_audio_quality_btn" android:background="@*android:color/transparent" android:layout_width="0.0dip" android:layout_height="fill_parent" android:layout_weight="1.0" />
    </LinearLayout>
    <LinearLayout android:orientation="vertical" android:id="@id/footer" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true">
        <com.whatsapp.AnswerCallView android:id="@id/answer_call_slider" android:layout_width="fill_parent" android:layout_height="78.0dip" />
        <FrameLayout android:id="@id/answer_call_btns" android:layout_width="fill_parent" android:layout_height="78.0dip">
            <ImageButton android:layout_gravity="center" android:id="@id/reject_with_message" android:background="@drawable/reject_with_message_btn_background" android:padding="12.0dip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_reject_with_message" android:contentDescription="@string/voip_call_goto_chat_description" />
        </FrameLayout>
        <LinearLayout android:orientation="horizontal" android:id="@id/call_btns" android:visibility="gone" android:layout_width="fill_parent" android:layout_height="78.0dip">
            <ImageButton android:id="@id/end_call_btn" android:background="@drawable/btn_end_call_background" android:layout_width="0.0dip" android:layout_height="fill_parent" android:src="@drawable/btn_end_call" android:layout_weight="1.0" android:contentDescription="@string/voip_call_end_button_description" />
            <ImageButton android:id="@id/voip_call_btn" android:background="@drawable/btn_start_call_background" android:visibility="gone" android:layout_width="0.0dip" android:layout_height="fill_parent" android:src="@drawable/btn_start_call" android:layout_weight="1.0" android:contentDescription="@string/voip_call_start_button_description" />
        </LinearLayout>
        <LinearLayout android:orientation="horizontal" android:id="@id/audio_btns" android:visibility="gone" android:layout_width="fill_parent" android:layout_height="78.0dip">
            <LinearLayout android:orientation="vertical" android:layout_width="0.0dip" android:layout_height="wrap_content" android:layout_weight="1.0">
                <ImageButton android:id="@id/speaker_btn" android:background="#ff111111" android:paddingTop="17.0dip" android:paddingBottom="17.0dip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:src="@drawable/ic_speaker" android:contentDescription="@string/voip_call_speakerphone_toggle_description" />
                <View android:id="@id/speaker_btn_on" android:background="#b238caff" android:visibility="invisible" android:layout_width="wrap_content" android:layout_height="10.0dip" />
            </LinearLayout>
            <LinearLayout android:orientation="vertical" android:layout_width="0.0dip" android:layout_height="wrap_content" android:layout_weight="1.0">
                <ImageButton android:id="@id/chat_btn" android:background="#ff111111" android:paddingTop="17.0dip" android:paddingBottom="17.0dip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:src="@drawable/ic_chat" android:contentDescription="@string/voip_call_goto_chat_description" />
                <View android:id="@id/chat_btn_on" android:background="#b238caff" android:visibility="invisible" android:layout_width="wrap_content" android:layout_height="10.0dip" />
            </LinearLayout>
            <LinearLayout android:orientation="vertical" android:layout_width="0.0dip" android:layout_height="wrap_content" android:layout_weight="1.0">
                <ImageButton android:id="@id/mute_btn" android:background="#ff111111" android:paddingTop="17.0dip" android:paddingBottom="17.0dip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:src="@drawable/ic_mute" android:contentDescription="@string/voip_call_mute_toggle_description" />
                <View android:id="@id/mute_btn_on" android:background="#b238caff" android:visibility="invisible" android:layout_width="wrap_content" android:layout_height="10.0dip" />
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>
    <FrameLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_above="@id/footer" android:layout_below="@id/name">
        <ImageView android:id="@id/profile_picture" android:layout_width="fill_parent" android:layout_height="fill_parent" android:src="@drawable/ic_pic_contact_xlarge" android:scaleType="centerCrop" android:contentDescription="@string/contact_avatar_image" />
        <View android:layout_gravity="bottom" android:background="@drawable/call_photo_overlay" android:layout_width="fill_parent" android:layout_height="4.0dip" />
        <TextView android:textSize="28.0sp" android:textColor="@*android:color/white" android:gravity="center" android:layout_gravity="bottom" android:id="@id/user_hint" android:padding="12.0dip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:shadowColor="@*android:color/black" android:shadowDx="1.0" android:shadowDy="1.0" android:shadowRadius="2.0" android:fontFamily="sans-serif-light" />
    </FrameLayout>
    <LinearLayout android:gravity="center" android:orientation="horizontal" android:id="@id/call_status_bar" android:background="#cc4abc34" android:paddingLeft="16.0dip" android:paddingTop="7.0dip" android:paddingRight="16.0dip" android:paddingBottom="7.0dip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@id/name" android:animateLayoutChanges="true">
        <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="6.0dip" android:src="@drawable/call_icon" android:contentDescription="@string/call_icon_content_description" />
        <TextView android:textSize="16.0sp" android:textColor="#c6ffffff" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/voip_call_label" />
        <View android:layout_width="0.0dip" android:layout_height="1.0dip" android:layout_weight="1.0" />
        <TextView android:textSize="16.0sp" android:textColor="#c6ffffff" android:id="@id/call_status" android:layout_width="wrap_content" android:layout_height="wrap_content" />
    </LinearLayout>
</RelativeLayout>
 

Submit a changeset to Gerrit based on another user change

I’ve submited another change to CyanogenMod project that add the “Delete application” menu to the “Developer Option” advanced menu (link to gerrit)

An user has requested an edit to a file in order to change the string displayed in the setting application, and initially I’ve thought that the change was related to my submit (but after I realized that is another CM project…)

BTW the question is: “How to amend a commit that another user has previously changed with another changeset not in my working copy?” (…a little bit obscure….)

For example, the commit status on Gerrit is:

  • Patchset 1 : author Sarbyn
  • Patchset 2 : author Sarbyn
  • Patchset 3 : author Sarbyn
  • Patchset 4 : author XYZ

My working copy is on patchset 3 and I need to submit a change about patchset 4.

Solution

The steps are:

  • git stash
  • click on the checkout-link in Download on the right patch set and copy&paste the git command in your working copy
  • git stash pop
  • Edit the file
  • git commit –amend and keep the Change-Id the same