Thursday, December 31, 2015

Touchboard scrolling

Using proximity sensor for scrolling documents
What

For Christmas I bought myself a touchboard from bareconductive. It's a board with Atmel microprocessor ATMega32U4, 12 Analog inputs, 20 digital pins etc. It's from arduino family so you can use arduino IDE for your projects. This board comes with special conductive paint called "bare-paint". In combination with 12 analog inputs you can create touch or proximity sensors.

First touch

When I first tried the proximity sensor as a volume setting, it was like some kind of a magic, haha. I was positively surprised about the proximity precision. There SDK comes up with about 10 examples. The libraries are quite good and simple. You can do anything with a few lines of code. If you want to use the board for sound effects there are two options - mp3 or midi. MP3 files can be replaced on miroSD card and for midi, there are more than 100 musical instruments. To toggle between midi and mp3 version you have to join two connectors to the board.

First exercise

My main idea was to build some kind of a music table, but while getting into it I first tried scrolling. Scrolling can be done with keyboard or mouse. The only disadvantage of mouse scrolling is that you need to click after scrolling if you want to continue writing so I prefer the keyboard version.

I used HID library which allows you to trigger keyboard/mouse controls. In this exercise I only used one electrode E0 on touch board for proximity. The logic behind is that, when user touches the sensor first time, scrolling is enabled. After you scroll to your position you have to touch sensor again to disable scrolling. The area above the sensor is divided into two parts, the closer is for direction down, the opposite one is for up direction.

The filtering from "proximity example" is used there, that smooths input and set values to range <0-50>. Second filter is function that dynamically changes the delay for keyboard execution speed. In other words, if your hand is closer to the sensor, it scrolls faster.

Result

I tried this kind of a scrolling for a few hours, it wasn't helping me, it just slowed me down. But when I showed it to my friend's younger brother he almost immediately came up with a game who can scroll to a specific line faster.


       

// compiler error handling
#include "Compiler_Errors.h"

// touch  includes
#include 
#include 
#define MPR121_ADDR 0x5C
#define MPR121_INT 4

#include 
#include 

// serial rate
#define baudRate 57600

#define SCROLL_ELECTRODE 0

// define LED_BUILTIN for older versions of Arduino
#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif

// mapping and filter definitions
#define LOW_DIFF 0
#define HIGH_DIFF 50
#define filterWeight 0.3f // 0.0f to 1.0f - higher value = more smoothing
float lastProx = 0;

#define SCROLL_ENABLED 1
#define SCROLL_DISABLED 0

#define SCROLL_MOUSE 0
#define SCROLL_KEYBOARD 1

int scrollingState = SCROLL_DISABLED;
int lastReading = 0;
int scrollType = SCROLL_KEYBOARD;

void setup() {
  Serial.begin(baudRate);
  pinMode(LED_BUILTIN, OUTPUT);
  Wire.begin();

  if (!MPR121.begin(MPR121_ADDR)) {
    Serial.println("error setting up MPR121");
    switch (MPR121.getError()) {
      case NO_ERROR:
        Serial.println("no error");
        break;
      case ADDRESS_UNKNOWN:
        Serial.println("incorrect address");
        break;
      case READBACK_FAIL:
        Serial.println("readback failure");
        break;
      case OVERCURRENT_FLAG:
        Serial.println("overcurrent on REXT pin");
        break;
      case OUT_OF_RANGE:
        Serial.println("electrode out of range");
        break;
      case NOT_INITED:
        Serial.println("not initialised");
        break;
      default:
        Serial.println("unknown error");
        break;
    }
    while (1);
  }

  MPR121.updateAll();

  Keyboard.begin();
  Mouse.begin();
  setupProximity();
}

void setupProximity() {
  // slow down some of the MPR121 baseline filtering to avoid
  // filtering out slow hand movements
  MPR121.setRegister(NHDF, 0x01); //noise half delta (falling)
  MPR121.setRegister(FDLF, 0x3F); //filter delay limit (falling)
}

void loop() {
  MPR121.updateAll();
  proximitySensor(SCROLL_ELECTRODE);
}

int getScrollingState() {
  return scrollingState;
}

void toggleScrooling() {
  if (scrollingState == SCROLL_ENABLED) {
    scrollingState = SCROLL_DISABLED;
    Serial.println("Scrolling disabled");
    digitalWrite(LED_BUILTIN, LOW);
    // when scrool by mosewheel ends where to click ?
    //     if (scrollType == SCROLL_MOUSE) {
    //      Mouse.click();
    //     }
  } else {
    scrollingState = SCROLL_ENABLED;
    Serial.println("Scrolling enabled");
    digitalWrite(LED_BUILTIN, HIGH);
  }
}

void proximitySensor(int electrode) {

  // read the difference between the measured baseline and the measured continuous data
  int reading = MPR121.getBaselineData(electrode) - MPR121.getFilteredData(electrode);

  if (lastReading < 100 && reading > 100) {
    toggleScrooling();
  }
  lastReading = reading;

  // print out the reading value for debug
  Serial.print("Proximity electrode: ");
  Serial.println(reading);

  if (getScrollingState() == SCROLL_ENABLED) {

    // constrain the reading between our low and high mapping values
    unsigned int prox = constrain(reading, LOW_DIFF, HIGH_DIFF);

    // implement a simple (IIR lowpass) smoothing filter
    lastProx = (filterWeight * lastProx) + ((1 - filterWeight) * (float)prox);
    // Serial.print("lastProx: "); Serial.println(lastProx);

    if (lastProx > 0) {

      if (lastProx < (HIGH_DIFF * 0.4) ) {
        scroll(-1, lastProx);
        // Serial.println("UP");

      } else if (lastProx > (HIGH_DIFF * 0.6) ) {
        scroll(1, lastProx);
        // Serial.println("DOWN");

      }
    }
  }

}

/*
   scroll logic - 2 levels
   100-60% proximity => direction down
   0-40% porximity => direction up
*/
void scroll(int direction, float prox) {

  if (prox > HIGH_DIFF / 2) {
    prox = abs(prox - HIGH_DIFF);
  }

  // prox is now between 0-25

  // do some smooth filter
  prox =  prox * (prox / 4) * 1.3;

  if (scrollType == SCROLL_MOUSE) {
    Mouse.move(0, 0, 1 * direction * -1);
    delay(prox);

  } else if (scrollType == SCROLL_KEYBOARD) {

     if (directi 1) {
      Keyboard.press(KEY_DOWN_ARROW);
    } else {
      Keyboard.press(KEY_UP_ARROW);
    }
    Keyboard.releaseAll();
    delay(prox);

  }
}

 

  




Share:

Thursday, November 19, 2015

Programming MCU

Programming Atmel ATmega8l microcontroller

Intro

For the next article I've decided to go further into the hardware. For my next project I've chosen to program a micro controller (MCU). I've got no experience with MCUs before and this article will show my whole process.

First steps

My colleague advised me a usb-avr programmer which is compatible with AVR Atmel micro controllers.

So I ordered this usb-avr programmer, ATmega8L microcontroller, some resistors, leds and a breadboard.
I prefer using IDEs (syntax highlights, build from one place, save time and trees) so I chose Eclipse. There is a nice tutorial how to setup an Eclipse plugin for avr programming, but mainly you just need to install these libraries.


  • gcc-avr -> library for use with GCC on Atmel AVR micro controllers
  • binutils-avr -> cross compiling version of GNU binutils
  • gdb-avr -> GDB for (remote) debugging avr binaries
  • avr-libc -> C library for use with GCC on Atmel AVR micro controllers
  • avrdude -> transfers hex file onto microcontroller

work with MCU

This MCU ATmega8l is a 8-bit RISC Architecture Microcontroller with 8Kbytes of Flash program memory. It has many cool features (timer, ADC, 3 PWM channels etc. ). Here is a full documentation.

Anyway, it has 28 pins from which 23 can be used as GPIO. These 23 pins are divided into 3 ports. Each port has 8 bits (one of ports allows only 7 bits). Setting up these ports allows us to use pins as input or output. Here is a very helpful page about avr programming.

At the end, the program is compiled into hexadecimal format, and then written onto the flash memory of a micro controller.

Testing, first project

I used 20 red LEDs (5mm) and make a field 5 x 4 of them. Each led was joined to separate pin of micro controller. After making a mapping method for each led I created some basic effects and snake with backtrack search. You can download the source code below.

download source code here


              















Share:

Wednesday, September 30, 2015

Sms grocery list

Widget with grocery list created by your received messages

Why?

Many times I faced the problem when someone has sent me a message with a list, what to buy on a way back home. My first thought was to create an invisible app that reads incoming messages and puts their content into Google Keep messages and also shows it in a widget. After some googling I found out that Google Keep doesn't support API for the 3rd parties. So I decided to create an app with this feature.

What did I do?

Application contains a receiver that allows to read all new messages. After the message is received it searches for a key words in the message. You can set up a multiple keywords to look for in a message. Another option is to set up a reminder. Reminder shows notification at the specific time in a day where at least one message was received. You can find this under the settings in a side menu of the application.

Second part of the application is a widget. Widget shows you the list of all the received messages ordered by date and separated by subtitles like: today, this week, this month.

Can I try it?

I've put it on Google play. There is no internet connectivity so users don't have to be afraid if the permission for reading messages is required. I'm looking forward to hearing your feedback.

Android app on Google Play

           


Share:

Monday, September 7, 2015

Sony Smart Watch development

Creating app for Sony Smart Watches

Intro

In the next few lines we will talk about Sony Smart Watches first model. Currently, there is the third edition of Sony Smart Watches out. Recently I had an opportunity to try some development for this Smart Watches. I bought 2 Smart Watches at a local flea market (for 3 EUR each:). They were without an adapter and a wrist strap. Because Sony only created a proprietary cable I had to make one by myself. I used some old USB cables and follow this link. If you will ever face the same problem, don't bother to create a cable with all 4 pins. You only need two pins for charging (the first is VCC, and the last is ground - from left to right).

Setup

AFAIK there is no common android SDK for watches. Each company has its own SDK package. To run some official apps, you need to install "Smart Connect" in the first place that allows you to pair watches with your device. Now you can install apps from Google Play Store. Note that none of the apps doesn't appear in an app list. This is because any of the apps contains runnable Activity. You can setup these apps through "Smart Center". These apps are then automatically push to watches.

Development environment

Here's a few useful links to setup a Sony SDK and environment for developing:


Differences from android basic development

Application is installed to watches automatically if watches are paired with the device.
For testing you can use app instead of IDE emulator. This app ("Accessory Emulator") emulates its run.
Application does not contain any Activity, instead of that, it has something similar called ControlExtension
Only a few of examples are compatible with first version of watches (BackwardCompatible*, HelloSensorPreferenceActivity)

My contribution

If you remember the game I made earlier (Quick Circle Rocket) I've created a watch version of of it. Enjoy :)
You can download this game form Google Play Store

Android app on Google Play

        

Share:

Tuesday, August 18, 2015

Working with android contacts

Detecting changes in Android contacts

Intro

In next few lines we will talk about detecting changes in Android contacts. Android Os provides a storage for contacts called Contacts Provider. Contact Provider allows you to manage an access to a structured set of data and standard interface that connects data in one process with code running from another process.

Structure

Android Contacts structure consists of many tables, here are few of them.

ContactsRaw table: (listing important rows)
  • _id - RAW_CONTACT_ID
  • account_name - e.g. email address
  • account_type - account type e.g. com.google.com
  • deleted

Table ContactsContract.RawContacts.CONTENT_URI contains original contacts. This table mainly stores raw_id. The contact details are stored in Contact Contract table.

Contacts table: (listing important rows)

  • _id - CONTACT_ID (this id refers one group of raw_contact_id )
  • lookup - link to all contact details and contact in this table

Table Contacts.CONTENT_URI consists of grouped RawContacts based on merging rules. Some of raw contacts can be automatically grouped together (more about that later). CONTACT_ID refers to the ID of group. You cannot add or edit the records directly, it is read-only table. Content Provider creates new contact if there has been created a new raw contact and cannot be grouped to any other existing contact. Content Provider also creates new contacts if raw contact is changed so much so it cannot be assigned to the actual contact. If application or syncAdapter creates a new raw contact which can be assigned to existing contact, new contact is not created.
This table also contains raw LOOKUP_KEY which is a permanent link to only one contact, even if the contact_id is changed (because of some merging) LOOKUP_KEY is still the same.

ContactContract table: (listing important rows)

  • _id - CONTACT_DETAIL_ID
  • lookup -
  • raw_contact_id - RAW_CONTACT_ID (assign to a Contacts Raw Table)
  • contact_id - CONTACT_ID (assign to a contact table)
  • version - if any record of contact has been changed, all versions are updated (all has same value)
  • data1 - data16 - data content
  • mime_type - type of record (email, number, address)
  • is_primary - if there is more instances of contact (one of them is primary, e.g. there is only one primary phone number)

Table ContactsContract.Data.CONTENT_URI contains contact detials. Each contact can have one or more records here. Example: if contact has got name, phone and email, this table will have 3 records. Contact_id is reference to Contact table. Contact_raw_id is refference to the original contact.

AggregationException table: (listing important rows)

  • RAW_CONTACT_ID1
  • RAW_CONTACT_ID2
  • AggregationExceptions.TYPE

AggregationException table contains pairs or raw contacts that should not be groupped together. Last row contains one of the following:

  • TYPE_AUTOMATIC (merging is automatic, content provider drives it)
  • TYPE_KEEP_SEPARATE (contacts are not merged, even content provider can find the match)
  • TYPE_KEEP_TOGETHER (contacts are merged and will be merged, until user wants/ this row is deleted)


Automatic merging

These are the rules for merging one or more raw contacts into the one contact:
  • name and surname is equal
  • name and surname is equal, but in different order
  • surname is the same, short first name the same
  • name or surname, and one phone/email
  • one of the contacts has no name/surname but it has same phone/email


Detecting changes

There are a few ways how to detect that the contact has been changed.

To detect that the contact has been changed you can use ContentObserver. This is an abstract method that contains two methods ( onChange(boolean selfChange), onChange(boolean selfChange, Uri uri)). The first method just triggers that something has changed. Second method returns also Uri of changed contact but it is available since API 16.

Or there is a column with a version number in the table with contact details(ContactsContract). So if you have a local db with stored versions for contacts, you can just compare the version numbers. If one contact has changed, all his versions in rows with contact details are changed.

The other way is to keep a timestamp of the last change. Then if we want to know which contact has been changed, we just use timestamp in a query. This last_change row is available since API 18.


Detecting new contacts

The best solution is, I guess, to save the last RAW_ID and every time you want to know if there is a new contact you should ask if there is contact with higher RAW_ID. There are situations for example: when changes of contact are so big, so the new raw contact id is created. In this case you have to also check the LOOKUP key. Lookup key is never changed (his first part before dash).

Detecting that contact was removed

Just query for specific contact by LOOKUP_KEY if it is still in database. This can be a little overhead because you have to do it for all the contacts you are interested in.

SyncAdapter and server synchronization

The Contacts Provider is specifically designed for handling synchronization of contacts data between a device and the online service. The purpose of the adapter is to sync your contacts, assign to your custom account with server. This plugin does stuff like checking the network availability, scheduling synchronization and restarting synchronization. This might be useful for creating your own application that contains contacts. You don't have to write it on your own.

Any problems ?

Lately I was doing some synchronization with contacts (including 3rd party ), and I faced one strange situation. After updating contact (replace content with other contact) it was removed and it created a new one(raw_contact_id has been changed). As I read documentation I found the reason that might be it (Content Provider also creates new contacts if raw contact is changed so much so it cannot be assigned to actual contact.) So I tried to reproduce this situation. I tried to modify the contact both ways: by simple queries and using a batch operation, but it was unsuccessful.

What would be great?

It would be very helpful if there was some kind of listener that says which contact (also 3rd party accounts) is new, deleted or what has been changed (all details). On the other hand, there could be some kind of a history and you can simply ask what has changed since some timestamp.

Helpful links:

explanation of: android-contact-id-vs-raw-contact-id
official documentation
nice tutorial for syncAdapter

To be honest, I wrote this article to be able to remember all these things about contacts. If something is not correct, don't hesitate and use comments below please.

Share:

Monday, June 29, 2015

Fb vs G+ android sdk

Recently I had an opportunity to implement facebook and google plus login into android application. So i decided to write a small comparison of those two (fb v4.3, google services v4.3.23)

Google plus

  • server side setup required (create new app)
  • adding generated key into android manifest
  • implementation consists of a few methods, activity implements ConnectionCallbacks and OnConnectionFailedListener

Facebook

  • server side setup required (create new app)
  • adding generated key into android manifest, and declaration of one activity
  • sdk is not compatible with eclipse (there can be found an easy workaround on stackoverflow)
  • xml customization of fb button can be done only by overwriting styles in Fb SDK (which is not a recommended solution)
  • implementation consists of only a few lines of code (initialization, button listener, onActivityResult)
  • if user enters invalid fingerprint of debug/release key, the error shows value of your key (very helpful)

Conclusion

Apart from two fb drawbacks I have to admit their documentation is really helpful. Both tutorials have the initialization process split into few short articles but I was stuck for about a half day with g+ implementation. I wasn't able to revoke user's already granted access. Finally I found out (on stackoverflow) that I missed one line which was not in main documentation.

       
/* Client used to interact with Google APIs. */
mGoogleApiClient = new GoogleApiClient.Builder(this)
    .addConnectionCallbacks(this)
    .addOnConnectionFailedListener(this)
     //use 
    .addApi(Plus.API, Plus.PlusOptions.builder().build())
     //instead of 
     //addApi(Plus.API)
    .addScope(Scopes.PLUS_LOGIN)
    .addScope(Scopes.PLUS_ME)
    .build();
Share:

Tuesday, May 5, 2015

Thermal printer perl library

Adafruit thermal printer perl library

What

Last month I've created a web GUI for my raspberry Pi thermal printer. After spending some time with testing, I was still unable to do several basic operations like print text with diacritics or create my own characters etc. So I decided to create my own library. I found out that there are plenty of them in python or C, but only few in Perl (with missing methods that I expected).

What I did

I've created a Perl library for Adafruit thermal printer. I read the original printer documentation and wrote a method for almost every command mentioned. The library consist of all default methods for altering font, barcode and image methods. I also did a few more custom methods:
  • printing images with gradient effect
  • aligning image (if image is more narrower than printing paper)
  • registering custom font from bitmap image

Notes

During the testing I gathered some facts that might be interesting:
  • The printer buffer is limited (exact size unknown), but if you sent longer text than buffer is, you will overwrite the buffer. The printed result was that the printer skipped some text. So you have to give the printer some time for printing.
  • If you print longer than 30 seconds without break, the printed text/image gets blurred. I think the hardware is not designed for such use. Anyway printed paper has only 40m.
  • The custom printing method with gradient effect is a little slow. This is because the printing head has to set the right heat value otherwise you won't get the desired effect.
  • This printer is unable to print gray scale images. The problem is that the printer automatically moves to a new line after printing the first one. I failed to find a workaround, but I'm sure it is possible to overwrite the right place in firmware but I didn't find it on the internet.



you can find the library here:
https://github.com/svab-m/adafruit-thprinter-perl


Share:

Sunday, March 15, 2015

Thermal printer with RPi

Connecting thermal printer to Raspberry Pi B+ with server side GUI

What is thermal printer?

Thermal printers are now widely used for printing recipes in almost every store. The printer uses special paper that can be written without ink just using heated tip. You can purchase one in any e-shop. There are many tutorials, on how to connect it to raspberry pi. You should use an adapter with right level of Amps, or you experience bleached or no results.

What I did

I connected the thermal printer to the raspberry-pi and repeatedly requested server for new content. I used library in python that allows me to print images, barcodes and formatted text. Then I created a web gui for creating printer content like: formatted text, canvas with few features, sudoku generator with variable level of difficulty.

     

Share:

Friday, February 27, 2015

LG G3 quick circle case app

Using LG G3 circle case SDK to create a demo application

What is the circle case?

LG provides a special case for it's G3 model. This case allows users to use the NFC technology, wireless charging with Qi standard (only for European market) and also has a circle hole on it's upper case side. This hole allows users to control about 1/3 of their display with a customized GUI app.

LG G3 SDK

LG released a small SDK for developers to access the functions of their circle case. To sum it up, there is a hidden magnet inside the case and a small sensor behind the screen part. So the SDK just provides a receiver for detecting opening/closing of the case.
       

public class QCircleActivity extends Activity {

 // [START]declared in LGIntent.java of LG Framework
 public static final int EXTRA_ACCESSORY_COVER_OPENED = 0;
 public static final int EXTRA_ACCESSORY_COVER_CLOSED = 1;
 public static final String EXTRA_ACCESSORY_COVER_STATE = "com.lge.intent.extra.ACCESSORY_COVER_STATE";
  public static final String ACTI;

       // . . .

        private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {  

   //Receives a LG QCirle intent for the cover event
   if (ACTION_ACCESSORY_COVER_EVENT.equals(action)) {

    //Gets the current state of the cover
    mQuickCoverState = intent.getIntExtra(EXTRA_ACCESSORY_COVER_STATE,
      EXTRA_ACCESSORY_COVER_OPENED);

    if (mQuickCoverState == EXTRA_ACCESSORY_COVER_CLOSED) { // closed
     //Set window flags
     setQuickCircleWindowParam();
    } 
    else if (mQuickCoverState == EXTRA_ACCESSORY_COVER_OPENED) { // opened
      //Call FullScreenActivity
      Intent callFullscreen = new Intent(mContext, FullScreenActivity.class);
      startActivity(callFullscreen);
      
      //Finish QCircleActivity
      QCircleActivity.this.finish();
    }
   }
  }
 };

       // . . .
}


For the GUI part: You can use all the layout widgets but you will only see the part inside the circle hole. This can be easily remedied by any type of layout with exact margin values.
       


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="top|center_horizontal"
    android:background="#000000">

    <RelativeLayout
        android:id="@+id/cover_main_view"
        android:layout_width="@dimen/physical_width_of_quickcircle"
        android:layout_height="@dimen/physical_height_of_quickcircle"
        android:layout_gravity="center_horizontal"
        android:background="@drawable/background">

        <!-- ... -->
    >/RelativeLayout<

</RelativeLayout>


Result of the testing

I wanted to try the circle case feature, so I decided to write a quick game. It uses accelerometer and vibration, and for storing the game scores I used Sugar ORM. The aim of the game is navigate the ship through as many gates as possible. After each passed gate the game speeds up. The User has to lean the device to control the movement of the rocket.

I also put it on Google Play Store and you can see some screenshots below.

Android app on Google Play


Share:

Tuesday, January 20, 2015

Owi robotic arm with RPi

Owi robotic with RPi controlled by keyboard, websockets, openCV

How did it start

Recently I get a Raspberry Pi (Model B+) as a present for my 27th birthday. First thing I did was that I played with the GPIOs (general purpose input/output) pins that can be programmable. The first language I found that supports GPIOs, was python library RPI.GPIO. So I tried a few basic circuits. Toggled some leds, RGB-led, speaker beeps and so on. Few months earlier a colleague at work showed me a web page with some robotic arms that were looking catchy. The closest shop that sales any robot arms, was in Czech Republic with only one model - Owi. So I decides to get it.

Connecting to RPi

The first task was to connect the Owi-arm to the Raspberry Pi. You can find the Owi robotic arm with a USB or a joystick. Mine's get a joystick controller as an output, so I had to find how to connect it with RPi. After few hours of googling I found a video that showed RPi how to toggle relays with RPi. So I got two boards with relays and also two adapters for 3V DC.

Using keyboard to control RPi

After linking all motors to RPi through relays, it was easy to write a small script to toggle the relays and move with all Owi's joints.

Keyframing

As this arm is more of a toy than a real device, it has no step-motors. So the only way to do some keyframing was to store moves as periods of time. After few moments you will realize that it is almost impossible to return the arm to the previous position with just recorded time for each motor. So it is possible to save positions and then run them, but you have to restore the arm to it's default position from time to time. The only idea how to restore the arm to default position is to use openCV and user image features to learn how the default position looks like. This might be interesting but also a waste of time, you can rather get a new arm with step-motors instead.

Control via wifi

When keyframing was done, I was wondering about other ways to control the Owi-arm. I tried it with using a local wifi. The easiest solution, I think, is to use websockets. You can run it on any device that can browse web pages with javascript. First, I tried to set up my laptop as a server (I tried php and js-Node), but I ran into some issues, and was unable to connect from RPi. So I tried to set up a RPi as a server (in python) and client as html with js. And it worked. On the video below is a short demonstration of it. I'm using keys "q/a" "w/s" "e/d" "r/f" "t/g" "y/h" to toggle the relays.

Face detection

The most exciting part what I was looking forward to, was combining a computer vision with Owi arm. The first step was to install python libs openCV. Apart from installing few libs, the task with the follow movements was easy. If you can detect the face with a rectangle, you can move Owi-arm towards it.
After trying some resolutions I was a bit disappointed, because, RPi only allows 160x120px in a real-time. Larger resolutions has unacceptable lag. The other disappointment was the restriction of speed of the motors (because it is only a toy). There is no option to set a speed, you can only toggle motors.

Conclusion

Owi robotic arm is good for fun, and maybe for easy projects. But I think it is useless for any, more complex tasks.

download source code here

        


Share: