Edward Octavio Muñoz Sandoval                                                                                                                   Contact: edw_ard0@hotmail.com


Final Project: Fab Loom 2.0


My final project is about a semiautomatic Jacquard loom that is going to weave an image preprocessed in a software and send the signals to servomotors which will open the shed in every cycle of the fabric


Testing the mechanism


Gear and releasable lock





User Interface


What is a loom?

A loom is a device used to weave cloth and tapestry. The basic purpose of any loom is to hold the warp in tension to facilitate the interweaving of the weft threads.


Weaving is done by intersecting the longitudinal threads (the warp), with the transverse threads (the weft).

The Jacquard loom is a mechanical loom is that make simpler of manufacturing textiles with complex patterns.


The loom was controlled by a "chain of cards", a number of punched cards, in order to allow or stop the raising of the heddle frame. which have some threads of the warp.


This movement allows the weft to be woven with the warp in every way you want.


Return to beginning

My designed was based in a manual Jacquard loom. First I separated the different mechanic processes that the machine do in order to determine which processes were going to be automated and how.

The Image shows a lateral picture of the manual Jacquard loom I based my design upon.

This Image shows an isometric view picture, in this one we can see on the top of the machine, the handles that the user should move in order to create the design. The user must create manually the combination of up-handles and down-handles to create the design, in my project those will move automatically.

The Image shows the reed that tensed up the weft against the finished fabric.

This Image shows a gear that can only go one way. This also helps to tense the warp and can be released it manually

Testing the mechanism

Return to beginning

The distribution of the servomotors and its functionality was tested before make the final design.

The idea was build 20 modulus with 4 servomotors in each one, moving a cable that will move the threads, in this test doesn't have threads, just the cables and the spring in the end of it.

The first problem I had was the large of the lever, so I had to fix it by making it smaller and cutting just one hole instead of 12.

In the picture below show the distribution of the modulus and the servomotors:

Notice that the metal bars acts like a pulley changing the direction of the force applied by the servomotor and the spring

This piece help to guide the cable to the correct direction so they don't tangle, also help the user in the assembly.

The last part of the test was a base for the springs.

For this prototype I found a week spring that thought will work, but after the test I realized that need a weaker spring.

I redesign the prototype to make more test with other springs, the logic with this other springs was make them weaker reducing the diameter of the wire and making them larger.

In this new prototype I test 3 springs with different lengths, hoping to find a balance between enough strong to tensile the cable and week enough to being stretched for the servomotor.

Test 1: The smallest spring: It didn't work, it is still very strong for the servomotor.

Test 2: The middle spring: It didn't work, it is still very strong for the servomotor.

Test 3: The larger spring: finally It work, this are the springs used in the final design.

Return to beginning


An essential component of the mechanism is the eyelet, it's the join between the cable-servomotor mechanism and the threads forming the warp.

As almost every component of the machine, it started with the design. All the components were drawn in solid works.


The design shown int he pictures below is the finnal after redesign it

This was the first design of the eyelet.


I took the cables and pull both sides until the piece broke to see were were the maximum stress concentration

After the tests the first eyeley look like the left picture and the new design is in the right picture. As you can see the area were the first eyelet fail is bigger.

Gear and releasable lock

Return to beginning

This mechanism were inspired in the machine showed in the "Inspiration" section. The gear can spin only in one direction due to the lock, but it can be release and turn in the other direction.

The gear can spin only in one direction due to the lock, but it can be release and turn in the other direction.

In the loom, the function of this system is to tense the warp as is showed in the pictures below:

The election of the spring is documented in the "Testing the mechanism" section, this are mounted on a guide with 3mm screws.

The distribution of the springs is 1 cm of separation "Y" axis and with 2.5 mm in "X" axis, with 20 columns of 4 holes

Mounting the springs to the base and to the cable  was a monumental work

Servo motors modulus

Return to beginning

The servomotors modulus was made thinking in saving space by moving 90° instead of the 180°. But in the calibration I had to test which angle will raise the thread, 90° or 0°

The distribution was with 4 columns of 4 modulus, and each modulus separated from the other 1 cm, considering that each modulus have 4 servomotors and the warp density is 4 threads per cm.

Is very difficult to change a decomposed servomotor or modulus due to the design. This is a mistake I will fix in the second version of the Fab Jacquard.

This is a video of the modulus being moved by the final software:

The reed tights the weft against the warp every loop to form the fabric. The pieces were designed and machined in 12mm playwood.

This design is for allow the circular movement of the reed

Also have dog bones

This part is designed so the reed can be mounted with screws and can be easily replaced

I tried to do an 9 mm acrylic reed, but it didn't work because the lassercut melts it due to the lines were to close.

In a huge effort to get it done, I paused the lassercut every 3 lines so the acrylic didn´tn melt and it work, but with the use eventually broke.

Using 6mm MDF it works fine, but have to much friction. this is a device to be improved in the second version of the fab jacquard.

This is a video of the reed working.

The structure is have lateral walls, support for modulus and structure supports machined in 12mm playwood with the router

Lateral Walls

This was a difficult design because all the mechanisms and details I has to consider.


Below are some images of the design process, in my experience I highly recommend to do a base for your design and continue with the holes for everything you want to do but in separate operations. In that way you were be able to made modifications more easily than if you made a sketch with a lot of measurements and references.

In this design I consider the dog bones and a radius of at least 3.18mm because the end mill I used is 3.175mm

Support for modulus

This part was also designed for a 12mm plywood  with an End mill of 3.175mm

This design have dog bones and radious of 3.18mm

Structure supports

This piece is just for avoid stress concentration in some areas of the machine, I realized latter that it help also to straighten the playwood.


Return to beginning

The function of the electronics in this machine is convert a 0 or 1 from the software to an angle to the servomotor.

I designed 2 boards, the one shown in the picture below is were 8 servomotors connect and receive signals from the pines for flat cable, also have 4 clamps for external electrical feeding.

The next board I designed was a shield for the arduino mega, this device send the signals through the flat cable and also receive the button signal for notify the next loop.

After some test I develop a board to replace the Arduinos mega. It is based on the microcontroller Atmega328 and include some improvements for this application like pins for I2C communication and electrical feed shared, with this modifications I save some cables in the connection of the boards therefore save time.

Here is a video of the board moving a servomotor:

Code 1

I have two firmwares, one functional only for testing all the servomotors and one that communicates with the user interface.


The code below works with preloaded commands for all the loops in the warp, it take a line and rise the thread that should be raised, and wait until the user applie a signal to the pin 20.

//Library for handling Engines

#include <Servo.h>


char rowData[81];            // saving row to process

int length;                  // the length of data to recive

String read;                 // the data recived


int buttonState = 0;         // current state of the switch button

int lastButtonState = 0;     // previous state of the swith button

const int  buttonPin = 20;    // the pin that the pushbutton is attached to


//const int  ledPin = 50;      // the pin that the led for changing row is attached to


int enginePin[33] ={52,46,48,50,44,42,40,38,23,25,27,29,31,33,35,37};

int enginePinPower0[33] ={0,0,90,90,0,90,90,0,0,90,90,0,0,90,90,0};

int enginePinPower1[33] ={90,90,0,0,90,0,0,90,90,0,0,90,90,0,0,90};



const int numberOfEngines = 16;

Servo servo;


int MIN = 0, MAX = 90;      // configuration for the engines


//Other variables

int count = 0;

int count1 = 0;



int IMAGEN0[33]={0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};

int IMAGEN1[33]={0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0};

int IMAGEN2[33]={0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0};

int IMAGEN3[33]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0};

int IMAGEN4[33]={0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0};

int IMAGEN5[33]={0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};

int IMAGEN6[33]={0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0};

int IMAGEN7[33]={0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0};

int IMAGEN8[33]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0};

int IMAGEN9[33]={0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0};

int IMAGEN10[33]={0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};

int IMAGEN11[33]={0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0};

int IMAGEN12[33]={0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0};

int IMAGEN13[33]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0};

int IMAGEN14[33]={0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0};

int IMAGEN15[33]={0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};

int IMAGEN16[33]={0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0};

int IMAGEN17[33]={0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0};

int IMAGEN18[33]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0};

int IMAGEN19[33]={1,1,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0};

int IMAGEN20[33]={1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};

int IMAGEN21[33]={1,1,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0};

int IMAGEN22[33]={0,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,0};

int IMAGEN23[33]={1,1,1,0,0,1,0,0,0,0,1,0,0,0,0,1,0};

int IMAGEN24[33]={1,0,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0};

int IMAGEN25[33]={1,1,1,1,0,0,1,0,0,0,0,1,0,0,0,0,1};

int IMAGEN26[33]={1,1,0,1,1,0,0,0,0,1,0,0,0,0,1,0,0};

int IMAGEN27[33]={0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0};

int IMAGEN28[33]={1,0,0,0,0,1,0,1,1,1,1,0,0,0,0,1,0};

int IMAGEN29[33]={0,0,0,1,0,1,1,1,1,0,0,0,0,1,0,0,0};

int IMAGEN30[33]={0,1,1,0,1,1,1,0,1,1,0,1,0,0,0,0,1};

int IMAGEN31[33]={0,0,1,1,1,1,0,1,1,1,0,0,0,0,1,0,0};

int IMAGEN32[33]={0,1,1,1,0,1,1,1,0,1,0,0,1,0,0,0,0};

int IMAGEN33[33]={1,1,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0};

int IMAGEN34[33]={1,0,1,1,1,0,1,1,1,0,0,0,0,1,0,0,0};

int IMAGEN35[33]={1,1,1,1,0,1,1,1,1,0,0,1,0,0,0,0,1};

int IMAGEN36[33]={1,1,0,1,1,1,1,0,1,1,0,0,0,0,1,0,0};

int IMAGEN37[33]={0,1,1,1,1,0,1,1,1,0,0,0,1,0,0,0,0};

int IMAGEN38[33]={1,1,1,0,1,1,1,1,0,0,1,0,0,0,0,1,0};

int IMAGEN39[33]={1,0,1,1,1,1,0,1,1,0,0,0,0,1,0,0,0};

int IMAGEN40[33]={1,1,1,1,0,1,1,1,1,0,0,1,0,0,0,0,1};

int IMAGEN41[33]={1,1,0,1,1,1,1,0,1,1,0,0,0,0,1,0,0};

int IMAGEN42[33]={0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0};

int IMAGEN43[33]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0};

int IMAGEN44[33]={0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0};

int IMAGEN45[33]={0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};

int IMAGEN46[33]={0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0};

int IMAGEN47[33]={0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0};

int IMAGEN48[33]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0};

int IMAGEN49[33]={0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0};

int IMAGEN50[33]={0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};

int IMAGEN51[33]={0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0};

int IMAGEN52[33]={0,0,1,0,1,0,1,1,1,0,0,0,1,0,0,0,0};

int IMAGEN53[33]={1,0,1,1,1,1,1,1,0,0,1,0,0,0,0,1,0};

int IMAGEN54[33]={1,1,1,1,1,1,0,1,1,0,0,0,0,1,0,0,0};

int IMAGEN55[33]={1,1,0,1,0,1,1,1,1,0,0,1,0,0,0,0,1};

int IMAGEN56[33]={0,1,1,1,1,1,1,0,1,1,0,0,0,0,1,0,0};

int IMAGEN57[33]={1,1,1,0,1,0,1,1,1,0,0,0,1,0,0,0,0};

int IMAGEN58[33]={1,1,1,0,1,1,1,1,0,0,1,0,0,0,0,1,0};

int IMAGEN59[33]={1,0,1,1,1,1,0,1,1,0,0,0,0,1,0,0,0};

int IMAGEN60[33]={1,1,1,1,0,1,1,1,1,0,0,1,0,0,0,0,1};

int IMAGEN61[33]={1,1,0,1,1,1,1,0,1,1,0,0,0,0,1,0,0};

int IMAGEN62[33]={1,0,1,0,1,0,1,1,1,0,0,0,1,0,0,0,0};

int IMAGEN63[33]={0,1,1,1,1,1,1,1,0,0,1,0,0,0,0,1,0};

int IMAGEN64[33]={1,1,1,0,1,1,0,1,1,0,0,0,0,1,0,0,0};

int IMAGEN65[33]={1,0,1,1,0,1,1,1,1,0,0,1,0,0,0,0,1};

int IMAGEN66[33]={1,1,1,1,1,1,1,0,1,1,0,0,0,0,1,0,0};

int IMAGEN67[33]={1,1,0,1,0,0,0,1,0,0,0,0,1,0,0,0,0};

int IMAGEN68[33]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0};

int IMAGEN69[33]={0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0};

int IMAGEN70[33]={0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};

int IMAGEN71[33]={0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0};

int IMAGEN72[33]={0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0};

int IMAGEN73[33]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0};

int IMAGEN74[33]={0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0};

int IMAGEN75[33]={0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};

int IMAGEN76[33]={0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0};

int IMAGEN77[33]={0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0};

int IMAGEN78[33]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0};

int IMAGEN79[33]={0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0};

int IMAGEN80[33]={0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};

int IMAGEN81[33]={0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0};

int IMAGEN82[33]={0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0};

int IMAGEN83[33]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0};

int IMAGEN84[33]={0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0};

int IMAGEN85[33]={0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};

int IMAGEN86[33]={0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0};

int IMAGEN87[33]={0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0};

int IMAGEN88[33]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0};


void setup() {

  // Open serial communications:



  //Initialize all the engines at the value given

  for (int k = 0; k < numberOfEngines; k++) {










//Comment: All data recived from Serial needs to have an ACK througth the same Serial


void loop() {

while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN0);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN1);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN2);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN3);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN4);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN5);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN6);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN7);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN8);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN9);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN10);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN11);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN12);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN13);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN14);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN15);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN16);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN17);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN18);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN19);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN20);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN21);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN22);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN23);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN24);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN25);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN26);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN27);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN28);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN29);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN30);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN31);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN32);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN33);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN34);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN35);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN36);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN37);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN38);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN39);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN40);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN41);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN42);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN43);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN44);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN45);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN46);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN47);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN48);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN49);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN50);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN51);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN52);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN53);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN54);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN55);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN56);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN57);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN58);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN59);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN60);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN61);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN62);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN63);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN64);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN65);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN66);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN67);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN68);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN69);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN70);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN71);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN72);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN73);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN74);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN75);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN76);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN77);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN78);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN79);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN80);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN81);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN82);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN83);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN84);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN85);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN86);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN87);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);

 while (digitalRead(20)==LOW) {;} SUBIR(IMAGEN88);  delay(2000); while (digitalRead(20)==LOW) {;}  CEROS();  delay(2000);





void SUBIR(int cadena[33])


      for (int k = 0; k < numberOfEngines; k++) {


     if (cadena[k] == 1) {



          else {








void CEROS()


     for (int k = 0; k < numberOfEngines; k++) {










Code 2

The code below works with signals received from the software and send them to the servomotors, the software receibe a signal from the arduino when the user press the button and send the next code line.

//Library for handling Engines

#include <Servo.h>


char rowData[81];            // saving row to process

int length;                  // the length of data to recive

String read;                 // the data recived


int buttonState = 0;         // current state of the switch button

int lastButtonState = 0;     // previous state of the swith button

const int  buttonPin = 2;    // the pin that the pushbutton is attached to


const int  ledPin = 50;      // the pin that the led for changing row is attached to



const int numberOfEngines = 8;

Servo S1; Servo S2; Servo S3; Servo S4;

Servo S5; Servo S6; Servo S7; Servo S8;


int MIN = 0, MAX = 90;      // configuration for the engines

Servo arrayServo[8];        // array of engines


//Other variables

int count = 0;

int count1 = 0;


void setup() {

  // Open serial communications:



  // Attach the led pin as an OUTPUT

  pinMode(ledPin, OUTPUT);


  // Attach the number of pins to the engines

  S1.attach(10); S2.attach(11); S3.attach(12); S4.attach(13);

  S5.attach(6); S6.attach(7); S7.attach(8); S8.attach(9);


  //Initialize all the engines at the minimum value

  S1.write(MIN);   S2.write(MIN);   S3.write(MIN);   S4.write(MIN);

  S5.write(MIN);   S6.write(MIN);   S7.write(MIN);   S8.write(MIN);


  // Save all the engines for better processing

  arrayServo[0] = S1;

  arrayServo[1] = S2;

  arrayServo[2] = S3;

  arrayServo[3] = S4;

  arrayServo[4] = S5;

  arrayServo[5] = S6;

  arrayServo[6] = S7;

  arrayServo[7] = S8;




//Comment: All data recived from Serial needs to have an ACK througth the same Serial


void loop() {

  // Read data available for reading

  length = Serial.available();

  if (length > 0) {

    char serialInByte;


    // Read the incoming byte and flush the stream

    serialInByte = Serial.read();



    switch (serialInByte) {

      // New line has arrived

      case '3':

        count = 0;




      // End of a line has arrived, we need to set the engines in the position recived (1 = Max, 0=  MIN)

      case '4':

        for (int k = 0; k < numberOfEngines; k++) {

          if (rowData[k] == 1) {



          else {

            arrayServo[k].write(MIN) ;




        // Turn te ligth on to tell the user that the switch need to be activated

        digitalWrite(ledPin, HIGH);



      // New data arrived

      case '1':

        rowData[count] = 1;





      // New data arrived

      case '0':

        rowData[count] = 0;





      //Do Nothing






  // Check if the switch button changed state

  buttonState = digitalRead(buttonPin);

  if (buttonState != lastButtonState) {

    // This means it went from OFF to ON

    if (buttonState == HIGH) {

      //We turn OFF the led, and ask for a new line

      digitalWrite(ledPin, LOW);





  lastButtonState = buttonState;




User Interface

Return to beginning

The software has to take an image and transform it into a recognizable input by the electronic loom.

The loom will receive an array of 0s and 1s, where the black line is represented by the 1s and 0s will be white.


It is assumed as the maximum width of the loom is 20 cm, and that 4 wires are 1 cm, so the maximum width of the matrix will be 80 points and an indefinite length.

The loom processed 5x5 quadrants, in each of the rows of that quadrant, the numbers can not be all 0s or 1s.



The solution is based in Winforms in C#, using the extension for image processing Magick.NET (magick.codeplex.com) which is a wrapper for the ImageMagick framework (imagemagick.org).

The process is summarized as follows:

Select image to convert.

Converting the image to a maximum width of 80px, variable length to maintain the aspect ratio.

Processed per quadrant the image, for each pixel we will check if they have more white or black to translate it to the matrix.

Finished processing each row, if they are all 0s going to put a 1, and vice versa, will use the razo:

1 0 1 1 1

1 1 1 0 1

0 1 1 1 1

1 1 0 1 1

1 1 1 1 0

Visual Basic Code

using ImageMagick;

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.IO;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Windows.Forms;

using System.IO.Ports;

using MaterialSkin.Controls;


namespace telar_electronico


    public partial class Form1 : MaterialForm



        private string[] ArrayComPortsNames = null;

        private int index = -1;

        private string ComPortName = null;

        private int imageWidth = 80;

        private int imageHeight = 400;

        private string auxFile = "logo1AUX.png";


        //Serial port 1 for arduino

        private SerialPort serialPort, serialPort2, serialPort3;

        private bool recivedConfirmation = false, recivedConfirmation2 = false, recivedConfirmation3= false;


        public Form1()




            ArrayComPortsNames = SerialPort.GetPortNames();



                index += 1;






            while (!((ArrayComPortsNames[index] == ComPortName)

                          || (index == ArrayComPortsNames.GetUpperBound(0))));



            //want to get first out

            if (index == ArrayComPortsNames.GetUpperBound(0))


                ComPortName = ArrayComPortsNames[0];


            cboPorts.Text = ArrayComPortsNames[0];

            cboPorts2.Text = ArrayComPortsNames[0];

            cboPorts3.Text = ArrayComPortsNames[0];




        private int getYToFill(int num)


            int ret = 0;

            switch (num)


                case 0:

                    ret = 1;


                case 1:

                    ret = 3;


                case 2:

                    ret = 0;


                case 3:

                    ret = 2;


                case 4:

                    ret = 4;



            return ret;



        private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)


            // Show all the incoming data in the port's buffer

            string read = serialPort.ReadLine();


            recivedConfirmation = true;



        private void port2_DataReceived(object sender, SerialDataReceivedEventArgs e)


            // Show all the incoming data in the port's buffer

            string read = serialPort2.ReadLine();


            recivedConfirmation2= true;



        private void port3_DataReceived(object sender, SerialDataReceivedEventArgs e)


            // Show all the incoming data in the port's buffer

            string read = serialPort3.ReadLine();


            recivedConfirmation3 = true;



        private void button1_Click(object sender, EventArgs e)


            int size = -1;

            DialogResult result = openFileDialog1.ShowDialog();

            if (result == DialogResult.OK)


                string file = openFileDialog1.FileName;



                    string text = File.ReadAllText(file);

                    size = text.Length;

                    textBox1.Text= file;



                catch (IOException)






        private void button2_Click(object sender, EventArgs e)


            String file = textBox1.Text;

            if (!String.IsNullOrEmpty(file))



                //Arduino 1

                serialPort = new SerialPort(cboPorts.Text, 9600, Parity.None, 8, StopBits.One);

                serialPort.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);




                //Arduino 2

                serialPort2 = new SerialPort(cboPorts2.Text, 9600, Parity.None, 8, StopBits.One);

                serialPort2.DataReceived += new SerialDataReceivedEventHandler(port2_DataReceived);



               // Arduino 3

                serialPort3 = new SerialPort(cboPorts3.Text, 9600, Parity.None, 8, StopBits.One);

                serialPort3.DataReceived += new SerialDataReceivedEventHandler(port3_DataReceived);





                using (MagickImage image = new MagickImage(file))


                    //Resize the image and turn it into greyscale to save it in auxiliary file

                    image.Quality = 100;

                    image.Resize(new MagickGeometry(new System.Drawing.Rectangle(0, 0, imageWidth, imageWidth)));

                    image.ColorSpace = ColorSpace.Gray;



                    // Aux to process the color of the pixel

                    ushort[] color = new ushort[5];


                    //When we will save the image for sending it to arduino

                    int[,] matriz = new int[image.Width, image.Height];



                    // Process all the pixels of the image by squares of

                    PixelCollection pc = image.GetPixels();

                    for (int y = 0; y < image.Height - 4; y = y + 5)


                        for (int x = 0; x < image.Width - 4; x = x + 5)


                            int numRow = 0;

                            //Vamos a procesar desde la fila y a la y+4

                            for (int fromY = y; fromY < y + 5; fromY++)


                                int numbBlack = 0;


                                //Vamos a ir por cada una de sus culumnas

                                for (int fromX = x; fromX < x + 5; fromX++)




                                        //Obtenemos el color: 0->Negro y 65536->Blanco

                                        color = pc.GetValue(fromY, fromX);

                                        int porcentajeBlanco = (100 * color[0]) / 65536;


                                        if (porcentajeBlanco > 50)


                                            matriz[fromY, fromX] = 1;





                                            matriz[fromY, fromX] = 0;



                                    catch(Exception ex)


                                        //do nothing





                                if (numbBlack == 5)


                                    //If there are 5 blacks, we put a white

                                    matriz[fromY, x + getYToFill(numRow)] = 1;


                                else if (numbBlack == 0)


                                    //If there are all whites, we put a black

                                    matriz[fromY, x + getYToFill(numRow)] = 0;








                    // Row to print

                    string row = "";


                    for (int k = 0; k < matriz.GetLength(0); k++)


                        // A new row needs to be send: 3



                        while (!recivedConfirmation) { /*Wait Confirmation*/}

                        recivedConfirmation = false;


                        // Send the row information

                        for (int l = 0; l < matriz.GetLength(1); l++)



                            int val = matriz[k, l];


                            if (l < 32) {



                                while (!recivedConfirmation) { /*Wait Confirmation*/}

                                recivedConfirmation = false;


                            else if (l >= 32 && l < 64)




                                while (!recivedConfirmation2) { /*Wait Confirmation*/}

                                recivedConfirmation2 = false;


                            else if (l >= 64 && l < 96)




                                while (!recivedConfirmation3) { /*Wait Confirmation*/}

                                recivedConfirmation3 = false;




                        ////Send the end of the row



                        //while (!recivedConfirmation) { /*Wait Confirmation*/}

                        //recivedConfirmation = false;




                        while (!recivedConfirmation3) { /*Wait Confirmation*/}

                        recivedConfirmation3 = false;



                        // Print the already sent line

                        row = "";

                        for (int ind = 0; ind < image.Width; ind++)


                            row += matriz[k, ind];


                        log.AppendText(row + System.Environment.NewLine);









Below are 3 videos showing the Fab Jacquard 2.0 working: