electricityforprogress / BiodataFeather

Biodata Sonification Device using a 555 timer and Adafruit ESP32 allowing BLE and RTPMIDI via wifi
MIT License
42 stars 8 forks source link

Values not changing or result not accurate #7

Closed quintendewilde closed 9 months ago

quintendewilde commented 10 months ago

Hi

I'm hoping I get some response here even though the project is still not very much alive anymore.

But I'm making a simplified version of the project and stuck with the results being the same.

I'm using the breadboard example here. (I have all the parts for the PCB board too but, unable to recreate it with perfboard, my schematic reading isn't very good as I am no a very technical person) Breadboard example

This is my code

/*----------------------------------------------------------
   Everything for OSC && WiFi
  ----------------------------------------------------------*/

#if defined(ESP8266)
#include <ESP8266WiFi.h>        // Include the Wi-Fi library
#else
#include <WiFi.h>
#endif
#include <WiFiUdp.h>            // UDP library
#include <OSCMessage.h>         // OSC library
#include <OSCBundle.h>

#include <Wire.h>

WiFiUDP Udp; // A UDP instance to let us send and receive packets over UDP
//const IPAddress outIp(192, 168, 0, 234); // remote IP of your computer

const unsigned int outPort = 4560; // remote port to receive OSC
const unsigned int localPort = 8888; // local port to listen for OSC packets (actually not used for sending)

// Sensor and analysis setup
const int sensorPin = A0; // Wemos A0 gpio
const byte samplesize = 10;
volatile unsigned long samples[samplesize];
volatile byte SampleIndex = 0;
float threshold = 2.3; // Adjust as necessary

void setup()
{
    Serial.begin(115200);
    delay(10);
    Serial.println('\n');

    WiFi.begin(ssid, password);             // Connect to the network
    Serial.print("Connecting to ");
    Serial.print(ssid); Serial.println(" ...");

    int i = 0;
    while (WiFi.status() != WL_CONNECTED) { // Wait for the Wi-Fi to connect
    delay(1000);
    Serial.print(++i); Serial.print(' ');
    }
    Serial.println('\n');
    Serial.println("Connection established!");
    Serial.print("IP address:\t");
    Serial.println(WiFi.localIP());         // Send the IP address of the ESP8266 to the computer

    Serial.println("Starting UDP");
    Udp.begin(localPort);
    Serial.print("Local port: ");
    #ifdef ESP32
      Serial.println(localPort);
    #else
      //Serial.println(Udp.localPort());
    #endif

  // Sensor setup
  pinMode(sensorPin, INPUT);
  Serial.println("Setup complete");

}

/*----------------------------------------------------------
    Galvano sensor  loop
  ----------------------------------------------------------*/
void loop() {
  // If the samples array is full, analyze the sample
  if (SampleIndex >= samplesize) {
    analyzeSample();
  }

  // Read sensor data
  if (SampleIndex < samplesize) {
    samples[SampleIndex++] = analogRead(sensorPin);
    delay(5); // Delay between samples
  }
}

void analyzeSample() {
  unsigned long sum = 0;
  unsigned long maxVal = 0;
  unsigned long minVal = 4294967295; // Max unsigned long
  float stdDev = 0;

  for (byte i = 0; i < samplesize; i++) {
    sum += samples[i];
    if (samples[i] > maxVal) maxVal = samples[i];
    if (samples[i] < minVal) minVal = samples[i];
  }

  float avg = sum / (float)samplesize;
  for (byte i = 0; i < samplesize; i++) {
    stdDev += pow(samples[i] - avg, 2);
  }
  stdDev = sqrt(stdDev / samplesize);

if ((maxVal - minVal) > (stdDev * threshold)) {
    OSCMessage msg("/sensor/change");
    msg.add((float)avg); // Casting to float
    Serial.println(avg);
    msg.add((int)maxVal); // Casting to int
    Serial.println(maxVal);
    msg.add((int)minVal); // Casting to int
    Serial.println(minVal);
    msg.add((float)stdDev); // Casting to float
    sendOSCMessage(msg);
  }

  SampleIndex = 0; // Reset index for next samples
}

void sendOSCMessage(OSCMessage &msg) {
  Udp.beginPacket(outIp, outPort);
  msg.send(Udp); // Send the message
  Udp.endPacket(); 
  msg.empty(); // Free space occupied by the message
  Serial.println("OSC message sent");

}

Replaced every piece of the board incase something might be faulty (tried three different Wemos boards) Quadrupple checked my wiring

This is the output of the serial monitor:

2 3 4 

Connection established!
IP address: 192.168.0.114
Starting UDP
Local port: Setup complete
2.60
4
2
OSC message sent
0.80
2.50
4
2
OSC message sent
0.67
2.70
4
2
OSC message sent
0.78
2.70
4
2
OSC message sent
0.78
2.70
4
1
OSC message sent
1.00
2.70
4
2
OSC message sent
0.78
2.70
4
2
OSC message sent
0.78
2.90
3
2
OSC message sent
0.30
2.40
4
2
OSC message sent
0.66
2.50
4
2
OSC message sent
0.81
3.00
5
2
OSC message sent
0.89
2.50
3
1
OSC message sent
0.67
2.50
4
2
OSC message sent
0.67
2.60
4
2
OSC message sent
0.80
2.60
4
2
OSC message sent
0.66
2.60
4
2
OSC message sent
0.66
2.80
4
2
OSC message sent
0.75
2.80
3
2
OSC message sent
0.40
2.50
4
2
OSC message sent
0.81
2.50
4
2
OSC message sent
0.67
2.80
3
2
OSC message sent
0.40
2.50
4
2
OSC message sent
0.67
2.50
4
1
OSC message sent
0.92
2.70
5
2
OSC message sent
0.90
2.70
4
2
OSC message sent
0.78
2.60
4
2
OSC message sent
0.66
2.90
4
2
OSC message sent
0.70

This is what I get when I use the perfboard:

When I used my soldered version I get nothing..

@ IMG_1966 IMG_1967 IMG_1968 IMG_1969 IMG_1970 IMG_1971

electricityforprogress commented 10 months ago

Hello and thank you for your interest in Biodata Sonification. It sounds like you are trying to use the old breadboard kit instructions and use an ESP32. Did you follow the breadboard instructions, does the 555 timer flash when the you touch the electrodes to test the biodata circuit?

... luckily i have a different codebase which will work on ESP32. I recommend using an ESP32-S3 which has usb-midi as well as ble/wifi -- https://github.com/electricityforprogress/BiodataFeather/tree/master/Biodata_v007_ESP32S3

Looking briefly at your code, you don't seem to have the interrupt input declared in setup() or nor a function to perform the biodata reading.

quintendewilde commented 10 months ago

Yes I followed the exact wiring.
And I used our ESP32 code to recreate my example.

I though I had it figured out correctly. But if u say that I don't have the interrupt input declared in setup or missing a function. Then I'm pretty sure I did that wrong :(. Hmm this goes beyond my programming knowledge as well. I just want to send the variating data to osc and handle it from there...

@electricityforprogress Because I'm correct that I can't use the Biodata_Feather_ESP32_07.ino solely to make it work, right? I don't need the other midi stuff or changing of channels. So I tried to condens it into just the basics and OSC.

electricityforprogress commented 10 months ago

If you are trying a new project, i recommend getting it to work 'without changes' first and then go back and start to make your modifications, that will give you some ground to stand on while doing new work. You can test to ensure your hardware is working. Did you test the 555 circuit works with your fingers, that is step 0.

To use an 'arduino based' code project you need all of the .ino files in the project folder as well as any dependent libraries installed in the Arduino IDE. So grab the entire repository and use the folder Biodata_v007_ESP32S3

I didn't go into your code deeply and there may be other issues, but this is how an interrupt works --

  1. you need to declare the interrupt pin at the top of the code(in your case it is A0) const byte interruptPin = A0; //galvanometer input

  2. create the ISR function at the top of the code to read the pulse: void sample(){ if(sampleIndex < samplesize) { samples[sampleIndex] = micros() - microseconds; microseconds = samples[sampleIndex] + microseconds; //rebuild micros() value w/o recalling //micros() is very slow //try a higher precision counter //samples[index] = ((timer0_overflow_count << 8) + TCNT0) - microseconds; sampleIndex += 1; } }

  3. In Setup() you need to configure the pin and assign the ISR (interrupt routine):

    pinMode(interruptPin, INPUT_PULLUP); //pulse input //setup pulse input pin attachInterrupt(interruptPin, sample, RISING); //begin sampling from interrupt

  4. Now the code should automatically respond to the 555 timer pulses on the interrupt pin ;)

quintendewilde commented 9 months ago

@electricityforprogress I'll try it out this weekend.

And I know it's better to re-create the whole project, but time is not in my favor and I want the easiest (fastest) solution to just get the values.

In the slides found here on page 18 the orange jumper goes to the pin 12 of the Feather, is this for the ADC or is to read als gpio? I'm beginning to doubted everything for the moment.

electricityforprogress commented 9 months ago

Yes pin 12 is used as the interrupt input. It is defined in one of the main files of the program. This goes from pin 3 output of the 555 timer into your main MCU.

You will need to invest time to understand what you're doing, believe me it only takes a few minutes or a few hours or a few days, but you'll get it. Don't undercut your time involved and invested to produce something challenging. Follow the steps that are laid out before you, and once you truly understand what's going on then you should make changes.

ooktown commented 9 months ago

I agree with Sam. I got the MIDI Sprout and newer BiodataFeather working as-designed, and then I hacked the shizzle out of them. It was fairly easy with the code and schematics Sam provides (as long as you can find all the right parts).

quintendewilde commented 9 months ago

@ooktown @electricityforprogress Well I totally agree of course with the mindset. And this is what I am doing for the last couple of weeks. So definitely don't get me wrong, and I'm trying.

Yesterday evening I re-wrote everything and got results that made more sense.

But when I touched the sensor pads, the data flow stops. And when I touch it again, it starts again. But not consistently. When I touch the blue wire (image below) it starts and stops, when I touch the yellow wire it starts (doesn't stop), when touching both or hanging them on a mushroom, it stops again. So something is still wrong with the code?

So I think that the data scale is too big, and it can't average any more and freezes? So I added a "debouncing" piece of code. But still doesn't resolve the issue.

I'm trying to search other platforms for solution, but because it's very specific in this project, I'm not finding answers. I even asked ChatGPT 😅

#include <Arduino.h>

// Pin Definitions
const byte interruptPin = 15; // D5 On Wemos D1 mini

// Sample Data
volatile unsigned long microseconds;
volatile byte sampleIndex = 0;
const byte samplesize = 10; // size of sample
volatile unsigned long samples[samplesize];

void handleInterrupt() {
    unsigned long currentMicros = micros();
    unsigned long sampleValue = currentMicros - microseconds;
    microseconds = currentMicros;

    // 'debouncing': ignore extreme values
    if (sampleValue < 100000) { // ignore value
        samples[sampleIndex] = sampleValue;
        if (++sampleIndex >= samplesize) {
            sampleIndex = 0;
            digitalWrite(LED_BUILTIN, HIGH);
        }
    }
}

void analyseSample() {
    unsigned long maxSample = 0;
    unsigned long minSample = ~0ul; // use ~0ul as replacement ULONG_MAX
    unsigned long sumSamples = 0;

    for (byte i = 0; i < samplesize; i++) {
        sumSamples += samples[i];
        if (samples[i] > maxSample) maxSample = samples[i];
        if (samples[i] < minSample) minSample = samples[i];
    }

    unsigned long avg = sumSamples / samplesize;
    unsigned long range = maxSample - minSample;

    Serial.print("Gemiddelde: "); Serial.println(avg); // average
    Serial.print("Maxima: "); Serial.println(maxSample); // max sample
    Serial.print("Minima: "); Serial.println(minSample); // min sample
    Serial.print("Bereik: "); Serial.println(range); // range
}

void setup() {
    pinMode(interruptPin, INPUT_PULLUP);
    pinMode(LED_BUILTIN, OUTPUT); // Response
    attachInterrupt(digitalPinToInterrupt(interruptPin), handleInterrupt, RISING);

    Serial.begin(115200);
    while (!Serial); // Wait serial
    Serial.println("Interrupt Sample Reader started");
}

void loop() {
    if (digitalRead(LED_BUILTIN) == HIGH) {
        analyseSample();
        digitalWrite(LED_BUILTIN, LOW);
    }

}

OUTPUT: Gemiddelde = Average Maxima = Max Minima = Min Bereik = Range


`Gemiddelde: 125
Maxima: 282
Minima: 91
Bereik: 191
Gemiddelde: 87619
Maxima: 870502
Minima: 89
Bereik: 870413
Gemiddelde: 36
Maxima: 180
Minima: 6
Bereik: 174
Gemiddelde: 255
Maxima: 1133
Minima: 82
Bereik: 1051
Gemiddelde: 13
Maxima: 34
Minima: 6
Bereik: 28
Gemiddelde: 152
Maxima: 576
Minima: 6
Bereik: 570
Gemiddelde: 79
Maxima: 148
Minima: 29
Bereik: 119
Gemiddelde: 139688
Maxima: 1389750
Minima: 29
Bereik: 1389721
Gemiddelde: 52
Maxima: 128
Minima: 11
Bereik: 117
Gemiddelde: 41
Maxima: 140
Minima: 6
Bereik: 134
Gemiddelde: 138
Maxima: 328
Minima: 6
Bereik: 322
Gemiddelde: 1731
Maxima: 9942
Minima: 88
Bereik: 9854
Gemiddelde: 46
Maxima: 99
Minima: 7
Bereik: 92
Gemiddelde: 11
Maxima: 20
Minima: 5
Bereik: 15
Gemiddelde: 1627
Maxima: 9445
Minima: 5
Bereik: 9440
Gemiddelde: 23
Maxima: 42
Minima: 6
Bereik: 36
Gemiddelde: 1724
Maxima: 9876
Minima: 27
Bereik: 9849
Gemiddelde: 19
Maxima: 48
Minima: 6
Bereik: 42
Gemiddelde: 85
Maxima: 281
Minima: 6
Bereik: 275
Gemiddelde: 95
Maxima: 106
Minima: 77
Bereik: 29
Gemiddelde: 1233661
Maxima: 12331189
Minima: 7
Bereik: 12331182
Gemiddelde: 155
Maxima: 755
Minima: 6
Bereik: 749
Gemiddelde: 1197
Maxima: 10458
Minima: 82
Bereik: 10376
Gemiddelde: 34
Maxima: 236
Minima: 6
Bereik: 230
Gemiddelde: 223
Maxima: 859
Minima: 7
Bereik: 852
Gemiddelde: 110
Maxima: 185
Minima: 85
Bereik: 100
Gemiddelde: 66
Maxima: 238
Minima: 6
Bereik: 232
Gemiddelde: 39

The output wires here like the slides show on the tutorial on the website IMG_1997

electricityforprogress commented 9 months ago

I'm am excited that you are putting so much effort into this project and I want you to succeed. But you must understand that I am sorry that i am not going to debug your personal version of the code, and certainly not some GPT based code (deep sadness from an educator). From my review this morning, it looks like the code will run, and as you report your code does run but doesn't give you the results you want, so i dunno.

** You really should get the original code working first, and after that start to make your changes. Load the original code based on your current build, you dont need the other LEDs if that's what you are avoiding.

  1. Build the circuit per the instruction PDF
  2. Ensure the 555 timer circuit is working before you try to connect to your arduino !!!!~! this is a step you are skipping
  3. 'Add' blocks to the original code and try to get your desired outcomes

I think the only thing you need to do is take the original code, set 'rawSerial' = 1, upload and turn on the serial monitor to see the raw data values.

You need to find a local friend or Maker Arts Space where you can get direct assistance with your project.

just to be clear, this isn't an Issue with the codebase in github, rather this is a student question in an inappropriate forum.

Good luck with your project, try to rebuild it two or three more times (i'm serious) and you will get there

Issue CLOSED

electricityforprogress commented 9 months ago

CLOSED

quintendewilde commented 9 months ago

For the record, I did and still trying it on myself. I said I only used GPT to try to figure out what's wrong. Thanks for the advice, though!