Académique Documents
Professionnel Documents
Culture Documents
Dans cet article, nous vous montrons comment nous avons conçu le
boîtier pour protéger notre Smart Buoy de l'eau, des vagues et des
attaques de requins. Nous expliquons également comment nous
l'avons connecté à la station de base, qui hébergeait une base de
données et un tableau de bord interactif pour explorer les données en
temps réel.
Fourniture
Comme il s'agit de la dernière étape du projet, vous aurez besoin de
TOUTES les fournitures :
Module GPS — Amazon
Époxy — Amazon
Corde — Amazone
Flotteurs — Amazon
Colle — Amazon
3 Plus d'images
Étape 1 : Panneaux solaires
La bouée intelligente comportait quatre panneaux solaires également
espacés autour de sa circonférence. Chaque cellule était capable de
produire 5V à 60mA. Un Arduino nano typique nécessite environ 20
mA, nous espérions donc que cela conviendrait à notre
système. Nous avons soudé des fils à l'arrière des panneaux solaires
et les avons connectés au reste du système de charge.
Nous avons enfilé les fils à travers puis collé les panneaux solaires en
position étanche.
Étape 6 : Carte mère — Perfboard
Avec beaucoup de détermination, de soudure et de fil, nous avons
connecté l'électronique ensemble sur perfboard.
Étape 7 : Flottabilité
Étape 11 : V2
Principales améliorations
Fournitures
#include "Fil.h"
#include "I2Cdev.h"
#include "MPU6050.h"
void setup() {
Serial.begin(115200);
Wire.begin();
Serial.println("Initialisation du MP6050...");
mpu.initialize();
Serial.println(mpu.testConnection() ? "Connexion MPU6050 réussie" : "Échec de la
connexion MPU6050");}
boucle vide() {
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
printResults();
retard(10);
}
annuler printResults(){
Serial.print("Acc (x,y,z) m/s/s : ");
Serial.print(ax); Serial.print(" ");
Serial.print(ay); Serial.print(" ");
Serial.print(az);
Serial.print(" Gyro(x,y,z) °/s: ");
Serial.print(gx); Serial.print(" ");
Serial.print(gy); Serial.print(" ");
Serial.println(gz);
}
#include "Fil.h"
#include "I2Cdev.h"
#include "MPU6050.h"
#include "HMC5883L.h"
// -14° 59'
déclinaison du flotteur = (-14,0 - (59,0 / 60,0)) * (PI/180) ;
void setup() {
Serial.begin(115200);
Wire.begin();
Serial.println("Initialisation des périphériques I2C...");
mpu.setI2CMasterModeEnabled(false);
mpu.setI2CBypassEnabled(true) ;
mpu.setSleepEnabled(false);
mpu.initialize();
mag.initialize();
Serial.println(mpu.testConnection() ? "Connexion MPU6050 réussie" : "Échec de la
connexion MPU6050");
Serial.println(mag.testConnection() ? "Connexion HMC5883L réussie" : "Échec de la
connexion HMC5883L");
boucle vide() {
mag.getHeading(&mx, &my, &mz);
printResults();
retard(200);
}
annuler printResults(){
en-tête flottant = atan2(my, mx);
titre += déclinaison ;
si (cap < 0) cap += 2 * PI ;
si (cap > 2 * PI) cap -= 2 * PI ;
cap *= 180/M_PI;
Serial.print("Entête °:");
Serial.println(en-tête);
}
#include "Fil.h"
#include "I2Cdev.h"
#include "MPU6050.h"
#inclure <ms5611.h>
double température ;
longue pression;
double altitude;
void setup() {
Serial.begin(115200);
Wire.begin();
Serial.println("Initialisation des périphériques I2C...");
mpu.setI2CMasterModeEnabled(false);
mpu.setI2CBypassEnabled(true) ;
mpu.setSleepEnabled(false);
mpu.initialize();
Serial.println(mpu.testConnection() ? "Connexion MPU6050 réussie" : "Échec de la
connexion MPU6050");
tandis que (!baro.begin(MS5611_ULTRA_HIGH_RES)) {
Serial.println("Échec de la connexion MS5611, vérifiez le câblage !");
retard (500);
}
}
boucle vide() {
temp = baro.readTemperature(true);
pression = baro.readPressure(true);
altitude = baro.getAltitude(pression);
printResults();
retard(200);
}
annuler printResults(){
Serial.print("Pression hPa : "); Serial.print(pression);
Serial.print(" Altitude m : "); Serial.print(altitude);
flotteur tempC = temp / 340,00 + 36,53 ;
Serial.print(" Temp °C : "); Serial.println(tempC);
}
Une fois tous ces capteurs configurés, vous pouvez les utiliser pour
commencer à mesurer les propriétés des vagues.
#include "Fil.h"
#include "I2Cdev.h"
#include "MPU6050.h" // ^
#include <ms5611.h> //
longue pression;
double altitude, min_height, max_height, wave_height ;
void setup() {
Serial.begin(115200);
Wire.begin();
Serial.println("Initialisation des périphériques I2C...");
mpu.setI2CMasterModeEnabled(false);
mpu.setI2CBypassEnabled(true) ;
mpu.setSleepEnabled(false);
mpu.initialize();
Serial.println(mpu.testConnection() ? "Connexion MPU6050 réussie" : "Échec de la
connexion MPU6050");
tandis que (!baro.begin(MS5611_ULTRA_HIGH_RES)) {
Serial.println("Échec de la connexion MS5611, vérifiez le câblage !");
retard (500);
}
}
boucle vide() {
start_time long non signé = millis();
pression = baro.readPressure(true);
altitude = baro.getAltitude(pression);
hauteur_max = altitude ;
hauteur_min = altitude ;
// pendant 15 secondes
while(millis() - start_time < 15000){
pression = baro.readPressure(true);
altitude = baro.getAltitude(pression);
si (altitude < hauteur_min) hauteur_min = altitude ;
si (altitude > hauteur_max) hauteur_max = altitude ;
}
hauteur_onde = (hauteur_max - hauteur_min)/2.0 ;
Serial.print("Hauteur d'onde m : ");
Serial.println(wave_height);
}
#include "Fil.h"
#include "I2Cdev.h"
#include "MPU6050.h"
#inclure <ms5611.h>
void setup() {
Serial.begin(115200);
Wire.begin();
Serial.println("Initialisation des périphériques I2C...");
mpu.setI2CMasterModeEnabled(false);
mpu.setI2CBypassEnabled(true) ;
mpu.setSleepEnabled(false);
mpu.initialize();
Serial.println(mpu.testConnection() ? "Connexion MPU6050 réussie" : "Échec de la
connexion MPU6050");
tandis que (!baro.begin(MS5611_ULTRA_HIGH_RES)) {
Serial.println("Échec de la connexion MS5611, vérifiez le câblage !");
retard (500);
}
}
boucle vide() {
start_time long non signé = millis();
pression = baro.readPressure(true);
altitude = baro.getAltitude(pression);
hauteur_max = altitude ;
hauteur_min = altitude ;
// pendant 15 secondes
while(millis() - start_time < 15000){
pression = baro.readPressure(true);
altitude = baro.getAltitude(pression);
si (altitude < hauteur_min) hauteur_min = altitude ;
si (altitude > hauteur_max) hauteur_max = altitude ;
}
point_milieu = (hauteur_max + hauteur_min)/2.0 ;
smudge_factor = (max_height - mid_point)*0.15 ;
Serial.print("Mid Point m : "); Serial.print(mid_point);
heure_début = millis();
// pendant 15 secondes
while(millis() - start_time < 15000){
pression = baro.readPressure(true);
altitude = baro.getAltitude(pression);
// si dans une plage de 30 % de la hauteur des vagues à partir du point médian
// démarre le minuteur sinon l'arrête
if (altitude < mid_point + smudge_factor && altitude > mid_point - smudge_factor)
{
si ( ! commencé){
period_start = millis();
commencé = vrai ;
}
autre{
si (échappé){
// s'il a commencé et s'est échappé et revient maintenant
period_end = millis();
commencé = faux ;
échappé = faux ;
// si la variable a déjà été affectée
// utilise sa valeur précédente et sa nouvelle valeur pour calculer avg
if(avg_period != -1){
avg_period = (avg_period + (period_end-period_start)*2) / 2.0 ;
}
// l'assigne
autre{
avg_period = (period_end-period_start)*2 ;
}
}
}
}
autre{
échappé = vrai ;
}
}
Serial.print(" Période s : "); Serial.println(avg_period/1000);
}
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include "HMC5883L.h"
#include "Fil.h"
carte mère MPU6050 ;
Chargeur HMC5883L ;
// variables d'orientation/mouvement
Quaternion q ; // [w, x, y, z] conteneur de quaternions
VectorInt16 aa ; // [x, y, z] mesures du capteur d'accélération
VectorInt16 aaReal ; // [x, y, z] mesures du capteur d'accélération sans gravité
VectorInt16 aaMonde ; // [x, y, z] mesures du capteur d'accélération de trame
mondiale
Gravité VectorFloat ; // [x, y, z] vecteur de gravité
volatile bool mpuInterrupt = faux ; // indique si la broche d'interruption du MPU est
passée à l'état haut
void dmpDataReady() {
mpuInterrupt = vrai ;
}
void setup() {
Serial.begin(115200);
Wire.begin();
Wire.setClock(400000);
mpu.setI2CMasterModeEnabled(false);
mpu.setI2CBypassEnabled(true) ;
mpu.setSleepEnabled(false);
mpu.initialize();
mag.initialize();
// initialise l'appareil
Serial.println(F("Initialisation des périphériques I2C..."));
mpu.initialize();
pinMode(INTERRUPT_PIN, INPUT);
Serial.println(mpu.testConnection() ? F("Connexion MPU6050 réussie") : F("Échec
de la connexion MPU6050"));
devStatus = mpu.dmpInitialize();
// fournissez vos propres décalages gyroscopiques ici, mis à l'échelle pour une
sensibilité minimale
mpu.setXGyroOffset(112);
mpu.setYGyroOffset(5);
mpu.setZGyroOffset(-15);
mpu.setZAccelOffset(1487);
si (devStatus == 0) {
mpu.setDMPEnabled(true);
Serial.print(digitalPinToInterrupt(INTERRUPT_PIN));
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
dmpReady = vrai ;
packetSize = mpu.dmpGetFIFOPacketSize();
}
boucle vide() {
si (!dmpReady) retour ;
tandis que (!mpuInterrupt && fifoCount < taillepaquet) {
si (mpuInterrupt && fifoCount < taillepaquet) {
fifoCount = mpu.getFIFOCount();
}
}
mpuInterrupt = faux ;
mpuIntStatus = mpu.getIntStatus();
fifoCount = mpu.getFIFOCount();
si ((mpuIntStatus & _BV(MPU6050_INTERRUPT_FIFO_OFLOW_BIT)) || fifoCount >= 1024)
{
mpu.resetFIFO();
fifoCount = mpu.getFIFOCount();
}
sinon si (mpuIntStatus & _BV(MPU6050_INTERRUPT_DMP_INT_BIT)) {
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
mpu.getFIFOBytes(fifoBuffer, taillepaquet);
fifoCount -= taillepaquet ;
// OUTPUT_READABLE_WORLDACCEL
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetAccel(&aa, fifoBuffer);
mpu.dmpGetGravity(&gravité, &q);
mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
Serial.print("Accélération (x,y) m/s/s : ");
Serial.print(aaWorld.x);
Serial.print(" ");
Serial.println(aaWorld.y);
}
}
Étape 7 : Énergie brute des vagues
#include "Fil.h"
#include "I2Cdev.h"
#include "MPU6050.h"
#inclure <ms5611.h>
void setup() {
Serial.begin(115200);
Wire.begin();
Serial.println("Initialisation des périphériques I2C...");
mpu.setI2CMasterModeEnabled(false);
mpu.setI2CBypassEnabled(true) ;
mpu.setSleepEnabled(false);
mpu.initialize();
Serial.println(mpu.testConnection() ? "Connexion MPU6050 réussie" : "Échec de la
connexion MPU6050");
tandis que (!baro.begin(MS5611_ULTRA_HIGH_RES)) {
Serial.println("Échec de la connexion MS5611, vérifiez le câblage !");
retard (500);
}
}
boucle vide() {
start_time long non signé = millis();
pression = baro.readPressure(true);
altitude = baro.getAltitude(pression);
hauteur_max = altitude ;
hauteur_min = altitude ;
// pendant 15 secondes
while(millis() - start_time < 15000){
pression = baro.readPressure(true);
altitude = baro.getAltitude(pression);
si (altitude < hauteur_min) hauteur_min = altitude ;
si (altitude > hauteur_max) hauteur_max = altitude ;
}
point_milieu = (hauteur_max + hauteur_min)/2.0 ;
wave_height = (max_height - mid_point) / 2.0 ;
smudge_factor = wave_height * 0,15 ;
Serial.print("Hauteur d'onde m : "); Serial.print(wave_height);
heure_début = millis();
// pendant 15 secondes
while(millis() - start_time < 15000){
pression = baro.readPressure(true);
altitude = baro.getAltitude(pression);
// si dans une plage de 30 % de la hauteur des vagues à partir du point médian
// démarre le minuteur sinon l'arrête
if (altitude < mid_point + smudge_factor && altitude > mid_point - smudge_factor)
{
si ( ! commencé){
period_start = millis();
commencé = vrai ;
}
autre{
si (échappé){
// s'il a commencé et s'est échappé et revient maintenant
period_end = millis();
commencé = faux ;
échappé = faux ;
// si la variable a déjà été affectée
// utilise sa valeur précédente et sa nouvelle valeur pour calculer avg
if(avg_period != -1){
avg_period = (avg_period + (period_end-period_start)*2) / 2.0 ;
}
// l'assigne
autre{
avg_period = (period_end-period_start)*2 ;
}
}
}
}
autre{
échappé = vrai ;
}
}
Serial.print(" Période s : "); Serial.print(avg_period/1000);
wave_power = (0.5 * wave_height * wave_height * avg_period / 1000);
Serial.print(" Puissance des vagues kW/m : "); Serial.println(wave_power);
}
Étape 8 : Température de l'eau
#include <unfil.h>
#include <dallastemperature.h>
Un fil un fil(6);
Capteurs de température Dallas(&oneWire);
float water_temperature ;
void setup() {
Serial.begin(115200);
capteurs.begin();
}
boucle vide() {
capteurs.requestTemperatures();
température_eau = capteurs.getTempCByIndex(0);
Serial.print("Température de l'eau C ");
Serial.println(water_temperature);
retard(200);
}
Étape 9 : Merci
Fournitures
Carte SD - Amazon
2 X Arduino - Amazone
Étape 1 : Obtenir des données GPS
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
struct dataStruct{
double latitude;
double longitude;
unsigned long date;
unsigned long time;
}gpsData;
void setup(){
Serial.begin(115200);
ss.begin(9600);
}
void loop(){
while (ss.available() > 0){
if (gps.encode(ss.read())){
getInfo();
printResults();
}
}
}
void getInfo(){
if (gps.location.isValid()){
gpsData.latitude = gps.location.lat();
gpsData.longitude = gps.location.lng();
}
else{
Serial.println("Invalid location");
}
if (gps.date.isValid()){
gpsData.date = gps.date.value();
}
else{
Serial.println("Invalid date");
}
if (gps.time.isValid()){
gpsData.time = gps.time.value();
}
else{
Serial.println("Invalid time");
}
}
void printResults(){
Serial.print("Location: ");
Serial.print(gpsData.latitude, 6); Serial.print(", ");
Serial.print(gpsData.longitude, 6);
Serial.print(" Date: ");
Serial.print(gpsData.date);
Serial.print(" Time: ");
Serial.print(gpsData.time);
Serial.println();
}
Émetteur
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <SoftwareSerial.h>
#include <TinyGPS++.h>
TinyGPSPlus gps;
SoftwareSerial ss(4, 3);
RF24 radio(8, 7); // CE, CSN
struct dataStruct{
double latitude;
double longitude;
unsigned long date;
unsigned long time;
}gpsData;
void setup() {
Serial.begin(115200);
ss.begin(9600);
Serial.println("Setting up radio");
// Setup transmitter radio
radio.begin();
radio.openWritingPipe(0xF0F0F0F0E1LL);
radio.setChannel(0x76);
radio.setPALevel(RF24_PA_MAX);
radio.setDataRate(RF24_250KBPS);
radio.stopListening();
radio.enableDynamicPayloads();
radio.powerUp();
Serial.println("Starting to send");
}
void loop() {
while (ss.available() > 0){
if (gps.encode(ss.read())){
getInfo();
radio.write(&gpsData, sizeof(gpsData));
}
}
}
void getInfo(){
if (gps.location.isValid()){
gpsData.longitude = gps.location.lng();
gpsData.latitude = gps.location.lat();
}
else{
gpsData.longitude = 0.0;
gpsData.latitude = 0.0;
}
if (gps.date.isValid()){
gpsData.date = gps.date.value();
}
else{
gpsData.date = 0;
}
if (gps.time.isValid()){
gpsData.time = gps.time.value();
}
else{
gpsData.time = 0;
}
}
DESTINATAIRE
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
struct dataStruct{
double latitude;
double longitude;
unsigned long date;
unsigned long time;
}gpsData;
void setup() {
Serial.begin(115200);
// Setup receiver radio
radio.begin();
radio.openReadingPipe(1, 0xF0F0F0F0E1LL);
radio.setChannel(0x76);
radio.setPALevel(RF24_PA_MAX);
radio.setDataRate(RF24_250KBPS);
radio.startListening();
radio.enableDynamicPayloads();
radio.powerUp();
}
void loop() {
if (radio.available()) {
radio.read(&gpsData, sizeof(gpsData));
Serial.print("Location: ");
Serial.print(gpsData.latitude, 6);
Serial.print(", ");
Serial.print(gpsData.longitude, 6);
Serial.print(" Date: ");
Serial.print(gpsData.date);
Serial.print(" Time: ");
Serial.print(gpsData.time);
Serial.println();}
}
Le module radio est assez fiable, mais parfois vous avez besoin d'un
plan d'urgence en cas de coupure de courant côté récepteur ou si la
radio se déplace hors de portée. Notre plan d'urgence est un module
de carte SD qui nous permet de stocker les données que nous
collectons. La quantité de données collectées n'est pas si grande,
donc même une petite carte SD pourra facilement stocker la valeur
d'une journée de données.
#include <SPI.h>
#include <SD.h>
#include <SoftwareSerial.h>
#include <TinyGPS++.h>
struct dataStruct{
latitude double ;
longitude double;
date longue non signée ;
longtemps non signé;
}DonnéesGps ;
void setup() {
Serial.begin(115200);
ss.begin(9600);
si (!SD.begin(5)) {
Serial.println("La carte a échoué ou n'est pas présente");
retour;
}
Serial.println("carte initialisée.");
boucle vide() {
tandis que (ss.disponible() > 0){
si (gps.encode(ss.read())){
getInfo();
printResults();
saveInfo();
}
}
}
annuler getInfo(){
si (gps.location.isValid()){
gpsData.latitude = gps.location.lat();
gpsData.longitude = gps.location.lng();
}
autre{
Serial.println("Emplacement invalide");
}
si (gps.date.isValid()){
gpsData.date = gps.date.value();
}
autre{
Serial.println("Date invalide");
}
si (gps.time.isValid()){
gpsData.time = gps.time.value();
}
autre{
Serial.println("Heure invalide");
}
}
annuler printResults(){
Serial.print("Emplacement : ");
Serial.print(gpsData.latitude, 6); Serial.print(", ");
Serial.print(gpsData.longitude, 6);
Serial.print(" Date : ");
Serial.print(gpsData.date);
Serial.print(" Heure : ");
Serial.print(gpsData.time);
Serial.println();
}
annuler saveInfo(){
Fichier dataFile = SD.open("gps_data.csv", FILE_WRITE);
si (fichierdonnées) {
dataFile.print(gpsData.latitude);
dataFile.print(", ");
dataFile.print(gpsData.longitude);
dataFile.print(", ");
dataFile.print(gpsData.date);
dataFile.print(", ");
dataFile.println(gpsData.time);
dataFile.close();
}
autre{
Serial.println("non pas de fichier de données");
}
}
Fournitures
La bouée intelligente est alimentée par une batterie 18650 qui est
chargée par quatre panneaux solaires de 5 V, 60 mA en
parallèle. Dans notre conception, les quatre panneaux solaires se
trouvent autour du sommet de la bouée, capturant un maximum de
lumière solaire.
retourner wait_time ;
}</p>
}</p><p>boucle vide() {
shuntvoltage = ina219.getShuntVoltage_mV();
tension de bus = ina219.getBusVoltage_V();
courant_mA = ina219.getCurrent_mA();
puissance_mW = ina219.getPower_mW();
tension de charge = tension de bus + (tension shunt / 1000);
Serial.print("Tension de charge : "); Serial.print (tension de charge);
Serial.println(" V");
Serial.print("Actuel : "); Serial.print(current_mA); Serial.println(" mA");
Serial.print("Alimentation : "); Série.print(power_mW); Serial.println(" mW");
Serial.println();
retard(1000);
}</p>
Étape 4 : Utilisation du moniteur d'alimentation pour définir la prochaine
alarme RTC
Enfin, c'est ainsi que nous avons utilisé la tension pour prédire la
durée optimale entre les alarmes. La méthode est un peu grossière -
si quelqu'un a de meilleures idées, veuillez nous en informer dans les
commentaires ci-dessous.
(Nous expliquons le code dans cette vidéo
- https://youtu.be/5guIB8_YIGQ )
retourner wait_time ;
}</p>