Project showcase
émile – A Hommage in 5-Bit

émile – A Hommage in 5-Bit © GPL3+

émile is a writing machine using the Baudot code – a binary 5-bit code, predecessor of ASCII and EBCDID.

  • 1,814 views
  • 1 comment
  • 4 respects

Components and supplies

Necessary tools and machines

Wood working tools
For constructing the whole object
Aluminum pipe cutting tool
For make the alu pipe stand-offs
Lasercutter
Laser cutter (generic)
Cutting the "punch" cards and laser cut the small acrylic starting area for the marble as well as the arduino holder

Apps and online services

Ide web
Arduino IDE
Code written entirely in the Arduino platform
Processing3 logo
The Processing Foundation Processing
Used for the creation of the punch cards

About this project

émile is a philosophical writing machine using the Baudot code – a binary 5-bit code, predecessor of ASCII and EBCDID – intended for telecommunication and electronic devices, representing the entire alphabet.

Visualizing how electronic signals work can be difficult. A physical model can be useful in overcoming that difficulty. At a workshop entitled “Unboxing Black Boxes” the group of Interaction Designers (Irena, Jasna, David and Julian) created a device to show Baudot code in operation. This amalgam of wood and Arduino was dubbed émile in honor of Émile Baudot (1845-1903).

Baudot developed his code to transmit telegraph signals from one machine to another, in contrast to Morse code which was principally for human communication. Both codes were used throughout the 20th century. For example, those big clattering, mechanical teletype machines use a minor variation of Baudot code.

Baudot is a fixed length code of 5 bits, as opposed to Morse’s variable length code. Morse has a separate code for each characters while Baudot uses “shift’ codes to change between alphabet and figure characters. For instance, a binary 11 would represent either an ‘A’ or a ‘-‘ depending on the shift state. If the shift code was missed the receiver would get gibberish.

In émile the Baudot code is sent by five marbles - one for each bit in the Baudot code. Each marble rolls in a track toward the Arduino. How does the machine know which marbles to send?

Punch cards!

Each card represents a code. Each position in the card has a gap to allow a marble to pass (a set bit), or no gap to block the marble (an unset bit). The operator loads 5 marbles and a punch card and launches the marbles via a spring mechanism.

émile runs on Arduino Uno with a standard LCD display to show the letters.

From the interview:

The machine was built in six days with four people. In our group we came to the conclusion, that not every process in a computer is really transparent and it already starts when you type a simple letter on a keyboard. To unwrap this “black box” of data transmission, we set our goal to build a small writing machine where you can literally see bits rolling around. After some research we got back to the beginnings of Telefax machines and data transmission using Baudot-code. We then quickly designed punchcards and mapped them to a slightly altered baudot code table and cut them with a laser cutter from 5mm plywood. Whenever a marble hits a switch, a short timer goes off and waits for input on the other switches. If no other marbles are hitting those switches, we finally translate the switches that have been hit into the corresponding letter.

Take a look at the machine in action:

Arduino Blog feature: https://blog.arduino.cc/2015/08/20/a-tribute-to-5-bit-baudot-code

Code

PinballBaudot_v14.inoArduino
Upload this to your arduino
#include <LiquidCrystal.h>
#include "Timer.h"
#include "BaudotDecoder.h"
#include "Display.h"

#define DEBUG false
#define INPUT_COUNT BaudotDecoder::BIT_COUNT
#define DECODE_DELAY 1000 // waits for bit input for 1000 ms = 1 sec before decoding.
#define SLEEP_DELAY 300000 // puts the display to sleep after 300.000 ms = 5 min.


int inputPins[INPUT_COUNT] = { A1, A2, A3, A4, A5 };
int inputValues[INPUT_COUNT] = { 0, 0, 0, 0, 0 };
int readValue; // variable to temporarily store the value read from pin.
Timer _decodeTimer = Timer(DECODE_DELAY); // instantiate timer for delayed bit decoding.
Timer _sleepTimer = Timer(SLEEP_DELAY); // instantiate timer to put display to sleep when no activity is registered.
Display _display = Display();


void setup() {
  // initialize serial connection
  Serial.begin(9600);
  
  // set correct pin mode for buttons
  for(int i = 0; i < INPUT_COUNT; i++) {
    // use internal input pullup, which inverts the pin reading: 1 = 0, 0 = 1
    pinMode(inputPins[i], INPUT_PULLUP);
  }
  
//  char initValues[8] = {'A', ' ', 'P', 'A', 'T', 'I', 'E', 'N'};
//  for(int i = 0; i < 8; i++) {
//    _display.printChar(initValues[i]);
//  }
  
  // start sleep timer
  _sleepTimer.start();
}


void loop() {
  // update readings from pins
  for (int i = 0; i < INPUT_COUNT; i++) {
    // invert the reading due to pin mode.
    readValue = !digitalRead(inputPins[i]);
    // check whether the new pin value is different from last
    if (readValue == 1 && readValue != inputValues[i]) {
      // store the value in the array.
      inputValues[i] = readValue;
      // check if timer needs to started
      if (_decodeTimer.isStopped()) {
        _decodeTimer.start();
      }
      
      /*if (_sleepTimer.isStopped()) {
        _sleepTimer.start();
      } else {
        _sleepTimer.reset();
      }*/
    }
  }
  
  if (DEBUG) {
    BaudotDecoder::printBits(inputValues);
  }
  
  if (_decodeTimer.isFinished()) {
    // check whether the display is put to sleep.
    if (_display.is_sleeping()) {
      _display.wake_up();
    }
    
    // convert bits to char
    char c = BaudotDecoder::convertBitsToChar(inputValues);
    
    // evaluate action
    if (BaudotDecoder::getLastIndex() == BAUDOT_CLEAR_ID) {
      _display.clearAllChars();
    } else if (BaudotDecoder::getLastIndex() == BAUDOT_BACKSPACE_ID) {
      _display.deleteLastChar();
    } else {
      _display.printChar(c);
    }
    
    // reset array which stores the button readings
    for (int i = 0; i < INPUT_COUNT; i++) {
      inputValues[i] = 0;
    }
  }
  
  if (_sleepTimer.isFinished()) {
    Serial.println("SLEEP");
    
    _display.sleep();
    _display.clearAllChars();
    
    _decodeTimer.stop();
    _sleepTimer.stop();
  }
}
BaudotDecoder.hC Header File
#ifndef BaudotDecoder_h
#define BaudotDecoder_h

#include <Arduino.h>

#define BAUDOT_BACKSPACE_ID 8 // 0b01000
#define BAUDOT_CLEAR_ID 31 // 0b11111

class BaudotDecoder {
  public:
    static const unsigned int BIT_COUNT = 5;
    static char convertBitsToChar(int *bits);
    static void printBits(int *bits);
    static unsigned int getLastIndex();
  private:
    static char _charSetMode0[32];
    static char _charSetMode1[32];
    static unsigned int _lastDecodedIndex;
    static char _lastDecodedValue;
};

#endif
BaudotDecoder.cppC/C++
//#include <Arduino.h>
#include "BaudotDecoder.h"

char BaudotDecoder::_charSetMode0[] = {
  NULL, 'Y', 'E', 'I', 'A', 'U', 'P', 'O',
  NULL, 'B', 'G', 'F', 'J', 'C', 'H', 'D',
  ' ', 'S', 'X', 'W', '-', 'T', 'Z', 'V',
  '.', 'R', 'M', 'N', 'K', 'Q', 'L', NULL
};

unsigned int BaudotDecoder::_lastDecodedIndex = 0;
char BaudotDecoder::_lastDecodedValue = NULL;

char BaudotDecoder::convertBitsToChar(int* bits) {
  Serial.println("BaudotDecoder -> decode bits: ");
  printBits(bits);
  
  _lastDecodedIndex = 0;
  
  for (int i = 0; i < BIT_COUNT; i++) {
    Serial.print(i);
    Serial.print(" : ");
    Serial.println(bits[i]);
    _lastDecodedIndex = _lastDecodedIndex | (bits[i] << (4 - i));  
  }
  Serial.print("index: ");
  Serial.println(_lastDecodedIndex);
  
  Serial.print("char: ");
  Serial.println(_charSetMode0[_lastDecodedIndex]);
  
  return _charSetMode0[_lastDecodedIndex];
}

void BaudotDecoder::printBits (int* bits) {
  Serial.print(millis());
    Serial.print(" - [ ");
    for(int i = 0; i < BIT_COUNT; i++) {
      Serial.print(bits[i]);
      if(i < BIT_COUNT - 1){
        Serial.print(", ");
      }
    }
    Serial.println(" ]");
}

unsigned int BaudotDecoder::getLastIndex() {
  return _lastDecodedIndex;
}
Display.hC Header File
#ifndef Display_h
#define Display_h

#include <Arduino.h>
//TODO optimize import of system library
//#include <LiquidCrystal.h>
//#include "/Applications/Arduino.app/Contents/Resources/Java/libraries/LiquidCrystal/src/LiquidCrystal.h" // old Arduino
#include "/Applications/Arduino.app/Contents/Java/libraries/LiquidCrystal/src/LiquidCrystal.h" // Arduino 1.6.3

#define DISPLAY_MAX_CHAR_PER_LINE 16
#define DISPLAY_MAX_LINES 2
#define DISPLAY_MAX_CHARS (DISPLAY_MAX_LINES * DISPLAY_MAX_CHAR_PER_LINE)

class Display {
 public:
   Display();
   void printChar(char c);
   void clearAllChars();
   void deleteLastChar();
   void sleep();
   void wake_up();
   bool is_sleeping();
   bool is_awake();
 private:
   void _reset();
   void _stepBack();
   void _updateCursor();
   void _printBuffer();
   LiquidCrystal _lcd;
   char _charBuffer[DISPLAY_MAX_CHARS];
   uint8_t _row, _position, _bufferIndex;
   bool _sleeping;
};

#endif
Display.cppC/C++
#include "Display.h"

// Arduino c++ classes, How to make instance variables of another class/library
// http://stackoverflow.com/questions/26602268/arduino-c-classes-how-to-make-instance-variables-of-another-class-library

Display::Display () : _lcd(8, 9, 4, 5, 6, 7) {
  // setup display
  _lcd.begin(16, 2);
  
  // initialize counter variables and display cursor
  _reset();
  
  _sleeping = false;
}

void Display::printChar(char c) {
  // check for display constraints
  if (_row >= DISPLAY_MAX_LINES) {
    Serial.println("BEFORE CHAR SHIFT");
    _printBuffer();
    
    //TODO clear first 16 chars from buffer and shift the rest to the front
    for (int i = 0; i < DISPLAY_MAX_CHARS; i++) {
      if (i < DISPLAY_MAX_CHAR_PER_LINE) {
        _charBuffer[i] = _charBuffer[DISPLAY_MAX_CHAR_PER_LINE + i];
      } else {
        _charBuffer[i] = ' ';
      }
    }
    
    Serial.println("AFTER CHAR SHIFT");
    _printBuffer();
    
    //TODO redraw all display characters - clear and write first line
    _lcd.clear();
    
    for (int i = 0; i < DISPLAY_MAX_CHARS; i++) {
      _lcd.print(_charBuffer[i]);
    }
    
    _position = 0;
    _row = DISPLAY_MAX_LINES - 1;
    _bufferIndex = DISPLAY_MAX_CHAR_PER_LINE;
    
    _updateCursor();
  }
    
  Serial.print("row: ");
  Serial.print(_row);
  Serial.print(" - position: ");
  Serial.println(_position);
  
  // print new character to display
  _lcd.print(c);
  
  // save char to buffer
  _charBuffer[_bufferIndex++] = c;
  
  // look ahead for next cursor position
  _position++;
  
  // check for line break
  if (_position >= DISPLAY_MAX_CHAR_PER_LINE) {
    _row++;
    _position = 0;
    
    _updateCursor();
  }
  
  _printBuffer();
}

void Display::clearAllChars() {
  _lcd.clear();
  _reset();
}

void Display::deleteLastChar() {
  _stepBack();
  printChar(' ');
  _stepBack();
}

void Display::sleep(){
  if (!_sleeping) {
    _lcd.noDisplay();
    _sleeping = true;
  }
}

void Display::wake_up(){
  if (_sleeping) {
    _lcd.display();
    _sleeping = false;
  }
}

bool Display::is_sleeping(){
  return _sleeping;
}

bool Display::is_awake(){
  return !_sleeping;
}
   

void Display::_reset() {
  _row = 0;
  _position = 0;
  _bufferIndex = 0;
  
  for (int i = 0; i < DISPLAY_MAX_CHARS; i++) {
    _charBuffer[i] = ' ';
  } 
  
  _lcd.setCursor(_row, _position);
}

void Display::_stepBack() {
  
  if ((_row == 1 || _row == 2) && _position == 0) {
    _position = DISPLAY_MAX_CHAR_PER_LINE - 1;
    _bufferIndex = DISPLAY_MAX_CHAR_PER_LINE - 1;
    _row--;
    _updateCursor();
  } else if (_position != 0) {
    _position--;
    _bufferIndex--;
    _updateCursor();
  }
}

void Display::_updateCursor() {
  _lcd.setCursor(_position, _row);
}

void Display::_printBuffer() {
  Serial.print("[ ");
  for (int i = 0; i < DISPLAY_MAX_CHARS; i++) {
    Serial.print(_charBuffer[i]);
    if (i != DISPLAY_MAX_CHARS - 1) {
      Serial.print(", ");
    }
  }
  Serial.println(" ]");
}
Timer.hC Header File
#ifndef Timer_h
#define Timer_h

#include <Arduino.h>

class Timer {
  public:
    Timer(unsigned long timerDelay);
    void start();
    void reset();
    void stop();
    bool isRunning();
    bool isStopped();
    bool isFinished();
  private:
    bool _running;
    unsigned long _timerDelay;
    unsigned long _startTime, _millisRead;
};

#endif;
Timer.cppC/C++
//#include <Arduino.h>
#include "Timer.h"

Timer::Timer (unsigned long timerDelay) {
  _timerDelay = timerDelay;
  _startTime = 0;
  _running = false;
}

void Timer::start () {
  _startTime = millis();
  
  Serial.print("Timer -> start time: ");
  Serial.println(_startTime);
  Serial.print("Timer -> estimated end time: ");
  Serial.println(_startTime + _timerDelay);
  
  _running = true;
}

void Timer::reset () {
  _startTime = millis();
}

void Timer::stop () {
  _running = false;
}

bool Timer::isRunning () {
  return _running;
}

bool Timer::isStopped () {
  return !_running;
}

bool Timer::isFinished() {
  _millisRead = millis();
  
  if (_running && _millisRead > _startTime + _timerDelay) {
    Serial.print("Timer -> finished: ");
    Serial.println(_millisRead);
    
    _running = false;
    return true;
  } else {
    return false;
  }
}
cardCreator.pdeProcessing
Punch card creator made in Processing 3
import processing.pdf.*;

int character = 0; // A
String _charSetMode0[] = {
  "DEL", "Y", "E", "I", "A", "U", "P", "O",
  "SPC", "B", "G", "F", "J", "C", "H", "D",
  " ", "S", "X", "W", "-", "T", "Z", "V",
  ".", "R", "M", "N", "K", "Q", "L", "RESET"
};

String alphabet[] = {
	"A", "B", "C", "D", "E", "F", "G", "H",
	"I", "J", "K", "L", "M", "N", "O", "P",
	"Q", "R", "S", "T", "U", "V", "W", "X",
	"Y", "Z", ".", "-", " ", "DEL", "SPC"
};

PFont theFont;
PGraphics buffer;

// put in the material size in here
PVector materialSize = new PVector(594, 420);
// PVector materialSize = new PVector(300, 220);

int bits = 5; // how many bits are we encoding
PVector cardSize = new PVector(300,72);
PVector tabSize = new PVector(9, 12);
PVector binaryHoles = new PVector(30 , 20);
float offSetSides = 1.315;
float downOffSetSides = 15; // in mm
int howManyCards = 8;

float binaryStopSides = 6;
ArrayList<Punchcard> punchcards = new ArrayList<Punchcard>();

int qqq = 0;

int globalCounter = 24;

Punchcard card;

void setup(){
	// size(mm2pixel(materialSize.x), mm2pixel(materialSize.y));
	size(500, 500);
	buffer = createGraphics(
		mm2pixel(materialSize.x), mm2pixel(materialSize.y), 
		PDF, "tabLow_output"+ globalCounter +".pdf");
	theFont = createFont("DIN Next LT Pro", 18);
	// buffer.beginDraw();
	buffer.beginDraw();
    buffer.textFont(theFont);
	buffer.textAlign(CENTER);
	// start top-left corner
	buffer.stroke(#ff0000);
	buffer.noFill();
	buffer.endDraw();
	
	for(int i = 0; i<alphabet.length; i++) {
		punchcards.add(new Punchcard(i));	
	}
	card = punchcards.get(0);
}

void draw(){
	buffer.beginDraw();
	// buffer.background(255);
	
	for(int i = 0; i<howManyCards; i++) {
		buffer.resetMatrix();
		buffer.translate(mm2pixel(5), mm2pixel(5));
		// pushMatrix();
			if(i < (howManyCards-3)) {
				buffer.translate(0, mm2pixel(cardSize.y*i)+mm2pixel(i*3));

			} else {

				buffer.translate(mm2pixel(cardSize.y*(i-1))+mm2pixel(i*3), mm2pixel(cardSize.x));
				buffer.rotate(radians(-90));
				// buffer.translate(mm2pixel(72*i)+mm2pixel(i*1),0);
				
				
			}
			if(globalCounter < punchcards.size()) {
		card = punchcards.get(globalCounter);
		card.display(buffer);

		globalCounter++;
		}
		else {
			buffer.dispose();
			exit();
		}
		// popMatrix();

	}
	// Punchcard card = punchcards.get(0);
	// card.display();
	// translate(0,mm2pixel(75));
	// card = punchcards.get(1);
	// card.display();
	// delay(500);
	// card.setCharacter(qqq);
	// qqq++;
	// if(qqq > 29) qqq = 0;
	
	// for (Punchcard card : punchcard) {
 	//	card.display();
	// }
	
	noLoop();
	buffer.dispose();
	exit();
	// buffer.endDraw();
}

int inch2pixel(float f) {
	return int(f*72);
}

int mm2pixel(float mm) {
	// 72 = dpi
	return int(round((mm/25.4)*72));
}

int getKey(int n) {
	int c = 0;
	for(int i = 0; i<_charSetMode0.length; i++) {
		if(_charSetMode0[i].equals(alphabet[n])) {
			break;
		}
		c++;
	}
	return c;
}

String getBinary(int i) {
	String b = binary(i,bits);
	return b;
}

String reverseString(String s) {
	char[] c = new char[s.length()];
	for(int i = 0; i<s.length(); i++) {
	    c[i] = s.charAt(i);
	}
	return new String(reverse(c));
}
Punchcard.pdeProcessing
Punch card class for the Processing sketch
class Punchcard {
	// PVector cardPosition;
	int character;
	String bin;
	
	Punchcard (int character) {
		// this.cardPosition =  new Pvector(x, y);
		this.character = character;

		// this.character = 12;

		this.bin = getBinary(getKey(this.character));
		// print(this.bin);
		// println(" - " + _charSetMode0[getKey(this.character)]);
		this.character++;

	}

	void display() {
		beginShape();
			vertex(mm2pixel(0),mm2pixel(0));
			// draw tab
			for(int i = 0; i<_charSetMode0.length; i++) {			

				if(i == character) {
				    if(i != 1) {
						vertex(mm2pixel(offSetSides+tabSize.x),mm2pixel(0));
						vertex(mm2pixel(offSetSides+tabSize.x),mm2pixel(tabSize.y));
						vertex(mm2pixel(offSetSides+((tabSize.x)*2)),mm2pixel(tabSize.y));
						vertex(mm2pixel(offSetSides+((tabSize.x*i))),mm2pixel(tabSize.y)); // not for A
					}
					vertex(mm2pixel(offSetSides+((tabSize.x*i))),0);
					vertex(mm2pixel(offSetSides+((tabSize.x*i)+tabSize.x)),mm2pixel(0));
					if(i != _charSetMode0.length-1) {
						vertex(mm2pixel(offSetSides+(((tabSize.x*i)+tabSize.x))),mm2pixel(tabSize.y));
						vertex(mm2pixel((offSetSides*2)+(tabSize.x*_charSetMode0.length-2)), mm2pixel(tabSize.y));
						vertex(mm2pixel((offSetSides*2)+(tabSize.x*_charSetMode0.length-1)), mm2pixel(tabSize.y));
					}
					vertex(mm2pixel((offSetSides*2)+(tabSize.x*_charSetMode0.length-1)), mm2pixel(0));
				break;
				}				
			}
			// draw top-right corner
			vertex(mm2pixel(cardSize.x), mm2pixel(0));

			


			// draw right-bottom corner
			vertex(mm2pixel(cardSize.x), mm2pixel(cardSize.y));
			String rev = reverseString(bin);
			for(int i = 0; i<bits; i++) {
				int b = rev.charAt(i);
			
				// 1 gate
				if(b == 49) {
					vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))), mm2pixel(cardSize.y));
					vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))), mm2pixel(cardSize.y-binaryHoles.y));
					vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-binaryHoles.x), mm2pixel(cardSize.y-binaryHoles.y));
					vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-binaryHoles.x), mm2pixel(cardSize.y));
				} else if(b == 48) {
					// 0 gate
					vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))), mm2pixel(cardSize.y));
					vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))), mm2pixel(cardSize.y-binaryHoles.y));
					vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-binaryStopSides), mm2pixel(cardSize.y-binaryHoles.y));
					vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-binaryStopSides), mm2pixel(cardSize.y));
					vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-(binaryHoles.x-(binaryStopSides))), mm2pixel(cardSize.y));
					vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-(binaryHoles.x-(binaryStopSides))), mm2pixel(cardSize.y-binaryHoles.y));
					vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-binaryHoles.x), mm2pixel(cardSize.y-binaryHoles.y));
					vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-binaryHoles.x), mm2pixel(cardSize.y));
				}
			}
			


			// draw left-bottom corner
			vertex(0, mm2pixel(cardSize.y));



		endShape(CLOSE);
		pushStyle();
			fill(0);
			text(_charSetMode0[getKey(character-1)], mm2pixel(offSetSides+( tabSize.x*character ))+13, mm2pixel(tabSize.y));
		popStyle();

	}
	void display(PGraphics buffer) {
		buffer.beginShape();
			buffer.vertex(mm2pixel(0),mm2pixel(0));
			// draw tab
			for(int i = 0; i<_charSetMode0.length; i++) {			

				if(i == character) {
				    if(i != 1) {
						buffer.vertex(mm2pixel(offSetSides+tabSize.x),mm2pixel(0));
						buffer.vertex(mm2pixel(offSetSides+tabSize.x),mm2pixel(tabSize.y));
						buffer.vertex(mm2pixel(offSetSides+((tabSize.x)*2)),mm2pixel(tabSize.y));
						buffer.vertex(mm2pixel(offSetSides+((tabSize.x*i))),mm2pixel(tabSize.y)); // not for A
					}
					buffer.vertex(mm2pixel(offSetSides+((tabSize.x*i))),0);
					buffer.vertex(mm2pixel(offSetSides+((tabSize.x*i)+tabSize.x)),mm2pixel(0));
					if(i != _charSetMode0.length-1) {
						buffer.vertex(mm2pixel(offSetSides+(((tabSize.x*i)+tabSize.x))),mm2pixel(tabSize.y));
						buffer.vertex(mm2pixel((offSetSides*2)+(tabSize.x*_charSetMode0.length-2)), mm2pixel(tabSize.y));
						buffer.vertex(mm2pixel((offSetSides*2)+(tabSize.x*_charSetMode0.length-1)), mm2pixel(tabSize.y));
					}
					buffer.vertex(mm2pixel((offSetSides*2)+(tabSize.x*_charSetMode0.length-1)), mm2pixel(0));
				break;
				}				
			}
			// draw top-right corner
			buffer.vertex(mm2pixel(cardSize.x), mm2pixel(0));

			


			// draw right-bottom corner
			buffer.vertex(mm2pixel(cardSize.x), mm2pixel(cardSize.y));
			buffer.vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(0*2))), mm2pixel(cardSize.y));
			String rev = reverseString(bin);
			for(int i = 0; i<bits; i++) {
				int b = rev.charAt(i);
			
				// 1 gate
				if(b == 49) {
					// buffer.vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))), mm2pixel(cardSize.y));
					buffer.vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))), mm2pixel(cardSize.y-binaryHoles.y));
					buffer.vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-binaryHoles.x), mm2pixel(cardSize.y-binaryHoles.y));
					// buffer.vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-binaryHoles.x), mm2pixel(cardSize.y));
				} else if(b == 48) {
					// 0 gate
					// buffer.vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))), mm2pixel(cardSize.y));
					buffer.vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))), mm2pixel(cardSize.y-binaryHoles.y));
					buffer.vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-binaryStopSides), mm2pixel(cardSize.y-binaryHoles.y));
					buffer.vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-binaryStopSides), mm2pixel(cardSize.y));
					buffer.vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-(binaryHoles.x-(binaryStopSides))), mm2pixel(cardSize.y));
					buffer.vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-(binaryHoles.x-(binaryStopSides))), mm2pixel(cardSize.y-binaryHoles.y));
					buffer.vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-binaryHoles.x), mm2pixel(cardSize.y-binaryHoles.y));
					// buffer.vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*(i*2))-binaryHoles.x), mm2pixel(cardSize.y));
				}
			}
			
			buffer.vertex(mm2pixel((cardSize.x-downOffSetSides)-(binaryHoles.x*((bits-1)*2))-binaryHoles.x), mm2pixel(cardSize.y));

			// draw left-bottom corner
			buffer.vertex(0, mm2pixel(cardSize.y));



		buffer.endShape(CLOSE);
		buffer.pushStyle();
		buffer.fill(0);
		buffer.text(_charSetMode0[getKey(character-1)], mm2pixel(offSetSides+( tabSize.x*character ))+12, mm2pixel(tabSize.y)-10);
		buffer.popStyle();
	}

	void setCharacter(int c) {
		this.character = character;

		// this.character = 12;

		this.bin = getBinary(getKey(this.character));
		// print(this.bin);
		// println(" - " + _charSetMode0[getKey(this.character)]);
		this.character++;
	}

}

Custom parts and enclosures

Sketchup file of enclosing
Sketchup file for the project file
pinballwritting_Otw1gQNtBA.skp

Schematics

Punch card (A – H)
These are files for a laser cutter, so you can reproduce the cards that are seen in the video. They represent the 5-bit of Baudot in a physical form.
Punch card (I – P)
These are files for a laser cutter, so you can reproduce the cards that are seen in the video. They represent the 5-bit of Baudot in a physical form.
Punch card (Q – X)
These are files for a laser cutter, so you can reproduce the cards that are seen in the video. They represent the 5-bit of Baudot in a physical form.
Punch card (Y, Z – SPECIAL CHARACTERS)
These are files for a laser cutter, so you can reproduce the cards that are seen in the video. They represent the 5-bit of Baudot in a physical form.
Arduino holder plexi plate
to hold the arduino in place at the end of the device
arduinoholderplexiplate_G9z5CeEg4f.ai

Comments

Similar projects you might like

Minimal MIDI Drum Kit with 3D Printer

Project tutorial by ryokosaka

  • 9,884 views
  • 1 comment
  • 29 respects

RFID Based Automatic Door System

Project showcase by Robodia Technology

  • 36,580 views
  • 21 comments
  • 98 respects

PENXZYL 3.0 :: Arduino Brush Plotter :: CNC

Project showcase by Guiye Perez Bongiovanni

  • 7,921 views
  • 6 comments
  • 31 respects

Servo Control with TV Remote Control

Project showcase by eldo85

  • 7,548 views
  • 5 comments
  • 23 respects

Arduino Irrigation System

Project tutorial by smana_00

  • 5,160 views
  • 0 comments
  • 22 respects

Arduino Powered CPR Feedback Device

Project showcase by David Escobar

  • 4,133 views
  • 8 comments
  • 25 respects
Add projectSign up / Login