Projecte de fotònica!

L'objectiu és comprendre com funciona la llum mitjançant microcontroladors i programació. En el meu cas, és la transmissió de la llum a través de colorants de diferents colors.

El procediment per construïr un espectrofotòmetre consta de les seguents parts:

Trobar el màxim d'absorbància de cada colorant

Hem de buscar la bibliografia a google scholar el màxim d'absorbància de: àcid carmínic, luteïna i FCF

Valors d'absorbància màxima dels colorants:

Nom Carmín Luteina FCF Font
Raquel Albarrán 612nm (1) 650nm 630nm (3) 1. Autores=Dayana Donneys-Victori,Cristian Camilo Ospina-Toro,Mónica Vanesa Zapata-Villegas, Nilson Marriaga-Cabrales,Fiderman Machuca-Martínez, Juan M. Peralta-Hérnandez, Carlos A. Martínez-Huitle
Titulo= DYNA. Electrocoagulación de soluciones de índigo carmín empleando ánodos de magnesio y de aleación AZ31. Vol.85. Sept. 2018

https://www.scielo.cl/scielo.php?script=sci_arttext&pid=S0718-34292015000300004
2. https://grupofranja.net/danos-oculares-que-produce-la-luz-azul-y-mecanismos-fisiologicos-de-defensa/
3. Autores=? Orange Country Biothecnology Education Collavorative Titulo: 1.6 Espectofotometría. Libre Texts Español (MindTouch)
https://espanol.libretexts.org/Biologia/Biotecnolog%C3%ADa/Manual_de_Laboratorio%3A_Introducci%C3%B3n_a_la_Biotecnolog%C3%ADa/01%3A_T%C3%A9cnicas/1.06%3A_Espectrofotometr%C3%ADaibre Texts Español
Manal El Morabit 494 nm (4) 455 nm (5) 630 nm (6) Carmín: Tello V, Vargas J Efecto de la luz artificial a diferentes fotoperiodos sobre dos variables productivas de la grana cochinilla, Dactylopius coccus Costa (Hemiptera: Dactylopiidae) para su cultivo bajo condiciones controladas
VOLUMEN:33 no.3 Arica ago. 2015

Luteína:
FCF:
Alexis Álvarez 494 nm 445 nm 620 nm Àcid Carmínic: https://dialnet.unirioja.es/servlet/articulo?codigo=9252159
FCF: Luteina: https://digital.csic.es/bitstream/10261/172392/1/biomarocuhuman.pdf
Luteína: https://digital.csic.es/bitstream/10261/172392/1/biomarocuhuman.pdf
Bassim Lyamani 535nm (4) 422-445nm (5) 560nm (6) 4: Ocampo, C. Informe de pràctica de laboratorio, Universidad Peruana Union, Volumen nro. 5 pagines (9) (2006) (DOC) caracterizacion de colorantes | claudia ocampo - Academia.edu
5: Burgos J.T y Calderon F.R DETERMINACION DEL CONTENIDO DE CAROTENOIDES TOTALES EN
OCHO ESPECIES DE FRUTAS Y VERDURAS COMERCIALIZADAS EN
LA ZONA METROPOLITANA DE SAN SALVADOR
494nm 535nm 623nm ÀCID CARMÍNIC:
Luteina:
FCF:
Joel Cano 490-499 nm 440 nm 630 nm (7B) Àcid Carmínic: Salazar,KG et al Extracción de ácido carmínico como colorante natural a partir de la cochinilla Pol.Con 8 : 583-605 (2023) https://dialnet.unirioja.es/servlet/articulo?codi go=9252159
(7D) FCF: Rodriguez,RF et al Evaluación de colorantes sintéticos en bebidas comercializadas en la ciudad de Trujillo en el periodo 2018 - 2019 Alpha Centauri 2 : 124-139 (2021) https://dialnet.unirioja.es/servlet/articulo?codigo=8092600
(7C) Luteína: Odorrisi, AA et al DESENVOLVIMENTO E VALIDAÇÃO DE MÉTODO ESPECTROFOTOMÉTRICO PARA DETERMINAÇÃO DE CORANTE À BASE DE LUTEÍNA ADICIONADO EM IOGURTE DESNATADO Quim. Nova 35 : 2057-2062 (2012) https://www.scielo.br/j/qn/a/6qfQpCHJ8jn8rH8RYyrVXSK/?lang=pt#
Alex Roca 495 nm 445 nm 620 - 630 nm Carmín:
Luteina: Estèvez, R. Biomarcadores de luteína, zeaxantina y otros carotenoides en la relación dieta y salud ocular humana (Tesis Doctoral) Universidad Complutense de Madrid. (2016) https://digital.csic.es/bitstream/10261/172392/1/biomarocuhuman.pdf
FCF: Rodríguez, M.C. et al. Cuantificación simultánea de colorantes en bebidas deportivas utilizando espectroscopia visible y PLS–1. Revista FABICIB. volumen 17. PÁGS. 74 - 84. (2013) https://www.researchgate.net/publication/282977677_Cuantificacion_simultanea_de_colorantes_en_bebidas_deportivas_utilizando_espectroscopia_visible_y_PLS-1
Daniel Solis 494 nm 445 nm 482 nm (7B) Àcid Carmínic: Salazar,KG et al Extracción de ácido carmínico como colorante natural a partir de la cochinilla Pol.Con 8 583-605 (2023) file:///C:/Users/DanielSol%C3%ADsArteaga/Downloads/Dialnet-ExtraccionDeAcidoCarminicoComoColoranteNaturalAPar-9252159%20(1).pdf
Luteina: https://www.boe.es/buscar/pdf/2009/BOE-A-2009-16021-consolidado.pdf
FCF: https://www.argentina.gob.ar/normativa/recurso/86181/dto202-2003-12/htm
Ariadna Arcas 495 nm 455 nm 630 nm Carmín:https://www.scielo.cl/scielo.php?script=sci_arttext&pid=S0718-34292015000300004
Luteína:https://grupofranja.net/danos-oculares-que-produce-la-luz-azul-y-mecanismos-fisiologicos-de-defensa/
FCF:https://espanol.libretexts.org/Biologia/Biotecnolog%C3%ADa/Manual_de_Laboratorio%3A_Introducci%C3%B3n_a_la_Biotecnolog%C3%ADa/01%3A_T%C3%A9cnicas/1.06%3A_Espectrofotometr%C3%ADa Orange Country Biothecnology Education Collaborative Titulo: 1.6 Espectofotometría. Libre Texts Español (MindTouch)
Mariana Velasco 494nm 445nm 630nm https://docs.google.com/document/d/17PXOVwkcxc__sNLeHLdus3zdZb38olWlQAy8A463YLM/edit?usp=sharing
Mateo Pérez 494nm 440nm 630nm Carmín:
Luteína: https://dialnet.unirioja.es/servlet/articulo?codigo=8092600
FCF:

Comprobar el màxim d'absorbància amb codi

Utilitzarem codis que faran un escaneig o scan de forma que encendran els llums red green blue del led RGB de forma que generarem longuituds d'ona que van de 38o nanomètres a 780 nanomètres

Escriu la longitud d'ona en nanomètres i converteix-la en RGB en la seguent línea



A la figura 1 podem visualitzar la relació que té la longitud d'ona i el color.

Spectrum
Fig.1- Espectre de llum visible amb longitud d'ona en nanòmetres.

<input type="number" id="wavelength" value="550" min="380" max="780">

El codi anterior que transforma longitud d'ona en valors RGB funciona de la seguent manera: longitud d'ona té una etiqueta o tag anomenat label.

Permet introduir qualsevol tipus de valors pel teclat, però la propietat o atribut de input anomenat type esta definida com a number i només permet introduir números. El value és una propietat o atribut de in`put que fa que surti com a valor predeterminat i min i max són valors mínims i màxims, i no s'apceptaran valors fora de marge. La ID és una propietat molt importatnt, perque es com el DNI i el cridarem en getElementById.

<button onclick="convertWavelength()">Converteix a RGB</button>

La paraula button crea un botó de forma automàtica que te un atribut anomenat onclick, que està espereant se clickada. Altres que poden existir onmuseover (quan estic a sobre), onkeydown (quan presiono una tecla), onland (quan es carrega una pàgina o un element), onsubmit (quan envío un furmulari).

El contingut del butó s'escriu abans de final de button.

Quan detecti que onclick és igual a true, s'executarà una funció que no té cap parametre ni argument, perque té dos parametres buits. La funció s'anomena convertWaveleght i està definida més endevant amb la paraula function convertWaveleght(){...intruccions aquí...}. Això vol dir que quan clicko a un botó creat amb html crida a una funció creada en JavaScript que més endevant convertirà la longitud d'ona que estic en RGB.

<div id="result"></div>

Un div és un divisor que és com un paràgraf en aquest cas buit que té un identificador anoomenat resul que només es veurà i s'executarà desprès, és a dir, quan es carrega la pàgina està buit u quan clickem el botó de convertir farà la seva funció.

 
  
  function convertWavelength() {
        const wavelength = document.getElementById("wavelength").value;
        const R = Math.round(getRGBValue(wavelength, 'R'));
        const G = Math.round(getRGBValue(wavelength, 'G'));
        const B = Math.round(getRGBValue(wavelength, 'B'));
        const result = `RGB values: (${R}, ${G}, ${B})`;
        document.getElementById("result").textContent = result;
      }
  
  

Una funció és una manera que tenim de transfomar un valor que donem nosaltres inicialment, en un altre diferent. En aquest cas volem transformar el valor de la longitud d'ona en tres valors: RGB.

Sempre que escric la paraula function, per crear una funció, he de posar al costat el nom de la funció, en anglés, que expliqui el que fa, en aquest cas convertir la longitud d'ona i poso convertWaveLength(), en camelCase, i posem un parentesis buit perquè no depen de paràmetres o arguments.

Dins de la funció tenim diverses instruccions agrupades en una clau

Les primeres 5 línies de la funció defineixen constants locals perquè s'apliquen dins de la funció i no afecten a tot el codi, a diferència de les constants globals que es posen fora de les funcions i afecten a tot el codi(const PI = 3,14;).


  1. La primera línia de la funció agafa el valor (.value) de la longitud que s'ha introduït a l'input, i l'identifica perquè l'agafa amb el getElementById, i l'emmagatzemem amb un nom concret (wavelength) que no pot variar dins de la funció, però si globalment.
  2. const R = Math.round(getRGBValue(wavelength, 'R')); const G = Math.round(getRGBValue(wavelength, 'G')); const B = Math.round(getRGBValue(wavelength, 'B')); El que fa aquestes línia és agafar una funció que s'anomena RGBValue i que té dos paràmteres o arguments interns que són: primer la longitud d'ona introduïda per l'usuari i el segón, el valor RGB que generem. El valor del càlcul matemàtic que obté la funció getRGBValue, de RGB és un float(número decimal), i nosaltres el convertim en un int(integer o número sencer), i el guardem en una variable constant local anomenada R, G o B.
  3. const result = `RGB values: (${R}, ${G}, ${B})`;
    document.getElementById("result").textContent = result;
    El result col·loca els valors obtinguts anteriorment amb el sel·lector "$", dins del div buit amb identificador "result".

  
   function getRGBValue(wavelength, color) {
        const gamma = 0.8;
        const factor = 0.1;
        let R, G, B;
        const nm = parseFloat(wavelength);
        if (nm < 380 || nm > 780) {
          return 0;
        }
  
  
  1. La funció getRGBValue, té dos arguments interns que són: la longitud d'ona i el color, i necessita dos constants (gamma i factor) per fer la transformació matemàtica entre longitud d'ona i valor RGB, ja que el color és complexe i gamma i factor corregeixen les fòrmules que veurem més endavant perquè aquí la fòrmula és molt simple: si l'usuari introdueix un valor entre 380 nm i 780 nm, es donarà un valor 0 perquè estem fora de marge de la longitud d'ona visible..

 
  if (nm >= 380 && nm < 440) {
          R = -(nm - 440) / (440 - 380);
          G = 0;
          B = 1;
        } else if (nm >= 440 && nm < 490) {
          R = 0;
          G = (nm - 440) / (490 - 440);
          B = 1;
        } else if (nm >= 490 && nm < 510) {
          R = 0;
          G = 1;
          B = -(nm - 510) / (510 - 490);
        } else if (nm >= 510 && nm < 580) {
          R = (nm - 510) / (580 - 510);
          G = 1;
          B = 0;
        } else if (nm >= 580 && nm < 645) {
          R = 1;
          G = -(nm - 645) / (645 - 580);
          B = 0.0;
        } else if (nm >= 645 && nm < 781) {
          R = 1;
          G = 0;
          B = 0;
        }
        if (color === 'R') {
          return 255 * Math.pow(R, gamma);
        } else if (color === 'G') {
          return 255 * Math.pow(G, gamma);
        } else if (color === 'B') {
          return 255 * Math.pow(B, gamma);
        }
      }
 
 
  1. Si el valor que s'introdueix es troba entre els dos valors, es fa una divisió entre el valor en específic, menys el valor màxim de marge, entre la resta dels dos marges, donant el valor d'un dels tres colors.
  2. En el cas de R, la transformació és més complicada ja que hem de obtenir un nombre i multiplicar-lo per B elevat a gamma, ja que Math.pow, significa "elevat".

Projecte espectrofotometre amb Arduino, Python i PC

El codi Arduino es el seguent:


/*Creem 6 variables de tipus integer (int), vol dir que els pins del 12 al 17 els hi donem noms per identificaros, perque es mes fácil 
entendre pinLedR que 12 ja que vol dir el pin el que es conecta la pota R del led RGB. Aquestes definicions esn ajuden a entedre el codi els humans
i tenir ordenat el codi */ 
int pinLedR = 12; // Pin per al LED vermell (RGB)
int pinLedG = 13; // Pin per al LED verd (RGB)
int pinLedB = 14; // Pin per al LED blau (RGB)
int pinLedUV = 15; // Pin per al LED ultraviolat
int pinLedIR = 16; // Pin per al LED infraroig
int pinLDR = 17; // Pin per al sensor LDR

/*setup es una funció de configuració que és oblegatori declarar on diem que els pinMode que es el mode de conexio de cada pin, que pot ser sortida
(OUTPUT) perque la llum surt cap afora (output) en el cas del pinLedR, pinLedG, pinLedB, pinLedUV, pinLedIR. En el cas del sensor LDR és un INPUT, 
vol dir que entra informació, en aquest cas llum de l'exterior. */
void setup() {
pinMode(pinLedR, OUTPUT);
pinMode(pinLedG, OUTPUT);
pinMode(pinLedB, OUTPUT);
pinMode(pinLedUV, OUTPUT);
pinMode(pinLedIR, OUTPUT);

pinMode(pinLDR, INPUT);
/* serial esta en majuscula perque es una classe que controla la comunicació en serie entre el arduino i l'ordinador. Els arduino uno es comuniquen a 9600 bits per segon i els ESP-32 S3 
es comuniquen per segon. */
Serial.begin(9600); // Inicia la comunicació sèrie a 9600 bauds
}


void loop() {
if (Serial.available() > 0) {
/*La comunicació serie es atraves d'un cable que antigament s'anomenava RS232 que tenia 9 pins, dels 9 pins alguns eren per transmetre informació tipus duplex que vol dir que envia informació des de l'ordinador a l'arduino i a l'inversa. Internament el arduino i altres microcontroladors internament també tenen conexions físiques i protocols de softwere RS232. El usb té 4 pins, els dos més allunyats del centre són d'alimentació, per donar corrent (undona carrega positiva i l'altre es al terra), els dos pins del centre un circula l'informació cap dins de l'ordinador i l'altre cap fora. Serial.available és un metode available que significa disponible que apliquem per la sitaxi del punt a una classe Serial (en altres llenguatges hauriem de crear un objecte, es a dir Serial miniscula; o serial=new Serial();). El mètode avaliablle només acepta dos resultats mayor a 0 o igual a 0, si és igual a 0 significa que el cable no està conectat o dona error, i si és major a 0 significa que és capaç de rebre un carater ASCII. */
char comanda = Serial.read(); // Llegeix la comanda enviada pel port sèrie
/*La comanda és un caracter perquè ho definim així i perquè el port serial o USB envia caraters ASCII, i el port serial està rebent pel mètode read caracters que està llegint.*/

switch(comanda) {
case ‘I’: // Encén o apaga el LED infraroig i llegeix el sensor LDR
digitalWrite(pinLedIR, !digitalRead(pinLedIR));
llegirLDR();
break;
case ‘U’: // Encén o apaga el LED ultraviolat i llegeix el sensor LDR
digitalWrite(pinLedUV, !digitalRead(pinLedUV));
llegirLDR();
break;
/*Si pressiono la tecla 'I' envira el caràcter I a traves del cable mitjançant les instruccions serialavalabel i serialread, i quan arribi la I a l'Arduino anirà al prsoccessador de l'Arduino AVR i torbarà aquest codi que diu case I que significa que si es produeix apretant una I ha de fer un digitalwrite o encendre el LED infraroig. Passarà el mateix amb la tecla 'U', però en contes de encendre el LED infraroig encendrà el LED ultraviolat. Amés de encendre els LED els apagarà si estan encessos amb l'instrucció !, per últim en els dos casos també llegirà els LDR perquè hem cridat la funció llegirLDR();. */
case ‘R’: // Controla el LED RGB i llegeix el sensor LDR
int r = Serial.parseInt();
int g = Serial.parseInt();
int b = Serial.parseInt();
/*El LED RGB no es pot encendre com els LEDs anteriors perquè té 16 milions de combinacions, perquè són 255 vermells multiplicats per 255 blaus. Hem de posar un valor concret que pot ser introduït per l'usuari i amb SerialparceInt agafa el valor introduït per l'usuari i si és un caracter o un número decimal el transforma en un número int o integer o numero cencee, i amb Serial l'envia a l'Arduino i emmagatzema cada número en una variable sencera nomenada r, g o b.*/
analogWrite(pinLedR, r); // Controla el LED vermell
analogWrite(pinLedG, g); // Controla el LED verd
analogWrite(pinLedB, b); // Controla el LED blau
/*//La variable emmagatzemada r,g i b correspon a un valor entre 0 i 255 que introduint en les 3 instruccions anteriors amb analogWrite per encendre cada LED. La diferència entre digitalWrite que utilitzaven per el LED ultraviolat i infraroig, i analogWrite que estem utilitzant en LED RGB és que el digitalWrite és una funció digital que només és possible fer 2 funcions (0 o 1), o LOW i HIGH. En canvi analogWrite  permet fer 255 valors. Les dues funcions tenen en comú que tenen dos paràmetres o arguments entre parèntesis, el primer paràmetre és el número de Pin on està connectat, per exemple: el LED vermell (pota vermella del LedRGB) ÉS pinLEDr, que és el mateix que escriure el número 12. El segon paràmetre és r que pot ser 0 o 1, o 0 a 255 i és introduït per l'suari que ens diu l'intensitat en la que s'encendrà. De 0 a 255 és una tecnologia que realment no és analògica, sinó PWM. Per exemple quan escric 127 està a la meitat de la intensitat màxima, perquè? Realment el que observo és el Led menys intens, el que pasa és que la meitat de temps està encés i la meitat del temps està apagat: 0101010101010101, de manera molt ràpida. Per exemple quan escrivim 64, es representarà com una cuarta part de la intensitat màxima, aleshores seria 0001000100010001 a tota velocitat i no veuriem que s'apaga, perquè l'ordre de temps és en mil·lisegons i el nostre ull no l'observa. Per últim amb 190 seria tres quarts encessos: 1110111011101110. El valor 0 analògic seria 00000000000000 i 255: 111111111111111.*/
llegirLDR();
break;
}
}
// Altres parts de la teva lògica del programa
}

void llegirLDR() {
int valorLDR = analogRead(pinLDR);
Serial.print(“Valor LDR: “);
Serial.println(valorLDR);
}
/* La funció analogRead el que fa es llegir els valors que te el LDR, 

 


Aquests són els passos a seguir informaticament:

Tipus de variables en arduino

Primer de tot hem de crear ua variable anomenada sensorValue i per crear-la utilitzaem la instrucció float sensorValue;

Això crearà un espai a la memòria del microcontrolador capaç d'emmagatzemar un nombre decimal, o (floating point number). Aquest espai de memòria el puc anomenar com volgui, si l'anomenès valor Sensor, en castellà o català, només ho entendria la gent d'aquí.

El posem en anglès en estil camelCase o lletra de camell, hi ha altres estils com snake_case, PascalCase, kebab-case. Es a dir la variable que emmagatzema els valors del sensor es pot anomenar en els estils anteriors com: sensorValue, sensor_value, SensorValue, sensor-value. Sempre s'ha de fer servir el mateix estil en un codi, el més utilitzat és cameCase.

És important el punt i coma al final de cada línia, ja que indica final d'instrucció.

En els microcontroladors antics que tenen molt poca memòria (Arduino UNO 16kB molt poc comparat amb ESP32-s3que te 512kB) podria gastar menys memòria definint variables més petites que un nombre decimal, per exemple un nombre sencer. En aquest darrer cas la instrucció seria int sensorValue; perquè int vol dir integer number, i accepta 5 dígits però fins al número 64000 o 32000 i 1023 esta dins del marge. Si en comptes d'un sensor fos una sortida analògica de 8 bits que es igual a 28 igual a 255, quina variable utilitzaries?

Si vull utilitzar byte no arribaria a 1023 si no a 255 i no serviria per un sensor que llegeix de 0 a 1023. Aquest de 255 es podria fer servir per una sortida analògica PWM de 8 bits, però estem parlant d'entrades analògiques, de sensors.

Altres tipus de variables que consumeixen molt poca memòria es boolean. Boolean ve del nom d'un senyor Boole que va inventar àlgebra basada en 0 i 1, o en el seu equivalent false i true. Es pot fer servir per començar un moviment, primer és false i després es truesi es compleix una condició com per exemple pressionar una tecla.

Char es un altre tipus de variable, que vol dir caràcter i és compatible amb arduino, i que inclou els caràcters ASCII , on podem veure que els caràcters són números tant decimals com binaris. Per exemple la lletra a és el número decimal 97 i el número binari 01100001. Perquè 0*27+1*26+1*25+0*24+0*23+0*22+0*21+1*20= 0+64+32+0+0+0+0+1 = 97

Primer de tot hem de crear una variable anomenada sensorValue i per crear-la utilitzem la instrucció float sensorValue;

Convertidor analògic digital ADC

En el cas de arduino UNO, la velocitat de mostreig del rellotge inclós el ADC, és 125 kHz que vol dir 125000 cicles per segon. En el cas de ESP32-S3, la seva velocitat màxima és de 2 MHz, (2000000 de mostres per segon) però realment s'aprofita només 1000 per segon, via wifi. Aquests valors tan alts són teòrics.

Codi per manipular una entrada analògica (sensor)

const int uvLEDPin = 5; // Pin digital per al LED UV const int uvSensorPin = 11; // Pin analògic per al sensor UV

Al principi de tot codi sempre hi ha les constants i les variables que utilitzarem. En aquest cas tot són constants globals i no hi ha cap biblioteca inicial importada. Una constant global vol dir que afecta totes les funcions de tot el codi. En canvi una variable local està definida dins d'una funció i no la podem cridar desde fora de la funció. Les constants signifiquen que jo en comptes d'escriure el nombre 5 he d'escriure uvLEDPin, i en comptes d'escriure 11 escric uvSensorPin, fent el codi molt més llarg, però més comprensible per un humà, perquè s'enten molt millor si és un sensor o un led ultravioleta quejo connecto. Normalment es posa en anglès camelCase.

void setup() { pinMode(uvLEDPin, OUTPUT); Serial.begin(9600); }

El void setup és una funció obligatoria del programa arduino i significa la configuració del hardware, o maquinari del propi arduino. Ens diu en primer lloc si els pins que tenim connectats són entrades o sortides, amb l'argument OUTPUT, si és una sortida, o argument INPUT, si és una entrada. Ens hem d'adonar que pinMode és una funció predefinida d'arduno que accepta dos arguments. El primer és el número o nom del pin, i el segon és si és una entrada o sortida. Serial.begin és un mètode construït amb la sintaxi del punt, això vol dir que Serial és un objecte predefinit de la classe Serial

void loop() { // Encén el LED UV //digitalWrite(uvLEDPin, 80); analogWrite(uvLEDPin, 80); // Mesura el valor del sensor UV int uvValue = analogRead(uvSensorPin); // Imprimeix el valor a la consola sèrie Serial.print("UV Value: "); Serial.println(uvValue); // Apaga el LED UV //digitalWrite(uvLEDPin, LOW); // Espera un temps abans de fer la següent mesura delay(1000); }

Per exemple el codi seguent utilitza una entrada analògica de 12 bits d'ESP32-S3 i on esta conectat un sensor UV anomenat GUVA-S12

To learn more HTML/CSS, check out these tutorials!