Andres Sanchez
Published

PressSoul

A smart device supporting the backbone of our community utilizing foot pressure data.

IntermediateWork in progress41
PressSoul

Things used in this project

Hardware components

Rotary Encoder with Push-Button
Rotary Encoder with Push-Button
×1
Flora RGB Neopixel LEDs- Pack of 4
Adafruit Flora RGB Neopixel LEDs- Pack of 4
×1
Grove - Round Force Sensor (FSR402)
Seeed Studio Grove - Round Force Sensor (FSR402)
×1

Software apps and online services

VS Code
Microsoft VS Code

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

3D design

Schematics

insoleSketch

3D design

Code

insole

C#
/* 
 * Project Press Insole
 * Author: Andres
 * Date: 8/4/2025
 * For comprehensive documentation and examples, please visit:
 * https://docs.particle.io/firmware/best-practices/firmware-template/
 */
#include "Particle.h"
#include <Adafruit_MQTT.h>
#include "Adafruit_MQTT/Adafruit_MQTT_SPARK.h"
#include "Adafruit_MQTT/Adafruit_MQTT.h"
#include "credentials.h"
#include "neopixel.h"
#include "Colors.h"
#include "Encoder.h"
#include "Button.h"

SYSTEM_MODE(AUTOMATIC);
//SYSTEM_THREAD(ENABLED);
/************ Global State (you don't need to change this!) ***   ***************/ 
TCPClient TheClient; 

// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details. 
Adafruit_MQTT_SPARK mqtt(&TheClient,AIO_SERVER,AIO_SERVERPORT,AIO_USERNAME,AIO_KEY); 

/****************************** Feeds ***************************************/ 
// Setup Feeds to publish or subscribe 
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname> 
Adafruit_MQTT_Subscribe VIBRATION = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/foot-pressure.vibration"); 

// Adafruit_MQTT_Subscribe AI_INSIGHT = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/foot-pressure.ai-insight");

//Adafruit_MQTT_Publish footTemp = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/footTemp");
// Adafruit_MQTT_Publish FOOTpressure = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/FOOTpressure");
Adafruit_MQTT_Publish HeelPressure = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/foot-pressure.heelpressure");
Adafruit_MQTT_Publish ArchPressure = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/foot-pressure.archpressure");
// Adafruit_MQTT_Publish BallPressure = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/foot-pressure.archpressure");
Adafruit_MQTT_Publish ToePressure = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/foot-pressure.toepressure");
// Adafruit_MQTT_Publish pressureTimer = Adafruit_MQTT_Publish(&mqtt,AIO_USERNAME"/feeds/)
//Adafruit_MQTT_Publish fallAlert = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/fallAlert");


//Functions
void MQTT_connect();
bool MQTT_ping();
//void subscriptionHandler(const char *event, const char *data);
void updatePressurePixels();
//void updateTempPixels();
uint32_t pressureToColor(int pressure);

// NeoPixels
const int PIXELCOUNT = 4;
Adafruit_NeoPixel pixel(PIXELCOUNT, SPI, WS2812B);
int bri = 200;
// int  NEOPIXEL = D2;

// Pressure sensors
int pressurePinBigToe = A5;
int pressurePinBall = A2;
int pressurePinArch = A1;
int pressurePinHeel = A0;



// Vibration motor
int Vibe = D15;
float frequency;
int vibrationPWM = 180; // ~28Hz


const int RED_PRESSURE_THRESHOLD = 240; // Match your pressureToColor logic
const unsigned long THREE_HOURS_MS = 10800000; // 3 hours in milliseconds

unsigned long redHeelStartTime = 0;
bool redHeelActive = false;
bool redAlertTriggered = false;


// struct RGB {
//   int r;
//   int g;
//   int b;
// };



// Encoder
const int PINA = D3;
const int PINB = D4;
const int SWPIN = D16;
Encoder myEnc(PINA, PINB);
Button encButton(SWPIN);
bool VibeOn = false;
int lastEncVal = 0;

// Timers
unsigned long lastPressureTime = 0;
unsigned long lastTempTime = 0;
const unsigned long pressureInterval = 5000;
// const unsigned long tempInterval = 7000;


unsigned int last, lastTime;
// float subValue,pubHEELValue ,pubARCHValue, pubBALLValue ,pubTOEValue;
int subValue,pubHEELValue ,pubARCHValue, pubBALLValue ,pubTOEValue;

int VibebuttonState;
// bool onOFF;

void setup() {
  Serial.begin(9600);
  pixel.begin();
  pixel.setBrightness(bri);
  // pixel.clear();
  pixel.show();

  pinMode(Vibe, OUTPUT);

// mqtt.subscribe(&AI_INSIGHT);


  if (encButton.isClicked()) {
    analogWrite(VibeOn, 180);
  }

  // myEnc.write(0);
    // Setup MQTT subscription
  mqtt.subscribe(&VIBRATION);
    waitFor(Serial.isConnected,15000);

  // String subscriptionName = String::format("%s/%s/", System.deviceID().c_str(), EVENT_NAME);
  // Particle.subscribe(subscriptionName, subscriptionHandler, MY_DEVICES);
  // Serial.printf("Subscribing to %s\n", subscriptionName.c_str());
}

void loop() {
MQTT_connect();
MQTT_ping();

// this is our 'wait for incoming subscription packets' busy subloop 
  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(1000))) {
    if (subscription == &VIBRATION) {
     VibebuttonState = atoi((char *)VIBRATION.lastread);
    
      if (VibebuttonState == 1){
      digitalWrite(Vibe,HIGH);
      
      }
      if (VibebuttonState ==0){
       digitalWrite(Vibe,LOW);
      
      }
    }
  }
if((millis()-lastTime > 10000)) {
    if(mqtt.Update()) {

      pubHEELValue = analogRead(pressurePinHeel);
      HeelPressure.publish(pubHEELValue);
      Serial.printf("Publishing HEEL%f \n",pubHEELValue); 

      pubARCHValue = analogRead(pressurePinArch);
      ArchPressure.publish(pubARCHValue);
      Serial.printf("Publishing ARCH %f \n",pubARCHValue); 

      pubTOEValue = analogRead(pressurePinBigToe);
      ToePressure.publish(pubTOEValue);
      Serial.printf("Publishing TOE %f \n",pubTOEValue); 
    } 
    lastTime = millis();
  }

// if ((subscription = mqtt.readSubscription(1000))) {
//   if (subscription == &AI_INSIGHT) {
//     String insight = String((char *)AI_INSIGHT.lastread);
//     Serial.println("AI Insight: " + insight);

//     // Example: trigger vibration or change NeoPixel color
//     if (insight == "fatigue") {
//       for (int i = 0; i < PIXELCOUNT; i++) pixel.setPixelColor(i, pixel.Color(255, 0, 255)); // Magenta
//       pixel.show();
//     }
//   }
// }



// if((millis()-lastTime > 6000)) {
//     if(mqtt.Update()) {

//       pubARCHValue = analogRead(pressurePinArch);
//       ArchPressure.publish(pubHEELValue);
//       Serial.printf("Publishing ARCH %f \n",pubARCHValue); 
//       } 
//     lastTime = millis();
//   }


// if((millis()-lastTime > 6000)) {
//     if(mqtt.Update()) {

//       pubBALLValue = pressurePinBall;
//       pubBALLValue.publish(pubHEELValue);
//       Serial.printf("Publishing BALL %f \n",pubBALLValue); 
//       } 
//     lastTime = millis();
//   }

// if((millis()-lastTime > 6000)) {
//     if(mqtt.Update()) {

//       pubTOEValue = analogRead(pressurePinBigToe);
//       ToePressure.publish(pubTOEValue);
//       Serial.printf("Publishing TOE %f \n",pubTOEValue); 
//       } 
//     lastTime = millis();
//   }
//   }


  unsigned long currentTime = millis();

  // Pressure to NeoPixels every 5 seconds
  if (currentTime - lastPressureTime >= pressureInterval) {
    lastPressureTime = currentTime;
    updatePressurePixels();
  }


  // Toggle vibration on button click
  if (encButton.isClicked()) {
    VibeOn = !VibeOn;
    Serial.printf("Vibration %s\n", VibeOn ? "ON" : "OFF");
  }

  // Encoder adjusts vibration frequency
  int encVal = myEnc.read();
  if (encVal != lastEncVal) {
    if (encVal < 0) encVal = 0;
    if (encVal > 100) encVal = 100;

    int rpm = map(encVal, 0, 100, 1200, 2100);
    vibrationPWM = map(rpm, 1200, 2100, 130, 255);
    lastEncVal = encVal;

    Serial.printf("Encoder RPM: %i, PWM: %i\n", rpm, vibrationPWM);
  }

  // Apply vibration
analogWrite(Vibe, VibeOn ? vibrationPWM : 0);
  // analogWrite(Vibe, true);

}


// // Pressure sensor to NeoPixel mapping
void updatePressurePixels() {
  int heel = analogRead(pressurePinHeel);
  int arch = analogRead(pressurePinArch);
  int ball = analogRead(pressurePinBall);
  int toe = analogRead(pressurePinBigToe);
  pixel.setPixelColor(0, pressureToColor(heel));
  pixel.setPixelColor(1, pressureToColor(arch));
  pixel.setPixelColor(2, pressureToColor(ball));
  pixel.setPixelColor(3, pressureToColor(toe));
  pixel.show();
  Serial.printf("Heel: %i, Arch: %i, Ball: %i, Toe: %i\n", heel, arch, ball, toe);
}



// Pres// Pressure value to color
uint32_t pressureToColor(int pressure) {
  if (0 < pressure < 120) return pixel.Color(0, 0, 255);         // Blue
  // if (pressure < 120) return pixel.Color(0, 0, 255);         // Blue
  else if (pressure < 240) return pixel.Color(255, 255, 0);  // Yellow
  else return pixel.Color(255, 0, 0);                        // Red
}

void MQTT_connect() {
  int8_t ret;
  if (mqtt.connected()) {
    return;
  }
  Serial.print("Connecting to MQTT... ");
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
    Serial.printf("Error Code %s\n",mqtt.connectErrorString(ret));
    Serial.printf("Retrying MQTT connection in 5 seconds...\n");
    mqtt.disconnect();
   delay(5000);  // wait 5 seconds and try again
  }
Serial.printf("MQTT Connected!\n");
}
bool MQTT_ping() {
  static unsigned int last;
  bool pingStatus;
  if ((millis()-last)>120000) {
    Serial.printf("Pinging MQTT \n");
    pingStatus = mqtt.ping();
    if(!pingStatus) {
      Serial.printf("Disconnecting \n");
      mqtt.disconnect();
    }
  last = millis();
  }
return pingStatus;
}



// void updatePressurePixels() {
//   int heel = analogRead(pressurePinHeel);
//   int arch = analogRead(pressurePinArch);
//   int ball = analogRead(pressurePinBall);
//   int toe = analogRead(pressurePinBigToe);

//   // RGB heelColor = pressureToColor(heel);
//   // RGB archColor = pressureToColor(arch);
//   // RGB ballColor = pressureToColor(ball);
//   // RGB toeColor = pressureToColor(toe);

//   pressurePinHeel = pressureToColor(heel);
//   pressurePinArch = pressureToColor(arch);
//   pressurePinBall = pressureToColor(ball);
//   pressurePinBigToe = pressureToColor(toe);

//   pixel.setPixelColor(0, pixel.Color(heel));
//   pixel.setPixelColor(1, pixel.Color(arch));
//   pixel.setPixelColor(2, pixel.Color(ball));
//   pixel.setPixelColor(3, pixel.Color(toe));
//   pixel.show();

//   Serial.printf("Heel: %i, Arch: %i, Ball: %i, Toe: %i\n", heel, arch, ball, toe);
// }


// // BME280 temperature to NeoPixel alert
// void updateTempPixels() {
//   float tempC = bme.readTemperature();
//   tempf = (tempC * 9.0 / 5.0) + 32.0;
//   Serial.printf("Temp (F): %.2f\n", tempf);

//   uint32_t color = 0;
//   if (tempf >= 82 && tempf < 86) color = pixel.Color(0, 0, 255);       // Blue
//   else if (tempf >= 86 && tempf < 90) color = pixel.Color(255, 255, 0); // Yellow
//   else if (tempf >= 90 && tempf < 94) color = pixel.Color(255, 0, 0);   // Red

//   if (color != 0) {
//     for (int i = 0; i < PIXELCOUNT; i++) {
//       pixel.setPixelColor(i, color);
//     }
//     pixel.show();
//   }
// }

Credits

Andres Sanchez
3 projects • 3 followers
DESIGNER expanding my skills in the realms of smart devices implementing code and fabrication. To then be used for a better SOCIETY.

Comments