Arduino et vidéo? Facile


Il est connu qu'un microcontrôleur Arduino faible n'est pas capable de passer un flux vidéo à travers lui-même.
Et si vous abordez cette tâche d'un autre côté?

... et étirez le processus de prise de vue dans le temps.

Hé, beaucoup ont déjà deviné par eux-mêmes que cette technique est appelée timelapse (prise de vue au ralenti à vue unique). Autrement dit, ce n'est pas une prise de vue vidéo, mais une prise de vue, à la suite de laquelle une vidéo est créée.

J'avoue qu'au départ je n'avais pas prévu de faire un ralenti. Je voulais juste faire un enregistrement des événements pour mes Arduins, y compris avec des photos. Eh bien, puis de temps en temps. Si mon lecteur n'est pas impliqué dans le développement, il peut simplement voir le résultat (allez sous Chrome).

En quoi consiste mon système:
  • Arduino Mega Board
  • Module JPEG de la caméra;
  • utilitaire et base de données MongoDB;
  • Serveur WEB pour l'hébergement de fichiers HTML;


Lorsque l'arduina envoie ses données au serveur, son propre horodatage est attaché à chaque paramètre.
Le paramètre a été envoyé - un enregistrement a été ajouté au référentiel, deux autres fois ont été envoyés - deux autres enregistrements ont été enregistrés.

Tous les travaux avec le référentiel sont effectués via un programme utilitaire (ci-après dénommé l'intermédiaire), qui est lancé sur un ordinateur fixe. Dans le même temps, le serveur WEB lui-même ne donne que du contenu statique. Autrement dit, tous les clients procèdent à un échange d'informations via le programme utilitaire de l'intermédiaire, similaire au protocole MQTT populaire. La principale différence avec MQTT sera que cet intermédiaire travaille directement avec l'entrepôt de données, fournissant un travail avec des données historiques. Ce fait simplifie le schéma d'interaction et ne nécessite pas de trafic réseau supplémentaire pour enregistrer les données.

Pour faciliter le développement de mes applications Web, j'ai créé une bibliothèque javascript avec l'API suivante:

Cela crée un client pour travailler avec le stockage réseau:
var client = new MgtClient("localhost", "login", "password", onDebugLog);

Arguments de fonction:
  1. l'adresse réseau sur laquelle le programme intermédiaire s'exécute, vous pouvez simplement spécifier l'IP, par exemple, "127.0.0.1";
  2. connexion utilisateur;
  3. mot de passe utilisateur
  4. fonction de rappel, pour déboguer les messages de chaîne;


La fonction de rappel pour le débogage des messages peut ressembler à ceci:
function onDebugLog(aStr) {
  //          
  console.log((new Date()).getTimeString() + ' ' + aStr + '\n'); 
}


Pas encore difficile? De plus, ce sera difficile.

Structure de demande de stockage:
var request = {
  name: " 1", //   
  placeId: 1, //                         
  beginTime: 1458108472000, //      1  1970 
  endTime: 1458194872000, //      1  1970  ( )
  limit: 10000 //   ,    (   )
};


Pas encore confus?

Voici donc la structure de la réponse à la demande:
var result = {
  times: [], //      (    1  1970 )
  values: [], //    
  position: 20, //         (   )
  status: "progress", //    ("progress", "abort", "done", "fail")
  progress: 91 //   ( )   
};


Ouais, c'est plus compliqué?

Statut du champ «status»:
  • «Terminé» - a reçu tout ce qui était demandé (soit des données pour toute la plage de temps ont été reçues, soit la limite du nombre d'enregistrements traités);
  • «Progression» - indique qu'il ne s'agit pas de la dernière donnée, le processus de téléchargement n'est pas encore terminé;
  • «Abandonner» - le téléchargement des données a été interrompu (la restriction sur la quantité totale de données pompées a fonctionné), vous pouvez immédiatement générer une nouvelle demande de réception des données manquantes;
  • «Échec» - quelque chose s'est mal passé (peut-être qu'il n'y a pas de courant dans la prise?)


Pensez-vous que c'est tout? Malheureusement non.

Les paramètres demandés peuvent être de différents types.
  • Si le paramètre est numérique, les nombres apparaîtront dans le tableau de valeurs.
  • Si la chaîne, alors dans le tableau de valeurs seront des chaînes.
  • S'il est booléen, le tableau de valeurs sera "vrai" ou "faux".
  • S'il s'agit d'un fichier binaire (par exemple, une image JPEG), les tableaux d'octets seront dans le tableau de valeurs.
  • S'il s'agit d'un événement, renvoyez les tableaux formés d'une manière spéciale.

Un exemple d'un enregistrement d'événement unique:
var event = [
  "  ", //   
  "", //   1
  27.5, //   1
  "", //   2
  true, //   2
  ...
  "", //   
  1458108472000 // !!!       ,
                //        .
];

C'est-à-dire que chaque enregistrement d'événement peut contenir un ensemble arbitraire de paramètres. Un tel regroupement est très pratique pour analyser l'image globale.

Uff, la partie la plus difficile est terminée.

Et donc la requête qui s'envoie ressemble:
// aRequest -  
// onReadArchive -     /
client.readArchive(aRequest, onReadArchive);                                                           


Fonction inverse pour obtenir des réponses:
// aResult -  
onReadArchive(aResult) {
  // TODO       

  //   "false",     (    )
  //   "true",      (    ) 
  return false; 
}


Enfin, nous sommes arrivés au montage vidéo lui-même.

Pour créer la vidéo, j'ai utilisé la bibliothèque javascript Whammy, plus de détails ici.

Fonction qui crée la vidéo:

<script src="whammy.js"></script>
<canvas id="canvas" style="display:none"></canvas>
<video id="player" controls autoplay loop></video>

function createVideo() {
  var canvas = document.getElementById("canvas");
  var context = canvas.getContext("2d");
  canvas.width = '640'; //     
  canvas.height = '480'; //     
  var framerate = 10; //     
  var quality = 0.8; //    

  var video = new Whammy.Video(framerate, quality); //       WebM
 
  for (var i = 0; i < images.length; i++) { //    
    var image = images[i];
    context.globalAlpha = 1;
    context.drawImage(image, 0, 0, 640, 480); //     
    video.add(context); //   
  }

  var output = video.compile(); //    
  var url = URL.createObjectURL(output); //    

  document.getElementById('player').src = url; //    
}


Malheureusement, vous ne pouvez pas créer de vidéos dans tous les navigateurs. Par exemple, mon Firefox préféré ne sait pas convertir les images au format WebP, sur la base desquelles la conversion en vidéo a lieu. Et même si j'ai trouvé une bibliothèque javascript pour une telle conversion, elle s'est convertie si lentement (et il y avait tellement de cadres) que j'ai refusé de l'utiliser. Cependant, dans tous les navigateurs avec le moteur Chrome, cette chose fonctionnera.

Ici vous pouvez voir ce que j'ai fait.

Sans documentation complète, je peux proposer mes articles précédents.
Article 1
Article 2

Eh bien, c'est tout, et je n'ai plus rien à dire.

Source: https://habr.com/ru/post/fr391773/


All Articles