Construye una Plantilla Inteligente para Monitorizar Precisamente la Distribución de Presión Plantar
Componentes y suministros
| Arduino MKR1000 | × | 1 | |
| Resistor Fuerza Sensible (FSR) | × | 3 | Usé modelo Interlink 402 |
| Cable OpenBuilds - Por Metro | × | 1 | |
| Resistencia 10k ohm | × | 3 | |
| Power Bank | × | 1 | |
| SparkFun Triple Axis Accelerometer Breakout - ADXL345 | × | 1 |
Herramientas y máquinas necesarias
- Soldador (genérico)
Aplicaciones y servicios online
- Plataforma Thinger.io
- Arduino IDE
Sobre este proyecto
La medición de presión ya se usa en variedad de situaciones. Proporciona información sobre la mecánica de la marcha y tiene amplia gama de aplicaciones, p.ej. en situaciones clínicas y deportes. En este proyecto los sensores se usan para obtener información sobre la distribución del peso. La visualización en tiempo real del mapeo de presión también se incorpora porque facilita mucho entender tus datos.
Hardware requerido
- Arduino MKR1000 - La mayoría de placas 3.3V o 5V con Wi-Fi servirán, pero no recomendaría usar un ESP para esto. Por alguna razón, múltiples placas ESP devolvieron mediciones inexactas al crear este proyecto.
- Resistores Fuerza Sensible (3). Usé Interlink 402 (100 N). También usé un Interlink 406 más caro, pero en retrospectiva el 402 habría funcionado también. Usa más FSRs para mejor precisión.
- Resistencias 10K. Cambia la resistencia para escalar tus lecturas al rango que quieras. Cuanto mayor la resistencia, mayores los incrementos. Más info aquí.
- Acelerómetro, usé el ADXL345 16G. Úsalo para medir aceleración y movimiento del pie.
- Soldador para montarlo todo. Los clips de cocodrilo no funcionan bien en un zapato por razones obvias.
Cableado
Algunas notas rápidas sobre el circuito:
FSRs: Lee esto antes de empezar a soldar: ten extrema precaución al soldar cables a los FSRs. No intentes esto si no estás seguro de tener la habilidad. Debes soldar muy rápido o el plástico se derrite. Créeme, lo descubrí por las malas...
No hay lado positivo o negativo en un FSR, así que no te preocupes por eso.
Como mencioné, puedes cambiar las resistencias para escalar tus lecturas al rango que prefieras.
Tierra/5V: Como ves, todos los sensores están soldados a la línea de 5V y común.
Acelerómetro: Puedes omitir el acelerómetro si no lo necesitas para tu proyecto. No es estrictamente necesario para medir presión, pero puede ser útil si quieres medir aceleración o analizar el ciclo de la marcha. No es estrictamente necesario para este tutorial.
Fijar el hardware a la plantilla
Recomiendo hacer esto después de ejecutar el código primero, porque si cometiste errores de soldadura lo descubrirás en ese punto. Eso puede ahorrarte molestias de colocar y recolocar el hardware en la plantilla en caso de que necesites resoldar algo.
Los FSRs se fijan en tres puntos de la plantilla. El FSR superior izquierdo mide eversión, el superior derecho mide inversión, y el FSR en el talón mide presión del talón. Encontrar el sitio correcto para tus FSRs es cuestión de prueba. La mejor posición para fijarlos es donde el valor analógico cambia más al caminar.
Se usa cinta para sujetar los cables en su sitio. Si se mueven, puede causar interferencias en lecturas FSR. También usé velcro en la base de la plantilla y dentro del zapato para mantener la plantilla en su sitio.
Fija el acelerómetro en la parte trasera del talón del zapato si usas uno. Usé cinta de doble cara.
Medir las fuerzas
Ya estamos listos para medir las fuerzas. Nota: En las dos siguientes secciones asumo familiaridad con la librería Thinger.io. Para más info, refiérete a la sección "enviar datos через Thinger.io".
El programa tiene una parte de cabecera bastante larga, incluyendo variables necesarias para configurar cosas, como datos de conexión WiFi. Esta parte también incluye variables globales, mayormente arrays, usadas en el programa. Una elección importante fue usar arrays tanto como sea posible. La idea es usar elementos individuales de array para cada sensor FSR. En este caso los arrays tienen longitud 3.
No hagas costumbre de usar demasiadas variables globales, por efectos secundarios no deseados. Las usamos porque en varios casos son necesarias para enviar datos через Thinger.io.
El código está explicado en los comentarios. Iremos paso a paso. Puedes descargar el código completo abajo.
#define _DEBUG_ // nos permite usar Monitor Serie
#include <WiFi101.h>
#include <ThingerWifi101.h>
#include <Wire.h> // Acelerómetro
#include <Adafruit_Sensor.h> // Acelerómetro
#include <Adafruit_ADXL345_U.h> // Acelerómetro
#include <Adafruit_NeoPixel.h>
#define USERNAME "tuUsuario"
#define DEVICE_ID "tuDeviceID"
#define DEVICE_CREDENTIAL "tuDeviceCredential"
#define SSID "tuSSID"
#define SSID_PASSWORD "tuSSIDPassword"
//* Sensores FSR */
#define noFSRs 3 // Número de FSRs conectados
#define FSR1 A1
#define FSR2 A2
#define FSR3 A3
float fsrVoltageArray[3]; // Lectura analógica convertida y escalada a voltaje como float
float fsrForceArray[3]; // Fuerza en Newton
float fsrWeightInGramsArray[3]; // Peso convertido a gramos
int pinArray[3] = {FSR1, FSR2, FSR3}; // ID pin para los tres dispositivos
float forceMaxArray[3] = {100.0, 100.0, 100.0}; // Fuerzas máximas soportadas
float million = 1000000.0; // Unidad para "1/micro
float conversionToKgrams = 1.0/9.80665;
long K = 1000;
long R = 10*K; // R en K Ohm
long Vcc = 5000; // 5V=5000mV, 3.3V = 3300 mV
float voltageMax = 0.98 * Vcc; // Voltaje máximo al 95% de Vcc. Fijar fuerza al máximo más allá de este valor.
ThingerWifi101 thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL);
No entres en pánico si no lo entiendes todo aún. Tendrá más sentido una vez veas el resto del código.
Queremos visualizar los resultados. Para esto usamos la librería Wi-Fi Thinger.io. Esta conexión se configura mediante la línea:
ThingerWifi101 thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL);
Los argumentos de entrada se obtienen via tu cuenta en Thinger.io, así que necesitas configurarlo primero.
En la función setup(), primero se establece la conexión serial. Seguido de una llamada para crear la conexión WiFi.
void setup(void) {
Serial.begin(115200); // Iniciar Comunicación Serie
thing.add_wifi(SSID, SSID_PASSWORD); // llamada para configurar WiFi
}
void loop(void) {
thing.handle();
}
Luego definimos una "thing" llamada "pressure". Una "thing" es un concepto clave de la librería Thinger.io. Se comporta como una función, pero tiene estructura especial. Se necesita para enviar datos de resultado a la nube. Más detalles en sección "Enviar datos через Thinger.io".
La "thing" llamada "pressure" lee valores de los tres sensores FSR e imprime en consola serie. Los mismos valores se envían al canal "out". Así podemos verificar fácilmente los datos de entrada crudos.
En la "thing" llamada "voltage", los valores de voltaje se leen y almacenan en variable local "fsrReading". Con la función "map" el valor se escala, relativo a valores mínimo y máximo soportados, y devuelto al array "fsrVoltageArray". Mediante el bucle for, aseguramos que cada FSR tenga su propia ubicación en este array de resultados.
Nota que todo el código está dentro de la función setup. No pongas nada dentro del loop (como probablemente estás acostumbrado). La librería Thinger.io no lo maneja...
void setup(void) {
Serial.begin(115200); // Iniciar Comunicación Serie
thing.add_wifi(SSID, SSID_PASSWORD); // llamada para configurar WiFi
/* Sensores FSR */
thing["pressure"] >> [](pson & out) {
out["FSR1"] = analogRead(FSR1);
Serial.print("FSR1:");
Serial.println(analogRead(FSR1));
out["FSR2"] = analogRead(FSR2);
Serial.print("FSR2:");
Serial.println(analogRead(FSR2));
out["FSR3"] = analogRead(FSR3);
Serial.print("FSR3:");
Serial.println(analogRead(FSR3));
};
thing["voltage"] >> [](pson & out) {
for (int FSR = 0; FSR < noFSRs; FSR++) {
fsrVoltageArray[FSR] = 0.0; // Reset valores al entrar
fsrForceArray[FSR] = 0.0;
int fsrPin = pinArray[FSR];
int fsrReading = analogRead(fsrPin);
fsrVoltageArray[FSR] = (float) map(fsrReading, 0, 1023, 0, 5000); // cambia 5000 si no usas dispositivo 5V
} // fin bucle sobre FSRs
out["FSR1voltage"] = fsrVoltageArray[0];
out["FSR2voltage"] = fsrVoltageArray[1];
out["FSR3voltage"] = fsrVoltageArray[2];
};
void loop(void) {
thing.handle();
// No pongas código aquí porque Thinger.io no lo permitirá.
}
}
Después del bucle for, los valores se envían al canal de salida "out". Cada sensor tiene un string único, como "FSR1voltage".
La "thing" llamada "newton" es la función más compleja. Convierte voltaje a fuerza en Newtons. De nuevo se usa bucle for para cada FSR.
El cálculo es bastante complejo, porque necesitamos distinguir varios casos frontera. Si el valor de voltaje es demasiado pequeño o demasiado grande, asignamos un valor fijo a la fuerza. Si el voltaje está entre los valores de corte, se usa una función logarítmica para calcular la fuerza. Esta función se elige para reducir la pendiente de la curva.
Nota que las fórmulas son solo aproximadas. Cada sensor puede tener una curva (algo) diferente, pero por simplicidad no distinguimos esto aquí.
El valor resultante se almacena en array "fsrForceArray". Cada FSR tiene su posición en este array. Los tres valores finales se envían al canal de salida "out".
La función "thing" final se llama "weight". Se aplica una conversión directa para devolver la fuerza como peso en gramos.
thing["newton"] >> [](pson & out) {
for (int FSR = 0; FSR < noFSRs; FSR++) {
// El valor de la fuerza F como función del voltaje V se calcula como: F(V) = (Fmax/Vmax) * V
float force_value = (forceMaxArray[FSR]/voltageMax) * fsrVoltageArray[FSR];
// Tres situaciones se distinguen:
// 1. Si V está muy cerca del máximo (definido por voltageMax), la fuerza puede ir a infinito. Esto se evita fijándola al máximo apenas supera nuestro umbral voltageMax.
// 2. Si la fuerza calculada F es demasiado pequeña, la ponemos a cero para evitar efectos de ruido.
// 3. En todos los demás casos, tomamos el valor logarítmico para reducir la pendiente y
if ( fsrVoltageArray[FSR] < voltageMax ) {
// V no es demasiado alto en esta rama
if ( force_value <= 1.00 ) {
fsrForceArray[FSR] = 0.0; // Fuerza demasiado pequeña, poner a cero
} else {
fsrForceArray[FSR] = log10(force_value); // Valor OK, tomar log
}
} else {
// Limitar fuerza si voltaje muy cerca de Vcc (para Vcc sería infinito)
fsrForceArray[FSR] = log10(forceMaxArray[FSR]);
Serial.print("Corte activado para FSR = "); Serial.println(FSR);
}
} // Fin bucle sobre FSRs
out["FSR1newton"] = fsrForceArray[0];
out["FSR2newton"] = fsrForceArray[1];
out["FSR3newton"] = fsrForceArray[2];
}; // fin de thing
thing["weight"] >> [](pson & out) {
// Conversión directa para convertir fuerza en Newton a peso en gramos
for (int FSR = 0; FSR < noFSRs; FSR++) {
fsrWeightInGramsArray[FSR] = fsrForceArray[FSR] * conversionToKgrams * 1000.0;
}
out["FSR1weight"] = fsrWeightInGramsArray[0];
out["FSR2weight"] = fsrWeightInGramsArray[1];
out["FSR3weight"] = fsrWeightInGramsArray[2];
}; // fin de thing
No olvides configurar el dashboard en Thinger.io. Asumo que sabes cómo funciona.
Pista: Si todo funciona como se espera, tu dashboard debería verse similar a esto:
Medir la aceleración
Esto es en realidad más fácil que medir la presión. Necesitamos añadir algo de código a la cabecera primero. Empezamos incluyendo librerías "Wire.h" (para comunicar con pines SDA y SDL), "Adafruit_Sensor.h", y "Adafruit_ADXL345_U.h". También necesitaremos tres nuevas variables globales para medir y transferir aceleración en ejes x, y y z.
No elimines las librerías que añadiste antes. Las necesitarás para la conexión a Thinger.io.
En el siguiente bloque de código, comprobamos que el acelerómetro responda. Si no, no pasará nada. Si sí, se define el rango y se ejecuta una "thing" llamada "accelerometer". Esta consulta el dispositivo y envía los tres valores de aceleración en x, y, z al canal de salida "out".
Esta parte del código es similar al código de ejemplo incluido de Adafruit (Archivo > Ejemplos > Adafruit ADXL345), pero quité algunas partes porque no las necesitamos.
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_ADXL345_U.h>
/* Asignar ID único a este sensor al mismo tiempo */
Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345);
int x = 0;
int y = 0;
int z = 0;
void setup(void) {
Serial.begin(115200);
if(!accel.begin()) { // Inicializar sensor
Serial.println("No ADXL345 detectado");
} else {
// Rango para este sensor - Si no sabes el rango ejecuta
// displayDataRate del código de ejemplo del ADXL345 de Adafruit
accel.setRange(ADXL345_RANGE_16_G);
thing["accelerometer"] >> [](pson& out){ // Nueva función "thing" para Thinger.io
sensors_event_t event;
accel.getEvent(&event); // Obtener nuevo evento sensor
out["x"] = event.acceleration.x; // Mostrar resultados (aceleración en m/s^2)
out["y"] = event.acceleration.y;
out["z"] = event.acceleration.z;
Serial.print("X: "); Serial.print(event.acceleration.x); Serial.print(" ");
Serial.print("Y: "); Serial.print(event.acceleration.y); Serial.print(" ");
Serial.print("Z: "); Serial.print(event.acceleration.z); Serial.print(" ");Serial.println("m/s^2 ");
}; // fin de thing
} // fin if/else
}
Pista: tras ejecutar el código anterior, tu salida debería verse similar al ejemplo mostrado abajo
Unir todo
Encuentra el código completo al final de la página :)
Enviar datos через Thinger.io
Esta librería está bien documentada aquí, pero aquí hay info importante para hacer las "things" (sin juego de palabras) más fáciles de escribir y usar.
Thinger.io es una librería muy extensa. En esta sección solo cubrimos la funcionalidad necesaria y usada en nuestra aplicación. La estructura es así (nota el punto y coma ";" tras la llave de cierre "}"):
thing[] >> [](pson& out)
{
};
Esto se llama función lambda en C++. La primera palabra obligatoria para iniciar la definición es "thing" y toda la función también se refiere como "thing".
El string (p.ej. thing["miPrimeraThing"]) da nombre a la función. Cada "thing" necesita nombre único.
El símbolo ">>" indica que esta "thing" tiene solo valores de salida. Si se requiere entrada, cámbialo a "<<". Si se necesita tanto entrada como salida, usar "=". En nuestro caso solo tenemos valores de salida.
El argumento "pson &out" significa que nuestros valores de salida se envían через una estructura de datos llamada "out". El tipo de dato "pson" es muy flexible y puede contener varios tipos de variables, y también soporta documentos JSON.
En nuestro código usamos la forma "out[ para enviar valores a la visualización conectada con la llamada "ThingerWifi101 thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL)" en la primera parte del programa.
Como habrás notado, normalmente no hay mucho código dentro de la función loop. Solo necesitas llamar "thing.handle()". Puedes llamar endpoints y recursos de streaming en el loop también. Todo el otro código se maneja en las "things" que defines.
El código dentro de las "things" se ejecuta en bucle continuo. Es importante poner tus cálculos dentro de las "things" si tus datos se actualizan regularmente, como en nuestro caso.
Preguntas Frecuentes
¡Nada pasa!
Revisa tus cables. Probablemente algo se rompió en ese nivel. Si nada aparece en tu Monitor Serie, comprueba si "#define _DEBUG_" está en la primera línea de tu código. TLS puede interferir también. Puedes desactivarlo, pero ten cuidado. También verifica si tu conexión a Thinger.io funciona bien.
La salida no es la esperada
¿Todo el código que necesita actualizarse está dentro de una "thing"? Si está fuera no se actualiza. También he encontrado que los dispositivos ESP no funcionan bien con sensores FSR por ninguna razón aparente.
La salida no se actualiza regularmente
¿Añadiste delays a tu código? Quítalos :) Puedes configurar la tasa de refresco en Thinger.io.
Preferiría tener toda la salida en un gráfico
Pon todo el código dentro de una sola "thing"
¡Bien hecho!
Espero que este tutorial te haya ayudado a entender los fundamentos de sensores FSR, acelerómetros y Thinger.io. ¡Me encantaría leer sugerencias para mejorar este código, y no seas tímido y comparte tus creaciones!
Código
- Smart-Insole-Tutorial
Smart-Insole-Tutorial Arduino
Código completo de este tutorial
#define _DEBUG_ // Asegúrate que esto va antes de cualquier include o tu placa podría fallar
/* Por favor encuentra el tutorial aquí: https://www.hackster.io/projects/a5ceae */
#include <WiFi101.h> // Thinger
#include <ThingerWifi101.h> // Thinger
#include <Wire.h> // Acelerómetro
#include <Adafruit_Sensor.h> // Acelerómetro
#include <Adafruit_ADXL345_U.h> // Acelerómetro
#define USERNAME "tuUsuario"
#define DEVICE_ID "tuDevice"
#define DEVICE_CREDENTIAL "tuCredential"
#define SSID "tuSSID"
#define SSID_PASSWORD "tuSSIDPassword"
Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345); // Acelerómetro
int x = 0; // Reset a 0
int y = 0;
int z = 0;
//* Sensores FSR */
#define noFSRs 3 // Número de FSRs conectados
#define FSR1 A1 // Puertos analógicos
#define FSR2 A2
#define FSR3 A3
float fsrVoltageArray[3]; // Lectura analógica convertida y escalada a voltaje como float
float fsrForceArray[3]; // Fuerza en Newton
float fsrWeightInGramsArray[3]; // Peso convertido a gramos
int pinArray[3] = {FSR1, FSR2, FSR3}; // ID pin para los tres dispositivos
float forceMaxArray[3] = {100.0, 100.0, 100.0}; // Fuerzas máximas soportadas
float million = 1000000.0; // Unidad para "1/micro
float conversionToKgrams = 1.0/9.80665;
long K = 1000;
long R = 10*K; // R en K Ohm
long Vcc = 5000; // 5V=5000mV, 3.3V = 3300 mV
float voltageMax = 0.98 * Vcc; // Voltaje máximo al 95% de Vcc. Fijar fuerza al máximo más allá de este valor.
ThingerWifi101 thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL); // Llamada para configurar WiFi
void setup(void) {
Serial.begin(115200);
thing.add_wifi(SSID, SSID_PASSWORD);
if(!accel.begin()) { // Inicializar sensor
Serial.println("No ADXL345 detectado.");
} else {
accel.setRange(ADXL345_RANGE_16_G); // Rango para este sensor
thing["accelerometer"] >> [](pson& out){
sensors_event_t event;
accel.getEvent(&event);
out["x"] = event.acceleration.x;
out["y"] = event.acceleration.y;
out["z"] = event.acceleration.z;
};
}
/* Sensores FSR */
thing["pressure"] >> [](pson & out) {
out["FSR1"] = analogRead(FSR1);
// Serial.print("FSR1:");
// Serial.println(analogRead(FSR1));
out["FSR2"] = analogRead(FSR2);
// Serial.print("FSR2:");
// Serial.println(analogRead(FSR2));
out["FSR3"] = analogRead(FSR3);
// Serial.print("FSR3:");
// Serial.println(analogRead(FSR3));
};
thing["voltage"] >> [](pson & out) {
for (int FSR = 0; FSR < noFSRs; FSR++) {
fsrVoltageArray[FSR] = 0.0; // Reset valores al entrar
fsrForceArray[FSR] = 0.0;
int fsrPin = pinArray[FSR];
int fsrReading = analogRead(fsrPin);
fsrVoltageArray[FSR] = (float) map(fsrReading, 0, 1023, 0, 5000);
} // Fin bucle sobre FSRs
out["FSR1voltage"] = fsrVoltageArray[0];
out["FSR2voltage"] = fsrVoltageArray[1];
out["FSR3voltage"] = fsrVoltageArray[2];
};
thing["newton"] >> [](pson & out) {
for (int FSR = 0; FSR < noFSRs; FSR++) {
// El valor de la fuerza F como función del voltaje V se calcula como: F(V) = (Fmax/Vmax) * V
float force_value = (forceMaxArray[FSR]/voltageMax) * fsrVoltageArray[FSR];
// Tres situaciones se distinguen:
// 1. Si V está muy cerca del máximo (definido por voltageMax), la fuerza puede ir a infinito. Esto se evita fijándola al máximo apenas supera nuestro umbral voltageMax.
// 2. Si la fuerza calculada F es demasiado pequeña, la ponemos a cero para evitar efectos de ruido.
// 3. En todos los demás casos, tomamos el valor logarítmico para reducir la pendiente y distinguir mejor pequeños cambios.
if ( fsrVoltageArray[FSR] < voltageMax ) {
// V no es demasiado alto en esta rama
if ( force_value <= 1.00 ) {
fsrForceArray[FSR] = 0.0; // Fuerza demasiado pequeña, poner a cero
} else {
fsrForceArray[FSR] = log10(force_value); // Valor OK, tomar log
}
} else {
// Limitar fuerza si voltaje muy cerca de Vcc (para Vcc sería infinito)
fsrForceArray[FSR] = log10(forceMaxArray[FSR]);
Serial.print("Corte activado para FSR = "); Serial.println(FSR);
}
} // Fin bucle sobre FSRs
out["FSR1newton"] = fsrForceArray[0];
out["FSR2newton"] = fsrForceArray[1];
out["FSR3newton"] = fsrForceArray[2];
}; // Fin de thing
thing["weight"] >> [](pson & out) {
// Conversión directa para convertir fuerza en Newton a peso en gramos
for (int FSR = 0; FSR < noFSRs; FSR++) {
fsrWeightInGramsArray[FSR] = fsrForceArray[FSR] * conversionToKgrams * 1000.0;
}
out["FSR1weight"] = fsrWeightInGramsArray[0];
out["FSR2weight"] = fsrWeightInGramsArray[1];
out["FSR3weight"] = fsrWeightInGramsArray[2];
}; // Fin de thing
} // Fin de setup
void loop(void) {
thing.handle();
}
Esquemáticos
Tres FSRs (con divisores de voltaje) y un acelerómetro.
Proceso de manufactura
- Barman Inteligente: Construye tu Propia Estación de Cócteles con Raspberry Pi
- Construya un osciloscopio Arduino de 20 kHz en LCD Nokia 5110: Guía DIY fácil
- Test de Precisión de Tiempo de Reacción con Arduino UNO
- Por Qué la Bomba de Agua de su Hogar Lucha: Causas Comunes y Soluciones
- Chequeo trimestral de salud de la cadena de suministro: Garantice operaciones fluidas
- Minimice la pérdida de presión en su sistema de aire comprimido
- ABB lanza sensor inteligente para monitorizar la salud de rodamientos y reducir tiempo de inactividad
- Evolución del Hogar Inteligente: Qué Nos Depara el Futuro de la Vida Conectada
- Maximice el ROI de sus inversiones en IoT: Una guía práctica
- Sistemas de gestión de edificios: Impulsando ciudades inteligentes con IoT
- Medir eficientemente el uso de memoria de objetos Python con sys.getsizeof()