ASCII-Braille real time translation via Arduino


Circuit schematics & Pictures (click on any picture to enlarge)

Un'idea insensata e mal implementata che chiunque potrà migliorare: traduzione ASCII-Braille in tempo reale via Arduino.

Prendi un'idea assurda: creare una matrice 2x6 di solenoidi e tradurre i caratteri ASCII ricevuti tramite la porta seriale nella corrispondente configurazione di punti Braille.
Cercare di implementarlo nel modo più economico, veloce e sporco in pochi giorni prima delle vacanze estive.
Che cosa si ottiene? Un delirio. Ma interessante (almeno dal lato del software ...), e facilmente migliorabile da parte di chiunque!

Perché assurdo? Le persone ipovedenti usano screen reader, incredibilmente più efficaci di qualsiasi dispositivo meccanico. Inoltre, anche per le persone sorde e cieche, il mio prototipo, richiedendo che qualcuno mandi i caratteri ASCII ad un Arduino, sarebbe (a dir poco) poco pratico.
In ogni caso, si può anche implementare una matrice LED 2 x 6 (in effetti, è così che ho testato il software!) per ottenere la massima assurdità: il Braille visuale! E quella era la mia vera intenzione, realizzare un'implementazione insolita che richiedesse l'uso di maschere di bit in un processo di traduzione.

A senseless, badly implemented idea for anybody to improve: ASCII-Braille real time translation via Arduino.

Take an absurd idea: creating a 2x6 solenoid matrix, and translate ASCII characters received via serial port into the corresponding Braille dot configuration.
Try to implement it in the cheapest, quickest and dirtiest way in a few days before your summer holydays.
What do you get? A mess. But an interesting one (at least on the software side...), and easily improvable by everyone!

Why absurd? Visually impaired people use screen readers, incredibly more effective than any mechanical device. Plus, even for deaf and blind people, my prototype, requiring that somebody sends ASCII characters to an Arduino, would be very unpractical to say the least.
In any case, one can as well implement a 2 x 6 LED matrix (in fact, that's how I tested the software!) to get the utmost absurdity: visual Braille! And that was my true intention, making an unusual implementation requiring the use of bit masks in a translation process.



La paradossale implementazione di test - La matrice di LED "Visual Braille" - The paradoxical test implementation - The "Visual Braille" LED matrix

 

Ancor prima di descrivere l'idea, vorrei dire che so che avrei dovuto e voluto fare di meglio.
È ovvio che mi sono avventurato in un terreno inesplorato totalmente al di là delle mie modeste conoscenze. Accetterò volentieri di essere lo zimbello di qualsiasi competente fan di Arduino.
Contrariamente alla realizzazione su breadboard e alla cattiva scelta dei componenti, il software è pienamente riuscito e molto probabilmente è l'unica parte utile di questo progetto.

Even before describing the idea, I'd like to state that I know that I should and would have done better.
It's very obvious that i ventured in uncharted terrain totally beyond my minimal knowledge. I'll gladly accept to be the laughing stock of any competent Arduino fan.
Contrary to the breadboarding and poor component choice, the software is fully successful, and most probably is the only useful part of this project.

Problemi principali:
1) Avrei avuto bisogno di solenoidi push-pull, come questi:
https://core-electronics.com.au/solenoid-5v-small.html, che sono stati infatti utilizzati in questo tutorial di Aidan: https: // core- electronics.com.au/tutorials/solenoid-control-with-arduino.html.
I solenoidi push-pull hanno lo straordinario vantaggio di tornare alla loro posizione di default non appena viene rimossa la corrente: acquistati su ebay i solenoidi economici a spinta (non fatevi fuorviare! Nonostante l'annuncio, NON sono di tipo push-pull!): https://www.ebay.it/itm/Mini-DC-5V-5mm-Push-Pull-Type-Permanent-Magnet-Strong-magnetic-Electric-Solenoid/172438873301, la mia più grande perdita di tempo è stato il tentativo di trasformare in qualcosa che potesse resettarsi quando inattivato. E non ci sono riuscito ...

Main problems:
1) I would have needed push-pull solenoids, like these: https://core-electronics.com.au/solenoid-5v-small.html that were in fact used in this tutorial by Aidan: https://core-electronics.com.au/tutorials/solenoid-control-with-arduino.html.
Push-pull solenoids have the extraordinary advantage that they snap back in their default position as soon as the current is removed - having bought on ebay the cheap, push-only solenoids (Don't be mislead! Despite the ad, they are NOT push-pull type!): https://www.ebay.it/itm/Mini-DC-5V-5mm-Push-Pull-Type-Permanent-Magnet-Strong-magnetic-Electric-Solenoid/172438873301, my greatest waste of time was trying to transform them into something that could snap back when inactive. And I didn't succeed...

2) Non avrei dovuto essere preoccupato di usare solenoidi da 12V o 9V. Quando ho acquistato i più economici 5V, era anche per poter alimentare tutto con i classici 5V di Arduino (ignoravo ancora il problema di assorbimento di corrente dei solenoidi).
Una stupida preoccupazione da parte mia: è molto facile alimentare una breadboard a 9V o 12V con un alimentatore separato, quindi controllarlo con Arduino.

2) I shouldn't have been worried to use 12V or 9V solenoids. When purchasing the cheapest 5V ones, I wanted to power everything with the classic 5V from Arduino (I still ignored the current absorption problem of the solenoids).
A silly concern on my part: it's very easy to power a breadboard at 9V or 12V with a separate power supply, then control it with Arduino.

3) Corrente: i solenoidi, anche i 5V più piccoli e più economici che ho usato, sono dispositivi assetati. Qualsiasi tentativo di alimentare più di due solenoidi con Vin o 5V da Arduino si traduce in reset continui, a causa dell'assorbimento eccessivo di corrente dei solenoidi. Anche con alimentazione separata ai solenoidi, se la potenza è al limite, sono frequenti i mancati azionamenti.
Qualsiasi progetto serio che coinvolga solenoidi e Arduino richiede alimentatori indipendenti di potenza adeguata e circuiti ben concepiti. Importante: alimentando i miei solenoidi economici oltre la loro tensione nominale (ad esempio impostando l'alimentazione esterna commutabile a 7,5 V anziché 5 V), l'azione è molto più energica, con il "pistone" del solenoide espulso a una certa distanza.

3) Current: solenoids, even the smallest and cheapest 5V that I used, are thirsty devices. Any attempt to power more than two solenoids with Vin or 5V from Arduino results in continuous resets, due to the excessive current absorption by the solenoids. Even with separate power to the solenoids, if power is marginal, misfires are frequent.
Any serious project involving solenoids & Arduino requires independent supplies of adequate power and well-conceived circuits. Important: by powering my cheap solenoids above their nominal voltage (e.g. by setting the external switchable power supply to 7,5V instead of 5V), action is much more energetic, with the "piston" of the solenoid being ejected at some distance.

Ecco una lista di buone implementazioni di solenoidi + Arduino: https://www.google.com/search?tbs=li%3A1&q=arduino+solenoids&oq=arduino+solenoids - vedi in particolare https://arduino.stackexchange.com/questions/50668/can-arduino-control-8-solenoids per una buona consulenza tecnica.
Per un singolo solenoide da 5 V, Arduino da solo potrebbe bastare - ma vedrete che la maggior parte delle implementazioni richiede componenti speciali e una buona conoscenza dell'elettronica.

Here is a list of good implementations of solenoids + Arduino: https://www.google.com/search?tbs=li%3A1&q=arduino+solenoids&oq=arduino+solenoids - see in particular https://arduino.stackexchange.com/questions/50668/can-arduino-control-8-solenoids for good technical advice.
For a single 5V solenoid, Arduino alone may suffice - but you will see that most implementations require special components and a good knowledge of electronics.

FUNZIONAMENTO

Con i solenoidi disposti in un pacco 3x2 come illustrato (ciascun solenoide è stato avvolto individualmente con nastro isolante, quindi in file e in un unico blocco), l'obiettivo sarebbe quello di far fuoriuscire / estrarre il nucleo di ciascun solenoide se il rispettivo punto è attivato. E ciò funzionerebbe egregiamente con i *veri* solenoidi push-pull, che scatterebbero fuori decisamente e poi di nuovo dentro dopo essere stati disattivati.
In pratica, con il circuito e i componenti illustrati e con tutto alimentato a 5 V, anche con alimentazioni separate i solenoidi si muovono a mala pena (a meno che gli alimentatori non siano impostati a una tensione superiore... il che potrebbe non essere sicuro!) Ma, con il polpastrello del pollice premuto delicatamente sulla punta dei pistoni, si può comunque percepire quali punti si alzano e quali punti sono immobili.
Se un pistone si sgancia e viene espulso, deve essere fatto nuovamente premuto all'interno fino allo scatto: un altro inconveniente di questa attrezzatura subottimale.
Grazie per l'attenzione!

OPERATION

With the solenoids arranged in a 3x2 pack as illustrated (each solenoid was individually wrapped in insulating tape, then again in rows and in a single block), the objective would be to have the core of each solenoid pop out /stick out if its respective point is activated. And that would work egregiously with *real* push-pull solenoids, that would pop out decidedly then pop in as soon as inactivated.
In fact, with the illustrated circuit and components, and with everything powered at 5V, even with separate power supplies the solenoids barely move (unless power supplies are set at an higher voltage... that may be unsafe!) but, with the thumb's pad gently pressed on the tips of the pistons, one may anyway perceive which tips are being rised and which tips are motionless.
If one piston disengages and is ejected, it must be clicked in again: another drawback of this suboptimal equipment.
Thanks for the attention!

COMPONENTI

Attenzione! La realizzazione di un array di solenoidi completamente funzionante potrebbe richiedere una riprogettazione integrale, e un set differente di componenti.
  • Sei diodi 1N4001 (preferibilmente) o 1N4007
  • Sei transistor NPN 2N3904
  • Sei solenoidi (vedi testo)
  • Sei resistenze da 220 ω
  • Arduino UNO o simile
  • Cavi maschio-maschio Dupont, come necessari
  • uno o più alimentatori separati per i solenoidi

Il progetto su Arduino Project Hub
Grazie per l'attenzione!

COMPONENTS

Warning! The realization of a fully operational solenoid array may require a complete redesign, and a different set of components.
  • Six 1N4001 (preferrably) or 1N4007 diodes
  • Six 2N3904 NPN transistors
  • Six solenoids (see text)
  • Six 20-ω resistors
  • Breadboard
  • Arduino UNO or similar
  • Dupont male-male cables, as needed
  • One or more separate power supplies for the solenoids

The project on Arduino Project Hub
Thanks for the attention!


Source code

/*
  Arduino ASCII- Braille Translator
  by Cesare Brizio | CC by-sa-nc
  this sketch is in the public domain

  Version 1.0 - 29 June 2019
  ------------------------------
  This sketch will use six small solenoids/electromagnets arranged in
  three rows of two columns to operate a Braille dot matrix.
  Any ASCII character sent to Arduino via COM port will be "translated"
  in a timed sequence of Braille symbols by configuring, one character 
  at a time, the dot matrix, so that the "dots" (solenoids) corresponding
  to the current ASCII character are raised for 500 msec before passing to
  the subsequent character. 
  When an un-translatable character is sent, a short buzz is emitted.

  ==== DOT NUMBERING ====
  Dots are not numbered according to the Braille convention, but are
  conventionally numbered from left to right, then from top to bottom:
  1  2
  3  4
  5  6
  As an example, showing letter "b" (asterisk represent an high dot):
  *-
  *-
  --
  will require raising dots number 1 and 3


  ==== DOT CONFIGURATION ====
  Dot configuration will be stored as a series of six binary values. As an example, 
  according to the preceding explanation, the configuration for "b" is 101000.
  As long as configurations contain just 6 bits of information, they can be stored
  in a single char that, translated bitwise (least significant bit at right), corresponds 
  to the needed configuration.
  So, 101000 (configuration for "b"), corresponding to DEC 40 and HEX 28, will be
  represented by ASCII character "(".
  Summarizing, considering that ascii code for "b" is 98D (62H) I will say that 
  myBrailleDots[98] = 40; 
  that is equivalent to
  myBrailleDots[98] = '('; 
  meaning that the binary sextet corresponding to letter "b" is 101000.
 
  When needed, a bitwise AND with increasing powers of two (from 1 to 32) will reveal 
  which dots shall be raised: this is accomplished by iterating through a 6 bit mask:
   
  Some simplifications will be applied, e.g.:
  a) the multi-letter Braille glyphs (ch, ing, for, ar, th and others)
  b) number prefix (010111), 
  c) letter prefix (000101), 
  d) uppercase prefix (000001) and other prefixes
  will not be implemented in this version, but this sketch can
  be quite easily implemented with the needed modifications.
*/  

// Declare a char array with one index for every possible ASCII byte / character
byte myBrailleDots[255];
int firstOutputPin = 2; // pin corresponding to least significant bit
int buzzerPin = 8; // Buzzer on pin 8
byte matrixPoints = 0; // byte that will store the point matrix configuration
                       // for a specific ASCII character
byte inByte;
byte mask = 1; //our bitmask

void setup() {
  // Temporarily assign 99 to every possible ASCII byte/character
  // All the characters in the input string will decode to "99" by default
  for (int i = 0; i < 256; i = i + 1) {
     myBrailleDots[i] = 99;
  }

  // Now, only for the ASCII characters with a corresponding Braille character.
  // assign the corresponding Braille Dot configuration
  myBrailleDots[32] = 0;  // blank is 000000
  myBrailleDots[33] = 14;  // exlamation mark is 001110
  myBrailleDots[34] = 7;  // double quote is 000111
  myBrailleDots[34] = 2;  // single quote is 000010
  myBrailleDots[40] = 15;  // left parenthesis is 001111
  myBrailleDots[41] = 15;  // right parenthesis is 001111
  myBrailleDots[44] = 8;  // comma is 001000
  myBrailleDots[46] = 13;  // period is 001101
  myBrailleDots[48] = 28; // 0 is 011100
  myBrailleDots[49] = 32; // 1 is 100000
  myBrailleDots[50] = 40; // 2 is 101000
  myBrailleDots[51] = 48; // 3 is 110000
  myBrailleDots[52] = 52; // 4 is 110100
  myBrailleDots[53] = 36; // 5 is 100100
  myBrailleDots[54] = 56; // 6 is 111000
  myBrailleDots[55] = 60; // 7 is 111100
  myBrailleDots[56] = 44; // 8 is 101100
  myBrailleDots[57] = 24; // 9 is 011000
  myBrailleDots[58] = 12; // colon is 001100
  myBrailleDots[59] = 10; // semicolon is 001010
  myBrailleDots[63] = 11; // question mark is 001011
  myBrailleDots[65] = 32; // A is 100000
  myBrailleDots[66] = 40; // B is 101000
  myBrailleDots[67] = 48; // C is 110000
  myBrailleDots[68] = 52; // D is 110100
  myBrailleDots[69] = 36; // E is 100100
  myBrailleDots[70] = 56; // F is 111000
  myBrailleDots[71] = 60; // G is 111100
  myBrailleDots[72] = 44; // H is 101100
  myBrailleDots[73] = 24; // I is 011000
  myBrailleDots[74] = 28; // J is 011100
  myBrailleDots[75] = 34; // K is 100010
  myBrailleDots[76] = 42; // L is 101010
  myBrailleDots[77] = 50; // M is 110010
  myBrailleDots[78] = 54; // N is 110110
  myBrailleDots[79] = 38; // O is 100110
  myBrailleDots[80] = 58; // P is 111010
  myBrailleDots[81] = 62; // Q is 111110
  myBrailleDots[82] = 46; // R is 101110
  myBrailleDots[83] = 26; // S is 011010
  myBrailleDots[84] = 30; // T is 011110
  myBrailleDots[85] = 35; // U is 100011
  myBrailleDots[86] = 43; // V is 101011
  myBrailleDots[87] = 29; // W is 011101
  myBrailleDots[88] = 51; // X is 110011
  myBrailleDots[89] = 55; // Y is 110111
  myBrailleDots[90] = 39; // Z is 100111
  myBrailleDots[97] = 32; // A is 100000
  myBrailleDots[98] = 40; // B is 101000
  myBrailleDots[99] = 48; // C is 110000
  myBrailleDots[100] = 52; // D is 110100
  myBrailleDots[101] = 36; // E is 100100
  myBrailleDots[102] = 56; // F is 111000
  myBrailleDots[103] = 60; // G is 111100
  myBrailleDots[104] = 44; // H is 101100
  myBrailleDots[105] = 24; // I is 011000
  myBrailleDots[106] = 28; // J is 011100
  myBrailleDots[107] = 34; // K is 100010
  myBrailleDots[108] = 42; // L is 101010
  myBrailleDots[109] = 50; // M is 110010
  myBrailleDots[110] = 54; // N is 110110
  myBrailleDots[111] = 38; // O is 100110
  myBrailleDots[112] = 58; // P is 111010
  myBrailleDots[113] = 62; // Q is 111110
  myBrailleDots[114] = 46; // R is 101110
  myBrailleDots[115] = 26; // S is 011010
  myBrailleDots[116] = 30; // T is 011110
  myBrailleDots[117] = 35; // U is 100011
  myBrailleDots[118] = 43; // V is 101011
  myBrailleDots[119] = 29; // W is 011101
  myBrailleDots[120] = 51; // X is 110011
  myBrailleDots[121] = 55; // Y is 110111
  myBrailleDots[122] = 39; // Z is 100111
  pinMode(buzzerPin, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  Serial.begin(9600); 
  Serial.println("ASCII - Braille Arduino Converter");
  Serial.println("LED test - begin");
  digitalWrite(2,HIGH);
  digitalWrite(3,HIGH);
  digitalWrite(4,HIGH);
  digitalWrite(5,HIGH);
  digitalWrite(6,HIGH);
  digitalWrite(7,HIGH);
  delay(3000);
  digitalWrite(2,LOW);
  digitalWrite(3,LOW);
  digitalWrite(4,LOW);
  digitalWrite(5,LOW);
  digitalWrite(6,LOW);
  digitalWrite(7,LOW);
  Serial.println("LED test - end");
  Serial.println("Type some character: it will be transmitted to Arduino and displayed on a Braille 2 x 3 matrix");
}


void loop() {
   // Braille print data only when you receive data:
   if (Serial.available() > 0) {
      // read the incoming byte:
      inByte = Serial.read();
      // say what you got:
      Serial.print("Received (inByte): ");
      Serial.println(inByte);
      // Translate inByte in matrix points
      Serial.print("Matrix points variable (myBrailleDots[inByte]): ");
      Serial.println(myBrailleDots[inByte]);

      // Braille print only admissible characters
      // the unadmissible ones decode to 99
      if (myBrailleDots[inByte] == 99)   // if unadmissible
      {
         Serial.println("Not a translatable character");
         digitalWrite(buzzerPin,HIGH); // buzz
         delay(250); 
         digitalWrite(buzzerPin,LOW); // stop buzzing
      }
      else{ 
              int thisPin = 2;
              for (mask = 000001; mask<64; mask <<= 1) { 
                  Serial.print("thisPin = ");
                  Serial.println(thisPin); 
                  if (myBrailleDots[inByte] & mask){ // if bitwise AND resolves to true
                     Serial.print("AND successful, put pin on!");
                     Serial.println(mask);  
                     digitalWrite(thisPin,HIGH);               
                  }
                  else{ //if bitwise and resolves to false
                     Serial.print("AND unsuccessful, put pin off!");
                     Serial.println(mask);  
                     digitalWrite(thisPin,LOW);
                  }
              thisPin = thisPin + 1;   
              }
      } 
      delay(3000); // allow 3 sec before passing to next character
   }
}