Différences
Ci-dessous, les différences entre deux révisions de la page.
projets:brutpix:telecommande [2020/11/25 10:18] – modification externe 127.0.0.1 | — | ||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
- | ====== Brutpix ====== | ||
- | |||
- | Brutpix est un projet initié par Ben Farey, constructeur et plasticien sonore du collectif Tricyclique Dol et Yves Petit, photographe. Le projet s' | ||
- | * des appareils photographiques DIY solides, simples et peu coûteux, basés sur des Raspberry Pi | ||
- | * un système automatique d' | ||
- | * une télécommande autonome, simple, qui permettra de traiter en temps réél les images affichées | ||
- | |||
- | ===== Télécommande ===== | ||
- | |||
- | Description technique: il s'agit de réaliser une télécommande à 5 potentiomètres infinis (encodeurs), | ||
- | |||
- | ==== Étape 1: premiers tests et déconvenues ==== | ||
- | |||
- | Le choix technique s'est porté sur des ESP8266, carte de type Wemos. Nous avons l' | ||
- | |||
- | Dessin de la carte électronique du prototype sous Kicad, usinage sur la CNC du fablab. Après nettoyage des pistes (la gravure n'est jamais parfaite), puis soudure des composants, la programmation peut commencer (et les ennuis). | ||
- | |||
- | {{: | ||
- | {{: | ||
- | |||
- | **Problème n°1**: dans un souci de bien faire, la broche RST a été mise à la masse pour éviter de la laisser en l'air. Erreur. C'est quand elle est à la masse qu' | ||
- | |||
- | **Problèmes n°2**: pour utiliser les 11 GPIO disponibles, | ||
- | ``` | ||
- | pinMode(pin, | ||
- | ``` | ||
- | |||
- | {{: | ||
- | |||
- | Pour changer l' | ||
- | ``` | ||
- | pinMode(3, FUNCTION_3); | ||
- | pinMode(3, INPUT); | ||
- | pinMode(1, FUNCTION_3); | ||
- | pinMode(1, INPUT); | ||
- | ``` | ||
- | Après essai, cela fonctionne, problème 2 résolu. | ||
- | |||
- | **Problème n°3**: celui était inattendu. En effet, à la lecture du tableau cité plus haut, nous pouvons nous apercevoir que certaines broches ont des comportements particuliers au boot et à l' | ||
- | |||
- | Néanmoins, nous avons 3 encodeurs sur 5 qui sont prêts à être programmés. | ||
- | |||
- | ==== Étape 2: code pour lire les encodeurs ==== | ||
- | Le fonctionnement des encodeurs à quadrature est abondamment documenté sur le net, mais je vais malgré cela tenter d'en illustrer le fonctionnement. En réalité, c'est surtout le prétexte d' | ||
- | Voyons d' | ||
- | |||
- | Maintenant que nous avons bien saisi le fonctionnement électronique de ce composant, nous allons pouvoir rédiger un programme pour lire le sens de rotation. Notre base sera le code proposé sur https:// | ||
- | |||
- | ``` | ||
- | //Les broches de l' | ||
- | int encoderPin1 = D5; | ||
- | int encoderPin2 = D6; | ||
- | |||
- | volatile int lastEncoded = 0; | ||
- | volatile long encoderValue = 0; | ||
- | |||
- | long lastencoderValue = 0; | ||
- | |||
- | int lastMSB = 0; | ||
- | int lastLSB = 0; | ||
- | |||
- | void setup() { | ||
- | Serial.begin (9600); | ||
- | |||
- | pinMode(encoderPin1, | ||
- | pinMode(encoderPin2, | ||
- | | ||
- | //Commenté car les résistances pullup sont integrées au montage | ||
- | // | ||
- | // | ||
- | |||
- | //Nous appelon updateEncoder() à chaque changement d' | ||
- | attachInterrupt(digitalPinToInterrupt(encoderPin1), | ||
- | attachInterrupt(digitalPinToInterrupt(encoderPin2), | ||
- | |||
- | } | ||
- | |||
- | void loop() { | ||
- | Serial.println(encoderValue); | ||
- | delay(1000); | ||
- | } | ||
- | |||
- | ICACHE_RAM_ATTR void updateEncoder() { | ||
- | int MSB = digitalRead(encoderPin1); | ||
- | int LSB = digitalRead(encoderPin2); | ||
- | int encoded = (MSB << 1) | LSB; // | ||
- | int sum = (lastEncoded << 2) | encoded; //adding it to the previous encoded value | ||
- | // | ||
- | if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue ++; | ||
- | if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue --; | ||
- | lastEncoded = encoded; //store this value for next time | ||
- | } | ||
- | ``` | ||
- | ===== Utiliser les interruptions ===== | ||
- | |||
- | Le programme ci-dessus utilise des interruptions. Les interruptions externes permettent d' | ||
- | Nous nous servons donc de ces interruptions pour analyser la direction de l' | ||
- | |||
- | Il faut d' | ||
- | ``` | ||
- | pinMode(encoderPin1, | ||
- | pinMode(encoderPin2, | ||
- | ``` | ||
- | puis les lier à une interruption. il faudra également définir quelle fonction sera appelée à chaque fois qu'un événement sera détecté (ici *updateEncoder*), | ||
- | ``` | ||
- | attachInterrupt(digitalPinToInterrupt(encoderPin1), | ||
- | attachInterrupt(digitalPinToInterrupt(encoderPin2), | ||
- | |||
- | ``` | ||
- | et enfin, créer la fonction qui sera effectuée à chaque interruption. Celle-ci doit rester la plus brève possible, pour ne pas ralentir le programme. L' | ||
- | ``` | ||
- | ICACHE_RAM_ATTR void updateEncoder() { | ||
- | int MSB = digitalRead(encoderPin1); | ||
- | int LSB = digitalRead(encoderPin2); | ||
- | int encoded = (MSB << 1) | LSB; // | ||
- | int sum = (lastEncoded << 2) | encoded; //adding it to the previous encoded value | ||
- | // | ||
- | if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue ++; | ||
- | if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue --; | ||
- | lastEncoded = encoded; //store this value for next time | ||
- | } | ||
- | ``` | ||
- | |||
- | NB: Les variables globales que nous voudrons manipuler à l' | ||
- | |||
- | ==== Étape 3: deuxième prototype ==== | ||
- | Le deuxième prototype utilisera donc un multiplexeur, | ||
- | Tests à réaliser avec cette [[https:// | ||
- | |||
- | Notes: | ||
- | |||
- | https:// | ||
- | https:// | ||
- | |||
- | Voici les tests réalisés avec les composants reçus récemment. La puce MCP23017 fonctionne en i2c, et peut transmettre via cette connexion l' | ||
- | |||
- | Schéma à venir. | ||
- | |||
- | {{ : | ||
- | |||
- | Voici le code qui permet de tester le fonctionnement de ce multiplexeur. À noter, une info très importante et particulièrement étonnante: l'une des entrées est défectueuse, | ||
- | |||
- | Voici ce que dit le fabricant: | ||
- | |||
- | ``` | ||
- | On MCP23008/ | ||
- | Mar 4, 2017•Knowledge | ||
- | Title | ||
- | On MCP23008/ | ||
- | Article URL | ||
- | https:// | ||
- | Question | ||
- | On MCP23008 device, if the GPIO7 input changes, or on MCP23017 if GPIOA7 or GPIOB7 input changes while the I2C master is reading this bit from the GPIO register, the SDA signal can change and look like a STOP condition on the bus. | ||
- | Answer | ||
- | The solution is to use a different pin as input, no other workaround available now. | ||
- | ``` | ||
- | Bref, on évite les pin 7. Un maker averti en vaux deux, et nous avions prévu de mettre des borniers sur les entrées non utilisées du MCP23017. Le nouveau routage du prototype est donc assez facile. En revanche, vous remarquerez que l' | ||
- | |||
- | On notera également l' | ||
- | |||
- | Le code ci-dessous est une adaptation finalement assez simple de celui qui précède, le stockage des valeurs des 5 encodeurs se fait dans un tableau, qui sera réutilisé ensuite pour l' | ||
- | |||
- | ``` | ||
- | #include < | ||
- | #include < | ||
- | |||
- | Adafruit_MCP23017 mcp; | ||
- | |||
- | byte arduinoIntPinA = 14; | ||
- | byte arduinoIntPinB = 12; | ||
- | |||
- | //Les broches de l' | ||
- | //Rot1 | ||
- | byte mcpPin1A = 6; | ||
- | byte mcpPin1B = 7; | ||
- | |||
- | //Rot2 | ||
- | byte mcpPin2A = 4; | ||
- | byte mcpPin2B = 5; | ||
- | |||
- | //Rot3 | ||
- | byte mcpPin3A = 2; | ||
- | byte mcpPin3B = 3; | ||
- | |||
- | //Rot4 | ||
- | byte mcpPin4A = 0; | ||
- | byte mcpPin4B = 1; | ||
- | |||
- | //Rot5 | ||
- | byte mcpPin5A = 14; | ||
- | byte mcpPin5B = 11; | ||
- | |||
- | // There is an issue with thin pin, don't use GPB7, microchip is aware of it !!!! | ||
- | // | ||
- | // | ||
- | |||
- | byte mcpPins[10] = {mcpPin1A, mcpPin1B, mcpPin2A, mcpPin2B, mcpPin3A, mcpPin3B, mcpPin4A, mcpPin4B, mcpPin5A, mcpPin5B}; | ||
- | |||
- | int encoded[5]; | ||
- | volatile int lastEncoded[5]; | ||
- | volatile long encoderValue[5]; | ||
- | |||
- | long lastencoderValue[5]; | ||
- | |||
- | int MSB[5]; | ||
- | int LSB[5]; | ||
- | |||
- | int lastMSB[5]; | ||
- | int lastLSB[5]; | ||
- | |||
- | uint16_t allGPIO; | ||
- | |||
- | // 2 SWITCHES | ||
- | //Rot5 | ||
- | byte sw1 = 12; | ||
- | byte sw2 = 13; | ||
- | boolean sw1Pressed, sw2Pressed; | ||
- | |||
- | long lastPrint = 0; | ||
- | int delayPrint = 50; | ||
- | |||
- | void setup() { | ||
- | |||
- | Serial.begin(115200); | ||
- | Serial.println(" | ||
- | |||
- | pinMode(arduinoIntPinA, | ||
- | pinMode(arduinoIntPinB, | ||
- | |||
- | mcp.begin(); | ||
- | |||
- | mcp.pinMode(mcpPin1A, | ||
- | mcp.pinMode(mcpPin1B, | ||
- | mcp.pinMode(mcpPin2A, | ||
- | mcp.pinMode(mcpPin2A, | ||
- | mcp.pinMode(mcpPin3A, | ||
- | mcp.pinMode(mcpPin3A, | ||
- | mcp.pinMode(mcpPin4A, | ||
- | mcp.pinMode(mcpPin4A, | ||
- | mcp.pinMode(mcpPin5A, | ||
- | mcp.pinMode(mcpPin5A, | ||
- | |||
- | //SW | ||
- | mcp.pinMode(sw1, | ||
- | mcp.pinMode(sw2, | ||
- | |||
- | //other pins | ||
- | mcp.pinMode(8, | ||
- | mcp.pinMode(9, | ||
- | mcp.pinMode(10, | ||
- | |||
- | mcp.pinMode(15, | ||
- | |||
- | mcp.pullUp(8, | ||
- | mcp.pullUp(9, | ||
- | mcp.pullUp(10, | ||
- | mcp.pullUp(15, | ||
- | |||
- | mcp.setupInterrupts(false, | ||
- | // Mirroring (first param) must be set to false, may cause crashes when turning multiples buttons | ||
- | |||
- | //SW1 | ||
- | mcp.setupInterruptPin(mcpPin1A, | ||
- | mcp.setupInterruptPin(mcpPin1B, | ||
- | //SW2 | ||
- | mcp.setupInterruptPin(mcpPin2A, | ||
- | mcp.setupInterruptPin(mcpPin2B, | ||
- | //SW3 | ||
- | mcp.setupInterruptPin(mcpPin3A, | ||
- | mcp.setupInterruptPin(mcpPin3B, | ||
- | //SW4 | ||
- | mcp.setupInterruptPin(mcpPin4A, | ||
- | mcp.setupInterruptPin(mcpPin4B, | ||
- | //SW5 | ||
- | mcp.setupInterruptPin(mcpPin5A, | ||
- | mcp.setupInterruptPin(mcpPin5B, | ||
- | |||
- | //BTN1/2 | ||
- | mcp.setupInterruptPin(sw1, | ||
- | mcp.setupInterruptPin(sw2, | ||
- | |||
- | mcp.readGPIOAB(); | ||
- | |||
- | attachInterrupt(digitalPinToInterrupt(arduinoIntPinA), | ||
- | attachInterrupt(digitalPinToInterrupt(arduinoIntPinB), | ||
- | |||
- | |||
- | } | ||
- | |||
- | ICACHE_RAM_ATTR void updateEncoderA() { | ||
- | | ||
- | } | ||
- | |||
- | ICACHE_RAM_ATTR void updateEncoderB() { | ||
- | readGPIO_MCP(); | ||
- | } | ||
- | |||
- | ICACHE_RAM_ATTR void readGPIO_MCP() | ||
- | { | ||
- | |||
- | // on lit toutes les entrées d'un coup, | ||
- | // Reads all 16 pins (port A and B) into a single 16 bits variable. | ||
- | allGPIO = mcp.readGPIOAB(); | ||
- | |||
- | // On utilise bitRead pour récupérer la valeur pour chaque bouton | ||
- | //SW5 | ||
- | MSB[4] = bitRead(allGPIO, | ||
- | LSB[4] = bitRead(allGPIO, | ||
- | | ||
- | //SW4 | ||
- | MSB[3] = bitRead(allGPIO, | ||
- | LSB[3] = bitRead(allGPIO, | ||
- | |||
- | //SW3 | ||
- | MSB[2] = bitRead(allGPIO, | ||
- | LSB[2] = bitRead(allGPIO, | ||
- | |||
- | //SW2 | ||
- | MSB[1] = bitRead(allGPIO, | ||
- | LSB[1] = bitRead(allGPIO, | ||
- | |||
- | //SW1 | ||
- | MSB[0] = bitRead(allGPIO, | ||
- | LSB[0] = bitRead(allGPIO, | ||
- | |||
- | //btn | ||
- | sw1Pressed = bitRead(allGPIO, | ||
- | sw2Pressed = bitRead(allGPIO, | ||
- | |||
- | for (int i = 0; i < 5; i++) | ||
- | { | ||
- | encoded[i] = (MSB[i] << 1) | LSB[i]; // | ||
- | int sum = (lastEncoded[i] << 2) | encoded[i]; //adding it to the previous encoded value | ||
- | // | ||
- | if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue[i] ++; | ||
- | if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue[i] --; | ||
- | lastEncoded[i] = encoded[i]; //store this value for next time | ||
- | } | ||
- | |||
- | } | ||
- | |||
- | |||
- | void loop() { | ||
- | |||
- | if (millis() - lastPrint > delayPrint) | ||
- | { | ||
- | for (int i = 0; i < 5; i++) | ||
- | { | ||
- | Serial.print(encoderValue[i]); | ||
- | Serial.print(" | ||
- | } | ||
- | Serial.print(" | ||
- | Serial.print(sw1Pressed); | ||
- | Serial.print(" | ||
- | Serial.print(sw2Pressed); | ||
- | Serial.println(); | ||
- | lastPrint = millis(); | ||
- | } | ||
- | | ||
- | } | ||
- | ``` | ||
- | |||