[HowTo] Localisation via Tracker OBD II

Tous les autres capteurs hors RFXCOM et hors 1-Wire. Cette section comprend les réalisations et les montages comme le relevé téléinformation d'EDF.

[HowTo] Localisation via Tracker OBD II

Messagede geminium » 06 Oct 2014, 13:27

Bonjour,

Je vous propose de décrire un système de localisation via OBD II que j'ai mis en place depuis plusieurs mois et qui à mon sens a toute sa place dans nos systèmes automatisés.
Le système de localisation repose sur un Raspberry Pi et un tracker OBD II.
J'ai vu plusieurs solutions sur le marché: GPS Gate (uniquement sous windows et beaucoup trop complexe pour mon besoin) / Traccar (mon Raspberry n'a pas aimé la lourdeur de l'application JAVA...).
La solution qui est portée par un smartphone ne m’intéresse pas car je veux, en plus de déclencher des scénarios en fonction de ma distance, surveiller mes véhicules.
Donc au final, mon besoin étant très simple, je suis parti sur une solution perso.

En tout cas, je vous laisse imaginer tout ce qu'il est possible d'en faire (anti-vol, actions domotiques, flicage...).

Mais avant toute chose, je précise que ce programme répond à un besoin personnel, c'est sous forme d'un Proof Of Concept fait un week end de mauvais temps, il ne faut donc pas le considérer comme un livrable tel quel et il sera surement nécessaire de l'adapter pour l'améliorer, le sécuriser, etc...

Hardware:
  • Raspberry Pi : A, B ou B+ => (dans mon cas un B) Raspberry + Alimentation + Boitier pour environ 50€
  • Tracker OBD II (Meitrack TC68 => Environ 90€)
  • Carte SIM avec forfait data => Faible consommation du tracker: environ 20 Mo par mois pour une utilisation classique (1H de trajet quotidien en semaine avec 3H pour les Week End avec un intervalle de 10 secondes)

Software:
  • Raspbian
  • Node JS (pour le serveur TCP / Le serveur Web / Les WebSockets) - Pourquoi Node JS ? Rien de bien rationnel, juste parce que je voulais tester cette nouvelle techno sur un projet concret
  • SQLite 3 (pour le stockage des donnĂ©es) - Pourquoi SQLite ? C'est une base lĂ©gère et très simple qui tient dans un seul fichier.
  • Nginx (optionnel - uniquement si besoin d'un reverse proxy pour ouvrir l'application Ă  l'exterieur)

Présentation du tracker:
Image
Je vous invite Ă  vous documenter sur le site du fabriquant Meitrack, mais pour faire simple, ce tracker permet :
  • une installation extrĂŞmement simple en se connectant au port OBD II de votre voiture (obligatoire depuis 2002 pour les essences et 2006 pour les diesel)
  • de s'affranchir des batteries Ă  recharger tous les X jours...En effet, le tracker se recharge via la prise OBD II automatiquement quand le vĂ©hicule est en marche
  • une très bonne localisation via une puce SIRF IV, mĂŞme si votre port OBD II est sous le tableau de bord ou dans la boite Ă  gant. NĂ©anmoins, des rallonges existent pour le faire passer sous les sièges ou autre. Il n'y a donc pas besoin que le tracker dispose d'une vue directe sur les satellites.
  • de faire remonter les coordonnĂ©es GPS toutes les X secondes
  • de se mettre en veille automatiquement après 5 minutes d'arrĂŞt
  • de se rĂ©veiller automatiquement si la voiture subit un choc ou si une personne pĂ©nètre dans le vĂ©hicule (Capteur AccĂ©lĂ©romètre + Vibration)
  • d'envoyer des informations via SMS / Data et d'Ă©couter ce qu'il se passe dans le vĂ©hicule
  • de faire remonter des informations de diagnostique de la voiture (en fonction des modèle de vĂ©hicule)
  • contrairement aux solutions via les localisations GPS par SmartPhone, il n'y a aucun impact sur la batterie de ces derniers
  • de notifier le serveur mĂŞme si une personne arrache ou arrĂŞte le tracker de la prise OBD II. En effet, il y a des Ă©vĂ©nements pour ces actions

Installation des prérequis sur raspberry
Mettez Ă  jour votre distribution raspbian:
Code: Tout sélectionner
sudo apt-get update && sudo apt-get -y upgrade && sudo apt-get -y dist-upgrade

Code: Tout sélectionner
sudo apt-get install ca-certificates git-core
sudo wget https://raw.github.com/Hexxeh/rpi-update/master/rpi-update -O /usr/bin/rpi-update && sudo chmod +x /usr/bin/rpi-update
sudo rpi-update
sudo reboot

Installation de SQLite 3 (Base de données des trackers):
Code: Tout sélectionner
sudo apt-get install sqlite3

Création du répertoire de travail:
Code: Tout sélectionner
mkdir ~/software

Installation de Node JS (Serveur TCP + Web + Websocket):
Code: Tout sélectionner
cd ~/software
wget http://nodejs.org/dist/v0.10.28/node-v0.10.28-linux-arm-pi.tar.gz
tar -xvzf node-v0.10.28-linux-arm-pi.tar.gz
rm node-v0.10.28-linux-arm-pi.tar.gz
mv node-v0.10.28-linux-arm-pi/ nodejs

Création du répertoire de nos applications:
Code: Tout sélectionner
mkdir ~/software/track/
mkdir ~/software/track/web

Et c'est tout. La solution n'aura donc rien de bien gourmand.

Base de données:
Création:
Code: Tout sélectionner
sqlite3 ~/software/track/track.db

Copier/Coller ce script:
Code: Tout sélectionner
CREATE TABLE logs (
  ID INTEGER PRIMARY KEY AUTOINCREMENT,
  TRAME TEXT NOT NULL,
  TIME NUMERIC NOT NULL,
  BASE_ID TEXT NOT NULL,
  ALTITUDE NUMERIC NOT NULL,
  LONGITUDE NUMERIC NOT NULL,
  LATITUDE NUMERIC NOT NULL,
  EVENT_CODE NUMERIC NOT NULL,
  GPS_STATUS TEXT NOT NULL,
  SPEED NUMERIC NOT NULL,
  IMEI TEXT NOT NULL,
  UNIQUE (TRAME)
);

Pour quitter:
Code: Tout sélectionner
.exit

Serveur TCP:
Le tracker a été configuré (via le logiciel fourni qui est de qualité) pour envoyer les informations sur mon serveur en spécifiant un host et un port (Je vous passe la configuration NAT qui va dépendre de vos routeurs)

Le programme qui va accepter les trames des trackers est développé en utilisant Node JS.
Le but ici n'Ă©tant pas d'apprendre Ă  utiliser Node JS, je vous laisse donc voir la documentation.

Ajout des modules Node JS nécessaires au bon fonctionnement du serveur TCP:
Code: Tout sélectionner
cd ~/software/track/
sudo ../nodejs/bin/npm install crc
sudo ../nodejs/bin/npm install dateformat
sudo ../nodejs/bin/npm install request
sudo ../nodejs/bin/npm install sqlite3


Le programme:
Fonctionnalités :
  • RĂ©cupĂ©rer / Valider / DĂ©coder / Stocker la trame
  • Actions spĂ©cifiques en fonction des Ă©vĂ©nements remontĂ©s par le tracker (cf. documentation du tracker)
    • Notifications SMS (dans mon cas, mais vous pouvez le remplacer par du prowl)
    • CrĂ©ation d'un fichier xml par vĂ©hicule pour la remontĂ©e des informations Ă  destination du web via une websocket (que nous verrons plus tard)
    • Lien avec Google Geocode pour rĂ©cupĂ©rer l'adresse Ă  partir des coordonnĂ©es GPS
Code du fichier ~/software/track/server.js:
Code: Tout sélectionner
var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database('/home/pi/software/track/track.db');
var crc = require('crc');
var readline = require('readline');
var net = require('net');
var util = require('util');
var request = require("request");
var fs = require('fs');
var dateFormat = require('dateformat');

var events = { 17: manageGenericEventNotification, 35: manageIntervalEventNotification, 26: manageGenericEventNotification, 27: manageGenericEventNotification, 144: manageEmptyEventNotification, 145: manageEmptyEventNotification, 146: manageGenericEventNotification, 147: manageGenericEventNotification, 129: manageGenericEventNotification, 130: manageGenericEventNotification, 24: manageIntervalEventNotification, 25: manageIntervalEventNotification };
var eventsTitle = { 17: "Batterie faible", 35: "Intervall", 26: "Mise en veille", 27: "Sortie de veille", 146: "Démarrage", 147: "Arrêt", 129: "Déccélération brutale", 130: "Accélération brutale", 24: "Pas de signal GPS", 25: "Acquisition signal GPS" };
var eventsMapTitle = { 17: "Batterie faible", 35: "En marche", 26: "En veille", 27: "En marche", 146: "En marche", 147: "A l'arrêt", 129: "Déccélération brutale", 130: "Accélération brutale", 24: "Pas de signal GPS", 25: "Acquisition signal GPS" };
var devices = { "862106020840460": "Clio", "862106024346480": "Stepway" };
var phones = "06XXXXXXXX,07XXXXXXXX";
var intervalFilePath = "/home/pi/software/track/web/interval%s.xml";
var fileTemplate = "<?xml version='1.0' encoding='ISO-8859-1'?><coordinates><latitude>%s</latitude><longitude>%s</longitude><imei>%s</imei><speed>%s</speed><gps>%s</gps><name>%s</name><place>%s</place><eventCode>%s</eventCode><time>%s</time><eventCodeNumber>%s</eventCodeNumber></coordinates>";

net.createServer(function (socket) {

    socket.name = socket.remoteAddress + ":" + socket.remotePort

    var rl = readline.createInterface({
        input: socket
      , output: socket
      , terminal: false
    });

    rl.on('line', function (line) {
        try {
            analyseData(line);
        }
        catch (ex) {
            console.log(ex);
        }
    });

}).listen(8500);

function analyseData(trame) {
    if (checkSum(trame)) {
        var infos = splitTrame(trame);
        saveTrame(infos)
        manageEvent(infos);
    }
};

function manageEvent(infos) {
    var eventCode = infos["EVENT_CODE"];
    if (events[eventCode]) {
        events[eventCode](infos);
    }
    else {
        sendSMS(devices[infos["IMEI"]] + ':' + eventCode);
    }
}

function manageEmptyEventNotification(infos) {

}

function manageGenericEventNotification(infos) {
    isLastTrame(infos["EVENT_CODE"], infos["TIME"], infos["IMEI"], function (returnValue) {
        messagePattern(infos, function (message) {
            sendSMS(message);
        });
        updateMapInterval(infos);
    });

}

function manageIntervalEventNotification(infos) {
    isLastTrame(infos["EVENT_CODE"], infos["TIME"], infos["IMEI"], function (returnValue) {
        updateMapInterval(infos);
    });
}

function sendSMS(message) {
    var uri = util.format('http://localhost/smsd/send.php?clientaddress=%s&message=%s', phones, message);
    request({ url: uri, json: false }, function (error, response, body) {
        if (error)
            console.log(err);
    });
}

function messagePattern(infos, callback) {
    var trameTime = infos["TIME"];
    var trameDevice = devices[infos["IMEI"]];
    var trameEventTitle = eventsTitle[infos["EVENT_CODE"]];
    var trameSpeed = infos["SPEED"];
    var trameLatitude = infos["LATITUDE"];
    var trameLongitude = infos["LONGITUDE"];
    var trameGpsState = '';
    if (infos["GPS_STATUS"] == 'A')
        trameGpsState = 'GPS OK';
    else
        trameGpsState = 'GPS KO';
    getGoogleLocation(infos["LATITUDE"], infos["LONGITUDE"], function (trameGeoloc) {
        callback(util.format('%s de %s le %s, %sKm/h, %s, %s, http://maps.google.com/maps?q=%s,%s', trameEventTitle, trameDevice, trameTime, trameSpeed, trameGpsState, trameGeoloc, trameLatitude, trameLongitude));
    });
}

function updateMapInterval(infos) {
    var date = new Date(infos["TIME"]);
    var trameTime = dateFormat(date, 'dd/mm/yyyy Ă  HH:MM:ss');
    var trameDevice = devices[infos["IMEI"]];
    var trameEventTitle = eventsMapTitle[infos["EVENT_CODE"]];
    var trameEventNumber = infos["EVENT_CODE"];
    var trameSpeed = infos["SPEED"];
    var trameLatitude = infos["LATITUDE"];
    var trameLongitude = infos["LONGITUDE"];
    var trameGpsState = '';
    if (infos["GPS_STATUS"] == 'A')
        trameGpsState = 'GPS OK';
    else
        trameGpsState = 'GPS KO';
    getGoogleLocation(infos["LATITUDE"], infos["LONGITUDE"], function (trameGeoloc) {
        var fileToSave = util.format(intervalFilePath, infos["IMEI"]);
        var fileContent = util.format(fileTemplate, trameLatitude, trameLongitude, infos["IMEI"], trameSpeed, trameGpsState, trameDevice, trameGeoloc, trameEventTitle, trameTime, trameEventNumber);
        fs.writeFile(fileToSave, fileContent, { flag: 'w+' }, function (err) {
            if (err)
                console.log(err);
        });
    });

}

function getGoogleLocation(latitude, longitude, callback) {
    var uri = util.format('http://maps.googleapis.com/maps/api/geocode/json?sensor=false&latlng=%s,%s', latitude, longitude);
    request({ url: uri, json: true }, function (error, response, body) {
        if (!error && response.statusCode === 200) {
            try {
                callback(body.results[0]['formatted_address']);
            }
            catch (ex) {
                console.log(ex);
                callback('');
            }
        }
    });
}

function isLastTrame(eventCode, eventDate, eventImei, callback) {
    var query = 'select count(*) as TOTAL from logs where TIME > ? and EVENT_CODE = ? and IMEI = ?';
    db.each(query, eventDate, eventCode, eventImei, function (err, row) {
        if (err) {
            console.log(err);
        }
        else if (row.TOTAL == 0) {
            callback();
        }
    });
}

function saveTrame(infos) {
    var insertParameters = '';
    var insertValues = '';

    for (var key in infos) {
        insertParameters = insertParameters + key + ',';
        insertValues = insertValues + formatDbField(infos[key]) + ',';
    }

    insertParameters = insertParameters.substring(0, insertParameters.length - 1);
    insertValues = insertValues.substring(0, insertValues.length - 1);
    var query = 'INSERT INTO logs (' + insertParameters + ') VALUES (' + insertValues + ')';
    var id = db.run(query, function (err) {
        if (err)
            console.log(err);
    });
}

function formatDbField(field) {
    switch (typeof field) {
        case 'number':
            return field;
        default:
            return '\'' + field + '\'';
    }
}

function splitTrame(trame) {
    var d = {};
    infos = trame.split(',');
    d["TRAME"] = trame;
    d["IMEI"] = infos[1];
    d["EVENT_CODE"] = parseInt(infos[3]);
    d["LATITUDE"] = parseFloat(infos[4]);
    d["LONGITUDE"] = parseFloat(infos[5]);
    d["TIME"] = '20' + infos[6].substring(0, 2) + '-' + infos[6].substring(2, 4) + '-' + infos[6].substring(4, 6) + ' ' + infos[6].substring(6, 8) + ':' + infos[6].substring(8, 10) + ':' + infos[6].substring(10, 12);
    d["GPS_STATUS"] = infos[7];
    d["SPEED"] = parseFloat(infos[10]);
    d["ALTITUDE"] = parseFloat(infos[13]);
    d["BASE_ID"] = infos[16];
    return d;
}

function checkSum(trame) {
    var chk = trame.substring(trame.length - 2);
    var calcChk = crc.crc1(trame.substring(0, trame.length - 2)).toUpperCase();
    return chk === calcChk;
}

Quelques explications:
  1. net.createServer créer un serveur TCP et va appeler analyseData à chaque trame reçue
  2. analyseData:
    • checkSum: vĂ©rifie le checksum de la trame pour vĂ©rifier qu'elle est correcte
    • splitTrame: analyse et dĂ©coupe la trame
    • saveTrame: sauvegarde la trame en base de donnĂ©es
    • manageEvent: exĂ©cute les actions en fonction du type d’évĂ©nement
  3. manageEvent: exécute les actions en fonction du type d’événement. Si un événement n'est pas référencé via le dictionnaire de référence events{}, un sms est envoyé via sendSMS. les évènements sont gérés selon 3 critères:
    • manageEmptyEventNotification: on ne fait rien (principalement pour les Ă©vĂ©nements qui remontent et dont je n'ai pas besoin)
    • manageGenericEventNotification: on formate un message pour envoyer par SMS et dans le fichier Xml destinĂ© Ă  l'interface web (principalement l'arrĂŞt et le dĂ©marrage du vĂ©hicule)
    • manageIntervalEventNotification: on met Ă  jour le Xml destinĂ© Ă  l'interface web
  4. messagePattern: formate les informations du tracker pour les envoyer par SMS et/ou écrire le fichier Xml. Cette méthode utilise le service de google afin d'obtenir l'adresse à partir des coordonnées.
  5. isLastTrame: Si la data n'est plus disponible, le tracker bufferize les données et au moment de la reconnexion au réseau, il peut les renvoyer en les mélangeant avec les trames courantes. Cette méthode sert simplement à ne pas notifier les événements passés.

Ordonnancement du programme:
Le programme se lance via le script ~/software/track/launch.sh:
Code: Tout sélectionner
nano ~/software/track/launch.sh

Copier/Coller le code (Le programme tourne normalement en permanance, mais si une erreur stoppe le programme, il se relancera automatique toutes les 60 secondes):
Code: Tout sélectionner
#!/bin/sh
i=0
while [ $i -eq 0 ]
do
    /home/pi/software/nodejs/bin/node /home/pi/software/track/server.js
    sleep 60
done

Autoriser l'exécution:
Code: Tout sélectionner
chmod +x launch.sh

Lancer le programme au démarrage:
Code: Tout sélectionner
sudo nano /etc/rc.local

Rajouter la ligne:
Code: Tout sélectionner
/home/pi/software/track/launch.sh > /dev/null 2>&1 &

Serveur HTTP:
Node JS permet également de réaliser un serveur web avec une websocket très simplement.
Fonctionnalités:
  • serveur HTTP (pas de sĂ©curisation https => pas le temps, ni l'envie...)
  • authentification par cookie permanant
  • utilisation d'une websocket: les pages web sont notifiĂ©es en temps rĂ©el de la position du tracker. Ce n'est pas le client qui va chercher l'information toutes les X secondes, mais c'est le serveur qui envoie l'information dès qu'elle est disponible. C'est un très grand confort ergonomique. Le serveur HTTP utilise un watcher sur les fichiers Xml des vĂ©hicules traitĂ©s par le serveur TCP. Si un Xml change (nouvel Ă©vĂ©nement dĂ©tectĂ©) alors le serveur HTTP Ă©met l'information via le flux entre la websocket et les pages web
  • permet de visualiser la position instantanĂ©e et l'Ă©tat de sa flotte via une carte google map
  • permet de visualiser l'historique sur 24H des positions et de l'Ă©tat de sa flotte via une carte google map
  • permet de rĂ©cupĂ©rer la dernière au format json la trame pour un vĂ©hicule et un Ă©vĂ©nement donnĂ©
  • permet depuis un mobile d'afficher sa position par rapport aux vĂ©hicules (Exemple: retrouver son vĂ©hicule sur un parking)

Ajout des modules Node JS nécessaires au bon fonctionnement du serveur HTTP:
Code: Tout sélectionner
cd ~/software/track/web
sudo ../../nodejs/bin/npm install socket.io
sudo ../../nodejs/bin/npm install xml2json


Le programme:
Code du fichier ~/software/track/web/server.js:
Code: Tout sélectionner
var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database('/home/pi/software/track/track.db');
var app = require('http').createServer(handler),
io = require('socket.io').listen(app, { log: true }),
parser = new require('xml2json'),
url = require("url"),
path = require("path"),
fs = require('fs');

var forbiddenPage = { '/server.js': 404, '/launch.sh': 404 };
var authorizedPage = {'/login': true, '/404': true, '/404/404.png': true, '/favicon.ico' : true};
var authentication = { "user": "p@ssw0rd" };
var cookieKey = "k@djfsqdKHLDUH545@sdf|";
var cookieName = "TrackCookie";

app.listen(8000);

function handler(req, res) {
    var uri = url.parse(req.url).pathname;
    var filename = path.join(__dirname, uri);

    if (authorizedPage[uri] || checkCookie(req, res)) {
        if (forbiddenPage[uri]) {
            return writeError(req, res, forbiddenPage[uri]);
        }
        if (fs.existsSync(filename)) {
            if (fs.statSync(filename).isDirectory()) {
                filename += '/index.html';
            }
            fs.readFile(filename, function (err, data) {
                if (err) {
                    return writeError(req, res, 404);
                }
                res.writeHead(200);
                res.end(data);
            });
        }
        else {
            switch (uri) {
                case '/json/histo':
                    getHistorique(url.parse(req.url, true).query.imei, url.parse(req.url, true).query.time, function (row) {
                        var result = JSON.stringify(row);
                        res.writeHead(200);
                        return res.end(result);
                    });
                    break;
              case '/json/last':
                    getLast(url.parse(req.url, true).query.imei, url.parse(req.url, true).query.event, function (row) {
                        var result = JSON.stringify(row);
                        res.writeHead(200);
                        return res.end(result);
                    });
                    break;
                case '/login':
                    return checkLogin(req, res, url.parse(req.url, true).query.login, url.parse(req.url, true).query.pwd);
                    break;
                default:
                    return writeError(req, res, 404);
            }
        }
    }
    else {
        return writeError(req, res, 404);
    }
}

io.sockets.on('connection', function (socket) {
    sendInit(socket);
    fs.watch(__dirname + '/interval862106020840460.xml', function (curr, prev) {
        fs.readFile(__dirname + '/interval862106020840460.xml', function (err, data) {
            if (err) throw err;
       if(data.length > 0) {
               var json = parser.toJson(data);
               json.time = new Date();
               socket.emit('notification', json);
       }
        });
    });
    fs.watch(__dirname + '/interval862106024346480.xml', function (curr, prev) {
        fs.readFile(__dirname + '/interval862106024346480.xml', function (err, data) {
            if (err) throw err;
       if(data.length > 0) {
               var json = parser.toJson(data);
               json.time = new Date();
               socket.emit('notification', json);
       }
        });
    });
});

function sendInit(socket) {

    fs.readFile(__dirname + '/interval862106020840460.xml', function (err, data) {
        if (err) throw err;
        var json = parser.toJson(data);
        json.time = new Date();
        socket.emit('notification', json);
    });
    fs.readFile(__dirname + '/interval862106024346480.xml', function (err, data) {
        if (err) throw err;
        var json = parser.toJson(data);
        json.time = new Date();
        socket.emit('notification', json);
    });
};

function getHistorique(imei, histoTime, callback) {
    var query = "select ID, strftime('%d/%m/%Y Ă  %H:%M:%S', time) as TIME,  LONGITUDE, LATITUDE, EVENT_CODE,  CASE WHEN GPS_STATUS = 'A' THEN 'GPS OK' ELSE 'GPS KO' end as GPS_STATUS, SPEED, IMEI from logs where IMEI = ? AND TIME > " + "datetime('now', 'localtime', '-" + histoTime + " hour') order by TIME desc";
    db.all(query, imei, function (err, row) {
        if (err) {
            console.log(err);
            callback();
        }
        else {
            callback(row);
        }
    });
}

function getLast(imei, eventCode, callback) {
    var query = "select * from logs where time = (select max(time) from logs where event_code = ? and imei = ?)";
    db.all(query, eventCode, imei, function (err, row) {
        if (err) {
            console.log(err);
            callback();
        }
        else {
            callback(row);
        }
    });
}

function writeError(req, res, code) {
    res.writeHead(302, {
        'Location': '/404'
    });
    return res.end();
}

function checkCookie(req, res) {

    var cookies = parseCookies(req);
    if (cookies[cookieName]) {
        return cookies[cookieName] == cookieKey;
    }
    return false;
}

function parseCookies(req) {
    var list = {},
        rc = req.headers.cookie;

    rc && rc.split(';').forEach(function (cookie) {
        var parts = cookie.split('=');
        list[parts.shift().trim()] = unescape(parts.join('='));
    });

    return list;
}

function checkLogin(req, res, login, pwd) {
    if (authentication[login] && authentication[login] == pwd) {
        var myDate = new Date(2050,1,1);
        res.writeHead(302, {
            'Set-Cookie': cookieName + '=' + cookieKey + ';domain=.mondomaine.fr;path=/;expires=' + myDate,
            'Content-Type': 'text/plain',
            'Location': '/'
        });
        return res.end();
    }
    return writeError(req, res, 404);
}


Ordonnancement du programme:
Le programme se lance via le script ~/software/track/web/launch.sh:
Code: Tout sélectionner
nano ~/software/track/web/launch.sh

Copier/Coller le code (Le programme tourne normalement en permanance, mais si une erreur stoppe le programme, il se relancera automatique toutes les 60 secondes):
Code: Tout sélectionner
#!/bin/sh
i=0
while [ $i -eq 0 ]
do
    /home/pi/software/nodejs/bin/node /home/pi/software/track/web/server.js
    sleep 60
done

Autoriser l'exécution:
Code: Tout sélectionner
chmod +x launch.sh

Lancer le programme au démarrage:
Code: Tout sélectionner
sudo nano /etc/rc.local

Rajouter la ligne:
Code: Tout sélectionner
/home/pi/software/track/web/launch.sh > /dev/null 2>&1 &


Site Web:
Authentification sur le site: http://localhost:8000/login?login=user&pwd=p@ssw0rd

Arborescence du site:
Code: Tout sélectionner
.:
404  favicon.ico  histo  images  index.html  interval862106020840460.xml  interval862106024346480.xml  js  launch.sh  server.js

./404:
404.png  index.html

./histo:
index.html

./images:
marker_862106020840460.png  marker_862106020840460_s.png  marker_862106024346480.png  marker_862106024346480_s.png  marker_my.png

./js:
jquery-1.11.1.min.js  socket.io-1.0.3.js


Voici quelques captures d'Ă©cran:
Page d'accueil:
Image

Suivi en temps réel d'un véhicule:
Image

Historique d'un véhicule:
Image

Récupération de la dernière information en json: http://localhost:8000/json/last?imei=86 ... 0&event=35
Code: Tout sélectionner
[{"ID":74849,"TRAME":"$$c184,862106024346480,AAA,35,-20.953075,55.315888,141006141022,A,9,14,130,54,0.9,102,604887,2345590,647|10|2454|E74C,0000,|||0838|043D,00000003,,50,2669,36,78,160,33,,,193,72,90,5233,,*BD","TIME":"2014-10-06 14:10:22","BASE_ID":"647|10|2454|E74C","ALTITUDE":102,"LONGITUDE":55.315888,"LATITUDE":-20.953075,"EVENT_CODE":35,"GPS_STATUS":"A","SPEED":130,"IMEI":"862106024346480"}]


Fichiers du site: Télécharger

Améliorations prévues:
  • Pour le moment, la seule chose dont je vais avoir besoin est de dĂ©finir des Geofenses avec des notifications et des actions.
    Si ce sujet intéresse quelques personnes, je completerai ce tutoriel
geminium
Membre Actif
 
Messages: 10
Inscription: 04 Juin 2012, 14:48

Re: [HowTo] Localisation via Tracker OBD II

Messagede domotics » 08 Oct 2014, 20:38

Sympa comme tuto.
Merci
Domotics
Mon Skype : mr.domotics - Contactez moi pour une démo, des réponses à vos questions, ...
Mon Installation : Eedomus, Zwave, Oregon, Sonos, EnOcean, Chacon, Raspberry Pi, IPX800, WES, RFXTRX, Nas Synology et Cams IP
Mon Conseil : La domotique doit toujours rester simple et demander un minimum de maintenance; Pensez aux autres utilisateurs de la maison ...

Domotics propose ses services via sa société de conseils http://www.id2domotique.com et sa boutique http://laboutiquededomotique.com
Avatar de l’utilisateur
domotics
Administrateur
 
Messages: 5001
Inscription: 15 AoĂ» 2003, 14:38
Localisation: Toulouse

Re: [HowTo] Localisation via Tracker OBD II

Messagede giejo » 09 Oct 2014, 13:20

Super tuto !
ca me donne des idees, moi j'avais envie d'intégrer une tablette tactile dans la voiture avec affichage infos odb2 / GPS / etc
la tablette pourrait comme cela servir pour la musique la navigation en plus
mais bon trop de choses à faire et ce n'est pas la priorité
ton tracker a l'air très intéressant tu l'as acheté ou ?
giejo
P'tit Guru de domotique
 
Messages: 71
Inscription: 10 Jan 2008, 15:42

Re: [HowTo] Localisation via Tracker OBD II

Messagede geminium » 09 Oct 2014, 21:28

Bonjour giejo,
sur aliexpress en cliquant ici
geminium
Membre Actif
 
Messages: 10
Inscription: 04 Juin 2012, 14:48

Re: [HowTo] Localisation via Tracker OBD II

Messagede Arkhana » 17 Oct 2014, 13:13

Super tuto !!!!
Merci
Arkhana
Membre Actif
 
Messages: 12
Inscription: 29 AoĂ» 2013, 13:11

Re: [HowTo] Localisation via Tracker OBD II

Messagede flo2587 » 30 Oct 2014, 15:08

Bonjour Ă  tous,

Pour ceux qui s’intéressent au sujet, je viens de tomber sur ce produit, commercialisé depuis le début du mois (à première vue) :
http://www.xee.com/
Pour l'instant tous les véhicules ne sont pas encore compatibles et les applications Androïd associées ne sont pas vraiment bien notées...mais le produit à l'air intéressant pour le prix ! A voir s'il est possible de créer des alertes facilement, avec la possibilité de s'interfacer avec une box domotique (possibilité d'envoyer des requêtes http sur une alerte).

En vente sur Amazon pour 140€ + 10€ pour le câble à acheter séparément en fonction de la marque du véhicule :
http://www.amazon.fr/gp/product/B00O58J4N2

Si quelqu'un a testé, j'attend son retour avec impatience !
flo2587
Membre un peu timide !
 
Messages: 5
Inscription: 21 Jan 2014, 14:03

Re: [HowTo] Localisation via Tracker OBD II

Messagede mtdoc » 02 FĂ©v 2016, 23:47

Bonsoir,
Je reprend le fil de la conversation.

Ce matériel et logiciels seraient-ils susceptibles de fonctionner pour un tracker type COBAN TK102 ou T303B ?
Merci de votre réponse
mtdoc
Membre un peu timide !
 
Messages: 1
Inscription: 02 FĂ©v 2016, 09:42


Retourner vers Montages, DIY, ...

Qui est en ligne

Utilisateurs parcourant ce forum: Aucun utilisateur enregistré et 1 invité

Copyright © 2011 - Touteladomotique.com - Tous droits rĂ©servĂ©s
Les blogs partenaires : Abavala, Domo-Blog, Domotique34, Maison et Domotique