L’acceleració de la gravetat (g) és una constant física fonamental a la mesura de la qual, entre d’altres, dedico un apartat a la pàgina Determinació experimental de magnituds i constants físicoquímiques fonamentals (o no tant). Mesurar g amb precisió és un exercici clàssic en els laboratoris de física, que tradicionalment es realitza amb pèndols o cossos en caiguda lliure cronometrats amb fotocèl·lules, per exemple.

Això últim ja el vaig proposar a Temps que triga un objecte en passar per una barrera de llum on utilitzava una barrera de llum formada per un led IR enfrontat a un fototransistor treballant tots dos a l’infraroig (940 nm), deixant caure entre els dos una tanca amb franges alternes opaques i transparents, el que no era sinó fer el mateix que en Moviment de caiguda d’un regle (per l’alumnat , pel professorat) en el que s’utilitzava un datalogger, o en Estudi de moviments amb fotocèl•lula que es feia amb un telèfon mòbil i una fotoresistència.
En aquesta entrada proposo un mètode una mica diferent al de la barrera de llum IR que combina la mateixa tanca, però ara amb un sensor de barrera òptica tipus Flying-fish. La tanca es deixa caure verticalment davant el sensor que té, tant el led IR com el fototransistor IR, un al costat de l’altre, de manera que si passa un objecte pel davant seu la llum infraroja emesa pel led es reflecteix i és recollida pel fototransistor. Cada vegada que una franja transparent passa a ser opaca passant pel davant del sensor, la situació canvia d’estat. Registrant els instants de cada canvi, podem reconstruir la llei del moviment de caiguda uniformement accelerat i extreure el valor de g.
La tècnica que proposo aquí presenta dues avantatges respecte a la de la barrera de llum. Per una banda el muntatge és molt més simple ja que no cal més que comprar el sensor (no cal construir-lo a partir dels seus elements) i és de baix cost (3 €), i no cal més que un suport. Per una altra el programari consisteix en un sketch (programa per a l’IDE d’Arduino) una mica més elaborat que el de la barrera, que ara incorpora un filtre antirebot (debounce) el que evita falsos canvis d’estat, i a més funciona en un microcontrolador ESP32 (no només amb un Arduino).
- El maquinari: ESP32 i sensor Flying-Fish
- Muntatge experimental
- El codi per a l’ESP32
- Realització de l’ experiment i captura de dades
El maquinari: ESP32 i sensor Flying-Fish
El microcontrolador ESP32.
El microcontrolador ESP32 és el que és diu un sistema-en-xip (SoC) de baix cost (uns 5 €) i baix consum, amb doble nucli i WiFi i Bluetooth integrats. Per a les nostres finalitats es pot aprofitar la seva velocitat de procés (fins a 240 MHz) i els seus pins digitals per cronometrar esdeveniments en el nivell de fins els microsegons.
L’ESP32 en té diversos pins digitals configurables de manera que qualsevol dels pins GPIO pot llegir-se com a entrada digital (en el nostre cas el 12). També té temporització interna amb la funció millis() que retorna el temps transcorregut des de l’arrencada amb una resolució d’1 ms, suficient per a intervals típics de caiguda lliure (desenes a cents de ms).
En el muntatge utilitzarem qualsevol placa de desenvolupament ESP32 alimentada per USB (5 V) que al seu torn subministra 3,3 V als pins d’alimentació. A mi, com ja us he comentat diverses vegades, una placa que m’agrada molt és la tipus Wemos D1 R32 en la que és molt fàcil punxar els cables pont.

Sensor Flying-Fish.
El sensor Flying-Fish és un mòdul de barrera òptica o sensor de proximitat que integra un emissor infraroig (IR) i un fototransistor receptor, alineats en el mateix encapsulat. Quan un objecte opac interromp el fes de llum, la sortida digital canvia de nivell lògic. Les seves especificacions típiques son:
Voltatge d’alimentació: 3,3 – 5 V
Consum: ≈ 20 mA
Sortida digital: TTL compatible, activa a nivell HIGH o LOW segons el model.
Distància de detecció: per anar bé 10 centímetres màxim.
S’han de connectar els pins del sensor a la placa ESP32 segons: VCC a 3,3V (també es pot fer servir 5 V), GND a GND (Terra ) i OUT a GPIO12 (qualsevol pin digital lliure serveix; a l’sketch es defineix com a ‘PIN_SENSOR = 12’).
És un sensor d’obstacles per infrarojos clàssic (molt usat amb Arduino i de la sèrie Fling-Fish MH-Sensor). Si falla al funcionar es pot ajustar la distància de detecció (sensibilitat) amb el potenciòmetre blau que es veu al centre del dispositiu.
Calibrar el potenciòmetre
Per calibrar el potenciòmetre comencem per dona energia al mòdul connectant el pin Vcc a una font d’alimentació o a la pròpia placa ESP32 a 3.3 o 5 V i el pin GND al negatiu (terra). En fer-ho, el led que indica que està endollat s’encén, i l’altre pot quedar apagat o encès.
Ara, poseu un objecte (com la vostra mà o un tros de paper) just a la distància màxima a la que voleu que el sensor el detecti (per exemple, a uns 5 o 10 cm davant dels dos ulls del sensor). Ajusteu el potenciòmetre fent servir un tornavís d’estrella petit per girar el cargol fins que s’encengui o s’apagui l’altre led.
Compteu que en girar-lo en sentit horari augmenta la sensibilitat (el sensor pot detectar objectes que estiguin més lluny) i en girar-lo en sentit antihorari disminueix la sensibilitat (l’objecte haurà d’estar més a prop perquè el sensor el vegi).
Per a fer el calibratge allunyeu qualsevol objecte de la part frontal del sensor. Si els dos leds estan encesos, gireu el potenciòmetre en sentit antihorari fins que s’apagui un i a continuació, una vegada que col·loqueu un objecte a la distància desitjada, torneu a girar el potenciòmetre molt a poc a poc en sentit horari fins que el led de sortida s’il·lumini. En aquest punt exacte queda calibrat per a aquesta distància.
Tingueu en compte que el color importa, de manera que els objectes negres absorbeixen la llum infraroja (totes les llums!), per la qual cosa el sensor necessitarà que estiguin molt més a prop per detectar-los que si fossin blancs. A més, aquest sensor es torna una mica boig quan la llum ambiental te un component important de llum infraroja, per exemple quan dona el sol, així que funcionen més bé de nit o amb les persianes abaixades.
Una alternativa millorada
Hi ha d’altres sensors d’infrarojos una mica més sofisticats que no tenen aquest últim problema i que , per tant, funcionen més bé. Són els mòduls KY-032 IR. A diferència del model bàsic, aquest és més robust perquè emet un senyal modulat (freqüència de 38 kHz). Això ho fa molt resistent a les lectures falses provocades per la llum del sol o alguns focus de llum.
Aquest model és, doncs, més interessant ja que gairebé pel mateix preu, a més de controlar amb un dels dos potenciòmetres la potència d’emissió, com en els cas dels més senzills (de 2 a 40 cm), té un altre potenciòmetre que ajusta la freqüència de recepció i s’usa per sintonitzar el sensor exactament als 38 kHz perquè ignori qualsevol altra llum ambiental, encara que normalment no cal fer res
Aquest sensor te quatre pins, un de més que diu EN (de Enable), i que funciona si es treu el jumper (si es deixa el jumper posat, com està a la foto, el sensor sempre està encès i funcionant ), de manera que es pot connectar el pin EN a un pin del microcontrolador per apagar i encendre el sensor a voluntat i així estalviar bateria. Els altres tres pins s’han de connectar igual: G a GND (Negatiu/Terra), V a Vcc (Alimentació de 3.3V a 5V), i S al GPIO 12 (en el nostre sketch).
Muntatge experimental
Un cos (la tanca, en el nostre cas) en caiguda lliure a prop de la superfície terrestre, menyspreant la resistència de l’aire, es mou amb acceleració constant g. Si elegim un eix y vertical cap avall, la posició en funció del temps ve donada per:
y(t) = y0 + v0*t + ½ g*t2
on y0 és la posició inicial i v0 la velocitat inicial a la que hi arriba la tanca al sensor.
L’objecte que cau serà una tanca que té una successió de franges opaques i transparents d’igual amplada, o no, que s’han de mesurar el més bé possible. Quan la tanca cau, cada transició opaca-transparent (o transparent-opaca) succeeix quan la vora d’una franja coincideix amb el fes del sensor. Si es mesura el temps en el que es produeix cada transició es pot graficar les successives distàncies recorregudes per la tanca en la seva caiguda davant el temps i trobar l’equació del moviment, d’on obtindrem el valor de g (l’acceleració de la caiguda). Aquest serà el procediment d’anàlisi que seguirem després de capturar les dades experimentals.
Encara que l’amplada de les franges sigui igual els intervals de temps entre transicions consecutives no seran constants, sinó que minvaran a mesura que la tanca acceleri.
Fixeu el sensor a un suport (per exemple, una abraçada de laboratori o un bloc de fusta) de manera que el feix infraroig quedi horitzontal. El sensor s’ha de col·locar de manera que el fes incideixi a la tanca perpendicularment, a una distància constant durant tota la caiguda.

Com ja he comentat, la tanca de franges actua com un “obstacle mòbil” que alterna estats transparent (fes no interromput, i que es perd) i opac (fes interromput, i que es reflecteix). Col·loqueu la tanca de manera que pugui caure verticalment just davant del sensor, a una distància d’un parell de centímetres del front de l’encapsulat (per garantir que les franges opaques interrompin completament el fes), i que caigui lliurement, sense fregar el sensor ni altres obstacles. Pot subjectar-se amb els dits pel seu extrem superior i deixar-la anar netament.
Materials necessaris
- Placa ESP32 (qualsevol).
- Sensor Flying-fish o KY-032 IR .
- Tanca impresa o dibuixada i retallada amb almenys tres franges opaques i dues de transparents, cadascuna d’amplada coneguda, millor si son iguals, d’un mínim de 2 cm. La tanca s’imprimeix en paper o cartró, amb amplades idèntiques (o no) per a les zones opaques i transparents (per exemple, a L = 4,0 cm). Si és lleugera fora convenien enganxar-li un parell d’agulles d’estendre la roba a la par inferior per minimitzar l’efecte del fregament amb l’aire.
- Suport rígid (tres peus i pinça) per subjectar el sensor de manera que la tanca pugui caure lliurement davant seu.
- Cables dupont per a les connexions.
- PC amb port USB i terminal sèrie (Arduino IDE, Realterm, etc.).
El codi per a l’ESP32
El firmware que he preparat per realitzar les mesures es diu ‘sketch_flyingfish_sensor.ino‘ i està escrit en C++ per l’IDE d’Arduino. El codi complet amb comentaris línia a línia que expliquen cada bloc l’he ficat al final de l’entrada per no interrompre ara aquesta lectura.
Per pujar l’sketch a la placa ESP32 descarregueu-vos la carpeta amb l’arxiu .ino, obriu l’sketch a l’IDE d’Arduino (o empegueu el codi del final), seleccioneu la placa ESP32 Dev Module (genèrica ESP32) i polseu el botó de pujar (fletxa cap a la dreta). Després de que es carregui, obriu el Monitor Sèrie (115200 bps) per veure els missatges d’inici.
Hi ha vegades que l’IDE d’Arduino, una vegada que ha compilat el codi, dona error a l’intentar carregar-lo a la placa. En aquest cas desconnecteu de l’ESP32 el pin de sortida de dades del sensor i torneu a intentar carregar l’sketch. Una vegada carregat, torneu a connectar el sensor al pin 12 (el que hem escollit).
Com ja he comentat, l’sketch disposa d’un filtre antirebot (debounce) que no tenen els codis que vaig fer per l’entrada Estudi de moviments amb barreres de llum controlades per Arduino. El codi ara ignora qualsevol canvi que duri menys de ‘DEBOUNCE_MS’ (10 ms), un temps suficient per eliminar soroll sense perdre transicions llegides, ja que, sinó, poden aparèixer canvis falsos.
La funció millis() proporciona el nombre de mil·lisegons transcorreguts des de l’arrencada de l’ESP32 (es podria haver utilitzat micros()). La diferència entre dues trucades a millis() proporciona l’interval amb una precisió de ±1 ms, més que suficient per a intervals de caiguda lliure que solen ser de desenes de mil·lisegons. Les dades de cada transició s’imprimeixen en dos formats: una línia amb els valors pelats i una línia descriptiva dels temps transcorreguts.
Realització de l’ experiment i captura de dades
Una vegada pujat l’sketch a l’ESP32, si obriu el terminal sèrie de l’Arduino configurat a 115200 bps, veureu que mostra el missatge «SISTEMA INICIALITZAT – Esperant primera transició…». Llavors, deixeu anar la tanca sense imprimir-li velocitat lateral ni rotació.

El sensor detectarà la primera transició (vora de la primera franja) i emetrà per sèrie la línia corresponent. La caiguda continuarà i s’imprimiran les dades de cada transició successiva i quan la tanca hagi passat completament pel sensor podeu recollir les dades impreses al terminal. Ara, copieu els valors en un full de càlcul o un aplicatiu de tractament gràfic de dades i analitzeu-les.
Un exemple de sortida típica pel port sèrie dels valors detectats és el de sota en el que suposem que hem mesurat els temps procedents de la caiguda d’una tanca amb totes les separacions iguals d’amplada L = 7,0 cm = 0,070 m:
Transició, Temps des d’inici (ms), Interval (ms)
1, 0, 0
-> Transició 1 als 0 ms (interval: 0 ms)
2, 48, 48
-> Transició 2 als 48 ms (interval: 48 ms)
3, 84, 36
-> Transició 3 als 84 ms (interval: 36 ms)
4, 116, 32
-> Transició 4 als 116 ms (interval: 32 ms)
5, 144, 28
-> Transició 5 als 144 ms (interval: 28 ms)
Que traslladats a una taula de dades ens dona:
| Transició | t (ms) | t (s) | Distància (cm) | Distància (m) |
| 1 | 0 | 0,000 | 0 | 0,0 |
| 2 | 48 | 0,048 | 7 | 0,07 |
| 3 | 84 | 0,084 | 14 | 0,14 |
| 4 | 116 | 0,116 | 21 | 0,21 |
| 5 | 144 | 0,144 | 28 | 0,28 |
Si ara grafiquem en un full de càlcul la distància vers el temps obtenim la gràfica de sota en la que està representada la línia de tendència, l’equació a la que s’ajusta i el valor de la qualitat de l’ajust, R2.

Aquest valor és prou bo (g = 4,815 x 2 = 9.63 m/s2) ja que en l’exemple he fet servir números inventats. En un experiment real el resultat no serà tant bo (normalment menor ja que sempre hi ha fregaments). Per exemple, en un cas real en el que les franges no tenen la mateixa amplada:
| Transició | Temps (ms) | Temps (s) | Distància (cm) | Distància (m) |
| 1 | 0 | 0 | 0 | 0 |
| 2 | 50 | 0,05 | 6 | 0,06 |
| 3 | 72 | 0,072 | 11 | 0,11 |
| 4 | 107 | 0,107 | 17 | 0,17 |
| 5 | 125 | 0,125 | 22 | 0,22 |
| 153 | 0,153 | 28 | 0,28 |

Com es veu, aquí l’acceleració de la gravetat mesurada és g = 4,754 x 2 = 9,51 m/s2, que tampoc està malament. Aquí les principals fonts d’error podrien ser:
- Error sistemàtic en la mesura de les longituds: amb una regla mil·limetrada pots ajustar a 0.5 mm, com a molt.
- Errors aleatori en els temps: la resolució de ‘millis()’ és d’1 ms, però la incertesa en la detecció de les cantonades pot ser similar al temps d’eliminació de rebots (debounce) (10 ms).
- Desviació de la caiguda lliure ideal: fregament amb l’aire, possible frec lateral amb el suport, velocitat inicial no nul·la., etc.
Per tant ja veieu que és possible mesurar l’acceleració de la gravetat amb materials de baix cost i fàcils de muntar, i el tradicional mètode de la tanca amb franges que a mi tant m’agrada.
Codi de l’sketch: ‘sketch_flyingfish_sensor.ino‘
/*SKETCH PER A ESP32 AMB SENSOR FLYINGFISH - DETECCIÓ DE TANQUES OPAQUES/TRANSPARENTSDescripció física de l' experiment: Un sensor Flyingfish (barrera òptica) muntat al pin GPIO12 de l'ESP32 detecta el pas d' una tanca que alterna zones transparents i opaques d' igual longitud.Cada canvi d'opacitat (transparent → opaca, opaca → transparent) produeix una transició en el senyal digital del sensor.Aquest sketch mesura el temps absolut (ms) des de la primera transició en què passa cada transició i l'interval de temps (ms) entre transicions consecutives.Les dades s' envien pel port sèrie en format CSV per a la seva anàlisi posterior. */// CONFIGURACIÓ DEL MAQUINARI (EDITAR SEGONS EL MUNTATGE REAL)const uint8_t PIN_SENSOR = 12; //Pin digital on està connectat el sensorconst bool SENSOR_ACTIVE_STATE = LOW; //Estat lògic quan el sensor no detecta la seva pròpia llum (HIGH = llum detectada, LOW = obstacle)const unsigned long DEBOUNCE_MS = 10; //Temps d' estabilització per evitar falses transicionsconst unsigned long SERIAL_BAUD = 115200; //Velocitat del port sèrie (bits per segon)// VARIABLES GLOBALS (ESTAT DE L'EXPERIMENT)bool lastStableState; // Últim estat estable del sensor (després del debounce)bool lastRawState; // Última lectura crua (per a detecció de canvis)unsigned long lastDebounceTime = 0; //Moment en què es va iniciar el període de debounceunsigned long experimentStartTime = 0; // Temps de la primera transició (ms des de l'inici de l'sketch)unsigned long lastTransitionTime = 0; // Temps de la transició anterior (ms des d'experimentStartTime)unsigned int transitionCount = 0; // Nombre total de transicions detectadesbool experimentStarted = false; // Bandera que indica si ja ha ocorregut la primera transició// PROTOTIPS DE FUNCIONSvoid printTransitionData(unsigned long currentTime, unsigned long interval);void sendHeader();// SETUP: INICIALITZACIÓ DEL SISTEMAvoid setup() { // 1. Inicialitzar comunicació sèrie per a depuració i captura de dades Serial.begin(SERIAL_BAUD); while (! Serial) { ; // Esperar que el port estigui llest (en plaques amb USB nadiu) } // 2. Configurar el pin del sensor com a entrada digital pinMode(PIN_SENSOR, INPUT); // 3. Llegir l' estat inicial del sensor i emmagatzemar-lo com a referència lastRawState = digitalRead(PIN_SENSOR); lastStableState = lastRawState; // 4. Enviar capçalera explicativa pel port sèrie sendHeader(); // 5. Informar per sèrie que el sistema està llest Serial.println("SISTEMA INICIALITZAT"); Serial.println("Esperant primera transició..."); Serial.println();}// LOOP: BUCLE PRINCIPAL DE DETECCIÓvoid loop() { // 1. Llegir l'estat actual del sensor (nivell lògic HIGH/LOW) bool currentState = digitalRead(PIN_SENSOR); // 2. Detectar un canvi cru (sense debounce) respecte a l'última lectura if (currentState != lastRawState) { // Reiniciar el temporitzador de debounce lastDebounceTime = millis(); lastRawState = currentState; } // 3. Comprovar si ha passat el temps de debò des de l'últim canvi cru if ((millis() - lastDebounceTime) >= DEBOUNCE_MS) { // L'estat ja està estable; verificar si difereix de l' estat estable anterior if (currentState != lastStableState) { // S'ha produït una transició vàlida (flanc)! lastStableState = currentState; // Obtenir el temps actual absolut (ms des de l'arrencada del microcontrolador) unsigned long now = millis(); // 4. Gestionar la primera transició (inici de l'experiment) if (!experimentStarted) { experimentStarted = true; experimentStartTime = now; lastTransitionTime = now; transitionCount = 1; Serial.println("========================================"); Serial.println("PRIMERA TRANSICIÓ DETECTADA"); Serial.println("Inici del cronometratge experimental."); Serial.println("========================================"); Serial.println(); Serial.println("Transició, Temps des d'inici (ms), Interval (ms)"); //La primera transició té interval zero (no hi ha anterior) printTransitionData(0, 0); } else { // 5. Per a transicions posteriors, calcular temps relatius unsigned long timeSinceStart = now - experimentStartTime; unsigned long interval = now - lastTransitionTime; //Actualitzar l'instant de l'última transició lastTransitionTime = now; transitionCount++; // 6. Enviar les dades de la transició pel port sèrie printTransitionData(timeSinceStart, interval); } } }}// FUNCIÓ: TRAMESA DE CAPÇALERA EXPLICATIVAvoid sendHeader() { Serial.println("========================================"); Serial.println("EXPERIMENT: Valla opaca/transparent amb sensor Flyingfish"); Serial.println("Microcontrolador: ESP32"); Serial.println("Pin del sensor: GPIO" + String(PIN_SENSOR)); Serial.println("Estat actiu del sensor: " + String(SENSOR_ACTIVE_STATE == HIGH ? " HIGH (llum)" : "LOW (obstacle)")); Serial.println("Debounce: " + String(DEBOUNCE_MS) + " ms"); Serial.println("========================================"); Serial.println(); Serial.println("OBJECTIU:"); Serial.println(" - Registrar l'instant de cada transició (opac↔transparent)."); Serial.println(" - Mesurar l'interval entre transicions consecutives."); Serial.println(" - Els temps s'expressen en mil·lisegons (ms)."); Serial.println(); Serial.println("FORMAT DE DADES (CSV):"); Serial.println(" Transició, Temps des d'inici (ms), Interval (ms)"); Serial.println("========================================");}// FUNCIÓ: IMPRIMIR DADES D' UNA TRANSICIÓvoid printTransitionData(unsigned long timeSinceStart, unsigned long interval) { // Format CSV simple: número de transició, temps des d' inici, interval Serial.print(transitionCount); Serial.print(", "); Serial.print(timeSinceStart); Serial.print(", "); Serial.println(interval); // Per a més claredat, també es pot imprimir una línia descriptiva // (opcional, comentar si només es desitja CSV pur) Serial.print(" -> Transició "); Serial.print(transitionCount); Serial.print(" als "); Serial.print(timeSinceStart); Serial.print(" ms (interval: "); Serial.print(interval); Serial.println(" ms)");}

