ENIB 2023 : Les petits voyageurs : Différence entre versions
(→Catégories) |
|||
(17 révisions intermédiaires par un autre utilisateur non affichées) | |||
Ligne 2 : | Ligne 2 : | ||
==Les petits voyageurs:== | ==Les petits voyageurs:== | ||
+ | [[Fichier:Fini1.jpeg|600px]] | ||
==Description courte== | ==Description courte== | ||
Ligne 17 : | Ligne 18 : | ||
* câbles Dupont | * câbles Dupont | ||
* plaque de Cork de 30cm x 30cm | * plaque de Cork de 30cm x 30cm | ||
+ | * colle à bois | ||
+ | * peintures et pinceaux | ||
+ | * cutter | ||
+ | * pic à broque | ||
+ | * scotch | ||
+ | |||
* découpeuse laser | * découpeuse laser | ||
* poste à souder | * poste à souder | ||
− | |||
== Réalisation== | == Réalisation== | ||
Ligne 29 : | Ligne 35 : | ||
'''Etape 2:''' | '''Etape 2:''' | ||
Créer les énigmes. | Créer les énigmes. | ||
+ | Ci dessous la liste de nos énigmes | ||
+ | [[Fichier:Liste des énigmes.docx]] | ||
'''Etape 3:''' | '''Etape 3:''' | ||
Ligne 51 : | Ligne 59 : | ||
Sur inskape, creer un fichier avec toutes les murs du labyrinthe qui doivent être découpés / graver. | Sur inskape, creer un fichier avec toutes les murs du labyrinthe qui doivent être découpés / graver. | ||
+ | [[Media:Decoupe laser.svg|téléchargez le svg]] | ||
− | |||
'''Etape 8:''' | '''Etape 8:''' | ||
Ligne 61 : | Ligne 69 : | ||
'''Etape 9:''' | '''Etape 9:''' | ||
Peindre certaines cases du labyrinthe en fonction des couleurs nécessaires. | Peindre certaines cases du labyrinthe en fonction des couleurs nécessaires. | ||
+ | |||
Astuce : Mettre du scotch pour avoir des bords lisses | Astuce : Mettre du scotch pour avoir des bords lisses | ||
Ligne 70 : | Ligne 79 : | ||
A l'aide de la colle à bois, coller les murs du labyrinthe. | A l'aide de la colle à bois, coller les murs du labyrinthe. | ||
+ | [[Fichier:colle1.jpeg|600px]] —> [[Fichier:colle2.jpeg|600px]] | ||
+ | |||
+ | |||
+ | '''Etape 11:''' | ||
+ | Creer une petite protection pour le capteur de couleur afin de pouvoir le déplacer dans le labyrinthe. | ||
+ | |||
+ | [[Fichier:Protection.jpeg|600px]] | ||
+ | |||
+ | '''Etape 12:''' | ||
+ | Rajouter les différents éléments ( carte arduino, capteurs, boutons poussoirs). | ||
+ | [[Fichier:Fini1.jpeg.jpeg|600px]] | ||
+ | |||
==Code== | ==Code== | ||
+ | prog.cpp: | ||
+ | <pre> | ||
+ | |||
+ | #include "color.hpp" | ||
+ | #include "enigme.hpp" | ||
+ | #include <iostream> | ||
+ | #include <random> | ||
+ | #include <vector> | ||
+ | |||
+ | |||
+ | int main() | ||
+ | { | ||
+ | //variable initiale | ||
+ | int erreur=-1; | ||
+ | int test=1; | ||
+ | int bonneReponse=0; | ||
+ | |||
+ | //liste des couleurs disponible pour les drapeaux | ||
+ | deb::Color rouge{1024, 0, 0}; | ||
+ | deb::Color bleu{0, 0, 1024}; | ||
+ | deb::Color blanc{1024, 1024, 1024}; | ||
+ | deb::Color noir{0, 0, 0}; | ||
+ | deb::Color vert{0, 1024 ,0}; | ||
+ | deb::Color orange{0, 0, 1024}; | ||
+ | deb::Color jaune{0, 0, 1024}; | ||
+ | |||
+ | //liste des enigmes | ||
+ | deb::Enigme ea{bleu, blanc, rouge, "Pays du champagne"}; | ||
+ | deb::Enigme eb{vert, blanc, rouge, "Pays des pâtes"}; | ||
+ | deb::Enigme ec{vert, blanc, orange, "Pays des moutons "}; | ||
+ | deb::Enigme ed{rouge, blanc, rouge, "Pays d’origine de Marie Antoinette"}; | ||
+ | deb::Enigme ee{noir, rouge, jaune, "Pays d’origine des knödels "}; | ||
+ | deb::Enigme ef{jaune, vert, rouge, "Quel est ce pays ? Mjubojf"}; | ||
+ | deb::Enigme eg{rouge, blanc, bleu, "Quel est ce pays ? .-.. ..- -..- . -- -... --- ..- .-. --."}; | ||
+ | deb::Enigme eh{rouge, blanc, bleu, "Quel est ce pays ? Ozxr-Azr"}; | ||
+ | deb::Enigme ei{noir, jaune, rouge, "Pays de la gaufre"}; | ||
+ | deb::Enigme ej{vert, jaune, bleu, "Mon premier est un mec. Mon second qualifie quelque chose d’agréable."}; | ||
+ | deb::Enigme ek{blanc, bleu, noir, "Quel est ce pays ? . ... - --- -. .. ."}; | ||
+ | deb::Enigme el{rouge, blanc, rouge, "Quel est ce pays ? 53886643"}; | ||
+ | deb::Enigme em{rouge, blanc, vert, "Mon premier est un pronom personnel indéfini. Mon second contient autant de rouge que de vert que de bleu."}; | ||
+ | deb::Enigme en{bleu, jaune, rouge, "Quel est ce pays ? Spvnbojf"}; | ||
+ | deb::Enigme eo{bleu, jaune, rouge, "Ma capitale est vieille "}; | ||
+ | deb::Enigme ep{bleu, jaune, rouge, "Quel est ce pays ? - -.-. .... .- -.."}; | ||
+ | deb::Enigme eq{rouge, bleu, orange, "Son papier sent bon "}; | ||
+ | deb::Enigme er{bleu, rouge, vert, "Quel est ce pays ? 2937224526"}; | ||
+ | deb::Enigme es{rouge, jaune, vert, "Ma capitale est La Paz"}; | ||
+ | deb::Enigme et{rouge, jaune, vert, "Mon premier est la plante sous laquelle il faut s’embrasser. Mon second est l’arrivée en ce monde."}; | ||
+ | deb::Enigme eu{blanc, vert, rouge, "Mon premier est une sphère d’une substance dans une autre. Mon second est un mec. Mon troisième peut être cantonais, thai ou basmati. "}; | ||
+ | deb::Enigme ev{jaune, bleu, rouge, "Ma capitale est Bogota"}; | ||
+ | deb::Enigme ew{orange, blanc, vert, "Quel est ce pays ? Dpuf e’Jwpjsf"}; | ||
+ | deb::Enigme ex{vert, jaune, rouge, "Quel est ce pays ? -- .- .-.. .."}; | ||
+ | deb::Enigme ey{vert, blanc, vert, "Quel est ce pays ? 6443742"}; | ||
+ | deb::Enigme ez{rouge, blanc, rouge, "Pays du Macchu Picchu"}; | ||
+ | deb::Enigme eea{blanc, bleu, rouge, "Pays du Kremlin"}; | ||
+ | deb::Enigme eeb{rouge, blanc, noir, "Quel est ce pays ? Xdldm"}; | ||
+ | std::vector<deb::Enigme> vectEnigme{ea,eb,ec,ed,ee,ef,eg,eh,ei,ej,ek,el,em,en,eo,ep,eq,er,es,et,eu,ev,ew,ex,ez,eea,eeb}; | ||
+ | |||
+ | //loop | ||
+ | |||
+ | if (appui sur bouton acquisition) //on enregistre la couleur actuelle | ||
+ | { | ||
+ | int a,b,c = 10,10,1024; //on recupere les valeurs du capteur | ||
+ | |||
+ | if (test==4) | ||
+ | { | ||
+ | bonneReponse=0; //il y a trop de reponse le resultat sera faut | ||
+ | } | ||
+ | |||
+ | if (test==3) | ||
+ | { | ||
+ | if (enigmeEnCours.trois.isitme(a,b,c)) {bonneReponse=bonneReponse+1} //on compare a la couleur du drapeau | ||
+ | test=4; | ||
+ | } | ||
+ | |||
+ | if (test==2) | ||
+ | { | ||
+ | if (enigmeEnCours.deux.isitme(a,b,c)) {bonneReponse=bonneReponse+1} //on compare a la couleur du drapeau | ||
+ | test=3; | ||
+ | } | ||
+ | |||
+ | if (test==1) | ||
+ | { | ||
+ | if (enigmeEnCours.un.isitme(a,b,c)) {bonneReponse=bonneReponse+1} //on compare a la couleur du drapeau | ||
+ | test=2; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | if (appui sur le bouton validé and erreur!=-1)//on regarde le resultat final | ||
+ | { | ||
+ | if (bonneReponse==3) | ||
+ | { | ||
+ | std::cout <<"Victoire ! Si tu veux rejouer, clique sur JSP.\n"; | ||
+ | erreur =-1; | ||
+ | } | ||
+ | if (bonneReponse!=3 and erreur==0) | ||
+ | { | ||
+ | std::cout <<"Tu as fait une erreur. Repars de zéro et essaye encore. \n"; | ||
+ | erreur =1; | ||
+ | } | ||
+ | if (bonneReponse!=3 and erreur==1) | ||
+ | { | ||
+ | std::cout <<"Tu as perdu. Si tu veux rejouer, clique sur JSP.\n"; | ||
+ | erreur =-1; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | if (appui sur bouton validé and erreur==-1) //lancement nouvelle enigme | ||
+ | { | ||
+ | srand((unsigned) time(NULL)); | ||
+ | deb::Enigme enigmeEnCours=vectEnigme[rand() % 28]; | ||
+ | std::cout << enigmeEnCours.question << "\n"; | ||
+ | erreur=0; | ||
+ | test=1; | ||
+ | bonneReponse=0; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | </pre> | ||
+ | |||
+ | color.cpp: | ||
+ | |||
+ | <pre> | ||
+ | |||
+ | |||
+ | //---------------------------------------------------------------------------- | ||
+ | |||
+ | #include "color.hpp" | ||
+ | |||
+ | namespace deb | ||
+ | { | ||
+ | bool Color::isitme(int r,int g,int b) const | ||
+ | { | ||
+ | if (r<(red-200)) {return false;} | ||
+ | if (r>(red+200)) {return false;} | ||
+ | if (g<(green-200)) {return false;} | ||
+ | if (g>(green+200)) {return false;} | ||
+ | if (b<(blue-200)) {return false;} | ||
+ | if (b>(blue+200)) {return false;} | ||
+ | return true; | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | }// namespace deb | ||
+ | |||
+ | //---------------------------------------------------------------------------- | ||
+ | </pre> | ||
+ | |||
+ | color.hpp: | ||
+ | <pre> | ||
+ | |||
+ | //---------------------------------------------------------------------------- | ||
+ | |||
+ | #ifndef DEB_COLOR_HPP | ||
+ | #define DEB_COLOR_HPP | ||
+ | |||
+ | #include <iostream> | ||
+ | |||
+ | namespace deb { | ||
+ | |||
+ | struct Color | ||
+ | { | ||
+ | int red,green,blue; | ||
+ | |||
+ | Color(int red, int green, int blue): red{std::move(red)}, green{std::move(green)}, blue{std::move(blue)} | ||
+ | {/* nothing to do */} | ||
+ | |||
+ | bool isitme(int r,int g,int b) const; | ||
+ | }; | ||
+ | |||
+ | |||
+ | |||
+ | } // namespace deb | ||
+ | |||
+ | #endif // DEB_COLOR_HPP | ||
+ | |||
+ | //---------------------------------------------------------------------------- | ||
+ | |||
+ | |||
+ | </pre> | ||
+ | |||
+ | |||
+ | |||
+ | enigme.hpp | ||
+ | |||
<pre> | <pre> | ||
− | + | ||
+ | |||
+ | //---------------------------------------------------------------------------- | ||
+ | |||
+ | #ifndef DEB_ENIGME_HPP | ||
+ | #define DEB_ENIGME_HPP | ||
+ | |||
+ | #include <iostream> | ||
+ | #include <string> | ||
+ | #include "color.hpp" | ||
+ | |||
+ | namespace deb { | ||
+ | |||
+ | struct Enigme | ||
+ | { | ||
+ | Color un,deux,trois; | ||
+ | std::string question; | ||
+ | |||
+ | Enigme(Color u, Color d, Color t, std::string q): un{std::move(u)}, deux{std::move(d)}, trois{std::move(t)}, question{std::move(q)} | ||
+ | {/* nothing to do */} | ||
+ | |||
+ | }; | ||
+ | |||
+ | |||
+ | |||
+ | } // namespace deb | ||
+ | |||
+ | #endif // DEB_ENIGME_HPP | ||
+ | |||
+ | //---------------------------------------------------------------------------- | ||
+ | |||
+ | |||
+ | </pre> | ||
+ | |||
+ | '''code BP :''' | ||
+ | |||
+ | Les_petits_voyageurs.ino | ||
+ | |||
+ | <pre> | ||
+ | |||
+ | #include "Test_BP.h" | ||
+ | #include "Arduino.h" | ||
+ | int lancement=0; | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | Serial.begin(115200); | ||
+ | setup_BP(); | ||
+ | //constantes générées aussi en dehors de la loop | ||
+ | } | ||
+ | |||
+ | |||
+ | void loop() | ||
+ | { | ||
+ | lancement=BP(lancement); | ||
+ | if (lancement==1) | ||
+ | { | ||
+ | delay(20); | ||
+ | Serial.println("Bonjour ! "); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </pre> | ||
+ | Test_BP.cpp | ||
+ | |||
+ | <pre> | ||
+ | #include "Test_BP.h" | ||
+ | |||
+ | //BP1 | ||
+ | const int BP1=4; | ||
+ | |||
+ | int boutonState = 0; | ||
+ | int lastBoutonState = 0; | ||
+ | |||
+ | void setup_BP() | ||
+ | { | ||
+ | //setup fonction bouton | ||
+ | pinMode(BP1, INPUT); | ||
+ | } | ||
+ | |||
+ | int BP(int lancement) //permet de détecter les FM/FD du BP | ||
+ | { | ||
+ | /* | ||
+ | Serial.print("etat bouton : "); | ||
+ | Serial.println(boutonState); | ||
+ | Serial.print("etat bouton precedent : "); | ||
+ | Serial.println(lastBoutonState); | ||
+ | */ | ||
+ | |||
+ | boutonState=digitalRead(BP1); | ||
+ | Serial.println(boutonState); | ||
+ | if (boutonState==HIGH) | ||
+ | { | ||
+ | if (boutonState != lastBoutonState) | ||
+ | { | ||
+ | lancement=1; | ||
+ | //Serial.println("00"); | ||
+ | lastBoutonState=boutonState; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | lancement=0; //état récurrent de lancement, on reste appuyé sur BP | ||
+ | //Serial.println("10"); | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if (boutonState != lastBoutonState) | ||
+ | { | ||
+ | //Serial.println("01"); | ||
+ | lancement=0; | ||
+ | lastBoutonState=boutonState; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | //Serial.println("11"); | ||
+ | } | ||
+ | } | ||
+ | //Serial.println(lancement); | ||
+ | |||
+ | return lancement; | ||
+ | } | ||
+ | |||
+ | </pre> | ||
+ | |||
+ | |||
+ | <pre> | ||
+ | #ifndef TEST_BP_H | ||
+ | #define TEST_BP_H | ||
+ | |||
+ | #include <Arduino.h> | ||
+ | |||
+ | |||
+ | void setup_BP(); | ||
+ | int BP(int lancement); | ||
+ | |||
+ | #endif | ||
+ | |||
+ | </pre> | ||
+ | |||
+ | '''code capteur:''' | ||
+ | |||
+ | color_detection_methods.hpp | ||
+ | |||
+ | <pre> | ||
+ | /* This file is taken from MyOwnBricks project. | ||
+ | * MyOwnBricks is a library for the emulation of LEGO PoweredUp sensors on microcontrollers | ||
+ | * Copyright (C) 2021-2022 Ysard - <ysard@users.noreply.github.com> | ||
+ | * | ||
+ | * This program is free software: you can redistribute it and/or modify | ||
+ | * it under the terms of the GNU General Public License as published by | ||
+ | * the Free Software Foundation, either version 3 of the License, or | ||
+ | * (at your option) any later version. | ||
+ | * | ||
+ | * This program is distributed in the hope that it will be useful, | ||
+ | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
+ | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
+ | * GNU General Public License for more details. | ||
+ | * | ||
+ | * You should have received a copy of the GNU General Public License | ||
+ | * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
+ | */ | ||
+ | |||
+ | /** | ||
+ | * @brief Discretize colors and return uint8_t color code. | ||
+ | * Available colors: COLOR_NONE, COLOR_BLACK, COLOR_BLUE, | ||
+ | * COLOR_GREEN, COLOR_RED, COLOR_WHITE. | ||
+ | * | ||
+ | * Generally speaking, stable measuring conditions are required, i.e., | ||
+ | * a stable measuring distance not exceeding 4 cm, no interfering light | ||
+ | * reaching the side of the sensor. Think about matt black sensor shroud. | ||
+ | * | ||
+ | * Metrics: | ||
+ | * - BASIC_RGB: Simple comparison between channels; | ||
+ | * Very fast but is likely to produce errors. | ||
+ | * - MANHATTAN: Sum of absolute values of distances. | ||
+ | * Quite heavy, but quite accurate if the reference values have been | ||
+ | * measured seriously and if the measurement environment is controlled | ||
+ | * (reproducible). Distance between the sensor and the object should be | ||
+ | * the same as during learning. | ||
+ | * https://fr.wikipedia.org/wiki/Distance_de_Manhattan | ||
+ | * - CANBERRA: A weighted version of Manhattan distance; | ||
+ | * Very heavy but brings a higher accuracy and more tolerance/stability to variations | ||
+ | * in the measurement environment. | ||
+ | * Note: The manipulation of decimal numbers should be avoided | ||
+ | * on microcontrollers... Does it worth it? Probably not. | ||
+ | * https://en.wikipedia.org/wiki/Canberra_distance | ||
+ | */ | ||
+ | //#define BASIC_RGB | ||
+ | //#define MANHATTAN | ||
+ | //#define CANBERRA | ||
+ | |||
+ | // Colors (detected & LED (except NONE for this last one)) expected values | ||
+ | #define COLOR_NONE 0xFF | ||
+ | #define COLOR_BLACK 0 | ||
+ | #define COLOR_PINK 1 | ||
+ | #define COLOR_PURPLE 2 | ||
+ | #define COLOR_BLUE 3 | ||
+ | #define COLOR_LIGHTBLUE 4 | ||
+ | #define COLOR_CYAN 5 | ||
+ | #define COLOR_GREEN 6 | ||
+ | #define COLOR_YELLOW 7 | ||
+ | #define COLOR_ORANGE 8 | ||
+ | #define COLOR_RED 9 | ||
+ | #define COLOR_WHITE 10 | ||
+ | |||
+ | extern uint16_t red, green, blue; | ||
+ | |||
+ | |||
+ | #ifdef BASIC_RGB | ||
+ | uint8_t detectColor(const uint16_t &red, const uint16_t &green, const uint16_t &blue) { | ||
+ | if ((red > green) && (red > blue)) { | ||
+ | return COLOR_RED; | ||
+ | } else if ((green > red) && (green > blue)) { | ||
+ | return COLOR_GREEN; | ||
+ | } else if ((blue > red) && (blue > green)) { | ||
+ | return COLOR_BLUE; | ||
+ | } | ||
+ | } | ||
+ | #endif | ||
+ | |||
+ | |||
+ | #if (defined(MANHATTAN) || defined(CANBERRA)) | ||
+ | // *_1: measures at 1 cm | ||
+ | // *_3: measures at 3 cms | ||
+ | const uint16_t SAMPLES[][3] = { | ||
+ | { 297, 83, 56 }, // RED_1 | ||
+ | { 43, 20, 17 }, // RED_3 | ||
+ | { 35, 142, 193 }, // BLUE_1 | ||
+ | { 35, 94, 116 }, // BLUE_3 | ||
+ | { 86, 257, 257 }, // CYAN_1 | ||
+ | { 36, 98, 97 }, // CYAN_3 | ||
+ | { 120, 141, 46 }, // YELLOW_1 | ||
+ | { 72, 73, 30 }, // YELLOW_3 | ||
+ | { 338, 373, 120 }, // YELLOW_PLQ_1 | ||
+ | { 159, 267, 201 }, // WHITE_1 | ||
+ | { 87, 126, 102 }, // WHITE_3 | ||
+ | { 89, 322, 163 }, // GREEN_1 | ||
+ | { 58, 106, 68 }, // GREEN_3 | ||
+ | { 103, 189, 57 }, // GREEN_LIGHT_1 | ||
+ | { 51, 77, 33 }, // GREEN_LIGHT_3 | ||
+ | { 26, 34, 28 } // BLACK_1 | ||
+ | }; | ||
+ | |||
+ | const uint8_t SAMPLES_MAP[] = { | ||
+ | COLOR_RED, COLOR_RED, | ||
+ | COLOR_BLUE, COLOR_BLUE, | ||
+ | COLOR_BLUE, COLOR_BLUE, | ||
+ | COLOR_YELLOW, COLOR_YELLOW,COLOR_YELLOW, | ||
+ | COLOR_WHITE, COLOR_WHITE, | ||
+ | COLOR_GREEN, COLOR_GREEN, | ||
+ | COLOR_GREEN, COLOR_GREEN, | ||
+ | COLOR_BLACK | ||
+ | }; | ||
+ | |||
+ | // Number of samples | ||
+ | const uint8_t samplesCount = sizeof(SAMPLES) / sizeof(SAMPLES[0]); | ||
+ | |||
+ | uint8_t detectColor(const uint16_t &red, const uint16_t &green, const uint16_t &blue) { | ||
+ | #ifdef MANHATTAN | ||
+ | uint16_t minDist = 10000; | ||
+ | uint16_t expDist; | ||
+ | #else | ||
+ | float minDist = 3; | ||
+ | float expDist; | ||
+ | #endif | ||
+ | uint8_t bestSampleIndex = 0; | ||
+ | |||
+ | for (uint8_t i = 0; i < samplesCount; i++) { | ||
+ | #ifdef MANHATTAN | ||
+ | expDist = abs(static_cast<int16_t>(red - SAMPLES[i][0])) | ||
+ | + abs(static_cast<int16_t>(green - SAMPLES[i][1])) | ||
+ | + abs(static_cast<int16_t>(blue - SAMPLES[i][2])); | ||
+ | #else | ||
+ | // Yeah it's ugly but abs() of Arduino is a macro different from the stl implementation | ||
+ | // moreover the parameter must be explicitly signed. | ||
+ | // The numerator or denominator must be a float. | ||
+ | // https://www.best-microcontroller-projects.com/arduino-absolute-value.html | ||
+ | // https://github.com/arduino/reference-en/issues/362 | ||
+ | expDist = (abs(static_cast<int16_t>(red - SAMPLES[i][0])) / static_cast<float>(red + SAMPLES[i][0])) | ||
+ | + (abs(static_cast<int16_t>(green - SAMPLES[i][1])) / static_cast<float>(green + SAMPLES[i][1])) | ||
+ | + (abs(static_cast<int16_t>(blue - SAMPLES[i][2])) / static_cast<float>(blue + SAMPLES[i][2])); | ||
+ | #endif | ||
+ | if (expDist < minDist) { | ||
+ | bestSampleIndex = i; | ||
+ | minDist = expDist; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Arbitrary threshold to avoid erroneous identifications | ||
+ | #ifdef MANHATTAN | ||
+ | if (minDist > 100) { | ||
+ | #else | ||
+ | if (minDist > 1.9) { // Red color is quite difficult to identify even with this high threashold | ||
+ | #endif | ||
+ | // Matching is not acceptable | ||
+ | return COLOR_NONE; | ||
+ | } | ||
+ | // Get color value expected by the hub | ||
+ | return SAMPLES_MAP[bestSampleIndex]; | ||
+ | } | ||
+ | |||
+ | |||
+ | #endif | ||
+ | |||
+ | </pre> | ||
+ | Color_sensor.ino | ||
+ | <pre> | ||
+ | |||
+ | /* This file is taken from MyOwnBricks project. | ||
+ | * MyOwnBricks is a library for the emulation of LEGO PoweredUp sensors on microcontrollers | ||
+ | * Copyright (C) 2021-2022 Ysard - <ysard@users.noreply.github.com> | ||
+ | * | ||
+ | * This program is free software: you can redistribute it and/or modify | ||
+ | * it under the terms of the GNU General Public License as published by | ||
+ | * the Free Software Foundation, either version 3 of the License, or | ||
+ | * (at your option) any later version. | ||
+ | * | ||
+ | * This program is distributed in the hope that it will be useful, | ||
+ | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
+ | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
+ | * GNU General Public License for more details. | ||
+ | * | ||
+ | * You should have received a copy of the GNU General Public License | ||
+ | * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
+ | */ | ||
+ | |||
+ | /* | ||
+ | * Sensor Pro Micro | ||
+ | * SCL SCL pin 3 | ||
+ | * SDA SDA pin 2 | ||
+ | * INT PCINT4, port PB4, pin 8 | ||
+ | * VIN VCC (3.3V) | ||
+ | * GND GND | ||
+ | * | ||
+ | * LED pin of the sensor can be connected to its INT pin. | ||
+ | * By doing this the leds will turn off when the measurements are done. | ||
+ | * | ||
+ | * Pro Micro: | ||
+ | * Serial: UART via USB | ||
+ | * Serial1: pin 1 (TX), pin 0 (RX) | ||
+ | */ | ||
+ | #include <Wire.h> | ||
+ | #include <tcs34725.h> | ||
+ | |||
+ | #define MANHATTAN | ||
+ | #include "color_detection_methods.hpp" | ||
+ | |||
+ | |||
+ | #define RGB_SENSOR_INTERRUPT_PIN 8 | ||
+ | #define RGB_SENSOR_INTERRUPT_PORT PB4 // Port alias of pin 8 to be used in tstPin() | ||
+ | // Equivalent of digitalRead but for PORTB pins & much more quicker for a use in an ISR | ||
+ | // https://www.arduino.cc/en/Reference/PortManipulation | ||
+ | #define tstPin(b) ((PINB & (1 << (b))) != 0) | ||
+ | #define LUX_TO_PERCENTAGE(val) (getPercentage(val, 0.0105, -0.0843)) | ||
+ | #define REFLECTED_LIGHT_TO_PERCENTAGE(val) (getPercentage(val, 0.0017, -8)) | ||
+ | |||
+ | uint8_t sensorColor; | ||
+ | uint8_t reflectedLight; | ||
+ | uint8_t ambientLight; | ||
+ | uint16_t red, green, blue, clear, lux; | ||
+ | volatile bool sensorReady; | ||
+ | |||
+ | // Default settings: TCS34725_GAIN_4X, TCS34725_INTEGRATIONTIME_154MS | ||
+ | TCS34725 rgb_sensor; | ||
+ | |||
+ | /** | ||
+ | * @brief Callback for PCINT4 interrupt (PCINT0 - PCINT7) | ||
+ | */ | ||
+ | ISR(PCINT0_vect) { | ||
+ | // If RGB_SENSOR_INTERRUPT_PORT is LOW, sensor is ready | ||
+ | if (!tstPin(RGB_SENSOR_INTERRUPT_PORT)) | ||
+ | sensorReady = true; | ||
+ | } | ||
+ | |||
+ | |||
+ | /** | ||
+ | * @brief Map lux/reflected light values to percentages | ||
+ | * Weights of the equation must be calculated empirically | ||
+ | * Map equation: y = ax + b | ||
+ | * System to solve: | ||
+ | * 100% = MaxRawValue * a + b | ||
+ | * 0% = MinRawValue * a + b | ||
+ | * | ||
+ | * See macros LUX_TO_PERCENTAGE and REFLECTED_LIGHT_TO_PERCENTAGE. | ||
+ | */ | ||
+ | uint8_t getPercentage(const uint16_t rawValue, const float& a_coef, const float& b_coef) { | ||
+ | int8_t percent = static_cast<int8_t>(rawValue * a_coef + b_coef); | ||
+ | if (percent > 100) | ||
+ | return 100; | ||
+ | if (percent < 0) | ||
+ | return 0; | ||
+ | return static_cast<uint8_t>(percent); | ||
+ | } | ||
+ | |||
+ | |||
+ | void setup() { | ||
+ | Serial.begin(115200); | ||
+ | while (!Serial) { | ||
+ | // Wait for serial port to connect. | ||
+ | } | ||
+ | |||
+ | // Device config | ||
+ | sensorColor = COLOR_NONE; | ||
+ | |||
+ | // Colour sensor config | ||
+ | // Configure PinChange Interrupt | ||
+ | // See https://github.com/NicoHood/PinChangeInterrupt | ||
+ | // Note: INT-0,1,2,3 are occupied by UART and i2c transmissions on pro-micro | ||
+ | // /!\ DO NOT activate pullup from the arduino, the INT pin is usually already | ||
+ | // pulled up into the sensor board itself to 3.3V. These pins (SCL, SDA, INT) | ||
+ | // ARE NOT tolerant to more than VDD + 0.5V. Note that I2C pins are connected | ||
+ | // to level shifters, but not the others. | ||
+ | pinMode(RGB_SENSOR_INTERRUPT_PIN, INPUT); // TCS interrupt output is Active-LOW and Open-Drain | ||
+ | cli(); // Disable all interrupts: Avoid first and not wanted trigger of the interrupt | ||
+ | PCICR |= 0b00000001; // enable PORTB pin change interrupt | ||
+ | PCMSK0 |= 0b00010000; // enable PB4, PCINT4, pin 8 | ||
+ | sei(); // Enable all interrupts | ||
+ | |||
+ | while (!rgb_sensor.begin()) { | ||
+ | Serial.println(F("TCS34725 NOT found")); | ||
+ | delay(200); | ||
+ | } | ||
+ | Serial.println(F("Found sensor")); | ||
+ | |||
+ | // Set persistence filter to generate an interrupt for every RGB Cycle, | ||
+ | // regardless of the integration limits | ||
+ | rgb_sensor.tcs.write8(TCS34725_PERS, TCS34725_PERS_NONE); | ||
+ | // RGBC interrupt enable. When asserted, permits RGBC interrupts to be generated. | ||
+ | rgb_sensor.tcs.setInterrupt(true); | ||
+ | } | ||
+ | |||
+ | |||
+ | void loop() | ||
+ | { | ||
+ | if (sensorReady) { | ||
+ | // Data measurement | ||
+ | // noDelay param set to true: Asynchronous mode, must be used with interrupt configured. | ||
+ | bool status = rgb_sensor.updateData(true); | ||
+ | |||
+ | if (status) { | ||
+ | // Ambient light (lux) computation | ||
+ | rgb_sensor.updateLux(); | ||
+ | |||
+ | int16_t lux = lround(rgb_sensor.lux); | ||
+ | |||
+ | // Sometimes lux values are below 0; this coincides with erroneous data | ||
+ | // Moreover, we discard data taken below 40 lux. | ||
+ | if ((lux >= 40) && (static_cast<uint16_t>(lux) <= rgb_sensor.maxlux)) { | ||
+ | // Set ambient light (lux) - map 0-100 | ||
+ | //ambientLight = map(rgb_sensor.lux, 0, rgb_sensor.maxlux, 0, 100); | ||
+ | ambientLight = LUX_TO_PERCENTAGE(lux); // cast ? | ||
+ | |||
+ | // RGBC Channels are usable | ||
+ | // Map values to max ~440; | ||
+ | // Continuous values from 0-65535 (16bits) to 0-1023 (10bits) | ||
+ | red = rgb_sensor.r_comp >> 6, | ||
+ | green = rgb_sensor.g_comp >> 6, | ||
+ | blue = rgb_sensor.b_comp >> 6, | ||
+ | |||
+ | // Set clear channel as reflected light - map 0-100 | ||
+ | reflectedLight = REFLECTED_LIGHT_TO_PERCENTAGE(rgb_sensor.c_comp); | ||
+ | |||
+ | // Set detected color | ||
+ | sensorColor = detectColor(red, green, blue); | ||
+ | } else { | ||
+ | sensorColor = COLOR_NONE; | ||
+ | } | ||
+ | clear = rgb_sensor.c_comp >> 6; | ||
+ | /* | ||
+ | // Human readable debugging | ||
+ | Serial.print("Lux: "); Serial.print(rgb_sensor.lux, DEC); | ||
+ | Serial.print("; max: "); Serial.print(rgb_sensor.maxlux); | ||
+ | Serial.print("; R: "); Serial.print(red, DEC); | ||
+ | Serial.print("; G: "); Serial.print(green, DEC); | ||
+ | Serial.print("; B: "); Serial.print(blue, DEC); | ||
+ | Serial.print("; C: "); Serial.println(clear, DEC); | ||
+ | */ | ||
+ | |||
+ | // Spreadsheet debugging | ||
+ | Serial.print(rgb_sensor.lux, DEC); Serial.print(";"); | ||
+ | Serial.print(rgb_sensor.maxlux); Serial.print(";"); | ||
+ | Serial.print(red, DEC); Serial.print(";"); | ||
+ | Serial.print(green, DEC); Serial.print(";"); | ||
+ | Serial.print(blue, DEC); Serial.print(";"); | ||
+ | Serial.println(clear, DEC); | ||
+ | } else { | ||
+ | sensorColor = COLOR_NONE; | ||
+ | Serial.println(F("not valid data! wait next measure")); | ||
+ | } | ||
+ | // Interrupt tear down | ||
+ | rgb_sensor.tcs.clearInterrupt(); | ||
+ | sensorReady = false; | ||
+ | PCIFR &= ~(1 << PCIF0); // clear PC interrupt flag in case of bounce | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
</pre> | </pre> | ||
Ligne 81 : | Ligne 787 : | ||
[[Catégorie:Enib2023]] | [[Catégorie:Enib2023]] | ||
+ | |||
+ | [[Catégorie:Arduino]] |
Version actuelle datée du 15 janvier 2024 à 16:00
Sommaire
Les petits voyageurs:
Description courte
Résoudre une énigme faisant deviner un pays.
Déplacer le capteur de couleur dans le labyrinthe pour faire une suite de couleur correspondant au drapeau du pays.
Matériels et machines utilisés
- plaque de bois de 30cm x 30cm
- 2 boutons poussoirs
- 1 capteur RGB TCS3200
- carte Arduino
- cartons ( pour créer le prototype)
- câbles Dupont
- plaque de Cork de 30cm x 30cm
- colle à bois
- peintures et pinceaux
- cutter
- pic à broque
- scotch
- découpeuse laser
- poste à souder
Réalisation
Etape 1: Souder les composants si nécessaires.
Etape 2: Créer les énigmes. Ci dessous la liste de nos énigmes Fichier:Liste des énigmes.docx
Etape 3: Ecrire le code. (cf code ci dessous)
Etape 4: Etablir un plan du labyrinthe.
Etape 5: Faire un prototype du labyrinthe en carton.
Etape 6: tracer le labyrinthe sur la plaque de bois.
Etape 7: Sur inskape, creer un fichier avec toutes les murs du labyrinthe qui doivent être découpés / graver.
Etape 8:
Découper et graver les murs du labyrinthe à la découpe laser.
Etape 9: Peindre certaines cases du labyrinthe en fonction des couleurs nécessaires.
Astuce : Mettre du scotch pour avoir des bords lisses
Etape 10: A l'aide de la colle à bois, coller les murs du labyrinthe.
Etape 11:
Creer une petite protection pour le capteur de couleur afin de pouvoir le déplacer dans le labyrinthe.
Etape 12: Rajouter les différents éléments ( carte arduino, capteurs, boutons poussoirs).
Code
prog.cpp:
#include "color.hpp" #include "enigme.hpp" #include <iostream> #include <random> #include <vector> int main() { //variable initiale int erreur=-1; int test=1; int bonneReponse=0; //liste des couleurs disponible pour les drapeaux deb::Color rouge{1024, 0, 0}; deb::Color bleu{0, 0, 1024}; deb::Color blanc{1024, 1024, 1024}; deb::Color noir{0, 0, 0}; deb::Color vert{0, 1024 ,0}; deb::Color orange{0, 0, 1024}; deb::Color jaune{0, 0, 1024}; //liste des enigmes deb::Enigme ea{bleu, blanc, rouge, "Pays du champagne"}; deb::Enigme eb{vert, blanc, rouge, "Pays des pâtes"}; deb::Enigme ec{vert, blanc, orange, "Pays des moutons "}; deb::Enigme ed{rouge, blanc, rouge, "Pays d’origine de Marie Antoinette"}; deb::Enigme ee{noir, rouge, jaune, "Pays d’origine des knödels "}; deb::Enigme ef{jaune, vert, rouge, "Quel est ce pays ? Mjubojf"}; deb::Enigme eg{rouge, blanc, bleu, "Quel est ce pays ? .-.. ..- -..- . -- -... --- ..- .-. --."}; deb::Enigme eh{rouge, blanc, bleu, "Quel est ce pays ? Ozxr-Azr"}; deb::Enigme ei{noir, jaune, rouge, "Pays de la gaufre"}; deb::Enigme ej{vert, jaune, bleu, "Mon premier est un mec. Mon second qualifie quelque chose d’agréable."}; deb::Enigme ek{blanc, bleu, noir, "Quel est ce pays ? . ... - --- -. .. ."}; deb::Enigme el{rouge, blanc, rouge, "Quel est ce pays ? 53886643"}; deb::Enigme em{rouge, blanc, vert, "Mon premier est un pronom personnel indéfini. Mon second contient autant de rouge que de vert que de bleu."}; deb::Enigme en{bleu, jaune, rouge, "Quel est ce pays ? Spvnbojf"}; deb::Enigme eo{bleu, jaune, rouge, "Ma capitale est vieille "}; deb::Enigme ep{bleu, jaune, rouge, "Quel est ce pays ? - -.-. .... .- -.."}; deb::Enigme eq{rouge, bleu, orange, "Son papier sent bon "}; deb::Enigme er{bleu, rouge, vert, "Quel est ce pays ? 2937224526"}; deb::Enigme es{rouge, jaune, vert, "Ma capitale est La Paz"}; deb::Enigme et{rouge, jaune, vert, "Mon premier est la plante sous laquelle il faut s’embrasser. Mon second est l’arrivée en ce monde."}; deb::Enigme eu{blanc, vert, rouge, "Mon premier est une sphère d’une substance dans une autre. Mon second est un mec. Mon troisième peut être cantonais, thai ou basmati. "}; deb::Enigme ev{jaune, bleu, rouge, "Ma capitale est Bogota"}; deb::Enigme ew{orange, blanc, vert, "Quel est ce pays ? Dpuf e’Jwpjsf"}; deb::Enigme ex{vert, jaune, rouge, "Quel est ce pays ? -- .- .-.. .."}; deb::Enigme ey{vert, blanc, vert, "Quel est ce pays ? 6443742"}; deb::Enigme ez{rouge, blanc, rouge, "Pays du Macchu Picchu"}; deb::Enigme eea{blanc, bleu, rouge, "Pays du Kremlin"}; deb::Enigme eeb{rouge, blanc, noir, "Quel est ce pays ? Xdldm"}; std::vector<deb::Enigme> vectEnigme{ea,eb,ec,ed,ee,ef,eg,eh,ei,ej,ek,el,em,en,eo,ep,eq,er,es,et,eu,ev,ew,ex,ez,eea,eeb}; //loop if (appui sur bouton acquisition) //on enregistre la couleur actuelle { int a,b,c = 10,10,1024; //on recupere les valeurs du capteur if (test==4) { bonneReponse=0; //il y a trop de reponse le resultat sera faut } if (test==3) { if (enigmeEnCours.trois.isitme(a,b,c)) {bonneReponse=bonneReponse+1} //on compare a la couleur du drapeau test=4; } if (test==2) { if (enigmeEnCours.deux.isitme(a,b,c)) {bonneReponse=bonneReponse+1} //on compare a la couleur du drapeau test=3; } if (test==1) { if (enigmeEnCours.un.isitme(a,b,c)) {bonneReponse=bonneReponse+1} //on compare a la couleur du drapeau test=2; } } if (appui sur le bouton validé and erreur!=-1)//on regarde le resultat final { if (bonneReponse==3) { std::cout <<"Victoire ! Si tu veux rejouer, clique sur JSP.\n"; erreur =-1; } if (bonneReponse!=3 and erreur==0) { std::cout <<"Tu as fait une erreur. Repars de zéro et essaye encore. \n"; erreur =1; } if (bonneReponse!=3 and erreur==1) { std::cout <<"Tu as perdu. Si tu veux rejouer, clique sur JSP.\n"; erreur =-1; } } if (appui sur bouton validé and erreur==-1) //lancement nouvelle enigme { srand((unsigned) time(NULL)); deb::Enigme enigmeEnCours=vectEnigme[rand() % 28]; std::cout << enigmeEnCours.question << "\n"; erreur=0; test=1; bonneReponse=0; } }
color.cpp:
//---------------------------------------------------------------------------- #include "color.hpp" namespace deb { bool Color::isitme(int r,int g,int b) const { if (r<(red-200)) {return false;} if (r>(red+200)) {return false;} if (g<(green-200)) {return false;} if (g>(green+200)) {return false;} if (b<(blue-200)) {return false;} if (b>(blue+200)) {return false;} return true; } }// namespace deb //----------------------------------------------------------------------------
color.hpp:
//---------------------------------------------------------------------------- #ifndef DEB_COLOR_HPP #define DEB_COLOR_HPP #include <iostream> namespace deb { struct Color { int red,green,blue; Color(int red, int green, int blue): red{std::move(red)}, green{std::move(green)}, blue{std::move(blue)} {/* nothing to do */} bool isitme(int r,int g,int b) const; }; } // namespace deb #endif // DEB_COLOR_HPP //----------------------------------------------------------------------------
enigme.hpp
//---------------------------------------------------------------------------- #ifndef DEB_ENIGME_HPP #define DEB_ENIGME_HPP #include <iostream> #include <string> #include "color.hpp" namespace deb { struct Enigme { Color un,deux,trois; std::string question; Enigme(Color u, Color d, Color t, std::string q): un{std::move(u)}, deux{std::move(d)}, trois{std::move(t)}, question{std::move(q)} {/* nothing to do */} }; } // namespace deb #endif // DEB_ENIGME_HPP //----------------------------------------------------------------------------
code BP :
Les_petits_voyageurs.ino
#include "Test_BP.h" #include "Arduino.h" int lancement=0; void setup() { Serial.begin(115200); setup_BP(); //constantes générées aussi en dehors de la loop } void loop() { lancement=BP(lancement); if (lancement==1) { delay(20); Serial.println("Bonjour ! "); } }
Test_BP.cpp
#include "Test_BP.h" //BP1 const int BP1=4; int boutonState = 0; int lastBoutonState = 0; void setup_BP() { //setup fonction bouton pinMode(BP1, INPUT); } int BP(int lancement) //permet de détecter les FM/FD du BP { /* Serial.print("etat bouton : "); Serial.println(boutonState); Serial.print("etat bouton precedent : "); Serial.println(lastBoutonState); */ boutonState=digitalRead(BP1); Serial.println(boutonState); if (boutonState==HIGH) { if (boutonState != lastBoutonState) { lancement=1; //Serial.println("00"); lastBoutonState=boutonState; } else { lancement=0; //état récurrent de lancement, on reste appuyé sur BP //Serial.println("10"); } } else { if (boutonState != lastBoutonState) { //Serial.println("01"); lancement=0; lastBoutonState=boutonState; } else { //Serial.println("11"); } } //Serial.println(lancement); return lancement; }
#ifndef TEST_BP_H #define TEST_BP_H #include <Arduino.h> void setup_BP(); int BP(int lancement); #endif
code capteur:
color_detection_methods.hpp
/* This file is taken from MyOwnBricks project. * MyOwnBricks is a library for the emulation of LEGO PoweredUp sensors on microcontrollers * Copyright (C) 2021-2022 Ysard - <ysard@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ /** * @brief Discretize colors and return uint8_t color code. * Available colors: COLOR_NONE, COLOR_BLACK, COLOR_BLUE, * COLOR_GREEN, COLOR_RED, COLOR_WHITE. * * Generally speaking, stable measuring conditions are required, i.e., * a stable measuring distance not exceeding 4 cm, no interfering light * reaching the side of the sensor. Think about matt black sensor shroud. * * Metrics: * - BASIC_RGB: Simple comparison between channels; * Very fast but is likely to produce errors. * - MANHATTAN: Sum of absolute values of distances. * Quite heavy, but quite accurate if the reference values have been * measured seriously and if the measurement environment is controlled * (reproducible). Distance between the sensor and the object should be * the same as during learning. * https://fr.wikipedia.org/wiki/Distance_de_Manhattan * - CANBERRA: A weighted version of Manhattan distance; * Very heavy but brings a higher accuracy and more tolerance/stability to variations * in the measurement environment. * Note: The manipulation of decimal numbers should be avoided * on microcontrollers... Does it worth it? Probably not. * https://en.wikipedia.org/wiki/Canberra_distance */ //#define BASIC_RGB //#define MANHATTAN //#define CANBERRA // Colors (detected & LED (except NONE for this last one)) expected values #define COLOR_NONE 0xFF #define COLOR_BLACK 0 #define COLOR_PINK 1 #define COLOR_PURPLE 2 #define COLOR_BLUE 3 #define COLOR_LIGHTBLUE 4 #define COLOR_CYAN 5 #define COLOR_GREEN 6 #define COLOR_YELLOW 7 #define COLOR_ORANGE 8 #define COLOR_RED 9 #define COLOR_WHITE 10 extern uint16_t red, green, blue; #ifdef BASIC_RGB uint8_t detectColor(const uint16_t &red, const uint16_t &green, const uint16_t &blue) { if ((red > green) && (red > blue)) { return COLOR_RED; } else if ((green > red) && (green > blue)) { return COLOR_GREEN; } else if ((blue > red) && (blue > green)) { return COLOR_BLUE; } } #endif #if (defined(MANHATTAN) || defined(CANBERRA)) // *_1: measures at 1 cm // *_3: measures at 3 cms const uint16_t SAMPLES[][3] = { { 297, 83, 56 }, // RED_1 { 43, 20, 17 }, // RED_3 { 35, 142, 193 }, // BLUE_1 { 35, 94, 116 }, // BLUE_3 { 86, 257, 257 }, // CYAN_1 { 36, 98, 97 }, // CYAN_3 { 120, 141, 46 }, // YELLOW_1 { 72, 73, 30 }, // YELLOW_3 { 338, 373, 120 }, // YELLOW_PLQ_1 { 159, 267, 201 }, // WHITE_1 { 87, 126, 102 }, // WHITE_3 { 89, 322, 163 }, // GREEN_1 { 58, 106, 68 }, // GREEN_3 { 103, 189, 57 }, // GREEN_LIGHT_1 { 51, 77, 33 }, // GREEN_LIGHT_3 { 26, 34, 28 } // BLACK_1 }; const uint8_t SAMPLES_MAP[] = { COLOR_RED, COLOR_RED, COLOR_BLUE, COLOR_BLUE, COLOR_BLUE, COLOR_BLUE, COLOR_YELLOW, COLOR_YELLOW,COLOR_YELLOW, COLOR_WHITE, COLOR_WHITE, COLOR_GREEN, COLOR_GREEN, COLOR_GREEN, COLOR_GREEN, COLOR_BLACK }; // Number of samples const uint8_t samplesCount = sizeof(SAMPLES) / sizeof(SAMPLES[0]); uint8_t detectColor(const uint16_t &red, const uint16_t &green, const uint16_t &blue) { #ifdef MANHATTAN uint16_t minDist = 10000; uint16_t expDist; #else float minDist = 3; float expDist; #endif uint8_t bestSampleIndex = 0; for (uint8_t i = 0; i < samplesCount; i++) { #ifdef MANHATTAN expDist = abs(static_cast<int16_t>(red - SAMPLES[i][0])) + abs(static_cast<int16_t>(green - SAMPLES[i][1])) + abs(static_cast<int16_t>(blue - SAMPLES[i][2])); #else // Yeah it's ugly but abs() of Arduino is a macro different from the stl implementation // moreover the parameter must be explicitly signed. // The numerator or denominator must be a float. // https://www.best-microcontroller-projects.com/arduino-absolute-value.html // https://github.com/arduino/reference-en/issues/362 expDist = (abs(static_cast<int16_t>(red - SAMPLES[i][0])) / static_cast<float>(red + SAMPLES[i][0])) + (abs(static_cast<int16_t>(green - SAMPLES[i][1])) / static_cast<float>(green + SAMPLES[i][1])) + (abs(static_cast<int16_t>(blue - SAMPLES[i][2])) / static_cast<float>(blue + SAMPLES[i][2])); #endif if (expDist < minDist) { bestSampleIndex = i; minDist = expDist; } } // Arbitrary threshold to avoid erroneous identifications #ifdef MANHATTAN if (minDist > 100) { #else if (minDist > 1.9) { // Red color is quite difficult to identify even with this high threashold #endif // Matching is not acceptable return COLOR_NONE; } // Get color value expected by the hub return SAMPLES_MAP[bestSampleIndex]; } #endif
Color_sensor.ino
/* This file is taken from MyOwnBricks project. * MyOwnBricks is a library for the emulation of LEGO PoweredUp sensors on microcontrollers * Copyright (C) 2021-2022 Ysard - <ysard@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ /* * Sensor Pro Micro * SCL SCL pin 3 * SDA SDA pin 2 * INT PCINT4, port PB4, pin 8 * VIN VCC (3.3V) * GND GND * * LED pin of the sensor can be connected to its INT pin. * By doing this the leds will turn off when the measurements are done. * * Pro Micro: * Serial: UART via USB * Serial1: pin 1 (TX), pin 0 (RX) */ #include <Wire.h> #include <tcs34725.h> #define MANHATTAN #include "color_detection_methods.hpp" #define RGB_SENSOR_INTERRUPT_PIN 8 #define RGB_SENSOR_INTERRUPT_PORT PB4 // Port alias of pin 8 to be used in tstPin() // Equivalent of digitalRead but for PORTB pins & much more quicker for a use in an ISR // https://www.arduino.cc/en/Reference/PortManipulation #define tstPin(b) ((PINB & (1 << (b))) != 0) #define LUX_TO_PERCENTAGE(val) (getPercentage(val, 0.0105, -0.0843)) #define REFLECTED_LIGHT_TO_PERCENTAGE(val) (getPercentage(val, 0.0017, -8)) uint8_t sensorColor; uint8_t reflectedLight; uint8_t ambientLight; uint16_t red, green, blue, clear, lux; volatile bool sensorReady; // Default settings: TCS34725_GAIN_4X, TCS34725_INTEGRATIONTIME_154MS TCS34725 rgb_sensor; /** * @brief Callback for PCINT4 interrupt (PCINT0 - PCINT7) */ ISR(PCINT0_vect) { // If RGB_SENSOR_INTERRUPT_PORT is LOW, sensor is ready if (!tstPin(RGB_SENSOR_INTERRUPT_PORT)) sensorReady = true; } /** * @brief Map lux/reflected light values to percentages * Weights of the equation must be calculated empirically * Map equation: y = ax + b * System to solve: * 100% = MaxRawValue * a + b * 0% = MinRawValue * a + b * * See macros LUX_TO_PERCENTAGE and REFLECTED_LIGHT_TO_PERCENTAGE. */ uint8_t getPercentage(const uint16_t rawValue, const float& a_coef, const float& b_coef) { int8_t percent = static_cast<int8_t>(rawValue * a_coef + b_coef); if (percent > 100) return 100; if (percent < 0) return 0; return static_cast<uint8_t>(percent); } void setup() { Serial.begin(115200); while (!Serial) { // Wait for serial port to connect. } // Device config sensorColor = COLOR_NONE; // Colour sensor config // Configure PinChange Interrupt // See https://github.com/NicoHood/PinChangeInterrupt // Note: INT-0,1,2,3 are occupied by UART and i2c transmissions on pro-micro // /!\ DO NOT activate pullup from the arduino, the INT pin is usually already // pulled up into the sensor board itself to 3.3V. These pins (SCL, SDA, INT) // ARE NOT tolerant to more than VDD + 0.5V. Note that I2C pins are connected // to level shifters, but not the others. pinMode(RGB_SENSOR_INTERRUPT_PIN, INPUT); // TCS interrupt output is Active-LOW and Open-Drain cli(); // Disable all interrupts: Avoid first and not wanted trigger of the interrupt PCICR |= 0b00000001; // enable PORTB pin change interrupt PCMSK0 |= 0b00010000; // enable PB4, PCINT4, pin 8 sei(); // Enable all interrupts while (!rgb_sensor.begin()) { Serial.println(F("TCS34725 NOT found")); delay(200); } Serial.println(F("Found sensor")); // Set persistence filter to generate an interrupt for every RGB Cycle, // regardless of the integration limits rgb_sensor.tcs.write8(TCS34725_PERS, TCS34725_PERS_NONE); // RGBC interrupt enable. When asserted, permits RGBC interrupts to be generated. rgb_sensor.tcs.setInterrupt(true); } void loop() { if (sensorReady) { // Data measurement // noDelay param set to true: Asynchronous mode, must be used with interrupt configured. bool status = rgb_sensor.updateData(true); if (status) { // Ambient light (lux) computation rgb_sensor.updateLux(); int16_t lux = lround(rgb_sensor.lux); // Sometimes lux values are below 0; this coincides with erroneous data // Moreover, we discard data taken below 40 lux. if ((lux >= 40) && (static_cast<uint16_t>(lux) <= rgb_sensor.maxlux)) { // Set ambient light (lux) - map 0-100 //ambientLight = map(rgb_sensor.lux, 0, rgb_sensor.maxlux, 0, 100); ambientLight = LUX_TO_PERCENTAGE(lux); // cast ? // RGBC Channels are usable // Map values to max ~440; // Continuous values from 0-65535 (16bits) to 0-1023 (10bits) red = rgb_sensor.r_comp >> 6, green = rgb_sensor.g_comp >> 6, blue = rgb_sensor.b_comp >> 6, // Set clear channel as reflected light - map 0-100 reflectedLight = REFLECTED_LIGHT_TO_PERCENTAGE(rgb_sensor.c_comp); // Set detected color sensorColor = detectColor(red, green, blue); } else { sensorColor = COLOR_NONE; } clear = rgb_sensor.c_comp >> 6; /* // Human readable debugging Serial.print("Lux: "); Serial.print(rgb_sensor.lux, DEC); Serial.print("; max: "); Serial.print(rgb_sensor.maxlux); Serial.print("; R: "); Serial.print(red, DEC); Serial.print("; G: "); Serial.print(green, DEC); Serial.print("; B: "); Serial.print(blue, DEC); Serial.print("; C: "); Serial.println(clear, DEC); */ // Spreadsheet debugging Serial.print(rgb_sensor.lux, DEC); Serial.print(";"); Serial.print(rgb_sensor.maxlux); Serial.print(";"); Serial.print(red, DEC); Serial.print(";"); Serial.print(green, DEC); Serial.print(";"); Serial.print(blue, DEC); Serial.print(";"); Serial.println(clear, DEC); } else { sensorColor = COLOR_NONE; Serial.println(F("not valid data! wait next measure")); } // Interrupt tear down rgb_sensor.tcs.clearInterrupt(); sensorReady = false; PCIFR &= ~(1 << PCIF0); // clear PC interrupt flag in case of bounce } }