Projet de robot Wifi – Torture d’un routeur TPLink MR3020

J’ai en projet un robot piloté en wifi depuis un smartphone ou un PC. La base de ce robot est un routeur très économique le TP-Link MR3020.

MR3020

 

Seulement voilà, j’ai à brancher sur ce routeur:

  • Un arduino sur lequel seront connectés moteurs ( des servos modifiés ) et capteurs.
  • Une webcam basique ( Logitech CZ110 ).

 

Au départ j’avais en tête d’ajouter une connectique USB surement présente sur le microprocesseur du routeur, comme j’avais fait il y a quelque temps sur un routeur Netgear. Seulement voilà, le datasheet du microprocesseur n’est pas public et son « form factor » le rend très difficile à bidouiller.

Vous me direz, un usb, ça suffit pour la webcam. Sauf que le routeur n’a que 4Mo de mémoire flash ce qui rend impossible l’ajout de logiciels comme python et mjpeg-streamer. Il faut donc ajouter de la mémoire supplémentaire. Pour cet ajout 2 possibilités: 1 MMC utilisée avec des lignes GPIO ou 1 SD / clef en USB. Les lignes GPIO étant peu nombreuses ( sauf à retirer interrupteurs et LED ) j’ai opté pour l’USB ainsi qu’un lecteur de µSD. Je vais aussi utiliser 2 lignes GPIO en I2C pour communiquer avec l’arduino.

Je me suis procuré un hub USB STOREX à 3€ pour le démonter et récupérer son PCB. Malheureusement le hub n’est pas autoalimenté mais la consommation de la caméra + SD étant très limitée on va y remédier en branchant l’alimentation du hub sur l’USB.

Allez, hop on démonte la boîte qui est collée. Seule la carte nous intéresse. L’alimentation est inutile dans ce projet, on va la mettre de côté pour plus tard.

Le hub USB

 

La prise d’alimentation doit être retirée ainsi que les connecteurs USB afin de gagner de la place et du poids.

Hub usb / Alimentation

 

Le lecteur de µSD à 3 € est aussi démonté afin d’accéder facilement aux pistes USB. Il est démonté « proprement » pour être ensuite ré-assemblé, la carcasse de la prise USB étant indispensable au maintient de la carte µSD.

µSD reader

 

Le lecteur peut alors être directement soudé sur le hub USB toujours dans l’idée de gagner de la place.

Le hub usb

 

Ensuite, on peut ré-assembler le lecteur et le coller sur le hub.

Le hub et le lecteur de µSD

J’ai abîmé quelques pistes en dessoudant le connecteurs USB. Il faut dire qu’à 3€, le PCB ne vaut pas grand chose… mais bon c’est quand même de l’epoxy et non de la bakélite.

 

Retournons maintenant sur le routeur. Afin d’augmenter sa portée assez réduite, nous allons lui ajouter une antenne externe. Il suffit pour cela de supprimer le shunt en J4 et de venir souder l’âme du câble d’antenne sur la piste à côté de l’inscription J4 (1) et la masse sur le pad à côté (2).

 

Antenne externe en J4

 

C’est tout petit et en zoomant ont voit bien mes gros pâtés !

 

Connexion de l’antenne externe

 

voilà le résultat avec le routeur dans son coffret d’origine.

Antenne externe et console série

 

Pour ne pas abîmer le routeur, je n’ai pas dessoudé son port USB. J’ai juste soudé directement des fils sous le PCB pour les relier au hub. Un coup de fer et l’on peut rendre au routeur sa fonction d’origine. De plus, à cet endroit, la place occupée par le connecteur ne me gène pas.

 

La caméra est installée avec une nouvelle connectique à base de barette sécable en 2.54.

Le MR3020, son hub usb et la webcam

 

Voilà c’est tout pour les modifications matérielles. La suite concernera la recherche de GPIOs libres et la partie soft.

 

GPIO sur l’interrupteur de sélection du mode

 

Voilà par exemple une piste. Plutôt que de retirer les LEDS qui ont leur utilité, on peut retirer l’interrupteur de sélection du mode qui ne nous sert pas. Ça nous donnera nos 2 GPIO  (1: GND, 2: GPIO 18 & 20) pour l’i2c. Mais avant de faire ça, je vais voir s’il n’y a pas d’autres GPIO peu utiles voire inutiles comme sur son cousin le WR703N

 

Calibrer son écran sous Linux (Ubuntu 10.4)

Pop
Pop

Afin de traiter au mieux ses photos en RAW, il vaut mieux disposer d’un moniteur calibré. Pour cela il est nécessaire d’utiliser une sonde: c’est un petit périphérique USB ( On en trouve autour de 80€ ) qui vient se placer sur l’écran et qui va analyser le rendu des couleurs afin de déterminer le profil colorimétrique correspondant à votre écran. Ce profil, un fichier ICC, sera utilisé par le système pour avoir un rendu de couleur très proche de la réalité et ainsi permettre de traiter au mieux ses photos. Autant avant avec des photos JPG en 8bits l’intérêt était très limité, autant maintenant avec des fichiers RAW de 14 bits, la calibration prend tout son sens.


Sous linux, on commence à voir apparaitre des outils pour calibrer les écrans. Parmi ces logiciels, 3 vont nous intéresser ici:

  • Argyll: Logiciel permettant de gérer un grand nombre de sondes colorimétriques. Il est indispensable au calibrage de l’écran. Son utilisation est par contre peu aisée pour les novices.
  • dispcalGUI:Logiciel permettant de gérer la calibration de son écran. Il utilise Argyll pour piloter les sondes colorimétriques.
  • Gnome Color Manager: Permet de charger un profil colorimétrique dans gnome.


Première étape: Installer Argyll

Cette suite logicielle est fournie de base dans Ubuntu Lucid, il n’y a donc aucun soucis d’installation.

 sudo apt-get install argyll

Suivant le modèle de votre sonde, il faudra ou non importer le firmware du fabricant. Pour cela, reportez vous à la documentation officielle d’argyll.

Deuxième étape: installer dispcalGUI

Ce petit logiciel n’est malheureusement pas disponible dans les dépots officiels. Par contre, l’auteur nous fourni un système d’installation fort simple basé sur autopackage.






Il suffit de se rendre à cette addresse et de télécharger le fichier .package (Linux Autopackage)

ensuite dans une console rendez ce fichier exécutable:

chmod +x dispcalGUI-0.4.9.2.package

puis lancez l’installation:

./dispcalGUI-0.4.9.2.package

Suivez les étapes.

Le mot de passe peut vous être demandé plusieurs fois au cours de l’installation afin de copier les fichiers dans les répertoires système.





Les pré-requis sont vérifiés puis l’installation est lancée



Ca y est, dispcalGUI est installé sur votre système. Il peut être lancé depuis la console avec un « dispcalGUI » ou depuis le menu « Graphisme » de gnome.


Troisième étape: Installer Gnome Color Manager

La paquet fait partie des dépôts de base dans Ubuntu Lucid, il n’y a donc aucun soucis d’installation.

 sudo apt-get install gnome-color-manager


Maintenant que toute notre chaine de calibrage est prête, lançons nous !

Le calibrage

  • Branchez votre sonde (J’utilise une Spyder 3 Express)
  • Lancez dispcalGUI
  • Allez dans le menu « options » puis cliquez sur « Détecter les périphériques d’affichage est les instruments »

Vous devez alors voir le nom de votre sonde dans la fenêtre principale. Ici la Spyder 3.

Ne connaissant pas encore la signification de toutes les options du logiciel, j’ai préféré laisser les options par défaut. La seule modification apportée concerne la qualité du profil que j’ai poussé au maximum.

  • Cliquez alors sur « calibrer et caractériser ». Une fenêtre s’ouvre et vous invite à placer la sonde sur la croix. Faites le et cliquez sur « lancer la mesure ».

  • Dans le menu qui s’ouvre sur le côté, choisissez « Calibrer » en appuyant sur 7. Pour améliorer la qualité vous pouvez cocher « Arrière-plan noir » mais ce n’est pas très pratique, les derniers choix avant la calibration se faisant en aveugle.
  • Vous pouvez aller vous préparer un café, l’opération dure de longues minutes…

Vous disposez maintenant d’un fichier ICC prêt à être utilisé dans votre logiciel de traitement de photos. Vous pouvez aussi l’utiliser dans « gnome color manager » afin qu’il soit chargé au lancement de votre session. Pour cela allez dans les préférences de gnome est lancez « Gestion des couleurs ».

Si vous trouvez votre écran légèrement « jaune », ne vous inquiétez pas c’est qu’il est calibré correctement !

J’utilise BibblePro 5 ( Oui, je sais ce n’est pas Opensource, mais… ) paramétré comme suit:

  • Profil de l’écran: Le profil fraichement créé par la sonde
  • Epreuvage à l’écran: Encore le profil fraichement créé par la sonde
  • Gestion des couleurs: Profil ICC
  • Espace de travail: Pro Photo RGB

Pour ceux qui n’ont pas de sonde et qui ont comme moi un MacBook Unibody (5.1) voilà le fichier de calibration de l’écran.

Chargeur solaire pour gadgets USB ( téléphone, GPS… )

Mon premier projet 2010, tout simple mais bien utile: un chargeur de téléphone autonome. Le montage est basé sur une batterie Lithium polymère d’un élément (3.7v) qui alimente un « Mintyboost » transformant le 3.7v en 5V pour charger un périphérique USB. La charge de la batterie est assurée par un courant entre 3 et 6v au travers d’un MAX1555. Ce courant peut provenir de 3 sources:

  • Un port USB d’ordinateur.
  • Un chargeur secteur 5v.
  • Un panneau solaire.

Le montage se base sur 2 cartes open hardware provenant de 2 fournisseurs bien connus sparkfun et Adafruit industries.

L’une des cartes sert de support au MAX1555. Elle comporte 2 entrées et 2 sorties.

En entrée:

  • Un port d’alimentation 2.1mm.
  • Un mini usb.

En sortie:

  • La batterie Lipo.
  • la sortie 3.7v vers le mintyboost.

Elle est disponible préassemblée chez sparkfun

La batterie provient aussi de chez Sparkfun. C’est une batterie 1 élément de 2000 mAh.

L’autre partie, est un kit de chez Adafruit. Le mintyboost. Il n’a qu’un but: élever la tension de la batterie de 3.7v à 5v. Le kit est très simple, il faut moins de 15 min pour l’assembler.

Les instructions d’assemblage, les schémas et tout la documentation sont en ligne sur le site de ladyada.

Le panneau solaire provient lui aussi de chez adafruit.

Dernière étape: assembler l’ensemble dans un boitier de petite taille. J’ai choisi un boitier robuste en aluminium qui n’aura aucun mal à trainer dans un sac à dos.

Inside the box

L’intérieur du boitier. Comme vous pouvez le constater, on peut encore réduire un peu l’encombrement.

It work's !

Mon téléphone en charge. Le chargeur peut assurer à peu près une charge et demi du téléphone sans l’aide du panneau solaire. Avec le panneau il faut une dizaine d’heures pour recharger complètement la batterie du chargeur. Le chargeur est suffisamment petit pour rester dans un sac à portée de main. Quand vous partez en randonnée, il suffit juste d’emporter en plus le panneau solaire que l’on fixera sur le dessus du sac à dos par exemple.

L’avantage c’est qu’il peut être utilisé avec ou sans la partie solaire. Celle-ci assure une totale autonomie: rechargement de la batterie « tampon » dans la journée et charge des périphériques USB la nuit par exemple.

En se qui concerne la facture, elle est relativement élevée: ~90$ Ce chiffre pourrait être fortement réduit  en faisant nous même le circuit. Comme il s’agit d’un prototype, j’ai préféré rester sur le kit, plus simple à assembler.

Arduino PhotoLab – Schéma

Comme promis, voici le schéma de la carte principale du montage Arduino PhotoLab

Je débute avec Eagle alors mon schéma n’est pas forcément très clair, mais bon…

photolab

Le voici au format Eagle

  • Prise SENSORS: branchement des capteurs
  • Prise TTL: Branchement d’un convertisseur USB/TTL pour reprogrammer l’arduino.
  • Prise STROBE: Sortie pour la prise de commande du flash ou de l’appareil photo suivant l’usage.
  • Prise POWER: Alimentation 5V par transfo ou pack de piles.

Les 4 boutons permettent de se ballader dans les menus pour choisir les capteurs et changer les valeurs.

Il reste quelques ports dispo sur l’arduino pour un usage futur…

Je n’ai pas mis le schéma des capteurs. Vous pouvez en trouver plein sur le playground arduino ici. Il suffit d’utiliser une prise jack 3.5mm male et de la brancher sur la prise SENSORS. Vous avez alors VCC,  GND, DATA. Où DATA est un voltage entre 0 et 5 v.

Je ne suis pas sûr que mon système soit utilisable tel quel chez vous, mais vous pouvez vous en inspirer pour créer le votre. En tout cas, chez moi ça marche très bien 🙂

Pour ce qui est du code, le voici:

[code]

#include <LCD4Bit.h>

//BUG ?
#undef int()
// END BUG
#include <stdio.h>

LCD4Bit lcd = LCD4Bit(2);

//#define DEBUG 1

// global defs
#define shootPin  11
#define sensorPin 4
#define bt1Pin 3
#define bt2Pin 4
#define bt3Pin 5
#define bt4Pin 6
#define ledPin 13 //digital

#define MAXMENU 4

#define MENUSENSOR 1
#define MENUSTROBEDELAY 2
#define MENURUN 3

#define MODESENSOR 1

#define NO 0
#define YES 1
#define TEST 2

#define SOUNDSENSOR 0
#define IRSENSOR 1
#define CONTACTSENSOR 2
#define LIGHTSENSOR 3

volatile unsigned int menu0Pos = MENUSENSOR;
volatile unsigned int debounce = 0;
volatile unsigned int modeSensor = 0;
volatile unsigned int modeDrop= 0;
volatile unsigned int modeApp = MODESENSOR;
volatile unsigned int sensorType = IRSENSOR;
volatile unsigned int firstPass = 0;
volatile unsigned int sndLevel = 0;

volatile int strobeDelay = 10;

void setup() {
pinMode(ledPin,OUTPUT);
pinMode(shootPin,OUTPUT);
pinMode(bt1Pin,INPUT);
pinMode(bt2Pin,INPUT);
pinMode(bt3Pin,INPUT);
pinMode(bt4Pin,INPUT);
setLed(1);
lcd.init();

/*lcd.commandWrite(0x0F);//cursor on, display on, blink on.  (nasty!)
*/
lcd.clear();
lcd.printIn(« equinoxefr.org »);
lcd.cursorTo(2, 0);  //line=2, x=0.
lcd.printIn(« Photo lab v0.3″);
delay(2000);
fillLine(2, »FW Trigger »);
delay(2000);
lcd.clear();
setLed(0);
#ifdef DEBUG
Serial.begin (9600);
Serial.println(« start »);                // a personal quirk
#endif
}

void loop() {
int val=0;
int keyFactor=1;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  SENSOR SECTION
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (modeSensor)
{
while (1)
{
val=analogRead(sensorPin);
switch (sensorType)
{
case LIGHTSENSOR:

if (val > 500)
{
shoot();
}
//      char buffer[50];
//      strobeDelay=getValue(strobeDelay,-1,9999);
//      itoa(strobeDelay,buffer,DEC);
//      fillLine(2,buffer);
break;

case IRSENSOR:

if (val < 900)
{
shoot();
}
//      char buffer[50];
//      strobeDelay=getValue(strobeDelay,-1,9999);
//      itoa(strobeDelay,buffer,DEC);
//      fillLine(2,buffer);
break;

case SOUNDSENSOR:
if (firstPass)
{
fillLine(2, »Getting snd level »);
sndLevel=soundLevel();
clearLcdLine(2);
firstPass=0;
}
val = analogRead(sensorPin);

if ((val > (sndLevel + 40)) && (val < 1024))
{
shoot();
}

break;

case CONTACTSENSOR:
if ( val < 500)
{
shoot();
}
break;
}
}
}

/*
#ifdef DEBUG
Serial.println(menu0Pos,DEC);
#endif
*/
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  MENU SECTION
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

switch (menu0Pos)
{
case MENUSTROBEDELAY:
fillLine(1, »Strobe delay ms »);
modeSensor=0;
strobeDelay=getValue(strobeDelay,-1,9999);
if (strobeDelay==-1)
{
fillLine(2, »not used »);
}
else
{
char buffer[50];
itoa(strobeDelay,buffer,DEC);
fillLine(2,buffer);
}
break;
case MENUSENSOR:
fillLine(1, »Select sensor »);
sensorType=getValue(sensorType,0,3);
switch(sensorType)
{
case SOUNDSENSOR:
fillLine(2, »SOUND »);
firstPass=1;
break;
case IRSENSOR:
fillLine(2, »IR BARRIER »);
break;
case CONTACTSENSOR:
fillLine(2, »CONTACT »);
break;
case LIGHTSENSOR:
fillLine(2, »IR LIGHT »);
break;
}
//digitalWrite(ledPin, HIGH);
modeSensor=0;
break;

case MENURUN:
if (!modeSensor)
{
fillLine(1, »***SHOOT MODE*** »);
}
modeSensor=1;
break;
}
getMenu();
}

//
//  soundLevel()
//
int soundLevel()
{
int value=analogRead(sensorPin);

Serial.println(« Getting sound level… »);
for(int i=0; i < 50 && !modeSensor; i++)
{
value = ( value + analogRead(sensorPin) ) / 2;
delay(50);
}
Serial.println(« Done! »);

return value;

}

void clearLcdLine(int line)
{
lcd.cursorTo(line, 0);
lcd.printIn( »                     « );
}

void fillLine(int line,char* str)
{
char buffer[21];
int len=strlen(str);
for (int i=0;i<20;i++)
{
if (i < len)
{
buffer[i]=str[i];
}
else
{
buffer[i]=’ ‘;
}
}
lcd.cursorTo(line,0);
lcd.printIn(buffer);
}

void shoot()
{
if (strobeDelay > 0 )
{
delay(strobeDelay);
}
digitalWrite(shootPin,HIGH);
delay(10);
digitalWrite(shootPin,LOW);
fillLine(2, »Shoot ! »);
digitalWrite(ledPin,HIGH);
delay(3000);
digitalWrite(ledPin,LOW);
clearLcdLine(2);

}

void getMenu()
{
int bt1=digitalRead(bt3Pin);
int bt2=digitalRead(bt4Pin);

if (!bt1 && !bt2)
{
return;
}

if (bt1 && menu0Pos < MAXMENU)
{
menu0Pos++;
}
if (bt2 && menu0Pos > 0)
{
menu0Pos–;
}

}

int getValue(int value, int mini, int maxi)
{
unsigned int keyFactor=1;
int bt1=digitalRead(bt1Pin);
int bt2=digitalRead(bt2Pin);

if  (value < 50 )
{
keyFactor=1;
}
else
{
if (value >= 50 )
{
keyFactor=10;
}
}
if  ( bt1 )
{
value+=keyFactor;
}
if  ( bt2 )
{
value-=keyFactor;
}

if ( value <= mini)
{
value=mini;
}
else
{
if (value >= maxi)
{
value=maxi;
}
}
/*
if ( bt1 && bt2 )
{
modeSensor=0;
fillLine(2, »switch off »);
}
*/
return value;

}

void setLed(int value)
{
if (value)
{
digitalWrite(ledPin,HIGH);
}
else
{
digitalWrite(ledPin,LOW);
}
}
[/code]